no unique F14Chunk empty-tag-vector instance
[hiphop-php.git] / hphp / runtime / ext / pdo / ext_pdo.cpp
blob8cd82b20f36b2e3d53d1251c1af61caa6f4c217b
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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 +----------------------------------------------------------------------+
18 #include <set>
19 #include <string>
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"
42 #endif
43 #ifdef ENABLE_EXTENSION_PDO_SQLITE
44 #include "hphp/runtime/ext/pdo_sqlite/pdo_sqlite.h"
45 #endif
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); \
54 } \
56 #define PDO_HANDLE_STMT_ERR(stmt) \
57 if (strcmp(stmt->error_code, PDO_ERR_NONE)) { \
58 pdo_handle_error(stmt->dbh, stmt); \
59 } \
61 namespace HPHP {
63 struct PDOData {
64 sp_PDOResource m_dbh;
67 struct PDOStatementData {
68 PDOStatementData();
69 ~PDOStatementData();
71 sp_PDOStatement m_stmt;
72 Variant m_row;
73 int m_rowIndex;
76 using std::string;
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());
86 return ret.toArray();
89 ///////////////////////////////////////////////////////////////////////////////
90 // error handling
92 struct pdo_sqlstate_info {
93 const char *state;
94 const char *desc;
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" },
112 { "01S06",
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 *> {
368 PDOErrorHash() {
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);
378 if (iter != end()) {
379 return iter->second;
381 return "<<Unknown error>>";
384 static PDOErrorHash s_err_hash;
386 const StaticString
387 s_code("code"),
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);
396 va_list ap;
397 va_start(ap, fmt);
398 string msg;
399 string_vsnprintf(msg, fmt, ap);
400 obj->o_set(s_message, String(msg), s_PDOException);
401 va_end(ap);
403 if (!info.isNull()) {
404 obj->o_set(s_errorInfo, info, s_PDOException);
406 throw_object(obj);
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;
414 if (stmt) {
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;
421 if (supp) {
422 err += ": "; err += supp;
425 if (dbh->error_mode != PDO_ERRMODE_EXCEPTION) {
426 raise_warning("%s", err.c_str());
427 } else {
428 VecInit info(2);
429 info.append(String(*pdo_err, CopyString));
430 info.append(0LL);
431 throw_pdo_exception(info.toArray(), "%s",
432 err.c_str());
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);
441 namespace {
443 void pdo_handle_error(sp_PDOResource rsrc, PDOStatement* stmt) {
444 auto const& dbh = rsrc->conn();
446 if (dbh->error_mode == PDO_ERRMODE_SILENT) {
447 return;
449 PDOErrorType *pdo_err = &dbh->error_code;
450 if (stmt) {
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;
458 String supp;
459 Array info;
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;
473 if (!supp.empty()) {
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());
480 } else {
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,
495 int64_t defval) {
496 if (options.exists(name)) {
497 return options[name].toInt64();
499 return defval;
502 static Object pdo_stmt_instantiate(sp_PDOResource dbh, const String& clsname,
503 const Variant& ctor_args) {
504 String name = clsname;
505 if (name.empty()) {
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");
511 return Object();
513 Class* cls = Class::load(name.get());
514 if (!cls) {
515 return Object();
517 callerDynamicConstructChecks(cls);
518 return Object{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()) {
528 return;
530 Class* cls = Class::load(clsname.get());
531 if (!cls) {
532 return;
534 ObjectData* inst = object.get();
535 tvDecRefGen(
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())) {
545 pdo_raise_impl_error
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);
551 return false;
553 clsname = opt.toArray()[0].toString();
554 if (clsname == String("PDOStatement")) {
555 ctor_args = Variant(Array());
556 return true;
558 if (!HHVM_FN(is_a)(clsname, "PDOStatement", /* allow_string */ true)) {
559 pdo_raise_impl_error
560 (dbh, nullptr, "HY000",
561 "user-supplied statement class must be derived from PDOStatement");
562 PDO_HANDLE_DBH_ERR(dbh);
563 return false;
565 HPHP::Class* cls = HPHP::Class::load(clsname.get());
566 if (cls) {
567 const HPHP::Func* method = cls->getDeclaredCtor();
568 if (method && method->isPublic()) {
569 pdo_raise_impl_error
570 (dbh, nullptr, "HY000",
571 "user-supplied statement class cannot have a public constructor");
572 PDO_HANDLE_DBH_ERR(dbh);
573 return false;
576 if (opt.toArray().exists(1)) {
577 Variant item = opt.toArray()[1];
578 if (!item.isArray()) {
579 pdo_raise_impl_error
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);
584 return false;
586 ctor_args = item;
588 return true;
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)) {
594 return false;
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) {
603 case PDO_CASE_UPPER:
604 column->name = HHVM_FN(strtoupper)(column->name);
605 break;
606 case PDO_CASE_LOWER:
607 column->name = HHVM_FN(strtolower)(column->name);
608 break;
609 default:;
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;
620 return true;
623 static bool pdo_stmt_verify_mode(sp_PDOStatement stmt, int64_t mode,
624 bool fetch_all) {
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");
630 return false;
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;
638 switch (mode) {
639 case PDO_FETCH_FUNC:
640 if (!fetch_all) {
641 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
642 "PDO::FETCH_FUNC is only allowed in "
643 "PDOStatement::fetchAll()");
644 return false;
646 return true;
648 case PDO_FETCH_LAZY:
649 if (fetch_all) {
650 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
651 "PDO::FETCH_LAZY can't be used with "
652 "PDOStatement::fetchAll()");
653 return false;
655 [[fallthrough]];
657 default:
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");
662 return false;
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");
668 return false;
670 if (mode >= PDO_FETCH__MAX) {
671 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "invalid fetch mode");
672 return false;
674 [[fallthrough]];
676 case PDO_FETCH_CLASS:
677 break;
679 return true;
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());
689 if (cls) {
690 const HPHP::Func* method = cls->getDeclaredCtor();
691 if (method) {
692 stmt->fetch.constructor = method->nameStr();
693 return true;
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 "
700 "omit it");
701 return false;
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);
717 return false;
720 int flags = mode & PDO_FETCH_FLAGS;
721 bool retval = false;
722 switch (mode & ~PDO_FETCH_FLAGS) {
723 case PDO_FETCH_USE_DEFAULT:
724 case PDO_FETCH_LAZY:
725 case PDO_FETCH_ASSOC:
726 case PDO_FETCH_NUM:
727 case PDO_FETCH_BOTH:
728 case PDO_FETCH_OBJ:
729 case PDO_FETCH_BOUND:
730 case PDO_FETCH_NAMED:
731 case PDO_FETCH_KEY_PAIR:
732 if (_argc != 1) {
733 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
734 "fetch mode doesn't allow any extra arguments");
735 } else {
736 retval = true;
738 break;
740 case PDO_FETCH_COLUMN:
741 if (_argc != 2) {
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");
747 } else {
748 stmt->fetch.column = _argv[0].toInt64();
749 retval = true;
751 break;
753 case PDO_FETCH_CLASS:
754 /* Gets its class name from 1st column */
755 if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
756 if (_argc != 1) {
757 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
758 "fetch mode doesn't allow any extra arguments");
759 } else {
760 stmt->fetch.clsname.clear();
761 retval = true;
763 } else {
764 if (_argc < 2) {
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");
773 } else {
774 retval = HHVM_FN(class_exists)(_argv[0].toString());
775 if (retval) {
776 stmt->fetch.clsname = _argv[0].toString();
781 if (retval) {
782 stmt->fetch.ctor_args.unset();
783 if (_argc == 3) {
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");
787 retval = false;
788 } else {
789 stmt->fetch.ctor_args = _argv[1];
793 if (retval) {
794 do_fetch_class_prepare(stmt);
798 break;
800 case PDO_FETCH_INTO:
801 if (_argc != 2) {
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");
807 } else {
808 retval = true;
811 if (retval) {
812 stmt->fetch.into = _argv[0];
814 break;
816 default:
817 pdo_raise_impl_error(stmt->dbh, stmt, "22003",
818 "Invalid fetch mode specified");
821 if (retval) {
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);
833 return retval;
836 ///////////////////////////////////////////////////////////////////////////////
837 // forward declarations
839 bool HHVM_METHOD(PDO, setAttribute, int64_t attribute,
840 const Variant& value);
842 ///////////////////////////////////////////////////////////////////////////////
843 // PDO
845 namespace {
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(), ':');
863 if (!colon) {
864 /* let's see if this string has a matching dsn in the php.ini */
865 String name = "pdo.dsn."; name += data_source;
866 String ini_dsn;
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(), ':');
872 if (!colon) {
873 throw_pdo_exception(uninit_null(),
874 "invalid data source name (via INI: %s)",
875 ini_dsn.data());
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(), ':');
887 if (!colon) {
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;
917 } else {
918 is_persistent = v.toInt64();
919 hashkey.printf("PDO:DBH:DSN=%s:%s:%s",
920 data_source.data(), username.data(),
921 password.data());
925 if (is_persistent) {
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;
941 if (data->m_dbh) {
942 call_factory = false;
943 } else {
944 /* need a brand new pdbh */
945 data->m_dbh = driver->createResource(colon + 1, username,
946 password, options);
947 if (!data->m_dbh) {
948 throw_pdo_exception(uninit_null(), "unable to create a connection");
950 data->m_dbh->conn()->persistent_id = shashkey;
954 if (!data->m_dbh) {
955 data->m_dbh = driver->createResource(colon + 1, username,
956 password, options);
957 if (!data->m_dbh) {
958 throw_pdo_exception(uninit_null(), "unable to create a connection");
962 if (call_factory) {
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);
969 if (!call_factory) {
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(),
973 iter.second());
975 } else if (data->m_dbh) {
976 if (is_persistent) {
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(),
984 iter.second());
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;
997 String clsname;
998 Variant ctor_args;
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)) {
1002 return false;
1004 } else {
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);
1010 if (ret.isNull()) {
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);
1015 return false;
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;
1021 assertx(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);
1029 return ret;
1032 PDO_HANDLE_DBH_ERR(data->m_dbh);
1033 return false;
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;
1045 return true;
1047 if (strcmp(data->m_dbh->conn()->error_code, PDO_ERR_NONE)) {
1048 pdo_handle_error(data->m_dbh, nullptr);
1050 return false;
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;
1062 return true;
1064 PDO_HANDLE_DBH_ERR(data->m_dbh);
1065 return false;
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;
1084 return true;
1086 PDO_HANDLE_DBH_ERR(data->m_dbh);
1087 return false;
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); \
1101 return false; \
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();
1112 return true;
1113 default:
1114 pdo_raise_impl_error(data->m_dbh, nullptr, "HY000", "invalid error mode");
1115 PDO_HANDLE_DBH_ERR(data->m_dbh);
1116 return false;
1118 return false;
1120 case PDO_ATTR_CASE:
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();
1127 return true;
1128 default:
1129 pdo_raise_impl_error(data->m_dbh, nullptr, "HY000",
1130 "invalid case folding mode");
1131 PDO_HANDLE_DBH_ERR(data->m_dbh);
1132 return false;
1134 return false;
1136 case PDO_ATTR_ORACLE_NULLS:
1137 PDO_LONG_PARAM_CHECK;
1138 data->m_dbh->conn()->oracle_nulls = value.toInt64();
1139 return true;
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");
1150 return false;
1153 } else {
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");
1159 return false;
1161 data->m_dbh->conn()->default_fetch_type = (PDOFetchType)value.toInt64();
1162 return true;
1164 case PDO_ATTR_STRINGIFY_FETCHES:
1165 PDO_LONG_PARAM_CHECK;
1166 data->m_dbh->conn()->stringify = value.toInt64() ? 1 : 0;
1167 return true;
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);
1176 return false;
1178 String clsname;
1179 if (!valid_statement_class(data->m_dbh, value, clsname,
1180 data->m_dbh->def_stmt_ctor_args)) {
1181 return false;
1183 data->m_dbh->conn()->def_stmt_clsname = clsname.c_str();
1184 return true;
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)) {
1192 return true;
1196 if (attribute == PDO_ATTR_AUTOCOMMIT) {
1197 throw_pdo_exception(uninit_null(),
1198 "The auto-commit mode cannot be changed for this "
1199 "driver");
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");
1203 } else {
1204 PDO_HANDLE_DBH_ERR(data->m_dbh);
1206 return false;
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;
1221 case PDO_ATTR_CASE:
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: {
1234 Array ret;
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);
1239 return ret;
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");
1248 return false;
1251 Variant ret;
1252 switch (data->m_dbh->conn()->getAttribute(attribute, ret)) {
1253 case -1:
1254 PDO_HANDLE_DBH_ERR(data->m_dbh);
1255 return false;
1256 case 0:
1257 pdo_raise_impl_error(data->m_dbh, nullptr, "IM001",
1258 "driver does not support that attribute");
1259 return false;
1261 return ret;
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");
1271 return false;
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);
1279 if (ret == -1) {
1280 PDO_HANDLE_DBH_ERR(data->m_dbh);
1281 return false;
1283 return ret;
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()");
1297 return false;
1300 String ret = data->m_dbh->conn()->lastId(seqname.data());
1301 if (ret.empty()) {
1302 PDO_HANDLE_DBH_ERR(data->m_dbh);
1303 return false;
1305 return ret;
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') {
1317 return init_null();
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));
1335 } else {
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);
1356 return ret;
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);
1370 if (ret.isNull()) {
1371 pdo_raise_impl_error
1372 (data->m_dbh, nullptr, "HY000",
1373 "failed to instantiate user supplied statement class");
1374 return init_null();
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;
1380 assertx(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;
1395 if (argc == 1 ||
1396 pdo_stmt_set_fetch_mode(
1397 stmt,
1399 tvCastToInt64(_argv.lookup(0)),
1400 Variant::attach(HHVM_FN(array_splice)(argv_variant, 1)).toArray()
1401 )) {
1402 /* now execute the statement */
1403 setPDOErrorNone(stmt->error_code);
1404 if (stmt->executer()) {
1405 int ok = 1;
1406 if (!stmt->executed) {
1407 if (stmt->dbh->conn()->alloc_own_columns) {
1408 ok = pdo_stmt_describe_columns(stmt);
1410 stmt->executed = 1;
1412 if (ok) {
1413 pdo_stmt_construct(stmt, ret, data->m_dbh->conn()->def_stmt_clsname,
1414 data->m_dbh->def_stmt_ctor_args);
1415 return ret;
1419 /* something broke */
1420 data->m_dbh->query_stmt = stmt.get();
1421 PDO_HANDLE_STMT_ERR(stmt);
1422 } else {
1423 PDO_HANDLE_DBH_ERR(data->m_dbh);
1426 return false;
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");
1440 return false;
1443 String quoted;
1444 if (data->m_dbh->conn()->quoter(str, quoted, (PDOParamType)paramtype)) {
1445 return quoted;
1447 PDO_HANDLE_DBH_ERR(data->m_dbh);
1448 return false;
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) {
1458 return false;
1460 return res->createFunction(name, callback, argcount);
1461 #else
1462 raise_recoverable_error("PDO::sqliteCreateFunction not implemented");
1463 return false;
1464 #endif
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");
1471 return false;
1474 static Variant HHVM_METHOD(PDO, __wakeup) {
1475 throw_pdo_exception(uninit_null(),
1476 "You cannot serialize or unserialize PDO instances");
1477 return init_null();
1480 static Variant HHVM_METHOD(PDO, __sleep) {
1481 throw_pdo_exception(uninit_null(),
1482 "You cannot serialize or unserialize PDO instances");
1483 return init_null();
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
1500 * driver */
1501 if (stmt->named_rewrite_template) {
1502 /* this is not an error here */
1503 return true;
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();
1509 return true;
1511 pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
1512 "parameter was not defined");
1513 return false;
1516 int position = 0;
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");
1526 return true;
1528 param->paramno = position;
1529 return true;
1532 pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
1533 "parameter was not defined");
1534 return false;
1536 return true;
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)) {
1543 return true;
1545 for (ArrayIter iter(stmt->bound_params); iter; ++iter) {
1546 auto param = cast<PDOBoundParam>(iter.second());
1547 if (!stmt->paramHook(param.get(), event_type)) {
1548 return false;
1551 for (ArrayIter iter(stmt->bound_columns); iter; ++iter) {
1552 auto param = cast<PDOBoundParam>(iter.second());
1553 if (!stmt->paramHook(param.get(), event_type)) {
1554 return false;
1557 return true;
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();
1589 return false;
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
1595 * at this time. */
1596 if (stmt->support(PDOStatement::MethodParamHook)) {
1597 if (!stmt->paramHook(param.get(), PDO_PARAM_EVT_NORMALIZE)) {
1598 param->name.reset();
1599 return false;
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));
1613 } else {
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);
1624 } else {
1625 hash.remove(param->paramno);
1627 param->parameter.unset();
1628 return false;
1631 return true;
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) {
1637 return;
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) {
1646 switch (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,
1662 long offset) {
1663 if (!stmt->executed) {
1664 return false;
1666 if (!dispatch_param_event(stmt, PDO_PARAM_EVT_FETCH_PRE)) {
1667 return false;
1669 if (!stmt->fetcher(ori, offset)) {
1670 return false;
1672 /* some drivers might need to describe the columns now */
1673 if (stmt->columns.empty() && !pdo_stmt_describe_columns(stmt)) {
1674 return false;
1676 if (!dispatch_param_event(stmt, PDO_PARAM_EVT_FETCH_POST)) {
1677 return false;
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();
1686 /* set new value */
1687 fetch_value(stmt, param->parameter, param->paramno,
1688 (int *)&param->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 */
1697 return true;
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");
1704 return false;
1706 return true;
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,
1712 Variant& ret,
1713 PDOFetchType how,
1714 PDOFetchOrientation ori,
1715 long offset,
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)) {
1724 return false;
1727 if (how == PDO_FETCH_BOUND) {
1728 ret = true;
1729 return true;
1732 int colno;
1733 if ((flags & PDO_FETCH_GROUP) && stmt->fetch.column == -1) {
1734 colno = 1;
1735 } else {
1736 colno = stmt->fetch.column;
1739 if (how == PDO_FETCH_LAZY) {
1740 get_lazy_object(stmt, ret);
1741 return true;
1744 String clsname, old_clsname;
1745 Variant old_ctor_args;
1746 ret = false;
1747 int i = 0;
1748 switch (how) {
1749 case PDO_FETCH_USE_DEFAULT:
1750 case PDO_FETCH_ASSOC:
1751 case PDO_FETCH_BOTH:
1752 case PDO_FETCH_NUM:
1753 case PDO_FETCH_NAMED:
1754 ret = Array::CreateDict();
1755 break;
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.");
1762 return false;
1764 if (!return_all) {
1765 ret = Array::CreateDict();
1767 break;
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);
1775 } else {
1776 fetch_value(stmt, ret, colno, NULL);
1778 if (!return_all) {
1779 return true;
1781 break;
1782 } else {
1783 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Invalid column index");
1785 return false;
1787 case PDO_FETCH_OBJ:
1788 ret = SystemLib::AllocStdClassObject();
1789 break;
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;
1796 Variant val;
1797 fetch_value(stmt, val, i++, NULL);
1798 if (!val.isNull()) {
1799 if (!HHVM_FN(class_exists)(val.toString())) {
1800 stmt->fetch.clsname = "stdClass";
1801 } else {
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");
1812 return false;
1814 if ((flags & PDO_FETCH_SERIALIZE) == 0) {
1815 ret = create_object_only(clsname);
1816 if (!do_fetch_class_prepare(stmt)) {
1817 return false;
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());
1825 break;
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.");
1831 return false;
1834 ret = stmt->fetch.into;
1835 if (ret.isObject() &&
1836 ret.getObjectData()->instanceof(SystemLib::getstdClassClass())) {
1837 how = PDO_FETCH_OBJ;
1839 break;
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");
1845 return false;
1847 if (!do_fetch_func_prepare(stmt)) {
1848 return false;
1850 break;
1852 default:
1853 assertx(false);
1854 return false;
1857 Variant grp_val;
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);
1862 } else {
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 */
1868 } else {
1869 i++;
1873 for (int idx = 0; i < stmt->column_count; i++, idx++) {
1874 const String& name = cast<PDOColumn>(stmt->columns[i])->name;
1875 Variant val;
1876 fetch_value(stmt, val, i, NULL);
1878 switch (how) {
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());
1883 break;
1885 case PDO_FETCH_KEY_PAIR: {
1886 Variant tmp;
1887 fetch_value(stmt, tmp, ++i, NULL);
1888 if (return_all) {
1889 auto const val_key_ret =
1890 return_all->asArrRef().convertKey<IntishCast::Cast>(val);
1891 return_all->asArrRef().set(val_key_ret, *tmp.asTypedValue());
1892 } else {
1893 auto const val_key =
1894 ret.asArrRef().convertKey<IntishCast::Cast>(val);
1895 ret.asArrRef().set(val_key, *tmp.asTypedValue());
1897 return true;
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);
1905 break;
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? */
1912 forceToDict(ret);
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());
1918 arr.append(val);
1919 ret.toArray().set(name_key, make_array_like_tv(arr.get()));
1920 } else {
1921 asArrRef(curr_val).append(val);
1923 } else {
1924 ret.asArrRef().set(name_key, *val.asTypedValue());
1926 break;
1928 case PDO_FETCH_NUM:
1929 ret.asArrRef().append(val);
1930 break;
1932 case PDO_FETCH_OBJ:
1933 case PDO_FETCH_INTO:
1934 ret.toObject()->o_set(name, val);
1935 break;
1937 case PDO_FETCH_CLASS:
1938 if ((flags & PDO_FETCH_SERIALIZE) == 0 || idx) {
1939 ret.toObject()->o_set(name, val);
1940 } else {
1941 #ifdef MBO_0
1942 ret = unserialize_from_string(val);
1943 if (same(ret, false)) {
1944 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
1945 "cannot unserialize data");
1946 return false;
1948 #endif
1949 // hzhao: not sure how we support class serialization
1950 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
1951 "cannot unserialize class");
1952 return false;
1954 break;
1956 case PDO_FETCH_FUNC:
1957 forceToDict(stmt->fetch.values).set(idx, val);
1958 break;
1960 default:
1961 pdo_raise_impl_error(stmt->dbh, stmt, "22003", "mode is out of range");
1962 return false;
1966 switch (how) {
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;
1977 break;
1979 case PDO_FETCH_FUNC:
1980 ret = vm_call_user_func(stmt->fetch.func,
1981 stmt->fetch.values.toArray());
1982 break;
1984 default:
1985 break;
1988 if (return_all) {
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());
1993 } else {
1994 auto const lval = return_all->asArrRef().lval(grp_key);
1995 forceToArray(lval).append(ret);
1999 return true;
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) {
2008 return false;
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
2015 p->stmt = NULL;
2017 if (paramno.isNumeric()) {
2018 p->paramno = paramno.toInt64();
2019 } else {
2020 p->paramno = -1;
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");
2032 return false;
2035 if (!really_register_bound_param(p, stmt)) {
2036 p->parameter.unset();
2037 return false;
2039 return true;
2042 static bool generic_stmt_attr_get(sp_PDOStatement stmt, Variant &ret,
2043 long attr) {
2044 if (attr == PDO_ATTR_EMULATE_PREPARES) {
2045 ret = (bool)(stmt->supports_placeholders == PDO_PLACEHOLDER_NONE);
2046 return true;
2048 return false;
2051 ///////////////////////////////////////////////////////////////////////////////
2052 // SQL parser
2054 namespace {
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;
2072 } Scanner;
2074 static int scan(Scanner *s) {
2075 char* cursor = s->cur;
2076 char* limit = s->lim;
2077 s->tok = cursor;
2080 YYCTYPE yych;
2082 if ((YYLIMIT - YYCURSOR) < 2) { YYFILL(2); }
2083 yych = *YYCURSOR;
2084 switch (yych) {
2085 case 0x00: goto yy11;
2086 case '"': goto yy2;
2087 case '\'': goto yy4;
2088 case ':': goto yy5;
2089 case '?': goto yy6;
2090 default: goto yy8;
2092 yy2:
2093 yych = *(YYMARKER = ++YYCURSOR);
2094 if (yych >= 0x01) goto yy26;
2095 yy3:
2096 { SKIP_ONE(PDO_PARSER_TEXT); }
2097 yy4:
2098 yych = *(YYMARKER = ++YYCURSOR);
2099 if (yych <= 0x00) goto yy3;
2100 goto yy20;
2101 yy5:
2102 yych = *++YYCURSOR;
2103 switch (yych) {
2104 case '0':
2105 case '1':
2106 case '2':
2107 case '3':
2108 case '4':
2109 case '5':
2110 case '6':
2111 case '7':
2112 case '8':
2113 case '9':
2114 case 'A':
2115 case 'B':
2116 case 'C':
2117 case 'D':
2118 case 'E':
2119 case 'F':
2120 case 'G':
2121 case 'H':
2122 case 'I':
2123 case 'J':
2124 case 'K':
2125 case 'L':
2126 case 'M':
2127 case 'N':
2128 case 'O':
2129 case 'P':
2130 case 'Q':
2131 case 'R':
2132 case 'S':
2133 case 'T':
2134 case 'U':
2135 case 'V':
2136 case 'W':
2137 case 'X':
2138 case 'Y':
2139 case 'Z':
2140 case '_':
2141 case 'a':
2142 case 'b':
2143 case 'c':
2144 case 'd':
2145 case 'e':
2146 case 'f':
2147 case 'g':
2148 case 'h':
2149 case 'i':
2150 case 'j':
2151 case 'k':
2152 case 'l':
2153 case 'm':
2154 case 'n':
2155 case 'o':
2156 case 'p':
2157 case 'q':
2158 case 'r':
2159 case 's':
2160 case 't':
2161 case 'u':
2162 case 'v':
2163 case 'w':
2164 case 'x':
2165 case 'y':
2166 case 'z': goto yy16;
2167 case ':':
2168 case '?': goto yy13;
2169 default: goto yy3;
2171 yy6:
2172 ++YYCURSOR;
2173 switch ((yych = *YYCURSOR)) {
2174 case ':':
2175 case '?': goto yy13;
2176 default: goto yy7;
2178 yy7:
2179 { RET(PDO_PARSER_BIND_POS); }
2180 yy8:
2181 ++YYCURSOR;
2182 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2183 yych = *YYCURSOR;
2184 switch (yych) {
2185 case 0x00:
2186 case '"':
2187 case '\'':
2188 case ':':
2189 case '?': goto yy10;
2190 default: goto yy8;
2192 yy10:
2193 { RET(PDO_PARSER_TEXT); }
2194 yy11:
2195 ++YYCURSOR;
2196 { RET(PDO_PARSER_EOI); }
2197 yy13:
2198 ++YYCURSOR;
2199 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2200 yych = *YYCURSOR;
2201 switch (yych) {
2202 case ':':
2203 case '?': goto yy13;
2204 default: goto yy15;
2206 yy15:
2207 { RET(PDO_PARSER_TEXT); }
2208 yy16:
2209 ++YYCURSOR;
2210 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2211 yych = *YYCURSOR;
2212 switch (yych) {
2213 case '0':
2214 case '1':
2215 case '2':
2216 case '3':
2217 case '4':
2218 case '5':
2219 case '6':
2220 case '7':
2221 case '8':
2222 case '9':
2223 case 'A':
2224 case 'B':
2225 case 'C':
2226 case 'D':
2227 case 'E':
2228 case 'F':
2229 case 'G':
2230 case 'H':
2231 case 'I':
2232 case 'J':
2233 case 'K':
2234 case 'L':
2235 case 'M':
2236 case 'N':
2237 case 'O':
2238 case 'P':
2239 case 'Q':
2240 case 'R':
2241 case 'S':
2242 case 'T':
2243 case 'U':
2244 case 'V':
2245 case 'W':
2246 case 'X':
2247 case 'Y':
2248 case 'Z':
2249 case '_':
2250 case 'a':
2251 case 'b':
2252 case 'c':
2253 case 'd':
2254 case 'e':
2255 case 'f':
2256 case 'g':
2257 case 'h':
2258 case 'i':
2259 case 'j':
2260 case 'k':
2261 case 'l':
2262 case 'm':
2263 case 'n':
2264 case 'o':
2265 case 'p':
2266 case 'q':
2267 case 'r':
2268 case 's':
2269 case 't':
2270 case 'u':
2271 case 'v':
2272 case 'w':
2273 case 'x':
2274 case 'y':
2275 case 'z': goto yy16;
2276 default: goto yy18;
2278 yy18:
2279 { RET(PDO_PARSER_BIND); }
2280 yy19:
2281 ++YYCURSOR;
2282 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2283 yych = *YYCURSOR;
2284 yy20:
2285 switch (yych) {
2286 case 0x00: goto yy21;
2287 case '\'': goto yy23;
2288 case '\\': goto yy22;
2289 default: goto yy19;
2291 yy21:
2292 YYCURSOR = YYMARKER;
2293 goto yy3;
2294 yy22:
2295 ++YYCURSOR;
2296 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2297 yych = *YYCURSOR;
2298 if (yych <= 0x00) goto yy21;
2299 goto yy19;
2300 yy23:
2301 ++YYCURSOR;
2302 { RET(PDO_PARSER_TEXT); }
2303 yy25:
2304 ++YYCURSOR;
2305 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2306 yych = *YYCURSOR;
2307 yy26:
2308 switch (yych) {
2309 case 0x00: goto yy21;
2310 case '"': goto yy28;
2311 case '\\': goto yy27;
2312 default: goto yy25;
2314 yy27:
2315 ++YYCURSOR;
2316 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2317 yych = *YYCURSOR;
2318 if (yych <= 0x00) goto yy21;
2319 goto yy25;
2320 yy28:
2321 ++YYCURSOR;
2322 { RET(PDO_PARSER_TEXT); }
2329 struct placeholder {
2330 char *pos;
2331 int len;
2332 int bindno;
2333 String quoted; /* quoted value */
2334 struct placeholder *next;
2337 int pdo_parse_params(sp_PDOStatement stmt, const String& in, String &out) {
2338 Scanner s;
2339 const char *ptr;
2340 char *newbuffer;
2341 int t;
2342 int bindno = 0;
2343 int ret = 0;
2344 int newbuffer_len;
2345 Array params;
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))) {
2359 continue;
2361 query_type |= PDO_PLACEHOLDER_NAMED;
2362 } else {
2363 query_type |= PDO_PLACEHOLDER_POSITIONAL;
2366 plc = req::make_raw<placeholder>();
2367 memset(plc, 0, sizeof(*plc));
2368 plc->next = NULL;
2369 plc->pos = s.tok;
2370 plc->len = s.cur - s.tok;
2371 plc->bindno = bindno++;
2373 if (placetail) {
2374 placetail->next = plc;
2375 } else {
2376 placeholders = plc;
2378 placetail = plc;
2382 if (bindno == 0) {
2383 /* nothing to do; good! */
2384 return 0;
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");
2392 ret = -1;
2393 goto clean_up;
2396 if ((int)stmt->supports_placeholders == query_type &&
2397 !stmt->named_rewrite_template) {
2398 /* query matches native syntax */
2399 ret = 0;
2400 goto clean_up;
2403 if (stmt->named_rewrite_template) {
2404 /* magic/hack.
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,
2408 * but it works. */
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");
2418 ret = -1;
2419 goto clean_up;
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
2425 more then once */
2426 if (query_type != PDO_PLACEHOLDER_POSITIONAL && bindno > params.size()) {
2427 int ok = 1;
2428 for (plc = placeholders; plc; plc = plc->next) {
2429 if (!params.exists(String(plc->pos, plc->len, CopyString))) {
2430 ok = 0;
2431 break;
2434 if (ok) {
2435 goto safe;
2438 pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
2439 "number of bound variables does not match number "
2440 "of tokens");
2441 ret = -1;
2442 goto clean_up;
2444 safe:
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) {
2453 Variant vparam;
2454 if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
2455 vparam = params[plc->bindno];
2456 } else {
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 */
2463 ret = -1;
2464 pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
2465 "parameter was not defined");
2466 goto clean_up;
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)) {
2477 /* bork */
2478 ret = -1;
2479 setPDOError(stmt->error_code, stmt->dbh->conn()->error_code);
2480 goto clean_up;
2482 } else {
2483 pdo_raise_impl_error(stmt->dbh, stmt, "HY105",
2484 "Expected a stream resource");
2485 ret = -1;
2486 goto clean_up;
2488 } else {
2489 do {
2490 switch (param->parameter.getType()) {
2491 case KindOfUninit:
2492 case KindOfNull:
2493 plc->quoted = "NULL";
2494 continue;
2496 case KindOfInt64:
2497 case KindOfDouble:
2498 plc->quoted = param->parameter.toString();
2499 continue;
2501 case KindOfBoolean:
2502 param->parameter = param->parameter.toInt64();
2503 [[fallthrough]];
2504 case KindOfPersistentString:
2505 case KindOfString:
2506 case KindOfPersistentVec:
2507 case KindOfVec:
2508 case KindOfPersistentDict:
2509 case KindOfDict:
2510 case KindOfPersistentKeyset:
2511 case KindOfKeyset:
2512 case KindOfObject:
2513 case KindOfResource:
2514 case KindOfRFunc:
2515 case KindOfFunc:
2516 case KindOfClass:
2517 case KindOfLazyClass:
2518 case KindOfClsMeth:
2519 case KindOfRClsMeth:
2520 case KindOfEnumClassLabel:
2521 if (!stmt->dbh->conn()->quoter(
2522 param->parameter.toString(),
2523 plc->quoted,
2524 param->param_type)) {
2525 /* bork */
2526 ret = -1;
2527 setPDOError(stmt->error_code, stmt->dbh->conn()->error_code);
2528 goto clean_up;
2530 continue;
2532 not_reached();
2533 } while (0);
2535 } else {
2536 plc->quoted = param->parameter.toString();
2538 newbuffer_len += plc->quoted.size();
2541 rewrite:
2542 /* allocate output buffer */
2543 out = String(newbuffer_len, ReserveString);
2544 newbuffer = out.mutableData();
2546 /* and build the query */
2547 plc = placeholders;
2548 ptr = in.data();
2550 do {
2551 t = plc->pos - ptr;
2552 if (t) {
2553 memcpy(newbuffer, ptr, t);
2554 newbuffer += t;
2556 memcpy(newbuffer, plc->quoted.data(), plc->quoted.size());
2557 newbuffer += plc->quoted.size();
2558 ptr = plc->pos + plc->len;
2560 plc = plc->next;
2561 } while (plc);
2563 t = (in.data() + in.size()) - ptr;
2564 if (t) {
2565 memcpy(newbuffer, ptr, t);
2566 newbuffer += t;
2568 out.setSize(newbuffer - out.data());
2570 ret = 1;
2571 goto clean_up;
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";
2578 int bind_no = 1;
2580 newbuffer_len = in.size();
2582 for (plc = placeholders; plc; plc = plc->next) {
2583 int skip_map = 0;
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++);
2592 } else {
2593 idxbuf.clear();
2594 idxbuf.append(stmt->bound_param_map[name_key].toString());
2595 skip_map = 1;
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);
2611 goto rewrite;
2613 } else {
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);
2621 plc->quoted = "?";
2624 goto rewrite;
2627 clean_up:
2629 while (placeholders) {
2630 plc = placeholders;
2631 placeholders = plc->next;
2632 plc->quoted.reset();
2633 req::free(plc);
2636 return ret;
2639 ///////////////////////////////////////////////////////////////////////////////
2640 // PDOStatement
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();
2668 param->stmt = NULL;
2670 if (iter.first().isString()) {
2671 param->name = iter.first().toString();
2672 param->paramno = -1;
2673 } else {
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,
2678 "HY093", nullptr);
2679 return false;
2681 param->paramno = num_index;
2684 if (!really_register_bound_param(param, data->m_stmt)) {
2685 return false;
2690 int ret = 1;
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;
2698 ret = 1;
2699 } else if (ret == -1) {
2700 /* something broke */
2701 PDO_HANDLE_STMT_ERR(data->m_stmt);
2702 return false;
2704 } else if (!dispatch_param_event(data->m_stmt, PDO_PARAM_EVT_EXEC_PRE)) {
2705 PDO_HANDLE_STMT_ERR(data->m_stmt);
2706 return false;
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)) {
2724 return false;
2727 return (bool)ret;
2729 data->m_stmt->active_query_string.reset();
2730 PDO_HANDLE_STMT_ERR(data->m_stmt);
2731 return false;
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) {
2742 return false;
2745 setPDOErrorNone(data->m_stmt->error_code);
2746 if (!pdo_stmt_verify_mode(data->m_stmt, how, false)) {
2747 return false;
2750 Variant ret;
2751 if (!do_fetch(data->m_stmt, ret, (PDOFetchType)how,
2752 (PDOFetchOrientation)orientation, offset, NULL)) {
2753 PDO_HANDLE_STMT_ERR(data->m_stmt);
2754 return false;
2756 return ret;
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) {
2764 return false;
2767 setPDOErrorNone(data->m_stmt->error_code);
2768 if (!pdo_stmt_verify_mode(data->m_stmt, PDO_FETCH_CLASS, false)) {
2769 return false;
2772 String old_clsname = data->m_stmt->fetch.clsname;
2773 Variant old_ctor_args = data->m_stmt->fetch.ctor_args;
2774 bool error = false;
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");
2783 error = true;
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");
2788 error = true;
2790 data->m_stmt->fetch.ctor_args = ctor_args;
2792 Variant ret;
2793 if (!error && !do_fetch(data->m_stmt, ret, PDO_FETCH_CLASS,
2794 PDO_FETCH_ORI_NEXT, 0, NULL)) {
2795 error = true;
2797 if (error) {
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;
2803 if (error) {
2804 return false;
2806 return ret;
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) {
2813 return false;
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);
2819 return false;
2821 Variant ret;
2822 fetch_value(data->m_stmt, ret, column_numner, nullptr);
2823 return ret;
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) {
2831 return false;
2834 if (!pdo_stmt_verify_mode(self->m_stmt, how, true)) {
2835 return false;
2838 String old_clsname = self->m_stmt->fetch.clsname;
2839 Variant old_ctor_args = self->m_stmt->fetch.ctor_args;
2840 int error = 0;
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");
2851 error = 1;
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");
2856 error = 1;
2857 break;
2859 self->m_stmt->fetch.ctor_args = ctor_args;
2861 if (!error) {
2862 do_fetch_class_prepare(self->m_stmt);
2864 break;
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");
2870 error = 1;
2871 } else {
2872 self->m_stmt->fetch.func = class_name.toString();
2873 do_fetch_func_prepare(self->m_stmt);
2875 break;
2877 case PDO_FETCH_COLUMN:
2878 if (class_name.isNull()) {
2879 self->m_stmt->fetch.column = how & PDO_FETCH_GROUP ? -1 : 0;
2880 } else {
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");
2887 error = 1;
2889 break;
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;
2901 Variant data;
2902 if (!error) {
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)) {
2913 error = 2;
2916 if (!error) {
2917 if ((how & PDO_FETCH_GROUP)) {
2918 do {
2919 data.unset();
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)) {
2927 continue;
2929 } else {
2930 return_value = Array::CreateVec();
2931 do {
2932 return_value.asArrRef().append(data);
2933 data.unset();
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;
2942 if (error) {
2943 PDO_HANDLE_STMT_ERR(self->m_stmt);
2944 if (error != 2) {
2945 return false;
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) {
2959 return 0;
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) {
2968 return false;
2970 if (data->m_stmt->error_code[0] == '\0') {
2971 return init_null();
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) {
2979 return null_array;
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());
2997 return ret;
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) {
3004 return false;
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");
3010 return false;
3013 setPDOErrorNone(data->m_stmt->error_code);
3014 if (data->m_stmt->setAttribute(attribute, value)) {
3015 return true;
3017 PDO_HANDLE_STMT_ERR(data->m_stmt);
3018 return false;
3021 static Variant HHVM_METHOD(PDOStatement, getAttribute, int64_t attribute) {
3022 auto data = Native::data<PDOStatementData>(this_);
3023 if (data->m_stmt == nullptr) {
3024 return false;
3027 Variant ret;
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");
3032 return false;
3034 return ret;
3037 setPDOErrorNone(data->m_stmt->error_code);
3038 switch (data->m_stmt->getAttribute(attribute, ret)) {
3039 case -1:
3040 PDO_HANDLE_STMT_ERR(data->m_stmt);
3041 return false;
3042 case 0:
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");
3047 return false;
3049 break;
3050 default:
3051 break;
3053 return ret;
3056 static int64_t HHVM_METHOD(PDOStatement, columnCount) {
3057 auto data = Native::data<PDOStatementData>(this_);
3058 if (data->m_stmt == nullptr) {
3059 return 0;
3062 return data->m_stmt->column_count;
3065 const StaticString
3066 s_name("name"),
3067 s_len("len"),
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) {
3074 return false;
3077 if (column < 0) {
3078 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt, "42P10",
3079 "column number must be non-negative");
3080 return false;
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");
3086 return false;
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);
3093 return false;
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);
3105 return ret;
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) {
3112 return false;
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) {
3122 return false;
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");
3128 return false;
3131 setPDOErrorNone(data->m_stmt->error_code);
3133 /* un-describe */
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);
3141 return false;
3144 pdo_stmt_describe_columns(data->m_stmt);
3145 return true;
3148 static bool HHVM_METHOD(PDOStatement, closeCursor) {
3149 auto data = Native::data<PDOStatementData>(this_);
3150 if (data->m_stmt == nullptr) {
3151 return false;
3154 if (!data->m_stmt->support(PDOStatement::MethodCursorCloser)) {
3155 /* emulate it by fetching and discarding rows */
3156 do {
3157 while (data->m_stmt->fetcher(PDO_FETCH_ORI_NEXT, 0));
3158 // if (!data->t_nextrowset()) {
3159 if (HHVM_MN(PDOStatement, nextRowset)(this_)) {
3160 break;
3162 } while (true);
3163 data->m_stmt->executed = 0;
3164 return true;
3167 setPDOErrorNone(data->m_stmt->error_code);
3168 if (!data->m_stmt->cursorCloser()) {
3169 PDO_HANDLE_STMT_ERR(data->m_stmt);
3170 return false;
3172 data->m_stmt->executed = 0;
3173 return true;
3176 static Variant HHVM_METHOD(PDOStatement, debugDumpParams) {
3177 auto data = Native::data<PDOStatementData>(this_);
3178 if (data->m_stmt == nullptr) {
3179 return false;
3182 auto f = File::Open("php://output", "w");
3183 if (!f || f->isInvalid()) {
3184 return false;
3187 f->printf(
3188 "SQL: [%d] %.*s\n",
3189 make_vec_array(
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();
3201 f->printf(
3202 "Key: Name: [%d] %.*s\n",
3203 make_vec_array(key.size(), key.size(), key.data())
3205 } else {
3206 f->printf("Key: Position #%ld:\n",
3207 make_vec_array(iter.first().toInt64()));
3210 auto param = cast<PDOBoundParam>(iter.second());
3211 f->printf(
3212 "paramno=%d\nname=[%d] \"%.*s\"\nparam_type=%d\n",
3213 make_vec_array(
3214 param->paramno,
3215 param->name.size(),
3216 param->name.size(),
3217 param->name.data(),
3218 param->param_type
3222 return true;
3225 static Variant HHVM_METHOD(PDOStatement, current) {
3226 auto data = Native::data<PDOStatementData>(this_);
3228 return data->m_row;
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;
3243 } else {
3244 ++data->m_rowIndex;
3246 return init_null();
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_);
3254 return init_null();
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");
3267 return init_null();
3270 static Variant HHVM_METHOD(PDOStatement, __sleep) {
3271 throw_pdo_exception(uninit_null(),
3272 "You cannot serialize or unserialize "
3273 "PDOStatement instances");
3274 return init_null();
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);
3291 #endif
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);
3303 HHVM_ME(PDO, exec);
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);
3435 #endif
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);
3443 } s_pdo_extension;
3445 //////////////////////////////////////////////////////////////////////////////