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
36
37/* {{{ pdo_dblib_get_field_name
38 *
39 * Updated for MSSQL 2008 SR2 extended types
40 *
41 */
42static char *pdo_dblib_get_field_name(int type)
43{
44    switch (type) {
45        case 34: return "image";
46        case 35: return "text";
47        case 36: return "uniqueidentifier";
48        case 40: return "date";
49        case 41: return "time";
50        case 42: return "datetime2";
51        case 43: return "datetimeoffset";
52        case 48: return "tinyint";
53        case 52: return "smallint";
54        case 56: return "int";
55        case 58: return "smalldatetime";
56        case 59: return "real";
57        case 60: return "money";
58        case 61: return "datetime";
59        case 62: return "float";
60        case 98: return "sql_variant";
61        case 99: return "ntext";
62        case 104: return "bit";
63        case 106: return "decimal";
64        case 108: return "numeric";
65        case 122: return "smallmoney";
66        case 127: return "bigint";
67        case 240: return "geometry";
68        case 165: return "varbinary";
69        case 167: return "varchar";
70        case 173: return "binary";
71        case 175: return "char";
72        case 189: return "timestamp";
73        case 231: return "nvarchar";
74        case 239: return "nchar";
75        case 241: return "xml";
76        default:
77            return "unknown";
78            break;
79    }
80}
81/* }}} */
82
83static int dblib_dblib_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC)
84{
85    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
86    pdo_dblib_db_handle *H = S->H;
87
88    /* Cancel any pending results */
89    dbcancel(H->link);
90
91    efree(stmt->columns);
92    stmt->columns = NULL;
93
94    return 1;
95}
96
97static int pdo_dblib_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC)
98{
99    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
100
101    dblib_dblib_stmt_cursor_closer(stmt TSRMLS_CC);
102
103    efree(S);
104
105    return 1;
106}
107
108static int pdo_dblib_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC)
109{
110    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
111    pdo_dblib_db_handle *H = S->H;
112    RETCODE ret;
113
114    ret = dbresults(H->link);
115
116    if (ret == FAIL || ret == NO_MORE_RESULTS) {
117        return 0;
118    }
119
120    stmt->row_count = DBCOUNT(H->link);
121    stmt->column_count = dbnumcols(H->link);
122
123    return 1;
124}
125
126static int pdo_dblib_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC)
127{
128    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
129    pdo_dblib_db_handle *H = S->H;
130    RETCODE ret;
131
132    dbsetuserdata(H->link, (BYTE*) &S->err);
133
134    if (FAIL == dbcmd(H->link, stmt->active_query_string)) {
135        return 0;
136    }
137
138    if (FAIL == dbsqlexec(H->link)) {
139        return 0;
140    }
141
142    ret = pdo_dblib_stmt_next_rowset(stmt TSRMLS_CC);
143
144    if (ret == 0) {
145        return 0;
146    }
147
148    stmt->row_count = DBCOUNT(H->link);
149    stmt->column_count = dbnumcols(H->link);
150
151    return 1;
152}
153
154static int pdo_dblib_stmt_fetch(pdo_stmt_t *stmt,
155    enum pdo_fetch_orientation ori, long offset TSRMLS_DC)
156{
157
158    RETCODE ret;
159
160    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
161    pdo_dblib_db_handle *H = S->H;
162
163    ret = dbnextrow(H->link);
164
165    if (ret == FAIL || ret == NO_MORE_ROWS) {
166        return 0;
167    }
168
169    return 1;
170}
171
172static int pdo_dblib_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC)
173{
174    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
175    pdo_dblib_db_handle *H = S->H;
176
177    struct pdo_column_data *col = &stmt->columns[colno];
178
179    col->name = (char*)dbcolname(H->link, colno+1);
180    col->maxlen = dbcollen(H->link, colno+1);
181    col->namelen = strlen(col->name);
182    col->param_type = PDO_PARAM_STR;
183
184    return 1;
185}
186
187static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr,
188     unsigned long *len, int *caller_frees TSRMLS_DC)
189{
190
191    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
192    pdo_dblib_db_handle *H = S->H;
193
194    int coltype;
195    unsigned int tmp_len;
196    char *tmp_ptr = NULL;
197
198    coltype = dbcoltype(H->link, colno+1);
199
200    *len = dbdatlen(H->link, colno+1);
201    *ptr = dbdata(H->link, colno+1);
202
203    if (*len == 0 && *ptr == NULL) {
204        return 1;
205    }
206
207    switch (coltype) {
208        case SQLCHAR:
209        case SQLTEXT:
210        case SQLVARBINARY:
211        case SQLBINARY:
212        case SQLIMAGE:
213        case SQLVARCHAR:
214            tmp_ptr = emalloc(*len + 1);
215            memcpy(tmp_ptr, *ptr, *len);
216            tmp_ptr[*len] = '\0';
217            *ptr = tmp_ptr;
218            break;
219        case SQLMONEY:
220        case SQLMONEY4:
221        case SQLMONEYN: {
222            DBFLT8 money_value;
223            dbconvert(NULL, coltype, *ptr, *len, SQLFLT8, (LPBYTE)&money_value, 8);
224            *len = spprintf(&tmp_ptr, 0, "%.4f", money_value);
225            *ptr = tmp_ptr;
226            break;
227        }
228#ifdef SQLUNIQUE
229        case SQLUNIQUE: {
230#else
231        case 36: { /* FreeTDS hack, also used by ext/mssql */
232#endif
233            *len = 36+1;
234            tmp_ptr = emalloc(*len + 1);
235
236            /* uniqueidentifier is a 16-byte binary number, convert to 32 char hex string */
237#ifdef SQLUNIQUE
238            *len = dbconvert(NULL, SQLUNIQUE, *ptr, *len, SQLCHAR, tmp_ptr, *len);
239#else
240            *len = dbconvert(NULL, 36, *ptr, *len, SQLCHAR, tmp_ptr, *len);
241#endif
242            php_strtoupper(tmp_ptr, *len);
243            *ptr = tmp_ptr;
244            break;
245        }
246        default:
247            if (dbwillconvert(coltype, SQLCHAR)) {
248                tmp_len = 32 + (2 * (*len));
249                tmp_ptr = emalloc(tmp_len);
250                *len = dbconvert(NULL, coltype, *ptr, *len, SQLCHAR, tmp_ptr, -1);
251                *ptr = tmp_ptr;
252        } else {
253            *len = 0;
254            *ptr = NULL;
255        }
256    }
257
258    *caller_frees = 1;
259
260    return 1;
261}
262
263static int pdo_dblib_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,
264        enum pdo_param_event event_type TSRMLS_DC)
265{
266    return 1;
267}
268
269static int pdo_dblib_stmt_get_column_meta(pdo_stmt_t *stmt, long colno, zval *return_value TSRMLS_DC)
270{
271    pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
272    pdo_dblib_db_handle *H = S->H;
273
274    array_init(return_value);
275
276    DBTYPEINFO* dbtypeinfo;
277    dbtypeinfo = dbcoltypeinfo(H->link, colno+1);
278
279    add_assoc_long(return_value, "max_length", dbcollen(H->link, colno+1) );
280    add_assoc_long(return_value, "precision", (int) dbtypeinfo->precision );
281    add_assoc_long(return_value, "scale", (int) dbtypeinfo->scale );
282    add_assoc_string(return_value, "column_source", dbcolsource(H->link, colno+1), 1);
283    add_assoc_string(return_value, "native_type", pdo_dblib_get_field_name(dbcoltype(H->link, colno+1)), 1);
284
285    return 1;
286}
287
288
289struct pdo_stmt_methods dblib_stmt_methods = {
290    pdo_dblib_stmt_dtor,
291    pdo_dblib_stmt_execute,
292    pdo_dblib_stmt_fetch,
293    pdo_dblib_stmt_describe,
294    pdo_dblib_stmt_get_col,
295    pdo_dblib_stmt_param_hook,
296    NULL, /* set attr */
297    NULL, /* get attr */
298    pdo_dblib_stmt_get_column_meta, /* meta */
299    pdo_dblib_stmt_next_rowset, /* nextrow */
300    dblib_dblib_stmt_cursor_closer
301};
302
303