From b26b0155b871485498fb79c125c18d2f1f1bd498 Mon Sep 17 00:00:00 2001 From: helge Date: Fri, 5 Oct 2007 21:02:31 +0000 Subject: [PATCH] committed ludovics ora patches git-svn-id: http://svn.opengroupware.org/SOPE/trunk@1544 e4a50df8-12e2-0310-a44c-efbce7f8a7e3 --- sope-gdl1/Oracle8/ChangeLog | 10 +- sope-gdl1/Oracle8/GNUmakefile | 19 +- sope-gdl1/Oracle8/OracleAdaptorChannel.h | 16 +- sope-gdl1/Oracle8/OracleAdaptorChannel.m | 132 ++++++--- .../Oracle8/OracleAdaptorChannelController.h | 41 +++ .../Oracle8/OracleAdaptorChannelController.m | 277 ++++++++++++++++++ sope-gdl1/Oracle8/README | 7 +- sope-gdl1/Oracle8/err.h | 30 ++ sope-gdl1/Oracle8/err.m | 66 +++++ sope-gdl1/Oracle8/otest.m | 65 +++- 10 files changed, 604 insertions(+), 59 deletions(-) create mode 100644 sope-gdl1/Oracle8/OracleAdaptorChannelController.h create mode 100644 sope-gdl1/Oracle8/OracleAdaptorChannelController.m create mode 100644 sope-gdl1/Oracle8/err.h create mode 100644 sope-gdl1/Oracle8/err.m diff --git a/sope-gdl1/Oracle8/ChangeLog b/sope-gdl1/Oracle8/ChangeLog index 7d265091..7a95da96 100644 --- a/sope-gdl1/Oracle8/ChangeLog +++ b/sope-gdl1/Oracle8/ChangeLog @@ -1,6 +1,14 @@ +2007-10-05 Ludovic Marcotte + + * 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 - * Small for for 64-bit platforms. + * Small fix for 64-bit platforms. 2007-08-29 Helge Hess diff --git a/sope-gdl1/Oracle8/GNUmakefile b/sope-gdl1/Oracle8/GNUmakefile index 09b6a526..496d732e 100644 --- a/sope-gdl1/Oracle8/GNUmakefile +++ b/sope-gdl1/Oracle8/GNUmakefile @@ -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 diff --git a/sope-gdl1/Oracle8/OracleAdaptorChannel.h b/sope-gdl1/Oracle8/OracleAdaptorChannel.h index 7c6a4f02..fd20d5e4 100644 --- a/sope-gdl1/Oracle8/OracleAdaptorChannel.h +++ b/sope-gdl1/Oracle8/OracleAdaptorChannel.h @@ -27,11 +27,20 @@ #import -#import "../GDLContentStore/EOAdaptorChannel+GCS.h" +//#import "../GDLContentStore/EOAdaptorChannel+GCS.h" @class NSMutableArray; -@interface OracleAdaptorChannel : EOAdaptorChannel +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 @@ -40,11 +49,12 @@ OCIError* _oci_err; OCIStmt* _current_stm; - // ... + // ... NSMutableArray *_resultSetProperties; NSMutableArray *_row_buffer; } +- (OCIEnv *) environment; - (OCISvcCtx *) serviceContext; - (OCIError *) errorHandle; diff --git a/sope-gdl1/Oracle8/OracleAdaptorChannel.m b/sope-gdl1/Oracle8/OracleAdaptorChannel.m index 7dc70289..9dd236bc 100644 --- a/sope-gdl1/Oracle8/OracleAdaptorChannel.m +++ b/sope-gdl1/Oracle8/OracleAdaptorChannel.m @@ -22,22 +22,14 @@ #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 -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 index 00000000..842d6fe4 --- /dev/null +++ b/sope-gdl1/Oracle8/OracleAdaptorChannelController.h @@ -0,0 +1,41 @@ +/* +** OracleAdaptorChannelController.h +** +** Copyright (c) 2007 Inverse groupe conseil inc. and Ludovic Marcotte +** +** Author: Ludovic Marcotte +** +** 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 +#import + +@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 index 00000000..7f0ec89f --- /dev/null +++ b/sope-gdl1/Oracle8/OracleAdaptorChannelController.m @@ -0,0 +1,277 @@ +/* +** OracleAdaptorChannelController.m +** +** Copyright (c) 2007 Inverse groupe conseil inc. and Ludovic Marcotte +** +** Author: Ludovic Marcotte +** +** 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 + +#include "err.h" +#include "OracleAdaptorChannel.h" +#include "OracleAdaptorContext.h" + +#import +#import + +// +// +// +@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 **)¤t_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 diff --git a/sope-gdl1/Oracle8/README b/sope-gdl1/Oracle8/README index ba23d225..4fb92de6 100644 --- a/sope-gdl1/Oracle8/README +++ b/sope-gdl1/Oracle8/README @@ -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 index 00000000..a5925a2c --- /dev/null +++ b/sope-gdl1/Oracle8/err.h @@ -0,0 +1,30 @@ +/* +** err.h +** +** Copyright (c) 2007 Inverse groupe conseil inc. and Ludovic Marcotte +** +** Author: Ludovic Marcotte +** +** 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 + +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 index 00000000..0527714b --- /dev/null +++ b/sope-gdl1/Oracle8/err.m @@ -0,0 +1,66 @@ +/* +** err.m +** +** Copyright (c) 2007 Inverse groupe conseil inc. and Ludovic Marcotte +** +** Author: Ludovic Marcotte +** +** 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 + +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; + } +} diff --git a/sope-gdl1/Oracle8/otest.m b/sope-gdl1/Oracle8/otest.m index 4378c4da..645ce4c4 100644 --- a/sope-gdl1/Oracle8/otest.m +++ b/sope-gdl1/Oracle8/otest.m @@ -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]; -- 2.39.5