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