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 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include "php.h"
25 #include "php_ini.h"
26 #include "ext/standard/php_string.h"
27 #include "ext/standard/info.h"
28 #include "pdo/php_pdo.h"
29 #include "pdo/php_pdo_driver.h"
30 #include "php_pdo_dblib.h"
31 #include "php_pdo_dblib_int.h"
32 #include "zend_exceptions.h"
33
34
35 /* {{{ pdo_dblib_get_field_name
36 *
37 * Return the data type name for a given TDS number
38 *
39 */
pdo_dblib_get_field_name(int type)40 static char *pdo_dblib_get_field_name(int type)
41 {
42 /*
43 * I don't return dbprtype(type) because it does not fully describe the type
44 * (example: varchar is reported as char by dbprtype)
45 *
46 * FIX ME: Cache datatypes from server systypes table in pdo_dblib_handle_factory()
47 * to make this future proof.
48 */
49
50 switch (type) {
51 case 31: return "nvarchar";
52 case 34: return "image";
53 case 35: return "text";
54 case 36: return "uniqueidentifier";
55 case 37: return "varbinary"; /* & timestamp - Sybase AS12 */
56 case 38: return "bigint"; /* & bigintn - Sybase AS12 */
57 case 39: return "varchar"; /* & sysname & nvarchar - Sybase AS12 */
58 case 40: return "date";
59 case 41: return "time";
60 case 42: return "datetime2";
61 case 43: return "datetimeoffset";
62 case 45: return "binary"; /* Sybase AS12 */
63 case 47: return "char"; /* & nchar & uniqueidentifierstr Sybase AS12 */
64 case 48: return "tinyint";
65 case 50: return "bit"; /* Sybase AS12 */
66 case 52: return "smallint";
67 case 55: return "decimal"; /* Sybase AS12 */
68 case 56: return "int";
69 case 58: return "smalldatetime";
70 case 59: return "real";
71 case 60: return "money";
72 case 61: return "datetime";
73 case 62: return "float";
74 case 63: return "numeric"; /* or uint, ubigint, usmallint Sybase AS12 */
75 case 98: return "sql_variant";
76 case 99: return "ntext";
77 case 104: return "bit";
78 case 106: return "decimal"; /* decimal n on sybase */
79 case 108: return "numeric"; /* numeric n on sybase */
80 case 122: return "smallmoney";
81 case 127: return "bigint";
82 case 165: return "varbinary";
83 case 167: return "varchar";
84 case 173: return "binary";
85 case 175: return "char";
86 case 189: return "timestamp";
87 case 231: return "nvarchar";
88 case 239: return "nchar";
89 case 240: return "geometry";
90 case 241: return "xml";
91 default: return "unknown";
92 }
93 }
94 /* }}} */
95
pdo_dblib_stmt_cursor_closer(pdo_stmt_t * stmt)96 static int pdo_dblib_stmt_cursor_closer(pdo_stmt_t *stmt)
97 {
98 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
99 pdo_dblib_db_handle *H = S->H;
100
101 /* Cancel any pending results */
102 dbcancel(H->link);
103
104 pdo_dblib_err_dtor(&H->err);
105
106 return 1;
107 }
108
pdo_dblib_stmt_dtor(pdo_stmt_t * stmt)109 static int pdo_dblib_stmt_dtor(pdo_stmt_t *stmt)
110 {
111 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
112
113 pdo_dblib_err_dtor(&S->err);
114
115 efree(S);
116
117 return 1;
118 }
119
pdo_dblib_stmt_next_rowset_no_cancel(pdo_stmt_t * stmt)120 static int pdo_dblib_stmt_next_rowset_no_cancel(pdo_stmt_t *stmt)
121 {
122 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
123 pdo_dblib_db_handle *H = S->H;
124 RETCODE ret;
125 int num_fields;
126
127 do {
128 ret = dbresults(H->link);
129 num_fields = dbnumcols(H->link);
130 } while (H->skip_empty_rowsets && num_fields <= 0 && ret == SUCCEED);
131
132
133 if (FAIL == ret) {
134 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbresults() returned FAIL");
135 return 0;
136 }
137
138 if (NO_MORE_RESULTS == ret) {
139 return 0;
140 }
141
142 if (H->skip_empty_rowsets && num_fields <= 0) {
143 return 0;
144 }
145
146 stmt->row_count = DBCOUNT(H->link);
147 stmt->column_count = num_fields;
148
149 return 1;
150 }
151
pdo_dblib_stmt_next_rowset(pdo_stmt_t * stmt)152 static int pdo_dblib_stmt_next_rowset(pdo_stmt_t *stmt)
153 {
154 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
155 pdo_dblib_db_handle *H = S->H;
156 RETCODE ret = SUCCESS;
157
158 /* Ideally use dbcanquery here, but there is a bug in FreeTDS's implementation of dbcanquery
159 * It has been resolved but is currently only available in nightly builds
160 */
161 while (NO_MORE_ROWS != ret) {
162 ret = dbnextrow(H->link);
163
164 if (FAIL == ret) {
165 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbnextrow() returned FAIL");
166 return 0;
167 }
168 }
169
170 return pdo_dblib_stmt_next_rowset_no_cancel(stmt);
171 }
172
pdo_dblib_stmt_execute(pdo_stmt_t * stmt)173 static int pdo_dblib_stmt_execute(pdo_stmt_t *stmt)
174 {
175 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
176 pdo_dblib_db_handle *H = S->H;
177
178 dbsetuserdata(H->link, (BYTE*) &S->err);
179
180 pdo_dblib_stmt_cursor_closer(stmt);
181
182 if (FAIL == dbcmd(H->link, stmt->active_query_string)) {
183 return 0;
184 }
185
186 if (FAIL == dbsqlexec(H->link)) {
187 return 0;
188 }
189
190 pdo_dblib_stmt_next_rowset_no_cancel(stmt);
191
192 stmt->row_count = DBCOUNT(H->link);
193 stmt->column_count = dbnumcols(H->link);
194
195 return 1;
196 }
197
pdo_dblib_stmt_fetch(pdo_stmt_t * stmt,enum pdo_fetch_orientation ori,zend_long offset)198 static int pdo_dblib_stmt_fetch(pdo_stmt_t *stmt,
199 enum pdo_fetch_orientation ori, zend_long offset)
200 {
201
202 RETCODE ret;
203
204 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
205 pdo_dblib_db_handle *H = S->H;
206
207 ret = dbnextrow(H->link);
208
209 if (FAIL == ret) {
210 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbnextrow() returned FAIL");
211 return 0;
212 }
213
214 if(NO_MORE_ROWS == ret) {
215 return 0;
216 }
217
218 return 1;
219 }
220
pdo_dblib_stmt_describe(pdo_stmt_t * stmt,int colno)221 static int pdo_dblib_stmt_describe(pdo_stmt_t *stmt, int colno)
222 {
223 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
224 pdo_dblib_db_handle *H = S->H;
225 struct pdo_column_data *col;
226 char *fname;
227
228 if(colno >= stmt->column_count || colno < 0) {
229 return FAILURE;
230 }
231
232 if (colno == 0) {
233 S->computed_column_name_count = 0;
234 }
235
236 col = &stmt->columns[colno];
237 fname = (char*)dbcolname(H->link, colno+1);
238
239 if (fname && *fname) {
240 col->name = zend_string_init(fname, strlen(fname), 0);
241 } else {
242 if (S->computed_column_name_count > 0) {
243 char buf[16];
244 int len;
245
246 len = snprintf(buf, sizeof(buf), "computed%d", S->computed_column_name_count);
247 col->name = zend_string_init(buf, len, 0);
248 } else {
249 col->name = zend_string_init("computed", strlen("computed"), 0);
250 }
251
252 S->computed_column_name_count++;
253 }
254
255 col->maxlen = dbcollen(H->link, colno+1);
256 col->param_type = PDO_PARAM_ZVAL;
257
258 return 1;
259 }
260
pdo_dblib_stmt_should_stringify_col(pdo_stmt_t * stmt,int coltype)261 static int pdo_dblib_stmt_should_stringify_col(pdo_stmt_t *stmt, int coltype)
262 {
263 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
264 pdo_dblib_db_handle *H = S->H;
265
266 switch (coltype) {
267 case SQLDECIMAL:
268 case SQLNUMERIC:
269 case SQLMONEY:
270 case SQLMONEY4:
271 case SQLMONEYN:
272 case SQLFLT4:
273 case SQLFLT8:
274 case SQLINT4:
275 case SQLINT2:
276 case SQLINT1:
277 case SQLBIT:
278 if (stmt->dbh->stringify) {
279 return 1;
280 }
281 break;
282
283 case SQLINT8:
284 if (stmt->dbh->stringify) {
285 return 1;
286 }
287
288 /* force stringify if DBBIGINT won't fit in zend_long */
289 /* this should only be an issue for 32-bit machines */
290 if (sizeof(zend_long) < sizeof(DBBIGINT)) {
291 return 1;
292 }
293 break;
294
295 #ifdef SQLMSDATETIME2
296 case SQLMSDATETIME2:
297 #endif
298 case SQLDATETIME:
299 case SQLDATETIM4:
300 if (H->datetime_convert) {
301 return 1;
302 }
303 break;
304 }
305
306 return 0;
307 }
308
pdo_dblib_stmt_stringify_col(int coltype,LPBYTE data,DBINT data_len,zval ** ptr)309 static void pdo_dblib_stmt_stringify_col(int coltype, LPBYTE data, DBINT data_len, zval **ptr)
310 {
311 DBCHAR *tmp_data;
312 DBINT tmp_data_len;
313 zval *zv;
314
315 /* FIXME: We allocate more than we need here */
316 tmp_data_len = 32 + (2 * (data_len));
317
318 switch (coltype) {
319 case SQLDATETIME:
320 case SQLDATETIM4: {
321 if (tmp_data_len < DATETIME_MAX_LEN) {
322 tmp_data_len = DATETIME_MAX_LEN;
323 }
324 break;
325 }
326 }
327
328 tmp_data = emalloc(tmp_data_len);
329 data_len = dbconvert(NULL, coltype, data, data_len, SQLCHAR, (LPBYTE) tmp_data, tmp_data_len);
330
331 zv = emalloc(sizeof(zval));
332 if (data_len > 0) {
333 /* to prevent overflows, tmp_data_len is provided as a dest len for dbconvert()
334 * this code previously passed a dest len of -1
335 * the FreeTDS impl of dbconvert() does an rtrim with that value, so replicate that behavior
336 */
337 while (data_len > 0 && tmp_data[data_len - 1] == ' ') {
338 data_len--;
339 }
340
341 ZVAL_STRINGL(zv, tmp_data, data_len);
342 } else {
343 ZVAL_EMPTY_STRING(zv);
344 }
345
346 efree(tmp_data);
347
348 *ptr = zv;
349 }
350
pdo_dblib_stmt_get_col(pdo_stmt_t * stmt,int colno,char ** ptr,zend_ulong * len,int * caller_frees)351 static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr,
352 zend_ulong *len, int *caller_frees)
353 {
354
355 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
356 pdo_dblib_db_handle *H = S->H;
357
358 int coltype;
359 LPBYTE data;
360 DBCHAR *tmp_data;
361 DBINT data_len, tmp_data_len;
362 zval *zv = NULL;
363
364 coltype = dbcoltype(H->link, colno+1);
365 data = dbdata(H->link, colno+1);
366 data_len = dbdatlen(H->link, colno+1);
367
368 if (data_len != 0 || data != NULL) {
369 if (pdo_dblib_stmt_should_stringify_col(stmt, coltype) && dbwillconvert(coltype, SQLCHAR)) {
370 pdo_dblib_stmt_stringify_col(coltype, data, data_len, &zv);
371 }
372
373 if (!zv) {
374 switch (coltype) {
375 case SQLCHAR:
376 case SQLVARCHAR:
377 case SQLTEXT: {
378 #if ilia_0
379 while (data_len>0 && data[data_len-1] == ' ') { /* nuke trailing whitespace */
380 data_len--;
381 }
382 #endif
383 }
384 case SQLVARBINARY:
385 case SQLBINARY:
386 case SQLIMAGE: {
387 zv = emalloc(sizeof(zval));
388 ZVAL_STRINGL(zv, (DBCHAR *) data, data_len);
389
390 break;
391 }
392 #ifdef SQLMSDATETIME2
393 case SQLMSDATETIME2:
394 #endif
395 case SQLDATETIME:
396 case SQLDATETIM4: {
397 size_t dl;
398 DBDATEREC di;
399 DBDATEREC dt;
400
401 dbconvert(H->link, coltype, data, -1, SQLDATETIME, (LPBYTE) &dt, -1);
402 dbdatecrack(H->link, &di, (DBDATETIME *) &dt);
403
404 dl = spprintf(&tmp_data, 20, "%04d-%02d-%02d %02d:%02d:%02d",
405 #if defined(PHP_DBLIB_IS_MSSQL) || defined(MSDBLIB)
406 di.year, di.month, di.day, di.hour, di.minute, di.second
407 #else
408 di.dateyear, di.datemonth+1, di.datedmonth, di.datehour, di.dateminute, di.datesecond
409 #endif
410 );
411
412 zv = emalloc(sizeof(zval));
413 ZVAL_STRINGL(zv, tmp_data, dl);
414
415 efree(tmp_data);
416
417 break;
418 }
419 case SQLFLT4: {
420 zv = emalloc(sizeof(zval));
421 ZVAL_DOUBLE(zv, *(DBFLT4 *) data);
422
423 break;
424 }
425 case SQLFLT8: {
426 zv = emalloc(sizeof(zval));
427 ZVAL_DOUBLE(zv, *(DBFLT8 *) data);
428
429 break;
430 }
431 case SQLINT8: {
432 zv = emalloc(sizeof(zval));
433 ZVAL_LONG(zv, *(DBBIGINT *) data);
434
435 break;
436 }
437 case SQLINT4: {
438 zv = emalloc(sizeof(zval));
439 ZVAL_LONG(zv, *(DBINT *) data);
440
441 break;
442 }
443 case SQLINT2: {
444 zv = emalloc(sizeof(zval));
445 ZVAL_LONG(zv, *(DBSMALLINT *) data);
446
447 break;
448 }
449 case SQLINT1:
450 case SQLBIT: {
451 zv = emalloc(sizeof(zval));
452 ZVAL_LONG(zv, *(DBTINYINT *) data);
453
454 break;
455 }
456 case SQLDECIMAL:
457 case SQLNUMERIC:
458 case SQLMONEY:
459 case SQLMONEY4:
460 case SQLMONEYN: {
461 DBFLT8 float_value;
462 dbconvert(NULL, coltype, data, 8, SQLFLT8, (LPBYTE) &float_value, -1);
463
464 zv = emalloc(sizeof(zval));
465 ZVAL_DOUBLE(zv, float_value);
466
467 break;
468 }
469
470 case SQLUNIQUE: {
471 if (H->stringify_uniqueidentifier) {
472 /* 36-char hex string representation */
473 tmp_data_len = 36;
474 tmp_data = safe_emalloc(tmp_data_len, sizeof(char), 1);
475 data_len = dbconvert(NULL, SQLUNIQUE, data, data_len, SQLCHAR, (LPBYTE) tmp_data, tmp_data_len);
476 php_strtoupper(tmp_data, data_len);
477 zv = emalloc(sizeof(zval));
478 ZVAL_STRINGL(zv, tmp_data, data_len);
479 efree(tmp_data);
480 } else {
481 /* 16-byte binary representation */
482 zv = emalloc(sizeof(zval));
483 ZVAL_STRINGL(zv, (DBCHAR *) data, 16);
484 }
485 break;
486 }
487
488 default: {
489 if (dbwillconvert(coltype, SQLCHAR)) {
490 pdo_dblib_stmt_stringify_col(coltype, data, data_len, &zv);
491 }
492
493 break;
494 }
495 }
496 }
497 }
498
499 if (zv != NULL) {
500 *ptr = (char*)zv;
501 *len = sizeof(zval);
502 } else {
503 *ptr = NULL;
504 *len = 0;
505 }
506
507 *caller_frees = 1;
508
509 return 1;
510 }
511
pdo_dblib_stmt_get_column_meta(pdo_stmt_t * stmt,zend_long colno,zval * return_value)512 static int pdo_dblib_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)
513 {
514 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
515 pdo_dblib_db_handle *H = S->H;
516 DBTYPEINFO* dbtypeinfo;
517 int coltype;
518
519 if(colno >= stmt->column_count || colno < 0) {
520 return FAILURE;
521 }
522
523 array_init(return_value);
524
525 dbtypeinfo = dbcoltypeinfo(H->link, colno+1);
526
527 if(!dbtypeinfo) return FAILURE;
528
529 coltype = dbcoltype(H->link, colno+1);
530
531 add_assoc_long(return_value, "max_length", dbcollen(H->link, colno+1) );
532 add_assoc_long(return_value, "precision", (int) dbtypeinfo->precision );
533 add_assoc_long(return_value, "scale", (int) dbtypeinfo->scale );
534 add_assoc_string(return_value, "column_source", dbcolsource(H->link, colno+1));
535 add_assoc_string(return_value, "native_type", pdo_dblib_get_field_name(coltype));
536 add_assoc_long(return_value, "native_type_id", coltype);
537 add_assoc_long(return_value, "native_usertype_id", dbcolutype(H->link, colno+1));
538
539 switch (coltype) {
540 case SQLBIT:
541 case SQLINT1:
542 case SQLINT2:
543 case SQLINT4:
544 add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT);
545 break;
546 default:
547 add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
548 break;
549 }
550
551 return 1;
552 }
553
554
555 const struct pdo_stmt_methods dblib_stmt_methods = {
556 pdo_dblib_stmt_dtor,
557 pdo_dblib_stmt_execute,
558 pdo_dblib_stmt_fetch,
559 pdo_dblib_stmt_describe,
560 pdo_dblib_stmt_get_col,
561 NULL, /* param hook */
562 NULL, /* set attr */
563 NULL, /* get attr */
564 pdo_dblib_stmt_get_column_meta, /* meta */
565 pdo_dblib_stmt_next_rowset, /* nextrow */
566 pdo_dblib_stmt_cursor_closer
567 };
568