Skip to content

Commit 54ef55f

Browse files
committed
New fixes
1 parent 24696c0 commit 54ef55f

File tree

3 files changed

+180
-20
lines changed

3 files changed

+180
-20
lines changed

ext/odbc/php_odbc.c

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,42 @@ static void odbc_insert_new_result(odbc_connection *connection, zval *result) {
8181
ZEND_ASSERT(Z_TYPE_P(result) == IS_OBJECT && instanceof_function(Z_OBJCE_P(result), odbc_result_ce));
8282

8383
odbc_result *res = Z_ODBC_RESULT_P(result);
84+
8485
res->index = connection->results.nNextFreeElement;
85-
zend_hash_next_index_insert_new(&connection->results, result);
86+
zend_hash_index_add_new(&connection->results, res->index, result);
8687
Z_ADDREF_P(result);
8788
}
8889

90+
static inline odbc_link *odbc_link_from_obj(zend_object *obj) {
91+
return (odbc_link *)((char *)(obj) - XtOffsetOf(odbc_link, std));
92+
}
93+
94+
static int _close_pconn_with_res(zval *zv, void *p)
95+
{
96+
zend_resource *le = Z_RES_P(zv);
97+
if (le->type != le_pconn) {
98+
return ZEND_HASH_APPLY_KEEP;
99+
}
100+
101+
odbc_connection *list_conn = ((odbc_connection *)(le->ptr));
102+
odbc_connection *obj_conn = odbc_link_from_obj((zend_object*)p)->connection;
103+
if (list_conn == obj_conn) {
104+
return ZEND_HASH_APPLY_REMOVE;
105+
}
106+
107+
return ZEND_HASH_APPLY_KEEP;
108+
}
109+
110+
static int _close_pconn(zval *zv)
111+
{
112+
zend_resource *le = Z_RES_P(zv);
113+
if (le->type == le_pconn) {
114+
return ZEND_HASH_APPLY_REMOVE;
115+
} else {
116+
return ZEND_HASH_APPLY_KEEP;
117+
}
118+
}
119+
89120
static void odbc_link_free(odbc_link *link)
90121
{
91122
ZEND_ASSERT(link->connection && "link has already been closed");
@@ -101,6 +132,7 @@ static void odbc_link_free(odbc_link *link)
101132
}
102133
link->connection->hdbc = NULL;
103134
link->connection->henv = NULL;
135+
zend_hash_destroy(&link->connection->results);
104136
efree(link->connection);
105137
ODBCG(num_links)--;
106138

@@ -117,10 +149,6 @@ static void odbc_link_free(odbc_link *link)
117149
}
118150
}
119151

120-
static inline odbc_link *odbc_link_from_obj(zend_object *obj) {
121-
return (odbc_link *)((char *)(obj) - XtOffsetOf(odbc_link, std));
122-
}
123-
124152
static zend_object *odbc_connection_create_object(zend_class_entry *class_type) {
125153
odbc_link *intern = zend_object_alloc(sizeof(odbc_link), class_type);
126154

@@ -253,9 +281,10 @@ static void close_results_with_connection(odbc_connection *conn) {
253281
if (result->conn_ptr) {
254282
odbc_result_free(result);
255283
}
284+
GC_DELREF(&result->std);
256285
} ZEND_HASH_FOREACH_END();
257286

258-
zend_hash_destroy(&conn->results);
287+
zend_hash_clean(&conn->results);
259288
}
260289

261290
/* disconnect, and if it fails, then issue a rollback for any pending transaction (lurcher) */
@@ -277,14 +306,14 @@ static void _close_odbc_pconn(zend_resource *rsrc)
277306
odbc_connection *conn = (odbc_connection *)rsrc->ptr;
278307

279308
close_results_with_connection(conn);
309+
zend_hash_destroy(&conn->results);
280310

281311
/* If aborted via timer expiration, don't try to call any unixODBC function */
282312
if (!(PG(connection_status) & PHP_CONNECTION_TIMEOUT)) {
283313
safe_odbc_disconnect(conn->hdbc);
284314
SQLFreeConnect(conn->hdbc);
285315
SQLFreeEnv(conn->henv);
286316
}
287-
free(conn);
288317

289318
ODBCG(num_links)--;
290319
ODBCG(num_persistent)--;
@@ -855,12 +884,7 @@ PHP_FUNCTION(odbc_close_all)
855884

856885
zend_hash_clean(&ODBCG(non_persistent_connections));
857886

858-
/* Loop through the persistent connection list, now close all persistent connections and their results */
859-
ZEND_HASH_FOREACH_VAL(&EG(persistent_list), zv) {
860-
if (Z_RES_P(zv)->type == le_pconn) {
861-
zend_list_close(Z_RES_P(zv));
862-
}
863-
} ZEND_HASH_FOREACH_END();
887+
zend_hash_apply(&EG(persistent_list), _close_pconn);
864888
}
865889
/* }}} */
866890

@@ -2388,7 +2412,7 @@ void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
23882412
/* {{{ Close an ODBC connection */
23892413
PHP_FUNCTION(odbc_close)
23902414
{
2391-
zval *pv_conn, *zv;
2415+
zval *pv_conn;
23922416
odbc_link *link;
23932417

23942418
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_conn, odbc_connection_ce) == FAILURE) {
@@ -2398,13 +2422,11 @@ PHP_FUNCTION(odbc_close)
23982422
link = Z_ODBC_LINK_P(pv_conn);
23992423
CHECK_ODBC_CONNECTION(link->connection);
24002424

2401-
odbc_link_free(link);
2425+
if (link->persistent) {
2426+
zend_hash_apply_with_argument(&EG(persistent_list), _close_pconn_with_res, (void *) Z_OBJ_P(pv_conn));
2427+
}
24022428

2403-
ZEND_HASH_FOREACH_VAL(&EG(persistent_list), zv) {
2404-
if (Z_RES_P(zv)->type == le_pconn && link->connection == Z_RES_P(zv)->ptr) {
2405-
zend_list_close(Z_RES_P(zv));
2406-
}
2407-
} ZEND_HASH_FOREACH_END();
2429+
odbc_link_free(link);
24082430
}
24092431
/* }}} */
24102432

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
--TEST--
2+
odbc_pconnect(): Make sure non-persistent connections are reused
3+
--EXTENSIONS--
4+
odbc
5+
--SKIPIF--
6+
<?php
7+
8+
if (getenv('SKIP_ASAN')) {
9+
die('skip libmsodbcsql leaks, see https://github.com/php/php-src/pull/12132#issuecomment-1710392299.');
10+
}
11+
12+
include 'skipif.inc';
13+
14+
// The test can affect multiple drivers, but testing it is driver-specific.
15+
// Since CI runs ODBC tests with SQL Server, just use an SQL Server specific way of testing.
16+
$conn = odbc_connect($dsn, $user, $pass);
17+
$result = @odbc_exec($conn, "SELECT @@Version");
18+
if ($result) {
19+
$array = odbc_fetch_array($result);
20+
$info = (string) reset($array);
21+
if (!str_contains($info, "Microsoft SQL Server")) {
22+
echo "skip MS SQL specific test";
23+
}
24+
}
25+
?>
26+
--FILE--
27+
<?php
28+
29+
include 'config.inc';
30+
31+
// set a session specific variable via CONTEXT_INFO, if we get the same connection again, it should be identical
32+
function set_context_info($conn, $string) {
33+
$hexstring = bin2hex($string);
34+
return odbc_exec($conn, "SET CONTEXT_INFO 0x$hexstring");
35+
}
36+
37+
function fetch_context_info($conn) {
38+
$stmt = odbc_exec($conn, "SELECT CONTEXT_INFO() AS CONTEXT_INFO");
39+
if (!$stmt) {
40+
return false;
41+
}
42+
$res = odbc_fetch_array($stmt);
43+
if ($res) {
44+
// this is a binary, so we get a bunch of nulls at the end
45+
return $res["CONTEXT_INFO"] ? trim($res["CONTEXT_INFO"]) : null;
46+
} else {
47+
return false;
48+
}
49+
}
50+
51+
// run 1: set expectations
52+
$conn = odbc_connect($dsn, $user, $pass);
53+
set_context_info($conn, "PHP odbc_connect test");
54+
var_dump(fetch_context_info($conn));
55+
56+
// run 2: reuse same connection (imagine this is another request)
57+
$conn = odbc_connect($dsn, $user, $pass);
58+
var_dump(fetch_context_info($conn));
59+
60+
// run 3: close it and see if it's the same connection
61+
odbc_close($conn);
62+
$conn = odbc_connect($dsn, $user, $pass);
63+
var_dump(fetch_context_info($conn));
64+
65+
?>
66+
--EXPECT--
67+
string(21) "PHP odbc_connect test"
68+
string(21) "PHP odbc_connect test"
69+
NULL
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
--TEST--
2+
odbc_pconnect(): Make sure closing a persistent connection works in case of odbc_close_all()
3+
--EXTENSIONS--
4+
odbc
5+
--SKIPIF--
6+
<?php
7+
8+
if (getenv('SKIP_ASAN')) {
9+
die('skip libmsodbcsql leaks, see https://github.com/php/php-src/pull/12132#issuecomment-1710392299.');
10+
}
11+
12+
include 'skipif.inc';
13+
14+
// The test can affect multiple drivers, but testing it is driver-specific.
15+
// Since CI runs ODBC tests with SQL Server, just use an SQL Server specific way of testing.
16+
$conn = odbc_connect($dsn, $user, $pass);
17+
$result = @odbc_exec($conn, "SELECT @@Version");
18+
if ($result) {
19+
$array = odbc_fetch_array($result);
20+
$info = (string) reset($array);
21+
if (!str_contains($info, "Microsoft SQL Server")) {
22+
echo "skip MS SQL specific test";
23+
}
24+
}
25+
?>
26+
--FILE--
27+
<?php
28+
29+
include 'config.inc';
30+
31+
// set a session specific variable via CONTEXT_INFO, if we get the same connection again, it should be identical
32+
function set_context_info($conn, $string) {
33+
$hexstring = bin2hex($string);
34+
return odbc_exec($conn, "SET CONTEXT_INFO 0x$hexstring");
35+
}
36+
37+
function fetch_context_info($conn) {
38+
$stmt = odbc_exec($conn, "SELECT CONTEXT_INFO() AS CONTEXT_INFO");
39+
if (!$stmt) {
40+
return false;
41+
}
42+
$res = odbc_fetch_array($stmt);
43+
if ($res) {
44+
// this is a binary, so we get a bunch of nulls at the end
45+
return $res["CONTEXT_INFO"] ? trim($res["CONTEXT_INFO"]) : null;
46+
} else {
47+
return false;
48+
}
49+
}
50+
51+
// run 1: set expectations
52+
$conn = odbc_pconnect($dsn, $user, $pass);
53+
set_context_info($conn, "PHP odbc_pconnect test");
54+
var_dump(fetch_context_info($conn));
55+
56+
// run 2: reuse same connection (imagine this is another request)
57+
$conn = odbc_pconnect($dsn, $user, $pass);
58+
var_dump(fetch_context_info($conn));
59+
60+
// run 3: close it and see if it's the same connection
61+
odbc_close_all();
62+
$conn = odbc_pconnect($dsn, $user, $pass);
63+
var_dump(fetch_context_info($conn));
64+
65+
?>
66+
--EXPECT--
67+
string(22) "PHP odbc_pconnect test"
68+
string(22) "PHP odbc_pconnect test"
69+
NULL

0 commit comments

Comments
 (0)