Annotation of parser3/src/sql/pgsql/parser3pgsql.C, revision 1.5

1.1       parser      1: /** @file
                      2:        Parser PgSQL driver.
                      3: 
                      4:        Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)
                      5: 
                      6:        Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf)
                      7: 
                      8:        2001.07.30 using PgSQL 7.1.2
                      9: */
1.5     ! parser     10: static const char *RCSId="$Id: parser3pgsql.C,v 1.4 2001/07/31 12:44:36 parser Exp $"; 
1.1       parser     11: 
                     12: #include "config_includes.h"
                     13: 
                     14: #include "pa_sql_driver.h"
                     15: 
                     16: #include <libpq-fe.h>
1.4       parser     17: #include <libpq-fs.h>
                     18: 
                     19: // #include <catalog/pg_type.h>
                     20: #define OIDOID                 26
                     21: #define LO_BUFSIZE               8192
                     22: 
                     23: 
1.1       parser     24: #include "ltdl.h"
                     25: 
                     26: #define MAX_STRING 0x400
                     27: #define MAX_NUMBER 20
                     28: 
                     29: #if _MSC_VER
                     30: #      define snprintf _snprintf
                     31: #      define strcasecmp _stricmp
                     32: #endif
                     33: 
1.4       parser     34: #ifndef max
                     35: inline int max(int a,int b) { return a>b?a:b; }
                     36: inline int min(int a,int b){ return a<b?a:b; }
                     37: #endif
                     38: 
1.1       parser     39: static char *lsplit(char *string, char delim) {
                     40:     if(string) {
                     41:                char *v=strchr(string, delim);
                     42:                if(v) {
                     43:                        *v=0;
                     44:                        return v+1;
                     45:                }
                     46:     }
                     47:     return 0;
                     48: }
                     49: 
                     50: /**
                     51:        PgSQL server driver
                     52: */
                     53: class PgSQL_Driver : public SQL_Driver {
                     54: public:
                     55: 
                     56:        PgSQL_Driver() : SQL_Driver() {
                     57:        }
                     58: 
                     59:        /// get api version
                     60:        int api_version() { return SQL_DRIVER_API_VERSION; }
                     61:        /// initialize driver by loading sql dynamic link library
                     62:        const char *initialize(const char *dlopen_file_spec) {
                     63:                return dlopen_file_spec?
                     64:                        dlink(dlopen_file_spec):"client library column is empty";
                     65:        }
                     66:        /**     connect
                     67:                @param used_only_in_connect_url
                     68:                        format: @b user:pass@host[:port]|[local]/database
                     69:                        3.23.22b
                     70:                        Currently the only option for @b character_set_name is cp1251_koi8.
                     71:                        WARNING: must be used only to connect, for buffer doesn't live long
                     72:        */
                     73:        void connect(
                     74:                char *used_only_in_connect_url, 
                     75:                SQL_Driver_services& services, 
                     76:                void **connection ///< output: PGconn *
                     77:                ) {
                     78:                char *user=used_only_in_connect_url;
                     79:                char *host=lsplit(user, '@');
                     80:                char *db=lsplit(host, '/');
                     81:                char *pwd=lsplit(user, ':');
                     82:                char *port=lsplit(host, ':');
                     83: 
                     84: //             _asm  int 3; 
1.4       parser     85:                PGconn *conn=PQsetdbLogin(
1.1       parser     86:                        strcasecmp(host, "local")==0?NULL/* local Unix domain socket */:host, port, 
                     87:                        NULL, NULL, db, user, pwd);
1.4       parser     88:                if(!conn)
1.1       parser     89:                        services._throw("PQsetdbLogin failed");
1.4       parser     90:                if(PQstatus(conn)!=CONNECTION_OK)  
                     91:                        services._throw(PQerrorMessage(conn));
1.1       parser     92: 
1.4       parser     93:                *(PGconn **)connection=conn;
                     94:                begin_transaction(services, conn);
1.1       parser     95:        }
                     96:        void disconnect(SQL_Driver_services&, void *connection) {
1.4       parser     97: //             _asm int 3;
1.1       parser     98:            PQfinish((PGconn *)connection);
                     99:        }
1.4       parser    100:        void commit(SQL_Driver_services& services, void *connection) {
                    101: //             _asm int 3;
                    102:                PGconn *conn=(PGconn *)connection;
                    103:                if(PGresult *res=PQexec(conn, "COMMIT"))
                    104:                        PQclear(res);
                    105:                else
                    106:                        services._throw(PQerrorMessage(conn));
                    107:                begin_transaction(services, conn);
                    108:        }
                    109:        void rollback(SQL_Driver_services& services, void *connection) {
                    110: //             _asm int 3;
                    111:                PGconn *conn=(PGconn *)connection;
                    112:                if(PGresult *res=PQexec(conn, "ROLLBACK"))
                    113:                        PQclear(res);
                    114:                else
                    115:                        services._throw(PQerrorMessage(conn));
                    116:                begin_transaction(services, conn);
                    117:        }
1.1       parser    118: 
                    119:        bool ping(SQL_Driver_services&, void *connection) {
                    120:                return PQstatus((PGconn *)connection)==CONNECTION_OK;
                    121:        }
                    122: 
                    123:        unsigned int quote(
                    124:                SQL_Driver_services&, void *connection,
                    125:                char *to, const char *from, unsigned int length) {
                    126:                /*
                    127:                        3.23.22b
                    128:                        You must allocate the to buffer to be at least length*2+1 bytes long. 
                    129:                        (In the worse case, each character may need to be encoded as using two bytes, 
                    130:                        and you need room for the terminating null byte.)
                    131: 
                    132:                        it's already UNTAINT_TIMES_BIGGER
                    133:                */
                    134:                unsigned int result=length;
                    135:                while(length--) {
1.3       parser    136:                        switch(*from) {
                    137:                        case '\'': // "'" -> "''"
1.1       parser    138:                                *to++='\'';
1.3       parser    139:                                break;
                    140:                        case '\\': // "\" -> "\\"
                    141:                                *to++='\'';
                    142:                                break;
                    143:                        }
1.1       parser    144:                        *to++=*from++;
                    145:                }
                    146:                return result;
                    147:        }
                    148:        void query(
                    149:                SQL_Driver_services& services, void *connection, 
                    150:                const char *astatement, unsigned long offset, unsigned long limit,
                    151:                SQL_Driver_query_event_handlers& handlers) {
                    152: 
1.4       parser    153:                PGconn *conn=(PGconn *)connection;
1.5     ! parser    154:                #define PQclear_throwPQerror { \
        !           155:                                PQclear(res); \
        !           156:                                services._throw(PQerrorMessage(conn)); \
        !           157:                        }
        !           158:                #define PQclear_throw(msg) { \
        !           159:                                PQclear(res); \
        !           160:                                services._throw(msg); \
        !           161:                        }
        !           162:                                                
1.1       parser    163: 
                    164:                const char *statement;
                    165:                if(offset || limit) {
                    166:                        size_t statement_size=strlen(astatement);
                    167:                        char *statement_limited=(char *)services.malloc(
                    168:                                statement_size+MAX_NUMBER*2+15/* limit # offset #*/+1);
                    169:                        char *cur=statement_limited;
                    170:                        memcpy(cur, astatement, statement_size); cur+=statement_size;
                    171:                        if(limit)
                    172:                                cur+=snprintf(cur, 7+MAX_NUMBER, " limit %u", limit);
                    173:                        if(offset)
                    174:                                cur+=snprintf(cur, 8+MAX_NUMBER, " offset %u", offset);
                    175:                        statement=statement_limited;
                    176:                } else
                    177:                        statement=astatement;
                    178: 
1.4       parser    179:                PGresult *res=PQexec(conn, statement);
1.1       parser    180:                if(!res) 
1.4       parser    181:                        services._throw(PQerrorMessage(conn));
1.1       parser    182: 
                    183:                switch(PQresultStatus(res)) {
                    184:                case PGRES_EMPTY_QUERY: 
1.5     ! parser    185:                        PQclear_throw("no query");
1.1       parser    186:                        break;
                    187:                case PGRES_COMMAND_OK:
                    188:                        // empty result: insert|delete|update|...
                    189:                        return;
                    190:                case PGRES_TUPLES_OK: 
                    191:                        break;  
                    192:                default:
1.5     ! parser    193:                        PQclear_throw("unknown PQexec error"); 
1.1       parser    194:                        break;
                    195:                }
                    196:                
                    197:                int column_count=PQnfields(res);
1.5     ! parser    198:                if(!column_count)
        !           199:                        PQclear_throw("result contains no columns");
1.1       parser    200: 
                    201:                for(int i=0; i<column_count; i++){
                    202:                        char *name=PQfname(res, i);
                    203:                        size_t size=strlen(name);
                    204:                        void *ptr=services.malloc(size);
                    205:                        memcpy(ptr, name, size);
                    206:                        handlers.add_column(ptr, size);
                    207:                }
                    208: 
                    209:                handlers.before_rows();
1.4       parser    210: 
1.1       parser    211:                if(unsigned long row_count=(unsigned long)PQntuples(res))
                    212:                        for(unsigned long r=0; r<row_count; r++) {
                    213:                                handlers.add_row();
                    214:                                for(int i=0; i<column_count; i++){
1.4       parser    215:                                        const char *cell=PQgetvalue(res, r, i);
                    216:                                        size_t size;
1.1       parser    217:                                        void *ptr;
1.4       parser    218:                                        if(PQftype(res, i)==OIDOID) {
                    219:                                                // ObjectID column, read object bytes
                    220: //                                             _asm int 3;
                    221: 
                    222:                                                char *error_pos=0;
                    223:                                                Oid oid=cell?atoi(cell):0;
                    224:                                                int fd=lo_open(conn, oid, INV_READ);
                    225:                                                if(fd>=0) {
                    226:                                                        // seek to end
                    227:                                                        if(lo_lseek(conn, fd, 0, SEEK_END)<0)
1.5     ! parser    228:                                                                PQclear_throwPQerror;
1.4       parser    229:                                                        // get size
                    230:                                                        int size_tell=lo_tell(conn, fd);
                    231:                                                        if(size_tell<0)
1.5     ! parser    232:                                                                PQclear_throwPQerror;
1.4       parser    233:                                                        // seek to begin
                    234:                                                        if(lo_lseek(conn, fd, 0, SEEK_SET)<0)
1.5     ! parser    235:                                                                PQclear_throwPQerror;
1.4       parser    236:                                                        size=(size_t)size_tell;
                    237:                                                        if(size) {
                    238:                                                                // read 
                    239:                                                                ptr=services.malloc(size);
                    240:                                                                char *buf=(char *)ptr;
                    241:                                                                int countdown=size_tell;
                    242:                                                                int size_read;
                    243:                                                                while(countdown && (size_read=lo_read(conn, fd, buf, min(LO_BUFSIZE, countdown)))>0) {
                    244:                                                                        buf+=size_read;
                    245:                                                                        countdown-=size_read;                                                                   
                    246:                                                                }
1.5     ! parser    247:                                                                if(countdown)
        !           248:                                                                        PQclear_throw("lo_read can not read all bytes of object");
1.4       parser    249:                                                        } else
                    250:                                                                ptr=0;
                    251:                                                        if(lo_close(conn, fd)<0)
1.5     ! parser    252:                                                                PQclear_throwPQerror;
1.4       parser    253:                                                } else
1.5     ! parser    254:                                                        PQclear_throwPQerror;
1.4       parser    255:                                        } else {
                    256:                                                // normal column, read it as ASCII string
                    257:                                                size=(size_t)PQgetlength(res, r, i);
                    258:                                                if(size) {
                    259:                                                        ptr=services.malloc(size);
                    260:                                                        memcpy(ptr, cell, size);
                    261:                                                } else
                    262:                                                        ptr=0;
                    263:                                        }
1.1       parser    264:                                        handlers.add_row_cell(ptr, size);
                    265:                                }
                    266:                        }
                    267: 
                    268:                PQclear(res);
                    269:        }
                    270: 
1.4       parser    271: private: // private funcs
                    272: 
                    273:        void begin_transaction(SQL_Driver_services& services, PGconn *conn) {
                    274:                if(PGresult *res=PQexec(conn, "BEGIN"))
                    275:                        PQclear(res);
                    276:                else
                    277:                        services._throw(PQerrorMessage(conn));
                    278:        }
                    279: 
                    280: private: // conn client library funcs
1.1       parser    281: 
                    282:        typedef PGconn* (*t_PQsetdbLogin)(
                    283:                const char *pghost,
                    284:                const char *pgport,
                    285:                const char *pgoptions,
                    286:                const char *pgtty,
                    287:                const char *dbName,
                    288:                const char *login,
                    289:                const char *pwd); t_PQsetdbLogin PQsetdbLogin;
                    290:        typedef void (*t_PQfinish)(PGconn *conn);  t_PQfinish PQfinish;
                    291:        typedef char *(*t_PQerrorMessage)(const PGconn* conn); t_PQerrorMessage PQerrorMessage;
                    292:        typedef ConnStatusType (*t_PQstatus)(const PGconn *conn); t_PQstatus PQstatus;
                    293:        typedef PGresult *(*t_PQexec)(PGconn *conn,
                    294:                         const char *query); t_PQexec PQexec;
                    295:        typedef ExecStatusType (*t_PQresultStatus)(const PGresult *res); t_PQresultStatus PQresultStatus;
                    296:        typedef int (*t_PQgetlength)(const PGresult *res,
                    297:                                        int tup_num,
                    298:                                        int field_num); t_PQgetlength PQgetlength;
                    299:        typedef char* (*t_PQgetvalue)(const PGresult *res,
                    300:                                         int tup_num,
                    301:                                         int field_num); t_PQgetvalue PQgetvalue;
                    302:        typedef int (*t_PQntuples)(const PGresult *res); t_PQntuples PQntuples;
                    303:        typedef char *(*t_PQfname)(const PGresult *res,
                    304:                                                int field_index); t_PQfname PQfname;
                    305:        typedef int (*t_PQnfields)(const PGresult *res); t_PQnfields PQnfields;
                    306:        typedef void (*t_PQclear)(PGresult *res); t_PQclear PQclear;
                    307: 
1.4       parser    308:        typedef Oid     (*t_PQftype)(const PGresult *res, int field_num); t_PQftype PQftype;
                    309: 
                    310:        typedef int     (*t_lo_open)(PGconn *conn, Oid lobjId, int mode); t_lo_open lo_open;
                    311:        typedef int     (*t_lo_close)(PGconn *conn, int fd); t_lo_close lo_close;
                    312:        typedef int     (*t_lo_read)(PGconn *conn, int fd, char *buf, size_t len); t_lo_read lo_read;
                    313:        typedef int     (*t_lo_write)(PGconn *conn, int fd, char *buf, size_t len); t_lo_write lo_write;
                    314:        typedef int     (*t_lo_lseek)(PGconn *conn, int fd, int offset, int whence); t_lo_lseek lo_lseek;
                    315:        typedef Oid     (*t_lo_creat)(PGconn *conn, int mode); t_lo_creat lo_creat;
                    316:        typedef int     (*t_lo_tell)(PGconn *conn, int fd); t_lo_tell lo_tell;
                    317:        typedef int     (*t_lo_unlink)(PGconn *conn, Oid lobjId); t_lo_unlink lo_unlink;
                    318:        typedef Oid     (*t_lo_import)(PGconn *conn, const char *filename); t_lo_import lo_import;
                    319:        typedef int     (*t_lo_export)(PGconn *conn, Oid lobjId, const char *filename); t_lo_export lo_export;
1.1       parser    320: 
1.4       parser    321: private: // conn client library funcs linking
1.1       parser    322: 
                    323:        const char *dlink(const char *dlopen_file_spec) {
                    324:         lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
                    325:         if(!handle)
                    326:                        return "can not open the dynamic link module";
                    327: 
                    328:                #define DSLINK(name, action) \
                    329:                        name=(t_##name)lt_dlsym(handle, #name); \
                    330:                                if(!name) \
                    331:                                        action;
                    332: 
                    333:                #define DLINK(name) DSLINK(name, return "function " #name " was not found")
                    334:                
                    335:                DLINK(PQsetdbLogin);
                    336:                DLINK(PQerrorMessage);
                    337:                DLINK(PQstatus);
                    338:                DLINK(PQfinish);
                    339:                DLINK(PQgetvalue);
                    340:                DLINK(PQgetlength);
                    341:                DLINK(PQntuples);
                    342:                DLINK(PQfname);
                    343:                DLINK(PQnfields);
                    344:                DLINK(PQclear);
                    345:                DLINK(PQresultStatus);
                    346:                DLINK(PQexec);
1.4       parser    347:                DLINK(PQftype);
                    348:                DLINK(lo_open);         DLINK(lo_close);
                    349:                DLINK(lo_read);         DLINK(lo_write);
                    350:                DLINK(lo_lseek);                DLINK(lo_creat);
                    351:                DLINK(lo_tell);         DLINK(lo_unlink);
                    352:                DLINK(lo_import);               DLINK(lo_export);
1.1       parser    353: 
                    354:                return 0;
                    355:        }
                    356: 
                    357: };
                    358: 
                    359: extern "C" SQL_Driver *create() {
                    360:        return new PgSQL_Driver();
                    361: }

E-mail: