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