odbc_driver.c 14 KB

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