1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 5                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 1997-2016 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
36
37/* {{{ pdo_dblib_get_field_name
38 *
39 * Return the data type name for a given TDS number
40 *
41 */
42static char *pdo_dblib_get_field_name(int type)
43{
44    /*
45     * I don't return dbprtype(type) because it does not fully describe the type
46     * (example: varchar is reported as char by dbprtype)
47     *
48     * FIX ME: Cache datatypes from server systypes table in pdo_dblib_handle_factory()
49     *         to make this future proof.
50     */
51
52    switch (type) {
53        case 31: return "nvarchar";
54        case 34: return "image";
55        case 35: return "text";
56        case 36: return "uniqueidentifier";
57        case 37: return "varbinary"; /* & timestamp - Sybase AS12 */
58        case 38: return "bigint"; /* & bigintn - Sybase AS12 */
59        case 39: return "varchar"; /* & sysname & nvarchar - Sybase AS12 */
60        case 40: return "date";
61        case 41: return "time";
62        case 42: return "datetime2";
63        case 43: return "datetimeoffset";
64        case 45: return "binary"; /* Sybase AS12 */
65        case 47: return "char"; /* & nchar & uniqueidentifierstr Sybase AS12 */
66        case 48: return "tinyint";
67        case 50: return "bit"; /* Sybase AS12 */
68        case 52: return "smallint";
69        case 55: return "decimal"; /* Sybase AS12 */
70        case 56: return "int";
71        case 58: return "smalldatetime";
72        case 59: return "real";
73        case 60: return "money";
74        case 61: return "datetime";
75        case 62: return "float";
76        case 63: return "numeric"; /* or uint, ubigint, usmallint Sybase AS12 */
77        case 98: return "sql_variant";
78        case 99: return "ntext";
79        case 104: return "bit";
80        case 106: return "decimal"; /* decimal n on sybase */
81        case 108: return "numeric"; /* numeric n on sybase */
82        case 122: return "smallmoney";
83        case 127: return "bigint";
84        case 165: return "varbinary";
85        case 167: return "varchar";
86        case 173: return "binary";
87        case 175: return "char";
88        case 189: return "timestamp";
89        case 231: return "nvarchar";
90        case 239: return "nchar";
91        case 240: return "geometry";
92        case 241: return "xml";
93        default: return "unknown";
94    }
95}
96/* }}} */
97
98static int pdo_dblib_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC)
99{
100    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
101    pdo_dblib_db_handle *H = S->H;
102
103    /* Cancel any pending results */
104    dbcancel(H->link);
105
106    return 1;
107}
108
109static int pdo_dblib_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC)
110{
111    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
112
113    efree(S);
114
115    return 1;
116}
117
118static int pdo_dblib_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC)
119{
120    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
121    pdo_dblib_db_handle *H = S->H;
122    RETCODE ret;
123
124    ret = dbresults(H->link);
125
126    if (FAIL == ret) {
127        pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbresults() returned FAIL" TSRMLS_CC);
128        return 0;
129    }
130
131    if(NO_MORE_RESULTS == ret) {
132        return 0;
133    }
134
135    stmt->row_count = DBCOUNT(H->link);
136    stmt->column_count = dbnumcols(H->link);
137
138    return 1;
139}
140
141static int pdo_dblib_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC)
142{
143    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
144    pdo_dblib_db_handle *H = S->H;
145    RETCODE ret;
146
147    dbsetuserdata(H->link, (BYTE*) &S->err);
148
149    pdo_dblib_stmt_cursor_closer(stmt TSRMLS_CC);
150
151    if (FAIL == dbcmd(H->link, stmt->active_query_string)) {
152        return 0;
153    }
154
155    if (FAIL == dbsqlexec(H->link)) {
156        return 0;
157    }
158
159    ret = pdo_dblib_stmt_next_rowset(stmt TSRMLS_CC);
160
161    stmt->row_count = DBCOUNT(H->link);
162    stmt->column_count = dbnumcols(H->link);
163
164    return 1;
165}
166
167static int pdo_dblib_stmt_fetch(pdo_stmt_t *stmt,
168    enum pdo_fetch_orientation ori, long offset TSRMLS_DC)
169{
170
171    RETCODE ret;
172
173    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
174    pdo_dblib_db_handle *H = S->H;
175
176    ret = dbnextrow(H->link);
177
178    if (FAIL == ret) {
179        pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbnextrow() returned FAIL" TSRMLS_CC);
180        return 0;
181    }
182
183    if(NO_MORE_ROWS == ret) {
184        return 0;
185    }
186
187    return 1;
188}
189
190static int pdo_dblib_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC)
191{
192    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
193    pdo_dblib_db_handle *H = S->H;
194    struct pdo_column_data *col;
195    char *fname;
196
197    if(colno >= stmt->column_count || colno < 0)  {
198        return FAILURE;
199    }
200
201    col = &stmt->columns[colno];
202    fname = (char*)dbcolname(H->link, colno+1);
203
204    if (fname && *fname) {
205        col->name = estrdup(fname);
206        col->namelen = strlen(col->name);
207    } else {
208        col->namelen = spprintf(&col->name, 0, "computed%d", colno);
209    }
210    col->maxlen = dbcollen(H->link, colno+1);
211    col->param_type = PDO_PARAM_STR;
212
213    return 1;
214}
215
216static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr,
217     unsigned long *len, int *caller_frees TSRMLS_DC)
218{
219
220    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
221    pdo_dblib_db_handle *H = S->H;
222
223    int coltype;
224    unsigned int tmp_len;
225    char *tmp_ptr = NULL;
226
227    coltype = dbcoltype(H->link, colno+1);
228
229    *len = dbdatlen(H->link, colno+1);
230    *ptr = dbdata(H->link, colno+1);
231
232    if (*len == 0 && *ptr == NULL) {
233        return 1;
234    }
235
236    switch (coltype) {
237        case SQLVARBINARY:
238        case SQLBINARY:
239        case SQLIMAGE:
240        case SQLTEXT:
241            /* FIXME: Above types should be returned as a stream as they can be VERY large */
242        case SQLCHAR:
243        case SQLVARCHAR:
244            tmp_ptr = emalloc(*len + 1);
245            memcpy(tmp_ptr, *ptr, *len);
246            tmp_ptr[*len] = '\0';
247            *ptr = tmp_ptr;
248            break;
249        case SQLMONEY:
250        case SQLMONEY4:
251        case SQLMONEYN: {
252            DBFLT8 money_value;
253            dbconvert(NULL, coltype, *ptr, *len, SQLFLT8, (LPBYTE)&money_value, 8);
254            *len = spprintf(&tmp_ptr, 0, "%.4f", money_value);
255            *ptr = tmp_ptr;
256            break;
257        }
258        case SQLUNIQUE: {
259            *len = 36+1;
260            tmp_ptr = emalloc(*len + 1);
261
262            /* uniqueidentifier is a 16-byte binary number, convert to 32 char hex string */
263            *len = dbconvert(NULL, SQLUNIQUE, *ptr, *len, SQLCHAR, tmp_ptr, *len);
264            php_strtoupper(tmp_ptr, *len);
265            *ptr = tmp_ptr;
266            break;
267        }
268        case SQLDATETIM4:
269        case SQLDATETIME: {
270            DBDATETIME dt;
271            DBDATEREC di;
272
273            dbconvert(H->link, coltype, (BYTE*) *ptr, -1, SQLDATETIME, (LPBYTE) &dt, -1);
274            dbdatecrack(H->link, &di, &dt);
275
276            *len = spprintf((char**) &tmp_ptr, 20, "%d-%02d-%02d %02d:%02d:%02d",
277#if defined(PHP_DBLIB_IS_MSSQL) || defined(MSDBLIB)
278                    di.year,     di.month,       di.day,        di.hour,     di.minute,     di.second
279#else
280                    di.dateyear, di.datemonth+1, di.datedmonth, di.datehour, di.dateminute, di.datesecond
281#endif
282                );
283
284            *ptr = (char*) tmp_ptr;
285            break;
286        }
287        default:
288            if (dbwillconvert(coltype, SQLCHAR)) {
289                tmp_len = 32 + (2 * (*len)); /* FIXME: We allocate more than we need here */
290                tmp_ptr = emalloc(tmp_len);
291                *len = dbconvert(NULL, coltype, *ptr, *len, SQLCHAR, tmp_ptr, -1);
292                *ptr = tmp_ptr;
293            } else {
294                *len = 0; /* FIXME: Silently fails and returns null on conversion errors */
295                *ptr = NULL;
296            }
297    }
298
299    *caller_frees = 1;
300
301    return 1;
302}
303
304static int pdo_dblib_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,
305        enum pdo_param_event event_type TSRMLS_DC)
306{
307    return 1;
308}
309
310static int pdo_dblib_stmt_get_column_meta(pdo_stmt_t *stmt, long colno, zval *return_value TSRMLS_DC)
311{
312    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
313    pdo_dblib_db_handle *H = S->H;
314    DBTYPEINFO* dbtypeinfo;
315
316    if(colno >= stmt->column_count || colno < 0)  {
317        return FAILURE;
318    }
319
320    array_init(return_value);
321
322    dbtypeinfo = dbcoltypeinfo(H->link, colno+1);
323
324    if(!dbtypeinfo) return FAILURE;
325
326    add_assoc_long(return_value, "max_length", dbcollen(H->link, colno+1) );
327    add_assoc_long(return_value, "precision", (int) dbtypeinfo->precision );
328    add_assoc_long(return_value, "scale", (int) dbtypeinfo->scale );
329    add_assoc_string(return_value, "column_source", dbcolsource(H->link, colno+1), 1);
330    add_assoc_string(return_value, "native_type", pdo_dblib_get_field_name(dbcoltype(H->link, colno+1)), 1);
331    add_assoc_long(return_value, "native_type_id", dbcoltype(H->link, colno+1));
332    add_assoc_long(return_value, "native_usertype_id", dbcolutype(H->link, colno+1));
333
334    return 1;
335}
336
337
338struct pdo_stmt_methods dblib_stmt_methods = {
339    pdo_dblib_stmt_dtor,
340    pdo_dblib_stmt_execute,
341    pdo_dblib_stmt_fetch,
342    pdo_dblib_stmt_describe,
343    pdo_dblib_stmt_get_col,
344    pdo_dblib_stmt_param_hook,
345    NULL, /* set attr */
346    NULL, /* get attr */
347    pdo_dblib_stmt_get_column_meta, /* meta */
348    pdo_dblib_stmt_next_rowset, /* nextrow */
349    pdo_dblib_stmt_cursor_closer
350};
351
352