root/ext/pdo_odbc/odbc_driver.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pdo_odbc_fetch_error_func
  2. pdo_odbc_error
  3. odbc_handle_closer
  4. odbc_handle_preparer
  5. odbc_handle_doer
  6. odbc_handle_quoter
  7. odbc_handle_begin
  8. odbc_handle_commit
  9. odbc_handle_rollback
  10. odbc_handle_set_attr
  11. odbc_handle_get_attr
  12. pdo_odbc_handle_factory

   1 /*
   2   +----------------------------------------------------------------------+
   3   | PHP Version 7                                                        |
   4   +----------------------------------------------------------------------+
   5   | Copyright (c) 1997-2016 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 
  19 /* $Id$ */
  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)
  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         zend_string *message = NULL;
  40 
  41         if (stmt) {
  42                 S = (pdo_odbc_stmt*)stmt->driver_data;
  43                 einfo = &S->einfo;
  44         }
  45 
  46         message = strpprintf(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_str(info, message);
  53         add_next_index_string(info, einfo->last_state);
  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) /* {{{ */
  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, "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) {
 117                 char discard_state[6];
 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)
 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, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options)
 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         size_t 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);
 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);
 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 zend_long odbc_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len)
 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, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type param_type )
 265 {
 266         /* pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; */
 267         /* TODO: figure it out */
 268         return 0;
 269 }
 270 
 271 static int odbc_handle_begin(pdo_dbh_t *dbh)
 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)
 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)
 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, zend_long attr, zval *val)
 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, zend_long attr, zval *val)
 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);
 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) /* {{{ */
 392 {
 393         pdo_odbc_db_handle *H;
 394         RETCODE rc;
 395         int use_direct = 0;
 396         zend_ulong 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);
 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                 SQLSMALLINT 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  */

/* [<][>][^][v][top][bottom][index][help] */