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