Don't return Variant& from Array functions
[hiphop-php.git] / hphp / runtime / ext / pdo / ext_pdo.cpp
blobdf5018718af702634ee1fc2a2363c57fa8d2ef65
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/string-vsnprintf.h"
26 #include "hphp/runtime/base/array-init.h"
27 #include "hphp/runtime/base/comparisons.h"
28 #include "hphp/runtime/base/file.h"
29 #include "hphp/runtime/base/ini-setting.h"
30 #include "hphp/runtime/base/request-local.h"
31 #include "hphp/runtime/base/string-buffer.h"
32 #include "hphp/runtime/base/tv-refcount.h"
33 #include "hphp/runtime/vm/jit/translator-inline.h"
35 #include "hphp/runtime/ext/extension.h"
36 #include "hphp/runtime/ext/array/ext_array.h"
37 #include "hphp/runtime/ext/pdo/pdo_driver.h"
38 #ifdef ENABLE_EXTENSION_PDO_MYSQL
39 #include "hphp/runtime/ext/pdo_mysql/pdo_mysql.h"
40 #endif
41 #ifdef ENABLE_EXTENSION_PDO_SQLITE
42 #include "hphp/runtime/ext/pdo_sqlite/pdo_sqlite.h"
43 #endif
44 #include "hphp/runtime/ext/std/ext_std_classobj.h"
45 #include "hphp/runtime/ext/std/ext_std_function.h"
46 #include "hphp/runtime/ext/stream/ext_stream.h"
47 #include "hphp/runtime/ext/string/ext_string.h"
49 #define PDO_HANDLE_DBH_ERR(dbh) \
50 if (strcmp(dbh->conn()->error_code, PDO_ERR_NONE)) { \
51 pdo_handle_error(dbh, nullptr); \
52 } \
54 #define PDO_HANDLE_STMT_ERR(stmt) \
55 if (strcmp(stmt->error_code, PDO_ERR_NONE)) { \
56 pdo_handle_error(stmt->dbh, stmt); \
57 } \
59 namespace HPHP {
61 struct PDOData {
62 sp_PDOResource m_dbh;
65 struct PDOStatementData {
66 PDOStatementData();
67 ~PDOStatementData();
69 sp_PDOStatement m_stmt;
70 Variant m_row;
71 int m_rowIndex;
74 using std::string;
75 ///////////////////////////////////////////////////////////////////////////////
76 // extension functions
78 Array HHVM_FUNCTION(pdo_drivers) {
79 Array ret = Array::Create();
80 const PDODriverMap &drivers = PDODriver::GetDrivers();
81 for (PDODriverMap::const_iterator iter = drivers.begin();
82 iter != drivers.end(); ++iter) {
83 ret.append(iter->second->getName());
85 return ret;
88 ///////////////////////////////////////////////////////////////////////////////
89 // error handling
91 struct pdo_sqlstate_info {
92 const char *state;
93 const char *desc;
96 static const struct pdo_sqlstate_info err_initializer[] = {
97 { "00000", "No error" },
98 { "01000", "Warning" },
99 { "01001", "Cursor operation conflict" },
100 { "01002", "Disconnect error" },
101 { "01003", "NULL value eliminated in set function" },
102 { "01004", "String data, right truncated" },
103 { "01006", "Privilege not revoked" },
104 { "01007", "Privilege not granted" },
105 { "01008", "Implicit zero bit padding" },
106 { "0100C", "Dynamic result sets returned" },
107 { "01P01", "Deprecated feature" },
108 { "01S00", "Invalid connection string attribute" },
109 { "01S01", "Error in row" },
110 { "01S02", "Option value changed" },
111 { "01S06",
112 "Attempt to fetch before the result set returned the first rowset" },
113 { "01S07", "Fractional truncation" },
114 { "01S08", "Error saving File DSN" },
115 { "01S09", "Invalid keyword" },
116 { "02000", "No data" },
117 { "02001", "No additional dynamic result sets returned" },
118 { "03000", "Sql statement not yet complete" },
119 { "07002", "COUNT field incorrect" },
120 { "07005", "Prepared statement not a cursor-specification" },
121 { "07006", "Restricted data type attribute violation" },
122 { "07009", "Invalid descriptor index" },
123 { "07S01", "Invalid use of default parameter" },
124 { "08000", "Connection exception" },
125 { "08001", "Client unable to establish connection" },
126 { "08002", "Connection name in use" },
127 { "08003", "Connection does not exist" },
128 { "08004", "Server rejected the connection" },
129 { "08006", "Connection failure" },
130 { "08007", "Connection failure during transaction" },
131 { "08S01", "Communication link failure" },
132 { "09000", "Triggered action exception" },
133 { "0A000", "Feature not supported" },
134 { "0B000", "Invalid transaction initiation" },
135 { "0F000", "Locator exception" },
136 { "0F001", "Invalid locator specification" },
137 { "0L000", "Invalid grantor" },
138 { "0LP01", "Invalid grant operation" },
139 { "0P000", "Invalid role specification" },
140 { "21000", "Cardinality violation" },
141 { "21S01", "Insert value list does not match column list" },
142 { "21S02", "Degree of derived table does not match column list" },
143 { "22000", "Data exception" },
144 { "22001", "String data, right truncated" },
145 { "22002", "Indicator variable required but not supplied" },
146 { "22003", "Numeric value out of range" },
147 { "22004", "Null value not allowed" },
148 { "22005", "Error in assignment" },
149 { "22007", "Invalid datetime format" },
150 { "22008", "Datetime field overflow" },
151 { "22009", "Invalid time zone displacement value" },
152 { "2200B", "Escape character conflict" },
153 { "2200C", "Invalid use of escape character" },
154 { "2200D", "Invalid escape octet" },
155 { "2200F", "Zero length character string" },
156 { "2200G", "Most specific type mismatch" },
157 { "22010", "Invalid indicator parameter value" },
158 { "22011", "Substring error" },
159 { "22012", "Division by zero" },
160 { "22015", "Interval field overflow" },
161 { "22018", "Invalid character value for cast specification" },
162 { "22019", "Invalid escape character" },
163 { "2201B", "Invalid regular expression" },
164 { "2201E", "Invalid argument for logarithm" },
165 { "2201F", "Invalid argument for power function" },
166 { "2201G", "Invalid argument for width bucket function" },
167 { "22020", "Invalid limit value" },
168 { "22021", "Character not in repertoire" },
169 { "22022", "Indicator overflow" },
170 { "22023", "Invalid parameter value" },
171 { "22024", "Unterminated c string" },
172 { "22025", "Invalid escape sequence" },
173 { "22026", "String data, length mismatch" },
174 { "22027", "Trim error" },
175 { "2202E", "Array subscript error" },
176 { "22P01", "Floating point exception" },
177 { "22P02", "Invalid text representation" },
178 { "22P03", "Invalid binary representation" },
179 { "22P04", "Bad copy file format" },
180 { "22P05", "Untranslatable character" },
181 { "23000", "Integrity constraint violation" },
182 { "23001", "Restrict violation" },
183 { "23502", "Not null violation" },
184 { "23503", "Foreign key violation" },
185 { "23505", "Unique violation" },
186 { "23514", "Check violation" },
187 { "24000", "Invalid cursor state" },
188 { "25000", "Invalid transaction state" },
189 { "25001", "Active sql transaction" },
190 { "25002", "Branch transaction already active" },
191 { "25003", "Inappropriate access mode for branch transaction" },
192 { "25004", "Inappropriate isolation level for branch transaction" },
193 { "25005", "No active sql transaction for branch transaction" },
194 { "25006", "Read only sql transaction" },
195 { "25007", "Schema and data statement mixing not supported" },
196 { "25008", "Held cursor requires same isolation level" },
197 { "25P01", "No active sql transaction" },
198 { "25P02", "In failed sql transaction" },
199 { "25S01", "Transaction state" },
200 { "25S02", "Transaction is still active" },
201 { "25S03", "Transaction is rolled back" },
202 { "26000", "Invalid sql statement name" },
203 { "27000", "Triggered data change violation" },
204 { "28000", "Invalid authorization specification" },
205 { "2B000", "Dependent privilege descriptors still exist" },
206 { "2BP01", "Dependent objects still exist" },
207 { "2D000", "Invalid transaction termination" },
208 { "2F000", "Sql routine exception" },
209 { "2F002", "Modifying sql data not permitted" },
210 { "2F003", "Prohibited sql statement attempted" },
211 { "2F004", "Reading sql data not permitted" },
212 { "2F005", "Function executed no return statement" },
213 { "34000", "Invalid cursor name" },
214 { "38000", "External routine exception" },
215 { "38001", "Containing sql not permitted" },
216 { "38002", "Modifying sql data not permitted" },
217 { "38003", "Prohibited sql statement attempted" },
218 { "38004", "Reading sql data not permitted" },
219 { "39000", "External routine invocation exception" },
220 { "39001", "Invalid sqlstate returned" },
221 { "39004", "Null value not allowed" },
222 { "39P01", "Trigger protocol violated" },
223 { "39P02", "Srf protocol violated" },
224 { "3B000", "Savepoint exception" },
225 { "3B001", "Invalid savepoint specification" },
226 { "3C000", "Duplicate cursor name" },
227 { "3D000", "Invalid catalog name" },
228 { "3F000", "Invalid schema name" },
229 { "40000", "Transaction rollback" },
230 { "40001", "Serialization failure" },
231 { "40002", "Transaction integrity constraint violation" },
232 { "40003", "Statement completion unknown" },
233 { "40P01", "Deadlock detected" },
234 { "42000", "Syntax error or access violation" },
235 { "42501", "Insufficient privilege" },
236 { "42601", "Syntax error" },
237 { "42602", "Invalid name" },
238 { "42611", "Invalid column definition" },
239 { "42622", "Name too long" },
240 { "42701", "Duplicate column" },
241 { "42702", "Ambiguous column" },
242 { "42703", "Undefined column" },
243 { "42704", "Undefined object" },
244 { "42710", "Duplicate object" },
245 { "42712", "Duplicate alias" },
246 { "42723", "Duplicate function" },
247 { "42725", "Ambiguous function" },
248 { "42803", "Grouping error" },
249 { "42804", "Datatype mismatch" },
250 { "42809", "Wrong object type" },
251 { "42830", "Invalid foreign key" },
252 { "42846", "Cannot coerce" },
253 { "42883", "Undefined function" },
254 { "42939", "Reserved name" },
255 { "42P01", "Undefined table" },
256 { "42P02", "Undefined parameter" },
257 { "42P03", "Duplicate cursor" },
258 { "42P04", "Duplicate database" },
259 { "42P05", "Duplicate prepared statement" },
260 { "42P06", "Duplicate schema" },
261 { "42P07", "Duplicate table" },
262 { "42P08", "Ambiguous parameter" },
263 { "42P09", "Ambiguous alias" },
264 { "42P10", "Invalid column reference" },
265 { "42P11", "Invalid cursor definition" },
266 { "42P12", "Invalid database definition" },
267 { "42P13", "Invalid function definition" },
268 { "42P14", "Invalid prepared statement definition" },
269 { "42P15", "Invalid schema definition" },
270 { "42P16", "Invalid table definition" },
271 { "42P17", "Invalid object definition" },
272 { "42P18", "Indeterminate datatype" },
273 { "42S01", "Base table or view already exists" },
274 { "42S02", "Base table or view not found" },
275 { "42S11", "Index already exists" },
276 { "42S12", "Index not found" },
277 { "42S21", "Column already exists" },
278 { "42S22", "Column not found" },
279 { "44000", "WITH CHECK OPTION violation" },
280 { "53000", "Insufficient resources" },
281 { "53100", "Disk full" },
282 { "53200", "Out of memory" },
283 { "53300", "Too many connections" },
284 { "54000", "Program limit exceeded" },
285 { "54001", "Statement too complex" },
286 { "54011", "Too many columns" },
287 { "54023", "Too many arguments" },
288 { "55000", "Object not in prerequisite state" },
289 { "55006", "Object in use" },
290 { "55P02", "Cant change runtime param" },
291 { "55P03", "Lock not available" },
292 { "57000", "Operator intervention" },
293 { "57014", "Query canceled" },
294 { "57P01", "Admin shutdown" },
295 { "57P02", "Crash shutdown" },
296 { "57P03", "Cannot connect now" },
297 { "58030", "Io error" },
298 { "58P01", "Undefined file" },
299 { "58P02", "Duplicate file" },
300 { "F0000", "Config file error" },
301 { "F0001", "Lock file exists" },
302 { "HY000", "General error" },
303 { "HY001", "Memory allocation error" },
304 { "HY003", "Invalid application buffer type" },
305 { "HY004", "Invalid SQL data type" },
306 { "HY007", "Associated statement is not prepared" },
307 { "HY008", "Operation canceled" },
308 { "HY009", "Invalid use of null pointer" },
309 { "HY010", "Function sequence error" },
310 { "HY011", "Attribute cannot be set now" },
311 { "HY012", "Invalid transaction operation code" },
312 { "HY013", "Memory management error" },
313 { "HY014", "Limit on the number of handles exceeded" },
314 { "HY015", "No cursor name available" },
315 { "HY016", "Cannot modify an implementation row descriptor" },
316 { "HY017", "Invalid use of an automatically allocated descriptor handle" },
317 { "HY018", "Server declined cancel request" },
318 { "HY019", "Non-character and non-binary data sent in pieces" },
319 { "HY020", "Attempt to concatenate a null value" },
320 { "HY021", "Inconsistent descriptor information" },
321 { "HY024", "Invalid attribute value" },
322 { "HY090", "Invalid string or buffer length" },
323 { "HY091", "Invalid descriptor field identifier" },
324 { "HY092", "Invalid attribute/option identifier" },
325 { "HY093", "Invalid parameter number" },
326 { "HY095", "Function type out of range" },
327 { "HY096", "Invalid information type" },
328 { "HY097", "Column type out of range" },
329 { "HY098", "Scope type out of range" },
330 { "HY099", "Nullable type out of range" },
331 { "HY100", "Uniqueness option type out of range" },
332 { "HY101", "Accuracy option type out of range" },
333 { "HY103", "Invalid retrieval code" },
334 { "HY104", "Invalid precision or scale value" },
335 { "HY105", "Invalid parameter type" },
336 { "HY106", "Fetch type out of range" },
337 { "HY107", "Row value out of range" },
338 { "HY109", "Invalid cursor position" },
339 { "HY110", "Invalid driver completion" },
340 { "HY111", "Invalid bookmark value" },
341 { "HYC00", "Optional feature not implemented" },
342 { "HYT00", "Timeout expired" },
343 { "HYT01", "Connection timeout expired" },
344 { "IM001", "Driver does not support this function" },
345 { "IM002", "Data source name not found and no default driver specified" },
346 { "IM003", "Specified driver could not be loaded" },
347 { "IM004", "Driver's SQLAllocHandle on SQL_HANDLE_ENV failed" },
348 { "IM005", "Driver's SQLAllocHandle on SQL_HANDLE_DBC failed" },
349 { "IM006", "Driver's SQLSetConnectAttr failed" },
350 { "IM007", "No data source or driver specified; dialog prohibited" },
351 { "IM008", "Dialog failed" },
352 { "IM009", "Unable to load translation DLL" },
353 { "IM010", "Data source name too long" },
354 { "IM011", "Driver name too long" },
355 { "IM012", "DRIVER keyword syntax error" },
356 { "IM013", "Trace file error" },
357 { "IM014", "Invalid name of File DSN" },
358 { "IM015", "Corrupt file data source" },
359 { "P0000", "Plpgsql error" },
360 { "P0001", "Raise exception" },
361 { "XX000", "Internal error" },
362 { "XX001", "Data corrupted" },
363 { "XX002", "Index corrupted" }
366 struct PDOErrorHash : private hphp_const_char_map<const char *> {
367 PDOErrorHash() {
368 for (unsigned int i = 0;
369 i < sizeof(err_initializer)/sizeof(err_initializer[0]); i++) {
370 const struct pdo_sqlstate_info *info = &err_initializer[i];
371 (*this)[info->state] = info->desc;
375 const char *description(const char *state) {
376 const_iterator iter = find(state);
377 if (iter != end()) {
378 return iter->second;
380 return "<<Unknown error>>";
383 static PDOErrorHash s_err_hash;
385 const StaticString
386 s_code("code"),
387 s_message("message"),
388 s_errorInfo("errorInfo"),
389 s_PDOException("PDOException");
391 void throw_pdo_exception(const Variant& code, const Variant& info,
392 const char *fmt, ...) {
393 auto obj = SystemLib::AllocPDOExceptionObject();
394 obj->o_set(s_code, code, s_PDOException);
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 Array info;
429 info.append(String(*pdo_err, CopyString));
430 info.append(0LL);
431 throw_pdo_exception(String(sqlstate, CopyString), info, "%s", err.c_str());
435 void pdo_raise_impl_error(sp_PDOResource rsrc, sp_PDOStatement stmt,
436 const char *sqlstate, const char *supp) {
437 pdo_raise_impl_error(rsrc, stmt.get(), sqlstate, supp);
440 namespace {
442 void pdo_handle_error(sp_PDOResource rsrc, PDOStatement* stmt) {
443 auto const& dbh = rsrc->conn();
445 if (dbh->error_mode == PDO_ERRMODE_SILENT) {
446 return;
448 PDOErrorType *pdo_err = &dbh->error_code;
449 if (stmt) {
450 pdo_err = &stmt->error_code;
453 /* hash sqlstate to error messages */
454 const char *msg = s_err_hash.description(*pdo_err);
456 int64_t native_code = 0;
457 String supp;
458 Array info;
459 if (dbh->support(PDOConnection::MethodFetchErr)) {
460 info = Array::Create();
461 info.append(String(*pdo_err, CopyString));
462 if (dbh->fetchErr(stmt, info)) {
463 if (info.exists(1)) {
464 native_code = info[1].toInt64();
466 if (info.exists(2)) {
467 supp = info[2].toString();
472 string err = "SQLSTATE["; err += *pdo_err; err += "]: "; err += msg;
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(String(*pdo_err, CopyString), info, "%s", err.c_str());
485 void pdo_handle_error(sp_PDOResource rsrc, sp_PDOStatement stmt) {
486 pdo_handle_error(rsrc, stmt.get());
491 ///////////////////////////////////////////////////////////////////////////////
492 // helpers for PDO class
494 static inline int64_t pdo_attr_lval(const Array& options, PDOAttributeType name,
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 = Unit::loadClass(name.get());
514 if (!cls) {
515 return Object();
517 return Object{cls};
520 const StaticString s_queryString("queryString");
522 static void pdo_stmt_construct(sp_PDOStatement stmt, Object object,
523 const String& clsname,
524 const Variant& ctor_args) {
525 object->setProp(nullptr, s_queryString.get(),
526 make_tv<KindOfString>(stmt->query_string.get()));
527 if (clsname.empty()) {
528 return;
530 Class* cls = Unit::loadClass(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::Unit::loadClass(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 if (stmt->bound_columns.exists(column->name)) {
614 auto param = cast<PDOBoundParam>(stmt->bound_columns[column->name]);
615 param->paramno = col;
618 return true;
621 static bool pdo_stmt_verify_mode(sp_PDOStatement stmt, int64_t mode,
622 bool fetch_all) {
623 int flags = mode & PDO_FETCH_FLAGS;
624 mode = mode & ~PDO_FETCH_FLAGS;
626 if (mode < 0 || mode > PDO_FETCH__MAX) {
627 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "invalid fetch mode");
628 return false;
631 if (mode == PDO_FETCH_USE_DEFAULT) {
632 flags = stmt->default_fetch_type & PDO_FETCH_FLAGS;
633 mode = stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
636 switch (mode) {
637 case PDO_FETCH_FUNC:
638 if (!fetch_all) {
639 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
640 "PDO::FETCH_FUNC is only allowed in "
641 "PDOStatement::fetchAll()");
642 return false;
644 return true;
646 case PDO_FETCH_LAZY:
647 if (fetch_all) {
648 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
649 "PDO::FETCH_LAZY can't be used with "
650 "PDOStatement::fetchAll()");
651 return false;
654 default:
655 if ((flags & PDO_FETCH_SERIALIZE) == PDO_FETCH_SERIALIZE) {
656 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
657 "PDO::FETCH_SERIALIZE can only be used "
658 "together with PDO::FETCH_CLASS");
659 return false;
661 if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
662 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
663 "PDO::FETCH_CLASSTYPE can only be used "
664 "together with PDO::FETCH_CLASS");
665 return false;
667 if (mode >= PDO_FETCH__MAX) {
668 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "invalid fetch mode");
669 return false;
671 /* no break; */
673 case PDO_FETCH_CLASS:
674 break;
676 return true;
679 static bool do_fetch_class_prepare(sp_PDOStatement stmt) {
680 String clsname = stmt->fetch.clsname;
681 if (clsname.empty()) {
682 stmt->fetch.clsname = "stdclass";
684 stmt->fetch.constructor = empty_string(); //NULL;
685 HPHP::Class* cls = HPHP::Unit::loadClass(clsname.get());
686 if (cls) {
687 const HPHP::Func* method = cls->getDeclaredCtor();
688 if (method) {
689 stmt->fetch.constructor = method->nameStr();
690 return true;
693 if (!stmt->fetch.ctor_args.isNull()) {
694 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
695 "user-supplied class does not have a constructor, "
696 "use NULL for the ctor_params parameter, or simply "
697 "omit it");
698 return false;
700 return true; /* no ctor no args is also ok */
703 static bool pdo_stmt_set_fetch_mode(sp_PDOStatement stmt, int _argc,
704 int64_t mode, const Array& _argv) {
705 _argc = _argv.size() + 1;
707 if (stmt->default_fetch_type == PDO_FETCH_INTO) {
708 stmt->fetch.into.unset();
710 stmt->default_fetch_type = PDO_FETCH_BOTH;
712 if (!pdo_stmt_verify_mode(stmt, mode, false)) {
713 setPDOErrorNone(stmt->error_code);
714 return false;
717 int flags = mode & PDO_FETCH_FLAGS;
718 bool retval = false;
719 switch (mode & ~PDO_FETCH_FLAGS) {
720 case PDO_FETCH_USE_DEFAULT:
721 case PDO_FETCH_LAZY:
722 case PDO_FETCH_ASSOC:
723 case PDO_FETCH_NUM:
724 case PDO_FETCH_BOTH:
725 case PDO_FETCH_OBJ:
726 case PDO_FETCH_BOUND:
727 case PDO_FETCH_NAMED:
728 case PDO_FETCH_KEY_PAIR:
729 if (_argc != 1) {
730 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
731 "fetch mode doesn't allow any extra arguments");
732 } else {
733 retval = true;
735 break;
737 case PDO_FETCH_COLUMN:
738 if (_argc != 2) {
739 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
740 "fetch mode requires the colno argument");
741 } else if (!_argv[0].isInteger()) {
742 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
743 "colno must be an integer");
744 } else {
745 stmt->fetch.column = _argv[0].toInt64();
746 retval = true;
748 break;
750 case PDO_FETCH_CLASS:
751 /* Gets its class name from 1st column */
752 if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
753 if (_argc != 1) {
754 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
755 "fetch mode doesn't allow any extra arguments");
756 } else {
757 stmt->fetch.clsname.clear();
758 retval = true;
760 } else {
761 if (_argc < 2) {
762 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
763 "fetch mode requires the classname argument");
764 } else if (_argc > 3) {
765 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
766 "too many arguments");
767 } else if (!_argv[0].isString()) {
768 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
769 "classname must be a string");
770 } else {
771 retval = HHVM_FN(class_exists)(_argv[0].toString());
772 if (retval) {
773 stmt->fetch.clsname = _argv[0].toString();
778 if (retval) {
779 stmt->fetch.ctor_args.unset();
780 if (_argc == 3) {
781 if (!_argv[1].isNull() && !_argv[1].isArray()) {
782 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
783 "ctor_args must be either NULL or an array");
784 retval = false;
785 } else {
786 stmt->fetch.ctor_args = _argv[1];
790 if (retval) {
791 do_fetch_class_prepare(stmt);
795 break;
797 case PDO_FETCH_INTO:
798 if (_argc != 2) {
799 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
800 "fetch mode requires the object parameter");
801 } else if (!_argv[0].isObject()) {
802 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
803 "object must be an object");
804 } else {
805 retval = true;
808 if (retval) {
809 stmt->fetch.into = _argv[0];
811 break;
813 default:
814 pdo_raise_impl_error(stmt->dbh, stmt, "22003",
815 "Invalid fetch mode specified");
818 if (retval) {
819 stmt->default_fetch_type = (PDOFetchType)mode;
823 * PDO error (if any) has already been raised at this point.
825 * The error_code is cleared, otherwise the caller will read the
826 * last error message from the driver.
829 setPDOErrorNone(stmt->error_code);
830 return retval;
833 ///////////////////////////////////////////////////////////////////////////////
834 // forward declarations
836 static bool HHVM_METHOD(PDO, setattribute, int64_t attribute,
837 const Variant& value);
839 ///////////////////////////////////////////////////////////////////////////////
840 // PDO
842 namespace {
843 thread_local std::unordered_map<std::string,sp_PDOConnection> s_connections;
846 const StaticString s_PDO("PDO");
848 static void HHVM_METHOD(PDO, __construct, const String& dsn,
849 const String& username /* = null_string */,
850 const String& password /* = null_string */,
851 const Variant& optionsV /* = null_array */) {
852 auto data = Native::data<PDOData>(this_);
853 auto options = optionsV.isNull() ? null_array : optionsV.toArray();
855 String data_source = dsn;
857 /* parse the data source name */
858 const char *colon = strchr(data_source.data(), ':');
859 if (!colon) {
860 /* let's see if this string has a matching dsn in the php.ini */
861 String name = "pdo.dsn."; name += data_source;
862 String ini_dsn;
863 if (!IniSetting::Get(name, ini_dsn)) {
864 throw_pdo_exception(uninit_null(), uninit_null(),
865 "invalid data source name");
867 data_source = ini_dsn;
868 colon = strchr(data_source.data(), ':');
869 if (!colon) {
870 throw_pdo_exception(uninit_null(), uninit_null(),
871 "invalid data source name (via INI: %s)",
872 ini_dsn.data());
876 if (!strncmp(data_source.data(), "uri:", 4)) {
877 /* the specified URI holds connection details */
878 auto file = File::Open(data_source.substr(4), "rb");
879 if (!file || file->isInvalid()) {
880 throw_pdo_exception(uninit_null(), uninit_null(),
881 "invalid data source URI");
883 data_source = file->readLine(1024);
884 colon = strchr(data_source.data(), ':');
885 if (!colon) {
886 throw_pdo_exception(uninit_null(), uninit_null(),
887 "invalid data source name (via URI)");
891 const PDODriverMap &drivers = PDODriver::GetDrivers();
892 String name = data_source.substr(0, colon - data_source.data());
893 PDODriverMap::const_iterator iter = drivers.find(name.data());
894 if (iter == drivers.end()) {
895 /* NB: don't want to include the data_source in the error message as
896 * it might contain a password */
897 throw_pdo_exception(uninit_null(), uninit_null(), "could not find driver");
899 PDODriver *driver = iter->second;
901 /* is this supposed to be a persistent connection ? */
902 bool is_persistent = false;
903 bool call_factory = true;
904 std::string shashkey;
905 if (!options.empty()) {
906 StringBuffer hashkey;
907 if (options.exists(PDO_ATTR_PERSISTENT)) {
908 Variant v = options[PDO_ATTR_PERSISTENT];
909 String sv = v.toString();
910 if (v.isString() && !sv.isNumeric() && !sv.empty()) {
911 /* user specified key */
912 hashkey.printf("PDO:DBH:DSN=%s:%s:%s:%s",
913 data_source.data(), username.data(),
914 password.data(), sv.data());
915 is_persistent = true;
916 } else {
917 is_persistent = v.toInt64();
918 hashkey.printf("PDO:DBH:DSN=%s:%s:%s",
919 data_source.data(), username.data(),
920 password.data());
924 if (is_persistent) {
925 shashkey = hashkey.detach().toCppString();
927 /* let's see if we have one cached.... */
928 if (s_connections.count(shashkey)) {
929 auto const conn = s_connections[shashkey];
930 data->m_dbh = driver->createResource(conn);
932 /* is the connection still alive ? */
933 if (conn->support(PDOConnection::MethodCheckLiveness) &&
934 !conn->checkLiveness()) {
935 /* nope... need to kill it */
936 data->m_dbh = nullptr;
940 if (data->m_dbh) {
941 call_factory = false;
942 } else {
943 /* need a brand new pdbh */
944 data->m_dbh = driver->createResource(colon + 1, username,
945 password, options);
946 if (!data->m_dbh) {
947 throw_pdo_exception(uninit_null(), uninit_null(),
948 "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(), uninit_null(),
959 "unable to create a connection");
963 if (call_factory) {
964 data->m_dbh->conn()->default_fetch_type = PDO_FETCH_BOTH;
967 data->m_dbh->conn()->auto_commit =
968 pdo_attr_lval(options, PDO_ATTR_AUTOCOMMIT, 1);
970 if (!call_factory) {
971 /* we got a persistent guy from our cache */
972 for (ArrayIter iter(options); iter; ++iter) {
973 HHVM_MN(PDO, setattribute)(this_, iter.first().toInt64(),
974 iter.second());
976 } else if (data->m_dbh) {
977 if (is_persistent) {
978 assert(!shashkey.empty());
979 s_connections[shashkey] = data->m_dbh->conn();
982 data->m_dbh->conn()->driver = driver;
983 for (ArrayIter iter(options); iter; ++iter) {
984 HHVM_MN(PDO, setattribute)(this_, iter.first().toInt64(),
985 iter.second());
990 static Variant HHVM_METHOD(PDO, prepare, const String& statement,
991 const Array& options = null_array) {
992 auto data = Native::data<PDOData>(this_);
994 assert(data->m_dbh->conn()->driver);
995 setPDOErrorNone(data->m_dbh->conn()->error_code);
996 data->m_dbh->query_stmt = nullptr;
998 String clsname;
999 Variant ctor_args;
1000 if (options.exists(PDO_ATTR_STATEMENT_CLASS)) {
1001 Variant opt = options[PDO_ATTR_STATEMENT_CLASS];
1002 if (!valid_statement_class(data->m_dbh, opt, clsname, ctor_args)) {
1003 return false;
1005 } else {
1006 clsname = data->m_dbh->conn()->def_stmt_clsname;
1007 ctor_args = data->m_dbh->def_stmt_ctor_args;
1010 Object ret = pdo_stmt_instantiate(data->m_dbh, clsname, ctor_args);
1011 if (ret.isNull()) {
1012 pdo_raise_impl_error
1013 (data->m_dbh, nullptr, "HY000",
1014 "failed to instantiate user-supplied statement class");
1015 PDO_HANDLE_DBH_ERR(data->m_dbh);
1016 return false;
1018 PDOStatementData *pdostmt = Native::data<PDOStatementData>(ret);
1020 if (data->m_dbh->conn()->preparer(statement, &pdostmt->m_stmt, options)) {
1021 auto stmt = pdostmt->m_stmt;
1022 assert(stmt);
1024 /* unconditionally keep this for later reference */
1025 stmt->query_string = statement;
1026 stmt->default_fetch_type = data->m_dbh->conn()->default_fetch_type;
1027 stmt->dbh = data->m_dbh;
1029 pdo_stmt_construct(stmt, ret, clsname, ctor_args);
1030 return ret;
1033 PDO_HANDLE_DBH_ERR(data->m_dbh);
1034 return false;
1037 static bool HHVM_METHOD(PDO, begintransaction) {
1038 auto data = Native::data<PDOData>(this_);
1040 if (data->m_dbh->conn()->in_txn) {
1041 throw_pdo_exception(uninit_null(), uninit_null(),
1042 "There is already an active transaction");
1044 if (data->m_dbh->conn()->begin()) {
1045 data->m_dbh->conn()->in_txn = 1;
1046 return true;
1048 if (strcmp(data->m_dbh->conn()->error_code, PDO_ERR_NONE)) {
1049 pdo_handle_error(data->m_dbh, nullptr);
1051 return false;
1054 static bool HHVM_METHOD(PDO, commit) {
1055 auto data = Native::data<PDOData>(this_);
1057 assert(data->m_dbh->conn()->driver);
1058 if (!data->m_dbh->conn()->in_txn) {
1059 throw_pdo_exception(uninit_null(), uninit_null(),
1060 "There is no active transaction");
1062 if (data->m_dbh->conn()->commit()) {
1063 data->m_dbh->conn()->in_txn = 0;
1064 return true;
1066 PDO_HANDLE_DBH_ERR(data->m_dbh);
1067 return false;
1070 static bool HHVM_METHOD(PDO, intransaction) {
1071 auto data = Native::data<PDOData>(this_);
1073 assert(data->m_dbh->conn()->driver);
1074 return data->m_dbh->conn()->in_txn;
1077 static bool HHVM_METHOD(PDO, rollback) {
1078 auto data = Native::data<PDOData>(this_);
1080 assert(data->m_dbh->conn()->driver);
1081 if (!data->m_dbh->conn()->in_txn) {
1082 throw_pdo_exception(uninit_null(), uninit_null(),
1083 "There is no active transaction");
1085 if (data->m_dbh->conn()->rollback()) {
1086 data->m_dbh->conn()->in_txn = 0;
1087 return true;
1089 PDO_HANDLE_DBH_ERR(data->m_dbh);
1090 return false;
1093 static bool HHVM_METHOD(PDO, setattribute, int64_t attribute,
1094 const Variant& value) {
1095 auto data = Native::data<PDOData>(this_);
1097 assert(data->m_dbh->conn()->driver);
1099 #define PDO_LONG_PARAM_CHECK \
1100 if (!value.isInteger() && !value.isString() && !value.isBoolean()) { \
1101 pdo_raise_impl_error(data->m_dbh, nullptr, "HY000", \
1102 "attribute value must be an integer"); \
1103 PDO_HANDLE_DBH_ERR(data->m_dbh); \
1104 return false; \
1107 switch (attribute) {
1108 case PDO_ATTR_ERRMODE:
1109 PDO_LONG_PARAM_CHECK;
1110 switch (value.toInt64()) {
1111 case PDO_ERRMODE_SILENT:
1112 case PDO_ERRMODE_WARNING:
1113 case PDO_ERRMODE_EXCEPTION:
1114 data->m_dbh->conn()->error_mode = (PDOErrorMode)value.toInt64();
1115 return true;
1116 default:
1117 pdo_raise_impl_error(data->m_dbh, nullptr, "HY000", "invalid error mode");
1118 PDO_HANDLE_DBH_ERR(data->m_dbh);
1119 return false;
1121 return false;
1123 case PDO_ATTR_CASE:
1124 PDO_LONG_PARAM_CHECK;
1125 switch (value.toInt64()) {
1126 case PDO_CASE_NATURAL:
1127 case PDO_CASE_UPPER:
1128 case PDO_CASE_LOWER:
1129 data->m_dbh->conn()->desired_case = (PDOCaseConversion)value.toInt64();
1130 return true;
1131 default:
1132 pdo_raise_impl_error(data->m_dbh, nullptr, "HY000",
1133 "invalid case folding mode");
1134 PDO_HANDLE_DBH_ERR(data->m_dbh);
1135 return false;
1137 return false;
1139 case PDO_ATTR_ORACLE_NULLS:
1140 PDO_LONG_PARAM_CHECK;
1141 data->m_dbh->conn()->oracle_nulls = value.toInt64();
1142 return true;
1144 case PDO_ATTR_DEFAULT_FETCH_MODE:
1145 if (value.isArray()) {
1146 if (value.toCArrRef().exists(0)) {
1147 Variant tmp = value.toCArrRef()[0];
1148 if (tmp.isInteger() && ((tmp.toInt64() == PDO_FETCH_INTO ||
1149 tmp.toInt64() == PDO_FETCH_CLASS))) {
1150 pdo_raise_impl_error(data->m_dbh, nullptr, "HY000",
1151 "FETCH_INTO and FETCH_CLASS are not yet "
1152 "supported as default fetch modes");
1153 return false;
1156 } else {
1157 PDO_LONG_PARAM_CHECK;
1159 if (value.toInt64() == PDO_FETCH_USE_DEFAULT) {
1160 pdo_raise_impl_error(data->m_dbh, nullptr,
1161 "HY000", "invalid fetch mode type");
1162 return false;
1164 data->m_dbh->conn()->default_fetch_type = (PDOFetchType)value.toInt64();
1165 return true;
1167 case PDO_ATTR_STRINGIFY_FETCHES:
1168 PDO_LONG_PARAM_CHECK;
1169 data->m_dbh->conn()->stringify = value.toInt64() ? 1 : 0;
1170 return true;
1172 case PDO_ATTR_STATEMENT_CLASS:
1174 if (data->m_dbh->conn()->is_persistent) {
1175 pdo_raise_impl_error(data->m_dbh, nullptr, "HY000",
1176 "PDO::ATTR_STATEMENT_CLASS cannot be used "
1177 "with persistent PDO instances");
1178 PDO_HANDLE_DBH_ERR(data->m_dbh);
1179 return false;
1181 String clsname;
1182 if (!valid_statement_class(data->m_dbh, value, clsname,
1183 data->m_dbh->def_stmt_ctor_args)) {
1184 return false;
1186 data->m_dbh->conn()->def_stmt_clsname = clsname.c_str();
1187 return true;
1191 if (data->m_dbh->conn()->support(PDOConnection::MethodSetAttribute)) {
1192 setPDOErrorNone(data->m_dbh->conn()->error_code);
1193 data->m_dbh->query_stmt = nullptr;
1194 if (data->m_dbh->conn()->setAttribute(attribute, value)) {
1195 return true;
1199 if (attribute == PDO_ATTR_AUTOCOMMIT) {
1200 throw_pdo_exception(uninit_null(), uninit_null(),
1201 "The auto-commit mode cannot be changed for this "
1202 "driver");
1203 } else if (!data->m_dbh->conn()->support(PDOConnection::MethodSetAttribute)) {
1204 pdo_raise_impl_error(data->m_dbh, nullptr, "IM001",
1205 "driver does not support setting attributes");
1206 } else {
1207 PDO_HANDLE_DBH_ERR(data->m_dbh);
1209 return false;
1212 static Variant HHVM_METHOD(PDO, getattribute, int64_t attribute) {
1213 auto data = Native::data<PDOData>(this_);
1215 assert(data->m_dbh->conn()->driver);
1216 setPDOErrorNone(data->m_dbh->conn()->error_code);
1217 data->m_dbh->query_stmt = nullptr;
1219 /* handle generic PDO-level attributes */
1220 switch (attribute) {
1221 case PDO_ATTR_PERSISTENT:
1222 return (bool)data->m_dbh->conn()->is_persistent;
1224 case PDO_ATTR_CASE:
1225 return (int64_t)data->m_dbh->conn()->desired_case;
1227 case PDO_ATTR_ORACLE_NULLS:
1228 return (int64_t)data->m_dbh->conn()->oracle_nulls;
1230 case PDO_ATTR_ERRMODE:
1231 return (int64_t)data->m_dbh->conn()->error_mode;
1233 case PDO_ATTR_DRIVER_NAME:
1234 return String(data->m_dbh->conn()->driver->getName());
1236 case PDO_ATTR_STATEMENT_CLASS: {
1237 Array ret;
1238 ret.append(String(data->m_dbh->conn()->def_stmt_clsname));
1239 if (!data->m_dbh->def_stmt_ctor_args.isNull()) {
1240 ret.append(data->m_dbh->def_stmt_ctor_args);
1242 return ret;
1244 case PDO_ATTR_DEFAULT_FETCH_MODE:
1245 return (int64_t)data->m_dbh->conn()->default_fetch_type;
1248 if (!data->m_dbh->conn()->support(PDOConnection::MethodGetAttribute)) {
1249 pdo_raise_impl_error(data->m_dbh, nullptr, "IM001",
1250 "driver does not support getting attributes");
1251 return false;
1254 Variant ret;
1255 switch (data->m_dbh->conn()->getAttribute(attribute, ret)) {
1256 case -1:
1257 PDO_HANDLE_DBH_ERR(data->m_dbh);
1258 return false;
1259 case 0:
1260 pdo_raise_impl_error(data->m_dbh, nullptr, "IM001",
1261 "driver does not support that attribute");
1262 return false;
1264 return ret;
1267 static Variant HHVM_METHOD(PDO, exec, const String& query) {
1268 auto data = Native::data<PDOData>(this_);
1270 SYNC_VM_REGS_SCOPED();
1271 if (query.empty()) {
1272 pdo_raise_impl_error(data->m_dbh, nullptr, "HY000",
1273 "trying to execute an empty query");
1274 return false;
1277 assert(data->m_dbh->conn()->driver);
1278 setPDOErrorNone(data->m_dbh->conn()->error_code);
1279 data->m_dbh->query_stmt = nullptr;
1281 int64_t ret = data->m_dbh->conn()->doer(query);
1282 if (ret == -1) {
1283 PDO_HANDLE_DBH_ERR(data->m_dbh);
1284 return false;
1286 return ret;
1289 static Variant HHVM_METHOD(PDO, lastinsertid,
1290 const String& seqname /* = null_string */) {
1291 auto data = Native::data<PDOData>(this_);
1293 assert(data->m_dbh->conn()->driver);
1294 setPDOErrorNone(data->m_dbh->conn()->error_code);
1295 data->m_dbh->query_stmt = nullptr;
1297 if (!data->m_dbh->conn()->support(PDOConnection::MethodLastId)) {
1298 pdo_raise_impl_error(data->m_dbh, nullptr, "IM001",
1299 "driver does not support lastInsertId()");
1300 return false;
1303 String ret = data->m_dbh->conn()->lastId(seqname.data());
1304 if (ret.empty()) {
1305 PDO_HANDLE_DBH_ERR(data->m_dbh);
1306 return false;
1308 return ret;
1311 static Variant HHVM_METHOD(PDO, errorcode) {
1312 auto data = Native::data<PDOData>(this_);
1314 assert(data->m_dbh->conn()->driver);
1315 if (data->m_dbh->query_stmt) {
1316 return String(data->m_dbh->query_stmt->error_code, CopyString);
1319 if (data->m_dbh->conn()->error_code[0] == '\0') {
1320 return init_null();
1324 * Making sure that we fallback to the default implementation
1325 * if the dbh->error_code is not null.
1327 return String(data->m_dbh->conn()->error_code, CopyString);
1330 static Array HHVM_METHOD(PDO, errorinfo) {
1331 auto data = Native::data<PDOData>(this_);
1333 assert(data->m_dbh->conn()->driver);
1335 Array ret;
1336 if (data->m_dbh->query_stmt) {
1337 ret.append(String(data->m_dbh->query_stmt->error_code, CopyString));
1338 } else {
1339 ret.append(String(data->m_dbh->conn()->error_code, CopyString));
1342 if (data->m_dbh->conn()->support(PDOConnection::MethodFetchErr)) {
1343 data->m_dbh->conn()->fetchErr(data->m_dbh->query_stmt, ret);
1347 * In order to be consistent, we have to make sure we add the good amount
1348 * of nulls depending on the current number of elements. We make a simple
1349 * difference and add the needed elements
1351 int error_count = ret.size();
1352 int error_expected_count = 3;
1353 if (error_expected_count > error_count) {
1354 int error_count_diff = error_expected_count - error_count;
1355 for (int i = 0; i < error_count_diff; i++) {
1356 ret.append(init_null_variant);
1359 return ret;
1362 static Variant HHVM_METHOD(PDO, query, const String& sql,
1363 const Array& _argv) {
1365 auto data = Native::data<PDOData>(this_);
1366 SYNC_VM_REGS_SCOPED();
1367 assert(data->m_dbh->conn()->driver);
1368 setPDOErrorNone(data->m_dbh->conn()->error_code);
1369 data->m_dbh->query_stmt = nullptr;
1371 Object ret = pdo_stmt_instantiate(data->m_dbh,
1372 data->m_dbh->conn()->def_stmt_clsname,
1373 data->m_dbh->def_stmt_ctor_args);
1374 if (ret.isNull()) {
1375 pdo_raise_impl_error
1376 (data->m_dbh, nullptr, "HY000",
1377 "failed to instantiate user supplied statement class");
1378 return init_null();
1380 PDOStatementData *pdostmt = Native::data<PDOStatementData>(ret);
1382 if (data->m_dbh->conn()->preparer(sql, &pdostmt->m_stmt, Array())) {
1383 auto stmt = pdostmt->m_stmt;
1384 assert(stmt);
1386 /* unconditionally keep this for later reference */
1387 stmt->query_string = sql;
1388 stmt->default_fetch_type = data->m_dbh->conn()->default_fetch_type;
1389 stmt->active_query_string = stmt->query_string;
1390 stmt->dbh = data->m_dbh;
1391 stmt->lazy_object_ref.unset();
1393 setPDOErrorNone(stmt->error_code);
1395 // when we add support for varargs here, we only need to set the stmt if
1396 // the argument count is > 1
1397 int argc = _argv.size() + 1;
1398 if (argc == 1 ||
1399 pdo_stmt_set_fetch_mode(
1400 stmt,
1402 tvCastToInt64(_argv.rvalAt(0).tv()),
1403 Variant::attach(HHVM_FN(array_splice)(_argv, 1)).toArray()
1404 )) {
1405 /* now execute the statement */
1406 setPDOErrorNone(stmt->error_code);
1407 if (stmt->executer()) {
1408 int ok = 1;
1409 if (!stmt->executed) {
1410 if (stmt->dbh->conn()->alloc_own_columns) {
1411 ok = pdo_stmt_describe_columns(stmt);
1413 stmt->executed = 1;
1415 if (ok) {
1416 pdo_stmt_construct(stmt, ret, data->m_dbh->conn()->def_stmt_clsname,
1417 data->m_dbh->def_stmt_ctor_args);
1418 return ret;
1422 /* something broke */
1423 data->m_dbh->query_stmt = stmt.get();
1424 PDO_HANDLE_STMT_ERR(stmt);
1425 } else {
1426 PDO_HANDLE_DBH_ERR(data->m_dbh);
1429 return false;
1432 static Variant HHVM_METHOD(PDO, quote, const String& str,
1433 int64_t paramtype /* = PDO_PARAM_STR */) {
1434 auto data = Native::data<PDOData>(this_);
1436 assert(data->m_dbh->conn()->driver);
1437 setPDOErrorNone(data->m_dbh->conn()->error_code);
1438 data->m_dbh->query_stmt = nullptr;
1440 if (!data->m_dbh->conn()->support(PDOConnection::MethodQuoter)) {
1441 pdo_raise_impl_error(data->m_dbh, nullptr, "IM001",
1442 "driver does not support quoting");
1443 return false;
1446 String quoted;
1447 if (data->m_dbh->conn()->quoter(str, quoted, (PDOParamType)paramtype)) {
1448 return quoted;
1450 PDO_HANDLE_DBH_ERR(data->m_dbh);
1451 return false;
1454 static bool HHVM_METHOD(PDO, sqlitecreatefunction, const String& name,
1455 const Variant& callback, int64_t argcount /* = -1 */) {
1456 #ifdef ENABLE_EXTENSION_PDO_SQLITE
1457 auto data = Native::data<PDOData>(this_);
1459 auto res = dynamic_cast<PDOSqliteResource*>(data->m_dbh.get());
1460 if (res == nullptr) {
1461 return false;
1463 return res->createFunction(name, callback, argcount);
1464 #else
1465 raise_recoverable_error("PDO::sqliteCreateFunction not implemented");
1466 return false;
1467 #endif
1470 static bool HHVM_METHOD(PDO, sqlitecreateaggregate, const String& /*name*/,
1471 const Variant& /*step*/, const Variant& /*final*/,
1472 int64_t /*argcount*/ /* = -1 */) {
1473 raise_recoverable_error("PDO::sqliteCreateAggregate not implemented");
1474 return false;
1477 static Variant HHVM_METHOD(PDO, __wakeup) {
1478 throw_pdo_exception(uninit_null(), uninit_null(),
1479 "You cannot serialize or unserialize PDO instances");
1480 return init_null();
1483 static Variant HHVM_METHOD(PDO, __sleep) {
1484 throw_pdo_exception(uninit_null(), uninit_null(),
1485 "You cannot serialize or unserialize PDO instances");
1486 return init_null();
1489 static Array HHVM_STATIC_METHOD(PDO, getAvailableDrivers) {
1490 return HHVM_FN(pdo_drivers)();
1493 ///////////////////////////////////////////////////////////////////////////////
1495 static inline bool rewrite_name_to_position(sp_PDOStatement stmt,
1496 sp_PDOBoundParam param) {
1497 if (!stmt->bound_param_map.empty()) {
1498 /* rewriting :name to ? style.
1499 * We need to fixup the parameter numbers on the parameters.
1500 * If we find that a given named parameter has been used twice,
1501 * we will raise an error, as we can't be sure that it is safe
1502 * to bind multiple parameters onto the same zval in the underlying
1503 * driver */
1504 if (stmt->named_rewrite_template) {
1505 /* this is not an error here */
1506 return true;
1508 if (param->name.empty()) {
1509 /* do the reverse; map the parameter number to the name */
1510 if (stmt->bound_param_map.exists(param->paramno)) {
1511 param->name = stmt->bound_param_map[param->paramno].toString();
1512 return true;
1514 pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
1515 "parameter was not defined");
1516 return false;
1519 int position = 0;
1520 for (ArrayIter iter(stmt->bound_param_map); iter; ++iter, ++position) {
1521 if (iter.second().toString() == param->name) {
1522 if (param->paramno >= 0) {
1523 pdo_raise_impl_error
1524 (stmt->dbh, stmt, "IM001",
1525 "PDO refuses to handle repeating the same :named parameter "
1526 "for multiple positions with this driver, as it might be "
1527 "unsafe to do so. Consider using a separate name for each "
1528 "parameter instead");
1529 return true;
1531 param->paramno = position;
1532 return true;
1535 pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
1536 "parameter was not defined");
1537 return false;
1539 return true;
1542 /* trigger callback hook for parameters */
1543 static bool dispatch_param_event(sp_PDOStatement stmt,
1544 PDOParamEvent event_type) {
1545 if (!stmt->support(PDOStatement::MethodParamHook)) {
1546 return true;
1548 for (ArrayIter iter(stmt->bound_params); iter; ++iter) {
1549 auto param = cast<PDOBoundParam>(iter.second());
1550 if (!stmt->paramHook(param.get(), event_type)) {
1551 return false;
1554 for (ArrayIter iter(stmt->bound_columns); iter; ++iter) {
1555 auto param = cast<PDOBoundParam>(iter.second());
1556 if (!stmt->paramHook(param.get(), event_type)) {
1557 return false;
1560 return true;
1563 static void get_lazy_object(sp_PDOStatement stmt, Variant &ret) {
1564 if (stmt->lazy_object_ref.isNull()) {
1565 stmt->lazy_object_ref = stmt;
1567 ret = stmt->lazy_object_ref;
1570 static bool really_register_bound_param(sp_PDOBoundParam param,
1571 sp_PDOStatement stmt,
1572 bool is_param) {
1573 Array &hash = is_param ? stmt->bound_params : stmt->bound_columns;
1575 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_STR &&
1576 param->max_value_len <= 0 && !param->parameter.isNull()) {
1577 param->parameter = param->parameter.toString();
1578 } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_INT &&
1579 param->parameter.isBoolean()) {
1580 param->parameter = param->parameter.toInt64();
1581 } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL &&
1582 param->parameter.isInteger()) {
1583 param->parameter = param->parameter.toBoolean();
1585 param->stmt = stmt.get();
1586 param->is_param = is_param;
1588 if (!is_param && !param->name.empty() && !stmt->columns.empty()) {
1589 /* try to map the name to the column */
1590 for (int i = 0; i < stmt->column_count; i++) {
1591 if (cast<PDOColumn>(stmt->columns[i])->name == param->name) {
1592 param->paramno = i;
1593 break;
1597 /* if you prepare and then execute passing an array of params keyed by
1598 names, then this will trigger, and we don't want that */
1599 if (param->paramno == -1) {
1600 raise_warning("Did not found column name '%s' in the defined columns;"
1601 " it will not be bound", param->name.data());
1605 if (is_param && !param->name.empty() && param->name[0] != ':') {
1606 param->name = String(":") + param->name;
1609 if (is_param && !rewrite_name_to_position(stmt, param)) {
1610 param->name.reset();
1611 return false;
1614 /* ask the driver to perform any normalization it needs on the
1615 * parameter name. Note that it is illegal for the driver to take
1616 * a reference to param, as it resides in transient storage only
1617 * at this time. */
1618 if (stmt->support(PDOStatement::MethodParamHook)) {
1619 if (!stmt->paramHook(param.get(), PDO_PARAM_EVT_NORMALIZE)) {
1620 param->name.reset();
1621 return false;
1625 /* delete any other parameter registered with this number.
1626 * If the parameter is named, it will be removed and correctly
1627 * disposed of by the hash_update call that follows */
1628 if (param->paramno >= 0) {
1629 hash.remove(param->paramno);
1632 /* allocate storage for the parameter, keyed by its "canonical" name */
1633 if (!param->name.empty()) {
1634 hash.set(param->name, Variant(param));
1635 } else {
1636 hash.set(param->paramno, Variant(param));
1639 /* tell the driver we just created a parameter */
1640 if (stmt->support(PDOStatement::MethodParamHook)) {
1641 if (!stmt->paramHook(param.get(), PDO_PARAM_EVT_ALLOC)) {
1642 /* undo storage allocation; the hash will free the parameter
1643 * name if required */
1644 if (!param->name.empty()) {
1645 hash.remove(param->name);
1646 } else {
1647 hash.remove(param->paramno);
1649 param->parameter.unset();
1650 return false;
1653 return true;
1656 static inline void fetch_value(sp_PDOStatement stmt, Variant &dest, int colno,
1657 int *type_override) {
1658 if (colno < 0 || colno >= stmt->column_count) {
1659 return;
1661 auto col = cast<PDOColumn>(stmt->columns[colno]);
1662 int type = PDO_PARAM_TYPE(col->param_type);
1663 int new_type = type_override ? PDO_PARAM_TYPE(*type_override) : type;
1665 stmt->getColumn(colno, dest);
1667 if (type != new_type) {
1668 switch (new_type) {
1669 case PDO_PARAM_INT: dest = dest.toInt64(); break;
1670 case PDO_PARAM_BOOL: dest = dest.toBoolean(); break;
1671 case PDO_PARAM_STR: dest = dest.toString(); break;
1672 case PDO_PARAM_NULL: dest = init_null(); break;
1675 if (stmt->dbh->conn()->stringify && (dest.isInteger() || dest.isDouble())) {
1676 dest = dest.toString();
1678 if (dest.isNull() && stmt->dbh->conn()->oracle_nulls == PDO_NULL_TO_STRING) {
1679 dest = empty_string_variant();
1683 static bool do_fetch_common(sp_PDOStatement stmt, PDOFetchOrientation ori,
1684 long offset, bool do_bind) {
1685 if (!stmt->executed) {
1686 return false;
1688 if (!dispatch_param_event(stmt, PDO_PARAM_EVT_FETCH_PRE)) {
1689 return false;
1691 if (!stmt->fetcher(ori, offset)) {
1692 return false;
1694 /* some drivers might need to describe the columns now */
1695 if (stmt->columns.empty() && !pdo_stmt_describe_columns(stmt)) {
1696 return false;
1698 if (!dispatch_param_event(stmt, PDO_PARAM_EVT_FETCH_POST)) {
1699 return false;
1702 if (do_bind && !stmt->bound_columns.empty()) {
1703 /* update those bound column variables now */
1704 for (ArrayIter iter(stmt->bound_columns); iter; ++iter) {
1705 auto param = cast<PDOBoundParam>(iter.second());
1706 if (param->paramno >= 0) {
1707 param->parameter.setNull();
1708 /* set new value */
1709 fetch_value(stmt, param->parameter, param->paramno,
1710 (int *)&param->param_type);
1711 /* TODO: some smart thing that avoids duplicating the value in the
1712 * general loop below. For now, if you're binding output columns,
1713 * it's better to use LAZY or BOUND fetches if you want to shave
1714 * off those cycles */
1719 return true;
1722 static bool do_fetch_func_prepare(sp_PDOStatement stmt) {
1723 if (!is_callable(stmt->fetch.func)) {
1724 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
1725 "user-supplied function must be a valid callback");
1726 return false;
1728 return true;
1731 /* perform a fetch. If do_bind is true, update any bound columns.
1732 * If return_value is not null, store values into it according to HOW. */
1733 static bool do_fetch(sp_PDOStatement stmt,
1734 bool do_bind,
1735 Variant& ret,
1736 PDOFetchType how,
1737 PDOFetchOrientation ori,
1738 long offset,
1739 Variant *return_all) {
1740 if (how == PDO_FETCH_USE_DEFAULT) {
1741 how = stmt->default_fetch_type;
1743 int flags = how & PDO_FETCH_FLAGS;
1744 how = (PDOFetchType)(how & ~PDO_FETCH_FLAGS);
1746 if (!do_fetch_common(stmt, ori, offset, do_bind)) {
1747 return false;
1750 if (how == PDO_FETCH_BOUND) {
1751 ret = true;
1752 return true;
1755 int colno;
1756 if ((flags & PDO_FETCH_GROUP) && stmt->fetch.column == -1) {
1757 colno = 1;
1758 } else {
1759 colno = stmt->fetch.column;
1762 if (how == PDO_FETCH_LAZY) {
1763 get_lazy_object(stmt, ret);
1764 return true;
1767 String clsname, old_clsname;
1768 Variant old_ctor_args;
1769 ret = false;
1770 int i = 0;
1771 switch (how) {
1772 case PDO_FETCH_USE_DEFAULT:
1773 case PDO_FETCH_ASSOC:
1774 case PDO_FETCH_BOTH:
1775 case PDO_FETCH_NUM:
1776 case PDO_FETCH_NAMED:
1777 ret = Array::Create();
1778 break;
1780 case PDO_FETCH_KEY_PAIR:
1781 if (stmt->column_count != 2) {
1782 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
1783 "PDO::FETCH_KEY_PAIR fetch mode requires the "
1784 "result set to contain extactly 2 columns.");
1785 return false;
1787 if (!return_all) {
1788 ret = Array::Create();
1790 break;
1792 case PDO_FETCH_COLUMN:
1793 if (colno >= 0 && colno < stmt->column_count) {
1794 if (flags == PDO_FETCH_GROUP && stmt->fetch.column == -1) {
1795 fetch_value(stmt, ret, 1, NULL);
1796 } else if (flags == PDO_FETCH_GROUP && colno) {
1797 fetch_value(stmt, ret, 0, NULL);
1798 } else {
1799 fetch_value(stmt, ret, colno, NULL);
1801 if (!return_all) {
1802 return true;
1804 break;
1805 } else {
1806 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Invalid column index");
1808 return false;
1810 case PDO_FETCH_OBJ:
1811 ret = SystemLib::AllocStdClassObject();
1812 break;
1814 case PDO_FETCH_CLASS:
1815 if (flags & PDO_FETCH_CLASSTYPE) {
1816 old_clsname = stmt->fetch.clsname;
1817 old_ctor_args = stmt->fetch.ctor_args;
1819 Variant val;
1820 fetch_value(stmt, val, i++, NULL);
1821 if (!val.isNull()) {
1822 if (!HHVM_FN(class_exists)(val.toString())) {
1823 stmt->fetch.clsname = "stdclass";
1824 } else {
1825 stmt->fetch.clsname = val.toString();
1829 do_fetch_class_prepare(stmt);
1831 clsname = stmt->fetch.clsname;
1832 if (clsname.empty()) {
1833 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
1834 "No fetch class specified");
1835 return false;
1837 if ((flags & PDO_FETCH_SERIALIZE) == 0) {
1838 ret = create_object_only(clsname);
1839 if (!do_fetch_class_prepare(stmt)) {
1840 return false;
1842 if (!stmt->fetch.constructor.empty() &&
1843 (flags & PDO_FETCH_PROPS_LATE)) {
1844 ret.asCObjRef()->o_invoke(stmt->fetch.constructor,
1845 stmt->fetch.ctor_args.toArray());
1846 ret.asCObjRef()->clearNoDestruct();
1849 break;
1851 case PDO_FETCH_INTO:
1852 if (stmt->fetch.into.isNull()) {
1853 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
1854 "No fetch-into object specified.");
1855 return false;
1858 ret = stmt->fetch.into;
1859 if (ret.isObject() &&
1860 ret.getObjectData()->instanceof(SystemLib::s_stdclassClass)) {
1861 how = PDO_FETCH_OBJ;
1863 break;
1865 case PDO_FETCH_FUNC:
1866 if (stmt->fetch.func.empty()) {
1867 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
1868 "No fetch function specified");
1869 return false;
1871 if (!do_fetch_func_prepare(stmt)) {
1872 return false;
1874 break;
1876 default:
1877 assert(false);
1878 return false;
1881 Variant grp_val;
1882 if (return_all && how != PDO_FETCH_KEY_PAIR) {
1883 if (flags == PDO_FETCH_GROUP && how == PDO_FETCH_COLUMN &&
1884 stmt->fetch.column > 0) {
1885 fetch_value(stmt, grp_val, colno, NULL);
1886 } else {
1887 fetch_value(stmt, grp_val, i, NULL);
1889 grp_val = grp_val.toString();
1890 if (how == PDO_FETCH_COLUMN) {
1891 i = stmt->column_count; /* no more data to fetch */
1892 } else {
1893 i++;
1897 for (int idx = 0; i < stmt->column_count; i++, idx++) {
1898 const String& name = cast<PDOColumn>(stmt->columns[i])->name;
1899 Variant val;
1900 fetch_value(stmt, val, i, NULL);
1902 switch (how) {
1903 case PDO_FETCH_ASSOC:
1904 ret.toArrRef().set(name, val);
1905 break;
1907 case PDO_FETCH_KEY_PAIR: {
1908 Variant tmp;
1909 fetch_value(stmt, tmp, ++i, NULL);
1910 if (return_all) {
1911 return_all->toArrRef().set(val, tmp);
1912 } else {
1913 ret.toArrRef().set(val, tmp);
1915 return true;
1917 case PDO_FETCH_USE_DEFAULT:
1918 case PDO_FETCH_BOTH:
1919 ret.toArrRef().set(name, val);
1920 ret.toArrRef().append(val);
1921 break;
1923 case PDO_FETCH_NAMED: {
1924 /* already have an item with this name? */
1925 forceToArray(ret);
1926 if (ret.toArrRef().exists(name)) {
1927 auto const curr_val = ret.toArrRef().lvalAt(name).unboxed();
1928 if (!isArrayLikeType(curr_val.type())) {
1929 Array arr = Array::Create();
1930 arr.append(curr_val.tv());
1931 arr.append(val);
1932 ret.toArray().set(name, arr);
1933 } else {
1934 asArrRef(curr_val).append(val);
1936 } else {
1937 ret.toArrRef().set(name, val);
1939 break;
1941 case PDO_FETCH_NUM:
1942 ret.toArrRef().append(val);
1943 break;
1945 case PDO_FETCH_OBJ:
1946 case PDO_FETCH_INTO:
1947 ret.toObject()->o_set(name, val);
1948 break;
1950 case PDO_FETCH_CLASS:
1951 if ((flags & PDO_FETCH_SERIALIZE) == 0 || idx) {
1952 ret.toObject()->o_set(name, val);
1953 } else {
1954 #ifdef MBO_0
1955 ret = unserialize_from_string(val);
1956 if (same(ret, false)) {
1957 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
1958 "cannot unserialize data");
1959 return false;
1961 #endif
1962 // hzhao: not sure how we support class serialization
1963 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
1964 "cannot unserialize class");
1965 return false;
1967 break;
1969 case PDO_FETCH_FUNC:
1970 forceToArray(stmt->fetch.values).set(idx, val);
1971 break;
1973 default:
1974 pdo_raise_impl_error(stmt->dbh, stmt, "22003", "mode is out of range");
1975 return false;
1979 switch (how) {
1980 case PDO_FETCH_CLASS:
1981 if (!stmt->fetch.constructor.empty() &&
1982 !(flags & (PDO_FETCH_PROPS_LATE | PDO_FETCH_SERIALIZE))) {
1983 ret.toObject()->o_invoke(stmt->fetch.constructor,
1984 stmt->fetch.ctor_args.toArray());
1985 ret.toObject()->clearNoDestruct();
1987 if (flags & PDO_FETCH_CLASSTYPE) {
1988 stmt->fetch.clsname = old_clsname;
1989 stmt->fetch.ctor_args = old_ctor_args;
1991 break;
1993 case PDO_FETCH_FUNC:
1994 ret = vm_call_user_func(stmt->fetch.func,
1995 stmt->fetch.values.toArray());
1996 break;
1998 default:
1999 break;
2002 if (return_all) {
2003 if ((flags & PDO_FETCH_UNIQUE) == PDO_FETCH_UNIQUE) {
2004 return_all->toArrRef().set(grp_val, ret);
2005 } else {
2006 auto const lval = return_all->toArrRef().lvalAt(grp_val);
2007 forceToArray(lval).append(ret);
2011 return true;
2014 static int register_bound_param(const Variant& paramno, VRefParam param,
2015 int64_t type, int64_t max_value_len,
2016 const Variant& driver_params,
2017 sp_PDOStatement stmt, bool is_param) {
2018 auto p = req::make<PDOBoundParam>();
2019 // need to make sure this is NULL, in case a fatal errors occurs before it's
2020 // set inside really_register_bound_param
2021 p->stmt = NULL;
2023 if (paramno.isNumeric()) {
2024 p->paramno = paramno.toInt64();
2025 } else {
2026 p->paramno = -1;
2027 p->name = paramno.toString();
2030 p->parameter.setWithRef(param);
2031 p->param_type = (PDOParamType)type;
2032 p->max_value_len = max_value_len;
2033 p->driver_params = driver_params;
2035 if (p->paramno > 0) {
2036 --p->paramno; /* make it zero-based internally */
2037 } else if (p->name.empty()) {
2038 pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
2039 "Columns/Parameters are 1-based");
2040 return false;
2043 if (!really_register_bound_param(p, stmt, is_param)) {
2044 p->parameter.unset();
2045 return false;
2047 return true;
2050 static bool generic_stmt_attr_get(sp_PDOStatement stmt, Variant &ret,
2051 long attr) {
2052 if (attr == PDO_ATTR_EMULATE_PREPARES) {
2053 ret = (bool)(stmt->supports_placeholders == PDO_PLACEHOLDER_NONE);
2054 return true;
2056 return false;
2059 ///////////////////////////////////////////////////////////////////////////////
2060 // SQL parser
2062 namespace {
2064 #define PDO_PARSER_TEXT 1
2065 #define PDO_PARSER_BIND 2
2066 #define PDO_PARSER_BIND_POS 3
2067 #define PDO_PARSER_EOI 4
2069 #define RET(i) {s->cur = cursor; return i; }
2070 #define SKIP_ONE(i) {s->cur = s->tok + 1; return 1; }
2072 #define YYCTYPE unsigned char
2073 #define YYCURSOR cursor
2074 #define YYLIMIT limit
2075 #define YYMARKER s->ptr
2076 #define YYFILL(n) RET(PDO_PARSER_EOI)
2078 typedef struct Scanner {
2079 char *ptr, *cur, *lim, *tok;
2080 } Scanner;
2082 static int scan(Scanner *s) {
2083 char* cursor = s->cur;
2084 char* limit = s->lim;
2085 s->tok = cursor;
2088 YYCTYPE yych;
2090 if ((YYLIMIT - YYCURSOR) < 2) { YYFILL(2); }
2091 yych = *YYCURSOR;
2092 switch (yych) {
2093 case 0x00: goto yy11;
2094 case '"': goto yy2;
2095 case '\'': goto yy4;
2096 case ':': goto yy5;
2097 case '?': goto yy6;
2098 default: goto yy8;
2100 yy2:
2101 yych = *(YYMARKER = ++YYCURSOR);
2102 if (yych >= 0x01) goto yy26;
2103 yy3:
2104 { SKIP_ONE(PDO_PARSER_TEXT); }
2105 yy4:
2106 yych = *(YYMARKER = ++YYCURSOR);
2107 if (yych <= 0x00) goto yy3;
2108 goto yy20;
2109 yy5:
2110 yych = *++YYCURSOR;
2111 switch (yych) {
2112 case '0':
2113 case '1':
2114 case '2':
2115 case '3':
2116 case '4':
2117 case '5':
2118 case '6':
2119 case '7':
2120 case '8':
2121 case '9':
2122 case 'A':
2123 case 'B':
2124 case 'C':
2125 case 'D':
2126 case 'E':
2127 case 'F':
2128 case 'G':
2129 case 'H':
2130 case 'I':
2131 case 'J':
2132 case 'K':
2133 case 'L':
2134 case 'M':
2135 case 'N':
2136 case 'O':
2137 case 'P':
2138 case 'Q':
2139 case 'R':
2140 case 'S':
2141 case 'T':
2142 case 'U':
2143 case 'V':
2144 case 'W':
2145 case 'X':
2146 case 'Y':
2147 case 'Z':
2148 case '_':
2149 case 'a':
2150 case 'b':
2151 case 'c':
2152 case 'd':
2153 case 'e':
2154 case 'f':
2155 case 'g':
2156 case 'h':
2157 case 'i':
2158 case 'j':
2159 case 'k':
2160 case 'l':
2161 case 'm':
2162 case 'n':
2163 case 'o':
2164 case 'p':
2165 case 'q':
2166 case 'r':
2167 case 's':
2168 case 't':
2169 case 'u':
2170 case 'v':
2171 case 'w':
2172 case 'x':
2173 case 'y':
2174 case 'z': goto yy16;
2175 case ':':
2176 case '?': goto yy13;
2177 default: goto yy3;
2179 yy6:
2180 ++YYCURSOR;
2181 switch ((yych = *YYCURSOR)) {
2182 case ':':
2183 case '?': goto yy13;
2184 default: goto yy7;
2186 yy7:
2187 { RET(PDO_PARSER_BIND_POS); }
2188 yy8:
2189 ++YYCURSOR;
2190 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2191 yych = *YYCURSOR;
2192 switch (yych) {
2193 case 0x00:
2194 case '"':
2195 case '\'':
2196 case ':':
2197 case '?': goto yy10;
2198 default: goto yy8;
2200 yy10:
2201 { RET(PDO_PARSER_TEXT); }
2202 yy11:
2203 ++YYCURSOR;
2204 { RET(PDO_PARSER_EOI); }
2205 yy13:
2206 ++YYCURSOR;
2207 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2208 yych = *YYCURSOR;
2209 switch (yych) {
2210 case ':':
2211 case '?': goto yy13;
2212 default: goto yy15;
2214 yy15:
2215 { RET(PDO_PARSER_TEXT); }
2216 yy16:
2217 ++YYCURSOR;
2218 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2219 yych = *YYCURSOR;
2220 switch (yych) {
2221 case '0':
2222 case '1':
2223 case '2':
2224 case '3':
2225 case '4':
2226 case '5':
2227 case '6':
2228 case '7':
2229 case '8':
2230 case '9':
2231 case 'A':
2232 case 'B':
2233 case 'C':
2234 case 'D':
2235 case 'E':
2236 case 'F':
2237 case 'G':
2238 case 'H':
2239 case 'I':
2240 case 'J':
2241 case 'K':
2242 case 'L':
2243 case 'M':
2244 case 'N':
2245 case 'O':
2246 case 'P':
2247 case 'Q':
2248 case 'R':
2249 case 'S':
2250 case 'T':
2251 case 'U':
2252 case 'V':
2253 case 'W':
2254 case 'X':
2255 case 'Y':
2256 case 'Z':
2257 case '_':
2258 case 'a':
2259 case 'b':
2260 case 'c':
2261 case 'd':
2262 case 'e':
2263 case 'f':
2264 case 'g':
2265 case 'h':
2266 case 'i':
2267 case 'j':
2268 case 'k':
2269 case 'l':
2270 case 'm':
2271 case 'n':
2272 case 'o':
2273 case 'p':
2274 case 'q':
2275 case 'r':
2276 case 's':
2277 case 't':
2278 case 'u':
2279 case 'v':
2280 case 'w':
2281 case 'x':
2282 case 'y':
2283 case 'z': goto yy16;
2284 default: goto yy18;
2286 yy18:
2287 { RET(PDO_PARSER_BIND); }
2288 yy19:
2289 ++YYCURSOR;
2290 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2291 yych = *YYCURSOR;
2292 yy20:
2293 switch (yych) {
2294 case 0x00: goto yy21;
2295 case '\'': goto yy23;
2296 case '\\': goto yy22;
2297 default: goto yy19;
2299 yy21:
2300 YYCURSOR = YYMARKER;
2301 goto yy3;
2302 yy22:
2303 ++YYCURSOR;
2304 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2305 yych = *YYCURSOR;
2306 if (yych <= 0x00) goto yy21;
2307 goto yy19;
2308 yy23:
2309 ++YYCURSOR;
2310 { RET(PDO_PARSER_TEXT); }
2311 yy25:
2312 ++YYCURSOR;
2313 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2314 yych = *YYCURSOR;
2315 yy26:
2316 switch (yych) {
2317 case 0x00: goto yy21;
2318 case '"': goto yy28;
2319 case '\\': goto yy27;
2320 default: goto yy25;
2322 yy27:
2323 ++YYCURSOR;
2324 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2325 yych = *YYCURSOR;
2326 if (yych <= 0x00) goto yy21;
2327 goto yy25;
2328 yy28:
2329 ++YYCURSOR;
2330 { RET(PDO_PARSER_TEXT); }
2337 struct placeholder {
2338 char *pos;
2339 int len;
2340 int bindno;
2341 String quoted; /* quoted value */
2342 struct placeholder *next;
2345 int pdo_parse_params(sp_PDOStatement stmt, const String& in, String &out) {
2346 Scanner s;
2347 const char *ptr;
2348 char *newbuffer;
2349 int t;
2350 int bindno = 0;
2351 int ret = 0;
2352 int newbuffer_len;
2353 Array params;
2354 req::ptr<PDOBoundParam> param;
2355 int query_type = PDO_PLACEHOLDER_NONE;
2356 struct placeholder *placeholders = NULL, *placetail = NULL, *plc = NULL;
2358 s.cur = (char*)in.data();
2359 s.lim = (char*)in.data() + in.size() + 1;
2361 /* phase 1: look for args */
2362 while ((t = scan(&s)) != PDO_PARSER_EOI) {
2363 if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS) {
2364 if (t == PDO_PARSER_BIND) {
2365 int len = s.cur - s.tok;
2366 if ((in.data() < (s.cur - len)) && isalnum(*(s.cur - len - 1))) {
2367 continue;
2369 query_type |= PDO_PLACEHOLDER_NAMED;
2370 } else {
2371 query_type |= PDO_PLACEHOLDER_POSITIONAL;
2374 plc = req::make_raw<placeholder>();
2375 memset(plc, 0, sizeof(*plc));
2376 plc->next = NULL;
2377 plc->pos = s.tok;
2378 plc->len = s.cur - s.tok;
2379 plc->bindno = bindno++;
2381 if (placetail) {
2382 placetail->next = plc;
2383 } else {
2384 placeholders = plc;
2386 placetail = plc;
2390 if (bindno == 0) {
2391 /* nothing to do; good! */
2392 return 0;
2395 /* did the query make sense to me? */
2396 if (query_type == (PDO_PLACEHOLDER_NAMED|PDO_PLACEHOLDER_POSITIONAL)) {
2397 /* they mixed both types; punt */
2398 pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
2399 "mixed named and positional parameters");
2400 ret = -1;
2401 goto clean_up;
2404 if ((int)stmt->supports_placeholders == query_type &&
2405 !stmt->named_rewrite_template) {
2406 /* query matches native syntax */
2407 ret = 0;
2408 goto clean_up;
2411 if (stmt->named_rewrite_template) {
2412 /* magic/hack.
2413 * We we pretend that the query was positional even if
2414 * it was named so that we fall into the
2415 * named rewrite case below. Not too pretty,
2416 * but it works. */
2417 query_type = PDO_PLACEHOLDER_POSITIONAL;
2420 params = stmt->bound_params;
2422 /* Do we have placeholders but no bound params */
2423 if (bindno && params.empty() &&
2424 stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
2425 pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "no parameters were bound");
2426 ret = -1;
2427 goto clean_up;
2430 if (!params.empty() && bindno != params.size() &&
2431 stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
2432 /* extra bit of validation for instances when same params are bound
2433 more then once */
2434 if (query_type != PDO_PLACEHOLDER_POSITIONAL && bindno > params.size()) {
2435 int ok = 1;
2436 for (plc = placeholders; plc; plc = plc->next) {
2437 if (!params.exists(String(plc->pos, plc->len, CopyString))) {
2438 ok = 0;
2439 break;
2442 if (ok) {
2443 goto safe;
2446 pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
2447 "number of bound variables does not match number "
2448 "of tokens");
2449 ret = -1;
2450 goto clean_up;
2452 safe:
2453 /* what are we going to do ? */
2454 if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
2455 /* query generation */
2457 newbuffer_len = in.size();
2459 /* let's quote all the values */
2460 for (plc = placeholders; plc; plc = plc->next) {
2461 Variant vparam;
2462 if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
2463 vparam = params[plc->bindno];
2464 } else {
2465 vparam = params[String(plc->pos, plc->len, CopyString)];
2467 if (vparam.isNull()) {
2468 /* parameter was not defined */
2469 ret = -1;
2470 pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
2471 "parameter was not defined");
2472 goto clean_up;
2474 param = cast<PDOBoundParam>(vparam);
2475 if (stmt->dbh->conn()->support(PDOConnection::MethodQuoter)) {
2476 if (param->param_type == PDO_PARAM_LOB &&
2477 param->parameter.isResource()) {
2478 Variant buf = HHVM_FN(stream_get_contents)(
2479 param->parameter.toResource());
2480 if (!same(buf, false)) {
2481 if (!stmt->dbh->conn()->quoter(buf.toString(), plc->quoted,
2482 param->param_type)) {
2483 /* bork */
2484 ret = -1;
2485 setPDOError(stmt->error_code, stmt->dbh->conn()->error_code);
2486 goto clean_up;
2488 } else {
2489 pdo_raise_impl_error(stmt->dbh, stmt, "HY105",
2490 "Expected a stream resource");
2491 ret = -1;
2492 goto clean_up;
2494 } else {
2495 do {
2496 switch (param->parameter.getType()) {
2497 case KindOfUninit:
2498 case KindOfNull:
2499 plc->quoted = "NULL";
2500 continue;
2502 case KindOfInt64:
2503 case KindOfDouble:
2504 plc->quoted = param->parameter.toString();
2505 continue;
2507 case KindOfBoolean:
2508 param->parameter = param->parameter.toInt64();
2509 // fallthru
2510 case KindOfPersistentString:
2511 case KindOfString:
2512 case KindOfPersistentVec:
2513 case KindOfVec:
2514 case KindOfPersistentDict:
2515 case KindOfDict:
2516 case KindOfPersistentKeyset:
2517 case KindOfKeyset:
2518 case KindOfPersistentArray:
2519 case KindOfArray:
2520 case KindOfObject:
2521 case KindOfResource:
2522 case KindOfRef:
2523 if (!stmt->dbh->conn()->quoter(
2524 param->parameter.toString(),
2525 plc->quoted,
2526 param->param_type)) {
2527 /* bork */
2528 ret = -1;
2529 setPDOError(stmt->error_code, stmt->dbh->conn()->error_code);
2530 goto clean_up;
2532 continue;
2534 not_reached();
2535 } while (0);
2537 } else {
2538 plc->quoted = param->parameter.toString();
2540 newbuffer_len += plc->quoted.size();
2543 rewrite:
2544 /* allocate output buffer */
2545 out = String(newbuffer_len, ReserveString);
2546 newbuffer = out.mutableData();
2548 /* and build the query */
2549 plc = placeholders;
2550 ptr = in.data();
2552 do {
2553 t = plc->pos - ptr;
2554 if (t) {
2555 memcpy(newbuffer, ptr, t);
2556 newbuffer += t;
2558 memcpy(newbuffer, plc->quoted.data(), plc->quoted.size());
2559 newbuffer += plc->quoted.size();
2560 ptr = plc->pos + plc->len;
2562 plc = plc->next;
2563 } while (plc);
2565 t = (in.data() + in.size()) - ptr;
2566 if (t) {
2567 memcpy(newbuffer, ptr, t);
2568 newbuffer += t;
2570 out.setSize(newbuffer - out.data());
2572 ret = 1;
2573 goto clean_up;
2575 } else if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
2576 /* rewrite ? to :pdoX */
2577 StringBuffer idxbuf;
2578 const char *tmpl = stmt->named_rewrite_template ?
2579 stmt->named_rewrite_template : ":pdo%d";
2580 int bind_no = 1;
2582 newbuffer_len = in.size();
2584 for (plc = placeholders; plc; plc = plc->next) {
2585 int skip_map = 0;
2586 String name(plc->pos, plc->len, CopyString);
2588 /* check if bound parameter is already available */
2589 if (!strcmp(name.c_str(), "?") ||
2590 !stmt->bound_param_map.exists(name)) {
2591 idxbuf.printf(tmpl, bind_no++);
2592 } else {
2593 idxbuf.clear();
2594 idxbuf.append(stmt->bound_param_map[name].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, plc->quoted);
2606 /* map number to name */
2607 stmt->bound_param_map.set(plc->bindno, plc->quoted);
2610 goto rewrite;
2612 } else {
2613 /* rewrite :name to ? */
2615 newbuffer_len = in.size();
2617 for (plc = placeholders; plc; plc = plc->next) {
2618 String name(plc->pos, plc->len, CopyString);
2619 stmt->bound_param_map.set(plc->bindno, name);
2620 plc->quoted = "?";
2623 goto rewrite;
2626 clean_up:
2628 while (placeholders) {
2629 plc = placeholders;
2630 placeholders = plc->next;
2631 plc->quoted.reset();
2632 req::free(plc);
2635 return ret;
2638 ///////////////////////////////////////////////////////////////////////////////
2639 // PDOStatement
2641 const StaticString s_PDOStatement("PDOStatement");
2643 PDOStatementData::PDOStatementData() : m_rowIndex(-1) {
2646 PDOStatementData::~PDOStatementData() { }
2648 static Variant HHVM_METHOD(PDOStatement, execute,
2649 const Variant& paramsV /* = null_array */) {
2650 auto data = Native::data<PDOStatementData>(this_);
2651 auto params = paramsV.isNull() ? null_array : paramsV.toArray();
2653 SYNC_VM_REGS_SCOPED();
2655 if (data->m_stmt == nullptr) {
2656 return init_null_variant;
2659 setPDOErrorNone(data->m_stmt->error_code);
2661 if (!params.empty()) {
2662 data->m_stmt->bound_params.reset();
2663 for (ArrayIter iter(params); iter; ++iter) {
2664 auto param = req::make<PDOBoundParam>();
2665 param->param_type = PDO_PARAM_STR;
2666 param->parameter = iter.second();
2667 param->stmt = NULL;
2669 if (iter.first().isString()) {
2670 param->name = iter.first().toString();
2671 param->paramno = -1;
2672 } else {
2673 int64_t num_index = iter.first().toInt64();
2674 /* we're okay to be zero based here */
2675 if (num_index < 0) {
2676 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt,
2677 "HY093", nullptr);
2678 return false;
2680 param->paramno = num_index;
2683 if (!really_register_bound_param(param, data->m_stmt, true)) {
2684 return false;
2689 int ret = 1;
2690 if (PDO_PLACEHOLDER_NONE == data->m_stmt->supports_placeholders) {
2691 /* handle the emulated parameter binding, m_stmt->active_query_string
2692 holds the query with binds expanded and quoted. */
2693 ret = pdo_parse_params(data->m_stmt, data->m_stmt->query_string,
2694 data->m_stmt->active_query_string);
2695 if (ret == 0) { /* no changes were made */
2696 data->m_stmt->active_query_string = data->m_stmt->query_string;
2697 ret = 1;
2698 } else if (ret == -1) {
2699 /* something broke */
2700 PDO_HANDLE_STMT_ERR(data->m_stmt);
2701 return false;
2703 } else if (!dispatch_param_event(data->m_stmt, PDO_PARAM_EVT_EXEC_PRE)) {
2704 PDO_HANDLE_STMT_ERR(data->m_stmt);
2705 return false;
2707 if (data->m_stmt->executer()) {
2708 data->m_stmt->active_query_string.reset();
2709 if (!data->m_stmt->executed) {
2710 /* this is the first execute */
2712 if (data->m_stmt->dbh->conn()->alloc_own_columns
2713 && data->m_stmt->columns.empty()) {
2714 /* for "big boy" drivers, we need to allocate memory to fetch
2715 * the results into, so lets do that now */
2716 ret = pdo_stmt_describe_columns(data->m_stmt);
2719 data->m_stmt->executed = 1;
2722 if (ret && !dispatch_param_event(data->m_stmt, PDO_PARAM_EVT_EXEC_POST)) {
2723 return false;
2726 return (bool)ret;
2728 data->m_stmt->active_query_string.reset();
2729 PDO_HANDLE_STMT_ERR(data->m_stmt);
2730 return false;
2733 static Variant HHVM_METHOD(PDOStatement, fetch, int64_t how = 0,
2734 int64_t orientation = PDO_FETCH_ORI_NEXT,
2735 int64_t offset = 0) {
2736 auto data = Native::data<PDOStatementData>(this_);
2738 SYNC_VM_REGS_SCOPED();
2740 if (data->m_stmt == nullptr) {
2741 return false;
2744 setPDOErrorNone(data->m_stmt->error_code);
2745 if (!pdo_stmt_verify_mode(data->m_stmt, how, false)) {
2746 return false;
2749 Variant ret;
2750 if (!do_fetch(data->m_stmt, true, ret, (PDOFetchType)how,
2751 (PDOFetchOrientation)orientation, offset, NULL)) {
2752 PDO_HANDLE_STMT_ERR(data->m_stmt);
2753 return false;
2755 return ret;
2758 static Variant HHVM_METHOD(PDOStatement, fetchobject,
2759 const String& class_name /* = null_string */,
2760 const Variant& ctor_args /* = null */) {
2761 auto data = Native::data<PDOStatementData>(this_);
2762 if (data->m_stmt == nullptr) {
2763 return false;
2766 setPDOErrorNone(data->m_stmt->error_code);
2767 if (!pdo_stmt_verify_mode(data->m_stmt, PDO_FETCH_CLASS, false)) {
2768 return false;
2771 String old_clsname = data->m_stmt->fetch.clsname;
2772 Variant old_ctor_args = data->m_stmt->fetch.ctor_args;
2773 bool error = false;
2775 data->m_stmt->fetch.clsname = class_name;
2776 if (class_name.empty()) {
2777 data->m_stmt->fetch.clsname = "stdclass";
2779 if (!HHVM_FN(class_exists)(data->m_stmt->fetch.clsname)) {
2780 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt, "HY000",
2781 "Could not find user-supplied class");
2782 error = true;
2784 if (!ctor_args.isNull() && !ctor_args.isArray()) {
2785 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt, "HY000",
2786 "ctor_args must be either NULL or an array");
2787 error = true;
2789 data->m_stmt->fetch.ctor_args = ctor_args;
2791 Variant ret;
2792 if (!error && !do_fetch(data->m_stmt, true, ret, PDO_FETCH_CLASS,
2793 PDO_FETCH_ORI_NEXT, 0, NULL)) {
2794 error = true;
2796 if (error) {
2797 PDO_HANDLE_STMT_ERR(data->m_stmt);
2800 data->m_stmt->fetch.clsname = old_clsname;
2801 data->m_stmt->fetch.ctor_args = old_ctor_args;
2802 if (error) {
2803 return false;
2805 return ret;
2808 static Variant HHVM_METHOD(PDOStatement, fetchcolumn,
2809 int64_t column_numner /* = 0 */) {
2810 auto data = Native::data<PDOStatementData>(this_);
2811 if (data->m_stmt == nullptr) {
2812 return false;
2815 setPDOErrorNone(data->m_stmt->error_code);
2816 if (!do_fetch_common(data->m_stmt, PDO_FETCH_ORI_NEXT, 0, true)) {
2817 PDO_HANDLE_STMT_ERR(data->m_stmt);
2818 return false;
2820 Variant ret;
2821 fetch_value(data->m_stmt, ret, column_numner, nullptr);
2822 return ret;
2825 static Variant HHVM_METHOD(PDOStatement, fetchall, int64_t how /* = 0 */,
2826 const Variant& class_name /* = null */,
2827 const Variant& ctor_args /* = null */) {
2828 auto self = Native::data<PDOStatementData>(this_);
2829 if (self->m_stmt == nullptr) {
2830 return false;
2833 if (!pdo_stmt_verify_mode(self->m_stmt, how, true)) {
2834 return false;
2837 String old_clsname = self->m_stmt->fetch.clsname;
2838 Variant old_ctor_args = self->m_stmt->fetch.ctor_args;
2839 int error = 0;
2841 switch (how & ~PDO_FETCH_FLAGS) {
2842 case PDO_FETCH_CLASS:
2843 self->m_stmt->fetch.clsname = class_name.toString();
2844 if (class_name.isNull()) {
2845 self->m_stmt->fetch.clsname = "stdclass";
2847 if (!HHVM_FN(class_exists)(self->m_stmt->fetch.clsname)) {
2848 pdo_raise_impl_error(self->m_stmt->dbh, self->m_stmt, "HY000",
2849 "Could not find user-supplied class");
2850 error = 1;
2852 if (!ctor_args.isNull() && !ctor_args.isArray()) {
2853 pdo_raise_impl_error(self->m_stmt->dbh, self->m_stmt, "HY000",
2854 "ctor_args must be either NULL or an array");
2855 error = 1;
2856 break;
2858 self->m_stmt->fetch.ctor_args = ctor_args;
2860 if (!error) {
2861 do_fetch_class_prepare(self->m_stmt);
2863 break;
2865 case PDO_FETCH_FUNC:
2866 if (!HHVM_FN(function_exists)(class_name.toString())) {
2867 pdo_raise_impl_error(self->m_stmt->dbh, self->m_stmt, "HY000",
2868 "no fetch function specified");
2869 error = 1;
2870 } else {
2871 self->m_stmt->fetch.func = class_name.toString();
2872 do_fetch_func_prepare(self->m_stmt);
2874 break;
2876 case PDO_FETCH_COLUMN:
2877 if (class_name.isNull()) {
2878 self->m_stmt->fetch.column = how & PDO_FETCH_GROUP ? -1 : 0;
2879 } else {
2880 self->m_stmt->fetch.column = class_name.toInt64();
2882 if (!ctor_args.isNull()) {
2883 pdo_raise_impl_error(self->m_stmt->dbh, self->m_stmt, "HY000",
2884 "Third parameter not allowed for "
2885 "PDO::FETCH_COLUMN");
2886 error = 1;
2888 break;
2891 int flags = how & PDO_FETCH_FLAGS;
2893 if ((how & ~PDO_FETCH_FLAGS) == PDO_FETCH_USE_DEFAULT) {
2894 flags |= self->m_stmt->default_fetch_type & PDO_FETCH_FLAGS;
2895 how |= self->m_stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
2898 Variant *return_all = NULL;
2899 Variant return_value;
2900 Variant data;
2901 if (!error) {
2902 setPDOErrorNone(self->m_stmt->error_code);
2904 if ((how & PDO_FETCH_GROUP) || how == PDO_FETCH_KEY_PAIR ||
2905 (how == PDO_FETCH_USE_DEFAULT &&
2906 self->m_stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)) {
2907 return_value = Array::Create();
2908 return_all = &return_value;
2910 if (!do_fetch(self->m_stmt, true, data, (PDOFetchType)(how | flags),
2911 PDO_FETCH_ORI_NEXT, 0, return_all)) {
2912 error = 2;
2915 if (!error) {
2916 if ((how & PDO_FETCH_GROUP)) {
2917 do {
2918 data.unset();
2919 } while (do_fetch(self->m_stmt, true, data, (PDOFetchType)(how | flags),
2920 PDO_FETCH_ORI_NEXT, 0, return_all));
2921 } else if (how == PDO_FETCH_KEY_PAIR ||
2922 (how == PDO_FETCH_USE_DEFAULT &&
2923 self->m_stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)) {
2924 while (do_fetch(self->m_stmt, true, data, (PDOFetchType)(how | flags),
2925 PDO_FETCH_ORI_NEXT, 0, return_all)) {
2926 continue;
2928 } else {
2929 return_value = Array::Create();
2930 do {
2931 return_value.toArrRef().append(data);
2932 data.unset();
2933 } while (do_fetch(self->m_stmt, true, data, (PDOFetchType)(how | flags),
2934 PDO_FETCH_ORI_NEXT, 0, NULL));
2938 self->m_stmt->fetch.clsname = old_clsname;
2939 self->m_stmt->fetch.ctor_args = old_ctor_args;
2941 if (error) {
2942 PDO_HANDLE_STMT_ERR(self->m_stmt);
2943 if (error != 2) {
2944 return false;
2947 /* on no results, return an empty array */
2948 if (!return_value.isArray()) {
2949 return_value = Array::Create();
2952 return return_value;
2955 static bool HHVM_METHOD(PDOStatement, bindvalue, const Variant& paramno,
2956 const Variant& param,
2957 int64_t type /* = PDO_PARAM_STR */) {
2958 auto data = Native::data<PDOStatementData>(this_);
2959 if (data->m_stmt == nullptr) {
2960 return false;
2963 return register_bound_param(paramno, param, type, 0,
2964 uninit_null(), data->m_stmt, true);
2967 static bool HHVM_METHOD(PDOStatement, bindparam, const Variant& paramno,
2968 VRefParam param, int64_t type /* = PDO_PARAM_STR */,
2969 int64_t max_value_len /* = 0 */,
2970 const Variant& driver_params /*= null */) {
2971 auto data = Native::data<PDOStatementData>(this_);
2972 if (data->m_stmt == nullptr) {
2973 return false;
2976 return register_bound_param(paramno, ref(param), type, max_value_len,
2977 driver_params, data->m_stmt, true);
2980 static bool HHVM_METHOD(PDOStatement, bindcolumn, const Variant& paramno,
2981 VRefParam param, int64_t type /* = PDO_PARAM_STR */,
2982 int64_t max_value_len /* = 0 */,
2983 const Variant& driver_params /* = null */) {
2984 auto data = Native::data<PDOStatementData>(this_);
2985 if (data->m_stmt == nullptr) {
2986 return false;
2989 return register_bound_param(paramno, ref(param), type, max_value_len,
2990 driver_params, data->m_stmt, false);
2993 static int64_t HHVM_METHOD(PDOStatement, rowcount) {
2994 auto data = Native::data<PDOStatementData>(this_);
2995 if (data->m_stmt == nullptr) {
2996 return 0;
2999 return data->m_stmt->row_count;
3002 static Variant HHVM_METHOD(PDOStatement, errorcode) {
3003 auto data = Native::data<PDOStatementData>(this_);
3004 if (data->m_stmt == nullptr) {
3005 return false;
3007 if (data->m_stmt->error_code[0] == '\0') {
3008 return init_null();
3010 return String(data->m_stmt->error_code, CopyString);
3013 static Array HHVM_METHOD(PDOStatement, errorinfo) {
3014 auto data = Native::data<PDOStatementData>(this_);
3015 if (data->m_stmt == nullptr) {
3016 return null_array;
3019 Array ret;
3020 ret.append(String(data->m_stmt->error_code, CopyString));
3022 if (data->m_stmt->dbh->conn()->support(PDOConnection::MethodFetchErr)) {
3023 data->m_stmt->dbh->conn()->fetchErr(data->m_stmt.get(), ret);
3026 int error_count = ret.size();
3027 int error_expected_count = 3;
3028 if (error_expected_count > error_count) {
3029 int error_count_diff = error_expected_count - error_count;
3030 for (int i = 0; i < error_count_diff; i++) {
3031 ret.append(uninit_null());
3034 return ret;
3037 static Variant HHVM_METHOD(PDOStatement, setattribute, int64_t attribute,
3038 const Variant& value) {
3039 auto data = Native::data<PDOStatementData>(this_);
3040 if (data->m_stmt == nullptr) {
3041 return false;
3044 if (!data->m_stmt->support(PDOStatement::MethodSetAttribute)) {
3045 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt, "IM001",
3046 "This driver doesn't support setting attributes");
3047 return false;
3050 setPDOErrorNone(data->m_stmt->error_code);
3051 if (data->m_stmt->setAttribute(attribute, value)) {
3052 return true;
3054 PDO_HANDLE_STMT_ERR(data->m_stmt);
3055 return false;
3058 static Variant HHVM_METHOD(PDOStatement, getattribute, int64_t attribute) {
3059 auto data = Native::data<PDOStatementData>(this_);
3060 if (data->m_stmt == nullptr) {
3061 return false;
3064 Variant ret;
3065 if (!data->m_stmt->support(PDOStatement::MethodGetAttribute)) {
3066 if (!generic_stmt_attr_get(data->m_stmt, ret, attribute)) {
3067 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt, "IM001",
3068 "This driver doesn't support getting attributes");
3069 return false;
3071 return ret;
3074 setPDOErrorNone(data->m_stmt->error_code);
3075 switch (data->m_stmt->getAttribute(attribute, ret)) {
3076 case -1:
3077 PDO_HANDLE_STMT_ERR(data->m_stmt);
3078 return false;
3079 case 0:
3080 if (!generic_stmt_attr_get(data->m_stmt, ret, attribute)) {
3081 /* XXX: should do something better here */
3082 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt, "IM001",
3083 "driver doesn't support getting that attribute");
3084 return false;
3086 break;
3087 default:
3088 break;
3090 return ret;
3093 static int64_t HHVM_METHOD(PDOStatement, columncount) {
3094 auto data = Native::data<PDOStatementData>(this_);
3095 if (data->m_stmt == nullptr) {
3096 return 0;
3099 return data->m_stmt->column_count;
3102 const StaticString
3103 s_name("name"),
3104 s_len("len"),
3105 s_precision("precision"),
3106 s_pdo_type("pdo_type");
3108 static Variant HHVM_METHOD(PDOStatement, getcolumnmeta, int64_t column) {
3109 auto data = Native::data<PDOStatementData>(this_);
3110 if (data->m_stmt == nullptr) {
3111 return false;
3114 if (column < 0) {
3115 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt, "42P10",
3116 "column number must be non-negative");
3117 return false;
3120 if (!data->m_stmt->support(PDOStatement::MethodGetColumnMeta)) {
3121 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt, "IM001",
3122 "driver doesn't support meta data");
3123 return false;
3126 setPDOErrorNone(data->m_stmt->error_code);
3127 Array ret;
3128 if (!data->m_stmt->getColumnMeta(column, ret)) {
3129 PDO_HANDLE_STMT_ERR(data->m_stmt);
3130 return false;
3133 /* add stock items */
3134 auto col = cast<PDOColumn>(data->m_stmt->columns[column]);
3135 ret.set(s_name, col->name);
3136 ret.set(s_len, (int64_t)col->maxlen); /* FIXME: unsigned ? */
3137 ret.set(s_precision, (int64_t)col->precision);
3138 if (col->param_type != PDO_PARAM_ZVAL) {
3139 // if param_type is PDO_PARAM_ZVAL the driver has to provide correct data
3140 ret.set(s_pdo_type, (int64_t)col->param_type);
3142 return ret;
3145 static bool HHVM_METHOD(PDOStatement, setfetchmode,
3146 int64_t mode, const Array& _argv /* = null_array */) {
3147 auto data = Native::data<PDOStatementData>(this_);
3148 if (data->m_stmt == nullptr) {
3149 return false;
3151 int argc = _argv.size() + 1;
3153 return pdo_stmt_set_fetch_mode(data->m_stmt, argc, mode, _argv);
3156 static bool HHVM_METHOD(PDOStatement, nextrowset) {
3157 auto data = Native::data<PDOStatementData>(this_);
3158 if (data->m_stmt == nullptr) {
3159 return false;
3162 if (!data->m_stmt->support(PDOStatement::MethodNextRowset)) {
3163 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt, "IM001",
3164 "driver does not support multiple rowsets");
3165 return false;
3168 setPDOErrorNone(data->m_stmt->error_code);
3170 /* un-describe */
3171 if (!data->m_stmt->columns.empty()) {
3172 data->m_stmt->columns.clear();
3173 data->m_stmt->column_count = 0;
3176 if (!data->m_stmt->nextRowset()) {
3177 PDO_HANDLE_STMT_ERR(data->m_stmt);
3178 return false;
3181 pdo_stmt_describe_columns(data->m_stmt);
3182 return true;
3185 static bool HHVM_METHOD(PDOStatement, closecursor) {
3186 auto data = Native::data<PDOStatementData>(this_);
3187 if (data->m_stmt == nullptr) {
3188 return false;
3191 if (!data->m_stmt->support(PDOStatement::MethodCursorCloser)) {
3192 /* emulate it by fetching and discarding rows */
3193 do {
3194 while (data->m_stmt->fetcher(PDO_FETCH_ORI_NEXT, 0));
3195 // if (!data->t_nextrowset()) {
3196 if (HHVM_MN(PDOStatement, nextrowset)(this_)) {
3197 break;
3199 } while (true);
3200 data->m_stmt->executed = 0;
3201 return true;
3204 setPDOErrorNone(data->m_stmt->error_code);
3205 if (!data->m_stmt->cursorCloser()) {
3206 PDO_HANDLE_STMT_ERR(data->m_stmt);
3207 return false;
3209 data->m_stmt->executed = 0;
3210 return true;
3213 static Variant HHVM_METHOD(PDOStatement, debugdumpparams) {
3214 auto data = Native::data<PDOStatementData>(this_);
3215 if (data->m_stmt == nullptr) {
3216 return false;
3219 auto f = File::Open("php://output", "w");
3220 if (!f || f->isInvalid()) {
3221 return false;
3224 Array params;
3225 params.append(data->m_stmt->query_string.size());
3226 params.append(data->m_stmt->query_string.size());
3227 params.append(data->m_stmt->query_string.data());
3228 f->printf("SQL: [%d] %.*s\n", params);
3230 f->printf("Params: %d\n",
3231 make_packed_array(data->m_stmt->bound_params.size()));
3232 for (ArrayIter iter(data->m_stmt->bound_params); iter; ++iter) {
3233 if (iter.first().isString()) {
3234 String key = iter.first().toString();
3235 params = make_packed_array(key.size(), key.size(), key.data());
3236 f->printf("Key: Name: [%d] %.*s\n", params);
3237 } else {
3238 f->printf("Key: Position #%ld:\n",
3239 make_packed_array(iter.first().toInt64()));
3242 auto param = cast<PDOBoundParam>(iter.second());
3243 params.clear();
3244 params.append(param->paramno);
3245 params.append(param->name.size());
3246 params.append(param->name.size());
3247 params.append(param->name.data());
3248 params.append(param->is_param);
3249 params.append(param->param_type);
3250 f->printf("paramno=%d\nname=[%d] \"%.*s\"\nis_param=%d\nparam_type=%d\n",
3251 params);
3253 return true;
3256 static Variant HHVM_METHOD(PDOStatement, current) {
3257 auto data = Native::data<PDOStatementData>(this_);
3259 return data->m_row;
3262 static Variant HHVM_METHOD(PDOStatement, key) {
3263 auto data = Native::data<PDOStatementData>(this_);
3265 return data->m_rowIndex;
3268 static Variant HHVM_METHOD(PDOStatement, next) {
3269 auto data = Native::data<PDOStatementData>(this_);
3271 data->m_row = HHVM_MN(PDOStatement, fetch)(this_, PDO_FETCH_USE_DEFAULT);
3272 if (same(data->m_row, false)) {
3273 data->m_rowIndex = -1;
3274 } else {
3275 ++data->m_rowIndex;
3277 return init_null();
3280 static Variant HHVM_METHOD(PDOStatement, rewind) {
3281 auto data = Native::data<PDOStatementData>(this_);
3283 data->m_rowIndex = -1;
3284 HHVM_MN(PDOStatement, next)(this_);
3285 return init_null();
3288 static Variant HHVM_METHOD(PDOStatement, valid) {
3289 auto data = Native::data<PDOStatementData>(this_);
3291 return data->m_rowIndex >= 0;
3294 static Variant HHVM_METHOD(PDOStatement, __wakeup) {
3295 throw_pdo_exception(uninit_null(), uninit_null(),
3296 "You cannot serialize or unserialize "
3297 "PDOStatement instances");
3298 return init_null();
3301 static Variant HHVM_METHOD(PDOStatement, __sleep) {
3302 throw_pdo_exception(uninit_null(), uninit_null(),
3303 "You cannot serialize or unserialize "
3304 "PDOStatement instances");
3305 return init_null();
3308 ///////////////////////////////////////////////////////////////////////////////
3311 static struct PDOExtension final : Extension {
3312 PDOExtension() : Extension("pdo", " 1.0.4dev") {}
3314 #ifdef ENABLE_EXTENSION_PDO_MYSQL
3315 std::string mysql_default_socket;
3317 void moduleLoad(const IniSetting::Map& /*ini*/, Hdf /*config*/) override {
3318 IniSetting::Bind(this, IniSetting::PHP_INI_SYSTEM,
3319 "pdo_mysql.default_socket", nullptr,
3320 &mysql_default_socket);
3322 #endif
3324 void moduleInit() override {
3325 HHVM_FE(pdo_drivers);
3326 HHVM_ME(PDO, __construct);
3327 HHVM_ME(PDO, prepare);
3328 HHVM_ME(PDO, begintransaction);
3329 HHVM_ME(PDO, commit);
3330 HHVM_ME(PDO, intransaction);
3331 HHVM_ME(PDO, rollback);
3332 HHVM_ME(PDO, setattribute);
3333 HHVM_ME(PDO, getattribute);
3334 HHVM_ME(PDO, exec);
3335 HHVM_ME(PDO, lastinsertid);
3336 HHVM_ME(PDO, errorcode);
3337 HHVM_ME(PDO, errorinfo);
3338 HHVM_ME(PDO, query);
3339 HHVM_ME(PDO, quote);
3340 HHVM_ME(PDO, sqlitecreatefunction);
3341 HHVM_ME(PDO, sqlitecreateaggregate);
3342 HHVM_ME(PDO, __wakeup);
3343 HHVM_ME(PDO, __sleep);
3344 HHVM_STATIC_ME(PDO, getAvailableDrivers);
3345 HHVM_ME(PDOStatement, execute);
3346 HHVM_ME(PDOStatement, fetch);
3347 HHVM_ME(PDOStatement, fetchobject);
3348 HHVM_ME(PDOStatement, fetchcolumn);
3349 HHVM_ME(PDOStatement, fetchall);
3350 HHVM_ME(PDOStatement, bindvalue);
3351 HHVM_ME(PDOStatement, bindparam);
3352 HHVM_ME(PDOStatement, bindcolumn);
3353 HHVM_ME(PDOStatement, rowcount);
3354 HHVM_ME(PDOStatement, errorcode);
3355 HHVM_ME(PDOStatement, errorinfo);
3356 HHVM_ME(PDOStatement, setattribute);
3357 HHVM_ME(PDOStatement, getattribute);
3358 HHVM_ME(PDOStatement, columncount);
3359 HHVM_ME(PDOStatement, getcolumnmeta);
3360 HHVM_ME(PDOStatement, setfetchmode);
3361 HHVM_ME(PDOStatement, nextrowset);
3362 HHVM_ME(PDOStatement, closecursor);
3363 HHVM_ME(PDOStatement, debugdumpparams);
3364 HHVM_ME(PDOStatement, current);
3365 HHVM_ME(PDOStatement, key);
3366 HHVM_ME(PDOStatement, next);
3367 HHVM_ME(PDOStatement, rewind);
3368 HHVM_ME(PDOStatement, valid);
3369 HHVM_ME(PDOStatement, __wakeup);
3370 HHVM_ME(PDOStatement, __sleep);
3372 HHVM_RCC_INT(PDO, PARAM_BOOL, PDO_PARAM_BOOL);
3373 HHVM_RCC_INT(PDO, PARAM_NULL, PDO_PARAM_NULL);
3374 HHVM_RCC_INT(PDO, PARAM_INT, PDO_PARAM_INT);
3375 HHVM_RCC_INT(PDO, PARAM_STR, PDO_PARAM_STR);
3376 HHVM_RCC_INT(PDO, PARAM_LOB, PDO_PARAM_LOB);
3377 HHVM_RCC_INT(PDO, PARAM_STMT, PDO_PARAM_STMT);
3378 HHVM_RCC_INT(PDO, PARAM_INPUT_OUTPUT, PDO_PARAM_INPUT_OUTPUT);
3379 HHVM_RCC_INT(PDO, PARAM_EVT_ALLOC, PDO_PARAM_EVT_ALLOC);
3380 HHVM_RCC_INT(PDO, PARAM_EVT_FREE, PDO_PARAM_EVT_FREE);
3381 HHVM_RCC_INT(PDO, PARAM_EVT_EXEC_PRE, PDO_PARAM_EVT_EXEC_PRE);
3382 HHVM_RCC_INT(PDO, PARAM_EVT_EXEC_POST, PDO_PARAM_EVT_EXEC_POST);
3383 HHVM_RCC_INT(PDO, PARAM_EVT_FETCH_PRE, PDO_PARAM_EVT_FETCH_PRE);
3384 HHVM_RCC_INT(PDO, PARAM_EVT_FETCH_POST, PDO_PARAM_EVT_FETCH_POST);
3385 HHVM_RCC_INT(PDO, PARAM_EVT_NORMALIZE, PDO_PARAM_EVT_NORMALIZE);
3386 HHVM_RCC_INT(PDO, FETCH_USE_DEFAULT, PDO_FETCH_USE_DEFAULT);
3387 HHVM_RCC_INT(PDO, FETCH_LAZY, PDO_FETCH_LAZY);
3388 HHVM_RCC_INT(PDO, FETCH_ASSOC, PDO_FETCH_ASSOC);
3389 HHVM_RCC_INT(PDO, FETCH_NUM, PDO_FETCH_NUM);
3390 HHVM_RCC_INT(PDO, FETCH_BOTH, PDO_FETCH_BOTH);
3391 HHVM_RCC_INT(PDO, FETCH_OBJ, PDO_FETCH_OBJ);
3392 HHVM_RCC_INT(PDO, FETCH_BOUND, PDO_FETCH_BOUND);
3393 HHVM_RCC_INT(PDO, FETCH_COLUMN, PDO_FETCH_COLUMN);
3394 HHVM_RCC_INT(PDO, FETCH_CLASS, PDO_FETCH_CLASS);
3395 HHVM_RCC_INT(PDO, FETCH_INTO, PDO_FETCH_INTO);
3396 HHVM_RCC_INT(PDO, FETCH_FUNC, PDO_FETCH_FUNC);
3397 HHVM_RCC_INT(PDO, FETCH_GROUP, PDO_FETCH_GROUP);
3398 HHVM_RCC_INT(PDO, FETCH_UNIQUE, PDO_FETCH_UNIQUE);
3399 HHVM_RCC_INT(PDO, FETCH_KEY_PAIR, PDO_FETCH_KEY_PAIR);
3400 HHVM_RCC_INT(PDO, FETCH_CLASSTYPE, PDO_FETCH_CLASSTYPE);
3401 HHVM_RCC_INT(PDO, FETCH_SERIALIZE, PDO_FETCH_SERIALIZE);
3402 HHVM_RCC_INT(PDO, FETCH_PROPS_LATE, PDO_FETCH_PROPS_LATE);
3403 HHVM_RCC_INT(PDO, FETCH_NAMED, PDO_FETCH_NAMED);
3404 HHVM_RCC_INT(PDO, ATTR_AUTOCOMMIT, PDO_ATTR_AUTOCOMMIT);
3405 HHVM_RCC_INT(PDO, ATTR_PREFETCH, PDO_ATTR_PREFETCH);
3406 HHVM_RCC_INT(PDO, ATTR_TIMEOUT, PDO_ATTR_TIMEOUT);
3407 HHVM_RCC_INT(PDO, ATTR_ERRMODE, PDO_ATTR_ERRMODE);
3408 HHVM_RCC_INT(PDO, ATTR_SERVER_VERSION, PDO_ATTR_SERVER_VERSION);
3409 HHVM_RCC_INT(PDO, ATTR_CLIENT_VERSION, PDO_ATTR_CLIENT_VERSION);
3410 HHVM_RCC_INT(PDO, ATTR_SERVER_INFO, PDO_ATTR_SERVER_INFO);
3411 HHVM_RCC_INT(PDO, ATTR_CONNECTION_STATUS, PDO_ATTR_CONNECTION_STATUS);
3412 HHVM_RCC_INT(PDO, ATTR_CASE, PDO_ATTR_CASE);
3413 HHVM_RCC_INT(PDO, ATTR_CURSOR_NAME, PDO_ATTR_CURSOR_NAME);
3414 HHVM_RCC_INT(PDO, ATTR_CURSOR, PDO_ATTR_CURSOR);
3415 HHVM_RCC_INT(PDO, ATTR_ORACLE_NULLS, PDO_ATTR_ORACLE_NULLS);
3416 HHVM_RCC_INT(PDO, ATTR_PERSISTENT, PDO_ATTR_PERSISTENT);
3417 HHVM_RCC_INT(PDO, ATTR_STATEMENT_CLASS, PDO_ATTR_STATEMENT_CLASS);
3418 HHVM_RCC_INT(PDO, ATTR_FETCH_TABLE_NAMES, PDO_ATTR_FETCH_TABLE_NAMES);
3419 HHVM_RCC_INT(PDO, ATTR_FETCH_CATALOG_NAMES, PDO_ATTR_FETCH_CATALOG_NAMES);
3420 HHVM_RCC_INT(PDO, ATTR_DRIVER_NAME, PDO_ATTR_DRIVER_NAME);
3421 HHVM_RCC_INT(PDO, ATTR_STRINGIFY_FETCHES, PDO_ATTR_STRINGIFY_FETCHES);
3422 HHVM_RCC_INT(PDO, ATTR_MAX_COLUMN_LEN, PDO_ATTR_MAX_COLUMN_LEN);
3423 HHVM_RCC_INT(PDO, ATTR_EMULATE_PREPARES, PDO_ATTR_EMULATE_PREPARES);
3424 HHVM_RCC_INT(PDO, ATTR_DEFAULT_FETCH_MODE, PDO_ATTR_DEFAULT_FETCH_MODE);
3425 HHVM_RCC_INT(PDO, ERRMODE_SILENT, PDO_ERRMODE_SILENT);
3426 HHVM_RCC_INT(PDO, ERRMODE_WARNING, PDO_ERRMODE_WARNING);
3427 HHVM_RCC_INT(PDO, ERRMODE_EXCEPTION, PDO_ERRMODE_EXCEPTION);
3428 HHVM_RCC_INT(PDO, CASE_NATURAL, PDO_CASE_NATURAL);
3429 HHVM_RCC_INT(PDO, CASE_LOWER, PDO_CASE_LOWER);
3430 HHVM_RCC_INT(PDO, CASE_UPPER, PDO_CASE_UPPER);
3431 HHVM_RCC_INT(PDO, NULL_NATURAL, PDO_NULL_NATURAL);
3432 HHVM_RCC_INT(PDO, NULL_EMPTY_STRING, PDO_NULL_EMPTY_STRING);
3433 HHVM_RCC_INT(PDO, NULL_TO_STRING, PDO_NULL_TO_STRING);
3434 HHVM_RCC_INT(PDO, FETCH_ORI_NEXT, PDO_FETCH_ORI_NEXT);
3435 HHVM_RCC_INT(PDO, FETCH_ORI_PRIOR, PDO_FETCH_ORI_PRIOR);
3436 HHVM_RCC_INT(PDO, FETCH_ORI_FIRST, PDO_FETCH_ORI_FIRST);
3437 HHVM_RCC_INT(PDO, FETCH_ORI_LAST, PDO_FETCH_ORI_LAST);
3438 HHVM_RCC_INT(PDO, FETCH_ORI_ABS, PDO_FETCH_ORI_ABS);
3439 HHVM_RCC_INT(PDO, FETCH_ORI_REL, PDO_FETCH_ORI_REL);
3440 HHVM_RCC_INT(PDO, CURSOR_FWDONLY, PDO_CURSOR_FWDONLY);
3441 HHVM_RCC_INT(PDO, CURSOR_SCROLL, PDO_CURSOR_SCROLL);
3442 #ifdef ENABLE_EXTENSION_PDO_MYSQL
3443 HHVM_RCC_INT(PDO, MYSQL_ATTR_USE_BUFFERED_QUERY,
3444 PDO_MYSQL_ATTR_USE_BUFFERED_QUERY);
3445 HHVM_RCC_INT(PDO, MYSQL_ATTR_LOCAL_INFILE, PDO_MYSQL_ATTR_LOCAL_INFILE);
3446 HHVM_RCC_INT(PDO, MYSQL_ATTR_MAX_BUFFER_SIZE,
3447 PDO_MYSQL_ATTR_MAX_BUFFER_SIZE);
3448 HHVM_RCC_INT(PDO, MYSQL_ATTR_INIT_COMMAND, PDO_MYSQL_ATTR_INIT_COMMAND);
3449 HHVM_RCC_INT(PDO, MYSQL_ATTR_READ_DEFAULT_FILE,
3450 PDO_MYSQL_ATTR_READ_DEFAULT_FILE);
3451 HHVM_RCC_INT(PDO, MYSQL_ATTR_READ_DEFAULT_GROUP,
3452 PDO_MYSQL_ATTR_READ_DEFAULT_GROUP);
3453 HHVM_RCC_INT(PDO, MYSQL_ATTR_COMPRESS, PDO_MYSQL_ATTR_COMPRESS);
3454 HHVM_RCC_INT(PDO, MYSQL_ATTR_DIRECT_QUERY, PDO_MYSQL_ATTR_DIRECT_QUERY);
3455 HHVM_RCC_INT(PDO, MYSQL_ATTR_FOUND_ROWS, PDO_MYSQL_ATTR_FOUND_ROWS);
3456 HHVM_RCC_INT(PDO, MYSQL_ATTR_IGNORE_SPACE, PDO_MYSQL_ATTR_IGNORE_SPACE);
3457 HHVM_RCC_INT(PDO, MYSQL_ATTR_SSL_CA, PDO_MYSQL_ATTR_SSL_CA);
3458 HHVM_RCC_INT(PDO, MYSQL_ATTR_SSL_CAPATH, PDO_MYSQL_ATTR_SSL_CAPATH);
3459 HHVM_RCC_INT(PDO, MYSQL_ATTR_SSL_CERT, PDO_MYSQL_ATTR_SSL_CERT);
3460 HHVM_RCC_INT(PDO, MYSQL_ATTR_SSL_KEY, PDO_MYSQL_ATTR_SSL_KEY);
3461 HHVM_RCC_INT(PDO, MYSQL_ATTR_SSL_CIPHER, PDO_MYSQL_ATTR_SSL_CIPHER);
3462 HHVM_RCC_INT(PDO, MYSQL_ATTR_MULTI_STATEMENTS,
3463 PDO_MYSQL_ATTR_MULTI_STATEMENTS);
3464 HHVM_RCC_INT(PDO, HH_MYSQL_ATTR_READ_TIMEOUT,
3465 HH_PDO_MYSQL_ATTR_READ_TIMEOUT);
3466 HHVM_RCC_INT(PDO, HH_MYSQL_ATTR_WRITE_TIMEOUT,
3467 HH_PDO_MYSQL_ATTR_WRITE_TIMEOUT);
3468 #endif
3469 HHVM_RCC_STR(PDO, ERR_NONE, PDO_ERR_NONE);
3471 Native::registerNativeDataInfo<PDOData>(
3472 s_PDO.get(), Native::NDIFlags::NO_SWEEP);
3473 Native::registerNativeDataInfo<PDOStatementData>(
3474 s_PDOStatement.get(), Native::NDIFlags::NO_SWEEP);
3476 loadSystemlib("pdo");
3478 } s_pdo_extension;
3480 //////////////////////////////////////////////////////////////////////////////