|
|
1.1 parser 1: /** @file
2: Parser Oracle driver.
3:
4: Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)
5:
6: Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf)
7:
8: 2001.07.30 using Oracle 8.1.6 [@test tested with Oracle 7.x.x]
9: */
1.4 ! paf 10: static const char *RCSId="$Id: parser3oracle.C,v 1.3 2001/11/10 13:35:52 paf Exp $";
1.1 parser 11:
12: #include "config_includes.h"
13:
14: #include "pa_sql_driver.h"
15:
16: #include <oci.h>
17:
18: #define MAX_COLS 500
19: #define MAX_IN_LOBS 5
20: #define MAX_LOB_NAME_LENGTH 100
21: #define MAX_OUT_STRING_LENGTH 4000
22:
23: #define EMPTY_CLOB_FUNC_CALL "empty_clob()"
24:
25: #include "ltdl.h"
26:
27: #define MAX_STRING 0x400
28: #define MAX_NUMBER 20
29:
30: #if _MSC_VER
31: # define snprintf _snprintf
32: # define strcasecmp _stricmp
33: # define strncasecmp _strnicmp
34: #endif
35:
36: #ifndef max
37: inline int max(int a, int b) { return a>b?a:b; }
38: inline int min(int a, int b){ return a<b?a:b; }
39: #endif
40:
1.4 ! paf 41:
! 42: static int pa_setenv(const char *name, const char *value) {
! 43: #ifdef HAVE_PUTENV
! 44: // MEM_LEAK_HERE. refer to EOF man putenv
! 45: char *buf=(char *)::malloc(strlen(name)+1+strlen(value)+1);
! 46: strcpy(buf, name);
! 47: strcat(buf, "=");
! 48: strcat(buf, value);
! 49: return putenv(buf);
! 50: #else
! 51: //#ifdef HAVE_SETENV
! 52: if(value)
! 53: return setenv(name, value, 1/*overwrite*/);
! 54: else {
! 55: unsetenv(name);
! 56: return 0;
! 57: }
! 58: #endif
! 59: }
! 60:
1.1 parser 61: static char *lsplit(char *string, char delim) {
62: if(string) {
63: char *v=strchr(string, delim);
64: if(v) {
65: *v=0;
66: return v+1;
67: }
68: }
69: return 0;
70: }
71:
1.4 ! paf 72: static char *lsplit(char **string_ref, char delim) {
! 73: char *result=*string_ref;
! 74: char *next=lsplit(*string_ref, delim);
! 75: *string_ref=next;
! 76: return result;
! 77: }
! 78:
1.1 parser 79: #ifndef DOXYGEN
80: struct OracleSQL_connection_struct {
81: jmp_buf mark; char error[MAX_STRING];
82: OCIEnv *envhp;
83: OCIServer *srvhp;
84: OCIError *errhp;
85: OCISvcCtx *svchp;
86: OCISession *usrhp;
87: };
88:
89: struct OracleSQL_query_lobs {
90: struct return_rows {
91: struct return_row {
92: OCILobLocator *locator; ub4 len;
93: int ind;
94: ub2 rcode;
95: } *row;
96: int count;
97: };
98: struct cbf_context_struct {
99: SQL_Driver_services *services;
100: OracleSQL_connection_struct *cs;
101: return_rows *rows;
102: };
103: struct Item {
104: const char *name_ptr; size_t name_size;
105: char *data_ptr; size_t data_size;
106: OCILobLocator *locator;
107: OCIBind *bind;
108: return_rows rows;
109: } items[MAX_IN_LOBS];
110: int count;
111: };
112: #endif
113:
114: // forwards
115: void check(
116: SQL_Driver_services& services, OracleSQL_connection_struct &cs,
117: const char *step, sword status);
118: static sb4 cbf_no_data(
119: dvoid *ctxp,
120: OCIBind *bindp,
121: ub4 iter, ub4 index,
122: dvoid **bufpp,
123: ub4 *alenpp,
124: ub1 *piecep,
125: dvoid **indpp);
126: static sb4 cbf_get_data(dvoid *ctxp,
127: OCIBind *bindp,
128: ub4 iter, ub4 index,
129: dvoid **bufpp,
130: ub4 **alenp,
131: ub1 *piecep,
132: dvoid **indpp,
133: ub2 **rcodepp);
134: void tolower(char *out, const char *in, size_t size);
135:
136: /**
137: OracleSQL server driver
138: @test NLS_LANG=AMERICAN_AMERICA.CL8MSWIN1251
139: */
140: class OracleSQL_Driver : public SQL_Driver {
141: public:
142:
143: OracleSQL_Driver() : SQL_Driver() {
144: }
145:
146: /// get api version
147: int api_version() { return SQL_DRIVER_API_VERSION; }
148: /// initialize driver by loading sql dynamic link library
149: const char *initialize(const char *dlopen_file_spec) {
150:
151: const char *error=dlopen_file_spec?
152: dlink(dlopen_file_spec):"client library column is empty";
153: if(!error) {
154:
155: // error="OCIInitialize replacer";/*
1.3 paf 156: OCIInitialize((ub4)OCI_THREADED/*| OCI_OBJECT*/, (dvoid *)0,
1.1 parser 157: (dvoid * (*)(void *, unsigned int))0,
158: (dvoid * (*)(void*, void*, unsigned int))0,
159: (void (*)(void*, void*))0 );
160:
161: // */
162: }
163:
164: return error;
165: }
166:
167: /** connect
168: @param used_only_in_connect_url
1.4 ! paf 169: format: @b user:pass@service?
! 170: ORACLE_HOME=/u01/app/oracle/product/8.1.5&
! 171: NLS_LANG=RUSSIAN_AMERICA.CL8MSWIN1251&
! 172: ORA_ENCRYPT_LOGIN=TRUE
! 173:
! 174: @todo environment manupulation doesnt look thread safe
1.1 parser 175: */
176: void connect(
177: char *used_only_in_connect_url,
178: SQL_Driver_services& services,
179: void **connection ///< output: OracleSQL_connection_struct *
180: ) {
181: // connections are cross-request, do not use services._alloc [linked with request]
182: OracleSQL_connection_struct &cs=
183: *(OracleSQL_connection_struct *)::calloc(sizeof(OracleSQL_connection_struct), 1);
184:
185: char *user=used_only_in_connect_url;
186: char *service=lsplit(user, '@');
187: char *pwd=lsplit(user, ':');
1.4 ! paf 188: char *options=lsplit(service, '?');
1.1 parser 189:
190: if(!(user && pwd && service))
191: services._throw("mailformed connect part, must be 'user:pass@service'");
192:
1.4 ! paf 193: while(options) {
! 194: if(char *key=lsplit(&options, '&')) {
! 195: if(*key) {
! 196: if(char *value=lsplit(key, '=')) {
! 197: if(strncmp(key, "ORACLE_", 7)==0 // ORACLE_HOME & co
! 198: || strncmp(key, "ORA_", 4)==0 // ORA_ENCRYPT_LOGIN & co
! 199: || strncmp(key, "NLS_", 4)==0 // NLS_LANG & co
! 200: ) {
! 201: if(pa_setenv(key, value)!=0)
! 202: services._throw("problem changing process environment" /*key*/);
! 203: } else
! 204: services._throw("unknown connect option (option must start with ORACLE_, ORA_ or NLS_)" /*key*/);
! 205: } else
! 206: services._throw("connect option without =value" /*key*/);
! 207: }
! 208: }
! 209: }
! 210:
1.1 parser 211: if(setjmp(cs.mark))
212: services._throw(cs.error);
213:
214: // Allocate and initialize OCIError handle, attempt #1
215: /*
216: grabbed from sample
217: /server.804/a58234/oci_func.htm#446192
218: but doc
219: /server.804/a58234/oci_func.htm#446100
220: doesnt have this param listed as allowed
221: 8.1.6 client library barks as OCI_INVALID_HANDLE
222: and debugging revealed that OCI_HTYPE_ENV param value is invalid
223: later in doc
224: /server.804/a58234/oci_func.htm#446192
225: on OCIEnvInit thay say
226: "No changes are done to an already initialized handle"
227: think, this is some sort of backward compatibility wonder.
228: leaving as it is, and without check()
229: */
230: OCIHandleAlloc((dvoid *)NULL, (dvoid **) &cs.envhp, (ub4)OCI_HTYPE_ENV, 0, 0);
231: // Initialize an environment handle, attempt #2
232: check(services, cs, "EnvInit", OCIEnvInit(
233: &cs.envhp, (ub4)OCI_DEFAULT, 0, 0));
234: // Allocate and initialize OCIError handle
235: check(services, cs, "HandleAlloc errhp", OCIHandleAlloc(
236: (dvoid *)cs.envhp, (dvoid **) &cs.errhp, (ub4)OCI_HTYPE_ERROR, 0, 0));
237: // Allocate and initialize OCIServer handle
238: check(services, cs, "HandleAlloc srvhp", OCIHandleAlloc(
239: (dvoid *)cs.envhp, (dvoid **) &cs.srvhp, (ub4)OCI_HTYPE_SERVER, 0, 0));
240: // Attach to a 'service'; initialize server context handle
241: check(services, cs, "ServerAttach", OCIServerAttach(
242: cs.srvhp, cs.errhp, (text *)service, (sb4)strlen(service), (ub4)OCI_DEFAULT));
243: // Allocate and initialize OCISvcCtx handle
244: check(services, cs, "HandleAlloc svchp", OCIHandleAlloc(
245: (dvoid *)cs.envhp, (dvoid **) &cs.svchp, (ub4)OCI_HTYPE_SVCCTX, 0, 0));
246: // set attribute server context in the service context
247: check(services, cs, "AttrSet server-service", OCIAttrSet(
248: (dvoid *)cs.svchp, (ub4)OCI_HTYPE_SVCCTX,
249: (dvoid *)cs.srvhp, (ub4)0,
250: (ub4)OCI_ATTR_SERVER, (OCIError *)cs.errhp));
251: // allocate a user context handle
252: check(services, cs, "HandleAlloc usrhp", OCIHandleAlloc(
253: (dvoid *)cs.envhp, (dvoid **)&cs.usrhp, (ub4)OCI_HTYPE_SESSION, 0, 0));
254: // set 'user' name
255: check(services, cs, "AttrSet user-session", OCIAttrSet(
256: (dvoid *)cs.usrhp, (ub4)OCI_HTYPE_SESSION,
257: (dvoid *)user, (ub4)strlen(user),
258: OCI_ATTR_USERNAME, cs.errhp));
259: // set 'pwd' password
260: check(services, cs, "AttrSet pwd-session", OCIAttrSet(
261: (dvoid *)cs.usrhp, (ub4)OCI_HTYPE_SESSION,
262: (dvoid *)pwd, (ub4)strlen(pwd),
263: OCI_ATTR_PASSWORD, cs.errhp));
264: // Authenticate a user
265: check(services, cs, "SessionBegin", OCISessionBegin(
266: cs.svchp, cs.errhp, cs.usrhp,
267: OCI_CRED_RDBMS, OCI_DEFAULT));
268: // remember connection in session
269: check(services, cs, "AttrSet service-session", OCIAttrSet(
270: (dvoid *)cs.svchp, (ub4)OCI_HTYPE_SVCCTX,
271: (dvoid *)cs.usrhp, (ub4)0,
272: OCI_ATTR_SESSION, cs.errhp));
273:
274: // return created connection
275: *(OracleSQL_connection_struct **)connection=&cs;
276: }
1.2 paf 277: /// @test remove return
1.1 parser 278: void disconnect(void *connection) {
279:
280: return;
281:
282:
283: OracleSQL_connection_struct &cs=*(OracleSQL_connection_struct *)connection;
284: // Terminate a user session
285: OCISessionEnd(
286: cs.svchp, cs.errhp, cs.usrhp, (ub4)OCI_DEFAULT);
287: // Detach from a server; uninitialize server context handle
288: OCIServerDetach(
289: cs.srvhp, cs.errhp, (ub4)OCI_DEFAULT);
290: // Free a previously allocated handles
291: /*
292: oci will free them up as belonging to env
293: OCIHandleFree(
294: (dvoid *)cs.srvhp, (ub4)OCI_HTYPE_SERVER);
295: OCIHandleFree(
296: (dvoid *)cs.svchp, (ub4)OCI_HTYPE_SVCCTX);
297: OCIHandleFree(
298: (dvoid *)cs.errhp, (ub4)OCI_HTYPE_ERROR);
299: */
300: OCIHandleFree(
301: (dvoid *)cs.envhp, (ub4)OCI_HTYPE_ENV);
302:
303: // connections are cross-request, do not use services._alloc [linked with request]
304: ::free(&cs);
305: }
306: void commit(SQL_Driver_services& services, void *connection) {
307: OracleSQL_connection_struct &cs=*(OracleSQL_connection_struct *)connection;
308: if(setjmp(cs.mark))
309: services._throw(cs.error);
310:
311: check(services, cs, "commit", OCITransCommit(cs.svchp, cs.errhp, 0));
312: }
313: void rollback(SQL_Driver_services& services, void *connection) {
314: OracleSQL_connection_struct &cs=*(OracleSQL_connection_struct *)connection;
315: if(setjmp(cs.mark))
316: services._throw(cs.error);
317:
318: check(services, cs, "rollback", OCITransRollback(cs.svchp, cs.errhp, 0));
319: }
320:
321: bool ping(SQL_Driver_services&, void *connection) {
322: // maybe OCIServerVersion?
1.4 ! paf 323: // select 0 from dual
1.1 parser 324: return true;
325: }
326:
327: unsigned int quote(
328: SQL_Driver_services&, void *,
329: char *to, const char *from, unsigned int length) {
330: /*
331: it's already UNTAINT_TIMES_BIGGER
332: */
333: unsigned int result=length;
334: while(length--) {
335: switch(*from) {
336: case '\'': // "'" -> "''"
337: *to++='\'';
338: break;
339: case '\\': // "\" -> "\\"
340: *to++='\'';
341: break;
342: }
343: *to++=*from++;
344: }
345: return result;
346: }
347: void query(
348: SQL_Driver_services& services, void *connection,
349: const char *astatement, unsigned long offset, unsigned long limit,
350: SQL_Driver_query_event_handlers& handlers) {
351:
352: OracleSQL_connection_struct &cs=*(OracleSQL_connection_struct *)connection;
353: OracleSQL_query_lobs lobs={{0}, 0};
354: OCIStmt *stmthp=0;
355:
356: bool failed=false;
357: if(setjmp(cs.mark)) {
358: failed=true;
359: goto cleanup;
360: } else {
361: const char *statement=preprocess_statement(services, cs,
362: astatement, lobs);
363:
364: check(services, cs, "HandleAlloc STMT", OCIHandleAlloc(
365: (dvoid *)cs.envhp, (dvoid **) &stmthp, (ub4)OCI_HTYPE_STMT, 0, 0));
366: check(services, cs, "syntax",
367: OCIStmtPrepare(stmthp, cs.errhp, (unsigned char *)statement,
368: (ub4)strlen((char *)statement),
369: (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT));
370: {
371: for(int i=0; i<lobs.count; i++) {
372: check(services, cs, "alloc output var desc", OCIDescriptorAlloc(
373: (dvoid *)cs.envhp, (dvoid **)&lobs.items[i].locator, (ub4)OCI_DTYPE_LOB, 0, 0));
374:
375: check(services, cs, "bind output", OCIBindByPos(stmthp,
376: &lobs.items[i].bind, cs.errhp,
377: (ub4)1+i,
378: (dvoid *)&lobs.items[i].locator,
379: (sword)sizeof (lobs.items[i].locator), SQLT_CLOB, (dvoid *)0,
380: (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, OCI_DATA_AT_EXEC));
381:
382: lobs.items[i].rows.count=0;
383: OracleSQL_query_lobs::cbf_context_struct cbf_context={
384: &services, &cs, &lobs.items[i].rows};
385: check(services, cs, "bind dynamic", OCIBindDynamic(
386: lobs.items[i].bind, cs.errhp,
387: (dvoid *) &cbf_context, cbf_no_data,
388: (dvoid *) &cbf_context, cbf_get_data));
389: }
390: }
391:
392: execute_prepared(services, cs,
393: statement, stmthp, lobs,
394: offset, limit, handlers);
395: }
396: cleanup: // no check call after this point!
397: {
398: for(int i=0; i<lobs.count; i++) {
399: /* free var locator */
400: if(OCILobLocator *locator=lobs.items[i].locator)
401: OCIDescriptorFree((dvoid *)locator, (ub4)OCI_DTYPE_LOB);
402:
403: /* free rows descriptors */
404: OracleSQL_query_lobs::return_rows &rows=lobs.items[i].rows;
405: for(int r=0; r<rows.count; r++)
406: OCIDescriptorFree((dvoid *)rows.row[r].locator, (ub4)OCI_DTYPE_LOB);
407: }
408: }
409: if(stmthp)
410: OCIHandleFree((dvoid *)stmthp, (ub4)OCI_HTYPE_STMT);
411:
412: if(failed)
413: services._throw(cs.error);
414: }
415:
416: private: // private funcs
417:
418: const char *preprocess_statement(SQL_Driver_services& services, OracleSQL_connection_struct &cs,
419: const char *astatement, OracleSQL_query_lobs &lobs) {
420: size_t statement_size=strlen(astatement);
421:
422: char *result=(char *)services.malloc(statement_size
423: +MAX_STRING // in case of short 'strings'
424: +11/* returning */+6/* into */+(MAX_LOB_NAME_LENGTH+2/*:, */)*2/*ret into*/*MAX_IN_LOBS
425: +1);
426: const char *o=astatement;
427:
428: // /**xxx**/'literal' -> EMPTY_CLOB_FUNC_CALL
429: char *n=result;
430: while(*o) {
431: if(
432: o[0]=='/' &&
433: o[1]=='*' &&
434: o[2]=='*') { // name start
435: o+=3;
436: const char *name_begin=o;
437: while(*o)
438: if(
439: o[0]=='*' &&
440: o[1]=='*' &&
441: o[2]=='/' &&
442: o[3]=='\'') { // name end
443: const char *name_end=o;
444: o+=4;
445: OracleSQL_query_lobs::Item &item=lobs.items[lobs.count++];
446: item.name_ptr=name_begin; item.name_size=name_end-name_begin;
447: item.data_ptr=(char *)services.malloc(statement_size/*max*/); item.data_size=0;
448:
449: const char *start=o;
450: bool escaped=false;
451: while(*o && !(o[0]=='\'' && o[1]!='\'' && !escaped)) {
452: escaped=*o=='\\' || (o[0]=='\'' && o[1]=='\'');
453: if(escaped) {
454: // write pending, skip "\" or "'"
455: if(size_t size=o-start) {
456: memcpy(item.data_ptr+item.data_size, start, size);
457: item.data_size+=size;
458: }
459: start=++o;
460: } else
461: o++;
462: }
463: if(size_t size=o-start) {
464: memcpy(item.data_ptr+item.data_size, start, size);
465: item.data_size+=size;
466: }
467: if(*o)
468: o++; // skip "'"
469:
470: n+=sprintf(n, EMPTY_CLOB_FUNC_CALL);
471: break;
472: } else
473: o++; // /**skip**/'xxx'
474: } else
475: *n++=*o++;
476: }
477: *n=0;
478:
479: if(lobs.count) {
480: int i;
481: n+=sprintf(n, " returning ");
482: for(i=0; i<lobs.count; i++) {
483: if(i)
484: *n++=',';
485: n+=sprintf(n, "%.*s", lobs.items[i].name_size, lobs.items[i].name_ptr);
486: /*memcpy(n, lobs.items[i].name_ptr, lobs.items[i].name_size);
487: n+=lobs.items[i].name_size;*/
488: }
489: n+=sprintf(n, " into ");
490: for(i=0; i<lobs.count; i++) {
491: if(i)
492: *n++='x';
493: n+=sprintf(n, ":%.*s", lobs.items[i].name_size, lobs.items[i].name_ptr);
494: /**n++=':';
495: memcpy(n, lobs.items[i].name_ptr, lobs.items[i].name_size);
496: n+=lobs.items[i].name_size;*/
497: }
498: }
499:
500: return result;
501: }
502:
503: void execute_prepared(
504: SQL_Driver_services& services, OracleSQL_connection_struct &cs,
505: const char *statement, OCIStmt *stmthp, OracleSQL_query_lobs &lobs,
506: unsigned long offset, unsigned long limit,
507: SQL_Driver_query_event_handlers& handlers) {
508:
509: ub2 stmt_type=0; // UNKNOWN
510: /*
511: //gpfs on sun. paf 000818
512: //Zanyway, this is needed before.
513: check(services, cs, "get stmt type", OCIAttrGet(
514: (dvoid *)stmthp, (ub4)OCI_HTYPE_STMT, (ub1 *)&stmt_type,
515: (ub4 *)0, OCI_ATTR_STMT_TYPE, cs.errhp));
516: */
517: if(strncasecmp(statement, "select", 6)==0)
518: stmt_type=OCI_STMT_SELECT;
519: else if(strncasecmp(statement, "insert", 6)==0)
520: stmt_type=OCI_STMT_INSERT;
521: else if(strncasecmp(statement, "update", 6)==0)
522: stmt_type=OCI_STMT_UPDATE;
523:
524: sword status=OCIStmtExecute(cs.svchp, stmthp, cs.errhp,
525: (ub4)stmt_type==OCI_STMT_SELECT?0:1, (ub4)0,
526: (OCISnapshot *)NULL,
527: (OCISnapshot *)NULL, (ub4)OCI_DEFAULT);
528:
529: if(status!=OCI_NO_DATA)
530: check(services, cs, "execute", status);
531:
532: {
533: for(int i=0; i<lobs.count; i++)
534: if(ub4 bytes_to_write=lobs.items[i].data_size) {
535: OracleSQL_query_lobs::return_rows *rows=&lobs.items[i].rows;
536: for(int r=0; r<rows->count; r++) {
537: OCILobLocator *locator=rows->row[r].locator;
538: check(services, cs, "lobwrite", OCILobWrite (
539: cs.svchp, cs.errhp,
540: locator, &bytes_to_write, 1,
541: (dvoid *)lobs.items[i].data_ptr, (ub4)bytes_to_write, OCI_ONE_PIECE,
542: (dvoid *)0, 0, (ub2)0,
543: (ub1) SQLCS_IMPLICIT));
544: }
545: }
546: }
547:
548: switch(stmt_type) {
549: case OCI_STMT_SELECT:
550: fetch_table(services, cs,
551: stmthp, offset, limit,
552: handlers);
553: break;
554: default:
555: /*
556: case OCI_STMT_INSERT:
557: case OCI_STMT_UPDATE:
558: */
559: break;
560: }
561: }
562:
563: void fetch_table(SQL_Driver_services& services, OracleSQL_connection_struct &cs,
564: OCIStmt *stmthp, unsigned long offset, unsigned long limit,
565: SQL_Driver_query_event_handlers& handlers) {
566:
567: OCIParam *mypard;
568: ub2 dtype;
569: text *col_name;
570:
571: struct {
572: ub2 type;
573: char *str;
574: OCILobLocator *var;
575: OCIDefine *def;
576: sb2 indicator;
577: } cols[MAX_COLS]={0};
578: int column_count=0;
579:
580: bool failed=false;
581: jmp_buf saved_mark; memcpy(saved_mark, cs.mark, sizeof(jmp_buf));
582: if(setjmp(cs.mark)) {
583: failed=true;
584: goto cleanup;
585: } else {
586: // idea of preincrementing is that at error time all handles would free up
587: while(++column_count<=MAX_COLS) {
588: /* get next descriptor, if there is one */
589: if(OCIParamGet(stmthp, OCI_HTYPE_STMT, cs.errhp, (void **)&mypard,
1.3 paf 590: (ub4) column_count)!=OCI_SUCCESS) {
591: --column_count;
1.1 parser 592: break;
1.3 paf 593: }
1.1 parser 594:
595: /* Retrieve the data type attribute */
596: check(services, cs, "get type", OCIAttrGet(
597: (dvoid*) mypard, (ub4)OCI_DTYPE_PARAM,
598: (dvoid*) &dtype, (ub4 *)0, (ub4)OCI_ATTR_DATA_TYPE,
599: (OCIError *)cs.errhp));
600:
601: /* Retrieve the column name attribute */
602: ub4 col_name_len;
603: check(services, cs, "get name", OCIAttrGet(
604: (dvoid*) mypard, (ub4)OCI_DTYPE_PARAM,
605: (dvoid**) &col_name, (ub4 *) &col_name_len, (ub4)OCI_ATTR_NAME,
606: (OCIError *)cs.errhp));
607:
608: {
609: size_t size=(size_t)col_name_len;
610: char *ptr=(char *)services.malloc(size);
611: tolower(ptr, (char *)col_name, size);
612: handlers.add_column(ptr, size);
613: }
614:
615: ub2 coerce_type=dtype;
616: sb4 size=0;
617: void *ptr;
618:
619: switch(dtype) {
620: case SQLT_CLOB:
621: {
622: check(services, cs, "alloc output var desc", OCIDescriptorAlloc(
623: (dvoid *)cs.envhp, (dvoid **)(ptr=&cols[column_count-1].var),
624: (ub4)OCI_DTYPE_LOB,
625: 0, (dvoid **)0));
626:
627: size=0;
628: break;
629: }
630: default:
631: coerce_type=SQLT_STR;
632: ptr=cols[column_count-1].str=(char *)services.malloc(MAX_OUT_STRING_LENGTH+1);
633: size=MAX_OUT_STRING_LENGTH;
634: break;
635: }
636:
637: cols[column_count-1].type=coerce_type;
638:
639: check(services, cs, "DefineByPos", OCIDefineByPos(
640: stmthp, &cols[column_count-1].def, cs.errhp,
641: column_count, (ub1 *) ptr, size,
642: coerce_type, (dvoid *) &cols[column_count-1].indicator,
643: (ub2 *)0, (ub2 *)0, OCI_DEFAULT));
644: }
645:
646: handlers.before_rows();
647:
648: for(unsigned long row=0; !limit||row<limit+offset; row++) {
649: sword status=OCIStmtFetch(stmthp, cs.errhp, (ub4)1, (ub4)OCI_FETCH_NEXT,
650: (ub4)OCI_DEFAULT);
1.3 paf 651: if(status==OCI_NO_DATA)
1.1 parser 652: break;
1.3 paf 653: check(services, cs, "fetch", status);
654:
1.1 parser 655: if(row>=offset) {
656: handlers.add_row();
657: for(int i=0; i<column_count; i++) {
658: size_t size;
659: void *ptr;
660: if(cols[i].indicator) { // NULL?
661: size=0;
662: ptr=0;
663: } else // not NULL
664: switch(cols[i].type) {
665: case SQLT_CLOB:
666: {
667: ub4 amtp=4096000000UL;
668: ub4 offset=1;
669: ub4 loblen=0;
670: OCILobLocator *var=(OCILobLocator *)cols[i].var;
671: OCILobGetLength(cs.svchp, cs.errhp, var, &loblen);
672: if(loblen) {
673: size=(size_t)loblen;
674: ptr=services.malloc(size);
675: check(services, cs, "lobread", OCILobRead(cs.svchp, cs.errhp,
676: var, &amtp, offset, (dvoid *) ptr,
677: loblen, (dvoid *)0,
678: 0,
679: (ub2)0, (ub1) SQLCS_IMPLICIT));
680: }
681: break;
682: }
683: default:
684: if(const char *str=cols[i].str) {
685: size=strlen(str);
686: ptr=services.malloc(size);
687: memcpy(ptr, str, size);
688: } else {
689: size=0;
690: ptr=0;
691: }
692: break;
693: }
694: handlers.add_row_cell(ptr, size);
695: }
696: }
697: }
698: }
699:
700: cleanup: // no check call after this point!
701: for(int i=0; i<column_count; i++) {
702: switch(cols[i].type) {
703: case SQLT_CLOB:
704: /* free var locator */
705: OCIDescriptorFree((dvoid *) cols[i].var, (ub4)OCI_DTYPE_LOB);
706: break;
707: default:
708: break;
709: }
710: }
711:
712: if(failed) // need rethrow?
713: longjmp(saved_mark, 1);
714: }
715:
716: private: // conn client library funcs
717:
718: friend void check(
719: SQL_Driver_services& services, OracleSQL_connection_struct &cs,
720: const char *step, sword status);
721: friend sb4 cbf_get_data(dvoid *ctxp,
722: OCIBind *bindp,
723: ub4 iter, ub4 index,
724: dvoid **bufpp,
725: ub4 **alenp,
726: ub1 *piecep,
727: dvoid **indpp,
728: ub2 **rcodepp);
729:
730:
731: #define OCI_DECL(name, params) \
732: typedef sword (*t_OCI##name)params; t_OCI##name OCI##name
733:
734: OCI_DECL(Initialize, (ub4 mode, dvoid *ctxp,
735: dvoid * (*malocfp)(dvoid *ctxp, size_t size),
736: dvoid * (*ralocfp)(dvoid *ctxp, dvoid *memptr, size_t newsize),
737: void (*mfreefp)(dvoid *ctxp, dvoid *memptr) ));
738:
739: OCI_DECL(EnvInit, (OCIEnv **envp, ub4 mode,
740: size_t xtramem_sz, dvoid **usrmempp));
741:
742: OCI_DECL(AttrGet, (CONST dvoid *trgthndlp, ub4 trghndltyp,
743: dvoid *attributep, ub4 *sizep, ub4 attrtype,
744: OCIError *errhp));
745:
746: OCI_DECL(AttrSet, (dvoid *trgthndlp, ub4 trghndltyp, dvoid *attributep,
747: ub4 size, ub4 attrtype, OCIError *errhp));
748:
749: OCI_DECL(BindByPos, (OCIStmt *stmtp, OCIBind **bindp, OCIError *errhp,
750: ub4 position, dvoid *valuep, sb4 value_sz,
751: ub2 dty, dvoid *indp, ub2 *alenp, ub2 *rcodep,
752: ub4 maxarr_len, ub4 *curelep, ub4 mode));
753:
754: OCI_DECL(BindDynamic, (OCIBind *bindp, OCIError *errhp, dvoid *ictxp,
755: OCICallbackInBind icbfp, dvoid *octxp,
756: OCICallbackOutBind ocbfp));
757:
758: OCI_DECL(DefineByPos, (OCIStmt *stmtp, OCIDefine **defnp, OCIError *errhp,
759: ub4 position, dvoid *valuep, sb4 value_sz, ub2 dty,
760: dvoid *indp, ub2 *rlenp, ub2 *rcodep, ub4 mode));
761:
762: OCI_DECL(DescriptorAlloc, (CONST dvoid *parenth, dvoid **descpp,
763: CONST ub4 type, CONST size_t xtramem_sz,
764: dvoid **usrmempp));
765:
766: OCI_DECL(DescriptorFree, (dvoid *descp, CONST ub4 type));
767:
768:
769: OCI_DECL(ErrorGet, (dvoid *hndlp, ub4 recordno, OraText *sqlstate,
770: sb4 *errcodep, OraText *bufp, ub4 bufsiz, ub4 type));
771:
772: OCI_DECL(HandleAlloc, (CONST dvoid *parenth, dvoid **hndlpp, CONST ub4 type,
773: CONST size_t xtramem_sz, dvoid **usrmempp));
774:
775: OCI_DECL(HandleFree, (dvoid *hndlp, CONST ub4 type));
776:
777: OCI_DECL(LobGetLength, (OCISvcCtx *svchp, OCIError *errhp,
778: OCILobLocator *locp,
779: ub4 *lenp));
780:
781: OCI_DECL(LobRead, (OCISvcCtx *svchp, OCIError *errhp, OCILobLocator *locp,
782: ub4 *amtp, ub4 offset, dvoid *bufp, ub4 bufl,
783: dvoid *ctxp, sb4 (*cbfp)(dvoid *ctxp,
784: CONST dvoid *bufp,
785: ub4 len,
786: ub1 piece),
787: ub2 csid, ub1 csfrm));
788:
789: OCI_DECL(LobWrite, (OCISvcCtx *svchp, OCIError *errhp, OCILobLocator *locp,
790: ub4 *amtp, ub4 offset, dvoid *bufp, ub4 buflen,
791: ub1 piece, dvoid *ctxp,
792: sb4 (*cbfp)(dvoid *ctxp,
793: dvoid *bufp,
794: ub4 *len,
795: ub1 *piece),
796: ub2 csid, ub1 csfrm));
797:
798: OCI_DECL(ParamGet, (CONST dvoid *hndlp, ub4 htype, OCIError *errhp,
799: dvoid **parmdpp, ub4 pos));
800:
801: OCI_DECL(ServerAttach, (OCIServer *srvhp, OCIError *errhp,
802: CONST OraText *dblink, sb4 dblink_len, ub4 mode));
803:
804: OCI_DECL(ServerDetach, (OCIServer *srvhp, OCIError *errhp, ub4 mode));
805:
806: OCI_DECL(SessionBegin, (OCISvcCtx *svchp, OCIError *errhp, OCISession *usrhp,
807: ub4 credt, ub4 mode));
808:
809: OCI_DECL(SessionEnd, (OCISvcCtx *svchp, OCIError *errhp, OCISession *usrhp,
810: ub4 mode));
811:
812: OCI_DECL(StmtExecute, (OCISvcCtx *svchp, OCIStmt *stmtp, OCIError *errhp,
813: ub4 iters, ub4 rowoff, CONST OCISnapshot *snap_in,
814: OCISnapshot *snap_out, ub4 mode));
815:
816: OCI_DECL(StmtFetch, (OCIStmt *stmtp, OCIError *errhp, ub4 nrows,
817: ub2 orientation, ub4 mode));
818:
819: OCI_DECL(StmtPrepare, (OCIStmt *stmtp, OCIError *errhp, CONST OraText *stmt,
820: ub4 stmt_len, ub4 language, ub4 mode));
821:
822: OCI_DECL(TransCommit, (OCISvcCtx *svchp, OCIError *errhp, ub4 flags));
823:
824: OCI_DECL(TransRollback, (OCISvcCtx *svchp, OCIError *errhp, ub4 flags));
825:
826: private: // conn client library funcs linking
827:
828: const char *dlink(const char *dlopen_file_spec) {
829: if(lt_dlinit())
830:
831: return lt_dlerror();
832:
833:
834:
835: lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
836:
837: //return "hren31";
838:
839: if(!handle)
840: return lt_dlerror(); //"can not open the dynamic link module";
841:
842: #define DSLINK(name, action) \
843: name=(t_##name)lt_dlsym(handle, #name); \
844: if(!name) \
845: action;
846:
847: #define OCI_LINK(name) DSLINK(OCI##name, return "function OCI" #name " was not found")
848:
849: OCI_LINK(Initialize);
850: OCI_LINK(EnvInit);
851: OCI_LINK(AttrGet); OCI_LINK(AttrSet);
852: OCI_LINK(BindByPos); OCI_LINK(BindDynamic);
853: OCI_LINK(DefineByPos);
854: OCI_LINK(DescriptorAlloc); OCI_LINK(DescriptorFree);
855: OCI_LINK(ErrorGet);
856: OCI_LINK(HandleAlloc); OCI_LINK(HandleFree);
857: OCI_LINK(LobGetLength);
858: OCI_LINK(LobRead); OCI_LINK(LobWrite);
859: OCI_LINK(ParamGet);
860: OCI_LINK(ServerAttach); OCI_LINK(ServerDetach);
861: OCI_LINK(SessionBegin); OCI_LINK(SessionEnd);
862: OCI_LINK(StmtExecute); OCI_LINK(StmtFetch); OCI_LINK(StmtPrepare);
863: OCI_LINK(TransCommit); OCI_LINK(TransRollback);
864:
865: return 0;
866: }
867:
868: } *OracleSQL_driver;
869:
870: void check(
871: SQL_Driver_services& services, OracleSQL_connection_struct &cs,
872: const char *step, sword status) {
873:
874: const char *msg;
875: char reason[MAX_STRING/2];
876:
877: switch (status) {
878: case OCI_SUCCESS:
879: return; // hurrah
880: case OCI_ERROR:
881: {
882: sb4 errcode;
883: if(OracleSQL_driver->OCIErrorGet((dvoid *)cs.errhp, (ub4)1, (text *)NULL, &errcode,
884: (text *)reason, (ub4)sizeof(reason), OCI_HTYPE_ERROR)==OCI_SUCCESS)
885: msg=reason;
886: else
887: msg="[can not get error description]";
888: break;
889: }
890: case OCI_SUCCESS_WITH_INFO:
891: msg="SUCCESS_WITH_INFO"; break;
892: case OCI_NEED_DATA:
893: msg="NEED_DATA"; break;
894: case OCI_NO_DATA:
895: msg="NODATA"; break;
896: case OCI_INVALID_HANDLE:
897: msg="INVALID_HANDLE"; break;
898: case OCI_STILL_EXECUTING:
899: msg="STILL_EXECUTE"; break;
900: case OCI_CONTINUE:
901: msg="CONTINUE"; break;
902: default:
903: msg="unknown"; break;
904: }
905:
906: snprintf(cs.error, sizeof(cs.error), "%s (%s, %d)",
907: msg, step, (int)status);
908: longjmp(cs.mark, 1);
909: }
910:
911:
912: /* ----------------------------------------------------------------- */
913: /* Intbind callback that does not do any data input. */
914: /* ----------------------------------------------------------------- */
915: static sb4 cbf_no_data(
916: dvoid *ctxp,
917: OCIBind *bindp,
918: ub4 iter, ub4 index,
919: dvoid **bufpp,
920: ub4 *alenpp,
921: ub1 *piecep,
922: dvoid **indpp) {
923: *bufpp=(dvoid *)0;
924: *alenpp=0;
925: static sb2 null_ind=-1;
926: *indpp=(dvoid *) &null_ind;
927: *piecep=OCI_ONE_PIECE;
928:
929: return OCI_CONTINUE;
930: }
931:
932: /* ----------------------------------------------------------------- */
933: /* Outbind callback for returning data. */
934: /* ----------------------------------------------------------------- */
935: static sb4 cbf_get_data(dvoid *ctxp,
936: OCIBind *bindp,
937: ub4 iter, ub4 index,
938: dvoid **bufpp,
939: ub4 **alenp,
940: ub1 *piecep,
941: dvoid **indpp,
942: ub2 **rcodepp) {
943: OracleSQL_query_lobs::cbf_context_struct &context=
944: *(OracleSQL_query_lobs::cbf_context_struct *)ctxp;
945:
946: if(index==0) {
947: static ub4 rows;
948: check(*context.services, *context.cs, "AttrGet cbf_get_data ROWS_RETURNED",
949: OracleSQL_driver->OCIAttrGet(
950: (CONST dvoid *) bindp, OCI_HTYPE_BIND, (dvoid *)&rows,
951: (ub4 *)sizeof(ub2), OCI_ATTR_ROWS_RETURNED, context.cs->errhp)) ;
952: context.rows->count=(ub2)rows;
953: context.rows->row=(OracleSQL_query_lobs::return_rows::return_row *)
954: context.services->malloc(sizeof(OracleSQL_query_lobs::return_rows::return_row)*rows);
955: }
956:
957: OracleSQL_query_lobs::return_rows::return_row &var=context.rows->row[index];
958:
959: check(*context.services, *context.cs, "alloc output var desc dynamic", OracleSQL_driver->OCIDescriptorAlloc(
960: (dvoid *) context.cs->envhp, (dvoid **)&var.locator,
961: (ub4)OCI_DTYPE_LOB,
962: 0, (dvoid **)0));
963:
964: *bufpp=var.locator;
965: *alenp=&var.len;
966: *indpp=(dvoid *) &var.ind;
967: *piecep=OCI_ONE_PIECE;
968: *rcodepp=&var.rcode;
969:
970: return OCI_CONTINUE;
971: }
972:
973: void tolower(char *out, const char *in, size_t size) {
974: while(size--)
975: *out++=tolower(*in++);
976: }
977:
978: extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
979: //_asm int 3;
980: return OracleSQL_driver=new OracleSQL_Driver();
981: }