]> granicus.if.org Git - php/commitdiff
Added new PDO::PGSQL_ATTR_DISABLE_PREPARES that uses PQexecParams
authorMatteo Beccati <mbeccati@php.net>
Sun, 9 Mar 2014 13:08:17 +0000 (14:08 +0100)
committerMatteo Beccati <mbeccati@php.net>
Tue, 11 Mar 2014 22:10:48 +0000 (23:10 +0100)
Faster than prepared statements when queries are run once. Slightly
slower than PDO::ATTR_EMULATE_PREPARES but without the potential
security implications of embedding parameters in the query itself.

ext/pdo_pgsql/pdo_pgsql.c
ext/pdo_pgsql/pgsql_driver.c
ext/pdo_pgsql/pgsql_statement.c
ext/pdo_pgsql/php_pdo_pgsql_int.h
ext/pdo_pgsql/tests/disable_prepares.phpt [new file with mode: 0644]

index 8e4cc97fb53f418d98b489c3e9d722e48446e676..fe003f8ab9041c47e97784d215c2488c4bda724d 100644 (file)
@@ -86,6 +86,7 @@ ZEND_GET_MODULE(pdo_pgsql)
 PHP_MINIT_FUNCTION(pdo_pgsql)
 {
        REGISTER_PDO_CLASS_CONST_LONG("PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT", PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT);
+       REGISTER_PDO_CLASS_CONST_LONG("PGSQL_ATTR_DISABLE_PREPARES", PDO_PGSQL_ATTR_DISABLE_PREPARES);
        REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_IDLE", (long)PGSQL_TRANSACTION_IDLE);
        REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_ACTIVE", (long)PGSQL_TRANSACTION_ACTIVE);
        REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_INTRANS", (long)PGSQL_TRANSACTION_INTRANS);
index 0d72d5573e305ea771e8079675e9f3298ce91359..96f6fa7f72fb28eca240b7eec918ff4fec4bb9a0 100644 (file)
@@ -224,6 +224,7 @@ static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len,
        char *nsql = NULL;
        int nsql_len = 0;
        int emulate = 0;
+       int execute_only = 0;
 
        S->H = H;
        stmt->driver_data = S;
@@ -246,8 +247,12 @@ static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len,
                if (pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares TSRMLS_CC) == 1) {
                        emulate = 1;
                }
+               if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_PREPARES, H->disable_prepares TSRMLS_CC) == 1) {
+                       execute_only = 1;
+               }
        } else {
                emulate = H->disable_native_prepares || H->emulate_prepares;
+               execute_only = H->disable_prepares;
        }
 
        if (!emulate && PQprotocolVersion(H->server) > 2) {
@@ -264,8 +269,11 @@ static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len,
                        return 0;
                }
 
-               spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter);
-               /* that's all for now; we'll defer the actual prepare until the first execute call */
+               if (!execute_only) {
+                       /* prepared query: set the query name and defer the
+                          actual prepare until the first execute call */
+                       spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter);
+               }
 
                if (nsql) {
                        S->query = nsql;
@@ -1112,6 +1120,9 @@ static int pdo_pgsql_set_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
                        php_error_docref(NULL TSRMLS_CC, E_DEPRECATED, "PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT is deprecated, use PDO::ATTR_EMULATE_PREPARES instead");
                        H->disable_native_prepares = Z_LVAL_P(val);
                        return 1;
+               case PDO_PGSQL_ATTR_DISABLE_PREPARES:
+                       H->disable_prepares = Z_LVAL_P(val);
+                       return 1;
                default:
                        return 0;
        }
index 02fc1d6e90ee7f61455f06bbaea57179ed98278f..35e69a3c02dda368a942c57dd74bd59a4143bb5b 100644 (file)
@@ -204,7 +204,17 @@ stmt_retry:
                                S->param_lengths,
                                S->param_formats,
                                0);
+       } else if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED) {
+               /* execute query with parameters */
+               S->result = PQexecParams(H->server, S->query,
+                               stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0,
+                               S->param_types,
+                               (const char**)S->param_values,
+                               S->param_lengths,
+                               S->param_formats,
+                               0);
        } else {
+               /* execute plain query (with embedded parameters) */
                S->result = PQexec(H->server, stmt->active_query_string);
        }
        status = PQresultStatus(S->result);
@@ -234,7 +244,7 @@ static int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *
 {
        pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
 
-       if (S->stmt_name && param->is_param) {
+       if (stmt->supports_placeholders == PDO_PLACEHOLDER_NAMED && param->is_param) {
                switch (event_type) {
                        case PDO_PARAM_EVT_FREE:
                                if (param->driver_data) {
index 34d89d20783b808084ee4029e2cb0ab05cd99e92..1fedababd25409e5bd528097891e924d74699c99 100644 (file)
@@ -47,6 +47,7 @@ typedef struct {
           to keep track of two different attributes having the same effect. */
        int             emulate_prepares;
        int             disable_native_prepares; /* deprecated since 5.6 */
+       int             disable_prepares;
        unsigned int    stmt_counter;
 } pdo_pgsql_db_handle;
 
@@ -90,6 +91,7 @@ extern struct pdo_stmt_methods pgsql_stmt_methods;
 
 enum {
        PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT = PDO_ATTR_DRIVER_SPECIFIC,
+       PDO_PGSQL_ATTR_DISABLE_PREPARES,
 };
 
 struct pdo_pgsql_lob_self {
diff --git a/ext/pdo_pgsql/tests/disable_prepares.phpt b/ext/pdo_pgsql/tests/disable_prepares.phpt
new file mode 100644 (file)
index 0000000..99a7695
--- /dev/null
@@ -0,0 +1,54 @@
+--TEST--
+PDO PgSQL PGSQL_ATTR_DISABLE_PREPARES
+--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
+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);
+$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_COLUMN);
+
+$stmt = $db->prepare("SELECT statement FROM pg_prepared_statements", array(
+       PDO::ATTR_EMULATE_PREPARES => true));
+
+$stmt2 = $db->prepare("SELECT (?)::int2");
+$stmt2->execute(array(1));
+var_dump($stmt2->fetch());
+$stmt2->execute(array(2));
+var_dump($stmt2->fetch());
+
+$stmt->execute();
+$first = $stmt->fetchAll();
+
+$stmt3 = $db->prepare("SELECT (?)::int4", array(
+       PDO::PGSQL_ATTR_DISABLE_PREPARES => true));
+$stmt3->execute(array(3));
+var_dump($stmt3->fetch());
+$stmt3->execute(array(4));
+var_dump($stmt3->fetch());
+
+$stmt->execute();
+$second = $stmt->fetchAll();
+
+var_dump($first, $second);
+
+?>
+--EXPECT--
+string(1) "1"
+string(1) "2"
+string(1) "3"
+string(1) "4"
+array(1) {
+  [0]=>
+  string(17) "SELECT ($1)::int2"
+}
+array(1) {
+  [0]=>
+  string(17) "SELECT ($1)::int2"
+}