Manual remaining class -> struct conversion
[hiphop-php.git] / hphp / runtime / ext / pdo / ext_pdo.cpp
blobe0dfec469bb22f4748276d45836074666dc4890c
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2016 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 +----------------------------------------------------------------------+
17 /* @nolint */
19 #include "hphp/runtime/ext/pdo/ext_pdo.h"
21 #include <set>
22 #include <string>
23 #include <unordered_map>
25 #include "hphp/system/systemlib.h"
26 #include "hphp/util/string-vsnprintf.h"
28 #include "hphp/runtime/base/array-init.h"
29 #include "hphp/runtime/base/class-info.h"
30 #include "hphp/runtime/base/comparisons.h"
31 #include "hphp/runtime/base/file.h"
32 #include "hphp/runtime/base/ini-setting.h"
33 #include "hphp/runtime/base/request-event-handler.h"
34 #include "hphp/runtime/base/request-local.h"
35 #include "hphp/runtime/base/string-buffer.h"
36 #include "hphp/runtime/vm/jit/translator-inline.h"
38 #include "hphp/runtime/ext/array/ext_array.h"
39 #include "hphp/runtime/ext/pdo/pdo_driver.h"
40 #ifdef ENABLE_EXTENSION_PDO_MYSQL
41 #include "hphp/runtime/ext/pdo_mysql/pdo_mysql.h"
42 #endif
43 #ifdef ENABLE_EXTENSION_PDO_SQLITE
44 #include "hphp/runtime/ext/pdo_sqlite/pdo_sqlite.h"
45 #endif
46 #include "hphp/runtime/ext/std/ext_std_classobj.h"
47 #include "hphp/runtime/ext/std/ext_std_function.h"
48 #include "hphp/runtime/ext/stream/ext_stream.h"
49 #include "hphp/runtime/ext/string/ext_string.h"
51 #define PDO_HANDLE_DBH_ERR(dbh) \
52 if (strcmp(dbh->conn()->error_code, PDO_ERR_NONE)) { \
53 pdo_handle_error(dbh, nullptr); \
54 } \
56 #define PDO_HANDLE_STMT_ERR(stmt) \
57 if (strcmp(stmt->error_code, PDO_ERR_NONE)) { \
58 pdo_handle_error(stmt->dbh, stmt); \
59 } \
61 namespace HPHP {
63 using std::string;
64 ///////////////////////////////////////////////////////////////////////////////
65 // PDO constants
67 const int64_t q_PDO$$PARAM_BOOL = PDO_PARAM_BOOL;
68 const int64_t q_PDO$$PARAM_NULL = PDO_PARAM_NULL;
69 const int64_t q_PDO$$PARAM_INT = PDO_PARAM_INT;
70 const int64_t q_PDO$$PARAM_STR = PDO_PARAM_STR;
71 const int64_t q_PDO$$PARAM_LOB = PDO_PARAM_LOB;
72 const int64_t q_PDO$$PARAM_STMT = PDO_PARAM_STMT;
73 const int64_t q_PDO$$PARAM_INPUT_OUTPUT = PDO_PARAM_INPUT_OUTPUT;
75 const int64_t q_PDO$$PARAM_EVT_ALLOC = PDO_PARAM_EVT_ALLOC;
76 const int64_t q_PDO$$PARAM_EVT_FREE = PDO_PARAM_EVT_FREE;
77 const int64_t q_PDO$$PARAM_EVT_EXEC_PRE = PDO_PARAM_EVT_EXEC_PRE;
78 const int64_t q_PDO$$PARAM_EVT_EXEC_POST = PDO_PARAM_EVT_EXEC_POST;
79 const int64_t q_PDO$$PARAM_EVT_FETCH_PRE = PDO_PARAM_EVT_FETCH_PRE;
80 const int64_t q_PDO$$PARAM_EVT_FETCH_POST = PDO_PARAM_EVT_FETCH_POST;
81 const int64_t q_PDO$$PARAM_EVT_NORMALIZE = PDO_PARAM_EVT_NORMALIZE;
83 const int64_t q_PDO$$FETCH_USE_DEFAULT = PDO_FETCH_USE_DEFAULT;
84 const int64_t q_PDO$$FETCH_LAZY = PDO_FETCH_LAZY;
85 const int64_t q_PDO$$FETCH_ASSOC = PDO_FETCH_ASSOC;
86 const int64_t q_PDO$$FETCH_NUM = PDO_FETCH_NUM;
87 const int64_t q_PDO$$FETCH_BOTH = PDO_FETCH_BOTH;
88 const int64_t q_PDO$$FETCH_OBJ = PDO_FETCH_OBJ;
89 const int64_t q_PDO$$FETCH_BOUND = PDO_FETCH_BOUND;
90 const int64_t q_PDO$$FETCH_COLUMN = PDO_FETCH_COLUMN;
91 const int64_t q_PDO$$FETCH_CLASS = PDO_FETCH_CLASS;
92 const int64_t q_PDO$$FETCH_INTO = PDO_FETCH_INTO;
93 const int64_t q_PDO$$FETCH_FUNC = PDO_FETCH_FUNC;
94 const int64_t q_PDO$$FETCH_GROUP = PDO_FETCH_GROUP;
95 const int64_t q_PDO$$FETCH_UNIQUE = PDO_FETCH_UNIQUE;
96 const int64_t q_PDO$$FETCH_KEY_PAIR = PDO_FETCH_KEY_PAIR;
97 const int64_t q_PDO$$FETCH_CLASSTYPE = PDO_FETCH_CLASSTYPE;
98 const int64_t q_PDO$$FETCH_SERIALIZE = PDO_FETCH_SERIALIZE;
99 const int64_t q_PDO$$FETCH_PROPS_LATE = PDO_FETCH_PROPS_LATE;
100 const int64_t q_PDO$$FETCH_NAMED = PDO_FETCH_NAMED;
102 const int64_t q_PDO$$ATTR_AUTOCOMMIT = PDO_ATTR_AUTOCOMMIT;
103 const int64_t q_PDO$$ATTR_PREFETCH = PDO_ATTR_PREFETCH;
104 const int64_t q_PDO$$ATTR_TIMEOUT = PDO_ATTR_TIMEOUT;
105 const int64_t q_PDO$$ATTR_ERRMODE = PDO_ATTR_ERRMODE;
106 const int64_t q_PDO$$ATTR_SERVER_VERSION = PDO_ATTR_SERVER_VERSION;
107 const int64_t q_PDO$$ATTR_CLIENT_VERSION = PDO_ATTR_CLIENT_VERSION;
108 const int64_t q_PDO$$ATTR_SERVER_INFO = PDO_ATTR_SERVER_INFO;
109 const int64_t q_PDO$$ATTR_CONNECTION_STATUS = PDO_ATTR_CONNECTION_STATUS;
110 const int64_t q_PDO$$ATTR_CASE = PDO_ATTR_CASE;
111 const int64_t q_PDO$$ATTR_CURSOR_NAME = PDO_ATTR_CURSOR_NAME;
112 const int64_t q_PDO$$ATTR_CURSOR = PDO_ATTR_CURSOR;
113 const int64_t q_PDO$$ATTR_ORACLE_NULLS = PDO_ATTR_ORACLE_NULLS;
114 const int64_t q_PDO$$ATTR_PERSISTENT = PDO_ATTR_PERSISTENT;
115 const int64_t q_PDO$$ATTR_STATEMENT_CLASS = PDO_ATTR_STATEMENT_CLASS;
116 const int64_t q_PDO$$ATTR_FETCH_TABLE_NAMES = PDO_ATTR_FETCH_TABLE_NAMES;
117 const int64_t q_PDO$$ATTR_FETCH_CATALOG_NAMES = PDO_ATTR_FETCH_CATALOG_NAMES;
118 const int64_t q_PDO$$ATTR_DRIVER_NAME = PDO_ATTR_DRIVER_NAME;
119 const int64_t q_PDO$$ATTR_STRINGIFY_FETCHES = PDO_ATTR_STRINGIFY_FETCHES;
120 const int64_t q_PDO$$ATTR_MAX_COLUMN_LEN = PDO_ATTR_MAX_COLUMN_LEN;
121 const int64_t q_PDO$$ATTR_EMULATE_PREPARES = PDO_ATTR_EMULATE_PREPARES;
122 const int64_t q_PDO$$ATTR_DEFAULT_FETCH_MODE = PDO_ATTR_DEFAULT_FETCH_MODE;
124 const int64_t q_PDO$$ERRMODE_SILENT = PDO_ERRMODE_SILENT;
125 const int64_t q_PDO$$ERRMODE_WARNING = PDO_ERRMODE_WARNING;
126 const int64_t q_PDO$$ERRMODE_EXCEPTION = PDO_ERRMODE_EXCEPTION;
128 const int64_t q_PDO$$CASE_NATURAL = PDO_CASE_NATURAL;
129 const int64_t q_PDO$$CASE_LOWER = PDO_CASE_LOWER;
130 const int64_t q_PDO$$CASE_UPPER = PDO_CASE_UPPER;
132 const int64_t q_PDO$$NULL_NATURAL = PDO_NULL_NATURAL;
133 const int64_t q_PDO$$NULL_EMPTY_STRING = PDO_NULL_EMPTY_STRING;
134 const int64_t q_PDO$$NULL_TO_STRING = PDO_NULL_TO_STRING;
136 const StaticString q_PDO$$ERR_NONE(PDO_ERR_NONE);
138 const int64_t q_PDO$$FETCH_ORI_NEXT = PDO_FETCH_ORI_NEXT;
139 const int64_t q_PDO$$FETCH_ORI_PRIOR = PDO_FETCH_ORI_PRIOR;
140 const int64_t q_PDO$$FETCH_ORI_FIRST = PDO_FETCH_ORI_FIRST;
141 const int64_t q_PDO$$FETCH_ORI_LAST = PDO_FETCH_ORI_LAST;
142 const int64_t q_PDO$$FETCH_ORI_ABS = PDO_FETCH_ORI_ABS;
143 const int64_t q_PDO$$FETCH_ORI_REL = PDO_FETCH_ORI_REL;
145 const int64_t q_PDO$$CURSOR_FWDONLY = PDO_CURSOR_FWDONLY;
146 const int64_t q_PDO$$CURSOR_SCROLL = PDO_CURSOR_SCROLL;
148 ///////////////////////////////////////////////////////////////////////////////
150 #ifdef ENABLE_EXTENSION_PDO_MYSQL
151 const int64_t q_PDO$$MYSQL_ATTR_USE_BUFFERED_QUERY =
152 PDO_MYSQL_ATTR_USE_BUFFERED_QUERY;
153 const int64_t q_PDO$$MYSQL_ATTR_LOCAL_INFILE = PDO_MYSQL_ATTR_LOCAL_INFILE;
154 const int64_t q_PDO$$MYSQL_ATTR_MAX_BUFFER_SIZE =
155 PDO_MYSQL_ATTR_MAX_BUFFER_SIZE;
156 const int64_t q_PDO$$MYSQL_ATTR_INIT_COMMAND = PDO_MYSQL_ATTR_INIT_COMMAND;
157 const int64_t q_PDO$$MYSQL_ATTR_READ_DEFAULT_FILE =
158 PDO_MYSQL_ATTR_READ_DEFAULT_FILE;
159 const int64_t q_PDO$$MYSQL_ATTR_READ_DEFAULT_GROUP =
160 PDO_MYSQL_ATTR_READ_DEFAULT_GROUP;
161 const int64_t q_PDO$$MYSQL_ATTR_COMPRESS = PDO_MYSQL_ATTR_COMPRESS;
162 const int64_t q_PDO$$MYSQL_ATTR_DIRECT_QUERY = PDO_MYSQL_ATTR_DIRECT_QUERY;
163 const int64_t q_PDO$$MYSQL_ATTR_FOUND_ROWS = PDO_MYSQL_ATTR_FOUND_ROWS;
164 const int64_t q_PDO$$MYSQL_ATTR_IGNORE_SPACE = PDO_MYSQL_ATTR_IGNORE_SPACE;
165 #endif
167 ///////////////////////////////////////////////////////////////////////////////
168 // extension functions
170 Array f_pdo_drivers() {
171 Array ret = Array::Create();
172 const PDODriverMap &drivers = PDODriver::GetDrivers();
173 for (PDODriverMap::const_iterator iter = drivers.begin();
174 iter != drivers.end(); ++iter) {
175 ret.append(iter->second->getName());
177 return ret;
180 ///////////////////////////////////////////////////////////////////////////////
181 // error handling
183 struct pdo_sqlstate_info {
184 const char *state;
185 const char *desc;
188 static const struct pdo_sqlstate_info err_initializer[] = {
189 { "00000", "No error" },
190 { "01000", "Warning" },
191 { "01001", "Cursor operation conflict" },
192 { "01002", "Disconnect error" },
193 { "01003", "NULL value eliminated in set function" },
194 { "01004", "String data, right truncated" },
195 { "01006", "Privilege not revoked" },
196 { "01007", "Privilege not granted" },
197 { "01008", "Implicit zero bit padding" },
198 { "0100C", "Dynamic result sets returned" },
199 { "01P01", "Deprecated feature" },
200 { "01S00", "Invalid connection string attribute" },
201 { "01S01", "Error in row" },
202 { "01S02", "Option value changed" },
203 { "01S06",
204 "Attempt to fetch before the result set returned the first rowset" },
205 { "01S07", "Fractional truncation" },
206 { "01S08", "Error saving File DSN" },
207 { "01S09", "Invalid keyword" },
208 { "02000", "No data" },
209 { "02001", "No additional dynamic result sets returned" },
210 { "03000", "Sql statement not yet complete" },
211 { "07002", "COUNT field incorrect" },
212 { "07005", "Prepared statement not a cursor-specification" },
213 { "07006", "Restricted data type attribute violation" },
214 { "07009", "Invalid descriptor index" },
215 { "07S01", "Invalid use of default parameter" },
216 { "08000", "Connection exception" },
217 { "08001", "Client unable to establish connection" },
218 { "08002", "Connection name in use" },
219 { "08003", "Connection does not exist" },
220 { "08004", "Server rejected the connection" },
221 { "08006", "Connection failure" },
222 { "08007", "Connection failure during transaction" },
223 { "08S01", "Communication link failure" },
224 { "09000", "Triggered action exception" },
225 { "0A000", "Feature not supported" },
226 { "0B000", "Invalid transaction initiation" },
227 { "0F000", "Locator exception" },
228 { "0F001", "Invalid locator specification" },
229 { "0L000", "Invalid grantor" },
230 { "0LP01", "Invalid grant operation" },
231 { "0P000", "Invalid role specification" },
232 { "21000", "Cardinality violation" },
233 { "21S01", "Insert value list does not match column list" },
234 { "21S02", "Degree of derived table does not match column list" },
235 { "22000", "Data exception" },
236 { "22001", "String data, right truncated" },
237 { "22002", "Indicator variable required but not supplied" },
238 { "22003", "Numeric value out of range" },
239 { "22004", "Null value not allowed" },
240 { "22005", "Error in assignment" },
241 { "22007", "Invalid datetime format" },
242 { "22008", "Datetime field overflow" },
243 { "22009", "Invalid time zone displacement value" },
244 { "2200B", "Escape character conflict" },
245 { "2200C", "Invalid use of escape character" },
246 { "2200D", "Invalid escape octet" },
247 { "2200F", "Zero length character string" },
248 { "2200G", "Most specific type mismatch" },
249 { "22010", "Invalid indicator parameter value" },
250 { "22011", "Substring error" },
251 { "22012", "Division by zero" },
252 { "22015", "Interval field overflow" },
253 { "22018", "Invalid character value for cast specification" },
254 { "22019", "Invalid escape character" },
255 { "2201B", "Invalid regular expression" },
256 { "2201E", "Invalid argument for logarithm" },
257 { "2201F", "Invalid argument for power function" },
258 { "2201G", "Invalid argument for width bucket function" },
259 { "22020", "Invalid limit value" },
260 { "22021", "Character not in repertoire" },
261 { "22022", "Indicator overflow" },
262 { "22023", "Invalid parameter value" },
263 { "22024", "Unterminated c string" },
264 { "22025", "Invalid escape sequence" },
265 { "22026", "String data, length mismatch" },
266 { "22027", "Trim error" },
267 { "2202E", "Array subscript error" },
268 { "22P01", "Floating point exception" },
269 { "22P02", "Invalid text representation" },
270 { "22P03", "Invalid binary representation" },
271 { "22P04", "Bad copy file format" },
272 { "22P05", "Untranslatable character" },
273 { "23000", "Integrity constraint violation" },
274 { "23001", "Restrict violation" },
275 { "23502", "Not null violation" },
276 { "23503", "Foreign key violation" },
277 { "23505", "Unique violation" },
278 { "23514", "Check violation" },
279 { "24000", "Invalid cursor state" },
280 { "25000", "Invalid transaction state" },
281 { "25001", "Active sql transaction" },
282 { "25002", "Branch transaction already active" },
283 { "25003", "Inappropriate access mode for branch transaction" },
284 { "25004", "Inappropriate isolation level for branch transaction" },
285 { "25005", "No active sql transaction for branch transaction" },
286 { "25006", "Read only sql transaction" },
287 { "25007", "Schema and data statement mixing not supported" },
288 { "25008", "Held cursor requires same isolation level" },
289 { "25P01", "No active sql transaction" },
290 { "25P02", "In failed sql transaction" },
291 { "25S01", "Transaction state" },
292 { "25S02", "Transaction is still active" },
293 { "25S03", "Transaction is rolled back" },
294 { "26000", "Invalid sql statement name" },
295 { "27000", "Triggered data change violation" },
296 { "28000", "Invalid authorization specification" },
297 { "2B000", "Dependent privilege descriptors still exist" },
298 { "2BP01", "Dependent objects still exist" },
299 { "2D000", "Invalid transaction termination" },
300 { "2F000", "Sql routine exception" },
301 { "2F002", "Modifying sql data not permitted" },
302 { "2F003", "Prohibited sql statement attempted" },
303 { "2F004", "Reading sql data not permitted" },
304 { "2F005", "Function executed no return statement" },
305 { "34000", "Invalid cursor name" },
306 { "38000", "External routine exception" },
307 { "38001", "Containing sql not permitted" },
308 { "38002", "Modifying sql data not permitted" },
309 { "38003", "Prohibited sql statement attempted" },
310 { "38004", "Reading sql data not permitted" },
311 { "39000", "External routine invocation exception" },
312 { "39001", "Invalid sqlstate returned" },
313 { "39004", "Null value not allowed" },
314 { "39P01", "Trigger protocol violated" },
315 { "39P02", "Srf protocol violated" },
316 { "3B000", "Savepoint exception" },
317 { "3B001", "Invalid savepoint specification" },
318 { "3C000", "Duplicate cursor name" },
319 { "3D000", "Invalid catalog name" },
320 { "3F000", "Invalid schema name" },
321 { "40000", "Transaction rollback" },
322 { "40001", "Serialization failure" },
323 { "40002", "Transaction integrity constraint violation" },
324 { "40003", "Statement completion unknown" },
325 { "40P01", "Deadlock detected" },
326 { "42000", "Syntax error or access violation" },
327 { "42501", "Insufficient privilege" },
328 { "42601", "Syntax error" },
329 { "42602", "Invalid name" },
330 { "42611", "Invalid column definition" },
331 { "42622", "Name too long" },
332 { "42701", "Duplicate column" },
333 { "42702", "Ambiguous column" },
334 { "42703", "Undefined column" },
335 { "42704", "Undefined object" },
336 { "42710", "Duplicate object" },
337 { "42712", "Duplicate alias" },
338 { "42723", "Duplicate function" },
339 { "42725", "Ambiguous function" },
340 { "42803", "Grouping error" },
341 { "42804", "Datatype mismatch" },
342 { "42809", "Wrong object type" },
343 { "42830", "Invalid foreign key" },
344 { "42846", "Cannot coerce" },
345 { "42883", "Undefined function" },
346 { "42939", "Reserved name" },
347 { "42P01", "Undefined table" },
348 { "42P02", "Undefined parameter" },
349 { "42P03", "Duplicate cursor" },
350 { "42P04", "Duplicate database" },
351 { "42P05", "Duplicate prepared statement" },
352 { "42P06", "Duplicate schema" },
353 { "42P07", "Duplicate table" },
354 { "42P08", "Ambiguous parameter" },
355 { "42P09", "Ambiguous alias" },
356 { "42P10", "Invalid column reference" },
357 { "42P11", "Invalid cursor definition" },
358 { "42P12", "Invalid database definition" },
359 { "42P13", "Invalid function definition" },
360 { "42P14", "Invalid prepared statement definition" },
361 { "42P15", "Invalid schema definition" },
362 { "42P16", "Invalid table definition" },
363 { "42P17", "Invalid object definition" },
364 { "42P18", "Indeterminate datatype" },
365 { "42S01", "Base table or view already exists" },
366 { "42S02", "Base table or view not found" },
367 { "42S11", "Index already exists" },
368 { "42S12", "Index not found" },
369 { "42S21", "Column already exists" },
370 { "42S22", "Column not found" },
371 { "44000", "WITH CHECK OPTION violation" },
372 { "53000", "Insufficient resources" },
373 { "53100", "Disk full" },
374 { "53200", "Out of memory" },
375 { "53300", "Too many connections" },
376 { "54000", "Program limit exceeded" },
377 { "54001", "Statement too complex" },
378 { "54011", "Too many columns" },
379 { "54023", "Too many arguments" },
380 { "55000", "Object not in prerequisite state" },
381 { "55006", "Object in use" },
382 { "55P02", "Cant change runtime param" },
383 { "55P03", "Lock not available" },
384 { "57000", "Operator intervention" },
385 { "57014", "Query canceled" },
386 { "57P01", "Admin shutdown" },
387 { "57P02", "Crash shutdown" },
388 { "57P03", "Cannot connect now" },
389 { "58030", "Io error" },
390 { "58P01", "Undefined file" },
391 { "58P02", "Duplicate file" },
392 { "F0000", "Config file error" },
393 { "F0001", "Lock file exists" },
394 { "HY000", "General error" },
395 { "HY001", "Memory allocation error" },
396 { "HY003", "Invalid application buffer type" },
397 { "HY004", "Invalid SQL data type" },
398 { "HY007", "Associated statement is not prepared" },
399 { "HY008", "Operation canceled" },
400 { "HY009", "Invalid use of null pointer" },
401 { "HY010", "Function sequence error" },
402 { "HY011", "Attribute cannot be set now" },
403 { "HY012", "Invalid transaction operation code" },
404 { "HY013", "Memory management error" },
405 { "HY014", "Limit on the number of handles exceeded" },
406 { "HY015", "No cursor name available" },
407 { "HY016", "Cannot modify an implementation row descriptor" },
408 { "HY017", "Invalid use of an automatically allocated descriptor handle" },
409 { "HY018", "Server declined cancel request" },
410 { "HY019", "Non-character and non-binary data sent in pieces" },
411 { "HY020", "Attempt to concatenate a null value" },
412 { "HY021", "Inconsistent descriptor information" },
413 { "HY024", "Invalid attribute value" },
414 { "HY090", "Invalid string or buffer length" },
415 { "HY091", "Invalid descriptor field identifier" },
416 { "HY092", "Invalid attribute/option identifier" },
417 { "HY093", "Invalid parameter number" },
418 { "HY095", "Function type out of range" },
419 { "HY096", "Invalid information type" },
420 { "HY097", "Column type out of range" },
421 { "HY098", "Scope type out of range" },
422 { "HY099", "Nullable type out of range" },
423 { "HY100", "Uniqueness option type out of range" },
424 { "HY101", "Accuracy option type out of range" },
425 { "HY103", "Invalid retrieval code" },
426 { "HY104", "Invalid precision or scale value" },
427 { "HY105", "Invalid parameter type" },
428 { "HY106", "Fetch type out of range" },
429 { "HY107", "Row value out of range" },
430 { "HY109", "Invalid cursor position" },
431 { "HY110", "Invalid driver completion" },
432 { "HY111", "Invalid bookmark value" },
433 { "HYC00", "Optional feature not implemented" },
434 { "HYT00", "Timeout expired" },
435 { "HYT01", "Connection timeout expired" },
436 { "IM001", "Driver does not support this function" },
437 { "IM002", "Data source name not found and no default driver specified" },
438 { "IM003", "Specified driver could not be loaded" },
439 { "IM004", "Driver's SQLAllocHandle on SQL_HANDLE_ENV failed" },
440 { "IM005", "Driver's SQLAllocHandle on SQL_HANDLE_DBC failed" },
441 { "IM006", "Driver's SQLSetConnectAttr failed" },
442 { "IM007", "No data source or driver specified; dialog prohibited" },
443 { "IM008", "Dialog failed" },
444 { "IM009", "Unable to load translation DLL" },
445 { "IM010", "Data source name too long" },
446 { "IM011", "Driver name too long" },
447 { "IM012", "DRIVER keyword syntax error" },
448 { "IM013", "Trace file error" },
449 { "IM014", "Invalid name of File DSN" },
450 { "IM015", "Corrupt file data source" },
451 { "P0000", "Plpgsql error" },
452 { "P0001", "Raise exception" },
453 { "XX000", "Internal error" },
454 { "XX001", "Data corrupted" },
455 { "XX002", "Index corrupted" }
458 struct PDOErrorHash : private hphp_const_char_map<const char *> {
459 PDOErrorHash() {
460 for (unsigned int i = 0;
461 i < sizeof(err_initializer)/sizeof(err_initializer[0]); i++) {
462 const struct pdo_sqlstate_info *info = &err_initializer[i];
463 (*this)[info->state] = info->desc;
467 const char *description(const char *state) {
468 const_iterator iter = find(state);
469 if (iter != end()) {
470 return iter->second;
472 return "<<Unknown error>>";
475 static PDOErrorHash s_err_hash;
477 const StaticString
478 s_code("code"),
479 s_message("message"),
480 s_errorInfo("errorInfo"),
481 s_PDOException("PDOException");
483 void throw_pdo_exception(const Variant& code, const Variant& info,
484 const char *fmt, ...) {
485 auto obj = SystemLib::AllocPDOExceptionObject();
486 obj->o_set(s_code, code, s_PDOException);
488 va_list ap;
489 va_start(ap, fmt);
490 string msg;
491 string_vsnprintf(msg, fmt, ap);
492 obj->o_set(s_message, String(msg), s_PDOException);
493 va_end(ap);
495 if (!info.isNull()) {
496 obj->o_set(s_errorInfo, info, s_PDOException);
498 throw_object(obj);
501 void pdo_raise_impl_error(sp_PDOResource rsrc, PDOStatement* stmt,
502 const char *sqlstate, const char *supp) {
503 auto const& dbh = rsrc->conn();
505 PDOErrorType *pdo_err = &dbh->error_code;
506 if (stmt) {
507 pdo_err = &stmt->error_code;
509 setPDOError(*pdo_err, sqlstate);
511 const char *msg = s_err_hash.description(sqlstate);
512 string err = "SQLSTATE["; err += sqlstate; err += "]: "; err += msg;
513 if (supp) {
514 err += ": "; err += supp;
517 if (dbh->error_mode != PDO_ERRMODE_EXCEPTION) {
518 raise_warning("%s", err.c_str());
519 } else {
520 Array info;
521 info.append(String(*pdo_err, CopyString));
522 info.append(0LL);
523 throw_pdo_exception(String(sqlstate, CopyString), info, "%s", err.c_str());
527 void pdo_raise_impl_error(sp_PDOResource rsrc, sp_PDOStatement stmt,
528 const char *sqlstate, const char *supp) {
529 pdo_raise_impl_error(rsrc, stmt.get(), sqlstate, supp);
532 namespace {
534 void pdo_handle_error(sp_PDOResource rsrc, PDOStatement* stmt) {
535 auto const& dbh = rsrc->conn();
537 if (dbh->error_mode == PDO_ERRMODE_SILENT) {
538 return;
540 PDOErrorType *pdo_err = &dbh->error_code;
541 if (stmt) {
542 pdo_err = &stmt->error_code;
545 /* hash sqlstate to error messages */
546 const char *msg = s_err_hash.description(*pdo_err);
548 int64_t native_code = 0;
549 String supp;
550 Array info;
551 if (dbh->support(PDOConnection::MethodFetchErr)) {
552 info = Array::Create();
553 info.append(String(*pdo_err, CopyString));
554 if (dbh->fetchErr(stmt, info)) {
555 if (info.exists(1)) {
556 native_code = info[1].toInt64();
558 if (info.exists(2)) {
559 supp = info[2].toString();
564 string err = "SQLSTATE["; err += *pdo_err; err += "]: "; err += msg;
565 if (!supp.empty()) {
566 err += ": "; err += String(native_code).data();
567 err += " "; err += supp.data();
570 if (dbh->error_mode != PDO_ERRMODE_EXCEPTION) {
571 raise_warning("%s", err.c_str());
572 } else {
573 throw_pdo_exception(String(*pdo_err, CopyString), info, "%s", err.c_str());
577 void pdo_handle_error(sp_PDOResource rsrc, sp_PDOStatement stmt) {
578 pdo_handle_error(rsrc, stmt.get());
583 ///////////////////////////////////////////////////////////////////////////////
584 // helpers for PDO class
586 static inline int64_t pdo_attr_lval(const Array& options, PDOAttributeType name,
587 int64_t defval) {
588 if (options.exists(name)) {
589 return options[name].toInt64();
591 return defval;
594 static Object pdo_stmt_instantiate(sp_PDOResource dbh, const String& clsname,
595 const Variant& ctor_args) {
596 String name = clsname;
597 if (name.empty()) {
598 name = "PDOStatement";
600 if (!ctor_args.isNull() && !ctor_args.isArray()) {
601 pdo_raise_impl_error(dbh, nullptr, "HY000",
602 "constructor arguments must be passed as an array");
603 return Object();
605 Class* cls = Unit::loadClass(name.get());
606 if (!cls) {
607 return Object();
609 return Object{cls};
612 static void pdo_stmt_construct(sp_PDOStatement stmt, Object object,
613 const String& clsname,
614 const Variant& ctor_args) {
615 object->o_set("queryString", stmt->query_string);
616 if (clsname.empty()) {
617 return;
619 Class* cls = Unit::loadClass(clsname.get());
620 if (!cls) {
621 return;
623 TypedValue ret;
624 ObjectData* inst = object.get();
625 g_context->invokeFunc(&ret, cls->getCtor(), ctor_args.toArray(), inst);
626 tvRefcountedDecRef(&ret);
629 static bool valid_statement_class(sp_PDOResource dbh, const Variant& opt,
630 String &clsname, Variant &ctor_args) {
631 if (!opt.isArray() || !opt.toArray().exists(0) ||
632 !opt.toArray()[0].isString() ||
633 !HHVM_FN(class_exists)(opt.toArray()[0].toString())) {
634 pdo_raise_impl_error
635 (dbh, nullptr, "HY000",
636 "PDO::ATTR_STATEMENT_CLASS requires format array(classname, "
637 "array(ctor_args)); the classname must be a string specifying "
638 "an existing class");
639 PDO_HANDLE_DBH_ERR(dbh);
640 return false;
642 clsname = opt.toArray()[0].toString();
643 if (clsname == String("PDOStatement")) {
644 ctor_args = Variant(Array());
645 return true;
647 if (!HHVM_FN(is_a)(clsname, "PDOStatement", /* allow_string */ true)) {
648 pdo_raise_impl_error
649 (dbh, nullptr, "HY000",
650 "user-supplied statement class must be derived from PDOStatement");
651 PDO_HANDLE_DBH_ERR(dbh);
652 return false;
654 HPHP::Class* cls = HPHP::Unit::loadClass(clsname.get());
655 if (cls) {
656 const HPHP::Func* method = cls->getDeclaredCtor();
657 if (method && method->isPublic()) {
658 pdo_raise_impl_error
659 (dbh, nullptr, "HY000",
660 "user-supplied statement class cannot have a public constructor");
661 PDO_HANDLE_DBH_ERR(dbh);
662 return false;
665 if (opt.toArray().exists(1)) {
666 Variant item = opt.toArray()[1];
667 if (!item.isArray()) {
668 pdo_raise_impl_error
669 (dbh, nullptr, "HY000",
670 "PDO::ATTR_STATEMENT_CLASS requires format array(classname, "
671 "ctor_args); ctor_args must be an array");
672 PDO_HANDLE_DBH_ERR(dbh);
673 return false;
675 ctor_args = item;
677 return true;
680 static bool pdo_stmt_describe_columns(sp_PDOStatement stmt) {
681 for (int col = 0; col < stmt->column_count; col++) {
682 if (!stmt->describer(col)) {
683 return false;
686 auto column = cast<PDOColumn>(stmt->columns[col]);
688 /* if we are applying case conversions on column names, do so now */
689 if (stmt->dbh->conn()->native_case != stmt->dbh->conn()->desired_case &&
690 stmt->dbh->conn()->desired_case != PDO_CASE_NATURAL) {
691 switch (stmt->dbh->conn()->desired_case) {
692 case PDO_CASE_UPPER:
693 column->name = HHVM_FN(strtoupper)(column->name);
694 break;
695 case PDO_CASE_LOWER:
696 column->name = HHVM_FN(strtolower)(column->name);
697 break;
698 default:;
702 if (stmt->bound_columns.exists(column->name)) {
703 auto param = cast<PDOBoundParam>(stmt->bound_columns[column->name]);
704 param->paramno = col;
707 return true;
710 static bool pdo_stmt_verify_mode(sp_PDOStatement stmt, int64_t mode,
711 bool fetch_all) {
712 int flags = mode & PDO_FETCH_FLAGS;
713 mode = mode & ~PDO_FETCH_FLAGS;
715 if (mode < 0 || mode > PDO_FETCH__MAX) {
716 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "invalid fetch mode");
717 return false;
720 if (mode == PDO_FETCH_USE_DEFAULT) {
721 flags = stmt->default_fetch_type & PDO_FETCH_FLAGS;
722 mode = stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
725 switch (mode) {
726 case PDO_FETCH_FUNC:
727 if (!fetch_all) {
728 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
729 "PDO::FETCH_FUNC is only allowed in "
730 "PDOStatement::fetchAll()");
731 return false;
733 return true;
735 case PDO_FETCH_LAZY:
736 if (fetch_all) {
737 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
738 "PDO::FETCH_LAZY can't be used with "
739 "PDOStatement::fetchAll()");
740 return false;
743 default:
744 if ((flags & PDO_FETCH_SERIALIZE) == PDO_FETCH_SERIALIZE) {
745 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
746 "PDO::FETCH_SERIALIZE can only be used "
747 "together with PDO::FETCH_CLASS");
748 return false;
750 if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
751 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
752 "PDO::FETCH_CLASSTYPE can only be used "
753 "together with PDO::FETCH_CLASS");
754 return false;
756 if (mode >= PDO_FETCH__MAX) {
757 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "invalid fetch mode");
758 return false;
760 /* no break; */
762 case PDO_FETCH_CLASS:
763 break;
765 return true;
768 static bool do_fetch_class_prepare(sp_PDOStatement stmt) {
769 String clsname = stmt->fetch.clsname;
770 if (clsname.empty()) {
771 stmt->fetch.clsname = "stdclass";
773 stmt->fetch.constructor = empty_string(); //NULL;
774 HPHP::Class* cls = HPHP::Unit::loadClass(clsname.get());
775 if (cls) {
776 const HPHP::Func* method = cls->getDeclaredCtor();
777 if (method) {
778 stmt->fetch.constructor = method->nameStr();
779 return true;
782 if (!stmt->fetch.ctor_args.isNull()) {
783 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
784 "user-supplied class does not have a constructor, "
785 "use NULL for the ctor_params parameter, or simply "
786 "omit it");
787 return false;
789 return true; /* no ctor no args is also ok */
792 static bool pdo_stmt_set_fetch_mode(sp_PDOStatement stmt, int _argc,
793 int64_t mode, const Array& _argv) {
794 _argc = _argv.size() + 1;
796 if (stmt->default_fetch_type == PDO_FETCH_INTO) {
797 stmt->fetch.into.unset();
799 stmt->default_fetch_type = PDO_FETCH_BOTH;
801 if (!pdo_stmt_verify_mode(stmt, mode, false)) {
802 setPDOErrorNone(stmt->error_code);
803 return false;
806 int flags = mode & PDO_FETCH_FLAGS;
807 bool retval = false;
808 switch (mode & ~PDO_FETCH_FLAGS) {
809 case PDO_FETCH_USE_DEFAULT:
810 case PDO_FETCH_LAZY:
811 case PDO_FETCH_ASSOC:
812 case PDO_FETCH_NUM:
813 case PDO_FETCH_BOTH:
814 case PDO_FETCH_OBJ:
815 case PDO_FETCH_BOUND:
816 case PDO_FETCH_NAMED:
817 case PDO_FETCH_KEY_PAIR:
818 if (_argc != 1) {
819 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
820 "fetch mode doesn't allow any extra arguments");
821 } else {
822 retval = true;
824 break;
826 case PDO_FETCH_COLUMN:
827 if (_argc != 2) {
828 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
829 "fetch mode requires the colno argument");
830 } else if (!_argv[0].isInteger()) {
831 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
832 "colno must be an integer");
833 } else {
834 stmt->fetch.column = _argv[0].toInt64();
835 retval = true;
837 break;
839 case PDO_FETCH_CLASS:
840 /* Gets its class name from 1st column */
841 if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
842 if (_argc != 1) {
843 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
844 "fetch mode doesn't allow any extra arguments");
845 } else {
846 stmt->fetch.clsname.clear();
847 retval = true;
849 } else {
850 if (_argc < 2) {
851 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
852 "fetch mode requires the classname argument");
853 } else if (_argc > 3) {
854 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
855 "too many arguments");
856 } else if (!_argv[0].isString()) {
857 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
858 "classname must be a string");
859 } else {
860 retval = HHVM_FN(class_exists)(_argv[0].toString());
861 if (retval) {
862 stmt->fetch.clsname = _argv[0].toString();
867 if (retval) {
868 stmt->fetch.ctor_args.unset();
869 if (_argc == 3) {
870 if (!_argv[1].isNull() && !_argv[1].isArray()) {
871 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
872 "ctor_args must be either NULL or an array");
873 retval = false;
874 } else {
875 stmt->fetch.ctor_args = _argv[1];
879 if (retval) {
880 do_fetch_class_prepare(stmt);
884 break;
886 case PDO_FETCH_INTO:
887 if (_argc != 2) {
888 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
889 "fetch mode requires the object parameter");
890 } else if (!_argv[0].isObject()) {
891 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
892 "object must be an object");
893 } else {
894 retval = true;
897 if (retval) {
898 stmt->fetch.into = _argv[0];
900 break;
902 default:
903 pdo_raise_impl_error(stmt->dbh, stmt, "22003",
904 "Invalid fetch mode specified");
907 if (retval) {
908 stmt->default_fetch_type = (PDOFetchType)mode;
912 * PDO error (if any) has already been raised at this point.
914 * The error_code is cleared, otherwise the caller will read the
915 * last error message from the driver.
918 setPDOErrorNone(stmt->error_code);
919 return retval;
922 ///////////////////////////////////////////////////////////////////////////////
923 // forward declarations
925 static bool HHVM_METHOD(PDO, setattribute, int64_t attribute,
926 const Variant& value);
928 ///////////////////////////////////////////////////////////////////////////////
929 // PDO
931 namespace {
932 thread_local std::unordered_map<std::string,sp_PDOConnection> s_connections;
935 const StaticString s_PDO("PDO");
937 static void HHVM_METHOD(PDO, __construct, const String& dsn,
938 const String& username /* = null_string */,
939 const String& password /* = null_string */,
940 const Variant& optionsV /* = null_array */) {
941 auto data = Native::data<PDOData>(this_);
942 auto options = optionsV.isNull() ? null_array : optionsV.toArray();
944 String data_source = dsn;
946 /* parse the data source name */
947 const char *colon = strchr(data_source.data(), ':');
948 if (!colon) {
949 /* let's see if this string has a matching dsn in the php.ini */
950 String name = "pdo.dsn."; name += data_source;
951 String ini_dsn;
952 if (!IniSetting::Get(name, ini_dsn)) {
953 throw_pdo_exception(uninit_null(), uninit_null(),
954 "invalid data source name");
956 data_source = ini_dsn;
957 colon = strchr(data_source.data(), ':');
958 if (!colon) {
959 throw_pdo_exception(uninit_null(), uninit_null(),
960 "invalid data source name (via INI: %s)",
961 ini_dsn.data());
965 if (!strncmp(data_source.data(), "uri:", 4)) {
966 /* the specified URI holds connection details */
967 auto file = File::Open(data_source.substr(4), "rb");
968 if (!file || file->isInvalid()) {
969 throw_pdo_exception(uninit_null(), uninit_null(),
970 "invalid data source URI");
972 data_source = file->readLine(1024);
973 colon = strchr(data_source.data(), ':');
974 if (!colon) {
975 throw_pdo_exception(uninit_null(), uninit_null(),
976 "invalid data source name (via URI)");
980 const PDODriverMap &drivers = PDODriver::GetDrivers();
981 String name = data_source.substr(0, colon - data_source.data());
982 PDODriverMap::const_iterator iter = drivers.find(name.data());
983 if (iter == drivers.end()) {
984 /* NB: don't want to include the data_source in the error message as
985 * it might contain a password */
986 throw_pdo_exception(uninit_null(), uninit_null(), "could not find driver");
988 PDODriver *driver = iter->second;
990 /* is this supposed to be a persistent connection ? */
991 bool is_persistent = false;
992 bool call_factory = true;
993 std::string shashkey;
994 if (!options.empty()) {
995 StringBuffer hashkey;
996 if (options.exists(PDO_ATTR_PERSISTENT)) {
997 Variant v = options[PDO_ATTR_PERSISTENT];
998 String sv = v.toString();
999 if (v.isString() && !sv.isNumeric() && !sv.empty()) {
1000 /* user specified key */
1001 hashkey.printf("PDO:DBH:DSN=%s:%s:%s:%s",
1002 data_source.data(), username.data(),
1003 password.data(), sv.data());
1004 is_persistent = true;
1005 } else {
1006 is_persistent = v.toInt64();
1007 hashkey.printf("PDO:DBH:DSN=%s:%s:%s",
1008 data_source.data(), username.data(),
1009 password.data());
1013 if (is_persistent) {
1014 shashkey = hashkey.detach().toCppString();
1016 /* let's see if we have one cached.... */
1017 if (s_connections.count(shashkey)) {
1018 auto const conn = s_connections[shashkey];
1019 data->m_dbh = driver->createResource(conn);
1021 /* is the connection still alive ? */
1022 if (conn->support(PDOConnection::MethodCheckLiveness) &&
1023 !conn->checkLiveness()) {
1024 /* nope... need to kill it */
1025 data->m_dbh = nullptr;
1029 if (data->m_dbh) {
1030 call_factory = false;
1031 } else {
1032 /* need a brand new pdbh */
1033 data->m_dbh = driver->createResource(colon + 1, username,
1034 password, options);
1035 if (!data->m_dbh) {
1036 throw_pdo_exception(uninit_null(), uninit_null(),
1037 "unable to create a connection");
1039 data->m_dbh->conn()->persistent_id = shashkey;
1043 if (!data->m_dbh) {
1044 data->m_dbh = driver->createResource(colon + 1, username,
1045 password, options);
1046 if (!data->m_dbh) {
1047 throw_pdo_exception(uninit_null(), uninit_null(),
1048 "unable to create a connection");
1052 if (call_factory) {
1053 data->m_dbh->conn()->default_fetch_type = PDO_FETCH_BOTH;
1056 data->m_dbh->conn()->auto_commit =
1057 pdo_attr_lval(options, PDO_ATTR_AUTOCOMMIT, 1);
1059 if (!call_factory) {
1060 /* we got a persistent guy from our cache */
1061 for (ArrayIter iter(options); iter; ++iter) {
1062 HHVM_MN(PDO, setattribute)(this_, iter.first().toInt64(),
1063 iter.second());
1065 } else if (data->m_dbh) {
1066 if (is_persistent) {
1067 assert(!shashkey.empty());
1068 s_connections[shashkey] = data->m_dbh->conn();
1071 data->m_dbh->conn()->driver = driver;
1072 for (ArrayIter iter(options); iter; ++iter) {
1073 HHVM_MN(PDO, setattribute)(this_, iter.first().toInt64(),
1074 iter.second());
1079 static Variant HHVM_METHOD(PDO, prepare, const String& statement,
1080 const Array& options = null_array) {
1081 auto data = Native::data<PDOData>(this_);
1083 assert(data->m_dbh->conn()->driver);
1084 setPDOErrorNone(data->m_dbh->conn()->error_code);
1085 data->m_dbh->query_stmt = nullptr;
1087 String clsname;
1088 Variant ctor_args;
1089 if (options.exists(PDO_ATTR_STATEMENT_CLASS)) {
1090 Variant opt = options[PDO_ATTR_STATEMENT_CLASS];
1091 if (!valid_statement_class(data->m_dbh, opt, clsname, ctor_args)) {
1092 return false;
1094 } else {
1095 clsname = data->m_dbh->conn()->def_stmt_clsname;
1096 ctor_args = data->m_dbh->def_stmt_ctor_args;
1099 Object ret = pdo_stmt_instantiate(data->m_dbh, clsname, ctor_args);
1100 if (ret.isNull()) {
1101 pdo_raise_impl_error
1102 (data->m_dbh, nullptr, "HY000",
1103 "failed to instantiate user-supplied statement class");
1104 PDO_HANDLE_DBH_ERR(data->m_dbh);
1105 return false;
1107 PDOStatementData *pdostmt = Native::data<PDOStatementData>(ret);
1109 if (data->m_dbh->conn()->preparer(statement, &pdostmt->m_stmt, options)) {
1110 auto stmt = pdostmt->m_stmt;
1111 assert(stmt);
1113 /* unconditionally keep this for later reference */
1114 stmt->query_string = statement;
1115 stmt->default_fetch_type = data->m_dbh->conn()->default_fetch_type;
1116 stmt->dbh = data->m_dbh;
1118 pdo_stmt_construct(stmt, ret, clsname, ctor_args);
1119 return ret;
1122 PDO_HANDLE_DBH_ERR(data->m_dbh);
1123 return false;
1126 static bool HHVM_METHOD(PDO, begintransaction) {
1127 auto data = Native::data<PDOData>(this_);
1129 if (data->m_dbh->conn()->in_txn) {
1130 throw_pdo_exception(uninit_null(), uninit_null(),
1131 "There is already an active transaction");
1133 if (data->m_dbh->conn()->begin()) {
1134 data->m_dbh->conn()->in_txn = 1;
1135 return true;
1137 if (strcmp(data->m_dbh->conn()->error_code, PDO_ERR_NONE)) {
1138 pdo_handle_error(data->m_dbh, nullptr);
1140 return false;
1143 static bool HHVM_METHOD(PDO, commit) {
1144 auto data = Native::data<PDOData>(this_);
1146 assert(data->m_dbh->conn()->driver);
1147 if (!data->m_dbh->conn()->in_txn) {
1148 throw_pdo_exception(uninit_null(), uninit_null(),
1149 "There is no active transaction");
1151 if (data->m_dbh->conn()->commit()) {
1152 data->m_dbh->conn()->in_txn = 0;
1153 return true;
1155 PDO_HANDLE_DBH_ERR(data->m_dbh);
1156 return false;
1159 static bool HHVM_METHOD(PDO, intransaction) {
1160 auto data = Native::data<PDOData>(this_);
1162 assert(data->m_dbh->conn()->driver);
1163 return data->m_dbh->conn()->in_txn;
1166 static bool HHVM_METHOD(PDO, rollback) {
1167 auto data = Native::data<PDOData>(this_);
1169 assert(data->m_dbh->conn()->driver);
1170 if (!data->m_dbh->conn()->in_txn) {
1171 throw_pdo_exception(uninit_null(), uninit_null(),
1172 "There is no active transaction");
1174 if (data->m_dbh->conn()->rollback()) {
1175 data->m_dbh->conn()->in_txn = 0;
1176 return true;
1178 PDO_HANDLE_DBH_ERR(data->m_dbh);
1179 return false;
1182 static bool HHVM_METHOD(PDO, setattribute, int64_t attribute,
1183 const Variant& value) {
1184 auto data = Native::data<PDOData>(this_);
1186 assert(data->m_dbh->conn()->driver);
1188 #define PDO_LONG_PARAM_CHECK \
1189 if (!value.isInteger() && !value.isString() && !value.isBoolean()) { \
1190 pdo_raise_impl_error(data->m_dbh, nullptr, "HY000", \
1191 "attribute value must be an integer"); \
1192 PDO_HANDLE_DBH_ERR(data->m_dbh); \
1193 return false; \
1196 switch (attribute) {
1197 case PDO_ATTR_ERRMODE:
1198 PDO_LONG_PARAM_CHECK;
1199 switch (value.toInt64()) {
1200 case PDO_ERRMODE_SILENT:
1201 case PDO_ERRMODE_WARNING:
1202 case PDO_ERRMODE_EXCEPTION:
1203 data->m_dbh->conn()->error_mode = (PDOErrorMode)value.toInt64();
1204 return true;
1205 default:
1206 pdo_raise_impl_error(data->m_dbh, nullptr, "HY000", "invalid error mode");
1207 PDO_HANDLE_DBH_ERR(data->m_dbh);
1208 return false;
1210 return false;
1212 case PDO_ATTR_CASE:
1213 PDO_LONG_PARAM_CHECK;
1214 switch (value.toInt64()) {
1215 case PDO_CASE_NATURAL:
1216 case PDO_CASE_UPPER:
1217 case PDO_CASE_LOWER:
1218 data->m_dbh->conn()->desired_case = (PDOCaseConversion)value.toInt64();
1219 return true;
1220 default:
1221 pdo_raise_impl_error(data->m_dbh, nullptr, "HY000",
1222 "invalid case folding mode");
1223 PDO_HANDLE_DBH_ERR(data->m_dbh);
1224 return false;
1226 return false;
1228 case PDO_ATTR_ORACLE_NULLS:
1229 PDO_LONG_PARAM_CHECK;
1230 data->m_dbh->conn()->oracle_nulls = value.toInt64();
1231 return true;
1233 case PDO_ATTR_DEFAULT_FETCH_MODE:
1234 if (value.isArray()) {
1235 if (value.toCArrRef().exists(0)) {
1236 Variant tmp = value.toCArrRef()[0];
1237 if (tmp.isInteger() && ((tmp.toInt64() == PDO_FETCH_INTO ||
1238 tmp.toInt64() == PDO_FETCH_CLASS))) {
1239 pdo_raise_impl_error(data->m_dbh, nullptr, "HY000",
1240 "FETCH_INTO and FETCH_CLASS are not yet "
1241 "supported as default fetch modes");
1242 return false;
1245 } else {
1246 PDO_LONG_PARAM_CHECK;
1248 if (value.toInt64() == PDO_FETCH_USE_DEFAULT) {
1249 pdo_raise_impl_error(data->m_dbh, nullptr,
1250 "HY000", "invalid fetch mode type");
1251 return false;
1253 data->m_dbh->conn()->default_fetch_type = (PDOFetchType)value.toInt64();
1254 return true;
1256 case PDO_ATTR_STRINGIFY_FETCHES:
1257 PDO_LONG_PARAM_CHECK;
1258 data->m_dbh->conn()->stringify = value.toInt64() ? 1 : 0;
1259 return true;
1261 case PDO_ATTR_STATEMENT_CLASS:
1263 if (data->m_dbh->conn()->is_persistent) {
1264 pdo_raise_impl_error(data->m_dbh, nullptr, "HY000",
1265 "PDO::ATTR_STATEMENT_CLASS cannot be used "
1266 "with persistent PDO instances");
1267 PDO_HANDLE_DBH_ERR(data->m_dbh);
1268 return false;
1270 String clsname;
1271 if (!valid_statement_class(data->m_dbh, value, clsname,
1272 data->m_dbh->def_stmt_ctor_args)) {
1273 return false;
1275 data->m_dbh->conn()->def_stmt_clsname = clsname.c_str();
1276 return true;
1280 if (data->m_dbh->conn()->support(PDOConnection::MethodSetAttribute)) {
1281 setPDOErrorNone(data->m_dbh->conn()->error_code);
1282 data->m_dbh->query_stmt = nullptr;
1283 if (data->m_dbh->conn()->setAttribute(attribute, value)) {
1284 return true;
1288 if (attribute == PDO_ATTR_AUTOCOMMIT) {
1289 throw_pdo_exception(uninit_null(), uninit_null(),
1290 "The auto-commit mode cannot be changed for this "
1291 "driver");
1292 } else if (!data->m_dbh->conn()->support(PDOConnection::MethodSetAttribute)) {
1293 pdo_raise_impl_error(data->m_dbh, nullptr, "IM001",
1294 "driver does not support setting attributes");
1295 } else {
1296 PDO_HANDLE_DBH_ERR(data->m_dbh);
1298 return false;
1301 static Variant HHVM_METHOD(PDO, getattribute, int64_t attribute) {
1302 auto data = Native::data<PDOData>(this_);
1304 assert(data->m_dbh->conn()->driver);
1305 setPDOErrorNone(data->m_dbh->conn()->error_code);
1306 data->m_dbh->query_stmt = nullptr;
1308 /* handle generic PDO-level attributes */
1309 switch (attribute) {
1310 case PDO_ATTR_PERSISTENT:
1311 return (bool)data->m_dbh->conn()->is_persistent;
1313 case PDO_ATTR_CASE:
1314 return (int64_t)data->m_dbh->conn()->desired_case;
1316 case PDO_ATTR_ORACLE_NULLS:
1317 return (int64_t)data->m_dbh->conn()->oracle_nulls;
1319 case PDO_ATTR_ERRMODE:
1320 return (int64_t)data->m_dbh->conn()->error_mode;
1322 case PDO_ATTR_DRIVER_NAME:
1323 return String(data->m_dbh->conn()->driver->getName());
1325 case PDO_ATTR_STATEMENT_CLASS: {
1326 Array ret;
1327 ret.append(String(data->m_dbh->conn()->def_stmt_clsname));
1328 if (!data->m_dbh->def_stmt_ctor_args.isNull()) {
1329 ret.append(data->m_dbh->def_stmt_ctor_args);
1331 return ret;
1333 case PDO_ATTR_DEFAULT_FETCH_MODE:
1334 return (int64_t)data->m_dbh->conn()->default_fetch_type;
1337 if (!data->m_dbh->conn()->support(PDOConnection::MethodGetAttribute)) {
1338 pdo_raise_impl_error(data->m_dbh, nullptr, "IM001",
1339 "driver does not support getting attributes");
1340 return false;
1343 Variant ret;
1344 switch (data->m_dbh->conn()->getAttribute(attribute, ret)) {
1345 case -1:
1346 PDO_HANDLE_DBH_ERR(data->m_dbh);
1347 return false;
1348 case 0:
1349 pdo_raise_impl_error(data->m_dbh, nullptr, "IM001",
1350 "driver does not support that attribute");
1351 return false;
1353 return ret;
1356 static Variant HHVM_METHOD(PDO, exec, const String& query) {
1357 auto data = Native::data<PDOData>(this_);
1359 SYNC_VM_REGS_SCOPED();
1360 if (query.empty()) {
1361 pdo_raise_impl_error(data->m_dbh, nullptr, "HY000",
1362 "trying to execute an empty query");
1363 return false;
1366 assert(data->m_dbh->conn()->driver);
1367 setPDOErrorNone(data->m_dbh->conn()->error_code);
1368 data->m_dbh->query_stmt = nullptr;
1370 int64_t ret = data->m_dbh->conn()->doer(query);
1371 if (ret == -1) {
1372 PDO_HANDLE_DBH_ERR(data->m_dbh);
1373 return false;
1375 return ret;
1378 static Variant HHVM_METHOD(PDO, lastinsertid,
1379 const String& seqname /* = null_string */) {
1380 auto data = Native::data<PDOData>(this_);
1382 assert(data->m_dbh->conn()->driver);
1383 setPDOErrorNone(data->m_dbh->conn()->error_code);
1384 data->m_dbh->query_stmt = nullptr;
1386 if (!data->m_dbh->conn()->support(PDOConnection::MethodLastId)) {
1387 pdo_raise_impl_error(data->m_dbh, nullptr, "IM001",
1388 "driver does not support lastInsertId()");
1389 return false;
1392 String ret = data->m_dbh->conn()->lastId(seqname.data());
1393 if (ret.empty()) {
1394 PDO_HANDLE_DBH_ERR(data->m_dbh);
1395 return false;
1397 return ret;
1400 static Variant HHVM_METHOD(PDO, errorcode) {
1401 auto data = Native::data<PDOData>(this_);
1403 assert(data->m_dbh->conn()->driver);
1404 if (data->m_dbh->query_stmt) {
1405 return String(data->m_dbh->query_stmt->error_code, CopyString);
1408 if (data->m_dbh->conn()->error_code[0] == '\0') {
1409 return init_null();
1413 * Making sure that we fallback to the default implementation
1414 * if the dbh->error_code is not null.
1416 return String(data->m_dbh->conn()->error_code, CopyString);
1419 static Array HHVM_METHOD(PDO, errorinfo) {
1420 auto data = Native::data<PDOData>(this_);
1422 assert(data->m_dbh->conn()->driver);
1424 Array ret;
1425 if (data->m_dbh->query_stmt) {
1426 ret.append(String(data->m_dbh->query_stmt->error_code, CopyString));
1427 } else {
1428 ret.append(String(data->m_dbh->conn()->error_code, CopyString));
1431 if (data->m_dbh->conn()->support(PDOConnection::MethodFetchErr)) {
1432 data->m_dbh->conn()->fetchErr(data->m_dbh->query_stmt, ret);
1436 * In order to be consistent, we have to make sure we add the good amount
1437 * of nulls depending on the current number of elements. We make a simple
1438 * difference and add the needed elements
1440 int error_count = ret.size();
1441 int error_expected_count = 3;
1442 if (error_expected_count > error_count) {
1443 int error_count_diff = error_expected_count - error_count;
1444 for (int i = 0; i < error_count_diff; i++) {
1445 ret.append(uninit_null());
1448 return ret;
1451 static Variant HHVM_METHOD(PDO, query, const String& sql,
1452 const Array& _argv) {
1454 auto data = Native::data<PDOData>(this_);
1455 SYNC_VM_REGS_SCOPED();
1456 assert(data->m_dbh->conn()->driver);
1457 setPDOErrorNone(data->m_dbh->conn()->error_code);
1458 data->m_dbh->query_stmt = nullptr;
1460 Object ret = pdo_stmt_instantiate(data->m_dbh,
1461 data->m_dbh->conn()->def_stmt_clsname,
1462 data->m_dbh->def_stmt_ctor_args);
1463 if (ret.isNull()) {
1464 pdo_raise_impl_error
1465 (data->m_dbh, nullptr, "HY000",
1466 "failed to instantiate user supplied statement class");
1467 return init_null();
1469 PDOStatementData *pdostmt = Native::data<PDOStatementData>(ret);
1471 if (data->m_dbh->conn()->preparer(sql, &pdostmt->m_stmt, Array())) {
1472 auto stmt = pdostmt->m_stmt;
1473 assert(stmt);
1475 /* unconditionally keep this for later reference */
1476 stmt->query_string = sql;
1477 stmt->default_fetch_type = data->m_dbh->conn()->default_fetch_type;
1478 stmt->active_query_string = stmt->query_string;
1479 stmt->dbh = data->m_dbh;
1480 stmt->lazy_object_ref.unset();
1482 setPDOErrorNone(stmt->error_code);
1484 // when we add support for varargs here, we only need to set the stmt if
1485 // the argument count is > 1
1486 int argc = _argv.size() + 1;
1487 if (argc == 1 ||
1488 pdo_stmt_set_fetch_mode(stmt, 0, _argv.rvalAt(0).toInt64Val(),
1489 HHVM_FN(array_splice)(_argv, 1).toArray())) {
1490 /* now execute the statement */
1491 setPDOErrorNone(stmt->error_code);
1492 if (stmt->executer()) {
1493 int ok = 1;
1494 if (!stmt->executed) {
1495 if (stmt->dbh->conn()->alloc_own_columns) {
1496 ok = pdo_stmt_describe_columns(stmt);
1498 stmt->executed = 1;
1500 if (ok) {
1501 pdo_stmt_construct(stmt, ret, data->m_dbh->conn()->def_stmt_clsname,
1502 data->m_dbh->def_stmt_ctor_args);
1503 return ret;
1507 /* something broke */
1508 data->m_dbh->query_stmt = stmt.get();
1509 PDO_HANDLE_STMT_ERR(stmt);
1510 } else {
1511 PDO_HANDLE_DBH_ERR(data->m_dbh);
1514 return false;
1517 static Variant HHVM_METHOD(PDO, quote, const String& str,
1518 int64_t paramtype /* = q_PDO$$PARAM_STR */) {
1519 auto data = Native::data<PDOData>(this_);
1521 assert(data->m_dbh->conn()->driver);
1522 setPDOErrorNone(data->m_dbh->conn()->error_code);
1523 data->m_dbh->query_stmt = nullptr;
1525 if (!data->m_dbh->conn()->support(PDOConnection::MethodQuoter)) {
1526 pdo_raise_impl_error(data->m_dbh, nullptr, "IM001",
1527 "driver does not support quoting");
1528 return false;
1531 String quoted;
1532 if (data->m_dbh->conn()->quoter(str, quoted, (PDOParamType)paramtype)) {
1533 return quoted;
1535 PDO_HANDLE_DBH_ERR(data->m_dbh);
1536 return false;
1539 static bool HHVM_METHOD(PDO, sqlitecreatefunction, const String& name,
1540 const Variant& callback, int64_t argcount /* = -1 */) {
1541 #ifdef ENABLE_EXTENSION_PDO_SQLITE
1542 auto data = Native::data<PDOData>(this_);
1544 auto conn = std::dynamic_pointer_cast<PDOSqliteConnection>(
1545 data->m_dbh->conn());
1546 if (conn == nullptr) {
1547 return false;
1549 return conn->createFunction(name, callback, argcount);
1550 #else
1551 raise_recoverable_error("PDO::sqliteCreateFunction not implemented");
1552 return false;
1553 #endif
1556 static bool HHVM_METHOD(PDO, sqlitecreateaggregate, const String& name,
1557 const Variant& step, const Variant& final,
1558 int64_t argcount /* = -1 */) {
1559 raise_recoverable_error("PDO::sqliteCreateAggregate not implemented");
1560 return false;
1563 static Variant HHVM_METHOD(PDO, __wakeup) {
1564 throw_pdo_exception(uninit_null(), uninit_null(),
1565 "You cannot serialize or unserialize PDO instances");
1566 return init_null();
1569 static Variant HHVM_METHOD(PDO, __sleep) {
1570 throw_pdo_exception(uninit_null(), uninit_null(),
1571 "You cannot serialize or unserialize PDO instances");
1572 return init_null();
1575 static Array HHVM_STATIC_METHOD(PDO, getAvailableDrivers) {
1576 return f_pdo_drivers();
1579 ///////////////////////////////////////////////////////////////////////////////
1581 static inline bool rewrite_name_to_position(sp_PDOStatement stmt,
1582 sp_PDOBoundParam param) {
1583 if (!stmt->bound_param_map.empty()) {
1584 /* rewriting :name to ? style.
1585 * We need to fixup the parameter numbers on the parameters.
1586 * If we find that a given named parameter has been used twice,
1587 * we will raise an error, as we can't be sure that it is safe
1588 * to bind multiple parameters onto the same zval in the underlying
1589 * driver */
1590 if (stmt->named_rewrite_template) {
1591 /* this is not an error here */
1592 return true;
1594 if (param->name.empty()) {
1595 /* do the reverse; map the parameter number to the name */
1596 if (stmt->bound_param_map.exists(param->paramno)) {
1597 param->name = stmt->bound_param_map[param->paramno].toString();
1598 return true;
1600 pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
1601 "parameter was not defined");
1602 return false;
1605 int position = 0;
1606 for (ArrayIter iter(stmt->bound_param_map); iter; ++iter, ++position) {
1607 if (iter.second().toString() == param->name) {
1608 if (param->paramno >= 0) {
1609 pdo_raise_impl_error
1610 (stmt->dbh, stmt, "IM001",
1611 "PDO refuses to handle repeating the same :named parameter "
1612 "for multiple positions with this driver, as it might be "
1613 "unsafe to do so. Consider using a separate name for each "
1614 "parameter instead");
1615 return true;
1617 param->paramno = position;
1618 return true;
1621 pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
1622 "parameter was not defined");
1623 return false;
1625 return true;
1628 /* trigger callback hook for parameters */
1629 static bool dispatch_param_event(sp_PDOStatement stmt,
1630 PDOParamEvent event_type) {
1631 if (!stmt->support(PDOStatement::MethodParamHook)) {
1632 return true;
1634 for (ArrayIter iter(stmt->bound_params); iter; ++iter) {
1635 auto param = cast<PDOBoundParam>(iter.second());
1636 if (!stmt->paramHook(param.get(), event_type)) {
1637 return false;
1640 for (ArrayIter iter(stmt->bound_columns); iter; ++iter) {
1641 auto param = cast<PDOBoundParam>(iter.second());
1642 if (!stmt->paramHook(param.get(), event_type)) {
1643 return false;
1646 return true;
1649 static void get_lazy_object(sp_PDOStatement stmt, Variant &ret) {
1650 if (stmt->lazy_object_ref.isNull()) {
1651 stmt->lazy_object_ref = stmt;
1653 ret = stmt->lazy_object_ref;
1656 static bool really_register_bound_param(sp_PDOBoundParam param,
1657 sp_PDOStatement stmt,
1658 bool is_param) {
1659 Array &hash = is_param ? stmt->bound_params : stmt->bound_columns;
1661 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_STR &&
1662 param->max_value_len <= 0 && !param->parameter.isNull()) {
1663 param->parameter = param->parameter.toString();
1664 } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_INT &&
1665 param->parameter.isBoolean()) {
1666 param->parameter = param->parameter.toInt64();
1667 } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL &&
1668 param->parameter.isInteger()) {
1669 param->parameter = param->parameter.toBoolean();
1671 param->stmt = stmt.get();
1672 param->is_param = is_param;
1674 if (!is_param && !param->name.empty() && !stmt->columns.empty()) {
1675 /* try to map the name to the column */
1676 for (int i = 0; i < stmt->column_count; i++) {
1677 if (cast<PDOColumn>(stmt->columns[i])->name == param->name) {
1678 param->paramno = i;
1679 break;
1683 /* if you prepare and then execute passing an array of params keyed by
1684 names, then this will trigger, and we don't want that */
1685 if (param->paramno == -1) {
1686 raise_warning("Did not found column name '%s' in the defined columns;"
1687 " it will not be bound", param->name.data());
1691 if (is_param && !param->name.empty() && param->name[0] != ':') {
1692 param->name = String(":") + param->name;
1695 if (is_param && !rewrite_name_to_position(stmt, param)) {
1696 param->name.reset();
1697 return false;
1700 /* ask the driver to perform any normalization it needs on the
1701 * parameter name. Note that it is illegal for the driver to take
1702 * a reference to param, as it resides in transient storage only
1703 * at this time. */
1704 if (stmt->support(PDOStatement::MethodParamHook)) {
1705 if (!stmt->paramHook(param.get(), PDO_PARAM_EVT_NORMALIZE)) {
1706 param->name.reset();
1707 return false;
1711 /* delete any other parameter registered with this number.
1712 * If the parameter is named, it will be removed and correctly
1713 * disposed of by the hash_update call that follows */
1714 if (param->paramno >= 0) {
1715 hash.remove(param->paramno);
1718 /* allocate storage for the parameter, keyed by its "canonical" name */
1719 if (!param->name.empty()) {
1720 hash.set(param->name, Variant(param));
1721 } else {
1722 hash.set(param->paramno, Variant(param));
1725 /* tell the driver we just created a parameter */
1726 if (stmt->support(PDOStatement::MethodParamHook)) {
1727 if (!stmt->paramHook(param.get(), PDO_PARAM_EVT_ALLOC)) {
1728 /* undo storage allocation; the hash will free the parameter
1729 * name if required */
1730 if (!param->name.empty()) {
1731 hash.remove(param->name);
1732 } else {
1733 hash.remove(param->paramno);
1735 param->parameter.unset();
1736 return false;
1739 return true;
1742 static inline void fetch_value(sp_PDOStatement stmt, Variant &dest, int colno,
1743 int *type_override) {
1744 if (colno < 0 || colno >= stmt->column_count) {
1745 return;
1747 auto col = cast<PDOColumn>(stmt->columns[colno]);
1748 int type = PDO_PARAM_TYPE(col->param_type);
1749 int new_type = type_override ? PDO_PARAM_TYPE(*type_override) : type;
1751 stmt->getColumn(colno, dest);
1753 if (type != new_type) {
1754 switch (new_type) {
1755 case PDO_PARAM_INT: dest = dest.toInt64(); break;
1756 case PDO_PARAM_BOOL: dest = dest.toBoolean(); break;
1757 case PDO_PARAM_STR: dest = dest.toString(); break;
1758 case PDO_PARAM_NULL: dest = init_null(); break;
1761 if (stmt->dbh->conn()->stringify && (dest.isInteger() || dest.isDouble())) {
1762 dest = dest.toString();
1764 if (dest.isNull() && stmt->dbh->conn()->oracle_nulls == PDO_NULL_TO_STRING) {
1765 dest = empty_string_variant();
1769 static bool do_fetch_common(sp_PDOStatement stmt, PDOFetchOrientation ori,
1770 long offset, bool do_bind) {
1771 if (!stmt->executed) {
1772 return false;
1774 if (!dispatch_param_event(stmt, PDO_PARAM_EVT_FETCH_PRE)) {
1775 return false;
1777 if (!stmt->fetcher(ori, offset)) {
1778 return false;
1780 /* some drivers might need to describe the columns now */
1781 if (stmt->columns.empty() && !pdo_stmt_describe_columns(stmt)) {
1782 return false;
1784 if (!dispatch_param_event(stmt, PDO_PARAM_EVT_FETCH_POST)) {
1785 return false;
1788 if (do_bind && !stmt->bound_columns.empty()) {
1789 /* update those bound column variables now */
1790 for (ArrayIter iter(stmt->bound_columns); iter; ++iter) {
1791 auto param = cast<PDOBoundParam>(iter.second());
1792 if (param->paramno >= 0) {
1793 param->parameter.setNull();
1794 /* set new value */
1795 fetch_value(stmt, param->parameter, param->paramno,
1796 (int *)&param->param_type);
1797 /* TODO: some smart thing that avoids duplicating the value in the
1798 * general loop below. For now, if you're binding output columns,
1799 * it's better to use LAZY or BOUND fetches if you want to shave
1800 * off those cycles */
1805 return true;
1808 static bool do_fetch_func_prepare(sp_PDOStatement stmt) {
1809 if (!is_callable(stmt->fetch.func)) {
1810 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
1811 "user-supplied function must be a valid callback");
1812 return false;
1814 return true;
1817 /* perform a fetch. If do_bind is true, update any bound columns.
1818 * If return_value is not null, store values into it according to HOW. */
1819 static bool do_fetch(sp_PDOStatement stmt,
1820 bool do_bind,
1821 Variant& ret,
1822 PDOFetchType how,
1823 PDOFetchOrientation ori,
1824 long offset,
1825 Variant *return_all) {
1826 if (how == PDO_FETCH_USE_DEFAULT) {
1827 how = stmt->default_fetch_type;
1829 int flags = how & PDO_FETCH_FLAGS;
1830 how = (PDOFetchType)(how & ~PDO_FETCH_FLAGS);
1832 if (!do_fetch_common(stmt, ori, offset, do_bind)) {
1833 return false;
1836 if (how == PDO_FETCH_BOUND) {
1837 ret = true;
1838 return true;
1841 int colno;
1842 if ((flags & PDO_FETCH_GROUP) && stmt->fetch.column == -1) {
1843 colno = 1;
1844 } else {
1845 colno = stmt->fetch.column;
1848 if (how == PDO_FETCH_LAZY) {
1849 get_lazy_object(stmt, ret);
1850 return true;
1853 String clsname, old_clsname;
1854 Variant old_ctor_args;
1855 ret = false;
1856 int i = 0;
1857 switch (how) {
1858 case PDO_FETCH_USE_DEFAULT:
1859 case PDO_FETCH_ASSOC:
1860 case PDO_FETCH_BOTH:
1861 case PDO_FETCH_NUM:
1862 case PDO_FETCH_NAMED:
1863 ret = Array::Create();
1864 break;
1866 case PDO_FETCH_KEY_PAIR:
1867 if (stmt->column_count != 2) {
1868 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
1869 "PDO::FETCH_KEY_PAIR fetch mode requires the "
1870 "result set to contain extactly 2 columns.");
1871 return false;
1873 if (!return_all) {
1874 ret = Array::Create();
1876 break;
1878 case PDO_FETCH_COLUMN:
1879 if (colno >= 0 && colno < stmt->column_count) {
1880 if (flags == PDO_FETCH_GROUP && stmt->fetch.column == -1) {
1881 fetch_value(stmt, ret, 1, NULL);
1882 } else if (flags == PDO_FETCH_GROUP && colno) {
1883 fetch_value(stmt, ret, 0, NULL);
1884 } else {
1885 fetch_value(stmt, ret, colno, NULL);
1887 if (!return_all) {
1888 return true;
1890 break;
1891 } else {
1892 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Invalid column index");
1894 return false;
1896 case PDO_FETCH_OBJ:
1897 ret = SystemLib::AllocStdClassObject();
1898 break;
1900 case PDO_FETCH_CLASS:
1901 if (flags & PDO_FETCH_CLASSTYPE) {
1902 old_clsname = stmt->fetch.clsname;
1903 old_ctor_args = stmt->fetch.ctor_args;
1905 Variant val;
1906 fetch_value(stmt, val, i++, NULL);
1907 if (!val.isNull()) {
1908 if (!HHVM_FN(class_exists)(val.toString())) {
1909 stmt->fetch.clsname = "stdclass";
1910 } else {
1911 stmt->fetch.clsname = val.toString();
1915 do_fetch_class_prepare(stmt);
1917 clsname = stmt->fetch.clsname;
1918 if (clsname.empty()) {
1919 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
1920 "No fetch class specified");
1921 return false;
1923 if ((flags & PDO_FETCH_SERIALIZE) == 0) {
1924 ret = create_object_only(clsname);
1925 if (!do_fetch_class_prepare(stmt)) {
1926 return false;
1928 if (!stmt->fetch.constructor.empty() &&
1929 (flags & PDO_FETCH_PROPS_LATE)) {
1930 ret.asCObjRef()->o_invoke(stmt->fetch.constructor,
1931 stmt->fetch.ctor_args.toArray());
1932 ret.asCObjRef()->clearNoDestruct();
1935 break;
1937 case PDO_FETCH_INTO:
1938 if (stmt->fetch.into.isNull()) {
1939 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
1940 "No fetch-into object specified.");
1941 return false;
1944 ret = stmt->fetch.into;
1945 if (ret.isObject() &&
1946 ret.getObjectData()->instanceof(SystemLib::s_stdclassClass)) {
1947 how = PDO_FETCH_OBJ;
1949 break;
1951 case PDO_FETCH_FUNC:
1952 if (stmt->fetch.func.empty()) {
1953 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
1954 "No fetch function specified");
1955 return false;
1957 if (!do_fetch_func_prepare(stmt)) {
1958 return false;
1960 break;
1962 default:
1963 assert(false);
1964 return false;
1967 Variant grp_val;
1968 if (return_all && how != PDO_FETCH_KEY_PAIR) {
1969 if (flags == PDO_FETCH_GROUP && how == PDO_FETCH_COLUMN &&
1970 stmt->fetch.column > 0) {
1971 fetch_value(stmt, grp_val, colno, NULL);
1972 } else {
1973 fetch_value(stmt, grp_val, i, NULL);
1975 grp_val = grp_val.toString();
1976 if (how == PDO_FETCH_COLUMN) {
1977 i = stmt->column_count; /* no more data to fetch */
1978 } else {
1979 i++;
1983 for (int idx = 0; i < stmt->column_count; i++, idx++) {
1984 const String& name = cast<PDOColumn>(stmt->columns[i])->name;
1985 Variant val;
1986 fetch_value(stmt, val, i, NULL);
1988 switch (how) {
1989 case PDO_FETCH_ASSOC:
1990 ret.toArrRef().set(name, val);
1991 break;
1993 case PDO_FETCH_KEY_PAIR: {
1994 Variant tmp;
1995 fetch_value(stmt, tmp, ++i, NULL);
1996 if (return_all) {
1997 return_all->toArrRef().set(val, tmp);
1998 } else {
1999 ret.toArrRef().set(val, tmp);
2001 return true;
2003 case PDO_FETCH_USE_DEFAULT:
2004 case PDO_FETCH_BOTH:
2005 ret.toArrRef().set(name, val);
2006 ret.toArrRef().append(val);
2007 break;
2009 case PDO_FETCH_NAMED: {
2010 /* already have an item with this name? */
2011 forceToArray(ret);
2012 if (ret.toArrRef().exists(name)) {
2013 auto& curr_val = ret.toArrRef().lvalAt(name);
2014 if (!curr_val.isArray()) {
2015 Array arr = Array::Create();
2016 arr.append(curr_val);
2017 arr.append(val);
2018 ret.toArray().set(name, arr);
2019 } else {
2020 curr_val.toArrRef().append(val);
2022 } else {
2023 ret.toArrRef().set(name, val);
2025 break;
2027 case PDO_FETCH_NUM:
2028 ret.toArrRef().append(val);
2029 break;
2031 case PDO_FETCH_OBJ:
2032 case PDO_FETCH_INTO:
2033 ret.toObject()->o_set(name, val);
2034 break;
2036 case PDO_FETCH_CLASS:
2037 if ((flags & PDO_FETCH_SERIALIZE) == 0 || idx) {
2038 ret.toObject()->o_set(name, val);
2039 } else {
2040 #ifdef MBO_0
2041 ret = unserialize_from_string(val);
2042 if (same(ret, false)) {
2043 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
2044 "cannot unserialize data");
2045 return false;
2047 #endif
2048 // hzhao: not sure how we support class serialization
2049 pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
2050 "cannot unserialize class");
2051 return false;
2053 break;
2055 case PDO_FETCH_FUNC:
2056 forceToArray(stmt->fetch.values).set(idx, val);
2057 break;
2059 default:
2060 pdo_raise_impl_error(stmt->dbh, stmt, "22003", "mode is out of range");
2061 return false;
2065 switch (how) {
2066 case PDO_FETCH_CLASS:
2067 if (!stmt->fetch.constructor.empty() &&
2068 !(flags & (PDO_FETCH_PROPS_LATE | PDO_FETCH_SERIALIZE))) {
2069 ret.toObject()->o_invoke(stmt->fetch.constructor,
2070 stmt->fetch.ctor_args.toArray());
2071 ret.toObject()->clearNoDestruct();
2073 if (flags & PDO_FETCH_CLASSTYPE) {
2074 stmt->fetch.clsname = old_clsname;
2075 stmt->fetch.ctor_args = old_ctor_args;
2077 break;
2079 case PDO_FETCH_FUNC:
2080 ret = vm_call_user_func(stmt->fetch.func,
2081 stmt->fetch.values.toArray());
2082 break;
2084 default:
2085 break;
2088 if (return_all) {
2089 if ((flags & PDO_FETCH_UNIQUE) == PDO_FETCH_UNIQUE) {
2090 return_all->toArrRef().set(grp_val, ret);
2091 } else {
2092 auto& lval = return_all->toArrRef().lvalAt(grp_val);
2093 forceToArray(lval).append(ret);
2097 return true;
2100 static int register_bound_param(const Variant& paramno, VRefParam param,
2101 int64_t type, int64_t max_value_len,
2102 const Variant& driver_params,
2103 sp_PDOStatement stmt, bool is_param) {
2104 auto p = req::make<PDOBoundParam>();
2105 // need to make sure this is NULL, in case a fatal errors occurs before it's
2106 // set inside really_register_bound_param
2107 p->stmt = NULL;
2109 if (paramno.isNumeric()) {
2110 p->paramno = paramno.toInt64();
2111 } else {
2112 p->paramno = -1;
2113 p->name = paramno.toString();
2116 p->parameter.setWithRef(param);
2117 p->param_type = (PDOParamType)type;
2118 p->max_value_len = max_value_len;
2119 p->driver_params = driver_params;
2121 if (p->paramno > 0) {
2122 --p->paramno; /* make it zero-based internally */
2123 } else if (p->name.empty()) {
2124 pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
2125 "Columns/Parameters are 1-based");
2126 return false;
2129 if (!really_register_bound_param(p, stmt, is_param)) {
2130 p->parameter.unset();
2131 return false;
2133 return true;
2136 static bool generic_stmt_attr_get(sp_PDOStatement stmt, Variant &ret,
2137 long attr) {
2138 if (attr == PDO_ATTR_EMULATE_PREPARES) {
2139 ret = (bool)(stmt->supports_placeholders == PDO_PLACEHOLDER_NONE);
2140 return true;
2142 return false;
2145 ///////////////////////////////////////////////////////////////////////////////
2146 // SQL parser
2148 #define PDO_PARSER_TEXT 1
2149 #define PDO_PARSER_BIND 2
2150 #define PDO_PARSER_BIND_POS 3
2151 #define PDO_PARSER_EOI 4
2153 #define RET(i) {s->cur = cursor; return i; }
2154 #define SKIP_ONE(i) {s->cur = s->tok + 1; return 1; }
2156 #define YYCTYPE unsigned char
2157 #define YYCURSOR cursor
2158 #define YYLIMIT limit
2159 #define YYMARKER s->ptr
2160 #define YYFILL(n) RET(PDO_PARSER_EOI)
2162 typedef struct Scanner {
2163 char *ptr, *cur, *lim, *tok;
2164 } Scanner;
2166 static int scan(Scanner *s) {
2167 char* cursor = s->cur;
2168 char* limit = s->lim;
2169 s->tok = cursor;
2172 YYCTYPE yych;
2174 if ((YYLIMIT - YYCURSOR) < 2) { YYFILL(2); }
2175 yych = *YYCURSOR;
2176 switch (yych) {
2177 case 0x00: goto yy11;
2178 case '"': goto yy2;
2179 case '\'': goto yy4;
2180 case ':': goto yy5;
2181 case '?': goto yy6;
2182 default: goto yy8;
2184 yy2:
2185 yych = *(YYMARKER = ++YYCURSOR);
2186 if (yych >= 0x01) goto yy26;
2187 yy3:
2188 { SKIP_ONE(PDO_PARSER_TEXT); }
2189 yy4:
2190 yych = *(YYMARKER = ++YYCURSOR);
2191 if (yych <= 0x00) goto yy3;
2192 goto yy20;
2193 yy5:
2194 yych = *++YYCURSOR;
2195 switch (yych) {
2196 case '0':
2197 case '1':
2198 case '2':
2199 case '3':
2200 case '4':
2201 case '5':
2202 case '6':
2203 case '7':
2204 case '8':
2205 case '9':
2206 case 'A':
2207 case 'B':
2208 case 'C':
2209 case 'D':
2210 case 'E':
2211 case 'F':
2212 case 'G':
2213 case 'H':
2214 case 'I':
2215 case 'J':
2216 case 'K':
2217 case 'L':
2218 case 'M':
2219 case 'N':
2220 case 'O':
2221 case 'P':
2222 case 'Q':
2223 case 'R':
2224 case 'S':
2225 case 'T':
2226 case 'U':
2227 case 'V':
2228 case 'W':
2229 case 'X':
2230 case 'Y':
2231 case 'Z':
2232 case '_':
2233 case 'a':
2234 case 'b':
2235 case 'c':
2236 case 'd':
2237 case 'e':
2238 case 'f':
2239 case 'g':
2240 case 'h':
2241 case 'i':
2242 case 'j':
2243 case 'k':
2244 case 'l':
2245 case 'm':
2246 case 'n':
2247 case 'o':
2248 case 'p':
2249 case 'q':
2250 case 'r':
2251 case 's':
2252 case 't':
2253 case 'u':
2254 case 'v':
2255 case 'w':
2256 case 'x':
2257 case 'y':
2258 case 'z': goto yy16;
2259 case ':':
2260 case '?': goto yy13;
2261 default: goto yy3;
2263 yy6:
2264 ++YYCURSOR;
2265 switch ((yych = *YYCURSOR)) {
2266 case ':':
2267 case '?': goto yy13;
2268 default: goto yy7;
2270 yy7:
2271 { RET(PDO_PARSER_BIND_POS); }
2272 yy8:
2273 ++YYCURSOR;
2274 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2275 yych = *YYCURSOR;
2276 switch (yych) {
2277 case 0x00:
2278 case '"':
2279 case '\'':
2280 case ':':
2281 case '?': goto yy10;
2282 default: goto yy8;
2284 yy10:
2285 { RET(PDO_PARSER_TEXT); }
2286 yy11:
2287 ++YYCURSOR;
2288 { RET(PDO_PARSER_EOI); }
2289 yy13:
2290 ++YYCURSOR;
2291 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2292 yych = *YYCURSOR;
2293 switch (yych) {
2294 case ':':
2295 case '?': goto yy13;
2296 default: goto yy15;
2298 yy15:
2299 { RET(PDO_PARSER_TEXT); }
2300 yy16:
2301 ++YYCURSOR;
2302 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2303 yych = *YYCURSOR;
2304 switch (yych) {
2305 case '0':
2306 case '1':
2307 case '2':
2308 case '3':
2309 case '4':
2310 case '5':
2311 case '6':
2312 case '7':
2313 case '8':
2314 case '9':
2315 case 'A':
2316 case 'B':
2317 case 'C':
2318 case 'D':
2319 case 'E':
2320 case 'F':
2321 case 'G':
2322 case 'H':
2323 case 'I':
2324 case 'J':
2325 case 'K':
2326 case 'L':
2327 case 'M':
2328 case 'N':
2329 case 'O':
2330 case 'P':
2331 case 'Q':
2332 case 'R':
2333 case 'S':
2334 case 'T':
2335 case 'U':
2336 case 'V':
2337 case 'W':
2338 case 'X':
2339 case 'Y':
2340 case 'Z':
2341 case '_':
2342 case 'a':
2343 case 'b':
2344 case 'c':
2345 case 'd':
2346 case 'e':
2347 case 'f':
2348 case 'g':
2349 case 'h':
2350 case 'i':
2351 case 'j':
2352 case 'k':
2353 case 'l':
2354 case 'm':
2355 case 'n':
2356 case 'o':
2357 case 'p':
2358 case 'q':
2359 case 'r':
2360 case 's':
2361 case 't':
2362 case 'u':
2363 case 'v':
2364 case 'w':
2365 case 'x':
2366 case 'y':
2367 case 'z': goto yy16;
2368 default: goto yy18;
2370 yy18:
2371 { RET(PDO_PARSER_BIND); }
2372 yy19:
2373 ++YYCURSOR;
2374 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2375 yych = *YYCURSOR;
2376 yy20:
2377 switch (yych) {
2378 case 0x00: goto yy21;
2379 case '\'': goto yy23;
2380 case '\\': goto yy22;
2381 default: goto yy19;
2383 yy21:
2384 YYCURSOR = YYMARKER;
2385 goto yy3;
2386 yy22:
2387 ++YYCURSOR;
2388 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2389 yych = *YYCURSOR;
2390 if (yych <= 0x00) goto yy21;
2391 goto yy19;
2392 yy23:
2393 ++YYCURSOR;
2394 { RET(PDO_PARSER_TEXT); }
2395 yy25:
2396 ++YYCURSOR;
2397 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2398 yych = *YYCURSOR;
2399 yy26:
2400 switch (yych) {
2401 case 0x00: goto yy21;
2402 case '"': goto yy28;
2403 case '\\': goto yy27;
2404 default: goto yy25;
2406 yy27:
2407 ++YYCURSOR;
2408 if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
2409 yych = *YYCURSOR;
2410 if (yych <= 0x00) goto yy21;
2411 goto yy25;
2412 yy28:
2413 ++YYCURSOR;
2414 { RET(PDO_PARSER_TEXT); }
2419 struct placeholder {
2420 char *pos;
2421 int len;
2422 int bindno;
2423 String quoted; /* quoted value */
2424 struct placeholder *next;
2427 int pdo_parse_params(sp_PDOStatement stmt, const String& in, String &out) {
2428 Scanner s;
2429 const char *ptr;
2430 char *newbuffer;
2431 int t;
2432 int bindno = 0;
2433 int ret = 0;
2434 int newbuffer_len;
2435 Array params;
2436 req::ptr<PDOBoundParam> param;
2437 int query_type = PDO_PLACEHOLDER_NONE;
2438 struct placeholder *placeholders = NULL, *placetail = NULL, *plc = NULL;
2440 s.cur = (char*)in.data();
2441 s.lim = (char*)in.data() + in.size() + 1;
2443 /* phase 1: look for args */
2444 while ((t = scan(&s)) != PDO_PARSER_EOI) {
2445 if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS) {
2446 if (t == PDO_PARSER_BIND) {
2447 int len = s.cur - s.tok;
2448 if ((in.data() < (s.cur - len)) && isalnum(*(s.cur - len - 1))) {
2449 continue;
2451 query_type |= PDO_PLACEHOLDER_NAMED;
2452 } else {
2453 query_type |= PDO_PLACEHOLDER_POSITIONAL;
2456 plc = (placeholder*)req::malloc(sizeof(*plc));
2457 memset(plc, 0, sizeof(*plc));
2458 plc->next = NULL;
2459 plc->pos = s.tok;
2460 plc->len = s.cur - s.tok;
2461 plc->bindno = bindno++;
2463 if (placetail) {
2464 placetail->next = plc;
2465 } else {
2466 placeholders = plc;
2468 placetail = plc;
2472 if (bindno == 0) {
2473 /* nothing to do; good! */
2474 return 0;
2477 /* did the query make sense to me? */
2478 if (query_type == (PDO_PLACEHOLDER_NAMED|PDO_PLACEHOLDER_POSITIONAL)) {
2479 /* they mixed both types; punt */
2480 pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
2481 "mixed named and positional parameters");
2482 ret = -1;
2483 goto clean_up;
2486 if ((int)stmt->supports_placeholders == query_type &&
2487 !stmt->named_rewrite_template) {
2488 /* query matches native syntax */
2489 ret = 0;
2490 goto clean_up;
2493 if (stmt->named_rewrite_template) {
2494 /* magic/hack.
2495 * We we pretend that the query was positional even if
2496 * it was named so that we fall into the
2497 * named rewrite case below. Not too pretty,
2498 * but it works. */
2499 query_type = PDO_PLACEHOLDER_POSITIONAL;
2502 params = stmt->bound_params;
2504 /* Do we have placeholders but no bound params */
2505 if (bindno && params.empty() &&
2506 stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
2507 pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "no parameters were bound");
2508 ret = -1;
2509 goto clean_up;
2512 if (!params.empty() && bindno != params.size() &&
2513 stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
2514 /* extra bit of validation for instances when same params are bound
2515 more then once */
2516 if (query_type != PDO_PLACEHOLDER_POSITIONAL && bindno > params.size()) {
2517 int ok = 1;
2518 for (plc = placeholders; plc; plc = plc->next) {
2519 if (!params.exists(String(plc->pos, plc->len, CopyString))) {
2520 ok = 0;
2521 break;
2524 if (ok) {
2525 goto safe;
2528 pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
2529 "number of bound variables does not match number "
2530 "of tokens");
2531 ret = -1;
2532 goto clean_up;
2534 safe:
2535 /* what are we going to do ? */
2536 if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
2537 /* query generation */
2539 newbuffer_len = in.size();
2541 /* let's quote all the values */
2542 for (plc = placeholders; plc; plc = plc->next) {
2543 Variant vparam;
2544 if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
2545 vparam = params[plc->bindno];
2546 } else {
2547 vparam = params[String(plc->pos, plc->len, CopyString)];
2549 if (vparam.isNull()) {
2550 /* parameter was not defined */
2551 ret = -1;
2552 pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
2553 "parameter was not defined");
2554 goto clean_up;
2556 param = cast<PDOBoundParam>(vparam);
2557 if (stmt->dbh->conn()->support(PDOConnection::MethodQuoter)) {
2558 if (param->param_type == PDO_PARAM_LOB &&
2559 param->parameter.isResource()) {
2560 Variant buf = HHVM_FN(stream_get_contents)(
2561 param->parameter.toResource());
2562 if (!same(buf, false)) {
2563 if (!stmt->dbh->conn()->quoter(buf.toString(), plc->quoted,
2564 param->param_type)) {
2565 /* bork */
2566 ret = -1;
2567 setPDOError(stmt->error_code, stmt->dbh->conn()->error_code);
2568 goto clean_up;
2570 } else {
2571 pdo_raise_impl_error(stmt->dbh, stmt, "HY105",
2572 "Expected a stream resource");
2573 ret = -1;
2574 goto clean_up;
2576 } else {
2577 do {
2578 switch (param->parameter.getType()) {
2579 case KindOfUninit:
2580 case KindOfNull:
2581 plc->quoted = "NULL";
2582 continue;
2584 case KindOfInt64:
2585 case KindOfDouble:
2586 plc->quoted = param->parameter.toString();
2587 continue;
2589 case KindOfBoolean:
2590 param->parameter = param->parameter.toInt64();
2591 // fallthru
2592 case KindOfPersistentString:
2593 case KindOfString:
2594 case KindOfPersistentArray:
2595 case KindOfArray:
2596 case KindOfObject:
2597 case KindOfResource:
2598 case KindOfRef:
2599 if (!stmt->dbh->conn()->quoter(
2600 param->parameter.toString(),
2601 plc->quoted,
2602 param->param_type)) {
2603 /* bork */
2604 ret = -1;
2605 setPDOError(stmt->error_code, stmt->dbh->conn()->error_code);
2606 goto clean_up;
2608 continue;
2610 case KindOfClass:
2611 break;
2613 not_reached();
2614 } while (0);
2616 } else {
2617 plc->quoted = param->parameter;
2619 newbuffer_len += plc->quoted.size();
2622 rewrite:
2623 /* allocate output buffer */
2624 out = String(newbuffer_len, ReserveString);
2625 newbuffer = out.mutableData();
2627 /* and build the query */
2628 plc = placeholders;
2629 ptr = in.data();
2631 do {
2632 t = plc->pos - ptr;
2633 if (t) {
2634 memcpy(newbuffer, ptr, t);
2635 newbuffer += t;
2637 memcpy(newbuffer, plc->quoted.data(), plc->quoted.size());
2638 newbuffer += plc->quoted.size();
2639 ptr = plc->pos + plc->len;
2641 plc = plc->next;
2642 } while (plc);
2644 t = (in.data() + in.size()) - ptr;
2645 if (t) {
2646 memcpy(newbuffer, ptr, t);
2647 newbuffer += t;
2649 out.setSize(newbuffer - out.data());
2651 ret = 1;
2652 goto clean_up;
2654 } else if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
2655 /* rewrite ? to :pdoX */
2656 StringBuffer idxbuf;
2657 const char *tmpl = stmt->named_rewrite_template ?
2658 stmt->named_rewrite_template : ":pdo%d";
2659 int bind_no = 1;
2661 newbuffer_len = in.size();
2663 for (plc = placeholders; plc; plc = plc->next) {
2664 int skip_map = 0;
2665 String name(plc->pos, plc->len, CopyString);
2667 /* check if bound parameter is already available */
2668 if (!strcmp(name.c_str(), "?") ||
2669 !stmt->bound_param_map.exists(name)) {
2670 idxbuf.printf(tmpl, bind_no++);
2671 } else {
2672 idxbuf.clear();
2673 idxbuf.append(stmt->bound_param_map[name].toString());
2674 skip_map = 1;
2677 plc->quoted = idxbuf.detach();
2678 newbuffer_len += plc->quoted.size();
2680 if (!skip_map && stmt->named_rewrite_template) {
2681 /* create a mapping */
2682 stmt->bound_param_map.set(name, plc->quoted);
2685 /* map number to name */
2686 stmt->bound_param_map.set(plc->bindno, plc->quoted);
2689 goto rewrite;
2691 } else {
2692 /* rewrite :name to ? */
2694 newbuffer_len = in.size();
2696 for (plc = placeholders; plc; plc = plc->next) {
2697 String name(plc->pos, plc->len, CopyString);
2698 stmt->bound_param_map.set(plc->bindno, name);
2699 plc->quoted = "?";
2702 goto rewrite;
2705 clean_up:
2707 while (placeholders) {
2708 plc = placeholders;
2709 placeholders = plc->next;
2710 plc->quoted.reset();
2711 req::free(plc);
2714 return ret;
2717 ///////////////////////////////////////////////////////////////////////////////
2718 // PDOStatement
2720 const StaticString s_PDOStatement("PDOStatement");
2722 PDOStatementData::PDOStatementData() : m_rowIndex(-1) {
2725 PDOStatementData::~PDOStatementData() { }
2727 static Variant HHVM_METHOD(PDOStatement, execute,
2728 const Variant& paramsV /* = null_array */) {
2729 auto data = Native::data<PDOStatementData>(this_);
2730 auto params = paramsV.isNull() ? null_array : paramsV.toArray();
2732 SYNC_VM_REGS_SCOPED();
2734 if (data->m_stmt == nullptr) {
2735 return init_null_variant;
2738 setPDOErrorNone(data->m_stmt->error_code);
2740 if (!params.empty()) {
2741 data->m_stmt->bound_params.reset();
2742 for (ArrayIter iter(params); iter; ++iter) {
2743 auto param = req::make<PDOBoundParam>();
2744 param->param_type = PDO_PARAM_STR;
2745 param->parameter = iter.second();
2746 param->stmt = NULL;
2748 if (iter.first().isString()) {
2749 param->name = iter.first();
2750 param->paramno = -1;
2751 } else {
2752 int64_t num_index = iter.first().toInt64();
2753 /* we're okay to be zero based here */
2754 if (num_index < 0) {
2755 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt,
2756 "HY093", nullptr);
2757 return false;
2759 param->paramno = num_index;
2762 if (!really_register_bound_param(param, data->m_stmt, true)) {
2763 return false;
2768 int ret = 1;
2769 if (PDO_PLACEHOLDER_NONE == data->m_stmt->supports_placeholders) {
2770 /* handle the emulated parameter binding, m_stmt->active_query_string
2771 holds the query with binds expanded and quoted. */
2772 ret = pdo_parse_params(data->m_stmt, data->m_stmt->query_string,
2773 data->m_stmt->active_query_string);
2774 if (ret == 0) { /* no changes were made */
2775 data->m_stmt->active_query_string = data->m_stmt->query_string;
2776 ret = 1;
2777 } else if (ret == -1) {
2778 /* something broke */
2779 PDO_HANDLE_STMT_ERR(data->m_stmt);
2780 return false;
2782 } else if (!dispatch_param_event(data->m_stmt, PDO_PARAM_EVT_EXEC_PRE)) {
2783 PDO_HANDLE_STMT_ERR(data->m_stmt);
2784 return false;
2786 if (data->m_stmt->executer()) {
2787 data->m_stmt->active_query_string.reset();
2788 if (!data->m_stmt->executed) {
2789 /* this is the first execute */
2791 if (data->m_stmt->dbh->conn()->alloc_own_columns
2792 && data->m_stmt->columns.empty()) {
2793 /* for "big boy" drivers, we need to allocate memory to fetch
2794 * the results into, so lets do that now */
2795 ret = pdo_stmt_describe_columns(data->m_stmt);
2798 data->m_stmt->executed = 1;
2801 if (ret && !dispatch_param_event(data->m_stmt, PDO_PARAM_EVT_EXEC_POST)) {
2802 return false;
2805 return (bool)ret;
2807 data->m_stmt->active_query_string.reset();
2808 PDO_HANDLE_STMT_ERR(data->m_stmt);
2809 return false;
2812 static Variant HHVM_METHOD(PDOStatement, fetch, int64_t how = 0,
2813 int64_t orientation = q_PDO$$FETCH_ORI_NEXT,
2814 int64_t offset = 0) {
2815 auto data = Native::data<PDOStatementData>(this_);
2817 SYNC_VM_REGS_SCOPED();
2819 if (data->m_stmt == nullptr) {
2820 return false;
2823 setPDOErrorNone(data->m_stmt->error_code);
2824 if (!pdo_stmt_verify_mode(data->m_stmt, how, false)) {
2825 return false;
2828 Variant ret;
2829 if (!do_fetch(data->m_stmt, true, ret, (PDOFetchType)how,
2830 (PDOFetchOrientation)orientation, offset, NULL)) {
2831 PDO_HANDLE_STMT_ERR(data->m_stmt);
2832 return false;
2834 return ret;
2837 static Variant HHVM_METHOD(PDOStatement, fetchobject,
2838 const String& class_name /* = null_string */,
2839 const Variant& ctor_args /* = null */) {
2840 auto data = Native::data<PDOStatementData>(this_);
2841 if (data->m_stmt == nullptr) {
2842 return false;
2845 setPDOErrorNone(data->m_stmt->error_code);
2846 if (!pdo_stmt_verify_mode(data->m_stmt, PDO_FETCH_CLASS, false)) {
2847 return false;
2850 String old_clsname = data->m_stmt->fetch.clsname;
2851 Variant old_ctor_args = data->m_stmt->fetch.ctor_args;
2852 bool error = false;
2854 data->m_stmt->fetch.clsname = class_name;
2855 if (class_name.empty()) {
2856 data->m_stmt->fetch.clsname = "stdclass";
2858 if (!HHVM_FN(class_exists)(data->m_stmt->fetch.clsname)) {
2859 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt, "HY000",
2860 "Could not find user-supplied class");
2861 error = true;
2863 if (!ctor_args.isNull() && !ctor_args.isArray()) {
2864 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt, "HY000",
2865 "ctor_args must be either NULL or an array");
2866 error = true;
2868 data->m_stmt->fetch.ctor_args = ctor_args;
2870 Variant ret;
2871 if (!error && !do_fetch(data->m_stmt, true, ret, PDO_FETCH_CLASS,
2872 PDO_FETCH_ORI_NEXT, 0, NULL)) {
2873 error = true;
2875 if (error) {
2876 PDO_HANDLE_STMT_ERR(data->m_stmt);
2879 data->m_stmt->fetch.clsname = old_clsname;
2880 data->m_stmt->fetch.ctor_args = old_ctor_args;
2881 if (error) {
2882 return false;
2884 return ret;
2887 static Variant HHVM_METHOD(PDOStatement, fetchcolumn,
2888 int64_t column_numner /* = 0 */) {
2889 auto data = Native::data<PDOStatementData>(this_);
2890 if (data->m_stmt == nullptr) {
2891 return false;
2894 setPDOErrorNone(data->m_stmt->error_code);
2895 if (!do_fetch_common(data->m_stmt, PDO_FETCH_ORI_NEXT, 0, true)) {
2896 PDO_HANDLE_STMT_ERR(data->m_stmt);
2897 return false;
2899 Variant ret;
2900 fetch_value(data->m_stmt, ret, column_numner, nullptr);
2901 return ret;
2904 static Variant HHVM_METHOD(PDOStatement, fetchall, int64_t how /* = 0 */,
2905 const Variant& class_name /* = null */,
2906 const Variant& ctor_args /* = null */) {
2907 auto self = Native::data<PDOStatementData>(this_);
2908 if (self->m_stmt == nullptr) {
2909 return false;
2912 if (!pdo_stmt_verify_mode(self->m_stmt, how, true)) {
2913 return false;
2916 String old_clsname = self->m_stmt->fetch.clsname;
2917 Variant old_ctor_args = self->m_stmt->fetch.ctor_args;
2918 int error = 0;
2920 switch (how & ~PDO_FETCH_FLAGS) {
2921 case PDO_FETCH_CLASS:
2922 self->m_stmt->fetch.clsname = class_name;
2923 if (class_name.isNull()) {
2924 self->m_stmt->fetch.clsname = "stdclass";
2926 if (!HHVM_FN(class_exists)(self->m_stmt->fetch.clsname)) {
2927 pdo_raise_impl_error(self->m_stmt->dbh, self->m_stmt, "HY000",
2928 "Could not find user-supplied class");
2929 error = 1;
2931 if (!ctor_args.isNull() && !ctor_args.isArray()) {
2932 pdo_raise_impl_error(self->m_stmt->dbh, self->m_stmt, "HY000",
2933 "ctor_args must be either NULL or an array");
2934 error = 1;
2935 break;
2937 self->m_stmt->fetch.ctor_args = ctor_args;
2939 if (!error) {
2940 do_fetch_class_prepare(self->m_stmt);
2942 break;
2944 case PDO_FETCH_FUNC:
2945 if (!HHVM_FN(function_exists)(class_name.toString())) {
2946 pdo_raise_impl_error(self->m_stmt->dbh, self->m_stmt, "HY000",
2947 "no fetch function specified");
2948 error = 1;
2949 } else {
2950 self->m_stmt->fetch.func = class_name;
2951 do_fetch_func_prepare(self->m_stmt);
2953 break;
2955 case PDO_FETCH_COLUMN:
2956 if (class_name.isNull()) {
2957 self->m_stmt->fetch.column = how & PDO_FETCH_GROUP ? -1 : 0;
2958 } else {
2959 self->m_stmt->fetch.column = class_name.toInt64();
2961 if (!ctor_args.isNull()) {
2962 pdo_raise_impl_error(self->m_stmt->dbh, self->m_stmt, "HY000",
2963 "Third parameter not allowed for "
2964 "PDO::FETCH_COLUMN");
2965 error = 1;
2967 break;
2970 int flags = how & PDO_FETCH_FLAGS;
2972 if ((how & ~PDO_FETCH_FLAGS) == PDO_FETCH_USE_DEFAULT) {
2973 flags |= self->m_stmt->default_fetch_type & PDO_FETCH_FLAGS;
2974 how |= self->m_stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
2977 Variant *return_all = NULL;
2978 Variant return_value;
2979 Variant data;
2980 if (!error) {
2981 setPDOErrorNone(self->m_stmt->error_code);
2983 if ((how & PDO_FETCH_GROUP) || how == PDO_FETCH_KEY_PAIR ||
2984 (how == PDO_FETCH_USE_DEFAULT &&
2985 self->m_stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)) {
2986 return_value = Array::Create();
2987 return_all = &return_value;
2989 if (!do_fetch(self->m_stmt, true, data, (PDOFetchType)(how | flags),
2990 PDO_FETCH_ORI_NEXT, 0, return_all)) {
2991 error = 2;
2994 if (!error) {
2995 if ((how & PDO_FETCH_GROUP)) {
2996 do {
2997 data.unset();
2998 } while (do_fetch(self->m_stmt, true, data, (PDOFetchType)(how | flags),
2999 PDO_FETCH_ORI_NEXT, 0, return_all));
3000 } else if (how == PDO_FETCH_KEY_PAIR ||
3001 (how == PDO_FETCH_USE_DEFAULT &&
3002 self->m_stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)) {
3003 while (do_fetch(self->m_stmt, true, data, (PDOFetchType)(how | flags),
3004 PDO_FETCH_ORI_NEXT, 0, return_all)) {
3005 continue;
3007 } else {
3008 return_value = Array::Create();
3009 do {
3010 return_value.toArrRef().append(data);
3011 data.unset();
3012 } while (do_fetch(self->m_stmt, true, data, (PDOFetchType)(how | flags),
3013 PDO_FETCH_ORI_NEXT, 0, NULL));
3017 self->m_stmt->fetch.clsname = old_clsname;
3018 self->m_stmt->fetch.ctor_args = old_ctor_args;
3020 if (error) {
3021 PDO_HANDLE_STMT_ERR(self->m_stmt);
3022 if (error != 2) {
3023 return false;
3026 /* on no results, return an empty array */
3027 if (!return_value.isArray()) {
3028 return_value = Array::Create();
3031 return return_value;
3034 static bool HHVM_METHOD(PDOStatement, bindvalue, const Variant& paramno,
3035 const Variant& param,
3036 int64_t type /* = q_PDO$$PARAM_STR */) {
3037 auto data = Native::data<PDOStatementData>(this_);
3038 if (data->m_stmt == nullptr) {
3039 return false;
3042 return register_bound_param(paramno, param, type, 0,
3043 uninit_null(), data->m_stmt, true);
3046 static bool HHVM_METHOD(PDOStatement, bindparam, const Variant& paramno,
3047 VRefParam param, int64_t type /* = q_PDO$$PARAM_STR */,
3048 int64_t max_value_len /* = 0 */,
3049 const Variant& driver_params /*= null */) {
3050 auto data = Native::data<PDOStatementData>(this_);
3051 if (data->m_stmt == nullptr) {
3052 return false;
3055 return register_bound_param(paramno, ref(param), type, max_value_len,
3056 driver_params, data->m_stmt, true);
3059 static bool HHVM_METHOD(PDOStatement, bindcolumn, const Variant& paramno,
3060 VRefParam param, int64_t type /* = q_PDO$$PARAM_STR */,
3061 int64_t max_value_len /* = 0 */,
3062 const Variant& driver_params /* = null */) {
3063 auto data = Native::data<PDOStatementData>(this_);
3064 if (data->m_stmt == nullptr) {
3065 return false;
3068 return register_bound_param(paramno, ref(param), type, max_value_len,
3069 driver_params, data->m_stmt, false);
3072 static int64_t HHVM_METHOD(PDOStatement, rowcount) {
3073 auto data = Native::data<PDOStatementData>(this_);
3074 if (data->m_stmt == nullptr) {
3075 return 0;
3078 return data->m_stmt->row_count;
3081 static Variant HHVM_METHOD(PDOStatement, errorcode) {
3082 auto data = Native::data<PDOStatementData>(this_);
3083 if (data->m_stmt == nullptr) {
3084 return false;
3086 if (data->m_stmt->error_code[0] == '\0') {
3087 return init_null();
3089 return String(data->m_stmt->error_code, CopyString);
3092 static Array HHVM_METHOD(PDOStatement, errorinfo) {
3093 auto data = Native::data<PDOStatementData>(this_);
3094 if (data->m_stmt == nullptr) {
3095 return null_array;
3098 Array ret;
3099 ret.append(String(data->m_stmt->error_code, CopyString));
3101 if (data->m_stmt->dbh->conn()->support(PDOConnection::MethodFetchErr)) {
3102 data->m_stmt->dbh->conn()->fetchErr(data->m_stmt.get(), ret);
3105 int error_count = ret.size();
3106 int error_expected_count = 3;
3107 if (error_expected_count > error_count) {
3108 int error_count_diff = error_expected_count - error_count;
3109 for (int i = 0; i < error_count_diff; i++) {
3110 ret.append(uninit_null());
3113 return ret;
3116 static Variant HHVM_METHOD(PDOStatement, setattribute, int64_t attribute,
3117 const Variant& value) {
3118 auto data = Native::data<PDOStatementData>(this_);
3119 if (data->m_stmt == nullptr) {
3120 return false;
3123 if (!data->m_stmt->support(PDOStatement::MethodSetAttribute)) {
3124 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt, "IM001",
3125 "This driver doesn't support setting attributes");
3126 return false;
3129 setPDOErrorNone(data->m_stmt->error_code);
3130 if (data->m_stmt->setAttribute(attribute, value)) {
3131 return true;
3133 PDO_HANDLE_STMT_ERR(data->m_stmt);
3134 return false;
3137 static Variant HHVM_METHOD(PDOStatement, getattribute, int64_t attribute) {
3138 auto data = Native::data<PDOStatementData>(this_);
3139 if (data->m_stmt == nullptr) {
3140 return false;
3143 Variant ret;
3144 if (!data->m_stmt->support(PDOStatement::MethodGetAttribute)) {
3145 if (!generic_stmt_attr_get(data->m_stmt, ret, attribute)) {
3146 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt, "IM001",
3147 "This driver doesn't support getting attributes");
3148 return false;
3150 return ret;
3153 setPDOErrorNone(data->m_stmt->error_code);
3154 switch (data->m_stmt->getAttribute(attribute, ret)) {
3155 case -1:
3156 PDO_HANDLE_STMT_ERR(data->m_stmt);
3157 return false;
3158 case 0:
3159 if (!generic_stmt_attr_get(data->m_stmt, ret, attribute)) {
3160 /* XXX: should do something better here */
3161 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt, "IM001",
3162 "driver doesn't support getting that attribute");
3163 return false;
3165 break;
3166 default:
3167 break;
3169 return ret;
3172 static int64_t HHVM_METHOD(PDOStatement, columncount) {
3173 auto data = Native::data<PDOStatementData>(this_);
3174 if (data->m_stmt == nullptr) {
3175 return 0;
3178 return data->m_stmt->column_count;
3181 const StaticString
3182 s_name("name"),
3183 s_len("len"),
3184 s_precision("precision"),
3185 s_pdo_type("pdo_type");
3187 static Variant HHVM_METHOD(PDOStatement, getcolumnmeta, int64_t column) {
3188 auto data = Native::data<PDOStatementData>(this_);
3189 if (data->m_stmt == nullptr) {
3190 return false;
3193 if (column < 0) {
3194 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt, "42P10",
3195 "column number must be non-negative");
3196 return false;
3199 if (!data->m_stmt->support(PDOStatement::MethodGetColumnMeta)) {
3200 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt, "IM001",
3201 "driver doesn't support meta data");
3202 return false;
3205 setPDOErrorNone(data->m_stmt->error_code);
3206 Array ret;
3207 if (!data->m_stmt->getColumnMeta(column, ret)) {
3208 PDO_HANDLE_STMT_ERR(data->m_stmt);
3209 return false;
3212 /* add stock items */
3213 auto col = cast<PDOColumn>(data->m_stmt->columns[column]);
3214 ret.set(s_name, col->name);
3215 ret.set(s_len, (int64_t)col->maxlen); /* FIXME: unsigned ? */
3216 ret.set(s_precision, (int64_t)col->precision);
3217 if (col->param_type != PDO_PARAM_ZVAL) {
3218 // if param_type is PDO_PARAM_ZVAL the driver has to provide correct data
3219 ret.set(s_pdo_type, (int64_t)col->param_type);
3221 return ret;
3224 static bool HHVM_METHOD(PDOStatement, setfetchmode,
3225 int64_t mode, const Array& _argv /* = null_array */) {
3226 auto data = Native::data<PDOStatementData>(this_);
3227 if (data->m_stmt == nullptr) {
3228 return false;
3230 int argc = _argv.size() + 1;
3232 return pdo_stmt_set_fetch_mode(data->m_stmt, argc, mode, _argv);
3235 static bool HHVM_METHOD(PDOStatement, nextrowset) {
3236 auto data = Native::data<PDOStatementData>(this_);
3237 if (data->m_stmt == nullptr) {
3238 return false;
3241 if (!data->m_stmt->support(PDOStatement::MethodNextRowset)) {
3242 pdo_raise_impl_error(data->m_stmt->dbh, data->m_stmt, "IM001",
3243 "driver does not support multiple rowsets");
3244 return false;
3247 setPDOErrorNone(data->m_stmt->error_code);
3249 /* un-describe */
3250 if (!data->m_stmt->columns.empty()) {
3251 data->m_stmt->columns.clear();
3252 data->m_stmt->column_count = 0;
3255 if (!data->m_stmt->nextRowset()) {
3256 PDO_HANDLE_STMT_ERR(data->m_stmt);
3257 return false;
3260 pdo_stmt_describe_columns(data->m_stmt);
3261 return true;
3264 static bool HHVM_METHOD(PDOStatement, closecursor) {
3265 auto data = Native::data<PDOStatementData>(this_);
3266 if (data->m_stmt == nullptr) {
3267 return false;
3270 if (!data->m_stmt->support(PDOStatement::MethodCursorCloser)) {
3271 /* emulate it by fetching and discarding rows */
3272 do {
3273 while (data->m_stmt->fetcher(PDO_FETCH_ORI_NEXT, 0));
3274 // if (!data->t_nextrowset()) {
3275 if (HHVM_MN(PDOStatement, nextrowset)(this_)) {
3276 break;
3278 } while (true);
3279 data->m_stmt->executed = 0;
3280 return true;
3283 setPDOErrorNone(data->m_stmt->error_code);
3284 if (!data->m_stmt->cursorCloser()) {
3285 PDO_HANDLE_STMT_ERR(data->m_stmt);
3286 return false;
3288 data->m_stmt->executed = 0;
3289 return true;
3292 static Variant HHVM_METHOD(PDOStatement, debugdumpparams) {
3293 auto data = Native::data<PDOStatementData>(this_);
3294 if (data->m_stmt == nullptr) {
3295 return false;
3298 auto f = File::Open("php://output", "w");
3299 if (!f || f->isInvalid()) {
3300 return false;
3303 Array params;
3304 params.append(data->m_stmt->query_string.size());
3305 params.append(data->m_stmt->query_string.size());
3306 params.append(data->m_stmt->query_string.data());
3307 f->printf("SQL: [%d] %.*s\n", params);
3309 f->printf("Params: %d\n",
3310 make_packed_array(data->m_stmt->bound_params.size()));
3311 for (ArrayIter iter(data->m_stmt->bound_params); iter; ++iter) {
3312 if (iter.first().isString()) {
3313 String key = iter.first().toString();
3314 params = make_packed_array(key.size(), key.size(), key.data());
3315 f->printf("Key: Name: [%d] %.*s\n", params);
3316 } else {
3317 f->printf("Key: Position #%ld:\n",
3318 make_packed_array(iter.first().toInt64()));
3321 auto param = cast<PDOBoundParam>(iter.second());
3322 params.clear();
3323 params.append(param->paramno);
3324 params.append(param->name.size());
3325 params.append(param->name.size());
3326 params.append(param->name.data());
3327 params.append(param->is_param);
3328 params.append(param->param_type);
3329 f->printf("paramno=%d\nname=[%d] \"%.*s\"\nis_param=%d\nparam_type=%d\n",
3330 params);
3332 return true;
3335 static Variant HHVM_METHOD(PDOStatement, current) {
3336 auto data = Native::data<PDOStatementData>(this_);
3338 return data->m_row;
3341 static Variant HHVM_METHOD(PDOStatement, key) {
3342 auto data = Native::data<PDOStatementData>(this_);
3344 return data->m_rowIndex;
3347 static Variant HHVM_METHOD(PDOStatement, next) {
3348 auto data = Native::data<PDOStatementData>(this_);
3350 data->m_row = HHVM_MN(PDOStatement, fetch)(this_, PDO_FETCH_USE_DEFAULT);
3351 if (same(data->m_row, false)) {
3352 data->m_rowIndex = -1;
3353 } else {
3354 ++data->m_rowIndex;
3356 return init_null();
3359 static Variant HHVM_METHOD(PDOStatement, rewind) {
3360 auto data = Native::data<PDOStatementData>(this_);
3362 data->m_rowIndex = -1;
3363 HHVM_MN(PDOStatement, next)(this_);
3364 return init_null();
3367 static Variant HHVM_METHOD(PDOStatement, valid) {
3368 auto data = Native::data<PDOStatementData>(this_);
3370 return data->m_rowIndex >= 0;
3373 static Variant HHVM_METHOD(PDOStatement, __wakeup) {
3374 throw_pdo_exception(uninit_null(), uninit_null(),
3375 "You cannot serialize or unserialize "
3376 "PDOStatement instances");
3377 return init_null();
3380 static Variant HHVM_METHOD(PDOStatement, __sleep) {
3381 throw_pdo_exception(uninit_null(), uninit_null(),
3382 "You cannot serialize or unserialize "
3383 "PDOStatement instances");
3384 return init_null();
3387 ///////////////////////////////////////////////////////////////////////////////
3390 static struct PDOExtension final : Extension {
3391 PDOExtension() : Extension("pdo", " 1.0.4dev") {}
3393 #ifdef ENABLE_EXTENSION_PDO_MYSQL
3394 std::string mysql_default_socket;
3396 void moduleLoad(const IniSetting::Map& ini, Hdf config) override {
3397 IniSetting::Bind(this, IniSetting::PHP_INI_SYSTEM,
3398 "pdo_mysql.default_socket", nullptr,
3399 &mysql_default_socket);
3401 #endif
3403 void moduleInit() override {
3404 HHVM_FE(pdo_drivers);
3405 HHVM_ME(PDO, __construct);
3406 HHVM_ME(PDO, prepare);
3407 HHVM_ME(PDO, begintransaction);
3408 HHVM_ME(PDO, commit);
3409 HHVM_ME(PDO, intransaction);
3410 HHVM_ME(PDO, rollback);
3411 HHVM_ME(PDO, setattribute);
3412 HHVM_ME(PDO, getattribute);
3413 HHVM_ME(PDO, exec);
3414 HHVM_ME(PDO, lastinsertid);
3415 HHVM_ME(PDO, errorcode);
3416 HHVM_ME(PDO, errorinfo);
3417 HHVM_ME(PDO, query);
3418 HHVM_ME(PDO, quote);
3419 HHVM_ME(PDO, sqlitecreatefunction);
3420 HHVM_ME(PDO, sqlitecreateaggregate);
3421 HHVM_ME(PDO, __wakeup);
3422 HHVM_ME(PDO, __sleep);
3423 HHVM_STATIC_ME(PDO, getAvailableDrivers);
3424 HHVM_ME(PDOStatement, execute);
3425 HHVM_ME(PDOStatement, fetch);
3426 HHVM_ME(PDOStatement, fetchobject);
3427 HHVM_ME(PDOStatement, fetchcolumn);
3428 HHVM_ME(PDOStatement, fetchall);
3429 HHVM_ME(PDOStatement, bindvalue);
3430 HHVM_ME(PDOStatement, bindparam);
3431 HHVM_ME(PDOStatement, bindcolumn);
3432 HHVM_ME(PDOStatement, rowcount);
3433 HHVM_ME(PDOStatement, errorcode);
3434 HHVM_ME(PDOStatement, errorinfo);
3435 HHVM_ME(PDOStatement, setattribute);
3436 HHVM_ME(PDOStatement, getattribute);
3437 HHVM_ME(PDOStatement, columncount);
3438 HHVM_ME(PDOStatement, getcolumnmeta);
3439 HHVM_ME(PDOStatement, setfetchmode);
3440 HHVM_ME(PDOStatement, nextrowset);
3441 HHVM_ME(PDOStatement, closecursor);
3442 HHVM_ME(PDOStatement, debugdumpparams);
3443 HHVM_ME(PDOStatement, current);
3444 HHVM_ME(PDOStatement, key);
3445 HHVM_ME(PDOStatement, next);
3446 HHVM_ME(PDOStatement, rewind);
3447 HHVM_ME(PDOStatement, valid);
3448 HHVM_ME(PDOStatement, __wakeup);
3449 HHVM_ME(PDOStatement, __sleep);
3451 Native::registerClassConstant<KindOfInt64>(
3452 s_PDO.get(),
3453 s_PARAM_BOOL.get(),
3454 q_PDO$$PARAM_BOOL
3456 Native::registerClassConstant<KindOfInt64>(
3457 s_PDO.get(),
3458 s_PARAM_NULL.get(),
3459 q_PDO$$PARAM_NULL
3461 Native::registerClassConstant<KindOfInt64>(
3462 s_PDO.get(),
3463 s_PARAM_INT.get(),
3464 q_PDO$$PARAM_INT
3466 Native::registerClassConstant<KindOfInt64>(
3467 s_PDO.get(),
3468 s_PARAM_STR.get(),
3469 q_PDO$$PARAM_STR
3471 Native::registerClassConstant<KindOfInt64>(
3472 s_PDO.get(),
3473 s_PARAM_LOB.get(),
3474 q_PDO$$PARAM_LOB
3476 Native::registerClassConstant<KindOfInt64>(
3477 s_PDO.get(),
3478 s_PARAM_STMT.get(),
3479 q_PDO$$PARAM_STMT
3481 Native::registerClassConstant<KindOfInt64>(
3482 s_PDO.get(),
3483 s_PARAM_INPUT_OUTPUT.get(),
3484 q_PDO$$PARAM_INPUT_OUTPUT
3486 Native::registerClassConstant<KindOfInt64>(
3487 s_PDO.get(),
3488 s_PARAM_EVT_ALLOC.get(),
3489 q_PDO$$PARAM_EVT_ALLOC
3491 Native::registerClassConstant<KindOfInt64>(
3492 s_PDO.get(),
3493 s_PARAM_EVT_FREE.get(),
3494 q_PDO$$PARAM_EVT_FREE
3496 Native::registerClassConstant<KindOfInt64>(
3497 s_PDO.get(),
3498 s_PARAM_EVT_EXEC_PRE.get(),
3499 q_PDO$$PARAM_EVT_EXEC_PRE
3501 Native::registerClassConstant<KindOfInt64>(
3502 s_PDO.get(),
3503 s_PARAM_EVT_EXEC_POST.get(),
3504 q_PDO$$PARAM_EVT_EXEC_POST
3506 Native::registerClassConstant<KindOfInt64>(
3507 s_PDO.get(),
3508 s_PARAM_EVT_FETCH_PRE.get(),
3509 q_PDO$$PARAM_EVT_FETCH_PRE
3511 Native::registerClassConstant<KindOfInt64>(
3512 s_PDO.get(),
3513 s_PARAM_EVT_FETCH_POST.get(),
3514 q_PDO$$PARAM_EVT_FETCH_POST
3516 Native::registerClassConstant<KindOfInt64>(
3517 s_PDO.get(),
3518 s_PARAM_EVT_NORMALIZE.get(),
3519 q_PDO$$PARAM_EVT_NORMALIZE
3521 Native::registerClassConstant<KindOfInt64>(
3522 s_PDO.get(),
3523 s_FETCH_USE_DEFAULT.get(),
3524 q_PDO$$FETCH_USE_DEFAULT
3526 Native::registerClassConstant<KindOfInt64>(
3527 s_PDO.get(),
3528 s_FETCH_LAZY.get(),
3529 q_PDO$$FETCH_LAZY
3531 Native::registerClassConstant<KindOfInt64>(
3532 s_PDO.get(),
3533 s_FETCH_ASSOC.get(),
3534 q_PDO$$FETCH_ASSOC
3536 Native::registerClassConstant<KindOfInt64>(
3537 s_PDO.get(),
3538 s_FETCH_NUM.get(),
3539 q_PDO$$FETCH_NUM
3541 Native::registerClassConstant<KindOfInt64>(
3542 s_PDO.get(),
3543 s_FETCH_BOTH.get(),
3544 q_PDO$$FETCH_BOTH
3546 Native::registerClassConstant<KindOfInt64>(
3547 s_PDO.get(),
3548 s_FETCH_OBJ.get(),
3549 q_PDO$$FETCH_OBJ
3551 Native::registerClassConstant<KindOfInt64>(
3552 s_PDO.get(),
3553 s_FETCH_BOUND.get(),
3554 q_PDO$$FETCH_BOUND
3556 Native::registerClassConstant<KindOfInt64>(
3557 s_PDO.get(),
3558 s_FETCH_COLUMN.get(),
3559 q_PDO$$FETCH_COLUMN
3561 Native::registerClassConstant<KindOfInt64>(
3562 s_PDO.get(),
3563 s_FETCH_CLASS.get(),
3564 q_PDO$$FETCH_CLASS
3566 Native::registerClassConstant<KindOfInt64>(
3567 s_PDO.get(),
3568 s_FETCH_INTO.get(),
3569 q_PDO$$FETCH_INTO
3571 Native::registerClassConstant<KindOfInt64>(
3572 s_PDO.get(),
3573 s_FETCH_FUNC.get(),
3574 q_PDO$$FETCH_FUNC
3576 Native::registerClassConstant<KindOfInt64>(
3577 s_PDO.get(),
3578 s_FETCH_GROUP.get(),
3579 q_PDO$$FETCH_GROUP
3581 Native::registerClassConstant<KindOfInt64>(
3582 s_PDO.get(),
3583 s_FETCH_UNIQUE.get(),
3584 q_PDO$$FETCH_UNIQUE
3586 Native::registerClassConstant<KindOfInt64>(
3587 s_PDO.get(),
3588 s_FETCH_KEY_PAIR.get(),
3589 q_PDO$$FETCH_KEY_PAIR
3591 Native::registerClassConstant<KindOfInt64>(
3592 s_PDO.get(),
3593 s_FETCH_CLASSTYPE.get(),
3594 q_PDO$$FETCH_CLASSTYPE
3596 Native::registerClassConstant<KindOfInt64>(
3597 s_PDO.get(),
3598 s_FETCH_SERIALIZE.get(),
3599 q_PDO$$FETCH_SERIALIZE
3601 Native::registerClassConstant<KindOfInt64>(
3602 s_PDO.get(),
3603 s_FETCH_PROPS_LATE.get(),
3604 q_PDO$$FETCH_PROPS_LATE
3606 Native::registerClassConstant<KindOfInt64>(
3607 s_PDO.get(),
3608 s_FETCH_NAMED.get(),
3609 q_PDO$$FETCH_NAMED
3611 Native::registerClassConstant<KindOfInt64>(
3612 s_PDO.get(),
3613 s_ATTR_AUTOCOMMIT.get(),
3614 q_PDO$$ATTR_AUTOCOMMIT
3616 Native::registerClassConstant<KindOfInt64>(
3617 s_PDO.get(),
3618 s_ATTR_PREFETCH.get(),
3619 q_PDO$$ATTR_PREFETCH
3621 Native::registerClassConstant<KindOfInt64>(
3622 s_PDO.get(),
3623 s_ATTR_TIMEOUT.get(),
3624 q_PDO$$ATTR_TIMEOUT
3626 Native::registerClassConstant<KindOfInt64>(
3627 s_PDO.get(),
3628 s_ATTR_ERRMODE.get(),
3629 q_PDO$$ATTR_ERRMODE
3631 Native::registerClassConstant<KindOfInt64>(
3632 s_PDO.get(),
3633 s_ATTR_SERVER_VERSION.get(),
3634 q_PDO$$ATTR_SERVER_VERSION
3636 Native::registerClassConstant<KindOfInt64>(
3637 s_PDO.get(),
3638 s_ATTR_CLIENT_VERSION.get(),
3639 q_PDO$$ATTR_CLIENT_VERSION
3641 Native::registerClassConstant<KindOfInt64>(
3642 s_PDO.get(),
3643 s_ATTR_SERVER_INFO.get(),
3644 q_PDO$$ATTR_SERVER_INFO
3646 Native::registerClassConstant<KindOfInt64>(
3647 s_PDO.get(),
3648 s_ATTR_CONNECTION_STATUS.get(),
3649 q_PDO$$ATTR_CONNECTION_STATUS
3651 Native::registerClassConstant<KindOfInt64>(
3652 s_PDO.get(),
3653 s_ATTR_CASE.get(),
3654 q_PDO$$ATTR_CASE
3656 Native::registerClassConstant<KindOfInt64>(
3657 s_PDO.get(),
3658 s_ATTR_CURSOR_NAME.get(),
3659 q_PDO$$ATTR_CURSOR_NAME
3661 Native::registerClassConstant<KindOfInt64>(
3662 s_PDO.get(),
3663 s_ATTR_CURSOR.get(),
3664 q_PDO$$ATTR_CURSOR
3666 Native::registerClassConstant<KindOfInt64>(
3667 s_PDO.get(),
3668 s_ATTR_ORACLE_NULLS.get(),
3669 q_PDO$$ATTR_ORACLE_NULLS
3671 Native::registerClassConstant<KindOfInt64>(
3672 s_PDO.get(),
3673 s_ATTR_PERSISTENT.get(),
3674 q_PDO$$ATTR_PERSISTENT
3676 Native::registerClassConstant<KindOfInt64>(
3677 s_PDO.get(),
3678 s_ATTR_STATEMENT_CLASS.get(),
3679 q_PDO$$ATTR_STATEMENT_CLASS
3681 Native::registerClassConstant<KindOfInt64>(
3682 s_PDO.get(),
3683 s_ATTR_FETCH_TABLE_NAMES.get(),
3684 q_PDO$$ATTR_FETCH_TABLE_NAMES
3686 Native::registerClassConstant<KindOfInt64>(
3687 s_PDO.get(),
3688 s_ATTR_FETCH_CATALOG_NAMES.get(),
3689 q_PDO$$ATTR_FETCH_CATALOG_NAMES
3691 Native::registerClassConstant<KindOfInt64>(
3692 s_PDO.get(),
3693 s_ATTR_DRIVER_NAME.get(),
3694 q_PDO$$ATTR_DRIVER_NAME
3696 Native::registerClassConstant<KindOfInt64>(
3697 s_PDO.get(),
3698 s_ATTR_STRINGIFY_FETCHES.get(),
3699 q_PDO$$ATTR_STRINGIFY_FETCHES
3701 Native::registerClassConstant<KindOfInt64>(
3702 s_PDO.get(),
3703 s_ATTR_MAX_COLUMN_LEN.get(),
3704 q_PDO$$ATTR_MAX_COLUMN_LEN
3706 Native::registerClassConstant<KindOfInt64>(
3707 s_PDO.get(),
3708 s_ATTR_EMULATE_PREPARES.get(),
3709 q_PDO$$ATTR_EMULATE_PREPARES
3711 Native::registerClassConstant<KindOfInt64>(
3712 s_PDO.get(),
3713 s_ATTR_DEFAULT_FETCH_MODE.get(),
3714 q_PDO$$ATTR_DEFAULT_FETCH_MODE
3716 Native::registerClassConstant<KindOfInt64>(
3717 s_PDO.get(),
3718 s_ERRMODE_SILENT.get(),
3719 q_PDO$$ERRMODE_SILENT
3721 Native::registerClassConstant<KindOfInt64>(
3722 s_PDO.get(),
3723 s_ERRMODE_WARNING.get(),
3724 q_PDO$$ERRMODE_WARNING
3726 Native::registerClassConstant<KindOfInt64>(
3727 s_PDO.get(),
3728 s_ERRMODE_EXCEPTION.get(),
3729 q_PDO$$ERRMODE_EXCEPTION
3731 Native::registerClassConstant<KindOfInt64>(
3732 s_PDO.get(),
3733 s_CASE_NATURAL.get(),
3734 q_PDO$$CASE_NATURAL
3736 Native::registerClassConstant<KindOfInt64>(
3737 s_PDO.get(),
3738 s_CASE_LOWER.get(),
3739 q_PDO$$CASE_LOWER
3741 Native::registerClassConstant<KindOfInt64>(
3742 s_PDO.get(),
3743 s_CASE_UPPER.get(),
3744 q_PDO$$CASE_UPPER
3746 Native::registerClassConstant<KindOfInt64>(
3747 s_PDO.get(),
3748 s_NULL_NATURAL.get(),
3749 q_PDO$$NULL_NATURAL
3751 Native::registerClassConstant<KindOfInt64>(
3752 s_PDO.get(),
3753 s_NULL_EMPTY_STRING.get(),
3754 q_PDO$$NULL_EMPTY_STRING
3756 Native::registerClassConstant<KindOfInt64>(
3757 s_PDO.get(),
3758 s_NULL_TO_STRING.get(),
3759 q_PDO$$NULL_TO_STRING
3761 Native::registerClassConstant<KindOfInt64>(
3762 s_PDO.get(),
3763 s_FETCH_ORI_NEXT.get(),
3764 q_PDO$$FETCH_ORI_NEXT
3766 Native::registerClassConstant<KindOfInt64>(
3767 s_PDO.get(),
3768 s_FETCH_ORI_PRIOR.get(),
3769 q_PDO$$FETCH_ORI_PRIOR
3771 Native::registerClassConstant<KindOfInt64>(
3772 s_PDO.get(),
3773 s_FETCH_ORI_FIRST.get(),
3774 q_PDO$$FETCH_ORI_FIRST
3776 Native::registerClassConstant<KindOfInt64>(
3777 s_PDO.get(),
3778 s_FETCH_ORI_LAST.get(),
3779 q_PDO$$FETCH_ORI_LAST
3781 Native::registerClassConstant<KindOfInt64>(
3782 s_PDO.get(),
3783 s_FETCH_ORI_ABS.get(),
3784 q_PDO$$FETCH_ORI_ABS
3786 Native::registerClassConstant<KindOfInt64>(
3787 s_PDO.get(),
3788 s_FETCH_ORI_REL.get(),
3789 q_PDO$$FETCH_ORI_REL
3791 Native::registerClassConstant<KindOfInt64>(
3792 s_PDO.get(),
3793 s_CURSOR_FWDONLY.get(),
3794 q_PDO$$CURSOR_FWDONLY
3796 Native::registerClassConstant<KindOfInt64>(
3797 s_PDO.get(),
3798 s_CURSOR_SCROLL.get(),
3799 q_PDO$$CURSOR_SCROLL
3801 #ifdef ENABLE_EXTENSION_PDO_MYSQL
3802 Native::registerClassConstant<KindOfInt64>(
3803 s_PDO.get(),
3804 s_MYSQL_ATTR_USE_BUFFERED_QUERY.get(),
3805 q_PDO$$MYSQL_ATTR_USE_BUFFERED_QUERY
3807 Native::registerClassConstant<KindOfInt64>(
3808 s_PDO.get(),
3809 s_MYSQL_ATTR_LOCAL_INFILE.get(),
3810 q_PDO$$MYSQL_ATTR_LOCAL_INFILE
3812 Native::registerClassConstant<KindOfInt64>(
3813 s_PDO.get(),
3814 s_MYSQL_ATTR_MAX_BUFFER_SIZE.get(),
3815 q_PDO$$MYSQL_ATTR_MAX_BUFFER_SIZE
3817 Native::registerClassConstant<KindOfInt64>(
3818 s_PDO.get(),
3819 s_MYSQL_ATTR_INIT_COMMAND.get(),
3820 q_PDO$$MYSQL_ATTR_INIT_COMMAND
3822 Native::registerClassConstant<KindOfInt64>(
3823 s_PDO.get(),
3824 s_MYSQL_ATTR_READ_DEFAULT_FILE.get(),
3825 q_PDO$$MYSQL_ATTR_READ_DEFAULT_FILE
3827 Native::registerClassConstant<KindOfInt64>(
3828 s_PDO.get(),
3829 s_MYSQL_ATTR_READ_DEFAULT_GROUP.get(),
3830 q_PDO$$MYSQL_ATTR_READ_DEFAULT_GROUP
3832 Native::registerClassConstant<KindOfInt64>(
3833 s_PDO.get(),
3834 s_MYSQL_ATTR_COMPRESS.get(),
3835 q_PDO$$MYSQL_ATTR_COMPRESS
3837 Native::registerClassConstant<KindOfInt64>(
3838 s_PDO.get(),
3839 s_MYSQL_ATTR_DIRECT_QUERY.get(),
3840 q_PDO$$MYSQL_ATTR_DIRECT_QUERY
3842 Native::registerClassConstant<KindOfInt64>(
3843 s_PDO.get(),
3844 s_MYSQL_ATTR_FOUND_ROWS.get(),
3845 q_PDO$$MYSQL_ATTR_FOUND_ROWS
3847 Native::registerClassConstant<KindOfInt64>(
3848 s_PDO.get(),
3849 s_MYSQL_ATTR_IGNORE_SPACE.get(),
3850 q_PDO$$MYSQL_ATTR_IGNORE_SPACE
3852 #endif
3853 Native::registerClassConstant<KindOfPersistentString>(
3854 s_PDO.get(),
3855 s_ERR_NONE.get(),
3856 q_PDO$$ERR_NONE.get()
3859 Native::registerNativeDataInfo<PDOData>(
3860 s_PDO.get(), Native::NDIFlags::NO_SWEEP);
3861 Native::registerNativeDataInfo<PDOStatementData>(
3862 s_PDOStatement.get(), Native::NDIFlags::NO_SWEEP);
3864 loadSystemlib("pdo");
3866 } s_pdo_extension;
3868 //////////////////////////////////////////////////////////////////////////////