2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
20 #include <unordered_map>
22 #include "hphp/system/systemlib.h"
23 #include "hphp/util/hphp-config.h"
24 #include "hphp/util/string-vsnprintf.h"
26 #include "hphp/runtime/base/array-init.h"
27 #include "hphp/runtime/base/comparisons.h"
28 #include "hphp/runtime/base/file.h"
29 #include "hphp/runtime/base/ini-setting.h"
30 #include "hphp/runtime/base/request-local.h"
31 #include "hphp/runtime/base/string-buffer.h"
32 #include "hphp/runtime/base/tv-refcount.h"
33 #include "hphp/runtime/vm/jit/translator-inline.h"
35 #include "hphp/runtime/ext/extension.h"
36 #include "hphp/runtime/ext/array/ext_array.h"
37 #include "hphp/runtime/ext/pdo/pdo_driver.h"
38 #ifdef ENABLE_EXTENSION_PDO_MYSQL
39 #include "hphp/runtime/ext/pdo_mysql/pdo_mysql.h"
41 #ifdef ENABLE_EXTENSION_PDO_SQLITE
42 #include "hphp/runtime/ext/pdo_sqlite/pdo_sqlite.h"
44 #include "hphp/runtime/ext/std/ext_std_classobj.h"
45 #include "hphp/runtime/ext/std/ext_std_function.h"
46 #include "hphp/runtime/ext/stream/ext_stream.h"
47 #include "hphp/runtime/ext/string/ext_string.h"
49 #define PDO_HANDLE_DBH_ERR(dbh) \
50 if (strcmp(dbh->conn()->error_code, PDO_ERR_NONE)) { \
51 pdo_handle_error(dbh, nullptr); \
54 #define PDO_HANDLE_STMT_ERR(stmt) \
55 if (strcmp(stmt->error_code, PDO_ERR_NONE)) { \
56 pdo_handle_error(stmt->dbh, stmt); \
65 struct PDOStatementData
{
69 sp_PDOStatement m_stmt
;
75 ///////////////////////////////////////////////////////////////////////////////
76 // extension functions
78 Array
HHVM_FUNCTION(pdo_drivers
) {
79 Array ret
= Array::Create();
80 const PDODriverMap
&drivers
= PDODriver::GetDrivers();
81 for (PDODriverMap::const_iterator iter
= drivers
.begin();
82 iter
!= drivers
.end(); ++iter
) {
83 ret
.append(iter
->second
->getName());
88 ///////////////////////////////////////////////////////////////////////////////
91 struct pdo_sqlstate_info
{
96 static const struct pdo_sqlstate_info err_initializer
[] = {
97 { "00000", "No error" },
98 { "01000", "Warning" },
99 { "01001", "Cursor operation conflict" },
100 { "01002", "Disconnect error" },
101 { "01003", "NULL value eliminated in set function" },
102 { "01004", "String data, right truncated" },
103 { "01006", "Privilege not revoked" },
104 { "01007", "Privilege not granted" },
105 { "01008", "Implicit zero bit padding" },
106 { "0100C", "Dynamic result sets returned" },
107 { "01P01", "Deprecated feature" },
108 { "01S00", "Invalid connection string attribute" },
109 { "01S01", "Error in row" },
110 { "01S02", "Option value changed" },
112 "Attempt to fetch before the result set returned the first rowset" },
113 { "01S07", "Fractional truncation" },
114 { "01S08", "Error saving File DSN" },
115 { "01S09", "Invalid keyword" },
116 { "02000", "No data" },
117 { "02001", "No additional dynamic result sets returned" },
118 { "03000", "Sql statement not yet complete" },
119 { "07002", "COUNT field incorrect" },
120 { "07005", "Prepared statement not a cursor-specification" },
121 { "07006", "Restricted data type attribute violation" },
122 { "07009", "Invalid descriptor index" },
123 { "07S01", "Invalid use of default parameter" },
124 { "08000", "Connection exception" },
125 { "08001", "Client unable to establish connection" },
126 { "08002", "Connection name in use" },
127 { "08003", "Connection does not exist" },
128 { "08004", "Server rejected the connection" },
129 { "08006", "Connection failure" },
130 { "08007", "Connection failure during transaction" },
131 { "08S01", "Communication link failure" },
132 { "09000", "Triggered action exception" },
133 { "0A000", "Feature not supported" },
134 { "0B000", "Invalid transaction initiation" },
135 { "0F000", "Locator exception" },
136 { "0F001", "Invalid locator specification" },
137 { "0L000", "Invalid grantor" },
138 { "0LP01", "Invalid grant operation" },
139 { "0P000", "Invalid role specification" },
140 { "21000", "Cardinality violation" },
141 { "21S01", "Insert value list does not match column list" },
142 { "21S02", "Degree of derived table does not match column list" },
143 { "22000", "Data exception" },
144 { "22001", "String data, right truncated" },
145 { "22002", "Indicator variable required but not supplied" },
146 { "22003", "Numeric value out of range" },
147 { "22004", "Null value not allowed" },
148 { "22005", "Error in assignment" },
149 { "22007", "Invalid datetime format" },
150 { "22008", "Datetime field overflow" },
151 { "22009", "Invalid time zone displacement value" },
152 { "2200B", "Escape character conflict" },
153 { "2200C", "Invalid use of escape character" },
154 { "2200D", "Invalid escape octet" },
155 { "2200F", "Zero length character string" },
156 { "2200G", "Most specific type mismatch" },
157 { "22010", "Invalid indicator parameter value" },
158 { "22011", "Substring error" },
159 { "22012", "Division by zero" },
160 { "22015", "Interval field overflow" },
161 { "22018", "Invalid character value for cast specification" },
162 { "22019", "Invalid escape character" },
163 { "2201B", "Invalid regular expression" },
164 { "2201E", "Invalid argument for logarithm" },
165 { "2201F", "Invalid argument for power function" },
166 { "2201G", "Invalid argument for width bucket function" },
167 { "22020", "Invalid limit value" },
168 { "22021", "Character not in repertoire" },
169 { "22022", "Indicator overflow" },
170 { "22023", "Invalid parameter value" },
171 { "22024", "Unterminated c string" },
172 { "22025", "Invalid escape sequence" },
173 { "22026", "String data, length mismatch" },
174 { "22027", "Trim error" },
175 { "2202E", "Array subscript error" },
176 { "22P01", "Floating point exception" },
177 { "22P02", "Invalid text representation" },
178 { "22P03", "Invalid binary representation" },
179 { "22P04", "Bad copy file format" },
180 { "22P05", "Untranslatable character" },
181 { "23000", "Integrity constraint violation" },
182 { "23001", "Restrict violation" },
183 { "23502", "Not null violation" },
184 { "23503", "Foreign key violation" },
185 { "23505", "Unique violation" },
186 { "23514", "Check violation" },
187 { "24000", "Invalid cursor state" },
188 { "25000", "Invalid transaction state" },
189 { "25001", "Active sql transaction" },
190 { "25002", "Branch transaction already active" },
191 { "25003", "Inappropriate access mode for branch transaction" },
192 { "25004", "Inappropriate isolation level for branch transaction" },
193 { "25005", "No active sql transaction for branch transaction" },
194 { "25006", "Read only sql transaction" },
195 { "25007", "Schema and data statement mixing not supported" },
196 { "25008", "Held cursor requires same isolation level" },
197 { "25P01", "No active sql transaction" },
198 { "25P02", "In failed sql transaction" },
199 { "25S01", "Transaction state" },
200 { "25S02", "Transaction is still active" },
201 { "25S03", "Transaction is rolled back" },
202 { "26000", "Invalid sql statement name" },
203 { "27000", "Triggered data change violation" },
204 { "28000", "Invalid authorization specification" },
205 { "2B000", "Dependent privilege descriptors still exist" },
206 { "2BP01", "Dependent objects still exist" },
207 { "2D000", "Invalid transaction termination" },
208 { "2F000", "Sql routine exception" },
209 { "2F002", "Modifying sql data not permitted" },
210 { "2F003", "Prohibited sql statement attempted" },
211 { "2F004", "Reading sql data not permitted" },
212 { "2F005", "Function executed no return statement" },
213 { "34000", "Invalid cursor name" },
214 { "38000", "External routine exception" },
215 { "38001", "Containing sql not permitted" },
216 { "38002", "Modifying sql data not permitted" },
217 { "38003", "Prohibited sql statement attempted" },
218 { "38004", "Reading sql data not permitted" },
219 { "39000", "External routine invocation exception" },
220 { "39001", "Invalid sqlstate returned" },
221 { "39004", "Null value not allowed" },
222 { "39P01", "Trigger protocol violated" },
223 { "39P02", "Srf protocol violated" },
224 { "3B000", "Savepoint exception" },
225 { "3B001", "Invalid savepoint specification" },
226 { "3C000", "Duplicate cursor name" },
227 { "3D000", "Invalid catalog name" },
228 { "3F000", "Invalid schema name" },
229 { "40000", "Transaction rollback" },
230 { "40001", "Serialization failure" },
231 { "40002", "Transaction integrity constraint violation" },
232 { "40003", "Statement completion unknown" },
233 { "40P01", "Deadlock detected" },
234 { "42000", "Syntax error or access violation" },
235 { "42501", "Insufficient privilege" },
236 { "42601", "Syntax error" },
237 { "42602", "Invalid name" },
238 { "42611", "Invalid column definition" },
239 { "42622", "Name too long" },
240 { "42701", "Duplicate column" },
241 { "42702", "Ambiguous column" },
242 { "42703", "Undefined column" },
243 { "42704", "Undefined object" },
244 { "42710", "Duplicate object" },
245 { "42712", "Duplicate alias" },
246 { "42723", "Duplicate function" },
247 { "42725", "Ambiguous function" },
248 { "42803", "Grouping error" },
249 { "42804", "Datatype mismatch" },
250 { "42809", "Wrong object type" },
251 { "42830", "Invalid foreign key" },
252 { "42846", "Cannot coerce" },
253 { "42883", "Undefined function" },
254 { "42939", "Reserved name" },
255 { "42P01", "Undefined table" },
256 { "42P02", "Undefined parameter" },
257 { "42P03", "Duplicate cursor" },
258 { "42P04", "Duplicate database" },
259 { "42P05", "Duplicate prepared statement" },
260 { "42P06", "Duplicate schema" },
261 { "42P07", "Duplicate table" },
262 { "42P08", "Ambiguous parameter" },
263 { "42P09", "Ambiguous alias" },
264 { "42P10", "Invalid column reference" },
265 { "42P11", "Invalid cursor definition" },
266 { "42P12", "Invalid database definition" },
267 { "42P13", "Invalid function definition" },
268 { "42P14", "Invalid prepared statement definition" },
269 { "42P15", "Invalid schema definition" },
270 { "42P16", "Invalid table definition" },
271 { "42P17", "Invalid object definition" },
272 { "42P18", "Indeterminate datatype" },
273 { "42S01", "Base table or view already exists" },
274 { "42S02", "Base table or view not found" },
275 { "42S11", "Index already exists" },
276 { "42S12", "Index not found" },
277 { "42S21", "Column already exists" },
278 { "42S22", "Column not found" },
279 { "44000", "WITH CHECK OPTION violation" },
280 { "53000", "Insufficient resources" },
281 { "53100", "Disk full" },
282 { "53200", "Out of memory" },
283 { "53300", "Too many connections" },
284 { "54000", "Program limit exceeded" },
285 { "54001", "Statement too complex" },
286 { "54011", "Too many columns" },
287 { "54023", "Too many arguments" },
288 { "55000", "Object not in prerequisite state" },
289 { "55006", "Object in use" },
290 { "55P02", "Cant change runtime param" },
291 { "55P03", "Lock not available" },
292 { "57000", "Operator intervention" },
293 { "57014", "Query canceled" },
294 { "57P01", "Admin shutdown" },
295 { "57P02", "Crash shutdown" },
296 { "57P03", "Cannot connect now" },
297 { "58030", "Io error" },
298 { "58P01", "Undefined file" },
299 { "58P02", "Duplicate file" },
300 { "F0000", "Config file error" },
301 { "F0001", "Lock file exists" },
302 { "HY000", "General error" },
303 { "HY001", "Memory allocation error" },
304 { "HY003", "Invalid application buffer type" },
305 { "HY004", "Invalid SQL data type" },
306 { "HY007", "Associated statement is not prepared" },
307 { "HY008", "Operation canceled" },
308 { "HY009", "Invalid use of null pointer" },
309 { "HY010", "Function sequence error" },
310 { "HY011", "Attribute cannot be set now" },
311 { "HY012", "Invalid transaction operation code" },
312 { "HY013", "Memory management error" },
313 { "HY014", "Limit on the number of handles exceeded" },
314 { "HY015", "No cursor name available" },
315 { "HY016", "Cannot modify an implementation row descriptor" },
316 { "HY017", "Invalid use of an automatically allocated descriptor handle" },
317 { "HY018", "Server declined cancel request" },
318 { "HY019", "Non-character and non-binary data sent in pieces" },
319 { "HY020", "Attempt to concatenate a null value" },
320 { "HY021", "Inconsistent descriptor information" },
321 { "HY024", "Invalid attribute value" },
322 { "HY090", "Invalid string or buffer length" },
323 { "HY091", "Invalid descriptor field identifier" },
324 { "HY092", "Invalid attribute/option identifier" },
325 { "HY093", "Invalid parameter number" },
326 { "HY095", "Function type out of range" },
327 { "HY096", "Invalid information type" },
328 { "HY097", "Column type out of range" },
329 { "HY098", "Scope type out of range" },
330 { "HY099", "Nullable type out of range" },
331 { "HY100", "Uniqueness option type out of range" },
332 { "HY101", "Accuracy option type out of range" },
333 { "HY103", "Invalid retrieval code" },
334 { "HY104", "Invalid precision or scale value" },
335 { "HY105", "Invalid parameter type" },
336 { "HY106", "Fetch type out of range" },
337 { "HY107", "Row value out of range" },
338 { "HY109", "Invalid cursor position" },
339 { "HY110", "Invalid driver completion" },
340 { "HY111", "Invalid bookmark value" },
341 { "HYC00", "Optional feature not implemented" },
342 { "HYT00", "Timeout expired" },
343 { "HYT01", "Connection timeout expired" },
344 { "IM001", "Driver does not support this function" },
345 { "IM002", "Data source name not found and no default driver specified" },
346 { "IM003", "Specified driver could not be loaded" },
347 { "IM004", "Driver's SQLAllocHandle on SQL_HANDLE_ENV failed" },
348 { "IM005", "Driver's SQLAllocHandle on SQL_HANDLE_DBC failed" },
349 { "IM006", "Driver's SQLSetConnectAttr failed" },
350 { "IM007", "No data source or driver specified; dialog prohibited" },
351 { "IM008", "Dialog failed" },
352 { "IM009", "Unable to load translation DLL" },
353 { "IM010", "Data source name too long" },
354 { "IM011", "Driver name too long" },
355 { "IM012", "DRIVER keyword syntax error" },
356 { "IM013", "Trace file error" },
357 { "IM014", "Invalid name of File DSN" },
358 { "IM015", "Corrupt file data source" },
359 { "P0000", "Plpgsql error" },
360 { "P0001", "Raise exception" },
361 { "XX000", "Internal error" },
362 { "XX001", "Data corrupted" },
363 { "XX002", "Index corrupted" }
366 struct PDOErrorHash
: private hphp_const_char_map
<const char *> {
368 for (unsigned int i
= 0;
369 i
< sizeof(err_initializer
)/sizeof(err_initializer
[0]); i
++) {
370 const struct pdo_sqlstate_info
*info
= &err_initializer
[i
];
371 (*this)[info
->state
] = info
->desc
;
375 const char *description(const char *state
) {
376 const_iterator iter
= find(state
);
380 return "<<Unknown error>>";
383 static PDOErrorHash s_err_hash
;
387 s_message("message"),
388 s_errorInfo("errorInfo"),
389 s_PDOException("PDOException");
391 void throw_pdo_exception(const Variant
& code
, const Variant
& info
,
392 const char *fmt
, ...) {
393 auto obj
= SystemLib::AllocPDOExceptionObject();
394 obj
->o_set(s_code
, code
, s_PDOException
);
399 string_vsnprintf(msg
, fmt
, ap
);
400 obj
->o_set(s_message
, String(msg
), s_PDOException
);
403 if (!info
.isNull()) {
404 obj
->o_set(s_errorInfo
, info
, s_PDOException
);
409 void pdo_raise_impl_error(sp_PDOResource rsrc
, PDOStatement
* stmt
,
410 const char *sqlstate
, const char *supp
) {
411 auto const& dbh
= rsrc
->conn();
413 PDOErrorType
*pdo_err
= &dbh
->error_code
;
415 pdo_err
= &stmt
->error_code
;
417 setPDOError(*pdo_err
, sqlstate
);
419 const char *msg
= s_err_hash
.description(sqlstate
);
420 string err
= "SQLSTATE["; err
+= sqlstate
; err
+= "]: "; err
+= msg
;
422 err
+= ": "; err
+= supp
;
425 if (dbh
->error_mode
!= PDO_ERRMODE_EXCEPTION
) {
426 raise_warning("%s", err
.c_str());
429 info
.append(String(*pdo_err
, CopyString
));
431 throw_pdo_exception(String(sqlstate
, CopyString
), info
, "%s", err
.c_str());
435 void pdo_raise_impl_error(sp_PDOResource rsrc
, sp_PDOStatement stmt
,
436 const char *sqlstate
, const char *supp
) {
437 pdo_raise_impl_error(rsrc
, stmt
.get(), sqlstate
, supp
);
442 void pdo_handle_error(sp_PDOResource rsrc
, PDOStatement
* stmt
) {
443 auto const& dbh
= rsrc
->conn();
445 if (dbh
->error_mode
== PDO_ERRMODE_SILENT
) {
448 PDOErrorType
*pdo_err
= &dbh
->error_code
;
450 pdo_err
= &stmt
->error_code
;
453 /* hash sqlstate to error messages */
454 const char *msg
= s_err_hash
.description(*pdo_err
);
456 int64_t native_code
= 0;
459 if (dbh
->support(PDOConnection::MethodFetchErr
)) {
460 info
= Array::Create();
461 info
.append(String(*pdo_err
, CopyString
));
462 if (dbh
->fetchErr(stmt
, info
)) {
463 if (info
.exists(1)) {
464 native_code
= info
[1].toInt64();
466 if (info
.exists(2)) {
467 supp
= info
[2].toString();
472 string err
= "SQLSTATE["; err
+= *pdo_err
; err
+= "]: "; err
+= msg
;
474 err
+= ": "; err
+= String(native_code
).data();
475 err
+= " "; err
+= supp
.data();
478 if (dbh
->error_mode
!= PDO_ERRMODE_EXCEPTION
) {
479 raise_warning("%s", err
.c_str());
481 throw_pdo_exception(String(*pdo_err
, CopyString
), info
, "%s", err
.c_str());
485 void pdo_handle_error(sp_PDOResource rsrc
, sp_PDOStatement stmt
) {
486 pdo_handle_error(rsrc
, stmt
.get());
491 ///////////////////////////////////////////////////////////////////////////////
492 // helpers for PDO class
494 static inline int64_t pdo_attr_lval(const Array
& options
, PDOAttributeType name
,
496 if (options
.exists(name
)) {
497 return options
[name
].toInt64();
502 static Object
pdo_stmt_instantiate(sp_PDOResource dbh
, const String
& clsname
,
503 const Variant
& ctor_args
) {
504 String name
= clsname
;
506 name
= "PDOStatement";
508 if (!ctor_args
.isNull() && !ctor_args
.isArray()) {
509 pdo_raise_impl_error(dbh
, nullptr, "HY000",
510 "constructor arguments must be passed as an array");
513 Class
* cls
= Unit::loadClass(name
.get());
520 const StaticString
s_queryString("queryString");
522 static void pdo_stmt_construct(sp_PDOStatement stmt
, Object object
,
523 const String
& clsname
,
524 const Variant
& ctor_args
) {
525 object
->setProp(nullptr, s_queryString
.get(),
526 make_tv
<KindOfString
>(stmt
->query_string
.get()));
527 if (clsname
.empty()) {
530 Class
* cls
= Unit::loadClass(clsname
.get());
534 ObjectData
* inst
= object
.get();
536 g_context
->invokeFunc(cls
->getCtor(), ctor_args
.toArray(), inst
)
540 static bool valid_statement_class(sp_PDOResource dbh
, const Variant
& opt
,
541 String
&clsname
, Variant
&ctor_args
) {
542 if (!opt
.isArray() || !opt
.toArray().exists(0) ||
543 !opt
.toArray()[0].isString() ||
544 !HHVM_FN(class_exists
)(opt
.toArray()[0].toString())) {
546 (dbh
, nullptr, "HY000",
547 "PDO::ATTR_STATEMENT_CLASS requires format array(classname, "
548 "array(ctor_args)); the classname must be a string specifying "
549 "an existing class");
550 PDO_HANDLE_DBH_ERR(dbh
);
553 clsname
= opt
.toArray()[0].toString();
554 if (clsname
== String("PDOStatement")) {
555 ctor_args
= Variant(Array());
558 if (!HHVM_FN(is_a
)(clsname
, "PDOStatement", /* allow_string */ true)) {
560 (dbh
, nullptr, "HY000",
561 "user-supplied statement class must be derived from PDOStatement");
562 PDO_HANDLE_DBH_ERR(dbh
);
565 HPHP::Class
* cls
= HPHP::Unit::loadClass(clsname
.get());
567 const HPHP::Func
* method
= cls
->getDeclaredCtor();
568 if (method
&& method
->isPublic()) {
570 (dbh
, nullptr, "HY000",
571 "user-supplied statement class cannot have a public constructor");
572 PDO_HANDLE_DBH_ERR(dbh
);
576 if (opt
.toArray().exists(1)) {
577 Variant item
= opt
.toArray()[1];
578 if (!item
.isArray()) {
580 (dbh
, nullptr, "HY000",
581 "PDO::ATTR_STATEMENT_CLASS requires format array(classname, "
582 "ctor_args); ctor_args must be an array");
583 PDO_HANDLE_DBH_ERR(dbh
);
591 static bool pdo_stmt_describe_columns(sp_PDOStatement stmt
) {
592 for (int col
= 0; col
< stmt
->column_count
; col
++) {
593 if (!stmt
->describer(col
)) {
597 auto column
= cast
<PDOColumn
>(stmt
->columns
[col
]);
599 /* if we are applying case conversions on column names, do so now */
600 if (stmt
->dbh
->conn()->native_case
!= stmt
->dbh
->conn()->desired_case
&&
601 stmt
->dbh
->conn()->desired_case
!= PDO_CASE_NATURAL
) {
602 switch (stmt
->dbh
->conn()->desired_case
) {
604 column
->name
= HHVM_FN(strtoupper
)(column
->name
);
607 column
->name
= HHVM_FN(strtolower
)(column
->name
);
613 if (stmt
->bound_columns
.exists(column
->name
)) {
614 auto param
= cast
<PDOBoundParam
>(stmt
->bound_columns
[column
->name
]);
615 param
->paramno
= col
;
621 static bool pdo_stmt_verify_mode(sp_PDOStatement stmt
, int64_t mode
,
623 int flags
= mode
& PDO_FETCH_FLAGS
;
624 mode
= mode
& ~PDO_FETCH_FLAGS
;
626 if (mode
< 0 || mode
> PDO_FETCH__MAX
) {
627 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000", "invalid fetch mode");
631 if (mode
== PDO_FETCH_USE_DEFAULT
) {
632 flags
= stmt
->default_fetch_type
& PDO_FETCH_FLAGS
;
633 mode
= stmt
->default_fetch_type
& ~PDO_FETCH_FLAGS
;
639 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
640 "PDO::FETCH_FUNC is only allowed in "
641 "PDOStatement::fetchAll()");
648 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
649 "PDO::FETCH_LAZY can't be used with "
650 "PDOStatement::fetchAll()");
655 if ((flags
& PDO_FETCH_SERIALIZE
) == PDO_FETCH_SERIALIZE
) {
656 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
657 "PDO::FETCH_SERIALIZE can only be used "
658 "together with PDO::FETCH_CLASS");
661 if ((flags
& PDO_FETCH_CLASSTYPE
) == PDO_FETCH_CLASSTYPE
) {
662 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
663 "PDO::FETCH_CLASSTYPE can only be used "
664 "together with PDO::FETCH_CLASS");
667 if (mode
>= PDO_FETCH__MAX
) {
668 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000", "invalid fetch mode");
673 case PDO_FETCH_CLASS
:
679 static bool do_fetch_class_prepare(sp_PDOStatement stmt
) {
680 String clsname
= stmt
->fetch
.clsname
;
681 if (clsname
.empty()) {
682 stmt
->fetch
.clsname
= "stdclass";
684 stmt
->fetch
.constructor
= empty_string(); //NULL;
685 HPHP::Class
* cls
= HPHP::Unit::loadClass(clsname
.get());
687 const HPHP::Func
* method
= cls
->getDeclaredCtor();
689 stmt
->fetch
.constructor
= method
->nameStr();
693 if (!stmt
->fetch
.ctor_args
.isNull()) {
694 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
695 "user-supplied class does not have a constructor, "
696 "use NULL for the ctor_params parameter, or simply "
700 return true; /* no ctor no args is also ok */
703 static bool pdo_stmt_set_fetch_mode(sp_PDOStatement stmt
, int _argc
,
704 int64_t mode
, const Array
& _argv
) {
705 _argc
= _argv
.size() + 1;
707 if (stmt
->default_fetch_type
== PDO_FETCH_INTO
) {
708 stmt
->fetch
.into
.unset();
710 stmt
->default_fetch_type
= PDO_FETCH_BOTH
;
712 if (!pdo_stmt_verify_mode(stmt
, mode
, false)) {
713 setPDOErrorNone(stmt
->error_code
);
717 int flags
= mode
& PDO_FETCH_FLAGS
;
719 switch (mode
& ~PDO_FETCH_FLAGS
) {
720 case PDO_FETCH_USE_DEFAULT
:
722 case PDO_FETCH_ASSOC
:
726 case PDO_FETCH_BOUND
:
727 case PDO_FETCH_NAMED
:
728 case PDO_FETCH_KEY_PAIR
:
730 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
731 "fetch mode doesn't allow any extra arguments");
737 case PDO_FETCH_COLUMN
:
739 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
740 "fetch mode requires the colno argument");
741 } else if (!_argv
[0].isInteger()) {
742 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
743 "colno must be an integer");
745 stmt
->fetch
.column
= _argv
[0].toInt64();
750 case PDO_FETCH_CLASS
:
751 /* Gets its class name from 1st column */
752 if ((flags
& PDO_FETCH_CLASSTYPE
) == PDO_FETCH_CLASSTYPE
) {
754 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
755 "fetch mode doesn't allow any extra arguments");
757 stmt
->fetch
.clsname
.clear();
762 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
763 "fetch mode requires the classname argument");
764 } else if (_argc
> 3) {
765 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
766 "too many arguments");
767 } else if (!_argv
[0].isString()) {
768 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
769 "classname must be a string");
771 retval
= HHVM_FN(class_exists
)(_argv
[0].toString());
773 stmt
->fetch
.clsname
= _argv
[0].toString();
779 stmt
->fetch
.ctor_args
.unset();
781 if (!_argv
[1].isNull() && !_argv
[1].isArray()) {
782 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
783 "ctor_args must be either NULL or an array");
786 stmt
->fetch
.ctor_args
= _argv
[1];
791 do_fetch_class_prepare(stmt
);
799 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
800 "fetch mode requires the object parameter");
801 } else if (!_argv
[0].isObject()) {
802 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
803 "object must be an object");
809 stmt
->fetch
.into
= _argv
[0];
814 pdo_raise_impl_error(stmt
->dbh
, stmt
, "22003",
815 "Invalid fetch mode specified");
819 stmt
->default_fetch_type
= (PDOFetchType
)mode
;
823 * PDO error (if any) has already been raised at this point.
825 * The error_code is cleared, otherwise the caller will read the
826 * last error message from the driver.
829 setPDOErrorNone(stmt
->error_code
);
833 ///////////////////////////////////////////////////////////////////////////////
834 // forward declarations
836 static bool HHVM_METHOD(PDO
, setattribute
, int64_t attribute
,
837 const Variant
& value
);
839 ///////////////////////////////////////////////////////////////////////////////
843 thread_local
std::unordered_map
<std::string
,sp_PDOConnection
> s_connections
;
846 const StaticString
s_PDO("PDO");
848 static void HHVM_METHOD(PDO
, __construct
, const String
& dsn
,
849 const String
& username
/* = null_string */,
850 const String
& password
/* = null_string */,
851 const Variant
& optionsV
/* = null_array */) {
852 auto data
= Native::data
<PDOData
>(this_
);
853 auto options
= optionsV
.isNull() ? null_array
: optionsV
.toArray();
855 String data_source
= dsn
;
857 /* parse the data source name */
858 const char *colon
= strchr(data_source
.data(), ':');
860 /* let's see if this string has a matching dsn in the php.ini */
861 String name
= "pdo.dsn."; name
+= data_source
;
863 if (!IniSetting::Get(name
, ini_dsn
)) {
864 throw_pdo_exception(uninit_null(), uninit_null(),
865 "invalid data source name");
867 data_source
= ini_dsn
;
868 colon
= strchr(data_source
.data(), ':');
870 throw_pdo_exception(uninit_null(), uninit_null(),
871 "invalid data source name (via INI: %s)",
876 if (!strncmp(data_source
.data(), "uri:", 4)) {
877 /* the specified URI holds connection details */
878 auto file
= File::Open(data_source
.substr(4), "rb");
879 if (!file
|| file
->isInvalid()) {
880 throw_pdo_exception(uninit_null(), uninit_null(),
881 "invalid data source URI");
883 data_source
= file
->readLine(1024);
884 colon
= strchr(data_source
.data(), ':');
886 throw_pdo_exception(uninit_null(), uninit_null(),
887 "invalid data source name (via URI)");
891 const PDODriverMap
&drivers
= PDODriver::GetDrivers();
892 String name
= data_source
.substr(0, colon
- data_source
.data());
893 PDODriverMap::const_iterator iter
= drivers
.find(name
.data());
894 if (iter
== drivers
.end()) {
895 /* NB: don't want to include the data_source in the error message as
896 * it might contain a password */
897 throw_pdo_exception(uninit_null(), uninit_null(), "could not find driver");
899 PDODriver
*driver
= iter
->second
;
901 /* is this supposed to be a persistent connection ? */
902 bool is_persistent
= false;
903 bool call_factory
= true;
904 std::string shashkey
;
905 if (!options
.empty()) {
906 StringBuffer hashkey
;
907 if (options
.exists(PDO_ATTR_PERSISTENT
)) {
908 Variant v
= options
[PDO_ATTR_PERSISTENT
];
909 String sv
= v
.toString();
910 if (v
.isString() && !sv
.isNumeric() && !sv
.empty()) {
911 /* user specified key */
912 hashkey
.printf("PDO:DBH:DSN=%s:%s:%s:%s",
913 data_source
.data(), username
.data(),
914 password
.data(), sv
.data());
915 is_persistent
= true;
917 is_persistent
= v
.toInt64();
918 hashkey
.printf("PDO:DBH:DSN=%s:%s:%s",
919 data_source
.data(), username
.data(),
925 shashkey
= hashkey
.detach().toCppString();
927 /* let's see if we have one cached.... */
928 if (s_connections
.count(shashkey
)) {
929 auto const conn
= s_connections
[shashkey
];
930 data
->m_dbh
= driver
->createResource(conn
);
932 /* is the connection still alive ? */
933 if (conn
->support(PDOConnection::MethodCheckLiveness
) &&
934 !conn
->checkLiveness()) {
935 /* nope... need to kill it */
936 data
->m_dbh
= nullptr;
941 call_factory
= false;
943 /* need a brand new pdbh */
944 data
->m_dbh
= driver
->createResource(colon
+ 1, username
,
947 throw_pdo_exception(uninit_null(), uninit_null(),
948 "unable to create a connection");
950 data
->m_dbh
->conn()->persistent_id
= shashkey
;
955 data
->m_dbh
= driver
->createResource(colon
+ 1, username
,
958 throw_pdo_exception(uninit_null(), uninit_null(),
959 "unable to create a connection");
964 data
->m_dbh
->conn()->default_fetch_type
= PDO_FETCH_BOTH
;
967 data
->m_dbh
->conn()->auto_commit
=
968 pdo_attr_lval(options
, PDO_ATTR_AUTOCOMMIT
, 1);
971 /* we got a persistent guy from our cache */
972 for (ArrayIter
iter(options
); iter
; ++iter
) {
973 HHVM_MN(PDO
, setattribute
)(this_
, iter
.first().toInt64(),
976 } else if (data
->m_dbh
) {
978 assert(!shashkey
.empty());
979 s_connections
[shashkey
] = data
->m_dbh
->conn();
982 data
->m_dbh
->conn()->driver
= driver
;
983 for (ArrayIter
iter(options
); iter
; ++iter
) {
984 HHVM_MN(PDO
, setattribute
)(this_
, iter
.first().toInt64(),
990 static Variant
HHVM_METHOD(PDO
, prepare
, const String
& statement
,
991 const Array
& options
= null_array
) {
992 auto data
= Native::data
<PDOData
>(this_
);
994 assert(data
->m_dbh
->conn()->driver
);
995 setPDOErrorNone(data
->m_dbh
->conn()->error_code
);
996 data
->m_dbh
->query_stmt
= nullptr;
1000 if (options
.exists(PDO_ATTR_STATEMENT_CLASS
)) {
1001 Variant opt
= options
[PDO_ATTR_STATEMENT_CLASS
];
1002 if (!valid_statement_class(data
->m_dbh
, opt
, clsname
, ctor_args
)) {
1006 clsname
= data
->m_dbh
->conn()->def_stmt_clsname
;
1007 ctor_args
= data
->m_dbh
->def_stmt_ctor_args
;
1010 Object ret
= pdo_stmt_instantiate(data
->m_dbh
, clsname
, ctor_args
);
1012 pdo_raise_impl_error
1013 (data
->m_dbh
, nullptr, "HY000",
1014 "failed to instantiate user-supplied statement class");
1015 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1018 PDOStatementData
*pdostmt
= Native::data
<PDOStatementData
>(ret
);
1020 if (data
->m_dbh
->conn()->preparer(statement
, &pdostmt
->m_stmt
, options
)) {
1021 auto stmt
= pdostmt
->m_stmt
;
1024 /* unconditionally keep this for later reference */
1025 stmt
->query_string
= statement
;
1026 stmt
->default_fetch_type
= data
->m_dbh
->conn()->default_fetch_type
;
1027 stmt
->dbh
= data
->m_dbh
;
1029 pdo_stmt_construct(stmt
, ret
, clsname
, ctor_args
);
1033 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1037 static bool HHVM_METHOD(PDO
, begintransaction
) {
1038 auto data
= Native::data
<PDOData
>(this_
);
1040 if (data
->m_dbh
->conn()->in_txn
) {
1041 throw_pdo_exception(uninit_null(), uninit_null(),
1042 "There is already an active transaction");
1044 if (data
->m_dbh
->conn()->begin()) {
1045 data
->m_dbh
->conn()->in_txn
= 1;
1048 if (strcmp(data
->m_dbh
->conn()->error_code
, PDO_ERR_NONE
)) {
1049 pdo_handle_error(data
->m_dbh
, nullptr);
1054 static bool HHVM_METHOD(PDO
, commit
) {
1055 auto data
= Native::data
<PDOData
>(this_
);
1057 assert(data
->m_dbh
->conn()->driver
);
1058 if (!data
->m_dbh
->conn()->in_txn
) {
1059 throw_pdo_exception(uninit_null(), uninit_null(),
1060 "There is no active transaction");
1062 if (data
->m_dbh
->conn()->commit()) {
1063 data
->m_dbh
->conn()->in_txn
= 0;
1066 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1070 static bool HHVM_METHOD(PDO
, intransaction
) {
1071 auto data
= Native::data
<PDOData
>(this_
);
1073 assert(data
->m_dbh
->conn()->driver
);
1074 return data
->m_dbh
->conn()->in_txn
;
1077 static bool HHVM_METHOD(PDO
, rollback
) {
1078 auto data
= Native::data
<PDOData
>(this_
);
1080 assert(data
->m_dbh
->conn()->driver
);
1081 if (!data
->m_dbh
->conn()->in_txn
) {
1082 throw_pdo_exception(uninit_null(), uninit_null(),
1083 "There is no active transaction");
1085 if (data
->m_dbh
->conn()->rollback()) {
1086 data
->m_dbh
->conn()->in_txn
= 0;
1089 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1093 static bool HHVM_METHOD(PDO
, setattribute
, int64_t attribute
,
1094 const Variant
& value
) {
1095 auto data
= Native::data
<PDOData
>(this_
);
1097 assert(data
->m_dbh
->conn()->driver
);
1099 #define PDO_LONG_PARAM_CHECK \
1100 if (!value.isInteger() && !value.isString() && !value.isBoolean()) { \
1101 pdo_raise_impl_error(data->m_dbh, nullptr, "HY000", \
1102 "attribute value must be an integer"); \
1103 PDO_HANDLE_DBH_ERR(data->m_dbh); \
1107 switch (attribute) {
1108 case PDO_ATTR_ERRMODE
:
1109 PDO_LONG_PARAM_CHECK
;
1110 switch (value
.toInt64()) {
1111 case PDO_ERRMODE_SILENT
:
1112 case PDO_ERRMODE_WARNING
:
1113 case PDO_ERRMODE_EXCEPTION
:
1114 data
->m_dbh
->conn()->error_mode
= (PDOErrorMode
)value
.toInt64();
1117 pdo_raise_impl_error(data
->m_dbh
, nullptr, "HY000", "invalid error mode");
1118 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1124 PDO_LONG_PARAM_CHECK
;
1125 switch (value
.toInt64()) {
1126 case PDO_CASE_NATURAL
:
1127 case PDO_CASE_UPPER
:
1128 case PDO_CASE_LOWER
:
1129 data
->m_dbh
->conn()->desired_case
= (PDOCaseConversion
)value
.toInt64();
1132 pdo_raise_impl_error(data
->m_dbh
, nullptr, "HY000",
1133 "invalid case folding mode");
1134 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1139 case PDO_ATTR_ORACLE_NULLS
:
1140 PDO_LONG_PARAM_CHECK
;
1141 data
->m_dbh
->conn()->oracle_nulls
= value
.toInt64();
1144 case PDO_ATTR_DEFAULT_FETCH_MODE
:
1145 if (value
.isArray()) {
1146 if (value
.toCArrRef().exists(0)) {
1147 Variant tmp
= value
.toCArrRef()[0];
1148 if (tmp
.isInteger() && ((tmp
.toInt64() == PDO_FETCH_INTO
||
1149 tmp
.toInt64() == PDO_FETCH_CLASS
))) {
1150 pdo_raise_impl_error(data
->m_dbh
, nullptr, "HY000",
1151 "FETCH_INTO and FETCH_CLASS are not yet "
1152 "supported as default fetch modes");
1157 PDO_LONG_PARAM_CHECK
;
1159 if (value
.toInt64() == PDO_FETCH_USE_DEFAULT
) {
1160 pdo_raise_impl_error(data
->m_dbh
, nullptr,
1161 "HY000", "invalid fetch mode type");
1164 data
->m_dbh
->conn()->default_fetch_type
= (PDOFetchType
)value
.toInt64();
1167 case PDO_ATTR_STRINGIFY_FETCHES
:
1168 PDO_LONG_PARAM_CHECK
;
1169 data
->m_dbh
->conn()->stringify
= value
.toInt64() ? 1 : 0;
1172 case PDO_ATTR_STATEMENT_CLASS
:
1174 if (data
->m_dbh
->conn()->is_persistent
) {
1175 pdo_raise_impl_error(data
->m_dbh
, nullptr, "HY000",
1176 "PDO::ATTR_STATEMENT_CLASS cannot be used "
1177 "with persistent PDO instances");
1178 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1182 if (!valid_statement_class(data
->m_dbh
, value
, clsname
,
1183 data
->m_dbh
->def_stmt_ctor_args
)) {
1186 data
->m_dbh
->conn()->def_stmt_clsname
= clsname
.c_str();
1191 if (data
->m_dbh
->conn()->support(PDOConnection::MethodSetAttribute
)) {
1192 setPDOErrorNone(data
->m_dbh
->conn()->error_code
);
1193 data
->m_dbh
->query_stmt
= nullptr;
1194 if (data
->m_dbh
->conn()->setAttribute(attribute
, value
)) {
1199 if (attribute
== PDO_ATTR_AUTOCOMMIT
) {
1200 throw_pdo_exception(uninit_null(), uninit_null(),
1201 "The auto-commit mode cannot be changed for this "
1203 } else if (!data
->m_dbh
->conn()->support(PDOConnection::MethodSetAttribute
)) {
1204 pdo_raise_impl_error(data
->m_dbh
, nullptr, "IM001",
1205 "driver does not support setting attributes");
1207 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1212 static Variant
HHVM_METHOD(PDO
, getattribute
, int64_t attribute
) {
1213 auto data
= Native::data
<PDOData
>(this_
);
1215 assert(data
->m_dbh
->conn()->driver
);
1216 setPDOErrorNone(data
->m_dbh
->conn()->error_code
);
1217 data
->m_dbh
->query_stmt
= nullptr;
1219 /* handle generic PDO-level attributes */
1220 switch (attribute
) {
1221 case PDO_ATTR_PERSISTENT
:
1222 return (bool)data
->m_dbh
->conn()->is_persistent
;
1225 return (int64_t)data
->m_dbh
->conn()->desired_case
;
1227 case PDO_ATTR_ORACLE_NULLS
:
1228 return (int64_t)data
->m_dbh
->conn()->oracle_nulls
;
1230 case PDO_ATTR_ERRMODE
:
1231 return (int64_t)data
->m_dbh
->conn()->error_mode
;
1233 case PDO_ATTR_DRIVER_NAME
:
1234 return String(data
->m_dbh
->conn()->driver
->getName());
1236 case PDO_ATTR_STATEMENT_CLASS
: {
1238 ret
.append(String(data
->m_dbh
->conn()->def_stmt_clsname
));
1239 if (!data
->m_dbh
->def_stmt_ctor_args
.isNull()) {
1240 ret
.append(data
->m_dbh
->def_stmt_ctor_args
);
1244 case PDO_ATTR_DEFAULT_FETCH_MODE
:
1245 return (int64_t)data
->m_dbh
->conn()->default_fetch_type
;
1248 if (!data
->m_dbh
->conn()->support(PDOConnection::MethodGetAttribute
)) {
1249 pdo_raise_impl_error(data
->m_dbh
, nullptr, "IM001",
1250 "driver does not support getting attributes");
1255 switch (data
->m_dbh
->conn()->getAttribute(attribute
, ret
)) {
1257 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1260 pdo_raise_impl_error(data
->m_dbh
, nullptr, "IM001",
1261 "driver does not support that attribute");
1267 static Variant
HHVM_METHOD(PDO
, exec
, const String
& query
) {
1268 auto data
= Native::data
<PDOData
>(this_
);
1270 SYNC_VM_REGS_SCOPED();
1271 if (query
.empty()) {
1272 pdo_raise_impl_error(data
->m_dbh
, nullptr, "HY000",
1273 "trying to execute an empty query");
1277 assert(data
->m_dbh
->conn()->driver
);
1278 setPDOErrorNone(data
->m_dbh
->conn()->error_code
);
1279 data
->m_dbh
->query_stmt
= nullptr;
1281 int64_t ret
= data
->m_dbh
->conn()->doer(query
);
1283 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1289 static Variant
HHVM_METHOD(PDO
, lastinsertid
,
1290 const String
& seqname
/* = null_string */) {
1291 auto data
= Native::data
<PDOData
>(this_
);
1293 assert(data
->m_dbh
->conn()->driver
);
1294 setPDOErrorNone(data
->m_dbh
->conn()->error_code
);
1295 data
->m_dbh
->query_stmt
= nullptr;
1297 if (!data
->m_dbh
->conn()->support(PDOConnection::MethodLastId
)) {
1298 pdo_raise_impl_error(data
->m_dbh
, nullptr, "IM001",
1299 "driver does not support lastInsertId()");
1303 String ret
= data
->m_dbh
->conn()->lastId(seqname
.data());
1305 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1311 static Variant
HHVM_METHOD(PDO
, errorcode
) {
1312 auto data
= Native::data
<PDOData
>(this_
);
1314 assert(data
->m_dbh
->conn()->driver
);
1315 if (data
->m_dbh
->query_stmt
) {
1316 return String(data
->m_dbh
->query_stmt
->error_code
, CopyString
);
1319 if (data
->m_dbh
->conn()->error_code
[0] == '\0') {
1324 * Making sure that we fallback to the default implementation
1325 * if the dbh->error_code is not null.
1327 return String(data
->m_dbh
->conn()->error_code
, CopyString
);
1330 static Array
HHVM_METHOD(PDO
, errorinfo
) {
1331 auto data
= Native::data
<PDOData
>(this_
);
1333 assert(data
->m_dbh
->conn()->driver
);
1336 if (data
->m_dbh
->query_stmt
) {
1337 ret
.append(String(data
->m_dbh
->query_stmt
->error_code
, CopyString
));
1339 ret
.append(String(data
->m_dbh
->conn()->error_code
, CopyString
));
1342 if (data
->m_dbh
->conn()->support(PDOConnection::MethodFetchErr
)) {
1343 data
->m_dbh
->conn()->fetchErr(data
->m_dbh
->query_stmt
, ret
);
1347 * In order to be consistent, we have to make sure we add the good amount
1348 * of nulls depending on the current number of elements. We make a simple
1349 * difference and add the needed elements
1351 int error_count
= ret
.size();
1352 int error_expected_count
= 3;
1353 if (error_expected_count
> error_count
) {
1354 int error_count_diff
= error_expected_count
- error_count
;
1355 for (int i
= 0; i
< error_count_diff
; i
++) {
1356 ret
.append(init_null_variant
);
1362 static Variant
HHVM_METHOD(PDO
, query
, const String
& sql
,
1363 const Array
& _argv
) {
1365 auto data
= Native::data
<PDOData
>(this_
);
1366 SYNC_VM_REGS_SCOPED();
1367 assert(data
->m_dbh
->conn()->driver
);
1368 setPDOErrorNone(data
->m_dbh
->conn()->error_code
);
1369 data
->m_dbh
->query_stmt
= nullptr;
1371 Object ret
= pdo_stmt_instantiate(data
->m_dbh
,
1372 data
->m_dbh
->conn()->def_stmt_clsname
,
1373 data
->m_dbh
->def_stmt_ctor_args
);
1375 pdo_raise_impl_error
1376 (data
->m_dbh
, nullptr, "HY000",
1377 "failed to instantiate user supplied statement class");
1380 PDOStatementData
*pdostmt
= Native::data
<PDOStatementData
>(ret
);
1382 if (data
->m_dbh
->conn()->preparer(sql
, &pdostmt
->m_stmt
, Array())) {
1383 auto stmt
= pdostmt
->m_stmt
;
1386 /* unconditionally keep this for later reference */
1387 stmt
->query_string
= sql
;
1388 stmt
->default_fetch_type
= data
->m_dbh
->conn()->default_fetch_type
;
1389 stmt
->active_query_string
= stmt
->query_string
;
1390 stmt
->dbh
= data
->m_dbh
;
1391 stmt
->lazy_object_ref
.unset();
1393 setPDOErrorNone(stmt
->error_code
);
1395 // when we add support for varargs here, we only need to set the stmt if
1396 // the argument count is > 1
1397 int argc
= _argv
.size() + 1;
1399 pdo_stmt_set_fetch_mode(
1402 tvCastToInt64(_argv
.rvalAt(0).tv()),
1403 Variant::attach(HHVM_FN(array_splice
)(_argv
, 1)).toArray()
1405 /* now execute the statement */
1406 setPDOErrorNone(stmt
->error_code
);
1407 if (stmt
->executer()) {
1409 if (!stmt
->executed
) {
1410 if (stmt
->dbh
->conn()->alloc_own_columns
) {
1411 ok
= pdo_stmt_describe_columns(stmt
);
1416 pdo_stmt_construct(stmt
, ret
, data
->m_dbh
->conn()->def_stmt_clsname
,
1417 data
->m_dbh
->def_stmt_ctor_args
);
1422 /* something broke */
1423 data
->m_dbh
->query_stmt
= stmt
.get();
1424 PDO_HANDLE_STMT_ERR(stmt
);
1426 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1432 static Variant
HHVM_METHOD(PDO
, quote
, const String
& str
,
1433 int64_t paramtype
/* = PDO_PARAM_STR */) {
1434 auto data
= Native::data
<PDOData
>(this_
);
1436 assert(data
->m_dbh
->conn()->driver
);
1437 setPDOErrorNone(data
->m_dbh
->conn()->error_code
);
1438 data
->m_dbh
->query_stmt
= nullptr;
1440 if (!data
->m_dbh
->conn()->support(PDOConnection::MethodQuoter
)) {
1441 pdo_raise_impl_error(data
->m_dbh
, nullptr, "IM001",
1442 "driver does not support quoting");
1447 if (data
->m_dbh
->conn()->quoter(str
, quoted
, (PDOParamType
)paramtype
)) {
1450 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1454 static bool HHVM_METHOD(PDO
, sqlitecreatefunction
, const String
& name
,
1455 const Variant
& callback
, int64_t argcount
/* = -1 */) {
1456 #ifdef ENABLE_EXTENSION_PDO_SQLITE
1457 auto data
= Native::data
<PDOData
>(this_
);
1459 auto res
= dynamic_cast<PDOSqliteResource
*>(data
->m_dbh
.get());
1460 if (res
== nullptr) {
1463 return res
->createFunction(name
, callback
, argcount
);
1465 raise_recoverable_error("PDO::sqliteCreateFunction not implemented");
1470 static bool HHVM_METHOD(PDO
, sqlitecreateaggregate
, const String
& /*name*/,
1471 const Variant
& /*step*/, const Variant
& /*final*/,
1472 int64_t /*argcount*/ /* = -1 */) {
1473 raise_recoverable_error("PDO::sqliteCreateAggregate not implemented");
1477 static Variant
HHVM_METHOD(PDO
, __wakeup
) {
1478 throw_pdo_exception(uninit_null(), uninit_null(),
1479 "You cannot serialize or unserialize PDO instances");
1483 static Variant
HHVM_METHOD(PDO
, __sleep
) {
1484 throw_pdo_exception(uninit_null(), uninit_null(),
1485 "You cannot serialize or unserialize PDO instances");
1489 static Array
HHVM_STATIC_METHOD(PDO
, getAvailableDrivers
) {
1490 return HHVM_FN(pdo_drivers
)();
1493 ///////////////////////////////////////////////////////////////////////////////
1495 static inline bool rewrite_name_to_position(sp_PDOStatement stmt
,
1496 sp_PDOBoundParam param
) {
1497 if (!stmt
->bound_param_map
.empty()) {
1498 /* rewriting :name to ? style.
1499 * We need to fixup the parameter numbers on the parameters.
1500 * If we find that a given named parameter has been used twice,
1501 * we will raise an error, as we can't be sure that it is safe
1502 * to bind multiple parameters onto the same zval in the underlying
1504 if (stmt
->named_rewrite_template
) {
1505 /* this is not an error here */
1508 if (param
->name
.empty()) {
1509 /* do the reverse; map the parameter number to the name */
1510 if (stmt
->bound_param_map
.exists(param
->paramno
)) {
1511 param
->name
= stmt
->bound_param_map
[param
->paramno
].toString();
1514 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY093",
1515 "parameter was not defined");
1520 for (ArrayIter
iter(stmt
->bound_param_map
); iter
; ++iter
, ++position
) {
1521 if (iter
.second().toString() == param
->name
) {
1522 if (param
->paramno
>= 0) {
1523 pdo_raise_impl_error
1524 (stmt
->dbh
, stmt
, "IM001",
1525 "PDO refuses to handle repeating the same :named parameter "
1526 "for multiple positions with this driver, as it might be "
1527 "unsafe to do so. Consider using a separate name for each "
1528 "parameter instead");
1531 param
->paramno
= position
;
1535 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY093",
1536 "parameter was not defined");
1542 /* trigger callback hook for parameters */
1543 static bool dispatch_param_event(sp_PDOStatement stmt
,
1544 PDOParamEvent event_type
) {
1545 if (!stmt
->support(PDOStatement::MethodParamHook
)) {
1548 for (ArrayIter
iter(stmt
->bound_params
); iter
; ++iter
) {
1549 auto param
= cast
<PDOBoundParam
>(iter
.second());
1550 if (!stmt
->paramHook(param
.get(), event_type
)) {
1554 for (ArrayIter
iter(stmt
->bound_columns
); iter
; ++iter
) {
1555 auto param
= cast
<PDOBoundParam
>(iter
.second());
1556 if (!stmt
->paramHook(param
.get(), event_type
)) {
1563 static void get_lazy_object(sp_PDOStatement stmt
, Variant
&ret
) {
1564 if (stmt
->lazy_object_ref
.isNull()) {
1565 stmt
->lazy_object_ref
= stmt
;
1567 ret
= stmt
->lazy_object_ref
;
1570 static bool really_register_bound_param(sp_PDOBoundParam param
,
1571 sp_PDOStatement stmt
,
1573 Array
&hash
= is_param
? stmt
->bound_params
: stmt
->bound_columns
;
1575 if (PDO_PARAM_TYPE(param
->param_type
) == PDO_PARAM_STR
&&
1576 param
->max_value_len
<= 0 && !param
->parameter
.isNull()) {
1577 param
->parameter
= param
->parameter
.toString();
1578 } else if (PDO_PARAM_TYPE(param
->param_type
) == PDO_PARAM_INT
&&
1579 param
->parameter
.isBoolean()) {
1580 param
->parameter
= param
->parameter
.toInt64();
1581 } else if (PDO_PARAM_TYPE(param
->param_type
) == PDO_PARAM_BOOL
&&
1582 param
->parameter
.isInteger()) {
1583 param
->parameter
= param
->parameter
.toBoolean();
1585 param
->stmt
= stmt
.get();
1586 param
->is_param
= is_param
;
1588 if (!is_param
&& !param
->name
.empty() && !stmt
->columns
.empty()) {
1589 /* try to map the name to the column */
1590 for (int i
= 0; i
< stmt
->column_count
; i
++) {
1591 if (cast
<PDOColumn
>(stmt
->columns
[i
])->name
== param
->name
) {
1597 /* if you prepare and then execute passing an array of params keyed by
1598 names, then this will trigger, and we don't want that */
1599 if (param
->paramno
== -1) {
1600 raise_warning("Did not found column name '%s' in the defined columns;"
1601 " it will not be bound", param
->name
.data());
1605 if (is_param
&& !param
->name
.empty() && param
->name
[0] != ':') {
1606 param
->name
= String(":") + param
->name
;
1609 if (is_param
&& !rewrite_name_to_position(stmt
, param
)) {
1610 param
->name
.reset();
1614 /* ask the driver to perform any normalization it needs on the
1615 * parameter name. Note that it is illegal for the driver to take
1616 * a reference to param, as it resides in transient storage only
1618 if (stmt
->support(PDOStatement::MethodParamHook
)) {
1619 if (!stmt
->paramHook(param
.get(), PDO_PARAM_EVT_NORMALIZE
)) {
1620 param
->name
.reset();
1625 /* delete any other parameter registered with this number.
1626 * If the parameter is named, it will be removed and correctly
1627 * disposed of by the hash_update call that follows */
1628 if (param
->paramno
>= 0) {
1629 hash
.remove(param
->paramno
);
1632 /* allocate storage for the parameter, keyed by its "canonical" name */
1633 if (!param
->name
.empty()) {
1634 hash
.set(param
->name
, Variant(param
));
1636 hash
.set(param
->paramno
, Variant(param
));
1639 /* tell the driver we just created a parameter */
1640 if (stmt
->support(PDOStatement::MethodParamHook
)) {
1641 if (!stmt
->paramHook(param
.get(), PDO_PARAM_EVT_ALLOC
)) {
1642 /* undo storage allocation; the hash will free the parameter
1643 * name if required */
1644 if (!param
->name
.empty()) {
1645 hash
.remove(param
->name
);
1647 hash
.remove(param
->paramno
);
1649 param
->parameter
.unset();
1656 static inline void fetch_value(sp_PDOStatement stmt
, Variant
&dest
, int colno
,
1657 int *type_override
) {
1658 if (colno
< 0 || colno
>= stmt
->column_count
) {
1661 auto col
= cast
<PDOColumn
>(stmt
->columns
[colno
]);
1662 int type
= PDO_PARAM_TYPE(col
->param_type
);
1663 int new_type
= type_override
? PDO_PARAM_TYPE(*type_override
) : type
;
1665 stmt
->getColumn(colno
, dest
);
1667 if (type
!= new_type
) {
1669 case PDO_PARAM_INT
: dest
= dest
.toInt64(); break;
1670 case PDO_PARAM_BOOL
: dest
= dest
.toBoolean(); break;
1671 case PDO_PARAM_STR
: dest
= dest
.toString(); break;
1672 case PDO_PARAM_NULL
: dest
= init_null(); break;
1675 if (stmt
->dbh
->conn()->stringify
&& (dest
.isInteger() || dest
.isDouble())) {
1676 dest
= dest
.toString();
1678 if (dest
.isNull() && stmt
->dbh
->conn()->oracle_nulls
== PDO_NULL_TO_STRING
) {
1679 dest
= empty_string_variant();
1683 static bool do_fetch_common(sp_PDOStatement stmt
, PDOFetchOrientation ori
,
1684 long offset
, bool do_bind
) {
1685 if (!stmt
->executed
) {
1688 if (!dispatch_param_event(stmt
, PDO_PARAM_EVT_FETCH_PRE
)) {
1691 if (!stmt
->fetcher(ori
, offset
)) {
1694 /* some drivers might need to describe the columns now */
1695 if (stmt
->columns
.empty() && !pdo_stmt_describe_columns(stmt
)) {
1698 if (!dispatch_param_event(stmt
, PDO_PARAM_EVT_FETCH_POST
)) {
1702 if (do_bind
&& !stmt
->bound_columns
.empty()) {
1703 /* update those bound column variables now */
1704 for (ArrayIter
iter(stmt
->bound_columns
); iter
; ++iter
) {
1705 auto param
= cast
<PDOBoundParam
>(iter
.second());
1706 if (param
->paramno
>= 0) {
1707 param
->parameter
.setNull();
1709 fetch_value(stmt
, param
->parameter
, param
->paramno
,
1710 (int *)¶m
->param_type
);
1711 /* TODO: some smart thing that avoids duplicating the value in the
1712 * general loop below. For now, if you're binding output columns,
1713 * it's better to use LAZY or BOUND fetches if you want to shave
1714 * off those cycles */
1722 static bool do_fetch_func_prepare(sp_PDOStatement stmt
) {
1723 if (!is_callable(stmt
->fetch
.func
)) {
1724 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
1725 "user-supplied function must be a valid callback");
1731 /* perform a fetch. If do_bind is true, update any bound columns.
1732 * If return_value is not null, store values into it according to HOW. */
1733 static bool do_fetch(sp_PDOStatement stmt
,
1737 PDOFetchOrientation ori
,
1739 Variant
*return_all
) {
1740 if (how
== PDO_FETCH_USE_DEFAULT
) {
1741 how
= stmt
->default_fetch_type
;
1743 int flags
= how
& PDO_FETCH_FLAGS
;
1744 how
= (PDOFetchType
)(how
& ~PDO_FETCH_FLAGS
);
1746 if (!do_fetch_common(stmt
, ori
, offset
, do_bind
)) {
1750 if (how
== PDO_FETCH_BOUND
) {
1756 if ((flags
& PDO_FETCH_GROUP
) && stmt
->fetch
.column
== -1) {
1759 colno
= stmt
->fetch
.column
;
1762 if (how
== PDO_FETCH_LAZY
) {
1763 get_lazy_object(stmt
, ret
);
1767 String clsname
, old_clsname
;
1768 Variant old_ctor_args
;
1772 case PDO_FETCH_USE_DEFAULT
:
1773 case PDO_FETCH_ASSOC
:
1774 case PDO_FETCH_BOTH
:
1776 case PDO_FETCH_NAMED
:
1777 ret
= Array::Create();
1780 case PDO_FETCH_KEY_PAIR
:
1781 if (stmt
->column_count
!= 2) {
1782 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
1783 "PDO::FETCH_KEY_PAIR fetch mode requires the "
1784 "result set to contain extactly 2 columns.");
1788 ret
= Array::Create();
1792 case PDO_FETCH_COLUMN
:
1793 if (colno
>= 0 && colno
< stmt
->column_count
) {
1794 if (flags
== PDO_FETCH_GROUP
&& stmt
->fetch
.column
== -1) {
1795 fetch_value(stmt
, ret
, 1, NULL
);
1796 } else if (flags
== PDO_FETCH_GROUP
&& colno
) {
1797 fetch_value(stmt
, ret
, 0, NULL
);
1799 fetch_value(stmt
, ret
, colno
, NULL
);
1806 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000", "Invalid column index");
1811 ret
= SystemLib::AllocStdClassObject();
1814 case PDO_FETCH_CLASS
:
1815 if (flags
& PDO_FETCH_CLASSTYPE
) {
1816 old_clsname
= stmt
->fetch
.clsname
;
1817 old_ctor_args
= stmt
->fetch
.ctor_args
;
1820 fetch_value(stmt
, val
, i
++, NULL
);
1821 if (!val
.isNull()) {
1822 if (!HHVM_FN(class_exists
)(val
.toString())) {
1823 stmt
->fetch
.clsname
= "stdclass";
1825 stmt
->fetch
.clsname
= val
.toString();
1829 do_fetch_class_prepare(stmt
);
1831 clsname
= stmt
->fetch
.clsname
;
1832 if (clsname
.empty()) {
1833 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
1834 "No fetch class specified");
1837 if ((flags
& PDO_FETCH_SERIALIZE
) == 0) {
1838 ret
= create_object_only(clsname
);
1839 if (!do_fetch_class_prepare(stmt
)) {
1842 if (!stmt
->fetch
.constructor
.empty() &&
1843 (flags
& PDO_FETCH_PROPS_LATE
)) {
1844 ret
.asCObjRef()->o_invoke(stmt
->fetch
.constructor
,
1845 stmt
->fetch
.ctor_args
.toArray());
1846 ret
.asCObjRef()->clearNoDestruct();
1851 case PDO_FETCH_INTO
:
1852 if (stmt
->fetch
.into
.isNull()) {
1853 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
1854 "No fetch-into object specified.");
1858 ret
= stmt
->fetch
.into
;
1859 if (ret
.isObject() &&
1860 ret
.getObjectData()->instanceof(SystemLib::s_stdclassClass
)) {
1861 how
= PDO_FETCH_OBJ
;
1865 case PDO_FETCH_FUNC
:
1866 if (stmt
->fetch
.func
.empty()) {
1867 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
1868 "No fetch function specified");
1871 if (!do_fetch_func_prepare(stmt
)) {
1882 if (return_all
&& how
!= PDO_FETCH_KEY_PAIR
) {
1883 if (flags
== PDO_FETCH_GROUP
&& how
== PDO_FETCH_COLUMN
&&
1884 stmt
->fetch
.column
> 0) {
1885 fetch_value(stmt
, grp_val
, colno
, NULL
);
1887 fetch_value(stmt
, grp_val
, i
, NULL
);
1889 grp_val
= grp_val
.toString();
1890 if (how
== PDO_FETCH_COLUMN
) {
1891 i
= stmt
->column_count
; /* no more data to fetch */
1897 for (int idx
= 0; i
< stmt
->column_count
; i
++, idx
++) {
1898 const String
& name
= cast
<PDOColumn
>(stmt
->columns
[i
])->name
;
1900 fetch_value(stmt
, val
, i
, NULL
);
1903 case PDO_FETCH_ASSOC
:
1904 ret
.toArrRef().set(name
, val
);
1907 case PDO_FETCH_KEY_PAIR
: {
1909 fetch_value(stmt
, tmp
, ++i
, NULL
);
1911 return_all
->toArrRef().set(val
, tmp
);
1913 ret
.toArrRef().set(val
, tmp
);
1917 case PDO_FETCH_USE_DEFAULT
:
1918 case PDO_FETCH_BOTH
:
1919 ret
.toArrRef().set(name
, val
);
1920 ret
.toArrRef().append(val
);
1923 case PDO_FETCH_NAMED
: {
1924 /* already have an item with this name? */
1926 if (ret
.toArrRef().exists(name
)) {
1927 auto const curr_val
= ret
.toArrRef().lvalAt(name
).unboxed();
1928 if (!isArrayLikeType(curr_val
.type())) {
1929 Array arr
= Array::Create();
1930 arr
.append(curr_val
.tv());
1932 ret
.toArray().set(name
, arr
);
1934 asArrRef(curr_val
).append(val
);
1937 ret
.toArrRef().set(name
, val
);
1942 ret
.toArrRef().append(val
);
1946 case PDO_FETCH_INTO
:
1947 ret
.toObject()->o_set(name
, val
);
1950 case PDO_FETCH_CLASS
:
1951 if ((flags
& PDO_FETCH_SERIALIZE
) == 0 || idx
) {
1952 ret
.toObject()->o_set(name
, val
);
1955 ret
= unserialize_from_string(val
);
1956 if (same(ret
, false)) {
1957 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
1958 "cannot unserialize data");
1962 // hzhao: not sure how we support class serialization
1963 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
1964 "cannot unserialize class");
1969 case PDO_FETCH_FUNC
:
1970 forceToArray(stmt
->fetch
.values
).set(idx
, val
);
1974 pdo_raise_impl_error(stmt
->dbh
, stmt
, "22003", "mode is out of range");
1980 case PDO_FETCH_CLASS
:
1981 if (!stmt
->fetch
.constructor
.empty() &&
1982 !(flags
& (PDO_FETCH_PROPS_LATE
| PDO_FETCH_SERIALIZE
))) {
1983 ret
.toObject()->o_invoke(stmt
->fetch
.constructor
,
1984 stmt
->fetch
.ctor_args
.toArray());
1985 ret
.toObject()->clearNoDestruct();
1987 if (flags
& PDO_FETCH_CLASSTYPE
) {
1988 stmt
->fetch
.clsname
= old_clsname
;
1989 stmt
->fetch
.ctor_args
= old_ctor_args
;
1993 case PDO_FETCH_FUNC
:
1994 ret
= vm_call_user_func(stmt
->fetch
.func
,
1995 stmt
->fetch
.values
.toArray());
2003 if ((flags
& PDO_FETCH_UNIQUE
) == PDO_FETCH_UNIQUE
) {
2004 return_all
->toArrRef().set(grp_val
, ret
);
2006 auto const lval
= return_all
->toArrRef().lvalAt(grp_val
);
2007 forceToArray(lval
).append(ret
);
2014 static int register_bound_param(const Variant
& paramno
, VRefParam param
,
2015 int64_t type
, int64_t max_value_len
,
2016 const Variant
& driver_params
,
2017 sp_PDOStatement stmt
, bool is_param
) {
2018 auto p
= req::make
<PDOBoundParam
>();
2019 // need to make sure this is NULL, in case a fatal errors occurs before it's
2020 // set inside really_register_bound_param
2023 if (paramno
.isNumeric()) {
2024 p
->paramno
= paramno
.toInt64();
2027 p
->name
= paramno
.toString();
2030 p
->parameter
.setWithRef(param
);
2031 p
->param_type
= (PDOParamType
)type
;
2032 p
->max_value_len
= max_value_len
;
2033 p
->driver_params
= driver_params
;
2035 if (p
->paramno
> 0) {
2036 --p
->paramno
; /* make it zero-based internally */
2037 } else if (p
->name
.empty()) {
2038 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY093",
2039 "Columns/Parameters are 1-based");
2043 if (!really_register_bound_param(p
, stmt
, is_param
)) {
2044 p
->parameter
.unset();
2050 static bool generic_stmt_attr_get(sp_PDOStatement stmt
, Variant
&ret
,
2052 if (attr
== PDO_ATTR_EMULATE_PREPARES
) {
2053 ret
= (bool)(stmt
->supports_placeholders
== PDO_PLACEHOLDER_NONE
);
2059 ///////////////////////////////////////////////////////////////////////////////
2064 #define PDO_PARSER_TEXT 1
2065 #define PDO_PARSER_BIND 2
2066 #define PDO_PARSER_BIND_POS 3
2067 #define PDO_PARSER_EOI 4
2069 #define RET(i) {s->cur = cursor; return i; }
2070 #define SKIP_ONE(i) {s->cur = s->tok + 1; return 1; }
2072 #define YYCTYPE unsigned char
2073 #define YYCURSOR cursor
2074 #define YYLIMIT limit
2075 #define YYMARKER s->ptr
2076 #define YYFILL(n) RET(PDO_PARSER_EOI)
2078 typedef struct Scanner
{
2079 char *ptr
, *cur
, *lim
, *tok
;
2082 static int scan(Scanner
*s
) {
2083 char* cursor
= s
->cur
;
2084 char* limit
= s
->lim
;
2090 if ((YYLIMIT
- YYCURSOR
) < 2) { YYFILL(2); }
2093 case 0x00: goto yy11
;
2095 case '\'': goto yy4
;
2101 yych
= *(YYMARKER
= ++YYCURSOR
);
2102 if (yych
>= 0x01) goto yy26
;
2104 { SKIP_ONE(PDO_PARSER_TEXT
); }
2106 yych
= *(YYMARKER
= ++YYCURSOR
);
2107 if (yych
<= 0x00) goto yy3
;
2174 case 'z': goto yy16
;
2176 case '?': goto yy13
;
2181 switch ((yych
= *YYCURSOR
)) {
2183 case '?': goto yy13
;
2187 { RET(PDO_PARSER_BIND_POS
); }
2190 if (YYLIMIT
<= YYCURSOR
) { YYFILL(1); }
2197 case '?': goto yy10
;
2201 { RET(PDO_PARSER_TEXT
); }
2204 { RET(PDO_PARSER_EOI
); }
2207 if (YYLIMIT
<= YYCURSOR
) { YYFILL(1); }
2211 case '?': goto yy13
;
2215 { RET(PDO_PARSER_TEXT
); }
2218 if (YYLIMIT
<= YYCURSOR
) { YYFILL(1); }
2283 case 'z': goto yy16
;
2287 { RET(PDO_PARSER_BIND
); }
2290 if (YYLIMIT
<= YYCURSOR
) { YYFILL(1); }
2294 case 0x00: goto yy21
;
2295 case '\'': goto yy23
;
2296 case '\\': goto yy22
;
2300 YYCURSOR
= YYMARKER
;
2304 if (YYLIMIT
<= YYCURSOR
) { YYFILL(1); }
2306 if (yych
<= 0x00) goto yy21
;
2310 { RET(PDO_PARSER_TEXT
); }
2313 if (YYLIMIT
<= YYCURSOR
) { YYFILL(1); }
2317 case 0x00: goto yy21
;
2318 case '"': goto yy28
;
2319 case '\\': goto yy27
;
2324 if (YYLIMIT
<= YYCURSOR
) { YYFILL(1); }
2326 if (yych
<= 0x00) goto yy21
;
2330 { RET(PDO_PARSER_TEXT
); }
2337 struct placeholder
{
2341 String quoted
; /* quoted value */
2342 struct placeholder
*next
;
2345 int pdo_parse_params(sp_PDOStatement stmt
, const String
& in
, String
&out
) {
2354 req::ptr
<PDOBoundParam
> param
;
2355 int query_type
= PDO_PLACEHOLDER_NONE
;
2356 struct placeholder
*placeholders
= NULL
, *placetail
= NULL
, *plc
= NULL
;
2358 s
.cur
= (char*)in
.data();
2359 s
.lim
= (char*)in
.data() + in
.size() + 1;
2361 /* phase 1: look for args */
2362 while ((t
= scan(&s
)) != PDO_PARSER_EOI
) {
2363 if (t
== PDO_PARSER_BIND
|| t
== PDO_PARSER_BIND_POS
) {
2364 if (t
== PDO_PARSER_BIND
) {
2365 int len
= s
.cur
- s
.tok
;
2366 if ((in
.data() < (s
.cur
- len
)) && isalnum(*(s
.cur
- len
- 1))) {
2369 query_type
|= PDO_PLACEHOLDER_NAMED
;
2371 query_type
|= PDO_PLACEHOLDER_POSITIONAL
;
2374 plc
= req::make_raw
<placeholder
>();
2375 memset(plc
, 0, sizeof(*plc
));
2378 plc
->len
= s
.cur
- s
.tok
;
2379 plc
->bindno
= bindno
++;
2382 placetail
->next
= plc
;
2391 /* nothing to do; good! */
2395 /* did the query make sense to me? */
2396 if (query_type
== (PDO_PLACEHOLDER_NAMED
|PDO_PLACEHOLDER_POSITIONAL
)) {
2397 /* they mixed both types; punt */
2398 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY093",
2399 "mixed named and positional parameters");
2404 if ((int)stmt
->supports_placeholders
== query_type
&&
2405 !stmt
->named_rewrite_template
) {
2406 /* query matches native syntax */
2411 if (stmt
->named_rewrite_template
) {
2413 * We we pretend that the query was positional even if
2414 * it was named so that we fall into the
2415 * named rewrite case below. Not too pretty,
2417 query_type
= PDO_PLACEHOLDER_POSITIONAL
;
2420 params
= stmt
->bound_params
;
2422 /* Do we have placeholders but no bound params */
2423 if (bindno
&& params
.empty() &&
2424 stmt
->supports_placeholders
== PDO_PLACEHOLDER_NONE
) {
2425 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY093", "no parameters were bound");
2430 if (!params
.empty() && bindno
!= params
.size() &&
2431 stmt
->supports_placeholders
== PDO_PLACEHOLDER_NONE
) {
2432 /* extra bit of validation for instances when same params are bound
2434 if (query_type
!= PDO_PLACEHOLDER_POSITIONAL
&& bindno
> params
.size()) {
2436 for (plc
= placeholders
; plc
; plc
= plc
->next
) {
2437 if (!params
.exists(String(plc
->pos
, plc
->len
, CopyString
))) {
2446 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY093",
2447 "number of bound variables does not match number "
2453 /* what are we going to do ? */
2454 if (stmt
->supports_placeholders
== PDO_PLACEHOLDER_NONE
) {
2455 /* query generation */
2457 newbuffer_len
= in
.size();
2459 /* let's quote all the values */
2460 for (plc
= placeholders
; plc
; plc
= plc
->next
) {
2462 if (query_type
== PDO_PLACEHOLDER_POSITIONAL
) {
2463 vparam
= params
[plc
->bindno
];
2465 vparam
= params
[String(plc
->pos
, plc
->len
, CopyString
)];
2467 if (vparam
.isNull()) {
2468 /* parameter was not defined */
2470 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY093",
2471 "parameter was not defined");
2474 param
= cast
<PDOBoundParam
>(vparam
);
2475 if (stmt
->dbh
->conn()->support(PDOConnection::MethodQuoter
)) {
2476 if (param
->param_type
== PDO_PARAM_LOB
&&
2477 param
->parameter
.isResource()) {
2478 Variant buf
= HHVM_FN(stream_get_contents
)(
2479 param
->parameter
.toResource());
2480 if (!same(buf
, false)) {
2481 if (!stmt
->dbh
->conn()->quoter(buf
.toString(), plc
->quoted
,
2482 param
->param_type
)) {
2485 setPDOError(stmt
->error_code
, stmt
->dbh
->conn()->error_code
);
2489 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY105",
2490 "Expected a stream resource");
2496 switch (param
->parameter
.getType()) {
2499 plc
->quoted
= "NULL";
2504 plc
->quoted
= param
->parameter
.toString();
2508 param
->parameter
= param
->parameter
.toInt64();
2510 case KindOfPersistentString
:
2512 case KindOfPersistentVec
:
2514 case KindOfPersistentDict
:
2516 case KindOfPersistentKeyset
:
2518 case KindOfPersistentArray
:
2521 case KindOfResource
:
2523 if (!stmt
->dbh
->conn()->quoter(
2524 param
->parameter
.toString(),
2526 param
->param_type
)) {
2529 setPDOError(stmt
->error_code
, stmt
->dbh
->conn()->error_code
);
2538 plc
->quoted
= param
->parameter
.toString();
2540 newbuffer_len
+= plc
->quoted
.size();
2544 /* allocate output buffer */
2545 out
= String(newbuffer_len
, ReserveString
);
2546 newbuffer
= out
.mutableData();
2548 /* and build the query */
2555 memcpy(newbuffer
, ptr
, t
);
2558 memcpy(newbuffer
, plc
->quoted
.data(), plc
->quoted
.size());
2559 newbuffer
+= plc
->quoted
.size();
2560 ptr
= plc
->pos
+ plc
->len
;
2565 t
= (in
.data() + in
.size()) - ptr
;
2567 memcpy(newbuffer
, ptr
, t
);
2570 out
.setSize(newbuffer
- out
.data());
2575 } else if (query_type
== PDO_PLACEHOLDER_POSITIONAL
) {
2576 /* rewrite ? to :pdoX */
2577 StringBuffer idxbuf
;
2578 const char *tmpl
= stmt
->named_rewrite_template
?
2579 stmt
->named_rewrite_template
: ":pdo%d";
2582 newbuffer_len
= in
.size();
2584 for (plc
= placeholders
; plc
; plc
= plc
->next
) {
2586 String
name(plc
->pos
, plc
->len
, CopyString
);
2588 /* check if bound parameter is already available */
2589 if (!strcmp(name
.c_str(), "?") ||
2590 !stmt
->bound_param_map
.exists(name
)) {
2591 idxbuf
.printf(tmpl
, bind_no
++);
2594 idxbuf
.append(stmt
->bound_param_map
[name
].toString());
2598 plc
->quoted
= idxbuf
.detach();
2599 newbuffer_len
+= plc
->quoted
.size();
2601 if (!skip_map
&& stmt
->named_rewrite_template
) {
2602 /* create a mapping */
2603 stmt
->bound_param_map
.set(name
, plc
->quoted
);
2606 /* map number to name */
2607 stmt
->bound_param_map
.set(plc
->bindno
, plc
->quoted
);
2613 /* rewrite :name to ? */
2615 newbuffer_len
= in
.size();
2617 for (plc
= placeholders
; plc
; plc
= plc
->next
) {
2618 String
name(plc
->pos
, plc
->len
, CopyString
);
2619 stmt
->bound_param_map
.set(plc
->bindno
, name
);
2628 while (placeholders
) {
2630 placeholders
= plc
->next
;
2631 plc
->quoted
.reset();
2638 ///////////////////////////////////////////////////////////////////////////////
2641 const StaticString
s_PDOStatement("PDOStatement");
2643 PDOStatementData::PDOStatementData() : m_rowIndex(-1) {
2646 PDOStatementData::~PDOStatementData() { }
2648 static Variant
HHVM_METHOD(PDOStatement
, execute
,
2649 const Variant
& paramsV
/* = null_array */) {
2650 auto data
= Native::data
<PDOStatementData
>(this_
);
2651 auto params
= paramsV
.isNull() ? null_array
: paramsV
.toArray();
2653 SYNC_VM_REGS_SCOPED();
2655 if (data
->m_stmt
== nullptr) {
2656 return init_null_variant
;
2659 setPDOErrorNone(data
->m_stmt
->error_code
);
2661 if (!params
.empty()) {
2662 data
->m_stmt
->bound_params
.reset();
2663 for (ArrayIter
iter(params
); iter
; ++iter
) {
2664 auto param
= req::make
<PDOBoundParam
>();
2665 param
->param_type
= PDO_PARAM_STR
;
2666 param
->parameter
= iter
.second();
2669 if (iter
.first().isString()) {
2670 param
->name
= iter
.first().toString();
2671 param
->paramno
= -1;
2673 int64_t num_index
= iter
.first().toInt64();
2674 /* we're okay to be zero based here */
2675 if (num_index
< 0) {
2676 pdo_raise_impl_error(data
->m_stmt
->dbh
, data
->m_stmt
,
2680 param
->paramno
= num_index
;
2683 if (!really_register_bound_param(param
, data
->m_stmt
, true)) {
2690 if (PDO_PLACEHOLDER_NONE
== data
->m_stmt
->supports_placeholders
) {
2691 /* handle the emulated parameter binding, m_stmt->active_query_string
2692 holds the query with binds expanded and quoted. */
2693 ret
= pdo_parse_params(data
->m_stmt
, data
->m_stmt
->query_string
,
2694 data
->m_stmt
->active_query_string
);
2695 if (ret
== 0) { /* no changes were made */
2696 data
->m_stmt
->active_query_string
= data
->m_stmt
->query_string
;
2698 } else if (ret
== -1) {
2699 /* something broke */
2700 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
2703 } else if (!dispatch_param_event(data
->m_stmt
, PDO_PARAM_EVT_EXEC_PRE
)) {
2704 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
2707 if (data
->m_stmt
->executer()) {
2708 data
->m_stmt
->active_query_string
.reset();
2709 if (!data
->m_stmt
->executed
) {
2710 /* this is the first execute */
2712 if (data
->m_stmt
->dbh
->conn()->alloc_own_columns
2713 && data
->m_stmt
->columns
.empty()) {
2714 /* for "big boy" drivers, we need to allocate memory to fetch
2715 * the results into, so lets do that now */
2716 ret
= pdo_stmt_describe_columns(data
->m_stmt
);
2719 data
->m_stmt
->executed
= 1;
2722 if (ret
&& !dispatch_param_event(data
->m_stmt
, PDO_PARAM_EVT_EXEC_POST
)) {
2728 data
->m_stmt
->active_query_string
.reset();
2729 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
2733 static Variant
HHVM_METHOD(PDOStatement
, fetch
, int64_t how
= 0,
2734 int64_t orientation
= PDO_FETCH_ORI_NEXT
,
2735 int64_t offset
= 0) {
2736 auto data
= Native::data
<PDOStatementData
>(this_
);
2738 SYNC_VM_REGS_SCOPED();
2740 if (data
->m_stmt
== nullptr) {
2744 setPDOErrorNone(data
->m_stmt
->error_code
);
2745 if (!pdo_stmt_verify_mode(data
->m_stmt
, how
, false)) {
2750 if (!do_fetch(data
->m_stmt
, true, ret
, (PDOFetchType
)how
,
2751 (PDOFetchOrientation
)orientation
, offset
, NULL
)) {
2752 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
2758 static Variant
HHVM_METHOD(PDOStatement
, fetchobject
,
2759 const String
& class_name
/* = null_string */,
2760 const Variant
& ctor_args
/* = null */) {
2761 auto data
= Native::data
<PDOStatementData
>(this_
);
2762 if (data
->m_stmt
== nullptr) {
2766 setPDOErrorNone(data
->m_stmt
->error_code
);
2767 if (!pdo_stmt_verify_mode(data
->m_stmt
, PDO_FETCH_CLASS
, false)) {
2771 String old_clsname
= data
->m_stmt
->fetch
.clsname
;
2772 Variant old_ctor_args
= data
->m_stmt
->fetch
.ctor_args
;
2775 data
->m_stmt
->fetch
.clsname
= class_name
;
2776 if (class_name
.empty()) {
2777 data
->m_stmt
->fetch
.clsname
= "stdclass";
2779 if (!HHVM_FN(class_exists
)(data
->m_stmt
->fetch
.clsname
)) {
2780 pdo_raise_impl_error(data
->m_stmt
->dbh
, data
->m_stmt
, "HY000",
2781 "Could not find user-supplied class");
2784 if (!ctor_args
.isNull() && !ctor_args
.isArray()) {
2785 pdo_raise_impl_error(data
->m_stmt
->dbh
, data
->m_stmt
, "HY000",
2786 "ctor_args must be either NULL or an array");
2789 data
->m_stmt
->fetch
.ctor_args
= ctor_args
;
2792 if (!error
&& !do_fetch(data
->m_stmt
, true, ret
, PDO_FETCH_CLASS
,
2793 PDO_FETCH_ORI_NEXT
, 0, NULL
)) {
2797 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
2800 data
->m_stmt
->fetch
.clsname
= old_clsname
;
2801 data
->m_stmt
->fetch
.ctor_args
= old_ctor_args
;
2808 static Variant
HHVM_METHOD(PDOStatement
, fetchcolumn
,
2809 int64_t column_numner
/* = 0 */) {
2810 auto data
= Native::data
<PDOStatementData
>(this_
);
2811 if (data
->m_stmt
== nullptr) {
2815 setPDOErrorNone(data
->m_stmt
->error_code
);
2816 if (!do_fetch_common(data
->m_stmt
, PDO_FETCH_ORI_NEXT
, 0, true)) {
2817 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
2821 fetch_value(data
->m_stmt
, ret
, column_numner
, nullptr);
2825 static Variant
HHVM_METHOD(PDOStatement
, fetchall
, int64_t how
/* = 0 */,
2826 const Variant
& class_name
/* = null */,
2827 const Variant
& ctor_args
/* = null */) {
2828 auto self
= Native::data
<PDOStatementData
>(this_
);
2829 if (self
->m_stmt
== nullptr) {
2833 if (!pdo_stmt_verify_mode(self
->m_stmt
, how
, true)) {
2837 String old_clsname
= self
->m_stmt
->fetch
.clsname
;
2838 Variant old_ctor_args
= self
->m_stmt
->fetch
.ctor_args
;
2841 switch (how
& ~PDO_FETCH_FLAGS
) {
2842 case PDO_FETCH_CLASS
:
2843 self
->m_stmt
->fetch
.clsname
= class_name
.toString();
2844 if (class_name
.isNull()) {
2845 self
->m_stmt
->fetch
.clsname
= "stdclass";
2847 if (!HHVM_FN(class_exists
)(self
->m_stmt
->fetch
.clsname
)) {
2848 pdo_raise_impl_error(self
->m_stmt
->dbh
, self
->m_stmt
, "HY000",
2849 "Could not find user-supplied class");
2852 if (!ctor_args
.isNull() && !ctor_args
.isArray()) {
2853 pdo_raise_impl_error(self
->m_stmt
->dbh
, self
->m_stmt
, "HY000",
2854 "ctor_args must be either NULL or an array");
2858 self
->m_stmt
->fetch
.ctor_args
= ctor_args
;
2861 do_fetch_class_prepare(self
->m_stmt
);
2865 case PDO_FETCH_FUNC
:
2866 if (!HHVM_FN(function_exists
)(class_name
.toString())) {
2867 pdo_raise_impl_error(self
->m_stmt
->dbh
, self
->m_stmt
, "HY000",
2868 "no fetch function specified");
2871 self
->m_stmt
->fetch
.func
= class_name
.toString();
2872 do_fetch_func_prepare(self
->m_stmt
);
2876 case PDO_FETCH_COLUMN
:
2877 if (class_name
.isNull()) {
2878 self
->m_stmt
->fetch
.column
= how
& PDO_FETCH_GROUP
? -1 : 0;
2880 self
->m_stmt
->fetch
.column
= class_name
.toInt64();
2882 if (!ctor_args
.isNull()) {
2883 pdo_raise_impl_error(self
->m_stmt
->dbh
, self
->m_stmt
, "HY000",
2884 "Third parameter not allowed for "
2885 "PDO::FETCH_COLUMN");
2891 int flags
= how
& PDO_FETCH_FLAGS
;
2893 if ((how
& ~PDO_FETCH_FLAGS
) == PDO_FETCH_USE_DEFAULT
) {
2894 flags
|= self
->m_stmt
->default_fetch_type
& PDO_FETCH_FLAGS
;
2895 how
|= self
->m_stmt
->default_fetch_type
& ~PDO_FETCH_FLAGS
;
2898 Variant
*return_all
= NULL
;
2899 Variant return_value
;
2902 setPDOErrorNone(self
->m_stmt
->error_code
);
2904 if ((how
& PDO_FETCH_GROUP
) || how
== PDO_FETCH_KEY_PAIR
||
2905 (how
== PDO_FETCH_USE_DEFAULT
&&
2906 self
->m_stmt
->default_fetch_type
== PDO_FETCH_KEY_PAIR
)) {
2907 return_value
= Array::Create();
2908 return_all
= &return_value
;
2910 if (!do_fetch(self
->m_stmt
, true, data
, (PDOFetchType
)(how
| flags
),
2911 PDO_FETCH_ORI_NEXT
, 0, return_all
)) {
2916 if ((how
& PDO_FETCH_GROUP
)) {
2919 } while (do_fetch(self
->m_stmt
, true, data
, (PDOFetchType
)(how
| flags
),
2920 PDO_FETCH_ORI_NEXT
, 0, return_all
));
2921 } else if (how
== PDO_FETCH_KEY_PAIR
||
2922 (how
== PDO_FETCH_USE_DEFAULT
&&
2923 self
->m_stmt
->default_fetch_type
== PDO_FETCH_KEY_PAIR
)) {
2924 while (do_fetch(self
->m_stmt
, true, data
, (PDOFetchType
)(how
| flags
),
2925 PDO_FETCH_ORI_NEXT
, 0, return_all
)) {
2929 return_value
= Array::Create();
2931 return_value
.toArrRef().append(data
);
2933 } while (do_fetch(self
->m_stmt
, true, data
, (PDOFetchType
)(how
| flags
),
2934 PDO_FETCH_ORI_NEXT
, 0, NULL
));
2938 self
->m_stmt
->fetch
.clsname
= old_clsname
;
2939 self
->m_stmt
->fetch
.ctor_args
= old_ctor_args
;
2942 PDO_HANDLE_STMT_ERR(self
->m_stmt
);
2947 /* on no results, return an empty array */
2948 if (!return_value
.isArray()) {
2949 return_value
= Array::Create();
2952 return return_value
;
2955 static bool HHVM_METHOD(PDOStatement
, bindvalue
, const Variant
& paramno
,
2956 const Variant
& param
,
2957 int64_t type
/* = PDO_PARAM_STR */) {
2958 auto data
= Native::data
<PDOStatementData
>(this_
);
2959 if (data
->m_stmt
== nullptr) {
2963 return register_bound_param(paramno
, param
, type
, 0,
2964 uninit_null(), data
->m_stmt
, true);
2967 static bool HHVM_METHOD(PDOStatement
, bindparam
, const Variant
& paramno
,
2968 VRefParam param
, int64_t type
/* = PDO_PARAM_STR */,
2969 int64_t max_value_len
/* = 0 */,
2970 const Variant
& driver_params
/*= null */) {
2971 auto data
= Native::data
<PDOStatementData
>(this_
);
2972 if (data
->m_stmt
== nullptr) {
2976 return register_bound_param(paramno
, ref(param
), type
, max_value_len
,
2977 driver_params
, data
->m_stmt
, true);
2980 static bool HHVM_METHOD(PDOStatement
, bindcolumn
, const Variant
& paramno
,
2981 VRefParam param
, int64_t type
/* = PDO_PARAM_STR */,
2982 int64_t max_value_len
/* = 0 */,
2983 const Variant
& driver_params
/* = null */) {
2984 auto data
= Native::data
<PDOStatementData
>(this_
);
2985 if (data
->m_stmt
== nullptr) {
2989 return register_bound_param(paramno
, ref(param
), type
, max_value_len
,
2990 driver_params
, data
->m_stmt
, false);
2993 static int64_t HHVM_METHOD(PDOStatement
, rowcount
) {
2994 auto data
= Native::data
<PDOStatementData
>(this_
);
2995 if (data
->m_stmt
== nullptr) {
2999 return data
->m_stmt
->row_count
;
3002 static Variant
HHVM_METHOD(PDOStatement
, errorcode
) {
3003 auto data
= Native::data
<PDOStatementData
>(this_
);
3004 if (data
->m_stmt
== nullptr) {
3007 if (data
->m_stmt
->error_code
[0] == '\0') {
3010 return String(data
->m_stmt
->error_code
, CopyString
);
3013 static Array
HHVM_METHOD(PDOStatement
, errorinfo
) {
3014 auto data
= Native::data
<PDOStatementData
>(this_
);
3015 if (data
->m_stmt
== nullptr) {
3020 ret
.append(String(data
->m_stmt
->error_code
, CopyString
));
3022 if (data
->m_stmt
->dbh
->conn()->support(PDOConnection::MethodFetchErr
)) {
3023 data
->m_stmt
->dbh
->conn()->fetchErr(data
->m_stmt
.get(), ret
);
3026 int error_count
= ret
.size();
3027 int error_expected_count
= 3;
3028 if (error_expected_count
> error_count
) {
3029 int error_count_diff
= error_expected_count
- error_count
;
3030 for (int i
= 0; i
< error_count_diff
; i
++) {
3031 ret
.append(uninit_null());
3037 static Variant
HHVM_METHOD(PDOStatement
, setattribute
, int64_t attribute
,
3038 const Variant
& value
) {
3039 auto data
= Native::data
<PDOStatementData
>(this_
);
3040 if (data
->m_stmt
== nullptr) {
3044 if (!data
->m_stmt
->support(PDOStatement::MethodSetAttribute
)) {
3045 pdo_raise_impl_error(data
->m_stmt
->dbh
, data
->m_stmt
, "IM001",
3046 "This driver doesn't support setting attributes");
3050 setPDOErrorNone(data
->m_stmt
->error_code
);
3051 if (data
->m_stmt
->setAttribute(attribute
, value
)) {
3054 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
3058 static Variant
HHVM_METHOD(PDOStatement
, getattribute
, int64_t attribute
) {
3059 auto data
= Native::data
<PDOStatementData
>(this_
);
3060 if (data
->m_stmt
== nullptr) {
3065 if (!data
->m_stmt
->support(PDOStatement::MethodGetAttribute
)) {
3066 if (!generic_stmt_attr_get(data
->m_stmt
, ret
, attribute
)) {
3067 pdo_raise_impl_error(data
->m_stmt
->dbh
, data
->m_stmt
, "IM001",
3068 "This driver doesn't support getting attributes");
3074 setPDOErrorNone(data
->m_stmt
->error_code
);
3075 switch (data
->m_stmt
->getAttribute(attribute
, ret
)) {
3077 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
3080 if (!generic_stmt_attr_get(data
->m_stmt
, ret
, attribute
)) {
3081 /* XXX: should do something better here */
3082 pdo_raise_impl_error(data
->m_stmt
->dbh
, data
->m_stmt
, "IM001",
3083 "driver doesn't support getting that attribute");
3093 static int64_t HHVM_METHOD(PDOStatement
, columncount
) {
3094 auto data
= Native::data
<PDOStatementData
>(this_
);
3095 if (data
->m_stmt
== nullptr) {
3099 return data
->m_stmt
->column_count
;
3105 s_precision("precision"),
3106 s_pdo_type("pdo_type");
3108 static Variant
HHVM_METHOD(PDOStatement
, getcolumnmeta
, int64_t column
) {
3109 auto data
= Native::data
<PDOStatementData
>(this_
);
3110 if (data
->m_stmt
== nullptr) {
3115 pdo_raise_impl_error(data
->m_stmt
->dbh
, data
->m_stmt
, "42P10",
3116 "column number must be non-negative");
3120 if (!data
->m_stmt
->support(PDOStatement::MethodGetColumnMeta
)) {
3121 pdo_raise_impl_error(data
->m_stmt
->dbh
, data
->m_stmt
, "IM001",
3122 "driver doesn't support meta data");
3126 setPDOErrorNone(data
->m_stmt
->error_code
);
3128 if (!data
->m_stmt
->getColumnMeta(column
, ret
)) {
3129 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
3133 /* add stock items */
3134 auto col
= cast
<PDOColumn
>(data
->m_stmt
->columns
[column
]);
3135 ret
.set(s_name
, col
->name
);
3136 ret
.set(s_len
, (int64_t)col
->maxlen
); /* FIXME: unsigned ? */
3137 ret
.set(s_precision
, (int64_t)col
->precision
);
3138 if (col
->param_type
!= PDO_PARAM_ZVAL
) {
3139 // if param_type is PDO_PARAM_ZVAL the driver has to provide correct data
3140 ret
.set(s_pdo_type
, (int64_t)col
->param_type
);
3145 static bool HHVM_METHOD(PDOStatement
, setfetchmode
,
3146 int64_t mode
, const Array
& _argv
/* = null_array */) {
3147 auto data
= Native::data
<PDOStatementData
>(this_
);
3148 if (data
->m_stmt
== nullptr) {
3151 int argc
= _argv
.size() + 1;
3153 return pdo_stmt_set_fetch_mode(data
->m_stmt
, argc
, mode
, _argv
);
3156 static bool HHVM_METHOD(PDOStatement
, nextrowset
) {
3157 auto data
= Native::data
<PDOStatementData
>(this_
);
3158 if (data
->m_stmt
== nullptr) {
3162 if (!data
->m_stmt
->support(PDOStatement::MethodNextRowset
)) {
3163 pdo_raise_impl_error(data
->m_stmt
->dbh
, data
->m_stmt
, "IM001",
3164 "driver does not support multiple rowsets");
3168 setPDOErrorNone(data
->m_stmt
->error_code
);
3171 if (!data
->m_stmt
->columns
.empty()) {
3172 data
->m_stmt
->columns
.clear();
3173 data
->m_stmt
->column_count
= 0;
3176 if (!data
->m_stmt
->nextRowset()) {
3177 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
3181 pdo_stmt_describe_columns(data
->m_stmt
);
3185 static bool HHVM_METHOD(PDOStatement
, closecursor
) {
3186 auto data
= Native::data
<PDOStatementData
>(this_
);
3187 if (data
->m_stmt
== nullptr) {
3191 if (!data
->m_stmt
->support(PDOStatement::MethodCursorCloser
)) {
3192 /* emulate it by fetching and discarding rows */
3194 while (data
->m_stmt
->fetcher(PDO_FETCH_ORI_NEXT
, 0));
3195 // if (!data->t_nextrowset()) {
3196 if (HHVM_MN(PDOStatement
, nextrowset
)(this_
)) {
3200 data
->m_stmt
->executed
= 0;
3204 setPDOErrorNone(data
->m_stmt
->error_code
);
3205 if (!data
->m_stmt
->cursorCloser()) {
3206 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
3209 data
->m_stmt
->executed
= 0;
3213 static Variant
HHVM_METHOD(PDOStatement
, debugdumpparams
) {
3214 auto data
= Native::data
<PDOStatementData
>(this_
);
3215 if (data
->m_stmt
== nullptr) {
3219 auto f
= File::Open("php://output", "w");
3220 if (!f
|| f
->isInvalid()) {
3225 params
.append(data
->m_stmt
->query_string
.size());
3226 params
.append(data
->m_stmt
->query_string
.size());
3227 params
.append(data
->m_stmt
->query_string
.data());
3228 f
->printf("SQL: [%d] %.*s\n", params
);
3230 f
->printf("Params: %d\n",
3231 make_packed_array(data
->m_stmt
->bound_params
.size()));
3232 for (ArrayIter
iter(data
->m_stmt
->bound_params
); iter
; ++iter
) {
3233 if (iter
.first().isString()) {
3234 String key
= iter
.first().toString();
3235 params
= make_packed_array(key
.size(), key
.size(), key
.data());
3236 f
->printf("Key: Name: [%d] %.*s\n", params
);
3238 f
->printf("Key: Position #%ld:\n",
3239 make_packed_array(iter
.first().toInt64()));
3242 auto param
= cast
<PDOBoundParam
>(iter
.second());
3244 params
.append(param
->paramno
);
3245 params
.append(param
->name
.size());
3246 params
.append(param
->name
.size());
3247 params
.append(param
->name
.data());
3248 params
.append(param
->is_param
);
3249 params
.append(param
->param_type
);
3250 f
->printf("paramno=%d\nname=[%d] \"%.*s\"\nis_param=%d\nparam_type=%d\n",
3256 static Variant
HHVM_METHOD(PDOStatement
, current
) {
3257 auto data
= Native::data
<PDOStatementData
>(this_
);
3262 static Variant
HHVM_METHOD(PDOStatement
, key
) {
3263 auto data
= Native::data
<PDOStatementData
>(this_
);
3265 return data
->m_rowIndex
;
3268 static Variant
HHVM_METHOD(PDOStatement
, next
) {
3269 auto data
= Native::data
<PDOStatementData
>(this_
);
3271 data
->m_row
= HHVM_MN(PDOStatement
, fetch
)(this_
, PDO_FETCH_USE_DEFAULT
);
3272 if (same(data
->m_row
, false)) {
3273 data
->m_rowIndex
= -1;
3280 static Variant
HHVM_METHOD(PDOStatement
, rewind
) {
3281 auto data
= Native::data
<PDOStatementData
>(this_
);
3283 data
->m_rowIndex
= -1;
3284 HHVM_MN(PDOStatement
, next
)(this_
);
3288 static Variant
HHVM_METHOD(PDOStatement
, valid
) {
3289 auto data
= Native::data
<PDOStatementData
>(this_
);
3291 return data
->m_rowIndex
>= 0;
3294 static Variant
HHVM_METHOD(PDOStatement
, __wakeup
) {
3295 throw_pdo_exception(uninit_null(), uninit_null(),
3296 "You cannot serialize or unserialize "
3297 "PDOStatement instances");
3301 static Variant
HHVM_METHOD(PDOStatement
, __sleep
) {
3302 throw_pdo_exception(uninit_null(), uninit_null(),
3303 "You cannot serialize or unserialize "
3304 "PDOStatement instances");
3308 ///////////////////////////////////////////////////////////////////////////////
3311 static struct PDOExtension final
: Extension
{
3312 PDOExtension() : Extension("pdo", " 1.0.4dev") {}
3314 #ifdef ENABLE_EXTENSION_PDO_MYSQL
3315 std::string mysql_default_socket
;
3317 void moduleLoad(const IniSetting::Map
& /*ini*/, Hdf
/*config*/) override
{
3318 IniSetting::Bind(this, IniSetting::PHP_INI_SYSTEM
,
3319 "pdo_mysql.default_socket", nullptr,
3320 &mysql_default_socket
);
3324 void moduleInit() override
{
3325 HHVM_FE(pdo_drivers
);
3326 HHVM_ME(PDO
, __construct
);
3327 HHVM_ME(PDO
, prepare
);
3328 HHVM_ME(PDO
, begintransaction
);
3329 HHVM_ME(PDO
, commit
);
3330 HHVM_ME(PDO
, intransaction
);
3331 HHVM_ME(PDO
, rollback
);
3332 HHVM_ME(PDO
, setattribute
);
3333 HHVM_ME(PDO
, getattribute
);
3335 HHVM_ME(PDO
, lastinsertid
);
3336 HHVM_ME(PDO
, errorcode
);
3337 HHVM_ME(PDO
, errorinfo
);
3338 HHVM_ME(PDO
, query
);
3339 HHVM_ME(PDO
, quote
);
3340 HHVM_ME(PDO
, sqlitecreatefunction
);
3341 HHVM_ME(PDO
, sqlitecreateaggregate
);
3342 HHVM_ME(PDO
, __wakeup
);
3343 HHVM_ME(PDO
, __sleep
);
3344 HHVM_STATIC_ME(PDO
, getAvailableDrivers
);
3345 HHVM_ME(PDOStatement
, execute
);
3346 HHVM_ME(PDOStatement
, fetch
);
3347 HHVM_ME(PDOStatement
, fetchobject
);
3348 HHVM_ME(PDOStatement
, fetchcolumn
);
3349 HHVM_ME(PDOStatement
, fetchall
);
3350 HHVM_ME(PDOStatement
, bindvalue
);
3351 HHVM_ME(PDOStatement
, bindparam
);
3352 HHVM_ME(PDOStatement
, bindcolumn
);
3353 HHVM_ME(PDOStatement
, rowcount
);
3354 HHVM_ME(PDOStatement
, errorcode
);
3355 HHVM_ME(PDOStatement
, errorinfo
);
3356 HHVM_ME(PDOStatement
, setattribute
);
3357 HHVM_ME(PDOStatement
, getattribute
);
3358 HHVM_ME(PDOStatement
, columncount
);
3359 HHVM_ME(PDOStatement
, getcolumnmeta
);
3360 HHVM_ME(PDOStatement
, setfetchmode
);
3361 HHVM_ME(PDOStatement
, nextrowset
);
3362 HHVM_ME(PDOStatement
, closecursor
);
3363 HHVM_ME(PDOStatement
, debugdumpparams
);
3364 HHVM_ME(PDOStatement
, current
);
3365 HHVM_ME(PDOStatement
, key
);
3366 HHVM_ME(PDOStatement
, next
);
3367 HHVM_ME(PDOStatement
, rewind
);
3368 HHVM_ME(PDOStatement
, valid
);
3369 HHVM_ME(PDOStatement
, __wakeup
);
3370 HHVM_ME(PDOStatement
, __sleep
);
3372 HHVM_RCC_INT(PDO
, PARAM_BOOL
, PDO_PARAM_BOOL
);
3373 HHVM_RCC_INT(PDO
, PARAM_NULL
, PDO_PARAM_NULL
);
3374 HHVM_RCC_INT(PDO
, PARAM_INT
, PDO_PARAM_INT
);
3375 HHVM_RCC_INT(PDO
, PARAM_STR
, PDO_PARAM_STR
);
3376 HHVM_RCC_INT(PDO
, PARAM_LOB
, PDO_PARAM_LOB
);
3377 HHVM_RCC_INT(PDO
, PARAM_STMT
, PDO_PARAM_STMT
);
3378 HHVM_RCC_INT(PDO
, PARAM_INPUT_OUTPUT
, PDO_PARAM_INPUT_OUTPUT
);
3379 HHVM_RCC_INT(PDO
, PARAM_EVT_ALLOC
, PDO_PARAM_EVT_ALLOC
);
3380 HHVM_RCC_INT(PDO
, PARAM_EVT_FREE
, PDO_PARAM_EVT_FREE
);
3381 HHVM_RCC_INT(PDO
, PARAM_EVT_EXEC_PRE
, PDO_PARAM_EVT_EXEC_PRE
);
3382 HHVM_RCC_INT(PDO
, PARAM_EVT_EXEC_POST
, PDO_PARAM_EVT_EXEC_POST
);
3383 HHVM_RCC_INT(PDO
, PARAM_EVT_FETCH_PRE
, PDO_PARAM_EVT_FETCH_PRE
);
3384 HHVM_RCC_INT(PDO
, PARAM_EVT_FETCH_POST
, PDO_PARAM_EVT_FETCH_POST
);
3385 HHVM_RCC_INT(PDO
, PARAM_EVT_NORMALIZE
, PDO_PARAM_EVT_NORMALIZE
);
3386 HHVM_RCC_INT(PDO
, FETCH_USE_DEFAULT
, PDO_FETCH_USE_DEFAULT
);
3387 HHVM_RCC_INT(PDO
, FETCH_LAZY
, PDO_FETCH_LAZY
);
3388 HHVM_RCC_INT(PDO
, FETCH_ASSOC
, PDO_FETCH_ASSOC
);
3389 HHVM_RCC_INT(PDO
, FETCH_NUM
, PDO_FETCH_NUM
);
3390 HHVM_RCC_INT(PDO
, FETCH_BOTH
, PDO_FETCH_BOTH
);
3391 HHVM_RCC_INT(PDO
, FETCH_OBJ
, PDO_FETCH_OBJ
);
3392 HHVM_RCC_INT(PDO
, FETCH_BOUND
, PDO_FETCH_BOUND
);
3393 HHVM_RCC_INT(PDO
, FETCH_COLUMN
, PDO_FETCH_COLUMN
);
3394 HHVM_RCC_INT(PDO
, FETCH_CLASS
, PDO_FETCH_CLASS
);
3395 HHVM_RCC_INT(PDO
, FETCH_INTO
, PDO_FETCH_INTO
);
3396 HHVM_RCC_INT(PDO
, FETCH_FUNC
, PDO_FETCH_FUNC
);
3397 HHVM_RCC_INT(PDO
, FETCH_GROUP
, PDO_FETCH_GROUP
);
3398 HHVM_RCC_INT(PDO
, FETCH_UNIQUE
, PDO_FETCH_UNIQUE
);
3399 HHVM_RCC_INT(PDO
, FETCH_KEY_PAIR
, PDO_FETCH_KEY_PAIR
);
3400 HHVM_RCC_INT(PDO
, FETCH_CLASSTYPE
, PDO_FETCH_CLASSTYPE
);
3401 HHVM_RCC_INT(PDO
, FETCH_SERIALIZE
, PDO_FETCH_SERIALIZE
);
3402 HHVM_RCC_INT(PDO
, FETCH_PROPS_LATE
, PDO_FETCH_PROPS_LATE
);
3403 HHVM_RCC_INT(PDO
, FETCH_NAMED
, PDO_FETCH_NAMED
);
3404 HHVM_RCC_INT(PDO
, ATTR_AUTOCOMMIT
, PDO_ATTR_AUTOCOMMIT
);
3405 HHVM_RCC_INT(PDO
, ATTR_PREFETCH
, PDO_ATTR_PREFETCH
);
3406 HHVM_RCC_INT(PDO
, ATTR_TIMEOUT
, PDO_ATTR_TIMEOUT
);
3407 HHVM_RCC_INT(PDO
, ATTR_ERRMODE
, PDO_ATTR_ERRMODE
);
3408 HHVM_RCC_INT(PDO
, ATTR_SERVER_VERSION
, PDO_ATTR_SERVER_VERSION
);
3409 HHVM_RCC_INT(PDO
, ATTR_CLIENT_VERSION
, PDO_ATTR_CLIENT_VERSION
);
3410 HHVM_RCC_INT(PDO
, ATTR_SERVER_INFO
, PDO_ATTR_SERVER_INFO
);
3411 HHVM_RCC_INT(PDO
, ATTR_CONNECTION_STATUS
, PDO_ATTR_CONNECTION_STATUS
);
3412 HHVM_RCC_INT(PDO
, ATTR_CASE
, PDO_ATTR_CASE
);
3413 HHVM_RCC_INT(PDO
, ATTR_CURSOR_NAME
, PDO_ATTR_CURSOR_NAME
);
3414 HHVM_RCC_INT(PDO
, ATTR_CURSOR
, PDO_ATTR_CURSOR
);
3415 HHVM_RCC_INT(PDO
, ATTR_ORACLE_NULLS
, PDO_ATTR_ORACLE_NULLS
);
3416 HHVM_RCC_INT(PDO
, ATTR_PERSISTENT
, PDO_ATTR_PERSISTENT
);
3417 HHVM_RCC_INT(PDO
, ATTR_STATEMENT_CLASS
, PDO_ATTR_STATEMENT_CLASS
);
3418 HHVM_RCC_INT(PDO
, ATTR_FETCH_TABLE_NAMES
, PDO_ATTR_FETCH_TABLE_NAMES
);
3419 HHVM_RCC_INT(PDO
, ATTR_FETCH_CATALOG_NAMES
, PDO_ATTR_FETCH_CATALOG_NAMES
);
3420 HHVM_RCC_INT(PDO
, ATTR_DRIVER_NAME
, PDO_ATTR_DRIVER_NAME
);
3421 HHVM_RCC_INT(PDO
, ATTR_STRINGIFY_FETCHES
, PDO_ATTR_STRINGIFY_FETCHES
);
3422 HHVM_RCC_INT(PDO
, ATTR_MAX_COLUMN_LEN
, PDO_ATTR_MAX_COLUMN_LEN
);
3423 HHVM_RCC_INT(PDO
, ATTR_EMULATE_PREPARES
, PDO_ATTR_EMULATE_PREPARES
);
3424 HHVM_RCC_INT(PDO
, ATTR_DEFAULT_FETCH_MODE
, PDO_ATTR_DEFAULT_FETCH_MODE
);
3425 HHVM_RCC_INT(PDO
, ERRMODE_SILENT
, PDO_ERRMODE_SILENT
);
3426 HHVM_RCC_INT(PDO
, ERRMODE_WARNING
, PDO_ERRMODE_WARNING
);
3427 HHVM_RCC_INT(PDO
, ERRMODE_EXCEPTION
, PDO_ERRMODE_EXCEPTION
);
3428 HHVM_RCC_INT(PDO
, CASE_NATURAL
, PDO_CASE_NATURAL
);
3429 HHVM_RCC_INT(PDO
, CASE_LOWER
, PDO_CASE_LOWER
);
3430 HHVM_RCC_INT(PDO
, CASE_UPPER
, PDO_CASE_UPPER
);
3431 HHVM_RCC_INT(PDO
, NULL_NATURAL
, PDO_NULL_NATURAL
);
3432 HHVM_RCC_INT(PDO
, NULL_EMPTY_STRING
, PDO_NULL_EMPTY_STRING
);
3433 HHVM_RCC_INT(PDO
, NULL_TO_STRING
, PDO_NULL_TO_STRING
);
3434 HHVM_RCC_INT(PDO
, FETCH_ORI_NEXT
, PDO_FETCH_ORI_NEXT
);
3435 HHVM_RCC_INT(PDO
, FETCH_ORI_PRIOR
, PDO_FETCH_ORI_PRIOR
);
3436 HHVM_RCC_INT(PDO
, FETCH_ORI_FIRST
, PDO_FETCH_ORI_FIRST
);
3437 HHVM_RCC_INT(PDO
, FETCH_ORI_LAST
, PDO_FETCH_ORI_LAST
);
3438 HHVM_RCC_INT(PDO
, FETCH_ORI_ABS
, PDO_FETCH_ORI_ABS
);
3439 HHVM_RCC_INT(PDO
, FETCH_ORI_REL
, PDO_FETCH_ORI_REL
);
3440 HHVM_RCC_INT(PDO
, CURSOR_FWDONLY
, PDO_CURSOR_FWDONLY
);
3441 HHVM_RCC_INT(PDO
, CURSOR_SCROLL
, PDO_CURSOR_SCROLL
);
3442 #ifdef ENABLE_EXTENSION_PDO_MYSQL
3443 HHVM_RCC_INT(PDO
, MYSQL_ATTR_USE_BUFFERED_QUERY
,
3444 PDO_MYSQL_ATTR_USE_BUFFERED_QUERY
);
3445 HHVM_RCC_INT(PDO
, MYSQL_ATTR_LOCAL_INFILE
, PDO_MYSQL_ATTR_LOCAL_INFILE
);
3446 HHVM_RCC_INT(PDO
, MYSQL_ATTR_MAX_BUFFER_SIZE
,
3447 PDO_MYSQL_ATTR_MAX_BUFFER_SIZE
);
3448 HHVM_RCC_INT(PDO
, MYSQL_ATTR_INIT_COMMAND
, PDO_MYSQL_ATTR_INIT_COMMAND
);
3449 HHVM_RCC_INT(PDO
, MYSQL_ATTR_READ_DEFAULT_FILE
,
3450 PDO_MYSQL_ATTR_READ_DEFAULT_FILE
);
3451 HHVM_RCC_INT(PDO
, MYSQL_ATTR_READ_DEFAULT_GROUP
,
3452 PDO_MYSQL_ATTR_READ_DEFAULT_GROUP
);
3453 HHVM_RCC_INT(PDO
, MYSQL_ATTR_COMPRESS
, PDO_MYSQL_ATTR_COMPRESS
);
3454 HHVM_RCC_INT(PDO
, MYSQL_ATTR_DIRECT_QUERY
, PDO_MYSQL_ATTR_DIRECT_QUERY
);
3455 HHVM_RCC_INT(PDO
, MYSQL_ATTR_FOUND_ROWS
, PDO_MYSQL_ATTR_FOUND_ROWS
);
3456 HHVM_RCC_INT(PDO
, MYSQL_ATTR_IGNORE_SPACE
, PDO_MYSQL_ATTR_IGNORE_SPACE
);
3457 HHVM_RCC_INT(PDO
, MYSQL_ATTR_SSL_CA
, PDO_MYSQL_ATTR_SSL_CA
);
3458 HHVM_RCC_INT(PDO
, MYSQL_ATTR_SSL_CAPATH
, PDO_MYSQL_ATTR_SSL_CAPATH
);
3459 HHVM_RCC_INT(PDO
, MYSQL_ATTR_SSL_CERT
, PDO_MYSQL_ATTR_SSL_CERT
);
3460 HHVM_RCC_INT(PDO
, MYSQL_ATTR_SSL_KEY
, PDO_MYSQL_ATTR_SSL_KEY
);
3461 HHVM_RCC_INT(PDO
, MYSQL_ATTR_SSL_CIPHER
, PDO_MYSQL_ATTR_SSL_CIPHER
);
3462 HHVM_RCC_INT(PDO
, MYSQL_ATTR_MULTI_STATEMENTS
,
3463 PDO_MYSQL_ATTR_MULTI_STATEMENTS
);
3464 HHVM_RCC_INT(PDO
, HH_MYSQL_ATTR_READ_TIMEOUT
,
3465 HH_PDO_MYSQL_ATTR_READ_TIMEOUT
);
3466 HHVM_RCC_INT(PDO
, HH_MYSQL_ATTR_WRITE_TIMEOUT
,
3467 HH_PDO_MYSQL_ATTR_WRITE_TIMEOUT
);
3469 HHVM_RCC_STR(PDO
, ERR_NONE
, PDO_ERR_NONE
);
3471 Native::registerNativeDataInfo
<PDOData
>(
3472 s_PDO
.get(), Native::NDIFlags::NO_SWEEP
);
3473 Native::registerNativeDataInfo
<PDOStatementData
>(
3474 s_PDOStatement
.get(), Native::NDIFlags::NO_SWEEP
);
3476 loadSystemlib("pdo");
3480 //////////////////////////////////////////////////////////////////////////////