Implemented PDO Driver specific SQL parsers

RFC: http://wiki.php.net/rfc/pdo_driver_specific_parsers
This commit is contained in:
Matteo Beccati 2024-04-23 15:43:52 +02:00
parent ac947925c0
commit 715b9aaa09
No known key found for this signature in database
31 changed files with 391 additions and 44 deletions

2
.gitignore vendored
View File

@ -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
View 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); }

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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 */
};
/* }}} */

View 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)

View 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

View File

@ -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)

View File

@ -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");
}

View File

@ -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
};
/* }}} */

View 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); }
*/
}

View File

@ -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__)

View 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)

View File

@ -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) /* {{{ */

View 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)

View 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

View File

@ -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

View File

@ -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");
}

View File

@ -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) /* {{{ */

View 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); }
*/
}

View File

@ -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__)

View 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)

View 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

View File

@ -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

View File

@ -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");
}

View File

@ -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__)

View File

@ -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)

View 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); }
*/
}

View 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)

View File

@ -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 \