|
|
| version 1.4, 2001/07/31 12:44:36 | version 1.8, 2001/08/01 09:30:35 |
|---|---|
| Line 16 static const char *RCSId="$Id$"; | Line 16 static const char *RCSId="$Id$"; |
| #include <libpq-fe.h> | #include <libpq-fe.h> |
| #include <libpq-fs.h> | #include <libpq-fs.h> |
| // #include <catalog/pg_type.h> | // OIDOID from #include <catalog/pg_type.h> |
| #define OIDOID 26 | #define OIDOID 26 |
| #define LO_BUFSIZE 8192 | // LO_BUFSIZE from interfaces\libpq\fe-lobj.c = 8192 (0x2000) |
| // actually writing chunks of that size failed, reduced it twice | |
| #define LO_BUFSIZE 0x1000 | |
| #include "ltdl.h" | #include "ltdl.h" |
| Line 66 public: | Line 67 public: |
| /** connect | /** connect |
| @param used_only_in_connect_url | @param used_only_in_connect_url |
| format: @b user:pass@host[:port]|[local]/database | format: @b user:pass@host[:port]|[local]/database |
| 3.23.22b | |
| Currently the only option for @b character_set_name is cp1251_koi8. | |
| WARNING: must be used only to connect, for buffer doesn't live long | |
| */ | */ |
| void connect( | void connect( |
| char *used_only_in_connect_url, | char *used_only_in_connect_url, |
| Line 81 public: | Line 79 public: |
| char *pwd=lsplit(user, ':'); | char *pwd=lsplit(user, ':'); |
| char *port=lsplit(host, ':'); | char *port=lsplit(host, ':'); |
| // _asm int 3; | |
| PGconn *conn=PQsetdbLogin( | PGconn *conn=PQsetdbLogin( |
| strcasecmp(host, "local")==0?NULL/* local Unix domain socket */:host, port, | strcasecmp(host, "local")==0?NULL/* local Unix domain socket */:host, port, |
| NULL, NULL, db, user, pwd); | NULL, NULL, db, user, pwd); |
| Line 94 public: | Line 91 public: |
| begin_transaction(services, conn); | begin_transaction(services, conn); |
| } | } |
| void disconnect(SQL_Driver_services&, void *connection) { | void disconnect(SQL_Driver_services&, void *connection) { |
| // _asm int 3; | |
| PQfinish((PGconn *)connection); | PQfinish((PGconn *)connection); |
| } | } |
| void commit(SQL_Driver_services& services, void *connection) { | void commit(SQL_Driver_services& services, void *connection) { |
| // _asm int 3; | |
| PGconn *conn=(PGconn *)connection; | PGconn *conn=(PGconn *)connection; |
| if(PGresult *res=PQexec(conn, "COMMIT")) | if(PGresult *res=PQexec(conn, "COMMIT")) |
| PQclear(res); | PQclear(res); |
| Line 107 public: | Line 102 public: |
| begin_transaction(services, conn); | begin_transaction(services, conn); |
| } | } |
| void rollback(SQL_Driver_services& services, void *connection) { | void rollback(SQL_Driver_services& services, void *connection) { |
| // _asm int 3; | |
| PGconn *conn=(PGconn *)connection; | PGconn *conn=(PGconn *)connection; |
| if(PGresult *res=PQexec(conn, "ROLLBACK")) | if(PGresult *res=PQexec(conn, "ROLLBACK")) |
| PQclear(res); | PQclear(res); |
| Line 124 public: | Line 118 public: |
| SQL_Driver_services&, void *connection, | SQL_Driver_services&, void *connection, |
| char *to, const char *from, unsigned int length) { | char *to, const char *from, unsigned int length) { |
| /* | /* |
| 3.23.22b | |
| You must allocate the to buffer to be at least length*2+1 bytes long. | |
| (In the worse case, each character may need to be encoded as using two bytes, | |
| and you need room for the terminating null byte.) | |
| it's already UNTAINT_TIMES_BIGGER | it's already UNTAINT_TIMES_BIGGER |
| */ | */ |
| unsigned int result=length; | unsigned int result=length; |
| Line 149 public: | Line 138 public: |
| SQL_Driver_services& services, void *connection, | SQL_Driver_services& services, void *connection, |
| const char *astatement, unsigned long offset, unsigned long limit, | const char *astatement, unsigned long offset, unsigned long limit, |
| SQL_Driver_query_event_handlers& handlers) { | SQL_Driver_query_event_handlers& handlers) { |
| // _asm int 3; | |
| PGconn *conn=(PGconn *)connection; | PGconn *conn=(PGconn *)connection; |
| #define PQclear_throw(msg) { \ | |
| PQclear(res); \ | |
| services._throw(msg); \ | |
| } | |
| #define PQclear_throwPQerror PQclear_throw(PQerrorMessage(conn)) | |
| const char *statement; | const char *statement=preprocess_statement(services, conn, |
| if(offset || limit) { | astatement, offset, limit); |
| size_t statement_size=strlen(astatement); | |
| char *statement_limited=(char *)services.malloc( | |
| statement_size+MAX_NUMBER*2+15/* limit # offset #*/+1); | |
| char *cur=statement_limited; | |
| memcpy(cur, astatement, statement_size); cur+=statement_size; | |
| if(limit) | |
| cur+=snprintf(cur, 7+MAX_NUMBER, " limit %u", limit); | |
| if(offset) | |
| cur+=snprintf(cur, 8+MAX_NUMBER, " offset %u", offset); | |
| statement=statement_limited; | |
| } else | |
| statement=astatement; | |
| PGresult *res=PQexec(conn, statement); | PGresult *res=PQexec(conn, statement); |
| if(!res) | if(!res) |
| Line 173 public: | Line 156 public: |
| switch(PQresultStatus(res)) { | switch(PQresultStatus(res)) { |
| case PGRES_EMPTY_QUERY: | case PGRES_EMPTY_QUERY: |
| PQclear(res); | PQclear_throw("no query"); |
| services._throw("no query"); | |
| break; | break; |
| case PGRES_COMMAND_OK: | case PGRES_COMMAND_OK: |
| // empty result: insert|delete|update|... | // empty result: insert|delete|update|... |
| PQclear(res); | |
| return; | return; |
| case PGRES_TUPLES_OK: | case PGRES_TUPLES_OK: |
| break; | break; |
| default: | default: |
| PQclear(res); | PQclear_throwPQerror; |
| services._throw("unknown PQexec error"); | |
| break; | break; |
| } | } |
| int column_count=PQnfields(res); | int column_count=PQnfields(res); |
| if(!column_count) { | if(!column_count) |
| PQclear(res); | PQclear_throw("result contains no columns"); |
| services._throw("result contains no columns"); | |
| } | |
| for(int i=0; i<column_count; i++){ | for(int i=0; i<column_count; i++){ |
| char *name=PQfname(res, i); | char *name=PQfname(res, i); |
| Line 212 public: | Line 192 public: |
| void *ptr; | void *ptr; |
| if(PQftype(res, i)==OIDOID) { | if(PQftype(res, i)==OIDOID) { |
| // ObjectID column, read object bytes | // ObjectID column, read object bytes |
| // _asm int 3; | |
| #define PQclear_n_throw { \ | |
| PQclear(res); \ | |
| services._throw(PQerrorMessage(conn)); \ | |
| } | |
| char *error_pos=0; | char *error_pos=0; |
| Oid oid=cell?atoi(cell):0; | Oid oid=cell?atoi(cell):0; |
| int fd=lo_open(conn, oid, INV_READ); | int fd=lo_open(conn, oid, INV_READ); |
| if(fd>=0) { | if(fd>=0) { |
| // seek to end | // seek to end |
| if(lo_lseek(conn, fd, 0, SEEK_END)<0) | if(lo_lseek(conn, fd, 0, SEEK_END)<0) |
| PQclear_n_throw; | PQclear_throwPQerror; |
| // get size | // get size |
| int size_tell=lo_tell(conn, fd); | int size_tell=lo_tell(conn, fd); |
| if(size_tell<0) | if(size_tell<0) |
| PQclear_n_throw; | PQclear_throwPQerror; |
| // seek to begin | // seek to begin |
| if(lo_lseek(conn, fd, 0, SEEK_SET)<0) | if(lo_lseek(conn, fd, 0, SEEK_SET)<0) |
| PQclear_n_throw; | PQclear_throwPQerror; |
| size=(size_t)size_tell; | size=(size_t)size_tell; |
| if(size) { | if(size) { |
| // read | // read |
| ptr=services.malloc(size); | ptr=services.malloc(size); |
| char *buf=(char *)ptr; | if(!lo_read_ex(conn, fd, (const char *)ptr, size_tell)) |
| int countdown=size_tell; | PQclear_throw("lo_read can not read all bytes of object"); |
| int size_read; | |
| while(countdown && (size_read=lo_read(conn, fd, buf, min(LO_BUFSIZE, countdown)))>0) { | |
| buf+=size_read; | |
| countdown-=size_read; | |
| } | |
| if(countdown) { | |
| PQclear(res); | |
| services._throw("lo_read can not read all of object bytes"); | |
| } | |
| } else | } else |
| ptr=0; | ptr=0; |
| if(lo_close(conn, fd)<0) | if(lo_close(conn, fd)<0) |
| PQclear_n_throw; | PQclear_throwPQerror; |
| } else | } else |
| PQclear_n_throw; | PQclear_throwPQerror; |
| } else { | } else { |
| // normal column, read it as ASCII string | // normal column, read it normally |
| size=(size_t)PQgetlength(res, r, i); | size=(size_t)PQgetlength(res, r, i); |
| if(size) { | if(size) { |
| ptr=services.malloc(size); | ptr=services.malloc(size); |
| Line 279 private: // private funcs | Line 244 private: // private funcs |
| services._throw(PQerrorMessage(conn)); | services._throw(PQerrorMessage(conn)); |
| } | } |
| const char *preprocess_statement(SQL_Driver_services& services, PGconn *conn, | |
| const char *astatement, unsigned long offset, unsigned long limit) { | |
| size_t statement_size=strlen(astatement); | |
| //_asm int 3; | |
| #define throwPQerror services._throw(PQerrorMessage(conn)) | |
| char *result=(char *)services.malloc(statement_size | |
| +MAX_NUMBER*2+15 // limit # offset # | |
| +MAX_STRING // in case of short 'strings' | |
| +1); | |
| // offset & limit -> suffixes | |
| const char *o; | |
| if(offset || limit) { | |
| char *cur=result; | |
| memcpy(cur, astatement, statement_size); cur+=statement_size; | |
| if(limit) | |
| cur+=snprintf(cur, 7+MAX_NUMBER, " limit %u", limit); | |
| if(offset) | |
| cur+=snprintf(cur, 8+MAX_NUMBER, " offset %u", offset); | |
| o=result; | |
| } else | |
| o=astatement; | |
| // /**xxx**/'literal' -> oid | |
| char *n=result; | |
| while(*o) { | |
| if( | |
| o[0]=='/' && | |
| o[1]=='*' && | |
| o[2]=='*') { // name start | |
| o+=3; | |
| while(*o) | |
| if( | |
| o[0]=='*' && | |
| o[1]=='*' && | |
| o[2]=='/' && | |
| o[3]=='\'') { // name end | |
| o+=4; | |
| Oid oid=lo_creat(conn, INV_READ|INV_WRITE); | |
| if(oid==InvalidOid) | |
| throwPQerror; | |
| int fd=lo_open(conn, oid, INV_WRITE); | |
| if(fd>=0) { | |
| const char *start=o; | |
| bool escaped=false; | |
| while(*o && !(o[0]=='\'' && o[1]!='\'' && !escaped)) { | |
| escaped=*o=='\\' || (o[0]=='\'' && o[1]=='\''); | |
| if(escaped) { | |
| // write pending, skip "\" or "'" | |
| if(!lo_write_ex(conn, fd, start, o-start)) | |
| services._throw("lo_write could not write all bytes of object (1)"); | |
| start=++o; | |
| } else | |
| o++; | |
| } | |
| if(!lo_write_ex(conn, fd, start, o-start)) | |
| services._throw("lo_write can not write all bytes of object (2)"); | |
| if(lo_close(conn, fd)<0) | |
| throwPQerror; | |
| } else | |
| throwPQerror; | |
| if(*o) | |
| o++; // skip "'" | |
| n+=snprintf(n, MAX_NUMBER, "%u", oid); | |
| break; | |
| } else | |
| o++; // /**skip**/'xxx' | |
| } else | |
| *n++=*o++; | |
| } | |
| *n=0; | |
| return result; | |
| } | |
| private: // lo_read/write exchancements | |
| bool lo_read_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) { | |
| int size_read; | |
| while(len && (size_read=lo_read(conn, fd, buf, min(LO_BUFSIZE, len)))>0) { | |
| buf+=size_read; | |
| len-=size_read; | |
| } | |
| return len==0; | |
| } | |
| bool lo_write_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) { | |
| int size_written; | |
| while(len && (size_written=lo_write(conn, fd, buf, min(LO_BUFSIZE, len)))>0) { | |
| buf+=size_written; | |
| len-=size_written; | |
| } | |
| return len==0; | |
| } | |
| private: // conn client library funcs | private: // conn client library funcs |
| typedef PGconn* (*t_PQsetdbLogin)( | typedef PGconn* (*t_PQsetdbLogin)( |
| Line 311 private: // conn client library funcs | Line 373 private: // conn client library funcs |
| typedef int (*t_lo_open)(PGconn *conn, Oid lobjId, int mode); t_lo_open lo_open; | typedef int (*t_lo_open)(PGconn *conn, Oid lobjId, int mode); t_lo_open lo_open; |
| typedef int (*t_lo_close)(PGconn *conn, int fd); t_lo_close lo_close; | typedef int (*t_lo_close)(PGconn *conn, int fd); t_lo_close lo_close; |
| typedef int (*t_lo_read)(PGconn *conn, int fd, char *buf, size_t len); t_lo_read lo_read; | typedef int (*t_lo_read)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_read lo_read; |
| typedef int (*t_lo_write)(PGconn *conn, int fd, char *buf, size_t len); t_lo_write lo_write; | typedef int (*t_lo_write)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_write lo_write; |
| typedef int (*t_lo_lseek)(PGconn *conn, int fd, int offset, int whence); t_lo_lseek lo_lseek; | typedef int (*t_lo_lseek)(PGconn *conn, int fd, int offset, int whence); t_lo_lseek lo_lseek; |
| typedef Oid (*t_lo_creat)(PGconn *conn, int mode); t_lo_creat lo_creat; | typedef Oid (*t_lo_creat)(PGconn *conn, int mode); t_lo_creat lo_creat; |
| typedef int (*t_lo_tell)(PGconn *conn, int fd); t_lo_tell lo_tell; | typedef int (*t_lo_tell)(PGconn *conn, int fd); t_lo_tell lo_tell; |