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

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.7     ! parser     10: static const char *RCSId="$Id: parser3pgsql.C,v 1.6 2001/07/31 14:22:00 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
1.7     ! parser     21: #define LO_BUFSIZE               0x1000/*8192*/
1.4       parser     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:                        it's already UNTAINT_TIMES_BIGGER
                    128:                */
                    129:                unsigned int result=length;
                    130:                while(length--) {
1.3       parser    131:                        switch(*from) {
                    132:                        case '\'': // "'" -> "''"
1.1       parser    133:                                *to++='\'';
1.3       parser    134:                                break;
                    135:                        case '\\': // "\" -> "\\"
                    136:                                *to++='\'';
                    137:                                break;
                    138:                        }
1.1       parser    139:                        *to++=*from++;
                    140:                }
                    141:                return result;
                    142:        }
                    143:        void query(
                    144:                SQL_Driver_services& services, void *connection, 
                    145:                const char *astatement, unsigned long offset, unsigned long limit,
                    146:                SQL_Driver_query_event_handlers& handlers) {
1.6       parser    147: //             _asm int 3;
1.1       parser    148: 
1.4       parser    149:                PGconn *conn=(PGconn *)connection;
1.5       parser    150:                #define PQclear_throw(msg) { \
                    151:                                PQclear(res); \
                    152:                                services._throw(msg); \
1.6       parser    153:                        }                                               
                    154:                #define PQclear_throwPQerror PQclear_throw(PQerrorMessage(conn))
1.1       parser    155: 
1.6       parser    156:                const char *statement=preprocess_statement(services, conn,
                    157:                        astatement, offset, limit);
1.1       parser    158: 
1.4       parser    159:                PGresult *res=PQexec(conn, statement);
1.1       parser    160:                if(!res) 
1.4       parser    161:                        services._throw(PQerrorMessage(conn));
1.1       parser    162: 
                    163:                switch(PQresultStatus(res)) {
                    164:                case PGRES_EMPTY_QUERY: 
1.5       parser    165:                        PQclear_throw("no query");
1.1       parser    166:                        break;
                    167:                case PGRES_COMMAND_OK:
                    168:                        // empty result: insert|delete|update|...
1.6       parser    169:                        PQclear(res);
1.1       parser    170:                        return;
                    171:                case PGRES_TUPLES_OK: 
                    172:                        break;  
                    173:                default:
1.6       parser    174:                        PQclear_throwPQerror;
1.1       parser    175:                        break;
                    176:                }
                    177:                
                    178:                int column_count=PQnfields(res);
1.5       parser    179:                if(!column_count)
                    180:                        PQclear_throw("result contains no columns");
1.1       parser    181: 
                    182:                for(int i=0; i<column_count; i++){
                    183:                        char *name=PQfname(res, i);
                    184:                        size_t size=strlen(name);
                    185:                        void *ptr=services.malloc(size);
                    186:                        memcpy(ptr, name, size);
                    187:                        handlers.add_column(ptr, size);
                    188:                }
                    189: 
                    190:                handlers.before_rows();
1.4       parser    191: 
1.1       parser    192:                if(unsigned long row_count=(unsigned long)PQntuples(res))
                    193:                        for(unsigned long r=0; r<row_count; r++) {
                    194:                                handlers.add_row();
                    195:                                for(int i=0; i<column_count; i++){
1.4       parser    196:                                        const char *cell=PQgetvalue(res, r, i);
                    197:                                        size_t size;
1.1       parser    198:                                        void *ptr;
1.4       parser    199:                                        if(PQftype(res, i)==OIDOID) {
                    200:                                                // ObjectID column, read object bytes
                    201: //                                             _asm int 3;
                    202: 
                    203:                                                char *error_pos=0;
                    204:                                                Oid oid=cell?atoi(cell):0;
                    205:                                                int fd=lo_open(conn, oid, INV_READ);
                    206:                                                if(fd>=0) {
                    207:                                                        // seek to end
                    208:                                                        if(lo_lseek(conn, fd, 0, SEEK_END)<0)
1.5       parser    209:                                                                PQclear_throwPQerror;
1.4       parser    210:                                                        // get size
                    211:                                                        int size_tell=lo_tell(conn, fd);
                    212:                                                        if(size_tell<0)
1.5       parser    213:                                                                PQclear_throwPQerror;
1.4       parser    214:                                                        // seek to begin
                    215:                                                        if(lo_lseek(conn, fd, 0, SEEK_SET)<0)
1.5       parser    216:                                                                PQclear_throwPQerror;
1.4       parser    217:                                                        size=(size_t)size_tell;
                    218:                                                        if(size) {
                    219:                                                                // read 
                    220:                                                                ptr=services.malloc(size);
1.7     ! parser    221:                                                                if(!lo_read_ex(conn, fd, (const char *)ptr, size_tell))
1.5       parser    222:                                                                        PQclear_throw("lo_read can not read all bytes of object");
1.4       parser    223:                                                        } else
                    224:                                                                ptr=0;
                    225:                                                        if(lo_close(conn, fd)<0)
1.5       parser    226:                                                                PQclear_throwPQerror;
1.4       parser    227:                                                } else
1.5       parser    228:                                                        PQclear_throwPQerror;
1.4       parser    229:                                        } else {
                    230:                                                // normal column, read it as ASCII string
                    231:                                                size=(size_t)PQgetlength(res, r, i);
                    232:                                                if(size) {
                    233:                                                        ptr=services.malloc(size);
                    234:                                                        memcpy(ptr, cell, size);
                    235:                                                } else
                    236:                                                        ptr=0;
                    237:                                        }
1.1       parser    238:                                        handlers.add_row_cell(ptr, size);
                    239:                                }
                    240:                        }
                    241: 
                    242:                PQclear(res);
                    243:        }
                    244: 
1.4       parser    245: private: // private funcs
                    246: 
                    247:        void begin_transaction(SQL_Driver_services& services, PGconn *conn) {
                    248:                if(PGresult *res=PQexec(conn, "BEGIN"))
                    249:                        PQclear(res);
                    250:                else
                    251:                        services._throw(PQerrorMessage(conn));
                    252:        }
                    253: 
1.6       parser    254:        const char *preprocess_statement(SQL_Driver_services& services, PGconn *conn,
                    255:                const char *astatement, unsigned long offset, unsigned long limit) {
                    256:                size_t statement_size=strlen(astatement);
                    257:                //_asm int 3;
                    258: 
1.7     ! parser    259:                #define throwPQerror services._throw(PQerrorMessage(conn))
        !           260: 
1.6       parser    261:                char *result=(char *)services.malloc(statement_size
                    262:                        +MAX_NUMBER*2+15 // limit # offset #
                    263:                        +MAX_STRING // in case of short 'strings'
                    264:                        +1);
                    265:                // offset & limit -> suffixes
                    266:                const char *o;
                    267:                if(offset || limit) {
                    268:                        char *cur=result;
                    269:                        memcpy(cur, astatement, statement_size); cur+=statement_size;
                    270:                        if(limit)
                    271:                                cur+=snprintf(cur, 7+MAX_NUMBER, " limit %u", limit);
                    272:                        if(offset)
                    273:                                cur+=snprintf(cur, 8+MAX_NUMBER, " offset %u", offset);
                    274:                        o=result;
                    275:                } else 
                    276:                        o=astatement;
                    277: 
1.7     ! parser    278:                // /**xxx**/'literal' -> oid
1.6       parser    279:                char *n=result;
                    280:                while(*o) {
                    281:                        if(
                    282:                                o[0]=='/' &&
                    283:                                o[1]=='*' && 
1.7     ! parser    284:                                o[2]=='*') { // name start
1.6       parser    285:                                o+=3;
                    286:                                while(*o)
                    287:                                        if(
                    288:                                                o[0]=='*' &&
1.7     ! parser    289:                                                o[1]=='*' &&
        !           290:                                                o[2]=='/' &&
        !           291:                                                o[3]=='\'') { // name end
        !           292:                                                o+=4;
1.6       parser    293:                                                Oid oid=lo_creat(conn, INV_READ|INV_WRITE);
1.7     ! parser    294:                                                if(oid==InvalidOid)
        !           295:                                                        throwPQerror;
1.6       parser    296:                                                int fd=lo_open(conn, oid, INV_WRITE);
1.7     ! parser    297:                                                if(fd>=0) {
        !           298:                                                        const char *start=o;
        !           299:                                                        bool escaped=false;
        !           300:                                                        while(*o && !(o[0]=='\'' && o[1]!='\'' && !escaped)) {
        !           301:                                                                escaped=*o=='\\' || (o[0]=='\'' && o[1]=='\'');
        !           302:                                                                if(escaped) {
        !           303:                                                                        // write pending, skip "\" or "'"
        !           304:                                                                        if(!lo_write_ex(conn, fd, start, o-start))
        !           305:                                                                                services._throw("lo_write could not write all bytes of object (1)");
        !           306:                                                                        start=++o;
        !           307:                                                                } else
        !           308:                                                                        o++;
        !           309:                                                        }
        !           310:                                                        if(!lo_write_ex(conn, fd, start, o-start))
        !           311:                                                                services._throw("lo_write can not write all bytes of object (2)");
        !           312:                                                        if(lo_close(conn, fd)<0)
        !           313:                                                                throwPQerror;
        !           314:                                                } else
        !           315:                                                        throwPQerror;
1.6       parser    316:                                                if(*o)
                    317:                                                        o++; // skip "'"
                    318: 
                    319:                                                n+=snprintf(n, MAX_NUMBER, "%u", oid);
                    320:                                                break;
                    321:                                        } else
1.7     ! parser    322:                                                o++; // /**skip**/'xxx'
1.6       parser    323:                        } else
                    324:                                *n++=*o++;
                    325:                }
                    326:                *n=0;
                    327: 
                    328:                return result;
1.7     ! parser    329:        }
        !           330: 
        !           331: private: // lo_read/write exchancements
        !           332: 
        !           333:        bool lo_read_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) {
        !           334:                int size_read;
        !           335:                while(len && (size_read=lo_read(conn, fd, buf, min(LO_BUFSIZE, len)))>0) {
        !           336:                        buf+=size_read;
        !           337:                        len-=size_read;                                                                 
        !           338:                }
        !           339:                return len==0;
        !           340:        }
        !           341: 
        !           342:        bool lo_write_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) {
        !           343:                int size_written;
        !           344:                while(len && (size_written=lo_write(conn, fd, buf, min(LO_BUFSIZE, len)))>0) {
        !           345:                        buf+=size_written;
        !           346:                        len-=size_written;                                                                      
        !           347:                }
        !           348:                return len==0;
1.6       parser    349:        }
                    350: 
1.4       parser    351: private: // conn client library funcs
1.1       parser    352: 
                    353:        typedef PGconn* (*t_PQsetdbLogin)(
                    354:                const char *pghost,
                    355:                const char *pgport,
                    356:                const char *pgoptions,
                    357:                const char *pgtty,
                    358:                const char *dbName,
                    359:                const char *login,
                    360:                const char *pwd); t_PQsetdbLogin PQsetdbLogin;
                    361:        typedef void (*t_PQfinish)(PGconn *conn);  t_PQfinish PQfinish;
                    362:        typedef char *(*t_PQerrorMessage)(const PGconn* conn); t_PQerrorMessage PQerrorMessage;
                    363:        typedef ConnStatusType (*t_PQstatus)(const PGconn *conn); t_PQstatus PQstatus;
                    364:        typedef PGresult *(*t_PQexec)(PGconn *conn,
                    365:                         const char *query); t_PQexec PQexec;
                    366:        typedef ExecStatusType (*t_PQresultStatus)(const PGresult *res); t_PQresultStatus PQresultStatus;
                    367:        typedef int (*t_PQgetlength)(const PGresult *res,
                    368:                                        int tup_num,
                    369:                                        int field_num); t_PQgetlength PQgetlength;
                    370:        typedef char* (*t_PQgetvalue)(const PGresult *res,
                    371:                                         int tup_num,
                    372:                                         int field_num); t_PQgetvalue PQgetvalue;
                    373:        typedef int (*t_PQntuples)(const PGresult *res); t_PQntuples PQntuples;
                    374:        typedef char *(*t_PQfname)(const PGresult *res,
                    375:                                                int field_index); t_PQfname PQfname;
                    376:        typedef int (*t_PQnfields)(const PGresult *res); t_PQnfields PQnfields;
                    377:        typedef void (*t_PQclear)(PGresult *res); t_PQclear PQclear;
                    378: 
1.4       parser    379:        typedef Oid     (*t_PQftype)(const PGresult *res, int field_num); t_PQftype PQftype;
                    380: 
                    381:        typedef int     (*t_lo_open)(PGconn *conn, Oid lobjId, int mode); t_lo_open lo_open;
                    382:        typedef int     (*t_lo_close)(PGconn *conn, int fd); t_lo_close lo_close;
1.6       parser    383:        typedef int     (*t_lo_read)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_read lo_read;
                    384:        typedef int     (*t_lo_write)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_write lo_write;
1.4       parser    385:        typedef int     (*t_lo_lseek)(PGconn *conn, int fd, int offset, int whence); t_lo_lseek lo_lseek;
                    386:        typedef Oid     (*t_lo_creat)(PGconn *conn, int mode); t_lo_creat lo_creat;
                    387:        typedef int     (*t_lo_tell)(PGconn *conn, int fd); t_lo_tell lo_tell;
                    388:        typedef int     (*t_lo_unlink)(PGconn *conn, Oid lobjId); t_lo_unlink lo_unlink;
                    389:        typedef Oid     (*t_lo_import)(PGconn *conn, const char *filename); t_lo_import lo_import;
                    390:        typedef int     (*t_lo_export)(PGconn *conn, Oid lobjId, const char *filename); t_lo_export lo_export;
1.1       parser    391: 
1.4       parser    392: private: // conn client library funcs linking
1.1       parser    393: 
                    394:        const char *dlink(const char *dlopen_file_spec) {
                    395:         lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
                    396:         if(!handle)
                    397:                        return "can not open the dynamic link module";
                    398: 
                    399:                #define DSLINK(name, action) \
                    400:                        name=(t_##name)lt_dlsym(handle, #name); \
                    401:                                if(!name) \
                    402:                                        action;
                    403: 
                    404:                #define DLINK(name) DSLINK(name, return "function " #name " was not found")
                    405:                
                    406:                DLINK(PQsetdbLogin);
                    407:                DLINK(PQerrorMessage);
                    408:                DLINK(PQstatus);
                    409:                DLINK(PQfinish);
                    410:                DLINK(PQgetvalue);
                    411:                DLINK(PQgetlength);
                    412:                DLINK(PQntuples);
                    413:                DLINK(PQfname);
                    414:                DLINK(PQnfields);
                    415:                DLINK(PQclear);
                    416:                DLINK(PQresultStatus);
                    417:                DLINK(PQexec);
1.4       parser    418:                DLINK(PQftype);
                    419:                DLINK(lo_open);         DLINK(lo_close);
                    420:                DLINK(lo_read);         DLINK(lo_write);
                    421:                DLINK(lo_lseek);                DLINK(lo_creat);
                    422:                DLINK(lo_tell);         DLINK(lo_unlink);
                    423:                DLINK(lo_import);               DLINK(lo_export);
1.1       parser    424: 
                    425:                return 0;
                    426:        }
                    427: 
                    428: };
                    429: 
                    430: extern "C" SQL_Driver *create() {
                    431:        return new PgSQL_Driver();
                    432: }

E-mail: