xref: /PHP-7.2/ext/pdo_dblib/dblib_stmt.c (revision 7a7ec01a)
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