[PROPOSAL]a new data type 'bytea' for ECPG
Hi, Hackers
# This is my first post.
I will try to implement a new data type 'bytea' for ECPG.
I think that the implementation is not complicated.
Does anyone need it ?
* Why do I need bytea ?
Currently, ECPG program can treat binary data for bytea column with 'char' type
of C language, but it must convert from/to escaped format with PQunescapeBytea/
PQescapeBytea(). It forces users to add an unnecessary code and to pay cost for
the conversion in runtime.
# My PoC will not be able to solve output conversion cost.
I think that set/put data for host variable should be more simple.
The following is an example of Oracle Pro *C program for RAW type column.
VARCHAR raw_data[20];
/* preprocessed to the following
* struct
* {
* unsigned short len;
* unsigned char arr[20];
* } raw_data;
*/
raw_data.len = 10;
memcpy(raw_data.arr, data, 10);
see also:
https://docs.oracle.com/cd/E11882_01/appdev.112/e10825/pc_04dat.htm#i23305
In ECPG, varchar host variable cannot be used for bytea because it cannot treat
'\0' as part of data. If the length is set to 10 and there is '\0' at 3rd byte,
ecpglib truncates 3rd byte and later at the following:
[src/interfaces/ecpg/ecpglib/execute.c]
ecpg_store_input(const int lineno, const bool force_indicator, const struct
:
switch (var->type)
:
case ECPGt_varchar:
if (!(newcopy = (char *) ecpg_alloc(variable->len + 1, lineno)))
return false;
!! strncpy(newcopy, variable->arr, variable->len);
newcopy[variable->len] = '\0';
I also think that the behavior of varchar host variable should not be changed
because of compatibility.
Therefore, a new type of host variable 'bytea' is needed.
Since ecpglib can distinguish between C string and binary data, it can send
binary data to backend directly by using 'paramFormats' argument of PQexecParams().
Unfortunately, the conversion of output data cannot be omitted in ecpglib because
libpq doesn't provide like 'paramFormats'.
('resultFormat' means that *all* data from backend is formatted by binary or not.)
PQexecParams(PGconn *conn,
const char *command,
int nParams,
const Oid *paramTypes,
const char *const *paramValues,
const int *paramLengths,
!! const int *paramFormats,
int resultFormat)
* How to use new 'bytea' ?
ECPG programmers can use almost same as 'varchar' but cannot use as names.
(e.g. connection name, prepared statement name, cursor name and so on)
- Can use in Declare Section.
exec sql begin declare section;
bytea data1[512];
bytea data2[DATA_SIZE]; /* can use macro */
bytea send_data[DATA_NUM][DATA_SIZE]; /* can use two dimensional array */
bytea recv_data[][DATA_SIZE]; /* can use flexible array */
exec sql end declare section;
- Can *not* use for name.
exec sql begin declare section;
bytea conn_name[DATA_SIZE];
exec sql end declare section;
exec sql connect to :conn_name; !! error
- Conversion is not needed in user program.
exec sql begin declare section;
bytea send_buf[DATA_SIZE];
bytea recv_buf[DATA_SIZE - 13];
int ind_recv;
exec sql end declare section;
exec sql create table test (data1 bytea);
exec sql truncate test;
exec sql insert into test (data1) values (:send_buf);
exec sql select data1 into :recv_buf:ind_recv from test;
/* ind_recv is set to 13. */
* How to preprocess 'bytea' ?
'bytea' is preprocessed almost same as varchar.
The following is preprocessed to the next.
exec sql begin declare section;
bytea data[DATA_SIZE];
bytea send_data[DATA_NUM][DATA_SIZE];
bytea recv_data[][DATA_SIZE];
exec sql end declare section;
struct bytea_1 {int len; char arr[DATA_SIZE]} data;
struct bytea_2 {int len; char arr[DATA_SIZE]} send_data[DATA_NUM];
struct bytea_3 {int len; char arr[DATA_SIZE]} *recv_data;
Thank you for your consideration.
Regards
Ryo Matsumura
Hackers
No one commented to the proposal, but I'm not discouraged.
I attach a patch. Please review or comment to proposal.
Note:
- The patch cannot yet decode escape format data from backend.
- [ecpg/test/expected/sql-bytea.stderr] in patch includes non-ascii data.
I explain a little about the patch.
Preproc:
Almost same as varchar.
Ecpglib:
- ecpg_build_params()
Build two more arrays paramlengths and paramformats for PQexecParams().
If input variable type is bytea, set pramformats to 1(= is binary) and
set binary data length to paramlengths.
- ecpg_store_input()
If input variable type is bytea, copy its binary data to ecpg_alloc-ed area directly.
- ecpg_get_data()
If output variable type is bytea, decode received results to user area.
Encode/decode function is imported from backend/utils/adt/encode.c
- ECPGset_desc()
Currently ecpglib saves data to internal area(struct descriptor_item) for execution phase,
but doesn't save type information that is needed in case of bytea.
So I add a member is_binary to descriptor_item structure.
Thank you.
Regards
Ryo Matsumura
Show quoted text
-----Original Message-----
From: Matsumura, Ryo [mailto:matsumura.ryo@jp.fujitsu.com]
Sent: Monday, October 1, 2018 5:04 PM
To: pgsql-hackers@lists.postgresql.org
Subject: [PROPOSAL]a new data type 'bytea' for ECPGHi, Hackers
# This is my first post.
I will try to implement a new data type 'bytea' for ECPG.
I think that the implementation is not complicated.
Does anyone need it ?* Why do I need bytea ?
Currently, ECPG program can treat binary data for bytea column with 'char'
type
of C language, but it must convert from/to escaped format with PQunescapeBytea/
PQescapeBytea(). It forces users to add an unnecessary code and to pay cost
for
the conversion in runtime.
# My PoC will not be able to solve output conversion cost.I think that set/put data for host variable should be more simple.
The following is an example of Oracle Pro *C program for RAW type column.VARCHAR raw_data[20];
/* preprocessed to the following
* struct
* {
* unsigned short len;
* unsigned char arr[20];
* } raw_data;
*/raw_data.len = 10;
memcpy(raw_data.arr, data, 10);see also:
https://docs.oracle.com/cd/E11882_01/appdev.112/e10825/pc_04dat.htm#i2330
5In ECPG, varchar host variable cannot be used for bytea because it cannot treat
'\0' as part of data. If the length is set to 10 and there is '\0' at 3rd byte,
ecpglib truncates 3rd byte and later at the following:[src/interfaces/ecpg/ecpglib/execute.c]
ecpg_store_input(const int lineno, const bool force_indicator, const struct
:
switch (var->type)
:
case ECPGt_varchar:
if (!(newcopy = (char *) ecpg_alloc(variable->len + 1, lineno)))
return false;
!! strncpy(newcopy, variable->arr, variable->len);
newcopy[variable->len] = '\0';I also think that the behavior of varchar host variable should not be changed
because of compatibility.
Therefore, a new type of host variable 'bytea' is needed.Since ecpglib can distinguish between C string and binary data, it can send
binary data to backend directly by using 'paramFormats' argument of
PQexecParams().
Unfortunately, the conversion of output data cannot be omitted in ecpglib
because
libpq doesn't provide like 'paramFormats'.
('resultFormat' means that *all* data from backend is formatted by binary
or not.)PQexecParams(PGconn *conn,
const char *command,
int nParams,
const Oid *paramTypes,
const char *const *paramValues,
const int *paramLengths,
!! const int *paramFormats,
int resultFormat)* How to use new 'bytea' ?
ECPG programmers can use almost same as 'varchar' but cannot use as names.
(e.g. connection name, prepared statement name, cursor name and so on)- Can use in Declare Section.
exec sql begin declare section;
bytea data1[512];
bytea data2[DATA_SIZE]; /* can use macro */
bytea send_data[DATA_NUM][DATA_SIZE]; /* can use two dimensional array
*/
bytea recv_data[][DATA_SIZE]; /* can use flexible array */
exec sql end declare section;- Can *not* use for name.
exec sql begin declare section;
bytea conn_name[DATA_SIZE];
exec sql end declare section;exec sql connect to :conn_name; !! error
- Conversion is not needed in user program.
exec sql begin declare section;
bytea send_buf[DATA_SIZE];
bytea recv_buf[DATA_SIZE - 13];
int ind_recv;
exec sql end declare section;exec sql create table test (data1 bytea);
exec sql truncate test;
exec sql insert into test (data1) values (:send_buf);
exec sql select data1 into :recv_buf:ind_recv from test;
/* ind_recv is set to 13. */* How to preprocess 'bytea' ?
'bytea' is preprocessed almost same as varchar.
The following is preprocessed to the next.exec sql begin declare section;
bytea data[DATA_SIZE];
bytea send_data[DATA_NUM][DATA_SIZE];
bytea recv_data[][DATA_SIZE];
exec sql end declare section;struct bytea_1 {int len; char arr[DATA_SIZE]} data;
struct bytea_2 {int len; char arr[DATA_SIZE]} send_data[DATA_NUM];
struct bytea_3 {int len; char arr[DATA_SIZE]} *recv_data;Thank you for your consideration.
Regards
Ryo Matsumura
Attachments:
ecpg_bytea_v1.patchapplication/octet-stream; name=ecpg_bytea_v1.patchDownload
diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c
index f3d326a..bc01ca9 100644
--- a/src/interfaces/ecpg/ecpglib/data.c
+++ b/src/interfaces/ecpg/ecpglib/data.c
@@ -122,6 +122,71 @@ check_special_value(char *ptr, double *retval, char **endptr)
return false;
}
+/* imported from src/backend/utils/adt/encode.c */
+
+static unsigned
+hex_enc_len(unsigned srclen)
+{
+ return srclen << 1;
+}
+extern unsigned
+ecpg_hex_dec_len(unsigned srclen)
+{
+ return srclen >> 1;
+}
+
+static inline char
+get_hex(char c)
+{
+ static const int8 hexlookup[128] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+ int res = -1;
+
+ if (c > 0 && c < 127)
+ res = hexlookup[(unsigned char) c];
+
+ return (char) res;
+}
+
+static unsigned
+hex_decode(const char *src, unsigned len, char *dst)
+{
+ const char *s,
+ *srcend;
+ char v1,
+ v2,
+ *p;
+
+ srcend = src + len;
+ s = src;
+ p = dst;
+ while (s < srcend)
+ {
+ if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
+ {
+ s++;
+ continue;
+ }
+ v1 = get_hex(*s++) << 4;
+ if (s >= srcend)
+ return -1;
+
+ v2 = get_hex(*s++);
+ *p++ = v1 | v2;
+ }
+
+ return p - dst;
+}
+
+
bool
ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
enum ECPGttype type, enum ECPGttype ind_type,
@@ -447,6 +512,55 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
return false;
break;
+ case ECPGt_bytea:
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var + offset * act_tuple);
+ long dst_size,
+ src_size,
+ dec_size;
+
+ dst_size = hex_enc_len(varcharsize);
+ src_size = size - 2; /* exclude backslash + 'x' */
+ dec_size = src_size < dst_size ? src_size : dst_size;
+ variable->len = hex_decode(pval + 2, dec_size, variable->arr);
+
+ if (dst_size < src_size)
+ {
+ long rcv_size = ecpg_hex_dec_len(size - 2);
+
+ /* truncation */
+ switch (ind_type)
+ {
+ case ECPGt_short:
+ case ECPGt_unsigned_short:
+ *((short *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+ case ECPGt_int:
+ case ECPGt_unsigned_int:
+ *((int *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+ case ECPGt_long:
+ case ECPGt_unsigned_long:
+ *((long *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+#ifdef HAVE_LONG_LONG_INT
+ case ECPGt_long_long:
+ case ECPGt_unsigned_long_long:
+ *((long long int *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+#endif /* HAVE_LONG_LONG_INT */
+ default:
+ break;
+ }
+ sqlca->sqlwarn[0] = sqlca->sqlwarn[1] = 'W';
+ }
+
+ pval += size;
+
+ }
+ break;
+
case ECPGt_char:
case ECPGt_unsigned_char:
case ECPGt_string:
diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c
index 8fdf560..441b4e6 100644
--- a/src/interfaces/ecpg/ecpglib/descriptor.c
+++ b/src/interfaces/ecpg/ecpglib/descriptor.c
@@ -636,11 +636,29 @@ ECPGset_desc(int lineno, const char *desc_name, int index,...)
{
case ECPGd_data:
{
- if (!ecpg_store_input(lineno, true, var, &tobeinserted, false))
+ if (var->type != ECPGt_bytea)
{
- ecpg_free(var);
- va_end(args);
- return false;
+ if (!ecpg_store_input(lineno, true, var, &tobeinserted, false))
+ {
+ ecpg_free(var);
+ va_end(args);
+ return false;
+ }
+ desc_item->is_binary = false;
+ }
+ else
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var->value);
+
+ if (!(tobeinserted = ecpg_alloc(sizeof(int) + variable->len, lineno)))
+ {
+ ecpg_free(var);
+ va_end(args);
+ return false;
+ }
+ memcpy(tobeinserted, var->value, sizeof(int) + variable->len);
+ desc_item->is_binary = true;
}
ecpg_free(desc_item->data); /* free() takes care of a
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index 42640ba..942bc11 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -799,6 +799,20 @@ ecpg_store_input(const int lineno, const bool force_indicator, const struct vari
*tobeinserted_p = mallocedval;
}
break;
+
+ case ECPGt_bytea:
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var->value);
+
+ if (!(mallocedval = (char *) ecpg_alloc(variable->len, lineno)))
+ return false;
+
+ memcpy(mallocedval, variable->arr, variable->len);
+ *tobeinserted_p = mallocedval;
+ }
+ break;
+
case ECPGt_varchar:
{
struct ECPGgeneric_varchar *variable =
@@ -1053,7 +1067,11 @@ ecpg_free_params(struct statement *stmt, bool print)
ecpg_free(stmt->paramvalues[n]);
}
ecpg_free(stmt->paramvalues);
+ ecpg_free(stmt->paramlengths);
+ ecpg_free(stmt->paramformats);
stmt->paramvalues = NULL;
+ stmt->paramlengths = NULL;
+ stmt->paramformats = NULL;
stmt->nparams = 0;
}
@@ -1120,8 +1138,13 @@ ecpg_build_params(struct statement *stmt)
{
char *tobeinserted;
int counter = 1;
+ bool binary_format;
+ int binary_length;
+
tobeinserted = NULL;
+ binary_length = 0;
+ binary_format = false;
/*
* A descriptor is a special case since it contains many variables but
@@ -1146,12 +1169,27 @@ ecpg_build_params(struct statement *stmt)
{
if (desc_item->num == desc_counter)
{
- desc_inlist.type = ECPGt_char;
+ if (!desc_item->is_binary)
+ {
+ desc_inlist.type = ECPGt_char;
+ desc_inlist.varcharsize = strlen(desc_item->data);
+ }
+ else
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (desc_item->data);
+
+ binary_length = variable->len;
+ binary_format = true;
+
+ desc_inlist.type = ECPGt_bytea;
+ desc_inlist.varcharsize = binary_length;
+ }
desc_inlist.value = desc_item->data;
desc_inlist.pointer = &(desc_item->data);
- desc_inlist.varcharsize = strlen(desc_item->data);
desc_inlist.arrsize = 1;
desc_inlist.offset = 0;
+
if (!desc_item->indicator)
{
desc_inlist.ind_type = ECPGt_NO_INDICATOR;
@@ -1293,6 +1331,15 @@ ecpg_build_params(struct statement *stmt)
{
if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, var, &tobeinserted, false))
return false;
+
+ if (var->type == ECPGt_bytea)
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var->value);
+
+ binary_length = variable->len;
+ binary_format = true;
+ }
}
/*
@@ -1346,16 +1393,32 @@ ecpg_build_params(struct statement *stmt)
else
{
char **paramvalues;
+ int *paramlengths;
+ int *paramformats;
if (!(paramvalues = (char **) ecpg_realloc(stmt->paramvalues, sizeof(char *) * (stmt->nparams + 1), stmt->lineno)))
{
ecpg_free_params(stmt, false);
return false;
}
+ if (!(paramlengths = (int *) ecpg_realloc(stmt->paramlengths, sizeof(int) * (stmt->nparams + 1), stmt->lineno)))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+ if (!(paramformats = (int *) ecpg_realloc(stmt->paramformats, sizeof(int) * (stmt->nparams + 1), stmt->lineno)))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
stmt->nparams++;
stmt->paramvalues = paramvalues;
+ stmt->paramlengths = paramlengths;
+ stmt->paramformats = paramformats;
stmt->paramvalues[stmt->nparams - 1] = tobeinserted;
+ stmt->paramlengths[stmt->nparams - 1] = binary_length;
+ stmt->paramformats[stmt->nparams - 1] = (binary_format ? 1 : 0);
/* let's see if this was an old style placeholder */
if (stmt->command[position] == '?')
@@ -1428,7 +1491,13 @@ ecpg_execute(struct statement *stmt)
ecpg_log("ecpg_execute on line %d: query: %s; with %d parameter(s) on connection %s\n", stmt->lineno, stmt->command, stmt->nparams, stmt->connection->name);
if (stmt->statement_type == ECPGst_execute)
{
- stmt->results = PQexecPrepared(stmt->connection->connection, stmt->name, stmt->nparams, (const char *const *) stmt->paramvalues, NULL, NULL, 0);
+ stmt->results = PQexecPrepared(stmt->connection->connection,
+ stmt->name,
+ stmt->nparams,
+ (const char *const *) stmt->paramvalues,
+ (const int *) stmt->paramlengths,
+ (const int *) stmt->paramformats,
+ 0);
ecpg_log("ecpg_execute on line %d: using PQexecPrepared for \"%s\"\n", stmt->lineno, stmt->command);
}
else
@@ -1440,7 +1509,12 @@ ecpg_execute(struct statement *stmt)
}
else
{
- stmt->results = PQexecParams(stmt->connection->connection, stmt->command, stmt->nparams, NULL, (const char *const *) stmt->paramvalues, NULL, NULL, 0);
+ stmt->results = PQexecParams(stmt->connection->connection,
+ stmt->command, stmt->nparams, NULL,
+ (const char *const *) stmt->paramvalues,
+ (const int *) stmt->paramlengths,
+ (const int *) stmt->paramformats,
+ 0);
ecpg_log("ecpg_execute on line %d: using PQexecParams\n", stmt->lineno);
}
}
diff --git a/src/interfaces/ecpg/ecpglib/extern.h b/src/interfaces/ecpg/ecpglib/extern.h
index a88f341..79e451d 100644
--- a/src/interfaces/ecpg/ecpglib/extern.h
+++ b/src/interfaces/ecpg/ecpglib/extern.h
@@ -37,6 +37,13 @@ struct ECPGgeneric_varchar
char arr[FLEXIBLE_ARRAY_MEMBER];
};
+/* A generic bytea type. */
+struct ECPGgeneric_bytea
+{
+ int len;
+ char arr[FLEXIBLE_ARRAY_MEMBER];
+};
+
/*
* type information cache
*/
@@ -64,6 +71,8 @@ struct statement
char *oldlocale;
int nparams;
char **paramvalues;
+ int *paramlengths;
+ int *paramformats;
PGresult *results;
};
@@ -106,6 +115,8 @@ struct descriptor_item
int precision;
int scale;
int type;
+ bool is_binary;
+ int data_len;
struct descriptor_item *next;
};
@@ -194,6 +205,7 @@ struct sqlda_compat *ecpg_build_compat_sqlda(int, PGresult *, int, enum COMPAT_M
void ecpg_set_compat_sqlda(int, struct sqlda_compat **, const PGresult *, int, enum COMPAT_MODE);
struct sqlda_struct *ecpg_build_native_sqlda(int, PGresult *, int, enum COMPAT_MODE);
void ecpg_set_native_sqlda(int, struct sqlda_struct **, const PGresult *, int, enum COMPAT_MODE);
+unsigned ecpg_hex_dec_len(unsigned srclen);
/* SQLSTATE values generated or processed by ecpglib (intentionally
* not exported -- users should refer to the codes directly) */
diff --git a/src/interfaces/ecpg/ecpglib/misc.c b/src/interfaces/ecpg/ecpglib/misc.c
index be9cac6..e5432a2 100644
--- a/src/interfaces/ecpg/ecpglib/misc.c
+++ b/src/interfaces/ecpg/ecpglib/misc.c
@@ -355,6 +355,9 @@ ECPGset_noind_null(enum ECPGttype type, void *ptr)
*(((struct ECPGgeneric_varchar *) ptr)->arr) = 0x00;
((struct ECPGgeneric_varchar *) ptr)->len = 0;
break;
+ case ECPGt_bytea:
+ ((struct ECPGgeneric_bytea *) ptr)->len = 0;
+ break;
case ECPGt_decimal:
memset((char *) ptr, 0, sizeof(decimal));
((decimal *) ptr)->sign = NUMERIC_NULL;
@@ -428,6 +431,10 @@ ECPGis_noind_null(enum ECPGttype type, const void *ptr)
if (*(((const struct ECPGgeneric_varchar *) ptr)->arr) == 0x00)
return true;
break;
+ case ECPGt_bytea:
+ if (((struct ECPGgeneric_bytea *) ptr)->len == 0)
+ return true;
+ break;
case ECPGt_decimal:
if (((const decimal *) ptr)->sign == NUMERIC_NULL)
return true;
diff --git a/src/interfaces/ecpg/ecpglib/typename.c b/src/interfaces/ecpg/ecpglib/typename.c
index 9da1cdf..572bc78 100644
--- a/src/interfaces/ecpg/ecpglib/typename.c
+++ b/src/interfaces/ecpg/ecpglib/typename.c
@@ -48,6 +48,8 @@ ecpg_type_name(enum ECPGttype typ)
return "bool";
case ECPGt_varchar:
return "varchar";
+ case ECPGt_bytea:
+ return "bytea";
case ECPGt_char_variable:
return "char";
case ECPGt_decimal:
diff --git a/src/interfaces/ecpg/include/ecpgtype.h b/src/interfaces/ecpg/include/ecpgtype.h
index 38fb3b6..b9fc4ea 100644
--- a/src/interfaces/ecpg/include/ecpgtype.h
+++ b/src/interfaces/ecpg/include/ecpgtype.h
@@ -63,7 +63,8 @@ enum ECPGttype
ECPGt_EORT, /* End of result types. */
ECPGt_NO_INDICATOR, /* no indicator */
ECPGt_string, /* trimmed (char *) type */
- ECPGt_sqlda /* C struct descriptor */
+ ECPGt_sqlda, /* C struct descriptor */
+ ECPGt_bytea
};
/* descriptor items */
@@ -88,7 +89,7 @@ enum ECPGdtype
ECPGd_cardinality
};
-#define IS_SIMPLE_TYPE(type) (((type) >= ECPGt_char && (type) <= ECPGt_interval) || ((type) == ECPGt_string))
+#define IS_SIMPLE_TYPE(type) (((type) >= ECPGt_char && (type) <= ECPGt_interval) || ((type) == ECPGt_string) || ((type) == ECPGt_bytea))
/* we also have to handle different statement types */
enum ECPG_statement_type
diff --git a/src/interfaces/ecpg/preproc/ecpg.header b/src/interfaces/ecpg/preproc/ecpg.header
index 8921bcb..4669dff 100644
--- a/src/interfaces/ecpg/preproc/ecpg.header
+++ b/src/interfaces/ecpg/preproc/ecpg.header
@@ -46,6 +46,7 @@ static char pacounter_buffer[sizeof(int) * CHAR_BIT * 10 / 3]; /* a rough guess
static struct this_type actual_type[STRUCT_DEPTH];
static char *actual_startline[STRUCT_DEPTH];
static int varchar_counter = 1;
+static int bytea_counter = 1;
/* temporarily store struct members while creating the data structure */
struct ECPGstruct_member *struct_member_list[STRUCT_DEPTH] = { NULL };
@@ -562,6 +563,7 @@ add_typedef(char *name, char *dimension, char *length, enum ECPGttype type_enum,
ECPGstruct_member_dup(struct_member_list[struct_level]) : NULL;
if (type_enum != ECPGt_varchar &&
+ type_enum != ECPGt_bytea &&
type_enum != ECPGt_char &&
type_enum != ECPGt_unsigned_char &&
type_enum != ECPGt_string &&
diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer
index 19dc781..c44fcf9 100644
--- a/src/interfaces/ecpg/preproc/ecpg.trailer
+++ b/src/interfaces/ecpg/preproc/ecpg.trailer
@@ -550,6 +550,14 @@ var_type: simple_type
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
+ else if (strcmp($1, "bytea") == 0)
+ {
+ $$.type_enum = ECPGt_bytea;
+ $$.type_str = EMPTY;
+ $$.type_dimension = mm_strdup("-1");
+ $$.type_index = mm_strdup("-1");
+ $$.type_sizeof = NULL;
+ }
else if (strcmp($1, "float") == 0)
{
$$.type_enum = ECPGt_float;
@@ -627,7 +635,7 @@ var_type: simple_type
/* this is for typedef'ed types */
struct typedefs *this = get_typedef($1);
- $$.type_str = (this->type->type_enum == ECPGt_varchar) ? EMPTY : mm_strdup(this->name);
+ $$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? EMPTY : mm_strdup(this->name);
$$.type_enum = this->type->type_enum;
$$.type_dimension = this->type->type_dimension;
$$.type_index = this->type->type_index;
@@ -838,7 +846,7 @@ variable_list: variable
{ $$ = $1; }
| variable_list ',' variable
{
- if (actual_type[struct_level].type_enum == ECPGt_varchar)
+ if (actual_type[struct_level].type_enum == ECPGt_varchar || actual_type[struct_level].type_enum == ECPGt_bytea)
$$ = cat_str(3, $1, mm_strdup(";"), $3);
else
$$ = cat_str(3, $1, mm_strdup(","), $3);
@@ -852,9 +860,10 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
char *length = $3.index2; /* length of string */
char *dim_str;
char *vcn;
+ int *varlen_type_counter;
+ char *struct_name;
adjust_array(actual_type[struct_level].type_enum, &dimension, &length, actual_type[struct_level].type_dimension, actual_type[struct_level].type_index, strlen($1), false);
-
switch (actual_type[struct_level].type_enum)
{
case ECPGt_struct:
@@ -868,10 +877,21 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
break;
case ECPGt_varchar:
+ case ECPGt_bytea:
+ if (actual_type[struct_level].type_enum == ECPGt_varchar)
+ {
+ varlen_type_counter = &varchar_counter;
+ struct_name = " struct varchar_";
+ }
+ else
+ {
+ varlen_type_counter = &bytea_counter;
+ struct_name = " struct bytea_";
+ }
if (atoi(dimension) < 0)
- type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length, varchar_counter);
+ type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length, *varlen_type_counter);
else
- type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length, varchar_counter), dimension);
+ type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length, *varlen_type_counter), dimension);
if (strcmp(dimension, "0") == 0 || abs(atoi(dimension)) == 1)
dim_str=mm_strdup("");
@@ -883,12 +903,12 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
/* make sure varchar struct name is unique by adding a unique counter to its definition */
vcn = (char *) mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
- sprintf(vcn, "%d", varchar_counter);
+ sprintf(vcn, "%d", *varlen_type_counter);
if (strcmp(dimension, "0") == 0)
- $$ = cat_str(7, make2_str(mm_strdup(" struct varchar_"), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } *"), mm_strdup($2), $4, $5);
+ $$ = cat_str(7, make2_str(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } *"), mm_strdup($2), $4, $5);
else
- $$ = cat_str(8, make2_str(mm_strdup(" struct varchar_"), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } "), mm_strdup($2), dim_str, $4, $5);
- varchar_counter++;
+ $$ = cat_str(8, make2_str(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } "), mm_strdup($2), dim_str, $4, $5);
+ (*varlen_type_counter)++;
break;
case ECPGt_char:
@@ -1354,6 +1374,7 @@ ECPGVar: SQL_VAR
break;
case ECPGt_varchar:
+ case ECPGt_bytea:
if (atoi(dimension) == -1)
type = ECPGmake_simple_type($5.type_enum, length, 0);
else
diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c
index 253873d..44c8a2d 100644
--- a/src/interfaces/ecpg/preproc/type.c
+++ b/src/interfaces/ecpg/preproc/type.c
@@ -102,7 +102,7 @@ ECPGmake_simple_type(enum ECPGttype type, char *size, int counter)
ne->size = size;
ne->u.element = NULL;
ne->struct_sizeof = NULL;
- ne->counter = counter; /* only needed for varchar */
+ ne->counter = counter; /* only needed for varchar and bytea */
return ne;
}
@@ -175,6 +175,8 @@ get_type(enum ECPGttype type)
break;
case ECPGt_varchar:
return "ECPGt_varchar";
+ case ECPGt_bytea:
+ return "ECPGt_bytea";
case ECPGt_NO_INDICATOR: /* no indicator */
return "ECPGt_NO_INDICATOR";
break;
@@ -424,6 +426,7 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type,
{
char *variable = (char *) mm_alloc(strlen(name) + ((prefix == NULL) ? 0 : strlen(prefix)) + 4);
char *offset = (char *) mm_alloc(strlen(name) + strlen("sizeof(struct varchar_)") + 1 + strlen(varcharsize) + sizeof(int) * CHAR_BIT * 10 / 3);
+ char *struct_name;
switch (type)
{
@@ -433,6 +436,7 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type,
*/
case ECPGt_varchar:
+ case ECPGt_bytea:
/*
* we have to use the pointer except for arrays with given
@@ -449,10 +453,15 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type,
* If we created a varchar structure automatically, counter is
* greater than 0.
*/
+ if (type == ECPGt_varchar)
+ struct_name = "struct varchar";
+ else
+ struct_name = "struct bytea";
+
if (counter)
- sprintf(offset, "sizeof(struct varchar_%d)", counter);
+ sprintf(offset, "sizeof(%s_%d)", struct_name, counter);
else
- sprintf(offset, "sizeof(struct varchar)");
+ sprintf(offset, "sizeof(%s)", struct_name);
break;
case ECPGt_char:
case ECPGt_unsigned_char:
diff --git a/src/interfaces/ecpg/preproc/variable.c b/src/interfaces/ecpg/preproc/variable.c
index 39bf3b2..0939de5 100644
--- a/src/interfaces/ecpg/preproc/variable.c
+++ b/src/interfaces/ecpg/preproc/variable.c
@@ -560,6 +560,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
break;
case ECPGt_varchar:
+ case ECPGt_bytea:
/* pointer has to get dimension 0 */
if (pointer_len)
*dimension = mm_strdup("0");
diff --git a/src/interfaces/ecpg/test/ecpg_schedule b/src/interfaces/ecpg/test/ecpg_schedule
index 991b8cb..2478d03 100644
--- a/src/interfaces/ecpg/test/ecpg_schedule
+++ b/src/interfaces/ecpg/test/ecpg_schedule
@@ -32,6 +32,7 @@ test: preproc/whenever
test: preproc/whenever_do_continue
test: sql/array
test: sql/binary
+test: sql/bytea
test: sql/code100
test: sql/copystdout
test: sql/define
diff --git a/src/interfaces/ecpg/test/expected/sql-bytea.c b/src/interfaces/ecpg/test/expected/sql-bytea.c
new file mode 100644
index 0000000..bcaccd1
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-bytea.c
@@ -0,0 +1,316 @@
+/* Processed by ecpg (regression mode) */
+/* These include files are added by the preprocessor */
+#include <ecpglib.h>
+#include <ecpgerrno.h>
+#include <sqlca.h>
+/* End of automatic include section */
+#define ECPGdebug(X,Y) ECPGdebug((X)+100,(Y))
+
+#line 1 "bytea.pgc"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+
+
+#line 1 "regression.h"
+
+
+
+
+
+
+#line 6 "bytea.pgc"
+
+/* exec sql whenever sqlerror sqlprint ; */
+#line 7 "bytea.pgc"
+
+
+static void
+dump_binary(char *buf, int len, int ind)
+{
+ int i;
+
+ printf("len=%d, ind=%d, data=0x", len, ind);
+ for (i = 0; i < len; ++i)
+ printf("%02x", 0xff & buf[i]);
+ printf("\n");
+}
+
+#define DATA_SIZE 0x200
+#define LACK_SIZE 13
+#
+int
+main(void)
+{
+/* exec sql begin declare section */
+
+
+
+
+
+
+#line 27 "bytea.pgc"
+ struct bytea_1 { int len; char arr[ DATA_SIZE ]; } send_buf [ 2 ] ;
+
+#line 28 "bytea.pgc"
+ struct bytea_2 { int len; char arr[ DATA_SIZE ]; } recv_buf [ 2 ] ;
+
+#line 29 "bytea.pgc"
+ struct bytea_3 { int len; char arr[ DATA_SIZE ]; } * recv_vlen_buf ;
+
+#line 30 "bytea.pgc"
+ struct bytea_4 { int len; char arr[ DATA_SIZE - LACK_SIZE ]; } recv_short_buf ;
+
+#line 31 "bytea.pgc"
+ int ind [ 2 ] ;
+/* exec sql end declare section */
+#line 32 "bytea.pgc"
+
+ int i, j, c;
+
+#define init() { \
+ for (i = 0; i < 2; ++i) \
+ { \
+ memset(recv_buf[i].arr, 0x0, sizeof(recv_buf[i].arr)); \
+ recv_buf[i].len = 0; \
+ ind[i] = 0; \
+ } \
+ recv_vlen_buf = NULL, \
+ memset(recv_short_buf.arr, 0x0, sizeof(recv_short_buf.arr)); \
+} \
+while (0)
+
+
+ ECPGdebug(1, stderr);
+
+ for (i = 0; i < 2; ++i)
+ {
+ for (j = 0, c = 0xff; (c == -1 ? c = 0xff : 1), j < DATA_SIZE; ++j, --c)
+ send_buf[i].arr[j] = c;
+
+ send_buf[i].len = DATA_SIZE;
+ }
+
+ { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0);
+#line 58 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 58 "bytea.pgc"
+
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table if not exists test ( data1 bytea , data2 bytea )", ECPGt_EOIT, ECPGt_EORT);
+#line 60 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 60 "bytea.pgc"
+
+
+ { ECPGprepare(__LINE__, NULL, 0, "ins_stmt", "insert into test values(?,?)");
+#line 62 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 62 "bytea.pgc"
+
+ { ECPGprepare(__LINE__, NULL, 0, "sel_stmt", "select data1,data2 from test");
+#line 63 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 63 "bytea.pgc"
+
+ ECPGallocate_desc(__LINE__, "idesc");
+#line 64 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();
+#line 64 "bytea.pgc"
+
+ ECPGallocate_desc(__LINE__, "odesc");
+#line 65 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();
+#line 65 "bytea.pgc"
+
+
+ /* Test for static sql statement with normal host variable, indicator */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 69 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 69 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test values ( $1 , $2 )",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 70 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 70 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select data1 , data2 from test", ECPGt_EOIT,
+ ECPGt_bytea,&(recv_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_2),
+ ECPGt_int,&(ind[0]),(long)1,(long)1,sizeof(int),
+ ECPGt_bytea,&(recv_short_buf),(long)DATA_SIZE - LACK_SIZE,(long)1,sizeof(struct bytea_4),
+ ECPGt_int,&(ind[1]),(long)1,(long)1,sizeof(int), ECPGt_EORT);
+#line 71 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 71 "bytea.pgc"
+
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for variable length array */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 77 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 77 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test values ( $1 , $2 )",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 78 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 78 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test values ( $1 , $2 )",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 79 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 79 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select data1 from test", ECPGt_EOIT,
+ ECPGt_bytea,&(recv_vlen_buf),(long)DATA_SIZE,(long)0,sizeof(struct bytea_3),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 80 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 80 "bytea.pgc"
+
+ dump_binary(recv_vlen_buf[0].arr, recv_vlen_buf[0].len, 0);
+ dump_binary(recv_vlen_buf[1].arr, recv_vlen_buf[1].len, 0);
+
+ /* Test for dynamic sql statement with normal host variable, indicator */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 86 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 86 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "ins_stmt",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 87 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 87 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "sel_stmt", ECPGt_EOIT,
+ ECPGt_bytea,&(recv_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_2),
+ ECPGt_int,&(ind[0]),(long)1,(long)1,sizeof(int),
+ ECPGt_bytea,&(recv_short_buf),(long)DATA_SIZE - LACK_SIZE,(long)1,sizeof(struct bytea_4),
+ ECPGt_int,&(ind[1]),(long)1,(long)1,sizeof(int), ECPGt_EORT);
+#line 88 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 88 "bytea.pgc"
+
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for dynamic sql statement with sql descriptor */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 94 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 94 "bytea.pgc"
+
+ { ECPGset_desc(__LINE__, "idesc", 1,ECPGd_data,
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1), ECPGd_EODT);
+
+#line 95 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 95 "bytea.pgc"
+
+ { ECPGset_desc(__LINE__, "idesc", 2,ECPGd_data,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1), ECPGd_EODT);
+
+#line 96 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 96 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "ins_stmt",
+ ECPGt_descriptor, "idesc", 1L, 1L, 1L,
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 97 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 97 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "sel_stmt", ECPGt_EOIT,
+ ECPGt_descriptor, "odesc", 1L, 1L, 1L,
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 98 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 98 "bytea.pgc"
+
+ { ECPGget_desc(__LINE__, "odesc", 1,ECPGd_indicator,
+ ECPGt_int,&(ind[0]),(long)1,(long)1,sizeof(int), ECPGd_data,
+ ECPGt_bytea,&(recv_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_2), ECPGd_EODT);
+
+#line 99 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 99 "bytea.pgc"
+
+ { ECPGget_desc(__LINE__, "odesc", 2,ECPGd_indicator,
+ ECPGt_int,&(ind[1]),(long)1,(long)1,sizeof(int), ECPGd_data,
+ ECPGt_bytea,&(recv_short_buf),(long)DATA_SIZE - LACK_SIZE,(long)1,sizeof(struct bytea_4), ECPGd_EODT);
+
+#line 100 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 100 "bytea.pgc"
+
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table test", ECPGt_EOIT, ECPGt_EORT);
+#line 104 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 104 "bytea.pgc"
+
+ { ECPGtrans(__LINE__, NULL, "commit");
+#line 105 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 105 "bytea.pgc"
+
+ { ECPGdisconnect(__LINE__, "CURRENT");
+#line 106 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 106 "bytea.pgc"
+
+
+ return 0;
+}
diff --git a/src/interfaces/ecpg/test/expected/sql-bytea.stderr b/src/interfaces/ecpg/test/expected/sql-bytea.stderr
new file mode 100644
index 0000000..fd188cc
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-bytea.stderr
@@ -0,0 +1,160 @@
+[NO_PID]: ECPGdebug: set to 1
+[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 60: query: create table if not exists test ( data1 bytea , data2 bytea ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 60: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 60: OK: CREATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: prepare_common on line 62: name ins_stmt; query: "insert into test values($1,$2)"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: prepare_common on line 63: name sel_stmt; query: "select data1,data2 from test"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 69: query: truncate test; 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: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 70: query: insert into test values ( $1 , $2 ); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 70: using PQexecParams
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 70: parameter 1 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 70: parameter 2 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 70: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 71: query: select data1 , data2 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 71: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 71: correctly got 1 tuples with 2 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 71: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 71: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 77: query: truncate test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 77: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 77: OK: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 78: query: insert into test values ( $1 , $2 ); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 78: using PQexecParams
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 78: parameter 1 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 78: parameter 2 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 78: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 79: query: insert into test values ( $1 , $2 ); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 79: using PQexecParams
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 79: parameter 1 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 79: parameter 2 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 79: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 80: query: select data1 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 80: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 80: correctly got 2 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_store_result on line 80: allocating memory for 2 tuples
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 80: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 80: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 86: query: truncate test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 86: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 86: OK: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 87: query: insert into test values($1,$2); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 87: using PQexecPrepared for "insert into test values($1,$2)"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 87: parameter 1 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 87: parameter 2 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 87: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 88: query: select data1,data2 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 88: using PQexecPrepared for "select data1,data2 from test"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 88: correctly got 1 tuples with 2 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 88: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 88: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 94: query: truncate test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 94: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 94: OK: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 97: query: insert into test values($1,$2); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 97: using PQexecPrepared for "insert into test values($1,$2)"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 97: parameter 1 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 97: parameter 2 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 97: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 98: query: select data1,data2 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 98: using PQexecPrepared for "select data1,data2 from test"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 98: correctly got 1 tuples with 2 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 98: putting result (1 tuples) into descriptor odesc
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGget_desc: reading items for tuple 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 99: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGget_desc: reading items for tuple 2
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 100: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 104: query: drop table test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 104: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 104: OK: DROP TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGtrans on line 105: action "commit"; connection "ecpg1_regression"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: deallocate_one on line 0: name sel_stmt
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: deallocate_one on line 0: name ins_stmt
+[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/sql-bytea.stdout b/src/interfaces/ecpg/test/expected/sql-bytea.stdout
new file mode 100644
index 0000000..4c9ad4c
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-bytea.stdout
@@ -0,0 +1,8 @@
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=499, ind=512, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=499, ind=512, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=499, ind=512, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d
diff --git a/src/interfaces/ecpg/test/sql/Makefile b/src/interfaces/ecpg/test/sql/Makefile
index b7bc034..e173ed2 100644
--- a/src/interfaces/ecpg/test/sql/Makefile
+++ b/src/interfaces/ecpg/test/sql/Makefile
@@ -23,7 +23,8 @@ TESTS = array array.c \
quote quote.c \
show show.c \
twophase twophase.c \
- insupd insupd.c
+ insupd insupd.c \
+ bytea bytea.c
all: $(TESTS)
diff --git a/src/interfaces/ecpg/test/sql/bytea.pgc b/src/interfaces/ecpg/test/sql/bytea.pgc
new file mode 100644
index 0000000..be275a2
--- /dev/null
+++ b/src/interfaces/ecpg/test/sql/bytea.pgc
@@ -0,0 +1,109 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+
+exec sql include ../regression;
+exec sql whenever sqlerror sqlprint;
+
+static void
+dump_binary(char *buf, int len, int ind)
+{
+ int i;
+
+ printf("len=%d, ind=%d, data=0x", len, ind);
+ for (i = 0; i < len; ++i)
+ printf("%02x", 0xff & buf[i]);
+ printf("\n");
+}
+
+#define DATA_SIZE 0x200
+#define LACK_SIZE 13
+#
+int
+main(void)
+{
+exec sql begin declare section;
+ bytea send_buf[2][DATA_SIZE];
+ bytea recv_buf[2][DATA_SIZE];
+ bytea recv_vlen_buf[][DATA_SIZE];
+ bytea recv_short_buf[DATA_SIZE - LACK_SIZE];
+ int ind[2];
+exec sql end declare section;
+ int i, j, c;
+
+#define init() { \
+ for (i = 0; i < 2; ++i) \
+ { \
+ memset(recv_buf[i].arr, 0x0, sizeof(recv_buf[i].arr)); \
+ recv_buf[i].len = 0; \
+ ind[i] = 0; \
+ } \
+ recv_vlen_buf = NULL, \
+ memset(recv_short_buf.arr, 0x0, sizeof(recv_short_buf.arr)); \
+} \
+while (0)
+
+
+ ECPGdebug(1, stderr);
+
+ for (i = 0; i < 2; ++i)
+ {
+ for (j = 0, c = 0xff; (c == -1 ? c = 0xff : 1), j < DATA_SIZE; ++j, --c)
+ send_buf[i].arr[j] = c;
+
+ send_buf[i].len = DATA_SIZE;
+ }
+
+ exec sql connect to REGRESSDB1;
+
+ exec sql create table if not exists test (data1 bytea, data2 bytea);
+
+ exec sql prepare ins_stmt from "insert into test values(?,?)";
+ exec sql prepare sel_stmt from "select data1,data2 from test";
+ exec sql allocate descriptor idesc;
+ exec sql allocate descriptor odesc;
+
+ /* Test for static sql statement with normal host variable, indicator */
+ init();
+ exec sql truncate test;
+ exec sql insert into test values(:send_buf[0], :send_buf[1]);
+ exec sql select data1,data2 into :recv_buf[0]:ind[0], :recv_short_buf:ind[1] from test;
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for variable length array */
+ init();
+ exec sql truncate test;
+ exec sql insert into test values(:send_buf[0], :send_buf[1]);
+ exec sql insert into test values(:send_buf[0], :send_buf[1]);
+ exec sql select data1 into :recv_vlen_buf from test;
+ dump_binary(recv_vlen_buf[0].arr, recv_vlen_buf[0].len, 0);
+ dump_binary(recv_vlen_buf[1].arr, recv_vlen_buf[1].len, 0);
+
+ /* Test for dynamic sql statement with normal host variable, indicator */
+ init();
+ exec sql truncate test;
+ exec sql execute ins_stmt using :send_buf[0], :send_buf[1];
+ exec sql execute sel_stmt into :recv_buf[0]:ind[0], :recv_short_buf:ind[1];
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for dynamic sql statement with sql descriptor */
+ init();
+ exec sql truncate test;
+ exec sql set descriptor idesc value 1 data = :send_buf[0];
+ exec sql set descriptor idesc value 2 data = :send_buf[1];
+ exec sql execute ins_stmt using sql descriptor idesc;
+ exec sql execute sel_stmt into sql descriptor odesc;
+ exec sql get descriptor odesc value 1 :recv_buf[0] = data, :ind[0] = indicator;
+ exec sql get descriptor odesc value 2 :recv_short_buf = data, :ind[1] = indicator;
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ exec sql drop table test;
+ exec sql commit;
+ exec sql disconnect;
+
+ return 0;
+}
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 9fe950b..dd6b69a 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -330,7 +330,6 @@ ClosePortalStmt
ClosePtrType
Clump
ClusterInfo
-ClusterOption
ClusterStmt
CmdType
CoalesceExpr
@@ -402,6 +401,7 @@ ConversionLocation
ConvertRowtypeExpr
CookedConstraint
CopyDest
+CopyInsertMethod
CopyState
CopyStateData
CopyStmt
@@ -1079,6 +1079,7 @@ JOBOBJECT_BASIC_LIMIT_INFORMATION
JOBOBJECT_BASIC_UI_RESTRICTIONS
JOBOBJECT_SECURITY_LIMIT_INFORMATION
JitContext
+JitInstrumentation
JitProviderCallbacks
JitProviderCompileExprCB
JitProviderInit
@@ -1174,6 +1175,7 @@ LWLockMinimallyPadded
LWLockMode
LWLockPadded
LabelProvider
+LagTracker
LargeObjectDesc
LastAttnumInfo
Latch
@@ -1184,6 +1186,7 @@ LexemeHashKey
LexemeInfo
LexemeKey
LexizeData
+LibraryInfo
Limit
LimitPath
LimitState
@@ -1403,8 +1406,11 @@ PATH
PBOOL
PCtxtHandle
PFN
+PGAlignedBlock
+PGAlignedXLogBlock
PGAsyncStatusType
PGCALL2
+PGChecksummablePage
PGContextVisibility
PGEvent
PGEventConnDestroy
@@ -1611,6 +1617,7 @@ ParallelHashJoinState
ParallelHeapScanDesc
ParallelHeapScanDescData
ParallelIndexScanDesc
+ParallelReadyList
ParallelSlot
ParallelState
ParallelWorkerContext
@@ -1664,6 +1671,8 @@ PartitionRangeDatumKind
PartitionScheme
PartitionSpec
PartitionTupleRouting
+PartitionedRelPruneInfo
+PartitionedRelPruningData
PartitionwiseAggregateType
PasswordType
Path
@@ -1781,6 +1790,7 @@ PredXactList
PredXactListElement
PredicateLockData
PredicateLockTargetType
+PrepParallelRestorePtrType
PrepareStmt
PreparedParamsData
PreparedStatement
@@ -1931,7 +1941,6 @@ Relation
RelationData
RelationPtr
RelationSyncEntry
-RelativeTime
RelcacheCallbackFunction
RelfilenodeMapEntry
RelfilenodeMapKey
@@ -2070,7 +2079,6 @@ ScanDirection
ScanKey
ScanKeyData
ScanKeyword
-ScanStackEntry
ScanState
ScanTypeControl
SchemaQuery
@@ -2081,12 +2089,14 @@ SecLabelStmt
SeenRelsEntry
SelectStmt
Selectivity
+SemTPadded
SemiAntiJoinFactors
SeqScan
SeqScanState
SeqTable
SeqTableData
SerCommitSeqNo
+SerializedActiveRelMaps
SerializedReindexState
SerializedSnapshotData
Session
@@ -2119,6 +2129,7 @@ SharedInvalRelmapMsg
SharedInvalSmgrMsg
SharedInvalSnapshotMsg
SharedInvalidationMessage
+SharedJitInstrumentation
SharedRecordTableEntry
SharedRecordTableKey
SharedRecordTypmodRegistry
@@ -2201,6 +2212,7 @@ SpGistPageOpaque
SpGistPageOpaqueData
SpGistScanOpaque
SpGistScanOpaqueData
+SpGistSearchItem
SpGistState
SpGistTypeDesc
SpecialJoinInfo
@@ -2300,7 +2312,6 @@ TSVectorParseState
TSVectorStat
TState
TStoreState
-TTOffList
TYPCATEGORY
T_Action
T_WorkerStatus
@@ -2344,8 +2355,6 @@ TidPath
TidScan
TidScanState
TimeADT
-TimeInterval
-TimeIntervalData
TimeLineHistoryCmd
TimeLineHistoryEntry
TimeLineID
@@ -2535,6 +2544,7 @@ WindowAgg
WindowAggPath
WindowAggState
WindowClause
+WindowClauseSortData
WindowDef
WindowFunc
WindowFuncExprState
@@ -3113,6 +3123,7 @@ slist_mutable_iter
slist_node
slock_t
smgrid
+socket_set
spgBulkDeleteState
spgChooseIn
spgChooseOut
Hi Matsumoro-san,
thanks for your effort and apologies for the late reply.
I think that set/put data for host variable should be more simple.
The following is an example of Oracle Pro *C program for RAW type
column.
Just to be clear, Oracle can use varchars for binary data, right?
In ECPG, varchar host variable cannot be used for bytea because it
cannot treat
'\0' as part of data. If the length is set to 10 and there is '\0' at
3rd byte,
ecpglib truncates 3rd byte and later at the following:
I've been traveling too much to check, but does the standard say
anything about that?
I also think that the behavior of varchar host variable should not be
changed
because of compatibility.
Therefore, a new type of host variable 'bytea' is needed.
This I am actually not sure about. I think we should try to stick with
the standard and, if it does not comment on it, with what others in the
market do to make migrations easier. So far I do not remember any
database having a bytea datatype in embedded SQL.
Comments anyone?
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Hi Michael
Thank you for your comment.
# Please call me Ryo. Matsumura is too long.
I think that set/put data for host variable should be more simple.
The following is an example of Oracle Pro *C program for RAW type
column.Just to be clear, Oracle can use varchars for binary data, right?
I'm sorry. That is my mistake.
In Pro*C, the data should be represented as hex format C string.
In ECPG, varchar host variable cannot be used for bytea because it
cannot treat
'\0' as part of data. If the length is set to 10 and there is '\0' at
3rd byte,
ecpglib truncates 3rd byte and later at the following:I've been traveling too much to check, but does the standard say
anything about that?
bytea as a type of table definition may correspond to BLOB in the standard.
If it is true, the standard defines corresponding type in C as the following:
------
struct {
long hvn_reserved
unsigned long hvn_length
char hvn_data[L];
} hvn
* hvn is the name of the host variable defined to correspond
to the SQL data type
------
I also think that the behavior of varchar host variable should not be
changed
because of compatibility.
Therefore, a new type of host variable 'bytea' is needed.
This I am actually not sure about. I think we should try to stick with
the standard and, if it does not comment on it, with what others in the
market do to make migrations easier. So far I do not remember any
database having a bytea datatype in embedded SQL.
Maybe Oracle doesn't have it.
I found documents of DB2.
blob(n) correspond to BLOB in V11.
https://www.ibm.com/support/knowledgecenter/en/SSEPGG_11.1.0/com.ibm.db2.luw.apdv.embed.doc/doc/r0006090.html
Since I don't have DB2 installation, I cannot confirm typedef of blob(n).
But in V9 the following structure correspond to BLOB.
https://www.ibm.com/support/knowledgecenter/en/SSEPGG_9.7.0/com.ibm.db2.luw.apdv.routines.doc/doc/c0009190.html
struct sqludf_lob
{
sqluint32 length; /* length in bytes */
char data[1]; /* first byte of lob */
};
It seems that there is no defact and no product following to the standards.
I wonder whether bytea should follow to the standard completely or
follow to existing varchar for usability.
Thank you.
Regards
Ryo Matsumura
Hi Ryo-san,
# Please call me Ryo. Matsumura is too long.
Thanks.
In Pro*C, the data should be represented as hex format C string.
Just to clarify, there is no special datatype for binary data?
bytea as a type of table definition may correspond to BLOB in the
standard.
Would we prefer to add a blob datatype then?
It seems that there is no defact and no product following to the
standards.
I wonder whether bytea should follow to the standard completely or
follow to existing varchar for usability.
Do you see any disadvantage of following the standard? I don't really
see where the usability drawback is. In general I would prefer being as
close to the standard as reasonably possible.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
From: Michael Meskes [mailto:meskes@postgresql.org]
bytea as a type of table definition may correspond to BLOB in the
standard.Would we prefer to add a blob datatype then?
It seems that there is no defact and no product following to the
standards.
I wonder whether bytea should follow to the standard completely or
follow to existing varchar for usability.Do you see any disadvantage of following the standard? I don't really
see where the usability drawback is. In general I would prefer being as
close to the standard as reasonably possible.
I think the host variable data type that corresponds to the server-side bytea should be bytea. As the following pages state or imply, it would be better to create standard-compliant LOB types someday, and use the keyword BLOB in ECPG for that type. The server-side data types should have the names BLOB, CLOB and NCLOB. Those types should handle data larget than 1 GB and have the locator feature defined in the SQL standard. Maybe we should also advanced LOB features like Oracle's SecureFiles LOB and SQL Server's FileTables.
https://www.postgresql.org/docs/current/static/datatype-binary.html
"The SQL standard defines a different binary string type, called BLOB or BINARY LARGE OBJECT. The input format is different from bytea, but the provided functions and operators are mostly the same."
BinaryFilesInDB
https://wiki.postgresql.org/wiki/BinaryFilesInDB
Regards
Takayuki Tsunakawa
Hi Michael
In Pro*C, the data should be represented as hex format C string.
Just to clarify, there is no special datatype for binary data?
I apology for lack of research again.
Since it's a little difficult to answer, I explain by samples.
The following works.
unsigned char buffer[128]; /* It's not needed to declare in DECLARE section. */
exec sql var buffer is raw(128); /* This sematics may be different in ECPG. */
exec sql create table test(c1 raw(128));
exec sql insert into test(c1) values(:buffer);
The following cannot be pre-compiled.
In this sence, there is no special datatype for binary data.
exec sql begin declare section;
raw buffer[128]; /* error */
exec sql end declare section;
exec sql create table test(c1 raw(128));
exec sql insert into test(c1) values(:buffer);
bytea as a type of table definition may correspond to BLOB in the
standard.Would we prefer to add a blob datatype then?
I think that blob datatype is needed for large binary data *in finally*,
but blob datatype and its access methods(*) is not needed for non-large
binary data(e.g. use for text data of which encoding is different from
database encoding) because of its complexity.
My proposal is mainly for non-large binary data.
(*) e.g. In Pro*C, OPEN, READ, WRITE, CLOSE, APPEND, and so on.
It seems that there is no defact and no product following to the
standards.
I wonder whether bytea should follow to the standard completely or
follow to existing varchar for usability.Do you see any disadvantage of following the standard? I don't really
see where the usability drawback is. In general I would prefer being as
close to the standard as reasonably possible.
I think there is no special reason to follow to existing varchar.
I can accept the standard. (Re-implementation is not difficult.)
Thank you.
Regards
Ryo Matsumura
Show quoted text
-----Original Message-----
From: Michael Meskes [mailto:meskes@postgresql.org]
Sent: Saturday, October 27, 2018 3:43 AM
To: Matsumura, Ryo/松村 量 <matsumura.ryo@jp.fujitsu.com>;
pgsql-hackers@lists.postgresql.org
Subject: Re: [PROPOSAL]a new data type 'bytea' for ECPGHi Ryo-san,
# Please call me Ryo. Matsumura is too long.
Thanks.
In Pro*C, the data should be represented as hex format C string.
Just to clarify, there is no special datatype for binary data?
bytea as a type of table definition may correspond to BLOB in the
standard.Would we prefer to add a blob datatype then?
It seems that there is no defact and no product following to the
standards.
I wonder whether bytea should follow to the standard completely or
follow to existing varchar for usability.Do you see any disadvantage of following the standard? I don't really
see where the usability drawback is. In general I would prefer being as
close to the standard as reasonably possible.Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
From: Tsunakawa, Takayuki [mailto:tsunakawa.takay@jp.fujitsu.com]
I think the host variable data type that corresponds to the server-side bytea
should be bytea. As the following pages state or imply, it would be better
to create standard-compliant LOB types someday, and use the keyword BLOB in
ECPG for that type. The server-side data types should have the names BLOB,
CLOB and NCLOB. Those types should handle data larget than 1 GB and have the
locator feature defined in the SQL standard. Maybe we should also advanced
LOB features like Oracle's SecureFiles LOB and SQL Server's FileTables.
Tsunakawa-san, thanks for your advice.
I understand that C type definition of client-side bytea is not constrained by the standard BLOB.
What should I do next?
For now, I attach a patch that is removed noise(pgindent/typedef.list).
P.S.
The patch does not support ECPG.bytea in sqltype of "struct sqlvar_struct" because of compatibility.
Regards
Ryo Matsumura
Attachments:
ecpg_bytea_v1_1.patchapplication/octet-stream; name=ecpg_bytea_v1_1.patchDownload
diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c
index f3d326a..bc01ca9 100644
--- a/src/interfaces/ecpg/ecpglib/data.c
+++ b/src/interfaces/ecpg/ecpglib/data.c
@@ -122,6 +122,71 @@ check_special_value(char *ptr, double *retval, char **endptr)
return false;
}
+/* imported from src/backend/utils/adt/encode.c */
+
+static unsigned
+hex_enc_len(unsigned srclen)
+{
+ return srclen << 1;
+}
+extern unsigned
+ecpg_hex_dec_len(unsigned srclen)
+{
+ return srclen >> 1;
+}
+
+static inline char
+get_hex(char c)
+{
+ static const int8 hexlookup[128] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+ int res = -1;
+
+ if (c > 0 && c < 127)
+ res = hexlookup[(unsigned char) c];
+
+ return (char) res;
+}
+
+static unsigned
+hex_decode(const char *src, unsigned len, char *dst)
+{
+ const char *s,
+ *srcend;
+ char v1,
+ v2,
+ *p;
+
+ srcend = src + len;
+ s = src;
+ p = dst;
+ while (s < srcend)
+ {
+ if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
+ {
+ s++;
+ continue;
+ }
+ v1 = get_hex(*s++) << 4;
+ if (s >= srcend)
+ return -1;
+
+ v2 = get_hex(*s++);
+ *p++ = v1 | v2;
+ }
+
+ return p - dst;
+}
+
+
bool
ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
enum ECPGttype type, enum ECPGttype ind_type,
@@ -447,6 +512,55 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
return false;
break;
+ case ECPGt_bytea:
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var + offset * act_tuple);
+ long dst_size,
+ src_size,
+ dec_size;
+
+ dst_size = hex_enc_len(varcharsize);
+ src_size = size - 2; /* exclude backslash + 'x' */
+ dec_size = src_size < dst_size ? src_size : dst_size;
+ variable->len = hex_decode(pval + 2, dec_size, variable->arr);
+
+ if (dst_size < src_size)
+ {
+ long rcv_size = ecpg_hex_dec_len(size - 2);
+
+ /* truncation */
+ switch (ind_type)
+ {
+ case ECPGt_short:
+ case ECPGt_unsigned_short:
+ *((short *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+ case ECPGt_int:
+ case ECPGt_unsigned_int:
+ *((int *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+ case ECPGt_long:
+ case ECPGt_unsigned_long:
+ *((long *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+#ifdef HAVE_LONG_LONG_INT
+ case ECPGt_long_long:
+ case ECPGt_unsigned_long_long:
+ *((long long int *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+#endif /* HAVE_LONG_LONG_INT */
+ default:
+ break;
+ }
+ sqlca->sqlwarn[0] = sqlca->sqlwarn[1] = 'W';
+ }
+
+ pval += size;
+
+ }
+ break;
+
case ECPGt_char:
case ECPGt_unsigned_char:
case ECPGt_string:
diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c
index 8fdf560..441b4e6 100644
--- a/src/interfaces/ecpg/ecpglib/descriptor.c
+++ b/src/interfaces/ecpg/ecpglib/descriptor.c
@@ -636,11 +636,29 @@ ECPGset_desc(int lineno, const char *desc_name, int index,...)
{
case ECPGd_data:
{
- if (!ecpg_store_input(lineno, true, var, &tobeinserted, false))
+ if (var->type != ECPGt_bytea)
{
- ecpg_free(var);
- va_end(args);
- return false;
+ if (!ecpg_store_input(lineno, true, var, &tobeinserted, false))
+ {
+ ecpg_free(var);
+ va_end(args);
+ return false;
+ }
+ desc_item->is_binary = false;
+ }
+ else
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var->value);
+
+ if (!(tobeinserted = ecpg_alloc(sizeof(int) + variable->len, lineno)))
+ {
+ ecpg_free(var);
+ va_end(args);
+ return false;
+ }
+ memcpy(tobeinserted, var->value, sizeof(int) + variable->len);
+ desc_item->is_binary = true;
}
ecpg_free(desc_item->data); /* free() takes care of a
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index 42640ba..942bc11 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -799,6 +799,20 @@ ecpg_store_input(const int lineno, const bool force_indicator, const struct vari
*tobeinserted_p = mallocedval;
}
break;
+
+ case ECPGt_bytea:
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var->value);
+
+ if (!(mallocedval = (char *) ecpg_alloc(variable->len, lineno)))
+ return false;
+
+ memcpy(mallocedval, variable->arr, variable->len);
+ *tobeinserted_p = mallocedval;
+ }
+ break;
+
case ECPGt_varchar:
{
struct ECPGgeneric_varchar *variable =
@@ -1053,7 +1067,11 @@ ecpg_free_params(struct statement *stmt, bool print)
ecpg_free(stmt->paramvalues[n]);
}
ecpg_free(stmt->paramvalues);
+ ecpg_free(stmt->paramlengths);
+ ecpg_free(stmt->paramformats);
stmt->paramvalues = NULL;
+ stmt->paramlengths = NULL;
+ stmt->paramformats = NULL;
stmt->nparams = 0;
}
@@ -1120,8 +1138,13 @@ ecpg_build_params(struct statement *stmt)
{
char *tobeinserted;
int counter = 1;
+ bool binary_format;
+ int binary_length;
+
tobeinserted = NULL;
+ binary_length = 0;
+ binary_format = false;
/*
* A descriptor is a special case since it contains many variables but
@@ -1146,12 +1169,27 @@ ecpg_build_params(struct statement *stmt)
{
if (desc_item->num == desc_counter)
{
- desc_inlist.type = ECPGt_char;
+ if (!desc_item->is_binary)
+ {
+ desc_inlist.type = ECPGt_char;
+ desc_inlist.varcharsize = strlen(desc_item->data);
+ }
+ else
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (desc_item->data);
+
+ binary_length = variable->len;
+ binary_format = true;
+
+ desc_inlist.type = ECPGt_bytea;
+ desc_inlist.varcharsize = binary_length;
+ }
desc_inlist.value = desc_item->data;
desc_inlist.pointer = &(desc_item->data);
- desc_inlist.varcharsize = strlen(desc_item->data);
desc_inlist.arrsize = 1;
desc_inlist.offset = 0;
+
if (!desc_item->indicator)
{
desc_inlist.ind_type = ECPGt_NO_INDICATOR;
@@ -1293,6 +1331,15 @@ ecpg_build_params(struct statement *stmt)
{
if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, var, &tobeinserted, false))
return false;
+
+ if (var->type == ECPGt_bytea)
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var->value);
+
+ binary_length = variable->len;
+ binary_format = true;
+ }
}
/*
@@ -1346,16 +1393,32 @@ ecpg_build_params(struct statement *stmt)
else
{
char **paramvalues;
+ int *paramlengths;
+ int *paramformats;
if (!(paramvalues = (char **) ecpg_realloc(stmt->paramvalues, sizeof(char *) * (stmt->nparams + 1), stmt->lineno)))
{
ecpg_free_params(stmt, false);
return false;
}
+ if (!(paramlengths = (int *) ecpg_realloc(stmt->paramlengths, sizeof(int) * (stmt->nparams + 1), stmt->lineno)))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+ if (!(paramformats = (int *) ecpg_realloc(stmt->paramformats, sizeof(int) * (stmt->nparams + 1), stmt->lineno)))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
stmt->nparams++;
stmt->paramvalues = paramvalues;
+ stmt->paramlengths = paramlengths;
+ stmt->paramformats = paramformats;
stmt->paramvalues[stmt->nparams - 1] = tobeinserted;
+ stmt->paramlengths[stmt->nparams - 1] = binary_length;
+ stmt->paramformats[stmt->nparams - 1] = (binary_format ? 1 : 0);
/* let's see if this was an old style placeholder */
if (stmt->command[position] == '?')
@@ -1428,7 +1491,13 @@ ecpg_execute(struct statement *stmt)
ecpg_log("ecpg_execute on line %d: query: %s; with %d parameter(s) on connection %s\n", stmt->lineno, stmt->command, stmt->nparams, stmt->connection->name);
if (stmt->statement_type == ECPGst_execute)
{
- stmt->results = PQexecPrepared(stmt->connection->connection, stmt->name, stmt->nparams, (const char *const *) stmt->paramvalues, NULL, NULL, 0);
+ stmt->results = PQexecPrepared(stmt->connection->connection,
+ stmt->name,
+ stmt->nparams,
+ (const char *const *) stmt->paramvalues,
+ (const int *) stmt->paramlengths,
+ (const int *) stmt->paramformats,
+ 0);
ecpg_log("ecpg_execute on line %d: using PQexecPrepared for \"%s\"\n", stmt->lineno, stmt->command);
}
else
@@ -1440,7 +1509,12 @@ ecpg_execute(struct statement *stmt)
}
else
{
- stmt->results = PQexecParams(stmt->connection->connection, stmt->command, stmt->nparams, NULL, (const char *const *) stmt->paramvalues, NULL, NULL, 0);
+ stmt->results = PQexecParams(stmt->connection->connection,
+ stmt->command, stmt->nparams, NULL,
+ (const char *const *) stmt->paramvalues,
+ (const int *) stmt->paramlengths,
+ (const int *) stmt->paramformats,
+ 0);
ecpg_log("ecpg_execute on line %d: using PQexecParams\n", stmt->lineno);
}
}
diff --git a/src/interfaces/ecpg/ecpglib/extern.h b/src/interfaces/ecpg/ecpglib/extern.h
index a88f341..79e451d 100644
--- a/src/interfaces/ecpg/ecpglib/extern.h
+++ b/src/interfaces/ecpg/ecpglib/extern.h
@@ -37,6 +37,13 @@ struct ECPGgeneric_varchar
char arr[FLEXIBLE_ARRAY_MEMBER];
};
+/* A generic bytea type. */
+struct ECPGgeneric_bytea
+{
+ int len;
+ char arr[FLEXIBLE_ARRAY_MEMBER];
+};
+
/*
* type information cache
*/
@@ -64,6 +71,8 @@ struct statement
char *oldlocale;
int nparams;
char **paramvalues;
+ int *paramlengths;
+ int *paramformats;
PGresult *results;
};
@@ -106,6 +115,8 @@ struct descriptor_item
int precision;
int scale;
int type;
+ bool is_binary;
+ int data_len;
struct descriptor_item *next;
};
@@ -194,6 +205,7 @@ struct sqlda_compat *ecpg_build_compat_sqlda(int, PGresult *, int, enum COMPAT_M
void ecpg_set_compat_sqlda(int, struct sqlda_compat **, const PGresult *, int, enum COMPAT_MODE);
struct sqlda_struct *ecpg_build_native_sqlda(int, PGresult *, int, enum COMPAT_MODE);
void ecpg_set_native_sqlda(int, struct sqlda_struct **, const PGresult *, int, enum COMPAT_MODE);
+unsigned ecpg_hex_dec_len(unsigned srclen);
/* SQLSTATE values generated or processed by ecpglib (intentionally
* not exported -- users should refer to the codes directly) */
diff --git a/src/interfaces/ecpg/ecpglib/misc.c b/src/interfaces/ecpg/ecpglib/misc.c
index be9cac6..e5432a2 100644
--- a/src/interfaces/ecpg/ecpglib/misc.c
+++ b/src/interfaces/ecpg/ecpglib/misc.c
@@ -355,6 +355,9 @@ ECPGset_noind_null(enum ECPGttype type, void *ptr)
*(((struct ECPGgeneric_varchar *) ptr)->arr) = 0x00;
((struct ECPGgeneric_varchar *) ptr)->len = 0;
break;
+ case ECPGt_bytea:
+ ((struct ECPGgeneric_bytea *) ptr)->len = 0;
+ break;
case ECPGt_decimal:
memset((char *) ptr, 0, sizeof(decimal));
((decimal *) ptr)->sign = NUMERIC_NULL;
@@ -428,6 +431,10 @@ ECPGis_noind_null(enum ECPGttype type, const void *ptr)
if (*(((const struct ECPGgeneric_varchar *) ptr)->arr) == 0x00)
return true;
break;
+ case ECPGt_bytea:
+ if (((struct ECPGgeneric_bytea *) ptr)->len == 0)
+ return true;
+ break;
case ECPGt_decimal:
if (((const decimal *) ptr)->sign == NUMERIC_NULL)
return true;
diff --git a/src/interfaces/ecpg/ecpglib/typename.c b/src/interfaces/ecpg/ecpglib/typename.c
index 9da1cdf..572bc78 100644
--- a/src/interfaces/ecpg/ecpglib/typename.c
+++ b/src/interfaces/ecpg/ecpglib/typename.c
@@ -48,6 +48,8 @@ ecpg_type_name(enum ECPGttype typ)
return "bool";
case ECPGt_varchar:
return "varchar";
+ case ECPGt_bytea:
+ return "bytea";
case ECPGt_char_variable:
return "char";
case ECPGt_decimal:
diff --git a/src/interfaces/ecpg/include/ecpgtype.h b/src/interfaces/ecpg/include/ecpgtype.h
index 38fb3b6..b9fc4ea 100644
--- a/src/interfaces/ecpg/include/ecpgtype.h
+++ b/src/interfaces/ecpg/include/ecpgtype.h
@@ -63,7 +63,8 @@ enum ECPGttype
ECPGt_EORT, /* End of result types. */
ECPGt_NO_INDICATOR, /* no indicator */
ECPGt_string, /* trimmed (char *) type */
- ECPGt_sqlda /* C struct descriptor */
+ ECPGt_sqlda, /* C struct descriptor */
+ ECPGt_bytea
};
/* descriptor items */
@@ -88,7 +89,7 @@ enum ECPGdtype
ECPGd_cardinality
};
-#define IS_SIMPLE_TYPE(type) (((type) >= ECPGt_char && (type) <= ECPGt_interval) || ((type) == ECPGt_string))
+#define IS_SIMPLE_TYPE(type) (((type) >= ECPGt_char && (type) <= ECPGt_interval) || ((type) == ECPGt_string) || ((type) == ECPGt_bytea))
/* we also have to handle different statement types */
enum ECPG_statement_type
diff --git a/src/interfaces/ecpg/preproc/ecpg.header b/src/interfaces/ecpg/preproc/ecpg.header
index 8921bcb..4669dff 100644
--- a/src/interfaces/ecpg/preproc/ecpg.header
+++ b/src/interfaces/ecpg/preproc/ecpg.header
@@ -46,6 +46,7 @@ static char pacounter_buffer[sizeof(int) * CHAR_BIT * 10 / 3]; /* a rough guess
static struct this_type actual_type[STRUCT_DEPTH];
static char *actual_startline[STRUCT_DEPTH];
static int varchar_counter = 1;
+static int bytea_counter = 1;
/* temporarily store struct members while creating the data structure */
struct ECPGstruct_member *struct_member_list[STRUCT_DEPTH] = { NULL };
@@ -562,6 +563,7 @@ add_typedef(char *name, char *dimension, char *length, enum ECPGttype type_enum,
ECPGstruct_member_dup(struct_member_list[struct_level]) : NULL;
if (type_enum != ECPGt_varchar &&
+ type_enum != ECPGt_bytea &&
type_enum != ECPGt_char &&
type_enum != ECPGt_unsigned_char &&
type_enum != ECPGt_string &&
diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer
index 19dc781..c44fcf9 100644
--- a/src/interfaces/ecpg/preproc/ecpg.trailer
+++ b/src/interfaces/ecpg/preproc/ecpg.trailer
@@ -550,6 +550,14 @@ var_type: simple_type
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
+ else if (strcmp($1, "bytea") == 0)
+ {
+ $$.type_enum = ECPGt_bytea;
+ $$.type_str = EMPTY;
+ $$.type_dimension = mm_strdup("-1");
+ $$.type_index = mm_strdup("-1");
+ $$.type_sizeof = NULL;
+ }
else if (strcmp($1, "float") == 0)
{
$$.type_enum = ECPGt_float;
@@ -627,7 +635,7 @@ var_type: simple_type
/* this is for typedef'ed types */
struct typedefs *this = get_typedef($1);
- $$.type_str = (this->type->type_enum == ECPGt_varchar) ? EMPTY : mm_strdup(this->name);
+ $$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? EMPTY : mm_strdup(this->name);
$$.type_enum = this->type->type_enum;
$$.type_dimension = this->type->type_dimension;
$$.type_index = this->type->type_index;
@@ -838,7 +846,7 @@ variable_list: variable
{ $$ = $1; }
| variable_list ',' variable
{
- if (actual_type[struct_level].type_enum == ECPGt_varchar)
+ if (actual_type[struct_level].type_enum == ECPGt_varchar || actual_type[struct_level].type_enum == ECPGt_bytea)
$$ = cat_str(3, $1, mm_strdup(";"), $3);
else
$$ = cat_str(3, $1, mm_strdup(","), $3);
@@ -852,9 +860,10 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
char *length = $3.index2; /* length of string */
char *dim_str;
char *vcn;
+ int *varlen_type_counter;
+ char *struct_name;
adjust_array(actual_type[struct_level].type_enum, &dimension, &length, actual_type[struct_level].type_dimension, actual_type[struct_level].type_index, strlen($1), false);
-
switch (actual_type[struct_level].type_enum)
{
case ECPGt_struct:
@@ -868,10 +877,21 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
break;
case ECPGt_varchar:
+ case ECPGt_bytea:
+ if (actual_type[struct_level].type_enum == ECPGt_varchar)
+ {
+ varlen_type_counter = &varchar_counter;
+ struct_name = " struct varchar_";
+ }
+ else
+ {
+ varlen_type_counter = &bytea_counter;
+ struct_name = " struct bytea_";
+ }
if (atoi(dimension) < 0)
- type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length, varchar_counter);
+ type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length, *varlen_type_counter);
else
- type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length, varchar_counter), dimension);
+ type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length, *varlen_type_counter), dimension);
if (strcmp(dimension, "0") == 0 || abs(atoi(dimension)) == 1)
dim_str=mm_strdup("");
@@ -883,12 +903,12 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
/* make sure varchar struct name is unique by adding a unique counter to its definition */
vcn = (char *) mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
- sprintf(vcn, "%d", varchar_counter);
+ sprintf(vcn, "%d", *varlen_type_counter);
if (strcmp(dimension, "0") == 0)
- $$ = cat_str(7, make2_str(mm_strdup(" struct varchar_"), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } *"), mm_strdup($2), $4, $5);
+ $$ = cat_str(7, make2_str(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } *"), mm_strdup($2), $4, $5);
else
- $$ = cat_str(8, make2_str(mm_strdup(" struct varchar_"), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } "), mm_strdup($2), dim_str, $4, $5);
- varchar_counter++;
+ $$ = cat_str(8, make2_str(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } "), mm_strdup($2), dim_str, $4, $5);
+ (*varlen_type_counter)++;
break;
case ECPGt_char:
@@ -1354,6 +1374,7 @@ ECPGVar: SQL_VAR
break;
case ECPGt_varchar:
+ case ECPGt_bytea:
if (atoi(dimension) == -1)
type = ECPGmake_simple_type($5.type_enum, length, 0);
else
diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c
index 253873d..44c8a2d 100644
--- a/src/interfaces/ecpg/preproc/type.c
+++ b/src/interfaces/ecpg/preproc/type.c
@@ -102,7 +102,7 @@ ECPGmake_simple_type(enum ECPGttype type, char *size, int counter)
ne->size = size;
ne->u.element = NULL;
ne->struct_sizeof = NULL;
- ne->counter = counter; /* only needed for varchar */
+ ne->counter = counter; /* only needed for varchar and bytea */
return ne;
}
@@ -175,6 +175,8 @@ get_type(enum ECPGttype type)
break;
case ECPGt_varchar:
return "ECPGt_varchar";
+ case ECPGt_bytea:
+ return "ECPGt_bytea";
case ECPGt_NO_INDICATOR: /* no indicator */
return "ECPGt_NO_INDICATOR";
break;
@@ -424,6 +426,7 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type,
{
char *variable = (char *) mm_alloc(strlen(name) + ((prefix == NULL) ? 0 : strlen(prefix)) + 4);
char *offset = (char *) mm_alloc(strlen(name) + strlen("sizeof(struct varchar_)") + 1 + strlen(varcharsize) + sizeof(int) * CHAR_BIT * 10 / 3);
+ char *struct_name;
switch (type)
{
@@ -433,6 +436,7 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type,
*/
case ECPGt_varchar:
+ case ECPGt_bytea:
/*
* we have to use the pointer except for arrays with given
@@ -449,10 +453,15 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type,
* If we created a varchar structure automatically, counter is
* greater than 0.
*/
+ if (type == ECPGt_varchar)
+ struct_name = "struct varchar";
+ else
+ struct_name = "struct bytea";
+
if (counter)
- sprintf(offset, "sizeof(struct varchar_%d)", counter);
+ sprintf(offset, "sizeof(%s_%d)", struct_name, counter);
else
- sprintf(offset, "sizeof(struct varchar)");
+ sprintf(offset, "sizeof(%s)", struct_name);
break;
case ECPGt_char:
case ECPGt_unsigned_char:
diff --git a/src/interfaces/ecpg/preproc/variable.c b/src/interfaces/ecpg/preproc/variable.c
index 39bf3b2..0939de5 100644
--- a/src/interfaces/ecpg/preproc/variable.c
+++ b/src/interfaces/ecpg/preproc/variable.c
@@ -560,6 +560,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
break;
case ECPGt_varchar:
+ case ECPGt_bytea:
/* pointer has to get dimension 0 */
if (pointer_len)
*dimension = mm_strdup("0");
diff --git a/src/interfaces/ecpg/test/ecpg_schedule b/src/interfaces/ecpg/test/ecpg_schedule
index 991b8cb..2478d03 100644
--- a/src/interfaces/ecpg/test/ecpg_schedule
+++ b/src/interfaces/ecpg/test/ecpg_schedule
@@ -32,6 +32,7 @@ test: preproc/whenever
test: preproc/whenever_do_continue
test: sql/array
test: sql/binary
+test: sql/bytea
test: sql/code100
test: sql/copystdout
test: sql/define
diff --git a/src/interfaces/ecpg/test/expected/sql-bytea.c b/src/interfaces/ecpg/test/expected/sql-bytea.c
new file mode 100644
index 0000000..bcaccd1
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-bytea.c
@@ -0,0 +1,316 @@
+/* Processed by ecpg (regression mode) */
+/* These include files are added by the preprocessor */
+#include <ecpglib.h>
+#include <ecpgerrno.h>
+#include <sqlca.h>
+/* End of automatic include section */
+#define ECPGdebug(X,Y) ECPGdebug((X)+100,(Y))
+
+#line 1 "bytea.pgc"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+
+
+#line 1 "regression.h"
+
+
+
+
+
+
+#line 6 "bytea.pgc"
+
+/* exec sql whenever sqlerror sqlprint ; */
+#line 7 "bytea.pgc"
+
+
+static void
+dump_binary(char *buf, int len, int ind)
+{
+ int i;
+
+ printf("len=%d, ind=%d, data=0x", len, ind);
+ for (i = 0; i < len; ++i)
+ printf("%02x", 0xff & buf[i]);
+ printf("\n");
+}
+
+#define DATA_SIZE 0x200
+#define LACK_SIZE 13
+#
+int
+main(void)
+{
+/* exec sql begin declare section */
+
+
+
+
+
+
+#line 27 "bytea.pgc"
+ struct bytea_1 { int len; char arr[ DATA_SIZE ]; } send_buf [ 2 ] ;
+
+#line 28 "bytea.pgc"
+ struct bytea_2 { int len; char arr[ DATA_SIZE ]; } recv_buf [ 2 ] ;
+
+#line 29 "bytea.pgc"
+ struct bytea_3 { int len; char arr[ DATA_SIZE ]; } * recv_vlen_buf ;
+
+#line 30 "bytea.pgc"
+ struct bytea_4 { int len; char arr[ DATA_SIZE - LACK_SIZE ]; } recv_short_buf ;
+
+#line 31 "bytea.pgc"
+ int ind [ 2 ] ;
+/* exec sql end declare section */
+#line 32 "bytea.pgc"
+
+ int i, j, c;
+
+#define init() { \
+ for (i = 0; i < 2; ++i) \
+ { \
+ memset(recv_buf[i].arr, 0x0, sizeof(recv_buf[i].arr)); \
+ recv_buf[i].len = 0; \
+ ind[i] = 0; \
+ } \
+ recv_vlen_buf = NULL, \
+ memset(recv_short_buf.arr, 0x0, sizeof(recv_short_buf.arr)); \
+} \
+while (0)
+
+
+ ECPGdebug(1, stderr);
+
+ for (i = 0; i < 2; ++i)
+ {
+ for (j = 0, c = 0xff; (c == -1 ? c = 0xff : 1), j < DATA_SIZE; ++j, --c)
+ send_buf[i].arr[j] = c;
+
+ send_buf[i].len = DATA_SIZE;
+ }
+
+ { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0);
+#line 58 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 58 "bytea.pgc"
+
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table if not exists test ( data1 bytea , data2 bytea )", ECPGt_EOIT, ECPGt_EORT);
+#line 60 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 60 "bytea.pgc"
+
+
+ { ECPGprepare(__LINE__, NULL, 0, "ins_stmt", "insert into test values(?,?)");
+#line 62 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 62 "bytea.pgc"
+
+ { ECPGprepare(__LINE__, NULL, 0, "sel_stmt", "select data1,data2 from test");
+#line 63 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 63 "bytea.pgc"
+
+ ECPGallocate_desc(__LINE__, "idesc");
+#line 64 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();
+#line 64 "bytea.pgc"
+
+ ECPGallocate_desc(__LINE__, "odesc");
+#line 65 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();
+#line 65 "bytea.pgc"
+
+
+ /* Test for static sql statement with normal host variable, indicator */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 69 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 69 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test values ( $1 , $2 )",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 70 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 70 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select data1 , data2 from test", ECPGt_EOIT,
+ ECPGt_bytea,&(recv_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_2),
+ ECPGt_int,&(ind[0]),(long)1,(long)1,sizeof(int),
+ ECPGt_bytea,&(recv_short_buf),(long)DATA_SIZE - LACK_SIZE,(long)1,sizeof(struct bytea_4),
+ ECPGt_int,&(ind[1]),(long)1,(long)1,sizeof(int), ECPGt_EORT);
+#line 71 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 71 "bytea.pgc"
+
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for variable length array */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 77 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 77 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test values ( $1 , $2 )",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 78 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 78 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test values ( $1 , $2 )",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 79 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 79 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select data1 from test", ECPGt_EOIT,
+ ECPGt_bytea,&(recv_vlen_buf),(long)DATA_SIZE,(long)0,sizeof(struct bytea_3),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 80 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 80 "bytea.pgc"
+
+ dump_binary(recv_vlen_buf[0].arr, recv_vlen_buf[0].len, 0);
+ dump_binary(recv_vlen_buf[1].arr, recv_vlen_buf[1].len, 0);
+
+ /* Test for dynamic sql statement with normal host variable, indicator */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 86 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 86 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "ins_stmt",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 87 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 87 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "sel_stmt", ECPGt_EOIT,
+ ECPGt_bytea,&(recv_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_2),
+ ECPGt_int,&(ind[0]),(long)1,(long)1,sizeof(int),
+ ECPGt_bytea,&(recv_short_buf),(long)DATA_SIZE - LACK_SIZE,(long)1,sizeof(struct bytea_4),
+ ECPGt_int,&(ind[1]),(long)1,(long)1,sizeof(int), ECPGt_EORT);
+#line 88 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 88 "bytea.pgc"
+
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for dynamic sql statement with sql descriptor */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 94 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 94 "bytea.pgc"
+
+ { ECPGset_desc(__LINE__, "idesc", 1,ECPGd_data,
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1), ECPGd_EODT);
+
+#line 95 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 95 "bytea.pgc"
+
+ { ECPGset_desc(__LINE__, "idesc", 2,ECPGd_data,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1), ECPGd_EODT);
+
+#line 96 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 96 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "ins_stmt",
+ ECPGt_descriptor, "idesc", 1L, 1L, 1L,
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 97 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 97 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "sel_stmt", ECPGt_EOIT,
+ ECPGt_descriptor, "odesc", 1L, 1L, 1L,
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 98 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 98 "bytea.pgc"
+
+ { ECPGget_desc(__LINE__, "odesc", 1,ECPGd_indicator,
+ ECPGt_int,&(ind[0]),(long)1,(long)1,sizeof(int), ECPGd_data,
+ ECPGt_bytea,&(recv_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_2), ECPGd_EODT);
+
+#line 99 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 99 "bytea.pgc"
+
+ { ECPGget_desc(__LINE__, "odesc", 2,ECPGd_indicator,
+ ECPGt_int,&(ind[1]),(long)1,(long)1,sizeof(int), ECPGd_data,
+ ECPGt_bytea,&(recv_short_buf),(long)DATA_SIZE - LACK_SIZE,(long)1,sizeof(struct bytea_4), ECPGd_EODT);
+
+#line 100 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 100 "bytea.pgc"
+
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table test", ECPGt_EOIT, ECPGt_EORT);
+#line 104 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 104 "bytea.pgc"
+
+ { ECPGtrans(__LINE__, NULL, "commit");
+#line 105 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 105 "bytea.pgc"
+
+ { ECPGdisconnect(__LINE__, "CURRENT");
+#line 106 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 106 "bytea.pgc"
+
+
+ return 0;
+}
diff --git a/src/interfaces/ecpg/test/expected/sql-bytea.stderr b/src/interfaces/ecpg/test/expected/sql-bytea.stderr
new file mode 100644
index 0000000..fd188cc
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-bytea.stderr
@@ -0,0 +1,160 @@
+[NO_PID]: ECPGdebug: set to 1
+[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 60: query: create table if not exists test ( data1 bytea , data2 bytea ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 60: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 60: OK: CREATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: prepare_common on line 62: name ins_stmt; query: "insert into test values($1,$2)"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: prepare_common on line 63: name sel_stmt; query: "select data1,data2 from test"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 69: query: truncate test; 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: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 70: query: insert into test values ( $1 , $2 ); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 70: using PQexecParams
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 70: parameter 1 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 70: parameter 2 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 70: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 71: query: select data1 , data2 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 71: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 71: correctly got 1 tuples with 2 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 71: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 71: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 77: query: truncate test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 77: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 77: OK: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 78: query: insert into test values ( $1 , $2 ); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 78: using PQexecParams
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 78: parameter 1 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 78: parameter 2 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 78: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 79: query: insert into test values ( $1 , $2 ); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 79: using PQexecParams
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 79: parameter 1 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 79: parameter 2 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 79: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 80: query: select data1 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 80: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 80: correctly got 2 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_store_result on line 80: allocating memory for 2 tuples
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 80: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 80: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 86: query: truncate test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 86: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 86: OK: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 87: query: insert into test values($1,$2); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 87: using PQexecPrepared for "insert into test values($1,$2)"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 87: parameter 1 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 87: parameter 2 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 87: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 88: query: select data1,data2 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 88: using PQexecPrepared for "select data1,data2 from test"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 88: correctly got 1 tuples with 2 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 88: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 88: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 94: query: truncate test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 94: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 94: OK: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 97: query: insert into test values($1,$2); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 97: using PQexecPrepared for "insert into test values($1,$2)"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 97: parameter 1 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 97: parameter 2 = ��������������������������������������������������������������������������������������������������������������������������������~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!
+
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 97: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 98: query: select data1,data2 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 98: using PQexecPrepared for "select data1,data2 from test"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 98: correctly got 1 tuples with 2 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 98: putting result (1 tuples) into descriptor odesc
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGget_desc: reading items for tuple 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 99: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGget_desc: reading items for tuple 2
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 100: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 104: query: drop table test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 104: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 104: OK: DROP TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGtrans on line 105: action "commit"; connection "ecpg1_regression"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: deallocate_one on line 0: name sel_stmt
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: deallocate_one on line 0: name ins_stmt
+[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/sql-bytea.stdout b/src/interfaces/ecpg/test/expected/sql-bytea.stdout
new file mode 100644
index 0000000..4c9ad4c
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-bytea.stdout
@@ -0,0 +1,8 @@
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=499, ind=512, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=499, ind=512, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=499, ind=512, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d
diff --git a/src/interfaces/ecpg/test/sql/Makefile b/src/interfaces/ecpg/test/sql/Makefile
index b7bc034..e173ed2 100644
--- a/src/interfaces/ecpg/test/sql/Makefile
+++ b/src/interfaces/ecpg/test/sql/Makefile
@@ -23,7 +23,8 @@ TESTS = array array.c \
quote quote.c \
show show.c \
twophase twophase.c \
- insupd insupd.c
+ insupd insupd.c \
+ bytea bytea.c
all: $(TESTS)
diff --git a/src/interfaces/ecpg/test/sql/bytea.pgc b/src/interfaces/ecpg/test/sql/bytea.pgc
new file mode 100644
index 0000000..be275a2
--- /dev/null
+++ b/src/interfaces/ecpg/test/sql/bytea.pgc
@@ -0,0 +1,109 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+
+exec sql include ../regression;
+exec sql whenever sqlerror sqlprint;
+
+static void
+dump_binary(char *buf, int len, int ind)
+{
+ int i;
+
+ printf("len=%d, ind=%d, data=0x", len, ind);
+ for (i = 0; i < len; ++i)
+ printf("%02x", 0xff & buf[i]);
+ printf("\n");
+}
+
+#define DATA_SIZE 0x200
+#define LACK_SIZE 13
+#
+int
+main(void)
+{
+exec sql begin declare section;
+ bytea send_buf[2][DATA_SIZE];
+ bytea recv_buf[2][DATA_SIZE];
+ bytea recv_vlen_buf[][DATA_SIZE];
+ bytea recv_short_buf[DATA_SIZE - LACK_SIZE];
+ int ind[2];
+exec sql end declare section;
+ int i, j, c;
+
+#define init() { \
+ for (i = 0; i < 2; ++i) \
+ { \
+ memset(recv_buf[i].arr, 0x0, sizeof(recv_buf[i].arr)); \
+ recv_buf[i].len = 0; \
+ ind[i] = 0; \
+ } \
+ recv_vlen_buf = NULL, \
+ memset(recv_short_buf.arr, 0x0, sizeof(recv_short_buf.arr)); \
+} \
+while (0)
+
+
+ ECPGdebug(1, stderr);
+
+ for (i = 0; i < 2; ++i)
+ {
+ for (j = 0, c = 0xff; (c == -1 ? c = 0xff : 1), j < DATA_SIZE; ++j, --c)
+ send_buf[i].arr[j] = c;
+
+ send_buf[i].len = DATA_SIZE;
+ }
+
+ exec sql connect to REGRESSDB1;
+
+ exec sql create table if not exists test (data1 bytea, data2 bytea);
+
+ exec sql prepare ins_stmt from "insert into test values(?,?)";
+ exec sql prepare sel_stmt from "select data1,data2 from test";
+ exec sql allocate descriptor idesc;
+ exec sql allocate descriptor odesc;
+
+ /* Test for static sql statement with normal host variable, indicator */
+ init();
+ exec sql truncate test;
+ exec sql insert into test values(:send_buf[0], :send_buf[1]);
+ exec sql select data1,data2 into :recv_buf[0]:ind[0], :recv_short_buf:ind[1] from test;
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for variable length array */
+ init();
+ exec sql truncate test;
+ exec sql insert into test values(:send_buf[0], :send_buf[1]);
+ exec sql insert into test values(:send_buf[0], :send_buf[1]);
+ exec sql select data1 into :recv_vlen_buf from test;
+ dump_binary(recv_vlen_buf[0].arr, recv_vlen_buf[0].len, 0);
+ dump_binary(recv_vlen_buf[1].arr, recv_vlen_buf[1].len, 0);
+
+ /* Test for dynamic sql statement with normal host variable, indicator */
+ init();
+ exec sql truncate test;
+ exec sql execute ins_stmt using :send_buf[0], :send_buf[1];
+ exec sql execute sel_stmt into :recv_buf[0]:ind[0], :recv_short_buf:ind[1];
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for dynamic sql statement with sql descriptor */
+ init();
+ exec sql truncate test;
+ exec sql set descriptor idesc value 1 data = :send_buf[0];
+ exec sql set descriptor idesc value 2 data = :send_buf[1];
+ exec sql execute ins_stmt using sql descriptor idesc;
+ exec sql execute sel_stmt into sql descriptor odesc;
+ exec sql get descriptor odesc value 1 :recv_buf[0] = data, :ind[0] = indicator;
+ exec sql get descriptor odesc value 2 :recv_short_buf = data, :ind[1] = indicator;
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ exec sql drop table test;
+ exec sql commit;
+ exec sql disconnect;
+
+ return 0;
+}
Meskes-san
Sorry to bother you, but I hope any comment of yours.
Regards
Ryo Matsumura
Show quoted text
Subject: RE: [PROPOSAL]a new data type 'bytea' for ECPG
From: Tsunakawa, Takayuki [mailto:tsunakawa.takay@jp.fujitsu.com]
I think the host variable data type that corresponds to the server-side bytea
should be bytea. As the following pages state or imply, it would be better
to create standard-compliant LOB types someday, and use the keyword BLOBin
ECPG for that type. The server-side data types should have the names BLOB,
CLOB and NCLOB. Those types should handle data larget than 1 GB and havethe
locator feature defined in the SQL standard. Maybe we should also advanced
LOB features like Oracle's SecureFiles LOB and SQL Server's FileTables.Tsunakawa-san, thanks for your advice.
I understand that C type definition of client-side bytea is not constrained
by the standard BLOB.What should I do next?
For now, I attach a patch that is removed noise(pgindent/typedef.list).P.S.
The patch does not support ECPG.bytea in sqltype of "struct sqlvar_struct"
because of compatibility.Regards
Ryo Matsumura
Matsumura-san,
Sorry to bother you, but I hope any comment of yours.
It is no bother.
I'm fine with the patch if it does not work against the standard.
I do think, though, we should change the debug output for
ecpg_free_params(). The way it is now it prints binary values which we
also have in our test suite. I'm afraid this will come back to haunt
us.
The patch does not support ECPG.bytea in sqltype of "struct
sqlvar_struct"
because of compatibility.
Sorry I do not really understand what you mean. Could you please
explain?
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Meskes-san
Tnak you for your comment.
I do think, though, we should change the debug output for
ecpg_free_params().
I try to change about it. Next patch will print binary in hex-format.
The patch does not support ECPG.bytea in sqltype of "struct
sqlvar_struct"
because of compatibility.Sorry I do not really understand what you mean. Could you please
explain?
I meaned that existing applications that receive data of bytea column
with using sqlda will encounter their unknown type(=ECPG.bytea) in
sqlvar_struct.sqltype.
Regards
Ryo Matsumura
Show quoted text
-----Original Message-----
From: Michael Meskes [mailto:meskes@postgresql.org]
Sent: Wednesday, December 5, 2018 8:24 PM
To: Matsumura, Ryo/松村 量 <matsumura.ryo@jp.fujitsu.com>; Tsunakawa,
Takayuki/綱川 貴之 <tsunakawa.takay@jp.fujitsu.com>
Cc: pgsql-hackers@lists.postgresql.org
Subject: Re: [PROPOSAL]a new data type 'bytea' for ECPGMatsumura-san,
Sorry to bother you, but I hope any comment of yours.
It is no bother.
I'm fine with the patch if it does not work against the standard.
I do think, though, we should change the debug output for
ecpg_free_params(). The way it is now it prints binary values which we
also have in our test suite. I'm afraid this will come back to haunt
us.The patch does not support ECPG.bytea in sqltype of "struct
sqlvar_struct"
because of compatibility.Sorry I do not really understand what you mean. Could you please
explain?Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Matsumura-san,
I do think, though, we should change the debug output for
ecpg_free_params().I try to change about it. Next patch will print binary in hex-format.
Thank you.
The patch does not support ECPG.bytea in sqltype of "struct
sqlvar_struct"
because of compatibility.Sorry I do not really understand what you mean. Could you please
explain?I meaned that existing applications that receive data of bytea column
with using sqlda will encounter their unknown type(=ECPG.bytea) in
sqlvar_struct.sqltype.
You mean if they are not recompiled? If so, yes, how else could that be
handled?
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Meskes-san
The patch does not support ECPG.bytea in sqltype of "struct
sqlvar_struct"
because of compatibility.Sorry I do not really understand what you mean. Could you please
explain?I meaned that existing applications that receive data of bytea column
with using sqlda will encounter their unknown type(=ECPG.bytea) in
sqlvar_struct.sqltype.You mean if they are not recompiled? If so, yes, how else could that be
handled?
Even if they are recompiled, they will fail.
switch (sqlvar_struct.sqltype)
{
case ECPG.int: break;
case ECPG.char: break;
/* There is no case for ECPG.bytea */
default: abort();
There is an idea as following, but it seems to be ugly.
Implement a parameter for ecpglib.
The parameter means whether application want to receive
bytea data in binary format or not. Default is "not".
# I don't know any ecpglib's parameter like it.
In other words, if application uses "bytea" type host variable,
ecpglib could know its intent, but in case of sqlda ecpglib could
not know it.
Regards
Ryo Matsumura
Matsumura-san,
I meaned that existing applications that receive data of bytea
column
with using sqlda will encounter their unknown type(=ECPG.bytea) in
sqlvar_struct.sqltype.You mean if they are not recompiled? If so, yes, how else could
that be
handled?Even if they are recompiled, they will fail.
switch (sqlvar_struct.sqltype)
{
case ECPG.int: break;
case ECPG.char: break;
/* There is no case for ECPG.bytea */
default: abort();
Sorry, I should have been more precise. I meant if they are not
recompiled against the new ecpglib which has a case for ECPG.bytea.
There is an idea as following, but it seems to be ugly.
Implement a parameter for ecpglib.
The parameter means whether application want to receive
bytea data in binary format or not. Default is "not".
# I don't know any ecpglib's parameter like it.In other words, if application uses "bytea" type host variable,
ecpglib could know its intent, but in case of sqlda ecpglib could
not know it.
I'm at a loss here. I don't understand what you are trying to say.
An incompatible change is ok if we change the version number of the
library accordingly.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Meskes-san
Maybe I understand your comments about compatibility.
I will try to implement for sqlda.
# I am not goot at library version mechanism.
# I will learn about it.
Regards
Ryo Matsumura
Meskes-san
I noticed that I was confused.
My topic was about adding bytea as new host variable type.
The topic *didn't* include that receiving binary format data
into SQLDATA descriptor like the following.
sqlda_t *sqlda;
exec sql create table if not exists test (c1 bytea);
exec sql select c1 into descriptor sqlda from test;
/* It expects that sqlda->sqlvar[0].sqldata is binary format. */
So, please ignore the following in my mail at 2018-11-12 02:14:58.
P.S.
The patch does not support ECPG.bytea in sqltype of "struct sqlvar_struct"
because of compatibility.
The topic included that receiving binary data into Named SQL descriptor with
using bytea host variable like the following. It has already been
implemented in my patch.
exec sql begin declare section;
bytea var[128];
exec sql end declare section;
exec sql create table if not exists test (c1 bytea);
exec sql allocate descriptor odesc;
exec sql select c1 into sql descriptor odesc from test;
exec sql get descriptor odesc value 1 :var = data;
Regards
Ryo Matsumura
Meskes-san
I do think, though, we should change the debug output for
ecpg_free_params().I try to change about it. Next patch will print binary in hex-format.
I implement and attach it. Please review a new patch in this mail.
I have a question about ecpg manual when I add article for bytea.
I wonder what does the following about VARCHAR mean.
35.4.4. Type Mapping
Table 35.1. Mapping Between PostgreSQL Data Types and C Variable Types
character(n), varchar(n), text | char[n+1], VARCHAR[n+1] [b]
<footnote>
[b] declared in ecpglib.h
There is no declaration for VARCHAR in ecpglib.h.
There is a declaration for ECPGt_varchar in ecpgtype.h, but it may be
be unusefull information for users.
On the other hand, footnote for 'bool' is usefull because there is
C-definition macro in ecpglib.h.
<footnote>
[c] declared in ecpglib.h if not native
<ecpglib.h>
#ifndef bool
#define bool char
#endif
I think, if the footnote of VARCHAR is meaningless, I remove it while I add
the article for bytea. (I didn't remove in this patch.)
Regards
Ryo Matsumura
Attachments:
ecpg_bytea_pg_v1_2.patchapplication/octet-stream; name=ecpg_bytea_pg_v1_2.patchDownload
diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c
index 424b5ae..81f94cc 100644
--- a/src/interfaces/ecpg/ecpglib/data.c
+++ b/src/interfaces/ecpg/ecpglib/data.c
@@ -122,6 +122,86 @@ check_special_value(char *ptr, double *retval, char **endptr)
return false;
}
+/* imported from src/backend/utils/adt/encode.c */
+
+unsigned
+ecpg_hex_enc_len(unsigned srclen)
+{
+ return srclen << 1;
+}
+
+unsigned
+ecpg_hex_dec_len(unsigned srclen)
+{
+ return srclen >> 1;
+}
+
+static inline char
+get_hex(char c)
+{
+ static const int8 hexlookup[128] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+ int res = -1;
+
+ if (c > 0 && c < 127)
+ res = hexlookup[(unsigned char) c];
+
+ return (char) res;
+}
+
+static unsigned
+hex_decode(const char *src, unsigned len, char *dst)
+{
+ const char *s,
+ *srcend;
+ char v1,
+ v2,
+ *p;
+
+ srcend = src + len;
+ s = src;
+ p = dst;
+ while (s < srcend)
+ {
+ if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
+ {
+ s++;
+ continue;
+ }
+ v1 = get_hex(*s++) << 4;
+ if (s >= srcend)
+ return -1;
+
+ v2 = get_hex(*s++);
+ *p++ = v1 | v2;
+ }
+
+ return p - dst;
+}
+
+unsigned
+ecpg_hex_encode(const char *src, unsigned len, char *dst)
+{
+ static const char hextbl[] = "0123456789abcdef";
+ const char *end = src + len;
+
+ while (src < end)
+ {
+ *dst++ = hextbl[(*src >> 4) & 0xF];
+ *dst++ = hextbl[*src & 0xF];
+ src++;
+ }
+ return len * 2;
+}
+
bool
ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
enum ECPGttype type, enum ECPGttype ind_type,
@@ -447,6 +527,55 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
return false;
break;
+ case ECPGt_bytea:
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var + offset * act_tuple);
+ long dst_size,
+ src_size,
+ dec_size;
+
+ dst_size = ecpg_hex_enc_len(varcharsize);
+ src_size = size - 2; /* exclude backslash + 'x' */
+ dec_size = src_size < dst_size ? src_size : dst_size;
+ variable->len = hex_decode(pval + 2, dec_size, variable->arr);
+
+ if (dst_size < src_size)
+ {
+ long rcv_size = ecpg_hex_dec_len(size - 2);
+
+ /* truncation */
+ switch (ind_type)
+ {
+ case ECPGt_short:
+ case ECPGt_unsigned_short:
+ *((short *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+ case ECPGt_int:
+ case ECPGt_unsigned_int:
+ *((int *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+ case ECPGt_long:
+ case ECPGt_unsigned_long:
+ *((long *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+#ifdef HAVE_LONG_LONG_INT
+ case ECPGt_long_long:
+ case ECPGt_unsigned_long_long:
+ *((long long int *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+#endif /* HAVE_LONG_LONG_INT */
+ default:
+ break;
+ }
+ sqlca->sqlwarn[0] = sqlca->sqlwarn[1] = 'W';
+ }
+
+ pval += size;
+
+ }
+ break;
+
case ECPGt_char:
case ECPGt_unsigned_char:
case ECPGt_string:
diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c
index 186f92c..ae1a7db 100644
--- a/src/interfaces/ecpg/ecpglib/descriptor.c
+++ b/src/interfaces/ecpg/ecpglib/descriptor.c
@@ -636,11 +636,29 @@ ECPGset_desc(int lineno, const char *desc_name, int index,...)
{
case ECPGd_data:
{
- if (!ecpg_store_input(lineno, true, var, &tobeinserted, false))
+ if (var->type != ECPGt_bytea)
{
- ecpg_free(var);
- va_end(args);
- return false;
+ if (!ecpg_store_input(lineno, true, var, &tobeinserted, false))
+ {
+ ecpg_free(var);
+ va_end(args);
+ return false;
+ }
+ desc_item->is_binary = false;
+ }
+ else
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var->value);
+
+ if (!(tobeinserted = ecpg_alloc(sizeof(int) + variable->len, lineno)))
+ {
+ ecpg_free(var);
+ va_end(args);
+ return false;
+ }
+ memcpy(tobeinserted, var->value, sizeof(int) + variable->len);
+ desc_item->is_binary = true;
}
ecpg_free(desc_item->data); /* free() takes care of a
diff --git a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
index 1c9bce1..a4e593c 100644
--- a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
+++ b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
@@ -37,6 +37,13 @@ struct ECPGgeneric_varchar
char arr[FLEXIBLE_ARRAY_MEMBER];
};
+/* A generic bytea type. */
+struct ECPGgeneric_bytea
+{
+ int len;
+ char arr[FLEXIBLE_ARRAY_MEMBER];
+};
+
/*
* type information cache
*/
@@ -64,6 +71,8 @@ struct statement
char *oldlocale;
int nparams;
char **paramvalues;
+ int *paramlengths;
+ int *paramformats;
PGresult *results;
};
@@ -106,6 +115,8 @@ struct descriptor_item
int precision;
int scale;
int type;
+ bool is_binary;
+ int data_len;
struct descriptor_item *next;
};
@@ -194,6 +205,9 @@ struct sqlda_compat *ecpg_build_compat_sqlda(int, PGresult *, int, enum COMPAT_M
void ecpg_set_compat_sqlda(int, struct sqlda_compat **, const PGresult *, int, enum COMPAT_MODE);
struct sqlda_struct *ecpg_build_native_sqlda(int, PGresult *, int, enum COMPAT_MODE);
void ecpg_set_native_sqlda(int, struct sqlda_struct **, const PGresult *, int, enum COMPAT_MODE);
+unsigned ecpg_hex_dec_len(unsigned srclen);
+unsigned ecpg_hex_enc_len(unsigned srclen);
+unsigned ecpg_hex_encode(const char *src, unsigned len, char *dst);
/* SQLSTATE values generated or processed by ecpglib (intentionally
* not exported -- users should refer to the codes directly) */
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index 3f5034e..96d3107 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -799,6 +799,20 @@ ecpg_store_input(const int lineno, const bool force_indicator, const struct vari
*tobeinserted_p = mallocedval;
}
break;
+
+ case ECPGt_bytea:
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var->value);
+
+ if (!(mallocedval = (char *) ecpg_alloc(variable->len, lineno)))
+ return false;
+
+ memcpy(mallocedval, variable->arr, variable->len);
+ *tobeinserted_p = mallocedval;
+ }
+ break;
+
case ECPGt_varchar:
{
struct ECPGgeneric_varchar *variable =
@@ -1041,6 +1055,30 @@ ecpg_store_input(const int lineno, const bool force_indicator, const struct vari
return true;
}
+static void
+print_param_value(char *value, int len, int is_binary, int lineno, int nth)
+{
+ char *value_s;
+ bool malloced = false;
+
+ if (value == NULL)
+ value_s = "null";
+ else if (! is_binary)
+ value_s = value;
+ else
+ {
+ value_s = ecpg_alloc(ecpg_hex_enc_len(len), lineno);
+ ecpg_hex_encode(value, len, value_s);
+ malloced = true;
+ }
+
+ ecpg_log("ecpg_free_params on line %d: parameter %d = %s\n",
+ lineno, nth, value_s);
+
+ if (malloced)
+ ecpg_free(value_s);
+}
+
void
ecpg_free_params(struct statement *stmt, bool print)
{
@@ -1049,11 +1087,16 @@ ecpg_free_params(struct statement *stmt, bool print)
for (n = 0; n < stmt->nparams; n++)
{
if (print)
- ecpg_log("ecpg_free_params on line %d: parameter %d = %s\n", stmt->lineno, n + 1, stmt->paramvalues[n] ? stmt->paramvalues[n] : "null");
+ print_param_value(stmt->paramvalues[n], stmt->paramlengths[n],
+ stmt->paramformats[n], stmt->lineno, n + 1);
ecpg_free(stmt->paramvalues[n]);
}
ecpg_free(stmt->paramvalues);
+ ecpg_free(stmt->paramlengths);
+ ecpg_free(stmt->paramformats);
stmt->paramvalues = NULL;
+ stmt->paramlengths = NULL;
+ stmt->paramformats = NULL;
stmt->nparams = 0;
}
@@ -1120,8 +1163,13 @@ ecpg_build_params(struct statement *stmt)
{
char *tobeinserted;
int counter = 1;
+ bool binary_format;
+ int binary_length;
+
tobeinserted = NULL;
+ binary_length = 0;
+ binary_format = false;
/*
* A descriptor is a special case since it contains many variables but
@@ -1146,12 +1194,27 @@ ecpg_build_params(struct statement *stmt)
{
if (desc_item->num == desc_counter)
{
- desc_inlist.type = ECPGt_char;
+ if (!desc_item->is_binary)
+ {
+ desc_inlist.type = ECPGt_char;
+ desc_inlist.varcharsize = strlen(desc_item->data);
+ }
+ else
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (desc_item->data);
+
+ binary_length = variable->len;
+ binary_format = true;
+
+ desc_inlist.type = ECPGt_bytea;
+ desc_inlist.varcharsize = binary_length;
+ }
desc_inlist.value = desc_item->data;
desc_inlist.pointer = &(desc_item->data);
- desc_inlist.varcharsize = strlen(desc_item->data);
desc_inlist.arrsize = 1;
desc_inlist.offset = 0;
+
if (!desc_item->indicator)
{
desc_inlist.ind_type = ECPGt_NO_INDICATOR;
@@ -1293,6 +1356,15 @@ ecpg_build_params(struct statement *stmt)
{
if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, var, &tobeinserted, false))
return false;
+
+ if (var->type == ECPGt_bytea)
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var->value);
+
+ binary_length = variable->len;
+ binary_format = true;
+ }
}
/*
@@ -1346,16 +1418,32 @@ ecpg_build_params(struct statement *stmt)
else
{
char **paramvalues;
+ int *paramlengths;
+ int *paramformats;
if (!(paramvalues = (char **) ecpg_realloc(stmt->paramvalues, sizeof(char *) * (stmt->nparams + 1), stmt->lineno)))
{
ecpg_free_params(stmt, false);
return false;
}
+ if (!(paramlengths = (int *) ecpg_realloc(stmt->paramlengths, sizeof(int) * (stmt->nparams + 1), stmt->lineno)))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+ if (!(paramformats = (int *) ecpg_realloc(stmt->paramformats, sizeof(int) * (stmt->nparams + 1), stmt->lineno)))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
stmt->nparams++;
stmt->paramvalues = paramvalues;
+ stmt->paramlengths = paramlengths;
+ stmt->paramformats = paramformats;
stmt->paramvalues[stmt->nparams - 1] = tobeinserted;
+ stmt->paramlengths[stmt->nparams - 1] = binary_length;
+ stmt->paramformats[stmt->nparams - 1] = (binary_format ? 1 : 0);
/* let's see if this was an old style placeholder */
if (stmt->command[position] == '?')
@@ -1428,7 +1516,13 @@ ecpg_execute(struct statement *stmt)
ecpg_log("ecpg_execute on line %d: query: %s; with %d parameter(s) on connection %s\n", stmt->lineno, stmt->command, stmt->nparams, stmt->connection->name);
if (stmt->statement_type == ECPGst_execute)
{
- stmt->results = PQexecPrepared(stmt->connection->connection, stmt->name, stmt->nparams, (const char *const *) stmt->paramvalues, NULL, NULL, 0);
+ stmt->results = PQexecPrepared(stmt->connection->connection,
+ stmt->name,
+ stmt->nparams,
+ (const char *const *) stmt->paramvalues,
+ (const int *) stmt->paramlengths,
+ (const int *) stmt->paramformats,
+ 0);
ecpg_log("ecpg_execute on line %d: using PQexecPrepared for \"%s\"\n", stmt->lineno, stmt->command);
}
else
@@ -1440,7 +1534,12 @@ ecpg_execute(struct statement *stmt)
}
else
{
- stmt->results = PQexecParams(stmt->connection->connection, stmt->command, stmt->nparams, NULL, (const char *const *) stmt->paramvalues, NULL, NULL, 0);
+ stmt->results = PQexecParams(stmt->connection->connection,
+ stmt->command, stmt->nparams, NULL,
+ (const char *const *) stmt->paramvalues,
+ (const int *) stmt->paramlengths,
+ (const int *) stmt->paramformats,
+ 0);
ecpg_log("ecpg_execute on line %d: using PQexecParams\n", stmt->lineno);
}
}
diff --git a/src/interfaces/ecpg/ecpglib/misc.c b/src/interfaces/ecpg/ecpglib/misc.c
index a26dfdb..349dec3 100644
--- a/src/interfaces/ecpg/ecpglib/misc.c
+++ b/src/interfaces/ecpg/ecpglib/misc.c
@@ -355,6 +355,9 @@ ECPGset_noind_null(enum ECPGttype type, void *ptr)
*(((struct ECPGgeneric_varchar *) ptr)->arr) = 0x00;
((struct ECPGgeneric_varchar *) ptr)->len = 0;
break;
+ case ECPGt_bytea:
+ ((struct ECPGgeneric_bytea *) ptr)->len = 0;
+ break;
case ECPGt_decimal:
memset((char *) ptr, 0, sizeof(decimal));
((decimal *) ptr)->sign = NUMERIC_NULL;
@@ -428,6 +431,10 @@ ECPGis_noind_null(enum ECPGttype type, const void *ptr)
if (*(((const struct ECPGgeneric_varchar *) ptr)->arr) == 0x00)
return true;
break;
+ case ECPGt_bytea:
+ if (((struct ECPGgeneric_bytea *) ptr)->len == 0)
+ return true;
+ break;
case ECPGt_decimal:
if (((const decimal *) ptr)->sign == NUMERIC_NULL)
return true;
diff --git a/src/interfaces/ecpg/ecpglib/typename.c b/src/interfaces/ecpg/ecpglib/typename.c
index a3f2817..9251450 100644
--- a/src/interfaces/ecpg/ecpglib/typename.c
+++ b/src/interfaces/ecpg/ecpglib/typename.c
@@ -48,6 +48,8 @@ ecpg_type_name(enum ECPGttype typ)
return "bool";
case ECPGt_varchar:
return "varchar";
+ case ECPGt_bytea:
+ return "bytea";
case ECPGt_char_variable:
return "char";
case ECPGt_decimal:
diff --git a/src/interfaces/ecpg/include/ecpgtype.h b/src/interfaces/ecpg/include/ecpgtype.h
index 38fb3b6..b9fc4ea 100644
--- a/src/interfaces/ecpg/include/ecpgtype.h
+++ b/src/interfaces/ecpg/include/ecpgtype.h
@@ -63,7 +63,8 @@ enum ECPGttype
ECPGt_EORT, /* End of result types. */
ECPGt_NO_INDICATOR, /* no indicator */
ECPGt_string, /* trimmed (char *) type */
- ECPGt_sqlda /* C struct descriptor */
+ ECPGt_sqlda, /* C struct descriptor */
+ ECPGt_bytea
};
/* descriptor items */
@@ -88,7 +89,7 @@ enum ECPGdtype
ECPGd_cardinality
};
-#define IS_SIMPLE_TYPE(type) (((type) >= ECPGt_char && (type) <= ECPGt_interval) || ((type) == ECPGt_string))
+#define IS_SIMPLE_TYPE(type) (((type) >= ECPGt_char && (type) <= ECPGt_interval) || ((type) == ECPGt_string) || ((type) == ECPGt_bytea))
/* we also have to handle different statement types */
enum ECPG_statement_type
diff --git a/src/interfaces/ecpg/preproc/ecpg.header b/src/interfaces/ecpg/preproc/ecpg.header
index 00143a7..0bd38bd 100644
--- a/src/interfaces/ecpg/preproc/ecpg.header
+++ b/src/interfaces/ecpg/preproc/ecpg.header
@@ -46,6 +46,7 @@ static char pacounter_buffer[sizeof(int) * CHAR_BIT * 10 / 3]; /* a rough guess
static struct this_type actual_type[STRUCT_DEPTH];
static char *actual_startline[STRUCT_DEPTH];
static int varchar_counter = 1;
+static int bytea_counter = 1;
/* temporarily store struct members while creating the data structure */
struct ECPGstruct_member *struct_member_list[STRUCT_DEPTH] = { NULL };
@@ -562,6 +563,7 @@ add_typedef(char *name, char *dimension, char *length, enum ECPGttype type_enum,
ECPGstruct_member_dup(struct_member_list[struct_level]) : NULL;
if (type_enum != ECPGt_varchar &&
+ type_enum != ECPGt_bytea &&
type_enum != ECPGt_char &&
type_enum != ECPGt_unsigned_char &&
type_enum != ECPGt_string &&
diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer
index 19dc781..c44fcf9 100644
--- a/src/interfaces/ecpg/preproc/ecpg.trailer
+++ b/src/interfaces/ecpg/preproc/ecpg.trailer
@@ -550,6 +550,14 @@ var_type: simple_type
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
+ else if (strcmp($1, "bytea") == 0)
+ {
+ $$.type_enum = ECPGt_bytea;
+ $$.type_str = EMPTY;
+ $$.type_dimension = mm_strdup("-1");
+ $$.type_index = mm_strdup("-1");
+ $$.type_sizeof = NULL;
+ }
else if (strcmp($1, "float") == 0)
{
$$.type_enum = ECPGt_float;
@@ -627,7 +635,7 @@ var_type: simple_type
/* this is for typedef'ed types */
struct typedefs *this = get_typedef($1);
- $$.type_str = (this->type->type_enum == ECPGt_varchar) ? EMPTY : mm_strdup(this->name);
+ $$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? EMPTY : mm_strdup(this->name);
$$.type_enum = this->type->type_enum;
$$.type_dimension = this->type->type_dimension;
$$.type_index = this->type->type_index;
@@ -838,7 +846,7 @@ variable_list: variable
{ $$ = $1; }
| variable_list ',' variable
{
- if (actual_type[struct_level].type_enum == ECPGt_varchar)
+ if (actual_type[struct_level].type_enum == ECPGt_varchar || actual_type[struct_level].type_enum == ECPGt_bytea)
$$ = cat_str(3, $1, mm_strdup(";"), $3);
else
$$ = cat_str(3, $1, mm_strdup(","), $3);
@@ -852,9 +860,10 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
char *length = $3.index2; /* length of string */
char *dim_str;
char *vcn;
+ int *varlen_type_counter;
+ char *struct_name;
adjust_array(actual_type[struct_level].type_enum, &dimension, &length, actual_type[struct_level].type_dimension, actual_type[struct_level].type_index, strlen($1), false);
-
switch (actual_type[struct_level].type_enum)
{
case ECPGt_struct:
@@ -868,10 +877,21 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
break;
case ECPGt_varchar:
+ case ECPGt_bytea:
+ if (actual_type[struct_level].type_enum == ECPGt_varchar)
+ {
+ varlen_type_counter = &varchar_counter;
+ struct_name = " struct varchar_";
+ }
+ else
+ {
+ varlen_type_counter = &bytea_counter;
+ struct_name = " struct bytea_";
+ }
if (atoi(dimension) < 0)
- type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length, varchar_counter);
+ type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length, *varlen_type_counter);
else
- type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length, varchar_counter), dimension);
+ type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length, *varlen_type_counter), dimension);
if (strcmp(dimension, "0") == 0 || abs(atoi(dimension)) == 1)
dim_str=mm_strdup("");
@@ -883,12 +903,12 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
/* make sure varchar struct name is unique by adding a unique counter to its definition */
vcn = (char *) mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
- sprintf(vcn, "%d", varchar_counter);
+ sprintf(vcn, "%d", *varlen_type_counter);
if (strcmp(dimension, "0") == 0)
- $$ = cat_str(7, make2_str(mm_strdup(" struct varchar_"), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } *"), mm_strdup($2), $4, $5);
+ $$ = cat_str(7, make2_str(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } *"), mm_strdup($2), $4, $5);
else
- $$ = cat_str(8, make2_str(mm_strdup(" struct varchar_"), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } "), mm_strdup($2), dim_str, $4, $5);
- varchar_counter++;
+ $$ = cat_str(8, make2_str(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } "), mm_strdup($2), dim_str, $4, $5);
+ (*varlen_type_counter)++;
break;
case ECPGt_char:
@@ -1354,6 +1374,7 @@ ECPGVar: SQL_VAR
break;
case ECPGt_varchar:
+ case ECPGt_bytea:
if (atoi(dimension) == -1)
type = ECPGmake_simple_type($5.type_enum, length, 0);
else
diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c
index 40ce3fb..5488db3 100644
--- a/src/interfaces/ecpg/preproc/type.c
+++ b/src/interfaces/ecpg/preproc/type.c
@@ -102,7 +102,7 @@ ECPGmake_simple_type(enum ECPGttype type, char *size, int counter)
ne->size = size;
ne->u.element = NULL;
ne->struct_sizeof = NULL;
- ne->counter = counter; /* only needed for varchar */
+ ne->counter = counter; /* only needed for varchar and bytea */
return ne;
}
@@ -175,6 +175,8 @@ get_type(enum ECPGttype type)
break;
case ECPGt_varchar:
return "ECPGt_varchar";
+ case ECPGt_bytea:
+ return "ECPGt_bytea";
case ECPGt_NO_INDICATOR: /* no indicator */
return "ECPGt_NO_INDICATOR";
break;
@@ -424,6 +426,7 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type,
{
char *variable = (char *) mm_alloc(strlen(name) + ((prefix == NULL) ? 0 : strlen(prefix)) + 4);
char *offset = (char *) mm_alloc(strlen(name) + strlen("sizeof(struct varchar_)") + 1 + strlen(varcharsize) + sizeof(int) * CHAR_BIT * 10 / 3);
+ char *struct_name;
switch (type)
{
@@ -433,6 +436,7 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type,
*/
case ECPGt_varchar:
+ case ECPGt_bytea:
/*
* we have to use the pointer except for arrays with given
@@ -449,10 +453,15 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type,
* If we created a varchar structure automatically, counter is
* greater than 0.
*/
+ if (type == ECPGt_varchar)
+ struct_name = "struct varchar";
+ else
+ struct_name = "struct bytea";
+
if (counter)
- sprintf(offset, "sizeof(struct varchar_%d)", counter);
+ sprintf(offset, "sizeof(%s_%d)", struct_name, counter);
else
- sprintf(offset, "sizeof(struct varchar)");
+ sprintf(offset, "sizeof(%s)", struct_name);
break;
case ECPGt_char:
case ECPGt_unsigned_char:
diff --git a/src/interfaces/ecpg/preproc/variable.c b/src/interfaces/ecpg/preproc/variable.c
index a953498..887d479 100644
--- a/src/interfaces/ecpg/preproc/variable.c
+++ b/src/interfaces/ecpg/preproc/variable.c
@@ -560,6 +560,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
break;
case ECPGt_varchar:
+ case ECPGt_bytea:
/* pointer has to get dimension 0 */
if (pointer_len)
*dimension = mm_strdup("0");
ecpg_bytea_test_v1_2.patchapplication/octet-stream; name=ecpg_bytea_test_v1_2.patchDownload
diff --git a/src/interfaces/ecpg/test/ecpg_schedule b/src/interfaces/ecpg/test/ecpg_schedule
index 991b8cb..2478d03 100644
--- a/src/interfaces/ecpg/test/ecpg_schedule
+++ b/src/interfaces/ecpg/test/ecpg_schedule
@@ -32,6 +32,7 @@ test: preproc/whenever
test: preproc/whenever_do_continue
test: sql/array
test: sql/binary
+test: sql/bytea
test: sql/code100
test: sql/copystdout
test: sql/define
diff --git a/src/interfaces/ecpg/test/expected/sql-bytea.c b/src/interfaces/ecpg/test/expected/sql-bytea.c
new file mode 100644
index 0000000..f230d73
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-bytea.c
@@ -0,0 +1,316 @@
+/* Processed by ecpg (regression mode) */
+/* These include files are added by the preprocessor */
+#include <ecpglib.h>
+#include <ecpgerrno.h>
+#include <sqlca.h>
+/* End of automatic include section */
+#define ECPGdebug(X,Y) ECPGdebug((X)+100,(Y))
+
+#line 1 "bytea.pgc"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+
+
+#line 1 "regression.h"
+
+
+
+
+
+
+#line 6 "bytea.pgc"
+
+/* exec sql whenever sqlerror sqlprint ; */
+#line 7 "bytea.pgc"
+
+
+static void
+dump_binary(char *buf, int len, int ind)
+{
+ int i;
+
+ printf("len=%d, ind=%d, data=0x", len, ind);
+ for (i = 0; i < len; ++i)
+ printf("%02x", 0xff & buf[i]);
+ printf("\n");
+}
+
+#define DATA_SIZE 0x200
+#define LACK_SIZE 13
+#
+int
+main(void)
+{
+/* exec sql begin declare section */
+
+
+
+
+
+
+#line 27 "bytea.pgc"
+ struct bytea_1 { int len; char arr[ DATA_SIZE ]; } send_buf [ 2 ] ;
+
+#line 28 "bytea.pgc"
+ struct bytea_2 { int len; char arr[ DATA_SIZE ]; } recv_buf [ 2 ] ;
+
+#line 29 "bytea.pgc"
+ struct bytea_3 { int len; char arr[ DATA_SIZE ]; } * recv_vlen_buf ;
+
+#line 30 "bytea.pgc"
+ struct bytea_4 { int len; char arr[ DATA_SIZE - LACK_SIZE ]; } recv_short_buf ;
+
+#line 31 "bytea.pgc"
+ int ind [ 2 ] ;
+/* exec sql end declare section */
+#line 32 "bytea.pgc"
+
+ int i, j, c;
+
+#define init() { \
+ for (i = 0; i < 2; ++i) \
+ { \
+ memset(recv_buf[i].arr, 0x0, sizeof(recv_buf[i].arr)); \
+ recv_buf[i].len = 0; \
+ ind[i] = 0; \
+ } \
+ recv_vlen_buf = NULL, \
+ memset(recv_short_buf.arr, 0x0, sizeof(recv_short_buf.arr)); \
+} \
+while (0)
+
+
+ ECPGdebug(1, stderr);
+
+ for (i = 0; i < 2; ++i)
+ {
+ for (j = 0, c = 0xff; (c == -1 ? c = 0xff : 1), j < DATA_SIZE; ++j, --c)
+ send_buf[i].arr[j] = c;
+
+ send_buf[i].len = DATA_SIZE;
+ }
+
+ { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0);
+#line 58 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 58 "bytea.pgc"
+
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table if not exists test ( data1 bytea , data2 bytea )", ECPGt_EOIT, ECPGt_EORT);
+#line 60 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 60 "bytea.pgc"
+
+
+ { ECPGprepare(__LINE__, NULL, 0, "ins_stmt", "insert into test values(?,?)");
+#line 62 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 62 "bytea.pgc"
+
+ { ECPGprepare(__LINE__, NULL, 0, "sel_stmt", "select data1,data2 from test");
+#line 63 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 63 "bytea.pgc"
+
+ ECPGallocate_desc(__LINE__, "idesc");
+#line 64 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();
+#line 64 "bytea.pgc"
+
+ ECPGallocate_desc(__LINE__, "odesc");
+#line 65 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();
+#line 65 "bytea.pgc"
+
+
+ /* Test for static sql statement with normal host variable, indicator */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 69 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 69 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test values ( $1 , $2 )",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 70 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 70 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select data1 , data2 from test", ECPGt_EOIT,
+ ECPGt_bytea,&(recv_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_2),
+ ECPGt_int,&(ind[0]),(long)1,(long)1,sizeof(int),
+ ECPGt_bytea,&(recv_short_buf),(long)DATA_SIZE - LACK_SIZE,(long)1,sizeof(struct bytea_4),
+ ECPGt_int,&(ind[1]),(long)1,(long)1,sizeof(int), ECPGt_EORT);
+#line 71 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 71 "bytea.pgc"
+
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for variable length array */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 77 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 77 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test values ( $1 , $2 )",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 78 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 78 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test values ( $1 , $2 )",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 79 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 79 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select data1 from test", ECPGt_EOIT,
+ ECPGt_bytea,&(recv_vlen_buf),(long)DATA_SIZE,(long)0,sizeof(struct bytea_3),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 80 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 80 "bytea.pgc"
+
+ dump_binary(recv_vlen_buf[0].arr, recv_vlen_buf[0].len, 0);
+ dump_binary(recv_vlen_buf[1].arr, recv_vlen_buf[1].len, 0);
+
+ /* Test for dynamic sql statement with normal host variable, indicator */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 86 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 86 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "ins_stmt",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 87 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 87 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "sel_stmt", ECPGt_EOIT,
+ ECPGt_bytea,&(recv_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_2),
+ ECPGt_int,&(ind[0]),(long)1,(long)1,sizeof(int),
+ ECPGt_bytea,&(recv_short_buf),(long)DATA_SIZE - LACK_SIZE,(long)1,sizeof(struct bytea_4),
+ ECPGt_int,&(ind[1]),(long)1,(long)1,sizeof(int), ECPGt_EORT);
+#line 88 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 88 "bytea.pgc"
+
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for dynamic sql statement with sql descriptor */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 94 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 94 "bytea.pgc"
+
+ { ECPGset_desc(__LINE__, "idesc", 1,ECPGd_data,
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1), ECPGd_EODT);
+
+#line 95 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 95 "bytea.pgc"
+
+ { ECPGset_desc(__LINE__, "idesc", 2,ECPGd_data,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1), ECPGd_EODT);
+
+#line 96 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 96 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "ins_stmt",
+ ECPGt_descriptor, "idesc", 1L, 1L, 1L,
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 97 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 97 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "sel_stmt", ECPGt_EOIT,
+ ECPGt_descriptor, "odesc", 1L, 1L, 1L,
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 98 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 98 "bytea.pgc"
+
+ { ECPGget_desc(__LINE__, "odesc", 1,ECPGd_indicator,
+ ECPGt_int,&(ind[0]),(long)1,(long)1,sizeof(int), ECPGd_data,
+ ECPGt_bytea,&(recv_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_2), ECPGd_EODT);
+
+#line 99 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 99 "bytea.pgc"
+
+ { ECPGget_desc(__LINE__, "odesc", 2,ECPGd_indicator,
+ ECPGt_int,&(ind[1]),(long)1,(long)1,sizeof(int), ECPGd_data,
+ ECPGt_bytea,&(recv_short_buf),(long)DATA_SIZE - LACK_SIZE,(long)1,sizeof(struct bytea_4), ECPGd_EODT);
+
+#line 100 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 100 "bytea.pgc"
+
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table test", ECPGt_EOIT, ECPGt_EORT);
+#line 104 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 104 "bytea.pgc"
+
+ { ECPGtrans(__LINE__, NULL, "commit");
+#line 105 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 105 "bytea.pgc"
+
+ { ECPGdisconnect(__LINE__, "CURRENT");
+#line 106 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 106 "bytea.pgc"
+
+
+ return 0;
+}
diff --git a/src/interfaces/ecpg/test/expected/sql-bytea.stderr b/src/interfaces/ecpg/test/expected/sql-bytea.stderr
new file mode 100644
index 0000000..c9f071e
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-bytea.stderr
@@ -0,0 +1,150 @@
+[NO_PID]: ECPGdebug: set to 1
+[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 60: query: create table if not exists test ( data1 bytea , data2 bytea ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 60: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 60: OK: CREATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: prepare_common on line 62: name ins_stmt; query: "insert into test values($1,$2)"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: prepare_common on line 63: name sel_stmt; query: "select data1,data2 from test"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 69: query: truncate test; 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: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 70: query: insert into test values ( $1 , $2 ); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 70: using PQexecParams
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 70: parameter 1 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 70: parameter 2 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 70: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 71: query: select data1 , data2 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 71: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 71: correctly got 1 tuples with 2 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 71: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 71: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 77: query: truncate test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 77: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 77: OK: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 78: query: insert into test values ( $1 , $2 ); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 78: using PQexecParams
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 78: parameter 1 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 78: parameter 2 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 78: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 79: query: insert into test values ( $1 , $2 ); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 79: using PQexecParams
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 79: parameter 1 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 79: parameter 2 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 79: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 80: query: select data1 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 80: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 80: correctly got 2 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_store_result on line 80: allocating memory for 2 tuples
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 80: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 80: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 86: query: truncate test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 86: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 86: OK: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 87: query: insert into test values($1,$2); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 87: using PQexecPrepared for "insert into test values($1,$2)"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 87: parameter 1 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 87: parameter 2 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 87: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 88: query: select data1,data2 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 88: using PQexecPrepared for "select data1,data2 from test"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 88: correctly got 1 tuples with 2 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 88: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 88: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 94: query: truncate test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 94: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 94: OK: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 97: query: insert into test values($1,$2); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 97: using PQexecPrepared for "insert into test values($1,$2)"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 97: parameter 1 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 97: parameter 2 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 97: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 98: query: select data1,data2 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 98: using PQexecPrepared for "select data1,data2 from test"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 98: correctly got 1 tuples with 2 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 98: putting result (1 tuples) into descriptor odesc
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGget_desc: reading items for tuple 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 99: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGget_desc: reading items for tuple 2
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 100: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 104: query: drop table test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 104: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 104: OK: DROP TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGtrans on line 105: action "commit"; connection "ecpg1_regression"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: deallocate_one on line 0: name sel_stmt
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: deallocate_one on line 0: name ins_stmt
+[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/sql-bytea.stdout b/src/interfaces/ecpg/test/expected/sql-bytea.stdout
new file mode 100644
index 0000000..4c9ad4c
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-bytea.stdout
@@ -0,0 +1,8 @@
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=499, ind=512, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=499, ind=512, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=499, ind=512, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d
diff --git a/src/interfaces/ecpg/test/sql/Makefile b/src/interfaces/ecpg/test/sql/Makefile
index b7bc034..e173ed2 100644
--- a/src/interfaces/ecpg/test/sql/Makefile
+++ b/src/interfaces/ecpg/test/sql/Makefile
@@ -23,7 +23,8 @@ TESTS = array array.c \
quote quote.c \
show show.c \
twophase twophase.c \
- insupd insupd.c
+ insupd insupd.c \
+ bytea bytea.c
all: $(TESTS)
diff --git a/src/interfaces/ecpg/test/sql/bytea.pgc b/src/interfaces/ecpg/test/sql/bytea.pgc
new file mode 100644
index 0000000..b2c5c03
--- /dev/null
+++ b/src/interfaces/ecpg/test/sql/bytea.pgc
@@ -0,0 +1,109 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+
+exec sql include ../regression;
+exec sql whenever sqlerror sqlprint;
+
+static void
+dump_binary(char *buf, int len, int ind)
+{
+ int i;
+
+ printf("len=%d, ind=%d, data=0x", len, ind);
+ for (i = 0; i < len; ++i)
+ printf("%02x", 0xff & buf[i]);
+ printf("\n");
+}
+
+#define DATA_SIZE 0x200
+#define LACK_SIZE 13
+#
+int
+main(void)
+{
+exec sql begin declare section;
+ bytea send_buf[2][DATA_SIZE];
+ bytea recv_buf[2][DATA_SIZE];
+ bytea recv_vlen_buf[][DATA_SIZE];
+ bytea recv_short_buf[DATA_SIZE - LACK_SIZE];
+ int ind[2];
+exec sql end declare section;
+ int i, j, c;
+
+#define init() { \
+ for (i = 0; i < 2; ++i) \
+ { \
+ memset(recv_buf[i].arr, 0x0, sizeof(recv_buf[i].arr)); \
+ recv_buf[i].len = 0; \
+ ind[i] = 0; \
+ } \
+ recv_vlen_buf = NULL, \
+ memset(recv_short_buf.arr, 0x0, sizeof(recv_short_buf.arr)); \
+} \
+while (0)
+
+
+ ECPGdebug(1, stderr);
+
+ for (i = 0; i < 2; ++i)
+ {
+ for (j = 0, c = 0xff; (c == -1 ? c = 0xff : 1), j < DATA_SIZE; ++j, --c)
+ send_buf[i].arr[j] = c;
+
+ send_buf[i].len = DATA_SIZE;
+ }
+
+ exec sql connect to REGRESSDB1;
+
+ exec sql create table if not exists test (data1 bytea, data2 bytea);
+
+ exec sql prepare ins_stmt from "insert into test values(?,?)";
+ exec sql prepare sel_stmt from "select data1,data2 from test";
+ exec sql allocate descriptor idesc;
+ exec sql allocate descriptor odesc;
+
+ /* Test for static sql statement with normal host variable, indicator */
+ init();
+ exec sql truncate test;
+ exec sql insert into test values(:send_buf[0], :send_buf[1]);
+ exec sql select data1,data2 into :recv_buf[0]:ind[0], :recv_short_buf:ind[1] from test;
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for variable length array */
+ init();
+ exec sql truncate test;
+ exec sql insert into test values(:send_buf[0], :send_buf[1]);
+ exec sql insert into test values(:send_buf[0], :send_buf[1]);
+ exec sql select data1 into :recv_vlen_buf from test;
+ dump_binary(recv_vlen_buf[0].arr, recv_vlen_buf[0].len, 0);
+ dump_binary(recv_vlen_buf[1].arr, recv_vlen_buf[1].len, 0);
+
+ /* Test for dynamic sql statement with normal host variable, indicator */
+ init();
+ exec sql truncate test;
+ exec sql execute ins_stmt using :send_buf[0], :send_buf[1];
+ exec sql execute sel_stmt into :recv_buf[0]:ind[0], :recv_short_buf:ind[1];
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for dynamic sql statement with sql descriptor */
+ init();
+ exec sql truncate test;
+ exec sql set descriptor idesc value 1 data = :send_buf[0];
+ exec sql set descriptor idesc value 2 data = :send_buf[1];
+ exec sql execute ins_stmt using sql descriptor idesc;
+ exec sql execute sel_stmt into sql descriptor odesc;
+ exec sql get descriptor odesc value 1 :recv_buf[0] = data, :ind[0] = indicator;
+ exec sql get descriptor odesc value 2 :recv_short_buf = data, :ind[1] = indicator;
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ exec sql drop table test;
+ exec sql commit;
+ exec sql disconnect;
+
+ return 0;
+}
ecpg_bytea_doc_v1_2.patchapplication/octet-stream; name=ecpg_bytea_doc_v1_2.patchDownload
diff --git a/doc/src/sgml/ecpg.sgml b/doc/src/sgml/ecpg.sgml
index fac4540..a20ac34 100644
--- a/doc/src/sgml/ecpg.sgml
+++ b/doc/src/sgml/ecpg.sgml
@@ -936,7 +936,7 @@ do
<row>
<entry><type>bytea</type></entry>
- <entry><type>char *</type></entry>
+ <entry><type>char *</type>, <type>bytea[<replaceable>n</replaceable>]</type></entry>
</row>
</tbody>
</tgroup>
@@ -1193,6 +1193,36 @@ EXEC SQL END DECLARE SECTION;
</programlisting>
</para>
</sect4>
+
+ <sect4>
+ <title id="ecpg-type-bytea">bytea</title>
+
+ <para>
+ The handling of the <type>bytea</type> type is also similar to
+ the <type>VARCHAR</type>. The definition on an array of type
+ <type>bytea</type> is converted into a named struct for every
+ variable. A declaration like:
+<programlisting>
+bytea var[180];
+</programlisting>
+ is converted into:
+<programlisting>
+struct bytea_var { int len; char arr[180]; } var;
+</programlisting>
+ The member <structfield>arr</structfield> hosts binary format
+ data. It also can handle even <literal>'\0'</literal> as part of
+ data unlike <type>VARCHAR</type>.
+ The data is converted from/to hex format and sent/received by
+ ecpglib.
+ </para>
+
+ <note>
+ <para>
+ <type>bytea</type> variable can be used only when
+ <xref linkend="guc-bytea-output"/> is set to <literal>hex</literal>.
+ </para>
+ </note>
+ </sect4>
</sect3>
<sect3 id="ecpg-variables-nonprimitive-c">
Meskes-san
Sorry to bother you, but I would be grateful if you would comment to me.
Regards
Ryo Matsumura
Show quoted text
-----Original Message-----
From: Matsumura, Ryo [mailto:matsumura.ryo@jp.fujitsu.com]
Sent: Wednesday, December 19, 2018 7:05 PM
<tsunakawa.takay@jp.fujitsu.com>
Cc: pgsql-hackers@lists.postgresql.org
Subject: RE: [PROPOSAL]a new data type 'bytea' for ECPGMeskes-san
I do think, though, we should change the debug output for
ecpg_free_params().I try to change about it. Next patch will print binary in hex-format.
I implement and attach it. Please review a new patch in this mail.
I have a question about ecpg manual when I add article for bytea.
I wonder what does the following about VARCHAR mean.35.4.4. Type Mapping
Table 35.1. Mapping Between PostgreSQL Data Types and C Variable Typescharacter(n), varchar(n), text | char[n+1], VARCHAR[n+1] [b]
<footnote>
[b] declared in ecpglib.hThere is no declaration for VARCHAR in ecpglib.h.
There is a declaration for ECPGt_varchar in ecpgtype.h, but it may be
be unusefull information for users.On the other hand, footnote for 'bool' is usefull because there is
C-definition macro in ecpglib.h.<footnote>
[c] declared in ecpglib.h if not native<ecpglib.h>
#ifndef bool
#define bool char
#endifI think, if the footnote of VARCHAR is meaningless, I remove it while I add
the article for bytea. (I didn't remove in this patch.)Regards
Ryo Matsumura
Matsumura-san,
Sorry to bother you, but I would be grateful if you would comment to me.
Sorry, I didn't know you were waiting on a reply by me.
I have a question about ecpg manual when I add article for bytea.
I wonder what does the following about VARCHAR mean.
...
I think, if the footnote of VARCHAR is meaningless, I remove it while I add
the article for bytea. (I didn't remove in this patch.)
I have no idea where the footnote comes from, but I agree that it doesn't seem
to make sense. The datatype varchar in the C code is handled by the
preprocessor and replaced by a struct definition anyway.
Feel free to remove.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! Go SF 49ers! Use Debian GNU/Linux, PostgreSQL
On Fri, Feb 01, 2019 at 10:29:11AM +0100, Michael Meskes wrote:
I have no idea where the footnote comes from, but I agree that it doesn't seem
to make sense. The datatype varchar in the C code is handled by the
preprocessor and replaced by a struct definition anyway.Feel free to remove.
Moved to next CF as the discussion moves on.
--
Michael
Meskes-san
Thank you for your comment.
I remove it and attach a new patch. Please review it.
I feel sorry for asking you to reveiw without contribution.
Regards
Ryo Matsumura
Show quoted text
-----Original Message-----
From: Michael Meskes [mailto:meskes@postgresql.org]
Sent: Friday, February 1, 2019 6:29 PM
To: Matsumura, Ryo/松村 量 <matsumura.ryo@jp.fujitsu.com>
Cc: 'Michael Meskes' <meskes@postgresql.org>; Tsunakawa, Takayuki/綱川 貴
之 <tsunakawa.takay@jp.fujitsu.com>; pgsql-hackers@lists.postgresql.org
Subject: Re: [PROPOSAL]a new data type 'bytea' for ECPGMatsumura-san,
Sorry to bother you, but I would be grateful if you would comment to me.
Sorry, I didn't know you were waiting on a reply by me.
I have a question about ecpg manual when I add article for bytea.
I wonder what does the following about VARCHAR mean.
...
I think, if the footnote of VARCHAR is meaningless, I remove it while Iadd
the article for bytea. (I didn't remove in this patch.)
I have no idea where the footnote comes from, but I agree that it doesn't seem
to make sense. The datatype varchar in the C code is handled by the
preprocessor and replaced by a struct definition anyway.Feel free to remove.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! Go SF 49ers! Use Debian GNU/Linux, PostgreSQL
Attachments:
ecpg_bytea_v1_3.patchapplication/octet-stream; name=ecpg_bytea_v1_3.patchDownload
diff --git a/doc/src/sgml/ecpg.sgml b/doc/src/sgml/ecpg.sgml
index fac4540..2f02231 100644
--- a/doc/src/sgml/ecpg.sgml
+++ b/doc/src/sgml/ecpg.sgml
@@ -906,7 +906,7 @@ do
<row>
<entry><type>character(<replaceable>n</replaceable>)</type>, <type>varchar(<replaceable>n</replaceable>)</type>, <type>text</type></entry>
- <entry><type>char[<replaceable>n</replaceable>+1]</type>, <type>VARCHAR[<replaceable>n</replaceable>+1]</type><footnote><para>declared in <filename>ecpglib.h</filename></para></footnote></entry>
+ <entry><type>char[<replaceable>n</replaceable>+1]</type>, <type>VARCHAR[<replaceable>n</replaceable>+1]</type></entry>
</row>
<row>
@@ -936,7 +936,7 @@ do
<row>
<entry><type>bytea</type></entry>
- <entry><type>char *</type></entry>
+ <entry><type>char *</type>, <type>bytea[<replaceable>n</replaceable>]</type></entry>
</row>
</tbody>
</tgroup>
@@ -1193,6 +1193,36 @@ EXEC SQL END DECLARE SECTION;
</programlisting>
</para>
</sect4>
+
+ <sect4>
+ <title id="ecpg-type-bytea">bytea</title>
+
+ <para>
+ The handling of the <type>bytea</type> type is also similar to
+ the <type>VARCHAR</type>. The definition on an array of type
+ <type>bytea</type> is converted into a named struct for every
+ variable. A declaration like:
+<programlisting>
+bytea var[180];
+</programlisting>
+ is converted into:
+<programlisting>
+struct bytea_var { int len; char arr[180]; } var;
+</programlisting>
+ The member <structfield>arr</structfield> hosts binary format
+ data. It also can handle even <literal>'\0'</literal> as part of
+ data unlike <type>VARCHAR</type>.
+ The data is converted from/to hex format and sent/received by
+ ecpglib.
+ </para>
+
+ <note>
+ <para>
+ <type>bytea</type> variable can be used only when
+ <xref linkend="guc-bytea-output"/> is set to <literal>hex</literal>.
+ </para>
+ </note>
+ </sect4>
</sect3>
<sect3 id="ecpg-variables-nonprimitive-c">
diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c
index 424b5ae..81f94cc 100644
--- a/src/interfaces/ecpg/ecpglib/data.c
+++ b/src/interfaces/ecpg/ecpglib/data.c
@@ -122,6 +122,86 @@ check_special_value(char *ptr, double *retval, char **endptr)
return false;
}
+/* imported from src/backend/utils/adt/encode.c */
+
+unsigned
+ecpg_hex_enc_len(unsigned srclen)
+{
+ return srclen << 1;
+}
+
+unsigned
+ecpg_hex_dec_len(unsigned srclen)
+{
+ return srclen >> 1;
+}
+
+static inline char
+get_hex(char c)
+{
+ static const int8 hexlookup[128] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+ int res = -1;
+
+ if (c > 0 && c < 127)
+ res = hexlookup[(unsigned char) c];
+
+ return (char) res;
+}
+
+static unsigned
+hex_decode(const char *src, unsigned len, char *dst)
+{
+ const char *s,
+ *srcend;
+ char v1,
+ v2,
+ *p;
+
+ srcend = src + len;
+ s = src;
+ p = dst;
+ while (s < srcend)
+ {
+ if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
+ {
+ s++;
+ continue;
+ }
+ v1 = get_hex(*s++) << 4;
+ if (s >= srcend)
+ return -1;
+
+ v2 = get_hex(*s++);
+ *p++ = v1 | v2;
+ }
+
+ return p - dst;
+}
+
+unsigned
+ecpg_hex_encode(const char *src, unsigned len, char *dst)
+{
+ static const char hextbl[] = "0123456789abcdef";
+ const char *end = src + len;
+
+ while (src < end)
+ {
+ *dst++ = hextbl[(*src >> 4) & 0xF];
+ *dst++ = hextbl[*src & 0xF];
+ src++;
+ }
+ return len * 2;
+}
+
bool
ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
enum ECPGttype type, enum ECPGttype ind_type,
@@ -447,6 +527,55 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
return false;
break;
+ case ECPGt_bytea:
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var + offset * act_tuple);
+ long dst_size,
+ src_size,
+ dec_size;
+
+ dst_size = ecpg_hex_enc_len(varcharsize);
+ src_size = size - 2; /* exclude backslash + 'x' */
+ dec_size = src_size < dst_size ? src_size : dst_size;
+ variable->len = hex_decode(pval + 2, dec_size, variable->arr);
+
+ if (dst_size < src_size)
+ {
+ long rcv_size = ecpg_hex_dec_len(size - 2);
+
+ /* truncation */
+ switch (ind_type)
+ {
+ case ECPGt_short:
+ case ECPGt_unsigned_short:
+ *((short *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+ case ECPGt_int:
+ case ECPGt_unsigned_int:
+ *((int *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+ case ECPGt_long:
+ case ECPGt_unsigned_long:
+ *((long *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+#ifdef HAVE_LONG_LONG_INT
+ case ECPGt_long_long:
+ case ECPGt_unsigned_long_long:
+ *((long long int *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+#endif /* HAVE_LONG_LONG_INT */
+ default:
+ break;
+ }
+ sqlca->sqlwarn[0] = sqlca->sqlwarn[1] = 'W';
+ }
+
+ pval += size;
+
+ }
+ break;
+
case ECPGt_char:
case ECPGt_unsigned_char:
case ECPGt_string:
diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c
index 5fca4dd..06594ca 100644
--- a/src/interfaces/ecpg/ecpglib/descriptor.c
+++ b/src/interfaces/ecpg/ecpglib/descriptor.c
@@ -659,11 +659,29 @@ ECPGset_desc(int lineno, const char *desc_name, int index,...)
{
case ECPGd_data:
{
- if (!ecpg_store_input(lineno, true, var, &tobeinserted, false))
+ if (var->type != ECPGt_bytea)
{
- ecpg_free(var);
- va_end(args);
- return false;
+ if (!ecpg_store_input(lineno, true, var, &tobeinserted, false))
+ {
+ ecpg_free(var);
+ va_end(args);
+ return false;
+ }
+ desc_item->is_binary = false;
+ }
+ else
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var->value);
+
+ if (!(tobeinserted = ecpg_alloc(sizeof(int) + variable->len, lineno)))
+ {
+ ecpg_free(var);
+ va_end(args);
+ return false;
+ }
+ memcpy(tobeinserted, var->value, sizeof(int) + variable->len);
+ desc_item->is_binary = true;
}
ecpg_free(desc_item->data); /* free() takes care of a
diff --git a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
index ae2dcfc..fea8103 100644
--- a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
+++ b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
@@ -40,6 +40,13 @@ struct ECPGgeneric_varchar
char arr[FLEXIBLE_ARRAY_MEMBER];
};
+/* A generic bytea type. */
+struct ECPGgeneric_bytea
+{
+ int len;
+ char arr[FLEXIBLE_ARRAY_MEMBER];
+};
+
/*
* type information cache
*/
@@ -75,6 +82,8 @@ struct statement
#endif
int nparams;
char **paramvalues;
+ int *paramlengths;
+ int *paramformats;
PGresult *results;
};
@@ -117,6 +126,8 @@ struct descriptor_item
int precision;
int scale;
int type;
+ bool is_binary;
+ int data_len;
struct descriptor_item *next;
};
@@ -205,6 +216,9 @@ struct sqlda_compat *ecpg_build_compat_sqlda(int, PGresult *, int, enum COMPAT_M
void ecpg_set_compat_sqlda(int, struct sqlda_compat **, const PGresult *, int, enum COMPAT_MODE);
struct sqlda_struct *ecpg_build_native_sqlda(int, PGresult *, int, enum COMPAT_MODE);
void ecpg_set_native_sqlda(int, struct sqlda_struct **, const PGresult *, int, enum COMPAT_MODE);
+unsigned ecpg_hex_dec_len(unsigned srclen);
+unsigned ecpg_hex_enc_len(unsigned srclen);
+unsigned ecpg_hex_encode(const char *src, unsigned len, char *dst);
/* SQLSTATE values generated or processed by ecpglib (intentionally
* not exported -- users should refer to the codes directly) */
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index 4c499fb..e0f3a63 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -804,6 +804,20 @@ ecpg_store_input(const int lineno, const bool force_indicator, const struct vari
*tobeinserted_p = mallocedval;
}
break;
+
+ case ECPGt_bytea:
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var->value);
+
+ if (!(mallocedval = (char *) ecpg_alloc(variable->len, lineno)))
+ return false;
+
+ memcpy(mallocedval, variable->arr, variable->len);
+ *tobeinserted_p = mallocedval;
+ }
+ break;
+
case ECPGt_varchar:
{
struct ECPGgeneric_varchar *variable =
@@ -1046,6 +1060,30 @@ ecpg_store_input(const int lineno, const bool force_indicator, const struct vari
return true;
}
+static void
+print_param_value(char *value, int len, int is_binary, int lineno, int nth)
+{
+ char *value_s;
+ bool malloced = false;
+
+ if (value == NULL)
+ value_s = "null";
+ else if (! is_binary)
+ value_s = value;
+ else
+ {
+ value_s = ecpg_alloc(ecpg_hex_enc_len(len), lineno);
+ ecpg_hex_encode(value, len, value_s);
+ malloced = true;
+ }
+
+ ecpg_log("ecpg_free_params on line %d: parameter %d = %s\n",
+ lineno, nth, value_s);
+
+ if (malloced)
+ ecpg_free(value_s);
+}
+
void
ecpg_free_params(struct statement *stmt, bool print)
{
@@ -1054,11 +1092,16 @@ ecpg_free_params(struct statement *stmt, bool print)
for (n = 0; n < stmt->nparams; n++)
{
if (print)
- ecpg_log("ecpg_free_params on line %d: parameter %d = %s\n", stmt->lineno, n + 1, stmt->paramvalues[n] ? stmt->paramvalues[n] : "null");
+ print_param_value(stmt->paramvalues[n], stmt->paramlengths[n],
+ stmt->paramformats[n], stmt->lineno, n + 1);
ecpg_free(stmt->paramvalues[n]);
}
ecpg_free(stmt->paramvalues);
+ ecpg_free(stmt->paramlengths);
+ ecpg_free(stmt->paramformats);
stmt->paramvalues = NULL;
+ stmt->paramlengths = NULL;
+ stmt->paramformats = NULL;
stmt->nparams = 0;
}
@@ -1125,8 +1168,13 @@ ecpg_build_params(struct statement *stmt)
{
char *tobeinserted;
int counter = 1;
+ bool binary_format;
+ int binary_length;
+
tobeinserted = NULL;
+ binary_length = 0;
+ binary_format = false;
/*
* A descriptor is a special case since it contains many variables but
@@ -1151,12 +1199,27 @@ ecpg_build_params(struct statement *stmt)
{
if (desc_item->num == desc_counter)
{
- desc_inlist.type = ECPGt_char;
+ if (!desc_item->is_binary)
+ {
+ desc_inlist.type = ECPGt_char;
+ desc_inlist.varcharsize = strlen(desc_item->data);
+ }
+ else
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (desc_item->data);
+
+ binary_length = variable->len;
+ binary_format = true;
+
+ desc_inlist.type = ECPGt_bytea;
+ desc_inlist.varcharsize = binary_length;
+ }
desc_inlist.value = desc_item->data;
desc_inlist.pointer = &(desc_item->data);
- desc_inlist.varcharsize = strlen(desc_item->data);
desc_inlist.arrsize = 1;
desc_inlist.offset = 0;
+
if (!desc_item->indicator)
{
desc_inlist.ind_type = ECPGt_NO_INDICATOR;
@@ -1298,6 +1361,15 @@ ecpg_build_params(struct statement *stmt)
{
if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, var, &tobeinserted, false))
return false;
+
+ if (var->type == ECPGt_bytea)
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var->value);
+
+ binary_length = variable->len;
+ binary_format = true;
+ }
}
/*
@@ -1351,16 +1423,32 @@ ecpg_build_params(struct statement *stmt)
else
{
char **paramvalues;
+ int *paramlengths;
+ int *paramformats;
if (!(paramvalues = (char **) ecpg_realloc(stmt->paramvalues, sizeof(char *) * (stmt->nparams + 1), stmt->lineno)))
{
ecpg_free_params(stmt, false);
return false;
}
+ if (!(paramlengths = (int *) ecpg_realloc(stmt->paramlengths, sizeof(int) * (stmt->nparams + 1), stmt->lineno)))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+ if (!(paramformats = (int *) ecpg_realloc(stmt->paramformats, sizeof(int) * (stmt->nparams + 1), stmt->lineno)))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
stmt->nparams++;
stmt->paramvalues = paramvalues;
+ stmt->paramlengths = paramlengths;
+ stmt->paramformats = paramformats;
stmt->paramvalues[stmt->nparams - 1] = tobeinserted;
+ stmt->paramlengths[stmt->nparams - 1] = binary_length;
+ stmt->paramformats[stmt->nparams - 1] = (binary_format ? 1 : 0);
/* let's see if this was an old style placeholder */
if (stmt->command[position] == '?')
@@ -1433,7 +1521,13 @@ ecpg_execute(struct statement *stmt)
ecpg_log("ecpg_execute on line %d: query: %s; with %d parameter(s) on connection %s\n", stmt->lineno, stmt->command, stmt->nparams, stmt->connection->name);
if (stmt->statement_type == ECPGst_execute)
{
- stmt->results = PQexecPrepared(stmt->connection->connection, stmt->name, stmt->nparams, (const char *const *) stmt->paramvalues, NULL, NULL, 0);
+ stmt->results = PQexecPrepared(stmt->connection->connection,
+ stmt->name,
+ stmt->nparams,
+ (const char *const *) stmt->paramvalues,
+ (const int *) stmt->paramlengths,
+ (const int *) stmt->paramformats,
+ 0);
ecpg_log("ecpg_execute on line %d: using PQexecPrepared for \"%s\"\n", stmt->lineno, stmt->command);
}
else
@@ -1445,7 +1539,12 @@ ecpg_execute(struct statement *stmt)
}
else
{
- stmt->results = PQexecParams(stmt->connection->connection, stmt->command, stmt->nparams, NULL, (const char *const *) stmt->paramvalues, NULL, NULL, 0);
+ stmt->results = PQexecParams(stmt->connection->connection,
+ stmt->command, stmt->nparams, NULL,
+ (const char *const *) stmt->paramvalues,
+ (const int *) stmt->paramlengths,
+ (const int *) stmt->paramformats,
+ 0);
ecpg_log("ecpg_execute on line %d: using PQexecParams\n", stmt->lineno);
}
}
diff --git a/src/interfaces/ecpg/ecpglib/misc.c b/src/interfaces/ecpg/ecpglib/misc.c
index ee0d3e9..02338a4 100644
--- a/src/interfaces/ecpg/ecpglib/misc.c
+++ b/src/interfaces/ecpg/ecpglib/misc.c
@@ -355,6 +355,9 @@ ECPGset_noind_null(enum ECPGttype type, void *ptr)
*(((struct ECPGgeneric_varchar *) ptr)->arr) = 0x00;
((struct ECPGgeneric_varchar *) ptr)->len = 0;
break;
+ case ECPGt_bytea:
+ ((struct ECPGgeneric_bytea *) ptr)->len = 0;
+ break;
case ECPGt_decimal:
memset((char *) ptr, 0, sizeof(decimal));
((decimal *) ptr)->sign = NUMERIC_NULL;
@@ -428,6 +431,10 @@ ECPGis_noind_null(enum ECPGttype type, const void *ptr)
if (*(((const struct ECPGgeneric_varchar *) ptr)->arr) == 0x00)
return true;
break;
+ case ECPGt_bytea:
+ if (((struct ECPGgeneric_bytea *) ptr)->len == 0)
+ return true;
+ break;
case ECPGt_decimal:
if (((const decimal *) ptr)->sign == NUMERIC_NULL)
return true;
diff --git a/src/interfaces/ecpg/ecpglib/typename.c b/src/interfaces/ecpg/ecpglib/typename.c
index a3f2817..9251450 100644
--- a/src/interfaces/ecpg/ecpglib/typename.c
+++ b/src/interfaces/ecpg/ecpglib/typename.c
@@ -48,6 +48,8 @@ ecpg_type_name(enum ECPGttype typ)
return "bool";
case ECPGt_varchar:
return "varchar";
+ case ECPGt_bytea:
+ return "bytea";
case ECPGt_char_variable:
return "char";
case ECPGt_decimal:
diff --git a/src/interfaces/ecpg/include/ecpgtype.h b/src/interfaces/ecpg/include/ecpgtype.h
index 38fb3b6..b9fc4ea 100644
--- a/src/interfaces/ecpg/include/ecpgtype.h
+++ b/src/interfaces/ecpg/include/ecpgtype.h
@@ -63,7 +63,8 @@ enum ECPGttype
ECPGt_EORT, /* End of result types. */
ECPGt_NO_INDICATOR, /* no indicator */
ECPGt_string, /* trimmed (char *) type */
- ECPGt_sqlda /* C struct descriptor */
+ ECPGt_sqlda, /* C struct descriptor */
+ ECPGt_bytea
};
/* descriptor items */
@@ -88,7 +89,7 @@ enum ECPGdtype
ECPGd_cardinality
};
-#define IS_SIMPLE_TYPE(type) (((type) >= ECPGt_char && (type) <= ECPGt_interval) || ((type) == ECPGt_string))
+#define IS_SIMPLE_TYPE(type) (((type) >= ECPGt_char && (type) <= ECPGt_interval) || ((type) == ECPGt_string) || ((type) == ECPGt_bytea))
/* we also have to handle different statement types */
enum ECPG_statement_type
diff --git a/src/interfaces/ecpg/preproc/ecpg.header b/src/interfaces/ecpg/preproc/ecpg.header
index 00143a7..0bd38bd 100644
--- a/src/interfaces/ecpg/preproc/ecpg.header
+++ b/src/interfaces/ecpg/preproc/ecpg.header
@@ -46,6 +46,7 @@ static char pacounter_buffer[sizeof(int) * CHAR_BIT * 10 / 3]; /* a rough guess
static struct this_type actual_type[STRUCT_DEPTH];
static char *actual_startline[STRUCT_DEPTH];
static int varchar_counter = 1;
+static int bytea_counter = 1;
/* temporarily store struct members while creating the data structure */
struct ECPGstruct_member *struct_member_list[STRUCT_DEPTH] = { NULL };
@@ -562,6 +563,7 @@ add_typedef(char *name, char *dimension, char *length, enum ECPGttype type_enum,
ECPGstruct_member_dup(struct_member_list[struct_level]) : NULL;
if (type_enum != ECPGt_varchar &&
+ type_enum != ECPGt_bytea &&
type_enum != ECPGt_char &&
type_enum != ECPGt_unsigned_char &&
type_enum != ECPGt_string &&
diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer
index 60e1f53..0f2b8d7 100644
--- a/src/interfaces/ecpg/preproc/ecpg.trailer
+++ b/src/interfaces/ecpg/preproc/ecpg.trailer
@@ -546,6 +546,14 @@ var_type: simple_type
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
+ else if (strcmp($1, "bytea") == 0)
+ {
+ $$.type_enum = ECPGt_bytea;
+ $$.type_str = EMPTY;
+ $$.type_dimension = mm_strdup("-1");
+ $$.type_index = mm_strdup("-1");
+ $$.type_sizeof = NULL;
+ }
else if (strcmp($1, "float") == 0)
{
$$.type_enum = ECPGt_float;
@@ -623,7 +631,7 @@ var_type: simple_type
/* this is for typedef'ed types */
struct typedefs *this = get_typedef($1);
- $$.type_str = (this->type->type_enum == ECPGt_varchar) ? EMPTY : mm_strdup(this->name);
+ $$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? EMPTY : mm_strdup(this->name);
$$.type_enum = this->type->type_enum;
$$.type_dimension = this->type->type_dimension;
$$.type_index = this->type->type_index;
@@ -834,7 +842,7 @@ variable_list: variable
{ $$ = $1; }
| variable_list ',' variable
{
- if (actual_type[struct_level].type_enum == ECPGt_varchar)
+ if (actual_type[struct_level].type_enum == ECPGt_varchar || actual_type[struct_level].type_enum == ECPGt_bytea)
$$ = cat_str(3, $1, mm_strdup(";"), $3);
else
$$ = cat_str(3, $1, mm_strdup(","), $3);
@@ -848,9 +856,10 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
char *length = $3.index2; /* length of string */
char *dim_str;
char *vcn;
+ int *varlen_type_counter;
+ char *struct_name;
adjust_array(actual_type[struct_level].type_enum, &dimension, &length, actual_type[struct_level].type_dimension, actual_type[struct_level].type_index, strlen($1), false);
-
switch (actual_type[struct_level].type_enum)
{
case ECPGt_struct:
@@ -864,10 +873,21 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
break;
case ECPGt_varchar:
+ case ECPGt_bytea:
+ if (actual_type[struct_level].type_enum == ECPGt_varchar)
+ {
+ varlen_type_counter = &varchar_counter;
+ struct_name = " struct varchar_";
+ }
+ else
+ {
+ varlen_type_counter = &bytea_counter;
+ struct_name = " struct bytea_";
+ }
if (atoi(dimension) < 0)
- type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length, varchar_counter);
+ type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length, *varlen_type_counter);
else
- type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length, varchar_counter), dimension);
+ type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length, *varlen_type_counter), dimension);
if (strcmp(dimension, "0") == 0 || abs(atoi(dimension)) == 1)
dim_str=mm_strdup("");
@@ -879,12 +899,12 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
/* make sure varchar struct name is unique by adding a unique counter to its definition */
vcn = (char *) mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
- sprintf(vcn, "%d", varchar_counter);
+ sprintf(vcn, "%d", *varlen_type_counter);
if (strcmp(dimension, "0") == 0)
- $$ = cat_str(7, make2_str(mm_strdup(" struct varchar_"), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } *"), mm_strdup($2), $4, $5);
+ $$ = cat_str(7, make2_str(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } *"), mm_strdup($2), $4, $5);
else
- $$ = cat_str(8, make2_str(mm_strdup(" struct varchar_"), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } "), mm_strdup($2), dim_str, $4, $5);
- varchar_counter++;
+ $$ = cat_str(8, make2_str(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } "), mm_strdup($2), dim_str, $4, $5);
+ (*varlen_type_counter)++;
break;
case ECPGt_char:
@@ -1350,6 +1370,7 @@ ECPGVar: SQL_VAR
break;
case ECPGt_varchar:
+ case ECPGt_bytea:
if (atoi(dimension) == -1)
type = ECPGmake_simple_type($5.type_enum, length, 0);
else
diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c
index afe9049..a9b4acf 100644
--- a/src/interfaces/ecpg/preproc/type.c
+++ b/src/interfaces/ecpg/preproc/type.c
@@ -102,7 +102,7 @@ ECPGmake_simple_type(enum ECPGttype type, char *size, int counter)
ne->size = size;
ne->u.element = NULL;
ne->struct_sizeof = NULL;
- ne->counter = counter; /* only needed for varchar */
+ ne->counter = counter; /* only needed for varchar and bytea */
return ne;
}
@@ -175,6 +175,8 @@ get_type(enum ECPGttype type)
break;
case ECPGt_varchar:
return "ECPGt_varchar";
+ case ECPGt_bytea:
+ return "ECPGt_bytea";
case ECPGt_NO_INDICATOR: /* no indicator */
return "ECPGt_NO_INDICATOR";
break;
@@ -424,6 +426,7 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type,
{
char *variable = (char *) mm_alloc(strlen(name) + ((prefix == NULL) ? 0 : strlen(prefix)) + 4);
char *offset = (char *) mm_alloc(strlen(name) + strlen("sizeof(struct varchar_)") + 1 + strlen(varcharsize) + sizeof(int) * CHAR_BIT * 10 / 3);
+ char *struct_name;
switch (type)
{
@@ -433,6 +436,7 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type,
*/
case ECPGt_varchar:
+ case ECPGt_bytea:
/*
* we have to use the pointer except for arrays with given
@@ -449,10 +453,15 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type,
* If we created a varchar structure automatically, counter is
* greater than 0.
*/
+ if (type == ECPGt_varchar)
+ struct_name = "struct varchar";
+ else
+ struct_name = "struct bytea";
+
if (counter)
- sprintf(offset, "sizeof(struct varchar_%d)", counter);
+ sprintf(offset, "sizeof(%s_%d)", struct_name, counter);
else
- sprintf(offset, "sizeof(struct varchar)");
+ sprintf(offset, "sizeof(%s)", struct_name);
break;
case ECPGt_char:
case ECPGt_unsigned_char:
diff --git a/src/interfaces/ecpg/preproc/variable.c b/src/interfaces/ecpg/preproc/variable.c
index a953498..887d479 100644
--- a/src/interfaces/ecpg/preproc/variable.c
+++ b/src/interfaces/ecpg/preproc/variable.c
@@ -560,6 +560,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
break;
case ECPGt_varchar:
+ case ECPGt_bytea:
/* pointer has to get dimension 0 */
if (pointer_len)
*dimension = mm_strdup("0");
diff --git a/src/interfaces/ecpg/test/ecpg_schedule b/src/interfaces/ecpg/test/ecpg_schedule
index 991b8cb..2478d03 100644
--- a/src/interfaces/ecpg/test/ecpg_schedule
+++ b/src/interfaces/ecpg/test/ecpg_schedule
@@ -32,6 +32,7 @@ test: preproc/whenever
test: preproc/whenever_do_continue
test: sql/array
test: sql/binary
+test: sql/bytea
test: sql/code100
test: sql/copystdout
test: sql/define
diff --git a/src/interfaces/ecpg/test/expected/sql-bytea.c b/src/interfaces/ecpg/test/expected/sql-bytea.c
new file mode 100644
index 0000000..f230d73
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-bytea.c
@@ -0,0 +1,316 @@
+/* Processed by ecpg (regression mode) */
+/* These include files are added by the preprocessor */
+#include <ecpglib.h>
+#include <ecpgerrno.h>
+#include <sqlca.h>
+/* End of automatic include section */
+#define ECPGdebug(X,Y) ECPGdebug((X)+100,(Y))
+
+#line 1 "bytea.pgc"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+
+
+#line 1 "regression.h"
+
+
+
+
+
+
+#line 6 "bytea.pgc"
+
+/* exec sql whenever sqlerror sqlprint ; */
+#line 7 "bytea.pgc"
+
+
+static void
+dump_binary(char *buf, int len, int ind)
+{
+ int i;
+
+ printf("len=%d, ind=%d, data=0x", len, ind);
+ for (i = 0; i < len; ++i)
+ printf("%02x", 0xff & buf[i]);
+ printf("\n");
+}
+
+#define DATA_SIZE 0x200
+#define LACK_SIZE 13
+#
+int
+main(void)
+{
+/* exec sql begin declare section */
+
+
+
+
+
+
+#line 27 "bytea.pgc"
+ struct bytea_1 { int len; char arr[ DATA_SIZE ]; } send_buf [ 2 ] ;
+
+#line 28 "bytea.pgc"
+ struct bytea_2 { int len; char arr[ DATA_SIZE ]; } recv_buf [ 2 ] ;
+
+#line 29 "bytea.pgc"
+ struct bytea_3 { int len; char arr[ DATA_SIZE ]; } * recv_vlen_buf ;
+
+#line 30 "bytea.pgc"
+ struct bytea_4 { int len; char arr[ DATA_SIZE - LACK_SIZE ]; } recv_short_buf ;
+
+#line 31 "bytea.pgc"
+ int ind [ 2 ] ;
+/* exec sql end declare section */
+#line 32 "bytea.pgc"
+
+ int i, j, c;
+
+#define init() { \
+ for (i = 0; i < 2; ++i) \
+ { \
+ memset(recv_buf[i].arr, 0x0, sizeof(recv_buf[i].arr)); \
+ recv_buf[i].len = 0; \
+ ind[i] = 0; \
+ } \
+ recv_vlen_buf = NULL, \
+ memset(recv_short_buf.arr, 0x0, sizeof(recv_short_buf.arr)); \
+} \
+while (0)
+
+
+ ECPGdebug(1, stderr);
+
+ for (i = 0; i < 2; ++i)
+ {
+ for (j = 0, c = 0xff; (c == -1 ? c = 0xff : 1), j < DATA_SIZE; ++j, --c)
+ send_buf[i].arr[j] = c;
+
+ send_buf[i].len = DATA_SIZE;
+ }
+
+ { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0);
+#line 58 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 58 "bytea.pgc"
+
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table if not exists test ( data1 bytea , data2 bytea )", ECPGt_EOIT, ECPGt_EORT);
+#line 60 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 60 "bytea.pgc"
+
+
+ { ECPGprepare(__LINE__, NULL, 0, "ins_stmt", "insert into test values(?,?)");
+#line 62 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 62 "bytea.pgc"
+
+ { ECPGprepare(__LINE__, NULL, 0, "sel_stmt", "select data1,data2 from test");
+#line 63 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 63 "bytea.pgc"
+
+ ECPGallocate_desc(__LINE__, "idesc");
+#line 64 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();
+#line 64 "bytea.pgc"
+
+ ECPGallocate_desc(__LINE__, "odesc");
+#line 65 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();
+#line 65 "bytea.pgc"
+
+
+ /* Test for static sql statement with normal host variable, indicator */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 69 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 69 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test values ( $1 , $2 )",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 70 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 70 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select data1 , data2 from test", ECPGt_EOIT,
+ ECPGt_bytea,&(recv_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_2),
+ ECPGt_int,&(ind[0]),(long)1,(long)1,sizeof(int),
+ ECPGt_bytea,&(recv_short_buf),(long)DATA_SIZE - LACK_SIZE,(long)1,sizeof(struct bytea_4),
+ ECPGt_int,&(ind[1]),(long)1,(long)1,sizeof(int), ECPGt_EORT);
+#line 71 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 71 "bytea.pgc"
+
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for variable length array */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 77 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 77 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test values ( $1 , $2 )",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 78 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 78 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test values ( $1 , $2 )",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 79 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 79 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select data1 from test", ECPGt_EOIT,
+ ECPGt_bytea,&(recv_vlen_buf),(long)DATA_SIZE,(long)0,sizeof(struct bytea_3),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 80 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 80 "bytea.pgc"
+
+ dump_binary(recv_vlen_buf[0].arr, recv_vlen_buf[0].len, 0);
+ dump_binary(recv_vlen_buf[1].arr, recv_vlen_buf[1].len, 0);
+
+ /* Test for dynamic sql statement with normal host variable, indicator */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 86 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 86 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "ins_stmt",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 87 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 87 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "sel_stmt", ECPGt_EOIT,
+ ECPGt_bytea,&(recv_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_2),
+ ECPGt_int,&(ind[0]),(long)1,(long)1,sizeof(int),
+ ECPGt_bytea,&(recv_short_buf),(long)DATA_SIZE - LACK_SIZE,(long)1,sizeof(struct bytea_4),
+ ECPGt_int,&(ind[1]),(long)1,(long)1,sizeof(int), ECPGt_EORT);
+#line 88 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 88 "bytea.pgc"
+
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for dynamic sql statement with sql descriptor */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 94 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 94 "bytea.pgc"
+
+ { ECPGset_desc(__LINE__, "idesc", 1,ECPGd_data,
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1), ECPGd_EODT);
+
+#line 95 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 95 "bytea.pgc"
+
+ { ECPGset_desc(__LINE__, "idesc", 2,ECPGd_data,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1), ECPGd_EODT);
+
+#line 96 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 96 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "ins_stmt",
+ ECPGt_descriptor, "idesc", 1L, 1L, 1L,
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 97 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 97 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "sel_stmt", ECPGt_EOIT,
+ ECPGt_descriptor, "odesc", 1L, 1L, 1L,
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 98 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 98 "bytea.pgc"
+
+ { ECPGget_desc(__LINE__, "odesc", 1,ECPGd_indicator,
+ ECPGt_int,&(ind[0]),(long)1,(long)1,sizeof(int), ECPGd_data,
+ ECPGt_bytea,&(recv_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_2), ECPGd_EODT);
+
+#line 99 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 99 "bytea.pgc"
+
+ { ECPGget_desc(__LINE__, "odesc", 2,ECPGd_indicator,
+ ECPGt_int,&(ind[1]),(long)1,(long)1,sizeof(int), ECPGd_data,
+ ECPGt_bytea,&(recv_short_buf),(long)DATA_SIZE - LACK_SIZE,(long)1,sizeof(struct bytea_4), ECPGd_EODT);
+
+#line 100 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 100 "bytea.pgc"
+
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table test", ECPGt_EOIT, ECPGt_EORT);
+#line 104 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 104 "bytea.pgc"
+
+ { ECPGtrans(__LINE__, NULL, "commit");
+#line 105 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 105 "bytea.pgc"
+
+ { ECPGdisconnect(__LINE__, "CURRENT");
+#line 106 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 106 "bytea.pgc"
+
+
+ return 0;
+}
diff --git a/src/interfaces/ecpg/test/expected/sql-bytea.stderr b/src/interfaces/ecpg/test/expected/sql-bytea.stderr
new file mode 100644
index 0000000..c9f071e
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-bytea.stderr
@@ -0,0 +1,150 @@
+[NO_PID]: ECPGdebug: set to 1
+[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 60: query: create table if not exists test ( data1 bytea , data2 bytea ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 60: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 60: OK: CREATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: prepare_common on line 62: name ins_stmt; query: "insert into test values($1,$2)"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: prepare_common on line 63: name sel_stmt; query: "select data1,data2 from test"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 69: query: truncate test; 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: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 70: query: insert into test values ( $1 , $2 ); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 70: using PQexecParams
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 70: parameter 1 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 70: parameter 2 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 70: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 71: query: select data1 , data2 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 71: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 71: correctly got 1 tuples with 2 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 71: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 71: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 77: query: truncate test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 77: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 77: OK: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 78: query: insert into test values ( $1 , $2 ); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 78: using PQexecParams
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 78: parameter 1 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 78: parameter 2 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 78: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 79: query: insert into test values ( $1 , $2 ); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 79: using PQexecParams
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 79: parameter 1 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 79: parameter 2 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 79: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 80: query: select data1 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 80: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 80: correctly got 2 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_store_result on line 80: allocating memory for 2 tuples
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 80: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 80: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 86: query: truncate test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 86: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 86: OK: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 87: query: insert into test values($1,$2); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 87: using PQexecPrepared for "insert into test values($1,$2)"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 87: parameter 1 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 87: parameter 2 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 87: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 88: query: select data1,data2 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 88: using PQexecPrepared for "select data1,data2 from test"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 88: correctly got 1 tuples with 2 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 88: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 88: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 94: query: truncate test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 94: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 94: OK: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 97: query: insert into test values($1,$2); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 97: using PQexecPrepared for "insert into test values($1,$2)"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 97: parameter 1 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 97: parameter 2 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 97: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 98: query: select data1,data2 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 98: using PQexecPrepared for "select data1,data2 from test"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 98: correctly got 1 tuples with 2 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 98: putting result (1 tuples) into descriptor odesc
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGget_desc: reading items for tuple 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 99: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGget_desc: reading items for tuple 2
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 100: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 104: query: drop table test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 104: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 104: OK: DROP TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGtrans on line 105: action "commit"; connection "ecpg1_regression"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: deallocate_one on line 0: name sel_stmt
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: deallocate_one on line 0: name ins_stmt
+[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/sql-bytea.stdout b/src/interfaces/ecpg/test/expected/sql-bytea.stdout
new file mode 100644
index 0000000..4c9ad4c
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-bytea.stdout
@@ -0,0 +1,8 @@
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=499, ind=512, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=499, ind=512, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=499, ind=512, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d
diff --git a/src/interfaces/ecpg/test/sql/Makefile b/src/interfaces/ecpg/test/sql/Makefile
index b7bc034..e173ed2 100644
--- a/src/interfaces/ecpg/test/sql/Makefile
+++ b/src/interfaces/ecpg/test/sql/Makefile
@@ -23,7 +23,8 @@ TESTS = array array.c \
quote quote.c \
show show.c \
twophase twophase.c \
- insupd insupd.c
+ insupd insupd.c \
+ bytea bytea.c
all: $(TESTS)
diff --git a/src/interfaces/ecpg/test/sql/bytea.pgc b/src/interfaces/ecpg/test/sql/bytea.pgc
new file mode 100644
index 0000000..b2c5c03
--- /dev/null
+++ b/src/interfaces/ecpg/test/sql/bytea.pgc
@@ -0,0 +1,109 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+
+exec sql include ../regression;
+exec sql whenever sqlerror sqlprint;
+
+static void
+dump_binary(char *buf, int len, int ind)
+{
+ int i;
+
+ printf("len=%d, ind=%d, data=0x", len, ind);
+ for (i = 0; i < len; ++i)
+ printf("%02x", 0xff & buf[i]);
+ printf("\n");
+}
+
+#define DATA_SIZE 0x200
+#define LACK_SIZE 13
+#
+int
+main(void)
+{
+exec sql begin declare section;
+ bytea send_buf[2][DATA_SIZE];
+ bytea recv_buf[2][DATA_SIZE];
+ bytea recv_vlen_buf[][DATA_SIZE];
+ bytea recv_short_buf[DATA_SIZE - LACK_SIZE];
+ int ind[2];
+exec sql end declare section;
+ int i, j, c;
+
+#define init() { \
+ for (i = 0; i < 2; ++i) \
+ { \
+ memset(recv_buf[i].arr, 0x0, sizeof(recv_buf[i].arr)); \
+ recv_buf[i].len = 0; \
+ ind[i] = 0; \
+ } \
+ recv_vlen_buf = NULL, \
+ memset(recv_short_buf.arr, 0x0, sizeof(recv_short_buf.arr)); \
+} \
+while (0)
+
+
+ ECPGdebug(1, stderr);
+
+ for (i = 0; i < 2; ++i)
+ {
+ for (j = 0, c = 0xff; (c == -1 ? c = 0xff : 1), j < DATA_SIZE; ++j, --c)
+ send_buf[i].arr[j] = c;
+
+ send_buf[i].len = DATA_SIZE;
+ }
+
+ exec sql connect to REGRESSDB1;
+
+ exec sql create table if not exists test (data1 bytea, data2 bytea);
+
+ exec sql prepare ins_stmt from "insert into test values(?,?)";
+ exec sql prepare sel_stmt from "select data1,data2 from test";
+ exec sql allocate descriptor idesc;
+ exec sql allocate descriptor odesc;
+
+ /* Test for static sql statement with normal host variable, indicator */
+ init();
+ exec sql truncate test;
+ exec sql insert into test values(:send_buf[0], :send_buf[1]);
+ exec sql select data1,data2 into :recv_buf[0]:ind[0], :recv_short_buf:ind[1] from test;
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for variable length array */
+ init();
+ exec sql truncate test;
+ exec sql insert into test values(:send_buf[0], :send_buf[1]);
+ exec sql insert into test values(:send_buf[0], :send_buf[1]);
+ exec sql select data1 into :recv_vlen_buf from test;
+ dump_binary(recv_vlen_buf[0].arr, recv_vlen_buf[0].len, 0);
+ dump_binary(recv_vlen_buf[1].arr, recv_vlen_buf[1].len, 0);
+
+ /* Test for dynamic sql statement with normal host variable, indicator */
+ init();
+ exec sql truncate test;
+ exec sql execute ins_stmt using :send_buf[0], :send_buf[1];
+ exec sql execute sel_stmt into :recv_buf[0]:ind[0], :recv_short_buf:ind[1];
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for dynamic sql statement with sql descriptor */
+ init();
+ exec sql truncate test;
+ exec sql set descriptor idesc value 1 data = :send_buf[0];
+ exec sql set descriptor idesc value 2 data = :send_buf[1];
+ exec sql execute ins_stmt using sql descriptor idesc;
+ exec sql execute sel_stmt into sql descriptor odesc;
+ exec sql get descriptor odesc value 1 :recv_buf[0] = data, :ind[0] = indicator;
+ exec sql get descriptor odesc value 2 :recv_short_buf = data, :ind[1] = indicator;
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ exec sql drop table test;
+ exec sql commit;
+ exec sql disconnect;
+
+ return 0;
+}
Matsumura-san,
I remove it and attach a new patch. Please review it.
I feel sorry for asking you to reveiw without contribution.
Don't worry. There is one thing that I don't understand right now. YOu
change ecpg_store_input() to handle the bytea data type, yet you also
change ECPGset_desc() to not use ecpg_store_input() in case of an
bytea. This looks odd to me. Can you please explain that to me?
Thanks
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Meskes-san
Thank you for your review.
There is one thing that I don't understand right now. YOu
change ecpg_store_input() to handle the bytea data type, yet you also
change ECPGset_desc() to not use ecpg_store_input() in case of an
bytea. This looks odd to me. Can you please explain that to me?
I try to explain as follows. I would like to receive your comment.
The current architecture of data copying of descriptor walks through the following path.
The important point is that it walks through two ecpg_store_input().
step 1. ECPGset_desc
Store to descriptor_item with ecpg_store_input().
step 2. ecpg_build_params(setup for tobeinserted)
Store to tobeinserted with ecpg_store_input().
step 3. ecpg_build_params(building stmt->param*)
Set tobeinserted to stmt->paramvalues.
On the other hand, the part of ecpg_build_params(building stmt->param*)
for bytea needs two information that are is_binary and binary_length.
But, data copying with ecpg_store_input() losts them.
There are two ideas to pass the information to part of ecpg_build_params(building stmt->param*).
But they are same in terms of copying data without ecpg_store_input() at least ones.
I selected Idea-1.
Idea-1.
step 1. ECPGset_desc
Set descriptor_item.is_binary.
Memcpy both bytea.length and bytea.array to descriptor_item.data.
step 2. ecpg_build_params(setup for tobeinserted)
Store bytea.array to tobeinserted with ecpg_store_input(bytea route).
Set is_binary(local) from descriptor_item.is_binary.
Set binary_length(local) from descriptor_item.data.
step 3. ecpg_build_params(building stmt->param*)
Set stmt->paramvalues from tobeinserted.
Set stmt->formats from is_binary(local).
Set stmt->lengths from binary_length(local).
Idea-2.
step 1. ECPGset_desc
Set descriptor_item.is_binary.
Set bytea.length to descriptor_item.data_len. (different!)
Set bytea.array to descriptor_item.data. (different!)
step 2. ecpg_build_params(setup for tobeinserted)
Memcpy bytea.array to tobeinserted by using alloc and memcpy whitout store_input. (different!)
Set is_binary(local) from descriptor_item.is_binary.
Set binary_length(local) from descriptor_item.data_len. (different!)
step 3. ecpg_build_params(building stmt->param*)
Set stmt->paramvalues with tobeinserted.
Set stmt->formats from is_binary(local).
Set stmt->lengths from binary_length(local).
Regards
Ryo Matsumura
Matsumura-san,
I try to explain as follows. I would like to receive your comment.
...
I'm afraid I don't really understand your explanation. Why is handling
a bytea so different from handling a varchar? I can see some
differences due to its binary nature, but I do not understand why it
needs so much special handling for stuff like its length? There is a
length field in the structure but instead of using it the data field is
used to store both, the length and the data. What am I missing?
Please keep in mind that I did not write the descriptor code, so I may
very well not see the obvious.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Meskes-san
At first, I find my mistake that the following member is not used in my patch.
Sorry...
[ecpglib_extern.h]
120 struct descriptor_item
130 int data_len;
Why is handling a bytea so different from handling a varchar?
Current architecture:
Internal expression of varchar is C-string that includes length information implicitly
because the length can be computed by strlen().
ECPGdo(ecpg_build_params) assumes that the data in descriptor is C-string encoded.
In other hand, bytea data is binary that doesn't include any length information.
And the merit of my proposal is that bytea data can be sent to backend without
C-string encodeing overhead. They are different points from varchar.
I try to explain current data flow and my ideas.
# It may not be simple explanation...
Current data flow:
/* exec sql set descriptor idesc value 1 data = :binary_var; */
{ ECPGset_desc(__LINE__, "idesc", 1,ECPGd_data,
ECPGt_bytea,&(binary_var),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1), ECPGd_EODT);
Ecpglib stores user data into [struct descriptor_item].
Ecpglib stores only C-string encoded data to 'data' member with ecpg_store_input.
Of course, if user specifies length(*), ecpg_store_input strncpy() with the length.
(*)len member of struct varchar_1 { int len; char arr[ DATA_SIZE ]; }
# desctiptor_item has 'type' and 'length' member. But the above statement doesn't set
# these fields because I think they should be set by user explicitly as the following:
# exec sql set descriptor idesc value 1 length = 3;
# I explain later that the above user statement is ignored in result.
/* exec sql execute ins_stmt using sql descriptor idesc; */
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "ins_stmt",
ECPGt_descriptor, "idesc", 1L, 1L, 1L,
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
ecpg_build_params, the first step of ECPGdo, only strcpy() from descriptor_item.data to
tobeinserted by ecpg_store_input because the input [struct variable] for ecpg_store_input
is always set type='ECPGt_char'. descriptor_item.type and descriptor_item.length are
always not used.
# varcharsize member is set to value of strlen(descriptor_item.data) but it's ignored
# by ecpg_store_input.
In that flow, how user binary data is set to tobeinserted without C-string encoding?
The premise are the followings:
- The length information set by user must be inform upto ecpg_build_params.
- The media of the length information from ECPGset_desc to ECPGdo is only [struct descriptor_item].
My Idea-1 in the previous mail is that:
- ECPGset_desc copies whole of the struct(*) to descriptor_item.data and sets type
information to descriptor_item.is_binary.
(*)bytea_a { int len; char arr[DATA_SIZE]; }
- ecpg_build_params calls ecpg_store_input for the descriptor_item.data just as
the folowing input variable.
execute sql insert into foo values(:binary_var);
My Idea-2 is that:
- ECPGset_desc copies data to descriptor_item.data, set the length to
dscriptor_item.data_len and set type information to descriptor_item.is_binary.
- ecpg_build_params only memcpy as folowing without ecpg_store_input:
if (descriptor_item.is_binary)
memcpy(&tobeinserted, descriptor_item.data, descriptor_item.data_len)
Thank you.
Ryo Matsumura
Show quoted text
-----Original Message-----
From: Michael Meskes [mailto:meskes@postgresql.org]
Sent: Tuesday, February 12, 2019 11:06 PM
To: Matsumura, Ryo/松村 量 <matsumura.ryo@jp.fujitsu.com>
Cc: Tsunakawa, Takayuki/綱川 貴之 <tsunakawa.takay@jp.fujitsu.com>;
pgsql-hackers@lists.postgresql.org
Subject: Re: [PROPOSAL]a new data type 'bytea' for ECPGMatsumura-san,
I try to explain as follows. I would like to receive your comment.
...I'm afraid I don't really understand your explanation. Why is handling
a bytea so different from handling a varchar? I can see some
differences due to its binary nature, but I do not understand why it
needs so much special handling for stuff like its length? There is a
length field in the structure but instead of using it the data field is
used to store both, the length and the data. What am I missing?Please keep in mind that I did not write the descriptor code, so I may
very well not see the obvious.Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Matsumura-san,
Current architecture:
Internal expression of varchar is C-string that includes length
information implicitly
because the length can be computed by strlen().
ECPGdo(ecpg_build_params) assumes that the data in descriptor is C-
string encoded.In other hand, bytea data is binary that doesn't include any length
information.
And the merit of my proposal is that bytea data can be sent to
backend without
C-string encodeing overhead. They are different points from varchar.
Yes, I agree with this. But it does not explain why we cannot just add
a length parameter. And it neither explains why we need so many if
(!bytea) { thisandthat } else { somethingelse } blocks. I would prefer
the integration to be smoother. Hopefully that is possible.
My Idea-2 is that:
- ECPGset_desc copies data to descriptor_item.data, set the length to
dscriptor_item.data_len and set type information to
descriptor_item.is_binary.
- ecpg_build_params only memcpy as folowing without ecpg_store_input:if (descriptor_item.is_binary)
memcpy(&tobeinserted, descriptor_item.data,
descriptor_item.data_len)
Isn't that a better way then? This looks more smoothly to me.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Meskes-san
Yes, I agree with this. But it does not explain why we cannot just add
a length parameter. And it neither explains why we need so many if
(!bytea) { thisandthat } else { somethingelse } blocks. I would prefer
the integration to be smoother. Hopefully that is possible.
I agree that the special route is ugly, but I cannot remove them completely.
I try to implement Idea-2. In same time, I try to move if(bytea) blocks to
new function for readability.
e.g. move the following to new function set_data_attr().
if (var->type != ECPGt_bytea)
desc_item->is_binary = false;
else
{
struct ECPGgeneric_varchar *variable =
(struct ECPGgeneric_varchar *) (var->value);
desc_item->is_binary = true;
desc_item->data_len = variable->len;
}
ecpg_free(desc_item->data);
desc_item->data = (char *) tobeinserted;
Regards
Ryo Matsumura
Show quoted text
-----Original Message-----
From: Michael Meskes [mailto:meskes@postgresql.org]
Sent: Wednesday, February 13, 2019 9:09 PM
To: Matsumura, Ryo/松村 量 <matsumura.ryo@jp.fujitsu.com>
Cc: Tsunakawa, Takayuki/綱川 貴之 <tsunakawa.takay@jp.fujitsu.com>;
pgsql-hackers@lists.postgresql.org
Subject: Re: [PROPOSAL]a new data type 'bytea' for ECPGMatsumura-san,
Current architecture:
Internal expression of varchar is C-string that includes length
information implicitly
because the length can be computed by strlen().
ECPGdo(ecpg_build_params) assumes that the data in descriptor is C-
string encoded.In other hand, bytea data is binary that doesn't include any length
information.
And the merit of my proposal is that bytea data can be sent to
backend without
C-string encodeing overhead. They are different points from varchar.Yes, I agree with this. But it does not explain why we cannot just add
a length parameter. And it neither explains why we need so many if
(!bytea) { thisandthat } else { somethingelse } blocks. I would prefer
the integration to be smoother. Hopefully that is possible.My Idea-2 is that:
- ECPGset_desc copies data to descriptor_item.data, set the length to
dscriptor_item.data_len and set type information to
descriptor_item.is_binary.
- ecpg_build_params only memcpy as folowing without ecpg_store_input:if (descriptor_item.is_binary)
memcpy(&tobeinserted, descriptor_item.data,
descriptor_item.data_len)Isn't that a better way then? This looks more smoothly to me.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Matsumura-san,
I agree that the special route is ugly, but I cannot remove them
completely.
I try to implement Idea-2. In same time, I try to move if(bytea)
blocks to
new function for readability.e.g. move the following to new function set_data_attr().
I don't think this will help much, don't bother.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL
Meskes-san
I attach a new patch.
- ECPGset_desc and ecpg_build_params are changed.
- implement Idea-2.
- get if(is_binary)-block out from main flow to new functions (set_desc_attr, store_input_from_desc).
Regards
Ryo Matsumura
Attachments:
ecpg_bytea_v1_4.patchapplication/octet-stream; name=ecpg_bytea_v1_4.patchDownload
diff --git a/doc/src/sgml/ecpg.sgml b/doc/src/sgml/ecpg.sgml
index d2319c8..b7f7850 100644
--- a/doc/src/sgml/ecpg.sgml
+++ b/doc/src/sgml/ecpg.sgml
@@ -917,7 +917,7 @@ do
<row>
<entry><type>character(<replaceable>n</replaceable>)</type>, <type>varchar(<replaceable>n</replaceable>)</type>, <type>text</type></entry>
- <entry><type>char[<replaceable>n</replaceable>+1]</type>, <type>VARCHAR[<replaceable>n</replaceable>+1]</type><footnote><para>declared in <filename>ecpglib.h</filename></para></footnote></entry>
+ <entry><type>char[<replaceable>n</replaceable>+1]</type>, <type>VARCHAR[<replaceable>n</replaceable>+1]</type></entry>
</row>
<row>
@@ -947,7 +947,7 @@ do
<row>
<entry><type>bytea</type></entry>
- <entry><type>char *</type></entry>
+ <entry><type>char *</type>, <type>bytea[<replaceable>n</replaceable>]</type></entry>
</row>
</tbody>
</tgroup>
@@ -1204,6 +1204,36 @@ EXEC SQL END DECLARE SECTION;
</programlisting>
</para>
</sect4>
+
+ <sect4>
+ <title id="ecpg-type-bytea">bytea</title>
+
+ <para>
+ The handling of the <type>bytea</type> type is also similar to
+ the <type>VARCHAR</type>. The definition on an array of type
+ <type>bytea</type> is converted into a named struct for every
+ variable. A declaration like:
+<programlisting>
+bytea var[180];
+</programlisting>
+ is converted into:
+<programlisting>
+struct bytea_var { int len; char arr[180]; } var;
+</programlisting>
+ The member <structfield>arr</structfield> hosts binary format
+ data. It also can handle even <literal>'\0'</literal> as part of
+ data unlike <type>VARCHAR</type>.
+ The data is converted from/to hex format and sent/received by
+ ecpglib.
+ </para>
+
+ <note>
+ <para>
+ <type>bytea</type> variable can be used only when
+ <xref linkend="guc-bytea-output"/> is set to <literal>hex</literal>.
+ </para>
+ </note>
+ </sect4>
</sect3>
<sect3 id="ecpg-variables-nonprimitive-c">
diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c
index 424b5ae..81f94cc 100644
--- a/src/interfaces/ecpg/ecpglib/data.c
+++ b/src/interfaces/ecpg/ecpglib/data.c
@@ -122,6 +122,86 @@ check_special_value(char *ptr, double *retval, char **endptr)
return false;
}
+/* imported from src/backend/utils/adt/encode.c */
+
+unsigned
+ecpg_hex_enc_len(unsigned srclen)
+{
+ return srclen << 1;
+}
+
+unsigned
+ecpg_hex_dec_len(unsigned srclen)
+{
+ return srclen >> 1;
+}
+
+static inline char
+get_hex(char c)
+{
+ static const int8 hexlookup[128] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+ int res = -1;
+
+ if (c > 0 && c < 127)
+ res = hexlookup[(unsigned char) c];
+
+ return (char) res;
+}
+
+static unsigned
+hex_decode(const char *src, unsigned len, char *dst)
+{
+ const char *s,
+ *srcend;
+ char v1,
+ v2,
+ *p;
+
+ srcend = src + len;
+ s = src;
+ p = dst;
+ while (s < srcend)
+ {
+ if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
+ {
+ s++;
+ continue;
+ }
+ v1 = get_hex(*s++) << 4;
+ if (s >= srcend)
+ return -1;
+
+ v2 = get_hex(*s++);
+ *p++ = v1 | v2;
+ }
+
+ return p - dst;
+}
+
+unsigned
+ecpg_hex_encode(const char *src, unsigned len, char *dst)
+{
+ static const char hextbl[] = "0123456789abcdef";
+ const char *end = src + len;
+
+ while (src < end)
+ {
+ *dst++ = hextbl[(*src >> 4) & 0xF];
+ *dst++ = hextbl[*src & 0xF];
+ src++;
+ }
+ return len * 2;
+}
+
bool
ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
enum ECPGttype type, enum ECPGttype ind_type,
@@ -447,6 +527,55 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
return false;
break;
+ case ECPGt_bytea:
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var + offset * act_tuple);
+ long dst_size,
+ src_size,
+ dec_size;
+
+ dst_size = ecpg_hex_enc_len(varcharsize);
+ src_size = size - 2; /* exclude backslash + 'x' */
+ dec_size = src_size < dst_size ? src_size : dst_size;
+ variable->len = hex_decode(pval + 2, dec_size, variable->arr);
+
+ if (dst_size < src_size)
+ {
+ long rcv_size = ecpg_hex_dec_len(size - 2);
+
+ /* truncation */
+ switch (ind_type)
+ {
+ case ECPGt_short:
+ case ECPGt_unsigned_short:
+ *((short *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+ case ECPGt_int:
+ case ECPGt_unsigned_int:
+ *((int *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+ case ECPGt_long:
+ case ECPGt_unsigned_long:
+ *((long *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+#ifdef HAVE_LONG_LONG_INT
+ case ECPGt_long_long:
+ case ECPGt_unsigned_long_long:
+ *((long long int *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+#endif /* HAVE_LONG_LONG_INT */
+ default:
+ break;
+ }
+ sqlca->sqlwarn[0] = sqlca->sqlwarn[1] = 'W';
+ }
+
+ pval += size;
+
+ }
+ break;
+
case ECPGt_char:
case ECPGt_unsigned_char:
case ECPGt_string:
diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c
index d19dce2..97849d0 100644
--- a/src/interfaces/ecpg/ecpglib/descriptor.c
+++ b/src/interfaces/ecpg/ecpglib/descriptor.c
@@ -587,6 +587,28 @@ ECPGset_desc_header(int lineno, const char *desc_name, int count)
return true;
}
+static void
+set_desc_attr(struct descriptor_item *desc_item, struct variable *var,
+ char *tobeinserted)
+{
+ if (var->type != ECPGt_bytea)
+ desc_item->is_binary = false;
+
+ else
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var->value);
+
+ desc_item->is_binary = true;
+ desc_item->data_len = variable->len;
+ }
+
+ ecpg_free(desc_item->data); /* free() takes care of a
+ * potential NULL value */
+ desc_item->data = (char *) tobeinserted;
+}
+
+
bool
ECPGset_desc(int lineno, const char *desc_name, int index,...)
{
@@ -666,9 +688,7 @@ ECPGset_desc(int lineno, const char *desc_name, int index,...)
return false;
}
- ecpg_free(desc_item->data); /* free() takes care of a
- * potential NULL value */
- desc_item->data = (char *) tobeinserted;
+ set_desc_attr(desc_item, var, tobeinserted);
tobeinserted = NULL;
break;
}
diff --git a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
index f3e41d3..1ec2bf4 100644
--- a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
+++ b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
@@ -40,6 +40,13 @@ struct ECPGgeneric_varchar
char arr[FLEXIBLE_ARRAY_MEMBER];
};
+/* A generic bytea type. */
+struct ECPGgeneric_bytea
+{
+ int len;
+ char arr[FLEXIBLE_ARRAY_MEMBER];
+};
+
/*
* type information cache
*/
@@ -75,6 +82,8 @@ struct statement
#endif
int nparams;
char **paramvalues;
+ int *paramlengths;
+ int *paramformats;
PGresult *results;
};
@@ -133,6 +142,8 @@ struct descriptor_item
int precision;
int scale;
int type;
+ bool is_binary;
+ int data_len;
struct descriptor_item *next;
};
@@ -226,6 +237,9 @@ struct sqlda_compat *ecpg_build_compat_sqlda(int, PGresult *, int, enum COMPAT_M
void ecpg_set_compat_sqlda(int, struct sqlda_compat **, const PGresult *, int, enum COMPAT_MODE);
struct sqlda_struct *ecpg_build_native_sqlda(int, PGresult *, int, enum COMPAT_MODE);
void ecpg_set_native_sqlda(int, struct sqlda_struct **, const PGresult *, int, enum COMPAT_MODE);
+unsigned ecpg_hex_dec_len(unsigned srclen);
+unsigned ecpg_hex_enc_len(unsigned srclen);
+unsigned ecpg_hex_encode(const char *src, unsigned len, char *dst);
/* SQLSTATE values generated or processed by ecpglib (intentionally
* not exported -- users should refer to the codes directly) */
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index 2bdb558..6768195 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -804,6 +804,20 @@ ecpg_store_input(const int lineno, const bool force_indicator, const struct vari
*tobeinserted_p = mallocedval;
}
break;
+
+ case ECPGt_bytea:
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var->value);
+
+ if (!(mallocedval = (char *) ecpg_alloc(variable->len, lineno)))
+ return false;
+
+ memcpy(mallocedval, variable->arr, variable->len);
+ *tobeinserted_p = mallocedval;
+ }
+ break;
+
case ECPGt_varchar:
{
struct ECPGgeneric_varchar *variable =
@@ -1046,6 +1060,30 @@ ecpg_store_input(const int lineno, const bool force_indicator, const struct vari
return true;
}
+static void
+print_param_value(char *value, int len, int is_binary, int lineno, int nth)
+{
+ char *value_s;
+ bool malloced = false;
+
+ if (value == NULL)
+ value_s = "null";
+ else if (! is_binary)
+ value_s = value;
+ else
+ {
+ value_s = ecpg_alloc(ecpg_hex_enc_len(len), lineno);
+ ecpg_hex_encode(value, len, value_s);
+ malloced = true;
+ }
+
+ ecpg_log("ecpg_free_params on line %d: parameter %d = %s\n",
+ lineno, nth, value_s);
+
+ if (malloced)
+ ecpg_free(value_s);
+}
+
void
ecpg_free_params(struct statement *stmt, bool print)
{
@@ -1054,11 +1092,16 @@ ecpg_free_params(struct statement *stmt, bool print)
for (n = 0; n < stmt->nparams; n++)
{
if (print)
- ecpg_log("ecpg_free_params on line %d: parameter %d = %s\n", stmt->lineno, n + 1, stmt->paramvalues[n] ? stmt->paramvalues[n] : "null");
+ print_param_value(stmt->paramvalues[n], stmt->paramlengths[n],
+ stmt->paramformats[n], stmt->lineno, n + 1);
ecpg_free(stmt->paramvalues[n]);
}
ecpg_free(stmt->paramvalues);
+ ecpg_free(stmt->paramlengths);
+ ecpg_free(stmt->paramformats);
stmt->paramvalues = NULL;
+ stmt->paramlengths = NULL;
+ stmt->paramformats = NULL;
stmt->nparams = 0;
}
@@ -1094,6 +1137,53 @@ insert_tobeinserted(int position, int ph_len, struct statement *stmt, char *tobe
return true;
}
+static bool
+store_input_from_desc(struct statement *stmt, struct descriptor_item *desc_item,
+ char **tobeinserted)
+{
+ struct variable var;
+
+ /*
+ * In case of binary data, only allocate memory and memcpy because
+ * binary data have been already stored into desc_item->data with
+ * ecpg_store_input() at ECPGset_desc().
+ */
+ if (desc_item->is_binary)
+ {
+ if (!(*tobeinserted = ecpg_alloc(desc_item->data_len, stmt->lineno)))
+ return false;
+ memcpy(*tobeinserted, desc_item->data, desc_item->data_len);
+ return true;
+ }
+
+ var.type = ECPGt_char;
+ var.varcharsize = strlen(desc_item->data);
+ var.value = desc_item->data;
+ var.pointer = &(desc_item->data);
+ var.arrsize = 1;
+ var.offset = 0;
+
+ if (!desc_item->indicator)
+ {
+ var.ind_type = ECPGt_NO_INDICATOR;
+ var.ind_value = var.ind_pointer = NULL;
+ var.ind_varcharsize = var.ind_arrsize = var.ind_offset = 0;
+ }
+ else
+ {
+ var.ind_type = ECPGt_int;
+ var.ind_value = &(desc_item->indicator);
+ var.ind_pointer = &(var.ind_value);
+ var.ind_varcharsize = var.ind_arrsize = 1;
+ var.ind_offset = 0;
+ }
+
+ if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, &var, tobeinserted, false))
+ return false;
+
+ return true;
+}
+
/*
* ecpg_build_params
* Build statement parameters
@@ -1125,8 +1215,13 @@ ecpg_build_params(struct statement *stmt)
{
char *tobeinserted;
int counter = 1;
+ bool binary_format;
+ int binary_length;
+
tobeinserted = NULL;
+ binary_length = 0;
+ binary_format = false;
/*
* A descriptor is a special case since it contains many variables but
@@ -1138,7 +1233,6 @@ ecpg_build_params(struct statement *stmt)
* We create an additional variable list here, so the same logic
* applies.
*/
- struct variable desc_inlist;
struct descriptor *desc;
struct descriptor_item *desc_item;
@@ -1149,33 +1243,18 @@ ecpg_build_params(struct statement *stmt)
desc_counter++;
for (desc_item = desc->items; desc_item; desc_item = desc_item->next)
{
- if (desc_item->num == desc_counter)
- {
- desc_inlist.type = ECPGt_char;
- desc_inlist.value = desc_item->data;
- desc_inlist.pointer = &(desc_item->data);
- desc_inlist.varcharsize = strlen(desc_item->data);
- desc_inlist.arrsize = 1;
- desc_inlist.offset = 0;
- if (!desc_item->indicator)
- {
- desc_inlist.ind_type = ECPGt_NO_INDICATOR;
- desc_inlist.ind_value = desc_inlist.ind_pointer = NULL;
- desc_inlist.ind_varcharsize = desc_inlist.ind_arrsize = desc_inlist.ind_offset = 0;
- }
- else
- {
- desc_inlist.ind_type = ECPGt_int;
- desc_inlist.ind_value = &(desc_item->indicator);
- desc_inlist.ind_pointer = &(desc_inlist.ind_value);
- desc_inlist.ind_varcharsize = desc_inlist.ind_arrsize = 1;
- desc_inlist.ind_offset = 0;
- }
- if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, &desc_inlist, &tobeinserted, false))
- return false;
+ if (desc_item->num != desc_counter)
+ continue;
- break;
+ if (!store_input_from_desc(stmt, desc_item, &tobeinserted))
+ return false;
+
+ if (desc_item->is_binary)
+ {
+ binary_length = desc_item->data_len;
+ binary_format = true;
}
+ break;
}
if (desc->count == desc_counter)
desc_counter = 0;
@@ -1298,6 +1377,12 @@ ecpg_build_params(struct statement *stmt)
{
if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, var, &tobeinserted, false))
return false;
+
+ if (var->type == ECPGt_bytea)
+ {
+ binary_length = ((struct ECPGgeneric_varchar *) (var->value))->len;
+ binary_format = true;
+ }
}
/*
@@ -1351,16 +1436,32 @@ ecpg_build_params(struct statement *stmt)
else
{
char **paramvalues;
+ int *paramlengths;
+ int *paramformats;
if (!(paramvalues = (char **) ecpg_realloc(stmt->paramvalues, sizeof(char *) * (stmt->nparams + 1), stmt->lineno)))
{
ecpg_free_params(stmt, false);
return false;
}
+ if (!(paramlengths = (int *) ecpg_realloc(stmt->paramlengths, sizeof(int) * (stmt->nparams + 1), stmt->lineno)))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+ if (!(paramformats = (int *) ecpg_realloc(stmt->paramformats, sizeof(int) * (stmt->nparams + 1), stmt->lineno)))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
stmt->nparams++;
stmt->paramvalues = paramvalues;
+ stmt->paramlengths = paramlengths;
+ stmt->paramformats = paramformats;
stmt->paramvalues[stmt->nparams - 1] = tobeinserted;
+ stmt->paramlengths[stmt->nparams - 1] = binary_length;
+ stmt->paramformats[stmt->nparams - 1] = (binary_format ? 1 : 0);
/* let's see if this was an old style placeholder */
if (stmt->command[position] == '?')
@@ -1433,7 +1534,13 @@ ecpg_execute(struct statement *stmt)
ecpg_log("ecpg_execute on line %d: query: %s; with %d parameter(s) on connection %s\n", stmt->lineno, stmt->command, stmt->nparams, stmt->connection->name);
if (stmt->statement_type == ECPGst_execute)
{
- stmt->results = PQexecPrepared(stmt->connection->connection, stmt->name, stmt->nparams, (const char *const *) stmt->paramvalues, NULL, NULL, 0);
+ stmt->results = PQexecPrepared(stmt->connection->connection,
+ stmt->name,
+ stmt->nparams,
+ (const char *const *) stmt->paramvalues,
+ (const int *) stmt->paramlengths,
+ (const int *) stmt->paramformats,
+ 0);
ecpg_log("ecpg_execute on line %d: using PQexecPrepared for \"%s\"\n", stmt->lineno, stmt->command);
}
else
@@ -1445,7 +1552,12 @@ ecpg_execute(struct statement *stmt)
}
else
{
- stmt->results = PQexecParams(stmt->connection->connection, stmt->command, stmt->nparams, NULL, (const char *const *) stmt->paramvalues, NULL, NULL, 0);
+ stmt->results = PQexecParams(stmt->connection->connection,
+ stmt->command, stmt->nparams, NULL,
+ (const char *const *) stmt->paramvalues,
+ (const int *) stmt->paramlengths,
+ (const int *) stmt->paramformats,
+ 0);
ecpg_log("ecpg_execute on line %d: using PQexecParams\n", stmt->lineno);
}
}
diff --git a/src/interfaces/ecpg/ecpglib/misc.c b/src/interfaces/ecpg/ecpglib/misc.c
index ee0d3e9..02338a4 100644
--- a/src/interfaces/ecpg/ecpglib/misc.c
+++ b/src/interfaces/ecpg/ecpglib/misc.c
@@ -355,6 +355,9 @@ ECPGset_noind_null(enum ECPGttype type, void *ptr)
*(((struct ECPGgeneric_varchar *) ptr)->arr) = 0x00;
((struct ECPGgeneric_varchar *) ptr)->len = 0;
break;
+ case ECPGt_bytea:
+ ((struct ECPGgeneric_bytea *) ptr)->len = 0;
+ break;
case ECPGt_decimal:
memset((char *) ptr, 0, sizeof(decimal));
((decimal *) ptr)->sign = NUMERIC_NULL;
@@ -428,6 +431,10 @@ ECPGis_noind_null(enum ECPGttype type, const void *ptr)
if (*(((const struct ECPGgeneric_varchar *) ptr)->arr) == 0x00)
return true;
break;
+ case ECPGt_bytea:
+ if (((struct ECPGgeneric_bytea *) ptr)->len == 0)
+ return true;
+ break;
case ECPGt_decimal:
if (((const decimal *) ptr)->sign == NUMERIC_NULL)
return true;
diff --git a/src/interfaces/ecpg/ecpglib/typename.c b/src/interfaces/ecpg/ecpglib/typename.c
index a3f2817..9251450 100644
--- a/src/interfaces/ecpg/ecpglib/typename.c
+++ b/src/interfaces/ecpg/ecpglib/typename.c
@@ -48,6 +48,8 @@ ecpg_type_name(enum ECPGttype typ)
return "bool";
case ECPGt_varchar:
return "varchar";
+ case ECPGt_bytea:
+ return "bytea";
case ECPGt_char_variable:
return "char";
case ECPGt_decimal:
diff --git a/src/interfaces/ecpg/include/ecpgtype.h b/src/interfaces/ecpg/include/ecpgtype.h
index 9b1f3a8..4a7e8e7 100644
--- a/src/interfaces/ecpg/include/ecpgtype.h
+++ b/src/interfaces/ecpg/include/ecpgtype.h
@@ -63,7 +63,8 @@ enum ECPGttype
ECPGt_EORT, /* End of result types. */
ECPGt_NO_INDICATOR, /* no indicator */
ECPGt_string, /* trimmed (char *) type */
- ECPGt_sqlda /* C struct descriptor */
+ ECPGt_sqlda, /* C struct descriptor */
+ ECPGt_bytea
};
/* descriptor items */
@@ -88,7 +89,7 @@ enum ECPGdtype
ECPGd_cardinality
};
-#define IS_SIMPLE_TYPE(type) (((type) >= ECPGt_char && (type) <= ECPGt_interval) || ((type) == ECPGt_string))
+#define IS_SIMPLE_TYPE(type) (((type) >= ECPGt_char && (type) <= ECPGt_interval) || ((type) == ECPGt_string) || ((type) == ECPGt_bytea))
/* we also have to handle different statement types */
enum ECPG_statement_type
diff --git a/src/interfaces/ecpg/preproc/ecpg.header b/src/interfaces/ecpg/preproc/ecpg.header
index a6f870d..366dc23 100644
--- a/src/interfaces/ecpg/preproc/ecpg.header
+++ b/src/interfaces/ecpg/preproc/ecpg.header
@@ -47,6 +47,7 @@ static char pacounter_buffer[sizeof(int) * CHAR_BIT * 10 / 3]; /* a rough guess
static struct this_type actual_type[STRUCT_DEPTH];
static char *actual_startline[STRUCT_DEPTH];
static int varchar_counter = 1;
+static int bytea_counter = 1;
/* temporarily store struct members while creating the data structure */
struct ECPGstruct_member *struct_member_list[STRUCT_DEPTH] = { NULL };
@@ -563,6 +564,7 @@ add_typedef(char *name, char *dimension, char *length, enum ECPGttype type_enum,
ECPGstruct_member_dup(struct_member_list[struct_level]) : NULL;
if (type_enum != ECPGt_varchar &&
+ type_enum != ECPGt_bytea &&
type_enum != ECPGt_char &&
type_enum != ECPGt_unsigned_char &&
type_enum != ECPGt_string &&
diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer
index 6755d4a..88b27a2 100644
--- a/src/interfaces/ecpg/preproc/ecpg.trailer
+++ b/src/interfaces/ecpg/preproc/ecpg.trailer
@@ -580,6 +580,14 @@ var_type: simple_type
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
+ else if (strcmp($1, "bytea") == 0)
+ {
+ $$.type_enum = ECPGt_bytea;
+ $$.type_str = EMPTY;
+ $$.type_dimension = mm_strdup("-1");
+ $$.type_index = mm_strdup("-1");
+ $$.type_sizeof = NULL;
+ }
else if (strcmp($1, "float") == 0)
{
$$.type_enum = ECPGt_float;
@@ -657,7 +665,7 @@ var_type: simple_type
/* this is for typedef'ed types */
struct typedefs *this = get_typedef($1);
- $$.type_str = (this->type->type_enum == ECPGt_varchar) ? EMPTY : mm_strdup(this->name);
+ $$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? EMPTY : mm_strdup(this->name);
$$.type_enum = this->type->type_enum;
$$.type_dimension = this->type->type_dimension;
$$.type_index = this->type->type_index;
@@ -868,7 +876,7 @@ variable_list: variable
{ $$ = $1; }
| variable_list ',' variable
{
- if (actual_type[struct_level].type_enum == ECPGt_varchar)
+ if (actual_type[struct_level].type_enum == ECPGt_varchar || actual_type[struct_level].type_enum == ECPGt_bytea)
$$ = cat_str(3, $1, mm_strdup(";"), $3);
else
$$ = cat_str(3, $1, mm_strdup(","), $3);
@@ -882,9 +890,10 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
char *length = $3.index2; /* length of string */
char *dim_str;
char *vcn;
+ int *varlen_type_counter;
+ char *struct_name;
adjust_array(actual_type[struct_level].type_enum, &dimension, &length, actual_type[struct_level].type_dimension, actual_type[struct_level].type_index, strlen($1), false);
-
switch (actual_type[struct_level].type_enum)
{
case ECPGt_struct:
@@ -898,10 +907,21 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
break;
case ECPGt_varchar:
+ case ECPGt_bytea:
+ if (actual_type[struct_level].type_enum == ECPGt_varchar)
+ {
+ varlen_type_counter = &varchar_counter;
+ struct_name = " struct varchar_";
+ }
+ else
+ {
+ varlen_type_counter = &bytea_counter;
+ struct_name = " struct bytea_";
+ }
if (atoi(dimension) < 0)
- type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length, varchar_counter);
+ type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length, *varlen_type_counter);
else
- type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length, varchar_counter), dimension);
+ type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length, *varlen_type_counter), dimension);
if (strcmp(dimension, "0") == 0 || abs(atoi(dimension)) == 1)
dim_str=mm_strdup("");
@@ -913,12 +933,12 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
/* make sure varchar struct name is unique by adding a unique counter to its definition */
vcn = (char *) mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
- sprintf(vcn, "%d", varchar_counter);
+ sprintf(vcn, "%d", *varlen_type_counter);
if (strcmp(dimension, "0") == 0)
- $$ = cat_str(7, make2_str(mm_strdup(" struct varchar_"), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } *"), mm_strdup($2), $4, $5);
+ $$ = cat_str(7, make2_str(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } *"), mm_strdup($2), $4, $5);
else
- $$ = cat_str(8, make2_str(mm_strdup(" struct varchar_"), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } "), mm_strdup($2), dim_str, $4, $5);
- varchar_counter++;
+ $$ = cat_str(8, make2_str(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } "), mm_strdup($2), dim_str, $4, $5);
+ (*varlen_type_counter)++;
break;
case ECPGt_char:
@@ -1384,6 +1404,7 @@ ECPGVar: SQL_VAR
break;
case ECPGt_varchar:
+ case ECPGt_bytea:
if (atoi(dimension) == -1)
type = ECPGmake_simple_type($5.type_enum, length, 0);
else
diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c
index afe9049..a9b4acf 100644
--- a/src/interfaces/ecpg/preproc/type.c
+++ b/src/interfaces/ecpg/preproc/type.c
@@ -102,7 +102,7 @@ ECPGmake_simple_type(enum ECPGttype type, char *size, int counter)
ne->size = size;
ne->u.element = NULL;
ne->struct_sizeof = NULL;
- ne->counter = counter; /* only needed for varchar */
+ ne->counter = counter; /* only needed for varchar and bytea */
return ne;
}
@@ -175,6 +175,8 @@ get_type(enum ECPGttype type)
break;
case ECPGt_varchar:
return "ECPGt_varchar";
+ case ECPGt_bytea:
+ return "ECPGt_bytea";
case ECPGt_NO_INDICATOR: /* no indicator */
return "ECPGt_NO_INDICATOR";
break;
@@ -424,6 +426,7 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type,
{
char *variable = (char *) mm_alloc(strlen(name) + ((prefix == NULL) ? 0 : strlen(prefix)) + 4);
char *offset = (char *) mm_alloc(strlen(name) + strlen("sizeof(struct varchar_)") + 1 + strlen(varcharsize) + sizeof(int) * CHAR_BIT * 10 / 3);
+ char *struct_name;
switch (type)
{
@@ -433,6 +436,7 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type,
*/
case ECPGt_varchar:
+ case ECPGt_bytea:
/*
* we have to use the pointer except for arrays with given
@@ -449,10 +453,15 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type,
* If we created a varchar structure automatically, counter is
* greater than 0.
*/
+ if (type == ECPGt_varchar)
+ struct_name = "struct varchar";
+ else
+ struct_name = "struct bytea";
+
if (counter)
- sprintf(offset, "sizeof(struct varchar_%d)", counter);
+ sprintf(offset, "sizeof(%s_%d)", struct_name, counter);
else
- sprintf(offset, "sizeof(struct varchar)");
+ sprintf(offset, "sizeof(%s)", struct_name);
break;
case ECPGt_char:
case ECPGt_unsigned_char:
diff --git a/src/interfaces/ecpg/preproc/variable.c b/src/interfaces/ecpg/preproc/variable.c
index a953498..887d479 100644
--- a/src/interfaces/ecpg/preproc/variable.c
+++ b/src/interfaces/ecpg/preproc/variable.c
@@ -560,6 +560,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
break;
case ECPGt_varchar:
+ case ECPGt_bytea:
/* pointer has to get dimension 0 */
if (pointer_len)
*dimension = mm_strdup("0");
diff --git a/src/interfaces/ecpg/test/ecpg_schedule b/src/interfaces/ecpg/test/ecpg_schedule
index 81afc5d..89b2153 100644
--- a/src/interfaces/ecpg/test/ecpg_schedule
+++ b/src/interfaces/ecpg/test/ecpg_schedule
@@ -32,6 +32,7 @@ test: preproc/whenever
test: preproc/whenever_do_continue
test: sql/array
test: sql/binary
+test: sql/bytea
test: sql/code100
test: sql/copystdout
test: sql/define
diff --git a/src/interfaces/ecpg/test/expected/sql-bytea.c b/src/interfaces/ecpg/test/expected/sql-bytea.c
new file mode 100644
index 0000000..f230d73
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-bytea.c
@@ -0,0 +1,316 @@
+/* Processed by ecpg (regression mode) */
+/* These include files are added by the preprocessor */
+#include <ecpglib.h>
+#include <ecpgerrno.h>
+#include <sqlca.h>
+/* End of automatic include section */
+#define ECPGdebug(X,Y) ECPGdebug((X)+100,(Y))
+
+#line 1 "bytea.pgc"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+
+
+#line 1 "regression.h"
+
+
+
+
+
+
+#line 6 "bytea.pgc"
+
+/* exec sql whenever sqlerror sqlprint ; */
+#line 7 "bytea.pgc"
+
+
+static void
+dump_binary(char *buf, int len, int ind)
+{
+ int i;
+
+ printf("len=%d, ind=%d, data=0x", len, ind);
+ for (i = 0; i < len; ++i)
+ printf("%02x", 0xff & buf[i]);
+ printf("\n");
+}
+
+#define DATA_SIZE 0x200
+#define LACK_SIZE 13
+#
+int
+main(void)
+{
+/* exec sql begin declare section */
+
+
+
+
+
+
+#line 27 "bytea.pgc"
+ struct bytea_1 { int len; char arr[ DATA_SIZE ]; } send_buf [ 2 ] ;
+
+#line 28 "bytea.pgc"
+ struct bytea_2 { int len; char arr[ DATA_SIZE ]; } recv_buf [ 2 ] ;
+
+#line 29 "bytea.pgc"
+ struct bytea_3 { int len; char arr[ DATA_SIZE ]; } * recv_vlen_buf ;
+
+#line 30 "bytea.pgc"
+ struct bytea_4 { int len; char arr[ DATA_SIZE - LACK_SIZE ]; } recv_short_buf ;
+
+#line 31 "bytea.pgc"
+ int ind [ 2 ] ;
+/* exec sql end declare section */
+#line 32 "bytea.pgc"
+
+ int i, j, c;
+
+#define init() { \
+ for (i = 0; i < 2; ++i) \
+ { \
+ memset(recv_buf[i].arr, 0x0, sizeof(recv_buf[i].arr)); \
+ recv_buf[i].len = 0; \
+ ind[i] = 0; \
+ } \
+ recv_vlen_buf = NULL, \
+ memset(recv_short_buf.arr, 0x0, sizeof(recv_short_buf.arr)); \
+} \
+while (0)
+
+
+ ECPGdebug(1, stderr);
+
+ for (i = 0; i < 2; ++i)
+ {
+ for (j = 0, c = 0xff; (c == -1 ? c = 0xff : 1), j < DATA_SIZE; ++j, --c)
+ send_buf[i].arr[j] = c;
+
+ send_buf[i].len = DATA_SIZE;
+ }
+
+ { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0);
+#line 58 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 58 "bytea.pgc"
+
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table if not exists test ( data1 bytea , data2 bytea )", ECPGt_EOIT, ECPGt_EORT);
+#line 60 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 60 "bytea.pgc"
+
+
+ { ECPGprepare(__LINE__, NULL, 0, "ins_stmt", "insert into test values(?,?)");
+#line 62 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 62 "bytea.pgc"
+
+ { ECPGprepare(__LINE__, NULL, 0, "sel_stmt", "select data1,data2 from test");
+#line 63 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 63 "bytea.pgc"
+
+ ECPGallocate_desc(__LINE__, "idesc");
+#line 64 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();
+#line 64 "bytea.pgc"
+
+ ECPGallocate_desc(__LINE__, "odesc");
+#line 65 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();
+#line 65 "bytea.pgc"
+
+
+ /* Test for static sql statement with normal host variable, indicator */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 69 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 69 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test values ( $1 , $2 )",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 70 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 70 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select data1 , data2 from test", ECPGt_EOIT,
+ ECPGt_bytea,&(recv_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_2),
+ ECPGt_int,&(ind[0]),(long)1,(long)1,sizeof(int),
+ ECPGt_bytea,&(recv_short_buf),(long)DATA_SIZE - LACK_SIZE,(long)1,sizeof(struct bytea_4),
+ ECPGt_int,&(ind[1]),(long)1,(long)1,sizeof(int), ECPGt_EORT);
+#line 71 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 71 "bytea.pgc"
+
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for variable length array */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 77 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 77 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test values ( $1 , $2 )",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 78 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 78 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test values ( $1 , $2 )",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 79 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 79 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select data1 from test", ECPGt_EOIT,
+ ECPGt_bytea,&(recv_vlen_buf),(long)DATA_SIZE,(long)0,sizeof(struct bytea_3),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 80 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 80 "bytea.pgc"
+
+ dump_binary(recv_vlen_buf[0].arr, recv_vlen_buf[0].len, 0);
+ dump_binary(recv_vlen_buf[1].arr, recv_vlen_buf[1].len, 0);
+
+ /* Test for dynamic sql statement with normal host variable, indicator */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 86 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 86 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "ins_stmt",
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1),
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 87 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 87 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "sel_stmt", ECPGt_EOIT,
+ ECPGt_bytea,&(recv_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_2),
+ ECPGt_int,&(ind[0]),(long)1,(long)1,sizeof(int),
+ ECPGt_bytea,&(recv_short_buf),(long)DATA_SIZE - LACK_SIZE,(long)1,sizeof(struct bytea_4),
+ ECPGt_int,&(ind[1]),(long)1,(long)1,sizeof(int), ECPGt_EORT);
+#line 88 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 88 "bytea.pgc"
+
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for dynamic sql statement with sql descriptor */
+ init();
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "truncate test", ECPGt_EOIT, ECPGt_EORT);
+#line 94 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 94 "bytea.pgc"
+
+ { ECPGset_desc(__LINE__, "idesc", 1,ECPGd_data,
+ ECPGt_bytea,&(send_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1), ECPGd_EODT);
+
+#line 95 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 95 "bytea.pgc"
+
+ { ECPGset_desc(__LINE__, "idesc", 2,ECPGd_data,
+ ECPGt_bytea,&(send_buf[1]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_1), ECPGd_EODT);
+
+#line 96 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 96 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "ins_stmt",
+ ECPGt_descriptor, "idesc", 1L, 1L, 1L,
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 97 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 97 "bytea.pgc"
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "sel_stmt", ECPGt_EOIT,
+ ECPGt_descriptor, "odesc", 1L, 1L, 1L,
+ ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 98 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 98 "bytea.pgc"
+
+ { ECPGget_desc(__LINE__, "odesc", 1,ECPGd_indicator,
+ ECPGt_int,&(ind[0]),(long)1,(long)1,sizeof(int), ECPGd_data,
+ ECPGt_bytea,&(recv_buf[0]),(long)DATA_SIZE,(long)1,sizeof(struct bytea_2), ECPGd_EODT);
+
+#line 99 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 99 "bytea.pgc"
+
+ { ECPGget_desc(__LINE__, "odesc", 2,ECPGd_indicator,
+ ECPGt_int,&(ind[1]),(long)1,(long)1,sizeof(int), ECPGd_data,
+ ECPGt_bytea,&(recv_short_buf),(long)DATA_SIZE - LACK_SIZE,(long)1,sizeof(struct bytea_4), ECPGd_EODT);
+
+#line 100 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 100 "bytea.pgc"
+
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table test", ECPGt_EOIT, ECPGt_EORT);
+#line 104 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 104 "bytea.pgc"
+
+ { ECPGtrans(__LINE__, NULL, "commit");
+#line 105 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 105 "bytea.pgc"
+
+ { ECPGdisconnect(__LINE__, "CURRENT");
+#line 106 "bytea.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 106 "bytea.pgc"
+
+
+ return 0;
+}
diff --git a/src/interfaces/ecpg/test/expected/sql-bytea.stderr b/src/interfaces/ecpg/test/expected/sql-bytea.stderr
new file mode 100644
index 0000000..c9f071e
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-bytea.stderr
@@ -0,0 +1,150 @@
+[NO_PID]: ECPGdebug: set to 1
+[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 60: query: create table if not exists test ( data1 bytea , data2 bytea ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 60: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 60: OK: CREATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: prepare_common on line 62: name ins_stmt; query: "insert into test values($1,$2)"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: prepare_common on line 63: name sel_stmt; query: "select data1,data2 from test"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 69: query: truncate test; 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: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 70: query: insert into test values ( $1 , $2 ); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 70: using PQexecParams
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 70: parameter 1 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 70: parameter 2 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 70: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 71: query: select data1 , data2 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 71: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 71: correctly got 1 tuples with 2 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 71: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 71: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 77: query: truncate test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 77: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 77: OK: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 78: query: insert into test values ( $1 , $2 ); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 78: using PQexecParams
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 78: parameter 1 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 78: parameter 2 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 78: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 79: query: insert into test values ( $1 , $2 ); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 79: using PQexecParams
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 79: parameter 1 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 79: parameter 2 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 79: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 80: query: select data1 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 80: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 80: correctly got 2 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_store_result on line 80: allocating memory for 2 tuples
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 80: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 80: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 86: query: truncate test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 86: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 86: OK: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 87: query: insert into test values($1,$2); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 87: using PQexecPrepared for "insert into test values($1,$2)"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 87: parameter 1 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 87: parameter 2 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 87: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 88: query: select data1,data2 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 88: using PQexecPrepared for "select data1,data2 from test"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 88: correctly got 1 tuples with 2 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 88: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 88: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 94: query: truncate test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 94: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 94: OK: TRUNCATE TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 97: query: insert into test values($1,$2); with 2 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 97: using PQexecPrepared for "insert into test values($1,$2)"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 97: parameter 1 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_free_params on line 97: parameter 2 = fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 97: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 98: query: select data1,data2 from test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 98: using PQexecPrepared for "select data1,data2 from test"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 98: correctly got 1 tuples with 2 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 98: putting result (1 tuples) into descriptor odesc
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGget_desc: reading items for tuple 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 99: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGget_desc: reading items for tuple 2
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 100: RESULT: \xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100 offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 104: query: drop table test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 104: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 104: OK: DROP TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGtrans on line 105: action "commit"; connection "ecpg1_regression"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: deallocate_one on line 0: name sel_stmt
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: deallocate_one on line 0: name ins_stmt
+[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/sql-bytea.stdout b/src/interfaces/ecpg/test/expected/sql-bytea.stdout
new file mode 100644
index 0000000..4c9ad4c
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/sql-bytea.stdout
@@ -0,0 +1,8 @@
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=499, ind=512, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=499, ind=512, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d
+len=512, ind=0, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
+len=499, ind=512, data=0xfffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d
diff --git a/src/interfaces/ecpg/test/sql/Makefile b/src/interfaces/ecpg/test/sql/Makefile
index c3d86ae..d7182c9 100644
--- a/src/interfaces/ecpg/test/sql/Makefile
+++ b/src/interfaces/ecpg/test/sql/Makefile
@@ -24,7 +24,9 @@ TESTS = array array.c \
show show.c \
insupd insupd.c \
twophase twophase.c \
- declare declare.c
+ insupd insupd.c \
+ declare declare.c \
+ bytea bytea.c
all: $(TESTS)
diff --git a/src/interfaces/ecpg/test/sql/bytea.pgc b/src/interfaces/ecpg/test/sql/bytea.pgc
new file mode 100644
index 0000000..b2c5c03
--- /dev/null
+++ b/src/interfaces/ecpg/test/sql/bytea.pgc
@@ -0,0 +1,109 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+
+exec sql include ../regression;
+exec sql whenever sqlerror sqlprint;
+
+static void
+dump_binary(char *buf, int len, int ind)
+{
+ int i;
+
+ printf("len=%d, ind=%d, data=0x", len, ind);
+ for (i = 0; i < len; ++i)
+ printf("%02x", 0xff & buf[i]);
+ printf("\n");
+}
+
+#define DATA_SIZE 0x200
+#define LACK_SIZE 13
+#
+int
+main(void)
+{
+exec sql begin declare section;
+ bytea send_buf[2][DATA_SIZE];
+ bytea recv_buf[2][DATA_SIZE];
+ bytea recv_vlen_buf[][DATA_SIZE];
+ bytea recv_short_buf[DATA_SIZE - LACK_SIZE];
+ int ind[2];
+exec sql end declare section;
+ int i, j, c;
+
+#define init() { \
+ for (i = 0; i < 2; ++i) \
+ { \
+ memset(recv_buf[i].arr, 0x0, sizeof(recv_buf[i].arr)); \
+ recv_buf[i].len = 0; \
+ ind[i] = 0; \
+ } \
+ recv_vlen_buf = NULL, \
+ memset(recv_short_buf.arr, 0x0, sizeof(recv_short_buf.arr)); \
+} \
+while (0)
+
+
+ ECPGdebug(1, stderr);
+
+ for (i = 0; i < 2; ++i)
+ {
+ for (j = 0, c = 0xff; (c == -1 ? c = 0xff : 1), j < DATA_SIZE; ++j, --c)
+ send_buf[i].arr[j] = c;
+
+ send_buf[i].len = DATA_SIZE;
+ }
+
+ exec sql connect to REGRESSDB1;
+
+ exec sql create table if not exists test (data1 bytea, data2 bytea);
+
+ exec sql prepare ins_stmt from "insert into test values(?,?)";
+ exec sql prepare sel_stmt from "select data1,data2 from test";
+ exec sql allocate descriptor idesc;
+ exec sql allocate descriptor odesc;
+
+ /* Test for static sql statement with normal host variable, indicator */
+ init();
+ exec sql truncate test;
+ exec sql insert into test values(:send_buf[0], :send_buf[1]);
+ exec sql select data1,data2 into :recv_buf[0]:ind[0], :recv_short_buf:ind[1] from test;
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for variable length array */
+ init();
+ exec sql truncate test;
+ exec sql insert into test values(:send_buf[0], :send_buf[1]);
+ exec sql insert into test values(:send_buf[0], :send_buf[1]);
+ exec sql select data1 into :recv_vlen_buf from test;
+ dump_binary(recv_vlen_buf[0].arr, recv_vlen_buf[0].len, 0);
+ dump_binary(recv_vlen_buf[1].arr, recv_vlen_buf[1].len, 0);
+
+ /* Test for dynamic sql statement with normal host variable, indicator */
+ init();
+ exec sql truncate test;
+ exec sql execute ins_stmt using :send_buf[0], :send_buf[1];
+ exec sql execute sel_stmt into :recv_buf[0]:ind[0], :recv_short_buf:ind[1];
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ /* Test for dynamic sql statement with sql descriptor */
+ init();
+ exec sql truncate test;
+ exec sql set descriptor idesc value 1 data = :send_buf[0];
+ exec sql set descriptor idesc value 2 data = :send_buf[1];
+ exec sql execute ins_stmt using sql descriptor idesc;
+ exec sql execute sel_stmt into sql descriptor odesc;
+ exec sql get descriptor odesc value 1 :recv_buf[0] = data, :ind[0] = indicator;
+ exec sql get descriptor odesc value 2 :recv_short_buf = data, :ind[1] = indicator;
+ dump_binary(recv_buf[0].arr, recv_buf[0].len, ind[0]);
+ dump_binary(recv_short_buf.arr, recv_short_buf.len, ind[1]);
+
+ exec sql drop table test;
+ exec sql commit;
+ exec sql disconnect;
+
+ return 0;
+}
Matsumura-san,
I attach a new patch.
...
Thank you so much.
This looks very good. Committed to HEAD.
Michael
--
Michael Meskes
Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
Meskes at (Debian|Postgresql) dot Org
Jabber: michael at xmpp dot meskes dot org
VfL Borussia! Força Barça! SF 49ers! Use Debian GNU/Linux, PostgreSQL