1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2018 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 */
pdo_dblib_get_field_name(int type)42 static 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
pdo_dblib_stmt_cursor_closer(pdo_stmt_t * stmt)98 static int pdo_dblib_stmt_cursor_closer(pdo_stmt_t *stmt)
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 pdo_dblib_err_dtor(&H->err);
107
108 return 1;
109 }
110
pdo_dblib_stmt_dtor(pdo_stmt_t * stmt)111 static int pdo_dblib_stmt_dtor(pdo_stmt_t *stmt)
112 {
113 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
114
115 pdo_dblib_err_dtor(&S->err);
116
117 efree(S);
118
119 return 1;
120 }
121
pdo_dblib_stmt_next_rowset_no_cancel(pdo_stmt_t * stmt)122 static int pdo_dblib_stmt_next_rowset_no_cancel(pdo_stmt_t *stmt)
123 {
124 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
125 pdo_dblib_db_handle *H = S->H;
126 RETCODE ret;
127
128 ret = dbresults(H->link);
129
130 if (FAIL == ret) {
131 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbresults() returned FAIL");
132 return 0;
133 }
134
135 if(NO_MORE_RESULTS == ret) {
136 return 0;
137 }
138
139 stmt->row_count = DBCOUNT(H->link);
140 stmt->column_count = dbnumcols(H->link);
141
142 return 1;
143 }
144
pdo_dblib_stmt_next_rowset(pdo_stmt_t * stmt)145 static int pdo_dblib_stmt_next_rowset(pdo_stmt_t *stmt)
146 {
147 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
148 pdo_dblib_db_handle *H = S->H;
149 RETCODE ret = SUCCESS;
150
151 /* Ideally use dbcanquery here, but there is a bug in FreeTDS's implementation of dbcanquery
152 * It has been resolved but is currently only available in nightly builds
153 */
154 while (NO_MORE_ROWS != ret) {
155 ret = dbnextrow(H->link);
156
157 if (FAIL == ret) {
158 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbnextrow() returned FAIL");
159 return 0;
160 }
161 }
162
163 return pdo_dblib_stmt_next_rowset_no_cancel(stmt);
164 }
165
pdo_dblib_stmt_execute(pdo_stmt_t * stmt)166 static int pdo_dblib_stmt_execute(pdo_stmt_t *stmt)
167 {
168 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
169 pdo_dblib_db_handle *H = S->H;
170
171 dbsetuserdata(H->link, (BYTE*) &S->err);
172
173 pdo_dblib_stmt_cursor_closer(stmt);
174
175 if (FAIL == dbcmd(H->link, stmt->active_query_string)) {
176 return 0;
177 }
178
179 if (FAIL == dbsqlexec(H->link)) {
180 return 0;
181 }
182
183 pdo_dblib_stmt_next_rowset_no_cancel(stmt);
184
185 stmt->row_count = DBCOUNT(H->link);
186 stmt->column_count = dbnumcols(H->link);
187
188 return 1;
189 }
190
pdo_dblib_stmt_fetch(pdo_stmt_t * stmt,enum pdo_fetch_orientation ori,zend_long offset)191 static int pdo_dblib_stmt_fetch(pdo_stmt_t *stmt,
192 enum pdo_fetch_orientation ori, zend_long offset)
193 {
194
195 RETCODE ret;
196
197 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
198 pdo_dblib_db_handle *H = S->H;
199
200 ret = dbnextrow(H->link);
201
202 if (FAIL == ret) {
203 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbnextrow() returned FAIL");
204 return 0;
205 }
206
207 if(NO_MORE_ROWS == ret) {
208 return 0;
209 }
210
211 return 1;
212 }
213
pdo_dblib_stmt_describe(pdo_stmt_t * stmt,int colno)214 static int pdo_dblib_stmt_describe(pdo_stmt_t *stmt, int colno)
215 {
216 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
217 pdo_dblib_db_handle *H = S->H;
218 struct pdo_column_data *col;
219 char *fname;
220
221 if(colno >= stmt->column_count || colno < 0) {
222 return FAILURE;
223 }
224
225 if (colno == 0) {
226 S->computed_column_name_count = 0;
227 }
228
229 col = &stmt->columns[colno];
230 fname = (char*)dbcolname(H->link, colno+1);
231
232 if (fname && *fname) {
233 col->name = zend_string_init(fname, strlen(fname), 0);
234 } else {
235 if (S->computed_column_name_count > 0) {
236 char buf[16];
237 int len;
238
239 len = snprintf(buf, sizeof(buf), "computed%d", S->computed_column_name_count);
240 col->name = zend_string_init(buf, len, 0);
241 } else {
242 col->name = zend_string_init("computed", strlen("computed"), 0);
243 }
244
245 S->computed_column_name_count++;
246 }
247
248 col->maxlen = dbcollen(H->link, colno+1);
249 col->param_type = PDO_PARAM_ZVAL;
250
251 return 1;
252 }
253
pdo_dblib_stmt_get_col(pdo_stmt_t * stmt,int colno,char ** ptr,zend_ulong * len,int * caller_frees)254 static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr,
255 zend_ulong *len, int *caller_frees)
256 {
257
258 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
259 pdo_dblib_db_handle *H = S->H;
260
261 int coltype;
262 LPBYTE data;
263 DBCHAR *tmp_data;
264 DBINT data_len, tmp_data_len;
265 zval *zv = NULL;
266
267 coltype = dbcoltype(H->link, colno+1);
268 data = dbdata(H->link, colno+1);
269 data_len = dbdatlen(H->link, colno+1);
270
271 if (data_len != 0 || data != NULL) {
272 /* force stringify if DBBIGINT won't fit in zend_long */
273 /* this should only be an issue for 32-bit machines */
274 if (stmt->dbh->stringify || (coltype == SQLINT8 && sizeof(zend_long) < sizeof(DBBIGINT))) {
275 switch (coltype) {
276 case SQLDECIMAL:
277 case SQLNUMERIC:
278 case SQLMONEY:
279 case SQLMONEY4:
280 case SQLMONEYN:
281 case SQLFLT4:
282 case SQLFLT8:
283 case SQLINT8:
284 case SQLINT4:
285 case SQLINT2:
286 case SQLINT1:
287 case SQLBIT: {
288 if (dbwillconvert(coltype, SQLCHAR)) {
289 tmp_data_len = 32 + (2 * (data_len)); /* FIXME: We allocate more than we need here */
290 tmp_data = emalloc(tmp_data_len);
291 data_len = dbconvert(NULL, coltype, data, data_len, SQLCHAR, (LPBYTE) tmp_data, -1);
292
293 zv = emalloc(sizeof(zval));
294 ZVAL_STRING(zv, tmp_data);
295
296 efree(tmp_data);
297 }
298 break;
299 }
300 }
301 }
302
303 if (!zv) {
304 switch (coltype) {
305 case SQLCHAR:
306 case SQLVARCHAR:
307 case SQLTEXT: {
308 #if ilia_0
309 while (data_len>0 && data[data_len-1] == ' ') { /* nuke trailing whitespace */
310 data_len--;
311 }
312 #endif
313 }
314 case SQLVARBINARY:
315 case SQLBINARY:
316 case SQLIMAGE: {
317 zv = emalloc(sizeof(zval));
318 ZVAL_STRINGL(zv, (DBCHAR *) data, data_len);
319
320 break;
321 }
322 case SQLDATETIME:
323 case SQLDATETIM4: {
324 int dl;
325 DBDATEREC di;
326 DBDATEREC dt;
327
328 dbconvert(H->link, coltype, data, -1, SQLDATETIME, (LPBYTE) &dt, -1);
329 dbdatecrack(H->link, &di, (DBDATETIME *) &dt);
330
331 dl = spprintf(&tmp_data, 20, "%d-%02d-%02d %02d:%02d:%02d",
332 #if defined(PHP_DBLIB_IS_MSSQL) || defined(MSDBLIB)
333 di.year, di.month, di.day, di.hour, di.minute, di.second
334 #else
335 di.dateyear, di.datemonth+1, di.datedmonth, di.datehour, di.dateminute, di.datesecond
336 #endif
337 );
338
339 zv = emalloc(sizeof(zval));
340 ZVAL_STRINGL(zv, tmp_data, dl);
341
342 efree(tmp_data);
343
344 break;
345 }
346 case SQLFLT4: {
347 zv = emalloc(sizeof(zval));
348 ZVAL_DOUBLE(zv, *(DBFLT4 *) data);
349
350 break;
351 }
352 case SQLFLT8: {
353 zv = emalloc(sizeof(zval));
354 ZVAL_DOUBLE(zv, *(DBFLT8 *) data);
355
356 break;
357 }
358 case SQLINT8: {
359 zv = emalloc(sizeof(zval));
360 ZVAL_LONG(zv, *(DBBIGINT *) data);
361
362 break;
363 }
364 case SQLINT4: {
365 zv = emalloc(sizeof(zval));
366 ZVAL_LONG(zv, *(DBINT *) data);
367
368 break;
369 }
370 case SQLINT2: {
371 zv = emalloc(sizeof(zval));
372 ZVAL_LONG(zv, *(DBSMALLINT *) data);
373
374 break;
375 }
376 case SQLINT1:
377 case SQLBIT: {
378 zv = emalloc(sizeof(zval));
379 ZVAL_LONG(zv, *(DBTINYINT *) data);
380
381 break;
382 }
383 case SQLDECIMAL:
384 case SQLNUMERIC:
385 case SQLMONEY:
386 case SQLMONEY4:
387 case SQLMONEYN: {
388 DBFLT8 float_value;
389 dbconvert(NULL, coltype, data, 8, SQLFLT8, (LPBYTE) &float_value, -1);
390
391 zv = emalloc(sizeof(zval));
392 ZVAL_DOUBLE(zv, float_value);
393
394 break;
395 }
396
397 case SQLUNIQUE: {
398 if (H->stringify_uniqueidentifier) {
399 /* 36-char hex string representation */
400 tmp_data_len = 36;
401 tmp_data = safe_emalloc(tmp_data_len, sizeof(char), 1);
402 data_len = dbconvert(NULL, SQLUNIQUE, data, data_len, SQLCHAR, (LPBYTE) tmp_data, tmp_data_len);
403 php_strtoupper(tmp_data, data_len);
404 zv = emalloc(sizeof(zval));
405 ZVAL_STRINGL(zv, tmp_data, data_len);
406 efree(tmp_data);
407
408 } else {
409 /* 16-byte binary representation */
410 zv = emalloc(sizeof(zval));
411 ZVAL_STRINGL(zv, (DBCHAR *) data, 16);
412 }
413 break;
414 }
415
416 default: {
417 if (dbwillconvert(coltype, SQLCHAR)) {
418 tmp_data_len = 32 + (2 * (data_len)); /* FIXME: We allocate more than we need here */
419 tmp_data = emalloc(tmp_data_len);
420 data_len = dbconvert(NULL, coltype, data, data_len, SQLCHAR, (LPBYTE) tmp_data, -1);
421
422 zv = emalloc(sizeof(zval));
423 ZVAL_STRING(zv, tmp_data);
424
425 efree(tmp_data);
426 }
427
428 break;
429 }
430 }
431 }
432 }
433
434 if (zv != NULL) {
435 *ptr = (char*)zv;
436 *len = sizeof(zval);
437 } else {
438 *ptr = NULL;
439 *len = 0;
440 }
441
442 *caller_frees = 1;
443
444 return 1;
445 }
446
pdo_dblib_stmt_get_column_meta(pdo_stmt_t * stmt,zend_long colno,zval * return_value)447 static int pdo_dblib_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)
448 {
449 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
450 pdo_dblib_db_handle *H = S->H;
451 DBTYPEINFO* dbtypeinfo;
452 int coltype;
453
454 if(colno >= stmt->column_count || colno < 0) {
455 return FAILURE;
456 }
457
458 array_init(return_value);
459
460 dbtypeinfo = dbcoltypeinfo(H->link, colno+1);
461
462 if(!dbtypeinfo) return FAILURE;
463
464 coltype = dbcoltype(H->link, colno+1);
465
466 add_assoc_long(return_value, "max_length", dbcollen(H->link, colno+1) );
467 add_assoc_long(return_value, "precision", (int) dbtypeinfo->precision );
468 add_assoc_long(return_value, "scale", (int) dbtypeinfo->scale );
469 add_assoc_string(return_value, "column_source", dbcolsource(H->link, colno+1));
470 add_assoc_string(return_value, "native_type", pdo_dblib_get_field_name(coltype));
471 add_assoc_long(return_value, "native_type_id", coltype);
472 add_assoc_long(return_value, "native_usertype_id", dbcolutype(H->link, colno+1));
473
474 switch (coltype) {
475 case SQLBIT:
476 case SQLINT1:
477 case SQLINT2:
478 case SQLINT4:
479 add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT);
480 break;
481 default:
482 add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
483 break;
484 }
485
486 return 1;
487 }
488
489
490 struct pdo_stmt_methods dblib_stmt_methods = {
491 pdo_dblib_stmt_dtor,
492 pdo_dblib_stmt_execute,
493 pdo_dblib_stmt_fetch,
494 pdo_dblib_stmt_describe,
495 pdo_dblib_stmt_get_col,
496 NULL, /* param hook */
497 NULL, /* set attr */
498 NULL, /* get attr */
499 pdo_dblib_stmt_get_column_meta, /* meta */
500 pdo_dblib_stmt_next_rowset, /* nextrow */
501 pdo_dblib_stmt_cursor_closer
502 };
503