summaryrefslogtreecommitdiff
path: root/src/interfaces/ecpg/ecpglib
diff options
context:
space:
mode:
authorMichael Meskes2019-05-22 02:58:29 +0000
committerMichael Meskes2019-05-22 02:58:29 +0000
commita1dc6ab465986a62b308dd1bb8da316b5ed9685a (patch)
treee7ca21165238a726731a69e7fb1e50e77c51263f /src/interfaces/ecpg/ecpglib
parent5af2e976d72aa345337596cc986237c57e1146b2 (diff)
Implement PREPARE AS statement for ECPG.
Besides implementing the new statement this change fix some issues with the parsing of PREPARE and EXECUTE statements. The different forms of these statements are now all handled in a ujnified way. Author: Matsumura-san <[email protected]>
Diffstat (limited to 'src/interfaces/ecpg/ecpglib')
-rw-r--r--src/interfaces/ecpg/ecpglib/ecpglib_extern.h1
-rw-r--r--src/interfaces/ecpg/ecpglib/execute.c82
-rw-r--r--src/interfaces/ecpg/ecpglib/prepare.c54
3 files changed, 135 insertions, 2 deletions
diff --git a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
index 1ec2bf49f05..d63efd5459e 100644
--- a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
+++ b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
@@ -231,6 +231,7 @@ char *ecpg_prepared(const char *, struct connection *);
bool ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection *conn);
void ecpg_log(const char *format,...) pg_attribute_printf(1, 2);
bool ecpg_auto_prepare(int, const char *, const int, char **, const char *);
+bool ecpg_register_prepared_stmt(struct statement *);
void ecpg_init_sqlca(struct sqlca_t *sqlca);
struct sqlda_compat *ecpg_build_compat_sqlda(int, PGresult *, int, enum COMPAT_MODE);
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index 8e61339ae30..2db1b620586 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -488,6 +488,23 @@ sprintf_float_value(char *ptr, float value, const char *delim)
sprintf(ptr, "%.15g%s", value, delim);
}
+static char*
+convert_bytea_to_string(char *from_data, int from_len, int lineno)
+{
+ char *to_data;
+ int to_len = ecpg_hex_enc_len(from_len) + 4 + 1; /* backslash + 'x' + quote + quote */
+
+ to_data = ecpg_alloc(to_len, lineno);
+ if (!to_data)
+ return NULL;
+
+ strcpy(to_data, "'\\x");
+ ecpg_hex_encode(from_data, from_len, to_data + 3);
+ strcpy(to_data + 3 + ecpg_hex_enc_len(from_len), "\'");
+
+ return to_data;
+}
+
bool
ecpg_store_input(const int lineno, const bool force_indicator, const struct variable *var,
char **tobeinserted_p, bool quote)
@@ -1433,6 +1450,36 @@ ecpg_build_params(struct statement *stmt)
*/
else if (stmt->command[position] == '0')
{
+ if (stmt->statement_type == ECPGst_prepare ||
+ stmt->statement_type == ECPGst_exec_with_exprlist)
+ {
+ /* Add double quote both side for embedding statement name. */
+ char *str = ecpg_alloc(strlen(tobeinserted) + 2 + 1, stmt->lineno);
+ sprintf(str, "\"%s\"", tobeinserted);
+ ecpg_free(tobeinserted);
+ tobeinserted = str;
+ }
+ if (!insert_tobeinserted(position, 2, stmt, tobeinserted))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+ tobeinserted = NULL;
+ }
+ else if (stmt->statement_type == ECPGst_exec_with_exprlist)
+ {
+
+ if (binary_format)
+ {
+ char *p = convert_bytea_to_string(tobeinserted, binary_length, stmt->lineno);
+ if (!p)
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+ tobeinserted = p;
+ }
+
if (!insert_tobeinserted(position, 2, stmt, tobeinserted))
{
ecpg_free_params(stmt, false);
@@ -1493,8 +1540,12 @@ ecpg_build_params(struct statement *stmt)
var = var->next;
}
- /* Check if there are unmatched things left. */
- if (next_insert(stmt->command, position, stmt->questionmarks, std_strings) >= 0)
+ /*
+ * Check if there are unmatched things left.
+ * PREPARE AS has no parameter. Check other statement.
+ */
+ if (stmt->statement_type != ECPGst_prepare &&
+ next_insert(stmt->command, position, stmt->questionmarks, std_strings) >= 0)
{
ecpg_raise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS,
ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL);
@@ -1560,8 +1611,18 @@ ecpg_execute(struct statement *stmt)
(const int *) stmt->paramlengths,
(const int *) stmt->paramformats,
0);
+
ecpg_log("ecpg_execute on line %d: using PQexecParams\n", stmt->lineno);
}
+
+ if (stmt->statement_type == ECPGst_prepare)
+ {
+ if(! ecpg_register_prepared_stmt(stmt))
+ {
+ ecpg_free_params(stmt, true);
+ return false;
+ }
+ }
}
ecpg_free_params(stmt, true);
@@ -1874,6 +1935,7 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
enum ECPGttype type;
struct variable **list;
char *prepname;
+ bool is_prepared_name_set;
*stmt_out = NULL;
@@ -1975,6 +2037,7 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
return false;
}
}
+ /* name of PREPARE AS will be set in loop of inlist */
stmt->connection = con;
stmt->lineno = lineno;
@@ -2004,6 +2067,8 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
*------
*/
+ is_prepared_name_set = false;
+
list = &(stmt->inlist);
type = va_arg(args, enum ECPGttype);
@@ -2092,6 +2157,12 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
*list = var;
else
ptr->next = var;
+
+ if (!is_prepared_name_set && stmt->statement_type == ECPGst_prepare)
+ {
+ stmt->name = ecpg_strdup(var->value, lineno);
+ is_prepared_name_set = true;
+ }
}
type = va_arg(args, enum ECPGttype);
@@ -2105,6 +2176,13 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
return false;
}
+ if (!is_prepared_name_set && stmt->statement_type == ECPGst_prepare)
+ {
+ ecpg_raise(lineno, ECPG_TOO_FEW_ARGUMENTS, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, (con) ? con->name : ecpg_gettext("<empty>"));
+ ecpg_do_epilogue(stmt);
+ return false;
+ }
+
/* initialize auto_mem struct */
ecpg_clear_auto_mem();
diff --git a/src/interfaces/ecpg/ecpglib/prepare.c b/src/interfaces/ecpg/ecpglib/prepare.c
index 0ef85c9de18..83de5e88b1e 100644
--- a/src/interfaces/ecpg/ecpglib/prepare.c
+++ b/src/interfaces/ecpg/ecpglib/prepare.c
@@ -56,6 +56,60 @@ isvarchar(unsigned char c)
return false;
}
+bool
+ecpg_register_prepared_stmt(struct statement *stmt)
+{
+ struct statement *prep_stmt;
+ struct prepared_statement *this;
+ struct connection *con = NULL;
+ struct prepared_statement *prev = NULL;
+ char *real_connection_name;
+ int lineno = stmt->lineno;
+
+ real_connection_name = ecpg_get_con_name_by_declared_name(stmt->name);
+ if (real_connection_name == NULL)
+ real_connection_name = stmt->connection->name;
+
+ con = ecpg_get_connection(real_connection_name);
+ if (!ecpg_init(con, real_connection_name, stmt->lineno))
+ return false;
+
+ /* check if we already have prepared this statement */
+ this = ecpg_find_prepared_statement(stmt->name, con, &prev);
+ if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
+ return false;
+
+ /* allocate new statement */
+ this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
+ if (!this)
+ return false;
+
+ prep_stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
+ if (!stmt)
+ {
+ ecpg_free(this);
+ return false;
+ }
+ memset(prep_stmt, 0, sizeof(struct statement));
+
+ /* create statement */
+ prep_stmt->lineno = lineno;
+ prep_stmt->connection = con;
+ prep_stmt->command = ecpg_strdup(stmt->command, lineno);
+ prep_stmt->inlist = prep_stmt->outlist = NULL;
+ this->name = ecpg_strdup(stmt->name, lineno);
+ this->stmt = prep_stmt;
+ this->prepared = true;
+
+ if (con->prep_stmts == NULL)
+ this->next = NULL;
+ else
+ this->next = con->prep_stmts;
+
+ con->prep_stmts = this;
+ return true;
+}
+
static bool
replace_variables(char **text, int lineno)
{