]> err.no Git - sope/commitdiff
committed ludovics ora patches
authorhelge <helge@e4a50df8-12e2-0310-a44c-efbce7f8a7e3>
Fri, 5 Oct 2007 21:02:31 +0000 (21:02 +0000)
committerhelge <helge@e4a50df8-12e2-0310-a44c-efbce7f8a7e3>
Fri, 5 Oct 2007 21:02:31 +0000 (21:02 +0000)
git-svn-id: http://svn.opengroupware.org/SOPE/trunk@1544 e4a50df8-12e2-0310-a44c-efbce7f8a7e3

sope-gdl1/Oracle8/ChangeLog
sope-gdl1/Oracle8/GNUmakefile
sope-gdl1/Oracle8/OracleAdaptorChannel.h
sope-gdl1/Oracle8/OracleAdaptorChannel.m
sope-gdl1/Oracle8/OracleAdaptorChannelController.h [new file with mode: 0644]
sope-gdl1/Oracle8/OracleAdaptorChannelController.m [new file with mode: 0644]
sope-gdl1/Oracle8/README
sope-gdl1/Oracle8/err.h [new file with mode: 0644]
sope-gdl1/Oracle8/err.m [new file with mode: 0644]
sope-gdl1/Oracle8/otest.m

index 7d2650912bd133224701db2660c29cb79f21138b..7a95da968a7c6e9742e5c8bd8d0a87e6957640fd 100644 (file)
@@ -1,6 +1,14 @@
+2007-10-05     Ludovic Marcotte <ludovic@inverse.ca>
+
+       * Fixed otest wrt bundle name.
+       * Modified sqlFolderFormat so that for the c_deleted
+         columns, we default the value to 0.
+       * Added full CLOB support for reading / writing more
+         than 4000 characters in a CLOB column.
+
 2007-09-20  Ludovic Marcotte <ludovic@inverse.ca>
 
-        * Small for for 64-bit platforms.
+        * Small fix for 64-bit platforms.
 
 2007-08-29  Helge Hess  <helge.hess@opengroupware.org>
 
index 09b6a52694758420a9848cca1fbab2cd2b1bc355..496d732e2dd5177e24eec33810224071203e2897 100644 (file)
@@ -26,15 +26,16 @@ include ./Version
 
 # Global properties and dependancies
 SOPE_ROOT=../..
-
-ADDITIONAL_INCLUDE_DIRS += -I../GDLAccess -I.. -I/usr/include/oracle/10.2.0.3/client
+ORACLE_VERSION=10.2.0.3
+#ORACLE_VERSION=11.1.0.1
+ADDITIONAL_INCLUDE_DIRS += -I../GDLAccess -I.. -I/usr/include/oracle/$(ORACLE_VERSION)/client
 
 ifneq ($(frameworks),yes)
-Oracle8_BUNDLE_LIBS += -L/usr/lib/oracle/10.2.0.3/client/lib/ -locci -lociei -lclntsh -lnnz10 -lGDLAccess -lEOControl
-otest_TOOL_LIBS   += -L/usr/lib/oracle/10.2.0.3/client/lib/ -locci -lociei -lclntsh -lnnz10 -lGDLAccess -lEOControl
+Oracle8_BUNDLE_LIBS += -L/usr/lib/oracle/$(ORACLE_VERSION)/client/lib/ -locci -lociei -lclntsh -lnnz10 -lGDLAccess -lEOControl
+otest_TOOL_LIBS   += -L/usr/lib/oracle/$(ORACLE_VERSION)/client/lib/ -locci -lociei -lclntsh -lnnz10 -lGDLAccess -lEOControl
 else
-Oracle8_BUNDLE_LIBS += -L/usr/lib/oracle/10.2.0.3/client/lib/ -locci -lociei -lclntsh -lnnz10 -framework GDLAccess -framework EOControl
-otest_TOOL_LIBS   += -L/usr/lib/oracle/10.2.0.3/client/lib/ -locci -lociei -lclntsh -lnnz10 -framework GDLAccess -framework EOControl
+Oracle8_BUNDLE_LIBS += -L/usr/lib/oracle/$(ORACLE_VERSION)/client/lib/ -locci -lociei -lclntsh -lnnz10 -framework GDLAccess -framework EOControl
+otest_TOOL_LIBS   += -L/usr/lib/oracle/$(ORACLE_VERSION)/client/lib/ -locci -lociei -lclntsh -lnnz10 -framework GDLAccess -framework EOControl
 endif
 
 # Bundle
@@ -49,9 +50,11 @@ Oracle8_OBJC_FILES = \
        EOAttribute+Oracle.m \
        OracleAdaptor.m \
        OracleAdaptorChannel.m \
+       OracleAdaptorChannelController.m \
        OracleAdaptorContext.m \
        OracleSQLExpression.m \
-       OracleValues.m
+       OracleValues.m \
+       err.m
 
 Oracle8_PRINCIPAL_CLASS = OracleAdaptor
 
@@ -64,4 +67,4 @@ TOOL_NAME = otest
 otest_OBJC_FILES = otest.m
 
 include $(GNUSTEP_MAKEFILES)/bundle.make
-include $(GNUSTEP_MAKEFILES)/tool.make
\ No newline at end of file
+include $(GNUSTEP_MAKEFILES)/tool.make
index 7c6a4f02b46335dedc405227a920b84ae61c0da9..fd20d5e41cd8af6d6e3e226adeec8b1c300c7b17 100644 (file)
 
 #import <GDLAccess/EOAdaptorChannel.h>
 
-#import "../GDLContentStore/EOAdaptorChannel+GCS.h"
+//#import "../GDLContentStore/EOAdaptorChannel+GCS.h"
 
 @class NSMutableArray;
 
-@interface OracleAdaptorChannel : EOAdaptorChannel <GCSEOAdaptorChannel>
+typedef struct
+{
+  OCIDefine *def;   // The define information handle of the column
+  dvoid *value;     // The value of the column (the buffer size is max_width bytes)
+  ub2 type;         // The type of the column
+  ub2 width;        // The current width of the value that has been read
+  ub2 max_width;    // The maximum width of the column
+} column_info;
+
+@interface OracleAdaptorChannel : EOAdaptorChannel
 {
 @private
   // Oracle's related ivars
   OCIError* _oci_err;
   OCIStmt* _current_stm;
 
-    // ...
+  // ...
   NSMutableArray *_resultSetProperties;
   NSMutableArray *_row_buffer;
 }
 
+- (OCIEnv *) environment;
 - (OCISvcCtx *) serviceContext;
 - (OCIError *) errorHandle;
 
index 7dc70289a2efad8f660138f4926bda7d0cf6ec8e..9dd236bcbb4cfe3b2366686b3849b6448e7c8223 100644 (file)
 
 #import "OracleAdaptorChannel.h"
 
-#import "OracleAdaptor.h"
-#import "OracleAdaptorContext.h"
-#import "EOAttribute+Oracle.h"
+#include "err.h"
+#include "OracleAdaptor.h"
+#include "OracleAdaptorChannelController.h"
+#include "OracleAdaptorContext.h"
+#include "EOAttribute+Oracle.h"
 
 #import <NGExtensions/NSObject+Logs.h>
 
-typedef struct
-{
-  OCIDefine *def;   // The define information handle of the column
-  dvoid *value;     // The value of the column (the buffer size is max_width bytes)
-  ub2 type;         // The type of the column
-  ub2 width;        // The current width of the value that has been read
-  ub2 max_width;    // The maximum width of the column
-} column_info;
-
-
 //
 //
 //
@@ -63,8 +55,12 @@ typedef struct
       info = [[_row_buffer objectAtIndex: c] pointerValue];
       [_row_buffer removeObjectAtIndex: c];
 
-      free(info->value);
-      free(info);
+      // We free our LOB object. If it fails, it likely mean it isn't a LOB
+      // so we just free the value instead.
+      if (OCIDescriptorFree((dvoid *)info->value, (ub4)OCI_DTYPE_LOB) != OCI_SUCCESS)
+       {
+         free(info->value);
+       }
     }
 
   OCIHandleFree(_current_stm, OCI_HTYPE_STMT);
@@ -89,6 +85,11 @@ typedef struct
       _oci_env = (OCIEnv *)0;
       _oci_err = (OCIError *)0;
       _current_stm = (OCIStmt *)0;
+
+      // This will also initialize ivars in EOAdaptorChannel for
+      // delegate calls so it's important to call -setDelegate:
+      [self setDelegate: [[OracleAdaptorChannelController alloc] init]];
+      
       return self;
     }
 
@@ -111,7 +112,7 @@ typedef struct
   // Oracle's doc says: "If you call OCIStmtFetch2() with the nrows parameter set to 0, this cancels the cursor."
   if (OCIStmtFetch2(_current_stm, _oci_err, (ub4)0, (ub4)OCI_FETCH_NEXT, (sb4)0, (ub4)OCI_DEFAULT))
     {
-      [self logWithFormat: @"Fetch cancellation failed"];
+      NSLog( @"Fetch cancellation failed");
     }
   
   [self _cleanup];
@@ -130,7 +131,7 @@ typedef struct
       // We logoff from the database.
       if (OCILogoff(_oci_ctx, _oci_err))
        {
-         [self logWithFormat: @"FAILED: OCILogoff()"];
+         NSLog( @"FAILED: OCILogoff()");
        }
     }
 }
@@ -150,6 +151,8 @@ typedef struct
   OCIHandleFree(_oci_err, OCI_HTYPE_ERROR);
   OCIHandleFree(_oci_env, OCI_HTYPE_ENV);
 
+  RELEASE(delegate);
+
   [super dealloc];
 }
 
@@ -210,18 +213,20 @@ typedef struct
     }
   
   sql = (text *)[theExpression UTF8String];
-
+  
   // We alloc our statement handle
-  if (OCIHandleAlloc((dvoid *)_oci_env, (dvoid **)&_current_stm, (ub4)OCI_HTYPE_STMT, (CONST size_t) 0, (dvoid **) 0))
+  if ((status = OCIHandleAlloc((dvoid *)_oci_env, (dvoid **)&_current_stm, (ub4)OCI_HTYPE_STMT, (CONST size_t) 0, (dvoid **) 0)))
     {
-      [self logWithFormat: @"Can't allocate statement."];
+      checkerr(_oci_err, status);
+      NSLog(@"Can't allocate statement.");
       return NO;
     }
 
   // We prepare our statement
-  if (OCIStmtPrepare(_current_stm, _oci_err, sql, strlen((const char *)sql), OCI_NTV_SYNTAX, OCI_DEFAULT))
+  if ((status = OCIStmtPrepare(_current_stm, _oci_err, sql, strlen((const char *)sql), OCI_NTV_SYNTAX, OCI_DEFAULT)))
     {
-      [self logWithFormat: @"Prepare failed: OCI_ERROR"];
+      checkerr(_oci_err, status);
+      NSLog(@"Prepare failed: OCI_ERROR");
       return NO;
     }
 
@@ -230,17 +235,19 @@ typedef struct
   self->isFetchInProgress = (type == OCI_STMT_SELECT ? YES : NO);
   
   // We execute our statement. Not that we _MUST_ set iter to 0 for non-SELECT statements.
-  if (OCIStmtExecute(_oci_ctx, _current_stm, _oci_err, (self->isFetchInProgress ? (ub4)0 : (ub4)1), (ub4)0, (CONST OCISnapshot *)NULL, (OCISnapshot *)NULL,
-                    ([(OracleAdaptorContext *)[self adaptorContext] autoCommit] ? OCI_COMMIT_ON_SUCCESS :OCI_DEFAULT)))
+  if ((status = OCIStmtExecute(_oci_ctx, _current_stm, _oci_err, (self->isFetchInProgress ? (ub4)0 : (ub4)1), (ub4)0, (CONST OCISnapshot *)NULL, (OCISnapshot *)NULL,
+                              ([(OracleAdaptorContext *)[self adaptorContext] autoCommit] ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT))))
     {
-      [self logWithFormat: @"Statement execute failed (OCI_ERROR): %@", theExpression];
+      checkerr(_oci_err, status);
+      NSLog(@"Statement execute failed (OCI_ERROR): %@", theExpression);
       return NO;
     }
 
   // We check the number of params (ie., columns)
-  if (OCIAttrGet((dvoid *)_current_stm, OCI_HTYPE_STMT, (dvoid *)&count, (ub4 *)0, OCI_ATTR_PARAM_COUNT, _oci_err))
+  if ((status = OCIAttrGet((dvoid *)_current_stm, OCI_HTYPE_STMT, (dvoid *)&count, (ub4 *)0, OCI_ATTR_PARAM_COUNT, _oci_err)))
     {
-      [self logWithFormat: @"Attribute get failed (OCI_ERROR): %@", theExpression];
+      checkerr(_oci_err, status);
+      NSLog(@"Attribute get failed (OCI_ERROR): %@", theExpression);
       return NO;
     }
 
@@ -280,9 +287,19 @@ typedef struct
       //
       switch (info->type)
        {
-       case SQLT_CLOB:
-         type = SQLT_CHR;
-         break;
+         case SQLT_CLOB:
+           //type = SQLT_CHR;
+           type = SQLT_CLOB;
+           free(info->value);
+           if (OCIDescriptorAlloc((dvoid *)_oci_env, &(info->value), (ub4)OCI_DTYPE_LOB, (size_t)0, (dvoid **)0) != OCI_SUCCESS) 
+             {
+               NSLog(@"Unable to alloc descriptor");
+               abort();
+             }
+           // "For descriptors, locators, or REFs, whose size is unknown to client applications, use the size of the structure you
+           // are passing in: for example, sizeof (OCILobLocator *).
+           info->max_width = sizeof(OCILobLocator *);
+           break;
 
        case SQLT_NUM:
          type = SQLT_INT;
@@ -299,7 +316,8 @@ typedef struct
       //
       info->def = (OCIDefine*)0;
 
-      if ((status = OCIDefineByPos(_current_stm, &(info->def), _oci_err, i, (dvoid *)info->value, info->max_width, type,
+#warning cleanup
+      if ((status = OCIDefineByPos(_current_stm, &(info->def), _oci_err, i, (info->type == SQLT_CLOB ? &info->value : (dvoid *)info->value), info->max_width, type,
                                   (dvoid *)0, (ub2 *)&(info->width), (ub2 *)0, OCI_DEFAULT)))
        {
          NSLog(@"OCIDefineByPos FAILED");
@@ -351,19 +369,19 @@ typedef struct
                     (dvoid * (*)(dvoid *, dvoid *, size_t))0,
                     (void (*)(dvoid *, dvoid *)) 0 ))
     {
-      [self logWithFormat: @"FAILED: OCIInitialize()"];
+      NSLog( @"FAILED: OCIInitialize()");
       return NO;
     }
   
   if (OCIEnvInit((OCIEnv **)&_oci_env, (ub4)OCI_DEFAULT, (size_t)0, (dvoid **)0))
     {
-      [self logWithFormat: @"FAILED: OCIEnvInit()"];
+      NSLog( @"FAILED: OCIEnvInit()");
       return NO;
     }
   
   if (OCIHandleAlloc((dvoid *)_oci_env, (dvoid *)&_oci_err, (ub4)OCI_HTYPE_ERROR, (size_t)0, (dvoid **)0))
     {
-      [self logWithFormat: @"FAILED: OCIHandleAlloc() on errhp"];
+      NSLog( @"FAILED: OCIHandleAlloc() on errhp");
       return NO;
     }
   
@@ -380,8 +398,8 @@ typedef struct
   if (OCILogon(_oci_env, _oci_err, &_oci_ctx, (const OraText*)username, strlen(username),
               (const OraText*)password, strlen(password), (const OraText*)database, strlen(database)))
     {
-      [self logWithFormat: @"FAILED: OCILogon(). username = %s  password = %s"
-           @"  database = %s", username, password, database];
+      NSLog( @"FAILED: OCILogon(). username = %s  password = %s"
+           @"  database = %s", username, password, database);
       return NO;
     }
   
@@ -399,10 +417,9 @@ typedef struct
   sword status;
   
   status = OCIStmtFetch2(_current_stm, _oci_err, (ub4)1, (ub4)OCI_FETCH_NEXT, (sb4)0, (ub4)OCI_DEFAULT);
-  
+
   if (status == OCI_NO_DATA)
     {
-      //NSLog(@"No DATA!");
       self->isFetchInProgress = NO;
     }
   else
@@ -425,7 +442,7 @@ typedef struct
          o = nil;
       
          // On Oracle, if we've read a lenght of 0, it means that we got a NULL.
-         if (info->width == 0)
+         if (info->type != SQLT_CLOB && info->width == 0)
            {
              o = [NSNull null];
            }
@@ -434,11 +451,34 @@ typedef struct
              switch (info->type)
                {
                case SQLT_CHR:
-               case SQLT_CLOB:
                case SQLT_STR:
                  o = AUTORELEASE([[NSString alloc] initWithBytes: info->value  length: info->width  encoding: NSUTF8StringEncoding]);
                  break;
-                 
+
+               case SQLT_CLOB:
+                 {
+                   ub4 len;
+                   
+                   status = OCILobGetLength(_oci_ctx, _oci_err, info->value, &len);
+                   
+                   // We might get a OCI_INVALID_HANDLE if we try to read a NULL CLOB.
+                   // This would be avoided if folks using CLOB would use Oracle's empty_clob()
+                   // function but instead of relying on this, we check for invalid handles.
+                   if (status != OCI_INVALID_HANDLE && status != OCI_SUCCESS)
+                     {
+                       checkerr(_oci_err, status);
+                       o = [NSString string];
+                     }
+                   else
+                     {
+                       // We alloc twice the size of the LOB length. OCILobGetLength() returns us the LOB length in UTF-16 characters.
+                       o = calloc(len*2, 1);
+                       OCILobRead(_oci_ctx, _oci_err, info->value, &len, 1, o, len*2, (dvoid *)0, (sb4 (*)(dvoid *, CONST dvoid *, ub4, ub1))0, (ub2)0, (ub1)SQLCS_IMPLICIT);
+                       o = AUTORELEASE([[NSString alloc] initWithBytesNoCopy: o  length: len  encoding: NSUTF8StringEncoding  freeWhenDone: YES]);
+                     }
+                 }
+                 break;
                case SQLT_DAT:
                  {
                    signed char *buf;
@@ -529,6 +569,14 @@ typedef struct
 }
 #endif
 
+//
+//
+//
+- (OCIEnv *) environment
+{
+  return _oci_env;
+}
+
 //
 //
 //
@@ -544,7 +592,7 @@ static NSString *sqlFolderFormat = (@"CREATE TABLE %@ (\n"  \
                                    @"  c_creationdate INTEGER NOT NULL,\n"
                                    @"  c_lastmodified INTEGER NOT NULL,\n"
                                    @"  c_version INTEGER NOT NULL,\n"
-                                   @"  c_deleted INTEGER NOT NULL\n"
+                                   @"  c_deleted INTEGER DEFAULT 0 NOT NULL\n"
                                    @")");
 static NSString *sqlFolderACLFormat = (@"CREATE TABLE %@ (\n"  \
                                       @"  c_uid VARCHAR (256) NOT NULL,\n"
diff --git a/sope-gdl1/Oracle8/OracleAdaptorChannelController.h b/sope-gdl1/Oracle8/OracleAdaptorChannelController.h
new file mode 100644 (file)
index 0000000..842d6fe
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+**  OracleAdaptorChannelController.h
+**
+**  Copyright (c) 2007  Inverse groupe conseil inc. and Ludovic Marcotte
+**
+**  Author: Ludovic Marcotte <ludovic@inverse.ca>
+**
+**  This library is free software; you can redistribute it and/or
+**  modify it under the terms of the GNU Lesser General Public
+**  License as published by the Free Software Foundation; either
+**  version 2.1 of the License, or (at your option) any later version.
+**
+**  This library is distributed in the hope that it will be useful,
+**  but WITHOUT ANY WARRANTY; without even the implied warranty of
+**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+**  Lesser General Public License for more details.
+**
+**  You should have received a copy of the GNU Lesser General Public
+**  License along with this library; if not, write to the Free Software
+**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef _OracleAdaptorChannelController_H
+#define _OracleAdaptorChannelController_H
+
+#import <GDLAccess/EODelegateResponse.h>
+#import <GDLAccess/EOEntity.h>
+
+@interface OracleAdaptorChannelController : NSObject
+
+- (EODelegateResponse) adaptorChannel: (id) theChannel
+                        willInsertRow: (NSMutableDictionary *) theRow
+                            forEntity: (EOEntity *) theEntity;
+
+- (EODelegateResponse) adaptorChannel: (id) theChannel
+                        willUpdateRow: (NSMutableDictionary *) theRow
+                 describedByQualifier: (EOSQLQualifier *) theQualifier;
+
+@end
+
+#endif
diff --git a/sope-gdl1/Oracle8/OracleAdaptorChannelController.m b/sope-gdl1/Oracle8/OracleAdaptorChannelController.m
new file mode 100644 (file)
index 0000000..7f0ec89
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+**  OracleAdaptorChannelController.m
+**
+**  Copyright (c) 2007  Inverse groupe conseil inc. and Ludovic Marcotte
+**
+**  Author: Ludovic Marcotte <ludovic@inverse.ca>
+**
+**  This library is free software; you can redistribute it and/or
+**  modify it under the terms of the GNU Lesser General Public
+**  License as published by the Free Software Foundation; either
+**  version 2.1 of the License, or (at your option) any later version.
+**
+**  This library is distributed in the hope that it will be useful,
+**  but WITHOUT ANY WARRANTY; without even the implied warranty of
+**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+**  Lesser General Public License for more details.
+**
+**  You should have received a copy of the GNU Lesser General Public
+**  License along with this library; if not, write to the Free Software
+**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#import "OracleAdaptorChannelController.h"
+
+#include <oci.h>
+
+#include "err.h"
+#include "OracleAdaptorChannel.h"
+#include "OracleAdaptorContext.h"
+
+#import <Foundation/Foundation.h>
+#import <GDLAccess/EOSQLExpression.h>
+
+//
+//
+//
+@interface OracleAdaptorChannelController (Private)
+
+- (BOOL) _evaluateExpression: (NSString *) theExpression
+                       keys: (NSArray *) theKeys
+                     values: (NSMutableDictionary *) theValues
+                    channel: (id) theChannel;
+
+@end
+
+//
+//
+//
+@implementation OracleAdaptorChannelController
+
+- (EODelegateResponse) adaptorChannel: (id) theChannel
+                        willInsertRow: (NSMutableDictionary *) theRow
+                            forEntity: (EOEntity *) theEntity
+{
+  NSMutableString *s;
+  NSArray *keys;
+  int i, c;
+
+  NSLog(@"willInsertRow: %@ %@", [theRow description], [theEntity description]);
+
+  s = AUTORELEASE([[NSMutableString alloc] init]);
+
+  [s appendFormat: @"INSERT INTO %@ (", [theEntity externalName]];
+  keys = [theRow allKeys];
+  c = [keys count];
+
+  for (i = 0; i < c; i++)
+    {
+      [s appendString: [keys objectAtIndex: i]];
+      
+      if (i < c-1) [s appendString: @", "];
+    }
+
+  [s appendString: @") VALUES ("];
+
+  for (i = 0; i < c; i++)
+    {
+      [s appendFormat: @":%d", i+1];
+      
+      if (i < c-1) [s appendString: @", "];
+    }
+
+  [s appendString: @")"];
+
+  if ([self _evaluateExpression: s  keys: keys  values: theRow  channel: theChannel])
+    {
+      return EODelegateOverrides;
+    }
+
+  return EODelegateRejects;
+}
+
+//
+//
+//
+- (EODelegateResponse) adaptorChannel: (id) theChannel
+                        willUpdateRow: (NSMutableDictionary *) theRow
+                 describedByQualifier: (EOSQLQualifier *) theQualifier
+{
+  NSMutableString *s;
+  NSArray *keys;
+  int i, c;
+
+  NSLog(@"willUpdatetRow: %@ %@", [theRow description], [theQualifier description]);
+
+  s = AUTORELEASE([[NSMutableString alloc] init]);
+
+  [s appendFormat: @"UPDATE %@ SET ", [[theQualifier entity] externalName]];
+  keys = [theRow allKeys];
+  c = [keys count];
+
+  for (i = 0; i < c; i++)
+    {
+      [s appendFormat: @"%@ = :%d", [keys objectAtIndex: i], i+1];
+      
+      if (i < c-1) [s appendString: @", "];
+    }
+  
+  [s appendFormat: @" WHERE %@", [theQualifier expressionValueForContext: AUTORELEASE([[EOSQLExpression alloc] initWithEntity: [theQualifier entity]])]];;
+
+  if ([self _evaluateExpression: s  keys: keys  values: theRow  channel: theChannel])
+    {
+      return EODelegateOverrides;
+    }
+
+  return EODelegateRejects;
+}
+
+@end
+
+//
+//
+//
+@implementation  OracleAdaptorChannelController (Private)
+
+- (void) _cleanup: (NSArray *) theColumns
+       statement: (OCIStmt *) theStatement
+         channel: (id) theChannel
+{
+  column_info *info;
+  int c;
+
+  c = [theColumns count];
+
+  while (c--)
+    {
+      info = [[theColumns objectAtIndex: c] pointerValue];
+
+      if (info->type == SQLT_INT)
+       {
+         free(info->value);
+       }
+      else if (info->type == SQLT_CLOB)
+       {
+         OCILobFreeTemporary([theChannel serviceContext], [theChannel errorHandle], info->value);
+         OCIDescriptorFree((dvoid *)info->value, (ub4)OCI_DTYPE_LOB);
+       }
+    }
+
+  OCIHandleFree(theStatement, OCI_HTYPE_STMT);
+}
+
+//
+//
+//
+- (BOOL) _evaluateExpression: (NSString *) theExpression
+                       keys: (NSArray *) theKeys
+                     values: (NSMutableDictionary *) theValues
+                    channel: (id) theChannel
+{
+  NSMutableArray *columns;;
+
+  OCIStmt* current_stm;
+  OCIBind* bind;
+
+  column_info *info;
+  sword status;
+  text *sql;
+  int i,c;
+  id v;
+
+  sql = (text *)[theExpression UTF8String];
+
+  // We alloc our statement handle
+  if (OCIHandleAlloc((dvoid *)[theChannel environment], (dvoid **)&current_stm, (ub4)OCI_HTYPE_STMT, (CONST size_t) 0, (dvoid **) 0))
+    {
+      NSLog(@"Can't allocate statement.");
+      return NO;
+    }
+  
+  // We prepare the statement
+  if (OCIStmtPrepare(current_stm, [theChannel errorHandle], sql, (ub4)strlen((char *)sql), (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT))
+    {
+      NSLog(@"FAILED: OCIStmtPrepare() insert\n");
+      return NO;
+  }
+
+  columns = [[NSMutableArray alloc] init];
+  c = [theKeys count];
+
+  // We bind all input variables
+  for (i = 0; i < c; i++)
+    {
+      info = (void *)malloc(sizeof(column_info));
+      [columns addObject: [NSValue valueWithPointer: info]];
+      
+      v = [theValues objectForKey: [theKeys objectAtIndex: i]];
+      bind = (OCIBind *)0;
+
+      if ([v isKindOfClass: [NSString class]])
+       {
+         const char *buf;
+         ub4 len;
+         
+         buf = [v UTF8String];
+         len = strlen(buf);
+         
+         if (len <= 4000)
+           {
+             info->type = SQLT_CHR;
+             info->value = (dvoid *)buf;
+             info->width = len;
+           }
+         else
+           {
+             info->type = SQLT_CLOB;
+             info->width = sizeof(OCILobLocator *);
+
+             if ((status = OCIDescriptorAlloc((dvoid *)[theChannel environment], &(info->value), (ub4)OCI_DTYPE_LOB, (size_t)0, (dvoid **)0)) != OCI_SUCCESS) 
+               {
+                 [self _cleanup: columns  statement: current_stm  channel: theChannel];
+                 checkerr([theChannel errorHandle], status);
+                 return NO;
+               }
+             
+             OCILobCreateTemporary([theChannel serviceContext], [theChannel errorHandle], info->value, OCI_DEFAULT, SQLCS_IMPLICIT,
+                                   OCI_TEMP_CLOB, FALSE, OCI_DURATION_SESSION);
+
+             OCILobWrite([theChannel serviceContext], [theChannel errorHandle], info->value, &len, 1, (dvoid *)buf, len, OCI_ONE_PIECE,
+                         (dvoid *)0, (sb4 (*)())0, (ub2)0, (ub1)SQLCS_IMPLICIT);
+           }
+       }
+      else
+       {
+         int x;
+
+         info->type = SQLT_INT;
+         x = [v intValue];
+         info->width = sizeof(x);
+         info->value = calloc(info->width, 1);
+         *(int *)info->value = x;
+       }
+      
+      if ((status = (OCIBindByPos(current_stm, &bind, [theChannel errorHandle], (ub4)i+1, (info->type == SQLT_CLOB ? &info->value : (dvoid *)info->value), 
+                                 (sb4)info->width, info->type, (dvoid *)0, (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, (ub4)OCI_DEFAULT))) != OCI_SUCCESS)
+       {
+         [self _cleanup: columns  statement: current_stm  channel: theChannel];
+         checkerr([theChannel errorHandle], status);
+         return NO;
+       }
+    }
+
+  // We execute the statement
+  if ((status = OCIStmtExecute([theChannel serviceContext], current_stm, [theChannel errorHandle], (ub4)1, (ub4)0, (CONST OCISnapshot*)0, (OCISnapshot*)0,
+                              ([(OracleAdaptorContext *)[theChannel adaptorContext] autoCommit] ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT))) != OCI_SUCCESS)
+    {
+      [self _cleanup: columns  statement: current_stm  channel: theChannel];
+      checkerr([theChannel errorHandle], status);
+      return NO;
+    }
+
+  [self _cleanup: columns  statement: current_stm  channel: theChannel];
+
+  return YES;
+}
+
+@end
index ba23d225f319fb02678b92ac2cfda8ad6059ff94..4fb92de6d5fb946e068659f4ce96241b67af0dba 100644 (file)
@@ -24,8 +24,11 @@ Installation and Configuration
 ==============================
 
 1- Based on where you've installed the Oracle dependancies, modify the
-   GNUmakefile so gnustep-make can fin the location of Oracle's headers
-   and client librairies.
+   GNUmakefile so gnustep-make can find the location of Oracle's headers
+   and client librairies. You would want to modify at least:
+
+   ORACLE_VERSION
+   -lnnz
 
 2- Compile and install the adaptor:
    % make
diff --git a/sope-gdl1/Oracle8/err.h b/sope-gdl1/Oracle8/err.h
new file mode 100644 (file)
index 0000000..a5925a2
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+**  err.h
+**
+**  Copyright (c) 2007  Inverse groupe conseil inc. and Ludovic Marcotte
+**
+**  Author: Ludovic Marcotte <ludovic@inverse.ca>
+**
+**  This program is free software; you can redistribute it and/or modify
+**  it under the terms of the GNU General Public License as published by
+**  the Free Software Foundation; either version 2 of the License, or
+**  (at your option) any later version.
+**
+**  This program is distributed in the hope that it will be useful,
+**  but WITHOUT ANY WARRANTY; without even the implied warranty of
+**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+**  GNU General Public License for more details.
+**
+**  You should have received a copy of the GNU General Public License
+**  along with this program; if not, write to the Free Software
+**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _err_H
+#define _err_H
+
+#include <oci.h>
+
+void checkerr(OCIError *errhp, sword status);
+
+#endif
diff --git a/sope-gdl1/Oracle8/err.m b/sope-gdl1/Oracle8/err.m
new file mode 100644 (file)
index 0000000..0527714
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+**  err.m
+**
+**  Copyright (c) 2007  Inverse groupe conseil inc. and Ludovic Marcotte
+**
+**  Author: Ludovic Marcotte <ludovic@inverse.ca>
+**
+**  This program is free software; you can redistribute it and/or modify
+**  it under the terms of the GNU General Public License as published by
+**  the Free Software Foundation; either version 2 of the License, or
+**  (at your option) any later version.
+**
+**  This program is distributed in the hope that it will be useful,
+**  but WITHOUT ANY WARRANTY; without even the implied warranty of
+**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+**  GNU General Public License for more details.
+**
+**  You should have received a copy of the GNU General Public License
+**  along with this program; if not, write to the Free Software
+**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "err.h"
+
+#include <Foundation/Foundation.h>
+
+void checkerr(OCIError *errhp, sword status)
+{
+  text errbuf[512];
+  sb4 errcode;
+
+  if (status == OCI_SUCCESS) return;
+
+  switch (status)
+    {
+    case OCI_SUCCESS_WITH_INFO:
+      NSLog(@"Error - OCI_SUCCESS_WITH_INFO\n");
+      OCIErrorGet ((dvoid *) errhp, (ub4) 1, (text *) NULL, &errcode,
+                  errbuf, (ub4) sizeof(errbuf), (ub4) OCI_HTYPE_ERROR);
+      NSLog(@"Error - %s\n", errbuf);
+      break;
+    case OCI_NEED_DATA:
+      NSLog(@"Error - OCI_NEED_DATA\n");
+      break;
+    case OCI_NO_DATA:
+      NSLog(@"Error - OCI_NO_DATA\n");
+      break;
+    case OCI_ERROR:
+      OCIErrorGet ((dvoid *) errhp, (ub4) 1, (text *) NULL, &errcode,
+                  errbuf, (ub4) sizeof(errbuf), (ub4) OCI_HTYPE_ERROR);
+      NSLog(@"Error - %s\n", errbuf);
+      break;
+    case OCI_INVALID_HANDLE:
+      NSLog(@"Error - OCI_INVALID_HANDLE\n");
+      break;
+    case OCI_STILL_EXECUTING:
+      NSLog(@"Error - OCI_STILL_EXECUTING\n");
+      break;
+    case OCI_CONTINUE:
+      NSLog(@"Error - OCI_CONTINUE\n");
+      break;
+    default:
+      NSLog(@"Error - %d\n", status);
+      break;
+    }
+}
index 4378c4da3d967f318a681eb487ff0c3f249576ee..645ce4c4e377afac63fd41e8d2c5de7330995659 100644 (file)
@@ -29,7 +29,7 @@
 void evaluate(EOAdaptorChannel *c, NSString *s)
 {
   NSLog(@"Evaluating:\t%@",s);
-  if ([c evaluateExpression: s])
+  if ([c evaluateExpression: s] && [c isFetchInProgress])
     {
       NSDictionary *record;
       NSArray *attributes;
@@ -44,21 +44,56 @@ void evaluate(EOAdaptorChannel *c, NSString *s)
     }
 }
 
+//
+//
+//
+void insert(EOAdaptorChannel *channel, EOEntity *entity, NSMutableDictionary *row)
+{
+  [row setObject: @"foo"  forKey: @"c_name"];
+  [row setObject: @"barrr"  forKey: @"c_content"];
+  [row setObject: [NSNumber numberWithInt: 1]  forKey: @"c_creationdate"];
+  [row setObject: [NSNumber numberWithInt: 2]  forKey: @"c_lastmodified"];
+  [row setObject: [NSNumber numberWithInt: 0]  forKey: @"c_version"];
+
+  [channel insertRow: row  forEntity: entity];
+}
+
+//
+//
+//
+void update(EOAdaptorChannel *channel, EOEntity *entity, NSMutableDictionary *row)
+{
+  EOSQLQualifier *qualifier;
+
+  qualifier = [[EOSQLQualifier alloc] initWithEntity: entity
+                                     qualifierFormat: @"%A = 'foo'", @"c_name"];
+                                    
+  [row setObject: @"bazzzzzzzzzzz"  forKey: @"c_content"];
+  [row setObject: [NSNumber numberWithInt: 2]  forKey: @"c_creationdate"];
+  [row setObject: [NSNumber numberWithInt: 3]  forKey: @"c_lastmodified"];
+  [row setObject: [NSNumber numberWithInt: 1]  forKey: @"c_version"];
+  [channel updateRow: row  describedByQualifier: qualifier];
+}
+
 //
 //
 //
 int main (int argc, char **argv, char **env)
 {
   NSAutoreleasePool *pool;
-  
+
   EOAdaptorChannel *channel;
+  NSMutableDictionary *row;
   EOAdaptorContext *ctx;
   EOAdaptor *adaptor;
+  EOEntity *entity;
+  EOAttribute *attribute;
 
   pool = [[NSAutoreleasePool alloc] init];
   [NSProcessInfo initializeWithArguments: argv  count: argc  environment: env];
 
-  adaptor = [EOAdaptor adaptorWithName: @"Oracle"];
+  adaptor = [EOAdaptor adaptorWithName: @"Oracle8"];
   [adaptor setConnectionDictionary: [NSDictionary dictionaryWithContentsOfFile: @"condict.plist"]];
   ctx = [adaptor createAdaptorContext];
   channel = [ctx createAdaptorChannel];
@@ -71,6 +106,30 @@ int main (int argc, char **argv, char **env)
   evaluate(channel, @"SELECT 1 FROM dual");
   evaluate(channel, @"SELECT sysdate FROM dual");
   
+  evaluate(channel, @"DROP table otest_demo");
+  evaluate(channel, @"CREATE TABLE otest_demo (\nc_name VARCHAR2 (256) NOT NULL,\n c_content CLOB NOT NULL,\n c_creationdate INTEGER NOT NULL,\n c_lastmodified INTEGER NOT NULL,\n c_version INTEGER NOT NULL,\n c_deleted INTEGER  DEFAULT 0 NOT NULL\n)");
+  
+  evaluate(channel, @"DELETE FROM otest_demo where c_name = 'foo'");
+
+  entity = [[EOEntity alloc] init];
+  [entity setName: @"otest_demo"];
+  [entity setExternalName: @"otest_demo"]; // table name
+
+  attribute = AUTORELEASE([[EOAttribute alloc] init]);
+  [attribute setName: @"c_name"];
+  [attribute setColumnName: @"c_name"];
+  [entity addAttribute: attribute];
+
+  row = [[NSMutableDictionary alloc] init];
+
+  insert(channel, entity, row);
+  evaluate(channel, @"SELECT * FROM otest_demo where c_name = 'foo'");
+  update(channel, entity, row);
+  evaluate(channel, @"SELECT * FROM otest_demo where c_name = 'foo'");
+
+  RELEASE(entity);
+  RELEASE(row);
+
   [ctx commitTransaction];
   [channel closeChannel];
   [pool release];