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 | http://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, 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 col->param_type = PDO_PARAM_ZVAL;
255
256 return 1;
257 }
258
pdo_dblib_stmt_should_stringify_col(pdo_stmt_t * stmt,int coltype)259 static int pdo_dblib_stmt_should_stringify_col(pdo_stmt_t *stmt, int coltype)
260 {
261 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
262 pdo_dblib_db_handle *H = S->H;
263
264 switch (coltype) {
265 case SQLDECIMAL:
266 case SQLNUMERIC:
267 case SQLMONEY:
268 case SQLMONEY4:
269 case SQLMONEYN:
270 case SQLFLT4:
271 case SQLFLT8:
272 case SQLINT4:
273 case SQLINT2:
274 case SQLINT1:
275 case SQLBIT:
276 if (stmt->dbh->stringify) {
277 return 1;
278 }
279 break;
280
281 case SQLINT8:
282 if (stmt->dbh->stringify) {
283 return 1;
284 }
285
286 /* force stringify if DBBIGINT won't fit in zend_long */
287 /* this should only be an issue for 32-bit machines */
288 if (sizeof(zend_long) < sizeof(DBBIGINT)) {
289 return 1;
290 }
291 break;
292
293 #ifdef SQLMSDATETIME2
294 case SQLMSDATETIME2:
295 #endif
296 case SQLDATETIME:
297 case SQLDATETIM4:
298 if (H->datetime_convert) {
299 return 1;
300 }
301 break;
302 }
303
304 return 0;
305 }
306
pdo_dblib_stmt_stringify_col(int coltype,LPBYTE data,DBINT data_len,zval ** ptr)307 static void pdo_dblib_stmt_stringify_col(int coltype, LPBYTE data, DBINT data_len, zval **ptr)
308 {
309 DBCHAR *tmp_data;
310 DBINT tmp_data_len;
311 zval *zv;
312
313 /* FIXME: We allocate more than we need here */
314 tmp_data_len = 32 + (2 * (data_len));
315
316 switch (coltype) {
317 case SQLDATETIME:
318 case SQLDATETIM4: {
319 if (tmp_data_len < DATETIME_MAX_LEN) {
320 tmp_data_len = DATETIME_MAX_LEN;
321 }
322 break;
323 }
324 }
325
326 tmp_data = emalloc(tmp_data_len);
327 data_len = dbconvert(NULL, coltype, data, data_len, SQLCHAR, (LPBYTE) tmp_data, tmp_data_len);
328
329 zv = emalloc(sizeof(zval));
330 if (data_len > 0) {
331 /* to prevent overflows, tmp_data_len is provided as a dest len for dbconvert()
332 * this code previously passed a dest len of -1
333 * the FreeTDS impl of dbconvert() does an rtrim with that value, so replicate that behavior
334 */
335 while (data_len > 0 && tmp_data[data_len - 1] == ' ') {
336 data_len--;
337 }
338
339 ZVAL_STRINGL(zv, tmp_data, data_len);
340 } else {
341 ZVAL_EMPTY_STRING(zv);
342 }
343
344 efree(tmp_data);
345
346 *ptr = zv;
347 }
348
pdo_dblib_stmt_get_col(pdo_stmt_t * stmt,int colno,char ** ptr,zend_ulong * len,int * caller_frees)349 static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr,
350 zend_ulong *len, int *caller_frees)
351 {
352
353 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
354 pdo_dblib_db_handle *H = S->H;
355
356 int coltype;
357 LPBYTE data;
358 DBCHAR *tmp_data;
359 DBINT data_len, tmp_data_len;
360 zval *zv = NULL;
361
362 coltype = dbcoltype(H->link, colno+1);
363 data = dbdata(H->link, colno+1);
364 data_len = dbdatlen(H->link, colno+1);
365
366 if (data_len != 0 || data != NULL) {
367 if (pdo_dblib_stmt_should_stringify_col(stmt, coltype) && dbwillconvert(coltype, SQLCHAR)) {
368 pdo_dblib_stmt_stringify_col(coltype, data, data_len, &zv);
369 }
370
371 if (!zv) {
372 switch (coltype) {
373 case SQLCHAR:
374 case SQLVARCHAR:
375 case SQLTEXT: {
376 #ifdef ilia_0
377 while (data_len>0 && data[data_len-1] == ' ') { /* nuke trailing whitespace */
378 data_len--;
379 }
380 #endif
381 }
382 case SQLVARBINARY:
383 case SQLBINARY:
384 case SQLIMAGE: {
385 zv = emalloc(sizeof(zval));
386 ZVAL_STRINGL(zv, (DBCHAR *) data, data_len);
387
388 break;
389 }
390 #ifdef SQLMSDATETIME2
391 case SQLMSDATETIME2:
392 #endif
393 case SQLDATETIME:
394 case SQLDATETIM4: {
395 size_t dl;
396 DBDATEREC di;
397 DBDATEREC dt;
398
399 dbconvert(H->link, coltype, data, -1, SQLDATETIME, (LPBYTE) &dt, -1);
400 dbdatecrack(H->link, &di, (DBDATETIME *) &dt);
401
402 dl = spprintf(&tmp_data, 20, "%04d-%02d-%02d %02d:%02d:%02d",
403 #if defined(PHP_DBLIB_IS_MSSQL) || defined(MSDBLIB)
404 di.year, di.month, di.day, di.hour, di.minute, di.second
405 #else
406 di.dateyear, di.datemonth+1, di.datedmonth, di.datehour, di.dateminute, di.datesecond
407 #endif
408 );
409
410 zv = emalloc(sizeof(zval));
411 ZVAL_STRINGL(zv, tmp_data, dl);
412
413 efree(tmp_data);
414
415 break;
416 }
417 case SQLFLT4: {
418 zv = emalloc(sizeof(zval));
419 ZVAL_DOUBLE(zv, *(DBFLT4 *) data);
420
421 break;
422 }
423 case SQLFLT8: {
424 zv = emalloc(sizeof(zval));
425 ZVAL_DOUBLE(zv, *(DBFLT8 *) data);
426
427 break;
428 }
429 case SQLINT8: {
430 zv = emalloc(sizeof(zval));
431 ZVAL_LONG(zv, *(DBBIGINT *) data);
432
433 break;
434 }
435 case SQLINT4: {
436 zv = emalloc(sizeof(zval));
437 ZVAL_LONG(zv, *(DBINT *) data);
438
439 break;
440 }
441 case SQLINT2: {
442 zv = emalloc(sizeof(zval));
443 ZVAL_LONG(zv, *(DBSMALLINT *) data);
444
445 break;
446 }
447 case SQLINT1:
448 case SQLBIT: {
449 zv = emalloc(sizeof(zval));
450 ZVAL_LONG(zv, *(DBTINYINT *) data);
451
452 break;
453 }
454 case SQLDECIMAL:
455 case SQLNUMERIC:
456 case SQLMONEY:
457 case SQLMONEY4:
458 case SQLMONEYN: {
459 DBFLT8 float_value;
460 dbconvert(NULL, coltype, data, 8, SQLFLT8, (LPBYTE) &float_value, -1);
461
462 zv = emalloc(sizeof(zval));
463 ZVAL_DOUBLE(zv, float_value);
464
465 break;
466 }
467
468 case SQLUNIQUE: {
469 if (H->stringify_uniqueidentifier) {
470 /* 36-char hex string representation */
471 tmp_data_len = 36;
472 tmp_data = safe_emalloc(tmp_data_len, sizeof(char), 1);
473 data_len = dbconvert(NULL, SQLUNIQUE, data, data_len, SQLCHAR, (LPBYTE) tmp_data, tmp_data_len);
474 php_strtoupper(tmp_data, data_len);
475 zv = emalloc(sizeof(zval));
476 ZVAL_STRINGL(zv, tmp_data, data_len);
477 efree(tmp_data);
478 } else {
479 /* 16-byte binary representation */
480 zv = emalloc(sizeof(zval));
481 ZVAL_STRINGL(zv, (DBCHAR *) data, 16);
482 }
483 break;
484 }
485
486 default: {
487 if (dbwillconvert(coltype, SQLCHAR)) {
488 pdo_dblib_stmt_stringify_col(coltype, data, data_len, &zv);
489 }
490
491 break;
492 }
493 }
494 }
495 }
496
497 if (zv != NULL) {
498 *ptr = (char*)zv;
499 *len = sizeof(zval);
500 } else {
501 *ptr = NULL;
502 *len = 0;
503 }
504
505 *caller_frees = 1;
506
507 return 1;
508 }
509
pdo_dblib_stmt_get_column_meta(pdo_stmt_t * stmt,zend_long colno,zval * return_value)510 static int pdo_dblib_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)
511 {
512 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
513 pdo_dblib_db_handle *H = S->H;
514 DBTYPEINFO* dbtypeinfo;
515 int coltype;
516
517 if(colno >= stmt->column_count || colno < 0) {
518 return FAILURE;
519 }
520
521 array_init(return_value);
522
523 dbtypeinfo = dbcoltypeinfo(H->link, colno+1);
524
525 if(!dbtypeinfo) return FAILURE;
526
527 coltype = dbcoltype(H->link, colno+1);
528
529 add_assoc_long(return_value, "max_length", dbcollen(H->link, colno+1) );
530 add_assoc_long(return_value, "precision", (int) dbtypeinfo->precision );
531 add_assoc_long(return_value, "scale", (int) dbtypeinfo->scale );
532 add_assoc_string(return_value, "column_source", dbcolsource(H->link, colno+1));
533 add_assoc_string(return_value, "native_type", pdo_dblib_get_field_name(coltype));
534 add_assoc_long(return_value, "native_type_id", coltype);
535 add_assoc_long(return_value, "native_usertype_id", dbcolutype(H->link, colno+1));
536
537 switch (coltype) {
538 case SQLBIT:
539 case SQLINT1:
540 case SQLINT2:
541 case SQLINT4:
542 add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT);
543 break;
544 default:
545 add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
546 break;
547 }
548
549 return 1;
550 }
551
552
553 const struct pdo_stmt_methods dblib_stmt_methods = {
554 pdo_dblib_stmt_dtor,
555 pdo_dblib_stmt_execute,
556 pdo_dblib_stmt_fetch,
557 pdo_dblib_stmt_describe,
558 pdo_dblib_stmt_get_col,
559 NULL, /* param hook */
560 NULL, /* set attr */
561 NULL, /* get attr */
562 pdo_dblib_stmt_get_column_meta, /* meta */
563 pdo_dblib_stmt_next_rowset, /* nextrow */
564 pdo_dblib_stmt_cursor_closer
565 };
566