Annotation of sql/pgsql/parser3pgsql.C, revision 1.1.1.1

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

E-mail: