odbc_driver.c 13 KB

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