PageRenderTime 62ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/runtime/ext/ext_pdo.cpp

http://github.com/facebook/hiphop-php
C++ | 3181 lines | 2844 code | 224 blank | 113 comment | 368 complexity | 90d855a73dd4ac677e3f5f928184c0dc MD5 | raw file
Possible License(s): LGPL-2.1, BSD-2-Clause, BSD-3-Clause, MPL-2.0-no-copyleft-exception, MIT, LGPL-2.0, Apache-2.0
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2013 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. +----------------------------------------------------------------------+
  16. */
  17. #include "hphp/runtime/ext/ext_pdo.h"
  18. #include "hphp/runtime/ext/pdo_driver.h"
  19. #include "hphp/runtime/ext/pdo_mysql.h"
  20. #include "hphp/runtime/ext/ext_class.h"
  21. #include "hphp/runtime/ext/ext_function.h"
  22. #include "hphp/runtime/ext/ext_stream.h"
  23. #include "hphp/runtime/ext/ext_string.h"
  24. #include "hphp/runtime/base/class-info.h"
  25. #include "hphp/runtime/base/ini-setting.h"
  26. #include "hphp/runtime/base/string-buffer.h"
  27. #include "hphp/runtime/base/request-local.h"
  28. #include "hphp/runtime/base/macros.h"
  29. #include "hphp/system/systemlib.h"
  30. #define PDO_HANDLE_DBH_ERR(dbh) \
  31. if (strcmp(dbh->error_code, PDO_ERR_NONE)) { \
  32. pdo_handle_error(dbh, nullptr); \
  33. } \
  34. #define PDO_HANDLE_STMT_ERR(stmt) \
  35. if (strcmp(stmt->error_code, PDO_ERR_NONE)) { \
  36. pdo_handle_error(stmt->dbh, stmt); \
  37. } \
  38. namespace HPHP {
  39. IMPLEMENT_DEFAULT_EXTENSION(PDO);
  40. ///////////////////////////////////////////////////////////////////////////////
  41. // PDO constants
  42. const int64_t q_PDO$$PARAM_BOOL = PDO_PARAM_BOOL;
  43. const int64_t q_PDO$$PARAM_NULL = PDO_PARAM_NULL;
  44. const int64_t q_PDO$$PARAM_INT = PDO_PARAM_INT;
  45. const int64_t q_PDO$$PARAM_STR = PDO_PARAM_STR;
  46. const int64_t q_PDO$$PARAM_LOB = PDO_PARAM_LOB;
  47. const int64_t q_PDO$$PARAM_STMT = PDO_PARAM_STMT;
  48. const int64_t q_PDO$$PARAM_INPUT_OUTPUT = PDO_PARAM_INPUT_OUTPUT;
  49. const int64_t q_PDO$$PARAM_EVT_ALLOC = PDO_PARAM_EVT_ALLOC;
  50. const int64_t q_PDO$$PARAM_EVT_FREE = PDO_PARAM_EVT_FREE;
  51. const int64_t q_PDO$$PARAM_EVT_EXEC_PRE = PDO_PARAM_EVT_EXEC_PRE;
  52. const int64_t q_PDO$$PARAM_EVT_EXEC_POST = PDO_PARAM_EVT_EXEC_POST;
  53. const int64_t q_PDO$$PARAM_EVT_FETCH_PRE = PDO_PARAM_EVT_FETCH_PRE;
  54. const int64_t q_PDO$$PARAM_EVT_FETCH_POST = PDO_PARAM_EVT_FETCH_POST;
  55. const int64_t q_PDO$$PARAM_EVT_NORMALIZE = PDO_PARAM_EVT_NORMALIZE;
  56. const int64_t q_PDO$$FETCH_USE_DEFAULT = PDO_FETCH_USE_DEFAULT;
  57. const int64_t q_PDO$$FETCH_LAZY = PDO_FETCH_LAZY;
  58. const int64_t q_PDO$$FETCH_ASSOC = PDO_FETCH_ASSOC;
  59. const int64_t q_PDO$$FETCH_NUM = PDO_FETCH_NUM;
  60. const int64_t q_PDO$$FETCH_BOTH = PDO_FETCH_BOTH;
  61. const int64_t q_PDO$$FETCH_OBJ = PDO_FETCH_OBJ;
  62. const int64_t q_PDO$$FETCH_BOUND = PDO_FETCH_BOUND;
  63. const int64_t q_PDO$$FETCH_COLUMN = PDO_FETCH_COLUMN;
  64. const int64_t q_PDO$$FETCH_CLASS = PDO_FETCH_CLASS;
  65. const int64_t q_PDO$$FETCH_INTO = PDO_FETCH_INTO;
  66. const int64_t q_PDO$$FETCH_FUNC = PDO_FETCH_FUNC;
  67. const int64_t q_PDO$$FETCH_GROUP = PDO_FETCH_GROUP;
  68. const int64_t q_PDO$$FETCH_UNIQUE = PDO_FETCH_UNIQUE;
  69. const int64_t q_PDO$$FETCH_KEY_PAIR = PDO_FETCH_KEY_PAIR;
  70. const int64_t q_PDO$$FETCH_CLASSTYPE = PDO_FETCH_CLASSTYPE;
  71. const int64_t q_PDO$$FETCH_SERIALIZE = PDO_FETCH_SERIALIZE;
  72. const int64_t q_PDO$$FETCH_PROPS_LATE = PDO_FETCH_PROPS_LATE;
  73. const int64_t q_PDO$$FETCH_NAMED = PDO_FETCH_NAMED;
  74. const int64_t q_PDO$$ATTR_AUTOCOMMIT = PDO_ATTR_AUTOCOMMIT;
  75. const int64_t q_PDO$$ATTR_PREFETCH = PDO_ATTR_PREFETCH;
  76. const int64_t q_PDO$$ATTR_TIMEOUT = PDO_ATTR_TIMEOUT;
  77. const int64_t q_PDO$$ATTR_ERRMODE = PDO_ATTR_ERRMODE;
  78. const int64_t q_PDO$$ATTR_SERVER_VERSION = PDO_ATTR_SERVER_VERSION;
  79. const int64_t q_PDO$$ATTR_CLIENT_VERSION = PDO_ATTR_CLIENT_VERSION;
  80. const int64_t q_PDO$$ATTR_SERVER_INFO = PDO_ATTR_SERVER_INFO;
  81. const int64_t q_PDO$$ATTR_CONNECTION_STATUS = PDO_ATTR_CONNECTION_STATUS;
  82. const int64_t q_PDO$$ATTR_CASE = PDO_ATTR_CASE;
  83. const int64_t q_PDO$$ATTR_CURSOR_NAME = PDO_ATTR_CURSOR_NAME;
  84. const int64_t q_PDO$$ATTR_CURSOR = PDO_ATTR_CURSOR;
  85. const int64_t q_PDO$$ATTR_ORACLE_NULLS = PDO_ATTR_ORACLE_NULLS;
  86. const int64_t q_PDO$$ATTR_PERSISTENT = PDO_ATTR_PERSISTENT;
  87. const int64_t q_PDO$$ATTR_STATEMENT_CLASS = PDO_ATTR_STATEMENT_CLASS;
  88. const int64_t q_PDO$$ATTR_FETCH_TABLE_NAMES = PDO_ATTR_FETCH_TABLE_NAMES;
  89. const int64_t q_PDO$$ATTR_FETCH_CATALOG_NAMES = PDO_ATTR_FETCH_CATALOG_NAMES;
  90. const int64_t q_PDO$$ATTR_DRIVER_NAME = PDO_ATTR_DRIVER_NAME;
  91. const int64_t q_PDO$$ATTR_STRINGIFY_FETCHES = PDO_ATTR_STRINGIFY_FETCHES;
  92. const int64_t q_PDO$$ATTR_MAX_COLUMN_LEN = PDO_ATTR_MAX_COLUMN_LEN;
  93. const int64_t q_PDO$$ATTR_EMULATE_PREPARES = PDO_ATTR_EMULATE_PREPARES;
  94. const int64_t q_PDO$$ATTR_DEFAULT_FETCH_MODE = PDO_ATTR_DEFAULT_FETCH_MODE;
  95. const int64_t q_PDO$$ERRMODE_SILENT = PDO_ERRMODE_SILENT;
  96. const int64_t q_PDO$$ERRMODE_WARNING = PDO_ERRMODE_WARNING;
  97. const int64_t q_PDO$$ERRMODE_EXCEPTION = PDO_ERRMODE_EXCEPTION;
  98. const int64_t q_PDO$$CASE_NATURAL = PDO_CASE_NATURAL;
  99. const int64_t q_PDO$$CASE_LOWER = PDO_CASE_LOWER;
  100. const int64_t q_PDO$$CASE_UPPER = PDO_CASE_UPPER;
  101. const int64_t q_PDO$$NULL_NATURAL = PDO_NULL_NATURAL;
  102. const int64_t q_PDO$$NULL_EMPTY_STRING = PDO_NULL_EMPTY_STRING;
  103. const int64_t q_PDO$$NULL_TO_STRING = PDO_NULL_TO_STRING;
  104. const StaticString q_PDO$$ERR_NONE(LITSTR_INIT(PDO_ERR_NONE));
  105. const int64_t q_PDO$$FETCH_ORI_NEXT = PDO_FETCH_ORI_NEXT;
  106. const int64_t q_PDO$$FETCH_ORI_PRIOR = PDO_FETCH_ORI_PRIOR;
  107. const int64_t q_PDO$$FETCH_ORI_FIRST = PDO_FETCH_ORI_FIRST;
  108. const int64_t q_PDO$$FETCH_ORI_LAST = PDO_FETCH_ORI_LAST;
  109. const int64_t q_PDO$$FETCH_ORI_ABS = PDO_FETCH_ORI_ABS;
  110. const int64_t q_PDO$$FETCH_ORI_REL = PDO_FETCH_ORI_REL;
  111. const int64_t q_PDO$$CURSOR_FWDONLY = PDO_CURSOR_FWDONLY;
  112. const int64_t q_PDO$$CURSOR_SCROLL = PDO_CURSOR_SCROLL;
  113. ///////////////////////////////////////////////////////////////////////////////
  114. const int64_t q_PDO$$MYSQL_ATTR_USE_BUFFERED_QUERY =
  115. PDO_MYSQL_ATTR_USE_BUFFERED_QUERY;
  116. const int64_t q_PDO$$MYSQL_ATTR_LOCAL_INFILE = PDO_MYSQL_ATTR_LOCAL_INFILE;
  117. const int64_t q_PDO$$MYSQL_ATTR_MAX_BUFFER_SIZE =
  118. PDO_MYSQL_ATTR_MAX_BUFFER_SIZE;
  119. const int64_t q_PDO$$MYSQL_ATTR_INIT_COMMAND = PDO_MYSQL_ATTR_INIT_COMMAND;
  120. const int64_t q_PDO$$MYSQL_ATTR_READ_DEFAULT_FILE =
  121. PDO_MYSQL_ATTR_READ_DEFAULT_FILE;
  122. const int64_t q_PDO$$MYSQL_ATTR_READ_DEFAULT_GROUP =
  123. PDO_MYSQL_ATTR_READ_DEFAULT_GROUP;
  124. const int64_t q_PDO$$MYSQL_ATTR_COMPRESS = PDO_MYSQL_ATTR_COMPRESS;
  125. const int64_t q_PDO$$MYSQL_ATTR_DIRECT_QUERY = PDO_MYSQL_ATTR_DIRECT_QUERY;
  126. const int64_t q_PDO$$MYSQL_ATTR_FOUND_ROWS = PDO_MYSQL_ATTR_FOUND_ROWS;
  127. const int64_t q_PDO$$MYSQL_ATTR_IGNORE_SPACE = PDO_MYSQL_ATTR_IGNORE_SPACE;
  128. ///////////////////////////////////////////////////////////////////////////////
  129. // extension functions
  130. Array f_pdo_drivers() {
  131. Array ret = Array::Create();
  132. const PDODriverMap &drivers = PDODriver::GetDrivers();
  133. for (PDODriverMap::const_iterator iter = drivers.begin();
  134. iter != drivers.end(); ++iter) {
  135. ret.append(iter->second->getName());
  136. }
  137. return ret;
  138. }
  139. ///////////////////////////////////////////////////////////////////////////////
  140. // error handling
  141. struct pdo_sqlstate_info {
  142. const char *state;
  143. const char *desc;
  144. };
  145. static const struct pdo_sqlstate_info err_initializer[] = {
  146. { "00000", "No error" },
  147. { "01000", "Warning" },
  148. { "01001", "Cursor operation conflict" },
  149. { "01002", "Disconnect error" },
  150. { "01003", "NULL value eliminated in set function" },
  151. { "01004", "String data, right truncated" },
  152. { "01006", "Privilege not revoked" },
  153. { "01007", "Privilege not granted" },
  154. { "01008", "Implicit zero bit padding" },
  155. { "0100C", "Dynamic result sets returned" },
  156. { "01P01", "Deprecated feature" },
  157. { "01S00", "Invalid connection string attribute" },
  158. { "01S01", "Error in row" },
  159. { "01S02", "Option value changed" },
  160. { "01S06",
  161. "Attempt to fetch before the result set returned the first rowset" },
  162. { "01S07", "Fractional truncation" },
  163. { "01S08", "Error saving File DSN" },
  164. { "01S09", "Invalid keyword" },
  165. { "02000", "No data" },
  166. { "02001", "No additional dynamic result sets returned" },
  167. { "03000", "Sql statement not yet complete" },
  168. { "07002", "COUNT field incorrect" },
  169. { "07005", "Prepared statement not a cursor-specification" },
  170. { "07006", "Restricted data type attribute violation" },
  171. { "07009", "Invalid descriptor index" },
  172. { "07S01", "Invalid use of default parameter" },
  173. { "08000", "Connection exception" },
  174. { "08001", "Client unable to establish connection" },
  175. { "08002", "Connection name in use" },
  176. { "08003", "Connection does not exist" },
  177. { "08004", "Server rejected the connection" },
  178. { "08006", "Connection failure" },
  179. { "08007", "Connection failure during transaction" },
  180. { "08S01", "Communication link failure" },
  181. { "09000", "Triggered action exception" },
  182. { "0A000", "Feature not supported" },
  183. { "0B000", "Invalid transaction initiation" },
  184. { "0F000", "Locator exception" },
  185. { "0F001", "Invalid locator specification" },
  186. { "0L000", "Invalid grantor" },
  187. { "0LP01", "Invalid grant operation" },
  188. { "0P000", "Invalid role specification" },
  189. { "21000", "Cardinality violation" },
  190. { "21S01", "Insert value list does not match column list" },
  191. { "21S02", "Degree of derived table does not match column list" },
  192. { "22000", "Data exception" },
  193. { "22001", "String data, right truncated" },
  194. { "22002", "Indicator variable required but not supplied" },
  195. { "22003", "Numeric value out of range" },
  196. { "22004", "Null value not allowed" },
  197. { "22005", "Error in assignment" },
  198. { "22007", "Invalid datetime format" },
  199. { "22008", "Datetime field overflow" },
  200. { "22009", "Invalid time zone displacement value" },
  201. { "2200B", "Escape character conflict" },
  202. { "2200C", "Invalid use of escape character" },
  203. { "2200D", "Invalid escape octet" },
  204. { "2200F", "Zero length character string" },
  205. { "2200G", "Most specific type mismatch" },
  206. { "22010", "Invalid indicator parameter value" },
  207. { "22011", "Substring error" },
  208. { "22012", "Division by zero" },
  209. { "22015", "Interval field overflow" },
  210. { "22018", "Invalid character value for cast specification" },
  211. { "22019", "Invalid escape character" },
  212. { "2201B", "Invalid regular expression" },
  213. { "2201E", "Invalid argument for logarithm" },
  214. { "2201F", "Invalid argument for power function" },
  215. { "2201G", "Invalid argument for width bucket function" },
  216. { "22020", "Invalid limit value" },
  217. { "22021", "Character not in repertoire" },
  218. { "22022", "Indicator overflow" },
  219. { "22023", "Invalid parameter value" },
  220. { "22024", "Unterminated c string" },
  221. { "22025", "Invalid escape sequence" },
  222. { "22026", "String data, length mismatch" },
  223. { "22027", "Trim error" },
  224. { "2202E", "Array subscript error" },
  225. { "22P01", "Floating point exception" },
  226. { "22P02", "Invalid text representation" },
  227. { "22P03", "Invalid binary representation" },
  228. { "22P04", "Bad copy file format" },
  229. { "22P05", "Untranslatable character" },
  230. { "23000", "Integrity constraint violation" },
  231. { "23001", "Restrict violation" },
  232. { "23502", "Not null violation" },
  233. { "23503", "Foreign key violation" },
  234. { "23505", "Unique violation" },
  235. { "23514", "Check violation" },
  236. { "24000", "Invalid cursor state" },
  237. { "25000", "Invalid transaction state" },
  238. { "25001", "Active sql transaction" },
  239. { "25002", "Branch transaction already active" },
  240. { "25003", "Inappropriate access mode for branch transaction" },
  241. { "25004", "Inappropriate isolation level for branch transaction" },
  242. { "25005", "No active sql transaction for branch transaction" },
  243. { "25006", "Read only sql transaction" },
  244. { "25007", "Schema and data statement mixing not supported" },
  245. { "25008", "Held cursor requires same isolation level" },
  246. { "25P01", "No active sql transaction" },
  247. { "25P02", "In failed sql transaction" },
  248. { "25S01", "Transaction state" },
  249. { "25S02", "Transaction is still active" },
  250. { "25S03", "Transaction is rolled back" },
  251. { "26000", "Invalid sql statement name" },
  252. { "27000", "Triggered data change violation" },
  253. { "28000", "Invalid authorization specification" },
  254. { "2B000", "Dependent privilege descriptors still exist" },
  255. { "2BP01", "Dependent objects still exist" },
  256. { "2D000", "Invalid transaction termination" },
  257. { "2F000", "Sql routine exception" },
  258. { "2F002", "Modifying sql data not permitted" },
  259. { "2F003", "Prohibited sql statement attempted" },
  260. { "2F004", "Reading sql data not permitted" },
  261. { "2F005", "Function executed no return statement" },
  262. { "34000", "Invalid cursor name" },
  263. { "38000", "External routine exception" },
  264. { "38001", "Containing sql not permitted" },
  265. { "38002", "Modifying sql data not permitted" },
  266. { "38003", "Prohibited sql statement attempted" },
  267. { "38004", "Reading sql data not permitted" },
  268. { "39000", "External routine invocation exception" },
  269. { "39001", "Invalid sqlstate returned" },
  270. { "39004", "Null value not allowed" },
  271. { "39P01", "Trigger protocol violated" },
  272. { "39P02", "Srf protocol violated" },
  273. { "3B000", "Savepoint exception" },
  274. { "3B001", "Invalid savepoint specification" },
  275. { "3C000", "Duplicate cursor name" },
  276. { "3D000", "Invalid catalog name" },
  277. { "3F000", "Invalid schema name" },
  278. { "40000", "Transaction rollback" },
  279. { "40001", "Serialization failure" },
  280. { "40002", "Transaction integrity constraint violation" },
  281. { "40003", "Statement completion unknown" },
  282. { "40P01", "Deadlock detected" },
  283. { "42000", "Syntax error or access violation" },
  284. { "42501", "Insufficient privilege" },
  285. { "42601", "Syntax error" },
  286. { "42602", "Invalid name" },
  287. { "42611", "Invalid column definition" },
  288. { "42622", "Name too long" },
  289. { "42701", "Duplicate column" },
  290. { "42702", "Ambiguous column" },
  291. { "42703", "Undefined column" },
  292. { "42704", "Undefined object" },
  293. { "42710", "Duplicate object" },
  294. { "42712", "Duplicate alias" },
  295. { "42723", "Duplicate function" },
  296. { "42725", "Ambiguous function" },
  297. { "42803", "Grouping error" },
  298. { "42804", "Datatype mismatch" },
  299. { "42809", "Wrong object type" },
  300. { "42830", "Invalid foreign key" },
  301. { "42846", "Cannot coerce" },
  302. { "42883", "Undefined function" },
  303. { "42939", "Reserved name" },
  304. { "42P01", "Undefined table" },
  305. { "42P02", "Undefined parameter" },
  306. { "42P03", "Duplicate cursor" },
  307. { "42P04", "Duplicate database" },
  308. { "42P05", "Duplicate prepared statement" },
  309. { "42P06", "Duplicate schema" },
  310. { "42P07", "Duplicate table" },
  311. { "42P08", "Ambiguous parameter" },
  312. { "42P09", "Ambiguous alias" },
  313. { "42P10", "Invalid column reference" },
  314. { "42P11", "Invalid cursor definition" },
  315. { "42P12", "Invalid database definition" },
  316. { "42P13", "Invalid function definition" },
  317. { "42P14", "Invalid prepared statement definition" },
  318. { "42P15", "Invalid schema definition" },
  319. { "42P16", "Invalid table definition" },
  320. { "42P17", "Invalid object definition" },
  321. { "42P18", "Indeterminate datatype" },
  322. { "42S01", "Base table or view already exists" },
  323. { "42S02", "Base table or view not found" },
  324. { "42S11", "Index already exists" },
  325. { "42S12", "Index not found" },
  326. { "42S21", "Column already exists" },
  327. { "42S22", "Column not found" },
  328. { "44000", "WITH CHECK OPTION violation" },
  329. { "53000", "Insufficient resources" },
  330. { "53100", "Disk full" },
  331. { "53200", "Out of memory" },
  332. { "53300", "Too many connections" },
  333. { "54000", "Program limit exceeded" },
  334. { "54001", "Statement too complex" },
  335. { "54011", "Too many columns" },
  336. { "54023", "Too many arguments" },
  337. { "55000", "Object not in prerequisite state" },
  338. { "55006", "Object in use" },
  339. { "55P02", "Cant change runtime param" },
  340. { "55P03", "Lock not available" },
  341. { "57000", "Operator intervention" },
  342. { "57014", "Query canceled" },
  343. { "57P01", "Admin shutdown" },
  344. { "57P02", "Crash shutdown" },
  345. { "57P03", "Cannot connect now" },
  346. { "58030", "Io error" },
  347. { "58P01", "Undefined file" },
  348. { "58P02", "Duplicate file" },
  349. { "F0000", "Config file error" },
  350. { "F0001", "Lock file exists" },
  351. { "HY000", "General error" },
  352. { "HY001", "Memory allocation error" },
  353. { "HY003", "Invalid application buffer type" },
  354. { "HY004", "Invalid SQL data type" },
  355. { "HY007", "Associated statement is not prepared" },
  356. { "HY008", "Operation canceled" },
  357. { "HY009", "Invalid use of null pointer" },
  358. { "HY010", "Function sequence error" },
  359. { "HY011", "Attribute cannot be set now" },
  360. { "HY012", "Invalid transaction operation code" },
  361. { "HY013", "Memory management error" },
  362. { "HY014", "Limit on the number of handles exceeded" },
  363. { "HY015", "No cursor name available" },
  364. { "HY016", "Cannot modify an implementation row descriptor" },
  365. { "HY017", "Invalid use of an automatically allocated descriptor handle" },
  366. { "HY018", "Server declined cancel request" },
  367. { "HY019", "Non-character and non-binary data sent in pieces" },
  368. { "HY020", "Attempt to concatenate a null value" },
  369. { "HY021", "Inconsistent descriptor information" },
  370. { "HY024", "Invalid attribute value" },
  371. { "HY090", "Invalid string or buffer length" },
  372. { "HY091", "Invalid descriptor field identifier" },
  373. { "HY092", "Invalid attribute/option identifier" },
  374. { "HY093", "Invalid parameter number" },
  375. { "HY095", "Function type out of range" },
  376. { "HY096", "Invalid information type" },
  377. { "HY097", "Column type out of range" },
  378. { "HY098", "Scope type out of range" },
  379. { "HY099", "Nullable type out of range" },
  380. { "HY100", "Uniqueness option type out of range" },
  381. { "HY101", "Accuracy option type out of range" },
  382. { "HY103", "Invalid retrieval code" },
  383. { "HY104", "Invalid precision or scale value" },
  384. { "HY105", "Invalid parameter type" },
  385. { "HY106", "Fetch type out of range" },
  386. { "HY107", "Row value out of range" },
  387. { "HY109", "Invalid cursor position" },
  388. { "HY110", "Invalid driver completion" },
  389. { "HY111", "Invalid bookmark value" },
  390. { "HYC00", "Optional feature not implemented" },
  391. { "HYT00", "Timeout expired" },
  392. { "HYT01", "Connection timeout expired" },
  393. { "IM001", "Driver does not support this function" },
  394. { "IM002", "Data source name not found and no default driver specified" },
  395. { "IM003", "Specified driver could not be loaded" },
  396. { "IM004", "Driver's SQLAllocHandle on SQL_HANDLE_ENV failed" },
  397. { "IM005", "Driver's SQLAllocHandle on SQL_HANDLE_DBC failed" },
  398. { "IM006", "Driver's SQLSetConnectAttr failed" },
  399. { "IM007", "No data source or driver specified; dialog prohibited" },
  400. { "IM008", "Dialog failed" },
  401. { "IM009", "Unable to load translation DLL" },
  402. { "IM010", "Data source name too long" },
  403. { "IM011", "Driver name too long" },
  404. { "IM012", "DRIVER keyword syntax error" },
  405. { "IM013", "Trace file error" },
  406. { "IM014", "Invalid name of File DSN" },
  407. { "IM015", "Corrupt file data source" },
  408. { "P0000", "Plpgsql error" },
  409. { "P0001", "Raise exception" },
  410. { "XX000", "Internal error" },
  411. { "XX001", "Data corrupted" },
  412. { "XX002", "Index corrupted" }
  413. };
  414. class PDOErrorHash : private hphp_const_char_map<const char *> {
  415. public:
  416. PDOErrorHash() {
  417. for (unsigned int i = 0;
  418. i < sizeof(err_initializer)/sizeof(err_initializer[0]); i++) {
  419. const struct pdo_sqlstate_info *info = &err_initializer[i];
  420. (*this)[info->state] = info->desc;
  421. }
  422. }
  423. const char *description(const char *state) {
  424. const_iterator iter = find(state);
  425. if (iter != end()) {
  426. return iter->second;
  427. }
  428. return "<<Unknown error>>";
  429. }
  430. };
  431. static PDOErrorHash s_err_hash;
  432. const StaticString
  433. s_code("code"),
  434. s_message("message"),
  435. s_errorInfo("errorInfo"),
  436. s_PDOException("PDOException");
  437. void throw_pdo_exception(CVarRef code, CVarRef info, const char *fmt, ...) {
  438. ObjectData *obj = SystemLib::AllocPDOExceptionObject();
  439. obj->o_set(s_code, code, s_PDOException);
  440. va_list ap;
  441. va_start(ap, fmt);
  442. string msg;
  443. Util::string_vsnprintf(msg, fmt, ap);
  444. obj->o_set(s_message, String(msg), s_PDOException);
  445. va_end(ap);
  446. if (!info.isNull()) {
  447. obj->o_set(s_errorInfo, info, s_PDOException);
  448. }
  449. throw Object(obj);
  450. }
  451. void pdo_raise_impl_error(sp_PDOConnection dbh, sp_PDOStatement stmt,
  452. const char *sqlstate, const char *supp) {
  453. PDOErrorType *pdo_err = &dbh->error_code;
  454. if (stmt.get()) {
  455. pdo_err = &stmt->error_code;
  456. }
  457. strcpy(*pdo_err, sqlstate);
  458. const char *msg = s_err_hash.description(sqlstate);
  459. string err = "SQLSTATE["; err += sqlstate; err += "]: "; err += msg;
  460. if (supp) {
  461. err += ": "; err += supp;
  462. }
  463. if (dbh->error_mode != PDO_ERRMODE_EXCEPTION) {
  464. raise_warning("%s", err.c_str());
  465. } else {
  466. Array info;
  467. info.append(String(*pdo_err, CopyString));
  468. info.append(0LL);
  469. throw_pdo_exception(String(sqlstate, CopyString), info, "%s", err.c_str());
  470. }
  471. }
  472. static void pdo_handle_error(sp_PDOConnection dbh, sp_PDOStatement stmt) {
  473. if (dbh->error_mode == PDO_ERRMODE_SILENT) {
  474. return;
  475. }
  476. PDOErrorType *pdo_err = &dbh->error_code;
  477. if (stmt.get()) {
  478. pdo_err = &stmt->error_code;
  479. }
  480. /* hash sqlstate to error messages */
  481. const char *msg = s_err_hash.description(*pdo_err);
  482. int64_t native_code = 0;
  483. String supp;
  484. Array info;
  485. if (dbh->support(PDOConnection::MethodFetchErr)) {
  486. info = Array::Create();
  487. info.append(String(*pdo_err, CopyString));
  488. if (dbh->fetchErr(stmt.get(), info)) {
  489. if (info.exists(1)) {
  490. native_code = info[1].toInt64();
  491. }
  492. if (info.exists(2)) {
  493. supp = info[2].toString();
  494. }
  495. }
  496. }
  497. string err = "SQLSTATE["; err += *pdo_err; err += "]: "; err += msg;
  498. if (!supp.empty()) {
  499. err += ": "; err += String(native_code).data();
  500. err += " "; err += supp.data();
  501. }
  502. if (dbh->error_mode != PDO_ERRMODE_EXCEPTION) {
  503. raise_warning("%s", err.c_str());
  504. } else {
  505. throw_pdo_exception(String(*pdo_err, CopyString), info, "%s", err.c_str());
  506. }
  507. }
  508. ///////////////////////////////////////////////////////////////////////////////
  509. // helpers for PDO class
  510. static inline int64_t pdo_attr_lval(CArrRef options, PDOAttributeType name,
  511. int64_t defval) {
  512. if (options.exists(name)) {
  513. return options[name].toInt64();
  514. }
  515. return defval;
  516. }
  517. static inline String pdo_attr_strval(CArrRef options, PDOAttributeType name,
  518. const String& defval) {
  519. if (options.exists(name)) {
  520. return options[name].toString();
  521. }
  522. return defval;
  523. }
  524. static Object pdo_stmt_instantiate(sp_PDOConnection dbh, const String& clsname,
  525. CVarRef ctor_args) {
  526. String name = clsname;
  527. if (name.empty()) {
  528. name = "PDOStatement";
  529. }
  530. if (!ctor_args.isNull() && !ctor_args.isArray()) {
  531. pdo_raise_impl_error(dbh, nullptr, "HY000",
  532. "constructor arguments must be passed as an array");
  533. return Object();
  534. }
  535. Class* cls = Unit::loadClass(name.get());
  536. if (!cls) {
  537. return Object();
  538. }
  539. return ObjectData::newInstance(cls);
  540. }
  541. static void pdo_stmt_construct(sp_PDOStatement stmt, Object object,
  542. const String& clsname, CVarRef ctor_args) {
  543. if (clsname.empty()) {
  544. return;
  545. }
  546. Class* cls = Unit::loadClass(clsname.get());
  547. if (!cls) {
  548. return;
  549. }
  550. object->o_set("queryString", stmt->query_string);
  551. TypedValue ret;
  552. ObjectData* inst = object.get();
  553. g_vmContext->invokeFunc(&ret, cls->getCtor(), ctor_args.toArray(), inst);
  554. tvRefcountedDecRef(&ret);
  555. }
  556. static bool valid_statement_class(sp_PDOConnection dbh, CVarRef opt,
  557. String &clsname, Variant &ctor_args) {
  558. if (!opt.isArray() || !opt.toArray().exists(0) || !opt[0].isString() ||
  559. !f_class_exists(opt[0].toString())) {
  560. pdo_raise_impl_error
  561. (dbh, nullptr, "HY000",
  562. "PDO::ATTR_STATEMENT_CLASS requires format array(classname, "
  563. "array(ctor_args)); the classname must be a string specifying "
  564. "an existing class");
  565. PDO_HANDLE_DBH_ERR(dbh);
  566. return false;
  567. }
  568. clsname = opt[0].toString();
  569. if (!f_is_subclass_of(clsname, "PDOStatement")) {
  570. pdo_raise_impl_error
  571. (dbh, nullptr, "HY000",
  572. "user-supplied statement class must be derived from PDOStatement");
  573. PDO_HANDLE_DBH_ERR(dbh);
  574. return false;
  575. }
  576. HPHP::Class* cls = HPHP::Unit::loadClass(clsname.get());
  577. if (cls) {
  578. const HPHP::Func* method = cls->getDeclaredCtor();
  579. if (method && method->isPublic()) {
  580. pdo_raise_impl_error
  581. (dbh, nullptr, "HY000",
  582. "user-supplied statement class cannot have a public constructor");
  583. PDO_HANDLE_DBH_ERR(dbh);
  584. return false;
  585. }
  586. }
  587. if (opt.toArray().exists(1)) {
  588. Variant item = opt[1];
  589. if (!item.isArray()) {
  590. pdo_raise_impl_error
  591. (dbh, nullptr, "HY000",
  592. "PDO::ATTR_STATEMENT_CLASS requires format array(classname, "
  593. "ctor_args); ctor_args must be an array");
  594. PDO_HANDLE_DBH_ERR(dbh);
  595. return false;
  596. }
  597. ctor_args = item;
  598. }
  599. return true;
  600. }
  601. static bool pdo_stmt_describe_columns(sp_PDOStatement stmt) {
  602. for (int col = 0; col < stmt->column_count; col++) {
  603. if (!stmt->describer(col)) {
  604. return false;
  605. }
  606. String &name = stmt->columns[col].toResource().getTyped<PDOColumn>()->name;
  607. /* if we are applying case conversions on column names, do so now */
  608. if (stmt->dbh->native_case != stmt->dbh->desired_case &&
  609. stmt->dbh->desired_case != PDO_CASE_NATURAL) {
  610. switch (stmt->dbh->desired_case) {
  611. case PDO_CASE_UPPER:
  612. name = f_strtoupper(name);
  613. break;
  614. case PDO_CASE_LOWER:
  615. name = f_strtolower(name);
  616. break;
  617. default:;
  618. }
  619. }
  620. if (stmt->bound_columns.exists(name)) {
  621. PDOBoundParam *param =
  622. stmt->bound_columns[name].toResource().getTyped<PDOBoundParam>();
  623. param->paramno = col;
  624. }
  625. }
  626. return true;
  627. }
  628. static bool pdo_stmt_verify_mode(sp_PDOStatement stmt, int64_t mode,
  629. bool fetch_all) {
  630. int flags = mode & PDO_FETCH_FLAGS;
  631. mode = mode & ~PDO_FETCH_FLAGS;
  632. if (mode < 0 || mode > PDO_FETCH__MAX) {
  633. pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "invalid fetch mode");
  634. return false;
  635. }
  636. if (mode == PDO_FETCH_USE_DEFAULT) {
  637. flags = stmt->default_fetch_type & PDO_FETCH_FLAGS;
  638. mode = stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
  639. }
  640. switch (mode) {
  641. case PDO_FETCH_FUNC:
  642. if (!fetch_all) {
  643. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  644. "PDO::FETCH_FUNC is only allowed in "
  645. "PDOStatement::fetchAll()");
  646. return false;
  647. }
  648. return true;
  649. case PDO_FETCH_LAZY:
  650. if (fetch_all) {
  651. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  652. "PDO::FETCH_LAZY can't be used with "
  653. "PDOStatement::fetchAll()");
  654. return false;
  655. }
  656. default:
  657. if ((flags & PDO_FETCH_SERIALIZE) == PDO_FETCH_SERIALIZE) {
  658. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  659. "PDO::FETCH_SERIALIZE can only be used "
  660. "together with PDO::FETCH_CLASS");
  661. return false;
  662. }
  663. if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
  664. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  665. "PDO::FETCH_CLASSTYPE can only be used "
  666. "together with PDO::FETCH_CLASS");
  667. return false;
  668. }
  669. if (mode >= PDO_FETCH__MAX) {
  670. pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "invalid fetch mode");
  671. return false;
  672. }
  673. /* no break; */
  674. case PDO_FETCH_CLASS:
  675. break;
  676. }
  677. return true;
  678. }
  679. static bool do_fetch_class_prepare(sp_PDOStatement stmt) {
  680. String clsname = stmt->fetch.clsname;
  681. if (clsname.empty()) {
  682. stmt->fetch.clsname = "stdclass";
  683. }
  684. stmt->fetch.constructor = empty_string; //NULL;
  685. HPHP::Class* cls = HPHP::Unit::loadClass(clsname.get());
  686. if (cls) {
  687. const HPHP::Func* method = cls->getDeclaredCtor();
  688. if (method) {
  689. stmt->fetch.constructor = method->nameRef();
  690. return true;
  691. }
  692. }
  693. if (!stmt->fetch.ctor_args.isNull()) {
  694. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  695. "user-supplied class does not have a constructor, "
  696. "use NULL for the ctor_params parameter, or simply "
  697. "omit it");
  698. return false;
  699. }
  700. return true; /* no ctor no args is also ok */
  701. }
  702. static bool pdo_stmt_set_fetch_mode(sp_PDOStatement stmt, int _argc, int64_t mode,
  703. CArrRef _argv) {
  704. _argc = _argv.size() + 1;
  705. if (stmt->default_fetch_type == PDO_FETCH_INTO) {
  706. stmt->fetch.into.reset();
  707. }
  708. stmt->default_fetch_type = PDO_FETCH_BOTH;
  709. if (!pdo_stmt_verify_mode(stmt, mode, false)) {
  710. strcpy(stmt->error_code, PDO_ERR_NONE);
  711. return false;
  712. }
  713. int flags = mode & PDO_FETCH_FLAGS;
  714. bool retval = false;
  715. switch (mode & ~PDO_FETCH_FLAGS) {
  716. case PDO_FETCH_USE_DEFAULT:
  717. case PDO_FETCH_LAZY:
  718. case PDO_FETCH_ASSOC:
  719. case PDO_FETCH_NUM:
  720. case PDO_FETCH_BOTH:
  721. case PDO_FETCH_OBJ:
  722. case PDO_FETCH_BOUND:
  723. case PDO_FETCH_NAMED:
  724. case PDO_FETCH_KEY_PAIR:
  725. if (_argc != 1) {
  726. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  727. "fetch mode doesn't allow any extra arguments");
  728. } else {
  729. retval = true;
  730. }
  731. break;
  732. case PDO_FETCH_COLUMN:
  733. if (_argc != 2) {
  734. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  735. "fetch mode requires the colno argument");
  736. } else if (!_argv[0].isInteger()) {
  737. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  738. "colno must be an integer");
  739. } else {
  740. stmt->fetch.column = _argv[0].toInt64();
  741. retval = true;
  742. }
  743. break;
  744. case PDO_FETCH_CLASS:
  745. /* Gets its class name from 1st column */
  746. if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
  747. if (_argc != 1) {
  748. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  749. "fetch mode doesn't allow any extra arguments");
  750. } else {
  751. stmt->fetch.clsname.clear();
  752. retval = true;
  753. }
  754. } else {
  755. if (_argc < 2) {
  756. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  757. "fetch mode requires the classname argument");
  758. } else if (_argc > 3) {
  759. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  760. "too many arguments");
  761. } else if (!_argv[0].isString()) {
  762. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  763. "classname must be a string");
  764. } else {
  765. retval = f_class_exists(_argv[0].toString());
  766. if (retval) {
  767. stmt->fetch.clsname = _argv[0].toString();
  768. }
  769. }
  770. }
  771. if (retval) {
  772. stmt->fetch.ctor_args.reset();
  773. if (_argc == 3) {
  774. if (!_argv[1].isNull() && !_argv[1].isArray()) {
  775. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  776. "ctor_args must be either NULL or an array");
  777. retval = false;
  778. } else {
  779. stmt->fetch.ctor_args = _argv[1];
  780. }
  781. }
  782. if (retval) {
  783. do_fetch_class_prepare(stmt);
  784. }
  785. }
  786. break;
  787. case PDO_FETCH_INTO:
  788. if (_argc != 2) {
  789. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  790. "fetch mode requires the object parameter");
  791. } else if (!_argv[0].isObject()) {
  792. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  793. "object must be an object");
  794. } else {
  795. retval = true;
  796. }
  797. if (retval) {
  798. stmt->fetch.into = _argv[0];
  799. }
  800. break;
  801. default:
  802. pdo_raise_impl_error(stmt->dbh, stmt, "22003",
  803. "Invalid fetch mode specified");
  804. }
  805. if (retval) {
  806. stmt->default_fetch_type = (PDOFetchType)mode;
  807. }
  808. /*
  809. * PDO error (if any) has already been raised at this point.
  810. *
  811. * The error_code is cleared, otherwise the caller will read the
  812. * last error message from the driver.
  813. *
  814. */
  815. strcpy(stmt->error_code, PDO_ERR_NONE);
  816. return retval;
  817. }
  818. ///////////////////////////////////////////////////////////////////////////////
  819. class PDORequestData : public RequestEventHandler {
  820. public:
  821. virtual void requestInit() {
  822. }
  823. virtual void requestShutdown() {
  824. for (std::set<PDOConnection*>::iterator iter =
  825. m_persistent_connections.begin();
  826. iter != m_persistent_connections.end(); ++iter) {
  827. PDOConnection *conn = *iter;
  828. if (!conn) {
  829. // Dead handle in the set
  830. continue;
  831. }
  832. if (conn->support(PDOConnection::MethodCheckLiveness) &&
  833. !conn->checkLiveness()) {
  834. // Dead connection in the handle
  835. continue;
  836. }
  837. // All seems right, save it
  838. conn->persistentSave();
  839. }
  840. }
  841. public:
  842. std::set<PDOConnection*> m_persistent_connections;
  843. };
  844. IMPLEMENT_STATIC_REQUEST_LOCAL(PDORequestData, s_pdo_request_data);
  845. ///////////////////////////////////////////////////////////////////////////////
  846. // PDO
  847. c_PDO::c_PDO(Class* cb) : ExtObjectData(cb) {
  848. }
  849. c_PDO::~c_PDO() {
  850. }
  851. void c_PDO::sweep() {
  852. // PDOConnection is not sweepable, so clean it up manually.
  853. static_assert(!std::is_base_of<Sweepable, PDOConnection>::value,
  854. "Remove the call to reset() below.");
  855. m_dbh.reset();
  856. }
  857. void c_PDO::t___construct(const String& dsn, const String& username /* = null_string */,
  858. const String& password /* = null_string */,
  859. CArrRef options /* = null_array */) {
  860. String data_source = dsn;
  861. /* parse the data source name */
  862. const char *colon = strchr(data_source.data(), ':');
  863. if (!colon) {
  864. /* let's see if this string has a matching dsn in the php.ini */
  865. String name = "pdo.dsn."; name += data_source;
  866. String ini_dsn;
  867. if (!IniSetting::Get(name, ini_dsn)) {
  868. throw_pdo_exception(uninit_null(), uninit_null(), "invalid data source name");
  869. }
  870. data_source = ini_dsn;
  871. colon = strchr(data_source.data(), ':');
  872. if (!colon) {
  873. throw_pdo_exception(uninit_null(), uninit_null(), "invalid data source name (via INI: %s)",
  874. ini_dsn.data());
  875. }
  876. }
  877. if (!strncmp(data_source.data(), "uri:", 4)) {
  878. /* the specified URI holds connection details */
  879. Variant stream = File::Open(data_source.substr(4), "rb");
  880. if (same(stream, false)) {
  881. throw_pdo_exception(uninit_null(), uninit_null(), "invalid data source URI");
  882. }
  883. data_source = stream.toResource().getTyped<File>()->readLine(1024);
  884. colon = strchr(data_source.data(), ':');
  885. if (!colon) {
  886. throw_pdo_exception(uninit_null(), uninit_null(), "invalid data source name (via URI)");
  887. }
  888. }
  889. const PDODriverMap &drivers = PDODriver::GetDrivers();
  890. String name = data_source.substr(0, colon - data_source.data());
  891. PDODriverMap::const_iterator iter = drivers.find(name.data());
  892. if (iter == drivers.end()) {
  893. /* NB: don't want to include the data_source in the error message as
  894. * it might contain a password */
  895. throw_pdo_exception(uninit_null(), uninit_null(), "could not find driver");
  896. }
  897. PDODriver *driver = iter->second;
  898. /* is this supposed to be a persistent connection ? */
  899. bool is_persistent = false;
  900. bool call_factory = true;
  901. String shashkey;
  902. if (!options.empty()) {
  903. StringBuffer hashkey;
  904. if (options.exists(PDO_ATTR_PERSISTENT)) {
  905. Variant v = options[PDO_ATTR_PERSISTENT];
  906. String sv = v.toString();
  907. if (v.isString() && !sv.isNumeric() && !sv.empty()) {
  908. /* user specified key */
  909. hashkey.printf("PDO:DBH:DSN=%s:%s:%s:%s",
  910. data_source.data(), username.data(),
  911. password.data(), sv.data());
  912. is_persistent = true;
  913. } else {
  914. is_persistent = v.toInt64();
  915. hashkey.printf("PDO:DBH:DSN=%s:%s:%s",
  916. data_source.data(), username.data(),
  917. password.data());
  918. }
  919. }
  920. if (is_persistent) {
  921. shashkey = hashkey.detach();
  922. /* let's see if we have one cached.... */
  923. m_dbh = dynamic_cast<PDOConnection*>
  924. (g_persistentObjects->get(PDOConnection::PersistentKey,
  925. shashkey.data()));
  926. if (m_dbh.get()) {
  927. m_dbh->persistentRestore();
  928. /* is the connection still alive ? */
  929. if (m_dbh->support(PDOConnection::MethodCheckLiveness) &&
  930. !m_dbh->checkLiveness()) {
  931. /* nope... need to kill it */
  932. s_pdo_request_data->m_persistent_connections.erase(m_dbh.get());
  933. m_dbh = nullptr;
  934. } else {
  935. /* Yep, use it and mark it for saving at rshutdown */
  936. s_pdo_request_data->m_persistent_connections.insert(m_dbh.get());
  937. }
  938. }
  939. if (m_dbh.get()) {
  940. call_factory = false;
  941. } else {
  942. /* need a brand new pdbh */
  943. m_dbh = driver->createConnection(colon+1, username, password, options);
  944. if (m_dbh.get() == nullptr) {
  945. throw_pdo_exception(uninit_null(), uninit_null(), "unable to create a connection");
  946. }
  947. m_dbh->persistent_id = string(shashkey.data(), shashkey.size());
  948. }
  949. }
  950. }
  951. if (!m_dbh.get()) {
  952. m_dbh = driver->createConnection(colon+1, username, password, options);
  953. if (m_dbh.get() == nullptr) {
  954. throw_pdo_exception(uninit_null(), uninit_null(), "unable to create a connection");
  955. }
  956. }
  957. if (call_factory) {
  958. m_dbh->default_fetch_type = PDO_FETCH_BOTH;
  959. }
  960. m_dbh->auto_commit = pdo_attr_lval(options, PDO_ATTR_AUTOCOMMIT, 1);
  961. if (!call_factory) {
  962. /* we got a persistent guy from our cache */
  963. for (ArrayIter iter(options); iter; ++iter) {
  964. t_setattribute(iter.first().toInt64(), iter.second());
  965. }
  966. } else if (m_dbh.get()) {
  967. if (is_persistent) {
  968. assert(!shashkey.empty());
  969. g_persistentObjects->set(PDOConnection::PersistentKey, shashkey.data(),
  970. m_dbh.get());
  971. s_pdo_request_data->m_persistent_connections.insert(m_dbh.get());
  972. }
  973. m_dbh->driver = driver;
  974. for (ArrayIter iter(options); iter; ++iter) {
  975. t_setattribute(iter.first().toInt64(), iter.second());
  976. }
  977. }
  978. }
  979. Variant c_PDO::t_prepare(const String& statement,
  980. CArrRef options /* = null_array */) {
  981. assert(m_dbh->driver);
  982. strcpy(m_dbh->error_code, PDO_ERR_NONE);
  983. m_dbh->query_stmt = NULL;
  984. String clsname;
  985. Variant ctor_args;
  986. if (options.exists(PDO_ATTR_STATEMENT_CLASS)) {
  987. Variant opt = options[PDO_ATTR_STATEMENT_CLASS];
  988. if (!valid_statement_class(m_dbh, opt, clsname, ctor_args)) {
  989. return false;
  990. }
  991. } else {
  992. clsname = m_dbh->def_stmt_clsname;
  993. ctor_args = m_dbh->def_stmt_ctor_args;
  994. }
  995. Object ret = pdo_stmt_instantiate(m_dbh, clsname, ctor_args);
  996. if (ret.isNull()) {
  997. pdo_raise_impl_error
  998. (m_dbh, nullptr, "HY000",
  999. "failed to instantiate user-supplied statement class");
  1000. PDO_HANDLE_DBH_ERR(m_dbh);
  1001. return false;
  1002. }
  1003. c_PDOStatement *pdostmt = ret.getTyped<c_PDOStatement>();
  1004. if (m_dbh->preparer(statement, &pdostmt->m_stmt, options)) {
  1005. PDOStatement *stmt = pdostmt->m_stmt.get();
  1006. assert(stmt);
  1007. /* unconditionally keep this for later reference */
  1008. stmt->query_string = statement;
  1009. stmt->default_fetch_type = m_dbh->default_fetch_type;
  1010. stmt->dbh = m_dbh;
  1011. pdo_stmt_construct(stmt, ret, clsname, ctor_args);
  1012. return ret;
  1013. }
  1014. PDO_HANDLE_DBH_ERR(m_dbh);
  1015. return false;
  1016. }
  1017. bool c_PDO::t_begintransaction() {
  1018. if (m_dbh->in_txn) {
  1019. throw_pdo_exception(uninit_null(), uninit_null(), "There is already an active transaction");
  1020. }
  1021. if (m_dbh->begin()) {
  1022. m_dbh->in_txn = 1;
  1023. return true;
  1024. }
  1025. if (strcmp(m_dbh->error_code, PDO_ERR_NONE)) {
  1026. pdo_handle_error(m_dbh, nullptr);
  1027. }
  1028. return false;
  1029. }
  1030. bool c_PDO::t_commit() {
  1031. assert(m_dbh->driver);
  1032. if (!m_dbh->in_txn) {
  1033. throw_pdo_exception(uninit_null(), uninit_null(), "There is no active transaction");
  1034. }
  1035. if (m_dbh->commit()) {
  1036. m_dbh->in_txn = 0;
  1037. return true;
  1038. }
  1039. PDO_HANDLE_DBH_ERR(m_dbh);
  1040. return false;
  1041. }
  1042. bool c_PDO::t_rollback() {
  1043. assert(m_dbh->driver);
  1044. if (!m_dbh->in_txn) {
  1045. throw_pdo_exception(uninit_null(), uninit_null(), "There is no active transaction");
  1046. }
  1047. if (m_dbh->rollback()) {
  1048. m_dbh->in_txn = 0;
  1049. return true;
  1050. }
  1051. PDO_HANDLE_DBH_ERR(m_dbh);
  1052. return false;
  1053. }
  1054. bool c_PDO::t_setattribute(int64_t attribute, CVarRef value) {
  1055. assert(m_dbh->driver);
  1056. #define PDO_LONG_PARAM_CHECK \
  1057. if (!value.isInteger() && !value.isString() && !value.isBoolean()) { \
  1058. pdo_raise_impl_error(m_dbh, nullptr, "HY000", \
  1059. "attribute value must be an integer"); \
  1060. PDO_HANDLE_DBH_ERR(m_dbh); \
  1061. return false; \
  1062. } \
  1063. switch (attribute) {
  1064. case PDO_ATTR_ERRMODE:
  1065. PDO_LONG_PARAM_CHECK;
  1066. switch (value.toInt64()) {
  1067. case PDO_ERRMODE_SILENT:
  1068. case PDO_ERRMODE_WARNING:
  1069. case PDO_ERRMODE_EXCEPTION:
  1070. m_dbh->error_mode = (PDOErrorMode)value.toInt64();
  1071. return true;
  1072. default:
  1073. pdo_raise_impl_error(m_dbh, nullptr, "HY000", "invalid error mode");
  1074. PDO_HANDLE_DBH_ERR(m_dbh);
  1075. return false;
  1076. }
  1077. return false;
  1078. case PDO_ATTR_CASE:
  1079. PDO_LONG_PARAM_CHECK;
  1080. switch (value.toInt64()) {
  1081. case PDO_CASE_NATURAL:
  1082. case PDO_CASE_UPPER:
  1083. case PDO_CASE_LOWER:
  1084. m_dbh->desired_case = (PDOCaseConversion)value.toInt64();
  1085. return true;
  1086. default:
  1087. pdo_raise_impl_error(m_dbh, nullptr, "HY000",
  1088. "invalid case folding mode");
  1089. PDO_HANDLE_DBH_ERR(m_dbh);
  1090. return false;
  1091. }
  1092. return false;
  1093. case PDO_ATTR_ORACLE_NULLS:
  1094. PDO_LONG_PARAM_CHECK;
  1095. m_dbh->oracle_nulls = value.toInt64();
  1096. return true;
  1097. case PDO_ATTR_DEFAULT_FETCH_MODE:
  1098. if (value.isArray()) {
  1099. if (value.toArray().exists(0)) {
  1100. Variant tmp = value[0];
  1101. if (tmp.isInteger() && ((tmp.toInt64() == PDO_FETCH_INTO ||
  1102. tmp.toInt64() == PDO_FETCH_CLASS))) {
  1103. pdo_raise_impl_error(m_dbh, nullptr, "HY000",
  1104. "FETCH_INTO and FETCH_CLASS are not yet "
  1105. "supported as default fetch modes");
  1106. return false;
  1107. }
  1108. }
  1109. } else {
  1110. PDO_LONG_PARAM_CHECK;
  1111. }
  1112. if (value.toInt64() == PDO_FETCH_USE_DEFAULT) {
  1113. pdo_raise_impl_error(m_dbh, nullptr, "HY000", "invalid fetch mode type");
  1114. return false;
  1115. }
  1116. m_dbh->default_fetch_type = (PDOFetchType)value.toInt64();
  1117. return true;
  1118. case PDO_ATTR_STRINGIFY_FETCHES:
  1119. PDO_LONG_PARAM_CHECK;
  1120. m_dbh->stringify = value.toInt64() ? 1 : 0;
  1121. return true;
  1122. case PDO_ATTR_STATEMENT_CLASS:
  1123. {
  1124. if (m_dbh->is_persistent) {
  1125. pdo_raise_impl_error(m_dbh, nullptr, "HY000",
  1126. "PDO::ATTR_STATEMENT_CLASS cannot be used "
  1127. "with persistent PDO instances");
  1128. PDO_HANDLE_DBH_ERR(m_dbh);
  1129. return false;
  1130. }
  1131. String clsname;
  1132. if (!valid_statement_class(m_dbh, value, clsname,
  1133. m_dbh->def_stmt_ctor_args)) {
  1134. return false;
  1135. }
  1136. m_dbh->def_stmt_clsname = clsname.c_str();
  1137. return true;
  1138. }
  1139. }
  1140. if (m_dbh->support(PDOConnection::MethodSetAttribute)) {
  1141. strcpy(m_dbh->error_code, PDO_ERR_NONE);
  1142. m_dbh->query_stmt = NULL;
  1143. if (m_dbh->setAttribute(attribute, value)) {
  1144. return true;
  1145. }
  1146. }
  1147. if (attribute == PDO_ATTR_AUTOCOMMIT) {
  1148. throw_pdo_exception(uninit_null(), uninit_null(),
  1149. "The auto-commit mode cannot be changed for this "
  1150. "driver");
  1151. } else if (!m_dbh->support(PDOConnection::MethodSetAttribute)) {
  1152. pdo_raise_impl_error(m_dbh, nullptr, "IM001",
  1153. "driver does not support setting attributes");
  1154. } else {
  1155. PDO_HANDLE_DBH_ERR(m_dbh);
  1156. }
  1157. return false;
  1158. }
  1159. Variant c_PDO::t_getattribute(int64_t attribute) {
  1160. assert(m_dbh->driver);
  1161. strcpy(m_dbh->error_code, PDO_ERR_NONE);
  1162. m_dbh->query_stmt = NULL;
  1163. /* handle generic PDO-level atributes */
  1164. switch (attribute) {
  1165. case PDO_ATTR_PERSISTENT:
  1166. return (bool)m_dbh->is_persistent;
  1167. case PDO_ATTR_CASE:
  1168. return (int64_t)m_dbh->desired_case;
  1169. case PDO_ATTR_ORACLE_NULLS:
  1170. return (int64_t)m_dbh->oracle_nulls;
  1171. case PDO_ATTR_ERRMODE:
  1172. return (int64_t)m_dbh->error_mode;
  1173. case PDO_ATTR_DRIVER_NAME:
  1174. return String(m_dbh->driver->getName());
  1175. case PDO_ATTR_STATEMENT_CLASS: {
  1176. Array ret;
  1177. ret.append(String(m_dbh->def_stmt_clsname));
  1178. if (!m_dbh->def_stmt_ctor_args.isNull()) {
  1179. ret.append(m_dbh->def_stmt_ctor_args);
  1180. }
  1181. return ret;
  1182. }
  1183. case PDO_ATTR_DEFAULT_FETCH_MODE:
  1184. return (int64_t)m_dbh->default_fetch_type;
  1185. }
  1186. if (!m_dbh->support(PDOConnection::MethodGetAttribute)) {
  1187. pdo_raise_impl_error(m_dbh, nullptr, "IM001",
  1188. "driver does not support getting attributes");
  1189. return false;
  1190. }
  1191. Variant ret;
  1192. switch (m_dbh->getAttribute(attribute, ret)) {
  1193. case -1:
  1194. PDO_HANDLE_DBH_ERR(m_dbh);
  1195. return false;
  1196. case 0:
  1197. pdo_raise_impl_error(m_dbh, nullptr, "IM001",
  1198. "driver does not support that attribute");
  1199. return false;
  1200. }
  1201. return ret;
  1202. }
  1203. Variant c_PDO::t_exec(const String& query) {
  1204. if (query.empty()) {
  1205. pdo_raise_impl_error(m_dbh, nullptr, "HY000",
  1206. "trying to execute an empty query");
  1207. return false;
  1208. }
  1209. assert(m_dbh->driver);
  1210. strcpy(m_dbh->error_code, PDO_ERR_NONE);
  1211. m_dbh->query_stmt = NULL;
  1212. int64_t ret = m_dbh->doer(query);
  1213. if (ret == -1) {
  1214. PDO_HANDLE_DBH_ERR(m_dbh);
  1215. return false;
  1216. }
  1217. return ret;
  1218. }
  1219. Variant c_PDO::t_lastinsertid(const String& seqname /* = null_string */) {
  1220. assert(m_dbh->driver);
  1221. strcpy(m_dbh->error_code, PDO_ERR_NONE);
  1222. m_dbh->query_stmt = NULL;
  1223. if (!m_dbh->support(PDOConnection::MethodLastId)) {
  1224. pdo_raise_impl_error(m_dbh, nullptr, "IM001",
  1225. "driver does not support lastInsertId()");
  1226. return false;
  1227. }
  1228. String ret = m_dbh->lastId(seqname.data());
  1229. if (ret.empty()) {
  1230. PDO_HANDLE_DBH_ERR(m_dbh);
  1231. return false;
  1232. }
  1233. return ret;
  1234. }
  1235. Variant c_PDO::t_errorcode() {
  1236. assert(m_dbh->driver);
  1237. if (m_dbh->query_stmt) {
  1238. return String(m_dbh->query_stmt->error_code, CopyString);
  1239. }
  1240. if (m_dbh->error_code[0] == '\0') {
  1241. return uninit_null();
  1242. }
  1243. /**
  1244. * Making sure that we fallback to the default implementation
  1245. * if the dbh->error_code is not null.
  1246. */
  1247. return String(m_dbh->error_code, CopyString);
  1248. }
  1249. Array c_PDO::t_errorinfo() {
  1250. assert(m_dbh->driver);
  1251. Array ret;
  1252. if (m_dbh->query_stmt) {
  1253. ret.append(String(m_dbh->query_stmt->error_code, CopyString));
  1254. } else {
  1255. ret.append(String(m_dbh->error_code, CopyString));
  1256. }
  1257. if (m_dbh->support(PDOConnection::MethodFetchErr)) {
  1258. m_dbh->fetchErr(m_dbh->query_stmt, ret);
  1259. }
  1260. /**
  1261. * In order to be consistent, we have to make sure we add the good amount
  1262. * of nulls depending on the current number of elements. We make a simple
  1263. * difference and add the needed elements
  1264. */
  1265. int error_count = ret.size();
  1266. int error_expected_count = 3;
  1267. if (error_expected_count > error_count) {
  1268. int error_count_diff = error_expected_count - error_count;
  1269. for (int i = 0; i < error_count_diff; i++) {
  1270. ret.append(uninit_null());
  1271. }
  1272. }
  1273. return ret;
  1274. }
  1275. Variant c_PDO::t_query(const String& sql) {
  1276. assert(m_dbh->driver);
  1277. strcpy(m_dbh->error_code, PDO_ERR_NONE);
  1278. m_dbh->query_stmt = NULL;
  1279. Object ret = pdo_stmt_instantiate(m_dbh, m_dbh->def_stmt_clsname,
  1280. m_dbh->def_stmt_ctor_args);
  1281. if (ret.isNull()) {
  1282. pdo_raise_impl_error
  1283. (m_dbh, nullptr, "HY000",
  1284. "failed to instantiate user supplied statement class");
  1285. return uninit_null();
  1286. }
  1287. c_PDOStatement *pdostmt = ret.getTyped<c_PDOStatement>();
  1288. if (m_dbh->preparer(sql, &pdostmt->m_stmt, Array())) {
  1289. PDOStatement *stmt = pdostmt->m_stmt.get();
  1290. assert(stmt);
  1291. /* unconditionally keep this for later reference */
  1292. stmt->query_string = sql;
  1293. stmt->default_fetch_type = m_dbh->default_fetch_type;
  1294. stmt->active_query_string = stmt->query_string;
  1295. stmt->dbh = m_dbh;
  1296. stmt->lazy_object_ref = uninit_null();
  1297. strcpy(stmt->error_code, PDO_ERR_NONE);
  1298. // when we add support for varargs here, we only need to set the stmt if
  1299. // the argument count is > 1
  1300. if (true || pdo_stmt_set_fetch_mode(stmt, 0, PDO_FETCH_BOTH, Array())) {
  1301. /* now execute the statement */
  1302. strcpy(stmt->error_code, PDO_ERR_NONE);
  1303. if (stmt->executer()) {
  1304. int ok = 1;
  1305. if (!stmt->executed) {
  1306. if (stmt->dbh->alloc_own_columns) {
  1307. ok = pdo_stmt_describe_columns(stmt);
  1308. }
  1309. stmt->executed = 1;
  1310. }
  1311. if (ok) {
  1312. pdo_stmt_construct(stmt, ret, m_dbh->def_stmt_clsname,
  1313. m_dbh->def_stmt_ctor_args);
  1314. return ret;
  1315. }
  1316. }
  1317. }
  1318. /* something broke */
  1319. m_dbh->query_stmt = stmt;
  1320. PDO_HANDLE_STMT_ERR(stmt);
  1321. } else {
  1322. PDO_HANDLE_DBH_ERR(m_dbh);
  1323. }
  1324. return false;
  1325. }
  1326. Variant c_PDO::t_quote(const String& str, int64_t paramtype /* = q_PDO$$PARAM_STR */) {
  1327. assert(m_dbh->driver);
  1328. strcpy(m_dbh->error_code, PDO_ERR_NONE);
  1329. m_dbh->query_stmt = NULL;
  1330. if (!m_dbh->support(PDOConnection::MethodQuoter)) {
  1331. pdo_raise_impl_error(m_dbh, nullptr, "IM001",
  1332. "driver does not support quoting");
  1333. return false;
  1334. }
  1335. String quoted;
  1336. if (m_dbh->quoter(str, quoted, (PDOParamType)paramtype)) {
  1337. return quoted;
  1338. }
  1339. PDO_HANDLE_DBH_ERR(m_dbh);
  1340. return false;
  1341. }
  1342. bool c_PDO::t_sqlitecreatefunction(const String& name,
  1343. CVarRef callback,
  1344. int64_t argcount /* = -1 */) {
  1345. raise_recoverable_error("PDO::sqliteCreateFunction not implemented");
  1346. return false;
  1347. }
  1348. bool c_PDO::t_sqlitecreateaggregate(const String& name,
  1349. CVarRef step, CVarRef final,
  1350. int64_t argcount /* = -1 */) {
  1351. raise_recoverable_error("PDO::sqliteCreateAggregate not implemented");
  1352. return false;
  1353. }
  1354. Variant c_PDO::t___wakeup() {
  1355. throw_pdo_exception(uninit_null(), uninit_null(),
  1356. "You cannot serialize or unserialize PDO instances");
  1357. return uninit_null();
  1358. }
  1359. Variant c_PDO::t___sleep() {
  1360. throw_pdo_exception(uninit_null(), uninit_null(),
  1361. "You cannot serialize or unserialize PDO instances");
  1362. return uninit_null();
  1363. }
  1364. Array c_PDO::ti_getavailabledrivers() {
  1365. return f_pdo_drivers();
  1366. }
  1367. ///////////////////////////////////////////////////////////////////////////////
  1368. static inline bool rewrite_name_to_position(sp_PDOStatement stmt,
  1369. PDOBoundParam *param) {
  1370. if (!stmt->bound_param_map.empty()) {
  1371. /* rewriting :name to ? style.
  1372. * We need to fixup the parameter numbers on the parameters.
  1373. * If we find that a given named parameter has been used twice,
  1374. * we will raise an error, as we can't be sure that it is safe
  1375. * to bind multiple parameters onto the same zval in the underlying
  1376. * driver */
  1377. if (stmt->named_rewrite_template) {
  1378. /* this is not an error here */
  1379. return true;
  1380. }
  1381. if (param->name.empty()) {
  1382. /* do the reverse; map the parameter number to the name */
  1383. if (stmt->bound_param_map.exists(param->paramno)) {
  1384. param->name = stmt->bound_param_map[param->paramno].toString();
  1385. return true;
  1386. }
  1387. pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
  1388. "parameter was not defined");
  1389. return false;
  1390. }
  1391. int position = 0;
  1392. for (ArrayIter iter(stmt->bound_param_map); iter; ++iter, ++position) {
  1393. if (iter.second().toString() == param->name) {
  1394. if (param->paramno >= 0) {
  1395. pdo_raise_impl_error
  1396. (stmt->dbh, stmt, "IM001",
  1397. "PDO refuses to handle repeating the same :named parameter "
  1398. "for multiple positions with this driver, as it might be "
  1399. "unsafe to do so. Consider using a separate name for each "
  1400. "parameter instead");
  1401. return true;
  1402. }
  1403. param->paramno = position;
  1404. return true;
  1405. }
  1406. }
  1407. pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
  1408. "parameter was not defined");
  1409. return false;
  1410. }
  1411. return true;
  1412. }
  1413. /* trigger callback hook for parameters */
  1414. static bool dispatch_param_event(sp_PDOStatement stmt,
  1415. PDOParamEvent event_type) {
  1416. if (!stmt->support(PDOStatement::MethodParamHook)) {
  1417. return true;
  1418. }
  1419. for (ArrayIter iter(stmt->bound_params); iter; ++iter) {
  1420. PDOBoundParam *param = iter.second().toResource().getTyped<PDOBoundParam>();
  1421. if (!stmt->paramHook(param, event_type)) {
  1422. return false;
  1423. }
  1424. }
  1425. for (ArrayIter iter(stmt->bound_columns); iter; ++iter) {
  1426. PDOBoundParam *param = iter.second().toResource().getTyped<PDOBoundParam>();
  1427. if (!stmt->paramHook(param, event_type)) {
  1428. return false;
  1429. }
  1430. }
  1431. return true;
  1432. }
  1433. static void get_lazy_object(sp_PDOStatement stmt, Variant &ret) {
  1434. if (stmt->lazy_object_ref.isNull()) {
  1435. stmt->lazy_object_ref = stmt;
  1436. }
  1437. ret = stmt->lazy_object_ref;
  1438. }
  1439. static bool really_register_bound_param(PDOBoundParam *param,
  1440. sp_PDOStatement stmt, bool is_param) {
  1441. Array &hash = is_param ? stmt->bound_params : stmt->bound_columns;
  1442. if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_STR &&
  1443. param->max_value_len <= 0 && !param->parameter.isNull()) {
  1444. param->parameter = param->parameter.toString();
  1445. } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_INT &&
  1446. param->parameter.isBoolean()) {
  1447. param->parameter = param->parameter.toInt64();
  1448. } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL &&
  1449. param->parameter.isInteger()) {
  1450. param->parameter = param->parameter.toBoolean();
  1451. }
  1452. param->stmt = stmt.get();
  1453. param->is_param = is_param;
  1454. if (!is_param && !param->name.empty() && !stmt->columns.empty()) {
  1455. /* try to map the name to the column */
  1456. for (int i = 0; i < stmt->column_count; i++) {
  1457. if (stmt->columns[i].toResource().getTyped<PDOColumn>()->name ==
  1458. param->name) {
  1459. param->paramno = i;
  1460. break;
  1461. }
  1462. }
  1463. /* if you prepare and then execute passing an array of params keyed by
  1464. names, then this will trigger, and we don't want that */
  1465. if (param->paramno == -1) {
  1466. raise_warning("Did not found column name '%s' in the defined columns;"
  1467. " it will not be bound", param->name.data());
  1468. }
  1469. }
  1470. if (is_param && !param->name.empty() && param->name[0] != ':') {
  1471. param->name = String(":") + param->name;
  1472. }
  1473. if (is_param && !rewrite_name_to_position(stmt, param)) {
  1474. param->name.reset();
  1475. return false;
  1476. }
  1477. /* ask the driver to perform any normalization it needs on the
  1478. * parameter name. Note that it is illegal for the driver to take
  1479. * a reference to param, as it resides in transient storage only
  1480. * at this time. */
  1481. if (stmt->support(PDOStatement::MethodParamHook)) {
  1482. if (!stmt->paramHook(param, PDO_PARAM_EVT_NORMALIZE)) {
  1483. param->name.reset();
  1484. return false;
  1485. }
  1486. }
  1487. /* delete any other parameter registered with this number.
  1488. * If the parameter is named, it will be removed and correctly
  1489. * disposed of by the hash_update call that follows */
  1490. if (param->paramno >= 0) {
  1491. hash.remove(param->paramno);
  1492. }
  1493. /* allocate storage for the parameter, keyed by its "canonical" name */
  1494. if (!param->name.empty()) {
  1495. hash.set(param->name, param);
  1496. } else {
  1497. hash.set(param->paramno, param);
  1498. }
  1499. /* tell the driver we just created a parameter */
  1500. if (stmt->support(PDOStatement::MethodParamHook)) {
  1501. if (!stmt->paramHook(param, PDO_PARAM_EVT_ALLOC)) {
  1502. /* undo storage allocation; the hash will free the parameter
  1503. * name if required */
  1504. if (!param->name.empty()) {
  1505. hash.remove(param->name);
  1506. } else {
  1507. hash.remove(param->paramno);
  1508. }
  1509. /* param->parameter is freed by hash dtor */
  1510. param->parameter.reset();
  1511. return false;
  1512. }
  1513. }
  1514. return true;
  1515. }
  1516. static inline void fetch_value(sp_PDOStatement stmt, Variant &dest, int colno,
  1517. int *type_override) {
  1518. PDOColumn *col = stmt->columns[colno].toResource().getTyped<PDOColumn>();
  1519. int type = PDO_PARAM_TYPE(col->param_type);
  1520. int new_type = type_override ? PDO_PARAM_TYPE(*type_override) : type;
  1521. stmt->getColumn(colno, dest);
  1522. if (type != new_type) {
  1523. switch (new_type) {
  1524. case PDO_PARAM_INT: dest = dest.toInt64(); break;
  1525. case PDO_PARAM_BOOL: dest = dest.toBoolean(); break;
  1526. case PDO_PARAM_STR: dest = dest.toString(); break;
  1527. case PDO_PARAM_NULL: dest = uninit_null(); break;
  1528. }
  1529. }
  1530. if (stmt->dbh->stringify && (dest.isInteger() || dest.isDouble())) {
  1531. dest = dest.toString();
  1532. }
  1533. if (dest.isNull() && stmt->dbh->oracle_nulls == PDO_NULL_TO_STRING) {
  1534. dest = empty_string;
  1535. }
  1536. }
  1537. static bool do_fetch_common(sp_PDOStatement stmt, PDOFetchOrientation ori,
  1538. long offset, bool do_bind) {
  1539. if (!stmt->executed) {
  1540. return false;
  1541. }
  1542. if (!dispatch_param_event(stmt, PDO_PARAM_EVT_FETCH_PRE)) {
  1543. return false;
  1544. }
  1545. if (!stmt->fetcher(ori, offset)) {
  1546. return false;
  1547. }
  1548. /* some drivers might need to describe the columns now */
  1549. if (stmt->columns.empty() && !pdo_stmt_describe_columns(stmt)) {
  1550. return false;
  1551. }
  1552. if (!dispatch_param_event(stmt, PDO_PARAM_EVT_FETCH_POST)) {
  1553. return false;
  1554. }
  1555. if (do_bind && !stmt->bound_columns.empty()) {
  1556. /* update those bound column variables now */
  1557. for (ArrayIter iter(stmt->bound_columns); iter; ++iter) {
  1558. PDOBoundParam *param =
  1559. iter.second().toResource().getTyped<PDOBoundParam>();
  1560. if (param->paramno >= 0) {
  1561. param->parameter.reset();
  1562. /* set new value */
  1563. fetch_value(stmt, param->parameter, param->paramno,
  1564. (int *)&param->param_type);
  1565. /* TODO: some smart thing that avoids duplicating the value in the
  1566. * general loop below. For now, if you're binding output columns,
  1567. * it's better to use LAZY or BOUND fetches if you want to shave
  1568. * off those cycles */
  1569. }
  1570. }
  1571. }
  1572. return true;
  1573. }
  1574. static bool do_fetch_func_prepare(sp_PDOStatement stmt) {
  1575. if (!f_is_callable(stmt->fetch.func)) {
  1576. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  1577. "user-supplied function must be a valid callback");
  1578. return false;
  1579. }
  1580. return true;
  1581. }
  1582. /* perform a fetch. If do_bind is true, update any bound columns.
  1583. * If return_value is not null, store values into it according to HOW. */
  1584. static bool do_fetch(sp_PDOStatement stmt, bool do_bind, Variant &ret,
  1585. PDOFetchType how, PDOFetchOrientation ori,
  1586. long offset, Variant *return_all) {
  1587. if (how == PDO_FETCH_USE_DEFAULT) {
  1588. how = stmt->default_fetch_type;
  1589. }
  1590. int flags = how & PDO_FETCH_FLAGS;
  1591. how = (PDOFetchType)(how & ~PDO_FETCH_FLAGS);
  1592. if (!do_fetch_common(stmt, ori, offset, do_bind)) {
  1593. return false;
  1594. }
  1595. if (how == PDO_FETCH_BOUND) {
  1596. ret = true;
  1597. return true;
  1598. }
  1599. int colno;
  1600. if ((flags & PDO_FETCH_GROUP) && stmt->fetch.column == -1) {
  1601. colno = 1;
  1602. } else {
  1603. colno = stmt->fetch.column;
  1604. }
  1605. if (how == PDO_FETCH_LAZY) {
  1606. get_lazy_object(stmt, ret);
  1607. return true;
  1608. }
  1609. String clsname, old_clsname;
  1610. Variant old_ctor_args;
  1611. ret = false;
  1612. int i = 0;
  1613. switch (how) {
  1614. case PDO_FETCH_USE_DEFAULT:
  1615. case PDO_FETCH_ASSOC:
  1616. case PDO_FETCH_BOTH:
  1617. case PDO_FETCH_NUM:
  1618. case PDO_FETCH_NAMED:
  1619. ret = Array::Create();
  1620. break;
  1621. case PDO_FETCH_KEY_PAIR:
  1622. if (stmt->column_count != 2) {
  1623. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  1624. "PDO::FETCH_KEY_PAIR fetch mode requires the "
  1625. "result set to contain extactly 2 columns.");
  1626. return false;
  1627. }
  1628. if (!return_all) {
  1629. ret = Array::Create();
  1630. }
  1631. break;
  1632. case PDO_FETCH_COLUMN:
  1633. if (colno >= 0 && colno < stmt->column_count) {
  1634. if (flags == PDO_FETCH_GROUP && stmt->fetch.column == -1) {
  1635. fetch_value(stmt, ret, 1, NULL);
  1636. } else if (flags == PDO_FETCH_GROUP && colno) {
  1637. fetch_value(stmt, ret, 0, NULL);
  1638. } else {
  1639. fetch_value(stmt, ret, colno, NULL);
  1640. }
  1641. if (!return_all) {
  1642. return true;
  1643. }
  1644. break;
  1645. } else {
  1646. pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Invalid column index");
  1647. }
  1648. return false;
  1649. case PDO_FETCH_OBJ:
  1650. ret = SystemLib::AllocStdClassObject();
  1651. break;
  1652. case PDO_FETCH_CLASS:
  1653. if (flags & PDO_FETCH_CLASSTYPE) {
  1654. old_clsname = stmt->fetch.clsname;
  1655. old_ctor_args = stmt->fetch.ctor_args;
  1656. Variant val;
  1657. fetch_value(stmt, val, i++, NULL);
  1658. if (!val.isNull()) {
  1659. if (!f_class_exists(val.toString())) {
  1660. stmt->fetch.clsname = "stdclass";
  1661. } else {
  1662. stmt->fetch.clsname = val.toString();
  1663. }
  1664. }
  1665. do_fetch_class_prepare(stmt);
  1666. }
  1667. clsname = stmt->fetch.clsname;
  1668. if (clsname.empty()) {
  1669. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  1670. "No fetch class specified");
  1671. return false;
  1672. }
  1673. if ((flags & PDO_FETCH_SERIALIZE) == 0) {
  1674. ret = create_object_only(clsname);
  1675. if (!do_fetch_class_prepare(stmt)) {
  1676. return false;
  1677. }
  1678. if (!stmt->fetch.constructor.empty() &&
  1679. (flags & PDO_FETCH_PROPS_LATE)) {
  1680. ret.asCObjRef().get()->o_invoke(stmt->fetch.constructor,
  1681. stmt->fetch.ctor_args.toArray());
  1682. ret.asCObjRef().get()->clearNoDestruct();
  1683. }
  1684. }
  1685. break;
  1686. case PDO_FETCH_INTO:
  1687. if (stmt->fetch.into.isNull()) {
  1688. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  1689. "No fetch-into object specified.");
  1690. return false;
  1691. }
  1692. ret = stmt->fetch.into;
  1693. if (ret.instanceof(SystemLib::s_stdclassClass)) {
  1694. how = PDO_FETCH_OBJ;
  1695. }
  1696. break;
  1697. case PDO_FETCH_FUNC:
  1698. if (stmt->fetch.func.empty()) {
  1699. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  1700. "No fetch function specified");
  1701. return false;
  1702. }
  1703. if (!do_fetch_func_prepare(stmt)) {
  1704. return false;
  1705. }
  1706. break;
  1707. default:
  1708. assert(false);
  1709. return false;
  1710. }
  1711. Variant grp_val;
  1712. if (return_all && how != PDO_FETCH_KEY_PAIR) {
  1713. if (flags == PDO_FETCH_GROUP && how == PDO_FETCH_COLUMN &&
  1714. stmt->fetch.column > 0) {
  1715. fetch_value(stmt, grp_val, colno, NULL);
  1716. } else {
  1717. fetch_value(stmt, grp_val, i, NULL);
  1718. }
  1719. grp_val = grp_val.toString();
  1720. if (how == PDO_FETCH_COLUMN) {
  1721. i = stmt->column_count; /* no more data to fetch */
  1722. } else {
  1723. i++;
  1724. }
  1725. }
  1726. for (int idx = 0; i < stmt->column_count; i++, idx++) {
  1727. String name = stmt->columns[i].toResource().getTyped<PDOColumn>()->name;
  1728. Variant val;
  1729. fetch_value(stmt, val, i, NULL);
  1730. switch (how) {
  1731. case PDO_FETCH_ASSOC:
  1732. ret.set(name, val);
  1733. break;
  1734. case PDO_FETCH_KEY_PAIR: {
  1735. Variant tmp;
  1736. fetch_value(stmt, tmp, ++i, NULL);
  1737. if (return_all) {
  1738. return_all->set(val, tmp);
  1739. } else {
  1740. ret.set(val, tmp);
  1741. }
  1742. return true;
  1743. }
  1744. case PDO_FETCH_USE_DEFAULT:
  1745. case PDO_FETCH_BOTH:
  1746. ret.set(name, val);
  1747. ret.append(val);
  1748. break;
  1749. case PDO_FETCH_NAMED: {
  1750. /* already have an item with this name? */
  1751. if (ret.toArray().exists(name)) {
  1752. Variant &curr_val = ret.lvalAt(name);
  1753. if (!curr_val.isArray()) {
  1754. Array arr = Array::Create();
  1755. arr.append(curr_val);
  1756. arr.append(val);
  1757. ret.set(name, arr);
  1758. } else {
  1759. curr_val.append(val);
  1760. }
  1761. } else {
  1762. ret.set(name, val);
  1763. }
  1764. break;
  1765. }
  1766. case PDO_FETCH_NUM:
  1767. ret.append(val);
  1768. break;
  1769. case PDO_FETCH_OBJ:
  1770. case PDO_FETCH_INTO:
  1771. ret.toObject()->o_set(name, val);
  1772. break;
  1773. case PDO_FETCH_CLASS:
  1774. if ((flags & PDO_FETCH_SERIALIZE) == 0 || idx) {
  1775. ret.toObject()->o_set(name, val);
  1776. } else {
  1777. #ifdef MBO_0
  1778. ret = unserialize_from_string(val);
  1779. if (same(ret, false)) {
  1780. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  1781. "cannot unserialize data");
  1782. return false;
  1783. }
  1784. #endif
  1785. // hzhao: not sure how we support class serialization
  1786. pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
  1787. "cannot unserialize class");
  1788. return false;
  1789. }
  1790. break;
  1791. case PDO_FETCH_FUNC:
  1792. stmt->fetch.values.set(idx, val);
  1793. break;
  1794. default:
  1795. pdo_raise_impl_error(stmt->dbh, stmt, "22003", "mode is out of range");
  1796. return false;
  1797. }
  1798. }
  1799. switch (how) {
  1800. case PDO_FETCH_CLASS:
  1801. if (!stmt->fetch.constructor.empty() &&
  1802. !(flags & (PDO_FETCH_PROPS_LATE | PDO_FETCH_SERIALIZE))) {
  1803. ret.toObject()->o_invoke(stmt->fetch.constructor,
  1804. stmt->fetch.ctor_args.toArray());
  1805. ret.toObject()->clearNoDestruct();
  1806. }
  1807. if (flags & PDO_FETCH_CLASSTYPE) {
  1808. stmt->fetch.clsname = old_clsname;
  1809. stmt->fetch.ctor_args = old_ctor_args;
  1810. }
  1811. break;
  1812. case PDO_FETCH_FUNC:
  1813. ret = vm_call_user_func(stmt->fetch.func,
  1814. stmt->fetch.values.toArray());
  1815. break;
  1816. default:
  1817. break;
  1818. }
  1819. if (return_all) {
  1820. if ((flags & PDO_FETCH_UNIQUE) == PDO_FETCH_UNIQUE) {
  1821. return_all->set(grp_val, ret);
  1822. } else {
  1823. return_all->lvalAt(grp_val).append(ret);
  1824. }
  1825. }
  1826. return true;
  1827. }
  1828. static int register_bound_param(CVarRef paramno, VRefParam param, int64_t type,
  1829. int64_t max_value_len, CVarRef driver_params,
  1830. sp_PDOStatement stmt, bool is_param) {
  1831. SmartResource<PDOBoundParam> p(new PDOBoundParam);
  1832. // need to make sure this is NULL, in case a fatal errors occurs before its set
  1833. // inside really_register_bound_param
  1834. p->stmt = NULL;
  1835. if (paramno.isNumeric()) {
  1836. p->paramno = paramno.toInt64();
  1837. } else {
  1838. p->paramno = -1;
  1839. p->name = paramno.toString();
  1840. }
  1841. p->parameter.setWithRef(param);
  1842. p->param_type = (PDOParamType)type;
  1843. p->max_value_len = max_value_len;
  1844. p->driver_params = driver_params;
  1845. if (p->paramno > 0) {
  1846. --p->paramno; /* make it zero-based internally */
  1847. } else if (p->name.empty()) {
  1848. pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
  1849. "Columns/Parameters are 1-based");
  1850. return false;
  1851. }
  1852. if (!really_register_bound_param(p.get(), stmt, is_param)) {
  1853. p->parameter.reset();
  1854. return false;
  1855. }
  1856. return true;
  1857. }
  1858. static bool generic_stmt_attr_get(sp_PDOStatement stmt, Variant &ret,
  1859. long attr) {
  1860. if (attr == PDO_ATTR_EMULATE_PREPARES) {
  1861. ret = (bool)(stmt->supports_placeholders == PDO_PLACEHOLDER_NONE);
  1862. return true;
  1863. }
  1864. return false;
  1865. }
  1866. ///////////////////////////////////////////////////////////////////////////////
  1867. // SQL parser
  1868. #define PDO_PARSER_TEXT 1
  1869. #define PDO_PARSER_BIND 2
  1870. #define PDO_PARSER_BIND_POS 3
  1871. #define PDO_PARSER_EOI 4
  1872. #define RET(i) {s->cur = cursor; return i; }
  1873. #define SKIP_ONE(i) {s->cur = s->tok + 1; return 1; }
  1874. #define YYCTYPE unsigned char
  1875. #define YYCURSOR cursor
  1876. #define YYLIMIT cursor
  1877. #define YYMARKER s->ptr
  1878. #define YYFILL(n)
  1879. typedef struct Scanner {
  1880. char *ptr, *cur, *tok;
  1881. } Scanner;
  1882. static int scan(Scanner *s) {
  1883. char *cursor = s->cur;
  1884. s->tok = cursor;
  1885. {
  1886. YYCTYPE yych;
  1887. if ((YYLIMIT - YYCURSOR) < 2) { YYFILL(2); }
  1888. yych = *YYCURSOR;
  1889. switch (yych) {
  1890. case 0x00: goto yy11;
  1891. case '"': goto yy2;
  1892. case '\'': goto yy4;
  1893. case ':': goto yy5;
  1894. case '?': goto yy6;
  1895. default: goto yy8;
  1896. }
  1897. yy2:
  1898. yych = *(YYMARKER = ++YYCURSOR);
  1899. if (yych >= 0x01) goto yy26;
  1900. yy3:
  1901. { SKIP_ONE(PDO_PARSER_TEXT); }
  1902. yy4:
  1903. yych = *(YYMARKER = ++YYCURSOR);
  1904. if (yych <= 0x00) goto yy3;
  1905. goto yy20;
  1906. yy5:
  1907. yych = *++YYCURSOR;
  1908. switch (yych) {
  1909. case '0':
  1910. case '1':
  1911. case '2':
  1912. case '3':
  1913. case '4':
  1914. case '5':
  1915. case '6':
  1916. case '7':
  1917. case '8':
  1918. case '9':
  1919. case 'A':
  1920. case 'B':
  1921. case 'C':
  1922. case 'D':
  1923. case 'E':
  1924. case 'F':
  1925. case 'G':
  1926. case 'H':
  1927. case 'I':
  1928. case 'J':
  1929. case 'K':
  1930. case 'L':
  1931. case 'M':
  1932. case 'N':
  1933. case 'O':
  1934. case 'P':
  1935. case 'Q':
  1936. case 'R':
  1937. case 'S':
  1938. case 'T':
  1939. case 'U':
  1940. case 'V':
  1941. case 'W':
  1942. case 'X':
  1943. case 'Y':
  1944. case 'Z':
  1945. case '_':
  1946. case 'a':
  1947. case 'b':
  1948. case 'c':
  1949. case 'd':
  1950. case 'e':
  1951. case 'f':
  1952. case 'g':
  1953. case 'h':
  1954. case 'i':
  1955. case 'j':
  1956. case 'k':
  1957. case 'l':
  1958. case 'm':
  1959. case 'n':
  1960. case 'o':
  1961. case 'p':
  1962. case 'q':
  1963. case 'r':
  1964. case 's':
  1965. case 't':
  1966. case 'u':
  1967. case 'v':
  1968. case 'w':
  1969. case 'x':
  1970. case 'y':
  1971. case 'z': goto yy16;
  1972. case ':':
  1973. case '?': goto yy13;
  1974. default: goto yy3;
  1975. }
  1976. yy6:
  1977. ++YYCURSOR;
  1978. switch ((yych = *YYCURSOR)) {
  1979. case ':':
  1980. case '?': goto yy13;
  1981. default: goto yy7;
  1982. }
  1983. yy7:
  1984. { RET(PDO_PARSER_BIND_POS); }
  1985. yy8:
  1986. ++YYCURSOR;
  1987. if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
  1988. yych = *YYCURSOR;
  1989. switch (yych) {
  1990. case 0x00:
  1991. case '"':
  1992. case '\'':
  1993. case ':':
  1994. case '?': goto yy10;
  1995. default: goto yy8;
  1996. }
  1997. yy10:
  1998. { RET(PDO_PARSER_TEXT); }
  1999. yy11:
  2000. ++YYCURSOR;
  2001. { RET(PDO_PARSER_EOI); }
  2002. yy13:
  2003. ++YYCURSOR;
  2004. if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
  2005. yych = *YYCURSOR;
  2006. switch (yych) {
  2007. case ':':
  2008. case '?': goto yy13;
  2009. default: goto yy15;
  2010. }
  2011. yy15:
  2012. { RET(PDO_PARSER_TEXT); }
  2013. yy16:
  2014. ++YYCURSOR;
  2015. if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
  2016. yych = *YYCURSOR;
  2017. switch (yych) {
  2018. case '0':
  2019. case '1':
  2020. case '2':
  2021. case '3':
  2022. case '4':
  2023. case '5':
  2024. case '6':
  2025. case '7':
  2026. case '8':
  2027. case '9':
  2028. case 'A':
  2029. case 'B':
  2030. case 'C':
  2031. case 'D':
  2032. case 'E':
  2033. case 'F':
  2034. case 'G':
  2035. case 'H':
  2036. case 'I':
  2037. case 'J':
  2038. case 'K':
  2039. case 'L':
  2040. case 'M':
  2041. case 'N':
  2042. case 'O':
  2043. case 'P':
  2044. case 'Q':
  2045. case 'R':
  2046. case 'S':
  2047. case 'T':
  2048. case 'U':
  2049. case 'V':
  2050. case 'W':
  2051. case 'X':
  2052. case 'Y':
  2053. case 'Z':
  2054. case '_':
  2055. case 'a':
  2056. case 'b':
  2057. case 'c':
  2058. case 'd':
  2059. case 'e':
  2060. case 'f':
  2061. case 'g':
  2062. case 'h':
  2063. case 'i':
  2064. case 'j':
  2065. case 'k':
  2066. case 'l':
  2067. case 'm':
  2068. case 'n':
  2069. case 'o':
  2070. case 'p':
  2071. case 'q':
  2072. case 'r':
  2073. case 's':
  2074. case 't':
  2075. case 'u':
  2076. case 'v':
  2077. case 'w':
  2078. case 'x':
  2079. case 'y':
  2080. case 'z': goto yy16;
  2081. default: goto yy18;
  2082. }
  2083. yy18:
  2084. { RET(PDO_PARSER_BIND); }
  2085. yy19:
  2086. ++YYCURSOR;
  2087. if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
  2088. yych = *YYCURSOR;
  2089. yy20:
  2090. switch (yych) {
  2091. case 0x00: goto yy21;
  2092. case '\'': goto yy23;
  2093. case '\\': goto yy22;
  2094. default: goto yy19;
  2095. }
  2096. yy21:
  2097. YYCURSOR = YYMARKER;
  2098. goto yy3;
  2099. yy22:
  2100. ++YYCURSOR;
  2101. if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
  2102. yych = *YYCURSOR;
  2103. if (yych <= 0x00) goto yy21;
  2104. goto yy19;
  2105. yy23:
  2106. ++YYCURSOR;
  2107. { RET(PDO_PARSER_TEXT); }
  2108. yy25:
  2109. ++YYCURSOR;
  2110. if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
  2111. yych = *YYCURSOR;
  2112. yy26:
  2113. switch (yych) {
  2114. case 0x00: goto yy21;
  2115. case '"': goto yy28;
  2116. case '\\': goto yy27;
  2117. default: goto yy25;
  2118. }
  2119. yy27:
  2120. ++YYCURSOR;
  2121. if (YYLIMIT <= YYCURSOR) { YYFILL(1); }
  2122. yych = *YYCURSOR;
  2123. if (yych <= 0x00) goto yy21;
  2124. goto yy25;
  2125. yy28:
  2126. ++YYCURSOR;
  2127. { RET(PDO_PARSER_TEXT); }
  2128. }
  2129. }
  2130. struct placeholder {
  2131. char *pos;
  2132. int len;
  2133. int bindno;
  2134. String quoted; /* quoted value */
  2135. struct placeholder *next;
  2136. };
  2137. int pdo_parse_params(PDOStatement *stmt, const String& in, String &out) {
  2138. Scanner s;
  2139. const char *ptr;
  2140. char *newbuffer;
  2141. int t;
  2142. int bindno = 0;
  2143. int ret = 0;
  2144. int newbuffer_len;
  2145. Array params;
  2146. PDOBoundParam *param;
  2147. int query_type = PDO_PLACEHOLDER_NONE;
  2148. struct placeholder *placeholders = NULL, *placetail = NULL, *plc = NULL;
  2149. s.cur = (char*)in.data();
  2150. /* phase 1: look for args */
  2151. while ((t = scan(&s)) != PDO_PARSER_EOI) {
  2152. if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS) {
  2153. if (t == PDO_PARSER_BIND) {
  2154. int len = s.cur - s.tok;
  2155. if ((in.data() < (s.cur - len)) && isalnum(*(s.cur - len - 1))) {
  2156. continue;
  2157. }
  2158. query_type |= PDO_PLACEHOLDER_NAMED;
  2159. } else {
  2160. query_type |= PDO_PLACEHOLDER_POSITIONAL;
  2161. }
  2162. plc = (placeholder*)malloc(sizeof(*plc));
  2163. memset(plc, 0, sizeof(*plc));
  2164. plc->next = NULL;
  2165. plc->pos = s.tok;
  2166. plc->len = s.cur - s.tok;
  2167. plc->bindno = bindno++;
  2168. if (placetail) {
  2169. placetail->next = plc;
  2170. } else {
  2171. placeholders = plc;
  2172. }
  2173. placetail = plc;
  2174. }
  2175. }
  2176. if (bindno == 0) {
  2177. /* nothing to do; good! */
  2178. return 0;
  2179. }
  2180. /* did the query make sense to me? */
  2181. if (query_type == (PDO_PLACEHOLDER_NAMED|PDO_PLACEHOLDER_POSITIONAL)) {
  2182. /* they mixed both types; punt */
  2183. pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
  2184. "mixed named and positional parameters");
  2185. ret = -1;
  2186. goto clean_up;
  2187. }
  2188. if ((int)stmt->supports_placeholders == query_type &&
  2189. !stmt->named_rewrite_template) {
  2190. /* query matches native syntax */
  2191. ret = 0;
  2192. goto clean_up;
  2193. }
  2194. if (stmt->named_rewrite_template) {
  2195. /* magic/hack.
  2196. * We we pretend that the query was positional even if
  2197. * it was named so that we fall into the
  2198. * named rewrite case below. Not too pretty,
  2199. * but it works. */
  2200. query_type = PDO_PLACEHOLDER_POSITIONAL;
  2201. }
  2202. params = stmt->bound_params;
  2203. /* Do we have placeholders but no bound params */
  2204. if (bindno && params.empty() &&
  2205. stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
  2206. pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "no parameters were bound");
  2207. ret = -1;
  2208. goto clean_up;
  2209. }
  2210. if (!params.empty() && bindno != params.size() &&
  2211. stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
  2212. /* extra bit of validation for instances when same params are bound
  2213. more then once */
  2214. if (query_type != PDO_PLACEHOLDER_POSITIONAL && bindno > params.size()) {
  2215. int ok = 1;
  2216. for (plc = placeholders; plc; plc = plc->next) {
  2217. if (!params.exists(String(plc->pos, plc->len, CopyString))) {
  2218. ok = 0;
  2219. break;
  2220. }
  2221. }
  2222. if (ok) {
  2223. goto safe;
  2224. }
  2225. }
  2226. pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
  2227. "number of bound variables does not match number "
  2228. "of tokens");
  2229. ret = -1;
  2230. goto clean_up;
  2231. }
  2232. safe:
  2233. /* what are we going to do ? */
  2234. if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
  2235. /* query generation */
  2236. newbuffer_len = in.size();
  2237. /* let's quote all the values */
  2238. for (plc = placeholders; plc; plc = plc->next) {
  2239. Variant vparam;
  2240. if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
  2241. vparam = params[plc->bindno];
  2242. } else {
  2243. vparam = params[String(plc->pos, plc->len, CopyString)];
  2244. }
  2245. if (vparam.isNull()) {
  2246. /* parameter was not defined */
  2247. ret = -1;
  2248. pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
  2249. "parameter was not defined");
  2250. goto clean_up;
  2251. }
  2252. param = vparam.toResource().getTyped<PDOBoundParam>();
  2253. if (stmt->dbh->support(PDOConnection::MethodQuoter)) {
  2254. if (param->param_type == PDO_PARAM_LOB &&
  2255. param->parameter.isResource()) {
  2256. Variant buf = f_stream_get_contents(param->parameter.toResource());
  2257. if (!same(buf, false)) {
  2258. if (!stmt->dbh->quoter(buf.toString(), plc->quoted,
  2259. param->param_type)) {
  2260. /* bork */
  2261. ret = -1;
  2262. strcpy(stmt->error_code, stmt->dbh->error_code);
  2263. goto clean_up;
  2264. }
  2265. } else {
  2266. pdo_raise_impl_error(stmt->dbh, stmt, "HY105",
  2267. "Expected a stream resource");
  2268. ret = -1;
  2269. goto clean_up;
  2270. }
  2271. } else {
  2272. switch (param->parameter.getType()) {
  2273. case KindOfUninit:
  2274. case KindOfNull:
  2275. plc->quoted = "NULL";
  2276. break;
  2277. case KindOfInt64:
  2278. case KindOfDouble:
  2279. plc->quoted = param->parameter.toString();
  2280. break;
  2281. case KindOfBoolean:
  2282. param->parameter = param->parameter.toInt64();
  2283. default:
  2284. if (!stmt->dbh->quoter(param->parameter.toString(), plc->quoted,
  2285. param->param_type)) {
  2286. /* bork */
  2287. ret = -1;
  2288. strcpy(stmt->error_code, stmt->dbh->error_code);
  2289. goto clean_up;
  2290. }
  2291. }
  2292. }
  2293. } else {
  2294. plc->quoted = param->parameter;
  2295. }
  2296. newbuffer_len += plc->quoted.size();
  2297. }
  2298. rewrite:
  2299. /* allocate output buffer */
  2300. out = String(newbuffer_len, ReserveString);
  2301. newbuffer = out.bufferSlice().ptr;
  2302. out.setSize(newbuffer_len);
  2303. /* and build the query */
  2304. plc = placeholders;
  2305. ptr = in.data();
  2306. do {
  2307. t = plc->pos - ptr;
  2308. if (t) {
  2309. memcpy(newbuffer, ptr, t);
  2310. newbuffer += t;
  2311. }
  2312. memcpy(newbuffer, plc->quoted.data(), plc->quoted.size());
  2313. newbuffer += plc->quoted.size();
  2314. ptr = plc->pos + plc->len;
  2315. plc = plc->next;
  2316. } while (plc);
  2317. t = (in.data() + in.size()) - ptr;
  2318. if (t) {
  2319. memcpy(newbuffer, ptr, t);
  2320. newbuffer += t;
  2321. }
  2322. *newbuffer = '\0';
  2323. out = out.substr(0, newbuffer - out.data());
  2324. ret = 1;
  2325. goto clean_up;
  2326. } else if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
  2327. /* rewrite ? to :pdoX */
  2328. StringBuffer idxbuf;
  2329. const char *tmpl = stmt->named_rewrite_template ?
  2330. stmt->named_rewrite_template : ":pdo%d";
  2331. int bind_no = 1;
  2332. newbuffer_len = in.size();
  2333. for (plc = placeholders; plc; plc = plc->next) {
  2334. int skip_map = 0;
  2335. String name(plc->pos, plc->len, CopyString);
  2336. /* check if bound parameter is already available */
  2337. if (!strcmp(name.c_str(), "?") ||
  2338. !stmt->bound_param_map.exists(name)) {
  2339. idxbuf.printf(tmpl, bind_no++);
  2340. } else {
  2341. idxbuf.clear();
  2342. idxbuf.append(stmt->bound_param_map[name].toString());
  2343. skip_map = 1;
  2344. }
  2345. plc->quoted = idxbuf.detach();
  2346. newbuffer_len += plc->quoted.size();
  2347. if (!skip_map && stmt->named_rewrite_template) {
  2348. /* create a mapping */
  2349. stmt->bound_param_map.set(name, plc->quoted);
  2350. }
  2351. /* map number to name */
  2352. stmt->bound_param_map.set(plc->bindno, plc->quoted);
  2353. }
  2354. goto rewrite;
  2355. } else {
  2356. /* rewrite :name to ? */
  2357. newbuffer_len = in.size();
  2358. for (plc = placeholders; plc; plc = plc->next) {
  2359. String name(plc->pos, plc->len, CopyString);
  2360. stmt->bound_param_map.set(plc->bindno, name);
  2361. plc->quoted = "?";
  2362. }
  2363. goto rewrite;
  2364. }
  2365. clean_up:
  2366. while (placeholders) {
  2367. plc = placeholders;
  2368. placeholders = plc->next;
  2369. free(plc);
  2370. }
  2371. return ret;
  2372. }
  2373. ///////////////////////////////////////////////////////////////////////////////
  2374. // PDOStatement
  2375. c_PDOStatement::c_PDOStatement(Class* cb) :
  2376. ExtObjectData(cb), m_rowIndex(-1) {
  2377. }
  2378. c_PDOStatement::~c_PDOStatement() {
  2379. m_stmt.reset();
  2380. m_row.reset();
  2381. }
  2382. void c_PDOStatement::sweep() {
  2383. // No resources allocated outside HHVM's control
  2384. }
  2385. void c_PDOStatement::t___construct() {
  2386. raise_error("You should not create a PDOStatement manually");
  2387. }
  2388. Variant c_PDOStatement::t_execute(CArrRef params /* = null_array */) {
  2389. strcpy(m_stmt->error_code, PDO_ERR_NONE);
  2390. if (!params.empty()) {
  2391. m_stmt->bound_params.reset();
  2392. for (ArrayIter iter(params); iter; ++iter) {
  2393. SmartResource<PDOBoundParam> param(new PDOBoundParam);
  2394. param->param_type = PDO_PARAM_STR;
  2395. param->parameter = iter.second();
  2396. param->stmt = NULL;
  2397. if (iter.first().isString()) {
  2398. param->name = iter.first();
  2399. param->paramno = -1;
  2400. } else {
  2401. int64_t num_index = iter.first().toInt64();
  2402. /* we're okay to be zero based here */
  2403. if (num_index < 0) {
  2404. pdo_raise_impl_error(m_stmt->dbh, m_stmt, "HY093", NULL);
  2405. return false;
  2406. }
  2407. param->paramno = num_index;
  2408. }
  2409. if (!really_register_bound_param(param.get(), m_stmt, true)) {
  2410. return false;
  2411. }
  2412. }
  2413. }
  2414. int ret = 1;
  2415. if (PDO_PLACEHOLDER_NONE == m_stmt->supports_placeholders) {
  2416. /* handle the emulated parameter binding, m_stmt->active_query_string
  2417. holds the query with binds expanded and quoted. */
  2418. ret = pdo_parse_params(m_stmt.get(), m_stmt->query_string,
  2419. m_stmt->active_query_string);
  2420. if (ret == 0) { /* no changes were made */
  2421. m_stmt->active_query_string = m_stmt->query_string;
  2422. } else if (ret == -1) {
  2423. /* something broke */
  2424. PDO_HANDLE_STMT_ERR(m_stmt);
  2425. return false;
  2426. }
  2427. } else if (!dispatch_param_event(m_stmt, PDO_PARAM_EVT_EXEC_PRE)) {
  2428. PDO_HANDLE_STMT_ERR(m_stmt);
  2429. return false;
  2430. }
  2431. if (m_stmt->executer()) {
  2432. m_stmt->active_query_string.reset();
  2433. if (!m_stmt->executed) {
  2434. /* this is the first execute */
  2435. if (m_stmt->dbh->alloc_own_columns && m_stmt->columns.empty()) {
  2436. /* for "big boy" drivers, we need to allocate memory to fetch
  2437. * the results into, so lets do that now */
  2438. ret = pdo_stmt_describe_columns(m_stmt);
  2439. }
  2440. m_stmt->executed = 1;
  2441. }
  2442. if (ret && !dispatch_param_event(m_stmt, PDO_PARAM_EVT_EXEC_POST)) {
  2443. return false;
  2444. }
  2445. return (bool)ret;
  2446. }
  2447. m_stmt->active_query_string.reset();
  2448. PDO_HANDLE_STMT_ERR(m_stmt);
  2449. return false;
  2450. }
  2451. Variant c_PDOStatement::t_fetch(int64_t how /* = 0 */,
  2452. int64_t orientation /* = q_PDO$$FETCH_ORI_NEXT */,
  2453. int64_t offset /* = 0 */) {
  2454. strcpy(m_stmt->error_code, PDO_ERR_NONE);
  2455. if (!pdo_stmt_verify_mode(m_stmt, how, false)) {
  2456. return false;
  2457. }
  2458. Variant ret;
  2459. if (!do_fetch(m_stmt, true, ret, (PDOFetchType)how,
  2460. (PDOFetchOrientation)orientation, offset, NULL)) {
  2461. PDO_HANDLE_STMT_ERR(m_stmt);
  2462. return false;
  2463. }
  2464. return ret;
  2465. }
  2466. Variant c_PDOStatement::t_fetchobject(const String& class_name /* = null_string */,
  2467. CVarRef ctor_args /* = null */) {
  2468. strcpy(m_stmt->error_code, PDO_ERR_NONE);
  2469. if (!pdo_stmt_verify_mode(m_stmt, PDO_FETCH_CLASS, false)) {
  2470. return false;
  2471. }
  2472. String old_clsname = m_stmt->fetch.clsname;
  2473. Variant old_ctor_args = m_stmt->fetch.ctor_args;
  2474. bool error = false;
  2475. m_stmt->fetch.clsname = class_name;
  2476. if (class_name.isNull()) {
  2477. m_stmt->fetch.clsname = "stdclass";
  2478. }
  2479. if (!f_class_exists(m_stmt->fetch.clsname)) {
  2480. pdo_raise_impl_error(m_stmt->dbh, m_stmt, "HY000",
  2481. "Could not find user-supplied class");
  2482. error = true;
  2483. }
  2484. if (!ctor_args.isNull() && !ctor_args.isArray()) {
  2485. pdo_raise_impl_error(m_stmt->dbh, m_stmt, "HY000",
  2486. "ctor_args must be either NULL or an array");
  2487. error = true;
  2488. }
  2489. m_stmt->fetch.ctor_args = ctor_args;
  2490. Variant ret;
  2491. if (!error && !do_fetch(m_stmt, true, ret, PDO_FETCH_CLASS,
  2492. PDO_FETCH_ORI_NEXT, 0, NULL)) {
  2493. error = true;
  2494. }
  2495. if (error) {
  2496. PDO_HANDLE_STMT_ERR(m_stmt);
  2497. }
  2498. m_stmt->fetch.clsname = old_clsname;
  2499. m_stmt->fetch.ctor_args = old_ctor_args;
  2500. if (error) {
  2501. return false;
  2502. }
  2503. return ret;
  2504. }
  2505. Variant c_PDOStatement::t_fetchcolumn(int64_t column_numner /* = 0 */) {
  2506. strcpy(m_stmt->error_code, PDO_ERR_NONE);
  2507. if (!do_fetch_common(m_stmt, PDO_FETCH_ORI_NEXT, 0, true)) {
  2508. PDO_HANDLE_STMT_ERR(m_stmt);
  2509. return false;
  2510. }
  2511. Variant ret;
  2512. fetch_value(m_stmt, ret, column_numner, NULL);
  2513. return ret;
  2514. }
  2515. Variant c_PDOStatement::t_fetchall(int64_t how /* = 0 */,
  2516. CVarRef class_name /* = null */,
  2517. CVarRef ctor_args /* = null */) {
  2518. if (!pdo_stmt_verify_mode(m_stmt, how, true)) {
  2519. return false;
  2520. }
  2521. String old_clsname = m_stmt->fetch.clsname;
  2522. Variant old_ctor_args = m_stmt->fetch.ctor_args;
  2523. int error = 0;
  2524. switch (how & ~PDO_FETCH_FLAGS) {
  2525. case PDO_FETCH_CLASS:
  2526. m_stmt->fetch.clsname = class_name;
  2527. if (class_name.isNull()) {
  2528. m_stmt->fetch.clsname = "stdclass";
  2529. }
  2530. if (!f_class_exists(m_stmt->fetch.clsname)) {
  2531. pdo_raise_impl_error(m_stmt->dbh, m_stmt, "HY000",
  2532. "Could not find user-supplied class");
  2533. error = 1;
  2534. }
  2535. if (!ctor_args.isNull() && !ctor_args.isArray()) {
  2536. pdo_raise_impl_error(m_stmt->dbh, m_stmt, "HY000",
  2537. "ctor_args must be either NULL or an array");
  2538. error = 1;
  2539. break;
  2540. }
  2541. m_stmt->fetch.ctor_args = ctor_args;
  2542. if (!error) {
  2543. do_fetch_class_prepare(m_stmt);
  2544. }
  2545. break;
  2546. case PDO_FETCH_FUNC:
  2547. if (!f_function_exists(class_name.toString())) {
  2548. pdo_raise_impl_error(m_stmt->dbh, m_stmt, "HY000",
  2549. "no fetch function specified");
  2550. error = 1;
  2551. } else {
  2552. m_stmt->fetch.func = class_name;
  2553. do_fetch_func_prepare(m_stmt);
  2554. }
  2555. break;
  2556. case PDO_FETCH_COLUMN:
  2557. if (class_name.isNull()) {
  2558. m_stmt->fetch.column = how & PDO_FETCH_GROUP ? -1 : 0;
  2559. } else {
  2560. m_stmt->fetch.column = class_name.toInt64();
  2561. }
  2562. if (!ctor_args.isNull()) {
  2563. pdo_raise_impl_error(m_stmt->dbh, m_stmt, "HY000",
  2564. "Third parameter not allowed for "
  2565. "PDO::FETCH_COLUMN");
  2566. error = 1;
  2567. }
  2568. break;
  2569. }
  2570. int flags = how & PDO_FETCH_FLAGS;
  2571. if ((how & ~PDO_FETCH_FLAGS) == PDO_FETCH_USE_DEFAULT) {
  2572. flags |= m_stmt->default_fetch_type & PDO_FETCH_FLAGS;
  2573. how |= m_stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
  2574. }
  2575. Variant *return_all = NULL;
  2576. Variant return_value;
  2577. Variant data;
  2578. if (!error) {
  2579. strcpy(m_stmt->error_code, PDO_ERR_NONE);
  2580. if ((how & PDO_FETCH_GROUP) || how == PDO_FETCH_KEY_PAIR ||
  2581. (how == PDO_FETCH_USE_DEFAULT &&
  2582. m_stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)) {
  2583. return_value = Array::Create();
  2584. return_all = &return_value;
  2585. }
  2586. if (!do_fetch(m_stmt, true, data, (PDOFetchType)(how | flags),
  2587. PDO_FETCH_ORI_NEXT, 0, return_all)) {
  2588. error = 2;
  2589. }
  2590. }
  2591. if (!error) {
  2592. if ((how & PDO_FETCH_GROUP)) {
  2593. do {
  2594. data.reset();
  2595. } while (do_fetch(m_stmt, true, data, (PDOFetchType)(how | flags),
  2596. PDO_FETCH_ORI_NEXT, 0, return_all));
  2597. } else if (how == PDO_FETCH_KEY_PAIR ||
  2598. (how == PDO_FETCH_USE_DEFAULT &&
  2599. m_stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)) {
  2600. while (do_fetch(m_stmt, true, data, (PDOFetchType)(how | flags),
  2601. PDO_FETCH_ORI_NEXT, 0, return_all));
  2602. } else {
  2603. return_value = Array::Create();
  2604. do {
  2605. return_value.append(data);
  2606. data.reset();
  2607. } while (do_fetch(m_stmt, true, data, (PDOFetchType)(how | flags),
  2608. PDO_FETCH_ORI_NEXT, 0, NULL));
  2609. }
  2610. }
  2611. m_stmt->fetch.clsname = old_clsname;
  2612. m_stmt->fetch.ctor_args = old_ctor_args;
  2613. if (error) {
  2614. PDO_HANDLE_STMT_ERR(m_stmt);
  2615. if (error != 2) {
  2616. return false;
  2617. }
  2618. /* on no results, return an empty array */
  2619. if (!return_value.isArray()) {
  2620. return_value = Array::Create();
  2621. }
  2622. }
  2623. return return_value;
  2624. }
  2625. bool c_PDOStatement::t_bindvalue(CVarRef paramno, CVarRef param,
  2626. int64_t type /* = q_PDO$$PARAM_STR */) {
  2627. return register_bound_param(paramno, param, type, 0, uninit_null(), m_stmt, true);
  2628. }
  2629. bool c_PDOStatement::t_bindparam(CVarRef paramno, VRefParam param,
  2630. int64_t type /* = q_PDO$$PARAM_STR */,
  2631. int64_t max_value_len /* = 0 */,
  2632. CVarRef driver_params /*= null */) {
  2633. return register_bound_param(paramno, ref(param), type, max_value_len,
  2634. driver_params, m_stmt, true);
  2635. }
  2636. bool c_PDOStatement::t_bindcolumn(CVarRef paramno, VRefParam param,
  2637. int64_t type /* = q_PDO$$PARAM_STR */,
  2638. int64_t max_value_len /* = 0 */,
  2639. CVarRef driver_params /* = null */) {
  2640. return register_bound_param(paramno, ref(param), type, max_value_len,
  2641. driver_params, m_stmt, false);
  2642. }
  2643. int64_t c_PDOStatement::t_rowcount() {
  2644. return m_stmt->row_count;
  2645. }
  2646. Variant c_PDOStatement::t_errorcode() {
  2647. if (m_stmt->error_code[0] == '\0') {
  2648. return uninit_null();
  2649. }
  2650. return String(m_stmt->error_code, CopyString);
  2651. }
  2652. Array c_PDOStatement::t_errorinfo() {
  2653. Array ret;
  2654. ret.append(String(m_stmt->error_code, CopyString));
  2655. if (m_stmt->dbh->support(PDOConnection::MethodFetchErr)) {
  2656. m_stmt->dbh->fetchErr(m_stmt.get(), ret);
  2657. }
  2658. int error_count = ret.size();
  2659. int error_expected_count = 3;
  2660. if (error_expected_count > error_count) {
  2661. int error_count_diff = error_expected_count - error_count;
  2662. for (int i = 0; i < error_count_diff; i++) {
  2663. ret.append(uninit_null());
  2664. }
  2665. }
  2666. return ret;
  2667. }
  2668. Variant c_PDOStatement::t_setattribute(int64_t attribute, CVarRef value) {
  2669. if (!m_stmt->support(PDOStatement::MethodSetAttribute)) {
  2670. pdo_raise_impl_error(m_stmt->dbh, m_stmt, "IM001",
  2671. "This driver doesn't support setting attributes");
  2672. return false;
  2673. }
  2674. strcpy(m_stmt->error_code, PDO_ERR_NONE);
  2675. if (m_stmt->setAttribute(attribute, value)) {
  2676. return true;
  2677. }
  2678. PDO_HANDLE_STMT_ERR(m_stmt);
  2679. return false;
  2680. }
  2681. Variant c_PDOStatement::t_getattribute(int64_t attribute) {
  2682. Variant ret;
  2683. if (!m_stmt->support(PDOStatement::MethodGetAttribute)) {
  2684. if (!generic_stmt_attr_get(m_stmt, ret, attribute)) {
  2685. pdo_raise_impl_error(m_stmt->dbh, m_stmt, "IM001",
  2686. "This driver doesn't support getting attributes");
  2687. return false;
  2688. }
  2689. return ret;
  2690. }
  2691. strcpy(m_stmt->error_code, PDO_ERR_NONE);
  2692. switch (m_stmt->getAttribute(attribute, ret)) {
  2693. case -1:
  2694. PDO_HANDLE_STMT_ERR(m_stmt);
  2695. return false;
  2696. case 0:
  2697. if (!generic_stmt_attr_get(m_stmt, ret, attribute)) {
  2698. /* XXX: should do something better here */
  2699. pdo_raise_impl_error(m_stmt->dbh, m_stmt, "IM001",
  2700. "driver doesn't support getting that attribute");
  2701. return false;
  2702. }
  2703. break;
  2704. default:
  2705. break;
  2706. }
  2707. return ret;
  2708. }
  2709. int64_t c_PDOStatement::t_columncount() {
  2710. return m_stmt->column_count;
  2711. }
  2712. const StaticString
  2713. s_name("name"),
  2714. s_len("len"),
  2715. s_precision("precision"),
  2716. s_pdo_type("pdo_type");
  2717. Variant c_PDOStatement::t_getcolumnmeta(int64_t column) {
  2718. if (column < 0) {
  2719. pdo_raise_impl_error(m_stmt->dbh, m_stmt, "42P10",
  2720. "column number must be non-negative");
  2721. return false;
  2722. }
  2723. if (!m_stmt->support(PDOStatement::MethodGetColumnMeta)) {
  2724. pdo_raise_impl_error(m_stmt->dbh, m_stmt, "IM001",
  2725. "driver doesn't support meta data");
  2726. return false;
  2727. }
  2728. strcpy(m_stmt->error_code, PDO_ERR_NONE);
  2729. Array ret;
  2730. if (!m_stmt->getColumnMeta(column, ret)) {
  2731. PDO_HANDLE_STMT_ERR(m_stmt);
  2732. return false;
  2733. }
  2734. /* add stock items */
  2735. PDOColumn *col = m_stmt->columns[column].toResource().getTyped<PDOColumn>();
  2736. ret.set(s_name, col->name);
  2737. ret.set(s_len, (int64_t)col->maxlen); /* FIXME: unsigned ? */
  2738. ret.set(s_precision, (int64_t)col->precision);
  2739. if (col->param_type != PDO_PARAM_ZVAL) {
  2740. // if param_type is PDO_PARAM_ZVAL the driver has to provide correct data
  2741. ret.set(s_pdo_type, (int64_t)col->param_type);
  2742. }
  2743. return ret;
  2744. }
  2745. bool c_PDOStatement::t_setfetchmode(int _argc, int64_t mode,
  2746. CArrRef _argv /* = null_array */) {
  2747. return pdo_stmt_set_fetch_mode(m_stmt, _argc, mode, _argv);
  2748. }
  2749. bool c_PDOStatement::t_nextrowset() {
  2750. if (!m_stmt->support(PDOStatement::MethodNextRowset)) {
  2751. pdo_raise_impl_error(m_stmt->dbh, m_stmt, "IM001",
  2752. "driver does not support multiple rowsets");
  2753. return false;
  2754. }
  2755. strcpy(m_stmt->error_code, PDO_ERR_NONE);
  2756. /* un-describe */
  2757. if (!m_stmt->columns.empty()) {
  2758. m_stmt->columns.clear();
  2759. m_stmt->column_count = 0;
  2760. }
  2761. if (!m_stmt->nextRowset()) {
  2762. PDO_HANDLE_STMT_ERR(m_stmt);
  2763. return false;
  2764. }
  2765. pdo_stmt_describe_columns(m_stmt);
  2766. return true;
  2767. }
  2768. bool c_PDOStatement::t_closecursor() {
  2769. if (!m_stmt->support(PDOStatement::MethodCursorCloser)) {
  2770. /* emulate it by fetching and discarding rows */
  2771. do {
  2772. while (m_stmt->fetcher(PDO_FETCH_ORI_NEXT, 0));
  2773. if (!t_nextrowset()) {
  2774. break;
  2775. }
  2776. } while (true);
  2777. m_stmt->executed = 0;
  2778. return true;
  2779. }
  2780. strcpy(m_stmt->error_code, PDO_ERR_NONE);
  2781. if (!m_stmt->cursorCloser()) {
  2782. PDO_HANDLE_STMT_ERR(m_stmt);
  2783. return false;
  2784. }
  2785. m_stmt->executed = 0;
  2786. return true;
  2787. }
  2788. Variant c_PDOStatement::t_debugdumpparams() {
  2789. Variant fobj = File::Open("php://output", "w");
  2790. if (same(fobj, false)) {
  2791. return false;
  2792. }
  2793. File *f = fobj.toResource().getTyped<File>();
  2794. Array params;
  2795. params.append(m_stmt->query_string.size());
  2796. params.append(m_stmt->query_string.size());
  2797. params.append(m_stmt->query_string.data());
  2798. f->printf("SQL: [%d] %.*s\n", params);
  2799. f->printf("Params: %d\n", make_packed_array(m_stmt->bound_params.size()));
  2800. for (ArrayIter iter(m_stmt->bound_params); iter; ++iter) {
  2801. if (iter.first().isString()) {
  2802. String key = iter.first().toString();
  2803. params = make_packed_array(key.size(), key.size(), key.data());
  2804. f->printf("Key: Name: [%d] %.*s\n", params);
  2805. } else {
  2806. f->printf("Key: Position #%ld:\n",
  2807. make_packed_array(iter.first().toInt64()));
  2808. }
  2809. PDOBoundParam *param = iter.second().toResource().getTyped<PDOBoundParam>();
  2810. params.clear();
  2811. params.append(param->paramno);
  2812. params.append(param->name.size());
  2813. params.append(param->name.size());
  2814. params.append(param->name.data());
  2815. params.append(param->is_param);
  2816. params.append(param->param_type);
  2817. f->printf("paramno=%d\nname=[%d] \"%.*s\"\nis_param=%d\nparam_type=%d\n",
  2818. params);
  2819. }
  2820. return true;
  2821. }
  2822. Variant c_PDOStatement::t_current() {
  2823. return m_row;
  2824. }
  2825. Variant c_PDOStatement::t_key() {
  2826. return m_rowIndex;
  2827. }
  2828. Variant c_PDOStatement::t_next() {
  2829. m_row = t_fetch(PDO_FETCH_USE_DEFAULT);
  2830. if (same(m_row, false)) {
  2831. m_rowIndex = -1;
  2832. } else {
  2833. ++m_rowIndex;
  2834. }
  2835. return uninit_null();
  2836. }
  2837. Variant c_PDOStatement::t_rewind() {
  2838. m_rowIndex = -1;
  2839. t_next();
  2840. return uninit_null();
  2841. }
  2842. Variant c_PDOStatement::t_valid() {
  2843. return m_rowIndex >= 0;
  2844. }
  2845. Variant c_PDOStatement::t___wakeup() {
  2846. throw_pdo_exception(uninit_null(), uninit_null(), "You cannot serialize or unserialize "
  2847. "PDOStatement instances");
  2848. return uninit_null();
  2849. }
  2850. Variant c_PDOStatement::t___sleep() {
  2851. throw_pdo_exception(uninit_null(), uninit_null(), "You cannot serialize or unserialize "
  2852. "PDOStatement instances");
  2853. return uninit_null();
  2854. }
  2855. ///////////////////////////////////////////////////////////////////////////////
  2856. }