Skip to content

Commit 42f25f7

Browse files
committed
Fix #78855: Native PHP types in database fetches (ext/pgsql)
1 parent 9362d6f commit 42f25f7

File tree

2 files changed

+240
-40
lines changed

2 files changed

+240
-40
lines changed

‎ext/pgsql/pgsql.c

Lines changed: 99 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#define PGSQL_ASSOC 1<<0
5151
#define PGSQL_NUM 1<<1
5252
#define PGSQL_BOTH (PGSQL_ASSOC|PGSQL_NUM)
53+
#define PGSQL_TYPED 1<<2
5354

5455
#define PGSQL_NOTICE_LAST 1 /* Get the last notice */
5556
#define PGSQL_NOTICE_ALL 2 /* Get all notices */
@@ -61,6 +62,17 @@
6162
#define PGSQL_MAX_LENGTH_OF_LONG 30
6263
#define PGSQL_MAX_LENGTH_OF_DOUBLE 60
6364

65+
/* from postgresql/src/include/catalog/pg_type.h */
66+
#define BOOLOID 16
67+
#define BYTEAOID 17
68+
#define INT2OID 21
69+
#define INT4OID 23
70+
#define INT8OID 20
71+
#define TEXTOID 25
72+
#define OIDOID 26
73+
#define FLOAT4OID 700
74+
#define FLOAT8OID 701
75+
6476
#if ZEND_LONG_MAX < UINT_MAX
6577
#define PGSQL_RETURN_OID(oid) do { \
6678
if (oid > ZEND_LONG_MAX) { \
@@ -478,6 +490,7 @@ PHP_MINIT_FUNCTION(pgsql)
478490
REGISTER_LONG_CONSTANT("PGSQL_ASSOC", PGSQL_ASSOC, CONST_CS | CONST_PERSISTENT);
479491
REGISTER_LONG_CONSTANT("PGSQL_NUM", PGSQL_NUM, CONST_CS | CONST_PERSISTENT);
480492
REGISTER_LONG_CONSTANT("PGSQL_BOTH", PGSQL_BOTH, CONST_CS | CONST_PERSISTENT);
493+
REGISTER_LONG_CONSTANT("PGSQL_TYPED", PGSQL_TYPED, CONST_CS | CONST_PERSISTENT);
481494
/* For pg_last_notice() */
482495
REGISTER_LONG_CONSTANT("PGSQL_NOTICE_LAST", PGSQL_NOTICE_LAST, CONST_CS | CONST_PERSISTENT);
483496
REGISTER_LONG_CONSTANT("PGSQL_NOTICE_ALL", PGSQL_NOTICE_ALL, CONST_CS | CONST_PERSISTENT);
@@ -1846,6 +1859,72 @@ PHP_FUNCTION(pg_fetch_result)
18461859
}
18471860
/* }}} */
18481861

1862+
/* {{{ void php_pgsql_get_field_value */
1863+
static inline void php_pgsql_get_field_value(zval *value, PGresult *pgsql_result, zend_long result_type, int row, int column)
1864+
{
1865+
if (PQgetisnull(pgsql_result, row, column)) {
1866+
ZVAL_NULL(value);
1867+
} else {
1868+
char *element = PQgetvalue(pgsql_result, row, column);
1869+
if (element) {
1870+
const size_t element_len = PQgetlength(pgsql_result, row, column);
1871+
Oid pgsql_type;
1872+
if (result_type & PGSQL_TYPED) {
1873+
pgsql_type = PQftype(pgsql_result, column);
1874+
} else {
1875+
pgsql_type = TEXTOID;
1876+
}
1877+
1878+
switch (pgsql_type) {
1879+
case BOOLOID:
1880+
ZVAL_BOOL(value, *element == 't');
1881+
break;
1882+
case FLOAT4OID:
1883+
case FLOAT8OID:
1884+
if (element_len == sizeof("Infinity") - 1 && strcmp(element, "Infinity") == 0) {
1885+
ZVAL_DOUBLE(value, ZEND_INFINITY);
1886+
} else if (element_len == sizeof("-Infinity") - 1 && strcmp(element, "-Infinity") == 0) {
1887+
ZVAL_DOUBLE(value, -ZEND_INFINITY);
1888+
} else if (element_len == sizeof("NaN") - 1 && strcmp(element, "NaN") == 0) {
1889+
ZVAL_DOUBLE(value, ZEND_NAN);
1890+
} else {
1891+
ZVAL_DOUBLE(value, zend_strtod(element, NULL));
1892+
}
1893+
break;
1894+
case OIDOID:
1895+
case INT2OID:
1896+
case INT4OID:
1897+
#if SIZEOF_ZEND_LONG >= 8
1898+
case INT8OID:
1899+
#endif
1900+
{
1901+
zend_long long_value = ZEND_ATOL(element);
1902+
ZVAL_LONG(value, long_value);
1903+
break;
1904+
}
1905+
case BYTEAOID:
1906+
{
1907+
size_t tmp_len;
1908+
char *tmp_ptr = (char *)PQunescapeBytea((unsigned char *)element, &tmp_len);
1909+
if (!tmp_ptr) {
1910+
/* PQunescapeBytea returned an error */
1911+
ZVAL_NULL(value);
1912+
} else {
1913+
ZVAL_STRINGL(value, tmp_ptr, tmp_len);
1914+
PQfreemem(tmp_ptr);
1915+
}
1916+
break;
1917+
}
1918+
default:
1919+
ZVAL_STRINGL(value, element, element_len);
1920+
}
1921+
} else {
1922+
ZVAL_NULL(value);
1923+
}
1924+
}
1925+
}
1926+
/* }}} */
1927+
18491928
/* {{{ void php_pgsql_fetch_hash */
18501929
static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_type, int into_object)
18511930
{
@@ -1906,28 +1985,17 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_
19061985

19071986
array_init(return_value);
19081987
for (i = 0, num_fields = PQnfields(pgsql_result); i < num_fields; i++) {
1909-
if (PQgetisnull(pgsql_result, pgsql_row, i)) {
1910-
if (result_type & PGSQL_NUM) {
1911-
add_index_null(return_value, i);
1912-
}
1913-
if (result_type & PGSQL_ASSOC) {
1914-
field_name = PQfname(pgsql_result, i);
1915-
add_assoc_null(return_value, field_name);
1916-
}
1917-
} else {
1918-
char *element = PQgetvalue(pgsql_result, pgsql_row, i);
1919-
if (element) {
1920-
const size_t element_len = strlen(element);
1921-
1922-
if (result_type & PGSQL_NUM) {
1923-
add_index_stringl(return_value, i, element, element_len);
1924-
}
1988+
if (result_type & PGSQL_NUM) {
1989+
zval value;
1990+
php_pgsql_get_field_value(&value, pgsql_result, result_type, pgsql_row, i);
1991+
add_index_zval(return_value, i, &value);
1992+
}
19251993

1926-
if (result_type & PGSQL_ASSOC) {
1927-
field_name = PQfname(pgsql_result, i);
1928-
add_assoc_stringl(return_value, field_name, element, element_len);
1929-
}
1930-
}
1994+
if (result_type & PGSQL_ASSOC) {
1995+
zval value;
1996+
php_pgsql_get_field_value(&value, pgsql_result, result_type, pgsql_row, i);
1997+
field_name = PQfname(pgsql_result, i);
1998+
add_assoc_zval(return_value, field_name, &value);
19311999
}
19322000
}
19332001

@@ -5788,25 +5856,16 @@ PHP_PGSQL_API void php_pgsql_result2array(PGresult *pg_result, zval *ret_array,
57885856
for (pg_row = 0; pg_row < pg_numrows; pg_row++) {
57895857
array_init(&row);
57905858
for (i = 0, num_fields = PQnfields(pg_result); i < num_fields; i++) {
5791-
field_name = PQfname(pg_result, i);
5792-
if (PQgetisnull(pg_result, pg_row, i)) {
5793-
if (result_type & PGSQL_ASSOC) {
5794-
add_assoc_null(&row, field_name);
5795-
}
5796-
if (result_type & PGSQL_NUM) {
5797-
add_next_index_null(&row);
5798-
}
5799-
} else {
5800-
char *element = PQgetvalue(pg_result, pg_row, i);
5801-
if (element) {
5802-
const size_t element_len = strlen(element);
5803-
if (result_type & PGSQL_ASSOC) {
5804-
add_assoc_stringl(&row, field_name, element, element_len);
5805-
}
5806-
if (result_type & PGSQL_NUM) {
5807-
add_next_index_stringl(&row, element, element_len);
5808-
}
5809-
}
5859+
if (result_type & PGSQL_ASSOC) {
5860+
zval value;
5861+
php_pgsql_get_field_value(&value, pg_result, result_type, pg_row, i);
5862+
field_name = PQfname(pg_result, i);
5863+
add_assoc_zval(&row, field_name, &value);
5864+
}
5865+
if (result_type & PGSQL_NUM) {
5866+
zval value;
5867+
php_pgsql_get_field_value(&value, pg_result, result_type, pg_row, i);
5868+
add_next_index_zval(&row, &value);
58105869
}
58115870
}
58125871
add_index_zval(ret_array, pg_row, &row);

‎ext/pgsql/tests/bug78855.phpt

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
--TEST--
2+
Bug #78855 Native PHP types in database fetches
3+
--SKIPIF--
4+
<?php
5+
include("skipif.inc");
6+
?>
7+
--FILE--
8+
<?php
9+
error_reporting(E_ALL);
10+
11+
include 'config.inc';
12+
13+
$db = pg_connect($conn_str);
14+
15+
$res = pg_query($db, "SELECT null, true, false, 1::smallint, 2::int, 3::oid, 'text', '\\x3031'::bytea, 1.1::real, 1.2::float, 'Infinity'::float, '-Infinity'::float, 'NaN'::float");
16+
var_dump(pg_fetch_array($res, 0, PGSQL_NUM));
17+
var_dump(pg_fetch_array($res, 0, PGSQL_NUM|PGSQL_TYPED));
18+
19+
var_dump(pg_fetch_all($res, PGSQL_NUM));
20+
var_dump(pg_fetch_all($res, PGSQL_NUM|PGSQL_TYPED));
21+
22+
?>
23+
--EXPECT--
24+
array(13) {
25+
[0]=>
26+
NULL
27+
[1]=>
28+
string(1) "t"
29+
[2]=>
30+
string(1) "f"
31+
[3]=>
32+
string(1) "1"
33+
[4]=>
34+
string(1) "2"
35+
[5]=>
36+
string(1) "3"
37+
[6]=>
38+
string(4) "text"
39+
[7]=>
40+
string(6) "\x3031"
41+
[8]=>
42+
string(3) "1.1"
43+
[9]=>
44+
string(3) "1.2"
45+
[10]=>
46+
string(8) "Infinity"
47+
[11]=>
48+
string(9) "-Infinity"
49+
[12]=>
50+
string(3) "NaN"
51+
}
52+
array(13) {
53+
[0]=>
54+
NULL
55+
[1]=>
56+
bool(true)
57+
[2]=>
58+
bool(false)
59+
[3]=>
60+
int(1)
61+
[4]=>
62+
int(2)
63+
[5]=>
64+
int(3)
65+
[6]=>
66+
string(4) "text"
67+
[7]=>
68+
string(2) "01"
69+
[8]=>
70+
float(1.1)
71+
[9]=>
72+
float(1.2)
73+
[10]=>
74+
float(INF)
75+
[11]=>
76+
float(-INF)
77+
[12]=>
78+
float(NAN)
79+
}
80+
array(1) {
81+
[0]=>
82+
array(13) {
83+
[0]=>
84+
NULL
85+
[1]=>
86+
string(1) "t"
87+
[2]=>
88+
string(1) "f"
89+
[3]=>
90+
string(1) "1"
91+
[4]=>
92+
string(1) "2"
93+
[5]=>
94+
string(1) "3"
95+
[6]=>
96+
string(4) "text"
97+
[7]=>
98+
string(6) "\x3031"
99+
[8]=>
100+
string(3) "1.1"
101+
[9]=>
102+
string(3) "1.2"
103+
[10]=>
104+
string(8) "Infinity"
105+
[11]=>
106+
string(9) "-Infinity"
107+
[12]=>
108+
string(3) "NaN"
109+
}
110+
}
111+
array(1) {
112+
[0]=>
113+
array(13) {
114+
[0]=>
115+
NULL
116+
[1]=>
117+
bool(true)
118+
[2]=>
119+
bool(false)
120+
[3]=>
121+
int(1)
122+
[4]=>
123+
int(2)
124+
[5]=>
125+
int(3)
126+
[6]=>
127+
string(4) "text"
128+
[7]=>
129+
string(2) "01"
130+
[8]=>
131+
float(1.1)
132+
[9]=>
133+
float(1.2)
134+
[10]=>
135+
float(INF)
136+
[11]=>
137+
float(-INF)
138+
[12]=>
139+
float(NAN)
140+
}
141+
}

0 commit comments

Comments
 (0)