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

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

E-mail: