mirror of
https://github.com/php/php-src.git
synced 2024-11-27 03:44:07 +08:00
Implemented PDO Driver specific SQL parsers
RFC: http://wiki.php.net/rfc/pdo_driver_specific_parsers
This commit is contained in:
parent
ac947925c0
commit
715b9aaa09
2
.gitignore
vendored
2
.gitignore
vendored
@ -137,7 +137,7 @@ php
|
||||
# ------------------------------------------------------------------------------
|
||||
/ext/json/json_scanner.c
|
||||
/ext/json/php_json_scanner_defs.h
|
||||
/ext/pdo/pdo_sql_parser.c
|
||||
/ext/pdo*/*_sql_parser.c
|
||||
/ext/phar/phar_path_check.c
|
||||
/ext/standard/url_scanner_ex.c
|
||||
/ext/standard/var_unserializer.c
|
||||
|
32
ext/pdo/pdo_sql_parser.h
Normal file
32
ext/pdo/pdo_sql_parser.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: George Schlossnagle <george@omniti.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#define PDO_PARSER_TEXT 1
|
||||
#define PDO_PARSER_BIND 2
|
||||
#define PDO_PARSER_BIND_POS 3
|
||||
#define PDO_PARSER_ESCAPED_QUESTION 4
|
||||
#define PDO_PARSER_EOI 5
|
||||
|
||||
#define PDO_PARSER_BINDNO_ESCAPED_CHAR -1
|
||||
|
||||
#define RET(i) {s->cur = cursor; return i; }
|
||||
#define SKIP_ONE(i) {s->cur = s->tok + 1; return i; }
|
||||
|
||||
#define YYCTYPE unsigned char
|
||||
#define YYCURSOR cursor
|
||||
#define YYLIMIT s->end
|
||||
#define YYMARKER s->ptr
|
||||
#define YYFILL(n) { RET(PDO_PARSER_EOI); }
|
@ -17,29 +17,9 @@
|
||||
#include "php.h"
|
||||
#include "php_pdo_driver.h"
|
||||
#include "php_pdo_int.h"
|
||||
#include "pdo_sql_parser.h"
|
||||
|
||||
#define PDO_PARSER_TEXT 1
|
||||
#define PDO_PARSER_BIND 2
|
||||
#define PDO_PARSER_BIND_POS 3
|
||||
#define PDO_PARSER_ESCAPED_QUESTION 4
|
||||
#define PDO_PARSER_EOI 5
|
||||
|
||||
#define PDO_PARSER_BINDNO_ESCAPED_CHAR -1
|
||||
|
||||
#define RET(i) {s->cur = cursor; return i; }
|
||||
#define SKIP_ONE(i) {s->cur = s->tok + 1; return i; }
|
||||
|
||||
#define YYCTYPE unsigned char
|
||||
#define YYCURSOR cursor
|
||||
#define YYLIMIT s->end
|
||||
#define YYMARKER s->ptr
|
||||
#define YYFILL(n) { RET(PDO_PARSER_EOI); }
|
||||
|
||||
typedef struct Scanner {
|
||||
const char *ptr, *cur, *tok, *end;
|
||||
} Scanner;
|
||||
|
||||
static int scan(Scanner *s)
|
||||
static int default_scanner(pdo_scanner_t *s)
|
||||
{
|
||||
const char *cursor = s->cur;
|
||||
|
||||
@ -47,18 +27,16 @@ static int scan(Scanner *s)
|
||||
/*!re2c
|
||||
BINDCHR = [:][a-zA-Z0-9_]+;
|
||||
QUESTION = [?];
|
||||
ESCQUESTION = [?][?];
|
||||
COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/"|"--"[^\r\n]*);
|
||||
SPECIALS = [:?"'-/];
|
||||
MULTICHAR = [:]{2,};
|
||||
SPECIALS = [:?"'/-];
|
||||
MULTICHAR = ([:]{2,}|[?]{2,});
|
||||
ANYNOEOF = [\001-\377];
|
||||
*/
|
||||
|
||||
/*!re2c
|
||||
(["](([\\]ANYNOEOF)|ANYNOEOF\["\\])*["]) { RET(PDO_PARSER_TEXT); }
|
||||
(['](([\\]ANYNOEOF)|ANYNOEOF\['\\])*[']) { RET(PDO_PARSER_TEXT); }
|
||||
(["]((["]["])|ANYNOEOF\["])*["]) { RET(PDO_PARSER_TEXT); }
|
||||
(['](([']['])|ANYNOEOF\['])*[']) { RET(PDO_PARSER_TEXT); }
|
||||
MULTICHAR { RET(PDO_PARSER_TEXT); }
|
||||
ESCQUESTION { RET(PDO_PARSER_ESCAPED_QUESTION); }
|
||||
BINDCHR { RET(PDO_PARSER_BIND); }
|
||||
QUESTION { RET(PDO_PARSER_BIND_POS); }
|
||||
SPECIALS { SKIP_ONE(PDO_PARSER_TEXT); }
|
||||
@ -81,7 +59,7 @@ static void free_param_name(zval *el) {
|
||||
|
||||
PDO_API int pdo_parse_params(pdo_stmt_t *stmt, zend_string *inquery, zend_string **outquery)
|
||||
{
|
||||
Scanner s;
|
||||
pdo_scanner_t s;
|
||||
char *newbuffer;
|
||||
ptrdiff_t t;
|
||||
uint32_t bindno = 0;
|
||||
@ -91,6 +69,9 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, zend_string *inquery, zend_string
|
||||
struct pdo_bound_param_data *param;
|
||||
int query_type = PDO_PLACEHOLDER_NONE;
|
||||
struct placeholder *placeholders = NULL, *placetail = NULL, *plc = NULL;
|
||||
int (*scan)(pdo_scanner_t *s);
|
||||
|
||||
scan = stmt->dbh->methods->scanner ? stmt->dbh->methods->scanner : default_scanner;
|
||||
|
||||
s.cur = ZSTR_VAL(inquery);
|
||||
s.end = s.cur + ZSTR_LEN(inquery) + 1;
|
||||
|
@ -24,6 +24,7 @@ typedef struct _pdo_dbh_t pdo_dbh_t;
|
||||
typedef struct _pdo_dbh_object_t pdo_dbh_object_t;
|
||||
typedef struct _pdo_stmt_t pdo_stmt_t;
|
||||
typedef struct _pdo_row_t pdo_row_t;
|
||||
typedef struct _pdo_scanner_t pdo_scanner_t;
|
||||
struct pdo_bound_param_data;
|
||||
|
||||
#ifndef TRUE
|
||||
@ -33,7 +34,7 @@ struct pdo_bound_param_data;
|
||||
# define FALSE 0
|
||||
#endif
|
||||
|
||||
#define PDO_DRIVER_API 20170320
|
||||
#define PDO_DRIVER_API 20240423
|
||||
|
||||
/* Doctrine hardcodes these constants, avoid changing their values. */
|
||||
enum pdo_param_type {
|
||||
@ -275,6 +276,9 @@ typedef void (*pdo_dbh_request_shutdown)(pdo_dbh_t *dbh);
|
||||
* with any zvals in the driver_data that would be freed if the handle is destroyed. */
|
||||
typedef void (*pdo_dbh_get_gc_func)(pdo_dbh_t *dbh, zend_get_gc_buffer *buffer);
|
||||
|
||||
/* driver specific re2s sql parser, overrides the default one if present */
|
||||
typedef int (*pdo_dbh_sql_scanner)(pdo_scanner_t *s);
|
||||
|
||||
/* for adding methods to the dbh or stmt objects
|
||||
pointer to a list of driver specific functions. The convention is
|
||||
to prefix the function names using the PDO driver name; this will
|
||||
@ -307,6 +311,7 @@ struct pdo_dbh_methods {
|
||||
/* if defined to NULL, PDO will use its internal transaction tracking state */
|
||||
pdo_dbh_txn_func in_transaction;
|
||||
pdo_dbh_get_gc_func get_gc;
|
||||
pdo_dbh_sql_scanner scanner;
|
||||
};
|
||||
|
||||
/* }}} */
|
||||
@ -647,6 +652,10 @@ struct _pdo_row_t {
|
||||
pdo_stmt_t *stmt;
|
||||
};
|
||||
|
||||
struct _pdo_scanner_t {
|
||||
const char *ptr, *cur, *tok, *end;
|
||||
};
|
||||
|
||||
/* Call this in MINIT to register the PDO driver.
|
||||
* Registering the driver might fail and should be reported accordingly in MINIT. */
|
||||
PDO_API zend_result php_pdo_register_driver(const pdo_driver_t *driver);
|
||||
|
@ -436,7 +436,8 @@ static const struct pdo_dbh_methods dblib_methods = {
|
||||
NULL, /* get driver methods */
|
||||
NULL, /* request shutdown */
|
||||
NULL, /* in transaction, use PDO's internal tracking mechanism */
|
||||
NULL /* get gc */
|
||||
NULL, /* get gc */
|
||||
NULL /* scanner */
|
||||
};
|
||||
|
||||
static int pdo_dblib_handle_factory(pdo_dbh_t *dbh, zval *driver_options)
|
||||
|
@ -1274,7 +1274,8 @@ static const struct pdo_dbh_methods firebird_methods = { /* {{{ */
|
||||
NULL, /* get driver methods */
|
||||
NULL, /* request shutdown */
|
||||
pdo_firebird_in_manually_transaction,
|
||||
NULL /* get gc */
|
||||
NULL, /* get gc */
|
||||
NULL /* scanner */
|
||||
};
|
||||
/* }}} */
|
||||
|
||||
|
7
ext/pdo_mysql/Makefile.frag
Normal file
7
ext/pdo_mysql/Makefile.frag
Normal file
@ -0,0 +1,7 @@
|
||||
$(srcdir)/mysql_sql_parser.c: $(srcdir)/mysql_sql_parser.re
|
||||
@(cd $(top_srcdir); \
|
||||
if test -f ./mysql_sql_parser.re; then \
|
||||
$(RE2C) $(RE2C_FLAGS) --no-generation-date -o mysql_sql_parser.c mysql_sql_parser.re; \
|
||||
else \
|
||||
$(RE2C) $(RE2C_FLAGS) --no-generation-date -o ext/pdo_mysql/mysql_sql_parser.c ext/pdo_mysql/mysql_sql_parser.re; \
|
||||
fi)
|
3
ext/pdo_mysql/Makefile.frag.w32
Normal file
3
ext/pdo_mysql/Makefile.frag.w32
Normal file
@ -0,0 +1,3 @@
|
||||
ext\pdo_mysql\mysql_sql_parser.c: ext\pdo_mysql\mysql_sql_parser.re
|
||||
cd $(PHP_SRC_DIR)
|
||||
$(RE2C) $(RE2C_FLAGS) --no-generation-date -o ext/pdo_mysql/mysql_sql_parser.c ext/pdo_mysql/mysql_sql_parser.re
|
@ -85,9 +85,10 @@ if test "$PHP_PDO_MYSQL" != "no"; then
|
||||
AC_DEFINE_UNQUOTED(PDO_MYSQL_UNIX_ADDR, "$PDO_MYSQL_SOCKET", [ ])
|
||||
fi
|
||||
|
||||
PHP_NEW_EXTENSION(pdo_mysql, pdo_mysql.c mysql_driver.c mysql_statement.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
|
||||
PHP_NEW_EXTENSION(pdo_mysql, pdo_mysql.c mysql_driver.c mysql_statement.c mysql_sql_parser.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
|
||||
|
||||
PHP_ADD_EXTENSION_DEP(pdo_mysql, pdo)
|
||||
PHP_ADD_MAKEFILE_FRAGMENT
|
||||
|
||||
if test "$PHP_PDO_MYSQL" = "yes" || test "$PHP_PDO_MYSQL" = "mysqlnd"; then
|
||||
PHP_ADD_EXTENSION_DEP(pdo_mysql, mysqlnd)
|
||||
|
@ -6,15 +6,17 @@ if (PHP_PDO_MYSQL != "no") {
|
||||
if (PHP_PDO_MYSQL == "yes" || PHP_PDO_MYSQL == "mysqlnd") {
|
||||
AC_DEFINE('PDO_USE_MYSQLND', 1, 'Using MySQL native driver');
|
||||
STDOUT.WriteLine("INFO: mysqlnd build");
|
||||
EXTENSION("pdo_mysql", "pdo_mysql.c mysql_driver.c mysql_statement.c");
|
||||
EXTENSION("pdo_mysql", "pdo_mysql.c mysql_driver.c mysql_statement.c mysql_sql_parser.c");
|
||||
ADD_EXTENSION_DEP('pdo_mysql', 'pdo');
|
||||
ADD_MAKEFILE_FRAGMENT();
|
||||
} else {
|
||||
if (CHECK_LIB("libmysql.lib", "pdo_mysql", PHP_PDO_MYSQL) &&
|
||||
CHECK_HEADER_ADD_INCLUDE("mysql.h", "CFLAGS_PDO_MYSQL",
|
||||
PHP_PDO_MYSQL + "\\include;" +
|
||||
PHP_PHP_BUILD + "\\include\\mysql;" +
|
||||
PHP_PDO_MYSQL)) {
|
||||
EXTENSION("pdo_mysql", "pdo_mysql.c mysql_driver.c mysql_statement.c", null, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
|
||||
EXTENSION("pdo_mysql", "pdo_mysql.c mysql_driver.c mysql_statement.c mysql_sql_parser.c", null, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
|
||||
ADD_MAKEFILE_FRAGMENT();
|
||||
} else {
|
||||
WARNING("pdo_mysql not enabled; libraries and headers not found");
|
||||
}
|
||||
|
@ -650,7 +650,8 @@ static const struct pdo_dbh_methods mysql_methods = {
|
||||
NULL,
|
||||
pdo_mysql_request_shutdown,
|
||||
pdo_mysql_in_transaction,
|
||||
NULL /* get_gc */
|
||||
NULL, /* get_gc */
|
||||
pdo_mysql_scanner
|
||||
};
|
||||
/* }}} */
|
||||
|
||||
|
48
ext/pdo_mysql/mysql_sql_parser.re
Normal file
48
ext/pdo_mysql/mysql_sql_parser.re
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Matteo Beccati <mbeccati@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
|
||||
#include "php.h"
|
||||
#include "ext/pdo/php_pdo_driver.h"
|
||||
#include "ext/pdo/php_pdo_int.h"
|
||||
#include "ext/pdo/pdo_sql_parser.h"
|
||||
|
||||
int pdo_mysql_scanner(pdo_scanner_t *s)
|
||||
{
|
||||
const char *cursor = s->cur;
|
||||
|
||||
s->tok = cursor;
|
||||
/*!re2c
|
||||
BINDCHR = [:][a-zA-Z0-9_]+;
|
||||
QUESTION = [?];
|
||||
COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/"|("--"|[#])[^\r\n]*);
|
||||
SPECIALS = [:?"'`/#-];
|
||||
MULTICHAR = ([:]{2,}|[?]{2,});
|
||||
ANYNOEOF = [\001-\377];
|
||||
*/
|
||||
|
||||
/*!re2c
|
||||
(["]((["]["])|([\\]ANYNOEOF)|ANYNOEOF\["\\])*["]) { RET(PDO_PARSER_TEXT); }
|
||||
(['](([']['])|([\\]ANYNOEOF)|ANYNOEOF\['\\])*[']) { RET(PDO_PARSER_TEXT); }
|
||||
([`]([`][`]|ANYNOEOF\[`])*[`]) { RET(PDO_PARSER_TEXT); }
|
||||
MULTICHAR { RET(PDO_PARSER_TEXT); }
|
||||
BINDCHR { RET(PDO_PARSER_BIND); }
|
||||
QUESTION { RET(PDO_PARSER_BIND_POS); }
|
||||
SPECIALS { SKIP_ONE(PDO_PARSER_TEXT); }
|
||||
COMMENTS { RET(PDO_PARSER_TEXT); }
|
||||
(ANYNOEOF\SPECIALS)+ { RET(PDO_PARSER_TEXT); }
|
||||
*/
|
||||
}
|
@ -147,6 +147,8 @@ typedef struct {
|
||||
|
||||
extern const pdo_driver_t pdo_mysql_driver;
|
||||
|
||||
extern int pdo_mysql_scanner(pdo_scanner_t *s);
|
||||
|
||||
extern int _pdo_mysql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line);
|
||||
#define pdo_mysql_error(s) _pdo_mysql_error(s, NULL, __FILE__, __LINE__)
|
||||
#define pdo_mysql_error_stmt(s) _pdo_mysql_error(stmt->dbh, stmt, __FILE__, __LINE__)
|
||||
|
58
ext/pdo_mysql/tests/pdo_mysql_parser.phpt
Normal file
58
ext/pdo_mysql/tests/pdo_mysql_parser.phpt
Normal file
@ -0,0 +1,58 @@
|
||||
--TEST--
|
||||
MySQL PDO Parser custom syntax
|
||||
--EXTENSIONS--
|
||||
pdo_mysql
|
||||
--SKIPIF--
|
||||
<?php
|
||||
require_once __DIR__ . '/inc/mysql_pdo_test.inc';
|
||||
MySQLPDOTest::skip();
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once __DIR__ . '/inc/mysql_pdo_test.inc';
|
||||
$db = MySQLPDOTest::factory();
|
||||
|
||||
$table = 'pdo_mysql_parser';
|
||||
|
||||
$db->exec("DROP TABLE IF EXISTS {$table}");
|
||||
$db->exec("CREATE TABLE {$table} (`a``?` int NOT NULL)");
|
||||
$db->exec("INSERT INTO {$table} VALUES (1)");
|
||||
|
||||
// No parameters
|
||||
$queries = [
|
||||
"SELECT * FROM {$table}",
|
||||
"SELECT * FROM {$table} -- ?",
|
||||
"SELECT * FROM {$table} # ?",
|
||||
"SELECT * FROM {$table} /* ? */",
|
||||
];
|
||||
|
||||
foreach ($queries as $k => $query) {
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute();
|
||||
var_dump($stmt->fetch(PDO::FETCH_NUM) === [0 => 1]);
|
||||
}
|
||||
|
||||
// One parameter
|
||||
$queries = [
|
||||
"SELECT * FROM {$table} WHERE 1 = ?",
|
||||
"SELECT * FROM {$table} WHERE \"?\" IN (?, '?')",
|
||||
"SELECT * FROM {$table} WHERE `a``?` = ?",
|
||||
];
|
||||
|
||||
foreach ($queries as $k => $query) {
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute([1]);
|
||||
var_dump($stmt->fetch(PDO::FETCH_NUM) === [0 => 1]);
|
||||
}
|
||||
|
||||
$db->exec("DROP TABLE pdo_mysql_parser");
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
@ -461,7 +461,8 @@ static const struct pdo_dbh_methods odbc_methods = {
|
||||
NULL, /* get_driver_methods */
|
||||
NULL, /* request_shutdown */
|
||||
NULL, /* in transaction, use PDO's internal tracking mechanism */
|
||||
NULL /* get_gc */
|
||||
NULL, /* get_gc */
|
||||
NULL /* scanner */
|
||||
};
|
||||
|
||||
static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */
|
||||
|
7
ext/pdo_pgsql/Makefile.frag
Normal file
7
ext/pdo_pgsql/Makefile.frag
Normal file
@ -0,0 +1,7 @@
|
||||
$(srcdir)/pgsql_sql_parser.c: $(srcdir)/pgsql_sql_parser.re
|
||||
@(cd $(top_srcdir); \
|
||||
if test -f ./pgsql_sql_parser.re; then \
|
||||
$(RE2C) $(RE2C_FLAGS) --no-generation-date -o pgsql_sql_parser.c pgsql_sql_parser.re; \
|
||||
else \
|
||||
$(RE2C) $(RE2C_FLAGS) --no-generation-date -o ext/pdo_pgsql/pgsql_sql_parser.c ext/pdo_pgsql/pgsql_sql_parser.re; \
|
||||
fi)
|
3
ext/pdo_pgsql/Makefile.frag.w32
Normal file
3
ext/pdo_pgsql/Makefile.frag.w32
Normal file
@ -0,0 +1,3 @@
|
||||
ext\pdo_pgsql\pgsql_sql_parser.c: ext\pdo_pgsql\pgsql_sql_parser.re
|
||||
cd $(PHP_SRC_DIR)
|
||||
$(RE2C) $(RE2C_FLAGS) --no-generation-date -o ext/pdo_pgsql/pgsql_sql_parser.c ext/pdo_pgsql/pgsql_sql_parser.re
|
@ -79,6 +79,7 @@ if test "$PHP_PDO_PGSQL" != "no"; then
|
||||
|
||||
PHP_CHECK_PDO_INCLUDES
|
||||
|
||||
PHP_NEW_EXTENSION(pdo_pgsql, pdo_pgsql.c pgsql_driver.c pgsql_statement.c, $ext_shared)
|
||||
PHP_NEW_EXTENSION(pdo_pgsql, pdo_pgsql.c pgsql_driver.c pgsql_statement.c pgsql_sql_parser.c, $ext_shared)
|
||||
PHP_ADD_EXTENSION_DEP(pdo_pgsql, pdo)
|
||||
PHP_ADD_MAKEFILE_FRAGMENT
|
||||
fi
|
||||
|
@ -5,7 +5,7 @@ ARG_WITH("pdo-pgsql", "PostgreSQL support for PDO", "no");
|
||||
if (PHP_PDO_PGSQL != "no") {
|
||||
if (CHECK_LIB("libpq.lib", "pdo_pgsql", PHP_PDO_PGSQL) &&
|
||||
CHECK_HEADER_ADD_INCLUDE("libpq-fe.h", "CFLAGS_PDO_PGSQL", PHP_PDO_PGSQL + "\\include;" + PHP_PHP_BUILD + "\\include\\pgsql;" + PHP_PHP_BUILD + "\\include\\libpq;")) {
|
||||
EXTENSION("pdo_pgsql", "pdo_pgsql.c pgsql_driver.c pgsql_statement.c");
|
||||
EXTENSION("pdo_pgsql", "pdo_pgsql.c pgsql_driver.c pgsql_statement.c pgsql_sql_parser.c");
|
||||
|
||||
if (X64) {
|
||||
ADD_FLAG('CFLAGS_PDO_PGSQL', "/D HAVE_PG_LO64=1");
|
||||
@ -14,6 +14,7 @@ if (PHP_PDO_PGSQL != "no") {
|
||||
AC_DEFINE('HAVE_PDO_PGSQL', 1, 'Have PostgreSQL library');
|
||||
|
||||
ADD_EXTENSION_DEP('pdo_pgsql', 'pdo');
|
||||
ADD_MAKEFILE_FRAGMENT();
|
||||
} else {
|
||||
WARNING("pdo_pgsql not enabled; libraries and headers not found");
|
||||
}
|
||||
|
@ -1321,7 +1321,8 @@ static const struct pdo_dbh_methods pgsql_methods = {
|
||||
pdo_pgsql_get_driver_methods, /* get_driver_methods */
|
||||
NULL,
|
||||
pgsql_handle_in_transaction,
|
||||
NULL /* get_gc */
|
||||
NULL, /* get_gc */
|
||||
pdo_pgsql_scanner
|
||||
};
|
||||
|
||||
static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */
|
||||
|
49
ext/pdo_pgsql/pgsql_sql_parser.re
Normal file
49
ext/pdo_pgsql/pgsql_sql_parser.re
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Matteo Beccati <mbeccati@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
|
||||
#include "php.h"
|
||||
#include "ext/pdo/php_pdo_driver.h"
|
||||
#include "ext/pdo/php_pdo_int.h"
|
||||
#include "ext/pdo/pdo_sql_parser.h"
|
||||
|
||||
int pdo_pgsql_scanner(pdo_scanner_t *s)
|
||||
{
|
||||
const char *cursor = s->cur;
|
||||
|
||||
s->tok = cursor;
|
||||
/*!re2c
|
||||
BINDCHR = [:][a-zA-Z0-9_]+;
|
||||
QUESTION = [?];
|
||||
ESCQUESTION = [?][?];
|
||||
COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/"|"--"[^\r\n]*);
|
||||
SPECIALS = [:?"'/-];
|
||||
MULTICHAR = [:]{2,};
|
||||
ANYNOEOF = [\001-\377];
|
||||
*/
|
||||
|
||||
/*!re2c
|
||||
(["]((["]["])|ANYNOEOF\["])*["]) { RET(PDO_PARSER_TEXT); }
|
||||
(['](([']['])|ANYNOEOF\['])*[']) { RET(PDO_PARSER_TEXT); }
|
||||
MULTICHAR { RET(PDO_PARSER_TEXT); }
|
||||
ESCQUESTION { RET(PDO_PARSER_ESCAPED_QUESTION); }
|
||||
BINDCHR { RET(PDO_PARSER_BIND); }
|
||||
QUESTION { RET(PDO_PARSER_BIND_POS); }
|
||||
SPECIALS { SKIP_ONE(PDO_PARSER_TEXT); }
|
||||
COMMENTS { RET(PDO_PARSER_TEXT); }
|
||||
(ANYNOEOF\SPECIALS)+ { RET(PDO_PARSER_TEXT); }
|
||||
*/
|
||||
}
|
@ -74,6 +74,8 @@ typedef struct {
|
||||
|
||||
extern const pdo_driver_t pdo_pgsql_driver;
|
||||
|
||||
extern int pdo_pgsql_scanner(pdo_scanner_t *s);
|
||||
|
||||
extern int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line);
|
||||
#define pdo_pgsql_error(d,e,z) _pdo_pgsql_error(d, NULL, e, z, NULL, __FILE__, __LINE__)
|
||||
#define pdo_pgsql_error_msg(d,e,m) _pdo_pgsql_error(d, NULL, e, NULL, m, __FILE__, __LINE__)
|
||||
|
7
ext/pdo_sqlite/Makefile.frag
Normal file
7
ext/pdo_sqlite/Makefile.frag
Normal file
@ -0,0 +1,7 @@
|
||||
$(srcdir)/sqlite_sql_parser.c: $(srcdir)/sqlite_sql_parser.re
|
||||
@(cd $(top_srcdir); \
|
||||
if test -f ./sqlite_sql_parser.re; then \
|
||||
$(RE2C) $(RE2C_FLAGS) --no-generation-date -o sqlite_sql_parser.c sqlite_sql_parser.re; \
|
||||
else \
|
||||
$(RE2C) $(RE2C_FLAGS) --no-generation-date -o ext/pdo_sqlite/sqlite_sql_parser.c ext/pdo_sqlite/sqlite_sql_parser.re; \
|
||||
fi)
|
3
ext/pdo_sqlite/Makefile.frag.w32
Normal file
3
ext/pdo_sqlite/Makefile.frag.w32
Normal file
@ -0,0 +1,3 @@
|
||||
ext\pdo_sqlite\sqlite_sql_parser.c: ext\pdo_sqlite\sqlite_sql_parser.re
|
||||
cd $(PHP_SRC_DIR)
|
||||
$(RE2C) $(RE2C_FLAGS) --no-generation-date -o ext/pdo_sqlite/sqlite_sql_parser.c ext/pdo_sqlite/sqlite_sql_parser.re
|
@ -29,8 +29,9 @@ if test "$PHP_PDO_SQLITE" != "no"; then
|
||||
)
|
||||
|
||||
PHP_SUBST(PDO_SQLITE_SHARED_LIBADD)
|
||||
PHP_NEW_EXTENSION(pdo_sqlite, pdo_sqlite.c sqlite_driver.c sqlite_statement.c,
|
||||
PHP_NEW_EXTENSION(pdo_sqlite, pdo_sqlite.c sqlite_driver.c sqlite_statement.c sqlite_sql_parser.c,
|
||||
$ext_shared)
|
||||
|
||||
PHP_ADD_EXTENSION_DEP(pdo_sqlite, pdo)
|
||||
PHP_ADD_MAKEFILE_FRAGMENT
|
||||
fi
|
||||
|
@ -4,11 +4,12 @@ ARG_WITH("pdo-sqlite", "for pdo_sqlite support", "no");
|
||||
|
||||
if (PHP_PDO_SQLITE != "no") {
|
||||
if (SETUP_SQLITE3("pdo_sqlite", PHP_PDO_SQLITE, PHP_PDO_SQLITE_SHARED)) {
|
||||
EXTENSION("pdo_sqlite", "pdo_sqlite.c sqlite_driver.c sqlite_statement.c");
|
||||
EXTENSION("pdo_sqlite", "pdo_sqlite.c sqlite_driver.c sqlite_statement.c sqlite_sql_parser.c");
|
||||
|
||||
ADD_EXTENSION_DEP('pdo_sqlite', 'pdo');
|
||||
AC_DEFINE("HAVE_SQLITE3_COLUMN_TABLE_NAME", 1, "have sqlite3_column_table_name");
|
||||
AC_DEFINE("HAVE_SQLITE3_CLOSE_V2", 1, "have sqlite3_close_v2");
|
||||
ADD_MAKEFILE_FRAGMENT();
|
||||
} else {
|
||||
WARNING("pdo_sqlite not enabled; libraries and/or headers not found");
|
||||
}
|
||||
|
@ -61,6 +61,8 @@ typedef struct {
|
||||
|
||||
extern const pdo_driver_t pdo_sqlite_driver;
|
||||
|
||||
extern int pdo_sqlite_scanner(pdo_scanner_t *s);
|
||||
|
||||
extern int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line);
|
||||
#define pdo_sqlite_error(s) _pdo_sqlite_error(s, NULL, __FILE__, __LINE__)
|
||||
#define pdo_sqlite_error_stmt(s) _pdo_sqlite_error(stmt->dbh, stmt, __FILE__, __LINE__)
|
||||
|
@ -743,7 +743,8 @@ static const struct pdo_dbh_methods sqlite_methods = {
|
||||
get_driver_methods,
|
||||
pdo_sqlite_request_shutdown,
|
||||
pdo_sqlite_in_transaction,
|
||||
pdo_sqlite_get_gc
|
||||
pdo_sqlite_get_gc,
|
||||
pdo_sqlite_scanner
|
||||
};
|
||||
|
||||
static char *make_filename_safe(const char *filename)
|
||||
|
48
ext/pdo_sqlite/sqlite_sql_parser.re
Normal file
48
ext/pdo_sqlite/sqlite_sql_parser.re
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Matteo Beccati <mbeccati@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
|
||||
#include "php.h"
|
||||
#include "ext/pdo/php_pdo_driver.h"
|
||||
#include "ext/pdo/php_pdo_int.h"
|
||||
#include "ext/pdo/pdo_sql_parser.h"
|
||||
|
||||
int pdo_sqlite_scanner(pdo_scanner_t *s)
|
||||
{
|
||||
const char *cursor = s->cur;
|
||||
|
||||
s->tok = cursor;
|
||||
/*!re2c
|
||||
BINDCHR = [:][a-zA-Z0-9_]+;
|
||||
QUESTION = [?];
|
||||
COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/"|"--"[^\r\n]*);
|
||||
SPECIALS = [:?"'`/-];
|
||||
MULTICHAR = ([:]{2,}|[?]{2,});
|
||||
ANYNOEOF = [\001-\377];
|
||||
*/
|
||||
|
||||
/*!re2c
|
||||
(["]((["]["])|ANYNOEOF)*["]) { RET(PDO_PARSER_TEXT); }
|
||||
(['](([']['])|ANYNOEOF)*[']) { RET(PDO_PARSER_TEXT); }
|
||||
([`](([`][`])|ANYNOEOF)*[`]) { RET(PDO_PARSER_TEXT); }
|
||||
MULTICHAR { RET(PDO_PARSER_TEXT); }
|
||||
BINDCHR { RET(PDO_PARSER_BIND); }
|
||||
QUESTION { RET(PDO_PARSER_BIND_POS); }
|
||||
SPECIALS { SKIP_ONE(PDO_PARSER_TEXT); }
|
||||
COMMENTS { RET(PDO_PARSER_TEXT); }
|
||||
(ANYNOEOF\SPECIALS)+ { RET(PDO_PARSER_TEXT); }
|
||||
*/
|
||||
}
|
58
ext/pdo_sqlite/tests/pdo_sqlite_parser.phpt
Normal file
58
ext/pdo_sqlite/tests/pdo_sqlite_parser.phpt
Normal file
@ -0,0 +1,58 @@
|
||||
--TEST--
|
||||
PDO_sqlite: Parser custom syntax
|
||||
--EXTENSIONS--
|
||||
pdo_sqlite
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$filename = __DIR__ . DIRECTORY_SEPARATOR . "pdo_sqlite_parser.db";
|
||||
|
||||
// Default open flag is read-write|create
|
||||
$db = new PDO('sqlite:' . $filename, null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
|
||||
|
||||
$table = 'pdo_sqlite_parser';
|
||||
|
||||
$db->exec("CREATE TABLE {$table} (`a``?` int NOT NULL)");
|
||||
$db->exec("INSERT INTO {$table} VALUES (1)");
|
||||
|
||||
// No parameters
|
||||
$queries = [
|
||||
"SELECT * FROM {$table}",
|
||||
"SELECT * FROM {$table} -- ?",
|
||||
"SELECT * FROM {$table} /* ? */",
|
||||
];
|
||||
|
||||
foreach ($queries as $k => $query) {
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute();
|
||||
var_dump($stmt->fetch(PDO::FETCH_NUM) === [0 => 1]);
|
||||
}
|
||||
|
||||
// One parameter
|
||||
$queries = [
|
||||
"SELECT * FROM {$table} WHERE '1' = ?",
|
||||
"SELECT * FROM {$table} WHERE \"?\" IN (?, '?')",
|
||||
"SELECT * FROM {$table} WHERE `a``?` = ?",
|
||||
];
|
||||
|
||||
foreach ($queries as $k => $query) {
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute([1]);
|
||||
var_dump($stmt->fetch(PDO::FETCH_NUM) === [0 => 1]);
|
||||
}
|
||||
|
||||
?>
|
||||
--CLEAN--
|
||||
<?php
|
||||
$filename = __DIR__ . DIRECTORY_SEPARATOR . "pdo_sqlite_parser.db";
|
||||
if (file_exists($filename)) {
|
||||
unlink($filename);
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
@ -125,6 +125,21 @@ $MAKE RE2C="$RE2C" RE2C_FLAGS="$RE2C_FLAGS" srcdir=ext/pdo builddir=ext/pdo top_
|
||||
-f ext/pdo/Makefile.frag \
|
||||
ext/pdo/pdo_sql_parser.c
|
||||
|
||||
echo "genfiles: Generating PDO_mysql lexer file"
|
||||
$MAKE RE2C="$RE2C" RE2C_FLAGS="$RE2C_FLAGS" srcdir=ext/pdo_mysql builddir=ext/pdo_mysql top_srcdir=. \
|
||||
-f ext/pdo_mysql/Makefile.frag \
|
||||
ext/pdo_mysql/mysql_sql_parser.c
|
||||
|
||||
echo "genfiles: Generating PDO_pgsql lexer file"
|
||||
$MAKE RE2C="$RE2C" RE2C_FLAGS="$RE2C_FLAGS" srcdir=ext/pdo_pgsql builddir=ext/pdo_pgsql top_srcdir=. \
|
||||
-f ext/pdo_pgsql/Makefile.frag \
|
||||
ext/pdo_pgsql/pgsql_sql_parser.c
|
||||
|
||||
echo "genfiles: Generating PDO_sqlite lexer file"
|
||||
$MAKE RE2C="$RE2C" RE2C_FLAGS="$RE2C_FLAGS" srcdir=ext/pdo_sqlite builddir=ext/pdo_sqlite top_srcdir=. \
|
||||
-f ext/pdo_sqlite/Makefile.frag \
|
||||
ext/pdo_sqlite/sqlite_sql_parser.c
|
||||
|
||||
echo "genfiles: Generating standard extension lexer files"
|
||||
$MAKE RE2C="$RE2C" RE2C_FLAGS="$RE2C_FLAGS" srcdir=ext/standard builddir=ext/standard top_srcdir=. \
|
||||
-f ext/standard/Makefile.frag \
|
||||
|
Loading…
Reference in New Issue
Block a user