PageRenderTime 57ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/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

Large files files are truncated, but you can click here to view the full file

  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. /* unconditiona…

Large files files are truncated, but you can click here to view the full file