1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-11 10:01:57 +03:00

Add bytea datatype to ECPG.

So far ECPG programs had to treat binary data for bytea column as 'char' type.
But this meant converting from/to escaped format with PQunescapeBytea/
PQescapeBytea() and therefore forcing users to add unnecessary code and cost
for the conversion in runtime. By adding a dedicated datatype for bytea most of
this special handling is no longer needed.

Author: Matsumura-san ("Matsumura, Ryo" <matsumura.ryo@jp.fujitsu.com>)

Discussion: https://postgr.es/m/flat/03040DFF97E6E54E88D3BFEE5F5480F737A141F9@G01JPEXMBYT04
This commit is contained in:
Michael Meskes
2019-02-18 10:20:31 +01:00
parent 3fdc374b5d
commit 050710b369
18 changed files with 985 additions and 51 deletions

View File

@ -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);
}
}