diff --git a/ChangeLog.txt b/ChangeLog.txt index b062125705..4305a88311 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -7,6 +7,69 @@ Entries may not always be in chronological/commit order. See license at the end of file. */ +2021-03-31 21:26 UTC+0200 Aleksander Czajczynski (hb fki.pl) + * contrib/hbpgsql/hbpgsql.hbx + * contrib/hbpgsql/postgres.c + * contrib/hbpgsql/postgres.ch + + add most new wrappers from this repository of Petr Chornyj: + https://github.com/petr-ch/hbpgsql9 + Version guards have been added and "params" functions reworked + to accept hashes instead of two arrays. Other minor corrections + to fit into the contrib env. + + add PQsslAttribute() + * some existing functions were modified to make the parameter + check (and fail if wrong) even if the underlying API is not + available, instead of silently returning a default value. + ; all of the above was build-tested only and some features + require at least postgresql 8, 9 or 9.1 + + + add PQlibVersion() -> (returns 0 for pre-9.1 postresql versions) + Ref: https://github.com/harbour/core/pull/127/ by @VerchenkoAG + + + PQEXECPARAMS(): add 4th parameter to set + Refs: + https://www.postgresql.org/docs/9.1/static/libpq-exec.html + https://groups.google.com/d/msg/harbour-users/hXhuVzU9pHA/RrDGLIiUAwAJ + + + add PQresStatus( ) -> + + + added wrapper function + PQresultErrorField( result, nFieldCode ) -> cString + based on: https://groups.google.com/d/msg/harbour-users/XSvRpbzfcHc/ztSL32fYpl4J + + added PG_DIAG_* constants + + + added pg_encoding_to_char() wrapper to convert numeric + encoding ID to string + + ! fixed to use hb_fopen() instead of fopen() + + + TPQserver():New(): all parameters are now optional + ! TPQserver():New(): fixed to escape connect parameter values + + TPQserver():New(): added 7th optional hash parameter to pass custom + connection parameters (e.g. SSL) + https://www.postgresql.org/docs/devel/static/libpq-connect.html + + * contrib/hbpgsql/tests/test.prg + + use pg_encoding_to_char(), plus some more feedback regarding encodings + + * contrib/hbpgsql/tests/dbf2pg.prg + + use VF IO + + support more source field types + + ; synced with Viktor's 3.4 branch at https://github.com/vszakats/hb + 2017-02-15 15:14 UTC Viktor Szakats (vszakats users.noreply.github.com) + 2016-09-05 18:55 UTC+0200 Viktor Szakats (vszakats users.noreply.github.com) + 2016-09-05 10:43 UTC+0200 Viktor Szakats (vszakats users.noreply.github.com) + 2016-09-02 01:58 UTC+0200 Viktor Szakats (vszakats users.noreply.github.com) + 2016-06-20 22:50 UTC+0200 Viktor Szakats (vszakats users.noreply.github.com) + 2015-07-19 11:56 UTC+0200 Viktor Szakats (vszakats users.noreply.github.com) + 2015-04-06 18:39 UTC+0200 Viktor Szakats (vszakats users.noreply.github.com) + 2015-03-31 23:31 UTC+0200 Viktor Szakats (vszakats users.noreply.github.com) + 2014-11-29 02:47 UTC+0100 Viktor Szakats (vszakats users.noreply.github.com) + 2014-07-08 13:21 UTC+0200 Viktor Szakats (vszakats users.noreply.github.com) + 2014-02-11 18:18 UTC+0100 Viktor Szakats (vszakats users.noreply.github.com) + ; tons of cleanups in this library, many thanks + 2021-03-31 20:38 UTC+0200 Aleksander Czajczynski (hb fki.pl) * contrib/hbpgsql/hbpgsql.h * contrib/hbpgsql/postgres.c diff --git a/contrib/hbpgsql/hbpgsql.h b/contrib/hbpgsql/hbpgsql.h index e57c31b938..efb0e1f0b7 100644 --- a/contrib/hbpgsql/hbpgsql.h +++ b/contrib/hbpgsql/hbpgsql.h @@ -1,7 +1,7 @@ /* * PostgreSQL RDBMS wrapper header. * - * Copyright 2010 Viktor Szakats (vszakats.net/harbour) (GC support) + * Copyright 2010 Viktor Szakats (vsz.me/hb) (GC support) * Copyright 2003 Rodrigo Moreno rodrigo_moreno@yahoo.com * * This program is free software; you can redistribute it and/or modify @@ -84,9 +84,10 @@ HB_EXTERN_BEGIN -extern HB_EXPORT void hb_PGconn_ret( PGconn * p ); -extern HB_EXPORT PGconn * hb_PGconn_par( int iParam ); -extern HB_EXPORT void hb_PGresult_ret( PGresult * p ); +extern HB_EXPORT void hb_PGconn_ret( PGconn * p ); +extern HB_EXPORT PGconn * hb_PGconn_par( int iParam ); + +extern HB_EXPORT void hb_PGresult_ret( PGresult * p ); extern HB_EXPORT PGresult * hb_PGresult_par( int iParam ); HB_EXTERN_END diff --git a/contrib/hbpgsql/hbpgsql.hbx b/contrib/hbpgsql/hbpgsql.hbx index 8286c40e6d..c828cfaed8 100644 --- a/contrib/hbpgsql/hbpgsql.hbx +++ b/contrib/hbpgsql/hbpgsql.hbx @@ -25,12 +25,20 @@ DYNAMIC hb_PQCopyFromWA DYNAMIC lo_export DYNAMIC lo_import DYNAMIC lo_unlink +DYNAMIC pg_encoding_to_char +DYNAMIC PQbackendPID DYNAMIC PQbinaryTuples DYNAMIC PQcancel DYNAMIC PQclientEncoding DYNAMIC PQcmdStatus DYNAMIC PQcmdTuples DYNAMIC PQconnectdb +DYNAMIC PQconnectdbparams +DYNAMIC PQconnectionNeedsPassword +DYNAMIC PQconnectionUsedPassword +DYNAMIC PQconnectPoll +DYNAMIC PQconnectStart +DYNAMIC PQconnectStartParams DYNAMIC PQconsumeInput DYNAMIC PQdb DYNAMIC PQerrorMessage @@ -56,14 +64,19 @@ DYNAMIC PQgetvalue DYNAMIC PQhost DYNAMIC PQisBusy DYNAMIC PQisnonblocking +DYNAMIC PQisthreadsafe DYNAMIC PQlastrec +DYNAMIC PQlibVersion DYNAMIC PQmetadata DYNAMIC PQnfields DYNAMIC PQntuples DYNAMIC PQoidStatus DYNAMIC PQoidValue DYNAMIC PQoptions +DYNAMIC PQparameterStatus DYNAMIC PQpass +DYNAMIC PQping +DYNAMIC PQpingParams DYNAMIC PQport DYNAMIC PQprepare DYNAMIC PQprotocolVersion @@ -71,7 +84,11 @@ DYNAMIC PQputCopyData DYNAMIC PQputCopyEnd DYNAMIC PQrequestCancel DYNAMIC PQreset +DYNAMIC PQresetPoll +DYNAMIC PQresetStart +DYNAMIC PQresStatus DYNAMIC PQresult2array +DYNAMIC PQresultErrorField DYNAMIC PQresultErrorMessage DYNAMIC PQresultStatus DYNAMIC PQsendQuery @@ -80,6 +97,9 @@ DYNAMIC PQsetClientEncoding DYNAMIC PQsetdbLogin DYNAMIC PQsetErrorVerbosity DYNAMIC PQsetnonblocking +DYNAMIC PQsocket +DYNAMIC PQsslAttribute +DYNAMIC PQsslInUse DYNAMIC PQstatus DYNAMIC PQtrace DYNAMIC PQtracecreate diff --git a/contrib/hbpgsql/postgres.c b/contrib/hbpgsql/postgres.c index 7cc1bbe58b..8b726157d8 100644 --- a/contrib/hbpgsql/postgres.c +++ b/contrib/hbpgsql/postgres.c @@ -2,7 +2,7 @@ * PostgreSQL RDBMS low-level (client API) interface code. * * Copyright 2016 P.Chornyj - * Copyright 2010-2016 Viktor Szakats (vszakats.net/harbour) (GC support, etc) + * Copyright 2010-2016 Viktor Szakats (vsz.me/hb) (GC support, etc) * Copyright 2003 Rodrigo Moreno rodrigo_moreno@yahoo.com * * This program is free software; you can redistribute it and/or modify @@ -227,9 +227,51 @@ static FILE * hb_FILE_par( int iParam ) #endif -/* - * Connection handling functions - */ +/* Get the version of the libpq library in use */ + +HB_FUNC( PQLIBVERSION ) +{ +#if PG_VERSION_NUM >= 90100 + hb_retni( PQlibVersion() ); +#else + hb_retni( 0 ); +#endif +} + +/* Connection handling functions */ + +/* 31.1. Database Connection Control Functions + The following functions deal with making a connection to a PostgreSQL backend server. */ + +HB_FUNC( PQCONNECTDBPARAMS ) +{ + PHB_ITEM pParam = hb_param( 1, HB_IT_HASH ); + int len; + + if( pParam && ( len = ( int ) hb_hashLen( pParam ) ) > 0 ) + { +#if PG_VERSION_NUM >= 90000 + const char ** paramKeyValues = ( const char ** ) hb_xgrab( sizeof( char * ) * len ); + const char ** paramValValues = ( const char ** ) hb_xgrab( sizeof( char * ) * len ); + int pos; + + for( pos = 0; pos < len; ++pos ) + { + paramKeyValues[ pos ] = hb_itemGetCPtr( hb_hashGetKeyAt( pParam, pos + 1 ) ); + paramValValues[ pos ] = hb_itemGetCPtr( hb_hashGetValueAt( pParam, pos + 1 ) ); + } + + hb_PGconn_ret( PQconnectdbParams( paramKeyValues, paramValValues, hb_parl( 2 ) ) ); + + hb_xfree( ( void * ) paramKeyValues ); + hb_xfree( ( void * ) paramValValues ); +#else + hb_retptr( NULL ); +#endif + } + else + hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} HB_FUNC( PQCONNECTDB ) { @@ -251,6 +293,54 @@ HB_FUNC( PQSETDBLOGIN ) hb_parcx( 7 ) /* pwd */ ) ); } +HB_FUNC( PQCONNECTSTARTPARAMS ) +{ + PHB_ITEM pParam = hb_param( 1, HB_IT_HASH ); + int len; + + if( pParam && ( len = ( int ) hb_hashLen( pParam ) ) > 0 ) + { +#if PG_VERSION_NUM >= 90000 + const char ** paramKeyValues = ( const char ** ) hb_xgrab( sizeof( char * ) * len ); + const char ** paramValValues = ( const char ** ) hb_xgrab( sizeof( char * ) * len ); + int pos; + + for( pos = 0; pos < len; ++pos ) + { + paramKeyValues[ pos ] = hb_itemGetCPtr( hb_hashGetKeyAt( pParam, pos + 1 ) ); + paramValValues[ pos ] = hb_itemGetCPtr( hb_hashGetValueAt( pParam, pos + 1 ) ); + } + + hb_PGconn_ret( PQconnectStartParams( paramKeyValues, paramValValues, hb_parl( 2 ) ) ); + + hb_xfree( ( void * ) paramKeyValues ); + hb_xfree( ( void * ) paramValValues ); +#else + hb_retptr( NULL ); +#endif + } + else + hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( PQCONNECTSTART ) +{ + if( HB_ISCHAR( 1 ) ) + hb_PGconn_ret( PQconnectStart( hb_parc( 1 ) ) ); + else + hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( PQCONNECTPOLL ) +{ + PGconn * conn = hb_PGconn_par( 1 ); + + if( conn ) + hb_retni( PQconnectPoll( conn ) ); + else + hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + HB_FUNC( PQRESET ) { PGconn * conn = hb_PGconn_par( 1 ); @@ -261,16 +351,71 @@ HB_FUNC( PQRESET ) hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } -HB_FUNC( PQPROTOCOLVERSION ) +HB_FUNC( PQRESETSTART ) { PGconn * conn = hb_PGconn_par( 1 ); if( conn ) - hb_retni( PQprotocolVersion( conn ) ); + PQresetStart( conn ); else hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } +HB_FUNC( PQRESETPOLL ) +{ + PGconn * conn = hb_PGconn_par( 1 ); + + if( conn ) + hb_retni( PQresetPoll( conn ) ); + else + hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( PQPINGPARAMS ) +{ + PHB_ITEM pParam = hb_param( 1, HB_IT_HASH ); + int len; + + if( pParam && ( len = ( int ) hb_hashLen( pParam ) ) > 0 ) + { +#if PG_VERSION_NUM >= 90100 + const char ** paramKeyValues = ( const char ** ) hb_xgrab( sizeof( char * ) * len ); + const char ** paramValValues = ( const char ** ) hb_xgrab( sizeof( char * ) * len ); + int pos; + + for( pos = 0; pos < len; ++pos ) + { + paramKeyValues[ pos ] = hb_itemGetCPtr( hb_hashGetKeyAt( pParam, pos + 1 ) ); + paramValValues[ pos ] = hb_itemGetCPtr( hb_hashGetValueAt( pParam, pos + 1 ) ); + } + + hb_retni( PQpingParams( paramKeyValues, paramValValues, hb_parl( 3 ) ) ); + + hb_xfree( ( void * ) paramKeyValues ); + hb_xfree( ( void * ) paramValValues ); +#else + hb_retptr( NULL ); +#endif + } + else + hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( PQPING ) +{ + if( HB_ISCHAR( 1 ) ) +#if PG_VERSION_NUM >= 90100 + hb_retni( PQping( hb_parc( 1 ) ) ); +#else + hb_ret(); +#endif + else + hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +/* 31.2. Connection Status Functions. + These functions can be used to interrogate the status of an existing database connection object. */ + HB_FUNC( PQCLIENTENCODING ) { PGconn * conn = hb_PGconn_par( 1 ); @@ -361,6 +506,38 @@ HB_FUNC( PQOPTIONS ) hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } +HB_FUNC( PQRESULTERRORFIELD ) +{ + PGresult * res = hb_PGresult_par( 1 ); + + if( res ) +#if PG_VERSION_NUM >= 70400 + hb_retc( PQresultErrorField( res, hb_parni( 2 ) ) ); +#else + hb_retc_null(); +#endif + else + hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( PQRESSTATUS ) +{ + hb_retc( PQresStatus( ( ExecStatusType ) hb_parnl( 1 ) ) ); +} + +/* 31.2. Connection Status Functions. + These functions can be used to interrogate the status of an existing database connection object. */ + +HB_FUNC( PQSTATUS ) +{ + PGconn * conn = hb_PGconn_par( 1 ); + + if( conn ) + hb_retni( PQstatus( conn ) ); + else + hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + HB_FUNC( PQTRANSACTIONSTATUS ) { PGconn * conn = hb_PGconn_par( 1 ); @@ -371,6 +548,40 @@ HB_FUNC( PQTRANSACTIONSTATUS ) hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } +HB_FUNC( PQPARAMETERSTATUS ) +{ + PGconn * conn = hb_PGconn_par( 1 ); + + if( conn ) + hb_retc( PQparameterStatus( conn, hb_parcx( 2 ) ) ); + else + hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( PQPROTOCOLVERSION ) +{ + PGconn * conn = hb_PGconn_par( 1 ); + + if( conn ) + hb_retni( PQprotocolVersion( conn ) ); + else + hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( PQSERVERVERSION ) +{ + PGconn * conn = hb_PGconn_par( 1 ); + + if( conn ) +#if PG_VERSION_NUM >= 80000 + hb_retni( PQserverVersion( conn ) ); +#else + hb_retni( 0 ); +#endif + else + hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + HB_FUNC( PQERRORMESSAGE ) { PGconn * conn = hb_PGconn_par( 1 ); @@ -381,19 +592,83 @@ HB_FUNC( PQERRORMESSAGE ) hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } -HB_FUNC( PQSTATUS ) +HB_FUNC( PQSOCKET ) { PGconn * conn = hb_PGconn_par( 1 ); if( conn ) - hb_retni( PQstatus( conn ) ); + hb_retni( PQsocket( conn ) ); else hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } -/* - * Query handling functions - */ +HB_FUNC( PQBACKENDPID ) +{ + PGconn * conn = hb_PGconn_par( 1 ); + + if( conn ) + hb_retni( PQbackendPID( conn ) ); + else + hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( PQCONNECTIONNEEDSPASSWORD ) +{ + PGconn * conn = hb_PGconn_par( 1 ); + + if( conn ) +#if PG_VERSION_NUM >= 80300 + hb_retl( PQconnectionNeedsPassword( conn ) ? HB_TRUE : HB_FALSE ); +#else + hb_ret(); +#endif + else + hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( PQCONNECTIONUSEDPASSWORD ) +{ + PGconn * conn = hb_PGconn_par( 1 ); + + if( conn ) +#if PG_VERSION_NUM >= 80300 + hb_retl( PQconnectionUsedPassword( conn ) ? HB_TRUE : HB_FALSE ); +#else + hb_ret(); +#endif + else + hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( PQSSLINUSE ) +{ + PGconn * conn = hb_PGconn_par( 1 ); + + if( conn ) +#if PG_VERSION_NUM >= 90500 + hb_retl( PQsslInUse( conn ) ? HB_TRUE : HB_FALSE ); +#else + hb_ret(); +#endif + else + hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( PQSSLATTRIBUTE ) +{ + PGconn * conn = hb_PGconn_par( 1 ); + + if( conn ) +#if PG_VERSION_NUM >= 90500 + hb_retc( PQsslAttribute( conn, hb_parcx( 2 ) ) ); +#else + hb_retc_null(); +#endif + else + hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +/* Query handling functions */ HB_FUNC( PQEXEC ) { @@ -420,7 +695,7 @@ HB_FUNC( PQEXECPARAMS ) for( i = 0; i < n; ++i ) paramvalues[ i ] = hb_arrayGetCPtr( aParam, i + 1 ); - hb_PGresult_ret( PQexecParams( conn, hb_parcx( 2 ), n, NULL, paramvalues, NULL, NULL, 1 ) ); + hb_PGresult_ret( PQexecParams( conn, hb_parcx( 2 ), n, NULL, paramvalues, NULL, NULL, hb_parnidef( 4, 1 ) ) ); hb_xfree( ( void * ) paramvalues ); } @@ -725,7 +1000,7 @@ HB_FUNC( PQESCAPESTRING ) hb_retc_buffer( dest ); } -HB_FUNC( PQESCAPEBYTEA ) /* deprecated */ +HB_FUNC( PQESCAPEBYTEA ) /* deprecated */ { if( HB_ISCHAR( 1 ) ) { @@ -777,7 +1052,7 @@ HB_FUNC( PQBINARYTUPLES ) PGresult * res = hb_PGresult_par( 1 ); if( res ) - hb_retl( PQbinaryTuples( res ) ); + hb_retl( PQbinaryTuples( res ) ? HB_TRUE : HB_FALSE ); else hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } @@ -837,7 +1112,7 @@ HB_FUNC( PQGETISNULL ) PGresult * res = hb_PGresult_par( 1 ); if( res ) - hb_retl( PQgetisnull( res, hb_parni( 2 ) - 1, hb_parni( 3 ) - 1 ) ); + hb_retl( PQgetisnull( res, hb_parni( 2 ) - 1, hb_parni( 3 ) - 1 ) ? HB_TRUE : HB_FALSE ); else hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } @@ -872,9 +1147,7 @@ HB_FUNC( PQNFIELDS ) hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } -/* - * Asynchronous functions - */ +/* Asynchronous functions */ HB_FUNC( PQSENDQUERY ) { @@ -901,7 +1174,7 @@ HB_FUNC( PQCONSUMEINPUT ) PGconn * conn = hb_PGconn_par( 1 ); if( conn ) - hb_retl( PQconsumeInput( conn ) ); + hb_retl( PQconsumeInput( conn ) ? HB_TRUE : HB_FALSE ); else hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } @@ -936,12 +1209,14 @@ HB_FUNC( PQFLUSH ) hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } +/* Set blocking/nonblocking connection to the backend */ + HB_FUNC( PQSETNONBLOCKING ) { PGconn * conn = hb_PGconn_par( 1 ); if( conn ) - hb_retl( PQsetnonblocking( conn, hb_parl( 2 ) ) ); + hb_retl( PQsetnonblocking( conn, hb_parl( 2 ) ) ? HB_TRUE : HB_FALSE ); else hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } @@ -951,19 +1226,17 @@ HB_FUNC( PQISNONBLOCKING ) PGconn * conn = hb_PGconn_par( 1 ); if( conn ) - hb_retl( PQisnonblocking( conn ) ); + hb_retl( PQisnonblocking( conn ) ? HB_TRUE : HB_FALSE ); else hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } -/* - * Trace Connection handling functions - */ +/* Trace Connection handling functions */ HB_FUNC( PQTRACECREATE ) /* not a direct wrapper */ { #ifdef NODLL - hb_FILE_ret( fopen( hb_parcx( 1 ), "w+b" ) ); + hb_FILE_ret( hb_fopen( hb_parcx( 1 ), "w+b" ) ); #else hb_retptr( NULL ); #endif @@ -1006,9 +1279,7 @@ HB_FUNC( PQSETERRORVERBOSITY ) hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } -/* - * Large Object functions - */ +/* Large Object functions */ HB_FUNC( LO_IMPORT ) { @@ -1040,20 +1311,6 @@ HB_FUNC( LO_UNLINK ) hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } -HB_FUNC( PQSERVERVERSION ) -{ -#if PG_VERSION_NUM >= 80000 - PGconn * conn = hb_PGconn_par( 1 ); - - if( conn ) - hb_retni( PQserverVersion( conn ) ); - else - hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); -#else - hb_retni( 0 ); -#endif -} - HB_FUNC( PQGETCANCEL ) { #if PG_VERSION_NUM >= 80000 @@ -1079,7 +1336,7 @@ HB_FUNC( PQCANCEL ) errbuf[ 0 ] = '\0'; - hb_retl( PQcancel( cancel, errbuf, sizeof( errbuf ) - 1 ) == 1 ); + hb_retl( PQcancel( cancel, errbuf, sizeof( errbuf ) - 1 ) ? HB_TRUE : HB_FALSE ); hb_storc( errbuf, 2 ); } @@ -1173,6 +1430,22 @@ HB_FUNC( PQPUTCOPYEND ) #endif } +HB_FUNC( PG_ENCODING_TO_CHAR ) +{ + hb_retc( pg_encoding_to_char( hb_parni( 1 ) ) ); +} + +/* 31.19 Behavior in Threaded Programs */ + +HB_FUNC( PQISTHREADSAFE ) +{ +#if PG_VERSION_NUM >= 80200 + hb_retl( PQisthreadsafe() ? HB_TRUE : HB_FALSE ); +#else + hb_retl( HB_FALSE ); +#endif +} + #if 0 TODO: Implement Full Large Objects Support diff --git a/contrib/hbpgsql/postgres.ch b/contrib/hbpgsql/postgres.ch index 4bd52c84d9..7d052f54ec 100644 --- a/contrib/hbpgsql/postgres.ch +++ b/contrib/hbpgsql/postgres.ch @@ -2,7 +2,7 @@ * PostgreSQL RDBMS low-level (client API) interface code. * * Copyright 2016 P.Chornyj - * Copyright 2014 Viktor Szakats (vszakats.net/harbour) + * Copyright 2014 Viktor Szakats (vsz.me/hb) * Copyright 2003 Rodrigo Moreno rodrigo_moreno@yahoo.com * * This program is free software; you can redistribute it and/or modify @@ -60,6 +60,13 @@ #define CONNECTION_SSL_STARTUP 7 #define CONNECTION_NEEDED 8 +/* PQconnectPoll(), PQresetPoll() */ +#define PGRES_POLLING_FAILED 0 +#define PGRES_POLLING_READING 1 +#define PGRES_POLLING_WRITING 2 +#define PGRES_POLLING_OK 3 +#define PGRES_POLLING_ACTIVE 4 + /* PQresultStatus() */ #define PGRES_EMPTY_QUERY 0 #define PGRES_COMMAND_OK 1 @@ -86,4 +93,34 @@ #define HBPG_META_TABLECOL 6 #define HBPG_META_LEN_ 6 +/* PQsetErrorVerbosity() */ +#define PQERRORS_TERSE 0 +#define PQERRORS_DEFAULT 1 +#define PQERRORS_VERBOSE 2 + +/* PQping() */ +#define PQPING_OK 0 /* server is accepting connections */ +#define PQPING_REJECT 1 /* server is alive but rejecting connections */ +#define PQPING_NO_RESPONSE 2 /* could not establish connection */ +#define PQPING_NO_ATTEMPT 3 /* connection not attempted (bad params) */ + +/* PQresultErrorField() fieldcode parameters */ +#define PG_DIAG_SEVERITY hb_BCode( "S" ) +#define PG_DIAG_SQLSTATE hb_BCode( "C" ) +#define PG_DIAG_MESSAGE_PRIMARY hb_BCode( "M" ) +#define PG_DIAG_MESSAGE_DETAIL hb_BCode( "D" ) +#define PG_DIAG_MESSAGE_HINT hb_BCode( "H" ) +#define PG_DIAG_STATEMENT_POSITION hb_BCode( "P" ) +#define PG_DIAG_INTERNAL_POSITION hb_BCode( "p" ) +#define PG_DIAG_INTERNAL_QUERY hb_BCode( "q" ) +#define PG_DIAG_CONTEXT hb_BCode( "W" ) +#define PG_DIAG_SCHEMA_NAME hb_BCode( "s" ) +#define PG_DIAG_TABLE_NAME hb_BCode( "t" ) +#define PG_DIAG_COLUMN_NAME hb_BCode( "c" ) +#define PG_DIAG_DATATYPE_NAME hb_BCode( "d" ) +#define PG_DIAG_CONSTRAINT_NAME hb_BCode( "n" ) +#define PG_DIAG_SOURCE_FILE hb_BCode( "F" ) +#define PG_DIAG_SOURCE_LINE hb_BCode( "L" ) +#define PG_DIAG_SOURCE_FUNCTION hb_BCode( "R" ) + #endif diff --git a/contrib/hbpgsql/rddcopy.c b/contrib/hbpgsql/rddcopy.c index 07917cc900..5edbe6028d 100644 --- a/contrib/hbpgsql/rddcopy.c +++ b/contrib/hbpgsql/rddcopy.c @@ -274,8 +274,8 @@ HB_FUNC( HB_PQCOPYFROMWA ) static const char * sc_szDelim = ","; const char * szTable = hb_parcx( 2 ); - PHB_ITEM pWhile = hb_param( 3, HB_IT_BLOCK ); - PHB_ITEM pFor = hb_param( 4, HB_IT_BLOCK ); + PHB_ITEM pWhile = hb_param( 3, HB_IT_EVALITEM ); + PHB_ITEM pFor = hb_param( 4, HB_IT_EVALITEM ); PHB_ITEM pFields = hb_param( 5, HB_IT_ARRAY ); HB_ULONG nCount = hb_parnldef( 6, 0 ); HB_BOOL str_rtrim = hb_parldef( 7, HB_TRUE ); @@ -296,8 +296,7 @@ HB_FUNC( HB_PQCOPYFROMWA ) pItem = hb_itemNew( NULL ); - context = ( pgCopyContext * ) hb_xgrab( sizeof( pgCopyContext ) ); - memset( context, 0, sizeof( pgCopyContext ) ); + context = ( pgCopyContext * ) hb_xgrabz( sizeof( pgCopyContext ) ); context->buffer = ( char * ) hb_xgrab( sizeof( char ) * nBufLen * 1400 ); context->position = 0; diff --git a/contrib/hbpgsql/tests/async.prg b/contrib/hbpgsql/tests/async.prg index ce8642c354..d0145debdc 100644 --- a/contrib/hbpgsql/tests/async.prg +++ b/contrib/hbpgsql/tests/async.prg @@ -1,18 +1,21 @@ -/* - * This sample show howto use asynchronous/nonblocking queries - */ +/* This sample show howto use asynchronous/non-blocking queries */ #require "hbpgsql" #include "inkey.ch" -PROCEDURE Main( cServer, cDatabase, cUser, cPass ) +PROCEDURE Main( cHost, cDatabase, cUser, cPass ) LOCAL conn CLS - ? "Connect", conn := PQconnectdb( "dbname = " + cDatabase + " host = " + cServer + " user = " + cUser + " password = " + cPass + " port = 5432" ) + ? "Connect", conn := PQconnectdb( ; + "dbname = '" + hb_defaultValue( cDatabase, "postgres" ) + "' " + ; + "host = '" + hb_defaultValue( cHost, "localhost" ) + "' " + ; + "user = '" + hb_defaultValue( cUser, hb_UserName() ) + "' " + ; + "password = '" + hb_defaultValue( cPass, "" ) + "' " + ; + "port = 5432" ) ? "Conection status", PQerrorMessage( conn ), PQstatus( conn ) @@ -22,9 +25,9 @@ PROCEDURE Main( cServer, cDatabase, cUser, cPass ) RETURN -PROCEDURE Query( conn, cQuery, lCancel ) +STATIC PROCEDURE Query( conn, cQuery, lCancel ) - LOCAL pCancel, cErrMsg := Space( 30 ) + LOCAL pCancel, cErrMsg := "" LOCAL res, x, y, cTime ? "PQSendQuery", PQsendQuery( conn, cQuery ) @@ -32,20 +35,16 @@ PROCEDURE Query( conn, cQuery, lCancel ) cTime := Time() CLEAR TYPEAHEAD - DO WHILE Inkey() != K_ESC + DO WHILE hb_keyStd( Inkey() ) != K_ESC DevPos( Row(), 20 ) - DevOut( "Processing: " + ElapTime( cTime, Time() ) ) + DevOut( "Processing:", ElapTime( cTime, Time() ) ) Inkey( 1 ) IF lCancel - IF .T. - pCancel := PQgetCancel( conn ) - ? "Canceled: ", PQcancel( pCancel, @cErrMsg ), cErrMsg - pCancel := NIL - ELSE - ? PQrequestCancel( conn ) /* Deprecated */ - ENDIF + pCancel := PQgetCancel( conn ) + ? "Canceled:", PQcancel( pCancel, @cErrMsg ), cErrMsg + pCancel := NIL ENDIF IF PQconsumeInput( conn ) @@ -55,15 +54,17 @@ PROCEDURE Query( conn, cQuery, lCancel ) ENDIF ENDDO - IF Inkey() != K_ESC + IF hb_keyStd( Inkey() ) != K_ESC ? "PQgetResult", hb_ValToExp( res := PQgetResult( conn ) ) - FOR x := 1 TO PQlastrec( res ) - ? - FOR y := 1 TO PQfcount( res ) - ?? PQgetvalue( res, x, y ), " " + IF ! Empty( res ) + FOR x := 1 TO PQlastrec( res ) + ? + FOR y := 1 TO PQfcount( res ) + ?? PQgetvalue( res, x, y ), " " + NEXT NEXT - NEXT + ENDIF ELSE ? "Canceling Query", PQrequestCancel( conn ) ENDIF diff --git a/contrib/hbpgsql/tests/cache.prg b/contrib/hbpgsql/tests/cache.prg index 2188668e4b..9d1281581f 100644 --- a/contrib/hbpgsql/tests/cache.prg +++ b/contrib/hbpgsql/tests/cache.prg @@ -1,56 +1,49 @@ -/* - * This samples show how to use dbf to cache postgres records. - */ +/* This samples show how to use dbf to cache postgres records. */ #require "hbpgsql" -#define DB_ALIAS 1 -#define DB_FILE 2 -#define DB_QUERY 3 -#define DB_ROW 4 -#define DB_FETCH 5 +#define DB_ALIAS 1 // Table Name +#define DB_QUERY 2 // Object Query +#define DB_ROW 3 // Current Row +#define DB_FETCH 4 // Fetch Status -STATIC s_oServer -STATIC s_aTableTemp := {} -STATIC s_aTempDBF := {} +THREAD STATIC t_oServer +THREAD STATIC t_aTableTemp := {} -PROCEDURE Main( cServer, cDatabase, cUser, cPass ) +PROCEDURE Main( cHost, cDatabase, cUser, cPass ) LOCAL i - LOCAL cQuery - IF SQLConnect( cServer, cDatabase, cUser, cPass ) + IF SQLConnect( cHost, hb_defaultValue( cDatabase, "postgres" ), cUser, cPass ) + QuickQuery( "DROP TABLE test" ) - cQuery := "CREATE TABLE test ( " - cQuery += " codigo integer primary key, " - cQuery += " descri char(50), " - cQuery += " email varchar(50) ) " - SQLQuery( cQuery ) + SQLQuery( "CREATE TABLE test (" + ; + " codigo integer primary key," + ; + " descri char(50)," + ; + " email varchar(50) )" ) SQLOpen( "nomes", "SELECT * FROM test" ) FOR i := 1 TO 50 - APPEND BLANK - REPLACE codigo WITH i - REPLACE descri WITH "test " + Str( i ) + dbAppend() + nomes->codigo := i + nomes->descri := "test " + hb_ntos( i ) NEXT SQLApplyUpdates() - cQuery := "SELECT * FROM test WHERE codigo >= :1 ORDER BY codigo" - cQuery := SQLPrepare( cQuery, 1 ) - SQLOpen( "nomes", cQuery ) + SQLOpen( "nomes", SQLPrepare( "SELECT * FROM test WHERE codigo >= :1 ORDER BY codigo", 1 ) ) DO WHILE ! Eof() ? RecNo(), nomes->Codigo, nomes->descri, nomes->email IF RecNo() == 10 - DELETE + dbDelete() ENDIF IF RecNo() == 20 - REPLACE email WITH "teste" + nomes->email := "teste" ENDIF SQLFetch() @@ -63,7 +56,6 @@ PROCEDURE Main( cServer, cDatabase, cUser, cPass ) RETURN - /* Put theses functions in a library */ FUNCTION SQLApplyUpdates() @@ -76,11 +68,9 @@ FUNCTION SQLApplyUpdates() LOCAL lError := .F. LOCAL cError - i := AScan( s_aTableTemp, {| aVal | aVal[ DB_ALIAS ] == cAlias } ) + IF ( i := AScan( t_aTableTemp, {| aVal | aVal[ DB_ALIAS ] == cAlias } ) ) > 0 - IF i != 0 - - oQuery := s_aTableTemp[ i ][ 3 ] + oQuery := t_aTableTemp[ i ][ DB_QUERY ] FOR i := 1 TO LastRec() @@ -88,7 +78,7 @@ FUNCTION SQLApplyUpdates() IF i > oQuery:LastRec() - /* Verifica se eh um registro novo */ + /* Check if it's a new record */ IF ! Deleted() oRow := oQuery:GetBlankRow() @@ -100,44 +90,32 @@ FUNCTION SQLApplyUpdates() NEXT oQuery:Append( oRow ) - cError := oQuery:ErrorMsg() - lError := oQuery:NetErr() - ENDIF ELSE - oRow := oQuery:GetRow( i ) - lUpdate := .F. - IF Deleted() - oQuery:Delete( oRow ) cError := oQuery:ErrorMsg() lError := oQuery:NetErr() ELSE + /* Update if any of the fields have changed */ - /* Faz update, mas compara quais campos sao diferentes */ - + lUpdate := .F. FOR x := 1 TO FCount() - - IF oRow:FieldPos( FieldName( x ) ) != 0 - - IF ! ( FieldGet( x ) == oRow:FieldGet( FieldName( x ) ) ) - oRow:FieldPut( FieldName( x ), FieldGet( x ) ) - lUpdate := .T. - ENDIF + IF oRow:FieldPos( FieldName( x ) ) != 0 .AND. ; + ! FieldGet( x ) == oRow:FieldGet( FieldName( x ) ) + oRow:FieldPut( FieldName( x ), FieldGet( x ) ) + lUpdate := .T. ENDIF NEXT IF lUpdate - oQuery:Update( oRow ) cError := oQuery:ErrorMsg() lError := oQuery:NetErr() - ENDIF ENDIF ENDIF @@ -149,68 +127,43 @@ FUNCTION SQLApplyUpdates() ENDIF IF lError - Alert( cError ) + ? cError ENDIF RETURN ! lError - PROCEDURE SQLCloseTemp( cAlias ) LOCAL x - IF ! Empty( Select( cAlias ) ) + IF Select( cAlias ) != 0 ( cAlias )->( dbCloseArea() ) ENDIF - x := AScan( s_aTableTemp, {| aVal | aVal[ DB_ALIAS ] == cAlias } ) - - IF ! Empty( x ) - ADel( s_aTableTemp, x ) - // ASize( s_aTableTemp, Len( s_aTableTemp ) - 1 ) + IF ( x := AScan( t_aTableTemp, {| aVal | aVal[ DB_ALIAS ] == cAlias } ) ) > 0 + hb_ADel( t_aTableTemp, x, .F. /* .T. */ ) ENDIF RETURN - PROCEDURE SQLGarbageCollector() - LOCAL i + LOCAL item LOCAL oQuery dbCloseAll() - FOR i := 1 TO Len( s_aTableTemp ) - /* Apaga arquivos dbfs criados */ - FErase( s_aTableTemp[ i ][ DB_FILE ] ) - oQuery := s_aTableTemp[ i ][ DB_QUERY ] - - IF oQuery != NIL + FOR EACH item IN t_aTableTemp + IF ( oQuery := item[ DB_QUERY ] ) != NIL oQuery:Destroy() ENDIF NEXT - FOR i := 1 TO Len( s_aTempDBF ) - IF hb_FileExists( s_aTempDBF[ i ] ) - FErase( s_aTempDBF[ i ] ) - ENDIF - - IF hb_FileExists( StrTran( s_aTempDBF[ i ], ".tmp", ".dbf" ) ) - FErase( StrTran( s_aTempDBF[ i ], ".tmp", ".dbf" ) ) - ENDIF - - IF hb_FileExists( StrTran( s_aTempDBF[ i ], ".tmp", ".dbt" ) ) - FErase( StrTran( s_aTempDBF[ i ], ".tmp", ".dbt" ) ) - ENDIF - NEXT - - s_aTableTemp := {} - s_aTempDBF := {} + ASize( t_aTableTemp, 0 ) RETURN - -FUNCTION SQLFetch( fetchall ) +FUNCTION SQLFetch( lFetchAll ) LOCAL oQuery LOCAL oRow @@ -219,26 +172,24 @@ FUNCTION SQLFetch( fetchall ) LOCAL nPos LOCAL lEof := .F. - hb_default( @Fetchall, .F. ) + hb_default( @lFetchAll, .F. ) - /* Procura pela tabela no array */ - i := AScan( s_aTableTemp, {| aVal | aVal[ DB_ALIAS ] == cAlias } ) + /* Search for table in array */ + IF ( i := AScan( t_aTableTemp, {| aVal | aVal[ DB_ALIAS ] == cAlias } ) ) > 0 - IF i != 0 - /* Traz registros da base de dados */ + /* Get database records */ + oQuery := t_aTableTemp[ i ][ DB_QUERY ] + nPos := t_aTableTemp[ i ][ DB_ROW ] + 1 - oQuery := s_aTableTemp[ i ][ DB_QUERY ] - nPos := s_aTableTemp[ i ][ DB_ROW ] + 1 - - IF Fetchall - s_aTableTemp[ i ][ DB_FETCH ] := .T. + IF lFetchAll + t_aTableTemp[ i ][ DB_FETCH ] := .T. ENDIF IF oQuery:LastRec() >= nPos y := nPos - DO WHILE nPos <= iif( FetchAll, oQuery:LastRec(), y ) + DO WHILE nPos <= iif( lFetchAll, oQuery:LastRec(), y ) oRow := oQuery:GetRow( nPos ) dbAppend() @@ -246,7 +197,7 @@ FUNCTION SQLFetch( fetchall ) FieldPut( FieldPos( oRow:FieldName( x ) ), oRow:FieldGet( x ) ) NEXT - s_aTableTemp[ i ][ DB_ROW ] := nPos + t_aTableTemp[ i ][ DB_ROW ] := nPos nPos++ ENDDO @@ -260,7 +211,6 @@ FUNCTION SQLFetch( fetchall ) RETURN lEof - PROCEDURE SQLFetchAll() SQLFetch( .T. ) @@ -268,25 +218,19 @@ PROCEDURE SQLFetchAll() RETURN - FUNCTION SQLOpen( cAlias, cQuery, xFetch, cOrder ) - LOCAL cFile - LOCAL Result := .T. LOCAL x - LOCAL s_oServer + LOCAL oServer LOCAL oQuery - LOCAL aStrudbf LOCAL lFetch - s_oServer := SQLCurrentServer() + oServer := SQLCurrentServer() cAlias := Upper( cAlias ) - /* Procura por query na area temporaria */ - x := AScan( s_aTableTemp, {| aVal | aVal[ DB_ALIAS ] == cAlias } ) - - IF ! Empty( x ) - oQuery := s_aTableTemp[ x ][ 3 ] + /* Search by query in temporary area */ + IF ( x := AScan( t_aTableTemp, {| aVal | aVal[ DB_ALIAS ] == cAlias } ) ) > 0 + oQuery := t_aTableTemp[ x ][ DB_QUERY ] oQuery:Destroy() ENDIF @@ -297,28 +241,18 @@ FUNCTION SQLOpen( cAlias, cQuery, xFetch, cOrder ) ENDIF ENDIF - cQuery := cQuery - oQuery := s_oServer:Query( cQuery ) + oQuery := oServer:Query( cQuery ) IF oQuery:NetErr() - Alert( oQuery:ErrorMsg() ) + ? oQuery:ErrorMsg() RETURN .F. ENDIF - IF Empty( Select( cAlias ) ) - /* Pega estrutura da base de dados */ - aStrudbf := oQuery:Struct() - - /* Cria tabela */ - cFile := TempFile() - dbCreate( cFile, aStrudbf ) - - /* Abre Tabela */ - dbUseArea( .T., NIL, cFile, cAlias, .F. ) - + IF Select( cAlias ) == 0 + hb_dbCreateTemp( cAlias, oQuery:Struct() ) ELSE - Select( cAlias ) - ZAP + dbSelectArea( cAlias ) + hb_dbZap() ENDIF IF xFetch != NIL @@ -327,182 +261,165 @@ FUNCTION SQLOpen( cAlias, cQuery, xFetch, cOrder ) lFetch := .F. ENDIF - /* Se nao houver query na area temporaria entao adiciona, caso contrario, apenas atualiza */ - IF Empty( x ) - AAdd( s_aTableTemp, { ; - cAlias, ; // Table Name - cFile, ; // Temporary File Name - oQuery, ; // Object Query - 0, ; // Current Row - lFetch } ) // Fetch Status + /* If there is no query in the temporary area then add, otherwise just refresh. */ + IF x == 0 + AAdd( t_aTableTemp, { ; + cAlias, ; // DB_ALIAS + oQuery, ; // DB_QUERY + 0, ; // DB_ROW + lFetch } ) // DB_FETCH ELSE - s_aTableTemp[ x ][ DB_QUERY ] := oQuery - s_aTableTemp[ x ][ DB_ROW ] := 0 - s_aTableTemp[ x ][ DB_FETCH ] := lFetch + t_aTableTemp[ x ][ DB_QUERY ] := oQuery + t_aTableTemp[ x ][ DB_ROW ] := 0 + t_aTableTemp[ x ][ DB_FETCH ] := lFetch ENDIF - /* Traz registros da base de dados */ + /* Get database records */ SQLFetch( lFetch ) IF lFetch dbGoTop() ENDIF - RETURN result + RETURN .T. - -FUNCTION SQLConnect( cServer, cDatabase, cUser, cPassword, cSchema ) +FUNCTION SQLConnect( cHost, cDatabase, cUser, cPassword, cSchema ) LOCAL lRetval := .T. - s_oServer := TPQServer():New( cServer, cDatabase, cUser, cPassWord, 5432, cSchema ) - IF s_oServer:NetErr() - Alert( s_oServer:ErrorMsg() ) + t_oServer := TPQServer():New( cHost, cDatabase, cUser, cPassword, 5432, cSchema ) + IF t_oServer:NetErr() + ? t_oServer:ErrorMsg() lRetval := .F. ENDIF - s_oServer:lAllCols := .F. + t_oServer:lAllCols := .F. RETURN lRetval - PROCEDURE SQLDestroy() - IF s_oServer != NIL - s_oServer:Destroy() + IF t_oServer != NIL + t_oServer:Destroy() ENDIF RETURN - FUNCTION SQLCurrentServer - RETURN s_oServer - + RETURN t_oServer FUNCTION SQLQuery( cQuery ) - LOCAL oQuery := s_oServer:Query( cQuery ) + LOCAL oQuery := t_oServer:Query( cQuery ) IF oQuery:NetErr() - Alert( cQuery + ":" + oQuery:ErrorMsg() ) + ? cQuery + ":" + oQuery:ErrorMsg() ENDIF RETURN oQuery - FUNCTION SQLExecQuery( cQuery ) - LOCAL oQuery - LOCAL result := .T. + LOCAL oQuery := t_oServer:Query( cQuery ) - oQuery := s_oServer:Query( cQuery ) IF oQuery:NetErr() - Alert( "Cannot execute " + cQuery + ":" + oQuery:ErrorMsg() ) - - result := .F. - ELSE - oQuery:Destroy() + ? "Cannot execute", cQuery + ":" + oQuery:ErrorMsg() + RETURN .F. ENDIF - RETURN result + oQuery:Destroy() + RETURN .T. FUNCTION SQLPrepare( cQuery, ... ) LOCAL i, x IF PCount() >= 2 - /* Limpa espacos desnecessarios */ - DO WHILE At( Space( 2 ), cQuery ) != 0 + /* Remove unnecessary whitespace */ + DO WHILE Space( 2 ) $ cQuery cQuery := StrTran( cQuery, Space( 2 ), Space( 1 ) ) ENDDO - /* Coloca {} nos parametros */ + /* Place {} in the parameters */ FOR i := 1 TO PCount() - 1 - IF ! Empty( x := At( ":" + hb_ntos( i ), cQuery ) ) + IF ( x := At( ":" + hb_ntos( i ), cQuery ) ) > 0 cQuery := Stuff( cQuery, x, 0, "{" ) cQuery := Stuff( cQuery, x + Len( hb_ntos( i ) ) + 2, 0, "}" ) ENDIF NEXT - /* Substitui parametros por valores passados */ + /* Replace parameters with values */ FOR i := 2 TO PCount() x := hb_PValue( i ) - IF x != NIL .AND. Empty( x ) + DO CASE + CASE x != NIL .AND. Empty( x ) x := "null" - ELSEIF HB_ISNUMERIC( x ) + CASE HB_ISNUMERIC( x ) x := hb_ntos( x ) - ELSEIF HB_ISDATE( x ) - x := DToQ( x ) + CASE HB_ISDATE( x ) + x := "'" + hb_DToC( x, "yyyy-mm-dd" ) + "'" - ELSEIF HB_ISLOGICAL( x ) + CASE HB_ISLOGICAL( x ) x := iif( x, "'t'", "'f'" ) - ELSEIF HB_ISSTRING( x ) + CASE HB_ISSTRING( x ) x := SToQ( RTrim( x ) ) - ELSE + OTHERWISE x := "null" - ENDIF + ENDCASE cQuery := StrTran( cQuery, "{:" + hb_ntos( i - 1 ) + "}", x ) NEXT ENDIF - cQuery := StrTran( cQuery, "==", "=" ) - cQuery := StrTran( cQuery, "!=", "<>" ) - cQuery := StrTran( cQuery, ".and.", "and" ) - cQuery := StrTran( cQuery, ".or.", "or" ) - cQuery := StrTran( cQuery, ".not.", "not" ) + RETURN hb_StrReplace( cQuery, { ; + "==" => "=" , ; + "!=" => "<>" , ; + ".and." => "and" , ; + ".or." => "or" , ; + ".not." => "not" } ) - RETURN cQuery - - -/* Pega resultado de uma sequence */ +/* Get next result of a sequence */ FUNCTION SQLSequence( Sequence_name ) RETURN Val( QuickQuery( "SELECT nextval(" + SToQ( sequence_name ) + ")" ) ) - PROCEDURE SQLStartTrans() - IF PQtransactionStatus( s_oServer:pDB ) != PQTRANS_INTRANS - s_oServer:StartTransaction() + IF PQtransactionStatus( t_oServer:pDB ) != PQTRANS_INTRANS + t_oServer:StartTransaction() ENDIF RETURN - FUNCTION SQLInTrans() - RETURN PQtransactionStatus( s_oServer:pDB ) == PQTRANS_INTRANS - + RETURN PQtransactionStatus( t_oServer:pDB ) == PQTRANS_INTRANS PROCEDURE SQLCommitTrans() - s_oServer:Commit() + t_oServer:Commit() RETURN - PROCEDURE SQLRollbackTrans() - s_oServer:rollback() + t_oServer:rollback() RETURN - -/* Faz querie que retorna apenas 1 valor de coluna */ +/* Do query that returns only 1 column value */ FUNCTION QuickQuery( cQuery ) - LOCAL pQuery LOCAL result := "" LOCAL temp, aTemp LOCAL x, y - pQuery := PQexec( s_oServer:pDB, cQuery ) + LOCAL pQuery := PQexec( t_oServer:pDB, cQuery ) IF PQresultStatus( pQuery ) == PGRES_TUPLES_OK IF PQlastrec( pQuery ) != 0 @@ -525,51 +442,5 @@ FUNCTION QuickQuery( cQuery ) RETURN result - -PROCEDURE MakeDBF( cAlias, aStructure, aIndex ) - - LOCAL cFile, i, cIndex, cKey - - hb_default( @aIndex, {} ) - - cFile := TempFile() - dbCreate( cFile, aStructure ) - - /* Abre Tabela */ - dbUseArea( .T., NIL, cFile, cAlias, .F. ) - - FOR i := 1 TO Len( aIndex ) - cKey := aIndex[ i ] - cIndex := TempFile() - - INDEX ON &cKey TO ( cIndex ) - - AAdd( s_aTempDBF, cIndex ) - NEXT - - AAdd( s_aTempDBF, cFile ) - - RETURN - - -FUNCTION TempFile( cPath, cExt ) - - LOCAL cString - - hb_default( @cPath, hb_DirTemp() ) - hb_default( @cExt, "tmp" ) - - cString := cPath + StrZero( Int( hb_Random( Val( StrTran( Time(), ":" ) ) ) ), 8 ) + "." + cExt - - DO WHILE hb_FileExists( cString ) - cString := cPath + StrZero( Int( hb_Random( Val( StrTran( Time(), ":" ) ) ) ), 8 ) + "." + cExt - ENDDO - - RETURN cString - - -FUNCTION DToQ( cData ) - RETURN "'" + Str( Year( cData ), 4 ) + "-" + StrZero( Month( cData ), 2 ) + "-" + StrZero( Day( cData ), 2 ) + "'" - FUNCTION SToQ( cData ) RETURN "'" + cData + "'" diff --git a/contrib/hbpgsql/tests/dbf2pg.prg b/contrib/hbpgsql/tests/dbf2pg.prg index 6db6c78847..a64ddda3d4 100644 --- a/contrib/hbpgsql/tests/dbf2pg.prg +++ b/contrib/hbpgsql/tests/dbf2pg.prg @@ -47,27 +47,26 @@ #require "hbpgsql" -#include "inkey.ch" #include "fileio.ch" +#include "inkey.ch" #include "hbextcdp.ch" -PROCEDURE Main( ... ) +PROCEDURE Main() - LOCAL cTok - LOCAL cHostName := "localhost" - LOCAL cUser := "postgres" - LOCAL cPassWord := "" - LOCAL cDataBase, cTable, cFile - LOCAL aDbfStruct, i + LOCAL cHostName + LOCAL cUser + LOCAL cPassword + LOCAL cDatabase := "postgres", cTable, cFile + LOCAL i LOCAL lCreateTable := .F. LOCAL oServer, oTable, oRecord LOCAL cField - LOCAL sType - LOCAL dType + LOCAL cTypeDB + LOCAL cTypePG LOCAL cValue LOCAL nCommit := 100 - LOCAL nHandle + LOCAL hFile LOCAL nCount := 0 LOCAL nRecno := 0 LOCAL lTruncate := .F. @@ -75,80 +74,48 @@ PROCEDURE Main( ... ) LOCAL cPath := "public" Set( _SET_DATEFORMAT, "yyyy-mm-dd" ) - SET DELETE ON + Set( _SET_DELETED, .T. ) - rddSetDefault( "DBFDBT" ) - - IF PCount() < 6 - help() - QUIT - ENDIF - - i := 1 /* Scan parameters and setup workings */ - DO WHILE i <= PCount() - - cTok := hb_PValue( i++ ) - - DO CASE - CASE cTok == "-h" - cHostName := hb_PValue( i++ ) - - CASE cTok == "-d" - cDataBase := hb_PValue( i++ ) - - CASE cTok == "-t" - cTable := hb_PValue( i++ ) - - CASE cTok == "-f" - cFile := hb_PValue( i++ ) - - CASE cTok == "-u" - cUser := hb_PValue( i++ ) - - CASE cTok == "-p" - cPassWord := hb_PValue( i++ ) - - CASE cTok == "-c" - lCreateTable := .T. - - CASE cTok == "-x" - lTruncate := .T. - - CASE cTok == "-s" - lUseTrans := .T. - - CASE cTok == "-m" - nCommit := Val( hb_PValue( i++ ) ) - - CASE cTok == "-r" - nRecno := Val( hb_PValue( i++ ) ) - - CASE cTok == "-e" - cPath := hb_PValue( i++ ) - - CASE cTok == "-cp" - hb_cdpSelect( hb_PValue( i++ ) ) + FOR i := 1 TO PCount() + SWITCH hb_PValue( i ) + CASE "-h" ; cHostName := hb_PValue( ++i ) ; EXIT + CASE "-d" ; cDatabase := hb_PValue( ++i ) ; EXIT + CASE "-t" ; cTable := AllTrim( hb_PValue( ++i ) ) ; EXIT + CASE "-f" ; cFile := hb_PValue( ++i ) ; EXIT + CASE "-u" ; cUser := hb_PValue( ++i ) ; EXIT + CASE "-p" ; cPassword := hb_PValue( ++i ) ; EXIT + CASE "-c" ; lCreateTable := .T. ; EXIT + CASE "-x" ; lTruncate := .T. ; EXIT + CASE "-s" ; lUseTrans := .T. ; EXIT + CASE "-m" ; nCommit := Val( hb_PValue( ++i ) ) ; EXIT + CASE "-r" ; nRecno := Val( hb_PValue( ++i ) ) ; EXIT + CASE "-e" ; cPath := hb_PValue( ++i ) ; EXIT + CASE "-cp" ; Set( _SET_DBCODEPAGE, hb_PValue( ++i ) ) ; EXIT OTHERWISE help() - QUIT - ENDCASE - ENDDO + RETURN + ENDSWITCH + NEXT - // create log file - IF ( nHandle := FCreate( RTrim( cTable ) + ".log" ) ) == F_ERROR - ? "Cannot create log file" - QUIT + IF Empty( cTable ) .OR. cFile == "" + help() + RETURN ENDIF - USE ( cFile ) SHARED - aDbfStruct := dbStruct() + // create log file + IF ( hFile := hb_vfOpen( cTable + ".log", FO_CREAT + FO_TRUNC + FO_WRITE ) ) == NIL + ? "Cannot create log file" + RETURN + ENDIF - oServer := TPQServer():New( cHostName, cDatabase, cUser, cPassWord, NIL, cPath ) + USE ( cFile ) SHARED READONLY + + oServer := TPQServer():New( cHostName, cDatabase, cUser, cPassword, , cPath ) IF oServer:NetErr() ? oServer:ErrorMsg() - QUIT + RETURN ENDIF oServer:lallCols := .F. @@ -158,18 +125,18 @@ PROCEDURE Main( ... ) oServer:DeleteTable( cTable ) IF oServer:NetErr() ? oServer:ErrorMsg() - FWrite( nHandle, "Error: " + oServer:ErrorMsg() + hb_eol() ) - FClose( nHandle ) - QUIT + hb_vfWrite( hFile, "Error: " + oServer:ErrorMsg() + hb_eol() ) + hb_vfClose( hFile ) + RETURN ENDIF ENDIF - oServer:CreateTable( cTable, aDbfStruct ) + oServer:CreateTable( cTable, dbStruct() ) IF oServer:NetErr() ? oServer:ErrorMsg() - FWrite( nHandle, "Error: " + oServer:ErrorMsg() + hb_eol() ) - FClose( nHandle ) - QUIT + hb_vfWrite( hFile, "Error: " + oServer:ErrorMsg() + hb_eol() ) + hb_vfClose( hFile ) + RETURN ENDIF ENDIF @@ -177,75 +144,57 @@ PROCEDURE Main( ... ) oServer:Execute( "truncate table " + cTable ) IF oServer:NetErr() ? oServer:ErrorMsg() - FWrite( nHandle, "Error: " + oServer:ErrorMsg() + hb_eol() ) - FClose( nHandle ) - QUIT + hb_vfWrite( hFile, "Error: " + oServer:ErrorMsg() + hb_eol() ) + hb_vfClose( hFile ) + RETURN ENDIF ENDIF oTable := oServer:Query( "SELECT * FROM " + cTable + " LIMIT 1" ) IF oTable:NetErr() - Alert( oTable:ErrorMsg() ) - FWrite( nHandle, "Error: " + oTable:ErrorMsg() + hb_eol() ) - FClose( nHandle ) - QUIT + ? oTable:ErrorMsg() + hb_vfWrite( hFile, "Error: " + oTable:ErrorMsg() + hb_eol() ) + hb_vfClose( hFile ) + RETURN ENDIF IF lUseTrans oServer:StartTransaction() ENDIF - FWrite( nHandle, "Start: " + Time() + hb_eol() ) + hb_vfWrite( hFile, "Start: " + Time() + hb_eol() ) - ? "Start: ", Time() + ? "Start:", Time() ? - IF ! Empty( nRecno ) + IF nRecno != 0 dbGoto( nRecno ) ENDIF - DO WHILE ! Eof() .AND. Inkey() != K_ESC .AND. ( Empty( nRecno ) .OR. nRecno == RecNo() ) + DO WHILE ! Eof() .AND. hb_keyStd( Inkey() ) != K_ESC .AND. ( nRecno == 0 .OR. nRecno == RecNo() ) oRecord := oTable:GetBlankRow() FOR i := 1 TO oTable:FCount() cField := Lower( oTable:FieldName( i ) ) - sType := FieldType( FieldPos( cField ) ) - dType := oRecord:FieldType( i ) + cTypeDB := Left( hb_FieldType( FieldPos( cField ) ), 1 ) + cTypePG := oRecord:FieldType( i ) cValue := FieldGet( FieldPos( cField ) ) IF cValue != NIL - IF dType != sType - IF dType == "C" .AND. sType == "N" - cValue := Str( cValue ) - - ELSEIF dType == "C" .AND. sType == "D" - cValue := DToC( cValue ) - - ELSEIF dType == "C" .AND. sType == "L" - cValue := iif( cValue, "S", "N" ) - - ELSEIF dType == "N" .AND. sType == "C" - cValue := Val( cValue ) - - ELSEIF dType == "N" .AND. sType == "D" - cValue := Val( DToS( cValue ) ) - - ELSEIF dType == "N" .AND. sType == "L" - cValue := iif( cValue, 1, 0 ) - - ELSEIF dType == "D" .AND. sType == "C" - cValue := CToD( cValue ) - - ELSEIF dType == "D" .AND. sType == "N" - cValue := hb_SToD( Str( cValue ) ) - - ELSEIF dType == "L" .AND. sType == "N" - cValue := ! Empty( cValue ) - - ELSEIF dType == "L" .AND. sType == "C" - cValue := iif( AllTrim( cValue ) $ "YySs1", .T., .F. ) - - ENDIF + IF cTypePG != cTypeDB + DO CASE + CASE cTypePG == "C" .AND. cTypeDB $ "NIYF8BZ24" ; cValue := hb_ntos( cValue ) + CASE cTypePG == "C" .AND. cTypeDB == "D" ; cValue := DToC( cValue ) + CASE cTypePG == "C" .AND. cTypeDB $ "T@" ; cValue := hb_TToC( cValue ) + CASE cTypePG == "C" .AND. cTypeDB == "L" ; cValue := iif( cValue, "S", "N" ) + CASE cTypePG == "N" .AND. cTypeDB $ "CQ" ; cValue := Val( cValue ) + CASE cTypePG == "N" .AND. cTypeDB == "D" ; cValue := Val( DToS( cValue ) ) + CASE cTypePG == "N" .AND. cTypeDB == "L" ; cValue := iif( cValue, 1, 0 ) + CASE cTypePG == "D" .AND. cTypeDB $ "CQ" ; cValue := CToD( cValue ) + CASE cTypePG == "D" .AND. cTypeDB $ "NIYF8BZ24" ; cValue := hb_SToD( hb_ntos( cValue ) ) + CASE cTypePG == "L" .AND. cTypeDB $ "NIYF8BZ24" ; cValue := ! Empty( cValue ) + CASE cTypePG == "L" .AND. cTypeDB $ "CQ" ; cValue := AllTrim( cValue ) $ "YySs1" + ENDCASE ENDIF IF cValue != NIL @@ -262,18 +211,18 @@ PROCEDURE Main( ... ) IF oTable:NetErr() ? - ? "Error Record: ", RecNo(), Left( oTable:ErrorMsg(), 70 ) + ? "Error Record:", RecNo(), Left( oTable:ErrorMsg(), 70 ) ? - FWrite( nHandle, "Error at record: " + hb_ntos( RecNo() ) + " Description: " + oTable:ErrorMsg() + hb_eol() ) + hb_vfWrite( hFile, "Error at record: " + hb_ntos( RecNo() ) + " Description: " + oTable:ErrorMsg() + hb_eol() ) ELSE nCount++ ENDIF dbSkip() - IF ( nCount % nCommit ) == 0 + IF nCount % nCommit == 0 DevPos( Row(), 1 ) - DevOut( "imported recs: " + Str( nCount ) ) + DevOut( "imported recs:", hb_ntos( nCount ) ) IF lUseTrans oServer:commit() @@ -282,41 +231,38 @@ PROCEDURE Main( ... ) ENDIF ENDDO - IF ( nCount % nCommit ) != 0 - IF lUseTrans - oServer:commit() - ENDIF + IF nCount % nCommit != 0 .AND. lUseTrans + oServer:commit() ENDIF - FWrite( nHandle, "End: " + Time() + ", records in dbf: " + hb_ntos( RecNo() ) + ", imported recs: " + hb_ntos( nCount ) + hb_eol() ) + hb_vfWrite( hFile, "End: " + Time() + ", records in dbf: " + hb_ntos( RecNo() ) + ", imported recs: " + hb_ntos( nCount ) + hb_eol() ) - ? "End: ", Time() - ? + ? "End:", Time() - FClose( nHandle ) + hb_vfClose( hFile ) + + dbCloseAll() - CLOSE ALL oTable:Destroy() oServer:Destroy() RETURN -PROCEDURE Help() +STATIC PROCEDURE Help() ? "dbf2pg - dbf file to PostgreSQL table conversion utility" - ? "-h hostname (default: localhost)" - ? "-u user (default: root)" - ? "-p password (default no password)" - ? "-d name of database to use" - ? "-t name of table to add records to" + ? "-h hostname" + ? "-u user" + ? "-p password" + ? "-d name of database to use (default: postgres)" + ? "-t name of table to add records to (required)" ? "-c delete existing table and create a new one" - ? "-f name of .dbf file to import" + ? "-f name of .dbf file to import (required)" ? "-x truncate table before append records" ? "-s use transaction" ? "-m commit interval" ? "-r insert only record number" ? "-e search path" - - ? "" + ? RETURN diff --git a/contrib/hbpgsql/tests/ping.prg b/contrib/hbpgsql/tests/ping.prg new file mode 100644 index 0000000000..02538714cb --- /dev/null +++ b/contrib/hbpgsql/tests/ping.prg @@ -0,0 +1,75 @@ +#require "hbpgsql" + +PROCEDURE Main( cHost, cDatabase, cUser, cPass ) + + LOCAL nVersion + + CLS + + ? "The function PQlibVersion() returns", nVersion := PQlibVersion() + ? + + IF nVersion < 90100 + ? "Function PQping() not supported." + QUIT + ENDIF + + hb_default( @cHost, "localhost" ) + hb_default( @cDatabase, "postgres" ) + hb_default( @cUser, hb_UserName() ) + hb_default( @cPass, "" ) + + /* PQping() reports the status of the server. + It accepts connection parameters identical to those of PQconnectdb(). + It is not, however, necessary to supply correct user name, password, + or database name values to obtain the server status. */ + HB_SYMBOL_UNUSED( cDatabase ) + HB_SYMBOL_UNUSED( cUser ) + HB_SYMBOL_UNUSED( cPass ) + + /* the ConnInfo string can be empty to use ALL default parameters */ + PingTest( "" ) + + /* 'database' is not allowed parameter key, you can find the currently + recognized parameter key words on + https://www.postgresql.org/docs/9.1/static/libpq-connect.html */ + PingTest( ; + "host = localhost" + " " + ; + "database = test" ) + + /* the default port for Postgres is 5432, but we can try connect to an + alternate port and see what happens. */ + PingTest( ; + "host = " + cHost + " " + ; + "port = " + "3333" ) + + /* next attempt */ + PingTest( ; + "host = " + cHost + " " + ; + "hostaddr = " + "127.0.0.1" + " " + ; + "port = " + "5432" + " " + ; + "connect_timeout = " + "10" ) + + RETURN + +STATIC PROCEDURE PingTest( cConnInfo ) + + ? "cConnInfo is", '"' + cConnInfo + '"' + ? "PQPing( cConnInfo ) returns:", GetPingResult( PQPing( cConnInfo ) ) + ? + + RETURN + +STATIC FUNCTION GetPingResult( n ) + + LOCAL aMsg := { ; + { "PQPING_OK" , "Server is accepting connections" }, ; + { "PQPING_REJECT" , "Server is alive but rejecting connections" }, ; + { "PQPING_NO_RESPONSE", "Could not establish connection" }, ; + { "PQPING_NO_ATTEMPT" , "Connection not attempted (bad params)" } } + + IF n >= 0 .AND. n < Len( aMsg ) + RETURN aMsg[ n + 1 ][ 1 ] + " " + aMsg[ n + 1 ][ 2 ] + ENDIF + + RETURN "" diff --git a/contrib/hbpgsql/tests/simple.prg b/contrib/hbpgsql/tests/simple.prg index 54dbf27260..ced82e8ad1 100644 --- a/contrib/hbpgsql/tests/simple.prg +++ b/contrib/hbpgsql/tests/simple.prg @@ -2,15 +2,13 @@ PROCEDURE Main( cHost, cDatabase, cUser, cPass ) - LOCAL oServer, oQuery, oRow, i, x, aTables, aStruct + LOCAL cQuery, oQuery, oRow, i, x - LOCAL cQuery - - oServer := TPQServer():New( cHost, cDatabase, cUser, cPass ) + LOCAL oServer := TPQServer():New( cHost, hb_defaultValue( cDatabase, "postgres" ), cUser, cPass ) IF oServer:NetErr() ? oServer:ErrorMsg() - QUIT + RETURN ENDIF oServer:SetVerbosity( 2 ) @@ -18,32 +16,29 @@ PROCEDURE Main( cHost, cDatabase, cUser, cPass ) ? "Tables..." - FOR x := 1 TO 1 - aTables := oServer:ListTables() - - FOR i := 1 TO Len( aTables ) - ? aTables[ i ] - NEXT + FOR EACH i IN oServer:ListTables() + ? i NEXT - IF oServer:TableExists( "TEST" ) - ? oQuery := oServer:Execute( "DROP TABLE Test" ) + IF oServer:TableExists( "test" ) + ? oQuery := oServer:Execute( "DROP TABLE test" ) oQuery:Destroy() ENDIF ? "Creating test table..." - cQuery := "CREATE TABLE test(" - cQuery += " Code integer not null primary key, " - cQuery += " dept Integer, " - cQuery += " Name Varchar(40), " - cQuery += " Sales boolean, " - cQuery += " Tax Float4, " - cQuery += " Salary Double Precision, " - cQuery += " Budget Numeric(12,2), " - cQuery += " Discount Numeric(5,2), " - cQuery += " Creation Date, " - cQuery += " Description text ) " + cQuery := ; + "CREATE TABLE test(" + ; + " Code integer not null primary key," + ; + " dept Integer," + ; + " Name Varchar(40)," + ; + " Sales boolean," + ; + " Tax Float4," + ; + " Salary Double Precision," + ; + " Budget Numeric(12,2)," + ; + " Discount Numeric(5,2)," + ; + " Creation Date," + ; + " Description text )" oQuery := oServer:Query( cQuery ) @@ -54,21 +49,20 @@ PROCEDURE Main( cHost, cDatabase, cUser, cPass ) oQuery:Destroy() ? "Structure of test table" - aStruct := oServer:TableStruct( "test" ) - FOR i := 1 TO Len( aStruct ) + FOR EACH i IN oServer:TableStruct( "test" ) ? - FOR x := 1 TO Len( aStruct[ i ] ) - ?? aStruct[ i ][ x ], " " + FOR EACH x IN i + ?? x, "" NEXT NEXT - ? "Inserting, declared transaction control " + ? "Inserting, declared transaction control" oServer:StartTransaction() FOR i := 1 TO 10 cQuery := "INSERT INTO test(code, dept, name, sales, tax, salary, budget, Discount, Creation, Description) " + ; - "VALUES( " + Str( i ) + ", 2, 'TEST', 'y', 5, 3000, 1500.2, 7.5, '2003-12-17', 'Short Description about what ? ')" + "VALUES( " + hb_ntos( i ) + ", 2, 'TEST', 'y', 5, 3000, 1500.2, 7.5, '2003-12-17', 'Short Description about what ?')" oQuery := oServer:Query( cQuery ) @@ -83,13 +77,11 @@ PROCEDURE Main( cHost, cDatabase, cUser, cPass ) oQuery := oServer:Query( "SELECT code, name, description, sales FROM test" ) - aStruct := oQuery:Struct() - - FOR i := 1 TO Len( aStruct ) - ? aStruct[ i ][ 1 ], aStruct[ i ][ 2 ], aStruct[ i ][ 3 ], aStruct[ i ][ 4 ] + FOR EACH i IN oQuery:Struct() + ? i[ 1 ], i[ 2 ], i[ 3 ], i[ 4 ] NEXT - ? "Fields: ", oQuery:FCount() + ? "Fields:", oQuery:FCount() oRow := oQuery:Blank() @@ -130,12 +122,12 @@ PROCEDURE Main( cHost, cDatabase, cUser, cPass ) oRow := oQuery:getrow() oRow:FieldPut( 2, "My Second test" ) - ? "Update: ", oQuery:Update( oRow ) + ? "Update:", oQuery:Update( oRow ) ENDIF IF oQuery:RecNo() == 60 oRow := oQuery:getrow() - ? "Delete: ", oQuery:Delete( oRow ) + ? "Delete:", oQuery:Delete( oRow ) ENDIF oQuery:Skip() diff --git a/contrib/hbpgsql/tests/stress.prg b/contrib/hbpgsql/tests/stress.prg index 5d975e2168..fb3f0f08e7 100644 --- a/contrib/hbpgsql/tests/stress.prg +++ b/contrib/hbpgsql/tests/stress.prg @@ -1,10 +1,8 @@ -/* - * VERY IMPORTANT: Don't use this querys as sample, they are used for stress tests !!! - */ +/* IMPORTANT: Don't use these queries as sample, they are used for stress tests!!! */ #require "hbpgsql" -PROCEDURE Main( cServer, cDatabase, cUser, cPass ) +PROCEDURE Main( cHost, cDatabase, cUser, cPass ) LOCAL conn, res, i, x @@ -12,13 +10,18 @@ PROCEDURE Main( cServer, cDatabase, cUser, cPass ) CLS - ? "Connecting...." - conn := PQconnectdb( "dbname = " + cDatabase + " host = " + cServer + " user = " + cUser + " password = " + cPass + " port = 5432" ) + ? "Connecting..." + conn := PQconnectdb( ; + "dbname = '" + hb_defaultValue( cDatabase, "postgres" ) + "' " + ; + "host = '" + hb_defaultValue( cHost, "localhost" ) + "' " + ; + "user = '" + hb_defaultValue( cUser, hb_UserName() ) + "' " + ; + "password = '" + hb_defaultValue( cPass, "" ) + "' " + ; + "port = 5432" ) ? PQstatus( conn ), PQerrorMessage( conn ) IF PQstatus( conn ) != CONNECTION_OK - QUIT + RETURN ENDIF ? "Dropping table..." @@ -26,55 +29,57 @@ PROCEDURE Main( cServer, cDatabase, cUser, cPass ) PQexec( conn, "DROP TABLE test" ) ? "Creating test table..." - cQuery := "CREATE TABLE test(" - cQuery += " Code integer not null primary key, " - cQuery += " dept Integer, " - cQuery += " Name Varchar(40), " - cQuery += " Sales boolean, " - cQuery += " Tax Float4, " - cQuery += " Salary Double Precision, " - cQuery += " Budget Numeric(12,2), " - cQuery += " Discount Numeric(5,2), " - cQuery += " Creation Date, " - cQuery += " Description text ) " + cQuery := ; + "CREATE TABLE test(" + ; + " Code integer not null primary key," + ; + " dept Integer," + ; + " Name Varchar(40)," + ; + " Sales boolean," + ; + " Tax Float4," + ; + " Salary Double Precision," + ; + " Budget Numeric(12,2)," + ; + " Discount Numeric(5,2)," + ; + " Creation Date," + ; + " Description text )" PQexec( conn, cQuery ) PQexec( conn, "SELECT code, dept, name, sales, salary, creation FROM test" ) PQexec( conn, "BEGIN" ) + ? FOR i := 1 TO 10000 - @ 15, 0 SAY "Inserting values...." + Str( i ) + @ 15, 0 SAY "Inserting values... " + hb_ntos( i ) cQuery := "INSERT INTO test(code, dept, name, sales, salary, creation) " + ; - "VALUES( " + Str( i ) + "," + Str( i + 1 ) + ", 'DEPARTMENT NAME " + StrZero( i ) + "', 'y', " + Str( 300.49 + i ) + ", '2003-12-28' )" + "VALUES( " + hb_ntos( i ) + "," + hb_ntos( i + 1 ) + ", 'DEPARTMENT NAME " + StrZero( i ) + "', 'y', " + hb_ntos( 300.49 + i ) + ", '2003-12-28' )" PQexec( conn, cQuery ) - IF Mod( i, 100 ) == 0 + IF i % 100 == 0 ? PQexec( conn, "COMMIT" ) ? PQexec( conn, "BEGIN" ) ENDIF NEXT FOR i := 5000 TO 7000 - @ 16, 0 SAY "Deleting values...." + Str( i ) + @ 16, 0 SAY "Deleting values... " + hb_ntos( i ) - cQuery := "DELETE FROM test WHERE code = " + Str( i ) + cQuery := "DELETE FROM test WHERE code = " + hb_ntos( i ) PQexec( conn, cQuery ) - IF Mod( i, 100 ) == 0 + IF i % 100 == 0 PQexec( conn, "COMMIT" ) PQexec( conn, "BEGIN" ) ENDIF NEXT FOR i := 2000 TO 3000 - @ 17, 0 SAY "Updating values...." + Str( i ) + @ 17, 0 SAY "Updating values... " + hb_ntos( i ) - cQuery := "UPDATE FROM test SET salary = 400 WHERE code = " + Str( i ) + cQuery := "UPDATE FROM test SET salary = 400 WHERE code = " + hb_ntos( i ) PQexec( conn, cQuery ) - IF Mod( i, 100 ) == 0 + IF i % 100 == 0 PQexec( conn, "COMMIT" ) PQexec( conn, "BEGIN" ) ENDIF @@ -83,21 +88,20 @@ PROCEDURE Main( cServer, cDatabase, cUser, cPass ) res := PQexec( conn, "SELECT sum(salary) as sum_salary FROM test WHERE code between 1 and 4000" ) IF PQresultStatus( res ) == PGRES_TUPLES_OK - @ 18, 0 SAY "Sum values...." + PQgetvalue( res, 1, 1 ) + @ 18, 0 SAY "Sum values... " + PQgetvalue( res, 1, 1 ) ENDIF x := 0 FOR i := 1 TO 4000 - res := PQexec( conn, "SELECT salary FROM test WHERE code = " + Str( i ) ) + res := PQexec( conn, "SELECT salary FROM test WHERE code = " + hb_ntos( i ) ) IF PQresultStatus( res ) == PGRES_TUPLES_OK x += Val( PQgetvalue( res, 1, 1 ) ) - @ 19, 0 SAY "Sum values...." + Str( x ) + @ 19, 0 SAY "Sum values... " + hb_ntos( x ) ENDIF NEXT ? "Closing..." - conn := NIL RETURN diff --git a/contrib/hbpgsql/tests/test.prg b/contrib/hbpgsql/tests/test.prg index bbc3a23d66..b3af6b9635 100644 --- a/contrib/hbpgsql/tests/test.prg +++ b/contrib/hbpgsql/tests/test.prg @@ -1,37 +1,39 @@ #require "hbpgsql" -PROCEDURE Main() +PROCEDURE Main( cHost, cDatabase, cUser, cPass ) - LOCAL conn, res, aTemp, x, y, pFile - LOCAL cDb := "test" - LOCAL cUser := "user" - LOCAL cPass := "pass" + LOCAL conn, res, x, y, pFile CLS - conn := PQsetdbLogin( "localhost", "5432", NIL, NIL, cDb, cUser, cPass ) + conn := PQsetdbLogin( cHost, "5432", , , cDatabase, cUser, cPass ) ? PQdb( conn ), PQuser( conn ), PQpass( conn ), PQhost( conn ), PQport( conn ), PQtty( conn ), PQoptions( conn ) - conn := PQconnectdb( "dbname = " + cDb + " host = localhost user = " + cUser + " password = " + cPass + " port = 5432" ) + conn := PQconnectdb( ; + "dbname = '" + hb_defaultValue( cDatabase, "postgres" ) + "' " + ; + "host = '" + hb_defaultValue( cHost, "localhost" ) + "' " + ; + "user = '" + hb_defaultValue( cUser, hb_UserName() ) + "' " + ; + "password = '" + hb_defaultValue( cPass, "" ) + "' " + ; + "port = 5432" ) ? PQstatus( conn ), PQerrorMessage( conn ) IF PQstatus( conn ) != CONNECTION_OK - QUIT + RETURN ENDIF - ? "Blocking: ", PQisnonblocking( conn ), PQsetnonblocking( conn, .T. ), PQisnonblocking( conn ) + ? "Blocking:", PQisnonblocking( conn ), PQsetnonblocking( conn, .T. ), PQisnonblocking( conn ) - pFile := PQtracecreate( "trace.log" ) + pFile := PQtracecreate( hb_DirBase() + "trace.log" ) PQtrace( conn, pFile ) - ? "Verbose: ", PQsetErrorVerbosity( conn, 2 ) + ? "Verbose:", PQsetErrorVerbosity( conn, 2 ) - ? ; - "Protocol: ", PQprotocolVersion( conn ), ; - " Server Version: ", PQserverVersion( conn ), ; - " Client Encoding: ", PQsetClientEncoding( conn, "ASCII" ), ; - "New encode: ", PQclientEncoding( conn ) + ? "Protocol:", PQprotocolVersion( conn ) + ? "Server version:", PQserverVersion( conn ) + ? "Client encoding (old):", pg_encoding_to_char( PQclientEncoding( conn ) ) + ? "Set client encoding:", PQsetClientEncoding( conn, "UTF-8" ) + ? "Client encoding (new):", pg_encoding_to_char( PQclientEncoding( conn ) ) ? PQdb( conn ), PQuser( conn ), PQpass( conn ), PQhost( conn ), PQport( conn ), PQtty( conn ), PQoptions( conn ) @@ -42,8 +44,8 @@ PROCEDURE Main() res := PQexec( conn, "create table products ( product_no numeric(10), name varchar(20), price numeric(10,2) )" ) ? PQresultStatus( res ), PQresultErrorMessage( res ) - res := PQexecParams( conn, "insert into products(product_no, name, price) values ($1, $2, $3)", { "2", "bread", "10.95" } ) - ? "Oid Row: ", PQoidValue( res ), PQoidStatus( res ) + res := PQexecParams( conn, "insert into products( product_no, name, price ) values ($1, $2, $3)", { "2", "bread", "10.95" } ) + ? "Oid Row:", PQoidValue( res ), PQoidStatus( res ) IF PQresultStatus( res ) != PGRES_COMMAND_OK ? PQresultStatus( res ), PQresultErrorMessage( res ) @@ -55,16 +57,15 @@ PROCEDURE Main() ? PQresultStatus( res ), PQresultErrorMessage( res ) ENDIF - ? "Binary: ", PQbinaryTuples( res ) - ? "Rows: ", PQntuples( res ), "Cols: ", PQnfields( res ) + ? "Binary:", PQbinaryTuples( res ) + ? "Rows:", PQntuples( res ) + ? "Cols:", PQnfields( res ) ? PQfname( res, 1 ), PQftable( res, 1 ), PQftype( res, 1 ), PQfnumber( res, "name" ), PQfmod( res, 1 ), PQfsize( res, 1 ), PQgetisnull( res, 1, 1 ) - aTemp := PQmetadata( res ) - - FOR x := 1 TO Len( aTemp ) - ? "Linha 1: " + FOR EACH x IN PQmetadata( res ) + ? "Line 1:", "" FOR y := 1 TO 6 - ?? aTemp[ x ][ y ], ", " + ?? x[ y ], ",", "" NEXT NEXT diff --git a/contrib/hbpgsql/tpostgre.prg b/contrib/hbpgsql/tpostgre.prg index bf8be7fa79..24e4d97b83 100644 --- a/contrib/hbpgsql/tpostgre.prg +++ b/contrib/hbpgsql/tpostgre.prg @@ -56,170 +56,161 @@ CREATE CLASS TPQServer - VAR pDb - VAR lTrans - VAR lallCols INIT .T. - VAR Schema INIT "public" - VAR lError INIT .F. - VAR cError INIT "" - VAR lTrace INIT .F. - VAR pTrace - VAR lNull INIT .F. + VAR pDb + VAR lTrans + VAR lAllCols INIT .T. + VAR lNull INIT .F. + VAR Schema INIT "public" + VAR lError INIT .F. + VAR cError INIT "" + VAR lTrace INIT .F. + VAR pTrace - METHOD New( cHost, cDatabase, cUser, cPass, nPort, Schema ) - METHOD Destroy() - METHOD Close() INLINE ::Destroy() + METHOD New( cHost, cDatabase, cUser, cPass, nPort, cSchema, hCustom ) + METHOD Destroy() + METHOD Close() INLINE ::Destroy() - METHOD StartTransaction() - METHOD TransactionStatus() INLINE PQtransactionStatus( ::pDb ) - METHOD Commit() - METHOD Rollback() + METHOD StartTransaction() + METHOD TransactionStatus() INLINE PQtransactionStatus( ::pDb ) + METHOD Commit() + METHOD Rollback() - METHOD Query( cQuery, lNull ) - METHOD Execute( cQuery ) INLINE ::Query( cQuery ) - METHOD SetSchema( cSchema ) + METHOD Query( cQuery, lNull ) + METHOD Execute( cQuery ) INLINE ::Query( cQuery ) + METHOD SetSchema( cSchema ) - METHOD NetErr() INLINE ::lError - METHOD ErrorMsg() INLINE ::cError + METHOD NetErr() INLINE ::lError + METHOD ErrorMsg() INLINE ::cError - METHOD TableExists( cTable ) - METHOD ListTables() - METHOD TableStruct( cTable ) - METHOD CreateTable( cTable, aStruct ) - METHOD DeleteTable( cTable ) - METHOD TraceOn( cFile ) - METHOD TraceOff() - METHOD SetVerbosity( num ) INLINE PQsetErrorVerbosity( ::pDb, iif( num >= 0 .AND. num <= 2, num, 1 ) ) - METHOD SetNull( lNewSet ) + METHOD TableExists( cTable ) + METHOD ListTables() + METHOD TableStruct( cTable ) + METHOD CreateTable( cTable, aStruct ) + METHOD DeleteTable( cTable ) + METHOD TraceOn( cFile ) + METHOD TraceOff() + METHOD SetVerbosity( num ) INLINE PQsetErrorVerbosity( ::pDb, iif( num >= 0 .AND. num <= 2, num, 1 ) ) + METHOD SetNull( lValue ) ENDCLASS - -METHOD New( cHost, cDatabase, cUser, cPass, nPort, Schema ) CLASS TPQserver +METHOD New( cHost, cDatabase, cUser, cPass, nPort, cSchema, hCustom ) CLASS TPQserver LOCAL res + LOCAL item - hb_default( @nPort, 5432 ) + LOCAL cConnect := ; + iif( HB_ISSTRING( cDatabase ), " dbname = " + EscapeParam( cDatabase ), "" ) + ; + iif( HB_ISSTRING( cHost ), " host = " + EscapeParam( cHost ), "" ) + ; + iif( HB_ISSTRING( cUser ), " user = " + EscapeParam( cUser ), "" ) + ; + iif( HB_ISSTRING( cPass ), " password = " + EscapeParam( cPass ), "" ) + ; + iif( HB_ISNUMERIC( nPort ), " port = " + hb_ntos( nPort ), "" ) - ::pDB := PQconnectdb( "dbname = " + cDatabase + " host = " + cHost + " user = " + cUser + " password = " + cPass + " port = " + hb_ntos( nPort ) ) + IF HB_ISHASH( hCustom ) + FOR EACH item IN hCustom + cConnect += " " + item:__enumKey() + " = " + EscapeParam( item ) + NEXT + ENDIF + + ::pDB := PQconnectdb( cConnect ) IF PQstatus( ::pDb ) != CONNECTION_OK ::lError := .T. ::cError := PQerrorMessage( ::pDb ) ELSE - IF ! Empty( Schema ) - ::SetSchema( Schema ) + IF HB_ISSTRING( cSchema ) + ::SetSchema( cSchema ) ELSE res := PQexec( ::pDB, "SELECT current_schema()" ) IF PQresultStatus( res ) == PGRES_TUPLES_OK ::Schema := PQgetvalue( res, 1, 1 ) ENDIF - res := NIL ENDIF ENDIF RETURN Self -METHOD Destroy() CLASS TPQserver +METHOD PROCEDURE Destroy() CLASS TPQserver ::TraceOff() ::pDb := NIL - RETURN NIL + RETURN METHOD SetSchema( cSchema ) CLASS TPQserver LOCAL res - LOCAL result := .F. + LOCAL result IF PQstatus( ::pDb ) == CONNECTION_OK ::Schema := cSchema res := PQexec( ::pDB, "SET search_path TO " + cSchema ) result := ( PQresultStatus( res ) == PGRES_COMMAND_OK ) - res := NIL + ELSE + result := .F. ENDIF RETURN result METHOD StartTransaction() CLASS TPQserver - LOCAL res - LOCAL lError + LOCAL res := PQexec( ::pDB, "BEGIN" ) - res := PQexec( ::pDB, "BEGIN" ) - lError := PQresultStatus( res ) != PGRES_COMMAND_OK - - IF lError - ::lError := .T. + IF ( ::lError := PQresultStatus( res ) != PGRES_COMMAND_OK ) ::cError := PQresultErrorMessage( res ) ELSE - ::lError := .F. ::cError := "" ENDIF - RETURN lError + RETURN ::lError METHOD Commit() CLASS TPQserver - LOCAL res - LOCAL lError + LOCAL res := PQexec( ::pDB, "COMMIT" ) - res := PQexec( ::pDB, "COMMIT" ) - lError := PQresultStatus( res ) != PGRES_COMMAND_OK - - IF lError - ::lError := .T. + IF ( ::lError := PQresultStatus( res ) != PGRES_COMMAND_OK ) ::cError := PQresultErrorMessage( res ) ELSE - ::lError := .F. ::cError := "" ENDIF - RETURN lError + RETURN ::lError METHOD Rollback() CLASS TPQserver - LOCAL res - LOCAL lError + LOCAL res := PQexec( ::pDB, "ROLLBACK" ) - res := PQexec( ::pDB, "ROLLBACK" ) - lError := PQresultStatus( res ) != PGRES_COMMAND_OK - - IF lError - ::lError := .T. + IF ( ::lError := PQresultStatus( res ) != PGRES_COMMAND_OK ) ::cError := PQresultErrorMessage( res ) ELSE - ::lError := .F. ::cError := "" ENDIF - RETURN lError + RETURN ::lError METHOD Query( cQuery, lNull ) CLASS TPQserver - IF !HB_IsLogical( lNull ) + + IF ! HB_ISLOGICAL( lNull ) lNull := ::lNull ENDIF - RETURN TPQQuery():New( ::pDB, cQuery, ::lallCols, ::Schema,, lNull ) + + RETURN TPQQuery():New( ::pDB, cQuery, ::lAllCols, ::Schema, lNull ) METHOD TableExists( cTable ) CLASS TPQserver - LOCAL result := .F. - LOCAL cQuery - LOCAL res + LOCAL result - cQuery := "SELECT table_name " - cQuery += " FROM information_schema.tables " - cQuery += " WHERE table_type = 'BASE TABLE' AND table_schema = " + DataToSql( ::Schema ) + " AND table_name = " + DataToSql( Lower( cTable ) ) + LOCAL res := PQexec( ::pDB, ; + "SELECT table_name" + ; + " FROM information_schema.tables" + ; + " WHERE table_type = 'BASE TABLE' AND table_schema = " + DataToSql( ::Schema ) + " AND table_name = " + DataToSql( Lower( cTable ) ) ) - res := PQexec( ::pDB, cQuery ) - - IF PQresultStatus( res ) == PGRES_TUPLES_OK - result := ( PQlastrec( res ) != 0 ) - ::lError := .F. - ::cError := "" - ELSE - ::lError := .T. + IF ( ::lError := PQresultStatus( res ) != PGRES_TUPLES_OK ) ::cError := PQresultErrorMessage( res ) + result := .F. + ELSE + ::cError := "" + result := ( PQlastrec( res ) != 0 ) ENDIF RETURN result @@ -227,25 +218,20 @@ METHOD TableExists( cTable ) CLASS TPQserver METHOD ListTables() CLASS TPQserver LOCAL result := {} - LOCAL cQuery - LOCAL res LOCAL i - cQuery := "SELECT table_name " - cQuery += " FROM information_schema.tables " - cQuery += " WHERE table_schema = " + DataToSql( ::Schema ) + " AND table_type = 'BASE TABLE' " + LOCAL res := PQexec( ::pDB, ; + "SELECT table_name" + ; + " FROM information_schema.tables" + ; + " WHERE table_schema = " + DataToSql( ::Schema ) + " AND table_type = 'BASE TABLE'" ) - res := PQexec( ::pDB, cQuery ) - - IF PQresultStatus( res ) == PGRES_TUPLES_OK + IF ( ::lError := PQresultStatus( res ) != PGRES_TUPLES_OK ) + ::cError := PQresultErrorMessage( res ) + ELSE FOR i := 1 TO PQlastrec( res ) AAdd( result, PQgetvalue( res, i, 1 ) ) NEXT - ::lError := .F. ::cError := "" - ELSE - ::lError := .T. - ::cError := PQresultErrorMessage( res ) ENDIF RETURN result @@ -253,67 +239,68 @@ METHOD ListTables() CLASS TPQserver METHOD TableStruct( cTable ) CLASS TPQserver LOCAL result := {} - LOCAL cQuery - LOCAL res LOCAL i LOCAL cField LOCAL cType LOCAL nSize LOCAL nDec - cQuery := "SELECT column_name, data_type, character_maximum_length, numeric_precision, numeric_scale " - cQuery += " FROM information_schema.columns " - cQuery += " WHERE table_schema = " + DataToSql( ::Schema ) + " AND table_name = " + DataToSql( Lower( cTable ) ) - cQuery += "ORDER BY ordinal_position " + LOCAL res := PQexec( ::pDB, ; + "SELECT column_name, data_type, character_maximum_length, numeric_precision, numeric_scale" + ; + " FROM information_schema.columns" + ; + " WHERE table_schema = " + DataToSql( ::Schema ) + " AND table_name = " + DataToSql( Lower( cTable ) ) + ; + " ORDER BY ordinal_position" ) - res := PQexec( ::pDB, cQuery ) - - IF PQresultStatus( res ) == PGRES_TUPLES_OK + IF ( ::lError := PQresultStatus( res ) != PGRES_TUPLES_OK ) + ::cError := PQresultErrorMessage( res ) + ELSE + ::cError := "" FOR i := 1 TO PQlastrec( res ) cField := PQgetvalue( res, i, 1 ) cType := PQgetvalue( res, i, 2 ) - nSize := PQgetvalue( res, i, 4 ) - nDec := PQgetvalue( res, i, 5 ) + nSize := PQgetvalue( res, i, 4 ) /* string value */ + nDec := PQgetvalue( res, i, 5 ) /* string value */ - IF "char" $ cType + DO CASE + CASE "char" $ cType cType := "C" nSize := Val( PQgetvalue( res, i, 3 ) ) nDec := 0 - ELSEIF "text" $ cType + CASE "text" $ cType cType := "M" nSize := 10 nDec := 0 - ELSEIF "boolean" $ cType + CASE "boolean" $ cType cType := "L" nSize := 1 nDec := 0 - ELSEIF "smallint" $ cType + CASE "smallint" $ cType cType := "N" nSize := 5 nDec := 0 - ELSEIF "integer" $ cType .OR. "serial" $ cType + CASE "integer" $ cType .OR. "serial" $ cType cType := "N" nSize := 9 nDec := 0 - ELSEIF "bigint" $ cType .OR. "bigserial" $ cType + CASE "bigint" $ cType .OR. "bigserial" $ cType cType := "N" nSize := 19 nDec := 0 - ELSEIF "decimal" $ cType .OR. "numeric" $ cType + CASE "decimal" $ cType .OR. "numeric" $ cType cType := "N" nDec := Val( nDec ) /* Postgres doesn't store ".", but .dbf does, it can cause data width problem */ nSize := Val( nSize ) + iif( nDec > 0, 1, 0 ) - /* Numeric/Decimal without scale/precision can genarete big values, so, i limit this to 10,5 */ + /* Numeric/Decimal without scale/precision can generate big values, so, I limit this to 10,5 */ IF nDec > 100 nDec := 5 @@ -323,80 +310,82 @@ METHOD TableStruct( cTable ) CLASS TPQserver nSize := 15 ENDIF - ELSEIF "real" $ cType .OR. "float4" $ cType + CASE "real" $ cType .OR. "float4" $ cType cType := "N" nSize := 15 nDec := 4 - ELSEIF "double precision" $ cType .OR. "float8" $ cType + CASE "double precision" $ cType .OR. "float8" $ cType cType := "N" nSize := 19 nDec := 9 - ELSEIF "money" $ cType + CASE "money" $ cType cType := "N" nSize := 9 nDec := 2 - ELSEIF "timestamp" $ cType + CASE "timestamp" $ cType cType := "C" nSize := 20 nDec := 0 - ELSEIF "date" $ cType + CASE "date" $ cType cType := "D" nSize := 8 nDec := 0 - ELSEIF "time" $ cType + CASE "time" $ cType cType := "C" nSize := 10 nDec := 0 - ELSE - /* Unsuported */ + CASE "name" $ cType + cType := "C" + nSize := 64 + nDec := 0 + + CASE "oid" $ cType + cType := "N" + nSize := 19 + nDec := 0 + + OTHERWISE + /* Unsupported */ cType := "U" nSize := 0 nDec := -1 - ENDIF + ENDCASE - IF !( cType == "U" ) + IF ! cType == "U" AAdd( result, { cField, cType, nSize, nDec } ) ENDIF NEXT - - ::lError := .F. - ::cError := "" - ELSE - ::lError := .T. - ::cError := PQresultErrorMessage( res ) ENDIF RETURN result METHOD CreateTable( cTable, aStruct ) CLASS TPQserver - LOCAL result := .T. - LOCAL cQuery LOCAL res - LOCAL i + LOCAL fld - cQuery := "CREATE TABLE " + ::Schema + "." + cTable + "( " + LOCAL cQuery := "CREATE TABLE " + ::Schema + "." + cTable + "( " - FOR i := 1 TO Len( aStruct ) + FOR EACH fld IN aStruct - cQuery += aStruct[ i ][ _STRU_FIELDNAME ] + cQuery += fld[ _STRU_FIELDNAME ] - SWITCH aStruct[ i ][ _STRU_FIELDTYPE ] + SWITCH fld[ _STRU_FIELDTYPE ] CASE "C" - cQuery += " Char(" + hb_ntos( aStruct[ i ][ _STRU_FIELDLEN ] ) + ")" + cQuery += " Char(" + hb_ntos( fld[ _STRU_FIELDLEN ] ) + ")" EXIT CASE "D" cQuery += " Date " EXIT CASE "N" - cQuery += " Numeric(" + hb_ntos( aStruct[ i ][ _STRU_FIELDLEN ] ) + "," + hb_ntos( aStruct[ i ][ _STRU_FIELDDEC ] ) + ")" + cQuery += " Numeric(" + hb_ntos( fld[ _STRU_FIELDLEN ] ) + "," + hb_ntos( fld[ _STRU_FIELDDEC ] ) + ")" EXIT CASE "L" cQuery += " boolean " @@ -406,45 +395,32 @@ METHOD CreateTable( cTable, aStruct ) CLASS TPQserver EXIT ENDSWITCH - IF i == Len( aStruct ) - cQuery += ")" - ELSE - cQuery += "," - ENDIF + cQuery += iif( fld:__enumIsLast(), ")", "," ) NEXT res := PQexec( ::pDB, cQuery ) - IF PQresultStatus( res ) != PGRES_COMMAND_OK - result := .F. - ::lError := .T. + IF ( ::lError := PQresultStatus( res ) != PGRES_COMMAND_OK ) ::cError := PQresultErrorMessage( res ) ELSE - ::lError := .F. ::cError := "" ENDIF - RETURN result + RETURN ! ::lError METHOD DeleteTable( cTable ) CLASS TPQserver - LOCAL result := .T. - LOCAL res + LOCAL res := PQexec( ::pDB, "DROP TABLE " + ::Schema + "." + cTable ) - res := PQexec( ::pDB, "DROP TABLE " + ::Schema + "." + cTable ) - - IF PQresultStatus( res ) != PGRES_COMMAND_OK - result := .F. - ::lError := .T. + IF ( ::lError := PQresultStatus( res ) != PGRES_COMMAND_OK ) ::cError := PQresultErrorMessage( res ) ELSE - ::lError := .F. ::cError := "" ENDIF - RETURN result + RETURN ! ::lError -METHOD TraceOn( cFile ) CLASS TPQserver +METHOD PROCEDURE TraceOn( cFile ) CLASS TPQserver ::pTrace := PQtracecreate( cFile ) @@ -453,9 +429,9 @@ METHOD TraceOn( cFile ) CLASS TPQserver ::lTrace := .T. ENDIF - RETURN NIL + RETURN -METHOD TraceOff() CLASS TPQserver +METHOD PROCEDURE TraceOff() CLASS TPQserver IF ::pTrace != NIL PQuntrace( ::pDb ) @@ -464,98 +440,99 @@ METHOD TraceOff() CLASS TPQserver ::lTrace := .F. - RETURN NIL + RETURN -METHOD SetNull( lNewSet ) CLASS TPQserver - LOCAL lSet := ::lNull +METHOD SetNull( lValue ) CLASS TPQserver - IF HB_IsLogical( lNewSet ) - ::lNull := lNewSet + LOCAL lOldValue := ::lNull + + IF HB_ISLOGICAL( lValue ) + ::lNull := lValue ENDIF - RETURN lSet + RETURN lOldValue CREATE CLASS TPQQuery - VAR pQuery - VAR pDB + VAR pQuery + VAR pDB - VAR nResultStatus + VAR nResultStatus - VAR lBof - VAR lEof - VAR lRead - VAR lAllCols INIT .T. + VAR lBof + VAR lEof + VAR lRead + VAR lAllCols INIT .T. + VAR lNull INIT .F. - VAR lError INIT .F. - VAR cError INIT "" + VAR lError INIT .F. + VAR cError INIT "" - VAR cQuery - VAR nRecno - VAR nFields - VAR nLastrec + VAR cQuery + VAR nRecno + VAR nFields + VAR nLastrec - VAR aStruct - VAR aKeys - VAR TableName - VAR Schema - VAR rows INIT 0 - VAR lNull INIT .F. + VAR aStruct + VAR aKeys + VAR TableName + VAR Schema + VAR rows INIT 0 - METHOD New( pDB, cQuery, lallCols, cSchema, res, lNull ) - METHOD Destroy() - METHOD Close() INLINE ::Destroy() + METHOD New( pDB, cQuery, lAllCols, cSchema, res, lNull ) + METHOD Destroy() + METHOD Close() INLINE ::Destroy() - METHOD Refresh( lQuery, lMeta ) - METHOD Fetch() INLINE ::Skip() - METHOD Read() - METHOD Skip( nRecno ) + METHOD Refresh( lQuery, lMeta ) + METHOD Fetch() INLINE ::Skip() + METHOD Read() + METHOD Skip( nRecno ) - METHOD Bof() INLINE ::lBof - METHOD Eof() INLINE ::lEof - METHOD RecNo() INLINE ::nRecno - METHOD LastRec() INLINE ::nLastrec - METHOD Goto( nRecno ) + METHOD Bof() INLINE ::lBof + METHOD Eof() INLINE ::lEof + METHOD RecNo() INLINE ::nRecno + METHOD LastRec() INLINE ::nLastrec + METHOD Goto( nRecno ) - METHOD NetErr() INLINE ::lError - METHOD ErrorMsg() INLINE ::cError + METHOD NetErr() INLINE ::lError + METHOD ErrorMsg() INLINE ::cError - METHOD FCount() INLINE ::nFields - METHOD FieldName( nField ) - METHOD FieldPos( cField ) - METHOD FieldLen( nField ) - METHOD FieldDec( nField ) - METHOD FieldType( nField ) - METHOD Update( oRow ) - METHOD Delete( oRow ) - METHOD Append( oRow ) - METHOD SetKey() + METHOD FCount() INLINE ::nFields + METHOD FieldName( nField ) + METHOD FieldPos( cField ) + METHOD FieldLen( nField ) + METHOD FieldDec( nField ) + METHOD FieldType( nField ) + METHOD Update( oRow ) + METHOD Delete( oRow ) + METHOD Append( oRow ) + METHOD SetKey() - METHOD Changed( nField ) INLINE !( ::aRow[ nField ] == ::aOld[ nField ] ) - METHOD Blank() INLINE ::GetBlankRow() + METHOD Changed( nField ) INLINE ! ::aRow[ nField ] == ::aOld[ nField ] + METHOD Blank() INLINE ::GetBlankRow() - METHOD Struct() + METHOD Struct() - METHOD FieldGet( nField, nRow ) - METHOD GetRow( nRow ) - METHOD GetBlankRow() + METHOD FieldGet( nField, nRow ) + METHOD GetRow( nRow ) + METHOD GetBlankRow() ENDCLASS -METHOD New( pDB, cQuery, lallCols, cSchema, res, lNull ) CLASS TPQquery +METHOD New( pDB, cQuery, lAllCols, cSchema, res, lNull ) CLASS TPQquery ::pDB := pDB ::nResultStatus := -1 ::cQuery := cQuery - ::lallCols := lallCols + ::lAllCols := lAllCols ::Schema := cSchema IF res != NIL ::pQuery := res ENDIF - IF HB_IsLogical( lNull ) + IF HB_ISLOGICAL( lNull ) ::lNull := lNull ENDIF @@ -580,9 +557,6 @@ METHOD Refresh( lQuery, lMeta ) CLASS TPQquery LOCAL i LOCAL cType, nDec, nSize - hb_default( @lQuery, .T. ) - hb_default( @lMeta, .T. ) - ::Destroy() ::lBof := .T. @@ -592,7 +566,7 @@ METHOD Refresh( lQuery, lMeta ) CLASS TPQquery ::nLastrec := 0 ::Rows := 0 - IF lQuery + IF hb_defaultValue( lQuery, .T. ) res := PQexec( ::pDB, ::cQuery ) ELSE res := ::pQuery @@ -602,32 +576,33 @@ METHOD Refresh( lQuery, lMeta ) CLASS TPQquery IF ::nResultStatus == PGRES_TUPLES_OK - IF lMeta + IF hb_defaultValue( lMeta, .T. ) - ::aStruct := {} - ::nFields := 0 + ::aStruct := {} + ::nFields := 0 /* Get some information about metadata */ aTemp := PQmetadata( res ) IF HB_ISARRAY( aTemp ) - FOR i := 1 TO Len( aTemp ) + FOR EACH i IN aTemp - cType := aTemp[ i ][ HBPG_META_FIELDTYPE ] - nSize := aTemp[ i ][ HBPG_META_FIELDLEN ] - nDec := aTemp[ i ][ HBPG_META_FIELDDEC ] + cType := i[ HBPG_META_FIELDTYPE ] + nSize := i[ HBPG_META_FIELDLEN ] + nDec := i[ HBPG_META_FIELDDEC ] - IF "char" $ cType + DO CASE + CASE "char" $ cType cType := "C" - ELSEIF "numeric" $ cType .OR. "decimal" $ cType + CASE "numeric" $ cType .OR. "decimal" $ cType cType := "N" /* Postgres don't store ".", but .dbf does, it can cause data width problem */ IF nDec > 0 nSize++ - /* Numeric/Decimal without scale/precision can genarete big values, so, i limit this to 10,5 */ + /* Numeric/Decimal without scale/precision can generate big values, so, I limit this to 10,5 */ IF nDec > 100 nDec := 5 ENDIF @@ -637,78 +612,77 @@ METHOD Refresh( lQuery, lMeta ) CLASS TPQquery nSize := 15 ENDIF - ELSEIF "date" $ cType + CASE "date" $ cType cType := "D" nSize := 8 - ELSEIF "text" $ cType + CASE "text" $ cType cType := "M" - ELSEIF "boolean" $ cType + CASE "boolean" $ cType cType := "L" nSize := 1 - ELSEIF "smallint" $ cType + CASE "smallint" $ cType cType := "N" nSize := 5 - ELSEIF "integer" $ cType .OR. "serial" $ cType + CASE "integer" $ cType .OR. "serial" $ cType cType := "N" nSize := 9 - ELSEIF "bigint" $ cType .OR. "bigserial" $ cType + CASE "bigint" $ cType .OR. "bigserial" $ cType cType := "N" nSize := 19 - ELSEIF "real" $ cType .OR. "float4" $ cType + CASE "real" $ cType .OR. "float4" $ cType cType := "N" nSize := 15 nDec := 4 - ELSEIF "double precision" $ cType .OR. "float8" $ cType + CASE "double precision" $ cType .OR. "float8" $ cType cType := "N" nSize := 19 nDec := 9 - ELSEIF "money" $ cType + CASE "money" $ cType cType := "N" nSize := 10 nDec := 2 - ELSEIF "timestamp" $ cType + CASE "timestamp" $ cType cType := "C" nSize := 20 - ELSEIF "time" $ cType + CASE "time" $ cType cType := "C" nSize := 10 - ELSEIF "name" $ cType + CASE "name" $ cType cType := "C" nSize := 64 - ELSEIF "oid" $ cType + CASE "oid" $ cType cType := "N" nSize := 19 - ELSE - /* Unsuported */ + OTHERWISE + /* Unsupported */ cType := "K" - ENDIF + ENDCASE AAdd( aStruct, { ; - aTemp[ i ][ HBPG_META_FIELDNAME ], ; + i[ HBPG_META_FIELDNAME ], ; cType, ; nSize, ; nDec, ; - aTemp[ i ][ HBPG_META_TABLE ], ; - aTemp[ i ][ HBPG_META_TABLECOL ] } ) + i[ HBPG_META_TABLE ], ; + i[ HBPG_META_TABLECOL ] } ) NEXT ::nFields := PQfcount( res ) ::aStruct := aStruct - ENDIF ENDIF @@ -722,13 +696,13 @@ METHOD Refresh( lQuery, lMeta ) CLASS TPQquery ::lEof := .F. ENDIF - ELSEIF ::nResultStatus == PGRES_COMMAND_OK - ::lError := .F. - ::cError := "" - ::rows := Val( PQcmdTuples( res ) ) ELSE - ::lError := .T. - ::cError := PQresultErrorMessage( res ) + IF ( ::lError := ::nResultStatus == PGRES_COMMAND_OK ) + ::cError := "" + ::rows := Val( PQcmdTuples( res ) ) + ELSE + ::cError := PQresultErrorMessage( res ) + ENDIF ENDIF ::pQuery := res @@ -737,11 +711,15 @@ METHOD Refresh( lQuery, lMeta ) CLASS TPQquery METHOD Struct() CLASS TPQquery - LOCAL result := Array( Len( ::aStruct ) ) + LOCAL result := {} LOCAL i - FOR i := 1 TO Len( ::aStruct ) - result[ i ] := { ::aStruct[ i ][ _STRU_FIELDNAME ], ::aStruct[ i ][ _STRU_FIELDTYPE ], ::aStruct[ i ][ _STRU_FIELDLEN ], ::aStruct[ i ][ _STRU_FIELDDEC ] } + FOR EACH i IN ::aStruct + AAdd( result, { ; + i[ _STRU_FIELDNAME ], ; + i[ _STRU_FIELDTYPE ], ; + i[ _STRU_FIELDLEN ], ; + i[ _STRU_FIELDDEC ] } ) NEXT RETURN result @@ -749,10 +727,10 @@ METHOD Struct() CLASS TPQquery METHOD Read() CLASS TPQquery IF ! ::lEof - IF ! ::lRead - ::lRead := .T. - ELSE + IF ::lRead ::Skip( 1 ) + ELSE + ::lRead := .T. ENDIF ENDIF @@ -782,7 +760,7 @@ METHOD Skip( nrecno ) CLASS TPQquery METHOD Goto( nRecno ) CLASS TPQquery - IF nRecno > 0 .AND. nRecno <= ::nLastrec + IF nRecno >= 1 .AND. nRecno <= ::nLastrec ::nRecno := nRecno ::lEof := .F. ENDIF @@ -797,8 +775,6 @@ METHOD FieldPos( cField ) CLASS TPQquery METHOD FieldName( nField ) CLASS TPQquery - LOCAL result - IF HB_ISSTRING( nField ) nField := ::FieldPos( nField ) ELSEIF nField < 1 .OR. nField > Len( ::aStruct ) @@ -806,15 +782,13 @@ METHOD FieldName( nField ) CLASS TPQquery ENDIF IF nField > 0 - result := ::aStruct[ nField ][ _STRU_FIELDNAME ] + RETURN ::aStruct[ nField ][ _STRU_FIELDNAME ] ENDIF - RETURN result + RETURN NIL METHOD FieldType( nField ) CLASS TPQquery - LOCAL result - IF HB_ISSTRING( nField ) nField := ::FieldPos( nField ) ELSEIF nField < 1 .OR. nField > Len( ::aStruct ) @@ -822,15 +796,13 @@ METHOD FieldType( nField ) CLASS TPQquery ENDIF IF nField > 0 - result := ::aStruct[ nField ][ _STRU_FIELDTYPE ] + RETURN ::aStruct[ nField ][ _STRU_FIELDTYPE ] ENDIF - RETURN result + RETURN NIL METHOD FieldLen( nField ) CLASS TPQquery - LOCAL result - IF HB_ISSTRING( nField ) nField := ::FieldPos( nField ) ELSEIF nField < 1 .OR. nField > Len( ::aStruct ) @@ -838,15 +810,13 @@ METHOD FieldLen( nField ) CLASS TPQquery ENDIF IF nField > 0 - result := ::aStruct[ nField ][ _STRU_FIELDLEN ] + RETURN ::aStruct[ nField ][ _STRU_FIELDLEN ] ENDIF - RETURN result + RETURN NIL METHOD FieldDec( nField ) CLASS TPQquery - LOCAL result - IF HB_ISSTRING( nField ) nField := ::FieldPos( nField ) ELSEIF nField < 1 .OR. nField > Len( ::aStruct ) @@ -854,10 +824,10 @@ METHOD FieldDec( nField ) CLASS TPQquery ENDIF IF nField > 0 - result := ::aStruct[ nField ][ _STRU_FIELDDEC ] + RETURN ::aStruct[ nField ][ _STRU_FIELDDEC ] ENDIF - RETURN result + RETURN NIL METHOD Delete( oRow ) CLASS TPQquery @@ -865,40 +835,38 @@ METHOD Delete( oRow ) CLASS TPQquery LOCAL i LOCAL nField LOCAL xField - LOCAL cQuery LOCAL cWhere := "" LOCAL aParams := {} ::SetKey() IF ! Empty( ::Tablename ) .AND. ! Empty( ::aKeys ) - FOR i := 1 TO Len( ::aKeys ) - nField := oRow:FieldPos( ::aKeys[ i ] ) + + FOR EACH i IN ::aKeys + nField := oRow:FieldPos( i ) xField := oRow:FieldGetOld( nField ) - cWhere += ::aKeys[ i ] + " = $" + hb_ntos( i ) + cWhere += i + " = $" + hb_ntos( i:__enumIndex() ) AAdd( aParams, ValueToString( xField ) ) - IF i != Len( ::aKeys ) + IF ! i:__enumIsLast() cWhere += " AND " ENDIF NEXT - IF !( cWhere == "" ) - cQuery := "DELETE FROM " + ::Schema + "." + ::Tablename + " WHERE " + cWhere - res := PQexecParams( ::pDB, cQuery, aParams ) + IF ! cWhere == "" - IF PQresultStatus( res ) != PGRES_COMMAND_OK - ::lError := .T. + res := PQexecParams( ::pDB, ; + "DELETE FROM " + ::Schema + "." + ::Tablename + " WHERE " + cWhere, aParams ) + + IF ( ::lError := PQresultStatus( res ) != PGRES_COMMAND_OK ) ::cError := PQresultErrorMessage( res ) ::rows := 0 ELSE - ::lError := .F. ::cError := "" ::rows := Val( PQcmdTuples( res ) ) ENDIF - res := NIL ENDIF ELSE ::lError := .T. @@ -919,40 +887,38 @@ METHOD Append( oRow ) CLASS TPQquery ::SetKey() IF ! Empty( ::Tablename ) + cQuery := "INSERT INTO " + ::Schema + "." + ::Tablename + "(" + FOR i := 1 TO oRow:FCount() - IF ::lallCols .OR. oRow:changed( i ) + IF ::lAllCols .OR. oRow:Changed( i ) lChanged := .T. cQuery += oRow:FieldName( i ) + "," ENDIF NEXT - cQuery := Left( cQuery, Len( cQuery ) - 1 ) + ") VALUES (" + cQuery := hb_StrShrink( cQuery ) + ") VALUES (" FOR i := 1 TO oRow:FCount() - IF ::lallCols .OR. oRow:Changed( i ) + IF ::lAllCols .OR. oRow:Changed( i ) nParams++ cQuery += "$" + hb_ntos( nParams ) + "," AAdd( aParams, ValueToString( oRow:FieldGet( i ) ) ) ENDIF NEXT - cQuery := Left( cQuery, Len( cQuery ) - 1 ) + ")" + cQuery := hb_StrShrink( cQuery ) + ")" IF lChanged res := PQexecParams( ::pDB, cQuery, aParams ) - IF PQresultStatus( res ) != PGRES_COMMAND_OK - ::lError := .T. + IF ( ::lError := PQresultStatus( res ) != PGRES_COMMAND_OK ) ::cError := PQresultErrorMessage( res ) ::rows := 0 ELSE - ::lError := .F. ::cError := "" ::rows := Val( PQcmdTuples( res ) ) ENDIF - - res := NIL ENDIF ELSE ::lError := .T. @@ -976,22 +942,25 @@ METHOD Update( oRow ) CLASS TPQquery ::SetKey() IF ! Empty( ::Tablename ) .AND. ! Empty( ::aKeys ) - cWhere := "" - FOR i := 1 TO Len( ::aKeys ) - nField := oRow:FieldPos( ::aKeys[ i ] ) + cWhere := "" + + FOR EACH i IN ::aKeys + + nField := oRow:FieldPos( i ) xField := oRow:FieldGetOld( nField ) - cWhere += ::aKeys[ i ] + "=" + DataToSql( xField ) + cWhere += i + "=" + DataToSql( xField ) - IF i != Len( ::aKeys ) + IF ! i:__enumIsLast() cWhere += " AND " ENDIF NEXT cQuery := "UPDATE " + ::Schema + "." + ::Tablename + " SET " + FOR i := 1 TO oRow:FCount() - IF ::lallcols .OR. oRow:Changed( i ) + IF ::lAllCols .OR. oRow:Changed( i ) lChanged := .T. nParams++ cQuery += oRow:FieldName( i ) + " = $" + hb_ntos( nParams ) + "," @@ -999,23 +968,19 @@ METHOD Update( oRow ) CLASS TPQquery ENDIF NEXT - IF !( cWhere == "" ) .AND. lChanged + IF ! cWhere == "" .AND. lChanged - cQuery := Left( cQuery, Len( cQuery ) - 1 ) + " WHERE " + cWhere + cQuery := hb_StrShrink( cQuery ) + " WHERE " + cWhere res := PQexecParams( ::pDB, cQuery, aParams ) - IF PQresultStatus( res ) != PGRES_COMMAND_OK - ::lError := .T. + IF ( ::lError := PQresultStatus( res ) != PGRES_COMMAND_OK ) ::cError := PQresultErrorMessage( res ) ::rows := 0 ELSE - ::lError := .F. ::cError := "" ::rows := Val( PQcmdTuples( res ) ) ENDIF - - res := NIL ENDIF ELSE ::lError := .T. @@ -1093,7 +1058,7 @@ METHOD Getrow( nRow ) CLASS TPQquery IF ::nResultStatus == PGRES_TUPLES_OK - IF nRow > 0 .AND. nRow <= ::nLastRec + IF nRow >= 1 .AND. nRow <= ::nLastRec aRow := Array( ::nFields ) aOld := Array( ::nFields ) @@ -1142,7 +1107,7 @@ METHOD GetBlankRow() CLASS TPQquery RETURN TPQRow():New( aRow, aOld, ::aStruct ) -METHOD SetKey() CLASS TPQquery +METHOD PROCEDURE SetKey() CLASS TPQquery LOCAL cQuery LOCAL i, x @@ -1154,9 +1119,9 @@ METHOD SetKey() CLASS TPQquery IF ::nResultStatus == PGRES_TUPLES_OK IF ::Tablename == NIL /* set the table name looking for table oid */ - FOR i := 1 TO Len( ::aStruct ) + FOR EACH i IN ::aStruct /* Store table codes oid */ - nTableId := ::aStruct[ i ][ _STRU_TABLE ] + nTableId := i[ _STRU_TABLE ] IF nTableId != xTableId xTableId := nTableId @@ -1166,48 +1131,45 @@ METHOD SetKey() CLASS TPQquery IF nCount == 1 /* first, try get the table name from select, else get from pg_catalog */ - IF ( npos := At( "FROM ", Upper( ::cQuery ) ) ) != 0 + IF ( nPos := At( "FROM ", Upper( ::cQuery ) ) ) > 0 cQuery := Lower( LTrim( SubStr( ::cQuery, nPos + 5 ) ) ) - IF ( npos := At( ".", cQuery ) ) != 0 - ::Schema := AllTrim( Left( cQuery, npos - 1 ) ) + IF ( nPos := At( ".", cQuery ) ) > 0 + ::Schema := AllTrim( Left( cQuery, nPos - 1 ) ) cQuery := SubStr( cQuery, nPos + 1 ) ENDIF - IF ( npos := At( " ", cQuery ) ) != 0 - ::Tablename := RTrim( Left( cQuery, npos ) ) + IF ( nPos := At( " ", cQuery ) ) > 0 + ::Tablename := RTrim( Left( cQuery, nPos ) ) ELSE ::Tablename := cQuery ENDIF ENDIF IF Empty( ::Tablename ) - cQuery := "SELECT relname FROM pg_class WHERE oid = " + Str( xTableId ) - res := PQexec( ::pDB, cQuery ) + res := PQexec( ::pDB, "SELECT relname FROM pg_class WHERE oid = " + hb_ntos( xTableId ) ) IF PQresultStatus( res ) == PGRES_TUPLES_OK .AND. PQlastrec( res ) != 0 ::Tablename := RTrim( PQgetvalue( res, 1, 1 ) ) ENDIF - - res := NIL ENDIF ENDIF ENDIF IF ::aKeys == NIL .AND. ! Empty( ::Tablename ) - /* Set the table primary keys */ - cQuery := "SELECT c.attname " - cQuery += " FROM pg_class a, pg_class b, pg_attribute c, pg_index d, pg_namespace e " - cQuery += " WHERE a.oid = d.indrelid " - cQuery += " AND a.relname = '" + ::Tablename + "'" - cQuery += " AND b.oid = d.indexrelid " - cQuery += " AND c.attrelid = b.oid " - cQuery += " AND d.indisprimary " - cQuery += " AND e.oid = a.relnamespace " - cQuery += " AND e.nspname = " + DataToSql( ::Schema ) - res := PQexec( ::pDB, cQuery ) + /* Set the table primary keys */ + res := PQexec( ::pDB, ; + "SELECT c.attname" + ; + " FROM pg_class a, pg_class b, pg_attribute c, pg_index d, pg_namespace e" + ; + " WHERE a.oid = d.indrelid" + ; + " AND a.relname = '" + ::Tablename + "'" + ; + " AND b.oid = d.indexrelid" + ; + " AND c.attrelid = b.oid" + ; + " AND d.indisprimary" + ; + " AND e.oid = a.relnamespace" + ; + " AND e.nspname = " + DataToSql( ::Schema ) ) IF PQresultStatus( res ) == PGRES_TUPLES_OK .AND. PQlastrec( res ) != 0 ::aKeys := {} @@ -1216,36 +1178,34 @@ METHOD SetKey() CLASS TPQquery AAdd( ::aKeys, PQgetvalue( res, x, 1 ) ) NEXT ENDIF - - res := NIL ENDIF ENDIF - RETURN NIL + RETURN CREATE CLASS TPQRow - VAR aRow - VAR aOld - VAR aStruct + VAR aRow + VAR aOld + VAR aStruct - METHOD New( row, old, struct ) + METHOD New( row, old, struct ) - METHOD FCount() INLINE Len( ::aRow ) - METHOD FieldGet( nField ) - METHOD FieldPut( nField, Value ) - METHOD FieldName( nField ) - METHOD FieldPos( cField ) - METHOD FieldLen( nField ) - METHOD FieldDec( nField ) - METHOD FieldType( nField ) - METHOD Changed( nField ) INLINE !( ::aRow[ nField ] == ::aOld[ nField ] ) - METHOD FieldGetOld( nField ) INLINE ::aOld[ nField ] + METHOD FCount() INLINE Len( ::aRow ) + METHOD FieldGet( nField ) + METHOD FieldPut( nField, Value ) + METHOD FieldName( nField ) + METHOD FieldPos( cField ) + METHOD FieldLen( nField ) + METHOD FieldDec( nField ) + METHOD FieldType( nField ) + METHOD Changed( nField ) INLINE ! ::aRow[ nField ] == ::aOld[ nField ] + METHOD FieldGetOld( nField ) INLINE ::aOld[ nField ] ENDCLASS -METHOD new( row, old, struct ) CLASS TPQrow +METHOD New( row, old, struct ) CLASS TPQrow ::aRow := row ::aOld := old @@ -1255,45 +1215,39 @@ METHOD new( row, old, struct ) CLASS TPQrow METHOD FieldGet( nField ) CLASS TPQrow - LOCAL result - IF HB_ISSTRING( nField ) nField := ::FieldPos( nField ) ENDIF IF nField >= 1 .AND. nField <= Len( ::aRow ) - result := ::aRow[ nField ] + RETURN ::aRow[ nField ] ENDIF - RETURN result + RETURN NIL METHOD FieldPut( nField, Value ) CLASS TPQrow - LOCAL result - IF HB_ISSTRING( nField ) nField := ::FieldPos( nField ) ENDIF IF nField >= 1 .AND. nField <= Len( ::aRow ) - result := ::aRow[ nField ] := Value + RETURN ::aRow[ nField ] := Value ENDIF - RETURN result + RETURN NIL METHOD FieldName( nField ) CLASS TPQrow - LOCAL result - IF HB_ISSTRING( nField ) nField := ::FieldPos( nField ) ENDIF IF nField >= 1 .AND. nField <= Len( ::aStruct ) - result := ::aStruct[ nField ][ _STRU_FIELDNAME ] + RETURN ::aStruct[ nField ][ _STRU_FIELDNAME ] ENDIF - RETURN result + RETURN NIL METHOD FieldPos( cField ) CLASS TPQrow @@ -1303,58 +1257,56 @@ METHOD FieldPos( cField ) CLASS TPQrow METHOD FieldType( nField ) CLASS TPQrow - LOCAL result - IF HB_ISSTRING( nField ) nField := ::FieldPos( nField ) ENDIF IF nField >= 1 .AND. nField <= Len( ::aStruct ) - result := ::aStruct[ nField ][ _STRU_FIELDTYPE ] + RETURN ::aStruct[ nField ][ _STRU_FIELDTYPE ] ENDIF - RETURN result + RETURN NIL METHOD FieldLen( nField ) CLASS TPQrow - LOCAL result - IF HB_ISSTRING( nField ) nField := ::FieldPos( nField ) ENDIF IF nField >= 1 .AND. nField <= Len( ::aStruct ) - result := ::aStruct[ nField ][ _STRU_FIELDLEN ] + RETURN ::aStruct[ nField ][ _STRU_FIELDLEN ] ENDIF - RETURN result + RETURN NIL METHOD FieldDec( nField ) CLASS TPQrow - LOCAL result - IF HB_ISSTRING( nField ) nField := ::FieldPos( nField ) ENDIF IF nField >= 1 .AND. nField <= Len( ::aStruct ) - result := ::aStruct[ nField ][ _STRU_FIELDDEC ] + RETURN ::aStruct[ nField ][ _STRU_FIELDDEC ] ENDIF - RETURN result + RETURN NIL + +STATIC FUNCTION EscapeParam( cString ) + + cString := hb_StrReplace( cString, { ; + "'" => "\'", ; + "\" => "\\" } ) + + RETURN iif( Empty( cString ) .OR. " " $ cString, "'" + cString + "'", cString ) STATIC FUNCTION DataToSql( xField ) SWITCH ValType( xField ) CASE "C" - CASE "M" - RETURN "'" + StrTran( xField, "'", " " ) + "'" - CASE "D" - RETURN DToS( xField ) - CASE "N" - RETURN Str( xField ) - CASE "L" - RETURN iif( xField, "'t'", "'f'" ) + CASE "M" ; RETURN "'" + StrTran( xField, "'", " " ) + "'" + CASE "D" ; RETURN DToS( xField ) + CASE "N" ; RETURN hb_ntos( xField ) + CASE "L" ; RETURN iif( xField, "'t'", "'f'" ) ENDSWITCH RETURN "NULL" @@ -1362,15 +1314,11 @@ STATIC FUNCTION DataToSql( xField ) STATIC FUNCTION ValueToString( xField ) SWITCH ValType( xField ) - CASE "D" - RETURN DToS( xField ) - CASE "N" - RETURN Str( xField ) - CASE "L" - RETURN iif( xField, "t", "f" ) CASE "C" - CASE "M" - RETURN xField + CASE "M" ; RETURN xField + CASE "D" ; RETURN DToS( xField ) + CASE "N" ; RETURN hb_ntos( xField ) + CASE "L" ; RETURN iif( xField, "t", "f" ) ENDSWITCH RETURN NIL