diff --git a/src/interfaces/ecpg/ecpglib/Makefile b/src/interfaces/ecpg/ecpglib/Makefile index fbb1407..132e53a 100644 --- a/src/interfaces/ecpg/ecpglib/Makefile +++ b/src/interfaces/ecpg/ecpglib/Makefile @@ -26,7 +26,7 @@ override CFLAGS += $(PTHREAD_CFLAGS) LIBS := $(filter-out -lpgport, $(LIBS)) OBJS= execute.o typename.o descriptor.o sqlda.o data.o error.o prepare.o memory.o \ - connect.o misc.o path.o pgstrcasecmp.o \ + connect.o misc.o path.o pgstrcasecmp.o cursor.o \ $(filter snprintf.o strlcpy.o win32setlocale.o isinf.o, $(LIBOBJS)) $(WIN32RES) # thread.c is needed only for non-WIN32 implementation of path.c diff --git a/src/interfaces/ecpg/ecpglib/connect.c b/src/interfaces/ecpg/ecpglib/connect.c index c90f13d..3aa3abd 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 0000000..b4d9b53 --- /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 "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 15fd7a0..84189a4 100644 --- a/src/interfaces/ecpg/ecpglib/descriptor.c +++ b/src/interfaces/ecpg/ecpglib/descriptor.c @@ -818,6 +818,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) @@ -826,11 +827,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/error.c b/src/interfaces/ecpg/ecpglib/error.c index 656b2c4..11a1a7c 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 bd9b86b..35360b1 100644 --- a/src/interfaces/ecpg/ecpglib/execute.c +++ b/src/interfaces/ecpg/ecpglib/execute.c @@ -2031,9 +2031,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 69e9617..08fa1bb 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/extern.h b/src/interfaces/ecpg/ecpglib/extern.h index fb9b5ae..056b6aa 100644 --- a/src/interfaces/ecpg/ecpglib/extern.h +++ b/src/interfaces/ecpg/ecpglib/extern.h @@ -66,6 +66,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 { @@ -75,6 +84,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 { @@ -83,6 +98,7 @@ struct connection bool autocommit; struct ECPGtype_information_cache *cache_head; struct prepared_statement *prep_stmts; + struct cursor_statement *cursor_stmts; struct connection *next; }; @@ -165,6 +181,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/prepare.c b/src/interfaces/ecpg/ecpglib/prepare.c index 983b242..140b0b7 100644 --- a/src/interfaces/ecpg/ecpglib/prepare.c +++ b/src/interfaces/ecpg/ecpglib/prepare.c @@ -26,9 +26,11 @@ static int nextStmtID = 1; static const int stmtCacheNBuckets = 2039; /* # buckets - a prime # */ static const int stmtCacheEntPerBucket = 8; /* # entries/bucket */ static stmtCacheEntry stmtCacheEntries[16384] = {{0, {0}, 0, 0, 0}}; +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) @@ -117,6 +119,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; @@ -163,11 +166,22 @@ ECPGprepare(int lineno, const char *connection_name, const bool questionmarks, c 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); - if (!ecpg_init(con, connection_name, lineno)) + 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, real_connection_name, lineno)) return false; /* check if we already have prepared this statement */ @@ -255,9 +269,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; @@ -305,8 +329,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)); } /* @@ -513,3 +550,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 36b15b7..fae9440 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 c32df6c..8c91212 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 7cc47e9..7512398 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