xref: /PHP-7.4/ext/pdo_dblib/dblib_stmt.c (revision 0cf7de1c)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 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