diff options
author | Michael Meskes <meskes@postgresql.org> | 2019-02-16 10:55:17 +0100 |
---|---|---|
committer | Michael Meskes <meskes@postgresql.org> | 2019-02-16 11:05:54 +0100 |
commit | bd7c95f0c1a38becffceb3ea7234d57167f6d4bf (patch) | |
tree | 8ab862d314ec1e241ef0b90fc42edf3f6f5e4be8 /src/interfaces/ecpg/ecpglib/cursor.c | |
parent | 02a6a54ecd6632f974b1b4eebfb2373363431084 (diff) |
Add DECLARE STATEMENT support to ECPG.
DECLARE STATEMENT is a statement that lets users declare an identifier
pointing at a connection. This identifier will be used in other embedded
dynamic SQL statement such as PREPARE, EXECUTE, DECLARE CURSOR and so on.
When connecting to a non-default connection, the AT clause can be used in
a DECLARE STATEMENT once and is no longer needed in every dynamic SQL
statement. This makes ECPG applications easier and more efficient. Moreover,
writing code without designating connection explicitly improves portability.
Authors: Ideriha-san ("Ideriha, Takeshi" <ideriha.takeshi@jp.fujitsu.com>)
Kuroda-san ("Kuroda, Hayato" <kuroda.hayato@jp.fujitsu.com>)
Discussion: https://postgr.es/m4E72940DA2BF16479384A86D54D0988A565669DF@G01JPEXMBKW04
Diffstat (limited to 'src/interfaces/ecpg/ecpglib/cursor.c')
-rw-r--r-- | src/interfaces/ecpg/ecpglib/cursor.c | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/src/interfaces/ecpg/ecpglib/cursor.c b/src/interfaces/ecpg/ecpglib/cursor.c new file mode 100644 index 00000000000..ae67bfe8e08 --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/cursor.c @@ -0,0 +1,258 @@ +/* src/interfaces/ecpg/ecpglib/cursor.c */ + +#define POSTGRES_ECPG_INTERNAL +#include "postgres_fe.h" + +#include <ctype.h> +#include <locale.h> +#include <string.h> + +#include "ecpgtype.h" +#include "ecpglib.h" +#include "ecpgerrno.h" +#include "ecpglib_extern.h" +#include "sqlca.h" + +static void add_cursor(const int, const char *, const char *); +static void remove_cursor(const char *, struct connection *); +static bool find_cursor(const char *, const struct connection *); + +/* + * Function: Handle the EXEC SQL OPEN cursor statement: + * Input: + * cursor_name --- cursor name + * prepared_name --- prepared name + * others --- keep same as the parameters in ECPGdo() function + */ +bool +ECPGopen(const char *cursor_name,const char *prepared_name, + const int lineno, const int compat,const int force_indicator, + const char *connection_name, const bool questionmarks, + const int st, const char *query,...) +{ + va_list args; + bool status; + const char *real_connection_name = NULL; + + if (!query) + { + ecpg_raise(lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL); + return false; + } + + /* + * If the declared name is referred by the PREPARE statement then the + * prepared_name is same as declared name + */ + real_connection_name = ecpg_get_con_name_by_declared_name(prepared_name); + if (real_connection_name) + { + /* Add the cursor name into the declared node */ + ecpg_update_declare_statement(prepared_name, cursor_name, lineno); + } + else + { + /* + * If can't get the connection name by declared name then using connection name + * coming from the parameter connection_name + */ + real_connection_name = connection_name; + } + + + /* Add the cursor into the connection */ + add_cursor(lineno, cursor_name, real_connection_name); + + va_start(args, query); + + status = ecpg_do(lineno, compat, force_indicator, real_connection_name, questionmarks, st, query, args); + + va_end(args); + + return status; +} + + +/* + * Function: Handle the EXEC SQL FETCH/MOVE CURSOR statements: + * Input: + * cursor_name --- cursor name + * others --- keep same as the parameters in ECPGdo() function + */ +bool +ECPGfetch(const char *cursor_name, + const int lineno, const int compat,const int force_indicator, + const char *connection_name, const bool questionmarks, + const int st, const char *query,...) +{ + va_list args; + bool status; + const char *real_connection_name = NULL; + + if (!query) + { + ecpg_raise(lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL); + return (false); + } + + real_connection_name = ecpg_get_con_name_by_cursor_name(cursor_name); + if (real_connection_name == NULL) + { + /* + * If can't get the connection name by cursor name then using connection name + * coming from the parameter connection_name + */ + real_connection_name = connection_name; + } + + va_start(args, query); + + status = ecpg_do(lineno, compat, force_indicator, real_connection_name, questionmarks, st, query, args); + + va_end(args); + + return status; +} + + +/* + * Function: Handle the EXEC SQL CLOSE CURSOR statements: + * Input: + * cursor_name --- cursor name + * others --- keep same as the parameters in ECPGdo() function + */ +bool +ECPGclose(const char *cursor_name, + const int lineno, const int compat,const int force_indicator, + const char *connection_name, const bool questionmarks, + const int st, const char *query,...) +{ + va_list args; + bool status; + const char *real_connection_name = NULL; + struct connection *con = NULL; + + if (!query) + { + ecpg_raise(lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL); + return false; + } + + real_connection_name = ecpg_get_con_name_by_cursor_name(cursor_name); + if (real_connection_name == NULL) + { + /* + * If can't get the connection name by cursor name then using connection name + * coming from the parameter connection_name + */ + real_connection_name = connection_name; + } + + con = ecpg_get_connection(real_connection_name); + + /* check the existence of the cursor in the connection */ + if (find_cursor(cursor_name, con) == false) + { + ecpg_raise(lineno, ECPG_INVALID_CURSOR, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL); + return false; + } + + va_start(args, query); + + status = ecpg_do(lineno, compat, force_indicator, real_connection_name, questionmarks, st, query, args); + + va_end(args); + + remove_cursor(cursor_name, con); + + return status; +} + +/* + * Function: Add a cursor into the connection + * The duplication of cursor_name is checked at ecpg.trailer, + * so we don't check here. + */ +static void +add_cursor(const int lineno, const char *cursor_name, const char *connection_name) +{ + struct connection *con; + struct cursor_statement *new = NULL; + + if (!cursor_name) + { + ecpg_raise(lineno, ECPG_INVALID_CURSOR, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL); + return; + } + + con = ecpg_get_connection(connection_name); + if (!con) + { + ecpg_raise(lineno, ECPG_NO_CONN, ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST, + connection_name ? connection_name : ecpg_gettext("NULL")); + return; + } + + /* allocate a node to store the new cursor */ + new = (struct cursor_statement *)ecpg_alloc(sizeof(struct cursor_statement), lineno); + if (new) + { + new->name = ecpg_strdup(cursor_name, lineno); + new->next = con->cursor_stmts; + con->cursor_stmts = new; + } +} + +/* + * Function: Remove the cursor from the connection + */ +static void +remove_cursor(const char *cursor_name, struct connection *connection) +{ + struct cursor_statement *cur = NULL; + struct cursor_statement *prev = NULL; + + if (!connection || !cursor_name) + return; + + cur = connection->cursor_stmts; + while (cur) + { + if (strcmp(cur->name, cursor_name) == 0) + { + if (!prev) + connection->cursor_stmts = cur->next; + else + prev->next = cur->next; + + ecpg_free(cur->name); + ecpg_free(cur); + + break; + } + prev = cur; + cur = cur->next; + } +} + +/* + * Function: check the existence of the cursor in the connection + * Return: true ---Found + * false --- Not found + */ +static bool +find_cursor(const char *cursor_name, const struct connection *connection) +{ + struct cursor_statement *cur = NULL; + + if (!connection || !cursor_name) + return false; + + for (cur = connection->cursor_stmts; cur != NULL; cur = cur->next) + { + if (strcmp(cur->name, cursor_name) == 0) + return true; + } + + return false; +} |