#include #include #include #include #include #include "my_postgres.h" MODULE = PAB3::DB::Driver::Postgres PACKAGE = PAB3::DB::Driver::Postgres BOOT: { MY_CXT_INIT; MY_CXT.firstcon = MY_CXT.lastcon = NULL; MY_CXT.lasterror[0] = '\0'; } #/***************************************************************************** # * CLONE( ... ) # *****************************************************************************/ #if defined(USE_ITHREADS) && defined(MY_CXT_KEY) void CLONE( ... ) CODE: MY_CXT_CLONE; #endif #/****************************************************************************** # * _connect( [host [, user [, auth [, db [, port [, flags]]]]]] ) # ******************************************************************************/ void * _connect( server = NULL, user = NULL, auth = NULL, db = NULL, client_flag = 0 ) const char *server; const char *user; const char *auth; const char *db; U32 client_flag; PREINIT: dMY_CXT; char *s1, *tmp = NULL; MY_CON *con; PGconn *pcon; DWORD l1; const char *host = NULL, *port = NULL; CODE: if( server && server[0] != '\0' ) { l1 = strlen( server ); STR_CREATEANDCOPYN( server, tmp, l1 ); host = tmp; if( ( s1 = strchr( tmp, ':' ) ) ) { *s1 ++ = '\0'; port = s1; } } pcon = PQsetdbLogin( host, port, NULL, NULL, db, user, auth ); Safefree( tmp ); if( PQstatus( pcon ) == CONNECTION_OK ) { con = my_con_add( &MY_CXT, pcon ); con->client_flag = client_flag | MYCF_AUTOCOMMIT; RETVAL = con; } else { s1 = PQerrorMessage( pcon ); my_strcpy( MY_CXT.lasterror, s1 ); PQfinish( pcon ); RETVAL = 0; } OUTPUT: RETVAL #/****************************************************************************** # * close( [linkid] ) # ******************************************************************************/ void close( linkid = 0 ) void *linkid; PREINIT: dMY_CXT; CODE: switch( my_get_type( &MY_CXT, &linkid ) ) { case MY_TYPE_CON: my_con_rem( &MY_CXT, (MY_CON *) linkid ); break; case MY_TYPE_RES: my_result_rem( (MY_RES *) linkid ); break; case MY_TYPE_STMT: my_stmt_rem( (MY_STMT *) linkid ); break; } #/****************************************************************************** # * reconnect( [linkid] ) # ******************************************************************************/ int reconnect( linkid = 0 ) void * linkid; PREINIT: dMY_CXT; MY_CON *con; CODE: con = my_con_verify( &MY_CXT, linkid ); if( con == NULL ) goto error; my_con_cleanup( con ); PQreset( con->con ); if( PQstatus( con->con ) != CONNECTION_OK ) goto error; if( con->charset ) PQsetClientEncoding( con->con, con->charset ); RETVAL = 1; goto exit; error: RETVAL = 0; exit: OUTPUT: RETVAL #/****************************************************************************** # * query( sql, [linkid] ) # ******************************************************************************/ IV query( ... ) PREINIT: dMY_CXT; void *linkid = NULL; const char *sql; MY_CON *con; MY_RES *res; PGresult *pres; ExecStatusType stat; int step = 0, itemp = 0; CODE: switch( items ) { case 2: linkid = INT2PTR( void *, SvIV( ST( itemp ) ) ); itemp ++; case 1: sql = (const char *) SvPV_nolen( ST( itemp ) ); break; default: Perl_croak( aTHX_ "Usage: " __PACKAGE__ "::query(linkid = 0, query)" ); } con = my_con_verify( &MY_CXT, linkid ); if( con == NULL ) goto error; retry: pres = PQexec( con->con, sql ); stat = PQresultStatus( pres ); switch( stat ) { case PGRES_TUPLES_OK: res = my_result_add( con, pres ); con->affected_rows = res->numrows; RETVAL = PTR2IV( res ); break; case PGRES_COMMAND_OK: case PGRES_COPY_OUT: case PGRES_COPY_IN: con->affected_rows = atol( PQcmdTuples( pres ) ); //printf( "oid: %u\n,", PQoidValue( pres ) ); PQclear( pres ); RETVAL = 1; break; default: PQclear( pres ); con->affected_rows = 0; if( step || ( con->client_flag & CLIENT_RECONNECT ) == 0 ) goto error; if( PQstatus( con->con ) != CONNECTION_OK ) { step ++; my_con_cleanup( con ); PQreset( con->con ); if( con->charset ) PQsetClientEncoding( con->con, con->charset ); goto retry; } break; } goto exit; error: RETVAL = 0; exit: OUTPUT: RETVAL #/****************************************************************************** # * prepare( [linkid, ] sql ) # ******************************************************************************/ IV prepare( ... ) PREINIT: dMY_CXT; const char *sql; char *stmtname = NULL, *tmp = NULL; void *linkid = NULL; STRLEN sqllen = 0; MY_CON *con; MY_STMT *stmt; PGresult *pstmt = NULL; DWORD plen; int itemp = 0; CODE: switch( items ) { case 3: linkid = INT2PTR( void *, SvIV( ST( 0 ) ) ); sql = (const char *) SvPVx( ST( 1 ), sqllen ); stmtname = (char *) SvPV_nolen( ST( 2 ) ); break; case 2: linkid = INT2PTR( void *, SvIV( ST( 0 ) ) ); itemp ++; case 1: sql = (const char *) SvPVx( ST( itemp ), sqllen ); break; default: Perl_croak( aTHX_ "Usage: " __PACKAGE__ "::prepare(linkid = 0, query)" ); } con = (MY_CON *) my_con_verify( &MY_CXT, linkid ); if( con == NULL ) goto error; New( 1, stmtname, sizeof( DWORD ) * 2 + 3, char ); stmtname[0] = 's'; stmtname[1] = 't'; my_itoa( &stmtname[2], (int) con->stmt_counter ++, 16 ); //printf( "using statement name [%s] %u\n", stmtname, con->stmt_counter - 1 ); tmp = my_stmt_convert( sql, sqllen, &plen, NULL ); pstmt = PQprepare( con->con, stmtname, tmp, 0, NULL ); switch( PQresultStatus( pstmt ) ) { case PGRES_COMMAND_OK: stmt = my_stmt_add( con, stmtname, plen ); RETVAL = PTR2IV( stmt ); goto exit; default: goto error; } error: RETVAL = 0; if( stmtname != NULL ) Safefree( stmtname ); exit: if( pstmt != NULL ) PQclear( pstmt ); if( tmp != NULL ) Safefree( tmp ); OUTPUT: RETVAL #/****************************************************************************** # * bind_param( stmtid, p_num, val [, type] ) # ******************************************************************************/ int bind_param( stmtid, p_num, val = NULL, type = 0 ) void *stmtid; U32 p_num; SV *val; char type; PREINIT: dMY_CXT; CODE: if( ! my_stmt_exists( &MY_CXT, stmtid ) ) // statement does not exists RETVAL = 0; else RETVAL = my_stmt_bind_param( (MY_STMT *) stmtid, p_num, val, type ); OUTPUT: RETVAL #/****************************************************************************** # * execute( stmtid, [*params] ) # ******************************************************************************/ IV execute( stmtid, ... ) void *stmtid; PREINIT: dMY_CXT; MY_CON *con; MY_STMT *stmt; MY_RES *res; DWORD step = 0; int i; PGresult *pres = NULL; CODE: if( ! my_stmt_exists( &MY_CXT, stmtid ) ) goto error; stmt = (MY_STMT *) stmtid; if( stmt->res != NULL ) { if( stmt->res->bound ) my_result_rem( stmt->res ); else stmt->res = NULL; } con = stmt->con; for( i = 1; i < items; i ++ ) if( ! my_stmt_bind_param( stmt, i, ST( i ), 0 ) ) goto error; pres = PQexecPrepared( con->con, stmt->id, stmt->param_count, (const char * const *) stmt->param_values, stmt->param_lengths, stmt->param_formats, 0 ); switch( PQresultStatus( pres ) ) { case PGRES_TUPLES_OK: res = my_result_add( stmt->con, pres ); stmt->res = res; res->stmt = stmt; con->affected_rows = res->numrows; RETVAL = PTR2IV( res ); break; case PGRES_COMMAND_OK: RETVAL = 1; con->affected_rows = atol( PQcmdTuples( pres ) ); PQclear( pres ); break; default: con->affected_rows = 0; PQclear( pres ); if( step || ( con->client_flag & CLIENT_RECONNECT ) == 0 ) goto error; if( PQstatus( con->con ) != CONNECTION_OK ) { step ++; my_con_cleanup( con ); PQreset( con->con ); if( con->charset ) PQsetClientEncoding( con->con, con->charset ); } goto error; } goto exit; error: RETVAL = 0; exit: OUTPUT: RETVAL #/****************************************************************************** # * free_result( resid ) # ******************************************************************************/ int free_result( resid ) void * resid; PREINIT: dMY_CXT; CODE: switch( my_stmt_or_result( &MY_CXT, resid ) ) { case MY_TYPE_RES: if( ((MY_RES *) resid)->stmt == NULL ) my_result_rem( (MY_RES *) resid ); else ((MY_RES *) resid)->bound = 1; RETVAL = 1; break; case MY_TYPE_STMT: my_stmt_rem( (MY_STMT *) resid ); RETVAL = 1; break; default: RETVAL = 0; } OUTPUT: RETVAL #/****************************************************************************** # * num_fields( resid ) # ******************************************************************************/ U32 num_fields( resid ) void * resid; PREINIT: dMY_CXT; CODE: switch( my_stmt_or_result( &MY_CXT, resid ) ) { case MY_TYPE_RES: RETVAL = ( (MY_RES *) resid )->numfields; break; case MY_TYPE_STMT: RETVAL = ( (MY_STMT *) resid )->res != NULL ? ( (MY_STMT *) resid )->res->numfields : 0; break; default: RETVAL = 0; } OUTPUT: RETVAL #/****************************************************************************** # * num_rows( resid ) # ******************************************************************************/ U32 num_rows( resid ) void * resid; PREINIT: dMY_CXT; CODE: switch( my_stmt_or_result( &MY_CXT, resid ) ) { case MY_TYPE_RES: RETVAL = ( (MY_RES *) resid )->numrows; break; case MY_TYPE_STMT: RETVAL = ( (MY_STMT *) resid )->res != NULL ? ( (MY_STMT *) resid )->res->numrows : 0; break; default: RETVAL = 0; } OUTPUT: RETVAL #/****************************************************************************** # * fetch_names( resid ) # ******************************************************************************/ void fetch_names( resid ) void * resid; PREINIT: dMY_CXT; MY_RES *res; const char *name; int num_fields, i; PPCODE: switch( my_stmt_or_result( &MY_CXT, resid ) ) { case MY_TYPE_RES: res = (MY_RES *) resid; break; case MY_TYPE_STMT: res = ( (MY_STMT *) resid )->res; break; } if( res != NULL ) { res = (MY_RES *) resid; num_fields = res->numfields; for( i = 0; i < num_fields; i ++ ) { name = PQfname( res->res, i ); XPUSHs( sv_2mortal( newSVpvn( name, strlen( name ) ) ) ); } } #/****************************************************************************** # * fetch_field( resid [, offset] ) # ******************************************************************************/ void fetch_field( resid, offset = -1 ) void * resid; long offset; PREINIT: dMY_CXT; MY_RES *res = NULL; const char *tmps; UV tmpu; PPCODE: switch( my_stmt_or_result( &MY_CXT, resid ) ) { case MY_TYPE_RES: res = (MY_RES *) resid; break; case MY_TYPE_STMT: res = ( (MY_STMT *) resid )->res; break; } if( res != NULL ) { if( offset >= 0 ) { if( (UV) offset >= res->numfields ) res->fieldpos = res->numfields - 1; else res->fieldpos = offset; } if( res->fieldpos < res->numfields ) { XPUSHs( sv_2mortal( newSVpvn( "name", 4 ) ) ); tmps = PQfname( res->res, res->fieldpos ); XPUSHs( sv_2mortal( newSVpvn( tmps, strlen( tmps ) ) ) ); XPUSHs( sv_2mortal( newSVpvn( "length", 6 ) ) ); tmpu = PQfsize( res->res, res->fieldpos ); XPUSHs( sv_2mortal( newSVuv( tmpu ) ) ); } } #/****************************************************************************** # * field_seek( resid [, offset] ) # ******************************************************************************/ U32 field_seek( resid, offset = 0 ) void * resid; U32 offset; PREINIT: dMY_CXT; MY_RES *res; CODE: switch( my_stmt_or_result( &MY_CXT, resid ) ) { case MY_TYPE_RES: res = (MY_RES *) resid; break; case MY_TYPE_STMT: res = ( (MY_STMT *) resid )->res; break; } if( res != NULL ) { RETVAL = res->fieldpos; if( offset < 0 ) res->fieldpos = 0; else if( offset >= res->numfields ) res->fieldpos = res->numfields - 1; else res->fieldpos = offset; } else RETVAL = 0; OUTPUT: RETVAL #/****************************************************************************** # * field_tell( resid ) # ******************************************************************************/ U32 field_tell( resid ) void * resid; PREINIT: dMY_CXT; CODE: switch( my_stmt_or_result( &MY_CXT, resid ) ) { case 1: RETVAL = ( (MY_RES *) resid )->fieldpos; break; case 2: RETVAL = ( (MY_STMT *) resid )->res != NULL ? ( (MY_STMT *) resid )->res->fieldpos : 0; break; default: RETVAL = 0; break; } OUTPUT: RETVAL #/****************************************************************************** # * fetch_row( resid ) # ******************************************************************************/ void fetch_row( resid ) void * resid; PREINIT: dMY_CXT; DWORD i, l; MY_RES *res; const char *val; PPCODE: switch( my_stmt_or_result( &MY_CXT, resid ) ) { case MY_TYPE_RES: res = (MY_RES *) resid; break; case MY_TYPE_STMT: res = ( (MY_STMT *) resid )->res; break; } if( res != NULL ) { if( res->rowpos < res->numrows ) { EXTEND( SP, res->numfields ); for( i = 0; i < res->numfields; i ++ ) { if( PQgetisnull( res->res, res->rowpos, i ) ) { XPUSHs( &PL_sv_undef ); } else { l = PQgetlength( res->res, res->rowpos, i ); val = PQgetvalue( res->res, res->rowpos, i ); XPUSHs( sv_2mortal( newSVpvn( val, l ) ) ); } } res->rowpos ++; } } #/****************************************************************************** # * fetch_col( resid ) # ******************************************************************************/ void fetch_col( resid ) void * resid; PREINIT: dMY_CXT; MY_RES *res; DWORD i, l; const char *val; PPCODE: switch( my_stmt_or_result( &MY_CXT, resid ) ) { case MY_TYPE_RES: res = (MY_RES *) resid; break; case MY_TYPE_STMT: res = ( (MY_STMT *) resid )->res; break; } if( res != NULL ) { EXTEND( SP, res->numrows ); for( i = 0; i < res->numrows; i ++ ) { if( PQgetisnull( res->res, i, 0 ) ) { XPUSHs( &PL_sv_undef ); } else { l = PQgetlength( res->res, i, 0 ); val = PQgetvalue( res->res, i, 0 ); XPUSHs( sv_2mortal( newSVpvn( val, l ) ) ); } } res->rowpos = res->numrows; } #/****************************************************************************** # * fetch_hash( resid ) # ******************************************************************************/ void fetch_hash( resid ) void * resid; PREINIT: dMY_CXT; MY_RES *res; DWORD i, l; const char *val, *name; PPCODE: switch( my_stmt_or_result( &MY_CXT, resid ) ) { case MY_TYPE_RES: res = (MY_RES *) resid; break; case MY_TYPE_STMT: res = ( (MY_STMT *) resid )->res; break; } if( res != NULL ) { if( res->rowpos < res->numrows ) { EXTEND( SP, res->numfields * 2 ); for( i = 0; i < res->numfields; i ++ ) { name = PQfname( res->res, i ); XPUSHs( sv_2mortal( newSVpvn( name, strlen( name ) ) ) ); if( PQgetisnull( res->res, res->rowpos, i ) ) { XPUSHs( &PL_sv_undef ); } else { l = PQgetlength( res->res, res->rowpos, i ); val = PQgetvalue( res->res, res->rowpos, i ); XPUSHs( sv_2mortal( newSVpvn( val, l ) ) ); } } res->rowpos ++; } } #/****************************************************************************** # * fetch_lengths( resid ) # ******************************************************************************/ void fetch_lengths( resid ) void * resid; PREINIT: dMY_CXT; MY_RES *res; DWORD i, s; PPCODE: switch( my_stmt_or_result( &MY_CXT, resid ) ) { case MY_TYPE_RES: res = (MY_RES *) resid; break; case MY_TYPE_STMT: res = ( (MY_STMT *) resid )->res; break; } if( res != NULL ) { EXTEND( SP, res->numfields ); for( i = 0; i < res->numfields; i ++ ) { s = PQfsize( res->res, i ); XPUSHs( sv_2mortal( newSVuv( s ) ) ); } } #/****************************************************************************** # * row_seek( resid, offset ) # ******************************************************************************/ long row_seek( resid, offset = 0 ) void * resid; U32 offset; PREINIT: dMY_CXT; MY_RES *res; CODE: switch( my_stmt_or_result( &MY_CXT, resid ) ) { case MY_TYPE_RES: res = (MY_RES *) resid; break; case MY_TYPE_STMT: res = ( (MY_STMT *) resid )->res; break; } if( res != NULL ) { RETVAL = res->rowpos; if( offset < 0 ) res->rowpos = 0; else if( offset >= res->numrows ) res->rowpos = res->numrows - 1; else res->rowpos = offset; } else RETVAL = -1; OUTPUT: RETVAL #/****************************************************************************** # * row_tell( resid ) # ******************************************************************************/ long row_tell( resid ) void * resid; PREINIT: dMY_CXT; CODE: switch( my_stmt_or_result( &MY_CXT, resid ) ) { case MY_TYPE_RES: RETVAL = ( (MY_RES *) resid )->rowpos; break; case MY_TYPE_STMT: RETVAL = ( (MY_STMT *) resid )->res != NULL ? ( (MY_STMT *) resid )->res->rowpos : 0; break; } OUTPUT: RETVAL #/****************************************************************************** # * insert_id( [linkid [, field [, table [, schema]]]] ) # ******************************************************************************/ # --> select currval('public."test_id_seq"'); void insert_id( ... ) PREINIT: dMY_CXT; void * linkid = 0; int itemp = 0; const char *field = NULL; const char *table = NULL; const char *schema = NULL; MY_CON *con; char sql[256], *p1; PGresult *res; CODE: if( items < 0 || items > 4 ) Perl_croak( aTHX_ "Usage: " __PACKAGE__ "::insert_id(linkid = 0, field = 0, table = 0, schema = 0)" ); if( SvIOK( ST( itemp ) ) ) { linkid = INT2PTR( void *, SvIV( ST( itemp ) ) ); itemp ++; } if( itemp < items ) { field = (const char *) SvPV_nolen( ST( itemp ) ); itemp ++; } if( itemp < items ) { table = (const char *) SvPV_nolen( ST( itemp ) ); itemp ++; } if( itemp < items ) { schema = (const char *) SvPV_nolen( ST( itemp ) ); itemp ++; } switch( my_stmt_or_con( &MY_CXT, &linkid ) ) { case MY_TYPE_CON: con = (MY_CON *) linkid; break; case MY_TYPE_STMT: con = ((MY_STMT *) linkid)->con; break; default: goto error; } if( ! table || ! field || table[0] == '\0' || field[0] == '\0' ) goto error; p1 = my_strcpy( sql, "SELECT CURRVAL('" ); if( schema ) { p1 = my_strcpyl( p1, schema ); *p1 ++ = '.'; } *p1 ++ = '"'; p1 = my_strcpyl( p1, table ); *p1 ++ = '_'; p1 = my_strcpyl( p1, field ); p1 = my_strcpy( p1, "_seq\"')" ); res = PQexec( con->con, sql ); if( PQresultStatus( res ) == PGRES_TUPLES_OK ) { p1 = PQgetvalue( res, 0, 0 ); ST(0) = sv_2mortal( newSVpvn( p1, strlen( p1 ) ) ); } else { ST(0) = &PL_sv_undef; } PQclear( res ); goto exit; error: ST(0) = &PL_sv_undef; exit: {} #/****************************************************************************** # * affected_rows( [linkid] ) # ******************************************************************************/ U32 affected_rows( linkid = 0 ) void * linkid; PREINIT: dMY_CXT; CODE: switch( my_stmt_or_con( &MY_CXT, &linkid ) ) { case MY_TYPE_CON: RETVAL = ((MY_CON *) linkid)->affected_rows; break; case MY_TYPE_STMT: RETVAL = ((MY_STMT *) linkid)->con->affected_rows; break; default: RETVAL = 0; } OUTPUT: RETVAL #/****************************************************************************** # * quote( val ) # ******************************************************************************/ void quote( val ) const char *val; INIT: char *res = 0; int l, lmax, i, dp; CODE: l = strlen( val ); lmax = l * 2 + 3; New( 1, res, lmax, char ); dp = 1; res[0] = '\''; for( i = 0; i < l; i ++ ) { if( val[i] == '\'' ) { res[dp ++] = '\''; res[dp ++] = '\''; } else { res[dp ++] = val[i]; } } res[dp ++] = '\''; res[dp] = 0; ST(0) = sv_2mortal( newSVpvn( res, dp ) ); CLEANUP: Safefree( res ); #/****************************************************************************** # * quote_id( p1, ... ) # ******************************************************************************/ void quote_id( p1, ... ) const char *p1; INIT: const char *str; char *res = 0; int i; unsigned long j, rlen, rpos; STRLEN len; CODE: rlen = items * 127; New( 1, res, rlen, char ); rpos = 0; for( i = 0; i < items; i ++ ) { len = SvLEN( ST(i) ); str = (const char *) SvPV( ST(i), len ); if( rpos + len * 2 > rlen ) { rlen = rpos + len * 2 + 3; Renew( res, rlen, char ); } if( i > 0 ) res[rpos ++] = '.'; if( i == items - 1 && len == 1 && str[0] == '*' ) { res[rpos ++] = '*'; } else { res[rpos ++] = '"'; for( j = 0; j < len; j ++ ) { if( str[j] == '"' ) { res[rpos ++] = '"'; res[rpos ++] = '"'; } else { res[rpos ++] = str[j]; } } res[rpos ++] = '"'; } } res[rpos] = '\0'; ST(0) = sv_2mortal( newSVpvn( res, rpos ) ); CLEANUP: Safefree( res ); #/****************************************************************************** # * set_charset( [linkid, ] charset ) # ******************************************************************************/ unsigned int set_charset( ... ) INIT: dMY_CXT; void * linkid = 0; const char *charset; MY_CON *con; STRLEN cslen; int res, itemp = 0; CODE: if( items < 1 || items > 2 ) Perl_croak( aTHX_ "Usage: " __PACKAGE__ "::set_charset(linkid = 0, charset)" ); if( items > 1 ) { linkid = INT2PTR( void *, SvIV( ST( itemp ) ) ); itemp ++; } charset = SvPVx( ST( itemp ), cslen ); con = my_con_verify( &MY_CXT, linkid ); if( con == NULL ) goto error; res = PQsetClientEncoding( con->con, charset ); if( res != 0 ) goto error; Safefree( con->charset ); New( 1, con->charset, cslen + 1, char ); memcpy( con->charset, charset, cslen + 1 ); con->charset_length = cslen; RETVAL = 1; goto exit; error: RETVAL = 0; exit: OUTPUT: RETVAL #/****************************************************************************** # * get_charset( [linkid] ) # ******************************************************************************/ const char * get_charset( linkid = 0 ) void * linkid; PREINIT: dMY_CXT; MY_CON *con; CODE: con = my_con_verify( &MY_CXT, linkid ); RETVAL = con != NULL ? con->charset : NULL; OUTPUT: RETVAL #/****************************************************************************** # * sql_limit( sql, length, limit [, offset] ) # ******************************************************************************/ char * sql_limit( sql, length, limit, offset = -1 ) const char *sql; U32 length; long limit; long offset; PREINIT: char *res, *rp; const char *fc; long i, fl, fp; CODE: if( sql ) { const char *find = "limit"; fl = 4; fp = 4; fc = 0; for( i = length - 1; i >= 0; i -- ) { if( tolower( sql[i] ) == find[fp] ) { fp --; if( fp < 0 ) { while( i > 0 && sql[-- i] == '0' ) {} fc = &sql[i]; break; } } else if( fp < fl ) { fp = fl; } } if( fc ) { New( 1, res, fc - sql + 27, char ); strncpy( res, sql, fc - sql ); rp = res + ( fc - sql ); } else { New( 1, res, length + 27, char ); strncpy( res, sql, length ); rp = res + length; } if( offset >= 0 ) sprintf( rp, " LIMIT %u OFFSET %u", limit, offset ); else sprintf( rp, " LIMIT %u", limit ); } else { res = 0; } RETVAL = res; OUTPUT: RETVAL CLEANUP: Safefree( res ); #/****************************************************************************** # * auto_commit( [linkid, ] mode ) # ******************************************************************************/ int auto_commit( linkid = 0, mode = 1 ) void * linkid; int mode; PREINIT: dMY_CXT; MY_CON *con; CODE: con = my_con_verify( &MY_CXT, linkid ); if( con == NULL ) goto error; if( mode ) { if( ( con->my_flags & MYCF_AUTOCOMMIT ) == 0 ) con->my_flags |= MYCF_AUTOCOMMIT; } else { if( ( con->my_flags & MYCF_AUTOCOMMIT ) != 0 ) con->my_flags ^= MYCF_AUTOCOMMIT; } //my_set_error( "Auto commit mode not supported" ); RETVAL = 1; goto exit; error: RETVAL = 0; exit: OUTPUT: RETVAL #/****************************************************************************** # * begin_work( [linkid] ) # ******************************************************************************/ int begin_work( linkid ) void * linkid; PREINIT: dMY_CXT; MY_CON *con; PGresult *res; CODE: RETVAL = 0; con = my_con_verify( &MY_CXT, linkid ); if( con == NULL ) goto exit; if( ( con->my_flags & MYCF_TRANSACTION ) == 0 ) { res = PQexec( con->con, "BEGIN" ); if( PQresultStatus( res ) == PGRES_COMMAND_OK ) { con->my_flags |= MYCF_TRANSACTION; RETVAL = 1; } else RETVAL = 0; PQclear( res ); } else RETVAL = 1; exit: OUTPUT: RETVAL #/****************************************************************************** # * commit( [linkid] ) # ******************************************************************************/ int commit( linkid ) void * linkid; PREINIT: dMY_CXT; MY_CON *con; PGresult *res; CODE: RETVAL = 0; con = my_con_verify( &MY_CXT, linkid ); if( con == NULL ) goto exit; if( ( con->my_flags & MYCF_TRANSACTION ) != 0 ) { res = PQexec( con->con, "COMMIT" ); if( PQresultStatus( res ) == PGRES_COMMAND_OK ) { con->my_flags ^= MYCF_TRANSACTION; if( ( con->my_flags & MYCF_AUTOCOMMIT ) == 0 ) { // disable auto commit PQclear( res ); res = PQexec( con->con, "BEGIN" ); if( PQresultStatus( res ) == PGRES_COMMAND_OK ) RETVAL = 1; else RETVAL = 0; } else RETVAL = 1; } else RETVAL = 0; PQclear( res ); } else RETVAL = 1; exit: OUTPUT: RETVAL #/****************************************************************************** # * rollback( [linkid] ) # ******************************************************************************/ int rollback( linkid ) void * linkid; PREINIT: dMY_CXT; MY_CON *con; PGresult *res; CODE: RETVAL = 0; con = my_con_verify( &MY_CXT, linkid ); if( con == NULL ) goto exit; if( ( con->my_flags & MYCF_TRANSACTION ) != 0 ) { res = PQexec( con->con, "ROLLBACK" ); if( PQresultStatus( res ) == PGRES_COMMAND_OK ) { con->my_flags ^= MYCF_TRANSACTION; if( ( con->my_flags & MYCF_AUTOCOMMIT ) == 0 ) { // disable auto commit PQclear( res ); res = PQexec( con->con, "BEGIN" ); if( PQresultStatus( res ) == PGRES_COMMAND_OK ) RETVAL = 1; else RETVAL = 0; } else RETVAL = 1; } else RETVAL = 0; PQclear( res ); } else RETVAL = 1; exit: OUTPUT: RETVAL #/****************************************************************************** # * show_catalogs( [linkid [, wild]] ) # ******************************************************************************/ void show_catalogs( linkid = 0, wild = 0 ) void * linkid; const char *wild; PREINIT: dMY_CXT; MY_CON *con; PGresult *pres; DWORD numrows, i, l; const char *val; PPCODE: con = my_con_verify( &MY_CXT, linkid ); if( con == NULL ) goto error; pres = PQexec( con->con, "select datname from pg_database" ); if( PQresultStatus( pres ) == PGRES_TUPLES_OK ) { numrows = PQntuples( pres ); for( i = 0; i < numrows; i ++ ) { l = PQgetlength( pres, i, 0 ); val = PQgetvalue( pres, i, 0 ); XPUSHs( sv_2mortal( newSVpvn( val, l ) ) ); } } PQclear( pres ); error: {} #/****************************************************************************** # * show_tables( [linkid [, schema [, db [, wild]]]] ) # ******************************************************************************/ void show_tables( ... ) PREINIT: dMY_CXT; void *linkid = NULL; const char *db = NULL; const char *schema = NULL; const char *wild = NULL; MY_CON *con; char sql[1024], *p1; DWORD numrows, i, l, dbl; int itemp = 0; const char *val; AV *av; PGresult *res; PPCODE: if( items < 0 || items > 4 ) Perl_croak( aTHX_ "Usage: " __PACKAGE__ "::show_tables(linkid = 0, schema = NULL, db = NULL, wild = NULL)" ); if( SvIOK( ST( itemp ) ) ) { linkid = INT2PTR( void *, SvIV( ST( itemp ) ) ); itemp ++; } if( itemp < items ) { schema = (const char *) SvPV_nolen( ST( itemp ) ); itemp ++; } if( itemp < items ) { db = (const char *) SvPV_nolen( ST( itemp ) ); itemp ++; } if( itemp < items ) wild = (const char *) SvPV_nolen( ST( itemp ) ); con = my_con_verify( &MY_CXT, linkid ); if( con == NULL ) goto error; /* SELECT c.relname AS objectname, n.nspname AS schemaname, c.relkind AS objecttype FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace WHERE ( c.relkind = 'r'::"char" OR c.relkind = 'v'::"char" ) AND ( n.nspname = 'schema' ) ORDER BY c.relkind, c.relname */ p1 = my_strcpy( sql, "SELECT c.relname AS f0, n.nspname AS f1, c.relkind AS f2" " FROM pg_class c" " LEFT JOIN pg_namespace n ON n.oid = c.relnamespace" " WHERE (" ); itemp = 0; if( wild == NULL || my_stristr( wild, "table" ) != NULL ) { p1 = my_strcpy( p1, "c.relkind = 'r'::\"char\"" ); itemp ++; } if( wild == NULL || my_stristr( wild, "view" ) != NULL ) { if( itemp ) p1 = my_strcpy( p1, " OR " ); p1 = my_strcpy( p1, "c.relkind = 'v'::\"char\"" ); itemp ++; } p1 = my_strcpy( p1, ")" ); if( schema && schema[0] != '\0' ) { p1 = my_strcpy( p1, " AND n.nspname = '" ); p1 = my_strcpy( p1, schema ); p1 = my_strcpy( p1, "'" ); } p1 = my_strcpy( p1, " ORDER BY c.relkind, c.relname" ); //printf( "%s\n", sql ); res = PQexec( con->con, sql ); if( PQresultStatus( res ) == PGRES_TUPLES_OK ) { // only connected db is permitted db = PQdb( con->con ); dbl = strlen( db ); numrows = PQntuples( res ); for( i = 0; i < numrows; i ++ ) { // TABLE, SCHEMA, DB, TYPE av = (AV *) sv_2mortal( (SV *) newAV() ); l = PQgetlength( res, i, 0 ); val = PQgetvalue( res, i, 0 ); av_push( av, newSVpvn( val, l ) ); l = PQgetlength( res, i, 1 ); val = PQgetvalue( res, i, 1 ); av_push( av, newSVpvn( val, l ) ); av_push( av, newSVpvn( db, dbl ) ); val = PQgetvalue( res, i, 2 ); switch( val[0] ) { case 'r': av_push( av, newSVpvn( "table", 5 ) ); break; case 'v': av_push( av, newSVpvn( "view", 4 ) ); break; } XPUSHs( newRV( (SV *) av ) ); } } PQclear( res ); error: {} #/****************************************************************************** # * errno( [linkid] ) # ******************************************************************************/ int errno( linkid = 0 ) void * linkid; PREINIT: dMY_CXT; MY_CON *con; CODE: con = my_con_verify_noerror( &MY_CXT, linkid ); RETVAL = con != NULL ? PQstatus( con->con ) : 0; OUTPUT: RETVAL #/****************************************************************************** # * error( [linkid] ) # ******************************************************************************/ void error( linkid = 0 ) void * linkid; PREINIT: dMY_CXT; MY_CON *con; const char *error; CODE: con = my_con_verify_noerror( &MY_CXT, linkid ); if( con != NULL ) { error = PQerrorMessage( con->con ); if( error[0] == '\0' ) error = con->my_error; } else { error = MY_CXT.lasterror; } if( error && error != '\0' ) ST(0) = sv_2mortal( newSVpvn( error, strlen( error ) ) ); else ST(0) = &PL_sv_undef; #/****************************************************************************** # * _cleanup(); # ******************************************************************************/ void _cleanup() PREINIT: dMY_CXT; CODE: my_cleanup( &MY_CXT ); #/****************************************************************************** # * _cleanupSession(); # ******************************************************************************/ void _cleanupSession() PREINIT: dMY_CXT; CODE: my_cleanup_session( &MY_CXT ); #/****************************************************************************** # * _verify_linkid( [linkid] ); # ******************************************************************************/ void * _verify_linkid( linkid = 0 ) void * linkid; PREINIT: dMY_CXT; CODE: RETVAL = (void *) my_con_verify( &MY_CXT, linkid ); OUTPUT: RETVAL