+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>
# 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
EOAttribute+Oracle.m \
OracleAdaptor.m \
OracleAdaptorChannel.m \
+ OracleAdaptorChannelController.m \
OracleAdaptorContext.m \
OracleSQLExpression.m \
- OracleValues.m
+ OracleValues.m \
+ err.m
Oracle8_PRINCIPAL_CLASS = OracleAdaptor
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
#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;
#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;
-
-
//
//
//
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);
_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;
}
// 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];
// We logoff from the database.
if (OCILogoff(_oci_ctx, _oci_err))
{
- [self logWithFormat: @"FAILED: OCILogoff()"];
+ NSLog( @"FAILED: OCILogoff()");
}
}
}
OCIHandleFree(_oci_err, OCI_HTYPE_ERROR);
OCIHandleFree(_oci_env, OCI_HTYPE_ENV);
+ RELEASE(delegate);
+
[super dealloc];
}
}
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;
}
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;
}
//
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;
//
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");
(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;
}
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;
}
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
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];
}
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;
}
#endif
+//
+//
+//
+- (OCIEnv *) environment
+{
+ return _oci_env;
+}
+
//
//
//
@" 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"
--- /dev/null
+/*
+** 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
--- /dev/null
+/*
+** 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 **)¤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
==============================
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
--- /dev/null
+/*
+** 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
--- /dev/null
+/*
+** 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;
+ }
+}
void evaluate(EOAdaptorChannel *c, NSString *s)
{
NSLog(@"Evaluating:\t%@",s);
- if ([c evaluateExpression: s])
+ if ([c evaluateExpression: s] && [c isFetchInProgress])
{
NSDictionary *record;
NSArray *attributes;
}
}
+//
+//
+//
+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];
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];