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.01 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_01.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  |         Frank M. Kromann <frank@kromann.info>                        |
17  +----------------------------------------------------------------------+
18*/
19
20/* $Id$ */
21
22#ifdef HAVE_CONFIG_H
23# include "config.h"
24#endif
25
26#include "php.h"
27#include "php_ini.h"
28#include "ext/standard/php_string.h"
29#include "ext/standard/info.h"
30#include "pdo/php_pdo.h"
31#include "pdo/php_pdo_driver.h"
32#include "php_pdo_dblib.h"
33#include "php_pdo_dblib_int.h"
34#include "zend_exceptions.h"
35
36static void free_rows(pdo_dblib_stmt *S TSRMLS_DC)
37{
38    int i, j;
39
40    for (i = 0; i < S->nrows; i++) {
41        for (j = 0; j < S->ncols; j++) {
42            pdo_dblib_colval *val = &S->rows[i*S->ncols] + j;
43            if (val->data) {
44                efree(val->data);
45                val->data = NULL;
46            }
47        }
48    }
49    efree(S->rows);
50    S->rows = NULL;
51    S->nrows = 0;
52}
53
54static int pdo_dblib_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC)
55{
56    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
57
58    if (S->rows) {
59        free_rows(S TSRMLS_CC);
60    }
61    if (S->cols) {
62        efree(S->cols);
63    }
64    efree(S);
65
66    return 1;
67}
68
69static int pdo_dblib_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC)
70{
71    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
72    pdo_dblib_db_handle *H = S->H;
73    RETCODE resret, ret;
74    int i, j;
75    int arows;
76    unsigned int size;
77
78    dbsetuserdata(H->link, &S->err);
79
80    if (S->rows) {
81        /* clean them up */
82        free_rows(S TSRMLS_CC);
83    }
84
85    if (FAIL == dbcmd(H->link, stmt->active_query_string)) {
86        return 0;
87    }
88    if (FAIL == dbsqlexec(H->link)) {
89        return 0;
90    }
91
92    resret = dbresults(H->link);
93    if (resret == FAIL) {
94        return 0;
95    }
96
97    ret = dbnextrow(H->link);
98
99    stmt->row_count = DBCOUNT(H->link);
100
101    if (ret == NO_MORE_ROWS) {
102       return 1;
103    }
104
105    if (!S->cols) {
106        S->ncols = dbnumcols(H->link);
107
108        if (S->ncols <= 0) {
109            return 1;
110        }
111
112        S->cols = ecalloc(S->ncols, sizeof(pdo_dblib_col));
113        stmt->column_count = S->ncols;
114
115        for (i = 0, j = 0; i < S->ncols; i++) {
116            char *tmp = NULL;
117
118            S->cols[i].coltype = dbcoltype(H->link, i+1);
119            S->cols[i].name = (char*)dbcolname(H->link, i+1);
120
121            if (!strlen(S->cols[i].name)) {
122                if (j) {
123                    spprintf(&tmp, 0, "computed%d", j++);
124                    strlcpy(S->cols[i].name, tmp, strlen(tmp)+1);
125                    efree(tmp);
126                } else {
127                    S->cols[i].name = "computed";
128                    j++;
129                }
130            }
131
132            S->cols[i].source = (char*)dbcolsource(H->link, i+1);
133            tmp = estrdup(S->cols[i].source ? S->cols[i].source : "");
134            S->cols[i].source = tmp;
135            efree(tmp);
136
137            S->cols[i].maxlen = dbcollen(H->link, i+1);
138        }
139    }
140
141    arows = 100;
142    size = S->ncols * sizeof(pdo_dblib_colval);
143    S->rows = safe_emalloc(arows, size, 0);
144
145    /* let's fetch all the data */
146    do {
147        if (S->nrows >= arows) {
148            arows *= 2;
149            S->rows = erealloc(S->rows, arows * size);
150        }
151        for (i = 0; i < S->ncols; i++) {
152            pdo_dblib_colval *val = &S->rows[S->nrows * S->ncols + i];
153
154            if (dbdatlen(H->link, i+1) == 0 && dbdata(H->link, i+1) == NULL) {
155                val->len = 0;
156                val->data = NULL;
157            } else {
158                switch (S->cols[i].coltype) {
159                    case SQLCHAR:
160                    case SQLTEXT:
161                    case SQLVARBINARY:
162                    case SQLBINARY:
163                    case SQLIMAGE:
164                        val->len = dbdatlen(H->link, i+1);
165                        val->data = emalloc(val->len + 1);
166                        memcpy(val->data, dbdata(H->link, i+1), val->len);
167                        val->data[val->len] = '\0';
168                        break;
169                    case SQLMONEY:
170                    case SQLMONEY4:
171                    case SQLMONEYN: {
172                        DBFLT8 money_value;
173                        dbconvert(NULL, S->cols[i].coltype, dbdata(H->link, i+1), dbdatlen(H->link, i+1), SQLFLT8, (LPBYTE)&money_value, 8);
174                        val->len = spprintf(&val->data, 0, "%.4f", money_value);
175                        }
176                        break;
177#ifdef SQLUNIQUE
178                    case SQLUNIQUE: {
179#else
180                    case 36: { /* FreeTDS hack, also used by ext/mssql */
181#endif
182                        val->len = 36+1;
183                        val->data = emalloc(val->len + 1);
184
185                        /* uniqueidentifier is a 16-byte binary number, convert to 32 char hex string */
186#ifdef SQLUNIQUE
187                        val->len = dbconvert(NULL, SQLUNIQUE, dbdata(H->link, i+1), dbdatlen(H->link, i+1), SQLCHAR, val->data, val->len);
188#else
189                        val->len = dbconvert(NULL, 36, dbdata(H->link, i+1), dbdatlen(H->link, i+1), SQLCHAR, val->data, val->len);
190#endif
191                        php_strtoupper(val->data, val->len);
192                        break;
193                        }
194                    default:
195                        if (dbwillconvert(S->cols[i].coltype, SQLCHAR)) {
196                            val->len = 32 + (2 * dbdatlen(H->link, i+1));
197                            val->data = emalloc(val->len);
198
199                            val->len = dbconvert(NULL, S->cols[i].coltype, dbdata(H->link, i+1),
200                                    dbdatlen(H->link, i+1), SQLCHAR, val->data, val->len);
201
202                            if (val->len >= 0) {
203                                val->data[val->len] = '\0';
204                            }
205                        } else {
206                            val->len = 0;
207                            val->data = NULL;
208                        }
209                }
210            }
211        }
212
213        S->nrows++;
214
215        ret = dbnextrow(H->link);
216
217        if (ret == BUF_FULL) {
218            dbclrbuf(H->link, DBLASTROW(H->link)-1);
219        }
220    } while (ret != FAIL && ret != NO_MORE_ROWS);
221
222    if (resret != NO_MORE_RESULTS) {
223        /* there are additional result sets available */
224        dbresults(H->link);
225        /* cancel pending rows */
226        dbcanquery(H->link);
227
228        /* TODO: figure out a sane solution */
229    }
230
231    S->current = -1;
232
233    return 1;
234}
235
236static int pdo_dblib_stmt_fetch(pdo_stmt_t *stmt,
237    enum pdo_fetch_orientation ori, long offset TSRMLS_DC)
238{
239    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
240
241    if (!S->rows) {
242        return 0;
243    }
244
245    if (++S->current < S->nrows) {
246        return 1;
247    }
248
249    return 0;
250}
251
252static int pdo_dblib_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC)
253{
254    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
255    struct pdo_column_data *col = &stmt->columns[colno];
256
257    if (!S->rows) {
258        return 0;
259    }
260
261    col->maxlen = S->cols[colno].maxlen;
262    col->namelen = strlen(S->cols[colno].name);
263    col->name = estrdup(S->cols[colno].name);
264    col->param_type = PDO_PARAM_STR;
265
266    return 1;
267}
268
269static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr,
270     unsigned long *len, int *caller_frees TSRMLS_DC)
271{
272    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
273    pdo_dblib_colval *val = &S->rows[S->current * S->ncols + colno];
274
275    *ptr = val->data;
276    *len = val->len;
277    return 1;
278}
279
280static int pdo_dblib_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,
281        enum pdo_param_event event_type TSRMLS_DC)
282{
283    return 1;
284}
285
286static int dblib_dblib_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC)
287{
288    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
289
290    if (S->rows) {
291        free_rows(S TSRMLS_CC);
292        S->rows = NULL;
293    }
294
295    return 1;
296}
297
298struct pdo_stmt_methods dblib_stmt_methods = {
299    pdo_dblib_stmt_dtor,
300    pdo_dblib_stmt_execute,
301    pdo_dblib_stmt_fetch,
302    pdo_dblib_stmt_describe,
303    pdo_dblib_stmt_get_col,
304    pdo_dblib_stmt_param_hook,
305    NULL, /* set attr */
306    NULL, /* get attr */
307    NULL, /* meta */
308    NULL, /* nextrow */
309    dblib_dblib_stmt_cursor_closer
310};
311
312