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