Annotation of sql/pgsql/parser3pgsql.C, revision 1.32
1.32 ! 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.30 2008-06-26 15:49:40 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 auto commit, 'BEGIN TRAN' at connection start and COMMIT/ROLLBACK at the end [can't be used together 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: _begin_transaction(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: if(!connection.without_default_transactions){
! 221: _execute_cmd(connection, "COMMIT");
! 222: }
! 223: _begin_transaction(connection);
! 224: }
! 225:
! 226: void rollback(void *aconnection){
! 227: Connection& connection=*static_cast<Connection*>(aconnection);
! 228: if(!connection.without_default_transactions){
! 229: _execute_cmd(connection, "ROLLBACK");
! 230: }
! 231: _begin_transaction(connection);
! 232: }
! 233:
! 234: bool ping(void *aconnection) {
! 235: Connection& connection=*static_cast<Connection*>(aconnection);
! 236: return PQstatus(connection.conn)==CONNECTION_OK;
! 237: }
! 238:
! 239: const char* quote(void *aconnection, const char *from, unsigned int length){
! 240: Connection& connection=*static_cast<Connection*>(aconnection);
! 241:
! 242: char *result=(char*)connection.services->malloc_atomic(length*2+1);
! 243: int err=0;
! 244: PQescapeStringConn(connection.conn, result, from, length, &err);
! 245: return result;
! 246: }
! 247:
! 248: void query(void *aconnection,
! 249: const char *astatement,
! 250: size_t placeholders_count, Placeholder* placeholders,
! 251: unsigned long offset, unsigned long limit,
! 252: SQL_Driver_query_event_handlers& handlers
! 253: ){
! 254: Connection& connection=*static_cast<Connection*>(aconnection);
! 255: SQL_Driver_services& services=*connection.services;
! 256: PGconn *conn=connection.conn;
! 257:
! 258: const char* client_charset=connection.client_charset;
! 259: const char* request_charset=services.request_charset();
! 260: bool transcode_needed=client_charset && strcmp(client_charset, request_charset)!=0;
! 261:
! 262: const char** paramValues;
! 263: if(placeholders_count>0){
! 264: int binds_size=sizeof(char)*placeholders_count;
! 265: paramValues = static_cast<const char**>(services.malloc_atomic(binds_size));
! 266: _bind_parameters(placeholders_count, placeholders, paramValues, connection, transcode_needed);
! 267: }
! 268:
! 269: if(transcode_needed){
! 270: // transcode query from $request:charset to ?ClientCharset
! 271: size_t length=strlen(astatement);
! 272: services.transcode(astatement, length,
! 273: astatement, length,
! 274: request_charset,
! 275: client_charset);
! 276: }
! 277:
! 278: const char *statement=_preprocess_statement(connection, astatement, offset, limit);
! 279: // error after prepare?
! 280:
! 281: PGresult *res;
! 282: if(placeholders_count>0){
! 283: res=PQexecParams(conn, statement, placeholders_count, NULL, paramValues, NULL, NULL, 0);
! 284: } else {
! 285: res=PQexec(conn, statement);
! 286: }
! 287: if(!res)
! 288: throwPQerror;
! 289:
! 290: switch(PQresultStatus(res)) {
! 291: case PGRES_EMPTY_QUERY:
! 292: PQclear_throw("no query");
! 293: break;
! 294: case PGRES_COMMAND_OK: // empty result: insert|delete|update|...
! 295: PQclear(res);
! 296: return;
! 297: case PGRES_TUPLES_OK:
! 298: break;
! 299: default:
! 300: PQclear_throwPQerror;
! 301: break;
! 302: }
! 303:
! 304: int column_count=PQnfields(res);
! 305: if(!column_count)
! 306: PQclear_throw("result contains no columns");
! 307:
! 308: bool failed=false;
! 309: SQL_Error sql_error;
! 310: #define CHECK(afailed) \
! 311: if(afailed) { \
! 312: failed=true; \
! 313: goto cleanup; \
! 314: }
! 315:
! 316: if(column_count>MAX_COLS)
! 317: column_count=MAX_COLS;
! 318:
! 319: unsigned int column_types[MAX_COLS];
! 320: bool transcode_column[MAX_COLS];
! 321:
! 322: for(int i=0; i<column_count; i++){
! 323: char *name=PQfname(res, i);
! 324: size_t length=strlen(name);
! 325: column_types[i]=PQftype(res, i);
! 326: switch(column_types[i]){
! 327: case BOOLOID:
! 328: case INT8OID:
! 329: case INT2OID:
! 330: case INT4OID:
! 331: case FLOAT4OID:
! 332: case FLOAT8OID:
! 333: case DATEOID:
! 334: case TIMEOID:
! 335: case TIMESTAMPOID:
! 336: case TIMESTAMPTZOID:
! 337: case TIMETZOID:
! 338: case NUMERICOID:
! 339: transcode_column[i]=false;
! 340: break;
! 341: default:
! 342: transcode_column[i]=transcode_needed;
! 343: break;
! 344: }
! 345: char* strm=(char*)services.malloc(length+1);
! 346: memcpy(strm, name, length+1);
! 347: const char* str=strm;
! 348:
! 349: if(transcode_needed)
! 350: // transcode column name from ?ClientCharset to $request:charset
! 351: services.transcode(str, length,
! 352: str, length,
! 353: client_charset,
! 354: request_charset);
! 355:
! 356: CHECK(handlers.add_column(sql_error, str, length));
! 357: }
! 358:
! 359: CHECK(handlers.before_rows(sql_error));
! 360:
! 361: if(unsigned long row_count=(unsigned long)PQntuples(res))
! 362: for(unsigned long r=0; r<row_count; r++) {
! 363: CHECK(handlers.add_row(sql_error));
! 364: for(int i=0; i<column_count; i++){
! 365: const char *cell=PQgetvalue(res, r, i);
! 366: size_t length;
! 367: const char* str;
! 368:
! 369: switch(column_types[i]){
! 370: case OIDOID:
! 371: {
! 372: char *error_pos=0;
! 373: Oid oid=cell?atoi(cell):0;
! 374: int fd=lo_open(conn, oid, INV_READ);
! 375: if(fd>=0){
! 376: // seek to end
! 377: if(lo_lseek(conn, fd, 0, SEEK_END)<0)
! 378: PQclear_throwPQerror;
! 379: // get length
! 380: int size_tell=lo_tell(conn, fd);
! 381: if(size_tell<0)
! 382: PQclear_throwPQerror;
! 383: // seek to begin
! 384: if(lo_lseek(conn, fd, 0, SEEK_SET)<0)
! 385: PQclear_throwPQerror;
! 386: length=(size_t)size_tell;
! 387: if(length){
! 388: // read
! 389: char* strm=(char*)services.malloc(length+1);
! 390: if(!lo_read_ex(conn, fd, strm, size_tell))
! 391: PQclear_throw("lo_read can not read all bytes of object");
! 392: strm[length]=0;
! 393: str=strm;
! 394: } else
! 395: str=0;
! 396: if(lo_close(conn, fd)<0)
! 397: PQclear_throwPQerror;
! 398: } else
! 399: PQclear_throwPQerror;
! 400: }
! 401: default:
! 402: // normal column, read it normally
! 403: length=(size_t)PQgetlength(res, r, i);
! 404: if(length){
! 405: char* strm=(char*)services.malloc(length+1);
! 406: memcpy(strm, cell, length+1);
! 407: str=strm;
! 408: } else
! 409: str=0;
! 410: }
! 411:
! 412: if(str && length && transcode_column[i]){
! 413: //services._throw("tr");
! 414: // transcode cell value from ?ClientCharset to $request:charset
! 415: services.transcode(str, length,
! 416: str, length,
! 417: client_charset,
! 418: request_charset);
! 419: }
! 420:
! 421: CHECK(handlers.add_row_cell(sql_error, str, length));
! 422: }
! 423: }
! 424: cleanup:
! 425: PQclear(res);
! 426: if(failed)
! 427: services._throw(sql_error);
! 428:
! 429: if(connection.autocommit)
! 430: commit(aconnection);
! 431: }
! 432:
! 433: private:
! 434: void _bind_parameters(
! 435: size_t placeholders_count,
! 436: Placeholder* placeholders,
! 437: const char** paramValues,
! 438: Connection& connection,
! 439: bool transcode_needed
! 440: ){
! 441: for(size_t i=0; i<placeholders_count; i++){
! 442: Placeholder& ph=placeholders[i];
! 443: if(transcode_needed){
! 444: size_t name_length;
! 445: size_t value_length;
! 446: connection.services->transcode(ph.name, strlen(ph.name),
! 447: ph.name, name_length,
! 448: connection.services->request_charset(),
! 449: connection.client_charset);
! 450:
! 451: if(ph.value) {
! 452: connection.services->transcode(ph.value, strlen(ph.value),
! 453: ph.value, value_length,
! 454: connection.services->request_charset(),
! 455: connection.client_charset);
! 456: }
! 457: }
! 458: int name_numner=atoi(ph.name);
! 459: if(name_numner <= 0 || name_numner > placeholders_count)
! 460: connection.services->_throw("bad bind parameter key");
! 461:
! 462: paramValues[name_numner-1]=ph.value;
! 463: }
! 464: }
! 465:
! 466:
! 467: /**
! 468: Executes a query and throw away the result.
! 469: */
! 470: void _execute_cmd(const Connection& connection, const char *query){
! 471: if(PGresult *res=PQexec(connection.conn, query))
! 472: PQclear(res); // throw out the result [don't need but must call]
! 473: else
! 474: throwPQerror;
! 475: }
! 476:
! 477: void _begin_transaction(Connection& connection){
! 478: if(!connection.without_default_transactions)
! 479: _execute_cmd(connection, "BEGIN");
! 480: }
! 481:
! 482: const char *_preprocess_statement(
! 483: Connection& connection,
! 484: const char *astatement,
! 485: unsigned long offset,
! 486: unsigned long limit
! 487: ){
! 488: PGconn *conn=connection.conn;
! 489:
! 490: size_t statement_size=strlen(astatement);
! 491:
! 492: char *result=(char *)connection.services->malloc(statement_size
! 493: +MAX_NUMBER*2+15 // limit # offset #
! 494: +MAX_STRING // in case of short 'strings'
! 495: +1);
! 496: // offset & limit -> suffixes
! 497: const char *o;
! 498: if(offset || limit!=SQL_NO_LIMIT){
! 499: char *cur=result;
! 500: memcpy(cur, astatement, statement_size); cur+=statement_size;
! 501: if(limit!=SQL_NO_LIMIT)
! 502: cur+=snprintf(cur, 7+MAX_NUMBER, " limit %u", limit);
! 503: if(offset)
! 504: cur+=snprintf(cur, 8+MAX_NUMBER, " offset %u", offset);
! 505: o=result;
! 506: } else
! 507: o=astatement;
! 508:
! 509: // /**xxx**/'literal' -> oid
! 510: char *n=result;
! 511: while(*o) {
! 512: if(
! 513: o[0]=='/' &&
! 514: o[1]=='*' &&
! 515: o[2]=='*') { // name start
! 516: const char* saved_o=o;
! 517: o+=3;
! 518: while(*o)
! 519: if(
! 520: o[0]=='*' &&
! 521: o[1]=='*' &&
! 522: o[2]=='/' &&
! 523: o[3]=='\'') { // name end
! 524: saved_o=0; // found, marking that
! 525: o+=4;
! 526: Oid oid=lo_creat(conn, INV_READ|INV_WRITE);
! 527: if(oid==InvalidOid)
! 528: throwPQerror;
! 529: int fd=lo_open(conn, oid, INV_WRITE);
! 530: if(fd>=0) {
! 531: const char *start=o;
! 532: bool escaped=false;
! 533: while(*o && !(o[0]=='\'' && o[1]!='\'' && !escaped)) {
! 534: escaped=*o=='\\' || (o[0]=='\'' && o[1]=='\'');
! 535: if(escaped) {
! 536: // write pending, skip "\" or "'"
! 537: if(!lo_write_ex(conn, fd, start, o-start))
! 538: connection.services->_throw("lo_write could not write all bytes of object (1)");
! 539: start=++o;
! 540: } else
! 541: o++;
! 542: }
! 543: if(!lo_write_ex(conn, fd, start, o-start))
! 544: connection.services->_throw("lo_write can not write all bytes of object (2)");
! 545: if(lo_close(conn, fd)<0)
! 546: throwPQerror;
! 547: } else
! 548: throwPQerror;
! 549: if(*o)
! 550: o++; // skip "'"
! 551:
! 552: n+=snprintf(n, MAX_NUMBER, "%u", oid);
! 553: break;
! 554: } else
! 555: o++; // /**skip**/'xxx'
! 556: if(saved_o) {
! 557: o=saved_o;
! 558: *n++=*o++;
! 559: }
! 560: } else
! 561: *n++=*o++;
! 562: }
! 563: *n=0;
! 564:
! 565: return result;
! 566: }
! 567:
! 568: private: // lo_read/write exchancements
! 569:
! 570: bool lo_read_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) {
! 571: return lo_rw_method (conn, fd, buf, len, lo_read);
! 572: }
! 573:
! 574: bool lo_write_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) {
! 575: return lo_rw_method (conn, fd, buf, len, lo_write);
! 576: }
! 577:
! 578: 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)) {
! 579: int size_op;
! 580: while(len && (size_op=lo_func(conn, fd, buf, min(LO_BUFSIZE, len)))>0) {
! 581: buf+=size_op;
! 582: len-=size_op;
! 583: }
! 584: return len==0;
! 585: }
! 586:
! 587: private: // conn client library funcs
! 588:
! 589: typedef PGconn* (*t_PQsetdbLogin)(
! 590: const char *pghost,
! 591: const char *pgport,
! 592: const char *pgoptions,
! 593: const char *pgtty,
! 594: const char *dbName,
! 595: const char *login,
! 596: const char *pwd); t_PQsetdbLogin PQsetdbLogin;
! 597: typedef void (*t_PQfinish)(PGconn *conn); t_PQfinish PQfinish;
! 598: typedef char *(*t_PQerrorMessage)(const PGconn* conn); t_PQerrorMessage PQerrorMessage;
! 599: typedef ConnStatusType (*t_PQstatus)(const PGconn *conn); t_PQstatus PQstatus;
! 600: typedef PGresult *(*t_PQexec)(PGconn *conn,
! 601: const char *query); t_PQexec PQexec;
! 602: typedef PGresult *(*t_PQexecParams)(
! 603: PGconn *conn,
! 604: const char *query,
! 605: int nParams,
! 606: const Oid *paramTypes,
! 607: const char * const *paramValues,
! 608: const int *paramLengths,
! 609: const int *paramFormats,
! 610: int resultFormat); t_PQexecParams PQexecParams;
! 611:
! 612: typedef ExecStatusType (*t_PQresultStatus)(const PGresult *res); t_PQresultStatus PQresultStatus;
! 613: typedef int (*t_PQgetlength)(const PGresult *res,
! 614: int tup_num,
! 615: int field_num); t_PQgetlength PQgetlength;
! 616: typedef char* (*t_PQgetvalue)(const PGresult *res,
! 617: int tup_num,
! 618: int field_num); t_PQgetvalue PQgetvalue;
! 619: typedef int (*t_PQntuples)(const PGresult *res); t_PQntuples PQntuples;
! 620: typedef char *(*t_PQfname)(const PGresult *res,
! 621: int field_index); t_PQfname PQfname;
! 622: typedef int (*t_PQnfields)(const PGresult *res); t_PQnfields PQnfields;
! 623: typedef void (*t_PQclear)(PGresult *res); t_PQclear PQclear;
! 624:
! 625: typedef Oid (*t_PQftype)(const PGresult *res, int field_num); t_PQftype PQftype;
! 626:
! 627: typedef size_t (*t_PQescapeStringConn)(PGconn *conn,
! 628: char *to, const char *from, size_t length,
! 629: int *error); t_PQescapeStringConn PQescapeStringConn;
! 630:
! 631: typedef int (*t_lo_open)(PGconn *conn, Oid lobjId, int mode); t_lo_open lo_open;
! 632: typedef int (*t_lo_close)(PGconn *conn, int fd); t_lo_close lo_close;
! 633: typedef int (*t_lo_read)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_read lo_read;
! 634: typedef int (*t_lo_write)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_write lo_write;
! 635: typedef int (*t_lo_lseek)(PGconn *conn, int fd, int offset, int whence); t_lo_lseek lo_lseek;
! 636: typedef Oid (*t_lo_creat)(PGconn *conn, int mode); t_lo_creat lo_creat;
! 637: typedef int (*t_lo_tell)(PGconn *conn, int fd); t_lo_tell lo_tell;
! 638: typedef int (*t_lo_unlink)(PGconn *conn, Oid lobjId); t_lo_unlink lo_unlink;
! 639: typedef Oid (*t_lo_import)(PGconn *conn, const char *filename); t_lo_import lo_import;
! 640: typedef int (*t_lo_export)(PGconn *conn, Oid lobjId, const char *filename); t_lo_export lo_export;
! 641:
! 642: private: // conn client library funcs linking
! 643:
! 644: const char *dlink(const char *dlopen_file_spec) {
! 645: if(lt_dlinit())
! 646: return lt_dlerror();
! 647: lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
! 648: if(!handle)
! 649: return "can not open the dynamic link module";
! 650:
! 651: #define DSLINK(name, action) \
! 652: name=(t_##name)lt_dlsym(handle, #name); \
! 653: if(!name) \
! 654: action;
! 655:
! 656: #define DLINK(name) DSLINK(name, return "function " #name " was not found")
! 657:
! 658: DLINK(PQsetdbLogin);
! 659: DLINK(PQerrorMessage);
! 660: DLINK(PQstatus);
! 661: DLINK(PQfinish);
! 662: DLINK(PQgetvalue);
! 663: DLINK(PQgetlength);
! 664: DLINK(PQntuples);
! 665: DLINK(PQfname);
! 666: DLINK(PQnfields);
! 667: DLINK(PQclear);
! 668: DLINK(PQresultStatus);
! 669: DLINK(PQexec);
! 670: DLINK(PQexecParams);
! 671: DLINK(PQftype);
! 672: DLINK(PQescapeStringConn);
! 673: DLINK(lo_open); DLINK(lo_close);
! 674: DLINK(lo_read); DLINK(lo_write);
! 675: DLINK(lo_lseek); DLINK(lo_creat);
! 676: DLINK(lo_tell); DLINK(lo_unlink);
! 677: DLINK(lo_import); DLINK(lo_export);
! 678:
! 679: return 0;
! 680: }
! 681: };
! 682:
! 683: extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
! 684: return new PgSQL_Driver();
! 685: }
E-mail: