diff options
| author | Michael Meskes | 2019-05-22 02:58:29 +0000 |
|---|---|---|
| committer | Michael Meskes | 2019-05-22 02:58:29 +0000 |
| commit | a1dc6ab465986a62b308dd1bb8da316b5ed9685a (patch) | |
| tree | e7ca21165238a726731a69e7fb1e50e77c51263f /src/interfaces/ecpg/ecpglib | |
| parent | 5af2e976d72aa345337596cc986237c57e1146b2 (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.h | 1 | ||||
| -rw-r--r-- | src/interfaces/ecpg/ecpglib/execute.c | 82 | ||||
| -rw-r--r-- | src/interfaces/ecpg/ecpglib/prepare.c | 54 |
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) { |
