--- sql/odbc/parser3odbc.C 2008/06/27 12:54:44 1.27 +++ sql/odbc/parser3odbc.C 2008/07/01 10:21:03 1.32 @@ -5,7 +5,7 @@ Author: Alexandr Petrosian (http://paf.design.ru) */ -static const char *RCSId="$Id: parser3odbc.C,v 1.27 2008/06/27 12:54:44 misha Exp $"; +static const char *RCSId="$Id: parser3odbc.C,v 1.32 2008/07/01 10:21:03 misha Exp $"; #ifndef _MSC_VER # error compile ISAPI module with MSVC [no urge for now to make it autoconf-ed (PAF)] @@ -44,6 +44,9 @@ static const char *RCSId="$Id: parser3od #ifndef strncasecmp # define strncasecmp _strnicmp #endif +#ifndef strcasecmp +# define strcasecmp _stricmp +#endif static char *lsplit(char *string, char delim){ if(string){ @@ -55,6 +58,13 @@ static char *lsplit(char *string, char d return 0; } +static char *lsplit(char **string_ref, char delim){ + char *result=*string_ref; + char *next=lsplit(*string_ref, delim); + *string_ref=next; + return result; +} + static void toupper_str(char *out, const char *in, size_t size){ while(size--) *out++=(char)toupper(*in++); @@ -66,7 +76,7 @@ struct Connection { CDatabase* db; const char* client_charset; bool autocommit; - bool use_multi_row_fetch; + bool fast_offset_search; }; /** @@ -84,10 +94,10 @@ public: const char *initialize(char *dlopen_file_spec) { return 0; } /** connect @param url - format: @b DSN=dsn;UID=user;PWD=password (ODBC connect string) - ;ClientCharset=charset // transcode with parser - ;MultiRowFetch=1 // 0 -- disable (slower) - ;autocommit=1 // 0 -- disable auto commit + format: @b DSN=dsn;UID=user;PWD=password? (ODBC connect string) + ClientCharset=charset& // transcode with parser + autocommit=1& // 0 -- disable auto commit + FastOffsetSearch=0 WARNING: must be used only to connect, for buffer doesn't live long */ @@ -100,60 +110,39 @@ public: *connection_ref=&connection; connection.services=&services; connection.client_charset=0; - connection.use_multi_row_fetch=true; + connection.fast_offset_search=false; connection.autocommit=true; size_t url_length=strlen(url); + char *options=lsplit(url, '?'); - if(const char* key_start=strstr(url, "ClientCharset=")){ - const char* value_start=key_start+14/*strlen("ClientCharset=")*/; - const char* value_end=strchr(value_start, ';'); - if(!value_end) - value_end=url+url_length; - - if(size_t value_length=value_end-value_start){ - char* client_charset=(char*)services.malloc_atomic(value_length+1); - toupper_str(client_charset, value_start, value_length); - client_charset[value_length]=0; + // todo: analize connect string and if 'SQL Server' found, modify query and add TOP into SELECTs - connection.client_charset=client_charset; - } - } - if(const char* key_start=strstr(url, "MultiRowFetch=")){ - const char* value_start=key_start+14/*strlen("MultiRowFetch=")*/; - const char* value_end=strchr(value_start, ';'); - if(!value_end) - value_end=url+url_length; - - if(size_t value_length=value_end-value_start){ - char* value=(char*)services.malloc_atomic(value_length+1); - memcpy(value, value_start, value_length); - value[value_length]=0; - if(atoi(value)==0) - connection.use_multi_row_fetch=false; - } - } - if(const char* key_start=strstr(url, "autocommit=")){ - const char* value_start=key_start+11/*strlen("autocommit=")*/; - const char* value_end=strchr(value_start, ';'); - if(!value_end) - value_end=url+url_length; - - if(size_t value_length=value_end-value_start){ - char* value=(char*)services.malloc_atomic(value_length+1); - memcpy(value, value_start, value_length); - value[value_length]=0; - if(atoi(value)==0) - connection.autocommit=false; + while(options){ + if(char *key=lsplit(&options, '&')){ + if(*key){ + if(char *value=lsplit(key, '=')){ + if(strcmp(key, "ClientCharset")==0){ + toupper_str(value, value, strlen(value)); + connection.client_charset=value; + } else if(strcasecmp(key, "autocommit")==0){ + if(atoi(value)==0) + connection.autocommit=false; + } else if(strcmp(key, "FastOffsetSearch")==0){ + if(atoi(value)==1) + connection.fast_offset_search=true; + } else + services._throw("unknown connect option" /*key*/); + } else + services._throw("connect option without =value" /*key*/); + } } } TRY { connection.db=new CDatabase(); connection.db->OpenEx(url, CDatabase::noOdbcDialog); - - if(!connection.autocommit) - connection.db->BeginTrans(); + connection.db->BeginTrans(); } CATCH_ALL (e) { _throw(services, e); @@ -174,28 +163,24 @@ public: void commit(void *aconnection){ Connection& connection=*static_cast(aconnection); - if(!connection.autocommit){ - TRY - connection.db->CommitTrans(); - connection.db->BeginTrans(); - CATCH_ALL (e) { - _throw(*connection.services, e); - } - END_CATCH_ALL + TRY + connection.db->CommitTrans(); + connection.db->BeginTrans(); + CATCH_ALL (e) { + _throw(*connection.services, e); } + END_CATCH_ALL } void rollback(void *aconnection){ Connection& connection=*static_cast(aconnection); - if(!connection.autocommit){ - TRY - connection.db->Rollback(); - connection.db->BeginTrans(); - CATCH_ALL (e) { - _throw(*connection.services, e); - } - END_CATCH_ALL + TRY + connection.db->Rollback(); + connection.db->BeginTrans(); + CATCH_ALL (e) { + _throw(*connection.services, e); } + END_CATCH_ALL } bool ping(void *connection){ @@ -231,15 +216,17 @@ public: if(placeholders_count>0) services._throw("bind variables not supported (yet)"); - bool transcode_needed=_transcode_required(connection); + const char* client_charset=connection.client_charset; + const char* request_charset=services.request_charset(); + bool transcode_needed=(client_charset && strcmp(client_charset, request_charset)!=0); // transcode query from $request:charset to ?ClientCharset if(transcode_needed){ size_t length=strlen(statement); services.transcode(statement, length, statement, length, - services.request_charset(), - connection.client_charset); + request_charset, + client_charset); } while(isspace((unsigned char)*statement)) @@ -266,16 +253,16 @@ public: //CRecordset::useMultiRowFetch //CRecordset::userAllocMultiRowBuffers //CRecordset::useExtendedFetch - /* - if(connection.use_multi_row_fetch){ +/* + if(connection.fast_offset_search){ options+=CRecordset::useMultiRowFetch+CRecordset::userAllocMultiRowBuffers; } else { options+=CRecordset::skipDeletedRecords; } - */ +*/ TRY { rs.Open( - (connection.use_multi_row_fetch)?CRecordset::dynamic:CRecordset::forwardOnly, + (connection.fast_offset_search)?CRecordset::dynamic:CRecordset::forwardOnly, statement, options ); @@ -300,6 +287,7 @@ public: column_count=MAX_COLS; SWORD column_types[MAX_COLS]; + bool transcode_column[MAX_COLS]; SQL_Error sql_error; #define CHECK(afailed) if(afailed) services._throw(sql_error) @@ -309,6 +297,24 @@ public: CODBCFieldInfo fieldinfo; rs.GetODBCFieldInfo(i, fieldinfo); column_types[i]=fieldinfo.m_nSQLType; + switch(fieldinfo.m_nSQLType){ + case SQL_NUMERIC: + case SQL_DECIMAL: + case SQL_INTEGER: + case SQL_SMALLINT: + case SQL_FLOAT: + case SQL_REAL: + case SQL_DOUBLE: + case SQL_DATETIME: + case SQL_SMALLDATETIME: + case SQL_BIGINT: + case SQL_TINYINT: + transcode_column[i]=false; + break; + default: + transcode_column[i]=transcode_needed; + break; + } size_t length=fieldinfo.m_strName.GetLength(); char *str=0; if(length){ @@ -319,8 +325,8 @@ public: if(transcode_needed){ services.transcode(str, length, str, length, - connection.client_charset, - services.request_charset()); + client_charset, + request_charset); } } CHECK(handlers.add_column(sql_error, str, length)); @@ -330,14 +336,12 @@ public: // skip offset rows if(offset){ - if(connection.use_multi_row_fetch){ + if(connection.fast_offset_search){ rs.Move(offset); } else { unsigned long row=offset; - while(!rs.IsEOF() && row>0){ + while(!rs.IsEOF() && row--) rs.MoveNext(); - row--; - } } } @@ -351,7 +355,6 @@ public: char* str; switch(column_types[i]){ //case xBOOL: - //case SQL_INTEGER: // serg@design.ru did that in parser2. test first! //case SQL_DATETIME: << default: handles that more properly (?) case SQL_BINARY: case SQL_VARBINARY: @@ -369,11 +372,12 @@ public: } // transcode cell value from ?ClientCharset to $request:charset - if(transcode_needed && length) + if(length && transcode_column[i]){ services.transcode(str, length, str, length, - connection.client_charset, - services.request_charset()); + client_charset, + request_charset); + } CHECK(handlers.add_row_cell(sql_error, str, length)); } @@ -388,6 +392,9 @@ public: } CATCH_ALL (e) { _throw(services, e); } END_CATCH_ALL + + if(connection.autocommit) + commit(aconnection); } private: @@ -412,7 +419,7 @@ private: ptr=v.m_boolVal?"1":"0"; length=1; break;*/ -/* case DBVT_UCHAR: +/* case DBVT_UCHAR: length=strlen(ptr=v.m_chVal); break; case DBVT_SHORT: @@ -489,10 +496,6 @@ private: connection.services->_throw(msg); } - bool _transcode_required(Connection& connection){ - return (connection.client_charset && strcmp(connection.client_charset, connection.services->request_charset())!=0); - } - }; extern "C" SQL_Driver *SQL_DRIVER_CREATE() {