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