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