--- sql/oracle/parser3oracle.C 2004/05/25 07:00:09 1.58 +++ sql/oracle/parser3oracle.C 2013/05/27 20:10:14 1.78 @@ -1,13 +1,12 @@ /** @file Parser Oracle driver. - Copyright(c) 2001, 2003 ArtLebedev Group (http://www.artlebedev.com) + Copyright (c) 2001-2012 Art. Lebedev Studio (http://www.artlebedev.com) Author: Alexandr Petrosian (http://paf.design.ru) 2001.07.30 using Oracle 8.1.6 [@test tested with Oracle 7.x.x] */ -static const char *RCSId="$Id: parser3oracle.C,v 1.58 2004/05/25 07:00:09 paf Exp $"; #include "config_includes.h" @@ -15,10 +14,13 @@ static const char *RCSId="$Id: parser3or #include +volatile const char * IDENT_PARSER3ORACLE_C="$Id: parser3oracle.C,v 1.78 2013/05/27 20:10:14 moko Exp $" IDENT_PA_SQL_DRIVER_H; + #define MAX_COLS 500 #define MAX_IN_LOBS 5 #define MAX_LOB_NAME_LENGTH 100 #define MAX_OUT_STRING_LENGTH 4000 +#define MAX_BINDS 100 #define EMPTY_CLOB_FUNC_CALL "empty_clob()" @@ -44,6 +46,8 @@ inline int min(int a, int b){ return a(aconnection); @@ -376,6 +420,7 @@ public: // free connection. leave that to GC [no such services func. yet?] // connection.services->free(&connection); } + void commit(void *aconnection) { Connection& connection=*static_cast(aconnection); if(setjmp(connection.mark)) @@ -383,6 +428,7 @@ public: check(connection, "commit", OCITransCommit(connection.svchp, connection.errhp, 0)); } + void rollback(void *aconnection) { Connection& connection=*static_cast(aconnection); if(setjmp(connection.mark)) @@ -399,49 +445,83 @@ public: return true; } - const char* quote(void *aconnection, - const char *from, unsigned int length) + // charset here is services.request_charset(), not connection.client_charset + // thus we can't use the sql server quoting support + const char* quote(void *aconnection, const char *str, unsigned int length) { - Connection& connection=*static_cast(aconnection); - char *result=(char*)connection.services->malloc_atomic(length*2+1); - char *to=result; - while(length--) { - switch(*from) { - case '\'': // "'" -> "''" - *to++='\''; - break; - } - *to++=*from++; + const char* from; + const char* from_end=str+length; + + size_t quoted=0; + + for(from=str; from(aconnection); + char *result=(char*)connection.services->malloc_atomic(length + quoted + 1); + char *to = result; + + for(from=str; from '' + *to++=*from; } + *to=0; return result; } - void query(void *aconnection, - const char *astatement, unsigned long offset, unsigned long limit, - SQL_Driver_query_event_handlers& handlers) - { + + void query(void* aconnection, + const char* astatement, + size_t placeholders_count, Placeholder* placeholders, + unsigned long offset, unsigned long limit, + SQL_Driver_query_event_handlers& handlers + ){ + Connection& connection=*static_cast(aconnection); - const char* cstrClientCharset=connection.options.cstrClientCharset; - OracleSQL_query_lobs lobs={{0}, 0}; + Query_lobs lobs={{0}, 0}; OCIStmt *stmthp=0; SQL_Driver_services& services=*connection.services; - // transcode from $request:charset to connect-string?client_charset - if(cstrClientCharset) { - size_t transcoded_statement_size; - services.transcode(astatement, strlen(astatement), - astatement, transcoded_statement_size, - services.request_charset(), - cstrClientCharset); - } - bool failed=false; if(setjmp(connection.mark)) { failed=true; goto cleanup; } else { - const char *statement=preprocess_statement(connection, astatement, lobs); + if(placeholders_count>MAX_BINDS) + fail(connection, "too many bind variables"); + + while(isspace((unsigned char)*astatement)) + astatement++; + + const char* client_charset=connection.options.client_charset; + const char* request_charset=services.request_charset(); + bool transcode_needed=transcode_required(connection); + + if(transcode_needed){ + // transcode query from $request:charset to ?ClientCharset + size_t transcoded_xxx_size; + services.transcode(astatement, strlen(astatement), + astatement, transcoded_xxx_size, + request_charset, + client_charset); + } + + const char *statement=_preprocess_statement_lobs(connection, astatement, lobs); + + modified_statement mstatement=_preprocess_statement_limit(connection, statement, offset, limit); + statement=mstatement.statement; + + if(mstatement.limit) // limit was added in statement + limit=SQL_NO_LIMIT; + if(mstatement.offset) // limit was added in statement + offset=0; check(connection, "HandleAlloc STMT", OCIHandleAlloc( (dvoid *)connection.envhp, (dvoid **) &stmthp, (ub4)OCI_HTYPE_STMT, 0, 0)); @@ -449,15 +529,76 @@ public: OCIStmtPrepare(stmthp, connection.errhp, (unsigned char *)statement, (ub4)strlen((char *)statement), (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT)); + + struct Bind_info { + OCIBind *bind; + sb2 indicator; + }; + + int binds_size=sizeof(Bind_info) * placeholders_count; + // we DO store OCIBind* into ATOMIC gc memory, + // but we do not allocate/free it, that's done automatically from oracle [using environment handles] + // so we don't have to bother with that + Bind_info* binds=static_cast(services.malloc_atomic(binds_size)); { + for(size_t i=0; i EMPTY_CLOB_FUNC_CALL char *n=result; @@ -530,14 +711,14 @@ private: // private funcs saved_o=0; // found, marking that const char *name_end=o; o+=4; - OracleSQL_query_lobs::Item &item=lobs.items[lobs.count++]; + Query_lobs::Item &item=lobs.items[lobs.count++]; item.name_ptr=name_begin; item.name_size=name_end-name_begin; item.data_ptr=(char *)services.malloc_atomic(statement_size/*max*/); item.data_size=0; const char *start=o; bool escaped=false; while(*o && !(o[0]=='\'' && o[1]!='\'' && !escaped)) { - escaped=o[0]=='\'' && o[1]=='\''; + escaped=!escaped && (o[0]=='\'' && o[1]=='\''); if(escaped) { // write pending, skip "\" or "'" if(size_t size=o-start) { @@ -589,9 +770,9 @@ private: // private funcs void execute_prepared( Connection& connection, - const char *statement, OCIStmt *stmthp, OracleSQL_query_lobs &lobs, - unsigned long offset, unsigned long limit, - SQL_Driver_query_event_handlers& handlers) { + const char* astatement, OCIStmt *stmthp, Query_lobs &lobs, + unsigned long offset, unsigned long limit, + SQL_Driver_query_event_handlers& handlers, bool skip_rownum_column) { ub2 stmt_type=0; // UNKNOWN /* @@ -602,13 +783,11 @@ private: // private funcs (ub4 *)0, OCI_ATTR_STMT_TYPE, connection.errhp)); */ - while(isspace(*statement)) - statement++; - if(strncasecmp(statement, "select", 6)==0) + if(strncasecmp(astatement, "select", 6)==0) stmt_type=OCI_STMT_SELECT; - else if(strncasecmp(statement, "insert", 6)==0) + else if(strncasecmp(astatement, "insert", 6)==0) stmt_type=OCI_STMT_INSERT; - else if(strncasecmp(statement, "update", 6)==0) + else if(strncasecmp(astatement, "update", 6)==0) stmt_type=OCI_STMT_UPDATE; sword status=OCIStmtExecute(connection.svchp, stmthp, connection.errhp, @@ -622,7 +801,7 @@ private: // private funcs { for(int i=0; icount; r++) { OCILobLocator *locator=rows->row[r].locator; check(connection, "lobwrite", OCILobWrite ( @@ -639,7 +818,7 @@ private: // private funcs case OCI_STMT_SELECT: fetch_table(connection, stmthp, offset, limit, - handlers); + handlers, skip_rownum_column); break; default: /* @@ -651,10 +830,9 @@ private: // private funcs } void fetch_table(Connection& connection, - OCIStmt *stmthp, unsigned long offset, unsigned long limit, - SQL_Driver_query_event_handlers& handlers) + OCIStmt *stmthp, unsigned long offset, unsigned long limit, + SQL_Driver_query_event_handlers& handlers, bool skip_rownum_column) { - const char* cstrClientCharset=connection.options.cstrClientCharset; SQL_Driver_services& services=*connection.services; ub4 prefetch_rows=100; @@ -669,9 +847,9 @@ private: // private funcs (dvoid *)&prefetch_mem_size, (ub4)0, (ub4)OCI_ATTR_PREFETCH_MEMORY, (OCIError *)connection.errhp)); - OCIParam *mypard; - ub2 dtype; - const char* col_name; + OCIParam *mypard; + ub2 dtype; + const char* col_name; struct Col { ub2 type; @@ -688,6 +866,10 @@ private: // private funcs failed=true; goto cleanup; } else { + bool transcode_needed=transcode_required(connection); + const char* client_charset=connection.options.client_charset; + const char* request_charset=services.request_charset(); + // idea of preincrementing is that at error time all handles would free up while(++column_count<=MAX_COLS) { /* get next descriptor, if there is one */ @@ -696,7 +878,10 @@ private: // private funcs --column_count; break; } - + + if(skip_rownum_column && column_count==1) + continue; + /* Retrieve the data type attribute */ check(connection, "get type", OCIAttrGet( (dvoid*) mypard, (ub4)OCI_DTYPE_PARAM, @@ -709,17 +894,18 @@ private: // private funcs (dvoid*) mypard, (ub4)OCI_DTYPE_PARAM, (dvoid**) &col_name, (ub4 *) &col_name_len, (ub4)OCI_ATTR_NAME, (OCIError *)connection.errhp)); - // transcode to $request:charset from connect-string?client_charset - if(cstrClientCharset) { + + size_t length=(size_t)col_name_len; + if(transcode_needed){ + // transcode column name from ?ClientCharset to $request:charset services.transcode(col_name, col_name_len, - col_name, col_name_len, - cstrClientCharset, - services.request_charset()); + col_name, length, + client_charset, + request_charset); } Col& col=cols[column_count-1]; { - size_t length=(size_t)col_name_len; char *ptr=(char *)services.malloc_atomic(length+1); if( connection.options.bLowerCaseColumnNames ) tolower_str(ptr, col_name, length); @@ -734,25 +920,25 @@ private: // private funcs void *ptr; switch(dtype) { - case SQLT_CLOB: - { - check(connection, "alloc output var desc", OCIDescriptorAlloc( - (dvoid *)connection.envhp, (dvoid **)(ptr=&col.var), - (ub4)OCI_DTYPE_LOB, - 0, (dvoid **)0)); - - size=0; + case SQLT_CLOB: + { + check(connection, "alloc output var desc", OCIDescriptorAlloc( + (dvoid *)connection.envhp, (dvoid **)(ptr=&col.var), + (ub4)OCI_DTYPE_LOB, + 0, (dvoid **)0)); + + size=0; + break; + } + default: + coerce_type=SQLT_STR; + char*& buf=connection.fetch_buffers[column_count-1]; + ptr=buf; // get cached buffer + if(!ptr) // allocate if needed, caching it + ptr=buf=(char *)services.malloc_atomic(MAX_OUT_STRING_LENGTH+1/*terminator*/); + col.str=(char*)ptr; + size=MAX_OUT_STRING_LENGTH; break; - } - default: - coerce_type=SQLT_STR; - char*& buf=connection.fetch_buffers[column_count-1]; - ptr=buf; // get cached buffer - if(!ptr) // allocate if needed, caching it - ptr=buf=(char *)services.malloc_atomic(MAX_OUT_STRING_LENGTH+1/*terminator*/); - col.str=(char*)ptr; - size=MAX_OUT_STRING_LENGTH; - break; } col.type=coerce_type; @@ -771,7 +957,7 @@ private: // private funcs check(connection, handlers.before_rows(connection.sql_error)); - for(unsigned long row=0; !limit||row=offset) { check(connection, handlers.add_row(connection.sql_error)); for(int i=0; ioffset + statement_limited=(char *)connection.services->malloc_atomic( + statement_size + +64/*SELECT * FROM (SELECT ROWNUM r__, z__.* FROM () z__) WHERE r__<=*/ + +MAX_NUMBER + +9/* AND r__>*/ + +MAX_NUMBER + +1/*terminator*/ + ); + + result.statement=statement_limited; + + strcpy(statement_limited, "SELECT * FROM (SELECT ROWNUM r__, z__.* FROM ("); + strcat(statement_limited, astatement); + + statement_limited+=46+statement_size; + statement_limited+=snprintf(statement_limited, 18+MAX_NUMBER, ") z__) WHERE r__<=%lu", limit+offset); + statement_limited+=snprintf(statement_limited, 9+MAX_NUMBER, " AND r__>%lu", offset); + + } else { + + // SELECT * FROM (user_query) WHERE ROWNUM<=limit + // this statement can be easy for the sql server but we can't use it with offset + + statement_limited=(char *)connection.services->malloc_atomic( + statement_size + +31/*SELECT * FROM () WHERE ROWNUM<=*/ + +MAX_NUMBER + +1/*terminator*/ + ); + + result.statement=statement_limited; + + strcpy(statement_limited, "SELECT * FROM ("); + strcat(statement_limited, astatement); + + statement_limited+=15+statement_size; + statement_limited+=snprintf(statement_limited, 16+MAX_NUMBER, ") WHERE ROWNUM<=%lu", limit); + + } + *statement_limited=0; + + //connection.services->_throw(result.statement); + } + return result; + } + private: // conn client library funcs + friend void fail(Connection& connection, const char *msg); friend void check(Connection& connection, const char *step, sword status); friend sb4 cbf_get_data(dvoid *ctxp, OCIBind *bindp, @@ -894,6 +1167,11 @@ private: // conn client library funcs ub2 dty, dvoid *indp, ub2 *alenp, ub2 *rcodep, ub4 maxarr_len, ub4 *curelep, ub4 mode)); + OCI_DECL(BindByName, (OCIStmt *stmtp, OCIBind **bindp, OCIError *errhp, + text* placeholder, sb4 placeh_len, dvoid *valuep, sb4 value_sz, + ub2 dty, dvoid *indp, ub2 *alenp, ub2 *rcodep, + ub4 maxarr_len, ub4 *curelep, ub4 mode)); + OCI_DECL(BindDynamic, (OCIBind *bindp, OCIError *errhp, dvoid *ictxp, OCICallbackInBind icbfp, dvoid *octxp, OCICallbackOutBind ocbfp)); @@ -969,11 +1247,19 @@ private: // conn client library funcs private: // conn client library funcs linking const char *dlink(const char *dlopen_file_spec) { - if(lt_dlinit()) - return lt_dlerror(); - lt_dlhandle handle=lt_dlopen(dlopen_file_spec); - if(!handle) - return lt_dlerror(); //"can not open the dynamic link module"; + if(lt_dlinit()){ + if(const char* result=lt_dlerror()) + return result; + return "can not prepare to dynamic loading"; + } + + lt_dlhandle handle=lt_dlopen(dlopen_file_spec); + + if(!handle){ + if(const char* result=lt_dlerror()) + return result; + return "can not open the dynamic link module"; + } #define DSLINK(name, action) \ name=(t_##name)lt_dlsym(handle, #name); \ @@ -985,7 +1271,7 @@ private: // conn client library funcs li OCI_LINK(Initialize); OCI_LINK(EnvInit); OCI_LINK(AttrGet); OCI_LINK(AttrSet); - OCI_LINK(BindByPos); OCI_LINK(BindDynamic); + OCI_LINK(BindByPos); OCI_LINK(BindByName); OCI_LINK(BindDynamic); OCI_LINK(DefineByPos); OCI_LINK(DescriptorAlloc); OCI_LINK(DescriptorFree); OCI_LINK(ErrorGet); @@ -1020,15 +1306,13 @@ void check(Connection& connection, const (text *)reason, (ub4)sizeof(reason), OCI_HTYPE_ERROR)==OCI_SUCCESS) { msg=reason; - // transcode to $request:charset from connect-string?client_charset - if(const char* cstrClientCharset=connection.options.cstrClientCharset) { - if(msg) { - if(size_t msg_length=strlen(msg)) { - connection.services->transcode(msg, msg_length, - msg, msg_length, - cstrClientCharset, - connection.services->request_charset()); - } + if(msg && transcode_required(connection)){ + // transcode server error message from ?ClientCharset to $request:charset + if(size_t msg_length=strlen(msg)){ + connection.services->transcode(msg, msg_length, + msg, msg_length, + connection.options.client_charset, + connection.services->request_charset()); } } } else @@ -1054,6 +1338,15 @@ void check(Connection& connection, const longjmp(connection.mark, 1); } +bool transcode_required(Connection& connection){ + return (connection.options.client_charset && strcmp(connection.options.client_charset, connection.services->request_charset())!=0); +} + +void fail(Connection& connection, const char* msg) { + snprintf(connection.error, sizeof(connection.error), "%s", msg); + longjmp(connection.mark, 1); +} + void check(Connection& connection, bool error) { if(error) longjmp(connection.mark, 1); @@ -1062,7 +1355,7 @@ void check(Connection& connection, bool /* ----------------------------------------------------------------- */ /* Intbind callback that does not do any data input. */ /* ----------------------------------------------------------------- */ -static sb4 cbf_no_data( +sb4 cbf_no_data( dvoid* /*ctxp*/, OCIBind* /*bindp*/, ub4 /*iter*/, ub4 /*index*/, @@ -1090,7 +1383,7 @@ static sb4 cbf_get_data(dvoid *ctxp, ub1 *piecep, dvoid **indpp, ub2 **rcodepp) { - OracleSQL_query_lobs::Item& context=*static_cast(ctxp); + Query_lobs::Item& context=*static_cast(ctxp); if(index==0) { static ub4 rows; @@ -1099,11 +1392,11 @@ static sb4 cbf_get_data(dvoid *ctxp, (CONST dvoid *) bindp, OCI_HTYPE_BIND, (dvoid *)&rows, (ub4 *)sizeof(ub2), OCI_ATTR_ROWS_RETURNED, context.connection->errhp)) ; context.rows.count=(ub2)rows; - context.rows.row=(OracleSQL_query_lobs::return_rows::return_row *) - context.connection->services->malloc_atomic(sizeof(OracleSQL_query_lobs::return_rows::return_row)*rows); + context.rows.row=(Query_lobs::return_rows::return_row *) + context.connection->services->malloc_atomic(sizeof(Query_lobs::return_rows::return_row)*rows); } - OracleSQL_query_lobs::return_rows::return_row &var=context.rows.row[index]; + Query_lobs::return_rows::return_row &var=context.rows.row[index]; check(*context.connection, "alloc output var desc dynamic", OracleSQL_driver->OCIDescriptorAlloc( (dvoid *) context.connection->envhp, (dvoid **)&var.locator, @@ -1119,16 +1412,6 @@ static sb4 cbf_get_data(dvoid *ctxp, return OCI_CONTINUE; } -static void tolower_str(char *out, const char *in, size_t size) { - while(size--) - *out++=(char)tolower(*in++); -} -static void toupper_str(char *out, const char *in, size_t size) { - while(size--) - *out++=(char)toupper(*in++); -} - - extern "C" SQL_Driver *SQL_DRIVER_CREATE() { return OracleSQL_driver=new OracleSQL_Driver(); }