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

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.6     ! parser     10: static const char *RCSId="$Id: parser3pgsql.C,v 1.5 2001/07/31 12:51:45 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:                        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);
                    221:                                                                char *buf=(char *)ptr;
                    222:                                                                int countdown=size_tell;
                    223:                                                                int size_read;
                    224:                                                                while(countdown && (size_read=lo_read(conn, fd, buf, min(LO_BUFSIZE, countdown)))>0) {
                    225:                                                                        buf+=size_read;
                    226:                                                                        countdown-=size_read;                                                                   
                    227:                                                                }
1.5       parser    228:                                                                if(countdown)
                    229:                                                                        PQclear_throw("lo_read can not read all bytes of object");
1.4       parser    230:                                                        } else
                    231:                                                                ptr=0;
                    232:                                                        if(lo_close(conn, fd)<0)
1.5       parser    233:                                                                PQclear_throwPQerror;
1.4       parser    234:                                                } else
1.5       parser    235:                                                        PQclear_throwPQerror;
1.4       parser    236:                                        } else {
                    237:                                                // normal column, read it as ASCII string
                    238:                                                size=(size_t)PQgetlength(res, r, i);
                    239:                                                if(size) {
                    240:                                                        ptr=services.malloc(size);
                    241:                                                        memcpy(ptr, cell, size);
                    242:                                                } else
                    243:                                                        ptr=0;
                    244:                                        }
1.1       parser    245:                                        handlers.add_row_cell(ptr, size);
                    246:                                }
                    247:                        }
                    248: 
                    249:                PQclear(res);
                    250:        }
                    251: 
1.4       parser    252: private: // private funcs
                    253: 
                    254:        void begin_transaction(SQL_Driver_services& services, PGconn *conn) {
                    255:                if(PGresult *res=PQexec(conn, "BEGIN"))
                    256:                        PQclear(res);
                    257:                else
                    258:                        services._throw(PQerrorMessage(conn));
                    259:        }
                    260: 
1.6     ! parser    261:        const char *preprocess_statement(SQL_Driver_services& services, PGconn *conn,
        !           262:                const char *astatement, unsigned long offset, unsigned long limit) {
        !           263:                size_t statement_size=strlen(astatement);
        !           264:                //_asm int 3;
        !           265: 
        !           266:                char *result=(char *)services.malloc(statement_size
        !           267:                        +MAX_NUMBER*2+15 // limit # offset #
        !           268:                        +MAX_STRING // in case of short 'strings'
        !           269:                        +1);
        !           270:                // offset & limit -> suffixes
        !           271:                const char *o;
        !           272:                if(offset || limit) {
        !           273:                        char *cur=result;
        !           274:                        memcpy(cur, astatement, statement_size); cur+=statement_size;
        !           275:                        if(limit)
        !           276:                                cur+=snprintf(cur, 7+MAX_NUMBER, " limit %u", limit);
        !           277:                        if(offset)
        !           278:                                cur+=snprintf(cur, 8+MAX_NUMBER, " offset %u", offset);
        !           279:                        o=result;
        !           280:                } else 
        !           281:                        o=astatement;
        !           282: 
        !           283:                // /*:zzzz*/'literal' -> oid
        !           284:                char *n=result;
        !           285:                while(*o) {
        !           286:                        if(
        !           287:                                o[0]=='/' &&
        !           288:                                o[1]=='*' && 
        !           289:                                o[2]==':') { // name start
        !           290:                                o+=3;
        !           291:                                while(*o)
        !           292:                                        if(
        !           293:                                                o[0]=='*' &&
        !           294:                                                o[1]=='/' &&
        !           295:                                                o[2]=='\'') { // name end
        !           296:                                                o+=3;
        !           297:                                                Oid oid=lo_creat(conn, INV_READ|INV_WRITE);
        !           298:                                                int fd=lo_open(conn, oid, INV_WRITE);
        !           299:                                                const char *start=o;
        !           300:                                                bool escaped=false;
        !           301:                                                while(*o && !(o[0]=='\'' && o[1]!='\'' && !escaped)) {
        !           302:                                                        escaped=*o=='\\' || (o[0]=='\'' && o[1]=='\'');
        !           303:                                                        if(escaped) {
        !           304:                                                                // write pending, skip "\" or "'"
        !           305:                                                                if(o!=start)
        !           306:                                                                        lo_write(conn, fd, start, o-start);
        !           307:                                                                start=++o;
        !           308:                                                        } else
        !           309:                                                                o++;
        !           310:                                                }
        !           311:                                                if(o!=start)
        !           312:                                                        lo_write(conn, fd, start, o-start);
        !           313:                                                lo_close(conn, fd);
        !           314:                                                if(*o)
        !           315:                                                        o++; // skip "'"
        !           316: 
        !           317:                                                n+=snprintf(n, MAX_NUMBER, "%u", oid);
        !           318:                                                break;
        !           319:                                        } else
        !           320:                                                o++; // /*:skip*/
        !           321:                        } else
        !           322:                                *n++=*o++;
        !           323:                }
        !           324:                *n=0;
        !           325: 
        !           326:                return result;
        !           327:        }
        !           328: 
1.4       parser    329: private: // conn client library funcs
1.1       parser    330: 
                    331:        typedef PGconn* (*t_PQsetdbLogin)(
                    332:                const char *pghost,
                    333:                const char *pgport,
                    334:                const char *pgoptions,
                    335:                const char *pgtty,
                    336:                const char *dbName,
                    337:                const char *login,
                    338:                const char *pwd); t_PQsetdbLogin PQsetdbLogin;
                    339:        typedef void (*t_PQfinish)(PGconn *conn);  t_PQfinish PQfinish;
                    340:        typedef char *(*t_PQerrorMessage)(const PGconn* conn); t_PQerrorMessage PQerrorMessage;
                    341:        typedef ConnStatusType (*t_PQstatus)(const PGconn *conn); t_PQstatus PQstatus;
                    342:        typedef PGresult *(*t_PQexec)(PGconn *conn,
                    343:                         const char *query); t_PQexec PQexec;
                    344:        typedef ExecStatusType (*t_PQresultStatus)(const PGresult *res); t_PQresultStatus PQresultStatus;
                    345:        typedef int (*t_PQgetlength)(const PGresult *res,
                    346:                                        int tup_num,
                    347:                                        int field_num); t_PQgetlength PQgetlength;
                    348:        typedef char* (*t_PQgetvalue)(const PGresult *res,
                    349:                                         int tup_num,
                    350:                                         int field_num); t_PQgetvalue PQgetvalue;
                    351:        typedef int (*t_PQntuples)(const PGresult *res); t_PQntuples PQntuples;
                    352:        typedef char *(*t_PQfname)(const PGresult *res,
                    353:                                                int field_index); t_PQfname PQfname;
                    354:        typedef int (*t_PQnfields)(const PGresult *res); t_PQnfields PQnfields;
                    355:        typedef void (*t_PQclear)(PGresult *res); t_PQclear PQclear;
                    356: 
1.4       parser    357:        typedef Oid     (*t_PQftype)(const PGresult *res, int field_num); t_PQftype PQftype;
                    358: 
                    359:        typedef int     (*t_lo_open)(PGconn *conn, Oid lobjId, int mode); t_lo_open lo_open;
                    360:        typedef int     (*t_lo_close)(PGconn *conn, int fd); t_lo_close lo_close;
1.6     ! parser    361:        typedef int     (*t_lo_read)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_read lo_read;
        !           362:        typedef int     (*t_lo_write)(PGconn *conn, int fd, const/*paf*/ char *buf, size_t len); t_lo_write lo_write;
1.4       parser    363:        typedef int     (*t_lo_lseek)(PGconn *conn, int fd, int offset, int whence); t_lo_lseek lo_lseek;
                    364:        typedef Oid     (*t_lo_creat)(PGconn *conn, int mode); t_lo_creat lo_creat;
                    365:        typedef int     (*t_lo_tell)(PGconn *conn, int fd); t_lo_tell lo_tell;
                    366:        typedef int     (*t_lo_unlink)(PGconn *conn, Oid lobjId); t_lo_unlink lo_unlink;
                    367:        typedef Oid     (*t_lo_import)(PGconn *conn, const char *filename); t_lo_import lo_import;
                    368:        typedef int     (*t_lo_export)(PGconn *conn, Oid lobjId, const char *filename); t_lo_export lo_export;
1.1       parser    369: 
1.4       parser    370: private: // conn client library funcs linking
1.1       parser    371: 
                    372:        const char *dlink(const char *dlopen_file_spec) {
                    373:         lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
                    374:         if(!handle)
                    375:                        return "can not open the dynamic link module";
                    376: 
                    377:                #define DSLINK(name, action) \
                    378:                        name=(t_##name)lt_dlsym(handle, #name); \
                    379:                                if(!name) \
                    380:                                        action;
                    381: 
                    382:                #define DLINK(name) DSLINK(name, return "function " #name " was not found")
                    383:                
                    384:                DLINK(PQsetdbLogin);
                    385:                DLINK(PQerrorMessage);
                    386:                DLINK(PQstatus);
                    387:                DLINK(PQfinish);
                    388:                DLINK(PQgetvalue);
                    389:                DLINK(PQgetlength);
                    390:                DLINK(PQntuples);
                    391:                DLINK(PQfname);
                    392:                DLINK(PQnfields);
                    393:                DLINK(PQclear);
                    394:                DLINK(PQresultStatus);
                    395:                DLINK(PQexec);
1.4       parser    396:                DLINK(PQftype);
                    397:                DLINK(lo_open);         DLINK(lo_close);
                    398:                DLINK(lo_read);         DLINK(lo_write);
                    399:                DLINK(lo_lseek);                DLINK(lo_creat);
                    400:                DLINK(lo_tell);         DLINK(lo_unlink);
                    401:                DLINK(lo_import);               DLINK(lo_export);
1.1       parser    402: 
                    403:                return 0;
                    404:        }
                    405: 
                    406: };
                    407: 
                    408: extern "C" SQL_Driver *create() {
                    409:        return new PgSQL_Driver();
                    410: }

E-mail: