Annotation of embedaddon/php/ext/pdo_odbc/odbc_stmt.c, revision 1.1.1.3

1.1       misho       1: /*
                      2:   +----------------------------------------------------------------------+
                      3:   | PHP Version 5                                                        |
                      4:   +----------------------------------------------------------------------+
1.1.1.3 ! misho       5:   | Copyright (c) 1997-2013 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: 
                     33: enum pdo_odbc_conv_result {
                     34:        PDO_ODBC_CONV_NOT_REQUIRED,
                     35:        PDO_ODBC_CONV_OK,
                     36:        PDO_ODBC_CONV_FAIL
                     37: };
                     38: 
                     39: static int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SWORD sqltype)
                     40: {
                     41:        if (!S->assume_utf8) return 0;
                     42:        switch (sqltype) {
                     43: #ifdef SQL_WCHAR
                     44:                case SQL_WCHAR:
                     45:                        return 1;
                     46: #endif
                     47: #ifdef SQL_WLONGVARCHAR
                     48:                case SQL_WLONGVARCHAR:
                     49:                        return 1;
                     50: #endif
                     51: #ifdef SQL_WVARCHAR
                     52:                case SQL_WVARCHAR:
                     53:                        return 1;
                     54: #endif
                     55:                default:
                     56:                        return 0;
                     57:        }
                     58: }
                     59: 
                     60: static int pdo_odbc_utf82ucs2(pdo_stmt_t *stmt, int is_unicode, const char *buf, 
                     61:        unsigned long buflen, unsigned long *outlen)
                     62: {
                     63: #ifdef PHP_WIN32
                     64:        if (is_unicode && buflen) {
                     65:                pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
                     66:                DWORD ret;
                     67: 
                     68:                ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, NULL, 0);
                     69:                if (ret == 0) {
                     70:                        /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/
                     71:                        return PDO_ODBC_CONV_FAIL;
                     72:                }
                     73: 
                     74:                ret *= sizeof(WCHAR);
                     75: 
                     76:                if (S->convbufsize <= ret) {
                     77:                        S->convbufsize = ret + sizeof(WCHAR);
                     78:                        S->convbuf = erealloc(S->convbuf, S->convbufsize);
                     79:                }
                     80:                
                     81:                ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, (LPWSTR)S->convbuf, S->convbufsize / sizeof(WCHAR));
                     82:                if (ret == 0) {
                     83:                        /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/
                     84:                        return PDO_ODBC_CONV_FAIL;
                     85:                }
                     86: 
                     87:                ret *= sizeof(WCHAR);
                     88:                *outlen = ret;
                     89:                return PDO_ODBC_CONV_OK;
                     90:        }
                     91: #endif
                     92:        return PDO_ODBC_CONV_NOT_REQUIRED;
                     93: }
                     94: 
                     95: static int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, const char *buf, 
                     96:        unsigned long buflen, unsigned long *outlen)
                     97: {
                     98: #ifdef PHP_WIN32
                     99:        if (is_unicode && buflen) {
                    100:                pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
                    101:                DWORD ret;
                    102: 
                    103:                ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)buf, buflen/sizeof(WCHAR), NULL, 0, NULL, NULL);
                    104:                if (ret == 0) {
                    105:                        return PDO_ODBC_CONV_FAIL;
                    106:                }
                    107: 
                    108:                if (S->convbufsize <= ret) {
                    109:                        S->convbufsize = ret + 1;
                    110:                        S->convbuf = erealloc(S->convbuf, S->convbufsize);
                    111:                }
                    112:                
                    113:                ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)buf, buflen/sizeof(WCHAR), S->convbuf, S->convbufsize, NULL, NULL);
                    114:                if (ret == 0) {
                    115:                        return PDO_ODBC_CONV_FAIL;
                    116:                }
                    117: 
                    118:                *outlen = ret;
                    119:                S->convbuf[*outlen] = '\0';
                    120:                return PDO_ODBC_CONV_OK;
                    121:        }
                    122: #endif
                    123:        return PDO_ODBC_CONV_NOT_REQUIRED;
                    124: }
                    125: 
                    126: static void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S TSRMLS_DC)
                    127: {
                    128:        if (S->cols) {
                    129:                int i;
                    130: 
                    131:                for (i = 0; i < stmt->column_count; i++) {
                    132:                        if (S->cols[i].data) {
                    133:                                efree(S->cols[i].data);
                    134:                        }
                    135:                }
                    136:                efree(S->cols);
                    137:                S->cols = NULL;
                    138:        }
                    139: }
                    140: 
                    141: static int odbc_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC)
                    142: {
                    143:        pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
                    144: 
                    145:        if (S->stmt != SQL_NULL_HANDLE) {
                    146:                if (stmt->executed) {
                    147:                        SQLCloseCursor(S->stmt);
                    148:                }
                    149:                SQLFreeHandle(SQL_HANDLE_STMT, S->stmt);
                    150:                S->stmt = SQL_NULL_HANDLE;
                    151:        }
                    152: 
                    153:        free_cols(stmt, S TSRMLS_CC);
                    154:        if (S->convbuf) {
                    155:                efree(S->convbuf);
                    156:        }
                    157:        efree(S);
                    158: 
                    159:        return 1;
                    160: }
                    161: 
                    162: static int odbc_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC)
                    163: {
                    164:        RETCODE rc;
                    165:        pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
                    166:        char *buf = NULL;
                    167:        SQLLEN row_count = -1;
                    168: 
                    169:        if (stmt->executed) {
                    170:                SQLCloseCursor(S->stmt);
                    171:        }
                    172:        
                    173:        rc = SQLExecute(S->stmt);       
                    174: 
                    175:        while (rc == SQL_NEED_DATA) {
                    176:                struct pdo_bound_param_data *param;
                    177: 
                    178:                rc = SQLParamData(S->stmt, (SQLPOINTER*)&param);
                    179:                if (rc == SQL_NEED_DATA) {
                    180:                        php_stream *stm;
                    181:                        int len;
                    182:                        pdo_odbc_param *P;
                    183:        
                    184:                        P = (pdo_odbc_param*)param->driver_data;
                    185:                        if (Z_TYPE_P(param->parameter) != IS_RESOURCE) {
                    186:                                /* they passed in a string */
                    187:                                unsigned long ulen;
                    188:                                convert_to_string(param->parameter);
                    189: 
                    190:                                switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode, 
                    191:                                                        Z_STRVAL_P(param->parameter),
                    192:                                                        Z_STRLEN_P(param->parameter),
                    193:                                                        &ulen)) {
                    194:                                        case PDO_ODBC_CONV_NOT_REQUIRED:
                    195:                                                SQLPutData(S->stmt, Z_STRVAL_P(param->parameter),
                    196:                                                        Z_STRLEN_P(param->parameter));
                    197:                                                break;
                    198:                                        case PDO_ODBC_CONV_OK:
                    199:                                                SQLPutData(S->stmt, S->convbuf, ulen);
                    200:                                                break;
                    201:                                        case PDO_ODBC_CONV_FAIL:
                    202:                                                pdo_odbc_stmt_error("error converting input string");
                    203:                                                SQLCloseCursor(S->stmt);
                    204:                                                if (buf) {
                    205:                                                        efree(buf);
                    206:                                                }
                    207:                                                return 0;
                    208:                                }
                    209:                                continue;
                    210:                        }
                    211: 
                    212:                        /* we assume that LOBs are binary and don't need charset
                    213:                         * conversion */
                    214: 
                    215:                        php_stream_from_zval_no_verify(stm, &param->parameter);
                    216:                        if (!stm) {
                    217:                                /* shouldn't happen either */
                    218:                                pdo_odbc_stmt_error("input LOB is no longer a stream");
                    219:                                SQLCloseCursor(S->stmt);
                    220:                                if (buf) {
                    221:                                        efree(buf);
                    222:                                }
                    223:                                return 0;
                    224:                        }
                    225: 
                    226:                        /* now suck data from the stream and stick it into the database */
                    227:                        if (buf == NULL) {
                    228:                                buf = emalloc(8192);
                    229:                        }
                    230: 
                    231:                        do {
                    232:                                len = php_stream_read(stm, buf, 8192);
                    233:                                if (len == 0) {
                    234:                                        break;
                    235:                                }
                    236:                                SQLPutData(S->stmt, buf, len);
                    237:                        } while (1);
                    238:                }
                    239:        }
                    240: 
                    241:        if (buf) {
                    242:                efree(buf);
                    243:        }
                    244: 
                    245:        switch (rc) {
                    246:                case SQL_SUCCESS:
                    247:                        break;
                    248:                case SQL_NO_DATA_FOUND:
                    249:                case SQL_SUCCESS_WITH_INFO:
                    250:                        pdo_odbc_stmt_error("SQLExecute");
                    251:                        break;
                    252: 
                    253:                default:
                    254:                        pdo_odbc_stmt_error("SQLExecute");
                    255:                        return 0;
                    256:        }
                    257: 
                    258:        SQLRowCount(S->stmt, &row_count);
                    259:        stmt->row_count = row_count;
                    260: 
                    261:        if (!stmt->executed) {
                    262:                /* do first-time-only definition of bind/mapping stuff */
                    263:                SQLSMALLINT colcount;
                    264: 
                    265:                /* how many columns do we have ? */
                    266:                SQLNumResultCols(S->stmt, &colcount);
                    267: 
                    268:                stmt->column_count = (int)colcount;
                    269:                S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));
                    270:                S->going_long = 0;
                    271:        }
                    272: 
                    273:        return 1;
                    274: }
                    275: 
                    276: static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,
                    277:                enum pdo_param_event event_type TSRMLS_DC)
                    278: {
                    279:        pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
                    280:        RETCODE rc;
                    281:        SWORD sqltype = 0, ctype = 0, scale = 0, nullable = 0;
                    282:        UDWORD precision = 0;
                    283:        pdo_odbc_param *P;
                    284:        
                    285:        /* we're only interested in parameters for prepared SQL right now */
                    286:        if (param->is_param) {
                    287: 
                    288:                switch (event_type) {
                    289:                        case PDO_PARAM_EVT_FREE:
                    290:                                P = param->driver_data;
                    291:                                if (P) {
                    292:                                        efree(P);
                    293:                                }
                    294:                                break;
                    295: 
                    296:                        case PDO_PARAM_EVT_ALLOC:
                    297:                        {
                    298:                                /* figure out what we're doing */
                    299:                                switch (PDO_PARAM_TYPE(param->param_type)) {
                    300:                                        case PDO_PARAM_LOB:
                    301:                                                break;
                    302: 
                    303:                                        case PDO_PARAM_STMT:
                    304:                                                return 0;
                    305:                                        
                    306:                                        default:
                    307:                                                break;
                    308:                                }
                    309: 
                    310:                                rc = SQLDescribeParam(S->stmt, (SQLUSMALLINT) param->paramno+1, &sqltype, &precision, &scale, &nullable);
                    311:                                if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
                    312:                                        /* MS Access, for instance, doesn't support SQLDescribeParam,
                    313:                                         * so we need to guess */
                    314:                                        sqltype = PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB ?
                    315:                                                                        SQL_LONGVARBINARY :
                    316:                                                                        SQL_LONGVARCHAR;
                    317:                                        precision = 4000;
                    318:                                        scale = 5;
                    319:                                        nullable = 1;
                    320: 
                    321:                                        if (param->max_value_len > 0) {
                    322:                                                precision = param->max_value_len;
                    323:                                        }
                    324:                                }
                    325:                                if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) {
                    326:                                        ctype = SQL_C_BINARY;
                    327:                                } else {
                    328:                                        ctype = SQL_C_CHAR;
                    329:                                }
                    330: 
                    331:                                P = emalloc(sizeof(*P));
                    332:                                param->driver_data = P;
                    333: 
                    334:                                P->len = 0; /* is re-populated each EXEC_PRE */
                    335:                                P->outbuf = NULL;
                    336: 
                    337:                                P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype);
                    338:                                if (P->is_unicode) {
                    339:                                        /* avoid driver auto-translation: we'll do it ourselves */
                    340:                                        ctype = SQL_C_BINARY;
                    341:                                }
                    342: 
                    343:                                if ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) {
                    344:                                        P->paramtype = SQL_PARAM_INPUT_OUTPUT;
                    345:                                } else if (param->max_value_len <= 0) {
                    346:                                        P->paramtype = SQL_PARAM_INPUT;
                    347:                                } else {
                    348:                                        P->paramtype = SQL_PARAM_OUTPUT;
                    349:                                }
                    350:                                
                    351:                                if (P->paramtype != SQL_PARAM_INPUT) {
                    352:                                        if (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_NULL) {
                    353:                                                /* need an explicit buffer to hold result */
                    354:                                                P->len = param->max_value_len > 0 ? param->max_value_len : precision;
                    355:                                                if (P->is_unicode) {
                    356:                                                        P->len *= 2;
                    357:                                                }
                    358:                                                P->outbuf = emalloc(P->len + (P->is_unicode ? 2:1));
                    359:                                        }
                    360:                                }
                    361:                                
                    362:                                if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) {
                    363:                                        pdo_odbc_stmt_error("Can't bind a lob for output");
                    364:                                        return 0;
                    365:                                }
                    366: 
                    367:                                rc = SQLBindParameter(S->stmt, (SQLUSMALLINT) param->paramno+1,
                    368:                                                P->paramtype, ctype, sqltype, precision, scale,
                    369:                                                P->paramtype == SQL_PARAM_INPUT ? 
                    370:                                                        (SQLPOINTER)param :
                    371:                                                        P->outbuf,
                    372:                                                P->len,
                    373:                                                &P->len
                    374:                                                );
                    375:        
                    376:                                if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
                    377:                                        return 1;
                    378:                                }
                    379:                                pdo_odbc_stmt_error("SQLBindParameter");
                    380:                                return 0;
                    381:                        }
                    382: 
                    383:                        case PDO_PARAM_EVT_EXEC_PRE:
                    384:                                P = param->driver_data;
                    385:                                if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
                    386:                                        if (Z_TYPE_P(param->parameter) == IS_RESOURCE) {
                    387:                                                php_stream *stm;
                    388:                                                php_stream_statbuf sb;
                    389: 
                    390:                                                php_stream_from_zval_no_verify(stm, &param->parameter);
                    391: 
                    392:                                                if (!stm) {
                    393:                                                        return 0;
                    394:                                                }
                    395: 
                    396:                                                if (0 == php_stream_stat(stm, &sb)) {
                    397:                                                        if (P->outbuf) {
                    398:                                                                int len, amount;
                    399:                                                                char *ptr = P->outbuf;
                    400:                                                                char *end = P->outbuf + P->len;
                    401: 
                    402:                                                                P->len = 0;
                    403:                                                                do {
                    404:                                                                        amount = end - ptr;
                    405:                                                                        if (amount == 0) {
                    406:                                                                                break;
                    407:                                                                        }
                    408:                                                                        if (amount > 8192)
                    409:                                                                                amount = 8192;
                    410:                                                                        len = php_stream_read(stm, ptr, amount);
                    411:                                                                        if (len == 0) {
                    412:                                                                                break;
                    413:                                                                        }
                    414:                                                                        ptr += len;
                    415:                                                                        P->len += len;
                    416:                                                                } while (1);
                    417: 
                    418:                                                        } else {
                    419:                                                                P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size);
                    420:                                                        }
                    421:                                                } else {
                    422:                                                        if (P->outbuf) {
                    423:                                                                P->len = 0;
                    424:                                                        } else {
                    425:                                                                P->len = SQL_LEN_DATA_AT_EXEC(0);
                    426:                                                        }
                    427:                                                }
                    428:                                        } else {
                    429:                                                convert_to_string(param->parameter);
                    430:                                                if (P->outbuf) {
                    431:                                                        P->len = Z_STRLEN_P(param->parameter);
                    432:                                                        memcpy(P->outbuf, Z_STRVAL_P(param->parameter), P->len);
                    433:                                                } else {
                    434:                                                        P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(param->parameter));
                    435:                                                }
                    436:                                        }
                    437:                                } else if (Z_TYPE_P(param->parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) {
                    438:                                        P->len = SQL_NULL_DATA;
                    439:                                } else {
                    440:                                        convert_to_string(param->parameter);
                    441:                                        if (P->outbuf) {
                    442:                                                unsigned long ulen;
                    443:                                                switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode,
                    444:                                                                Z_STRVAL_P(param->parameter),
                    445:                                                                Z_STRLEN_P(param->parameter),
                    446:                                                                &ulen)) {
                    447:                                                        case PDO_ODBC_CONV_FAIL:
                    448:                                                        case PDO_ODBC_CONV_NOT_REQUIRED:
                    449:                                                                P->len = Z_STRLEN_P(param->parameter);
                    450:                                                                memcpy(P->outbuf, Z_STRVAL_P(param->parameter), P->len);
                    451:                                                                break;
                    452:                                                        case PDO_ODBC_CONV_OK:
                    453:                                                                P->len = ulen;
                    454:                                                                memcpy(P->outbuf, S->convbuf, P->len);
                    455:                                                                break;
                    456:                                                }
                    457:                                        } else {
                    458:                                                P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(param->parameter));
                    459:                                        }
                    460:                                }
                    461:                                return 1;
                    462:                        
                    463:                        case PDO_PARAM_EVT_EXEC_POST:
                    464:                                P = param->driver_data;
                    465:                                if (P->outbuf) {
                    466:                                        if (P->outbuf) {
                    467:                                                unsigned long ulen;
                    468:                                                char *srcbuf;
                    469:                                                unsigned long srclen;
                    470: 
                    471:                                                switch (P->len) {
                    472:                                                        case SQL_NULL_DATA:
                    473:                                                                zval_dtor(param->parameter);
                    474:                                                                ZVAL_NULL(param->parameter);
                    475:                                                                break;
                    476:                                                        default:
                    477:                                                                convert_to_string(param->parameter);
                    478:                                                                switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, P->outbuf, P->len, &ulen)) {
                    479:                                                                        case PDO_ODBC_CONV_FAIL:
                    480:                                                                                /* something fishy, but allow it to come back as binary */
                    481:                                                                        case PDO_ODBC_CONV_NOT_REQUIRED:
                    482:                                                                                srcbuf = P->outbuf;
                    483:                                                                                srclen = P->len;
                    484:                                                                                break;
                    485:                                                                        case PDO_ODBC_CONV_OK:
                    486:                                                                                srcbuf = S->convbuf;
                    487:                                                                                srclen = ulen;
                    488:                                                                                break;
                    489:                                                                }
                    490:                                                                                
                    491:                                                                Z_STRVAL_P(param->parameter) = erealloc(Z_STRVAL_P(param->parameter), srclen+1);
                    492:                                                                memcpy(Z_STRVAL_P(param->parameter), srcbuf, srclen);
                    493:                                                                Z_STRLEN_P(param->parameter) = srclen;
                    494:                                                                Z_STRVAL_P(param->parameter)[srclen] = '\0';
                    495:                                                }
                    496:                                        }
                    497:                                }
                    498:                                return 1;
                    499:                }
                    500:        }
                    501:        return 1;
                    502: }
                    503: 
                    504: static int odbc_stmt_fetch(pdo_stmt_t *stmt,
                    505:        enum pdo_fetch_orientation ori, long offset TSRMLS_DC)
                    506: {
                    507:        RETCODE rc;
                    508:        SQLSMALLINT odbcori;
                    509:        pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
                    510: 
                    511:        switch (ori) {
                    512:                case PDO_FETCH_ORI_NEXT:        odbcori = SQL_FETCH_NEXT; break;
                    513:                case PDO_FETCH_ORI_PRIOR:       odbcori = SQL_FETCH_PRIOR; break;
                    514:                case PDO_FETCH_ORI_FIRST:       odbcori = SQL_FETCH_FIRST; break;
                    515:                case PDO_FETCH_ORI_LAST:        odbcori = SQL_FETCH_LAST; break;
                    516:                case PDO_FETCH_ORI_ABS:         odbcori = SQL_FETCH_ABSOLUTE; break;
                    517:                case PDO_FETCH_ORI_REL:         odbcori = SQL_FETCH_RELATIVE; break;
                    518:                default: 
                    519:                        strcpy(stmt->error_code, "HY106");
                    520:                        return 0;
                    521:        }
                    522:        rc = SQLFetchScroll(S->stmt, odbcori, offset);
                    523: 
                    524:        if (rc == SQL_SUCCESS) {
                    525:                return 1;
                    526:        }
                    527:        if (rc == SQL_SUCCESS_WITH_INFO) {
                    528:                pdo_odbc_stmt_error("SQLFetchScroll");
                    529:                return 1;
                    530:        }
                    531: 
                    532:        if (rc == SQL_NO_DATA) {
                    533:                /* pdo_odbc_stmt_error("SQLFetchScroll"); */
                    534:                return 0;
                    535:        }
                    536: 
                    537:        pdo_odbc_stmt_error("SQLFetchScroll");
                    538: 
                    539:        return 0;
                    540: }
                    541: 
                    542: static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC)
                    543: {
                    544:        pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
                    545:        struct pdo_column_data *col = &stmt->columns[colno];
                    546:        zend_bool dyn = FALSE;
                    547:        RETCODE rc;
                    548:        SWORD   colnamelen;
                    549:        SDWORD  colsize, displaysize;
                    550: 
                    551:        rc = SQLDescribeCol(S->stmt, colno+1, S->cols[colno].colname,
                    552:                        sizeof(S->cols[colno].colname)-1, &colnamelen,
                    553:                        &S->cols[colno].coltype, &colsize, NULL, NULL);
                    554: 
                    555:        if (rc != SQL_SUCCESS) {
                    556:                pdo_odbc_stmt_error("SQLDescribeCol");
                    557:                if (rc != SQL_SUCCESS_WITH_INFO) {
                    558:                        return 0;
                    559:                }
                    560:        }
                    561: 
                    562:        rc = SQLColAttribute(S->stmt, colno+1,
                    563:                        SQL_DESC_DISPLAY_SIZE,
                    564:                        NULL, 0, NULL, &displaysize);
                    565: 
                    566:        if (rc != SQL_SUCCESS) {
                    567:                pdo_odbc_stmt_error("SQLColAttribute");
                    568:                if (rc != SQL_SUCCESS_WITH_INFO) {
                    569:                        return 0;
                    570:                }
                    571:        }
                    572:        colsize = displaysize;
                    573: 
                    574:        col->maxlen = S->cols[colno].datalen = colsize;
                    575:        col->namelen = colnamelen;
                    576:        col->name = estrdup(S->cols[colno].colname);
                    577:        S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype);
                    578: 
                    579:        /* returning data as a string */
                    580:        col->param_type = PDO_PARAM_STR;
                    581: 
                    582:        /* tell ODBC to put it straight into our buffer, but only if it
                    583:         * isn't "long" data, and only if we haven't already bound a long
                    584:         * column. */
                    585:        if (colsize < 256 && !S->going_long) {
                    586:                S->cols[colno].data = emalloc(colsize+1);
                    587:                S->cols[colno].is_long = 0;
                    588: 
                    589:                rc = SQLBindCol(S->stmt, colno+1,
                    590:                        S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR,
                    591:                        S->cols[colno].data,
                    592:                        S->cols[colno].datalen+1, &S->cols[colno].fetched_len);
                    593: 
                    594:                if (rc != SQL_SUCCESS) {
                    595:                        pdo_odbc_stmt_error("SQLBindCol");
                    596:                        return 0;
                    597:                }
                    598:        } else {
                    599:                /* allocate a smaller buffer to keep around for smaller
                    600:                 * "long" columns */
                    601:                S->cols[colno].data = emalloc(256);
                    602:                S->going_long = 1;
                    603:                S->cols[colno].is_long = 1;
                    604:        }
                    605: 
                    606:        return 1;
                    607: }
                    608: 
                    609: static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC)
                    610: {
                    611:        pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
                    612:        pdo_odbc_column *C = &S->cols[colno];
                    613:        unsigned long ulen;
                    614: 
                    615:        /* if it is a column containing "long" data, perform late binding now */
                    616:        if (C->is_long) {
                    617:                unsigned long alloced = 4096;
                    618:                unsigned long used = 0;
                    619:                char *buf;
                    620:                RETCODE rc;
                    621: 
                    622:                /* fetch it into C->data, which is allocated with a length
                    623:                 * of 256 bytes; if there is more to be had, we then allocate
                    624:                 * bigger buffer for the caller to free */
                    625: 
                    626:                rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data,
                    627:                        256, &C->fetched_len);
                    628: 
                    629:                if (rc == SQL_SUCCESS) {
                    630:                        /* all the data fit into our little buffer;
                    631:                         * jump down to the generic bound data case */
                    632:                        goto in_data;
                    633:                }
                    634: 
                    635:                if (rc == SQL_SUCCESS_WITH_INFO) {
1.1.1.3 ! misho     636:                        /* this is a 'long column'
        !           637:                        
        !           638:                         read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks
        !           639:                         in order into the output buffer
        !           640:                        
        !           641:                         this loop has to work whether or not SQLGetData() provides the total column length.
        !           642:                         calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read
        !           643:                         for that size would be slower except maybe for extremely long columns.*/
        !           644:                        char *buf2;
        !           645: 
        !           646:                        buf2 = emalloc(256);
        !           647:                        buf = estrndup(C->data, 256);
        !           648:                        used = 255; /* not 256; the driver NUL terminated the buffer */
        !           649:                        
1.1       misho     650:                        do {
                    651:                                C->fetched_len = 0;
1.1.1.3 ! misho     652:                                /* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */
        !           653:                                rc = SQLGetData(S->stmt, colno+1, SQL_C_CHAR, buf2, 256, &C->fetched_len);
        !           654:                                
        !           655:                                /* resize output buffer and reassemble block */
        !           656:                                if (rc==SQL_SUCCESS_WITH_INFO) {
        !           657:                                        /* point 5, in section "Retrieving Data with SQLGetData" in http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx
        !           658:                                         states that if SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size)
        !           659:                                         (if a driver fails to follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */
        !           660:                                        buf = erealloc(buf, used + 255+1);
        !           661:                                        memcpy(buf + used, buf2, 255);
        !           662:                                        used = used + 255;
        !           663:                                } else if (rc==SQL_SUCCESS) {
        !           664:                                        buf = erealloc(buf, used + C->fetched_len+1);
        !           665:                                        memcpy(buf + used, buf2, C->fetched_len);
        !           666:                                        used = used + C->fetched_len;
1.1       misho     667:                                } else {
1.1.1.3 ! misho     668:                                        /* includes SQL_NO_DATA */
1.1       misho     669:                                        break;
                    670:                                }
1.1.1.3 ! misho     671:                                
1.1       misho     672:                        } while (1);
1.1.1.3 ! misho     673:                        
        !           674:                        efree(buf2);
        !           675:                        
        !           676:                        /* NULL terminate the buffer once, when finished, for use with the rest of PHP */
1.1       misho     677:                        buf[used] = '\0';
1.1.1.3 ! misho     678: 
1.1       misho     679:                        *ptr = buf;
                    680:                        *caller_frees = 1;
                    681:                        *len = used;
                    682:                        if (C->is_unicode) {
                    683:                                goto unicode_conv;
                    684:                        }
                    685:                        return 1;
                    686:                }
                    687: 
                    688:                /* something went caca */
                    689:                *ptr = NULL;
                    690:                *len = 0;
                    691:                return 1;
                    692:        }
                    693: 
                    694: in_data:
                    695:        /* check the indicator to ensure that the data is intact */
                    696:        if (C->fetched_len == SQL_NULL_DATA) {
                    697:                /* A NULL value */
                    698:                *ptr = NULL;
                    699:                *len = 0;
                    700:                return 1;
                    701:        } else if (C->fetched_len >= 0) {
                    702:                /* it was stored perfectly */
                    703:                *ptr = C->data;
                    704:                *len = C->fetched_len;
                    705:                if (C->is_unicode) {
                    706:                        goto unicode_conv;
                    707:                }
                    708:                return 1;
                    709:        } else {
                    710:                /* no data? */
                    711:                *ptr = NULL;
                    712:                *len = 0;
                    713:                return 1;
                    714:        }
                    715: 
                    716:        unicode_conv:
                    717:        switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, *ptr, *len, &ulen)) {
                    718:                case PDO_ODBC_CONV_FAIL:
                    719:                        /* oh well.  They can have the binary version of it */
                    720:                case PDO_ODBC_CONV_NOT_REQUIRED:
                    721:                        /* shouldn't happen... */
                    722:                        return 1;
                    723: 
                    724:                case PDO_ODBC_CONV_OK:
                    725:                        if (*caller_frees) {
                    726:                                efree(*ptr);
                    727:                        }
                    728:                        *ptr = emalloc(ulen + 1);
                    729:                        *len = ulen;
                    730:                        memcpy(*ptr, S->convbuf, ulen+1);
                    731:                        *caller_frees = 1;
                    732:                        return 1;
                    733:        }
                    734:        return 1;
                    735: }
                    736: 
                    737: static int odbc_stmt_set_param(pdo_stmt_t *stmt, long attr, zval *val TSRMLS_DC)
                    738: {
                    739:        SQLRETURN rc;
                    740:        pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
                    741: 
                    742:        switch (attr) {
                    743:                case PDO_ATTR_CURSOR_NAME:
                    744:                        convert_to_string(val);
                    745:                        rc = SQLSetCursorName(S->stmt, Z_STRVAL_P(val), Z_STRLEN_P(val));
                    746: 
                    747:                        if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
                    748:                                return 1;
                    749:                        }
                    750:                        pdo_odbc_stmt_error("SQLSetCursorName");
                    751:                        return 0;
                    752: 
                    753:                case PDO_ODBC_ATTR_ASSUME_UTF8:
                    754:                        S->assume_utf8 = zval_is_true(val);
                    755:                        return 0;
                    756:                default:
                    757:                        strcpy(S->einfo.last_err_msg, "Unknown Attribute");
                    758:                        S->einfo.what = "setAttribute";
                    759:                        strcpy(S->einfo.last_state, "IM001");
                    760:                        return -1;
                    761:        }
                    762: }
                    763: 
                    764: static int odbc_stmt_get_attr(pdo_stmt_t *stmt, long attr, zval *val TSRMLS_DC)
                    765: {
                    766:        SQLRETURN rc;
                    767:        pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
                    768: 
                    769:        switch (attr) {
                    770:                case PDO_ATTR_CURSOR_NAME:
                    771:                {
                    772:                        char buf[256];
                    773:                        SQLSMALLINT len = 0;
                    774:                        rc = SQLGetCursorName(S->stmt, buf, sizeof(buf), &len);
                    775: 
                    776:                        if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
                    777:                                ZVAL_STRINGL(val, buf, len, 1);
                    778:                                return 1;
                    779:                        }
                    780:                        pdo_odbc_stmt_error("SQLGetCursorName");
                    781:                        return 0;
                    782:                }
                    783: 
                    784:                case PDO_ODBC_ATTR_ASSUME_UTF8:
                    785:                        ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0);
                    786:                        return 0;
                    787: 
                    788:                default:
                    789:                        strcpy(S->einfo.last_err_msg, "Unknown Attribute");
                    790:                        S->einfo.what = "getAttribute";
                    791:                        strcpy(S->einfo.last_state, "IM001");
                    792:                        return -1;
                    793:        }
                    794: }
                    795: 
                    796: static int odbc_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC)
                    797: {
                    798:        SQLRETURN rc;
                    799:        SQLSMALLINT colcount;
                    800:        pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
                    801: 
                    802:        /* NOTE: can't guarantee that output or input/output parameters
                    803:         * are set until this fella returns SQL_NO_DATA, according to
                    804:         * MSDN ODBC docs */
                    805:        rc = SQLMoreResults(S->stmt);
                    806: 
                    807:        if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
                    808:                return 0;
                    809:        }
                    810: 
                    811:        free_cols(stmt, S TSRMLS_CC);
                    812:        /* how many columns do we have ? */
                    813:        SQLNumResultCols(S->stmt, &colcount);
                    814:        stmt->column_count = (int)colcount;
                    815:        S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));
                    816:        S->going_long = 0;
                    817: 
                    818:        return 1;
                    819: }
                    820: 
                    821: struct pdo_stmt_methods odbc_stmt_methods = {
                    822:        odbc_stmt_dtor,
                    823:        odbc_stmt_execute,
                    824:        odbc_stmt_fetch,
                    825:        odbc_stmt_describe,
                    826:        odbc_stmt_get_col,
                    827:        odbc_stmt_param_hook,
                    828:        odbc_stmt_set_param,
                    829:        odbc_stmt_get_attr, /* get attr */
                    830:        NULL, /* get column meta */
                    831:        odbc_stmt_next_rowset
                    832: };
                    833: 
                    834: /*
                    835:  * Local variables:
                    836:  * tab-width: 4
                    837:  * c-basic-offset: 4
                    838:  * End:
                    839:  * vim600: noet sw=4 ts=4 fdm=marker
                    840:  * vim<600: noet sw=4 ts=4
                    841:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>