1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2016 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
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
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
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
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
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
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
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
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
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 if (rc != SQL_SUCCESS) {
578 pdo_odbc_stmt_error("SQLDescribeCol");
579 if (rc != SQL_SUCCESS_WITH_INFO) {
580 return 0;
581 }
582 }
583
584 rc = SQLColAttribute(S->stmt, colno+1,
585 SQL_DESC_DISPLAY_SIZE,
586 NULL, 0, NULL, &displaysize);
587
588 if (rc != SQL_SUCCESS) {
589 pdo_odbc_stmt_error("SQLColAttribute");
590 if (rc != SQL_SUCCESS_WITH_INFO) {
591 return 0;
592 }
593 }
594 colsize = displaysize;
595
596 col->maxlen = S->cols[colno].datalen = colsize;
597 col->name = zend_string_init(S->cols[colno].colname, colnamelen, 0);
598 S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype);
599
600 /* returning data as a string */
601 col->param_type = PDO_PARAM_STR;
602
603 /* tell ODBC to put it straight into our buffer, but only if it
604 * isn't "long" data, and only if we haven't already bound a long
605 * column. */
606 if (colsize < 256 && !S->going_long) {
607 S->cols[colno].data = emalloc(colsize+1);
608 S->cols[colno].is_long = 0;
609
610 rc = SQLBindCol(S->stmt, colno+1,
611 S->cols[colno].is_unicode ? SQL_C_BINARY : SQL_C_CHAR,
612 S->cols[colno].data,
613 S->cols[colno].datalen+1, &S->cols[colno].fetched_len);
614
615 if (rc != SQL_SUCCESS) {
616 pdo_odbc_stmt_error("SQLBindCol");
617 return 0;
618 }
619 } else {
620 /* allocate a smaller buffer to keep around for smaller
621 * "long" columns */
622 S->cols[colno].data = emalloc(256);
623 S->going_long = 1;
624 S->cols[colno].is_long = 1;
625 }
626
627 return 1;
628 }
629
630 static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, zend_ulong *len, int *caller_frees)
631 {
632 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
633 pdo_odbc_column *C = &S->cols[colno];
634 zend_ulong ulen;
635
636 /* if it is a column containing "long" data, perform late binding now */
637 if (C->is_long) {
638 zend_ulong used = 0;
639 char *buf;
640 RETCODE rc;
641
642 /* fetch it into C->data, which is allocated with a length
643 * of 256 bytes; if there is more to be had, we then allocate
644 * bigger buffer for the caller to free */
645
646 rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data,
647 256, &C->fetched_len);
648
649 if (rc == SQL_SUCCESS) {
650 /* all the data fit into our little buffer;
651 * jump down to the generic bound data case */
652 goto in_data;
653 }
654
655 if (rc == SQL_SUCCESS_WITH_INFO) {
656 /* this is a 'long column'
657
658 read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks
659 in order into the output buffer
660
661 this loop has to work whether or not SQLGetData() provides the total column length.
662 calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read
663 for that size would be slower except maybe for extremely long columns.*/
664 char *buf2;
665
666 buf2 = emalloc(256);
667 buf = estrndup(C->data, 256);
668 used = 255; /* not 256; the driver NUL terminated the buffer */
669
670 do {
671 C->fetched_len = 0;
672 /* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */
673 rc = SQLGetData(S->stmt, colno+1, SQL_C_CHAR, buf2, 256, &C->fetched_len);
674
675 /* resize output buffer and reassemble block */
676 if (rc==SQL_SUCCESS_WITH_INFO) {
677 /* point 5, in section "Retrieving Data with SQLGetData" in http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx
678 states that if SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size)
679 (if a driver fails to follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */
680 buf = erealloc(buf, used + 255+1);
681 memcpy(buf + used, buf2, 255);
682 used = used + 255;
683 } else if (rc==SQL_SUCCESS) {
684 buf = erealloc(buf, used + C->fetched_len+1);
685 memcpy(buf + used, buf2, C->fetched_len);
686 used = used + C->fetched_len;
687 } else {
688 /* includes SQL_NO_DATA */
689 break;
690 }
691
692 } while (1);
693
694 efree(buf2);
695
696 /* NULL terminate the buffer once, when finished, for use with the rest of PHP */
697 buf[used] = '\0';
698
699 *ptr = buf;
700 *caller_frees = 1;
701 *len = used;
702 if (C->is_unicode) {
703 goto unicode_conv;
704 }
705 return 1;
706 }
707
708 /* something went caca */
709 *ptr = NULL;
710 *len = 0;
711 return 1;
712 }
713
714 in_data:
715 /* check the indicator to ensure that the data is intact */
716 if (C->fetched_len == SQL_NULL_DATA) {
717 /* A NULL value */
718 *ptr = NULL;
719 *len = 0;
720 return 1;
721 } else if (C->fetched_len >= 0) {
722 /* it was stored perfectly */
723 *ptr = C->data;
724 *len = C->fetched_len;
725 if (C->is_unicode) {
726 goto unicode_conv;
727 }
728 return 1;
729 } else {
730 /* no data? */
731 *ptr = NULL;
732 *len = 0;
733 return 1;
734 }
735
736 unicode_conv:
737 switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, *ptr, *len, &ulen)) {
738 case PDO_ODBC_CONV_FAIL:
739 /* oh well. They can have the binary version of it */
740 case PDO_ODBC_CONV_NOT_REQUIRED:
741 /* shouldn't happen... */
742 return 1;
743
744 case PDO_ODBC_CONV_OK:
745 if (*caller_frees) {
746 efree(*ptr);
747 }
748 *ptr = emalloc(ulen + 1);
749 *len = ulen;
750 memcpy(*ptr, S->convbuf, ulen+1);
751 *caller_frees = 1;
752 return 1;
753 }
754 return 1;
755 }
756
757 static int odbc_stmt_set_param(pdo_stmt_t *stmt, zend_long attr, zval *val)
758 {
759 SQLRETURN rc;
760 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
761
762 switch (attr) {
763 case PDO_ATTR_CURSOR_NAME:
764 convert_to_string(val);
765 rc = SQLSetCursorName(S->stmt, Z_STRVAL_P(val), Z_STRLEN_P(val));
766
767 if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
768 return 1;
769 }
770 pdo_odbc_stmt_error("SQLSetCursorName");
771 return 0;
772
773 case PDO_ODBC_ATTR_ASSUME_UTF8:
774 S->assume_utf8 = zval_is_true(val);
775 return 0;
776 default:
777 strcpy(S->einfo.last_err_msg, "Unknown Attribute");
778 S->einfo.what = "setAttribute";
779 strcpy(S->einfo.last_state, "IM001");
780 return -1;
781 }
782 }
783
784 static int odbc_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *val)
785 {
786 SQLRETURN rc;
787 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
788
789 switch (attr) {
790 case PDO_ATTR_CURSOR_NAME:
791 {
792 char buf[256];
793 SQLSMALLINT len = 0;
794 rc = SQLGetCursorName(S->stmt, buf, sizeof(buf), &len);
795
796 if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
797 ZVAL_STRINGL(val, buf, len);
798 return 1;
799 }
800 pdo_odbc_stmt_error("SQLGetCursorName");
801 return 0;
802 }
803
804 case PDO_ODBC_ATTR_ASSUME_UTF8:
805 ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0);
806 return 0;
807
808 default:
809 strcpy(S->einfo.last_err_msg, "Unknown Attribute");
810 S->einfo.what = "getAttribute";
811 strcpy(S->einfo.last_state, "IM001");
812 return -1;
813 }
814 }
815
816 static int odbc_stmt_next_rowset(pdo_stmt_t *stmt)
817 {
818 SQLRETURN rc;
819 SQLSMALLINT colcount;
820 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
821
822 /* NOTE: can't guarantee that output or input/output parameters
823 * are set until this fella returns SQL_NO_DATA, according to
824 * MSDN ODBC docs */
825 rc = SQLMoreResults(S->stmt);
826
827 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
828 return 0;
829 }
830
831 free_cols(stmt, S);
832 /* how many columns do we have ? */
833 SQLNumResultCols(S->stmt, &colcount);
834 stmt->column_count = (int)colcount;
835 S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));
836 S->going_long = 0;
837
838 return 1;
839 }
840
841 struct pdo_stmt_methods odbc_stmt_methods = {
842 odbc_stmt_dtor,
843 odbc_stmt_execute,
844 odbc_stmt_fetch,
845 odbc_stmt_describe,
846 odbc_stmt_get_col,
847 odbc_stmt_param_hook,
848 odbc_stmt_set_param,
849 odbc_stmt_get_attr, /* get attr */
850 NULL, /* get column meta */
851 odbc_stmt_next_rowset
852 };
853
854 /*
855 * Local variables:
856 * tab-width: 4
857 * c-basic-offset: 4
858 * End:
859 * vim600: noet sw=4 ts=4 fdm=marker
860 * vim<600: noet sw=4 ts=4
861 */