Hello, we encountered unexpected behavior from an ECPG program complied with the -C ORACLE option. The program executes the following query:
SELECT 123::numeric(3,0), 't'::char(2)"; Compilation and execution steps: $ ecpg -C ORACLE ecpgtest.pgc (attached) $ gcc -g -o ecpgtest ecpgtest.c -L `pg_config --libdir` -I `pg_config --includedir` -lecpg -lpgtypes $ ./ecpgtest type: numeric : data: "120" type: bpchar : data: "t" The expected numeric value is "123". The code below is the direct cause of the unanticipated data modification. interfaces/ecpg/ecpglib/data.c:581 (whitespaces are swueezed) > if (varcharsize == 0 || varcharsize > size) > { > /* > * compatibility mode, blank pad and null > * terminate char array > */ > if (ORACLE_MODE(compat) && (type == ECPGt_char || type == > ECPGt_unsigned_char)) > { > memset(str, ' ', varcharsize); > memcpy(str, pval, size); > str[varcharsize - 1] = '\0'; This results in overwriting str[-1], the last byte of the preceding numeric in this case, with 0x00, representing the digit '0'. When callers of ecpg_get_data() explicitly pass zero as varcharsize, they provide storage that precisely fitting the data. However, it remains uncertain if this assumption is valid when ecpg_store_result() passes var->varcharsize which is also zero. Consequently, the current fix presumes exact-fit storage when varcharsize is zero. I haven't fully checked, but at least back to 10 have this issue. Thoughts? regards. -- Kyotaro Horiguchi NTT Open Source Software Center
#include <stdlib.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <pgtypes_numeric.h> EXEC SQL include sqlda.h; int main(void) { sqlda_t *sqlda = NULL; char name_buf[16] = {0}; char var_buf[16] = {0}; EXEC SQL CONNECT TO 'postgres@/tmp:5432' AS conn1; EXEC SQL PREPARE stmt1 FROM "SELECT 123::numeric(3,0), 't'::varchar(2)"; EXEC SQL DECLARE cur1 CURSOR FOR stmt1; EXEC SQL OPEN cur1; EXEC SQL FETCH NEXT FROM cur1 INTO DESCRIPTOR sqlda; if (sqlda == NULL) { fprintf(stderr, "No data\n"); exit(1); } for (int i = 0 ; i < sqlda->sqld ; i++) { sqlvar_t v = sqlda->sqlvar[i]; char *sqldata = v.sqldata; if (v.sqltype == ECPGt_numeric) sqldata = PGTYPESnumeric_to_asc((numeric*)sqlda->sqlvar[i].sqldata, -1); printf("type: %-8s: data: \"%s\"\n", v.sqlname.data, sqldata); } EXEC SQL CLOSE cur1; EXEC SQL COMMIT; EXEC SQL DISCONNECT ALL; return 0; }
>From 6e5d3a513209cb3df581b4d565537eae1013cf63 Mon Sep 17 00:00:00 2001 From: Kyotaro Horiguchi <horikyota....@gmail.com> Date: Mon, 10 Apr 2023 16:38:43 +0900 Subject: [PATCH 1/2] test --- .../ecpg/test/compat_oracle/char_array.pgc | 33 ++- .../test/expected/compat_oracle-char_array.c | 226 +++++++++++++----- .../expected/compat_oracle-char_array.stderr | 198 ++++++++------- .../expected/compat_oracle-char_array.stdout | 5 + 4 files changed, 318 insertions(+), 144 deletions(-) diff --git a/src/interfaces/ecpg/test/compat_oracle/char_array.pgc b/src/interfaces/ecpg/test/compat_oracle/char_array.pgc index 6a5d383d4e..a39dc8f0c0 100644 --- a/src/interfaces/ecpg/test/compat_oracle/char_array.pgc +++ b/src/interfaces/ecpg/test/compat_oracle/char_array.pgc @@ -2,6 +2,10 @@ #include <stdlib.h> #include <string.h> +#include <pgtypes_numeric.h> + +EXEC SQL INCLUDE sqlda.h; + EXEC SQL INCLUDE ../regression; static void warn(void) @@ -20,6 +24,8 @@ int main() { const char *ppppp = "XXXXX"; int loopcount; + sqlda_t *sqlda = NULL; + EXEC SQL BEGIN DECLARE SECTION; char shortstr[5]; char bigstr[11]; @@ -53,11 +59,34 @@ int main() { EXEC SQL CLOSE C; EXEC SQL DROP TABLE strdbase; + EXEC SQL COMMIT WORK; + /* SQLDA handling */ + EXEC SQL WHENEVER SQLWARNING SQLPRINT; + EXEC SQL WHENEVER NOT FOUND STOP; + EXEC SQL PREPARE stmt1 FROM "SELECT 123::numeric(3,0), 't'::varchar(2)"; + EXEC SQL DECLARE cur1 CURSOR FOR stmt1; + EXEC SQL OPEN cur1; + EXEC SQL FETCH NEXT FROM cur1 INTO DESCRIPTOR sqlda; + + printf("\n-----------------\ntype : data\n"); + for (int i = 0 ; i < sqlda->sqld ; i++) + { + sqlvar_t v = sqlda->sqlvar[i]; + char *sqldata = v.sqldata; + + if (v.sqltype == ECPGt_numeric) + sqldata = + PGTYPESnumeric_to_asc((numeric*)sqlda->sqlvar[i].sqldata, -1); + + printf("%-8s: \"%s\"\n", v.sqlname.data, sqldata); + } + + EXEC SQL CLOSE cur1; + EXEC SQL COMMIT WORK; + printf("\nGOOD-BYE!!\n\n"); - EXEC SQL COMMIT WORK; - EXEC SQL DISCONNECT ALL; return 0; diff --git a/src/interfaces/ecpg/test/expected/compat_oracle-char_array.c b/src/interfaces/ecpg/test/expected/compat_oracle-char_array.c index 04d4e1969e..b82e45a090 100644 --- a/src/interfaces/ecpg/test/expected/compat_oracle-char_array.c +++ b/src/interfaces/ecpg/test/expected/compat_oracle-char_array.c @@ -11,6 +11,32 @@ #include <stdlib.h> #include <string.h> +#include <pgtypes_numeric.h> + + +#line 1 "sqlda.h" +#ifndef ECPG_SQLDA_H +#define ECPG_SQLDA_H + +#ifdef _ECPG_INFORMIX_H + +#include "sqlda-compat.h" +typedef struct sqlvar_compat sqlvar_t; +typedef struct sqlda_compat sqlda_t; + +#else + +#include "sqlda-native.h" +typedef struct sqlvar_struct sqlvar_t; +typedef struct sqlda_struct sqlda_t; + +#endif + +#endif /* ECPG_SQLDA_H */ + +#line 7 "char_array.pgc" + + #line 1 "regression.h" @@ -19,7 +45,7 @@ -#line 5 "char_array.pgc" +#line 9 "char_array.pgc" static void warn(void) @@ -34,125 +60,127 @@ static void warn(void) int main() { /* exec sql whenever sql_warning do warn ( ) ; */ -#line 18 "char_array.pgc" +#line 22 "char_array.pgc" /* exec sql whenever sqlerror stop ; */ -#line 19 "char_array.pgc" +#line 23 "char_array.pgc" const char *ppppp = "XXXXX"; int loopcount; + sqlda_t *sqlda = NULL; + /* exec sql begin declare section */ -#line 24 "char_array.pgc" +#line 30 "char_array.pgc" char shortstr [ 5 ] ; -#line 25 "char_array.pgc" +#line 31 "char_array.pgc" char bigstr [ 11 ] ; -#line 26 "char_array.pgc" +#line 32 "char_array.pgc" short shstr_ind = 0 ; -#line 27 "char_array.pgc" +#line 33 "char_array.pgc" short bigstr_ind = 0 ; /* exec sql end declare section */ -#line 28 "char_array.pgc" +#line 34 "char_array.pgc" ECPGdebug(1, stderr); { ECPGconnect(__LINE__, 3, "ecpg1_regression" , NULL, NULL , NULL, 0); -#line 31 "char_array.pgc" +#line 37 "char_array.pgc" if (sqlca.sqlwarn[0] == 'W') warn ( ); -#line 31 "char_array.pgc" +#line 37 "char_array.pgc" if (sqlca.sqlcode < 0) exit (1);} -#line 31 "char_array.pgc" +#line 37 "char_array.pgc" { ECPGdo(__LINE__, 3, 1, NULL, 0, ECPGst_normal, "create table strdbase ( strval varchar ( 10 ) )", ECPGt_EOIT, ECPGt_EORT); -#line 33 "char_array.pgc" +#line 39 "char_array.pgc" if (sqlca.sqlwarn[0] == 'W') warn ( ); -#line 33 "char_array.pgc" +#line 39 "char_array.pgc" if (sqlca.sqlcode < 0) exit (1);} -#line 33 "char_array.pgc" +#line 39 "char_array.pgc" { ECPGdo(__LINE__, 3, 1, NULL, 0, ECPGst_normal, "insert into strdbase values ( '' )", ECPGt_EOIT, ECPGt_EORT); -#line 34 "char_array.pgc" +#line 40 "char_array.pgc" if (sqlca.sqlwarn[0] == 'W') warn ( ); -#line 34 "char_array.pgc" +#line 40 "char_array.pgc" if (sqlca.sqlcode < 0) exit (1);} -#line 34 "char_array.pgc" +#line 40 "char_array.pgc" { ECPGdo(__LINE__, 3, 1, NULL, 0, ECPGst_normal, "insert into strdbase values ( 'AB' )", ECPGt_EOIT, ECPGt_EORT); -#line 35 "char_array.pgc" +#line 41 "char_array.pgc" if (sqlca.sqlwarn[0] == 'W') warn ( ); -#line 35 "char_array.pgc" +#line 41 "char_array.pgc" if (sqlca.sqlcode < 0) exit (1);} -#line 35 "char_array.pgc" +#line 41 "char_array.pgc" { ECPGdo(__LINE__, 3, 1, NULL, 0, ECPGst_normal, "insert into strdbase values ( 'ABCD' )", ECPGt_EOIT, ECPGt_EORT); -#line 36 "char_array.pgc" +#line 42 "char_array.pgc" if (sqlca.sqlwarn[0] == 'W') warn ( ); -#line 36 "char_array.pgc" +#line 42 "char_array.pgc" if (sqlca.sqlcode < 0) exit (1);} -#line 36 "char_array.pgc" +#line 42 "char_array.pgc" { ECPGdo(__LINE__, 3, 1, NULL, 0, ECPGst_normal, "insert into strdbase values ( 'ABCDE' )", ECPGt_EOIT, ECPGt_EORT); -#line 37 "char_array.pgc" +#line 43 "char_array.pgc" if (sqlca.sqlwarn[0] == 'W') warn ( ); -#line 37 "char_array.pgc" +#line 43 "char_array.pgc" if (sqlca.sqlcode < 0) exit (1);} -#line 37 "char_array.pgc" +#line 43 "char_array.pgc" { ECPGdo(__LINE__, 3, 1, NULL, 0, ECPGst_normal, "insert into strdbase values ( 'ABCDEF' )", ECPGt_EOIT, ECPGt_EORT); -#line 38 "char_array.pgc" +#line 44 "char_array.pgc" if (sqlca.sqlwarn[0] == 'W') warn ( ); -#line 38 "char_array.pgc" +#line 44 "char_array.pgc" if (sqlca.sqlcode < 0) exit (1);} -#line 38 "char_array.pgc" +#line 44 "char_array.pgc" { ECPGdo(__LINE__, 3, 1, NULL, 0, ECPGst_normal, "insert into strdbase values ( 'ABCDEFGHIJ' )", ECPGt_EOIT, ECPGt_EORT); -#line 39 "char_array.pgc" +#line 45 "char_array.pgc" if (sqlca.sqlwarn[0] == 'W') warn ( ); -#line 39 "char_array.pgc" +#line 45 "char_array.pgc" if (sqlca.sqlcode < 0) exit (1);} -#line 39 "char_array.pgc" +#line 45 "char_array.pgc" /* declare C cursor for select strval , strval from strdbase */ -#line 41 "char_array.pgc" +#line 47 "char_array.pgc" { ECPGdo(__LINE__, 3, 1, NULL, 0, ECPGst_normal, "declare C cursor for select strval , strval from strdbase", ECPGt_EOIT, ECPGt_EORT); -#line 42 "char_array.pgc" +#line 48 "char_array.pgc" if (sqlca.sqlwarn[0] == 'W') warn ( ); -#line 42 "char_array.pgc" +#line 48 "char_array.pgc" if (sqlca.sqlcode < 0) exit (1);} -#line 42 "char_array.pgc" +#line 48 "char_array.pgc" /* exec sql whenever not found break ; */ -#line 44 "char_array.pgc" +#line 50 "char_array.pgc" printf("Full Str. : Short Ind.\n"); @@ -164,59 +192,135 @@ if (sqlca.sqlcode < 0) exit (1);} ECPGt_short,&(bigstr_ind),(long)1,(long)1,sizeof(short), ECPGt_char,(shortstr),(long)5,(long)1,(5)*sizeof(char), ECPGt_short,&(shstr_ind),(long)1,(long)1,sizeof(short), ECPGt_EORT); -#line 50 "char_array.pgc" +#line 56 "char_array.pgc" if (sqlca.sqlcode == ECPG_NOT_FOUND) break; -#line 50 "char_array.pgc" +#line 56 "char_array.pgc" if (sqlca.sqlwarn[0] == 'W') warn ( ); -#line 50 "char_array.pgc" +#line 56 "char_array.pgc" if (sqlca.sqlcode < 0) exit (1);} -#line 50 "char_array.pgc" +#line 56 "char_array.pgc" printf("\"%s\": \"%s\" %d\n", bigstr, shortstr, shstr_ind); } { ECPGdo(__LINE__, 3, 1, NULL, 0, ECPGst_normal, "close C", ECPGt_EOIT, ECPGt_EORT); -#line 54 "char_array.pgc" +#line 60 "char_array.pgc" if (sqlca.sqlwarn[0] == 'W') warn ( ); -#line 54 "char_array.pgc" +#line 60 "char_array.pgc" if (sqlca.sqlcode < 0) exit (1);} -#line 54 "char_array.pgc" +#line 60 "char_array.pgc" { ECPGdo(__LINE__, 3, 1, NULL, 0, ECPGst_normal, "drop table strdbase", ECPGt_EOIT, ECPGt_EORT); -#line 55 "char_array.pgc" +#line 61 "char_array.pgc" if (sqlca.sqlwarn[0] == 'W') warn ( ); -#line 55 "char_array.pgc" +#line 61 "char_array.pgc" if (sqlca.sqlcode < 0) exit (1);} -#line 55 "char_array.pgc" +#line 61 "char_array.pgc" + { ECPGtrans(__LINE__, NULL, "commit work"); +#line 62 "char_array.pgc" +if (sqlca.sqlwarn[0] == 'W') warn ( ); +#line 62 "char_array.pgc" + +if (sqlca.sqlcode < 0) exit (1);} +#line 62 "char_array.pgc" + + + /* SQLDA handling */ + /* exec sql whenever sql_warning sqlprint ; */ +#line 65 "char_array.pgc" + + /* exec sql whenever not found stop ; */ +#line 66 "char_array.pgc" + + { ECPGprepare(__LINE__, NULL, 0, "stmt1", "SELECT 123::numeric(3,0), 't'::varchar(2)"); +#line 67 "char_array.pgc" + +if (sqlca.sqlwarn[0] == 'W') sqlprint(); +#line 67 "char_array.pgc" + +if (sqlca.sqlcode < 0) exit (1);} +#line 67 "char_array.pgc" + + /* declare cur1 cursor for $1 */ +#line 68 "char_array.pgc" + + { ECPGdo(__LINE__, 3, 1, NULL, 0, ECPGst_normal, "declare cur1 cursor for $1", + ECPGt_char_variable,(ECPGprepared_statement(NULL, "stmt1", __LINE__)),(long)1,(long)1,(1)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT); +#line 69 "char_array.pgc" + +if (sqlca.sqlwarn[0] == 'W') sqlprint(); +#line 69 "char_array.pgc" + +if (sqlca.sqlcode < 0) exit (1);} +#line 69 "char_array.pgc" + + { ECPGdo(__LINE__, 3, 1, NULL, 0, ECPGst_normal, "fetch next from cur1", ECPGt_EOIT, + ECPGt_sqlda, &sqlda, 0L, 0L, 0L, + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT); +#line 70 "char_array.pgc" + +if (sqlca.sqlcode == ECPG_NOT_FOUND) exit (1); +#line 70 "char_array.pgc" + +if (sqlca.sqlwarn[0] == 'W') sqlprint(); +#line 70 "char_array.pgc" + +if (sqlca.sqlcode < 0) exit (1);} +#line 70 "char_array.pgc" + + + printf("\n-----------------\ntype : data\n"); + for (int i = 0 ; i < sqlda->sqld ; i++) + { + sqlvar_t v = sqlda->sqlvar[i]; + char *sqldata = v.sqldata; + + if (v.sqltype == ECPGt_numeric) + sqldata = + PGTYPESnumeric_to_asc((numeric*)sqlda->sqlvar[i].sqldata, -1); + + printf("%-8s: \"%s\"\n", v.sqlname.data, sqldata); + } + + { ECPGdo(__LINE__, 3, 1, NULL, 0, ECPGst_normal, "close cur1", ECPGt_EOIT, ECPGt_EORT); +#line 85 "char_array.pgc" + +if (sqlca.sqlwarn[0] == 'W') sqlprint(); +#line 85 "char_array.pgc" + +if (sqlca.sqlcode < 0) exit (1);} +#line 85 "char_array.pgc" + + { ECPGtrans(__LINE__, NULL, "commit work"); +#line 86 "char_array.pgc" + +if (sqlca.sqlwarn[0] == 'W') sqlprint(); +#line 86 "char_array.pgc" + +if (sqlca.sqlcode < 0) exit (1);} +#line 86 "char_array.pgc" + + printf("\nGOOD-BYE!!\n\n"); - { ECPGtrans(__LINE__, NULL, "commit work"); -#line 59 "char_array.pgc" - -if (sqlca.sqlwarn[0] == 'W') warn ( ); -#line 59 "char_array.pgc" - -if (sqlca.sqlcode < 0) exit (1);} -#line 59 "char_array.pgc" - - { ECPGdisconnect(__LINE__, "ALL"); -#line 61 "char_array.pgc" +#line 90 "char_array.pgc" -if (sqlca.sqlwarn[0] == 'W') warn ( ); -#line 61 "char_array.pgc" +if (sqlca.sqlwarn[0] == 'W') sqlprint(); +#line 90 "char_array.pgc" if (sqlca.sqlcode < 0) exit (1);} -#line 61 "char_array.pgc" +#line 90 "char_array.pgc" return 0; diff --git a/src/interfaces/ecpg/test/expected/compat_oracle-char_array.stderr b/src/interfaces/ecpg/test/expected/compat_oracle-char_array.stderr index 40d9f7ddb0..24b9715d7b 100644 --- a/src/interfaces/ecpg/test/expected/compat_oracle-char_array.stderr +++ b/src/interfaces/ecpg/test/expected/compat_oracle-char_array.stderr @@ -2,138 +2,174 @@ [NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: ECPGconnect: opening database ecpg1_regression on <DEFAULT> port <DEFAULT> [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 33: query: create table strdbase ( strval varchar ( 10 ) ); with 0 parameter(s) on connection ecpg1_regression -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 33: using PQexec -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 33: OK: CREATE TABLE -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 34: query: insert into strdbase values ( '' ); with 0 parameter(s) on connection ecpg1_regression -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 34: using PQexec -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 34: OK: INSERT 0 1 -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 35: query: insert into strdbase values ( 'AB' ); with 0 parameter(s) on connection ecpg1_regression -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 35: using PQexec -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 35: OK: INSERT 0 1 -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 36: query: insert into strdbase values ( 'ABCD' ); with 0 parameter(s) on connection ecpg1_regression -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 36: using PQexec -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 36: OK: INSERT 0 1 -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 37: query: insert into strdbase values ( 'ABCDE' ); with 0 parameter(s) on connection ecpg1_regression -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 37: using PQexec -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 37: OK: INSERT 0 1 -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 38: query: insert into strdbase values ( 'ABCDEF' ); with 0 parameter(s) on connection ecpg1_regression -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 38: using PQexec -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 38: OK: INSERT 0 1 -[NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 39: query: insert into strdbase values ( 'ABCDEFGHIJ' ); with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: ecpg_execute on line 39: query: create table strdbase ( strval varchar ( 10 ) ); with 0 parameter(s) on connection ecpg1_regression [NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: ecpg_execute on line 39: using PQexec [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 39: OK: INSERT 0 1 +[NO_PID]: ecpg_process_output on line 39: OK: CREATE TABLE [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 42: query: declare C cursor for select strval , strval from strdbase; with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: ecpg_execute on line 40: query: insert into strdbase values ( '' ); with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 40: using PQexec +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_process_output on line 40: OK: INSERT 0 1 +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 41: query: insert into strdbase values ( 'AB' ); with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 41: using PQexec +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_process_output on line 41: OK: INSERT 0 1 +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 42: query: insert into strdbase values ( 'ABCD' ); with 0 parameter(s) on connection ecpg1_regression [NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: ecpg_execute on line 42: using PQexec [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 42: OK: DECLARE CURSOR +[NO_PID]: ecpg_process_output on line 42: OK: INSERT 0 1 [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 50: query: fetch C; with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: ecpg_execute on line 43: query: insert into strdbase values ( 'ABCDE' ); with 0 parameter(s) on connection ecpg1_regression [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 50: using PQexec +[NO_PID]: ecpg_execute on line 43: using PQexec [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 50: correctly got 1 tuples with 2 fields +[NO_PID]: ecpg_process_output on line 43: OK: INSERT 0 1 [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_get_data on line 50: RESULT: offset: -1; array: no +[NO_PID]: ecpg_execute on line 44: query: insert into strdbase values ( 'ABCDEF' ); with 0 parameter(s) on connection ecpg1_regression [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_get_data on line 50: RESULT: offset: -1; array: no +[NO_PID]: ecpg_execute on line 44: using PQexec [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 50: query: fetch C; with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: ecpg_process_output on line 44: OK: INSERT 0 1 [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 50: using PQexec +[NO_PID]: ecpg_execute on line 45: query: insert into strdbase values ( 'ABCDEFGHIJ' ); with 0 parameter(s) on connection ecpg1_regression [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 50: correctly got 1 tuples with 2 fields +[NO_PID]: ecpg_execute on line 45: using PQexec [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_get_data on line 50: RESULT: AB offset: -1; array: no +[NO_PID]: ecpg_process_output on line 45: OK: INSERT 0 1 [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_get_data on line 50: RESULT: AB offset: -1; array: no +[NO_PID]: ecpg_execute on line 48: query: declare C cursor for select strval , strval from strdbase; with 0 parameter(s) on connection ecpg1_regression [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 50: query: fetch C; with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: ecpg_execute on line 48: using PQexec [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 50: using PQexec +[NO_PID]: ecpg_process_output on line 48: OK: DECLARE CURSOR [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 50: correctly got 1 tuples with 2 fields +[NO_PID]: ecpg_execute on line 56: query: fetch C; with 0 parameter(s) on connection ecpg1_regression [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_get_data on line 50: RESULT: ABCD offset: -1; array: no +[NO_PID]: ecpg_execute on line 56: using PQexec [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_get_data on line 50: RESULT: ABCD offset: -1; array: no +[NO_PID]: ecpg_process_output on line 56: correctly got 1 tuples with 2 fields [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 50: query: fetch C; with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: ecpg_get_data on line 56: RESULT: offset: -1; array: no [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 50: using PQexec +[NO_PID]: ecpg_get_data on line 56: RESULT: offset: -1; array: no [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 50: correctly got 1 tuples with 2 fields +[NO_PID]: ecpg_execute on line 56: query: fetch C; with 0 parameter(s) on connection ecpg1_regression [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_get_data on line 50: RESULT: ABCDE offset: -1; array: no +[NO_PID]: ecpg_execute on line 56: using PQexec [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_get_data on line 50: RESULT: ABCDE offset: -1; array: no +[NO_PID]: ecpg_process_output on line 56: correctly got 1 tuples with 2 fields +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_get_data on line 56: RESULT: AB offset: -1; array: no +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_get_data on line 56: RESULT: AB offset: -1; array: no +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 56: query: fetch C; with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 56: using PQexec +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_process_output on line 56: correctly got 1 tuples with 2 fields +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_get_data on line 56: RESULT: ABCD offset: -1; array: no +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_get_data on line 56: RESULT: ABCD offset: -1; array: no +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 56: query: fetch C; with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 56: using PQexec +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_process_output on line 56: correctly got 1 tuples with 2 fields +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_get_data on line 56: RESULT: ABCDE offset: -1; array: no +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_get_data on line 56: RESULT: ABCDE offset: -1; array: no [NO_PID]: sqlca: code: 0, state: 00000 Warning: At least one column was truncated -[NO_PID]: ecpg_execute on line 50: query: fetch C; with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: ecpg_execute on line 56: query: fetch C; with 0 parameter(s) on connection ecpg1_regression [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 50: using PQexec +[NO_PID]: ecpg_execute on line 56: using PQexec [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 50: correctly got 1 tuples with 2 fields +[NO_PID]: ecpg_process_output on line 56: correctly got 1 tuples with 2 fields [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_get_data on line 50: RESULT: ABCDEF offset: -1; array: no +[NO_PID]: ecpg_get_data on line 56: RESULT: ABCDEF offset: -1; array: no [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_get_data on line 50: RESULT: ABCDEF offset: -1; array: no +[NO_PID]: ecpg_get_data on line 56: RESULT: ABCDEF offset: -1; array: no [NO_PID]: sqlca: code: 0, state: 00000 Warning: At least one column was truncated -[NO_PID]: ecpg_execute on line 50: query: fetch C; with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: ecpg_execute on line 56: query: fetch C; with 0 parameter(s) on connection ecpg1_regression [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 50: using PQexec +[NO_PID]: ecpg_execute on line 56: using PQexec [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 50: correctly got 1 tuples with 2 fields +[NO_PID]: ecpg_process_output on line 56: correctly got 1 tuples with 2 fields [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_get_data on line 50: RESULT: ABCDEFGHIJ offset: -1; array: no +[NO_PID]: ecpg_get_data on line 56: RESULT: ABCDEFGHIJ offset: -1; array: no [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_get_data on line 50: RESULT: ABCDEFGHIJ offset: -1; array: no +[NO_PID]: ecpg_get_data on line 56: RESULT: ABCDEFGHIJ offset: -1; array: no [NO_PID]: sqlca: code: 0, state: 00000 Warning: At least one column was truncated -[NO_PID]: ecpg_execute on line 50: query: fetch C; with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: ecpg_execute on line 56: query: fetch C; with 0 parameter(s) on connection ecpg1_regression [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 50: using PQexec +[NO_PID]: ecpg_execute on line 56: using PQexec [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 50: correctly got 0 tuples with 2 fields +[NO_PID]: ecpg_process_output on line 56: correctly got 0 tuples with 2 fields [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: raising sqlcode 100 on line 50: no data found on line 50 +[NO_PID]: raising sqlcode 100 on line 56: no data found on line 56 [NO_PID]: sqlca: code: 100, state: 02000 -[NO_PID]: ecpg_execute on line 54: query: close C; with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: ecpg_execute on line 60: query: close C; with 0 parameter(s) on connection ecpg1_regression [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 54: using PQexec +[NO_PID]: ecpg_execute on line 60: using PQexec [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 54: OK: CLOSE CURSOR +[NO_PID]: ecpg_process_output on line 60: OK: CLOSE CURSOR [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 55: query: drop table strdbase; with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: ecpg_execute on line 61: query: drop table strdbase; with 0 parameter(s) on connection ecpg1_regression [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_execute on line 55: using PQexec +[NO_PID]: ecpg_execute on line 61: using PQexec [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_process_output on line 55: OK: DROP TABLE +[NO_PID]: ecpg_process_output on line 61: OK: DROP TABLE [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ECPGtrans on line 59: action "commit work"; connection "ecpg1_regression" +[NO_PID]: ECPGtrans on line 62: action "commit work"; connection "ecpg1_regression" +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: prepare_common on line 67: name stmt1; query: "SELECT 123::numeric(3,0), 't'::varchar(2)" +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 69: query: declare cur1 cursor for SELECT 123::numeric(3,0), 't'::varchar(2); with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 69: using PQexec +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_process_output on line 69: OK: DECLARE CURSOR +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 70: query: fetch next from cur1; with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 70: using PQexec +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_process_output on line 70: correctly got 1 tuples with 2 fields +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_build_native_sqlda on line 70 sqld = 2 +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_process_output on line 70: new sqlda was built +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_set_native_sqlda on line 70 row 0 col 0 IS NOT NULL +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_set_native_sqlda on line 70 row 0 col 1 IS NOT NULL +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_get_data on line 70: RESULT: t offset: -1; array: no +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_process_output on line 70: putting result (1 tuple 2 fields) into sqlda descriptor +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 85: query: close cur1; with 0 parameter(s) on connection ecpg1_regression +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 85: using PQexec +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_process_output on line 85: OK: CLOSE CURSOR +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ECPGtrans on line 86: action "commit work"; connection "ecpg1_regression" +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: deallocate_one on line 0: name stmt1 [NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: ecpg_finish: connection ecpg1_regression closed [NO_PID]: sqlca: code: 0, state: 00000 diff --git a/src/interfaces/ecpg/test/expected/compat_oracle-char_array.stdout b/src/interfaces/ecpg/test/expected/compat_oracle-char_array.stdout index d58b3c7be4..3dc3e546c8 100644 --- a/src/interfaces/ecpg/test/expected/compat_oracle-char_array.stdout +++ b/src/interfaces/ecpg/test/expected/compat_oracle-char_array.stdout @@ -6,5 +6,10 @@ Full Str. : Short Ind. "ABCDEF ": "ABCD" 6 "ABCDEFGHIJ": "ABCD" 10 +----------------- +type : data +numeric : "123" +varchar : "t" + GOOD-BYE!! -- 2.31.1
>From 4b517af92da992abcaa04170a577887e11e29302 Mon Sep 17 00:00:00 2001 From: Kyotaro Horiguchi <horikyota....@gmail.com> Date: Mon, 10 Apr 2023 16:39:44 +0900 Subject: [PATCH 2/2] Fix sqlda handling in ORACLE compat code When compiled with -C ORACLE, ecpg_get_data incorrectly stores the null terminator byte to str[-1] when varcharsize is zero. Fix this by assuming the callers provided a exact-fit storage, as they actually do, on which no padding or truncation is required. --- src/interfaces/ecpg/ecpglib/data.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c index bf9b313a11..3e47724b93 100644 --- a/src/interfaces/ecpg/ecpglib/data.c +++ b/src/interfaces/ecpg/ecpglib/data.c @@ -578,7 +578,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno, if (varcharsize == 0 && offset == sizeof(char *)) str = *(char **) str; - if (varcharsize == 0 || varcharsize > size) + if (varcharsize > size) { /* * compatibility mode, blank pad and null @@ -638,16 +638,25 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno, } else { - strncpy(str, pval, varcharsize); + int charsize = varcharsize; + + /* + * assume the caller provided exact-fit storage + * when varcharsize is zero + */ + if (varcharsize == 0) + charsize = size + 1; + + strncpy(str, pval, charsize); /* compatibility mode, null terminate char array */ - if (ORACLE_MODE(compat) && (varcharsize - 1) < size) + if (ORACLE_MODE(compat) && (charsize - 1) < size) { if (type == ECPGt_char || type == ECPGt_unsigned_char) - str[varcharsize - 1] = '\0'; + str[charsize - 1] = '\0'; } - if (varcharsize < size || (ORACLE_MODE(compat) && (varcharsize - 1) < size)) + if (charsize < size || (ORACLE_MODE(compat) && (charsize - 1) < size)) { /* truncation */ switch (ind_type) -- 2.31.1