diff --git a/ChangeLog.txt b/ChangeLog.txt index 4305a88311..10edf6acec 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -7,6 +7,18 @@ Entries may not always be in chronological/commit order. See license at the end of file. */ +2021-03-31 23:43 UTC+0200 Aleksander Czajczynski (hb fki.pl) + * contrib/hbpgsql/postgres.c + ! guard PQEXECPARAMS() wrapper from generating unrecoverable error + on empty parameter array "hb_xgrab requested to allocate zero bytes" + + * contrib/hbpgsql/tpostgre.prg + ! corrected buggy parameter order in TPQserver():Query() + + * make TPQQuery, TPQRow classes more aware of NIL to NULL conversions, + added support for inserting and updating empty xBase date value + should fix issue #234, oRow:FieldPut( , NIL ) also looks good + 2021-03-31 21:26 UTC+0200 Aleksander Czajczynski (hb fki.pl) * contrib/hbpgsql/hbpgsql.hbx * contrib/hbpgsql/postgres.c diff --git a/contrib/hbpgsql/postgres.c b/contrib/hbpgsql/postgres.c index 8b726157d8..e137bf3d54 100644 --- a/contrib/hbpgsql/postgres.c +++ b/contrib/hbpgsql/postgres.c @@ -688,16 +688,22 @@ HB_FUNC( PQEXECPARAMS ) if( conn && aParam ) { int n = ( int ) hb_arrayLen( aParam ); - int i; - const char ** paramvalues = ( const char ** ) hb_xgrab( sizeof( char * ) * n ); + if( ! n ) + hb_PGresult_ret( PQexec( conn, hb_parcx( 2 ) ) ); + else + { + int i; - for( i = 0; i < n; ++i ) - paramvalues[ i ] = hb_arrayGetCPtr( aParam, i + 1 ); + const char ** paramvalues = ( const char ** ) hb_xgrab( sizeof( char * ) * n ); - hb_PGresult_ret( PQexecParams( conn, hb_parcx( 2 ), n, NULL, paramvalues, NULL, NULL, hb_parnidef( 4, 1 ) ) ); + for( i = 0; i < n; ++i ) + paramvalues[ i ] = hb_arrayGetCPtr( aParam, i + 1 ); - hb_xfree( ( void * ) paramvalues ); + hb_PGresult_ret( PQexecParams( conn, hb_parcx( 2 ), n, NULL, paramvalues, NULL, NULL, hb_parnidef( 4, 1 ) ) ); + + hb_xfree( ( void * ) paramvalues ); + } } else hb_errRT_BASE( EG_ARG, 2020, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); diff --git a/contrib/hbpgsql/tpostgre.prg b/contrib/hbpgsql/tpostgre.prg index 24e4d97b83..a3a0df73e8 100644 --- a/contrib/hbpgsql/tpostgre.prg +++ b/contrib/hbpgsql/tpostgre.prg @@ -194,7 +194,7 @@ METHOD Query( cQuery, lNull ) CLASS TPQserver lNull := ::lNull ENDIF - RETURN TPQQuery():New( ::pDB, cQuery, ::lAllCols, ::Schema, lNull ) + RETURN TPQQuery():New( ::pDB, cQuery, ::lAllCols, ::Schema,, lNull ) METHOD TableExists( cTable ) CLASS TPQserver @@ -882,7 +882,7 @@ METHOD Append( oRow ) CLASS TPQquery LOCAL res LOCAL lChanged := .F. LOCAL aParams := {} - LOCAL nParams := 0 + LOCAL xParam ::SetKey() @@ -893,21 +893,28 @@ METHOD Append( oRow ) CLASS TPQquery FOR i := 1 TO oRow:FCount() IF ::lAllCols .OR. oRow:Changed( i ) lChanged := .T. - cQuery += oRow:FieldName( i ) + "," + IF ! ( xParam := ValueToString( oRow:FieldGet( i ) ) ) == NIL + AAdd( aParams, xParam ) + cQuery += oRow:FieldName( i ) + "," + ENDIF ENDIF NEXT - cQuery := hb_StrShrink( cQuery ) + ") VALUES (" - - FOR i := 1 TO oRow:FCount() - IF ::lAllCols .OR. oRow:Changed( i ) - nParams++ - cQuery += "$" + hb_ntos( nParams ) + "," - AAdd( aParams, ValueToString( oRow:FieldGet( i ) ) ) - ENDIF - NEXT - - cQuery := hb_StrShrink( cQuery ) + ")" + IF lChanged .AND. Len( aParams ) == 0 + /* + * Edge case here, adding a row filled with NULL values only, + * should add at least one field to conform with SQL syntax. + * This is possible with no primary key and/or when default + * values provided in table schema. + */ + cQuery := cQuery + oRow:FieldName( 1 ) + ") VALUES (NULL)" + ELSE + cQuery := hb_StrShrink( cQuery ) + ") VALUES (" + FOR i := 1 TO Len( aParams ) + cQuery += "$" + hb_ntos( i ) + "," + NEXT + cQuery := hb_StrShrink( cQuery ) + ")" + ENDIF IF lChanged res := PQexecParams( ::pDB, cQuery, aParams ) @@ -938,6 +945,7 @@ METHOD Update( oRow ) CLASS TPQquery LOCAL lChanged := .F. LOCAL aParams := {} LOCAL nParams := 0 + LOCAL xParam ::SetKey() @@ -962,9 +970,13 @@ METHOD Update( oRow ) CLASS TPQquery FOR i := 1 TO oRow:FCount() IF ::lAllCols .OR. oRow:Changed( i ) lChanged := .T. - nParams++ - cQuery += oRow:FieldName( i ) + " = $" + hb_ntos( nParams ) + "," - AAdd( aParams, ValueToString( oRow:FieldGet( i ) ) ) + IF ( xParam := ValueToString( oRow:FieldGet( i ) ) ) == NIL + cQuery += oRow:FieldName( i ) + " = NULL," + ELSE + nParams++ + cQuery += oRow:FieldName( i ) + " = $" + hb_ntos( nParams ) + "," + AAdd( aParams, xParam ) + ENDIF ENDIF NEXT @@ -1316,7 +1328,7 @@ STATIC FUNCTION ValueToString( xField ) SWITCH ValType( xField ) CASE "C" CASE "M" ; RETURN xField - CASE "D" ; RETURN DToS( xField ) + CASE "D" ; RETURN IIF( Empty( xField ), NIL, DToS( xField ) ) CASE "N" ; RETURN hb_ntos( xField ) CASE "L" ; RETURN iif( xField, "t", "f" ) ENDSWITCH