root/ext/pdo_dblib/dblib_driver.c

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

DEFINITIONS

This source file includes following definitions.
  1. dblib_fetch_error
  2. dblib_handle_closer
  3. dblib_handle_preparer
  4. dblib_handle_doer
  5. dblib_handle_quoter
  6. pdo_dblib_transaction_cmd
  7. dblib_handle_begin
  8. dblib_handle_commit
  9. dblib_handle_rollback
  10. dblib_handle_last_id
  11. dblib_set_attr
  12. dblib_get_attribute
  13. pdo_dblib_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.01 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_01.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   |         Frank M. Kromann <frank@kromann.info>                        |
  17   +----------------------------------------------------------------------+
  18 */
  19 
  20 /* $Id$ */
  21 
  22 #ifdef HAVE_CONFIG_H
  23 # include "config.h"
  24 #endif
  25 
  26 #include "php.h"
  27 #include "php_ini.h"
  28 #include "ext/standard/info.h"
  29 #include "pdo/php_pdo.h"
  30 #include "pdo/php_pdo_driver.h"
  31 #include "php_pdo_dblib.h"
  32 #include "php_pdo_dblib_int.h"
  33 #include "zend_exceptions.h"
  34 
  35 /* Cache of the server supported datatypes, initialized in handle_factory */
  36 zval* pdo_dblib_datatypes;
  37 
  38 static int dblib_fetch_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info)
  39 {
  40         pdo_dblib_db_handle *H = (pdo_dblib_db_handle *)dbh->driver_data;
  41         pdo_dblib_err *einfo = &H->err;
  42         pdo_dblib_stmt *S = NULL;
  43         char *message;
  44         char *msg;
  45 
  46         if (stmt) {
  47                 S = (pdo_dblib_stmt*)stmt->driver_data;
  48                 einfo = &S->err;
  49         }
  50 
  51         if (einfo->dberr == SYBESMSG && einfo->lastmsg) {
  52                 msg = einfo->lastmsg;
  53         } else if (einfo->dberr == SYBESMSG && DBLIB_G(err).lastmsg) {
  54                 msg = DBLIB_G(err).lastmsg;
  55                 DBLIB_G(err).lastmsg = NULL;
  56         } else {
  57                 msg = einfo->dberrstr;
  58         }
  59 
  60         spprintf(&message, 0, "%s [%d] (severity %d) [%s]",
  61                 msg, einfo->dberr, einfo->severity, stmt ? stmt->active_query_string : "");
  62 
  63         add_next_index_long(info, einfo->dberr);
  64         add_next_index_string(info, message);
  65         efree(message);
  66         add_next_index_long(info, einfo->oserr);
  67         add_next_index_long(info, einfo->severity);
  68         if (einfo->oserrstr) {
  69                 add_next_index_string(info, einfo->oserrstr);
  70         }
  71 
  72         return 1;
  73 }
  74 
  75 
  76 static int dblib_handle_closer(pdo_dbh_t *dbh)
  77 {
  78         pdo_dblib_db_handle *H = (pdo_dblib_db_handle *)dbh->driver_data;
  79 
  80         if (H) {
  81                 if (H->link) {
  82                         dbclose(H->link);
  83                         H->link = NULL;
  84                 }
  85                 if (H->login) {
  86                         dbfreelogin(H->login);
  87                         H->login = NULL;
  88                 }
  89                 pefree(H, dbh->is_persistent);
  90                 dbh->driver_data = NULL;
  91         }
  92         return 0;
  93 }
  94 
  95 static int dblib_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options)
  96 {
  97         pdo_dblib_db_handle *H = (pdo_dblib_db_handle *)dbh->driver_data;
  98         pdo_dblib_stmt *S = ecalloc(1, sizeof(*S));
  99 
 100         S->H = H;
 101         stmt->driver_data = S;
 102         stmt->methods = &dblib_stmt_methods;
 103         stmt->supports_placeholders = PDO_PLACEHOLDER_NONE;
 104         S->err.sqlstate = stmt->error_code;
 105 
 106         return 1;
 107 }
 108 
 109 static zend_long dblib_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len)
 110 {
 111         pdo_dblib_db_handle *H = (pdo_dblib_db_handle *)dbh->driver_data;
 112         RETCODE ret, resret;
 113 
 114         dbsetuserdata(H->link, (BYTE*)&H->err);
 115 
 116         if (FAIL == dbcmd(H->link, sql)) {
 117                 return -1;
 118         }
 119 
 120         if (FAIL == dbsqlexec(H->link)) {
 121                 return -1;
 122         }
 123 
 124         resret = dbresults(H->link);
 125 
 126         if (resret == FAIL) {
 127                 return -1;
 128         }
 129 
 130         ret = dbnextrow(H->link);
 131         if (ret == FAIL) {
 132                 return -1;
 133         }
 134 
 135         if (dbnumcols(H->link) <= 0) {
 136                 return DBCOUNT(H->link);
 137         }
 138 
 139         /* throw away any rows it might have returned */
 140         dbcanquery(H->link);
 141 
 142         return DBCOUNT(H->link);
 143 }
 144 
 145 static int dblib_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype)
 146 {
 147 
 148         int useBinaryEncoding = 0;
 149         const char * hex = "0123456789abcdef";
 150         int i;
 151         char * q;
 152         *quotedlen = 0;
 153 
 154         /*
 155          * Detect quoted length and if we should use binary encoding
 156          */
 157         for(i=0;i<unquotedlen;i++) {
 158                 if( 32 > unquoted[i] || 127 < unquoted[i] ) {
 159                         useBinaryEncoding = 1;
 160                         break;
 161                 }
 162                 if(unquoted[i] == '\'') ++*quotedlen;
 163                 ++*quotedlen;
 164         }
 165 
 166         if(useBinaryEncoding) {
 167                 /*
 168                  * Binary safe quoting
 169                  * Will implicitly convert for all data types except Text, DateTime & SmallDateTime
 170                  *
 171                  */
 172                 *quotedlen = (unquotedlen * 2) + 2; /* 2 chars per byte +2 for "0x" prefix */
 173                 q = *quoted = emalloc(*quotedlen);
 174 
 175                 *q++ = '0';
 176                 *q++ = 'x';
 177                 for (i=0;i<unquotedlen;i++) {
 178                         *q++ = hex[ (*unquoted>>4)&0xF];
 179                         *q++ = hex[ (*unquoted++)&0xF];
 180                 }
 181         } else {
 182                 /* Alpha/Numeric Quoting */
 183                 *quotedlen += 2; /* +2 for opening, closing quotes */
 184                 q  = *quoted = emalloc(*quotedlen);
 185                 *q++ = '\'';
 186 
 187                 for (i=0;i<unquotedlen;i++) {
 188                         if (unquoted[i] == '\'') {
 189                                 *q++ = '\'';
 190                                 *q++ = '\'';
 191                         } else {
 192                                 *q++ = unquoted[i];
 193                         }
 194                 }
 195                 *q++ = '\'';
 196         }
 197 
 198         *q = 0;
 199 
 200         return 1;
 201 }
 202 
 203 static int pdo_dblib_transaction_cmd(const char *cmd, pdo_dbh_t *dbh)
 204 {
 205         pdo_dblib_db_handle *H = (pdo_dblib_db_handle *)dbh->driver_data;
 206 
 207         if (FAIL == dbcmd(H->link, cmd)) {
 208                 return 0;
 209         }
 210 
 211         if (FAIL == dbsqlexec(H->link)) {
 212                 return 0;
 213         }
 214 
 215         return 1;
 216 }
 217 
 218 static int dblib_handle_begin(pdo_dbh_t *dbh)
 219 {
 220         return pdo_dblib_transaction_cmd("BEGIN TRANSACTION", dbh);
 221 }
 222 
 223 static int dblib_handle_commit(pdo_dbh_t *dbh)
 224 {
 225         return pdo_dblib_transaction_cmd("COMMIT TRANSACTION", dbh);
 226 }
 227 
 228 static int dblib_handle_rollback(pdo_dbh_t *dbh)
 229 {
 230         return pdo_dblib_transaction_cmd("ROLLBACK TRANSACTION", dbh);
 231 }
 232 
 233 char *dblib_handle_last_id(pdo_dbh_t *dbh, const char *name, size_t *len)
 234 {
 235         pdo_dblib_db_handle *H = (pdo_dblib_db_handle *)dbh->driver_data;
 236 
 237         RETCODE ret;
 238         char *id = NULL;
 239 
 240         /*
 241          * Would use scope_identity() but it's not implemented on Sybase
 242          */
 243 
 244         if (FAIL == dbcmd(H->link, "SELECT @@IDENTITY")) {
 245                 return NULL;
 246         }
 247 
 248         if (FAIL == dbsqlexec(H->link)) {
 249                 return NULL;
 250         }
 251 
 252         ret = dbresults(H->link);
 253         if (ret == FAIL || ret == NO_MORE_RESULTS) {
 254                 dbcancel(H->link);
 255                 return NULL;
 256         }
 257 
 258         ret = dbnextrow(H->link);
 259 
 260         if (ret == FAIL || ret == NO_MORE_ROWS) {
 261                 dbcancel(H->link);
 262                 return NULL;
 263         }
 264 
 265         if (dbdatlen(H->link, 1) == 0) {
 266                 dbcancel(H->link);
 267                 return NULL;
 268         }
 269 
 270         id = emalloc(32);
 271         *len = dbconvert(NULL, (dbcoltype(H->link, 1)) , (dbdata(H->link, 1)) , (dbdatlen(H->link, 1)), SQLCHAR, (BYTE *)id, (DBINT)-1);
 272 
 273         dbcancel(H->link);
 274         return id;
 275 }
 276 
 277 static int dblib_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)
 278 {
 279         switch(attr) {
 280                 case PDO_ATTR_TIMEOUT:
 281                         return 0;
 282                 default:
 283                         return 1;
 284         }
 285 
 286 }
 287 
 288 static int dblib_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value)
 289 {
 290         /* dblib_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */
 291         return 0;
 292 }
 293 
 294 static struct pdo_dbh_methods dblib_methods = {
 295         dblib_handle_closer,
 296         dblib_handle_preparer,
 297         dblib_handle_doer,
 298         dblib_handle_quoter,
 299         dblib_handle_begin, /* begin */
 300         dblib_handle_commit, /* commit */
 301         dblib_handle_rollback, /* rollback */
 302         dblib_set_attr, /*set attr */
 303         dblib_handle_last_id, /* last insert id */
 304         dblib_fetch_error, /* fetch error */
 305         dblib_get_attribute, /* get attr */
 306         NULL, /* check liveness */
 307         NULL, /* get driver methods */
 308         NULL, /* request shutdown */
 309         NULL  /* in transaction */
 310 };
 311 
 312 static int pdo_dblib_handle_factory(pdo_dbh_t *dbh, zval *driver_options)
 313 {
 314         pdo_dblib_db_handle *H;
 315         int i, nvars, nvers, ret = 0;
 316 
 317         const pdo_dblib_keyval tdsver[] = {
 318                  {"4.2",DBVERSION_42}
 319                 ,{"4.6",DBVERSION_46}
 320                 ,{"5.0",DBVERSION_70} /* FIXME: This does not work with Sybase, but environ will */
 321                 ,{"6.0",DBVERSION_70}
 322                 ,{"7.0",DBVERSION_70}
 323 #ifdef DBVERSION_71
 324                 ,{"7.1",DBVERSION_71}
 325 #endif
 326 #ifdef DBVERSION_72
 327                 ,{"7.2",DBVERSION_72}
 328                 ,{"8.0",DBVERSION_72}
 329 #endif
 330                 ,{"10.0",DBVERSION_100}
 331                 ,{"auto",0} /* Only works with FreeTDS. Other drivers will bork */
 332 
 333         };
 334         
 335         struct pdo_data_src_parser vars[] = {
 336                 { "charset",    NULL,   0 }
 337                 ,{ "appname",   "PHP " PDO_DBLIB_FLAVOUR,       0 }
 338                 ,{ "host",              "127.0.0.1", 0 }
 339                 ,{ "dbname",    NULL,   0 }
 340                 ,{ "secure",    NULL,   0 } /* DBSETLSECURE */
 341                 ,{ "version",   NULL,   0 } /* DBSETLVERSION */
 342         };
 343 
 344         nvars = sizeof(vars)/sizeof(vars[0]);
 345         nvers = sizeof(tdsver)/sizeof(tdsver[0]);
 346         
 347         php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, nvars);
 348 
 349         if (driver_options) {
 350                 int timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30);
 351                 dbsetlogintime(timeout); /* Connection/Login Timeout */
 352                 dbsettime(timeout); /* Statement Timeout */
 353         }
 354 
 355         H = pecalloc(1, sizeof(*H), dbh->is_persistent);
 356         H->login = dblogin();
 357         H->err.sqlstate = dbh->error_code;
 358 
 359         if (!H->login) {
 360                 goto cleanup;
 361         }
 362 
 363         DBERRHANDLE(H->login, (EHANDLEFUNC) pdo_dblib_error_handler);
 364         DBMSGHANDLE(H->login, (MHANDLEFUNC) pdo_dblib_msg_handler);
 365 
 366         if(vars[5].optval) {
 367                 for(i=0;i<nvers;i++) {
 368                         if(strcmp(vars[5].optval,tdsver[i].key) == 0) {
 369                                 if(FAIL==dbsetlversion(H->login, tdsver[i].value)) {
 370                                         pdo_raise_impl_error(dbh, NULL, "HY000", "PDO_DBLIB: Failed to set version specified in connection string.");
 371                                         goto cleanup;
 372                                 }
 373                                 break;
 374                         }
 375                 }
 376 
 377                 if (i==nvers) {
 378                         printf("Invalid version '%s'\n", vars[5].optval);
 379                         pdo_raise_impl_error(dbh, NULL, "HY000", "PDO_DBLIB: Invalid version specified in connection string.");
 380                         goto cleanup; /* unknown version specified */
 381                 }
 382         }
 383 
 384         if (dbh->username) {
 385                 if(FAIL == DBSETLUSER(H->login, dbh->username)) {
 386                         goto cleanup;
 387                 }
 388         }
 389 
 390         if (dbh->password) {
 391                 if(FAIL == DBSETLPWD(H->login, dbh->password)) {
 392                         goto cleanup;
 393                 }
 394         }
 395 
 396 #if !PHP_DBLIB_IS_MSSQL
 397         if (vars[0].optval) {
 398                 DBSETLCHARSET(H->login, vars[0].optval);
 399         }
 400 #endif
 401 
 402         DBSETLAPP(H->login, vars[1].optval);
 403 
 404 /* DBSETLDBNAME is only available in FreeTDS 0.92 or above */
 405 #ifdef DBSETLDBNAME
 406         if (vars[3].optval) {
 407                 if(FAIL == DBSETLDBNAME(H->login, vars[3].optval)) goto cleanup;
 408         }
 409 #endif
 410 
 411         H->link = dbopen(H->login, vars[2].optval);
 412 
 413         if (!H->link) {
 414                 goto cleanup;
 415         }
 416 
 417 /*
 418  * FreeTDS < 0.92 does not support the DBSETLDBNAME option
 419  * Send use database here after login (Will not work with SQL Azure)
 420  */
 421 #ifndef DBSETLDBNAME
 422         if (vars[3].optval) {
 423                 if(FAIL == dbuse(H->link, vars[3].optval)) goto cleanup;
 424         }
 425 #endif
 426 
 427 #if PHP_DBLIB_IS_MSSQL
 428         /* dblib do not return more than this length from text/image */
 429         DBSETOPT(H->link, DBTEXTLIMIT, "2147483647");
 430 #endif
 431 
 432         /* limit text/image from network */
 433         DBSETOPT(H->link, DBTEXTSIZE, "2147483647");
 434 
 435         /* allow double quoted indentifiers */
 436         DBSETOPT(H->link, DBQUOTEDIDENT, "1");
 437 
 438         ret = 1;
 439         dbh->max_escaped_char_length = 2;
 440         dbh->alloc_own_columns = 1;
 441 
 442 cleanup:
 443         for (i = 0; i < nvars; i++) {
 444                 if (vars[i].freeme) {
 445                         efree(vars[i].optval);
 446                 }
 447         }
 448 
 449         dbh->methods = &dblib_methods;
 450         dbh->driver_data = H;
 451 
 452         if (!ret) {
 453                 zend_throw_exception_ex(php_pdo_get_exception(), DBLIB_G(err).dberr,
 454                         "SQLSTATE[%s] %s (severity %d)",
 455                         DBLIB_G(err).sqlstate,
 456                         DBLIB_G(err).dberrstr,
 457                         DBLIB_G(err).severity);
 458         }
 459 
 460         return ret;
 461 }
 462 
 463 pdo_driver_t pdo_dblib_driver = {
 464 #if PDO_DBLIB_IS_MSSQL
 465         PDO_DRIVER_HEADER(mssql),
 466 #elif defined(PHP_WIN32)
 467 #define PDO_DBLIB_IS_SYBASE
 468         PDO_DRIVER_HEADER(sybase),
 469 #else
 470         PDO_DRIVER_HEADER(dblib),
 471 #endif
 472         pdo_dblib_handle_factory
 473 };
 474 

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