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..ddbc08c 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 */ @@ -684,12 +686,15 @@ ECPGdisconnect(int lineno, const char *connection_name) if (strcmp(connection_name, "ALL") == 0) { + struct connection *f = NULL; + ecpg_init_sqlca(sqlca); for (con = all_connections; con;) { - struct connection *f = con; + f = con; con = con->next; + ecpg_release_declared_statement(f->name); ecpg_finish(f); } } @@ -705,7 +710,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..4391ec3 --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/cursor.c @@ -0,0 +1,260 @@ +/* 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 (false == find_cursor(cursor_name, con)) + { + 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 + */ +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) + return ; + + con = ecpg_get_connection(connection_name); + if (!con) + { + return; + } + + if (find_cursor(cursor_name, con)) + { + /* + * Should never goto here, because ECPG has checked the duplication of + * the cursor in pre-compile stage. + */ + 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: Find whether the cursor exists in the connection or not + * 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; +} \ No newline at end of file diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c index 15fd7a0..858be01 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,13 +827,20 @@ ECPGdescribe(int line, int compat, bool input, const char *connection_name, cons return ret; } - con = ecpg_get_connection(connection_name); - if (!con) + real_connection_name = ecpg_get_con_name_by_declared_name(stmt_name); + if (real_connection_name == NULL) { - ecpg_raise(line, ECPG_NO_CONN, ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST, - connection_name ? connection_name : ecpg_gettext("NULL")); - return ret; + /* + * 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, line)) + return false; + prep = ecpg_find_prepared_statement(stmt_name, con, NULL); if (!prep) { 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 d5a463d..1a50307 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 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 c3082be..13d978c 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); @@ -220,4 +241,4 @@ void ecpg_set_native_sqlda(int, struct sqlda_struct **, const PGresult *, int, #define ECPG_SQLSTATE_ECPG_INTERNAL_ERROR "YE000" #define ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY "YE001" -#endif /* _ECPG_LIB_EXTERN_H */ +#endif /* _ECPG_LIB_EXTERN_H */ \ No newline at end of file diff --git a/src/interfaces/ecpg/ecpglib/prepare.c b/src/interfaces/ecpg/ecpglib/prepare.c index 983b242..0a939cd 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,196 @@ 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 + * We don't allocate a node to store the declared name because the + * DECLARE STATEMENT without using AT clause will be ignored. + */ + 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 declared name referred by the cursor, + * then return the connection name used by the declared 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; + struct declared_statement *next = NULL; + + if(connection_name == NULL) + return; + + cur = g_declared_list; + while(cur) + { + next = cur->next; + if (strcmp(cur->connection_name, connection_name) == 0) + { + /* If find then release the declared node from the list */ + if(prev) + prev->next = next; + else + g_declared_list = 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; + + cur = next; + } +} \ No newline at end of file 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..2c3f348 100644 --- a/src/interfaces/ecpg/include/ecpgtype.h +++ b/src/interfaces/ecpg/include/ecpgtype.h @@ -99,8 +99,15 @@ enum ECPG_statement_type ECPGst_prepnormal }; +enum ECPG_cursor_statement_type +{ + ECPGcst_declare, + ECPGcst_open, + ECPGcst_fetch, + ECPGcst_close +}; #ifdef __cplusplus } #endif -#endif /* _ECPGTYPE_H */ +#endif /* _ECPGTYPE_H */