xref: /PHP-7.0/ext/pdo_dblib/dblib_stmt.c (revision 478f119a)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2017 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 	RETCODE ret;
171 
172 	dbsetuserdata(H->link, (BYTE*) &S->err);
173 
174 	pdo_dblib_stmt_cursor_closer(stmt);
175 
176 	if (FAIL == dbcmd(H->link, stmt->active_query_string)) {
177 		return 0;
178 	}
179 
180 	if (FAIL == dbsqlexec(H->link)) {
181 		return 0;
182 	}
183 
184 	ret = pdo_dblib_stmt_next_rowset_no_cancel(stmt);
185 
186 	stmt->row_count = DBCOUNT(H->link);
187 	stmt->column_count = dbnumcols(H->link);
188 
189 	return 1;
190 }
191 
pdo_dblib_stmt_fetch(pdo_stmt_t * stmt,enum pdo_fetch_orientation ori,zend_long offset)192 static int pdo_dblib_stmt_fetch(pdo_stmt_t *stmt,
193 	enum pdo_fetch_orientation ori, zend_long offset)
194 {
195 
196 	RETCODE ret;
197 
198 	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
199 	pdo_dblib_db_handle *H = S->H;
200 
201 	ret = dbnextrow(H->link);
202 
203 	if (FAIL == ret) {
204 		pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbnextrow() returned FAIL");
205 		return 0;
206 	}
207 
208 	if(NO_MORE_ROWS == ret) {
209 		return 0;
210 	}
211 
212 	return 1;
213 }
214 
pdo_dblib_stmt_describe(pdo_stmt_t * stmt,int colno)215 static int pdo_dblib_stmt_describe(pdo_stmt_t *stmt, int colno)
216 {
217 	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
218 	pdo_dblib_db_handle *H = S->H;
219 	struct pdo_column_data *col;
220 	char *fname;
221 
222 	if(colno >= stmt->column_count || colno < 0)  {
223 		return FAILURE;
224 	}
225 
226 	if (colno == 0) {
227 		S->computed_column_name_count = 0;
228 	}
229 
230 	col = &stmt->columns[colno];
231 	fname = (char*)dbcolname(H->link, colno+1);
232 
233 	if (fname && *fname) {
234 		col->name =  zend_string_init(fname, strlen(fname), 0);
235 	} else {
236 		if (S->computed_column_name_count > 0) {
237 			char buf[16];
238 			int len;
239 
240 			len = snprintf(buf, sizeof(buf), "computed%d", S->computed_column_name_count);
241 			col->name = zend_string_init(buf, len, 0);
242 		} else {
243 			col->name = zend_string_init("computed", strlen("computed"), 0);
244 		}
245 
246 		S->computed_column_name_count++;
247 	}
248 
249 	col->maxlen = dbcollen(H->link, colno+1);
250 	col->param_type = PDO_PARAM_ZVAL;
251 
252 	return 1;
253 }
254 
pdo_dblib_stmt_get_col(pdo_stmt_t * stmt,int colno,char ** ptr,zend_ulong * len,int * caller_frees)255 static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr,
256 	 zend_ulong *len, int *caller_frees)
257 {
258 
259 	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
260 	pdo_dblib_db_handle *H = S->H;
261 
262 	int coltype;
263 	char *data, *tmp_data;
264 	unsigned int 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 		if (stmt->dbh->stringify) {
273 			switch (coltype) {
274 				case SQLDECIMAL:
275 				case SQLNUMERIC:
276 				case SQLMONEY:
277 				case SQLMONEY4:
278 				case SQLMONEYN:
279 				case SQLFLT4:
280 				case SQLFLT8:
281 				case SQLINT4:
282 				case SQLINT2:
283 				case SQLINT1:
284 				case SQLBIT: {
285 					if (dbwillconvert(coltype, SQLCHAR)) {
286 						tmp_data_len = 32 + (2 * (data_len)); /* FIXME: We allocate more than we need here */
287 						tmp_data = emalloc(tmp_data_len);
288 						data_len = dbconvert(NULL, coltype, data, data_len, SQLCHAR, tmp_data, -1);
289 
290 						zv = emalloc(sizeof(zval));
291 						ZVAL_STRING(zv, tmp_data);
292 
293 						efree(tmp_data);
294 					}
295 					break;
296 				}
297 			}
298 		}
299 
300 		if (!zv) {
301 			switch (coltype) {
302 				case SQLCHAR:
303 				case SQLVARCHAR:
304 				case SQLTEXT: {
305 #if ilia_0
306 					while (data_len>0 && data[data_len-1] == ' ') { /* nuke trailing whitespace */
307 						data_len--;
308 					}
309 #endif
310 				}
311 				case SQLVARBINARY:
312 				case SQLBINARY:
313 				case SQLIMAGE: {
314 					zv = emalloc(sizeof(zval));
315 					ZVAL_STRINGL(zv, data, data_len);
316 
317 					break;
318 				}
319 				case SQLDATETIME:
320 				case SQLDATETIM4: {
321 					int dl;
322 					DBDATEREC di;
323 					DBDATEREC dt;
324 
325 					dbconvert(H->link, coltype, data, -1, SQLDATETIME, (LPBYTE) &dt, -1);
326 					dbdatecrack(H->link, &di, (DBDATETIME *) &dt);
327 
328 					dl = spprintf(&tmp_data, 20, "%d-%02d-%02d %02d:%02d:%02d",
329 #if defined(PHP_DBLIB_IS_MSSQL) || defined(MSDBLIB)
330 							di.year,     di.month,       di.day,        di.hour,     di.minute,     di.second
331 #else
332 							di.dateyear, di.datemonth+1, di.datedmonth, di.datehour, di.dateminute, di.datesecond
333 #endif
334 					);
335 
336 					zv = emalloc(sizeof(zval));
337 					ZVAL_STRINGL(zv, tmp_data, dl);
338 
339 					efree(tmp_data);
340 
341 					break;
342 				}
343 				case SQLFLT4: {
344 					zv = emalloc(sizeof(zval));
345 					ZVAL_DOUBLE(zv, (double) (*(DBFLT4 *) data));
346 
347 					break;
348 				}
349 				case SQLFLT8: {
350 					zv = emalloc(sizeof(zval));
351 					ZVAL_DOUBLE(zv, (double) (*(DBFLT8 *) data));
352 
353 					break;
354 				}
355 				case SQLINT4: {
356 					zv = emalloc(sizeof(zval));
357 					ZVAL_LONG(zv, (long) ((int) *(DBINT *) data));
358 
359 					break;
360 				}
361 				case SQLINT2: {
362 					zv = emalloc(sizeof(zval));
363 					ZVAL_LONG(zv, (long) ((int) *(DBSMALLINT *) data));
364 
365 					break;
366 				}
367 				case SQLINT1:
368 				case SQLBIT: {
369 					zv = emalloc(sizeof(zval));
370 					ZVAL_LONG(zv, (long) ((int) *(DBTINYINT *) data));
371 
372 					break;
373 				}
374 				case SQLDECIMAL:
375 				case SQLNUMERIC:
376 				case SQLMONEY:
377 				case SQLMONEY4:
378 				case SQLMONEYN: {
379 					DBFLT8 float_value;
380 					dbconvert(NULL, coltype, data, 8, SQLFLT8, (LPBYTE)&float_value, -1);
381 
382 					zv = emalloc(sizeof(zval));
383 					ZVAL_DOUBLE(zv, float_value);
384 
385 					break;
386 				}
387 
388 #ifdef SQLUNIQUE
389 				case SQLUNIQUE: {
390 #else
391 				case 36: { /* FreeTDS hack */
392 #endif
393 					if (H->stringify_uniqueidentifier) { // 36-char hex string representation
394 						tmp_data_len = 36;
395 						tmp_data = safe_emalloc(tmp_data_len, sizeof(char), 1);
396 						data_len = (unsigned int) dbconvert(NULL, SQLUNIQUE, (BYTE*)data, data_len, SQLCHAR, (BYTE*)tmp_data, tmp_data_len);
397 						php_strtoupper(tmp_data, data_len);
398 						zv = emalloc(sizeof(zval));
399 						ZVAL_STRINGL(zv, tmp_data, data_len);
400 						efree(tmp_data);
401 
402 					} else { // a 16-byte binary representation
403 						zv = emalloc(sizeof(zval));
404 						ZVAL_STRINGL(zv, data, 16);
405 					}
406 					break;
407 				}
408 
409 				default: {
410 					if (dbwillconvert(coltype, SQLCHAR)) {
411 						tmp_data_len = 32 + (2 * (data_len)); /* FIXME: We allocate more than we need here */
412 						tmp_data = emalloc(tmp_data_len);
413 						data_len = dbconvert(NULL, coltype, data, data_len, SQLCHAR, tmp_data, -1);
414 
415 						zv = emalloc(sizeof(zval));
416 						ZVAL_STRING(zv, tmp_data);
417 
418 						efree(tmp_data);
419 					}
420 
421 					break;
422 				}
423 			}
424 		}
425 	}
426 
427 	if (zv != NULL) {
428 		*ptr = (char*)zv;
429 		*len = sizeof(zval);
430 	} else {
431 		*ptr = NULL;
432 		*len = 0;
433 	}
434 
435 	*caller_frees = 1;
436 
437 	return 1;
438 }
439 
440 static int pdo_dblib_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,
441 		enum pdo_param_event event_type)
442 {
443 	return 1;
444 }
445 
446 static int pdo_dblib_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)
447 {
448 	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
449 	pdo_dblib_db_handle *H = S->H;
450 	DBTYPEINFO* dbtypeinfo;
451 	int coltype;
452 
453 	if(colno >= stmt->column_count || colno < 0)  {
454 		return FAILURE;
455 	}
456 
457 	array_init(return_value);
458 
459 	dbtypeinfo = dbcoltypeinfo(H->link, colno+1);
460 
461 	if(!dbtypeinfo) return FAILURE;
462 
463 	coltype = dbcoltype(H->link, colno+1);
464 
465 	add_assoc_long(return_value, "max_length", dbcollen(H->link, colno+1) );
466 	add_assoc_long(return_value, "precision", (int) dbtypeinfo->precision );
467 	add_assoc_long(return_value, "scale", (int) dbtypeinfo->scale );
468 	add_assoc_string(return_value, "column_source", dbcolsource(H->link, colno+1));
469 	add_assoc_string(return_value, "native_type", pdo_dblib_get_field_name(coltype));
470 	add_assoc_long(return_value, "native_type_id", coltype);
471 	add_assoc_long(return_value, "native_usertype_id", dbcolutype(H->link, colno+1));
472 
473 	switch (coltype) {
474 		case SQLBIT:
475 		case SQLINT1:
476 		case SQLINT2:
477 		case SQLINT4:
478 			add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT);
479 			break;
480 		default:
481 			add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
482 			break;
483 	}
484 
485 	return 1;
486 }
487 
488 
489 struct pdo_stmt_methods dblib_stmt_methods = {
490 	pdo_dblib_stmt_dtor,
491 	pdo_dblib_stmt_execute,
492 	pdo_dblib_stmt_fetch,
493 	pdo_dblib_stmt_describe,
494 	pdo_dblib_stmt_get_col,
495 	pdo_dblib_stmt_param_hook,
496 	NULL, /* set attr */
497 	NULL, /* get attr */
498 	pdo_dblib_stmt_get_column_meta, /* meta */
499 	pdo_dblib_stmt_next_rowset, /* nextrow */
500 	pdo_dblib_stmt_cursor_closer
501 };
502