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 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	return 1;
107}
108
109static int pdo_dblib_stmt_dtor(pdo_stmt_t *stmt)
110{
111	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
112
113	efree(S);
114
115	return 1;
116}
117
118static int pdo_dblib_stmt_next_rowset(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
124	ret = dbresults(H->link);
125
126	if (FAIL == ret) {
127		pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbresults() returned FAIL");
128		return 0;
129	}
130
131	if(NO_MORE_RESULTS == ret) {
132		return 0;
133	}
134
135	stmt->row_count = DBCOUNT(H->link);
136	stmt->column_count = dbnumcols(H->link);
137
138	return 1;
139}
140
141static int pdo_dblib_stmt_execute(pdo_stmt_t *stmt)
142{
143	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
144	pdo_dblib_db_handle *H = S->H;
145	RETCODE ret;
146
147	dbsetuserdata(H->link, (BYTE*) &S->err);
148
149	pdo_dblib_stmt_cursor_closer(stmt);
150
151	if (FAIL == dbcmd(H->link, stmt->active_query_string)) {
152		return 0;
153	}
154
155	if (FAIL == dbsqlexec(H->link)) {
156		return 0;
157	}
158
159	ret = pdo_dblib_stmt_next_rowset(stmt);
160
161	stmt->row_count = DBCOUNT(H->link);
162	stmt->column_count = dbnumcols(H->link);
163
164	return 1;
165}
166
167static int pdo_dblib_stmt_fetch(pdo_stmt_t *stmt,
168	enum pdo_fetch_orientation ori, zend_long offset)
169{
170
171	RETCODE ret;
172
173	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
174	pdo_dblib_db_handle *H = S->H;
175
176	ret = dbnextrow(H->link);
177
178	if (FAIL == ret) {
179		pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbnextrow() returned FAIL");
180		return 0;
181	}
182
183	if(NO_MORE_ROWS == ret) {
184		return 0;
185	}
186
187	return 1;
188}
189
190static int pdo_dblib_stmt_describe(pdo_stmt_t *stmt, int colno)
191{
192	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
193	pdo_dblib_db_handle *H = S->H;
194	struct pdo_column_data *col;
195	char *fname;
196
197	if(colno >= stmt->column_count || colno < 0)  {
198		return FAILURE;
199	}
200
201	col = &stmt->columns[colno];
202	fname = (char*)dbcolname(H->link, colno+1);
203
204	if (fname && *fname) {
205		col->name =  zend_string_init(fname, strlen(fname), 0);
206	} else {
207		char buf[16];
208		int len;
209
210		len = snprintf(buf, sizeof(buf), "computed%d", colno);
211		col->name = zend_string_init(buf, len, 0);
212	}
213
214	col->maxlen = dbcollen(H->link, colno+1);
215	col->param_type = PDO_PARAM_STR;
216
217	return 1;
218}
219
220static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr,
221	 zend_ulong *len, int *caller_frees)
222{
223
224	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
225	pdo_dblib_db_handle *H = S->H;
226
227	int coltype;
228	unsigned int tmp_len;
229	char *tmp_ptr = NULL;
230
231	coltype = dbcoltype(H->link, colno+1);
232
233	*len = dbdatlen(H->link, colno+1);
234	*ptr = dbdata(H->link, colno+1);
235
236	if (*len == 0 && *ptr == NULL) {
237		return 1;
238	}
239
240	switch (coltype) {
241		case SQLVARBINARY:
242		case SQLBINARY:
243		case SQLIMAGE:
244		case SQLTEXT:
245			/* FIXME: Above types should be returned as a stream as they can be VERY large */
246		case SQLCHAR:
247		case SQLVARCHAR:
248			tmp_ptr = emalloc(*len + 1);
249			memcpy(tmp_ptr, *ptr, *len);
250			tmp_ptr[*len] = '\0';
251			*ptr = tmp_ptr;
252			break;
253		case SQLMONEY:
254		case SQLMONEY4:
255		case SQLMONEYN: {
256			DBFLT8 money_value;
257			dbconvert(NULL, coltype, *ptr, *len, SQLFLT8, (LPBYTE)&money_value, 8);
258			*len = spprintf(&tmp_ptr, 0, "%.4f", money_value);
259			*ptr = tmp_ptr;
260			break;
261		}
262		case SQLUNIQUE: {
263			*len = 37;
264			tmp_ptr = emalloc(*len + 1);
265			*len = dbconvert(NULL, SQLUNIQUE, *ptr, *len, SQLCHAR, tmp_ptr, *len);
266			php_strtoupper(tmp_ptr, *len);
267			tmp_ptr[36] = '\0';
268			*ptr = tmp_ptr;
269			break;
270		}
271		default:
272			if (dbwillconvert(coltype, SQLCHAR)) {
273				tmp_len = 32 + (2 * (*len)); /* FIXME: We allocate more than we need here */
274				tmp_ptr = emalloc(tmp_len);
275				*len = dbconvert(NULL, coltype, *ptr, *len, SQLCHAR, tmp_ptr, -1);
276				*ptr = tmp_ptr;
277			} else {
278				*len = 0; /* FIXME: Silently fails and returns null on conversion errors */
279				*ptr = NULL;
280			}
281	}
282
283	*caller_frees = 1;
284
285	return 1;
286}
287
288static int pdo_dblib_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,
289		enum pdo_param_event event_type)
290{
291	return 1;
292}
293
294static int pdo_dblib_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)
295{
296	pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
297	pdo_dblib_db_handle *H = S->H;
298	DBTYPEINFO* dbtypeinfo;
299
300	if(colno >= stmt->column_count || colno < 0)  {
301		return FAILURE;
302	}
303
304	array_init(return_value);
305
306	dbtypeinfo = dbcoltypeinfo(H->link, colno+1);
307
308	if(!dbtypeinfo) return FAILURE;
309
310	add_assoc_long(return_value, "max_length", dbcollen(H->link, colno+1) );
311	add_assoc_long(return_value, "precision", (int) dbtypeinfo->precision );
312	add_assoc_long(return_value, "scale", (int) dbtypeinfo->scale );
313	add_assoc_string(return_value, "column_source", dbcolsource(H->link, colno+1));
314	add_assoc_string(return_value, "native_type", pdo_dblib_get_field_name(dbcoltype(H->link, colno+1)));
315	add_assoc_long(return_value, "native_type_id", dbcoltype(H->link, colno+1));
316	add_assoc_long(return_value, "native_usertype_id", dbcolutype(H->link, colno+1));
317
318	return 1;
319}
320
321
322struct pdo_stmt_methods dblib_stmt_methods = {
323	pdo_dblib_stmt_dtor,
324	pdo_dblib_stmt_execute,
325	pdo_dblib_stmt_fetch,
326	pdo_dblib_stmt_describe,
327	pdo_dblib_stmt_get_col,
328	pdo_dblib_stmt_param_hook,
329	NULL, /* set attr */
330	NULL, /* get attr */
331	pdo_dblib_stmt_get_column_meta, /* meta */
332	pdo_dblib_stmt_next_rowset, /* nextrow */
333	pdo_dblib_stmt_cursor_closer
334};
335
336