Annotation of sql/pgsql/parser3pgsql.C, revision 1.34
1.34 ! misha 1: /** @file
! 2: Parser PgSQL driver.
! 3:
! 4: Copyright(c) 2001, 2003 ArtLebedev Group (http://www.artlebedev.com)
! 5:
! 6: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
! 7:
! 8: 2007.10.25 using PgSQL 8.1.5
! 9: */
! 10: static const char *RCSId="$Id: parser3pgsql.C,v 1.33 2008-12-21 00:14:25 misha Exp $";
! 11:
! 12: #include "config_includes.h"
! 13:
! 14: #include "pa_sql_driver.h"
! 15:
! 16: #include <libpq-fe.h>
! 17: #include <libpq/libpq-fs.h>
! 18:
! 19: // from catalog/pg_type.h
! 20: #define BOOLOID 16
! 21: #define INT8OID 20
! 22: #define INT2OID 21
! 23: #define INT4OID 23
! 24: #define OIDOID 26
! 25: #define FLOAT4OID 700
! 26: #define FLOAT8OID 701
! 27: #define DATEOID 1082
! 28: #define TIMEOID 1083
! 29: #define TIMESTAMPOID 1114
! 30: #define TIMESTAMPTZOID 1184
! 31: #define TIMETZOID 1266
! 32: #define NUMERICOID 1700
! 33:
! 34: // LO_BUFSIZE from interfaces\libpq\fe-lobj.c = 8192 (0x2000)
! 35: // actually writing chunks of that size failed, reduced it twice
! 36: #define LO_BUFSIZE 0x1000
! 37:
! 38:
! 39: #include "ltdl.h"
! 40:
! 41: #define MAX_COLS 500
! 42:
! 43: #define MAX_STRING 0x400
! 44: #define MAX_NUMBER 20
! 45:
! 46: #if _MSC_VER
! 47: # define snprintf _snprintf
! 48: # define strcasecmp _stricmp
! 49: #endif
! 50:
! 51: #ifndef max
! 52: inline int max(int a,int b){ return a>b?a:b; }
! 53: inline int min(int a,int b){ return a<b?a:b; }
! 54: #endif
! 55:
! 56: static char *lsplit(char *string, char delim){
! 57: if(string){
! 58: if(char *v=strchr(string, delim)){
! 59: *v=0;
! 60: return v+1;
! 61: }
! 62: }
! 63: return 0;
! 64: }
! 65:
! 66: static char *lsplit(char **string_ref, char delim){
! 67: char *result=*string_ref;
! 68: char *next=lsplit(*string_ref, delim);
! 69: *string_ref=next;
! 70: return result;
! 71: }
! 72:
! 73: static char* rsplit(char* string, char delim){
! 74: if(string){
! 75: if(char* v=strrchr(string, delim)){
! 76: *v=0;
! 77: return v+1;
! 78: }
! 79: }
! 80: return NULL;
! 81: }
! 82:
! 83: static void toupper_str(char *out, const char *in, size_t size){
! 84: while(size--)
! 85: *out++=(char)toupper(*in++);
! 86: }
! 87:
! 88: struct Connection {
! 89: SQL_Driver_services* services;
! 90:
! 91: PGconn *conn;
! 92: const char* client_charset;
! 93: bool autocommit;
! 94: bool without_default_transactions;
! 95: };
! 96:
! 97: /**
! 98: PgSQL server driver
! 99: */
! 100: class PgSQL_Driver : public SQL_Driver {
! 101: public:
! 102:
! 103: PgSQL_Driver() : SQL_Driver() {
! 104: }
! 105:
! 106: /// get api version
! 107: int api_version(){ return SQL_DRIVER_API_VERSION; }
! 108:
! 109: /// initialize driver by loading sql dynamic link library
! 110: const char *initialize(char *dlopen_file_spec){
! 111: return dlopen_file_spec?
! 112: dlink(dlopen_file_spec):"client library column is empty";
! 113: }
! 114:
! 115: #define throwPQerror connection.services->_throw(PQerrorMessage(connection.conn))
! 116: #define PQclear_throw(msg) { \
! 117: PQclear(res); \
! 118: connection.services->_throw(msg); \
! 119: }
! 120: #define PQclear_throwPQerror PQclear_throw(PQerrorMessage(connection.conn))
! 121:
! 122: /** connect
! 123: @param url
! 124: format: @b user:pass@host[:port]|[local]/database?
! 125: ClientCharset=charset& // transcode by parser
! 126: charset=value& // transcode by server with 'SET CLIENT_ENCODING=value'
! 127: datestyle=value& // 'SET DATESTYLE=value' available values are: ISO|SQL|Postgres|European|US|German [default=ISO]
! 128: autocommit=1& // each transaction is commited automatically (default)
! 129: WithoutDefaultTransaction=0 // 1 -- disable any BEGIN TRAN/COMMIT/ROLLBACK [can NOT be used with autocommit option]
! 130: */
! 131: void connect(
! 132: char* url,
! 133: SQL_Driver_services& services,
! 134: void** connection_ref ///< output: Connection*
! 135: ){
! 136: char* user=url;
! 137: char* host=rsplit(user, '@');
! 138: char* db=lsplit(host, '/');
! 139: char* pwd=lsplit(user, ':');
! 140: char* port=lsplit(host, ':');
! 141:
! 142: char *options=lsplit(db, '?');
! 143:
! 144: char* charset=0;
! 145: char* datestyle=0;
! 146:
! 147: Connection& connection=*(Connection *)services.malloc(sizeof(Connection));
! 148: *connection_ref=&connection;
! 149: connection.services=&services;
! 150: connection.client_charset=0;
! 151: connection.autocommit=true;
! 152: connection.without_default_transactions=false;
! 153:
! 154: connection.conn=PQsetdbLogin(
! 155: (host&&strcasecmp(host, "local")==0)?NULL/* local Unix domain socket */:host, port,
! 156: NULL, NULL, db, user, pwd);
! 157:
! 158: if(!connection.conn)
! 159: services._throw("PQsetdbLogin failed");
! 160:
! 161: if(PQstatus(connection.conn)!=CONNECTION_OK)
! 162: throwPQerror;
! 163:
! 164: while(options){
! 165: if(char *key=lsplit(&options, '&')){
! 166: if(*key){
! 167: if(char *value=lsplit(key, '=')){
! 168: if(strcmp(key, "ClientCharset")==0){
! 169: toupper_str(value, value, strlen(value));
! 170: connection.client_charset=value;
! 171: } else if(strcasecmp(key, "charset")==0){
! 172: charset=value;
! 173: } else if(strcasecmp(key, "datestyle")==0){
! 174: datestyle=value;
! 175: } else if(strcasecmp(key, "autocommit")==0){
! 176: if(connection.without_default_transactions)
! 177: services._throw("options WithoutDefaultTransaction and autocommit can't be used together");
! 178: if(atoi(value)==0)
! 179: connection.autocommit=false;
! 180: } else if(strcmp(key, "WithoutDefaultTransaction")==0){
! 181: if(!connection.autocommit)
! 182: services._throw("options WithoutDefaultTransaction and autocommit can't be used together");
! 183: if(atoi(value)==1){
! 184: connection.without_default_transactions=true;
! 185: connection.autocommit=false;
! 186: }
! 187: } else
! 188: services._throw("unknown connect option" /*key*/);
! 189: } else
! 190: services._throw("connect option without =value" /*key*/);
! 191: }
! 192: }
! 193: }
! 194:
! 195: if(charset){
! 196: char statement[MAX_STRING]="SET CLIENT_ENCODING=";
! 197: strncat(statement, charset, MAX_STRING);
! 198:
! 199: _execute_cmd(connection, statement);
! 200: }
! 201:
! 202: if(datestyle){
! 203: char statement[MAX_STRING]="SET DATESTYLE=";
! 204: strncat(statement, datestyle, MAX_STRING);
! 205:
! 206: _execute_cmd(connection, statement);
! 207: }
! 208:
! 209: _transaction_begin(connection);
! 210: }
! 211:
! 212: void disconnect(void *aconnection){
! 213: Connection& connection=*static_cast<Connection*>(aconnection);
! 214: PQfinish(connection.conn);
! 215: connection.conn=0;
! 216: }
! 217:
! 218: void commit(void *aconnection){
! 219: Connection& connection=*static_cast<Connection*>(aconnection);
! 220: _transaction_commit(connection);
! 221: _transaction_begin(connection);
! 222: }
! 223:
! 224: void rollback(void *aconnection){
! 225: Connection& connection=*static_cast<Connection*>(aconnection);
! 226: _transaction_rollback(connection);
! 227: _transaction_begin(connection);
! 228: }
! 229:
! 230: bool ping(void *aconnection) {
! 231: Connection& connection=*static_cast<Connection*>(aconnection);
! 232: return PQstatus(connection.conn)==CONNECTION_OK;
! 233: }
! 234:
! 235: const char* quote(void *aconnection, const char *from, unsigned int length){
! 236: Connection& connection=*static_cast<Connection*>(aconnection);
! 237:
! 238: char *result=(char*)connection.services->malloc_atomic(length*2+1);
! 239: int err=0;
! 240: PQescapeStringConn(connection.conn, result, from, length, &err);
! 241: return result;
! 242: }
! 243:
! 244: void query(void *aconnection,
! 245: const char *astatement,
! 246: size_t placeholders_count, Placeholder* placeholders,
! 247: unsigned long offset, unsigned long limit,
! 248: SQL_Driver_query_event_handlers& handlers
! 249: ){
! 250: Connection& connection=*static_cast<Connection*>(aconnection);
! 251: SQL_Driver_services& services=*connection.services;
! 252: PGconn *conn=connection.conn;
! 253:
! 254: const char* client_charset=connection.client_charset;
! 255: const char* request_charset=services.request_charset();
! 256: bool transcode_needed=client_charset && strcmp(client_charset, request_charset)!=0;
! 257:
! 258: const char** paramValues;
! 259: if(placeholders_count>0){
! 260: int binds_size=sizeof(char)*placeholders_count;
! 261: paramValues = static_cast<const char**>(services.malloc_atomic(binds_size));
! 262: _bind_parameters(placeholders_count, placeholders, paramValues, connection, transcode_needed);
! 263: }
! 264:
! 265: if(transcode_needed){
! 266: // transcode query from $request:charset to ?ClientCharset
! 267: size_t length=strlen(astatement);
! 268: services.transcode(astatement, length,
! 269: astatement, length,
! 270: request_charset,
! 271: client_charset);
! 272: }
! 273:
! 274: const char *statement=_preprocess_statement(connection, astatement, offset, limit);
! 275: // error after prepare?
! 276:
! 277: PGresult *res;
! 278: if(placeholders_count>0){
! 279: res=PQexecParams(conn, statement, placeholders_count, NULL, paramValues, NULL, NULL, 0);
! 280: } else {
! 281: res=PQexec(conn, statement);
! 282: }
! 283: if(!res)
! 284: throwPQerror;
! 285:
! 286: bool failed=false;
! 287: SQL_Error sql_error;
! 288:
! 289: switch(PQresultStatus(res)) {
! 290: case PGRES_EMPTY_QUERY:
! 291: PQclear_throw("no query");
! 292: break;
! 293: case PGRES_COMMAND_OK: // empty result: insert|delete|update|...
! 294: PQclear(res);
! 295: if(connection.autocommit)
! 296: commit(aconnection);
! 297: return;
! 298: case PGRES_TUPLES_OK:
! 299: break;
! 300: default:
! 301: PQclear_throwPQerror;
! 302: break;
! 303: }
! 304:
! 305: #define CHECK(afailed) \
! 306: if(afailed) { \
! 307: failed=true; \
! 308: goto cleanup; \
! 309: }
! 310:
! 311: int column_count=PQnfields(res);
! 312: if(!column_count)
! 313: PQclear_throw("result contains no columns");
! 314:
! 315: if(column_count>MAX_COLS)
! 316: column_count=MAX_COLS;
! 317:
! 318: unsigned int column_types[MAX_COLS];
! 319: bool transcode_column[MAX_COLS];
! 320:
! 321: for(int i=0; i<column_count; i++){
! 322: column_types[i]=PQftype(res, i);
! 323: switch(column_types[i]){
! 324: case BOOLOID:
! 325: case INT8OID:
! 326: case INT2OID:
! 327: case INT4OID:
! 328: case FLOAT4OID:
! 329: case FLOAT8OID:
! 330: case DATEOID:
! 331: case TIMEOID:
! 332: case TIMESTAMPOID:
! 333: case TIMESTAMPTZOID:
! 334: case TIMETZOID:
! 335: case NUMERICOID:
! 336: transcode_column[i]=false;
! 337: break;
! 338: default:
! 339: transcode_column[i]=transcode_needed;
! 340: break;
! 341: }
! 342: char *name=PQfname(res, i);
! 343: size_t length=strlen(name);
! 344: char* strm=(char*)services.malloc(length+1);
! 345: memcpy(strm, name, length+1);
! 346: const char* str=strm;
! 347:
! 348: if(transcode_needed) // transcode column name from ?ClientCharset to $request:charset
! 349: services.transcode(str, length,
! 350: str, length,
! 351: client_charset,
! 352: request_charset);
! 353:
! 354: CHECK(handlers.add_column(sql_error, str, length));
! 355: }
! 356:
! 357: CHECK(handlers.before_rows(sql_error));
! 358:
! 359: if(unsigned long row_count=(unsigned long)PQntuples(res))
! 360: for(unsigned long r=0; r<row_count; r++) {
! 361: CHECK(handlers.add_row(sql_error));
! 362: for(int i=0; i<column_count; i++){
! 363: const char *cell=PQgetvalue(res, r, i);
! 364: size_t length;
! 365: const char* str;
! 366:
! 367: switch(column_types[i]){
! 368: case OIDOID:
! 369: {
! 370: char *error_pos=0;
! 371: Oid oid=cell?atoi(cell):0;
! 372: int fd=lo_open(conn, oid, INV_READ);
! 373: if(fd>=0){
! 374: // seek to end
! 375: if(lo_lseek(conn, fd, 0, SEEK_END)<0)
! 376: PQclear_throwPQerror;
! 377: // get length
! 378: int size_tell=lo_tell(conn, fd);
! 379: if(size_tell<0)
! 380: PQclear_throwPQerror;
! 381: // seek to begin
! 382: if(lo_lseek(conn, fd, 0, SEEK_SET)<0)
! 383: PQclear_throwPQerror;
! 384: length=(size_t)size_tell;
! 385: if(length){
! 386: // read
! 387: char* strm=(char*)services.malloc(length+1);
! 388: if(!lo_read_ex(conn, fd, strm, size_tell))
! 389: PQclear_throw("lo_read can not read all bytes of object");
! 390: strm[length]=0;
! 391: str=strm;
! 392: } else
! 393: str=0;
! 394: if(lo_close(conn, fd)<0)
! 395: PQclear_throwPQerror;
! 396: } else
! 397: PQclear_throwPQerror;
! 398: }
! 399: default:
! 400: // normal column, read it normally
! 401: length=(size_t)PQgetlength(res, r, i);
! 402: if(length){
! 403: char* strm=(char*)services.malloc(length+1);
! 404: memcpy(strm, cell, length+1);
! 405: str=strm;
! 406: } else
! 407: str=0;
! 408: }
! 409:
! 410: if(str && length && transcode_column[i]){
! 411: // transcode cell value from ?ClientCharset to $request:charset
! 412: services.transcode(str, length,
! 413: str, length,
! 414: client_charset,
! 415: request_charset);
! 416: }
! 417:
! 418: CHECK(handlers.add_row_cell(sql_error, str, length));
! 419: }
! 420: }
! 421: cleanup:
! 422: PQclear(res);
! 423: if(failed)
! 424: services._throw(sql_error);
! 425:
! 426: if(connection.autocommit)
! 427: commit(aconnection);
! 428: }
! 429:
! 430: private:
! 431: void _bind_parameters(
! 432: size_t placeholders_count,
! 433: Placeholder* placeholders,
! 434: const char** paramValues,
! 435: Connection& connection,
! 436: bool transcode_needed
! 437: ){
! 438: for(size_t i=0; i<placeholders_count; i++){
! 439: Placeholder& ph=placeholders[i];
! 440: if(transcode_needed){
! 441: size_t name_length;
! 442: connection.services->transcode(ph.name, strlen(ph.name),
! 443: ph.name, name_length,
! 444: connection.services->request_charset(),
! 445: connection.client_charset);
! 446:
! 447: if(ph.value) {
! 448: size_t value_length;
! 449: connection.services->transcode(ph.value, strlen(ph.value),
! 450: ph.value, value_length,
! 451: connection.services->request_charset(),
! 452: connection.client_charset);
! 453: }
! 454: }
! 455: int name_numner=atoi(ph.name);
! 456: if(name_numner <= 0 || (size_t)name_numner > placeholders_count)
! 457: connection.services->_throw("bad bind parameter key");
! 458:
! 459: paramValues[name_numner-1]=ph.value;
! 460: }
! 461: }
! 462:
! 463:
! 464: void _transaction_begin(Connection& connection){
! 465: _execute_transactions_cmd(connection, "BEGIN");
! 466: }
! 467:
! 468: void _transaction_commit(Connection& connection){
! 469: _execute_transactions_cmd(connection, "COMMIT");
! 470: }
! 471:
! 472: void _transaction_rollback(Connection& connection){
! 473: _execute_transactions_cmd(connection, "ROLLBACK");
! 474: }
! 475:
! 476: void _execute_transactions_cmd(const Connection& connection, const char *query){
! 477: if(!connection.without_default_transactions) // with option ?WithoutDefaultTransaction=1 user must execute BEGIN/COMMIT/ROLLBACK by himself
! 478: _execute_cmd(connection, query);
! 479: }
! 480:
! 481: // executes a query and throw away the result.
! 482: void _execute_cmd(const Connection& connection, const char *query){
! 483: if(PGresult *res=PQexec(connection.conn, query))
! 484: PQclear(res); // throw away the result [don't need but must call]
! 485: else
! 486: throwPQerror;
! 487: }
! 488:
! 489: const char *_preprocess_statement(
! 490: Connection& connection,
! 491: const char *astatement,
! 492: unsigned long offset,
! 493: unsigned long limit
! 494: ){
! 495: PGconn *conn=connection.conn;
! 496:
! 497: size_t statement_size=strlen(astatement);
! 498:
! 499: char *result=(char *)connection.services->malloc(statement_size
! 500: +MAX_NUMBER*2+15 // limit # offset #
! 501: +MAX_STRING // in case of short 'strings'
! 502: +1);
! 503: // offset & limit -> suffixes
! 504: const char *o;
! 505: if(offset || limit!=SQL_NO_LIMIT){
! 506: char *cur=result;
! 507: memcpy(cur, astatement, statement_size); cur+=statement_size;
! 508: if(limit!=SQL_NO_LIMIT)
! 509: cur+=snprintf(cur, 7+MAX_NUMBER, " limit %u", limit);
! 510: if(offset)
! 511: cur+=snprintf(cur, 8+MAX_NUMBER, " offset %u", offset);
! 512: o=result;
! 513: } else
! 514: o=astatement;
! 515:
! 516: // /**xxx**/'literal' -> oid
! 517: char *n=result;
! 518: while(*o) {
! 519: if(
! 520: o[0]=='/' &&
! 521: o[1]=='*' &&
! 522: o[2]=='*') { // name start
! 523: const char* saved_o=o;
! 524: o+=3;
! 525: while(*o)
! 526: if(
! 527: o[0]=='*' &&
! 528: o[1]=='*' &&
! 529: o[2]=='/' &&
! 530: o[3]=='\'') { // name end
! 531: saved_o=0; // found, marking that
! 532: o+=4;
! 533: Oid oid=lo_creat(conn, INV_READ|INV_WRITE);
! 534: if(oid==InvalidOid)
! 535: throwPQerror;
! 536: int fd=lo_open(conn, oid, INV_WRITE);
! 537: if(fd>=0) {
! 538: const char *start=o;
! 539: bool escaped=false;
! 540: while(*o && !(o[0]=='\'' && o[1]!='\'' && !escaped)) {
! 541: escaped=*o=='\\' || (o[0]=='\'' && o[1]=='\'');
! 542: if(escaped) {
! 543: // write pending, skip "\" or "'"
! 544: if(!lo_write_ex(conn, fd, start, o-start))
! 545: connection.services->_throw("lo_write could not write all bytes of object (1)");
! 546: start=++o;
! 547: } else
! 548: o++;
! 549: }
! 550: if(!lo_write_ex(conn, fd, start, o-start))
! 551: connection.services->_throw("lo_write can not write all bytes of object (2)");
! 552: if(lo_close(conn, fd)<0)
! 553: throwPQerror;
! 554: } else
! 555: throwPQerror;
! 556: if(*o)
! 557: o++; // skip "'"
! 558:
! 559: n+=snprintf(n, MAX_NUMBER, "%u", oid);
! 560: break;
! 561: } else
! 562: o++; // /**skip**/'xxx'
! 563: if(saved_o) {
! 564: o=saved_o;
! 565: *n++=*o++;
! 566: }
! 567: } else
! 568: *n++=*o++;
! 569: }
! 570: *n=0;
! 571:
! 572: return result;
! 573: }
! 574:
! 575: private: // lo_read/write exchancements
! 576:
! 577: bool lo_read_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) {
! 578: return lo_rw_method (conn, fd, buf, len, lo_read);
! 579: }
! 580:
! 581: bool lo_write_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) {
! 582: return lo_rw_method (conn, fd, buf, len, lo_write);
! 583: }
! 584:
! 585: bool lo_rw_method(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len, int (*lo_func)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len)) {
! 586: int size_op;
! 587: while(len && (size_op=lo_func(conn, fd, buf, min(LO_BUFSIZE, len)))>0) {
! 588: buf+=size_op;
! 589: len-=size_op;
! 590: }
! 591: return len==0;
! 592: }
! 593:
! 594: private: // conn client library funcs
! 595:
! 596: typedef PGconn* (*t_PQsetdbLogin)(
! 597: const char *pghost,
! 598: const char *pgport,
! 599: const char *pgoptions,
! 600: const char *pgtty,
! 601: const char *dbName,
! 602: const char *login,
! 603: const char *pwd); t_PQsetdbLogin PQsetdbLogin;
! 604: typedef void (*t_PQfinish)(PGconn *conn); t_PQfinish PQfinish;
! 605: typedef char *(*t_PQerrorMessage)(const PGconn* conn); t_PQerrorMessage PQerrorMessage;
! 606: typedef ConnStatusType (*t_PQstatus)(const PGconn *conn); t_PQstatus PQstatus;
! 607: typedef PGresult *(*t_PQexec)(PGconn *conn,
! 608: const char *query); t_PQexec PQexec;
! 609: typedef PGresult *(*t_PQexecParams)(
! 610: PGconn *conn,
! 611: const char *query,
! 612: int nParams,
! 613: const Oid *paramTypes,
! 614: const char * const *paramValues,
! 615: const int *paramLengths,
! 616: const int *paramFormats,
! 617: int resultFormat); t_PQexecParams PQexecParams;
! 618:
! 619: typedef ExecStatusType (*t_PQresultStatus)(const PGresult *res); t_PQresultStatus PQresultStatus;
! 620: typedef int (*t_PQgetlength)(const PGresult *res,
! 621: int tup_num,
! 622: int field_num); t_PQgetlength PQgetlength;
! 623: typedef char* (*t_PQgetvalue)(const PGresult *res,
! 624: int tup_num,
! 625: int field_num); t_PQgetvalue PQgetvalue;
! 626: typedef int (*t_PQntuples)(const PGresult *res); t_PQntuples PQntuples;
! 627: typedef char *(*t_PQfname)(const PGresult *res,
! 628: int field_index); t_PQfname PQfname;
! 629: typedef int (*t_PQnfields)(const PGresult *res); t_PQnfields PQnfields;
! 630: typedef void (*t_PQclear)(PGresult *res); t_PQclear PQclear;
! 631:
! 632: typedef Oid (*t_PQftype)(const PGresult *res, int field_num); t_PQftype PQftype;
! 633:
! 634: typedef size_t (*t_PQescapeStringConn)(PGconn *conn,
! 635: char *to, const char *from, size_t length,
! 636: int *error); t_PQescapeStringConn PQescapeStringConn;
! 637:
! 638: typedef int (*t_lo_open)(PGconn *conn, Oid lobjId, int mode); t_lo_open lo_open;
! 639: typedef int (*t_lo_close)(PGconn *conn, int fd); t_lo_close lo_close;
! 640: typedef int (*t_lo_read)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_read lo_read;
! 641: typedef int (*t_lo_write)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_write lo_write;
! 642: typedef int (*t_lo_lseek)(PGconn *conn, int fd, int offset, int whence); t_lo_lseek lo_lseek;
! 643: typedef Oid (*t_lo_creat)(PGconn *conn, int mode); t_lo_creat lo_creat;
! 644: typedef int (*t_lo_tell)(PGconn *conn, int fd); t_lo_tell lo_tell;
! 645: typedef int (*t_lo_unlink)(PGconn *conn, Oid lobjId); t_lo_unlink lo_unlink;
! 646: typedef Oid (*t_lo_import)(PGconn *conn, const char *filename); t_lo_import lo_import;
! 647: typedef int (*t_lo_export)(PGconn *conn, Oid lobjId, const char *filename); t_lo_export lo_export;
! 648:
! 649: private: // conn client library funcs linking
! 650:
! 651: const char *dlink(const char *dlopen_file_spec) {
! 652: if(lt_dlinit())
! 653: return lt_dlerror();
! 654: lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
! 655: if(!handle)
! 656: return "can not open the dynamic link module";
! 657:
! 658: #define DSLINK(name, action) \
! 659: name=(t_##name)lt_dlsym(handle, #name); \
! 660: if(!name) \
! 661: action;
! 662:
! 663: #define DLINK(name) DSLINK(name, return "function " #name " was not found")
! 664:
! 665: DLINK(PQsetdbLogin);
! 666: DLINK(PQerrorMessage);
! 667: DLINK(PQstatus);
! 668: DLINK(PQfinish);
! 669: DLINK(PQgetvalue);
! 670: DLINK(PQgetlength);
! 671: DLINK(PQntuples);
! 672: DLINK(PQfname);
! 673: DLINK(PQnfields);
! 674: DLINK(PQclear);
! 675: DLINK(PQresultStatus);
! 676: DLINK(PQexec);
! 677: DLINK(PQexecParams);
! 678: DLINK(PQftype);
! 679: DLINK(PQescapeStringConn);
! 680: DLINK(lo_open); DLINK(lo_close);
! 681: DLINK(lo_read); DLINK(lo_write);
! 682: DLINK(lo_lseek); DLINK(lo_creat);
! 683: DLINK(lo_tell); DLINK(lo_unlink);
! 684: DLINK(lo_import); DLINK(lo_export);
! 685:
! 686: return 0;
! 687: }
! 688: };
! 689:
! 690: extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
! 691: return new PgSQL_Driver();
! 692: }
E-mail: