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

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.8     ! parser     10: static const char *RCSId="$Id: parser3pgsql.C,v 1.7 2001/08/01 07:41:37 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: 
1.8     ! parser     19: // OIDOID from #include <catalog/pg_type.h>
1.4       parser     20: #define OIDOID                 26
1.8     ! parser     21: // LO_BUFSIZE from interfaces\libpq\fe-lobj.c = 8192 (0x2000)
        !            22: // actually writing chunks of that size failed, reduced it twice
        !            23: #define LO_BUFSIZE               0x1000
1.4       parser     24: 
1.1       parser     25: #include "ltdl.h"
                     26: 
                     27: #define MAX_STRING 0x400
                     28: #define MAX_NUMBER 20
                     29: 
                     30: #if _MSC_VER
                     31: #      define snprintf _snprintf
                     32: #      define strcasecmp _stricmp
                     33: #endif
                     34: 
1.4       parser     35: #ifndef max
                     36: inline int max(int a,int b) { return a>b?a:b; }
                     37: inline int min(int a,int b){ return a<b?a:b; }
                     38: #endif
                     39: 
1.1       parser     40: static char *lsplit(char *string, char delim) {
                     41:     if(string) {
                     42:                char *v=strchr(string, delim);
                     43:                if(v) {
                     44:                        *v=0;
                     45:                        return v+1;
                     46:                }
                     47:     }
                     48:     return 0;
                     49: }
                     50: 
                     51: /**
                     52:        PgSQL server driver
                     53: */
                     54: class PgSQL_Driver : public SQL_Driver {
                     55: public:
                     56: 
                     57:        PgSQL_Driver() : SQL_Driver() {
                     58:        }
                     59: 
                     60:        /// get api version
                     61:        int api_version() { return SQL_DRIVER_API_VERSION; }
                     62:        /// initialize driver by loading sql dynamic link library
                     63:        const char *initialize(const char *dlopen_file_spec) {
                     64:                return dlopen_file_spec?
                     65:                        dlink(dlopen_file_spec):"client library column is empty";
                     66:        }
                     67:        /**     connect
                     68:                @param used_only_in_connect_url
                     69:                        format: @b user:pass@host[:port]|[local]/database
                     70:        */
                     71:        void connect(
                     72:                char *used_only_in_connect_url, 
                     73:                SQL_Driver_services& services, 
                     74:                void **connection ///< output: PGconn *
                     75:                ) {
                     76:                char *user=used_only_in_connect_url;
                     77:                char *host=lsplit(user, '@');
                     78:                char *db=lsplit(host, '/');
                     79:                char *pwd=lsplit(user, ':');
                     80:                char *port=lsplit(host, ':');
                     81: 
1.4       parser     82:                PGconn *conn=PQsetdbLogin(
1.1       parser     83:                        strcasecmp(host, "local")==0?NULL/* local Unix domain socket */:host, port, 
                     84:                        NULL, NULL, db, user, pwd);
1.4       parser     85:                if(!conn)
1.1       parser     86:                        services._throw("PQsetdbLogin failed");
1.4       parser     87:                if(PQstatus(conn)!=CONNECTION_OK)  
                     88:                        services._throw(PQerrorMessage(conn));
1.1       parser     89: 
1.4       parser     90:                *(PGconn **)connection=conn;
                     91:                begin_transaction(services, conn);
1.1       parser     92:        }
                     93:        void disconnect(SQL_Driver_services&, void *connection) {
                     94:            PQfinish((PGconn *)connection);
                     95:        }
1.4       parser     96:        void commit(SQL_Driver_services& services, void *connection) {
                     97:                PGconn *conn=(PGconn *)connection;
                     98:                if(PGresult *res=PQexec(conn, "COMMIT"))
                     99:                        PQclear(res);
                    100:                else
                    101:                        services._throw(PQerrorMessage(conn));
                    102:                begin_transaction(services, conn);
                    103:        }
                    104:        void rollback(SQL_Driver_services& services, void *connection) {
                    105:                PGconn *conn=(PGconn *)connection;
                    106:                if(PGresult *res=PQexec(conn, "ROLLBACK"))
                    107:                        PQclear(res);
                    108:                else
                    109:                        services._throw(PQerrorMessage(conn));
                    110:                begin_transaction(services, conn);
                    111:        }
1.1       parser    112: 
                    113:        bool ping(SQL_Driver_services&, void *connection) {
                    114:                return PQstatus((PGconn *)connection)==CONNECTION_OK;
                    115:        }
                    116: 
                    117:        unsigned int quote(
                    118:                SQL_Driver_services&, void *connection,
                    119:                char *to, const char *from, unsigned int length) {
                    120:                /*
                    121:                        it's already UNTAINT_TIMES_BIGGER
                    122:                */
                    123:                unsigned int result=length;
                    124:                while(length--) {
1.3       parser    125:                        switch(*from) {
                    126:                        case '\'': // "'" -> "''"
1.1       parser    127:                                *to++='\'';
1.3       parser    128:                                break;
                    129:                        case '\\': // "\" -> "\\"
                    130:                                *to++='\'';
                    131:                                break;
                    132:                        }
1.1       parser    133:                        *to++=*from++;
                    134:                }
                    135:                return result;
                    136:        }
                    137:        void query(
                    138:                SQL_Driver_services& services, void *connection, 
                    139:                const char *astatement, unsigned long offset, unsigned long limit,
                    140:                SQL_Driver_query_event_handlers& handlers) {
1.6       parser    141: //             _asm int 3;
1.1       parser    142: 
1.4       parser    143:                PGconn *conn=(PGconn *)connection;
1.5       parser    144:                #define PQclear_throw(msg) { \
                    145:                                PQclear(res); \
                    146:                                services._throw(msg); \
1.6       parser    147:                        }                                               
                    148:                #define PQclear_throwPQerror PQclear_throw(PQerrorMessage(conn))
1.1       parser    149: 
1.6       parser    150:                const char *statement=preprocess_statement(services, conn,
                    151:                        astatement, offset, limit);
1.1       parser    152: 
1.4       parser    153:                PGresult *res=PQexec(conn, statement);
1.1       parser    154:                if(!res) 
1.4       parser    155:                        services._throw(PQerrorMessage(conn));
1.1       parser    156: 
                    157:                switch(PQresultStatus(res)) {
                    158:                case PGRES_EMPTY_QUERY: 
1.5       parser    159:                        PQclear_throw("no query");
1.1       parser    160:                        break;
                    161:                case PGRES_COMMAND_OK:
                    162:                        // empty result: insert|delete|update|...
1.6       parser    163:                        PQclear(res);
1.1       parser    164:                        return;
                    165:                case PGRES_TUPLES_OK: 
                    166:                        break;  
                    167:                default:
1.6       parser    168:                        PQclear_throwPQerror;
1.1       parser    169:                        break;
                    170:                }
                    171:                
                    172:                int column_count=PQnfields(res);
1.5       parser    173:                if(!column_count)
                    174:                        PQclear_throw("result contains no columns");
1.1       parser    175: 
                    176:                for(int i=0; i<column_count; i++){
                    177:                        char *name=PQfname(res, i);
                    178:                        size_t size=strlen(name);
                    179:                        void *ptr=services.malloc(size);
                    180:                        memcpy(ptr, name, size);
                    181:                        handlers.add_column(ptr, size);
                    182:                }
                    183: 
                    184:                handlers.before_rows();
1.4       parser    185: 
1.1       parser    186:                if(unsigned long row_count=(unsigned long)PQntuples(res))
                    187:                        for(unsigned long r=0; r<row_count; r++) {
                    188:                                handlers.add_row();
                    189:                                for(int i=0; i<column_count; i++){
1.4       parser    190:                                        const char *cell=PQgetvalue(res, r, i);
                    191:                                        size_t size;
1.1       parser    192:                                        void *ptr;
1.4       parser    193:                                        if(PQftype(res, i)==OIDOID) {
                    194:                                                // ObjectID column, read object bytes
                    195: 
                    196:                                                char *error_pos=0;
                    197:                                                Oid oid=cell?atoi(cell):0;
                    198:                                                int fd=lo_open(conn, oid, INV_READ);
                    199:                                                if(fd>=0) {
                    200:                                                        // seek to end
                    201:                                                        if(lo_lseek(conn, fd, 0, SEEK_END)<0)
1.5       parser    202:                                                                PQclear_throwPQerror;
1.4       parser    203:                                                        // get size
                    204:                                                        int size_tell=lo_tell(conn, fd);
                    205:                                                        if(size_tell<0)
1.5       parser    206:                                                                PQclear_throwPQerror;
1.4       parser    207:                                                        // seek to begin
                    208:                                                        if(lo_lseek(conn, fd, 0, SEEK_SET)<0)
1.5       parser    209:                                                                PQclear_throwPQerror;
1.4       parser    210:                                                        size=(size_t)size_tell;
                    211:                                                        if(size) {
                    212:                                                                // read 
                    213:                                                                ptr=services.malloc(size);
1.7       parser    214:                                                                if(!lo_read_ex(conn, fd, (const char *)ptr, size_tell))
1.5       parser    215:                                                                        PQclear_throw("lo_read can not read all bytes of object");
1.4       parser    216:                                                        } else
                    217:                                                                ptr=0;
                    218:                                                        if(lo_close(conn, fd)<0)
1.5       parser    219:                                                                PQclear_throwPQerror;
1.4       parser    220:                                                } else
1.5       parser    221:                                                        PQclear_throwPQerror;
1.4       parser    222:                                        } else {
1.8     ! parser    223:                                                // normal column, read it normally
1.4       parser    224:                                                size=(size_t)PQgetlength(res, r, i);
                    225:                                                if(size) {
                    226:                                                        ptr=services.malloc(size);
                    227:                                                        memcpy(ptr, cell, size);
                    228:                                                } else
                    229:                                                        ptr=0;
                    230:                                        }
1.1       parser    231:                                        handlers.add_row_cell(ptr, size);
                    232:                                }
                    233:                        }
                    234: 
                    235:                PQclear(res);
                    236:        }
                    237: 
1.4       parser    238: private: // private funcs
                    239: 
                    240:        void begin_transaction(SQL_Driver_services& services, PGconn *conn) {
                    241:                if(PGresult *res=PQexec(conn, "BEGIN"))
                    242:                        PQclear(res);
                    243:                else
                    244:                        services._throw(PQerrorMessage(conn));
                    245:        }
                    246: 
1.6       parser    247:        const char *preprocess_statement(SQL_Driver_services& services, PGconn *conn,
                    248:                const char *astatement, unsigned long offset, unsigned long limit) {
                    249:                size_t statement_size=strlen(astatement);
                    250:                //_asm int 3;
                    251: 
1.7       parser    252:                #define throwPQerror services._throw(PQerrorMessage(conn))
                    253: 
1.6       parser    254:                char *result=(char *)services.malloc(statement_size
                    255:                        +MAX_NUMBER*2+15 // limit # offset #
                    256:                        +MAX_STRING // in case of short 'strings'
                    257:                        +1);
                    258:                // offset & limit -> suffixes
                    259:                const char *o;
                    260:                if(offset || limit) {
                    261:                        char *cur=result;
                    262:                        memcpy(cur, astatement, statement_size); cur+=statement_size;
                    263:                        if(limit)
                    264:                                cur+=snprintf(cur, 7+MAX_NUMBER, " limit %u", limit);
                    265:                        if(offset)
                    266:                                cur+=snprintf(cur, 8+MAX_NUMBER, " offset %u", offset);
                    267:                        o=result;
                    268:                } else 
                    269:                        o=astatement;
                    270: 
1.7       parser    271:                // /**xxx**/'literal' -> oid
1.6       parser    272:                char *n=result;
                    273:                while(*o) {
                    274:                        if(
                    275:                                o[0]=='/' &&
                    276:                                o[1]=='*' && 
1.7       parser    277:                                o[2]=='*') { // name start
1.6       parser    278:                                o+=3;
                    279:                                while(*o)
                    280:                                        if(
                    281:                                                o[0]=='*' &&
1.7       parser    282:                                                o[1]=='*' &&
                    283:                                                o[2]=='/' &&
                    284:                                                o[3]=='\'') { // name end
                    285:                                                o+=4;
1.6       parser    286:                                                Oid oid=lo_creat(conn, INV_READ|INV_WRITE);
1.7       parser    287:                                                if(oid==InvalidOid)
                    288:                                                        throwPQerror;
1.6       parser    289:                                                int fd=lo_open(conn, oid, INV_WRITE);
1.7       parser    290:                                                if(fd>=0) {
                    291:                                                        const char *start=o;
                    292:                                                        bool escaped=false;
                    293:                                                        while(*o && !(o[0]=='\'' && o[1]!='\'' && !escaped)) {
                    294:                                                                escaped=*o=='\\' || (o[0]=='\'' && o[1]=='\'');
                    295:                                                                if(escaped) {
                    296:                                                                        // write pending, skip "\" or "'"
                    297:                                                                        if(!lo_write_ex(conn, fd, start, o-start))
                    298:                                                                                services._throw("lo_write could not write all bytes of object (1)");
                    299:                                                                        start=++o;
                    300:                                                                } else
                    301:                                                                        o++;
                    302:                                                        }
                    303:                                                        if(!lo_write_ex(conn, fd, start, o-start))
                    304:                                                                services._throw("lo_write can not write all bytes of object (2)");
                    305:                                                        if(lo_close(conn, fd)<0)
                    306:                                                                throwPQerror;
                    307:                                                } else
                    308:                                                        throwPQerror;
1.6       parser    309:                                                if(*o)
                    310:                                                        o++; // skip "'"
                    311: 
                    312:                                                n+=snprintf(n, MAX_NUMBER, "%u", oid);
                    313:                                                break;
                    314:                                        } else
1.7       parser    315:                                                o++; // /**skip**/'xxx'
1.6       parser    316:                        } else
                    317:                                *n++=*o++;
                    318:                }
                    319:                *n=0;
                    320: 
                    321:                return result;
1.7       parser    322:        }
                    323: 
                    324: private: // lo_read/write exchancements
                    325: 
                    326:        bool lo_read_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) {
                    327:                int size_read;
                    328:                while(len && (size_read=lo_read(conn, fd, buf, min(LO_BUFSIZE, len)))>0) {
                    329:                        buf+=size_read;
                    330:                        len-=size_read;                                                                 
                    331:                }
                    332:                return len==0;
                    333:        }
                    334: 
                    335:        bool lo_write_ex(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len) {
                    336:                int size_written;
                    337:                while(len && (size_written=lo_write(conn, fd, buf, min(LO_BUFSIZE, len)))>0) {
                    338:                        buf+=size_written;
                    339:                        len-=size_written;                                                                      
                    340:                }
                    341:                return len==0;
1.6       parser    342:        }
                    343: 
1.4       parser    344: private: // conn client library funcs
1.1       parser    345: 
                    346:        typedef PGconn* (*t_PQsetdbLogin)(
                    347:                const char *pghost,
                    348:                const char *pgport,
                    349:                const char *pgoptions,
                    350:                const char *pgtty,
                    351:                const char *dbName,
                    352:                const char *login,
                    353:                const char *pwd); t_PQsetdbLogin PQsetdbLogin;
                    354:        typedef void (*t_PQfinish)(PGconn *conn);  t_PQfinish PQfinish;
                    355:        typedef char *(*t_PQerrorMessage)(const PGconn* conn); t_PQerrorMessage PQerrorMessage;
                    356:        typedef ConnStatusType (*t_PQstatus)(const PGconn *conn); t_PQstatus PQstatus;
                    357:        typedef PGresult *(*t_PQexec)(PGconn *conn,
                    358:                         const char *query); t_PQexec PQexec;
                    359:        typedef ExecStatusType (*t_PQresultStatus)(const PGresult *res); t_PQresultStatus PQresultStatus;
                    360:        typedef int (*t_PQgetlength)(const PGresult *res,
                    361:                                        int tup_num,
                    362:                                        int field_num); t_PQgetlength PQgetlength;
                    363:        typedef char* (*t_PQgetvalue)(const PGresult *res,
                    364:                                         int tup_num,
                    365:                                         int field_num); t_PQgetvalue PQgetvalue;
                    366:        typedef int (*t_PQntuples)(const PGresult *res); t_PQntuples PQntuples;
                    367:        typedef char *(*t_PQfname)(const PGresult *res,
                    368:                                                int field_index); t_PQfname PQfname;
                    369:        typedef int (*t_PQnfields)(const PGresult *res); t_PQnfields PQnfields;
                    370:        typedef void (*t_PQclear)(PGresult *res); t_PQclear PQclear;
                    371: 
1.4       parser    372:        typedef Oid     (*t_PQftype)(const PGresult *res, int field_num); t_PQftype PQftype;
                    373: 
                    374:        typedef int     (*t_lo_open)(PGconn *conn, Oid lobjId, int mode); t_lo_open lo_open;
                    375:        typedef int     (*t_lo_close)(PGconn *conn, int fd); t_lo_close lo_close;
1.6       parser    376:        typedef int     (*t_lo_read)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_read lo_read;
                    377:        typedef int     (*t_lo_write)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_write lo_write;
1.4       parser    378:        typedef int     (*t_lo_lseek)(PGconn *conn, int fd, int offset, int whence); t_lo_lseek lo_lseek;
                    379:        typedef Oid     (*t_lo_creat)(PGconn *conn, int mode); t_lo_creat lo_creat;
                    380:        typedef int     (*t_lo_tell)(PGconn *conn, int fd); t_lo_tell lo_tell;
                    381:        typedef int     (*t_lo_unlink)(PGconn *conn, Oid lobjId); t_lo_unlink lo_unlink;
                    382:        typedef Oid     (*t_lo_import)(PGconn *conn, const char *filename); t_lo_import lo_import;
                    383:        typedef int     (*t_lo_export)(PGconn *conn, Oid lobjId, const char *filename); t_lo_export lo_export;
1.1       parser    384: 
1.4       parser    385: private: // conn client library funcs linking
1.1       parser    386: 
                    387:        const char *dlink(const char *dlopen_file_spec) {
                    388:         lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
                    389:         if(!handle)
                    390:                        return "can not open the dynamic link module";
                    391: 
                    392:                #define DSLINK(name, action) \
                    393:                        name=(t_##name)lt_dlsym(handle, #name); \
                    394:                                if(!name) \
                    395:                                        action;
                    396: 
                    397:                #define DLINK(name) DSLINK(name, return "function " #name " was not found")
                    398:                
                    399:                DLINK(PQsetdbLogin);
                    400:                DLINK(PQerrorMessage);
                    401:                DLINK(PQstatus);
                    402:                DLINK(PQfinish);
                    403:                DLINK(PQgetvalue);
                    404:                DLINK(PQgetlength);
                    405:                DLINK(PQntuples);
                    406:                DLINK(PQfname);
                    407:                DLINK(PQnfields);
                    408:                DLINK(PQclear);
                    409:                DLINK(PQresultStatus);
                    410:                DLINK(PQexec);
1.4       parser    411:                DLINK(PQftype);
                    412:                DLINK(lo_open);         DLINK(lo_close);
                    413:                DLINK(lo_read);         DLINK(lo_write);
                    414:                DLINK(lo_lseek);                DLINK(lo_creat);
                    415:                DLINK(lo_tell);         DLINK(lo_unlink);
                    416:                DLINK(lo_import);               DLINK(lo_export);
1.1       parser    417: 
                    418:                return 0;
                    419:        }
                    420: 
                    421: };
                    422: 
                    423: extern "C" SQL_Driver *create() {
                    424:        return new PgSQL_Driver();
                    425: }

E-mail: