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