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