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

Reply via email to