1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 7                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 1997-2016 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 */
42static 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
98static void pdo_dblib_err_dtor(pdo_dblib_err *err)
99{
100	if (err->dberrstr) {
101		efree(err->dberrstr);
102		err->dberrstr = NULL;
103	}
104	if (err->lastmsg) {
105		efree(err->lastmsg);
106		err->lastmsg = NULL;
107	}
108	if (err->oserrstr) {
109		efree(err->oserrstr);
110		err->oserrstr = NULL;
111	}
112}
113
114static int pdo_dblib_stmt_cursor_closer(pdo_stmt_t *stmt)
115{
116	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
117	pdo_dblib_db_handle *H = S->H;
118
119	/* Cancel any pending results */
120	dbcancel(H->link);
121
122	pdo_dblib_err_dtor(&H->err);
123
124	return 1;
125}
126
127static int pdo_dblib_stmt_dtor(pdo_stmt_t *stmt)
128{
129	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
130
131	pdo_dblib_err_dtor(&S->err);
132
133	efree(S);
134
135	return 1;
136}
137
138static int pdo_dblib_stmt_next_rowset(pdo_stmt_t *stmt)
139{
140	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
141	pdo_dblib_db_handle *H = S->H;
142	RETCODE ret;
143
144	ret = dbresults(H->link);
145
146	if (FAIL == ret) {
147		pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbresults() returned FAIL");
148		return 0;
149	}
150
151	if(NO_MORE_RESULTS == ret) {
152		return 0;
153	}
154
155	stmt->row_count = DBCOUNT(H->link);
156	stmt->column_count = dbnumcols(H->link);
157
158	return 1;
159}
160
161static int pdo_dblib_stmt_execute(pdo_stmt_t *stmt)
162{
163	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
164	pdo_dblib_db_handle *H = S->H;
165	RETCODE ret;
166
167	dbsetuserdata(H->link, (BYTE*) &S->err);
168
169	pdo_dblib_stmt_cursor_closer(stmt);
170
171	if (FAIL == dbcmd(H->link, stmt->active_query_string)) {
172		return 0;
173	}
174
175	if (FAIL == dbsqlexec(H->link)) {
176		return 0;
177	}
178
179	ret = pdo_dblib_stmt_next_rowset(stmt);
180
181	stmt->row_count = DBCOUNT(H->link);
182	stmt->column_count = dbnumcols(H->link);
183
184	return 1;
185}
186
187static int pdo_dblib_stmt_fetch(pdo_stmt_t *stmt,
188	enum pdo_fetch_orientation ori, zend_long offset)
189{
190
191	RETCODE ret;
192
193	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
194	pdo_dblib_db_handle *H = S->H;
195
196	ret = dbnextrow(H->link);
197
198	if (FAIL == ret) {
199		pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbnextrow() returned FAIL");
200		return 0;
201	}
202
203	if(NO_MORE_ROWS == ret) {
204		return 0;
205	}
206
207	return 1;
208}
209
210static int pdo_dblib_stmt_describe(pdo_stmt_t *stmt, int colno)
211{
212	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
213	pdo_dblib_db_handle *H = S->H;
214	struct pdo_column_data *col;
215	char *fname;
216
217	if(colno >= stmt->column_count || colno < 0)  {
218		return FAILURE;
219	}
220
221	if (colno == 0) {
222		S->computed_column_name_count = 0;
223	}
224
225	col = &stmt->columns[colno];
226	fname = (char*)dbcolname(H->link, colno+1);
227
228	if (fname && *fname) {
229		col->name =  zend_string_init(fname, strlen(fname), 0);
230	} else {
231		if (S->computed_column_name_count > 0) {
232			char buf[16];
233			int len;
234
235			len = snprintf(buf, sizeof(buf), "computed%d", S->computed_column_name_count);
236			col->name = zend_string_init(buf, len, 0);
237		} else {
238			col->name = zend_string_init("computed", strlen("computed"), 0);
239		}
240
241		S->computed_column_name_count++;
242	}
243
244	col->maxlen = dbcollen(H->link, colno+1);
245	col->param_type = PDO_PARAM_ZVAL;
246
247	return 1;
248}
249
250static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr,
251	 zend_ulong *len, int *caller_frees)
252{
253
254	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
255	pdo_dblib_db_handle *H = S->H;
256
257	int coltype;
258	char *data, *tmp_data;
259	unsigned int data_len, tmp_data_len;
260	zval *zv = NULL;
261
262	coltype = dbcoltype(H->link, colno+1);
263	data = dbdata(H->link, colno+1);
264	data_len = dbdatlen(H->link, colno+1);
265
266	if (data_len != 0 || data != NULL) {
267		if (stmt->dbh->stringify) {
268			switch (coltype) {
269				case SQLFLT4:
270				case SQLFLT8:
271				case SQLINT4:
272				case SQLINT2:
273				case SQLINT1:
274				case SQLBIT: {
275					if (dbwillconvert(coltype, SQLCHAR)) {
276						tmp_data_len = 32 + (2 * (data_len)); /* FIXME: We allocate more than we need here */
277						tmp_data = emalloc(tmp_data_len);
278						data_len = dbconvert(NULL, coltype, data, data_len, SQLCHAR, tmp_data, -1);
279
280						zv = emalloc(sizeof(zval));
281						ZVAL_STRING(zv, tmp_data);
282
283						efree(tmp_data);
284					}
285					break;
286				}
287			}
288		}
289
290		if (!zv) {
291			switch (coltype) {
292				case SQLCHAR:
293				case SQLVARCHAR:
294				case SQLTEXT: {
295#if ilia_0
296					while (data_len>0 && data[data_len-1] == ' ') { /* nuke trailing whitespace */
297						data_len--;
298					}
299#endif
300				}
301				case SQLVARBINARY:
302				case SQLBINARY:
303				case SQLIMAGE: {
304					zv = emalloc(sizeof(zval));
305					ZVAL_STRINGL(zv, data, data_len);
306
307					break;
308				}
309				case SQLDATETIME:
310				case SQLDATETIM4: {
311					int dl;
312					DBDATEREC di;
313					DBDATEREC dt;
314
315					dbconvert(H->link, coltype, data, -1, SQLDATETIME, (LPBYTE) &dt, -1);
316					dbdatecrack(H->link, &di, (DBDATETIME *) &dt);
317
318					dl = spprintf(&tmp_data, 20, "%d-%02d-%02d %02d:%02d:%02d",
319#if defined(PHP_DBLIB_IS_MSSQL) || defined(MSDBLIB)
320							di.year,     di.month,       di.day,        di.hour,     di.minute,     di.second
321#else
322							di.dateyear, di.datemonth+1, di.datedmonth, di.datehour, di.dateminute, di.datesecond
323#endif
324					);
325
326					zv = emalloc(sizeof(zval));
327					ZVAL_STRINGL(zv, tmp_data, dl);
328
329					efree(tmp_data);
330
331					break;
332				}
333				case SQLFLT4: {
334					zv = emalloc(sizeof(zval));
335					ZVAL_DOUBLE(zv, (double) (*(DBFLT4 *) data));
336
337					break;
338				}
339				case SQLFLT8: {
340					zv = emalloc(sizeof(zval));
341					ZVAL_DOUBLE(zv, (double) (*(DBFLT8 *) data));
342
343					break;
344				}
345				case SQLINT4: {
346					zv = emalloc(sizeof(zval));
347					ZVAL_LONG(zv, (long) ((int) *(DBINT *) data));
348
349					break;
350				}
351				case SQLINT2: {
352					zv = emalloc(sizeof(zval));
353					ZVAL_LONG(zv, (long) ((int) *(DBSMALLINT *) data));
354
355					break;
356				}
357				case SQLINT1:
358				case SQLBIT: {
359					zv = emalloc(sizeof(zval));
360					ZVAL_LONG(zv, (long) ((int) *(DBTINYINT *) data));
361
362					break;
363				}
364				case SQLMONEY:
365				case SQLMONEY4:
366				case SQLMONEYN: {
367					DBFLT8 money_value;
368					dbconvert(NULL, coltype, data, 8, SQLFLT8, (LPBYTE)&money_value, -1);
369
370					zv = emalloc(sizeof(zval));
371					ZVAL_DOUBLE(zv, money_value);
372
373					if (stmt->dbh->stringify) {
374						convert_to_string(zv);
375					}
376
377					break;
378				}
379#ifdef SQLUNIQUE
380				case SQLUNIQUE: {
381#else
382				case 36: { /* FreeTDS hack */
383#endif
384					zv = emalloc(sizeof(zval));
385					ZVAL_STRINGL(zv, data, 16); /* uniqueidentifier is a 16-byte binary number */
386
387					break;
388				}
389				default: {
390					if (dbwillconvert(coltype, SQLCHAR)) {
391						tmp_data_len = 32 + (2 * (data_len)); /* FIXME: We allocate more than we need here */
392						tmp_data = emalloc(tmp_data_len);
393						data_len = dbconvert(NULL, coltype, data, data_len, SQLCHAR, tmp_data, -1);
394
395						zv = emalloc(sizeof(zval));
396						ZVAL_STRING(zv, tmp_data);
397
398						efree(tmp_data);
399					}
400
401					break;
402				}
403			}
404		}
405	}
406
407	if (zv != NULL) {
408		*ptr = (char*)zv;
409		*len = sizeof(zval);
410	} else {
411		*ptr = NULL;
412		*len = 0;
413	}
414
415	*caller_frees = 1;
416
417	return 1;
418}
419
420static int pdo_dblib_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,
421		enum pdo_param_event event_type)
422{
423	return 1;
424}
425
426static int pdo_dblib_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)
427{
428	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
429	pdo_dblib_db_handle *H = S->H;
430	DBTYPEINFO* dbtypeinfo;
431	int coltype;
432
433	if(colno >= stmt->column_count || colno < 0)  {
434		return FAILURE;
435	}
436
437	array_init(return_value);
438
439	dbtypeinfo = dbcoltypeinfo(H->link, colno+1);
440
441	if(!dbtypeinfo) return FAILURE;
442
443	coltype = dbcoltype(H->link, colno+1);
444
445	add_assoc_long(return_value, "max_length", dbcollen(H->link, colno+1) );
446	add_assoc_long(return_value, "precision", (int) dbtypeinfo->precision );
447	add_assoc_long(return_value, "scale", (int) dbtypeinfo->scale );
448	add_assoc_string(return_value, "column_source", dbcolsource(H->link, colno+1));
449	add_assoc_string(return_value, "native_type", pdo_dblib_get_field_name(coltype));
450	add_assoc_long(return_value, "native_type_id", coltype);
451	add_assoc_long(return_value, "native_usertype_id", dbcolutype(H->link, colno+1));
452
453	switch (coltype) {
454		case SQLBIT:
455		case SQLINT1:
456		case SQLINT2:
457		case SQLINT4:
458			add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT);
459			break;
460		default:
461			add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
462			break;
463	}
464
465	return 1;
466}
467
468
469struct pdo_stmt_methods dblib_stmt_methods = {
470	pdo_dblib_stmt_dtor,
471	pdo_dblib_stmt_execute,
472	pdo_dblib_stmt_fetch,
473	pdo_dblib_stmt_describe,
474	pdo_dblib_stmt_get_col,
475	pdo_dblib_stmt_param_hook,
476	NULL, /* set attr */
477	NULL, /* get attr */
478	pdo_dblib_stmt_get_column_meta, /* meta */
479	pdo_dblib_stmt_next_rowset, /* nextrow */
480	pdo_dblib_stmt_cursor_closer
481};
482
483