|
|
| version 1.13.6.2, 2003/07/22 11:23:09 | version 1.23, 2004/06/23 07:32:07 |
|---|---|
| Line 18 static const char *RCSId="$Id$"; | Line 18 static const char *RCSId="$Id$"; |
| #include "pa_sql_driver.h" | #include "pa_sql_driver.h" |
| #define WINVER 0x0400 | |
| #include <AFXDB.H> | #include <AFXDB.H> |
| // defines | // defines |
| Line 55 static char *lsplit(char *string, char d | Line 56 static char *lsplit(char *string, char d |
| return 0; | 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 | ODBC server driver |
| */ | */ |
| Line 68 public: | Line 81 public: |
| int api_version() { return SQL_DRIVER_API_VERSION; } | int api_version() { return SQL_DRIVER_API_VERSION; } |
| const char *initialize(char *dlopen_file_spec) { return 0; } | const char *initialize(char *dlopen_file_spec) { return 0; } |
| /** connect | /** connect |
| @param used_only_in_connect_url | @param url |
| format: @b DSN=dsn;UID=user;PWD=password (ODBC connect string) | format: @b DSN=dsn;UID=user;PWD=password (ODBC connect string) |
| WARNING: must be used only to connect, for buffer doesn't live long | WARNING: must be used only to connect, for buffer doesn't live long |
| */ | */ |
| void connect( | void connect( |
| char *used_only_in_connect_url, | char *url, |
| SQL_Driver_services& services, | SQL_Driver_services& services, |
| void **connection ///< output: CDatabase * | void **connection_ref ///< output: Connection* |
| ) { | ) { |
| // _asm int 3; | Connection& connection=*(Connection *)services.malloc(sizeof(Connection)); |
| CDatabase *db; | *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 { | TRY { |
| db=new CDatabase(); | connection.db=new CDatabase(); |
| db->OpenEx(used_only_in_connect_url, CDatabase::noOdbcDialog); | connection.db->OpenEx(url, CDatabase::noOdbcDialog); |
| db->BeginTrans(); | connection.db->BeginTrans(); |
| } | } |
| CATCH_ALL (e) { | CATCH_ALL (e) { |
| _throw(services, e); | _throw(services, e); |
| db=0; // calm, compiler | |
| } | } |
| END_CATCH_ALL | END_CATCH_ALL |
| *(CDatabase **)connection=db; | |
| } | } |
| void disconnect(void *connection) { | void disconnect(void *aconnection) { |
| CDatabase *db=static_cast<CDatabase *>(connection); | Connection& connection=*static_cast<Connection*>(aconnection); |
| TRY | TRY |
| delete db; | delete connection.db; |
| connection.db=0; | |
| CATCH_ALL (e) { | CATCH_ALL (e) { |
| // nothing | // nothing |
| } | } |
| END_CATCH_ALL | END_CATCH_ALL |
| } | } |
| void commit(SQL_Driver_services& services, void *connection) { | void commit(void *aconnection) { |
| CDatabase *db=static_cast<CDatabase *>(connection); | Connection& connection=*static_cast<Connection*>(aconnection); |
| TRY | TRY |
| db->CommitTrans(); | connection.db->CommitTrans(); |
| db->BeginTrans(); | connection.db->BeginTrans(); |
| CATCH_ALL (e) { | CATCH_ALL (e) { |
| _throw(services, e); | _throw(*connection.services, e); |
| } | } |
| END_CATCH_ALL | END_CATCH_ALL |
| } | } |
| void rollback(SQL_Driver_services& services, void *connection) { | void rollback(void *aconnection) { |
| CDatabase *db=static_cast<CDatabase *>(connection); | Connection& connection=*static_cast<Connection*>(aconnection); |
| TRY | TRY |
| db->Rollback(); | connection.db->Rollback(); |
| db->BeginTrans(); | connection.db->BeginTrans(); |
| CATCH_ALL (e) { | CATCH_ALL (e) { |
| _throw(services, e); | _throw(*connection.services, e); |
| } | } |
| END_CATCH_ALL | END_CATCH_ALL |
| } | } |
| bool ping(SQL_Driver_services&, void *connection) { | bool ping(void *connection) { |
| return true; | return true; |
| } | } |
| const char* quote( | const char* quote(void *aconnection, const char *from, unsigned int length) { |
| SQL_Driver_services& services, void *connection, | Connection& connection=*static_cast<Connection*>(aconnection); |
| const char *from, unsigned int length) { | char *result=(char*)connection.services->malloc_atomic(length*2+1); |
| char *result=(char*)services.malloc_atomic(length*2+1); | |
| char *to=result; | char *to=result; |
| while(length--) { | while(length--) { |
| if(*from=='\'') { // ' -> '' | if(*from=='\'') { // ' -> '' |
| *to++='\''; result++; | *to++='\''; |
| } | } |
| *to++=*from++; | *to++=*from++; |
| } | } |
| *to=0; | *to=0; |
| return result; | return result; |
| } | } |
| void query( | void query(void *aconnection, |
| SQL_Driver_services& services, void *connection, | const char *statement, |
| const char *statement, unsigned long offset, unsigned long limit, | size_t placeholders_count, Placeholder* placeholders, |
| unsigned long offset, unsigned long limit, | |
| SQL_Driver_query_event_handlers& handlers) { | SQL_Driver_query_event_handlers& handlers) { |
| CDatabase *db=static_cast<CDatabase *>(connection); | Connection& connection=*static_cast<Connection*>(aconnection); |
| CDatabase *db=connection.db; | |
| SQL_Driver_services& services=*connection.services; | |
| if(placeholders_count>0) | |
| services._throw("bind variables not supported (yet)"); | |
| // 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)) | while(isspace(*statement)) |
| statement++; | statement++; |
| Line 199 public: | Line 242 public: |
| CODBCFieldInfo fieldinfo; | CODBCFieldInfo fieldinfo; |
| rs.GetODBCFieldInfo(i, fieldinfo); | rs.GetODBCFieldInfo(i, fieldinfo); |
| column_types[i]=fieldinfo.m_nSQLType; | column_types[i]=fieldinfo.m_nSQLType; |
| size_t size=fieldinfo.m_strName.GetLength(); | size_t length=fieldinfo.m_strName.GetLength(); |
| char *str=0; | char *str=0; |
| if(size) { | if(length) { |
| str=(char*)services.malloc_atomic(size+1); | str=(char*)services.malloc_atomic(length+1); |
| memcpy(str, (char *)LPCTSTR(fieldinfo.m_strName), size+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)); | CHECK(handlers.before_rows(sql_error)); |
| Line 217 public: | Line 268 public: |
| if(row>=offset) { | if(row>=offset) { |
| CHECK(handlers.add_row(sql_error)); | CHECK(handlers.add_row(sql_error)); |
| for(int i=0; i<column_count; i++) { | for(int i=0; i<column_count; i++) { |
| size_t size; | size_t length; |
| char* str; | char* str; |
| switch(column_types[i]) { | switch(column_types[i]) { |
| //case xBOOL: | //case xBOOL: |
| Line 227 public: | Line 278 public: |
| case SQL_VARBINARY: | case SQL_VARBINARY: |
| case SQL_LONGVARBINARY: | case SQL_LONGVARBINARY: |
| case SQL_SMALLDATETIME: | case SQL_SMALLDATETIME: |
| //case SQL_NVARCHAR: // mfc 7.1 has errors with nvarchar(length): SQLGetData in dbcore.cpp truncates last byte for unknown reason. could be fixed by uncommenting this and handing DBVT_WSTRING inside, but it's UNICODE | |
| rs.GetFieldValue(i, v); | rs.GetFieldValue(i, v); |
| getFromDBVariant(services, v, str, size); | getFromDBVariant(services, v, str, length); |
| break; | break; |
| default: | default: |
| rs.GetFieldValue(i, s); | rs.GetFieldValue(i, s); |
| getFromString(services, s, str, size); | getFromString(services, s, str, length); |
| break; | break; |
| } | } |
| CHECK(handlers.add_row_cell(sql_error, str, size)); | |
| // 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_row_cell(sql_error, str, length)); | |
| } | } |
| } | } |
| rs.MoveNext(); row++; | rs.MoveNext(); row++; |
| Line 250 public: | Line 310 public: |
| } END_CATCH_ALL | } END_CATCH_ALL |
| } | } |
| void getFromDBVariant(SQL_Driver_services& services, CDBVariant& v, char*& str, size_t& size) { | void getFromDBVariant(SQL_Driver_services& services, CDBVariant& v, char*& str, size_t& length) { |
| switch(v.m_dwType) { | switch(v.m_dwType) { |
| case DBVT_BINARY: /* << would cause problems with current String implementation | case DBVT_BINARY: /* << would cause problems with current String implementation |
| now falling into NULL case, effectively ignoring such columns [not failing] | now falling into NULL case, effectively ignoring such columns [not failing] |
| { | { |
| if(size=v.m_pbinary->m_dwDataLength) { | if(length=v.m_pbinary->m_dwDataLength) { |
| str=services.malloc_atomic(size+1); | str=services.malloc_atomic(length+1); |
| memcpy(ptr, ::GlobalLock(v.m_pbinary->m_hData), size); | memcpy(ptr, ::GlobalLock(v.m_pbinary->m_hData), length); |
| ::GlobalUnlock(v.m_pbinary->m_hData); | ::GlobalUnlock(v.m_pbinary->m_hData); |
| } else | } else |
| str=0; | str=0; |
| Line 265 public: | Line 325 public: |
| }*/ | }*/ |
| case DBVT_NULL: // No union member is valid for access. | case DBVT_NULL: // No union member is valid for access. |
| str=0; | str=0; |
| size=0; | length=0; |
| break; | break; |
| /* case DBVT_BOOL: | /* case DBVT_BOOL: |
| ptr=v.m_boolVal?"1":"0"; | ptr=v.m_boolVal?"1":"0"; |
| size=1; | length=1; |
| break;*/ | break;*/ |
| /* case DBVT_UCHAR: | /* case DBVT_UCHAR: |
| size=strlen(ptr=v.m_chVal); | length=strlen(ptr=v.m_chVal); |
| break; | break; |
| case DBVT_SHORT: | case DBVT_SHORT: |
| char buf[MAX_NUMBER]; | char buf[MAX_NUMBER]; |
| size=snprintf(HEAPIZE buf, "%d", v.m_iVal); | length=snprintf(HEAPIZE buf, "%d", v.m_iVal); |
| break;*/ | break;*/ |
| /* case DBVT_LONG: | /* case DBVT_LONG: |
| { | { |
| char local_buf[MAX_NUMBER]; | char local_buf[MAX_NUMBER]; |
| size=snprintf(local_buf, MAX_NUMBER, "%ld", v.m_lVal); | length=snprintf(local_buf, MAX_NUMBER, "%ld", v.m_lVal); |
| ptr=services.malloc_atomic(size); | ptr=services.malloc_atomic(length); |
| memcpy(ptr, local_buf, size); | memcpy(ptr, local_buf, length); |
| break; | break; |
| }*/ | }*/ |
| /*case DBVT_SINGLE: | /*case DBVT_SINGLE: |
| m_fltVal | m_fltVal |
| break; | break; |
| case DBVT_DOUBLE m_dblVal | case DBVT_DOUBLE m_dblVal |
| case DBVT_STRING m_pstring */ | case DBVT_STRING m_pstring */ |
| case DBVT_DATE: | case DBVT_DATE: |
| { | { |
| char local_buf[MAX_STRING]; | char local_buf[MAX_STRING]; |
| size=snprintf(local_buf, MAX_STRING, | length=snprintf(local_buf, MAX_STRING, |
| "%04d-%02d-%02d %02d:%02d:%02d.%03d", | "%04d-%02d-%02d %02d:%02d:%02d.%03d", |
| v.m_pdate->year, | v.m_pdate->year, |
| v.m_pdate->month, | v.m_pdate->month, |
| Line 303 public: | Line 363 public: |
| v.m_pdate->minute, | v.m_pdate->minute, |
| v.m_pdate->second, | v.m_pdate->second, |
| v.m_pdate->fraction); | v.m_pdate->fraction); |
| str=(char*)services.malloc_atomic(size+1); | str=(char*)services.malloc_atomic(length+1); |
| memcpy(str, local_buf, size+1); | memcpy(str, local_buf, length+1); |
| break; | break; |
| } | } |
| default: | default: |
| Line 315 public: | Line 375 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()) { | if(s.IsEmpty()) { |
| astr=0; | astr=0; |
| size=0; | length=0; |
| } else { | } else { |
| const char *cstr=LPCTSTR(s); | const char *cstr=LPCTSTR(s); |
| size=strlen(cstr); //string.GetLength() works wrong with non-string types: | length=strlen(cstr); //string.GetLength() works wrong with non-string types: |
| astr=(char*)services.malloc_atomic(size+1); | astr=(char*)services.malloc_atomic(length+1); |
| memcpy(astr, cstr, size+1); | memcpy(astr, cstr, length+1); |
| } | } |
| } | } |