xref: /PHP-8.1/ext/pdo_dblib/dblib_stmt.c (revision 01b3fc03)
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   | https://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, ZSTR_VAL(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 
255 	return 1;
256 }
257 
pdo_dblib_stmt_should_stringify_col(pdo_stmt_t * stmt,int coltype)258 static int pdo_dblib_stmt_should_stringify_col(pdo_stmt_t *stmt, int coltype)
259 {
260 	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
261 	pdo_dblib_db_handle *H = S->H;
262 
263 	switch (coltype) {
264 		case SQLDECIMAL:
265 		case SQLNUMERIC:
266 		case SQLMONEY:
267 		case SQLMONEY4:
268 		case SQLMONEYN:
269 		case SQLFLT4:
270 		case SQLFLT8:
271 		case SQLINT4:
272 		case SQLINT2:
273 		case SQLINT1:
274 		case SQLBIT:
275 			if (stmt->dbh->stringify) {
276 				return 1;
277 			}
278 			break;
279 
280 		case SQLINT8:
281 			if (stmt->dbh->stringify) {
282 				return 1;
283 			}
284 
285 			/* force stringify if DBBIGINT won't fit in zend_long */
286 			/* this should only be an issue for 32-bit machines */
287 			if (sizeof(zend_long) < sizeof(DBBIGINT)) {
288 				return 1;
289 			}
290 			break;
291 
292 #ifdef SQLMSDATETIME2
293 		case SQLMSDATETIME2:
294 #endif
295 		case SQLDATETIME:
296 		case SQLDATETIM4:
297 			if (H->datetime_convert) {
298 				return 1;
299 			}
300 			break;
301 	}
302 
303 	return 0;
304 }
305 
pdo_dblib_stmt_stringify_col(int coltype,LPBYTE data,DBINT data_len,zval * zv)306 static void pdo_dblib_stmt_stringify_col(int coltype, LPBYTE data, DBINT data_len, zval *zv)
307 {
308 	DBCHAR *tmp_data;
309 
310 	/* FIXME: We allocate more than we need here */
311 	DBINT tmp_data_len = 32 + (2 * (data_len));
312 
313 	switch (coltype) {
314 		case SQLDATETIME:
315 		case SQLDATETIM4: {
316 			if (tmp_data_len < DATETIME_MAX_LEN) {
317 				tmp_data_len = DATETIME_MAX_LEN;
318 			}
319 			break;
320 		}
321 	}
322 
323 	tmp_data = emalloc(tmp_data_len);
324 	data_len = dbconvert(NULL, coltype, data, data_len, SQLCHAR, (LPBYTE) tmp_data, tmp_data_len);
325 
326 	if (data_len > 0) {
327 		/* to prevent overflows, tmp_data_len is provided as a dest len for dbconvert()
328 		 * this code previously passed a dest len of -1
329 		 * the FreeTDS impl of dbconvert() does an rtrim with that value, so replicate that behavior
330 		 */
331 		while (data_len > 0 && tmp_data[data_len - 1] == ' ') {
332 			data_len--;
333 		}
334 
335 		ZVAL_STRINGL(zv, tmp_data, data_len);
336 	} else {
337 		ZVAL_EMPTY_STRING(zv);
338 	}
339 
340 	efree(tmp_data);
341 }
342 
pdo_dblib_stmt_get_col(pdo_stmt_t * stmt,int colno,zval * zv,enum pdo_param_type * type)343 static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *zv, enum pdo_param_type *type)
344 {
345 
346 	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
347 	pdo_dblib_db_handle *H = S->H;
348 
349 	int coltype;
350 	LPBYTE data;
351 	DBCHAR *tmp_data;
352 	DBINT data_len, tmp_data_len;
353 
354 	coltype = dbcoltype(H->link, colno+1);
355 	data = dbdata(H->link, colno+1);
356 	data_len = dbdatlen(H->link, colno+1);
357 
358 	if (data_len != 0 || data != NULL) {
359 		if (pdo_dblib_stmt_should_stringify_col(stmt, coltype) && dbwillconvert(coltype, SQLCHAR)) {
360 			pdo_dblib_stmt_stringify_col(coltype, data, data_len, zv);
361 		} else {
362 			switch (coltype) {
363 				case SQLCHAR:
364 				case SQLVARCHAR:
365 				case SQLTEXT: {
366 #ifdef ilia_0
367 					while (data_len>0 && data[data_len-1] == ' ') { /* nuke trailing whitespace */
368 						data_len--;
369 					}
370 #endif
371 				}
372 				case SQLVARBINARY:
373 				case SQLBINARY:
374 				case SQLIMAGE: {
375 					ZVAL_STRINGL(zv, (DBCHAR *) data, data_len);
376 
377 					break;
378 				}
379 #ifdef SQLMSDATETIME2
380 				case SQLMSDATETIME2:
381 #endif
382 				case SQLDATETIME:
383 				case SQLDATETIM4: {
384 					size_t dl;
385 					DBDATEREC di;
386 					DBDATEREC dt;
387 
388 					dbconvert(H->link, coltype, data, -1, SQLDATETIME, (LPBYTE) &dt, -1);
389 					dbdatecrack(H->link, &di, (DBDATETIME *) &dt);
390 
391 					dl = spprintf(&tmp_data, 20, "%04d-%02d-%02d %02d:%02d:%02d",
392 #if defined(PHP_DBLIB_IS_MSSQL) || defined(MSDBLIB)
393 							di.year,     di.month,       di.day,        di.hour,     di.minute,     di.second
394 #else
395 							di.dateyear, di.datemonth+1, di.datedmonth, di.datehour, di.dateminute, di.datesecond
396 #endif
397 					);
398 
399 					ZVAL_STRINGL(zv, tmp_data, dl);
400 
401 					efree(tmp_data);
402 
403 					break;
404 				}
405 				case SQLFLT4:
406 					ZVAL_DOUBLE(zv, *(DBFLT4 *) data);
407 					break;
408 				case SQLFLT8:
409 					ZVAL_DOUBLE(zv, *(DBFLT8 *) data);
410 					break;
411 				case SQLINT8:
412 					ZVAL_LONG(zv, *(DBBIGINT *) data);
413 					break;
414 				case SQLINT4:
415 					ZVAL_LONG(zv, *(DBINT *) data);
416 					break;
417 				case SQLINT2:
418 					ZVAL_LONG(zv, *(DBSMALLINT *) data);
419 					break;
420 				case SQLINT1:
421 				case SQLBIT:
422 					ZVAL_LONG(zv, *(DBTINYINT *) data);
423 					break;
424 				case SQLDECIMAL:
425 				case SQLNUMERIC:
426 				case SQLMONEY:
427 				case SQLMONEY4:
428 				case SQLMONEYN: {
429 					DBFLT8 float_value;
430 					dbconvert(NULL, coltype, data, 8, SQLFLT8, (LPBYTE) &float_value, -1);
431 					ZVAL_DOUBLE(zv, float_value);
432 					break;
433 				}
434 
435 				case SQLUNIQUE: {
436 					if (H->stringify_uniqueidentifier) {
437 						/* 36-char hex string representation */
438 						tmp_data_len = 36;
439 						tmp_data = safe_emalloc(tmp_data_len, sizeof(char), 1);
440 						data_len = dbconvert(NULL, SQLUNIQUE, data, data_len, SQLCHAR, (LPBYTE) tmp_data, tmp_data_len);
441 						php_strtoupper(tmp_data, data_len);
442 						ZVAL_STRINGL(zv, tmp_data, data_len);
443 						efree(tmp_data);
444 					} else {
445 						/* 16-byte binary representation */
446 						ZVAL_STRINGL(zv, (DBCHAR *) data, 16);
447 					}
448 					break;
449 				}
450 
451 				default: {
452 					if (dbwillconvert(coltype, SQLCHAR)) {
453 						pdo_dblib_stmt_stringify_col(coltype, data, data_len, zv);
454 					}
455 
456 					break;
457 				}
458 			}
459 		}
460 	}
461 
462 	return 1;
463 }
464 
pdo_dblib_stmt_get_column_meta(pdo_stmt_t * stmt,zend_long colno,zval * return_value)465 static int pdo_dblib_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)
466 {
467 	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
468 	pdo_dblib_db_handle *H = S->H;
469 	DBTYPEINFO* dbtypeinfo;
470 	int coltype;
471 
472 	if(colno >= stmt->column_count || colno < 0)  {
473 		return FAILURE;
474 	}
475 
476 	array_init(return_value);
477 
478 	dbtypeinfo = dbcoltypeinfo(H->link, colno+1);
479 
480 	if(!dbtypeinfo) return FAILURE;
481 
482 	coltype = dbcoltype(H->link, colno+1);
483 
484 	add_assoc_long(return_value, "max_length", dbcollen(H->link, colno+1) );
485 	add_assoc_long(return_value, "precision", (int) dbtypeinfo->precision );
486 	add_assoc_long(return_value, "scale", (int) dbtypeinfo->scale );
487 	add_assoc_string(return_value, "column_source", dbcolsource(H->link, colno+1));
488 	add_assoc_string(return_value, "native_type", pdo_dblib_get_field_name(coltype));
489 	add_assoc_long(return_value, "native_type_id", coltype);
490 	add_assoc_long(return_value, "native_usertype_id", dbcolutype(H->link, colno+1));
491 
492 	switch (coltype) {
493 		case SQLBIT:
494 		case SQLINT1:
495 		case SQLINT2:
496 		case SQLINT4:
497 			add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT);
498 			break;
499 		default:
500 			add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
501 			break;
502 	}
503 
504 	return 1;
505 }
506 
507 
508 const struct pdo_stmt_methods dblib_stmt_methods = {
509 	pdo_dblib_stmt_dtor,
510 	pdo_dblib_stmt_execute,
511 	pdo_dblib_stmt_fetch,
512 	pdo_dblib_stmt_describe,
513 	pdo_dblib_stmt_get_col,
514 	NULL, /* param hook */
515 	NULL, /* set attr */
516 	NULL, /* get attr */
517 	pdo_dblib_stmt_get_column_meta, /* meta */
518 	pdo_dblib_stmt_next_rowset, /* nextrow */
519 	pdo_dblib_stmt_cursor_closer
520 };
521