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/rds-local.h"
25 #include "hphp/util/string-vsnprintf.h"
27 #include "hphp/runtime/base/array-init.h"
28 #include "hphp/runtime/base/array-iterator.h"
29 #include "hphp/runtime/base/comparisons.h"
30 #include "hphp/runtime/base/file.h"
31 #include "hphp/runtime/base/ini-setting.h"
32 #include "hphp/runtime/base/string-buffer.h"
33 #include "hphp/runtime/base/tv-refcount.h"
34 #include "hphp/runtime/vm/jit/translator-inline.h"
35 #include "hphp/runtime/vm/interp-helpers.h"
37 #include "hphp/runtime/ext/extension.h"
38 #include "hphp/runtime/ext/array/ext_array.h"
39 #include "hphp/runtime/ext/pdo/pdo_driver.h"
40 #ifdef ENABLE_EXTENSION_PDO_MYSQL
41 #include "hphp/runtime/ext/pdo_mysql/pdo_mysql.h"
43 #ifdef ENABLE_EXTENSION_PDO_SQLITE
44 #include "hphp/runtime/ext/pdo_sqlite/pdo_sqlite.h"
46 #include "hphp/runtime/ext/std/ext_std_classobj.h"
47 #include "hphp/runtime/ext/std/ext_std_function.h"
48 #include "hphp/runtime/ext/stream/ext_stream.h"
49 #include "hphp/runtime/ext/string/ext_string.h"
51 #define PDO_HANDLE_DBH_ERR(dbh) \
52 if (strcmp(dbh->conn()->error_code, PDO_ERR_NONE)) { \
53 pdo_handle_error(dbh, nullptr); \
56 #define PDO_HANDLE_STMT_ERR(stmt) \
57 if (strcmp(stmt->error_code, PDO_ERR_NONE)) { \
58 pdo_handle_error(stmt->dbh, stmt); \
67 struct PDOStatementData
{
71 sp_PDOStatement m_stmt
;
77 ///////////////////////////////////////////////////////////////////////////////
78 // extension functions
80 Array
HHVM_FUNCTION(pdo_drivers
) {
81 const auto& drivers
= PDODriver::GetDrivers();
82 VecInit ret
{drivers
.size()};
83 for (const auto& driver
: drivers
) {
84 ret
.append(driver
.second
->getName());
89 ///////////////////////////////////////////////////////////////////////////////
92 struct pdo_sqlstate_info
{
97 static const struct pdo_sqlstate_info err_initializer
[] = {
98 { "00000", "No error" },
99 { "01000", "Warning" },
100 { "01001", "Cursor operation conflict" },
101 { "01002", "Disconnect error" },
102 { "01003", "NULL value eliminated in set function" },
103 { "01004", "String data, right truncated" },
104 { "01006", "Privilege not revoked" },
105 { "01007", "Privilege not granted" },
106 { "01008", "Implicit zero bit padding" },
107 { "0100C", "Dynamic result sets returned" },
108 { "01P01", "Deprecated feature" },
109 { "01S00", "Invalid connection string attribute" },
110 { "01S01", "Error in row" },
111 { "01S02", "Option value changed" },
113 "Attempt to fetch before the result set returned the first rowset" },
114 { "01S07", "Fractional truncation" },
115 { "01S08", "Error saving File DSN" },
116 { "01S09", "Invalid keyword" },
117 { "02000", "No data" },
118 { "02001", "No additional dynamic result sets returned" },
119 { "03000", "Sql statement not yet complete" },
120 { "07002", "COUNT field incorrect" },
121 { "07005", "Prepared statement not a cursor-specification" },
122 { "07006", "Restricted data type attribute violation" },
123 { "07009", "Invalid descriptor index" },
124 { "07S01", "Invalid use of default parameter" },
125 { "08000", "Connection exception" },
126 { "08001", "Client unable to establish connection" },
127 { "08002", "Connection name in use" },
128 { "08003", "Connection does not exist" },
129 { "08004", "Server rejected the connection" },
130 { "08006", "Connection failure" },
131 { "08007", "Connection failure during transaction" },
132 { "08S01", "Communication link failure" },
133 { "09000", "Triggered action exception" },
134 { "0A000", "Feature not supported" },
135 { "0B000", "Invalid transaction initiation" },
136 { "0F000", "Locator exception" },
137 { "0F001", "Invalid locator specification" },
138 { "0L000", "Invalid grantor" },
139 { "0LP01", "Invalid grant operation" },
140 { "0P000", "Invalid role specification" },
141 { "21000", "Cardinality violation" },
142 { "21S01", "Insert value list does not match column list" },
143 { "21S02", "Degree of derived table does not match column list" },
144 { "22000", "Data exception" },
145 { "22001", "String data, right truncated" },
146 { "22002", "Indicator variable required but not supplied" },
147 { "22003", "Numeric value out of range" },
148 { "22004", "Null value not allowed" },
149 { "22005", "Error in assignment" },
150 { "22007", "Invalid datetime format" },
151 { "22008", "Datetime field overflow" },
152 { "22009", "Invalid time zone displacement value" },
153 { "2200B", "Escape character conflict" },
154 { "2200C", "Invalid use of escape character" },
155 { "2200D", "Invalid escape octet" },
156 { "2200F", "Zero length character string" },
157 { "2200G", "Most specific type mismatch" },
158 { "22010", "Invalid indicator parameter value" },
159 { "22011", "Substring error" },
160 { "22012", "Division by zero" },
161 { "22015", "Interval field overflow" },
162 { "22018", "Invalid character value for cast specification" },
163 { "22019", "Invalid escape character" },
164 { "2201B", "Invalid regular expression" },
165 { "2201E", "Invalid argument for logarithm" },
166 { "2201F", "Invalid argument for power function" },
167 { "2201G", "Invalid argument for width bucket function" },
168 { "22020", "Invalid limit value" },
169 { "22021", "Character not in repertoire" },
170 { "22022", "Indicator overflow" },
171 { "22023", "Invalid parameter value" },
172 { "22024", "Unterminated c string" },
173 { "22025", "Invalid escape sequence" },
174 { "22026", "String data, length mismatch" },
175 { "22027", "Trim error" },
176 { "2202E", "Array subscript error" },
177 { "22P01", "Floating point exception" },
178 { "22P02", "Invalid text representation" },
179 { "22P03", "Invalid binary representation" },
180 { "22P04", "Bad copy file format" },
181 { "22P05", "Untranslatable character" },
182 { "23000", "Integrity constraint violation" },
183 { "23001", "Restrict violation" },
184 { "23502", "Not null violation" },
185 { "23503", "Foreign key violation" },
186 { "23505", "Unique violation" },
187 { "23514", "Check violation" },
188 { "24000", "Invalid cursor state" },
189 { "25000", "Invalid transaction state" },
190 { "25001", "Active sql transaction" },
191 { "25002", "Branch transaction already active" },
192 { "25003", "Inappropriate access mode for branch transaction" },
193 { "25004", "Inappropriate isolation level for branch transaction" },
194 { "25005", "No active sql transaction for branch transaction" },
195 { "25006", "Read only sql transaction" },
196 { "25007", "Schema and data statement mixing not supported" },
197 { "25008", "Held cursor requires same isolation level" },
198 { "25P01", "No active sql transaction" },
199 { "25P02", "In failed sql transaction" },
200 { "25S01", "Transaction state" },
201 { "25S02", "Transaction is still active" },
202 { "25S03", "Transaction is rolled back" },
203 { "26000", "Invalid sql statement name" },
204 { "27000", "Triggered data change violation" },
205 { "28000", "Invalid authorization specification" },
206 { "2B000", "Dependent privilege descriptors still exist" },
207 { "2BP01", "Dependent objects still exist" },
208 { "2D000", "Invalid transaction termination" },
209 { "2F000", "Sql routine exception" },
210 { "2F002", "Modifying sql data not permitted" },
211 { "2F003", "Prohibited sql statement attempted" },
212 { "2F004", "Reading sql data not permitted" },
213 { "2F005", "Function executed no return statement" },
214 { "34000", "Invalid cursor name" },
215 { "38000", "External routine exception" },
216 { "38001", "Containing sql not permitted" },
217 { "38002", "Modifying sql data not permitted" },
218 { "38003", "Prohibited sql statement attempted" },
219 { "38004", "Reading sql data not permitted" },
220 { "39000", "External routine invocation exception" },
221 { "39001", "Invalid sqlstate returned" },
222 { "39004", "Null value not allowed" },
223 { "39P01", "Trigger protocol violated" },
224 { "39P02", "Srf protocol violated" },
225 { "3B000", "Savepoint exception" },
226 { "3B001", "Invalid savepoint specification" },
227 { "3C000", "Duplicate cursor name" },
228 { "3D000", "Invalid catalog name" },
229 { "3F000", "Invalid schema name" },
230 { "40000", "Transaction rollback" },
231 { "40001", "Serialization failure" },
232 { "40002", "Transaction integrity constraint violation" },
233 { "40003", "Statement completion unknown" },
234 { "40P01", "Deadlock detected" },
235 { "42000", "Syntax error or access violation" },
236 { "42501", "Insufficient privilege" },
237 { "42601", "Syntax error" },
238 { "42602", "Invalid name" },
239 { "42611", "Invalid column definition" },
240 { "42622", "Name too long" },
241 { "42701", "Duplicate column" },
242 { "42702", "Ambiguous column" },
243 { "42703", "Undefined column" },
244 { "42704", "Undefined object" },
245 { "42710", "Duplicate object" },
246 { "42712", "Duplicate alias" },
247 { "42723", "Duplicate function" },
248 { "42725", "Ambiguous function" },
249 { "42803", "Grouping error" },
250 { "42804", "Datatype mismatch" },
251 { "42809", "Wrong object type" },
252 { "42830", "Invalid foreign key" },
253 { "42846", "Cannot coerce" },
254 { "42883", "Undefined function" },
255 { "42939", "Reserved name" },
256 { "42P01", "Undefined table" },
257 { "42P02", "Undefined parameter" },
258 { "42P03", "Duplicate cursor" },
259 { "42P04", "Duplicate database" },
260 { "42P05", "Duplicate prepared statement" },
261 { "42P06", "Duplicate schema" },
262 { "42P07", "Duplicate table" },
263 { "42P08", "Ambiguous parameter" },
264 { "42P09", "Ambiguous alias" },
265 { "42P10", "Invalid column reference" },
266 { "42P11", "Invalid cursor definition" },
267 { "42P12", "Invalid database definition" },
268 { "42P13", "Invalid function definition" },
269 { "42P14", "Invalid prepared statement definition" },
270 { "42P15", "Invalid schema definition" },
271 { "42P16", "Invalid table definition" },
272 { "42P17", "Invalid object definition" },
273 { "42P18", "Indeterminate datatype" },
274 { "42S01", "Base table or view already exists" },
275 { "42S02", "Base table or view not found" },
276 { "42S11", "Index already exists" },
277 { "42S12", "Index not found" },
278 { "42S21", "Column already exists" },
279 { "42S22", "Column not found" },
280 { "44000", "WITH CHECK OPTION violation" },
281 { "53000", "Insufficient resources" },
282 { "53100", "Disk full" },
283 { "53200", "Out of memory" },
284 { "53300", "Too many connections" },
285 { "54000", "Program limit exceeded" },
286 { "54001", "Statement too complex" },
287 { "54011", "Too many columns" },
288 { "54023", "Too many arguments" },
289 { "55000", "Object not in prerequisite state" },
290 { "55006", "Object in use" },
291 { "55P02", "Cant change runtime param" },
292 { "55P03", "Lock not available" },
293 { "57000", "Operator intervention" },
294 { "57014", "Query canceled" },
295 { "57P01", "Admin shutdown" },
296 { "57P02", "Crash shutdown" },
297 { "57P03", "Cannot connect now" },
298 { "58030", "Io error" },
299 { "58P01", "Undefined file" },
300 { "58P02", "Duplicate file" },
301 { "F0000", "Config file error" },
302 { "F0001", "Lock file exists" },
303 { "HY000", "General error" },
304 { "HY001", "Memory allocation error" },
305 { "HY003", "Invalid application buffer type" },
306 { "HY004", "Invalid SQL data type" },
307 { "HY007", "Associated statement is not prepared" },
308 { "HY008", "Operation canceled" },
309 { "HY009", "Invalid use of null pointer" },
310 { "HY010", "Function sequence error" },
311 { "HY011", "Attribute cannot be set now" },
312 { "HY012", "Invalid transaction operation code" },
313 { "HY013", "Memory management error" },
314 { "HY014", "Limit on the number of handles exceeded" },
315 { "HY015", "No cursor name available" },
316 { "HY016", "Cannot modify an implementation row descriptor" },
317 { "HY017", "Invalid use of an automatically allocated descriptor handle" },
318 { "HY018", "Server declined cancel request" },
319 { "HY019", "Non-character and non-binary data sent in pieces" },
320 { "HY020", "Attempt to concatenate a null value" },
321 { "HY021", "Inconsistent descriptor information" },
322 { "HY024", "Invalid attribute value" },
323 { "HY090", "Invalid string or buffer length" },
324 { "HY091", "Invalid descriptor field identifier" },
325 { "HY092", "Invalid attribute/option identifier" },
326 { "HY093", "Invalid parameter number" },
327 { "HY095", "Function type out of range" },
328 { "HY096", "Invalid information type" },
329 { "HY097", "Column type out of range" },
330 { "HY098", "Scope type out of range" },
331 { "HY099", "Nullable type out of range" },
332 { "HY100", "Uniqueness option type out of range" },
333 { "HY101", "Accuracy option type out of range" },
334 { "HY103", "Invalid retrieval code" },
335 { "HY104", "Invalid precision or scale value" },
336 { "HY105", "Invalid parameter type" },
337 { "HY106", "Fetch type out of range" },
338 { "HY107", "Row value out of range" },
339 { "HY109", "Invalid cursor position" },
340 { "HY110", "Invalid driver completion" },
341 { "HY111", "Invalid bookmark value" },
342 { "HYC00", "Optional feature not implemented" },
343 { "HYT00", "Timeout expired" },
344 { "HYT01", "Connection timeout expired" },
345 { "IM001", "Driver does not support this function" },
346 { "IM002", "Data source name not found and no default driver specified" },
347 { "IM003", "Specified driver could not be loaded" },
348 { "IM004", "Driver's SQLAllocHandle on SQL_HANDLE_ENV failed" },
349 { "IM005", "Driver's SQLAllocHandle on SQL_HANDLE_DBC failed" },
350 { "IM006", "Driver's SQLSetConnectAttr failed" },
351 { "IM007", "No data source or driver specified; dialog prohibited" },
352 { "IM008", "Dialog failed" },
353 { "IM009", "Unable to load translation DLL" },
354 { "IM010", "Data source name too long" },
355 { "IM011", "Driver name too long" },
356 { "IM012", "DRIVER keyword syntax error" },
357 { "IM013", "Trace file error" },
358 { "IM014", "Invalid name of File DSN" },
359 { "IM015", "Corrupt file data source" },
360 { "P0000", "Plpgsql error" },
361 { "P0001", "Raise exception" },
362 { "XX000", "Internal error" },
363 { "XX001", "Data corrupted" },
364 { "XX002", "Index corrupted" }
367 struct PDOErrorHash
: private hphp_const_char_map
<const char *> {
369 for (unsigned int i
= 0;
370 i
< sizeof(err_initializer
)/sizeof(err_initializer
[0]); i
++) {
371 const struct pdo_sqlstate_info
*info
= &err_initializer
[i
];
372 (*this)[info
->state
] = info
->desc
;
376 const char *description(const char *state
) {
377 const_iterator iter
= find(state
);
381 return "<<Unknown error>>";
384 static PDOErrorHash s_err_hash
;
388 s_message("message"),
389 s_errorInfo("errorInfo"),
390 s_PDOException("PDOException");
392 void throw_pdo_exception(const Variant
& info
, const char *fmt
, ...) {
393 auto obj
= SystemLib::AllocPDOExceptionObject();
394 obj
->o_set(s_code
, 0, 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(info
.toArray(), "%s",
436 void pdo_raise_impl_error(sp_PDOResource rsrc
, sp_PDOStatement stmt
,
437 const char *sqlstate
, const char *supp
) {
438 pdo_raise_impl_error(rsrc
, stmt
.get(), sqlstate
, supp
);
443 void pdo_handle_error(sp_PDOResource rsrc
, PDOStatement
* stmt
) {
444 auto const& dbh
= rsrc
->conn();
446 if (dbh
->error_mode
== PDO_ERRMODE_SILENT
) {
449 PDOErrorType
*pdo_err
= &dbh
->error_code
;
451 pdo_err
= &stmt
->error_code
;
454 /* hash sqlstate to error messages */
455 const char *msg
= s_err_hash
.description(*pdo_err
);
457 int64_t native_code
= 0;
460 if (dbh
->support(PDOConnection::MethodFetchErr
)) {
461 info
= make_vec_array(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(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
= Class::load(name
.get());
517 callerDynamicConstructChecks(cls
);
521 const StaticString
s_queryString("queryString");
523 static void pdo_stmt_construct(sp_PDOStatement stmt
, Object object
,
524 const String
& clsname
,
525 const Variant
& ctor_args
) {
526 object
->setProp(nullctx
, s_queryString
.get(), stmt
->query_string
.asTypedValue());
527 if (clsname
.empty()) {
530 Class
* cls
= Class::load(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::Class::load(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 auto const column_key
=
614 stmt
->bound_columns
.convertKey
<IntishCast::Cast
>(column
->name
);
615 if (stmt
->bound_columns
.exists(column_key
)) {
616 auto param
= cast
<PDOBoundParam
>(stmt
->bound_columns
[column_key
]);
617 param
->paramno
= col
;
623 static bool pdo_stmt_verify_mode(sp_PDOStatement stmt
, int64_t mode
,
625 int flags
= mode
& PDO_FETCH_FLAGS
;
626 mode
= mode
& ~PDO_FETCH_FLAGS
;
628 if (mode
< 0 || mode
> PDO_FETCH__MAX
) {
629 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000", "invalid fetch mode");
633 if (mode
== PDO_FETCH_USE_DEFAULT
) {
634 flags
= stmt
->default_fetch_type
& PDO_FETCH_FLAGS
;
635 mode
= stmt
->default_fetch_type
& ~PDO_FETCH_FLAGS
;
641 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
642 "PDO::FETCH_FUNC is only allowed in "
643 "PDOStatement::fetchAll()");
650 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
651 "PDO::FETCH_LAZY can't be used with "
652 "PDOStatement::fetchAll()");
658 if ((flags
& PDO_FETCH_SERIALIZE
) == PDO_FETCH_SERIALIZE
) {
659 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
660 "PDO::FETCH_SERIALIZE can only be used "
661 "together with PDO::FETCH_CLASS");
664 if ((flags
& PDO_FETCH_CLASSTYPE
) == PDO_FETCH_CLASSTYPE
) {
665 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
666 "PDO::FETCH_CLASSTYPE can only be used "
667 "together with PDO::FETCH_CLASS");
670 if (mode
>= PDO_FETCH__MAX
) {
671 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000", "invalid fetch mode");
676 case PDO_FETCH_CLASS
:
682 static bool do_fetch_class_prepare(sp_PDOStatement stmt
) {
683 String clsname
= stmt
->fetch
.clsname
;
684 if (clsname
.empty()) {
685 stmt
->fetch
.clsname
= "stdClass";
687 stmt
->fetch
.constructor
= empty_string(); //NULL;
688 HPHP::Class
* cls
= HPHP::Class::load(clsname
.get());
690 const HPHP::Func
* method
= cls
->getDeclaredCtor();
692 stmt
->fetch
.constructor
= method
->nameStr();
696 if (!stmt
->fetch
.ctor_args
.isNull()) {
697 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
698 "user-supplied class does not have a constructor, "
699 "use NULL for the ctor_params parameter, or simply "
703 return true; /* no ctor no args is also ok */
706 static bool pdo_stmt_set_fetch_mode(sp_PDOStatement stmt
, int _argc
,
707 int64_t mode
, const Array
& _argv
) {
708 _argc
= _argv
.size() + 1;
710 if (stmt
->default_fetch_type
== PDO_FETCH_INTO
) {
711 stmt
->fetch
.into
.unset();
713 stmt
->default_fetch_type
= PDO_FETCH_BOTH
;
715 if (!pdo_stmt_verify_mode(stmt
, mode
, false)) {
716 setPDOErrorNone(stmt
->error_code
);
720 int flags
= mode
& PDO_FETCH_FLAGS
;
722 switch (mode
& ~PDO_FETCH_FLAGS
) {
723 case PDO_FETCH_USE_DEFAULT
:
725 case PDO_FETCH_ASSOC
:
729 case PDO_FETCH_BOUND
:
730 case PDO_FETCH_NAMED
:
731 case PDO_FETCH_KEY_PAIR
:
733 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
734 "fetch mode doesn't allow any extra arguments");
740 case PDO_FETCH_COLUMN
:
742 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
743 "fetch mode requires the colno argument");
744 } else if (!_argv
[0].isInteger()) {
745 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
746 "colno must be an integer");
748 stmt
->fetch
.column
= _argv
[0].toInt64();
753 case PDO_FETCH_CLASS
:
754 /* Gets its class name from 1st column */
755 if ((flags
& PDO_FETCH_CLASSTYPE
) == PDO_FETCH_CLASSTYPE
) {
757 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
758 "fetch mode doesn't allow any extra arguments");
760 stmt
->fetch
.clsname
.clear();
765 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
766 "fetch mode requires the classname argument");
767 } else if (_argc
> 3) {
768 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
769 "too many arguments");
770 } else if (!_argv
[0].isString()) {
771 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
772 "classname must be a string");
774 retval
= HHVM_FN(class_exists
)(_argv
[0].toString());
776 stmt
->fetch
.clsname
= _argv
[0].toString();
782 stmt
->fetch
.ctor_args
.unset();
784 if (!_argv
[1].isNull() && !_argv
[1].isArray()) {
785 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
786 "ctor_args must be either NULL or an array");
789 stmt
->fetch
.ctor_args
= _argv
[1];
794 do_fetch_class_prepare(stmt
);
802 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
803 "fetch mode requires the object parameter");
804 } else if (!_argv
[0].isObject()) {
805 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
806 "object must be an object");
812 stmt
->fetch
.into
= _argv
[0];
817 pdo_raise_impl_error(stmt
->dbh
, stmt
, "22003",
818 "Invalid fetch mode specified");
822 stmt
->default_fetch_type
= (PDOFetchType
)mode
;
826 * PDO error (if any) has already been raised at this point.
828 * The error_code is cleared, otherwise the caller will read the
829 * last error message from the driver.
832 setPDOErrorNone(stmt
->error_code
);
836 ///////////////////////////////////////////////////////////////////////////////
837 // forward declarations
839 bool HHVM_METHOD(PDO
, setAttribute
, int64_t attribute
,
840 const Variant
& value
);
842 ///////////////////////////////////////////////////////////////////////////////
846 using StorageT
= std::unordered_map
<std::string
, sp_PDOConnection
>;
847 RDS_LOCAL(StorageT
, s_connections
);
850 const StaticString
s_PDO("PDO");
852 void HHVM_METHOD(PDO
, __construct
, const String
& dsn
,
853 const String
& username
/* = null_string */,
854 const String
& password
/* = null_string */,
855 const Variant
& optionsV
/* = null_array */) {
856 auto data
= Native::data
<PDOData
>(this_
);
857 auto options
= optionsV
.isNull() ? null_array
: optionsV
.toArray();
859 String data_source
= dsn
;
861 /* parse the data source name */
862 const char *colon
= strchr(data_source
.data(), ':');
864 /* let's see if this string has a matching dsn in the php.ini */
865 String name
= "pdo.dsn."; name
+= data_source
;
867 if (!IniSetting::Get(name
, ini_dsn
)) {
868 throw_pdo_exception(uninit_null(), "invalid data source name");
870 data_source
= ini_dsn
;
871 colon
= strchr(data_source
.data(), ':');
873 throw_pdo_exception(uninit_null(),
874 "invalid data source name (via INI: %s)",
879 if (!strncmp(data_source
.data(), "uri:", 4)) {
880 /* the specified URI holds connection details */
881 auto file
= File::Open(data_source
.substr(4), "rb");
882 if (!file
|| file
->isInvalid()) {
883 throw_pdo_exception(uninit_null(), "invalid data source URI");
885 data_source
= file
->readLine(1024);
886 colon
= strchr(data_source
.data(), ':');
888 throw_pdo_exception(uninit_null(), "invalid data source name (via URI)");
892 const PDODriverMap
&drivers
= PDODriver::GetDrivers();
893 String name
= data_source
.substr(0, colon
- data_source
.data());
894 PDODriverMap::const_iterator iter
= drivers
.find(name
.data());
895 if (iter
== drivers
.end()) {
896 /* NB: don't want to include the data_source in the error message as
897 * it might contain a password */
898 throw_pdo_exception(uninit_null(), "could not find driver");
900 PDODriver
*driver
= iter
->second
;
902 /* is this supposed to be a persistent connection ? */
903 bool is_persistent
= false;
904 bool call_factory
= true;
905 std::string shashkey
;
906 if (!options
.empty()) {
907 StringBuffer hashkey
;
908 if (options
.exists(PDO_ATTR_PERSISTENT
)) {
909 Variant v
= options
[PDO_ATTR_PERSISTENT
];
910 String sv
= v
.toString();
911 if (v
.isString() && !sv
.isNumeric() && !sv
.empty()) {
912 /* user specified key */
913 hashkey
.printf("PDO:DBH:DSN=%s:%s:%s:%s",
914 data_source
.data(), username
.data(),
915 password
.data(), sv
.data());
916 is_persistent
= true;
918 is_persistent
= v
.toInt64();
919 hashkey
.printf("PDO:DBH:DSN=%s:%s:%s",
920 data_source
.data(), username
.data(),
926 shashkey
= hashkey
.detach().toCppString();
928 /* let's see if we have one cached.... */
929 if (s_connections
->count(shashkey
)) {
930 auto const conn
= (*s_connections
)[shashkey
];
931 data
->m_dbh
= driver
->createResource(conn
);
933 /* is the connection still alive ? */
934 if (conn
->support(PDOConnection::MethodCheckLiveness
) &&
935 !conn
->checkLiveness()) {
936 /* nope... need to kill it */
937 data
->m_dbh
= nullptr;
942 call_factory
= false;
944 /* need a brand new pdbh */
945 data
->m_dbh
= driver
->createResource(colon
+ 1, username
,
948 throw_pdo_exception(uninit_null(), "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(), "unable to create a connection");
963 data
->m_dbh
->conn()->default_fetch_type
= PDO_FETCH_BOTH
;
966 data
->m_dbh
->conn()->auto_commit
=
967 pdo_attr_lval(options
, PDO_ATTR_AUTOCOMMIT
, 1);
970 /* we got a persistent guy from our cache */
971 for (ArrayIter
iter(options
); iter
; ++iter
) {
972 HHVM_MN(PDO
, setAttribute
)(this_
, iter
.first().toInt64(),
975 } else if (data
->m_dbh
) {
977 assertx(!shashkey
.empty());
978 (*s_connections
)[shashkey
] = data
->m_dbh
->conn();
981 data
->m_dbh
->conn()->driver
= driver
;
982 for (ArrayIter
iter(options
); iter
; ++iter
) {
983 HHVM_MN(PDO
, setAttribute
)(this_
, iter
.first().toInt64(),
989 Variant
HHVM_METHOD(PDO
, prepare
, const String
& statement
,
990 const Array
& options
= null_array
) {
991 auto data
= Native::data
<PDOData
>(this_
);
993 assertx(data
->m_dbh
->conn()->driver
);
994 setPDOErrorNone(data
->m_dbh
->conn()->error_code
);
995 data
->m_dbh
->query_stmt
= nullptr;
999 if (options
.exists(PDO_ATTR_STATEMENT_CLASS
)) {
1000 Variant opt
= options
[PDO_ATTR_STATEMENT_CLASS
];
1001 if (!valid_statement_class(data
->m_dbh
, opt
, clsname
, ctor_args
)) {
1005 clsname
= data
->m_dbh
->conn()->def_stmt_clsname
;
1006 ctor_args
= data
->m_dbh
->def_stmt_ctor_args
;
1009 Object ret
= pdo_stmt_instantiate(data
->m_dbh
, clsname
, ctor_args
);
1011 pdo_raise_impl_error
1012 (data
->m_dbh
, nullptr, "HY000",
1013 "failed to instantiate user-supplied statement class");
1014 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1017 PDOStatementData
*pdostmt
= Native::data
<PDOStatementData
>(ret
);
1019 if (data
->m_dbh
->conn()->preparer(statement
, &pdostmt
->m_stmt
, options
)) {
1020 auto stmt
= pdostmt
->m_stmt
;
1023 /* unconditionally keep this for later reference */
1024 stmt
->query_string
= statement
;
1025 stmt
->default_fetch_type
= data
->m_dbh
->conn()->default_fetch_type
;
1026 stmt
->dbh
= data
->m_dbh
;
1028 pdo_stmt_construct(stmt
, ret
, clsname
, ctor_args
);
1032 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1036 static bool HHVM_METHOD(PDO
, beginTransaction
) {
1037 auto data
= Native::data
<PDOData
>(this_
);
1039 if (data
->m_dbh
->conn()->in_txn
) {
1040 throw_pdo_exception(uninit_null(),
1041 "There is already an active transaction");
1043 if (data
->m_dbh
->conn()->begin()) {
1044 data
->m_dbh
->conn()->in_txn
= 1;
1047 if (strcmp(data
->m_dbh
->conn()->error_code
, PDO_ERR_NONE
)) {
1048 pdo_handle_error(data
->m_dbh
, nullptr);
1053 static bool HHVM_METHOD(PDO
, commit
) {
1054 auto data
= Native::data
<PDOData
>(this_
);
1056 assertx(data
->m_dbh
->conn()->driver
);
1057 if (!data
->m_dbh
->conn()->in_txn
) {
1058 throw_pdo_exception(uninit_null(), "There is no active transaction");
1060 if (data
->m_dbh
->conn()->commit()) {
1061 data
->m_dbh
->conn()->in_txn
= 0;
1064 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1068 static bool HHVM_METHOD(PDO
, inTransaction
) {
1069 auto data
= Native::data
<PDOData
>(this_
);
1071 assertx(data
->m_dbh
->conn()->driver
);
1072 return data
->m_dbh
->conn()->in_txn
;
1075 static bool HHVM_METHOD(PDO
, rollBack
) {
1076 auto data
= Native::data
<PDOData
>(this_
);
1078 assertx(data
->m_dbh
->conn()->driver
);
1079 if (!data
->m_dbh
->conn()->in_txn
) {
1080 throw_pdo_exception(uninit_null(), "There is no active transaction");
1082 if (data
->m_dbh
->conn()->rollback()) {
1083 data
->m_dbh
->conn()->in_txn
= 0;
1086 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1090 bool HHVM_METHOD(PDO
, setAttribute
, int64_t attribute
,
1091 const Variant
& value
) {
1092 auto data
= Native::data
<PDOData
>(this_
);
1094 assertx(data
->m_dbh
->conn()->driver
);
1096 #define PDO_LONG_PARAM_CHECK \
1097 if (!value.isInteger() && !value.isString() && !value.isBoolean()) { \
1098 pdo_raise_impl_error(data->m_dbh, nullptr, "HY000", \
1099 "attribute value must be an integer"); \
1100 PDO_HANDLE_DBH_ERR(data->m_dbh); \
1104 switch (attribute) {
1105 case PDO_ATTR_ERRMODE
:
1106 PDO_LONG_PARAM_CHECK
;
1107 switch (value
.toInt64()) {
1108 case PDO_ERRMODE_SILENT
:
1109 case PDO_ERRMODE_WARNING
:
1110 case PDO_ERRMODE_EXCEPTION
:
1111 data
->m_dbh
->conn()->error_mode
= (PDOErrorMode
)value
.toInt64();
1114 pdo_raise_impl_error(data
->m_dbh
, nullptr, "HY000", "invalid error mode");
1115 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1121 PDO_LONG_PARAM_CHECK
;
1122 switch (value
.toInt64()) {
1123 case PDO_CASE_NATURAL
:
1124 case PDO_CASE_UPPER
:
1125 case PDO_CASE_LOWER
:
1126 data
->m_dbh
->conn()->desired_case
= (PDOCaseConversion
)value
.toInt64();
1129 pdo_raise_impl_error(data
->m_dbh
, nullptr, "HY000",
1130 "invalid case folding mode");
1131 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1136 case PDO_ATTR_ORACLE_NULLS
:
1137 PDO_LONG_PARAM_CHECK
;
1138 data
->m_dbh
->conn()->oracle_nulls
= value
.toInt64();
1141 case PDO_ATTR_DEFAULT_FETCH_MODE
:
1142 if (value
.isArray()) {
1143 if (value
.asCArrRef().exists(0)) {
1144 Variant tmp
= value
.asCArrRef()[0];
1145 if (tmp
.isInteger() && ((tmp
.toInt64() == PDO_FETCH_INTO
||
1146 tmp
.toInt64() == PDO_FETCH_CLASS
))) {
1147 pdo_raise_impl_error(data
->m_dbh
, nullptr, "HY000",
1148 "FETCH_INTO and FETCH_CLASS are not yet "
1149 "supported as default fetch modes");
1154 PDO_LONG_PARAM_CHECK
;
1156 if (value
.toInt64() == PDO_FETCH_USE_DEFAULT
) {
1157 pdo_raise_impl_error(data
->m_dbh
, nullptr,
1158 "HY000", "invalid fetch mode type");
1161 data
->m_dbh
->conn()->default_fetch_type
= (PDOFetchType
)value
.toInt64();
1164 case PDO_ATTR_STRINGIFY_FETCHES
:
1165 PDO_LONG_PARAM_CHECK
;
1166 data
->m_dbh
->conn()->stringify
= value
.toInt64() ? 1 : 0;
1169 case PDO_ATTR_STATEMENT_CLASS
:
1171 if (data
->m_dbh
->conn()->is_persistent
) {
1172 pdo_raise_impl_error(data
->m_dbh
, nullptr, "HY000",
1173 "PDO::ATTR_STATEMENT_CLASS cannot be used "
1174 "with persistent PDO instances");
1175 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1179 if (!valid_statement_class(data
->m_dbh
, value
, clsname
,
1180 data
->m_dbh
->def_stmt_ctor_args
)) {
1183 data
->m_dbh
->conn()->def_stmt_clsname
= clsname
.c_str();
1188 if (data
->m_dbh
->conn()->support(PDOConnection::MethodSetAttribute
)) {
1189 setPDOErrorNone(data
->m_dbh
->conn()->error_code
);
1190 data
->m_dbh
->query_stmt
= nullptr;
1191 if (data
->m_dbh
->conn()->setAttribute(attribute
, value
)) {
1196 if (attribute
== PDO_ATTR_AUTOCOMMIT
) {
1197 throw_pdo_exception(uninit_null(),
1198 "The auto-commit mode cannot be changed for this "
1200 } else if (!data
->m_dbh
->conn()->support(PDOConnection::MethodSetAttribute
)) {
1201 pdo_raise_impl_error(data
->m_dbh
, nullptr, "IM001",
1202 "driver does not support setting attributes");
1204 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1209 Variant
HHVM_METHOD(PDO
, getAttribute
, int64_t attribute
) {
1210 auto data
= Native::data
<PDOData
>(this_
);
1212 assertx(data
->m_dbh
->conn()->driver
);
1213 setPDOErrorNone(data
->m_dbh
->conn()->error_code
);
1214 data
->m_dbh
->query_stmt
= nullptr;
1216 /* handle generic PDO-level attributes */
1217 switch (attribute
) {
1218 case PDO_ATTR_PERSISTENT
:
1219 return (bool)data
->m_dbh
->conn()->is_persistent
;
1222 return (int64_t)data
->m_dbh
->conn()->desired_case
;
1224 case PDO_ATTR_ORACLE_NULLS
:
1225 return (int64_t)data
->m_dbh
->conn()->oracle_nulls
;
1227 case PDO_ATTR_ERRMODE
:
1228 return (int64_t)data
->m_dbh
->conn()->error_mode
;
1230 case PDO_ATTR_DRIVER_NAME
:
1231 return String(data
->m_dbh
->conn()->driver
->getName());
1233 case PDO_ATTR_STATEMENT_CLASS
: {
1235 ret
.append(String(data
->m_dbh
->conn()->def_stmt_clsname
));
1236 if (!data
->m_dbh
->def_stmt_ctor_args
.isNull()) {
1237 ret
.append(data
->m_dbh
->def_stmt_ctor_args
);
1241 case PDO_ATTR_DEFAULT_FETCH_MODE
:
1242 return (int64_t)data
->m_dbh
->conn()->default_fetch_type
;
1245 if (!data
->m_dbh
->conn()->support(PDOConnection::MethodGetAttribute
)) {
1246 pdo_raise_impl_error(data
->m_dbh
, nullptr, "IM001",
1247 "driver does not support getting attributes");
1252 switch (data
->m_dbh
->conn()->getAttribute(attribute
, ret
)) {
1254 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1257 pdo_raise_impl_error(data
->m_dbh
, nullptr, "IM001",
1258 "driver does not support that attribute");
1264 Variant
HHVM_METHOD(PDO
, exec
, const String
& query
) {
1265 auto data
= Native::data
<PDOData
>(this_
);
1267 SYNC_VM_REGS_SCOPED();
1268 if (query
.empty()) {
1269 pdo_raise_impl_error(data
->m_dbh
, nullptr, "HY000",
1270 "trying to execute an empty query");
1274 assertx(data
->m_dbh
->conn()->driver
);
1275 setPDOErrorNone(data
->m_dbh
->conn()->error_code
);
1276 data
->m_dbh
->query_stmt
= nullptr;
1278 int64_t ret
= data
->m_dbh
->conn()->doer(query
);
1280 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1286 static Variant
HHVM_METHOD(PDO
, lastInsertId
,
1287 const String
& seqname
/* = null_string */) {
1288 auto data
= Native::data
<PDOData
>(this_
);
1290 assertx(data
->m_dbh
->conn()->driver
);
1291 setPDOErrorNone(data
->m_dbh
->conn()->error_code
);
1292 data
->m_dbh
->query_stmt
= nullptr;
1294 if (!data
->m_dbh
->conn()->support(PDOConnection::MethodLastId
)) {
1295 pdo_raise_impl_error(data
->m_dbh
, nullptr, "IM001",
1296 "driver does not support lastInsertId()");
1300 String ret
= data
->m_dbh
->conn()->lastId(seqname
.data());
1302 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1308 static Variant
HHVM_METHOD(PDO
, errorCode
) {
1309 auto data
= Native::data
<PDOData
>(this_
);
1311 assertx(data
->m_dbh
->conn()->driver
);
1312 if (data
->m_dbh
->query_stmt
) {
1313 return String(data
->m_dbh
->query_stmt
->error_code
, CopyString
);
1316 if (data
->m_dbh
->conn()->error_code
[0] == '\0') {
1321 * Making sure that we fallback to the default implementation
1322 * if the dbh->error_code is not null.
1324 return String(data
->m_dbh
->conn()->error_code
, CopyString
);
1327 static Array
HHVM_METHOD(PDO
, errorInfo
) {
1328 auto data
= Native::data
<PDOData
>(this_
);
1330 assertx(data
->m_dbh
->conn()->driver
);
1332 Array ret
= Array::CreateVec();
1333 if (data
->m_dbh
->query_stmt
) {
1334 ret
.append(String(data
->m_dbh
->query_stmt
->error_code
, CopyString
));
1336 ret
.append(String(data
->m_dbh
->conn()->error_code
, CopyString
));
1339 if (data
->m_dbh
->conn()->support(PDOConnection::MethodFetchErr
)) {
1340 data
->m_dbh
->conn()->fetchErr(data
->m_dbh
->query_stmt
, ret
);
1344 * In order to be consistent, we have to make sure we add the good amount
1345 * of nulls depending on the current number of elements. We make a simple
1346 * difference and add the needed elements
1348 int error_count
= ret
.size();
1349 int error_expected_count
= 3;
1350 if (error_expected_count
> error_count
) {
1351 int error_count_diff
= error_expected_count
- error_count
;
1352 for (int i
= 0; i
< error_count_diff
; i
++) {
1353 ret
.append(init_null_variant
);
1359 Variant
HHVM_METHOD(PDO
, query
, const String
& sql
, const Array
& _argv
) {
1361 auto data
= Native::data
<PDOData
>(this_
);
1362 SYNC_VM_REGS_SCOPED();
1363 assertx(data
->m_dbh
->conn()->driver
);
1364 setPDOErrorNone(data
->m_dbh
->conn()->error_code
);
1365 data
->m_dbh
->query_stmt
= nullptr;
1367 Object ret
= pdo_stmt_instantiate(data
->m_dbh
,
1368 data
->m_dbh
->conn()->def_stmt_clsname
,
1369 data
->m_dbh
->def_stmt_ctor_args
);
1371 pdo_raise_impl_error
1372 (data
->m_dbh
, nullptr, "HY000",
1373 "failed to instantiate user supplied statement class");
1376 PDOStatementData
*pdostmt
= Native::data
<PDOStatementData
>(ret
);
1378 if (data
->m_dbh
->conn()->preparer(sql
, &pdostmt
->m_stmt
, Array())) {
1379 auto stmt
= pdostmt
->m_stmt
;
1382 /* unconditionally keep this for later reference */
1383 stmt
->query_string
= sql
;
1384 stmt
->default_fetch_type
= data
->m_dbh
->conn()->default_fetch_type
;
1385 stmt
->active_query_string
= stmt
->query_string
;
1386 stmt
->dbh
= data
->m_dbh
;
1387 stmt
->lazy_object_ref
.unset();
1389 setPDOErrorNone(stmt
->error_code
);
1391 // when we add support for varargs here, we only need to set the stmt if
1392 // the argument count is > 1
1393 int argc
= _argv
.size() + 1;
1394 Variant argv_variant
= _argv
;
1396 pdo_stmt_set_fetch_mode(
1399 tvCastToInt64(_argv
.lookup(0)),
1400 Variant::attach(HHVM_FN(array_splice
)(argv_variant
, 1)).toArray()
1402 /* now execute the statement */
1403 setPDOErrorNone(stmt
->error_code
);
1404 if (stmt
->executer()) {
1406 if (!stmt
->executed
) {
1407 if (stmt
->dbh
->conn()->alloc_own_columns
) {
1408 ok
= pdo_stmt_describe_columns(stmt
);
1413 pdo_stmt_construct(stmt
, ret
, data
->m_dbh
->conn()->def_stmt_clsname
,
1414 data
->m_dbh
->def_stmt_ctor_args
);
1419 /* something broke */
1420 data
->m_dbh
->query_stmt
= stmt
.get();
1421 PDO_HANDLE_STMT_ERR(stmt
);
1423 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1429 static Variant
HHVM_METHOD(PDO
, quote
, const String
& str
,
1430 int64_t paramtype
/* = PDO_PARAM_STR */) {
1431 auto data
= Native::data
<PDOData
>(this_
);
1433 assertx(data
->m_dbh
->conn()->driver
);
1434 setPDOErrorNone(data
->m_dbh
->conn()->error_code
);
1435 data
->m_dbh
->query_stmt
= nullptr;
1437 if (!data
->m_dbh
->conn()->support(PDOConnection::MethodQuoter
)) {
1438 pdo_raise_impl_error(data
->m_dbh
, nullptr, "IM001",
1439 "driver does not support quoting");
1444 if (data
->m_dbh
->conn()->quoter(str
, quoted
, (PDOParamType
)paramtype
)) {
1447 PDO_HANDLE_DBH_ERR(data
->m_dbh
);
1451 static bool HHVM_METHOD(PDO
, sqliteCreateFunction
, const String
& name
,
1452 const Variant
& callback
, int64_t argcount
/* = -1 */) {
1453 #ifdef ENABLE_EXTENSION_PDO_SQLITE
1454 auto data
= Native::data
<PDOData
>(this_
);
1456 auto res
= dynamic_cast<PDOSqliteResource
*>(data
->m_dbh
.get());
1457 if (res
== nullptr) {
1460 return res
->createFunction(name
, callback
, argcount
);
1462 raise_recoverable_error("PDO::sqliteCreateFunction not implemented");
1467 static bool HHVM_METHOD(PDO
, sqliteCreateAggregate
, const String
& /*name*/,
1468 const Variant
& /*step*/, const Variant
& /*final*/,
1469 int64_t /*argcount*/ /* = -1 */) {
1470 raise_recoverable_error("PDO::sqliteCreateAggregate not implemented");
1474 static Variant
HHVM_METHOD(PDO
, __wakeup
) {
1475 throw_pdo_exception(uninit_null(),
1476 "You cannot serialize or unserialize PDO instances");
1480 static Variant
HHVM_METHOD(PDO
, __sleep
) {
1481 throw_pdo_exception(uninit_null(),
1482 "You cannot serialize or unserialize PDO instances");
1486 static Array
HHVM_STATIC_METHOD(PDO
, getAvailableDrivers
) {
1487 return HHVM_FN(pdo_drivers
)();
1490 ///////////////////////////////////////////////////////////////////////////////
1492 static inline bool rewrite_name_to_position(sp_PDOStatement stmt
,
1493 sp_PDOBoundParam param
) {
1494 if (!stmt
->bound_param_map
.empty()) {
1495 /* rewriting :name to ? style.
1496 * We need to fixup the parameter numbers on the parameters.
1497 * If we find that a given named parameter has been used twice,
1498 * we will raise an error, as we can't be sure that it is safe
1499 * to bind multiple parameters onto the same zval in the underlying
1501 if (stmt
->named_rewrite_template
) {
1502 /* this is not an error here */
1505 if (param
->name
.empty()) {
1506 /* do the reverse; map the parameter number to the name */
1507 if (stmt
->bound_param_map
.exists(param
->paramno
)) {
1508 param
->name
= stmt
->bound_param_map
[param
->paramno
].toString();
1511 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY093",
1512 "parameter was not defined");
1517 for (ArrayIter
iter(stmt
->bound_param_map
); iter
; ++iter
, ++position
) {
1518 if (iter
.second().toString() == param
->name
) {
1519 if (param
->paramno
>= 0) {
1520 pdo_raise_impl_error
1521 (stmt
->dbh
, stmt
, "IM001",
1522 "PDO refuses to handle repeating the same :named parameter "
1523 "for multiple positions with this driver, as it might be "
1524 "unsafe to do so. Consider using a separate name for each "
1525 "parameter instead");
1528 param
->paramno
= position
;
1532 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY093",
1533 "parameter was not defined");
1539 /* trigger callback hook for parameters */
1540 static bool dispatch_param_event(sp_PDOStatement stmt
,
1541 PDOParamEvent event_type
) {
1542 if (!stmt
->support(PDOStatement::MethodParamHook
)) {
1545 for (ArrayIter
iter(stmt
->bound_params
); iter
; ++iter
) {
1546 auto param
= cast
<PDOBoundParam
>(iter
.second());
1547 if (!stmt
->paramHook(param
.get(), event_type
)) {
1551 for (ArrayIter
iter(stmt
->bound_columns
); iter
; ++iter
) {
1552 auto param
= cast
<PDOBoundParam
>(iter
.second());
1553 if (!stmt
->paramHook(param
.get(), event_type
)) {
1560 static void get_lazy_object(sp_PDOStatement stmt
, Variant
&ret
) {
1561 if (stmt
->lazy_object_ref
.isNull()) {
1562 stmt
->lazy_object_ref
= stmt
;
1564 ret
= stmt
->lazy_object_ref
;
1567 static bool really_register_bound_param(sp_PDOBoundParam param
,
1568 sp_PDOStatement stmt
) {
1569 Array
&hash
= stmt
->bound_params
;
1571 if (PDO_PARAM_TYPE(param
->param_type
) == PDO_PARAM_STR
&&
1572 !param
->parameter
.isNull()) {
1573 param
->parameter
= param
->parameter
.toString();
1574 } else if (PDO_PARAM_TYPE(param
->param_type
) == PDO_PARAM_INT
&&
1575 param
->parameter
.isBoolean()) {
1576 param
->parameter
= param
->parameter
.toInt64();
1577 } else if (PDO_PARAM_TYPE(param
->param_type
) == PDO_PARAM_BOOL
&&
1578 param
->parameter
.isInteger()) {
1579 param
->parameter
= param
->parameter
.toBoolean();
1581 param
->stmt
= stmt
.get();
1583 if (!param
->name
.empty() && param
->name
[0] != ':') {
1584 param
->name
= String(":") + param
->name
;
1587 if (!rewrite_name_to_position(stmt
, param
)) {
1588 param
->name
.reset();
1592 /* ask the driver to perform any normalization it needs on the
1593 * parameter name. Note that it is illegal for the driver to take
1594 * a reference to param, as it resides in transient storage only
1596 if (stmt
->support(PDOStatement::MethodParamHook
)) {
1597 if (!stmt
->paramHook(param
.get(), PDO_PARAM_EVT_NORMALIZE
)) {
1598 param
->name
.reset();
1603 /* delete any other parameter registered with this number.
1604 * If the parameter is named, it will be removed and correctly
1605 * disposed of by the hash_update call that follows */
1606 if (param
->paramno
>= 0) {
1607 hash
.remove(param
->paramno
);
1610 /* allocate storage for the parameter, keyed by its "canonical" name */
1611 if (!param
->name
.empty()) {
1612 hash
.set(param
->name
, Variant(param
));
1614 hash
.set(param
->paramno
, Variant(param
));
1617 /* tell the driver we just created a parameter */
1618 if (stmt
->support(PDOStatement::MethodParamHook
)) {
1619 if (!stmt
->paramHook(param
.get(), PDO_PARAM_EVT_ALLOC
)) {
1620 /* undo storage allocation; the hash will free the parameter
1621 * name if required */
1622 if (!param
->name
.empty()) {
1623 hash
.remove(param
->name
);
1625 hash
.remove(param
->paramno
);
1627 param
->parameter
.unset();
1634 static inline void fetch_value(sp_PDOStatement stmt
, Variant
&dest
, int colno
,
1635 int *type_override
) {
1636 if (colno
< 0 || colno
>= stmt
->column_count
) {
1639 auto col
= cast
<PDOColumn
>(stmt
->columns
[colno
]);
1640 int type
= PDO_PARAM_TYPE(col
->param_type
);
1641 int new_type
= type_override
? PDO_PARAM_TYPE(*type_override
) : type
;
1643 stmt
->getColumn(colno
, dest
);
1645 if (type
!= new_type
) {
1647 case PDO_PARAM_INT
: dest
= dest
.toInt64(); break;
1648 case PDO_PARAM_BOOL
: dest
= dest
.toBoolean(); break;
1649 case PDO_PARAM_STR
: dest
= dest
.toString(); break;
1650 case PDO_PARAM_NULL
: dest
= init_null(); break;
1653 if (stmt
->dbh
->conn()->stringify
&& (dest
.isInteger() || dest
.isDouble())) {
1654 dest
= dest
.toString();
1656 if (dest
.isNull() && stmt
->dbh
->conn()->oracle_nulls
== PDO_NULL_TO_STRING
) {
1657 dest
= empty_string_variant();
1661 static bool do_fetch_common(sp_PDOStatement stmt
, PDOFetchOrientation ori
,
1663 if (!stmt
->executed
) {
1666 if (!dispatch_param_event(stmt
, PDO_PARAM_EVT_FETCH_PRE
)) {
1669 if (!stmt
->fetcher(ori
, offset
)) {
1672 /* some drivers might need to describe the columns now */
1673 if (stmt
->columns
.empty() && !pdo_stmt_describe_columns(stmt
)) {
1676 if (!dispatch_param_event(stmt
, PDO_PARAM_EVT_FETCH_POST
)) {
1680 if (!stmt
->bound_columns
.empty()) {
1681 /* update those bound column variables now */
1682 for (ArrayIter
iter(stmt
->bound_columns
); iter
; ++iter
) {
1683 auto param
= cast
<PDOBoundParam
>(iter
.second());
1684 if (param
->paramno
>= 0) {
1685 param
->parameter
.setNull();
1687 fetch_value(stmt
, param
->parameter
, param
->paramno
,
1688 (int *)¶m
->param_type
);
1689 /* TODO: some smart thing that avoids duplicating the value in the
1690 * general loop below. For now, if you're binding output columns,
1691 * it's better to use LAZY or BOUND fetches if you want to shave
1692 * off those cycles */
1700 static bool do_fetch_func_prepare(sp_PDOStatement stmt
) {
1701 if (!is_callable(stmt
->fetch
.func
)) {
1702 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
1703 "user-supplied function must be a valid callback");
1709 /* perform a fetch. If do_bind is true, update any bound columns.
1710 * If return_value is not null, store values into it according to HOW. */
1711 static bool do_fetch(sp_PDOStatement stmt
,
1714 PDOFetchOrientation ori
,
1716 Variant
*return_all
) {
1717 if (how
== PDO_FETCH_USE_DEFAULT
) {
1718 how
= stmt
->default_fetch_type
;
1720 int flags
= how
& PDO_FETCH_FLAGS
;
1721 how
= (PDOFetchType
)(how
& ~PDO_FETCH_FLAGS
);
1723 if (!do_fetch_common(stmt
, ori
, offset
)) {
1727 if (how
== PDO_FETCH_BOUND
) {
1733 if ((flags
& PDO_FETCH_GROUP
) && stmt
->fetch
.column
== -1) {
1736 colno
= stmt
->fetch
.column
;
1739 if (how
== PDO_FETCH_LAZY
) {
1740 get_lazy_object(stmt
, ret
);
1744 String clsname
, old_clsname
;
1745 Variant old_ctor_args
;
1749 case PDO_FETCH_USE_DEFAULT
:
1750 case PDO_FETCH_ASSOC
:
1751 case PDO_FETCH_BOTH
:
1753 case PDO_FETCH_NAMED
:
1754 ret
= Array::CreateDict();
1757 case PDO_FETCH_KEY_PAIR
:
1758 if (stmt
->column_count
!= 2) {
1759 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
1760 "PDO::FETCH_KEY_PAIR fetch mode requires the "
1761 "result set to contain extactly 2 columns.");
1765 ret
= Array::CreateDict();
1769 case PDO_FETCH_COLUMN
:
1770 if (colno
>= 0 && colno
< stmt
->column_count
) {
1771 if (flags
== PDO_FETCH_GROUP
&& stmt
->fetch
.column
== -1) {
1772 fetch_value(stmt
, ret
, 1, NULL
);
1773 } else if (flags
== PDO_FETCH_GROUP
&& colno
) {
1774 fetch_value(stmt
, ret
, 0, NULL
);
1776 fetch_value(stmt
, ret
, colno
, NULL
);
1783 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000", "Invalid column index");
1788 ret
= SystemLib::AllocStdClassObject();
1791 case PDO_FETCH_CLASS
:
1792 if (flags
& PDO_FETCH_CLASSTYPE
) {
1793 old_clsname
= stmt
->fetch
.clsname
;
1794 old_ctor_args
= stmt
->fetch
.ctor_args
;
1797 fetch_value(stmt
, val
, i
++, NULL
);
1798 if (!val
.isNull()) {
1799 if (!HHVM_FN(class_exists
)(val
.toString())) {
1800 stmt
->fetch
.clsname
= "stdClass";
1802 stmt
->fetch
.clsname
= val
.toString();
1806 do_fetch_class_prepare(stmt
);
1808 clsname
= stmt
->fetch
.clsname
;
1809 if (clsname
.empty()) {
1810 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
1811 "No fetch class specified");
1814 if ((flags
& PDO_FETCH_SERIALIZE
) == 0) {
1815 ret
= create_object_only(clsname
);
1816 if (!do_fetch_class_prepare(stmt
)) {
1819 if (!stmt
->fetch
.constructor
.empty() &&
1820 (flags
& PDO_FETCH_PROPS_LATE
)) {
1821 ret
.asCObjRef()->o_invoke(stmt
->fetch
.constructor
,
1822 stmt
->fetch
.ctor_args
.toArray());
1827 case PDO_FETCH_INTO
:
1828 if (stmt
->fetch
.into
.isNull()) {
1829 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
1830 "No fetch-into object specified.");
1834 ret
= stmt
->fetch
.into
;
1835 if (ret
.isObject() &&
1836 ret
.getObjectData()->instanceof(SystemLib::getstdClassClass())) {
1837 how
= PDO_FETCH_OBJ
;
1841 case PDO_FETCH_FUNC
:
1842 if (stmt
->fetch
.func
.empty()) {
1843 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
1844 "No fetch function specified");
1847 if (!do_fetch_func_prepare(stmt
)) {
1858 if (return_all
&& how
!= PDO_FETCH_KEY_PAIR
) {
1859 if (flags
== PDO_FETCH_GROUP
&& how
== PDO_FETCH_COLUMN
&&
1860 stmt
->fetch
.column
> 0) {
1861 fetch_value(stmt
, grp_val
, colno
, NULL
);
1863 fetch_value(stmt
, grp_val
, i
, NULL
);
1865 grp_val
= grp_val
.toString();
1866 if (how
== PDO_FETCH_COLUMN
) {
1867 i
= stmt
->column_count
; /* no more data to fetch */
1873 for (int idx
= 0; i
< stmt
->column_count
; i
++, idx
++) {
1874 const String
& name
= cast
<PDOColumn
>(stmt
->columns
[i
])->name
;
1876 fetch_value(stmt
, val
, i
, NULL
);
1879 case PDO_FETCH_ASSOC
: {
1880 auto const name_key
=
1881 ret
.asArrRef().convertKey
<IntishCast::Cast
>(name
);
1882 ret
.asArrRef().set(name_key
, *val
.asTypedValue());
1885 case PDO_FETCH_KEY_PAIR
: {
1887 fetch_value(stmt
, tmp
, ++i
, NULL
);
1889 auto const val_key_ret
=
1890 return_all
->asArrRef().convertKey
<IntishCast::Cast
>(val
);
1891 return_all
->asArrRef().set(val_key_ret
, *tmp
.asTypedValue());
1893 auto const val_key
=
1894 ret
.asArrRef().convertKey
<IntishCast::Cast
>(val
);
1895 ret
.asArrRef().set(val_key
, *tmp
.asTypedValue());
1899 case PDO_FETCH_USE_DEFAULT
:
1900 case PDO_FETCH_BOTH
: {
1901 auto const name_key
=
1902 ret
.asArrRef().convertKey
<IntishCast::Cast
>(name
);
1903 ret
.asArrRef().set(name_key
, *val
.asTypedValue());
1904 ret
.asArrRef().append(val
);
1908 case PDO_FETCH_NAMED
: {
1909 auto const name_key
=
1910 ret
.asArrRef().convertKey
<IntishCast::Cast
>(name
);
1911 /* already have an item with this name? */
1913 if (ret
.asArrRef().exists(name_key
)) {
1914 auto const curr_val
= ret
.asArrRef().lval(name_key
);
1915 if (!isArrayLikeType(curr_val
.type())) {
1916 Array arr
= Array::CreateVec();
1917 arr
.append(curr_val
.tv());
1919 ret
.toArray().set(name_key
, make_array_like_tv(arr
.get()));
1921 asArrRef(curr_val
).append(val
);
1924 ret
.asArrRef().set(name_key
, *val
.asTypedValue());
1929 ret
.asArrRef().append(val
);
1933 case PDO_FETCH_INTO
:
1934 ret
.toObject()->o_set(name
, val
);
1937 case PDO_FETCH_CLASS
:
1938 if ((flags
& PDO_FETCH_SERIALIZE
) == 0 || idx
) {
1939 ret
.toObject()->o_set(name
, val
);
1942 ret
= unserialize_from_string(val
);
1943 if (same(ret
, false)) {
1944 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
1945 "cannot unserialize data");
1949 // hzhao: not sure how we support class serialization
1950 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY000",
1951 "cannot unserialize class");
1956 case PDO_FETCH_FUNC
:
1957 forceToDict(stmt
->fetch
.values
).set(idx
, val
);
1961 pdo_raise_impl_error(stmt
->dbh
, stmt
, "22003", "mode is out of range");
1967 case PDO_FETCH_CLASS
:
1968 if (!stmt
->fetch
.constructor
.empty() &&
1969 !(flags
& (PDO_FETCH_PROPS_LATE
| PDO_FETCH_SERIALIZE
))) {
1970 ret
.toObject()->o_invoke(stmt
->fetch
.constructor
,
1971 stmt
->fetch
.ctor_args
.toArray());
1973 if (flags
& PDO_FETCH_CLASSTYPE
) {
1974 stmt
->fetch
.clsname
= old_clsname
;
1975 stmt
->fetch
.ctor_args
= old_ctor_args
;
1979 case PDO_FETCH_FUNC
:
1980 ret
= vm_call_user_func(stmt
->fetch
.func
,
1981 stmt
->fetch
.values
.toArray());
1989 auto const grp_key
=
1990 return_all
->asArrRef().convertKey
<IntishCast::Cast
>(grp_val
);
1991 if ((flags
& PDO_FETCH_UNIQUE
) == PDO_FETCH_UNIQUE
) {
1992 return_all
->asArrRef().set(grp_key
, *ret
.asTypedValue());
1994 auto const lval
= return_all
->asArrRef().lval(grp_key
);
1995 forceToArray(lval
).append(ret
);
2003 bool HHVM_METHOD(PDOStatement
, bindValue
, const Variant
& paramno
,
2004 const Variant
& param
,
2005 int64_t type
/* = PDO_PARAM_STR */) {
2006 auto data
= Native::data
<PDOStatementData
>(this_
);
2007 if (data
->m_stmt
== nullptr) {
2010 auto& stmt
= data
->m_stmt
;
2012 auto p
= req::make
<PDOBoundParam
>();
2013 // need to make sure this is NULL, in case a fatal errors occurs before it's
2014 // set inside really_register_bound_param
2017 if (paramno
.isNumeric()) {
2018 p
->paramno
= paramno
.toInt64();
2021 p
->name
= paramno
.toString();
2024 p
->parameter
= param
;
2025 p
->param_type
= (PDOParamType
)type
;
2027 if (p
->paramno
> 0) {
2028 --p
->paramno
; /* make it zero-based internally */
2029 } else if (p
->name
.empty()) {
2030 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY093",
2031 "Columns/Parameters are 1-based");
2035 if (!really_register_bound_param(p
, stmt
)) {
2036 p
->parameter
.unset();
2042 static bool generic_stmt_attr_get(sp_PDOStatement stmt
, Variant
&ret
,
2044 if (attr
== PDO_ATTR_EMULATE_PREPARES
) {
2045 ret
= (bool)(stmt
->supports_placeholders
== PDO_PLACEHOLDER_NONE
);
2051 ///////////////////////////////////////////////////////////////////////////////
2056 #define PDO_PARSER_TEXT 1
2057 #define PDO_PARSER_BIND 2
2058 #define PDO_PARSER_BIND_POS 3
2059 #define PDO_PARSER_EOI 4
2061 #define RET(i) {s->cur = cursor; return i; }
2062 #define SKIP_ONE(i) {s->cur = s->tok + 1; return 1; }
2064 #define YYCTYPE unsigned char
2065 #define YYCURSOR cursor
2066 #define YYLIMIT limit
2067 #define YYMARKER s->ptr
2068 #define YYFILL(n) RET(PDO_PARSER_EOI)
2070 typedef struct Scanner
{
2071 char *ptr
, *cur
, *lim
, *tok
;
2074 static int scan(Scanner
*s
) {
2075 char* cursor
= s
->cur
;
2076 char* limit
= s
->lim
;
2082 if ((YYLIMIT
- YYCURSOR
) < 2) { YYFILL(2); }
2085 case 0x00: goto yy11
;
2087 case '\'': goto yy4
;
2093 yych
= *(YYMARKER
= ++YYCURSOR
);
2094 if (yych
>= 0x01) goto yy26
;
2096 { SKIP_ONE(PDO_PARSER_TEXT
); }
2098 yych
= *(YYMARKER
= ++YYCURSOR
);
2099 if (yych
<= 0x00) goto yy3
;
2166 case 'z': goto yy16
;
2168 case '?': goto yy13
;
2173 switch ((yych
= *YYCURSOR
)) {
2175 case '?': goto yy13
;
2179 { RET(PDO_PARSER_BIND_POS
); }
2182 if (YYLIMIT
<= YYCURSOR
) { YYFILL(1); }
2189 case '?': goto yy10
;
2193 { RET(PDO_PARSER_TEXT
); }
2196 { RET(PDO_PARSER_EOI
); }
2199 if (YYLIMIT
<= YYCURSOR
) { YYFILL(1); }
2203 case '?': goto yy13
;
2207 { RET(PDO_PARSER_TEXT
); }
2210 if (YYLIMIT
<= YYCURSOR
) { YYFILL(1); }
2275 case 'z': goto yy16
;
2279 { RET(PDO_PARSER_BIND
); }
2282 if (YYLIMIT
<= YYCURSOR
) { YYFILL(1); }
2286 case 0x00: goto yy21
;
2287 case '\'': goto yy23
;
2288 case '\\': goto yy22
;
2292 YYCURSOR
= YYMARKER
;
2296 if (YYLIMIT
<= YYCURSOR
) { YYFILL(1); }
2298 if (yych
<= 0x00) goto yy21
;
2302 { RET(PDO_PARSER_TEXT
); }
2305 if (YYLIMIT
<= YYCURSOR
) { YYFILL(1); }
2309 case 0x00: goto yy21
;
2310 case '"': goto yy28
;
2311 case '\\': goto yy27
;
2316 if (YYLIMIT
<= YYCURSOR
) { YYFILL(1); }
2318 if (yych
<= 0x00) goto yy21
;
2322 { RET(PDO_PARSER_TEXT
); }
2329 struct placeholder
{
2333 String quoted
; /* quoted value */
2334 struct placeholder
*next
;
2337 int pdo_parse_params(sp_PDOStatement stmt
, const String
& in
, String
&out
) {
2346 req::ptr
<PDOBoundParam
> param
;
2347 int query_type
= PDO_PLACEHOLDER_NONE
;
2348 struct placeholder
*placeholders
= NULL
, *placetail
= NULL
, *plc
= NULL
;
2350 s
.cur
= (char*)in
.data();
2351 s
.lim
= (char*)in
.data() + in
.size() + 1;
2353 /* phase 1: look for args */
2354 while ((t
= scan(&s
)) != PDO_PARSER_EOI
) {
2355 if (t
== PDO_PARSER_BIND
|| t
== PDO_PARSER_BIND_POS
) {
2356 if (t
== PDO_PARSER_BIND
) {
2357 int len
= s
.cur
- s
.tok
;
2358 if ((in
.data() < (s
.cur
- len
)) && isalnum(*(s
.cur
- len
- 1))) {
2361 query_type
|= PDO_PLACEHOLDER_NAMED
;
2363 query_type
|= PDO_PLACEHOLDER_POSITIONAL
;
2366 plc
= req::make_raw
<placeholder
>();
2367 memset(plc
, 0, sizeof(*plc
));
2370 plc
->len
= s
.cur
- s
.tok
;
2371 plc
->bindno
= bindno
++;
2374 placetail
->next
= plc
;
2383 /* nothing to do; good! */
2387 /* did the query make sense to me? */
2388 if (query_type
== (PDO_PLACEHOLDER_NAMED
|PDO_PLACEHOLDER_POSITIONAL
)) {
2389 /* they mixed both types; punt */
2390 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY093",
2391 "mixed named and positional parameters");
2396 if ((int)stmt
->supports_placeholders
== query_type
&&
2397 !stmt
->named_rewrite_template
) {
2398 /* query matches native syntax */
2403 if (stmt
->named_rewrite_template
) {
2405 * We we pretend that the query was positional even if
2406 * it was named so that we fall into the
2407 * named rewrite case below. Not too pretty,
2409 query_type
= PDO_PLACEHOLDER_POSITIONAL
;
2412 params
= stmt
->bound_params
;
2414 /* Do we have placeholders but no bound params */
2415 if (bindno
&& params
.empty() &&
2416 stmt
->supports_placeholders
== PDO_PLACEHOLDER_NONE
) {
2417 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY093", "no parameters were bound");
2422 if (!params
.empty() && bindno
!= params
.size() &&
2423 stmt
->supports_placeholders
== PDO_PLACEHOLDER_NONE
) {
2424 /* extra bit of validation for instances when same params are bound
2426 if (query_type
!= PDO_PLACEHOLDER_POSITIONAL
&& bindno
> params
.size()) {
2428 for (plc
= placeholders
; plc
; plc
= plc
->next
) {
2429 if (!params
.exists(String(plc
->pos
, plc
->len
, CopyString
))) {
2438 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY093",
2439 "number of bound variables does not match number "
2445 /* what are we going to do ? */
2446 if (stmt
->supports_placeholders
== PDO_PLACEHOLDER_NONE
) {
2447 /* query generation */
2449 newbuffer_len
= in
.size();
2451 /* let's quote all the values */
2452 for (plc
= placeholders
; plc
; plc
= plc
->next
) {
2454 if (query_type
== PDO_PLACEHOLDER_POSITIONAL
) {
2455 vparam
= params
[plc
->bindno
];
2457 String
str(plc
->pos
, plc
->len
, CopyString
);
2458 auto const arrkey
= params
.convertKey
<IntishCast::Cast
>(str
);
2459 vparam
= params
[arrkey
];
2461 if (vparam
.isNull()) {
2462 /* parameter was not defined */
2464 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY093",
2465 "parameter was not defined");
2468 param
= cast
<PDOBoundParam
>(vparam
);
2469 if (stmt
->dbh
->conn()->support(PDOConnection::MethodQuoter
)) {
2470 if (param
->param_type
== PDO_PARAM_LOB
&&
2471 param
->parameter
.isResource()) {
2472 Variant buf
= HHVM_FN(stream_get_contents
)(
2473 param
->parameter
.toResource());
2474 if (!same(buf
, false)) {
2475 if (!stmt
->dbh
->conn()->quoter(buf
.toString(), plc
->quoted
,
2476 param
->param_type
)) {
2479 setPDOError(stmt
->error_code
, stmt
->dbh
->conn()->error_code
);
2483 pdo_raise_impl_error(stmt
->dbh
, stmt
, "HY105",
2484 "Expected a stream resource");
2490 switch (param
->parameter
.getType()) {
2493 plc
->quoted
= "NULL";
2498 plc
->quoted
= param
->parameter
.toString();
2502 param
->parameter
= param
->parameter
.toInt64();
2504 case KindOfPersistentString
:
2506 case KindOfPersistentVec
:
2508 case KindOfPersistentDict
:
2510 case KindOfPersistentKeyset
:
2513 case KindOfResource
:
2517 case KindOfLazyClass
:
2519 case KindOfRClsMeth
:
2520 case KindOfEnumClassLabel
:
2521 if (!stmt
->dbh
->conn()->quoter(
2522 param
->parameter
.toString(),
2524 param
->param_type
)) {
2527 setPDOError(stmt
->error_code
, stmt
->dbh
->conn()->error_code
);
2536 plc
->quoted
= param
->parameter
.toString();
2538 newbuffer_len
+= plc
->quoted
.size();
2542 /* allocate output buffer */
2543 out
= String(newbuffer_len
, ReserveString
);
2544 newbuffer
= out
.mutableData();
2546 /* and build the query */
2553 memcpy(newbuffer
, ptr
, t
);
2556 memcpy(newbuffer
, plc
->quoted
.data(), plc
->quoted
.size());
2557 newbuffer
+= plc
->quoted
.size();
2558 ptr
= plc
->pos
+ plc
->len
;
2563 t
= (in
.data() + in
.size()) - ptr
;
2565 memcpy(newbuffer
, ptr
, t
);
2568 out
.setSize(newbuffer
- out
.data());
2573 } else if (query_type
== PDO_PLACEHOLDER_POSITIONAL
) {
2574 /* rewrite ? to :pdoX */
2575 StringBuffer idxbuf
;
2576 const char *tmpl
= stmt
->named_rewrite_template
?
2577 stmt
->named_rewrite_template
: ":pdo%d";
2580 newbuffer_len
= in
.size();
2582 for (plc
= placeholders
; plc
; plc
= plc
->next
) {
2584 String
name(plc
->pos
, plc
->len
, CopyString
);
2585 auto const name_key
=
2586 stmt
->bound_param_map
.convertKey
<IntishCast::Cast
>(name
);
2588 /* check if bound parameter is already available */
2589 if (!strcmp(name
.c_str(), "?") ||
2590 !stmt
->bound_param_map
.exists(name_key
)) {
2591 idxbuf
.printf(tmpl
, bind_no
++);
2594 idxbuf
.append(stmt
->bound_param_map
[name_key
].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_key
,
2604 make_tv
<KindOfString
>(plc
->quoted
.get()));
2607 /* map number to name */
2608 stmt
->bound_param_map
.set(plc
->bindno
, plc
->quoted
);
2614 /* rewrite :name to ? */
2616 newbuffer_len
= in
.size();
2618 for (plc
= placeholders
; plc
; plc
= plc
->next
) {
2619 String
name(plc
->pos
, plc
->len
, CopyString
);
2620 stmt
->bound_param_map
.set(plc
->bindno
, name
);
2629 while (placeholders
) {
2631 placeholders
= plc
->next
;
2632 plc
->quoted
.reset();
2639 ///////////////////////////////////////////////////////////////////////////////
2642 const StaticString
s_PDOStatement("PDOStatement");
2644 PDOStatementData::PDOStatementData() : m_rowIndex(-1) {
2647 PDOStatementData::~PDOStatementData() { }
2649 Variant
HHVM_METHOD(PDOStatement
, execute
,
2650 const Variant
& paramsV
/* = null_array */) {
2651 auto data
= Native::data
<PDOStatementData
>(this_
);
2652 auto params
= paramsV
.isNull() ? null_array
: paramsV
.toArray();
2654 SYNC_VM_REGS_SCOPED();
2656 if (data
->m_stmt
== nullptr) {
2657 return init_null_variant
;
2660 setPDOErrorNone(data
->m_stmt
->error_code
);
2662 if (!params
.empty()) {
2663 data
->m_stmt
->bound_params
.reset();
2664 for (ArrayIter
iter(params
); iter
; ++iter
) {
2665 auto param
= req::make
<PDOBoundParam
>();
2666 param
->param_type
= PDO_PARAM_STR
;
2667 param
->parameter
= iter
.second();
2670 if (iter
.first().isString()) {
2671 param
->name
= iter
.first().toString();
2672 param
->paramno
= -1;
2674 int64_t num_index
= iter
.first().toInt64();
2675 /* we're okay to be zero based here */
2676 if (num_index
< 0) {
2677 pdo_raise_impl_error(data
->m_stmt
->dbh
, data
->m_stmt
,
2681 param
->paramno
= num_index
;
2684 if (!really_register_bound_param(param
, data
->m_stmt
)) {
2691 if (PDO_PLACEHOLDER_NONE
== data
->m_stmt
->supports_placeholders
) {
2692 /* handle the emulated parameter binding, m_stmt->active_query_string
2693 holds the query with binds expanded and quoted. */
2694 ret
= pdo_parse_params(data
->m_stmt
, data
->m_stmt
->query_string
,
2695 data
->m_stmt
->active_query_string
);
2696 if (ret
== 0) { /* no changes were made */
2697 data
->m_stmt
->active_query_string
= data
->m_stmt
->query_string
;
2699 } else if (ret
== -1) {
2700 /* something broke */
2701 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
2704 } else if (!dispatch_param_event(data
->m_stmt
, PDO_PARAM_EVT_EXEC_PRE
)) {
2705 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
2708 if (data
->m_stmt
->executer()) {
2709 data
->m_stmt
->active_query_string
.reset();
2710 if (!data
->m_stmt
->executed
) {
2711 /* this is the first execute */
2713 if (data
->m_stmt
->dbh
->conn()->alloc_own_columns
2714 && data
->m_stmt
->columns
.empty()) {
2715 /* for "big boy" drivers, we need to allocate memory to fetch
2716 * the results into, so lets do that now */
2717 ret
= pdo_stmt_describe_columns(data
->m_stmt
);
2720 data
->m_stmt
->executed
= 1;
2723 if (ret
&& !dispatch_param_event(data
->m_stmt
, PDO_PARAM_EVT_EXEC_POST
)) {
2729 data
->m_stmt
->active_query_string
.reset();
2730 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
2734 static Variant
HHVM_METHOD(PDOStatement
, fetch
, int64_t how
= 0,
2735 int64_t orientation
= PDO_FETCH_ORI_NEXT
,
2736 int64_t offset
= 0) {
2737 auto data
= Native::data
<PDOStatementData
>(this_
);
2739 SYNC_VM_REGS_SCOPED();
2741 if (data
->m_stmt
== nullptr) {
2745 setPDOErrorNone(data
->m_stmt
->error_code
);
2746 if (!pdo_stmt_verify_mode(data
->m_stmt
, how
, false)) {
2751 if (!do_fetch(data
->m_stmt
, ret
, (PDOFetchType
)how
,
2752 (PDOFetchOrientation
)orientation
, offset
, NULL
)) {
2753 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
2759 static Variant
HHVM_METHOD(PDOStatement
, fetchObject
,
2760 const String
& class_name
/* = null_string */,
2761 const Variant
& ctor_args
/* = null */) {
2762 auto data
= Native::data
<PDOStatementData
>(this_
);
2763 if (data
->m_stmt
== nullptr) {
2767 setPDOErrorNone(data
->m_stmt
->error_code
);
2768 if (!pdo_stmt_verify_mode(data
->m_stmt
, PDO_FETCH_CLASS
, false)) {
2772 String old_clsname
= data
->m_stmt
->fetch
.clsname
;
2773 Variant old_ctor_args
= data
->m_stmt
->fetch
.ctor_args
;
2776 data
->m_stmt
->fetch
.clsname
= class_name
;
2777 if (class_name
.empty()) {
2778 data
->m_stmt
->fetch
.clsname
= "stdClass";
2780 if (!HHVM_FN(class_exists
)(data
->m_stmt
->fetch
.clsname
)) {
2781 pdo_raise_impl_error(data
->m_stmt
->dbh
, data
->m_stmt
, "HY000",
2782 "Could not find user-supplied class");
2785 if (!ctor_args
.isNull() && !ctor_args
.isArray()) {
2786 pdo_raise_impl_error(data
->m_stmt
->dbh
, data
->m_stmt
, "HY000",
2787 "ctor_args must be either NULL or an array");
2790 data
->m_stmt
->fetch
.ctor_args
= ctor_args
;
2793 if (!error
&& !do_fetch(data
->m_stmt
, ret
, PDO_FETCH_CLASS
,
2794 PDO_FETCH_ORI_NEXT
, 0, NULL
)) {
2798 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
2801 data
->m_stmt
->fetch
.clsname
= old_clsname
;
2802 data
->m_stmt
->fetch
.ctor_args
= old_ctor_args
;
2809 static Variant
HHVM_METHOD(PDOStatement
, fetchColumn
,
2810 int64_t column_numner
/* = 0 */) {
2811 auto data
= Native::data
<PDOStatementData
>(this_
);
2812 if (data
->m_stmt
== nullptr) {
2816 setPDOErrorNone(data
->m_stmt
->error_code
);
2817 if (!do_fetch_common(data
->m_stmt
, PDO_FETCH_ORI_NEXT
, 0)) {
2818 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
2822 fetch_value(data
->m_stmt
, ret
, column_numner
, nullptr);
2826 Variant
HHVM_METHOD(PDOStatement
, fetchAll
, int64_t how
/* = 0 */,
2827 const Variant
& class_name
/* = null */,
2828 const Variant
& ctor_args
/* = null */) {
2829 auto self
= Native::data
<PDOStatementData
>(this_
);
2830 if (self
->m_stmt
== nullptr) {
2834 if (!pdo_stmt_verify_mode(self
->m_stmt
, how
, true)) {
2838 String old_clsname
= self
->m_stmt
->fetch
.clsname
;
2839 Variant old_ctor_args
= self
->m_stmt
->fetch
.ctor_args
;
2842 switch (how
& ~PDO_FETCH_FLAGS
) {
2843 case PDO_FETCH_CLASS
:
2844 self
->m_stmt
->fetch
.clsname
= class_name
.toString();
2845 if (class_name
.isNull()) {
2846 self
->m_stmt
->fetch
.clsname
= "stdClass";
2848 if (!HHVM_FN(class_exists
)(self
->m_stmt
->fetch
.clsname
)) {
2849 pdo_raise_impl_error(self
->m_stmt
->dbh
, self
->m_stmt
, "HY000",
2850 "Could not find user-supplied class");
2853 if (!ctor_args
.isNull() && !ctor_args
.isArray()) {
2854 pdo_raise_impl_error(self
->m_stmt
->dbh
, self
->m_stmt
, "HY000",
2855 "ctor_args must be either NULL or an array");
2859 self
->m_stmt
->fetch
.ctor_args
= ctor_args
;
2862 do_fetch_class_prepare(self
->m_stmt
);
2866 case PDO_FETCH_FUNC
:
2867 if (!HHVM_FN(function_exists
)(class_name
.toString())) {
2868 pdo_raise_impl_error(self
->m_stmt
->dbh
, self
->m_stmt
, "HY000",
2869 "no fetch function specified");
2872 self
->m_stmt
->fetch
.func
= class_name
.toString();
2873 do_fetch_func_prepare(self
->m_stmt
);
2877 case PDO_FETCH_COLUMN
:
2878 if (class_name
.isNull()) {
2879 self
->m_stmt
->fetch
.column
= how
& PDO_FETCH_GROUP
? -1 : 0;
2881 self
->m_stmt
->fetch
.column
= class_name
.toInt64();
2883 if (!ctor_args
.isNull()) {
2884 pdo_raise_impl_error(self
->m_stmt
->dbh
, self
->m_stmt
, "HY000",
2885 "Third parameter not allowed for "
2886 "PDO::FETCH_COLUMN");
2892 int flags
= how
& PDO_FETCH_FLAGS
;
2894 if ((how
& ~PDO_FETCH_FLAGS
) == PDO_FETCH_USE_DEFAULT
) {
2895 flags
|= self
->m_stmt
->default_fetch_type
& PDO_FETCH_FLAGS
;
2896 how
|= self
->m_stmt
->default_fetch_type
& ~PDO_FETCH_FLAGS
;
2899 Variant
*return_all
= NULL
;
2900 Variant return_value
;
2903 setPDOErrorNone(self
->m_stmt
->error_code
);
2905 if ((how
& PDO_FETCH_GROUP
) || how
== PDO_FETCH_KEY_PAIR
||
2906 (how
== PDO_FETCH_USE_DEFAULT
&&
2907 self
->m_stmt
->default_fetch_type
== PDO_FETCH_KEY_PAIR
)) {
2908 return_value
= Array::CreateDict();
2909 return_all
= &return_value
;
2911 if (!do_fetch(self
->m_stmt
, data
, (PDOFetchType
)(how
| flags
),
2912 PDO_FETCH_ORI_NEXT
, 0, return_all
)) {
2917 if ((how
& PDO_FETCH_GROUP
)) {
2920 } while (do_fetch(self
->m_stmt
, data
, (PDOFetchType
)(how
| flags
),
2921 PDO_FETCH_ORI_NEXT
, 0, return_all
));
2922 } else if (how
== PDO_FETCH_KEY_PAIR
||
2923 (how
== PDO_FETCH_USE_DEFAULT
&&
2924 self
->m_stmt
->default_fetch_type
== PDO_FETCH_KEY_PAIR
)) {
2925 while (do_fetch(self
->m_stmt
, data
, (PDOFetchType
)(how
| flags
),
2926 PDO_FETCH_ORI_NEXT
, 0, return_all
)) {
2930 return_value
= Array::CreateVec();
2932 return_value
.asArrRef().append(data
);
2934 } while (do_fetch(self
->m_stmt
, data
, (PDOFetchType
)(how
| flags
),
2935 PDO_FETCH_ORI_NEXT
, 0, NULL
));
2939 self
->m_stmt
->fetch
.clsname
= old_clsname
;
2940 self
->m_stmt
->fetch
.ctor_args
= old_ctor_args
;
2943 PDO_HANDLE_STMT_ERR(self
->m_stmt
);
2948 /* on no results, return an empty array */
2949 if (!return_value
.isArray()) {
2950 return_value
= Array::CreateDict();
2953 return return_value
;
2956 static int64_t HHVM_METHOD(PDOStatement
, rowCount
) {
2957 auto data
= Native::data
<PDOStatementData
>(this_
);
2958 if (data
->m_stmt
== nullptr) {
2962 return data
->m_stmt
->row_count
;
2965 static Variant
HHVM_METHOD(PDOStatement
, errorCode
) {
2966 auto data
= Native::data
<PDOStatementData
>(this_
);
2967 if (data
->m_stmt
== nullptr) {
2970 if (data
->m_stmt
->error_code
[0] == '\0') {
2973 return String(data
->m_stmt
->error_code
, CopyString
);
2976 static Array
HHVM_METHOD(PDOStatement
, errorInfo
) {
2977 auto data
= Native::data
<PDOStatementData
>(this_
);
2978 if (data
->m_stmt
== nullptr) {
2982 Array ret
= Array::CreateVec();
2983 ret
.append(String(data
->m_stmt
->error_code
, CopyString
));
2985 if (data
->m_stmt
->dbh
->conn()->support(PDOConnection::MethodFetchErr
)) {
2986 data
->m_stmt
->dbh
->conn()->fetchErr(data
->m_stmt
.get(), ret
);
2989 int error_count
= ret
.size();
2990 int error_expected_count
= 3;
2991 if (error_expected_count
> error_count
) {
2992 int error_count_diff
= error_expected_count
- error_count
;
2993 for (int i
= 0; i
< error_count_diff
; i
++) {
2994 ret
.append(uninit_null());
3000 static Variant
HHVM_METHOD(PDOStatement
, setAttribute
, int64_t attribute
,
3001 const Variant
& value
) {
3002 auto data
= Native::data
<PDOStatementData
>(this_
);
3003 if (data
->m_stmt
== nullptr) {
3007 if (!data
->m_stmt
->support(PDOStatement::MethodSetAttribute
)) {
3008 pdo_raise_impl_error(data
->m_stmt
->dbh
, data
->m_stmt
, "IM001",
3009 "This driver doesn't support setting attributes");
3013 setPDOErrorNone(data
->m_stmt
->error_code
);
3014 if (data
->m_stmt
->setAttribute(attribute
, value
)) {
3017 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
3021 static Variant
HHVM_METHOD(PDOStatement
, getAttribute
, int64_t attribute
) {
3022 auto data
= Native::data
<PDOStatementData
>(this_
);
3023 if (data
->m_stmt
== nullptr) {
3028 if (!data
->m_stmt
->support(PDOStatement::MethodGetAttribute
)) {
3029 if (!generic_stmt_attr_get(data
->m_stmt
, ret
, attribute
)) {
3030 pdo_raise_impl_error(data
->m_stmt
->dbh
, data
->m_stmt
, "IM001",
3031 "This driver doesn't support getting attributes");
3037 setPDOErrorNone(data
->m_stmt
->error_code
);
3038 switch (data
->m_stmt
->getAttribute(attribute
, ret
)) {
3040 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
3043 if (!generic_stmt_attr_get(data
->m_stmt
, ret
, attribute
)) {
3044 /* XXX: should do something better here */
3045 pdo_raise_impl_error(data
->m_stmt
->dbh
, data
->m_stmt
, "IM001",
3046 "driver doesn't support getting that attribute");
3056 static int64_t HHVM_METHOD(PDOStatement
, columnCount
) {
3057 auto data
= Native::data
<PDOStatementData
>(this_
);
3058 if (data
->m_stmt
== nullptr) {
3062 return data
->m_stmt
->column_count
;
3068 s_precision("precision"),
3069 s_pdo_type("pdo_type");
3071 static Variant
HHVM_METHOD(PDOStatement
, getColumnMeta
, int64_t column
) {
3072 auto data
= Native::data
<PDOStatementData
>(this_
);
3073 if (data
->m_stmt
== nullptr) {
3078 pdo_raise_impl_error(data
->m_stmt
->dbh
, data
->m_stmt
, "42P10",
3079 "column number must be non-negative");
3083 if (!data
->m_stmt
->support(PDOStatement::MethodGetColumnMeta
)) {
3084 pdo_raise_impl_error(data
->m_stmt
->dbh
, data
->m_stmt
, "IM001",
3085 "driver doesn't support meta data");
3089 setPDOErrorNone(data
->m_stmt
->error_code
);
3090 auto ret
= Array::CreateDict();
3091 if (!data
->m_stmt
->getColumnMeta(column
, ret
)) {
3092 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
3096 /* add stock items */
3097 auto col
= cast
<PDOColumn
>(data
->m_stmt
->columns
[column
]);
3098 ret
.set(s_name
, col
->name
);
3099 ret
.set(s_len
, (int64_t)col
->maxlen
); /* FIXME: unsigned ? */
3100 ret
.set(s_precision
, (int64_t)col
->precision
);
3101 if (col
->param_type
!= PDO_PARAM_ZVAL
) {
3102 // if param_type is PDO_PARAM_ZVAL the driver has to provide correct data
3103 ret
.set(s_pdo_type
, (int64_t)col
->param_type
);
3108 static bool HHVM_METHOD(PDOStatement
, setFetchMode
,
3109 int64_t mode
, const Array
& _argv
/* = null_array */) {
3110 auto data
= Native::data
<PDOStatementData
>(this_
);
3111 if (data
->m_stmt
== nullptr) {
3114 int argc
= _argv
.size() + 1;
3116 return pdo_stmt_set_fetch_mode(data
->m_stmt
, argc
, mode
, _argv
);
3119 static bool HHVM_METHOD(PDOStatement
, nextRowset
) {
3120 auto data
= Native::data
<PDOStatementData
>(this_
);
3121 if (data
->m_stmt
== nullptr) {
3125 if (!data
->m_stmt
->support(PDOStatement::MethodNextRowset
)) {
3126 pdo_raise_impl_error(data
->m_stmt
->dbh
, data
->m_stmt
, "IM001",
3127 "driver does not support multiple rowsets");
3131 setPDOErrorNone(data
->m_stmt
->error_code
);
3134 if (!data
->m_stmt
->columns
.empty()) {
3135 data
->m_stmt
->columns
.clear();
3136 data
->m_stmt
->column_count
= 0;
3139 if (!data
->m_stmt
->nextRowset()) {
3140 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
3144 pdo_stmt_describe_columns(data
->m_stmt
);
3148 static bool HHVM_METHOD(PDOStatement
, closeCursor
) {
3149 auto data
= Native::data
<PDOStatementData
>(this_
);
3150 if (data
->m_stmt
== nullptr) {
3154 if (!data
->m_stmt
->support(PDOStatement::MethodCursorCloser
)) {
3155 /* emulate it by fetching and discarding rows */
3157 while (data
->m_stmt
->fetcher(PDO_FETCH_ORI_NEXT
, 0));
3158 // if (!data->t_nextrowset()) {
3159 if (HHVM_MN(PDOStatement
, nextRowset
)(this_
)) {
3163 data
->m_stmt
->executed
= 0;
3167 setPDOErrorNone(data
->m_stmt
->error_code
);
3168 if (!data
->m_stmt
->cursorCloser()) {
3169 PDO_HANDLE_STMT_ERR(data
->m_stmt
);
3172 data
->m_stmt
->executed
= 0;
3176 static Variant
HHVM_METHOD(PDOStatement
, debugDumpParams
) {
3177 auto data
= Native::data
<PDOStatementData
>(this_
);
3178 if (data
->m_stmt
== nullptr) {
3182 auto f
= File::Open("php://output", "w");
3183 if (!f
|| f
->isInvalid()) {
3190 data
->m_stmt
->query_string
.size(),
3191 data
->m_stmt
->query_string
.size(),
3192 data
->m_stmt
->query_string
.data()
3196 f
->printf("Params: %d\n",
3197 make_vec_array(data
->m_stmt
->bound_params
.size()));
3198 for (ArrayIter
iter(data
->m_stmt
->bound_params
); iter
; ++iter
) {
3199 if (iter
.first().isString()) {
3200 String key
= iter
.first().toString();
3202 "Key: Name: [%d] %.*s\n",
3203 make_vec_array(key
.size(), key
.size(), key
.data())
3206 f
->printf("Key: Position #%ld:\n",
3207 make_vec_array(iter
.first().toInt64()));
3210 auto param
= cast
<PDOBoundParam
>(iter
.second());
3212 "paramno=%d\nname=[%d] \"%.*s\"\nparam_type=%d\n",
3225 static Variant
HHVM_METHOD(PDOStatement
, current
) {
3226 auto data
= Native::data
<PDOStatementData
>(this_
);
3231 static Variant
HHVM_METHOD(PDOStatement
, key
) {
3232 auto data
= Native::data
<PDOStatementData
>(this_
);
3234 return data
->m_rowIndex
;
3237 static Variant
HHVM_METHOD(PDOStatement
, next
) {
3238 auto data
= Native::data
<PDOStatementData
>(this_
);
3240 data
->m_row
= HHVM_MN(PDOStatement
, fetch
)(this_
, PDO_FETCH_USE_DEFAULT
);
3241 if (same(data
->m_row
, false)) {
3242 data
->m_rowIndex
= -1;
3249 static Variant
HHVM_METHOD(PDOStatement
, rewind
) {
3250 auto data
= Native::data
<PDOStatementData
>(this_
);
3252 data
->m_rowIndex
= -1;
3253 HHVM_MN(PDOStatement
, next
)(this_
);
3257 static bool HHVM_METHOD(PDOStatement
, valid
) {
3258 auto data
= Native::data
<PDOStatementData
>(this_
);
3260 return data
->m_rowIndex
>= 0;
3263 static Variant
HHVM_METHOD(PDOStatement
, __wakeup
) {
3264 throw_pdo_exception(uninit_null(),
3265 "You cannot serialize or unserialize "
3266 "PDOStatement instances");
3270 static Variant
HHVM_METHOD(PDOStatement
, __sleep
) {
3271 throw_pdo_exception(uninit_null(),
3272 "You cannot serialize or unserialize "
3273 "PDOStatement instances");
3277 ///////////////////////////////////////////////////////////////////////////////
3280 static struct PDOExtension final
: Extension
{
3281 PDOExtension() : Extension("pdo", " 1.0.4dev", NO_ONCALL_YET
) {}
3283 #ifdef ENABLE_EXTENSION_PDO_MYSQL
3284 std::string mysql_default_socket
;
3286 void moduleLoad(const IniSetting::Map
& /*ini*/, Hdf
/*config*/) override
{
3287 IniSetting::Bind(this, IniSetting::Mode::Config
,
3288 "pdo_mysql.default_socket", nullptr,
3289 &mysql_default_socket
);
3293 void moduleRegisterNative() override
{
3294 HHVM_FE(pdo_drivers
);
3295 HHVM_ME(PDO
, __construct
);
3296 HHVM_ME(PDO
, prepare
);
3297 HHVM_ME(PDO
, beginTransaction
);
3298 HHVM_ME(PDO
, commit
);
3299 HHVM_ME(PDO
, inTransaction
);
3300 HHVM_ME(PDO
, rollBack
);
3301 HHVM_ME(PDO
, setAttribute
);
3302 HHVM_ME(PDO
, getAttribute
);
3304 HHVM_ME(PDO
, lastInsertId
);
3305 HHVM_ME(PDO
, errorCode
);
3306 HHVM_ME(PDO
, errorInfo
);
3307 HHVM_ME(PDO
, query
);
3308 HHVM_ME(PDO
, quote
);
3309 HHVM_ME(PDO
, sqliteCreateFunction
);
3310 HHVM_ME(PDO
, sqliteCreateAggregate
);
3311 HHVM_ME(PDO
, __wakeup
);
3312 HHVM_ME(PDO
, __sleep
);
3313 HHVM_STATIC_ME(PDO
, getAvailableDrivers
);
3314 HHVM_ME(PDOStatement
, execute
);
3315 HHVM_ME(PDOStatement
, fetch
);
3316 HHVM_ME(PDOStatement
, fetchObject
);
3317 HHVM_ME(PDOStatement
, fetchColumn
);
3318 HHVM_ME(PDOStatement
, fetchAll
);
3319 HHVM_ME(PDOStatement
, bindValue
);
3320 HHVM_ME(PDOStatement
, rowCount
);
3321 HHVM_ME(PDOStatement
, errorCode
);
3322 HHVM_ME(PDOStatement
, errorInfo
);
3323 HHVM_ME(PDOStatement
, setAttribute
);
3324 HHVM_ME(PDOStatement
, getAttribute
);
3325 HHVM_ME(PDOStatement
, columnCount
);
3326 HHVM_ME(PDOStatement
, getColumnMeta
);
3327 HHVM_ME(PDOStatement
, setFetchMode
);
3328 HHVM_ME(PDOStatement
, nextRowset
);
3329 HHVM_ME(PDOStatement
, closeCursor
);
3330 HHVM_ME(PDOStatement
, debugDumpParams
);
3331 HHVM_ME(PDOStatement
, current
);
3332 HHVM_ME(PDOStatement
, key
);
3333 HHVM_ME(PDOStatement
, next
);
3334 HHVM_ME(PDOStatement
, rewind
);
3335 HHVM_ME(PDOStatement
, valid
);
3336 HHVM_ME(PDOStatement
, __wakeup
);
3337 HHVM_ME(PDOStatement
, __sleep
);
3339 HHVM_RCC_INT(PDO
, PARAM_BOOL
, PDO_PARAM_BOOL
);
3340 HHVM_RCC_INT(PDO
, PARAM_NULL
, PDO_PARAM_NULL
);
3341 HHVM_RCC_INT(PDO
, PARAM_INT
, PDO_PARAM_INT
);
3342 HHVM_RCC_INT(PDO
, PARAM_STR
, PDO_PARAM_STR
);
3343 HHVM_RCC_INT(PDO
, PARAM_LOB
, PDO_PARAM_LOB
);
3344 HHVM_RCC_INT(PDO
, PARAM_STMT
, PDO_PARAM_STMT
);
3345 HHVM_RCC_INT(PDO
, PARAM_INPUT_OUTPUT
, PDO_PARAM_INPUT_OUTPUT
);
3346 HHVM_RCC_INT(PDO
, PARAM_EVT_ALLOC
, PDO_PARAM_EVT_ALLOC
);
3347 HHVM_RCC_INT(PDO
, PARAM_EVT_FREE
, PDO_PARAM_EVT_FREE
);
3348 HHVM_RCC_INT(PDO
, PARAM_EVT_EXEC_PRE
, PDO_PARAM_EVT_EXEC_PRE
);
3349 HHVM_RCC_INT(PDO
, PARAM_EVT_EXEC_POST
, PDO_PARAM_EVT_EXEC_POST
);
3350 HHVM_RCC_INT(PDO
, PARAM_EVT_FETCH_PRE
, PDO_PARAM_EVT_FETCH_PRE
);
3351 HHVM_RCC_INT(PDO
, PARAM_EVT_FETCH_POST
, PDO_PARAM_EVT_FETCH_POST
);
3352 HHVM_RCC_INT(PDO
, PARAM_EVT_NORMALIZE
, PDO_PARAM_EVT_NORMALIZE
);
3353 HHVM_RCC_INT(PDO
, FETCH_USE_DEFAULT
, PDO_FETCH_USE_DEFAULT
);
3354 HHVM_RCC_INT(PDO
, FETCH_LAZY
, PDO_FETCH_LAZY
);
3355 HHVM_RCC_INT(PDO
, FETCH_ASSOC
, PDO_FETCH_ASSOC
);
3356 HHVM_RCC_INT(PDO
, FETCH_NUM
, PDO_FETCH_NUM
);
3357 HHVM_RCC_INT(PDO
, FETCH_BOTH
, PDO_FETCH_BOTH
);
3358 HHVM_RCC_INT(PDO
, FETCH_OBJ
, PDO_FETCH_OBJ
);
3359 HHVM_RCC_INT(PDO
, FETCH_BOUND
, PDO_FETCH_BOUND
);
3360 HHVM_RCC_INT(PDO
, FETCH_COLUMN
, PDO_FETCH_COLUMN
);
3361 HHVM_RCC_INT(PDO
, FETCH_CLASS
, PDO_FETCH_CLASS
);
3362 HHVM_RCC_INT(PDO
, FETCH_INTO
, PDO_FETCH_INTO
);
3363 HHVM_RCC_INT(PDO
, FETCH_FUNC
, PDO_FETCH_FUNC
);
3364 HHVM_RCC_INT(PDO
, FETCH_GROUP
, PDO_FETCH_GROUP
);
3365 HHVM_RCC_INT(PDO
, FETCH_UNIQUE
, PDO_FETCH_UNIQUE
);
3366 HHVM_RCC_INT(PDO
, FETCH_KEY_PAIR
, PDO_FETCH_KEY_PAIR
);
3367 HHVM_RCC_INT(PDO
, FETCH_CLASSTYPE
, PDO_FETCH_CLASSTYPE
);
3368 HHVM_RCC_INT(PDO
, FETCH_SERIALIZE
, PDO_FETCH_SERIALIZE
);
3369 HHVM_RCC_INT(PDO
, FETCH_PROPS_LATE
, PDO_FETCH_PROPS_LATE
);
3370 HHVM_RCC_INT(PDO
, FETCH_NAMED
, PDO_FETCH_NAMED
);
3371 HHVM_RCC_INT(PDO
, ATTR_AUTOCOMMIT
, PDO_ATTR_AUTOCOMMIT
);
3372 HHVM_RCC_INT(PDO
, ATTR_PREFETCH
, PDO_ATTR_PREFETCH
);
3373 HHVM_RCC_INT(PDO
, ATTR_TIMEOUT
, PDO_ATTR_TIMEOUT
);
3374 HHVM_RCC_INT(PDO
, ATTR_ERRMODE
, PDO_ATTR_ERRMODE
);
3375 HHVM_RCC_INT(PDO
, ATTR_SERVER_VERSION
, PDO_ATTR_SERVER_VERSION
);
3376 HHVM_RCC_INT(PDO
, ATTR_CLIENT_VERSION
, PDO_ATTR_CLIENT_VERSION
);
3377 HHVM_RCC_INT(PDO
, ATTR_SERVER_INFO
, PDO_ATTR_SERVER_INFO
);
3378 HHVM_RCC_INT(PDO
, ATTR_CONNECTION_STATUS
, PDO_ATTR_CONNECTION_STATUS
);
3379 HHVM_RCC_INT(PDO
, ATTR_CASE
, PDO_ATTR_CASE
);
3380 HHVM_RCC_INT(PDO
, ATTR_CURSOR_NAME
, PDO_ATTR_CURSOR_NAME
);
3381 HHVM_RCC_INT(PDO
, ATTR_CURSOR
, PDO_ATTR_CURSOR
);
3382 HHVM_RCC_INT(PDO
, ATTR_ORACLE_NULLS
, PDO_ATTR_ORACLE_NULLS
);
3383 HHVM_RCC_INT(PDO
, ATTR_PERSISTENT
, PDO_ATTR_PERSISTENT
);
3384 HHVM_RCC_INT(PDO
, ATTR_STATEMENT_CLASS
, PDO_ATTR_STATEMENT_CLASS
);
3385 HHVM_RCC_INT(PDO
, ATTR_FETCH_TABLE_NAMES
, PDO_ATTR_FETCH_TABLE_NAMES
);
3386 HHVM_RCC_INT(PDO
, ATTR_FETCH_CATALOG_NAMES
, PDO_ATTR_FETCH_CATALOG_NAMES
);
3387 HHVM_RCC_INT(PDO
, ATTR_DRIVER_NAME
, PDO_ATTR_DRIVER_NAME
);
3388 HHVM_RCC_INT(PDO
, ATTR_STRINGIFY_FETCHES
, PDO_ATTR_STRINGIFY_FETCHES
);
3389 HHVM_RCC_INT(PDO
, ATTR_MAX_COLUMN_LEN
, PDO_ATTR_MAX_COLUMN_LEN
);
3390 HHVM_RCC_INT(PDO
, ATTR_EMULATE_PREPARES
, PDO_ATTR_EMULATE_PREPARES
);
3391 HHVM_RCC_INT(PDO
, ATTR_DEFAULT_FETCH_MODE
, PDO_ATTR_DEFAULT_FETCH_MODE
);
3392 HHVM_RCC_INT(PDO
, ERRMODE_SILENT
, PDO_ERRMODE_SILENT
);
3393 HHVM_RCC_INT(PDO
, ERRMODE_WARNING
, PDO_ERRMODE_WARNING
);
3394 HHVM_RCC_INT(PDO
, ERRMODE_EXCEPTION
, PDO_ERRMODE_EXCEPTION
);
3395 HHVM_RCC_INT(PDO
, CASE_NATURAL
, PDO_CASE_NATURAL
);
3396 HHVM_RCC_INT(PDO
, CASE_LOWER
, PDO_CASE_LOWER
);
3397 HHVM_RCC_INT(PDO
, CASE_UPPER
, PDO_CASE_UPPER
);
3398 HHVM_RCC_INT(PDO
, NULL_NATURAL
, PDO_NULL_NATURAL
);
3399 HHVM_RCC_INT(PDO
, NULL_EMPTY_STRING
, PDO_NULL_EMPTY_STRING
);
3400 HHVM_RCC_INT(PDO
, NULL_TO_STRING
, PDO_NULL_TO_STRING
);
3401 HHVM_RCC_INT(PDO
, FETCH_ORI_NEXT
, PDO_FETCH_ORI_NEXT
);
3402 HHVM_RCC_INT(PDO
, FETCH_ORI_PRIOR
, PDO_FETCH_ORI_PRIOR
);
3403 HHVM_RCC_INT(PDO
, FETCH_ORI_FIRST
, PDO_FETCH_ORI_FIRST
);
3404 HHVM_RCC_INT(PDO
, FETCH_ORI_LAST
, PDO_FETCH_ORI_LAST
);
3405 HHVM_RCC_INT(PDO
, FETCH_ORI_ABS
, PDO_FETCH_ORI_ABS
);
3406 HHVM_RCC_INT(PDO
, FETCH_ORI_REL
, PDO_FETCH_ORI_REL
);
3407 HHVM_RCC_INT(PDO
, CURSOR_FWDONLY
, PDO_CURSOR_FWDONLY
);
3408 HHVM_RCC_INT(PDO
, CURSOR_SCROLL
, PDO_CURSOR_SCROLL
);
3409 #ifdef ENABLE_EXTENSION_PDO_MYSQL
3410 HHVM_RCC_INT(PDO
, MYSQL_ATTR_USE_BUFFERED_QUERY
,
3411 PDO_MYSQL_ATTR_USE_BUFFERED_QUERY
);
3412 HHVM_RCC_INT(PDO
, MYSQL_ATTR_LOCAL_INFILE
, PDO_MYSQL_ATTR_LOCAL_INFILE
);
3413 HHVM_RCC_INT(PDO
, MYSQL_ATTR_MAX_BUFFER_SIZE
,
3414 PDO_MYSQL_ATTR_MAX_BUFFER_SIZE
);
3415 HHVM_RCC_INT(PDO
, MYSQL_ATTR_INIT_COMMAND
, PDO_MYSQL_ATTR_INIT_COMMAND
);
3416 HHVM_RCC_INT(PDO
, MYSQL_ATTR_READ_DEFAULT_FILE
,
3417 PDO_MYSQL_ATTR_READ_DEFAULT_FILE
);
3418 HHVM_RCC_INT(PDO
, MYSQL_ATTR_READ_DEFAULT_GROUP
,
3419 PDO_MYSQL_ATTR_READ_DEFAULT_GROUP
);
3420 HHVM_RCC_INT(PDO
, MYSQL_ATTR_COMPRESS
, PDO_MYSQL_ATTR_COMPRESS
);
3421 HHVM_RCC_INT(PDO
, MYSQL_ATTR_DIRECT_QUERY
, PDO_MYSQL_ATTR_DIRECT_QUERY
);
3422 HHVM_RCC_INT(PDO
, MYSQL_ATTR_FOUND_ROWS
, PDO_MYSQL_ATTR_FOUND_ROWS
);
3423 HHVM_RCC_INT(PDO
, MYSQL_ATTR_IGNORE_SPACE
, PDO_MYSQL_ATTR_IGNORE_SPACE
);
3424 HHVM_RCC_INT(PDO
, MYSQL_ATTR_SSL_CA
, PDO_MYSQL_ATTR_SSL_CA
);
3425 HHVM_RCC_INT(PDO
, MYSQL_ATTR_SSL_CAPATH
, PDO_MYSQL_ATTR_SSL_CAPATH
);
3426 HHVM_RCC_INT(PDO
, MYSQL_ATTR_SSL_CERT
, PDO_MYSQL_ATTR_SSL_CERT
);
3427 HHVM_RCC_INT(PDO
, MYSQL_ATTR_SSL_KEY
, PDO_MYSQL_ATTR_SSL_KEY
);
3428 HHVM_RCC_INT(PDO
, MYSQL_ATTR_SSL_CIPHER
, PDO_MYSQL_ATTR_SSL_CIPHER
);
3429 HHVM_RCC_INT(PDO
, MYSQL_ATTR_MULTI_STATEMENTS
,
3430 PDO_MYSQL_ATTR_MULTI_STATEMENTS
);
3431 HHVM_RCC_INT(PDO
, HH_MYSQL_ATTR_READ_TIMEOUT
,
3432 HH_PDO_MYSQL_ATTR_READ_TIMEOUT
);
3433 HHVM_RCC_INT(PDO
, HH_MYSQL_ATTR_WRITE_TIMEOUT
,
3434 HH_PDO_MYSQL_ATTR_WRITE_TIMEOUT
);
3436 HHVM_RCC_STR(PDO
, ERR_NONE
, PDO_ERR_NONE
);
3438 Native::registerNativeDataInfo
<PDOData
>(
3439 s_PDO
.get(), Native::NDIFlags::NO_SWEEP
);
3440 Native::registerNativeDataInfo
<PDOStatementData
>(
3441 s_PDOStatement
.get(), Native::NDIFlags::NO_SWEEP
);
3445 //////////////////////////////////////////////////////////////////////////////