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/info.h"
25 #include "ext/pdo/php_pdo.h"
26 #include "ext/pdo/php_pdo_driver.h"
27 #include "php_pdo_dblib.h"
28 #include "php_pdo_dblib_int.h"
29 #include "zend_exceptions.h"
30
31
32 /* {{{ pdo_dblib_get_field_name
33 *
34 * Return the data type name for a given TDS number
35 *
36 */
pdo_dblib_get_field_name(int type)37 static char *pdo_dblib_get_field_name(int type)
38 {
39 /*
40 * I don't return dbprtype(type) because it does not fully describe the type
41 * (example: varchar is reported as char by dbprtype)
42 *
43 * FIX ME: Cache datatypes from server systypes table in pdo_dblib_handle_factory()
44 * to make this future-proof.
45 */
46
47 switch (type) {
48 case 31: return "nvarchar";
49 case 34: return "image";
50 case 35: return "text";
51 case 36: return "uniqueidentifier";
52 case 37: return "varbinary"; /* & timestamp - Sybase AS12 */
53 case 38: return "bigint"; /* & bigintn - Sybase AS12 */
54 case 39: return "varchar"; /* & sysname & nvarchar - Sybase AS12 */
55 case 40: return "date";
56 case 41: return "time";
57 case 42: return "datetime2";
58 case 43: return "datetimeoffset";
59 case 45: return "binary"; /* Sybase AS12 */
60 case 47: return "char"; /* & nchar & uniqueidentifierstr Sybase AS12 */
61 case 48: return "tinyint";
62 case 50: return "bit"; /* Sybase AS12 */
63 case 52: return "smallint";
64 case 55: return "decimal"; /* Sybase AS12 */
65 case 56: return "int";
66 case 58: return "smalldatetime";
67 case 59: return "real";
68 case 60: return "money";
69 case 61: return "datetime";
70 case 62: return "float";
71 case 63: return "numeric"; /* or uint, ubigint, usmallint Sybase AS12 */
72 case 98: return "sql_variant";
73 case 99: return "ntext";
74 case 104: return "bit";
75 case 106: return "decimal"; /* decimal n on sybase */
76 case 108: return "numeric"; /* numeric n on sybase */
77 case 122: return "smallmoney";
78 case 127: return "bigint";
79 case 165: return "varbinary";
80 case 167: return "varchar";
81 case 173: return "binary";
82 case 175: return "char";
83 case 189: return "timestamp";
84 case 231: return "nvarchar";
85 case 239: return "nchar";
86 case 240: return "geometry";
87 case 241: return "xml";
88 default: return "unknown";
89 }
90 }
91 /* }}} */
92
pdo_dblib_stmt_cursor_closer(pdo_stmt_t * stmt)93 static int pdo_dblib_stmt_cursor_closer(pdo_stmt_t *stmt)
94 {
95 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
96 pdo_dblib_db_handle *H = S->H;
97
98 /* Cancel any pending results */
99 dbcancel(H->link);
100
101 pdo_dblib_err_dtor(&H->err);
102
103 return 1;
104 }
105
pdo_dblib_stmt_dtor(pdo_stmt_t * stmt)106 static int pdo_dblib_stmt_dtor(pdo_stmt_t *stmt)
107 {
108 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
109
110 pdo_dblib_err_dtor(&S->err);
111
112 efree(S);
113
114 return 1;
115 }
116
pdo_dblib_stmt_next_rowset_no_cancel(pdo_stmt_t * stmt)117 static int pdo_dblib_stmt_next_rowset_no_cancel(pdo_stmt_t *stmt)
118 {
119 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
120 pdo_dblib_db_handle *H = S->H;
121 RETCODE ret;
122 int num_fields;
123
124 do {
125 ret = dbresults(H->link);
126 num_fields = dbnumcols(H->link);
127 } while (H->skip_empty_rowsets && num_fields <= 0 && ret == SUCCEED);
128
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 if (H->skip_empty_rowsets && num_fields <= 0) {
140 return 0;
141 }
142
143 stmt->row_count = DBCOUNT(H->link);
144 stmt->column_count = num_fields;
145
146 return 1;
147 }
148
pdo_dblib_stmt_next_rowset(pdo_stmt_t * stmt)149 static int pdo_dblib_stmt_next_rowset(pdo_stmt_t *stmt)
150 {
151 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
152 pdo_dblib_db_handle *H = S->H;
153 RETCODE ret = SUCCESS;
154
155 /* Ideally use dbcanquery here, but there is a bug in FreeTDS's implementation of dbcanquery
156 * It has been resolved but is currently only available in nightly builds
157 */
158 while (NO_MORE_ROWS != ret) {
159 ret = dbnextrow(H->link);
160
161 if (FAIL == ret) {
162 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbnextrow() returned FAIL");
163 return 0;
164 }
165 }
166
167 return pdo_dblib_stmt_next_rowset_no_cancel(stmt);
168 }
169
pdo_dblib_stmt_execute(pdo_stmt_t * stmt)170 static int pdo_dblib_stmt_execute(pdo_stmt_t *stmt)
171 {
172 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
173 pdo_dblib_db_handle *H = S->H;
174
175 dbsetuserdata(H->link, (BYTE*) &S->err);
176
177 pdo_dblib_stmt_cursor_closer(stmt);
178
179 if (FAIL == dbcmd(H->link, ZSTR_VAL(stmt->active_query_string))) {
180 return 0;
181 }
182
183 if (FAIL == dbsqlexec(H->link)) {
184 return 0;
185 }
186
187 pdo_dblib_stmt_next_rowset_no_cancel(stmt);
188
189 stmt->row_count = DBCOUNT(H->link);
190 stmt->column_count = dbnumcols(H->link);
191
192 return 1;
193 }
194
pdo_dblib_stmt_fetch(pdo_stmt_t * stmt,enum pdo_fetch_orientation ori,zend_long offset)195 static int pdo_dblib_stmt_fetch(pdo_stmt_t *stmt,
196 enum pdo_fetch_orientation ori, zend_long offset)
197 {
198
199 RETCODE ret;
200
201 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
202 pdo_dblib_db_handle *H = S->H;
203
204 ret = dbnextrow(H->link);
205
206 if (FAIL == ret) {
207 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO_DBLIB: dbnextrow() returned FAIL");
208 return 0;
209 }
210
211 if(NO_MORE_ROWS == ret) {
212 return 0;
213 }
214
215 return 1;
216 }
217
pdo_dblib_stmt_describe(pdo_stmt_t * stmt,int colno)218 static int pdo_dblib_stmt_describe(pdo_stmt_t *stmt, int colno)
219 {
220 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
221 pdo_dblib_db_handle *H = S->H;
222 struct pdo_column_data *col;
223 char *fname;
224
225 if(colno >= stmt->column_count || colno < 0) {
226 return FAILURE;
227 }
228
229 if (colno == 0) {
230 S->computed_column_name_count = 0;
231 }
232
233 col = &stmt->columns[colno];
234 fname = (char*)dbcolname(H->link, colno+1);
235
236 if (fname && *fname) {
237 col->name = zend_string_init(fname, strlen(fname), 0);
238 } else {
239 if (S->computed_column_name_count > 0) {
240 char buf[16];
241 int len;
242
243 len = snprintf(buf, sizeof(buf), "computed%d", S->computed_column_name_count);
244 col->name = zend_string_init(buf, len, 0);
245 } else {
246 col->name = ZSTR_INIT_LITERAL("computed", 0);
247 }
248
249 S->computed_column_name_count++;
250 }
251
252 col->maxlen = dbcollen(H->link, colno+1);
253
254 return 1;
255 }
256
pdo_dblib_stmt_should_stringify_col(pdo_stmt_t * stmt,int coltype)257 static int pdo_dblib_stmt_should_stringify_col(pdo_stmt_t *stmt, int coltype)
258 {
259 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
260 pdo_dblib_db_handle *H = S->H;
261
262 switch (coltype) {
263 case SQLDECIMAL:
264 case SQLNUMERIC:
265 case SQLMONEY:
266 case SQLMONEY4:
267 case SQLMONEYN:
268 case SQLFLT4:
269 case SQLFLT8:
270 case SQLINT4:
271 case SQLINT2:
272 case SQLINT1:
273 case SQLBIT:
274 if (stmt->dbh->stringify) {
275 return 1;
276 }
277 break;
278
279 case SQLINT8:
280 if (stmt->dbh->stringify) {
281 return 1;
282 }
283
284 /* force stringify if DBBIGINT won't fit in zend_long */
285 /* this should only be an issue for 32-bit machines */
286 if (sizeof(zend_long) < sizeof(DBBIGINT)) {
287 return 1;
288 }
289 break;
290
291 #ifdef SQLMSDATETIME2
292 case SQLMSDATETIME2:
293 #endif
294 case SQLDATETIME:
295 case SQLDATETIM4:
296 if (H->datetime_convert) {
297 return 1;
298 }
299 break;
300 }
301
302 return 0;
303 }
304
pdo_dblib_stmt_stringify_col(int coltype,LPBYTE data,DBINT data_len,zval * zv)305 static void pdo_dblib_stmt_stringify_col(int coltype, LPBYTE data, DBINT data_len, zval *zv)
306 {
307 DBCHAR *tmp_data;
308
309 /* FIXME: We allocate more than we need here */
310 DBINT tmp_data_len = 32 + (2 * (data_len));
311
312 switch (coltype) {
313 case SQLDATETIME:
314 case SQLDATETIM4: {
315 if (tmp_data_len < DATETIME_MAX_LEN) {
316 tmp_data_len = DATETIME_MAX_LEN;
317 }
318 break;
319 }
320 }
321
322 tmp_data = emalloc(tmp_data_len);
323 data_len = dbconvert(NULL, coltype, data, data_len, SQLCHAR, (LPBYTE) tmp_data, tmp_data_len);
324
325 if (data_len > 0) {
326 /* to prevent overflows, tmp_data_len is provided as a dest len for dbconvert()
327 * this code previously passed a dest len of -1
328 * the FreeTDS impl of dbconvert() does an rtrim with that value, so replicate that behavior
329 */
330 while (data_len > 0 && tmp_data[data_len - 1] == ' ') {
331 data_len--;
332 }
333
334 ZVAL_STRINGL(zv, tmp_data, data_len);
335 } else {
336 ZVAL_EMPTY_STRING(zv);
337 }
338
339 efree(tmp_data);
340 }
341
pdo_dblib_stmt_get_col(pdo_stmt_t * stmt,int colno,zval * zv,enum pdo_param_type * type)342 static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *zv, enum pdo_param_type *type)
343 {
344
345 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
346 pdo_dblib_db_handle *H = S->H;
347
348 int coltype;
349 LPBYTE data;
350 DBCHAR *tmp_data;
351 DBINT data_len, tmp_data_len;
352
353 coltype = dbcoltype(H->link, colno+1);
354 data = dbdata(H->link, colno+1);
355 data_len = dbdatlen(H->link, colno+1);
356
357 if (data_len != 0 || data != NULL) {
358 if (pdo_dblib_stmt_should_stringify_col(stmt, coltype) && dbwillconvert(coltype, SQLCHAR)) {
359 pdo_dblib_stmt_stringify_col(coltype, data, data_len, zv);
360 } else {
361 switch (coltype) {
362 case SQLCHAR:
363 case SQLVARCHAR:
364 case SQLTEXT: {
365 #ifdef ilia_0
366 while (data_len>0 && data[data_len-1] == ' ') { /* nuke trailing whitespace */
367 data_len--;
368 }
369 #endif
370 }
371 case SQLVARBINARY:
372 case SQLBINARY:
373 case SQLIMAGE: {
374 ZVAL_STRINGL(zv, (DBCHAR *) data, data_len);
375
376 break;
377 }
378 #ifdef SQLMSDATETIME2
379 case SQLMSDATETIME2:
380 #endif
381 case SQLDATETIME:
382 case SQLDATETIM4: {
383 size_t dl;
384 DBDATEREC di;
385 DBDATEREC dt;
386
387 dbconvert(H->link, coltype, data, -1, SQLDATETIME, (LPBYTE) &dt, -1);
388 dbdatecrack(H->link, &di, (DBDATETIME *) &dt);
389
390 dl = spprintf(&tmp_data, 20, "%04d-%02d-%02d %02d:%02d:%02d",
391 #if defined(PHP_DBLIB_IS_MSSQL) || defined(MSDBLIB)
392 di.year, di.month, di.day, di.hour, di.minute, di.second
393 #else
394 di.dateyear, di.datemonth+1, di.datedmonth, di.datehour, di.dateminute, di.datesecond
395 #endif
396 );
397
398 ZVAL_STRINGL(zv, tmp_data, dl);
399
400 efree(tmp_data);
401
402 break;
403 }
404 case SQLFLT4:
405 ZVAL_DOUBLE(zv, *(DBFLT4 *) data);
406 break;
407 case SQLFLT8:
408 ZVAL_DOUBLE(zv, *(DBFLT8 *) data);
409 break;
410 case SQLINT8:
411 ZVAL_LONG(zv, *(DBBIGINT *) data);
412 break;
413 case SQLINT4:
414 ZVAL_LONG(zv, *(DBINT *) data);
415 break;
416 case SQLINT2:
417 ZVAL_LONG(zv, *(DBSMALLINT *) data);
418 break;
419 case SQLINT1:
420 case SQLBIT:
421 ZVAL_LONG(zv, *(DBTINYINT *) data);
422 break;
423 case SQLDECIMAL:
424 case SQLNUMERIC:
425 case SQLMONEY:
426 case SQLMONEY4:
427 case SQLMONEYN: {
428 DBFLT8 float_value;
429 dbconvert(NULL, coltype, data, 8, SQLFLT8, (LPBYTE) &float_value, -1);
430 ZVAL_DOUBLE(zv, float_value);
431 break;
432 }
433
434 case SQLUNIQUE: {
435 if (H->stringify_uniqueidentifier) {
436 /* 36-char hex string representation */
437 tmp_data_len = 36;
438 tmp_data = safe_emalloc(tmp_data_len, sizeof(char), 1);
439 data_len = dbconvert(NULL, SQLUNIQUE, data, data_len, SQLCHAR, (LPBYTE) tmp_data, tmp_data_len);
440 zend_str_toupper(tmp_data, data_len);
441 ZVAL_STRINGL(zv, tmp_data, data_len);
442 efree(tmp_data);
443 } else {
444 /* 16-byte binary representation */
445 ZVAL_STRINGL(zv, (DBCHAR *) data, 16);
446 }
447 break;
448 }
449
450 default: {
451 if (dbwillconvert(coltype, SQLCHAR)) {
452 pdo_dblib_stmt_stringify_col(coltype, data, data_len, zv);
453 }
454
455 break;
456 }
457 }
458 }
459 }
460
461 return 1;
462 }
463
pdo_dblib_stmt_get_column_meta(pdo_stmt_t * stmt,zend_long colno,zval * return_value)464 static int pdo_dblib_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)
465 {
466 pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data;
467 pdo_dblib_db_handle *H = S->H;
468 DBTYPEINFO* dbtypeinfo;
469 int coltype;
470
471 if(colno >= stmt->column_count || colno < 0) {
472 return FAILURE;
473 }
474
475 array_init(return_value);
476
477 dbtypeinfo = dbcoltypeinfo(H->link, colno+1);
478
479 if(!dbtypeinfo) return FAILURE;
480
481 coltype = dbcoltype(H->link, colno+1);
482
483 add_assoc_long(return_value, "max_length", dbcollen(H->link, colno+1) );
484 add_assoc_long(return_value, "precision", (int) dbtypeinfo->precision );
485 add_assoc_long(return_value, "scale", (int) dbtypeinfo->scale );
486 add_assoc_string(return_value, "column_source", dbcolsource(H->link, colno+1));
487 add_assoc_string(return_value, "native_type", pdo_dblib_get_field_name(coltype));
488 add_assoc_long(return_value, "native_type_id", coltype);
489 add_assoc_long(return_value, "native_usertype_id", dbcolutype(H->link, colno+1));
490
491 switch (coltype) {
492 case SQLBIT:
493 case SQLINT1:
494 case SQLINT2:
495 case SQLINT4:
496 add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT);
497 break;
498 default:
499 add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
500 break;
501 }
502
503 return 1;
504 }
505
506
507 const struct pdo_stmt_methods dblib_stmt_methods = {
508 pdo_dblib_stmt_dtor,
509 pdo_dblib_stmt_execute,
510 pdo_dblib_stmt_fetch,
511 pdo_dblib_stmt_describe,
512 pdo_dblib_stmt_get_col,
513 NULL, /* param hook */
514 NULL, /* set attr */
515 NULL, /* get attr */
516 pdo_dblib_stmt_get_column_meta, /* meta */
517 pdo_dblib_stmt_next_rowset, /* nextrow */
518 pdo_dblib_stmt_cursor_closer
519 };
520