--- sql/odbc/parser3odbc.C 2003/11/10 08:44:53 1.16 +++ sql/odbc/parser3odbc.C 2004/05/25 07:07:28 1.22 @@ -5,7 +5,7 @@ Author: Alexandr Petrosian (http://paf.design.ru) */ -static const char *RCSId="$Id: parser3odbc.C,v 1.16 2003/11/10 08:44:53 paf Exp $"; +static const char *RCSId="$Id: parser3odbc.C,v 1.22 2004/05/25 07:07:28 paf Exp $"; #ifndef _MSC_VER # error compile ISAPI module with MSVC [no urge for now to make it autoconf-ed (PAF)] @@ -56,6 +56,18 @@ static char *lsplit(char *string, char d return 0; } +static void toupper_str(char *out, const char *in, size_t size) { + while(size--) + *out++=(char)toupper(*in++); +} + +struct Connection { + SQL_Driver_services* services; + + CDatabase* db; + const char* cstrClientCharset; +}; + /** ODBC server driver */ @@ -69,68 +81,83 @@ public: int api_version() { return SQL_DRIVER_API_VERSION; } const char *initialize(char *dlopen_file_spec) { return 0; } /** connect - @param used_only_in_connect_url + @param url format: @b DSN=dsn;UID=user;PWD=password (ODBC connect string) WARNING: must be used only to connect, for buffer doesn't live long */ void connect( - char *used_only_in_connect_url, + char *url, SQL_Driver_services& services, - void **connection ///< output: CDatabase * + void **connection_ref ///< output: Connection* ) { - // _asm int 3; - CDatabase *db; + Connection& connection=*(Connection *)services.malloc(sizeof(Connection)); + *connection_ref=&connection; + connection.services=&services; + connection.cstrClientCharset=0; + + 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+strlen(url); + + if(size_t ClientCharsetLength=value_end-value_start) { + char* cstrClientCharset=(char*)services.malloc_atomic(ClientCharsetLength+1); + toupper_str(cstrClientCharset, value_start, ClientCharsetLength); + cstrClientCharset[ClientCharsetLength]=0; + + connection.cstrClientCharset=cstrClientCharset; + } + } + TRY { - db=new CDatabase(); - db->OpenEx(used_only_in_connect_url, CDatabase::noOdbcDialog); - db->BeginTrans(); + connection.db=new CDatabase(); + connection.db->OpenEx(url, CDatabase::noOdbcDialog); + connection.db->BeginTrans(); } CATCH_ALL (e) { _throw(services, e); - db=0; // calm, compiler } END_CATCH_ALL - - *(CDatabase **)connection=db; } - void disconnect(void *connection) { - CDatabase *db=static_cast(connection); + void disconnect(void *aconnection) { + Connection& connection=*static_cast(aconnection); TRY - delete db; + delete connection.db; + connection.db=0; CATCH_ALL (e) { // nothing } END_CATCH_ALL } - void commit(SQL_Driver_services& services, void *connection) { - CDatabase *db=static_cast(connection); + void commit(void *aconnection) { + Connection& connection=*static_cast(aconnection); TRY - db->CommitTrans(); - db->BeginTrans(); + connection.db->CommitTrans(); + connection.db->BeginTrans(); CATCH_ALL (e) { - _throw(services, e); + _throw(*connection.services, e); } END_CATCH_ALL } - void rollback(SQL_Driver_services& services, void *connection) { - CDatabase *db=static_cast(connection); + void rollback(void *aconnection) { + Connection& connection=*static_cast(aconnection); TRY - db->Rollback(); - db->BeginTrans(); + connection.db->Rollback(); + connection.db->BeginTrans(); CATCH_ALL (e) { - _throw(services, e); + _throw(*connection.services, e); } END_CATCH_ALL } - bool ping(SQL_Driver_services&, void *connection) { + bool ping(void *connection) { return true; } - const char* quote( - SQL_Driver_services& services, void *connection, - const char *from, unsigned int length) { - char *result=(char*)services.malloc_atomic(length*2+1); + const char* quote(void *aconnection, const char *from, unsigned int length) { + Connection& connection=*static_cast(aconnection); + char *result=(char*)connection.services->malloc_atomic(length*2+1); char *to=result; while(length--) { if(*from=='\'') { // ' -> '' @@ -141,12 +168,22 @@ public: *to=0; return result; } - void query( - SQL_Driver_services& services, void *connection, + void query(void *aconnection, const char *statement, unsigned long offset, unsigned long limit, SQL_Driver_query_event_handlers& handlers) { - CDatabase *db=static_cast(connection); + Connection& connection=*static_cast(aconnection); + CDatabase *db=connection.db; + SQL_Driver_services& services=*connection.services; + + // transcode from $request:charset to connect-string?client_charset + if(const char* cstrClientCharset=connection.cstrClientCharset) { + size_t transcoded_statement_size; + services.transcode(statement, strlen(statement), + statement, transcoded_statement_size, + services.request_charset(), + cstrClientCharset); + } while(isspace(*statement)) statement++; @@ -200,13 +237,21 @@ public: CODBCFieldInfo fieldinfo; rs.GetODBCFieldInfo(i, fieldinfo); column_types[i]=fieldinfo.m_nSQLType; - size_t size=fieldinfo.m_strName.GetLength(); + size_t length=fieldinfo.m_strName.GetLength(); char *str=0; - if(size) { - str=(char*)services.malloc_atomic(size+1); - memcpy(str, (char *)LPCTSTR(fieldinfo.m_strName), size+1); + if(length) { + str=(char*)services.malloc_atomic(length+1); + memcpy(str, (char *)LPCTSTR(fieldinfo.m_strName), length+1); + + // transcode to $request:charset from connect-string?client_charset + if(const char* cstrClientCharset=connection.cstrClientCharset) { + services.transcode(str, length, + str, length, + cstrClientCharset, + services.request_charset()); + } } - CHECK(handlers.add_column(sql_error, str, size)); + CHECK(handlers.add_column(sql_error, str, length)); } CHECK(handlers.before_rows(sql_error)); @@ -218,7 +263,7 @@ public: if(row>=offset) { CHECK(handlers.add_row(sql_error)); for(int i=0; im_dwDataLength) { - str=services.malloc_atomic(size+1); - memcpy(ptr, ::GlobalLock(v.m_pbinary->m_hData), size); + if(length=v.m_pbinary->m_dwDataLength) { + str=services.malloc_atomic(length+1); + memcpy(ptr, ::GlobalLock(v.m_pbinary->m_hData), length); ::GlobalUnlock(v.m_pbinary->m_hData); } else str=0; @@ -266,36 +320,36 @@ public: }*/ case DBVT_NULL: // No union member is valid for access. str=0; - size=0; + length=0; break; /* case DBVT_BOOL: ptr=v.m_boolVal?"1":"0"; - size=1; + length=1; break;*/ /* case DBVT_UCHAR: - size=strlen(ptr=v.m_chVal); + length=strlen(ptr=v.m_chVal); break; case DBVT_SHORT: char buf[MAX_NUMBER]; - size=snprintf(HEAPIZE buf, "%d", v.m_iVal); + length=snprintf(HEAPIZE buf, "%d", v.m_iVal); break;*/ /* case DBVT_LONG: { char local_buf[MAX_NUMBER]; - size=snprintf(local_buf, MAX_NUMBER, "%ld", v.m_lVal); - ptr=services.malloc_atomic(size); - memcpy(ptr, local_buf, size); + length=snprintf(local_buf, MAX_NUMBER, "%ld", v.m_lVal); + ptr=services.malloc_atomic(length); + memcpy(ptr, local_buf, length); break; }*/ /*case DBVT_SINGLE: m_fltVal break; case DBVT_DOUBLE m_dblVal - case DBVT_STRING m_pstring */ + case DBVT_STRING m_pstring */ case DBVT_DATE: { char local_buf[MAX_STRING]; - size=snprintf(local_buf, MAX_STRING, + length=snprintf(local_buf, MAX_STRING, "%04d-%02d-%02d %02d:%02d:%02d.%03d", v.m_pdate->year, v.m_pdate->month, @@ -304,8 +358,8 @@ public: v.m_pdate->minute, v.m_pdate->second, v.m_pdate->fraction); - str=(char*)services.malloc_atomic(size+1); - memcpy(str, local_buf, size+1); + str=(char*)services.malloc_atomic(length+1); + memcpy(str, local_buf, length+1); break; } default: @@ -316,15 +370,15 @@ public: } } - void getFromString(SQL_Driver_services& services, CString& s, char*& astr, size_t& size) { + void getFromString(SQL_Driver_services& services, CString& s, char*& astr, size_t& length) { if(s.IsEmpty()) { astr=0; - size=0; + length=0; } else { const char *cstr=LPCTSTR(s); - size=strlen(cstr); //string.GetLength() works wrong with non-string types: - astr=(char*)services.malloc_atomic(size+1); - memcpy(astr, cstr, size+1); + length=strlen(cstr); //string.GetLength() works wrong with non-string types: + astr=(char*)services.malloc_atomic(length+1); + memcpy(astr, cstr, length+1); } }