diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c index f1898dec6a6..dc163d21fe7 100644 --- a/src/interfaces/ecpg/ecpglib/descriptor.c +++ b/src/interfaces/ecpg/ecpglib/descriptor.c @@ -482,6 +482,16 @@ ECPGget_desc(int lineno, const char *desc_name, int index,...) memset(&stmt, 0, sizeof stmt); stmt.lineno = lineno; + /* desperate try to guess something sensible */ + stmt.connection = ecpg_get_connection(NULL); + if (stmt.connection == NULL) + { + ecpg_raise(lineno, ECPG_NO_CONN, ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST, + ecpg_gettext("NULL")); + va_end(args); + return false; + } + /* Make sure we do NOT honor the locale for numeric input */ /* since the database gives the standard decimal point */ /* (see comments in execute.c) */ @@ -504,8 +514,6 @@ ECPGget_desc(int lineno, const char *desc_name, int index,...) setlocale(LC_NUMERIC, "C"); #endif - /* desperate try to guess something sensible */ - stmt.connection = ecpg_get_connection(NULL); ecpg_store_result(ECPGresult, index, &stmt, &data_var); #ifdef HAVE_USELOCALE diff --git a/src/interfaces/ecpg/ecpglib/prepare.c b/src/interfaces/ecpg/ecpglib/prepare.c index ea1146f520f..998c790c176 100644 --- a/src/interfaces/ecpg/ecpglib/prepare.c +++ b/src/interfaces/ecpg/ecpglib/prepare.c @@ -349,8 +349,12 @@ ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection *con) bool ECPGdeallocate_all(int lineno, int compat, const char *connection_name) { - return ecpg_deallocate_all_conn(lineno, compat, - ecpg_get_connection(connection_name)); + struct connection *con = ecpg_get_connection(connection_name); + + if (!ecpg_init(con, connection_name, lineno)) + return false; + + return ecpg_deallocate_all_conn(lineno, compat, con); } char * @@ -363,13 +367,15 @@ ecpg_prepared(const char *name, struct connection *con) } /* return the prepared statement */ -/* lineno is not used here, but kept in to not break API */ char * ECPGprepared_statement(const char *connection_name, const char *name, int lineno) { - (void) lineno; /* keep the compiler quiet */ + struct connection *con = ecpg_get_connection(connection_name); + + if (!ecpg_init(con, connection_name, lineno)) + return NULL; - return ecpg_prepared(name, ecpg_get_connection(connection_name)); + return ecpg_prepared(name, con); } /* @@ -466,10 +472,18 @@ ecpg_freeStmtCacheEntry(int lineno, int compat, con = ecpg_get_connection(entry->connection); - /* free the 'prepared_statement' list entry */ - this = ecpg_find_prepared_statement(entry->stmtID, con, &prev); - if (this && !deallocate_one(lineno, compat, con, prev, this)) - return -1; + /* + * If the connection is gone, the prepared_statement list it owned is + * already unreachable, so just skip that cleanup. We must still clear + * the cache slot below so it can be reused. + */ + if (con) + { + /* free the 'prepared_statement' list entry */ + this = ecpg_find_prepared_statement(entry->stmtID, con, &prev); + if (this && !deallocate_one(lineno, compat, con, prev, this)) + return -1; + } entry->stmtID[0] = '\0'; diff --git a/src/interfaces/ecpg/test/connect/.gitignore b/src/interfaces/ecpg/test/connect/.gitignore index e0639f3c8d1..02236847444 100644 --- a/src/interfaces/ecpg/test/connect/.gitignore +++ b/src/interfaces/ecpg/test/connect/.gitignore @@ -8,3 +8,5 @@ /test4.c /test5 /test5.c +/test6 +/test6.c diff --git a/src/interfaces/ecpg/test/connect/Makefile b/src/interfaces/ecpg/test/connect/Makefile index 2602d5d286f..17fa2667bf7 100644 --- a/src/interfaces/ecpg/test/connect/Makefile +++ b/src/interfaces/ecpg/test/connect/Makefile @@ -7,6 +7,7 @@ TESTS = test1 test1.c \ test2 test2.c \ test3 test3.c \ test4 test4.c \ - test5 test5.c + test5 test5.c \ + test6 test6.c all: $(TESTS) diff --git a/src/interfaces/ecpg/test/connect/test6.pgc b/src/interfaces/ecpg/test/connect/test6.pgc new file mode 100644 index 00000000000..d2c10dffb03 --- /dev/null +++ b/src/interfaces/ecpg/test/connect/test6.pgc @@ -0,0 +1,68 @@ +/* + * This test verifies that ecpg functions properly handle NULL connections + * (i.e., when a connection name doesn't exist or has been disconnected). + * Before the fix, these operations would cause a segmentation fault. + */ + +#include +#include +#include + +exec sql include ../regression; + +int +main(void) +{ +exec sql begin declare section; + int val1output = 2; + int val1 = 1; + char val2[6] = "data1"; + char *stmt1 = "SELECT * from test1 where a = $1 and b = $2"; +exec sql end declare section; + + ECPGdebug(1, stderr); + + /* Connect to the database */ + exec sql connect to REGRESSDB1 as myconn; + + /* Test 1: Try to get descriptor on a disconnected connection */ + printf("Test 1: Try to get descriptor on a disconnected connection\n"); + exec sql create table test1 (a int, b text); + exec sql insert into test1 (a,b) values (1, 'data1'); + + exec sql allocate descriptor indesc; + exec sql allocate descriptor outdesc; + + exec sql prepare foo2 from :stmt1; + + exec sql set descriptor indesc value 1 DATA = :val1; + exec sql set descriptor indesc value 2 DATA = :val2; + + exec sql execute foo2 using sql descriptor indesc into sql descriptor outdesc; + + exec sql rollback; + exec sql disconnect; + exec sql get descriptor outdesc value 1 :val1output = DATA; + printf("sqlca.sqlcode = %ld\n", sqlca.sqlcode); + + /* Test 2: Try to deallocate all on a non-existent connection */ + printf("Test 2: deallocate all with non-existent connection\n"); + exec sql at nonexistent deallocate all; + printf("sqlca.sqlcode = %ld\n", sqlca.sqlcode); + + /* Test 3: deallocate on disconnected connection */ + printf("Test 3: deallocate all on disconnected connection\n"); + exec sql deallocate all; + printf("sqlca.sqlcode = %ld\n", sqlca.sqlcode); + + /* Test 4: Use prepared statement from non-existent connection */ + printf("Test 4: Use prepared statement from non-existent connection\n"); + exec sql at nonexistent prepare stmt1 FROM "SELECT 1"; + exec sql at nonexistent declare cur1 cursor for stmt1; + exec sql at nonexistent open cur1; + printf("sqlca.sqlcode = %ld\n", sqlca.sqlcode); + + printf("All tests completed !\n"); + + return 0; +} diff --git a/src/interfaces/ecpg/test/ecpg_schedule b/src/interfaces/ecpg/test/ecpg_schedule index 363eced2dfb..c5d5939bd46 100644 --- a/src/interfaces/ecpg/test/ecpg_schedule +++ b/src/interfaces/ecpg/test/ecpg_schedule @@ -13,6 +13,7 @@ test: connect/test2 test: connect/test3 test: connect/test4 test: connect/test5 +test: connect/test6 test: pgtypeslib/dt_test test: pgtypeslib/dt_test2 test: pgtypeslib/num_test diff --git a/src/interfaces/ecpg/test/expected/connect-test6.c b/src/interfaces/ecpg/test/expected/connect-test6.c new file mode 100644 index 00000000000..eed3c46a38c --- /dev/null +++ b/src/interfaces/ecpg/test/expected/connect-test6.c @@ -0,0 +1,146 @@ +/* Processed by ecpg (regression mode) */ +/* These include files are added by the preprocessor */ +#include +#include +#include +/* End of automatic include section */ +#define ECPGdebug(X,Y) ECPGdebug((X)+100,(Y)) + +#line 1 "test6.pgc" +/* + * This test verifies that ecpg functions properly handle NULL connections + * (i.e., when a connection name doesn't exist or has been disconnected). + * Before the fix, these operations would cause a segmentation fault. + */ + +#include +#include +#include + + +#line 1 "regression.h" + + + + + + +#line 11 "test6.pgc" + + +int +main(void) +{ +/* exec sql begin declare section */ + + + + + +#line 17 "test6.pgc" + int val1output = 2 ; + +#line 18 "test6.pgc" + int val1 = 1 ; + +#line 19 "test6.pgc" + char val2 [ 6 ] = "data1" ; + +#line 20 "test6.pgc" + char * stmt1 = "SELECT * from test1 where a = $1 and b = $2" ; +/* exec sql end declare section */ +#line 21 "test6.pgc" + + + ECPGdebug(1, stderr); + + /* Connect to the database */ + { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , "myconn", 0); } +#line 26 "test6.pgc" + + + /* Test 1: Try to get descriptor on a disconnected connection */ + printf("Test 1: Try to get descriptor on a disconnected connection\n"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table test1 ( a int , b text )", ECPGt_EOIT, ECPGt_EORT);} +#line 30 "test6.pgc" + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into test1 ( a , b ) values ( 1 , 'data1' )", ECPGt_EOIT, ECPGt_EORT);} +#line 31 "test6.pgc" + + + ECPGallocate_desc(__LINE__, "indesc"); +#line 33 "test6.pgc" + + ECPGallocate_desc(__LINE__, "outdesc"); +#line 34 "test6.pgc" + + + { ECPGprepare(__LINE__, NULL, 0, "foo2", stmt1);} +#line 36 "test6.pgc" + + + { ECPGset_desc(__LINE__, "indesc", 1,ECPGd_data, + ECPGt_int,&(val1),(long)1,(long)1,sizeof(int), ECPGd_EODT); +} +#line 38 "test6.pgc" + + { ECPGset_desc(__LINE__, "indesc", 2,ECPGd_data, + ECPGt_char,(val2),(long)6,(long)1,(6)*sizeof(char), ECPGd_EODT); +} +#line 39 "test6.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_execute, "foo2", + ECPGt_descriptor, "indesc", 1L, 1L, 1L, + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_descriptor, "outdesc", 1L, 1L, 1L, + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 41 "test6.pgc" + + + { ECPGtrans(__LINE__, NULL, "rollback");} +#line 43 "test6.pgc" + + { ECPGdisconnect(__LINE__, "CURRENT");} +#line 44 "test6.pgc" + + { ECPGget_desc(__LINE__, "outdesc", 1,ECPGd_data, + ECPGt_int,&(val1output),(long)1,(long)1,sizeof(int), ECPGd_EODT); +} +#line 45 "test6.pgc" + + printf("sqlca.sqlcode = %ld\n", sqlca.sqlcode); + + /* Test 2: Try to deallocate all on a non-existent connection */ + printf("Test 2: deallocate all with non-existent connection\n"); + { ECPGdeallocate_all(__LINE__, 0, "nonexistent");} +#line 50 "test6.pgc" + + printf("sqlca.sqlcode = %ld\n", sqlca.sqlcode); + + /* Test 3: deallocate on disconnected connection */ + printf("Test 3: deallocate all on disconnected connection\n"); + { ECPGdeallocate_all(__LINE__, 0, NULL);} +#line 55 "test6.pgc" + + printf("sqlca.sqlcode = %ld\n", sqlca.sqlcode); + + /* Test 4: Use prepared statement from non-existent connection */ + printf("Test 4: Use prepared statement from non-existent connection\n"); + { ECPGprepare(__LINE__, "nonexistent", 0, "stmt1", "SELECT 1");} +#line 60 "test6.pgc" + + /* declare cur1 cursor for $1 */ +#line 61 "test6.pgc" + + { ECPGdo(__LINE__, 0, 1, "nonexistent", 0, ECPGst_normal, "declare cur1 cursor for $1", + ECPGt_char_variable,(ECPGprepared_statement("nonexistent", "stmt1", __LINE__)),(long)1,(long)1,(1)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 62 "test6.pgc" + + printf("sqlca.sqlcode = %ld\n", sqlca.sqlcode); + + printf("All tests completed !\n"); + + return 0; +} diff --git a/src/interfaces/ecpg/test/expected/connect-test6.stderr b/src/interfaces/ecpg/test/expected/connect-test6.stderr new file mode 100644 index 00000000000..8784d5a9d40 --- /dev/null +++ b/src/interfaces/ecpg/test/expected/connect-test6.stderr @@ -0,0 +1,50 @@ +[NO_PID]: ECPGdebug: set to 1 +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ECPGconnect: opening database ecpg1_regression on port +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 30: query: create table test1 ( a int , b text ); with 0 parameter(s) on connection myconn +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 30: using PQexec +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_process_output on line 30: OK: CREATE TABLE +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 31: query: insert into test1 ( a , b ) values ( 1 , 'data1' ); with 0 parameter(s) on connection myconn +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 31: using PQexec +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_process_output on line 31: OK: INSERT 0 1 +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: prepare_common on line 36: name foo2; query: "SELECT * from test1 where a = $1 and b = $2" +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 41: query: SELECT * from test1 where a = $1 and b = $2; with 2 parameter(s) on connection myconn +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_execute on line 41: using PQexecPrepared for "SELECT * from test1 where a = $1 and b = $2" +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_free_params on line 41: parameter 1 = 1 +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_free_params on line 41: parameter 2 = data1 +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_process_output on line 41: correctly got 1 tuples with 2 fields +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_process_output on line 41: putting result (1 tuples) into descriptor outdesc +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ECPGtrans on line 43: action "rollback"; connection "myconn" +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: deallocate_one on line 0: name foo2 +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ecpg_finish: connection myconn closed +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: ECPGget_desc: reading items for tuple 1 +[NO_PID]: sqlca: code: 0, state: 00000 +[NO_PID]: raising sqlcode -220 on line 45: connection "NULL" does not exist on line 45 +[NO_PID]: sqlca: code: -220, state: 08003 +[NO_PID]: raising sqlcode -220 on line 50: connection "nonexistent" does not exist on line 50 +[NO_PID]: sqlca: code: -220, state: 08003 +[NO_PID]: raising sqlcode -220 on line 55: connection "NULL" does not exist on line 55 +[NO_PID]: sqlca: code: -220, state: 08003 +[NO_PID]: raising sqlcode -220 on line 60: connection "nonexistent" does not exist on line 60 +[NO_PID]: sqlca: code: -220, state: 08003 +[NO_PID]: raising sqlcode -220 on line 63: connection "nonexistent" does not exist on line 63 +[NO_PID]: sqlca: code: -220, state: 08003 +[NO_PID]: raising sqlcode -220 on line 62: connection "nonexistent" does not exist on line 62 +[NO_PID]: sqlca: code: -220, state: 08003 diff --git a/src/interfaces/ecpg/test/expected/connect-test6.stdout b/src/interfaces/ecpg/test/expected/connect-test6.stdout new file mode 100644 index 00000000000..bf9a2e91051 --- /dev/null +++ b/src/interfaces/ecpg/test/expected/connect-test6.stdout @@ -0,0 +1,9 @@ +Test 1: Try to get descriptor on a disconnected connection +sqlca.sqlcode = -220 +Test 2: deallocate all with non-existent connection +sqlca.sqlcode = -220 +Test 3: deallocate all on disconnected connection +sqlca.sqlcode = -220 +Test 4: Use prepared statement from non-existent connection +sqlca.sqlcode = -220 +All tests completed !