Skip to content

Avoid database query in getColumnMeta for pdo_pgsql connector #1534

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 56 additions & 25 deletions ext/pdo_pgsql/pgsql_statement.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,27 @@
#endif

/* from postgresql/src/include/catalog/pg_type.h */
#define BOOLLABEL "bool"
#define BOOLOID 16
#define BYTEALABEL "bytea"
#define BYTEAOID 17
#define INT8OID 20
#define DATELABEL "date"
#define DATEOID 1082
#define INT2LABEL "int2"
#define INT2OID 21
#define INT4LABEL "int4"
#define INT4OID 23
#define TEXTOID 25
#define INT8LABEL "int8"
#define INT8OID 20
#define OIDOID 26
#define TEXTLABEL "text"
#define TEXTOID 25
#define TIMESTAMPLABEL "timestamp"
#define TIMESTAMPOID 1114
#define VARCHARLABEL "varchar"
#define VARCHAROID 1043



static int pgsql_stmt_dtor(pdo_stmt_t *stmt)
{
Expand Down Expand Up @@ -591,29 +605,46 @@ static int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *r
array_init(return_value);
add_assoc_long(return_value, "pgsql:oid", S->cols[colno].pgsql_type);

/* Fetch metadata from Postgres system catalogue */
spprintf(&q, 0, "SELECT TYPNAME FROM PG_TYPE WHERE OID=%u", S->cols[colno].pgsql_type);
res = PQexec(S->H->server, q);
efree(q);

status = PQresultStatus(res);

if (status != PGRES_TUPLES_OK) {
/* Failed to get system catalogue, but return success
* with the data we have collected so far
*/
goto done;
}

/* We want exactly one row returned */
if (1 != PQntuples(res)) {
goto done;
}

add_assoc_string(return_value, "native_type", PQgetvalue(res, 0, 0));
done:
PQclear(res);
return 1;
switch (S->cols[colno].pgsql_type) {
case BOOLOID:
add_assoc_string(return_value, "native_type", BOOLLABEL);
break;
case BYTEAOID:
add_assoc_string(return_value, "native_type", BYTEALABEL);
break;
case INT8OID:
add_assoc_string(return_value, "native_type", INT8LABEL);
break;
case INT2OID:
add_assoc_string(return_value, "native_type", INT2LABEL);
break;
case INT4OID:
add_assoc_string(return_value, "native_type", INT4LABEL);
break;
case TEXTOID:
add_assoc_string(return_value, "native_type", TEXTLABEL);
break;
case VARCHAROID:
add_assoc_string(return_value, "native_type", VARCHARLABEL);
break;
case DATEOID:
add_assoc_string(return_value, "native_type", DATELABEL);
break;
case TIMESTAMPOID:
add_assoc_string(return_value, "native_type", TIMESTAMPLABEL);
break;
default:
/* Fetch metadata from Postgres system catalogue */
spprintf(&q, 0, "SELECT TYPNAME FROM PG_TYPE WHERE OID=%u", S->cols[colno].pgsql_type);
res = PQexec(S->H->server, q);
efree(q);
status = PQresultStatus(res);
if (status == PGRES_TUPLES_OK && 1 == PQntuples(res)) {
add_assoc_string(return_value, "native_type", PQgetvalue(res, 0, 0));
}
PQclear(res);
}
return 1;
}

static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt)
Expand Down
103 changes: 103 additions & 0 deletions ext/pdo_pgsql/tests/bug62498.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
--TEST--
PDO PgSQL Bug #62498 (pdo_pgsql inefficient when getColumnMeta() is used)
--SKIPIF--
<?php
if (!extension_loaded('pdo') || !extension_loaded('pdo_pgsql')) die('skip not loaded');
require dirname(__FILE__) . '/config.inc';
require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
PDOTest::skip();
?>
--FILE--
<?php
echo "Begin test...\n";

require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
$db->setAttribute (\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);

// create the table
$db->exec("CREATE TEMPORARY TABLE bugtest_62498 (intcol INTEGER, stringcol VARCHAR(255), boolcol BOOLEAN, datecol DATE)");

// insert some data
$statement = $db->prepare("INSERT INTO bugtest_62498 (intcol, stringcol, boolcol, datecol) VALUES (:intval, :stringval, :boolval, :dateval)");
$statement->execute(array(
"intval" => "42",
"stringval" => "The Answer",
"boolval" => true,
"dateval" => '2015-12-14',
));

$select = $db->query('SELECT intcol, stringcol, boolcol, datecol FROM bugtest_62498');
$meta = [];
for ($i=0; $i < 4; $i++) {
$meta[] = $select->getColumnMeta(0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be getColumnMeta($i)?

}
var_dump($meta);

?>
Done
--EXPECT--
Begin test...
array(4) {
[0]=>
array(6) {
["pgsql:oid"]=>
int(23)
["native_type"]=>
string(4) "int4"
["name"]=>
string(6) "intcol"
["len"]=>
int(4)
["precision"]=>
int(-1)
["pdo_type"]=>
int(1)
}
[1]=>
array(6) {
["pgsql:oid"]=>
int(23)
["native_type"]=>
string(4) "int4"
["name"]=>
string(6) "intcol"
["len"]=>
int(4)
["precision"]=>
int(-1)
["pdo_type"]=>
int(1)
}
[2]=>
array(6) {
["pgsql:oid"]=>
int(23)
["native_type"]=>
string(4) "int4"
["name"]=>
string(6) "intcol"
["len"]=>
int(4)
["precision"]=>
int(-1)
["pdo_type"]=>
int(1)
}
[3]=>
array(6) {
["pgsql:oid"]=>
int(23)
["native_type"]=>
string(4) "int4"
["name"]=>
string(6) "intcol"
["len"]=>
int(4)
["precision"]=>
int(-1)
["pdo_type"]=>
int(1)
}
}
Done