1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 7                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 1997-2014 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->namelen = colnamelen;
598    col->name = estrdup(S->cols[colno].colname);
599    S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype);
600
601    /* returning data as a string */
602    col->param_type = PDO_PARAM_STR;
603
604    /* tell ODBC to put it straight into our buffer, but only if it
605     * isn't "long" data, and only if we haven't already bound a long
606     * column. */
607    if (colsize < 256 && !S->going_long) {
608        S->cols[colno].data = emalloc(colsize+1);
609        S->cols[colno].is_long = 0;
610
611        rc = SQLBindCol(S->stmt, colno+1,
612            S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR,
613            S->cols[colno].data,
614            S->cols[colno].datalen+1, &S->cols[colno].fetched_len);
615
616        if (rc != SQL_SUCCESS) {
617            pdo_odbc_stmt_error("SQLBindCol");
618            return 0;
619        }
620    } else {
621        /* allocate a smaller buffer to keep around for smaller
622         * "long" columns */
623        S->cols[colno].data = emalloc(256);
624        S->going_long = 1;
625        S->cols[colno].is_long = 1;
626    }
627
628    return 1;
629}
630
631static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, zend_ulong *len, int *caller_frees)
632{
633    pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
634    pdo_odbc_column *C = &S->cols[colno];
635    zend_ulong ulen;
636
637    /* if it is a column containing "long" data, perform late binding now */
638    if (C->is_long) {
639        zend_ulong used = 0;
640        char *buf;
641        RETCODE rc;
642
643        /* fetch it into C->data, which is allocated with a length
644         * of 256 bytes; if there is more to be had, we then allocate
645         * bigger buffer for the caller to free */
646
647        rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data,
648            256, &C->fetched_len);
649
650        if (rc == SQL_SUCCESS) {
651            /* all the data fit into our little buffer;
652             * jump down to the generic bound data case */
653            goto in_data;
654        }
655
656        if (rc == SQL_SUCCESS_WITH_INFO) {
657            /* this is a 'long column'
658
659             read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks
660             in order into the output buffer
661
662             this loop has to work whether or not SQLGetData() provides the total column length.
663             calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read
664             for that size would be slower except maybe for extremely long columns.*/
665            char *buf2;
666
667            buf2 = emalloc(256);
668            buf = estrndup(C->data, 256);
669            used = 255; /* not 256; the driver NUL terminated the buffer */
670
671            do {
672                C->fetched_len = 0;
673                /* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */
674                rc = SQLGetData(S->stmt, colno+1, SQL_C_CHAR, buf2, 256, &C->fetched_len);
675
676                /* resize output buffer and reassemble block */
677                if (rc==SQL_SUCCESS_WITH_INFO) {
678                    /* point 5, in section "Retrieving Data with SQLGetData" in http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx
679                     states that if SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size)
680                     (if a driver fails to follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */
681                    buf = erealloc(buf, used + 255+1);
682                    memcpy(buf + used, buf2, 255);
683                    used = used + 255;
684                } else if (rc==SQL_SUCCESS) {
685                    buf = erealloc(buf, used + C->fetched_len+1);
686                    memcpy(buf + used, buf2, C->fetched_len);
687                    used = used + C->fetched_len;
688                } else {
689                    /* includes SQL_NO_DATA */
690                    break;
691                }
692
693            } while (1);
694
695            efree(buf2);
696
697            /* NULL terminate the buffer once, when finished, for use with the rest of PHP */
698            buf[used] = '\0';
699
700            *ptr = buf;
701            *caller_frees = 1;
702            *len = used;
703            if (C->is_unicode) {
704                goto unicode_conv;
705            }
706            return 1;
707        }
708
709        /* something went caca */
710        *ptr = NULL;
711        *len = 0;
712        return 1;
713    }
714
715in_data:
716    /* check the indicator to ensure that the data is intact */
717    if (C->fetched_len == SQL_NULL_DATA) {
718        /* A NULL value */
719        *ptr = NULL;
720        *len = 0;
721        return 1;
722    } else if (C->fetched_len >= 0) {
723        /* it was stored perfectly */
724        *ptr = C->data;
725        *len = C->fetched_len;
726        if (C->is_unicode) {
727            goto unicode_conv;
728        }
729        return 1;
730    } else {
731        /* no data? */
732        *ptr = NULL;
733        *len = 0;
734        return 1;
735    }
736
737    unicode_conv:
738    switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, *ptr, *len, &ulen)) {
739        case PDO_ODBC_CONV_FAIL:
740            /* oh well.  They can have the binary version of it */
741        case PDO_ODBC_CONV_NOT_REQUIRED:
742            /* shouldn't happen... */
743            return 1;
744
745        case PDO_ODBC_CONV_OK:
746            if (*caller_frees) {
747                efree(*ptr);
748            }
749            *ptr = emalloc(ulen + 1);
750            *len = ulen;
751            memcpy(*ptr, S->convbuf, ulen+1);
752            *caller_frees = 1;
753            return 1;
754    }
755    return 1;
756}
757
758static int odbc_stmt_set_param(pdo_stmt_t *stmt, zend_long attr, zval *val)
759{
760    SQLRETURN rc;
761    pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
762
763    switch (attr) {
764        case PDO_ATTR_CURSOR_NAME:
765            convert_to_string(val);
766            rc = SQLSetCursorName(S->stmt, Z_STRVAL_P(val), Z_STRLEN_P(val));
767
768            if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
769                return 1;
770            }
771            pdo_odbc_stmt_error("SQLSetCursorName");
772            return 0;
773
774        case PDO_ODBC_ATTR_ASSUME_UTF8:
775            S->assume_utf8 = zval_is_true(val);
776            return 0;
777        default:
778            strcpy(S->einfo.last_err_msg, "Unknown Attribute");
779            S->einfo.what = "setAttribute";
780            strcpy(S->einfo.last_state, "IM001");
781            return -1;
782    }
783}
784
785static int odbc_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val)
786{
787    SQLRETURN rc;
788    pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
789
790    switch (attr) {
791        case PDO_ATTR_CURSOR_NAME:
792        {
793            char buf[256];
794            SQLSMALLINT len = 0;
795            rc = SQLGetCursorName(S->stmt, buf, sizeof(buf), &len);
796
797            if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
798                ZVAL_STRINGL(val, buf, len);
799                return 1;
800            }
801            pdo_odbc_stmt_error("SQLGetCursorName");
802            return 0;
803        }
804
805        case PDO_ODBC_ATTR_ASSUME_UTF8:
806            ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0);
807            return 0;
808
809        default:
810            strcpy(S->einfo.last_err_msg, "Unknown Attribute");
811            S->einfo.what = "getAttribute";
812            strcpy(S->einfo.last_state, "IM001");
813            return -1;
814    }
815}
816
817static int odbc_stmt_next_rowset(pdo_stmt_t *stmt)
818{
819    SQLRETURN rc;
820    SQLSMALLINT colcount;
821    pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
822
823    /* NOTE: can't guarantee that output or input/output parameters
824     * are set until this fella returns SQL_NO_DATA, according to
825     * MSDN ODBC docs */
826    rc = SQLMoreResults(S->stmt);
827
828    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
829        return 0;
830    }
831
832    free_cols(stmt, S);
833    /* how many columns do we have ? */
834    SQLNumResultCols(S->stmt, &colcount);
835    stmt->column_count = (int)colcount;
836    S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));
837    S->going_long = 0;
838
839    return 1;
840}
841
842struct pdo_stmt_methods odbc_stmt_methods = {
843    odbc_stmt_dtor,
844    odbc_stmt_execute,
845    odbc_stmt_fetch,
846    odbc_stmt_describe,
847    odbc_stmt_get_col,
848    odbc_stmt_param_hook,
849    odbc_stmt_set_param,
850    odbc_stmt_get_attr, /* get attr */
851    NULL, /* get column meta */
852    odbc_stmt_next_rowset
853};
854
855/*
856 * Local variables:
857 * tab-width: 4
858 * c-basic-offset: 4
859 * End:
860 * vim600: noet sw=4 ts=4 fdm=marker
861 * vim<600: noet sw=4 ts=4
862 */
863