diff --git a/src/interfaces/ecpg/ecpglib/Makefile b/src/interfaces/ecpg/ecpglib/Makefile index 3b0f39c671..e0269e9e31 100644 --- a/src/interfaces/ecpg/ecpglib/Makefile +++ b/src/interfaces/ecpg/ecpglib/Makefile @@ -23,7 +23,7 @@ override CPPFLAGS := -I../include -I$(top_srcdir)/src/interfaces/ecpg/include \ override CFLAGS += $(PTHREAD_CFLAGS) OBJS= execute.o typename.o descriptor.o sqlda.o data.o error.o prepare.o \ - memory.o connect.o misc.o $(WIN32RES) + memory.o connect.o misc.o cursor.o $(WIN32RES) SHLIB_LINK_INTERNAL = -L../pgtypeslib -lpgtypes $(libpq_pgport_shlib) SHLIB_LINK = $(filter -lintl -lm, $(LIBS)) $(PTHREAD_LIBS) diff --git a/src/interfaces/ecpg/ecpglib/connect.c b/src/interfaces/ecpg/ecpglib/connect.c index 5f8455020e..ec01f67b61 100644 --- a/src/interfaces/ecpg/ecpglib/connect.c +++ b/src/interfaces/ecpg/ecpglib/connect.c @@ -340,6 +340,8 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p return false; } + memset(this, 0, sizeof(struct connection)); + if (dbname != NULL) { /* get the detail information from dbname */ @@ -690,6 +692,7 @@ ECPGdisconnect(int lineno, const char *connection_name) struct connection *f = con; con = con->next; + ecpg_release_declared_statement(f->name); ecpg_finish(f); } } @@ -705,7 +708,10 @@ ECPGdisconnect(int lineno, const char *connection_name) return false; } else + { + ecpg_release_declared_statement(connection_name); ecpg_finish(con); + } } #ifdef ENABLE_THREAD_SAFETY diff --git a/src/interfaces/ecpg/ecpglib/cursor.c b/src/interfaces/ecpg/ecpglib/cursor.c new file mode 100644 index 0000000000..ae67bfe8e0 --- /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 +#include +#include + +#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; +} diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c index 186f92cc3e..3c884b1621 100644 --- a/src/interfaces/ecpg/ecpglib/descriptor.c +++ b/src/interfaces/ecpg/ecpglib/descriptor.c @@ -819,6 +819,7 @@ ECPGdescribe(int line, int compat, bool input, const char *connection_name, cons struct prepared_statement *prep; PGresult *res; va_list args; + const char *real_connection_name = NULL; /* DESCRIBE INPUT is not yet supported */ if (input) @@ -827,11 +828,21 @@ ECPGdescribe(int line, int compat, bool input, const char *connection_name, cons return ret; } - con = ecpg_get_connection(connection_name); + real_connection_name = ecpg_get_con_name_by_declared_name(stmt_name); + if (real_connection_name == NULL) + { + /* + * 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; + } + + con = ecpg_get_connection(real_connection_name); if (!con) { ecpg_raise(line, ECPG_NO_CONN, ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST, - connection_name ? connection_name : ecpg_gettext("NULL")); + real_connection_name ? real_connection_name : ecpg_gettext("NULL")); return ret; } prep = ecpg_find_prepared_statement(stmt_name, con, NULL); diff --git a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h index 1c9bce1456..3115c70763 100644 --- a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h +++ b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h @@ -67,6 +67,15 @@ struct statement PGresult *results; }; +/* structure to store declared statements */ +struct declared_statement +{ + char *name; /* declared name */ + char *connection_name; + char *cursor_name; + struct declared_statement *next; +}; + /* structure to store prepared statements for a connection */ struct prepared_statement { @@ -76,6 +85,12 @@ struct prepared_statement struct prepared_statement *next; }; +struct cursor_statement +{ + char *name; /*cursor name*/ + struct cursor_statement *next; +}; + /* structure to store connections */ struct connection { @@ -84,6 +99,7 @@ struct connection bool autocommit; struct ECPGtype_information_cache *cache_head; struct prepared_statement *prep_stmts; + struct cursor_statement *cursor_stmts; struct connection *next; }; @@ -166,6 +182,11 @@ struct descriptor *ecpg_find_desc(int line, const char *name); struct prepared_statement *ecpg_find_prepared_statement(const char *, struct connection *, struct prepared_statement **); +void ecpg_update_declare_statement(const char *, const char *, const int); +char *ecpg_get_con_name_by_declared_name(const char *); +const char *ecpg_get_con_name_by_cursor_name(const char *); +void ecpg_release_declared_statement(const char *); + bool ecpg_store_result(const PGresult *results, int act_field, const struct statement *stmt, struct variable *var); bool ecpg_store_input(const int, const bool, const struct variable *, char **, bool); diff --git a/src/interfaces/ecpg/ecpglib/error.c b/src/interfaces/ecpg/ecpglib/error.c index 9c56991a1e..397aa28190 100644 --- a/src/interfaces/ecpg/ecpglib/error.c +++ b/src/interfaces/ecpg/ecpglib/error.c @@ -200,6 +200,13 @@ ecpg_raise(int line, int code, const char *sqlstate, const char *str) ecpg_gettext("could not connect to database \"%s\" on line %d"), str, line); break; + case ECPG_INVALID_CURSOR: + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), + /*------ + translator: this string will be truncated at 149 characters expanded. */ + ecpg_gettext("The cursor is invalid on line %d"),line); + break; + default: snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), /*------ diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c index 3f5034e792..24fe3bca5f 100644 --- a/src/interfaces/ecpg/ecpglib/execute.c +++ b/src/interfaces/ecpg/ecpglib/execute.c @@ -2032,9 +2032,32 @@ ECPGdo(const int lineno, const int compat, const int force_indicator, const char { va_list args; bool ret; + const char *real_connection_name = NULL; + + real_connection_name = connection_name; + + if (!query) + { + ecpg_raise(lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL); + return false; + } + + /* Handle the EXEC SQL EXECUTE... statement */ + if (ECPGst_execute == st) + { + real_connection_name = ecpg_get_con_name_by_declared_name(query); + if (real_connection_name == NULL) + { + /* + * 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; + } + } va_start(args, query); - ret = ecpg_do(lineno, compat, force_indicator, connection_name, + ret = ecpg_do(lineno, compat, force_indicator, real_connection_name, questionmarks, st, query, args); va_end(args); diff --git a/src/interfaces/ecpg/ecpglib/exports.txt b/src/interfaces/ecpg/ecpglib/exports.txt index 69e96179d5..08fa1bbf19 100644 --- a/src/interfaces/ecpg/ecpglib/exports.txt +++ b/src/interfaces/ecpg/ecpglib/exports.txt @@ -29,3 +29,7 @@ ECPGget_PGconn 26 ECPGtransactionStatus 27 ECPGset_var 28 ECPGget_var 29 +ECPGdeclare 30 +ECPGopen 31 +ECPGfetch 32 +ECPGclose 33 \ No newline at end of file diff --git a/src/interfaces/ecpg/ecpglib/prepare.c b/src/interfaces/ecpg/ecpglib/prepare.c index 2dd56b02df..85c61d23f2 100644 --- a/src/interfaces/ecpg/ecpglib/prepare.c +++ b/src/interfaces/ecpg/ecpglib/prepare.c @@ -36,10 +36,11 @@ typedef struct static int nextStmtID = 1; static stmtCacheEntry *stmtCacheEntries = NULL; +static struct declared_statement *g_declared_list; static bool deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con, struct prepared_statement *prev, struct prepared_statement *this); - +static struct declared_statement *ecpg_find_declared_statement(const char *); static bool isvarchar(unsigned char c) { @@ -128,6 +129,7 @@ prepare_common(int lineno, struct connection *con, const char *name, const char ecpg_free(this); return false; } + memset(stmt, 0, sizeof(struct statement)); /* create statement */ stmt->lineno = lineno; @@ -175,12 +177,22 @@ ECPGprepare(int lineno, const char *connection_name, const bool questionmarks, struct connection *con; struct prepared_statement *this, *prev; + const char *real_connection_name = NULL; (void) questionmarks; /* quiet the compiler */ - con = ecpg_get_connection(connection_name); + real_connection_name = ecpg_get_con_name_by_declared_name(name); + if (real_connection_name == NULL) + { + /* + * 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; + } - if (!ecpg_init(con, connection_name, lineno)) + con = ecpg_get_connection(real_connection_name); + if (!ecpg_init(con, real_connection_name, lineno)) return false; /* check if we already have prepared this statement */ @@ -273,9 +285,19 @@ ECPGdeallocate(int lineno, int c, const char *connection_name, const char *name) struct connection *con; struct prepared_statement *this, *prev; + const char *real_connection_name = NULL; - con = ecpg_get_connection(connection_name); + real_connection_name = ecpg_get_con_name_by_declared_name(name); + if (real_connection_name == NULL) + { + /* + * 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; + } + con = ecpg_get_connection(real_connection_name); if (!ecpg_init(con, connection_name, lineno)) return false; @@ -324,8 +346,21 @@ ecpg_prepared(const char *name, struct connection *con) char * ECPGprepared_statement(const char *connection_name, const char *name, int lineno) { + const char *real_connection_name = NULL; + (void) lineno; /* keep the compiler quiet */ - return ecpg_prepared(name, ecpg_get_connection(connection_name)); + + real_connection_name = ecpg_get_con_name_by_declared_name(name); + if (real_connection_name == NULL) + { + /* + * 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; + } + + return ecpg_prepared(name, ecpg_get_connection(real_connection_name)); } /* @@ -556,3 +591,200 @@ ecpg_auto_prepare(int lineno, const char *connection_name, const int compat, cha return true; } + +/* + * handle the EXEC SQL DECLARE STATEMENT + * Input: connection_name -- connection name + * name -- declared name + */ +bool +ECPGdeclare(int lineno, const char *connection_name, const char *name) +{ + struct connection *con = NULL; + struct declared_statement *p = NULL; + + if (name == NULL) + { + /* Should never go to here because ECPG pre-compiler will check it */ + return false; + } + + if (connection_name == NULL) + { + /* + * Going to here means not using AT clause in the DECLARE STATEMENT + * ECPG pre-processor allows this case. + * However, we don't allocate a node to store the declared name + * because the DECLARE STATEMENT without using AT clause will be ignored. + * The following statement such as PREPARE, EXECUTE are executed + * as usual on the current connection. + */ + return true; + } + + con = ecpg_get_connection(connection_name); + if (!ecpg_init(con, connection_name, lineno)) + return false; + + if (ecpg_find_declared_statement(name)) + { + /* Should not go to here because the pre-compiler has check the duplicate name */ + return false; + } + + /* allocate a declared_statement as a new node */ + p = (struct declared_statement *) ecpg_alloc(sizeof(struct declared_statement), lineno); + if (!p) + return false; + + memset(p, 0, sizeof(struct declared_statement)); + + ecpg_log("ECPGdeclare on line %d: declared name %s on connection: \"%s\"\n", lineno, name, connection_name); + + p->name = ecpg_strdup(name, lineno); + p->connection_name = ecpg_strdup(connection_name, lineno); + + /* Add the new node into the g_declared_list */ + if (g_declared_list != NULL) + { + p->next = g_declared_list; + g_declared_list = p; + } + else + g_declared_list = p; + + return true; +} + +/* + * Find a declared node by declared name + * Input: name -- declared name + * Return: Found -- The pointer points to the declared node + * Not found -- NULL + */ +static struct declared_statement * +ecpg_find_declared_statement(const char *name) +{ + struct declared_statement *p; + + if (name == NULL) + return NULL; + + p = g_declared_list; + while (p) + { + if (strcmp(p->name, name) == 0) + return p; + p = p->next; + } + + return NULL; +} + +/* + * Build the relationship between the declared name and cursor name + * Input: declared_name -- the name declared in the DECLARE STATEMENT + * cursor_name -- cursor name declared in the DECLARE/OPEN CURSOR statement + */ +void +ecpg_update_declare_statement(const char *declared_name, const char *cursor_name, const int lineno) +{ + struct declared_statement *p = NULL; + + if (!declared_name || !cursor_name) + return; + + /* Find the declared node by declared name */ + p = ecpg_find_declared_statement(declared_name); + if (p) + p->cursor_name = ecpg_strdup(cursor_name,lineno); +} + +/* + * Find and return the connection name referred by the declared name + * Input: declared_name -- the name declared in the DECLARE STATEMENT + * Return: Found -- The connection name + * Not found -- NULL + */ +char * +ecpg_get_con_name_by_declared_name(const char *declared_name) +{ + struct declared_statement *p; + + p = ecpg_find_declared_statement(declared_name); + if (p) + return p->connection_name; + + return NULL; +} + +/* + * Find the connection name by referring the declared statements + * cursors by using the provided cursor name + * Input: cursor_name -- the cursor name + * Return: Found -- The connection name + * Not found -- NULL + */ +const char * +ecpg_get_con_name_by_cursor_name(const char *cursor_name) +{ + struct declared_statement *p; + + if (cursor_name == NULL) + return NULL; + + p = g_declared_list; + while (p) + { + /* Search the cursor name in the declared list */ + if (p->cursor_name && (strcmp(p->cursor_name, cursor_name) == 0)) + return p->connection_name; + + p = p->next; + } + + return NULL; +} + +/* + * Release the declare node from the g_declared_list which refers the connection_name + * Input: connection_name -- connection name + */ +void +ecpg_release_declared_statement(const char *connection_name) +{ + struct declared_statement *cur = NULL; + struct declared_statement *prev = NULL; + + if (connection_name == NULL) + return; + + cur = g_declared_list; + while (cur) + { + if (strcmp(cur->connection_name, connection_name) == 0) + { + /* If find then release the declared node from the list */ + if (prev) + prev->next = cur->next; + else + g_declared_list = cur->next; + + ecpg_log("ecpg_release_declared_statement: declared name %s is released\n", cur->name); + + ecpg_free(cur->name); + ecpg_free(cur->connection_name); + ecpg_free(cur->cursor_name); + ecpg_free(cur); + + /* One connection can be used by multiple declared name, so no break here */ + } + else + prev = cur; + + if (prev) + cur = prev->next; + else + cur = g_declared_list; + } +} diff --git a/src/interfaces/ecpg/include/ecpgerrno.h b/src/interfaces/ecpg/include/ecpgerrno.h index c4bc526463..a37956aa1b 100644 --- a/src/interfaces/ecpg/include/ecpgerrno.h +++ b/src/interfaces/ecpg/include/ecpgerrno.h @@ -44,6 +44,7 @@ #define ECPG_UNKNOWN_DESCRIPTOR_ITEM -242 #define ECPG_VAR_NOT_NUMERIC -243 #define ECPG_VAR_NOT_CHAR -244 +#define ECPG_INVALID_CURSOR -245 /* finally the backend error messages, they start at 400 */ #define ECPG_PGSQL -400 diff --git a/src/interfaces/ecpg/include/ecpglib.h b/src/interfaces/ecpg/include/ecpglib.h index 8a601996d2..1edf272ada 100644 --- a/src/interfaces/ecpg/include/ecpglib.h +++ b/src/interfaces/ecpg/include/ecpglib.h @@ -55,6 +55,10 @@ bool ECPGdisconnect(int, const char *); bool ECPGprepare(int, const char *, const bool, const char *, const char *); bool ECPGdeallocate(int, int, const char *, const char *); bool ECPGdeallocate_all(int, int, const char *); +bool ECPGdeclare(int, const char *, const char *); +bool ECPGopen(const char*, const char*, const int, const int, const int, const char *, const bool, const int, const char *,...); +bool ECPGfetch(const char*, const int, const int, const int, const char *, const bool, const int, const char *,...); +bool ECPGclose(const char*, const int, const int, const int, const char *, const bool, const int, const char *,...); char *ECPGprepared_statement(const char *, const char *, int); PGconn *ECPGget_PGconn(const char *); PGTransactionStatusType ECPGtransactionStatus(const char *); diff --git a/src/interfaces/ecpg/include/ecpgtype.h b/src/interfaces/ecpg/include/ecpgtype.h index 38fb3b6eaf..9b1f3a8066 100644 --- a/src/interfaces/ecpg/include/ecpgtype.h +++ b/src/interfaces/ecpg/include/ecpgtype.h @@ -99,6 +99,13 @@ enum ECPG_statement_type ECPGst_prepnormal }; +enum ECPG_cursor_statement_type +{ + ECPGcst_declare, + ECPGcst_open, + ECPGcst_fetch, + ECPGcst_close +}; #ifdef __cplusplus } #endif