PageRenderTime 23ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/php-src/ext/pdo_odbc/odbc_driver.c

https://gitlab.com/0072016/appengine-php
C | 493 lines | 366 code | 78 blank | 49 comment | 108 complexity | 0be63f55b3c0c7445fee434f51a37551 MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2013 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.0 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_0.txt. |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Author: Wez Furlong <wez@php.net> |
  16. +----------------------------------------------------------------------+
  17. */
  18. /* $Id$ */
  19. #ifdef HAVE_CONFIG_H
  20. #include "config.h"
  21. #endif
  22. #include "php.h"
  23. #include "php_ini.h"
  24. #include "ext/standard/info.h"
  25. #include "pdo/php_pdo.h"
  26. #include "pdo/php_pdo_driver.h"
  27. #include "php_pdo_odbc.h"
  28. #include "php_pdo_odbc_int.h"
  29. #include "zend_exceptions.h"
  30. static int pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC)
  31. {
  32. pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
  33. pdo_odbc_errinfo *einfo = &H->einfo;
  34. pdo_odbc_stmt *S = NULL;
  35. char *message = NULL;
  36. if (stmt) {
  37. S = (pdo_odbc_stmt*)stmt->driver_data;
  38. einfo = &S->einfo;
  39. }
  40. spprintf(&message, 0, "%s (%s[%ld] at %s:%d)",
  41. einfo->last_err_msg,
  42. einfo->what, einfo->last_error,
  43. einfo->file, einfo->line);
  44. add_next_index_long(info, einfo->last_error);
  45. add_next_index_string(info, message, 0);
  46. add_next_index_string(info, einfo->last_state, 1);
  47. return 1;
  48. }
  49. void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line TSRMLS_DC) /* {{{ */
  50. {
  51. SQLRETURN rc;
  52. SQLSMALLINT errmsgsize = 0;
  53. SQLHANDLE eh;
  54. SQLSMALLINT htype, recno = 1;
  55. pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data;
  56. pdo_odbc_errinfo *einfo = &H->einfo;
  57. pdo_odbc_stmt *S = NULL;
  58. pdo_error_type *pdo_err = &dbh->error_code;
  59. if (stmt) {
  60. S = (pdo_odbc_stmt*)stmt->driver_data;
  61. einfo = &S->einfo;
  62. pdo_err = &stmt->error_code;
  63. }
  64. if (statement == SQL_NULL_HSTMT && S) {
  65. statement = S->stmt;
  66. }
  67. if (statement) {
  68. htype = SQL_HANDLE_STMT;
  69. eh = statement;
  70. } else if (H->dbc) {
  71. htype = SQL_HANDLE_DBC;
  72. eh = H->dbc;
  73. } else {
  74. htype = SQL_HANDLE_ENV;
  75. eh = H->env;
  76. }
  77. rc = SQLGetDiagRec(htype, eh, recno++, einfo->last_state, &einfo->last_error,
  78. einfo->last_err_msg, sizeof(einfo->last_err_msg)-1, &errmsgsize);
  79. if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
  80. errmsgsize = 0;
  81. }
  82. einfo->last_err_msg[errmsgsize] = '\0';
  83. einfo->file = file;
  84. einfo->line = line;
  85. einfo->what = what;
  86. strcpy(*pdo_err, einfo->last_state);
  87. /* printf("@@ SQLSTATE[%s] %s\n", *pdo_err, einfo->last_err_msg); */
  88. if (!dbh->methods) {
  89. zend_throw_exception_ex(php_pdo_get_exception(), einfo->last_error TSRMLS_CC, "SQLSTATE[%s] %s: %d %s",
  90. *pdo_err, what, einfo->last_error, einfo->last_err_msg);
  91. }
  92. /* just like a cursor, once you start pulling, you need to keep
  93. * going until the end; SQL Server (at least) will mess with the
  94. * actual cursor state if you don't finish retrieving all the
  95. * diagnostic records (which can be generated by PRINT statements
  96. * in the query, for instance). */
  97. while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
  98. char discard_state[6];
  99. char discard_buf[1024];
  100. SQLINTEGER code;
  101. rc = SQLGetDiagRec(htype, eh, recno++, discard_state, &code,
  102. discard_buf, sizeof(discard_buf)-1, &errmsgsize);
  103. }
  104. }
  105. /* }}} */
  106. static int odbc_handle_closer(pdo_dbh_t *dbh TSRMLS_DC)
  107. {
  108. pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data;
  109. if (H->dbc != SQL_NULL_HANDLE) {
  110. SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);
  111. SQLDisconnect(H->dbc);
  112. SQLFreeHandle(SQL_HANDLE_DBC, H->dbc);
  113. H->dbc = NULL;
  114. }
  115. SQLFreeHandle(SQL_HANDLE_ENV, H->env);
  116. H->env = NULL;
  117. pefree(H, dbh->is_persistent);
  118. dbh->driver_data = NULL;
  119. return 0;
  120. }
  121. static int odbc_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC)
  122. {
  123. RETCODE rc;
  124. pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
  125. pdo_odbc_stmt *S = ecalloc(1, sizeof(*S));
  126. enum pdo_cursor_type cursor_type = PDO_CURSOR_FWDONLY;
  127. int ret;
  128. char *nsql = NULL;
  129. int nsql_len = 0;
  130. S->H = H;
  131. S->assume_utf8 = H->assume_utf8;
  132. /* before we prepare, we need to peek at the query; if it uses named parameters,
  133. * we want PDO to rewrite them for us */
  134. stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL;
  135. ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len TSRMLS_CC);
  136. if (ret == 1) {
  137. /* query was re-written */
  138. sql = nsql;
  139. } else if (ret == -1) {
  140. /* couldn't grok it */
  141. strcpy(dbh->error_code, stmt->error_code);
  142. efree(S);
  143. return 0;
  144. }
  145. rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &S->stmt);
  146. if (rc == SQL_INVALID_HANDLE || rc == SQL_ERROR) {
  147. efree(S);
  148. if (nsql) {
  149. efree(nsql);
  150. }
  151. pdo_odbc_drv_error("SQLAllocStmt");
  152. return 0;
  153. }
  154. cursor_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY TSRMLS_CC);
  155. if (cursor_type != PDO_CURSOR_FWDONLY) {
  156. rc = SQLSetStmtAttr(S->stmt, SQL_ATTR_CURSOR_SCROLLABLE, (void*)SQL_SCROLLABLE, 0);
  157. if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
  158. pdo_odbc_stmt_error("SQLSetStmtAttr: SQL_ATTR_CURSOR_SCROLLABLE");
  159. SQLFreeHandle(SQL_HANDLE_STMT, S->stmt);
  160. if (nsql) {
  161. efree(nsql);
  162. }
  163. return 0;
  164. }
  165. }
  166. rc = SQLPrepare(S->stmt, (char*)sql, SQL_NTS);
  167. if (nsql) {
  168. efree(nsql);
  169. }
  170. stmt->driver_data = S;
  171. stmt->methods = &odbc_stmt_methods;
  172. if (rc != SQL_SUCCESS) {
  173. pdo_odbc_stmt_error("SQLPrepare");
  174. if (rc != SQL_SUCCESS_WITH_INFO) {
  175. /* clone error information into the db handle */
  176. strcpy(H->einfo.last_err_msg, S->einfo.last_err_msg);
  177. H->einfo.file = S->einfo.file;
  178. H->einfo.line = S->einfo.line;
  179. H->einfo.what = S->einfo.what;
  180. strcpy(dbh->error_code, stmt->error_code);
  181. }
  182. }
  183. if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
  184. return 0;
  185. }
  186. return 1;
  187. }
  188. static long odbc_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC)
  189. {
  190. pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
  191. RETCODE rc;
  192. SQLLEN row_count = -1;
  193. PDO_ODBC_HSTMT stmt;
  194. rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt);
  195. if (rc != SQL_SUCCESS) {
  196. pdo_odbc_drv_error("SQLAllocHandle: STMT");
  197. return -1;
  198. }
  199. rc = SQLExecDirect(stmt, (char *)sql, sql_len);
  200. if (rc == SQL_NO_DATA) {
  201. /* If SQLExecDirect executes a searched update or delete statement that
  202. * does not affect any rows at the data source, the call to
  203. * SQLExecDirect returns SQL_NO_DATA. */
  204. row_count = 0;
  205. goto out;
  206. }
  207. if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
  208. pdo_odbc_doer_error("SQLExecDirect");
  209. goto out;
  210. }
  211. rc = SQLRowCount(stmt, &row_count);
  212. if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
  213. pdo_odbc_doer_error("SQLRowCount");
  214. goto out;
  215. }
  216. if (row_count == -1) {
  217. row_count = 0;
  218. }
  219. out:
  220. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  221. return row_count;
  222. }
  223. static int odbc_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, int unquotedlen, char **quoted, int *quotedlen, enum pdo_param_type param_type TSRMLS_DC)
  224. {
  225. pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
  226. /* TODO: figure it out */
  227. return 0;
  228. }
  229. static int odbc_handle_begin(pdo_dbh_t *dbh TSRMLS_DC)
  230. {
  231. if (dbh->auto_commit) {
  232. /* we need to disable auto-commit now, to be able to initiate a transaction */
  233. RETCODE rc;
  234. pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
  235. rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER);
  236. if (rc != SQL_SUCCESS) {
  237. pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = OFF");
  238. return 0;
  239. }
  240. }
  241. return 1;
  242. }
  243. static int odbc_handle_commit(pdo_dbh_t *dbh TSRMLS_DC)
  244. {
  245. pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
  246. RETCODE rc;
  247. rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_COMMIT);
  248. if (rc != SQL_SUCCESS) {
  249. pdo_odbc_drv_error("SQLEndTran: Commit");
  250. if (rc != SQL_SUCCESS_WITH_INFO) {
  251. return 0;
  252. }
  253. }
  254. if (dbh->auto_commit) {
  255. /* turn auto-commit back on again */
  256. rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);
  257. if (rc != SQL_SUCCESS) {
  258. pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON");
  259. return 0;
  260. }
  261. }
  262. return 1;
  263. }
  264. static int odbc_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC)
  265. {
  266. pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
  267. RETCODE rc;
  268. rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);
  269. if (rc != SQL_SUCCESS) {
  270. pdo_odbc_drv_error("SQLEndTran: Rollback");
  271. if (rc != SQL_SUCCESS_WITH_INFO) {
  272. return 0;
  273. }
  274. }
  275. if (dbh->auto_commit && H->dbc) {
  276. /* turn auto-commit back on again */
  277. rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);
  278. if (rc != SQL_SUCCESS) {
  279. pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON");
  280. return 0;
  281. }
  282. }
  283. return 1;
  284. }
  285. static int odbc_handle_set_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
  286. {
  287. pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
  288. switch (attr) {
  289. case PDO_ODBC_ATTR_ASSUME_UTF8:
  290. H->assume_utf8 = zval_is_true(val);
  291. return 1;
  292. default:
  293. strcpy(H->einfo.last_err_msg, "Unknown Attribute");
  294. H->einfo.what = "setAttribute";
  295. strcpy(H->einfo.last_state, "IM001");
  296. return -1;
  297. }
  298. }
  299. static int odbc_handle_get_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
  300. {
  301. pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
  302. switch (attr) {
  303. case PDO_ATTR_CLIENT_VERSION:
  304. ZVAL_STRING(val, "ODBC-" PDO_ODBC_TYPE, 1);
  305. return 1;
  306. case PDO_ATTR_SERVER_VERSION:
  307. case PDO_ATTR_PREFETCH:
  308. case PDO_ATTR_TIMEOUT:
  309. case PDO_ATTR_SERVER_INFO:
  310. case PDO_ATTR_CONNECTION_STATUS:
  311. break;
  312. case PDO_ODBC_ATTR_ASSUME_UTF8:
  313. ZVAL_BOOL(val, H->assume_utf8 ? 1 : 0);
  314. return 1;
  315. }
  316. return 0;
  317. }
  318. static struct pdo_dbh_methods odbc_methods = {
  319. odbc_handle_closer,
  320. odbc_handle_preparer,
  321. odbc_handle_doer,
  322. odbc_handle_quoter,
  323. odbc_handle_begin,
  324. odbc_handle_commit,
  325. odbc_handle_rollback,
  326. odbc_handle_set_attr,
  327. NULL, /* last id */
  328. pdo_odbc_fetch_error_func,
  329. odbc_handle_get_attr, /* get attr */
  330. NULL, /* check_liveness */
  331. };
  332. static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */
  333. {
  334. pdo_odbc_db_handle *H;
  335. RETCODE rc;
  336. int use_direct = 0;
  337. SQLUINTEGER cursor_lib;
  338. H = pecalloc(1, sizeof(*H), dbh->is_persistent);
  339. dbh->driver_data = H;
  340. SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H->env);
  341. rc = SQLSetEnvAttr(H->env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
  342. if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
  343. pdo_odbc_drv_error("SQLSetEnvAttr: ODBC3");
  344. goto fail;
  345. }
  346. #ifdef SQL_ATTR_CONNECTION_POOLING
  347. if (pdo_odbc_pool_on != SQL_CP_OFF) {
  348. rc = SQLSetEnvAttr(H->env, SQL_ATTR_CP_MATCH, (void*)pdo_odbc_pool_mode, 0);
  349. if (rc != SQL_SUCCESS) {
  350. pdo_odbc_drv_error("SQLSetEnvAttr: SQL_ATTR_CP_MATCH");
  351. goto fail;
  352. }
  353. }
  354. #endif
  355. rc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc);
  356. if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
  357. pdo_odbc_drv_error("SQLAllocHandle (DBC)");
  358. goto fail;
  359. }
  360. rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT,
  361. (SQLPOINTER)(dbh->auto_commit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF), SQL_IS_INTEGER);
  362. if (rc != SQL_SUCCESS) {
  363. pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT");
  364. goto fail;
  365. }
  366. /* set up the cursor library, if needed, or if configured explicitly */
  367. cursor_lib = pdo_attr_lval(driver_options, PDO_ODBC_ATTR_USE_CURSOR_LIBRARY, SQL_CUR_USE_IF_NEEDED TSRMLS_CC);
  368. rc = SQLSetConnectAttr(H->dbc, SQL_ODBC_CURSORS, (void*)cursor_lib, SQL_IS_INTEGER);
  369. if (rc != SQL_SUCCESS && cursor_lib != SQL_CUR_USE_IF_NEEDED) {
  370. pdo_odbc_drv_error("SQLSetConnectAttr SQL_ODBC_CURSORS");
  371. goto fail;
  372. }
  373. if (strchr(dbh->data_source, ';')) {
  374. char dsnbuf[1024];
  375. short dsnbuflen;
  376. use_direct = 1;
  377. /* Force UID and PWD to be set in the DSN */
  378. if (dbh->username && *dbh->username && !strstr(dbh->data_source, "uid")
  379. && !strstr(dbh->data_source, "UID")) {
  380. char *dsn;
  381. spprintf(&dsn, 0, "%s;UID=%s;PWD=%s", dbh->data_source, dbh->username, dbh->password);
  382. pefree((char*)dbh->data_source, dbh->is_persistent);
  383. dbh->data_source = dsn;
  384. }
  385. rc = SQLDriverConnect(H->dbc, NULL, (char*)dbh->data_source, strlen(dbh->data_source),
  386. dsnbuf, sizeof(dsnbuf)-1, &dsnbuflen, SQL_DRIVER_NOPROMPT);
  387. }
  388. if (!use_direct) {
  389. rc = SQLConnect(H->dbc, (char*)dbh->data_source, SQL_NTS, dbh->username, SQL_NTS, dbh->password, SQL_NTS);
  390. }
  391. if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
  392. pdo_odbc_drv_error(use_direct ? "SQLDriverConnect" : "SQLConnect");
  393. goto fail;
  394. }
  395. /* TODO: if we want to play nicely, we should check to see if the driver really supports ODBC v3 or not */
  396. dbh->methods = &odbc_methods;
  397. dbh->alloc_own_columns = 1;
  398. return 1;
  399. fail:
  400. dbh->methods = &odbc_methods;
  401. return 0;
  402. }
  403. /* }}} */
  404. pdo_driver_t pdo_odbc_driver = {
  405. PDO_DRIVER_HEADER(odbc),
  406. pdo_odbc_handle_factory
  407. };
  408. /*
  409. * Local variables:
  410. * tab-width: 4
  411. * c-basic-offset: 4
  412. * End:
  413. * vim600: noet sw=4 ts=4 fdm=marker
  414. * vim<600: noet sw=4 ts=4
  415. */