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
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
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 /* this file actually lives in main/ */
27 #include "php_odbc_utils.h"
28 #include "php_pdo_odbc.h"
29 #include "php_pdo_odbc_int.h"
30 #include "zend_exceptions.h"
31
pdo_odbc_fetch_error_func(pdo_dbh_t * dbh,pdo_stmt_t * stmt,zval * info)32 static void pdo_odbc_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info)
33 {
34 pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
35 pdo_odbc_errinfo *einfo = &H->einfo;
36 pdo_odbc_stmt *S = NULL;
37 zend_string *message = NULL;
38
39 if (stmt) {
40 S = (pdo_odbc_stmt*)stmt->driver_data;
41 einfo = &S->einfo;
42 }
43
44 message = strpprintf(0, "%s (%s[%ld] at %s:%d)",
45 einfo->last_err_msg,
46 einfo->what, (long) einfo->last_error,
47 einfo->file, einfo->line);
48
49 add_next_index_long(info, einfo->last_error);
50 add_next_index_str(info, message);
51 add_next_index_string(info, einfo->last_state);
52 }
53
54
pdo_odbc_error(pdo_dbh_t * dbh,pdo_stmt_t * stmt,PDO_ODBC_HSTMT statement,char * what,const char * file,int line)55 void pdo_odbc_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, PDO_ODBC_HSTMT statement, char *what, const char *file, int line) /* {{{ */
56 {
57 SQLRETURN rc;
58 SQLSMALLINT errmsgsize = 0;
59 SQLHANDLE eh;
60 SQLSMALLINT htype, recno = 1;
61 pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data;
62 pdo_odbc_errinfo *einfo = &H->einfo;
63 pdo_odbc_stmt *S = NULL;
64 pdo_error_type *pdo_err = &dbh->error_code;
65
66 if (stmt) {
67 S = (pdo_odbc_stmt*)stmt->driver_data;
68
69 einfo = &S->einfo;
70 pdo_err = &stmt->error_code;
71 }
72
73 if (statement == SQL_NULL_HSTMT && S) {
74 statement = S->stmt;
75 }
76
77 if (statement) {
78 htype = SQL_HANDLE_STMT;
79 eh = statement;
80 } else if (H->dbc) {
81 htype = SQL_HANDLE_DBC;
82 eh = H->dbc;
83 } else {
84 htype = SQL_HANDLE_ENV;
85 eh = H->env;
86 }
87
88 rc = SQLGetDiagRec(htype, eh, recno++, (SQLCHAR *) einfo->last_state, &einfo->last_error,
89 (SQLCHAR *) einfo->last_err_msg, sizeof(einfo->last_err_msg)-1, &errmsgsize);
90
91 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
92 errmsgsize = 0;
93 }
94
95 einfo->last_err_msg[errmsgsize] = '\0';
96 einfo->file = file;
97 einfo->line = line;
98 einfo->what = what;
99
100 strcpy(*pdo_err, einfo->last_state);
101 /* printf("@@ SQLSTATE[%s] %s\n", *pdo_err, einfo->last_err_msg); */
102 if (!dbh->methods) {
103 zend_throw_exception_ex(php_pdo_get_exception(), einfo->last_error, "SQLSTATE[%s] %s: %d %s",
104 *pdo_err, what, einfo->last_error, einfo->last_err_msg);
105 }
106
107 /* just like a cursor, once you start pulling, you need to keep
108 * going until the end; SQL Server (at least) will mess with the
109 * actual cursor state if you don't finish retrieving all the
110 * diagnostic records (which can be generated by PRINT statements
111 * in the query, for instance). */
112 while (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
113 SQLCHAR discard_state[6];
114 SQLCHAR discard_buf[1024];
115 SQLINTEGER code;
116 rc = SQLGetDiagRec(htype, eh, recno++, discard_state, &code,
117 discard_buf, sizeof(discard_buf)-1, &errmsgsize);
118 }
119
120 }
121 /* }}} */
122
odbc_handle_closer(pdo_dbh_t * dbh)123 static void odbc_handle_closer(pdo_dbh_t *dbh)
124 {
125 pdo_odbc_db_handle *H = (pdo_odbc_db_handle*)dbh->driver_data;
126
127 if (H->dbc != SQL_NULL_HANDLE) {
128 SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);
129 SQLDisconnect(H->dbc);
130 SQLFreeHandle(SQL_HANDLE_DBC, H->dbc);
131 H->dbc = NULL;
132 }
133 SQLFreeHandle(SQL_HANDLE_ENV, H->env);
134 H->env = NULL;
135 pefree(H, dbh->is_persistent);
136 dbh->driver_data = NULL;
137 }
138
odbc_handle_preparer(pdo_dbh_t * dbh,zend_string * sql,pdo_stmt_t * stmt,zval * driver_options)139 static bool odbc_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options)
140 {
141 RETCODE rc;
142 pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
143 pdo_odbc_stmt *S = ecalloc(1, sizeof(*S));
144 enum pdo_cursor_type cursor_type = PDO_CURSOR_FWDONLY;
145 int ret;
146 zend_string *nsql = NULL;
147
148 S->H = H;
149 S->assume_utf8 = H->assume_utf8;
150
151 /* before we prepare, we need to peek at the query; if it uses named parameters,
152 * we want PDO to rewrite them for us */
153 stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL;
154 ret = pdo_parse_params(stmt, sql, &nsql);
155
156 if (ret == 1) {
157 /* query was re-written */
158 sql = nsql;
159 } else if (ret == -1) {
160 /* couldn't grok it */
161 strcpy(dbh->error_code, stmt->error_code);
162 efree(S);
163 return false;
164 }
165
166 rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &S->stmt);
167
168 if (rc == SQL_INVALID_HANDLE || rc == SQL_ERROR) {
169 efree(S);
170 if (nsql) {
171 zend_string_release(nsql);
172 }
173 pdo_odbc_drv_error("SQLAllocStmt");
174 return false;
175 }
176
177 stmt->driver_data = S;
178
179 cursor_type = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY);
180 if (cursor_type != PDO_CURSOR_FWDONLY) {
181 rc = SQLSetStmtAttr(S->stmt, SQL_ATTR_CURSOR_SCROLLABLE, (void*)SQL_SCROLLABLE, 0);
182 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
183 pdo_odbc_stmt_error("SQLSetStmtAttr: SQL_ATTR_CURSOR_SCROLLABLE");
184 SQLFreeHandle(SQL_HANDLE_STMT, S->stmt);
185 if (nsql) {
186 zend_string_release(nsql);
187 }
188 return false;
189 }
190 }
191
192 rc = SQLPrepare(S->stmt, (SQLCHAR *) ZSTR_VAL(sql), SQL_NTS);
193 if (nsql) {
194 zend_string_release(nsql);
195 }
196
197 stmt->methods = &odbc_stmt_methods;
198
199 if (rc != SQL_SUCCESS) {
200 pdo_odbc_stmt_error("SQLPrepare");
201 if (rc != SQL_SUCCESS_WITH_INFO) {
202 /* clone error information into the db handle */
203 strcpy(H->einfo.last_err_msg, S->einfo.last_err_msg);
204 H->einfo.file = S->einfo.file;
205 H->einfo.line = S->einfo.line;
206 H->einfo.what = S->einfo.what;
207 strcpy(dbh->error_code, stmt->error_code);
208 }
209 }
210
211 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
212 return false;
213 }
214 return true;
215 }
216
odbc_handle_doer(pdo_dbh_t * dbh,const zend_string * sql)217 static zend_long odbc_handle_doer(pdo_dbh_t *dbh, const zend_string *sql)
218 {
219 pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
220 RETCODE rc;
221 SQLLEN row_count = -1;
222 PDO_ODBC_HSTMT stmt;
223
224 rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt);
225 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
226 pdo_odbc_drv_error("SQLAllocHandle: STMT");
227 return -1;
228 }
229
230 rc = SQLExecDirect(stmt, (SQLCHAR *) ZSTR_VAL(sql), ZSTR_LEN(sql));
231
232 if (rc == SQL_NO_DATA) {
233 /* If SQLExecDirect executes a searched update or delete statement that
234 * does not affect any rows at the data source, the call to
235 * SQLExecDirect returns SQL_NO_DATA. */
236 row_count = 0;
237 goto out;
238 }
239
240 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
241 pdo_odbc_doer_error("SQLExecDirect");
242 goto out;
243 }
244
245 rc = SQLRowCount(stmt, &row_count);
246 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
247 pdo_odbc_doer_error("SQLRowCount");
248 goto out;
249 }
250 if (row_count == -1) {
251 row_count = 0;
252 }
253 out:
254 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
255 return row_count;
256 }
257
258 /* TODO: Do ODBC quoter
259 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 )
260 {
261 // pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
262 // TODO: figure it out
263 return 0;
264 }
265 */
266
odbc_handle_begin(pdo_dbh_t * dbh)267 static bool odbc_handle_begin(pdo_dbh_t *dbh)
268 {
269 if (dbh->auto_commit) {
270 /* we need to disable auto-commit now, to be able to initiate a transaction */
271 RETCODE rc;
272 pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
273
274 rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER);
275 if (rc != SQL_SUCCESS) {
276 pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = OFF");
277 return false;
278 }
279 }
280 return true;
281 }
282
odbc_handle_commit(pdo_dbh_t * dbh)283 static bool odbc_handle_commit(pdo_dbh_t *dbh)
284 {
285 pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
286 RETCODE rc;
287
288 rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_COMMIT);
289
290 if (rc != SQL_SUCCESS) {
291 pdo_odbc_drv_error("SQLEndTran: Commit");
292
293 if (rc != SQL_SUCCESS_WITH_INFO) {
294 return false;
295 }
296 }
297
298 if (dbh->auto_commit) {
299 /* turn auto-commit back on again */
300 rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);
301 if (rc != SQL_SUCCESS) {
302 pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON");
303 return false;
304 }
305 }
306 return true;
307 }
308
odbc_handle_rollback(pdo_dbh_t * dbh)309 static bool odbc_handle_rollback(pdo_dbh_t *dbh)
310 {
311 pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
312 RETCODE rc;
313
314 rc = SQLEndTran(SQL_HANDLE_DBC, H->dbc, SQL_ROLLBACK);
315
316 if (rc != SQL_SUCCESS) {
317 pdo_odbc_drv_error("SQLEndTran: Rollback");
318
319 if (rc != SQL_SUCCESS_WITH_INFO) {
320 return false;
321 }
322 }
323 if (dbh->auto_commit && H->dbc) {
324 /* turn auto-commit back on again */
325 rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_IS_INTEGER);
326 if (rc != SQL_SUCCESS) {
327 pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT = ON");
328 return false;
329 }
330 }
331
332 return true;
333 }
334
odbc_handle_set_attr(pdo_dbh_t * dbh,zend_long attr,zval * val)335 static bool odbc_handle_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)
336 {
337 pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
338 bool bval;
339
340 switch (attr) {
341 case PDO_ODBC_ATTR_ASSUME_UTF8:
342 if (!pdo_get_bool_param(&bval, val)) {
343 return false;
344 }
345 H->assume_utf8 = bval;
346 return true;
347 case PDO_ATTR_AUTOCOMMIT:
348 if (!pdo_get_bool_param(&bval, val)) {
349 return false;
350 }
351 if (dbh->in_txn) {
352 pdo_raise_impl_error(dbh, NULL, "HY000", "Cannot change autocommit mode while a transaction is already open");
353 return false;
354 }
355 if (dbh->auto_commit ^ bval) {
356 dbh->auto_commit = bval;
357 RETCODE rc = SQLSetConnectAttr(
358 H->dbc,
359 SQL_ATTR_AUTOCOMMIT,
360 dbh->auto_commit ? (SQLPOINTER) SQL_AUTOCOMMIT_ON : (SQLPOINTER) SQL_AUTOCOMMIT_OFF,
361 SQL_IS_INTEGER
362 );
363 if (rc != SQL_SUCCESS) {
364 pdo_odbc_drv_error(
365 dbh->auto_commit ? "SQLSetConnectAttr AUTOCOMMIT = ON" : "SQLSetConnectAttr AUTOCOMMIT = OFF"
366 );
367 return false;
368 }
369 }
370 return true;
371 default:
372 strcpy(H->einfo.last_err_msg, "Unknown Attribute");
373 H->einfo.what = "setAttribute";
374 strcpy(H->einfo.last_state, "IM001");
375 return false;
376 }
377 }
378
pdo_odbc_get_info_string(pdo_dbh_t * dbh,SQLUSMALLINT type,zval * val)379 static int pdo_odbc_get_info_string(pdo_dbh_t *dbh, SQLUSMALLINT type, zval *val)
380 {
381 RETCODE rc;
382 SQLSMALLINT out_len;
383 char buf[256];
384 pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
385 rc = SQLGetInfo(H->dbc, type, (SQLPOINTER)buf, sizeof(buf), &out_len);
386 /* returning -1 is treated as an error, not as unsupported */
387 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
388 return -1;
389 }
390 ZVAL_STRINGL(val, buf, out_len);
391 return 1;
392 }
393
odbc_handle_get_attr(pdo_dbh_t * dbh,zend_long attr,zval * val)394 static int odbc_handle_get_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)
395 {
396 pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
397 switch (attr) {
398 case PDO_ATTR_CLIENT_VERSION:
399 ZVAL_STRING(val, "ODBC-" PDO_ODBC_TYPE);
400 return 1;
401
402 case PDO_ATTR_SERVER_VERSION:
403 return pdo_odbc_get_info_string(dbh, SQL_DBMS_VER, val);
404 case PDO_ATTR_SERVER_INFO:
405 return pdo_odbc_get_info_string(dbh, SQL_DBMS_NAME, val);
406 case PDO_ATTR_PREFETCH:
407 case PDO_ATTR_TIMEOUT:
408 case PDO_ATTR_CONNECTION_STATUS:
409 break;
410 case PDO_ODBC_ATTR_ASSUME_UTF8:
411 ZVAL_BOOL(val, H->assume_utf8 ? 1 : 0);
412 return 1;
413 case PDO_ATTR_AUTOCOMMIT:
414 ZVAL_BOOL(val, dbh->auto_commit);
415 return 1;
416 }
417 return 0;
418 }
419
odbc_handle_check_liveness(pdo_dbh_t * dbh)420 static zend_result odbc_handle_check_liveness(pdo_dbh_t *dbh)
421 {
422 RETCODE ret;
423 UCHAR d_name[32];
424 SQLSMALLINT len;
425 SQLUINTEGER dead = SQL_CD_FALSE;
426 pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data;
427
428 ret = SQLGetConnectAttr(H->dbc, SQL_ATTR_CONNECTION_DEAD, &dead, 0, NULL);
429 if (ret == SQL_SUCCESS && dead == SQL_CD_TRUE) {
430 /* Bail early here, since we know it's gone */
431 return FAILURE;
432 }
433 /*
434 * If the driver doesn't support SQL_ATTR_CONNECTION_DEAD, or if
435 * it returns false (which could be a false positive), fall back
436 * to using SQL_DATA_SOURCE_READ_ONLY, which isn't semantically
437 * correct, but works with many drivers.
438 */
439 ret = SQLGetInfo(H->dbc, SQL_DATA_SOURCE_READ_ONLY, d_name,
440 sizeof(d_name), &len);
441
442 if (ret != SQL_SUCCESS || len == 0) {
443 return FAILURE;
444 }
445 return SUCCESS;
446 }
447
448 static const struct pdo_dbh_methods odbc_methods = {
449 odbc_handle_closer,
450 odbc_handle_preparer,
451 odbc_handle_doer,
452 NULL, /* quoter */
453 odbc_handle_begin,
454 odbc_handle_commit,
455 odbc_handle_rollback,
456 odbc_handle_set_attr,
457 NULL, /* last id */
458 pdo_odbc_fetch_error_func,
459 odbc_handle_get_attr, /* get attr */
460 odbc_handle_check_liveness, /* check_liveness */
461 NULL, /* get_driver_methods */
462 NULL, /* request_shutdown */
463 NULL, /* in transaction, use PDO's internal tracking mechanism */
464 NULL /* get_gc */
465 };
466
pdo_odbc_handle_factory(pdo_dbh_t * dbh,zval * driver_options)467 static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */
468 {
469 pdo_odbc_db_handle *H;
470 RETCODE rc;
471 int use_direct = 0;
472 zend_ulong cursor_lib;
473
474 H = pecalloc(1, sizeof(*H), dbh->is_persistent);
475
476 dbh->driver_data = H;
477
478 rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &H->env);
479 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
480 pdo_odbc_drv_error("SQLAllocHandle: ENV");
481 goto fail;
482 }
483
484 rc = SQLSetEnvAttr(H->env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
485
486 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
487 pdo_odbc_drv_error("SQLSetEnvAttr: ODBC3");
488 goto fail;
489 }
490
491 #ifdef SQL_ATTR_CONNECTION_POOLING
492 if (pdo_odbc_pool_on != SQL_CP_OFF) {
493 rc = SQLSetEnvAttr(H->env, SQL_ATTR_CP_MATCH, (void*)pdo_odbc_pool_mode, 0);
494 if (rc != SQL_SUCCESS) {
495 pdo_odbc_drv_error("SQLSetEnvAttr: SQL_ATTR_CP_MATCH");
496 goto fail;
497 }
498 }
499 #endif
500
501 rc = SQLAllocHandle(SQL_HANDLE_DBC, H->env, &H->dbc);
502 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
503 pdo_odbc_drv_error("SQLAllocHandle: DBC");
504 goto fail;
505 }
506
507 rc = SQLSetConnectAttr(H->dbc, SQL_ATTR_AUTOCOMMIT,
508 (SQLPOINTER)(intptr_t)(dbh->auto_commit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF), SQL_IS_INTEGER);
509 if (rc != SQL_SUCCESS) {
510 pdo_odbc_drv_error("SQLSetConnectAttr AUTOCOMMIT");
511 goto fail;
512 }
513
514 /* set up the cursor library, if needed, or if configured explicitly */
515 cursor_lib = pdo_attr_lval(driver_options, PDO_ODBC_ATTR_USE_CURSOR_LIBRARY, SQL_CUR_USE_IF_NEEDED);
516 rc = SQLSetConnectAttr(H->dbc, SQL_ODBC_CURSORS, (void*)cursor_lib, SQL_IS_INTEGER);
517 if (rc != SQL_SUCCESS && cursor_lib != SQL_CUR_USE_IF_NEEDED) {
518 pdo_odbc_drv_error("SQLSetConnectAttr SQL_ODBC_CURSORS");
519 goto fail;
520 }
521
522 /* a connection string may have = but not ; - i.e. "DSN=PHP" */
523 if (strchr(dbh->data_source, '=')) {
524 SQLCHAR dsnbuf[1024];
525 SQLSMALLINT dsnbuflen;
526
527 use_direct = 1;
528
529 /* Force UID and PWD to be set in the DSN */
530 bool is_uid_set = dbh->username && *dbh->username
531 && !strstr(dbh->data_source, "uid=")
532 && !strstr(dbh->data_source, "UID=");
533 bool is_pwd_set = dbh->password && *dbh->password
534 && !strstr(dbh->data_source, "pwd=")
535 && !strstr(dbh->data_source, "PWD=");
536 if (is_uid_set && is_pwd_set) {
537 char *uid = NULL, *pwd = NULL;
538 bool should_quote_uid = !php_odbc_connstr_is_quoted(dbh->username) && php_odbc_connstr_should_quote(dbh->username);
539 bool should_quote_pwd = !php_odbc_connstr_is_quoted(dbh->password) && php_odbc_connstr_should_quote(dbh->password);
540 if (should_quote_uid) {
541 size_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->username);
542 uid = emalloc(estimated_length);
543 php_odbc_connstr_quote(uid, dbh->username, estimated_length);
544 } else {
545 uid = dbh->username;
546 }
547 if (should_quote_pwd) {
548 size_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->password);
549 pwd = emalloc(estimated_length);
550 php_odbc_connstr_quote(pwd, dbh->password, estimated_length);
551 } else {
552 pwd = dbh->password;
553 }
554 size_t new_dsn_size = strlen(dbh->data_source)
555 + strlen(uid) + strlen(pwd)
556 + strlen(";UID=;PWD=") + 1;
557 char *dsn = pemalloc(new_dsn_size, dbh->is_persistent);
558 snprintf(dsn, new_dsn_size, "%s;UID=%s;PWD=%s", dbh->data_source, uid, pwd);
559 pefree((char*)dbh->data_source, dbh->is_persistent);
560 dbh->data_source = dsn;
561 if (uid && should_quote_uid) {
562 efree(uid);
563 }
564 if (pwd && should_quote_pwd) {
565 efree(pwd);
566 }
567 }
568
569 rc = SQLDriverConnect(H->dbc, NULL, (SQLCHAR *) dbh->data_source, strlen(dbh->data_source),
570 dsnbuf, sizeof(dsnbuf)-1, &dsnbuflen, SQL_DRIVER_NOPROMPT);
571 }
572 if (!use_direct) {
573 rc = SQLConnect(H->dbc, (SQLCHAR *) dbh->data_source, SQL_NTS, (SQLCHAR *) dbh->username, SQL_NTS, (SQLCHAR *) dbh->password, SQL_NTS);
574 }
575
576 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
577 pdo_odbc_drv_error(use_direct ? "SQLDriverConnect" : "SQLConnect");
578 goto fail;
579 }
580
581 /* TODO: if we want to play nicely, we should check to see if the driver really supports ODBC v3 or not */
582
583 dbh->methods = &odbc_methods;
584 dbh->alloc_own_columns = 1;
585
586 return 1;
587
588 fail:
589 dbh->methods = &odbc_methods;
590 return 0;
591 }
592 /* }}} */
593
594 const pdo_driver_t pdo_odbc_driver = {
595 PDO_DRIVER_HEADER(odbc),
596 pdo_odbc_handle_factory
597 };
598