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 +----------------------------------------------------------------------+
15 */
16
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include "php.h"
22 #include "php_ini.h"
23 #include "ext/standard/info.h"
24 #include "pdo/php_pdo.h"
25 #include "pdo/php_pdo_driver.h"
26 #include "php_pdo_odbc.h"
27 #include "php_pdo_odbc_int.h"
28
29 enum pdo_odbc_conv_result {
30 PDO_ODBC_CONV_NOT_REQUIRED,
31 PDO_ODBC_CONV_OK,
32 PDO_ODBC_CONV_FAIL
33 };
34
pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt * S,SWORD sqltype)35 static int pdo_odbc_sqltype_is_unicode(pdo_odbc_stmt *S, SWORD sqltype)
36 {
37 if (!S->assume_utf8) return 0;
38 switch (sqltype) {
39 #ifdef SQL_WCHAR
40 case SQL_WCHAR:
41 return 1;
42 #endif
43 #ifdef SQL_WLONGVARCHAR
44 case SQL_WLONGVARCHAR:
45 return 1;
46 #endif
47 #ifdef SQL_WVARCHAR
48 case SQL_WVARCHAR:
49 return 1;
50 #endif
51 default:
52 return 0;
53 }
54 }
55
pdo_odbc_utf82ucs2(pdo_stmt_t * stmt,int is_unicode,const char * buf,zend_ulong buflen,zend_ulong * outlen)56 static int pdo_odbc_utf82ucs2(pdo_stmt_t *stmt, int is_unicode, const char *buf,
57 zend_ulong buflen, zend_ulong *outlen)
58 {
59 #ifdef PHP_WIN32
60 if (is_unicode && buflen) {
61 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
62 DWORD ret;
63
64 ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, NULL, 0);
65 if (ret == 0) {
66 /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/
67 return PDO_ODBC_CONV_FAIL;
68 }
69
70 ret *= sizeof(WCHAR);
71
72 if (S->convbufsize <= ret) {
73 S->convbufsize = ret + sizeof(WCHAR);
74 S->convbuf = erealloc(S->convbuf, S->convbufsize);
75 }
76
77 ret = MultiByteToWideChar(CP_UTF8, 0, buf, buflen, (LPWSTR)S->convbuf, S->convbufsize / sizeof(WCHAR));
78 if (ret == 0) {
79 /*printf("%s:%d %d [%d] %.*s\n", __FILE__, __LINE__, GetLastError(), buflen, buflen, buf);*/
80 return PDO_ODBC_CONV_FAIL;
81 }
82
83 ret *= sizeof(WCHAR);
84 *outlen = ret;
85 return PDO_ODBC_CONV_OK;
86 }
87 #endif
88 return PDO_ODBC_CONV_NOT_REQUIRED;
89 }
90
pdo_odbc_ucs22utf8(pdo_stmt_t * stmt,int is_unicode,zval * result)91 static int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, zval *result)
92 {
93 #ifdef PHP_WIN32
94 ZEND_ASSERT(Z_TYPE_P(result) == IS_STRING);
95 if (is_unicode && Z_STRLEN_P(result) != 0) {
96 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
97 DWORD ret;
98
99 ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) Z_STRVAL_P(result), Z_STRLEN_P(result)/sizeof(WCHAR), NULL, 0, NULL, NULL);
100 if (ret == 0) {
101 return PDO_ODBC_CONV_FAIL;
102 }
103
104 zend_string *str = zend_string_alloc(ret, 0);
105 ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) Z_STRVAL_P(result), Z_STRLEN_P(result)/sizeof(WCHAR), ZSTR_VAL(str), ZSTR_LEN(str), NULL, NULL);
106 if (ret == 0) {
107 return PDO_ODBC_CONV_FAIL;
108 }
109
110 ZSTR_VAL(str)[ret] = '\0';
111 zval_ptr_dtor_str(result);
112 ZVAL_STR(result, str);
113 return PDO_ODBC_CONV_OK;
114 }
115 #endif
116 return PDO_ODBC_CONV_NOT_REQUIRED;
117 }
118
free_cols(pdo_stmt_t * stmt,pdo_odbc_stmt * S)119 static void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S)
120 {
121 if (S->cols) {
122 int i;
123
124 for (i = 0; i < S->col_count; i++) {
125 if (S->cols[i].data) {
126 efree(S->cols[i].data);
127 }
128 }
129 efree(S->cols);
130 S->cols = NULL;
131 S->col_count = 0;
132 }
133 }
134
odbc_stmt_dtor(pdo_stmt_t * stmt)135 static int odbc_stmt_dtor(pdo_stmt_t *stmt)
136 {
137 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
138
139 if (S->stmt != SQL_NULL_HANDLE) {
140 if (stmt->executed) {
141 SQLCloseCursor(S->stmt);
142 }
143 SQLFreeHandle(SQL_HANDLE_STMT, S->stmt);
144 S->stmt = SQL_NULL_HANDLE;
145 }
146
147 free_cols(stmt, S);
148 if (S->convbuf) {
149 efree(S->convbuf);
150 }
151 efree(S);
152
153 return 1;
154 }
155
odbc_stmt_execute(pdo_stmt_t * stmt)156 static int odbc_stmt_execute(pdo_stmt_t *stmt)
157 {
158 RETCODE rc;
159 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
160 char *buf = NULL;
161 SQLLEN row_count = -1;
162
163 if (stmt->executed) {
164 SQLCloseCursor(S->stmt);
165 }
166
167 rc = SQLExecute(S->stmt);
168
169 while (rc == SQL_NEED_DATA) {
170 struct pdo_bound_param_data *param;
171
172 rc = SQLParamData(S->stmt, (SQLPOINTER*)¶m);
173 if (rc == SQL_NEED_DATA) {
174 php_stream *stm;
175 int len;
176 pdo_odbc_param *P;
177 zval *parameter;
178
179 P = (pdo_odbc_param*)param->driver_data;
180 if (Z_ISREF(param->parameter)) {
181 parameter = Z_REFVAL(param->parameter);
182 } else {
183 parameter = ¶m->parameter;
184 }
185 if (Z_TYPE_P(parameter) != IS_RESOURCE) {
186 /* they passed in a string */
187 zend_ulong ulen;
188 convert_to_string(parameter);
189
190 switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode,
191 Z_STRVAL_P(parameter),
192 Z_STRLEN_P(parameter),
193 &ulen)) {
194 case PDO_ODBC_CONV_NOT_REQUIRED:
195 SQLPutData(S->stmt, Z_STRVAL_P(parameter),
196 Z_STRLEN_P(parameter));
197 break;
198 case PDO_ODBC_CONV_OK:
199 SQLPutData(S->stmt, S->convbuf, ulen);
200 break;
201 case PDO_ODBC_CONV_FAIL:
202 pdo_odbc_stmt_error("error converting input string");
203 SQLCloseCursor(S->stmt);
204 if (buf) {
205 efree(buf);
206 }
207 return 0;
208 }
209 continue;
210 }
211
212 /* we assume that LOBs are binary and don't need charset
213 * conversion */
214
215 php_stream_from_zval_no_verify(stm, parameter);
216 if (!stm) {
217 /* shouldn't happen either */
218 pdo_odbc_stmt_error("input LOB is no longer a stream");
219 SQLCloseCursor(S->stmt);
220 if (buf) {
221 efree(buf);
222 }
223 return 0;
224 }
225
226 /* now suck data from the stream and stick it into the database */
227 if (buf == NULL) {
228 buf = emalloc(8192);
229 }
230
231 do {
232 len = php_stream_read(stm, buf, 8192);
233 if (len == 0) {
234 break;
235 }
236 SQLPutData(S->stmt, buf, len);
237 } while (1);
238 }
239 }
240
241 if (buf) {
242 efree(buf);
243 }
244
245 switch (rc) {
246 case SQL_SUCCESS:
247 break;
248 case SQL_NO_DATA_FOUND:
249 case SQL_SUCCESS_WITH_INFO:
250 pdo_odbc_stmt_error("SQLExecute");
251 break;
252
253 default:
254 pdo_odbc_stmt_error("SQLExecute");
255 return 0;
256 }
257
258 SQLRowCount(S->stmt, &row_count);
259 stmt->row_count = row_count;
260
261 if (S->cols == NULL) {
262 /* do first-time-only definition of bind/mapping stuff */
263 SQLSMALLINT colcount;
264
265 /* how many columns do we have ? */
266 SQLNumResultCols(S->stmt, &colcount);
267
268 stmt->column_count = S->col_count = (int)colcount;
269 S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));
270 S->going_long = 0;
271 }
272
273 return 1;
274 }
275
odbc_stmt_param_hook(pdo_stmt_t * stmt,struct pdo_bound_param_data * param,enum pdo_param_event event_type)276 static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,
277 enum pdo_param_event event_type)
278 {
279 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
280 RETCODE rc;
281 SWORD sqltype = 0, ctype = 0, scale = 0, nullable = 0;
282 SQLULEN precision = 0;
283 pdo_odbc_param *P;
284 zval *parameter;
285
286 /* we're only interested in parameters for prepared SQL right now */
287 if (param->is_param) {
288
289 switch (event_type) {
290 case PDO_PARAM_EVT_FETCH_PRE:
291 case PDO_PARAM_EVT_FETCH_POST:
292 case PDO_PARAM_EVT_NORMALIZE:
293 /* Do nothing */
294 break;
295
296 case PDO_PARAM_EVT_FREE:
297 P = param->driver_data;
298 if (P) {
299 efree(P);
300 }
301 break;
302
303 case PDO_PARAM_EVT_ALLOC:
304 {
305 /* figure out what we're doing */
306 switch (PDO_PARAM_TYPE(param->param_type)) {
307 case PDO_PARAM_LOB:
308 break;
309
310 case PDO_PARAM_STMT:
311 return 0;
312
313 default:
314 break;
315 }
316
317 rc = SQLDescribeParam(S->stmt, (SQLUSMALLINT) param->paramno+1, &sqltype, &precision, &scale, &nullable);
318 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
319 /* MS Access, for instance, doesn't support SQLDescribeParam,
320 * so we need to guess */
321 switch (PDO_PARAM_TYPE(param->param_type)) {
322 case PDO_PARAM_INT:
323 sqltype = SQL_INTEGER;
324 break;
325 case PDO_PARAM_LOB:
326 sqltype = SQL_LONGVARBINARY;
327 break;
328 default:
329 sqltype = SQL_LONGVARCHAR;
330 }
331 precision = 4000;
332 scale = 5;
333 nullable = 1;
334
335 if (param->max_value_len > 0) {
336 precision = param->max_value_len;
337 }
338 }
339 if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) {
340 ctype = SQL_C_BINARY;
341 } else {
342 ctype = SQL_C_CHAR;
343 }
344
345 P = emalloc(sizeof(*P));
346 param->driver_data = P;
347
348 P->len = 0; /* is re-populated each EXEC_PRE */
349 P->outbuf = NULL;
350
351 P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype);
352 if (P->is_unicode) {
353 /* avoid driver auto-translation: we'll do it ourselves */
354 ctype = SQL_C_BINARY;
355 }
356
357 if ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) {
358 P->paramtype = SQL_PARAM_INPUT_OUTPUT;
359 } else if (param->max_value_len <= 0) {
360 P->paramtype = SQL_PARAM_INPUT;
361 } else {
362 P->paramtype = SQL_PARAM_OUTPUT;
363 }
364
365 if (P->paramtype != SQL_PARAM_INPUT) {
366 if (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_NULL) {
367 /* need an explicit buffer to hold result */
368 P->len = param->max_value_len > 0 ? param->max_value_len : precision;
369 if (P->is_unicode) {
370 P->len *= 2;
371 }
372 P->outbuf = emalloc(P->len + (P->is_unicode ? 2:1));
373 }
374 }
375
376 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) {
377 pdo_odbc_stmt_error("Can't bind a lob for output");
378 return 0;
379 }
380
381 rc = SQLBindParameter(S->stmt, (SQLUSMALLINT) param->paramno+1,
382 P->paramtype, ctype, sqltype, precision, scale,
383 P->paramtype == SQL_PARAM_INPUT ?
384 (SQLPOINTER)param :
385 P->outbuf,
386 P->len,
387 &P->len
388 );
389
390 if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
391 return 1;
392 }
393 pdo_odbc_stmt_error("SQLBindParameter");
394 return 0;
395 }
396
397 case PDO_PARAM_EVT_EXEC_PRE:
398 P = param->driver_data;
399 if (!Z_ISREF(param->parameter)) {
400 parameter = ¶m->parameter;
401 } else {
402 parameter = Z_REFVAL(param->parameter);
403 }
404
405 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
406 if (Z_TYPE_P(parameter) == IS_RESOURCE) {
407 php_stream *stm;
408 php_stream_statbuf sb;
409
410 php_stream_from_zval_no_verify(stm, parameter);
411
412 if (!stm) {
413 return 0;
414 }
415
416 if (0 == php_stream_stat(stm, &sb)) {
417 if (P->outbuf) {
418 int len, amount;
419 char *ptr = P->outbuf;
420 char *end = P->outbuf + P->len;
421
422 P->len = 0;
423 do {
424 amount = end - ptr;
425 if (amount == 0) {
426 break;
427 }
428 if (amount > 8192)
429 amount = 8192;
430 len = php_stream_read(stm, ptr, amount);
431 if (len == 0) {
432 break;
433 }
434 ptr += len;
435 P->len += len;
436 } while (1);
437
438 } else {
439 P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size);
440 }
441 } else {
442 if (P->outbuf) {
443 P->len = 0;
444 } else {
445 P->len = SQL_LEN_DATA_AT_EXEC(0);
446 }
447 }
448 } else {
449 convert_to_string(parameter);
450 if (P->outbuf) {
451 P->len = Z_STRLEN_P(parameter);
452 memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len);
453 } else {
454 P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter));
455 }
456 }
457 } else if (Z_TYPE_P(parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) {
458 P->len = SQL_NULL_DATA;
459 } else {
460 convert_to_string(parameter);
461 if (P->outbuf) {
462 zend_ulong ulen;
463 switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode,
464 Z_STRVAL_P(parameter),
465 Z_STRLEN_P(parameter),
466 &ulen)) {
467 case PDO_ODBC_CONV_FAIL:
468 case PDO_ODBC_CONV_NOT_REQUIRED:
469 P->len = Z_STRLEN_P(parameter);
470 memcpy(P->outbuf, Z_STRVAL_P(parameter), P->len);
471 break;
472 case PDO_ODBC_CONV_OK:
473 P->len = ulen;
474 memcpy(P->outbuf, S->convbuf, P->len);
475 break;
476 }
477 } else {
478 P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter));
479 }
480 }
481 return 1;
482
483 case PDO_PARAM_EVT_EXEC_POST:
484 P = param->driver_data;
485
486 if (P->outbuf) {
487 if (Z_ISREF(param->parameter)) {
488 parameter = Z_REFVAL(param->parameter);
489 } else {
490 parameter = ¶m->parameter;
491 }
492 zval_ptr_dtor(parameter);
493
494 if (P->len >= 0) {
495 ZVAL_STRINGL(parameter, P->outbuf, P->len);
496 switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, parameter)) {
497 case PDO_ODBC_CONV_FAIL:
498 /* something fishy, but allow it to come back as binary */
499 case PDO_ODBC_CONV_NOT_REQUIRED:
500 break;
501 case PDO_ODBC_CONV_OK:
502 break;
503 }
504 } else {
505 ZVAL_NULL(parameter);
506 }
507 }
508 return 1;
509 }
510 }
511 return 1;
512 }
513
odbc_stmt_fetch(pdo_stmt_t * stmt,enum pdo_fetch_orientation ori,zend_long offset)514 static int odbc_stmt_fetch(pdo_stmt_t *stmt,
515 enum pdo_fetch_orientation ori, zend_long offset)
516 {
517 RETCODE rc;
518 SQLSMALLINT odbcori;
519 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
520
521 switch (ori) {
522 case PDO_FETCH_ORI_NEXT: odbcori = SQL_FETCH_NEXT; break;
523 case PDO_FETCH_ORI_PRIOR: odbcori = SQL_FETCH_PRIOR; break;
524 case PDO_FETCH_ORI_FIRST: odbcori = SQL_FETCH_FIRST; break;
525 case PDO_FETCH_ORI_LAST: odbcori = SQL_FETCH_LAST; break;
526 case PDO_FETCH_ORI_ABS: odbcori = SQL_FETCH_ABSOLUTE; break;
527 case PDO_FETCH_ORI_REL: odbcori = SQL_FETCH_RELATIVE; break;
528 default:
529 strcpy(stmt->error_code, "HY106");
530 return 0;
531 }
532 rc = SQLFetchScroll(S->stmt, odbcori, offset);
533
534 if (rc == SQL_SUCCESS) {
535 return 1;
536 }
537 if (rc == SQL_SUCCESS_WITH_INFO) {
538 pdo_odbc_stmt_error("SQLFetchScroll");
539 return 1;
540 }
541
542 if (rc == SQL_NO_DATA) {
543 /* pdo_odbc_stmt_error("SQLFetchScroll"); */
544 return 0;
545 }
546
547 pdo_odbc_stmt_error("SQLFetchScroll");
548
549 return 0;
550 }
551
odbc_stmt_describe(pdo_stmt_t * stmt,int colno)552 static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno)
553 {
554 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
555 struct pdo_column_data *col = &stmt->columns[colno];
556 RETCODE rc;
557 SWORD colnamelen;
558 SQLULEN colsize;
559 SQLLEN displaysize = 0;
560
561 rc = SQLDescribeCol(S->stmt, colno+1, (SQLCHAR *) S->cols[colno].colname,
562 sizeof(S->cols[colno].colname)-1, &colnamelen,
563 &S->cols[colno].coltype, &colsize, NULL, NULL);
564
565 /* This fixes a known issue with SQL Server and (max) lengths,
566 may affect others as well. If we are SQL_VARCHAR,
567 SQL_VARBINARY, or SQL_WVARCHAR (or any of the long variations)
568 and zero is returned from colsize then consider it long */
569 if (0 == colsize &&
570 (S->cols[colno].coltype == SQL_VARCHAR ||
571 S->cols[colno].coltype == SQL_LONGVARCHAR ||
572 #ifdef SQL_WVARCHAR
573 S->cols[colno].coltype == SQL_WVARCHAR ||
574 #endif
575 #ifdef SQL_WLONGVARCHAR
576 S->cols[colno].coltype == SQL_WLONGVARCHAR ||
577 #endif
578 S->cols[colno].coltype == SQL_VARBINARY ||
579 S->cols[colno].coltype == SQL_LONGVARBINARY)) {
580 S->going_long = 1;
581 }
582
583 if (rc != SQL_SUCCESS) {
584 pdo_odbc_stmt_error("SQLDescribeCol");
585 if (rc != SQL_SUCCESS_WITH_INFO) {
586 return 0;
587 }
588 }
589
590 rc = SQLColAttribute(S->stmt, colno+1,
591 SQL_DESC_DISPLAY_SIZE,
592 NULL, 0, NULL, &displaysize);
593
594 if (rc != SQL_SUCCESS) {
595 pdo_odbc_stmt_error("SQLColAttribute");
596 if (rc != SQL_SUCCESS_WITH_INFO) {
597 return 0;
598 }
599 }
600 colsize = displaysize;
601
602 col->maxlen = S->cols[colno].datalen = colsize;
603 col->name = zend_string_init(S->cols[colno].colname, colnamelen, 0);
604 S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype);
605
606 /* tell ODBC to put it straight into our buffer, but only if it
607 * isn't "long" data, and only if we haven't already bound a long
608 * column. */
609 if (colsize < 256 && !S->going_long) {
610 S->cols[colno].data = emalloc(colsize+1);
611 S->cols[colno].is_long = 0;
612
613 rc = SQLBindCol(S->stmt, colno+1,
614 S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR,
615 S->cols[colno].data,
616 S->cols[colno].datalen+1, &S->cols[colno].fetched_len);
617
618 if (rc != SQL_SUCCESS) {
619 pdo_odbc_stmt_error("SQLBindCol");
620 return 0;
621 }
622 } else {
623 /* allocate a smaller buffer to keep around for smaller
624 * "long" columns */
625 S->cols[colno].data = emalloc(256);
626 S->going_long = 1;
627 S->cols[colno].is_long = 1;
628 }
629
630 return 1;
631 }
632
odbc_stmt_get_column_meta(pdo_stmt_t * stmt,zend_long colno,zval * return_value)633 static int odbc_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value)
634 {
635 array_init(return_value);
636 add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR);
637 return 1;
638 }
639
odbc_stmt_get_col(pdo_stmt_t * stmt,int colno,zval * result,enum pdo_param_type * type)640 static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type)
641 {
642 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
643 pdo_odbc_column *C = &S->cols[colno];
644
645 /* if it is a column containing "long" data, perform late binding now */
646 if (C->is_long) {
647 SQLLEN orig_fetched_len = SQL_NULL_DATA;
648 RETCODE rc;
649
650 /* fetch it into C->data, which is allocated with a length
651 * of 256 bytes; if there is more to be had, we then allocate
652 * bigger buffer for the caller to free */
653
654 rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data,
655 256, &C->fetched_len);
656 orig_fetched_len = C->fetched_len;
657
658 if (rc == SQL_SUCCESS && C->fetched_len < 256) {
659 /* all the data fit into our little buffer;
660 * jump down to the generic bound data case */
661 goto in_data;
662 }
663
664 if (rc == SQL_SUCCESS_WITH_INFO || rc == SQL_SUCCESS) {
665 /* this is a 'long column'
666
667 read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks
668 in order into the output buffer; 255 bytes are an optimistic assumption, since the driver may assert
669 more or less NUL bytes at the end; we cater to that later, if actual length information is available
670
671 this loop has to work whether or not SQLGetData() provides the total column length.
672 calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read
673 for that size would be slower except maybe for extremely long columns.*/
674 char *buf2 = emalloc(256);
675 zend_string *str = zend_string_init(C->data, 256, 0);
676 size_t used = 255; /* not 256; the driver NUL terminated the buffer */
677
678 do {
679 C->fetched_len = 0;
680 /* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */
681 rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, buf2, 256, &C->fetched_len);
682
683 /* adjust `used` in case we have length info from the driver */
684 if (orig_fetched_len >= 0 && C->fetched_len >= 0) {
685 SQLLEN fixed_used = orig_fetched_len - C->fetched_len;
686 ZEND_ASSERT(fixed_used <= used + 1);
687 used = fixed_used;
688 }
689
690 /* resize output buffer and reassemble block */
691 if (rc==SQL_SUCCESS_WITH_INFO || (rc==SQL_SUCCESS && C->fetched_len > 255)) {
692 /* point 5, in section "Retrieving Data with SQLGetData" in http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx
693 states that if SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size)
694 (if a driver fails to follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */
695 str = zend_string_realloc(str, used + 256, 0);
696 memcpy(ZSTR_VAL(str) + used, buf2, 256);
697 used = used + 255;
698 } else if (rc==SQL_SUCCESS) {
699 str = zend_string_realloc(str, used + C->fetched_len, 0);
700 memcpy(ZSTR_VAL(str) + used, buf2, C->fetched_len);
701 used = used + C->fetched_len;
702 } else {
703 /* includes SQL_NO_DATA */
704 break;
705 }
706
707 } while (1);
708
709 efree(buf2);
710
711 /* NULL terminate the buffer once, when finished, for use with the rest of PHP */
712 ZSTR_VAL(str)[used] = '\0';
713 ZVAL_STR(result, str);
714 if (C->is_unicode) {
715 goto unicode_conv;
716 }
717 return 1;
718 }
719
720 /* something went caca */
721 return 1;
722 }
723
724 in_data:
725 /* check the indicator to ensure that the data is intact */
726 if (C->fetched_len == SQL_NULL_DATA) {
727 /* A NULL value */
728 ZVAL_NULL(result);
729 return 1;
730 } else if (C->fetched_len >= 0) {
731 /* it was stored perfectly */
732 ZVAL_STRINGL_FAST(result, C->data, C->fetched_len);
733 if (C->is_unicode) {
734 goto unicode_conv;
735 }
736 return 1;
737 } else {
738 /* no data? */
739 ZVAL_NULL(result);
740 return 1;
741 }
742
743 unicode_conv:
744 switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, result)) {
745 case PDO_ODBC_CONV_FAIL:
746 /* oh well. They can have the binary version of it */
747 case PDO_ODBC_CONV_NOT_REQUIRED:
748 /* shouldn't happen... */
749 return 1;
750 case PDO_ODBC_CONV_OK:
751 return 1;
752 }
753 return 1;
754 }
755
odbc_stmt_set_param(pdo_stmt_t * stmt,zend_long attr,zval * val)756 static int odbc_stmt_set_param(pdo_stmt_t *stmt, zend_long attr, zval *val)
757 {
758 SQLRETURN rc;
759 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
760
761 switch (attr) {
762 case PDO_ATTR_CURSOR_NAME:
763 convert_to_string(val);
764 rc = SQLSetCursorName(S->stmt, (SQLCHAR *) Z_STRVAL_P(val), Z_STRLEN_P(val));
765
766 if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
767 return 1;
768 }
769 pdo_odbc_stmt_error("SQLSetCursorName");
770 return 0;
771
772 case PDO_ODBC_ATTR_ASSUME_UTF8:
773 S->assume_utf8 = zval_is_true(val);
774 return 0;
775 default:
776 strcpy(S->einfo.last_err_msg, "Unknown Attribute");
777 S->einfo.what = "setAttribute";
778 strcpy(S->einfo.last_state, "IM001");
779 return -1;
780 }
781 }
782
odbc_stmt_get_attr(pdo_stmt_t * stmt,zend_long attr,zval * val)783 static int odbc_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val)
784 {
785 SQLRETURN rc;
786 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
787
788 switch (attr) {
789 case PDO_ATTR_CURSOR_NAME:
790 {
791 char buf[256];
792 SQLSMALLINT len = 0;
793 rc = SQLGetCursorName(S->stmt, (SQLCHAR *) buf, sizeof(buf), &len);
794
795 if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
796 ZVAL_STRINGL(val, buf, len);
797 return 1;
798 }
799 pdo_odbc_stmt_error("SQLGetCursorName");
800 return 0;
801 }
802
803 case PDO_ODBC_ATTR_ASSUME_UTF8:
804 ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0);
805 return 0;
806
807 default:
808 strcpy(S->einfo.last_err_msg, "Unknown Attribute");
809 S->einfo.what = "getAttribute";
810 strcpy(S->einfo.last_state, "IM001");
811 return -1;
812 }
813 }
814
odbc_stmt_next_rowset(pdo_stmt_t * stmt)815 static int odbc_stmt_next_rowset(pdo_stmt_t *stmt)
816 {
817 SQLRETURN rc;
818 SQLSMALLINT colcount;
819 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
820
821 /* NOTE: can't guarantee that output or input/output parameters
822 * are set until this fella returns SQL_NO_DATA, according to
823 * MSDN ODBC docs */
824 rc = SQLMoreResults(S->stmt);
825
826 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
827 return 0;
828 }
829
830 free_cols(stmt, S);
831 /* how many columns do we have ? */
832 SQLNumResultCols(S->stmt, &colcount);
833 stmt->column_count = S->col_count = (int)colcount;
834 S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));
835 S->going_long = 0;
836
837 return 1;
838 }
839
odbc_stmt_close_cursor(pdo_stmt_t * stmt)840 static int odbc_stmt_close_cursor(pdo_stmt_t *stmt)
841 {
842 SQLRETURN rc;
843 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
844
845 rc = SQLCloseCursor(S->stmt);
846 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
847 return 0;
848 }
849 return 1;
850 }
851
852 const struct pdo_stmt_methods odbc_stmt_methods = {
853 odbc_stmt_dtor,
854 odbc_stmt_execute,
855 odbc_stmt_fetch,
856 odbc_stmt_describe,
857 odbc_stmt_get_col,
858 odbc_stmt_param_hook,
859 odbc_stmt_set_param,
860 odbc_stmt_get_attr,
861 odbc_stmt_get_column_meta,
862 odbc_stmt_next_rowset,
863 odbc_stmt_close_cursor
864 };
865