1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
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
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,unsigned long buflen,unsigned long * outlen)60 static int pdo_odbc_utf82ucs2(pdo_stmt_t *stmt, int is_unicode, const char *buf,
61 unsigned long buflen, unsigned long *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,unsigned long buflen,unsigned long * outlen)95 static int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, const char *buf,
96 unsigned long buflen, unsigned long *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 TSRMLS_DC)126 static void free_cols(pdo_stmt_t *stmt, pdo_odbc_stmt *S TSRMLS_DC)
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 TSRMLS_DC)141 static int odbc_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC)
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 TSRMLS_CC);
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 TSRMLS_DC)162 static int odbc_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC)
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
184 P = (pdo_odbc_param*)param->driver_data;
185 if (Z_TYPE_P(param->parameter) != IS_RESOURCE) {
186 /* they passed in a string */
187 unsigned long ulen;
188 convert_to_string(param->parameter);
189
190 switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode,
191 Z_STRVAL_P(param->parameter),
192 Z_STRLEN_P(param->parameter),
193 &ulen)) {
194 case PDO_ODBC_CONV_NOT_REQUIRED:
195 SQLPutData(S->stmt, Z_STRVAL_P(param->parameter),
196 Z_STRLEN_P(param->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, ¶m->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 (!stmt->executed) {
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 = (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 TSRMLS_DC)276 static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param,
277 enum pdo_param_event event_type TSRMLS_DC)
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
285 /* we're only interested in parameters for prepared SQL right now */
286 if (param->is_param) {
287
288 switch (event_type) {
289 case PDO_PARAM_EVT_FETCH_PRE:
290 case PDO_PARAM_EVT_FETCH_POST:
291 case PDO_PARAM_EVT_NORMALIZE:
292 /* Do nothing */
293 break;
294
295 case PDO_PARAM_EVT_FREE:
296 P = param->driver_data;
297 if (P) {
298 efree(P);
299 }
300 break;
301
302 case PDO_PARAM_EVT_ALLOC:
303 {
304 /* figure out what we're doing */
305 switch (PDO_PARAM_TYPE(param->param_type)) {
306 case PDO_PARAM_LOB:
307 break;
308
309 case PDO_PARAM_STMT:
310 return 0;
311
312 default:
313 break;
314 }
315
316 rc = SQLDescribeParam(S->stmt, (SQLUSMALLINT) param->paramno+1, &sqltype, &precision, &scale, &nullable);
317 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
318 /* MS Access, for instance, doesn't support SQLDescribeParam,
319 * so we need to guess */
320 sqltype = PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB ?
321 SQL_LONGVARBINARY :
322 SQL_LONGVARCHAR;
323 precision = 4000;
324 scale = 5;
325 nullable = 1;
326
327 if (param->max_value_len > 0) {
328 precision = param->max_value_len;
329 }
330 }
331 if (sqltype == SQL_BINARY || sqltype == SQL_VARBINARY || sqltype == SQL_LONGVARBINARY) {
332 ctype = SQL_C_BINARY;
333 } else {
334 ctype = SQL_C_CHAR;
335 }
336
337 P = emalloc(sizeof(*P));
338 param->driver_data = P;
339
340 P->len = 0; /* is re-populated each EXEC_PRE */
341 P->outbuf = NULL;
342
343 P->is_unicode = pdo_odbc_sqltype_is_unicode(S, sqltype);
344 if (P->is_unicode) {
345 /* avoid driver auto-translation: we'll do it ourselves */
346 ctype = SQL_C_BINARY;
347 }
348
349 if ((param->param_type & PDO_PARAM_INPUT_OUTPUT) == PDO_PARAM_INPUT_OUTPUT) {
350 P->paramtype = SQL_PARAM_INPUT_OUTPUT;
351 } else if (param->max_value_len <= 0) {
352 P->paramtype = SQL_PARAM_INPUT;
353 } else {
354 P->paramtype = SQL_PARAM_OUTPUT;
355 }
356
357 if (P->paramtype != SQL_PARAM_INPUT) {
358 if (PDO_PARAM_TYPE(param->param_type) != PDO_PARAM_NULL) {
359 /* need an explicit buffer to hold result */
360 P->len = param->max_value_len > 0 ? param->max_value_len : precision;
361 if (P->is_unicode) {
362 P->len *= 2;
363 }
364 P->outbuf = emalloc(P->len + (P->is_unicode ? 2:1));
365 }
366 }
367
368 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && P->paramtype != SQL_PARAM_INPUT) {
369 pdo_odbc_stmt_error("Can't bind a lob for output");
370 return 0;
371 }
372
373 rc = SQLBindParameter(S->stmt, (SQLUSMALLINT) param->paramno+1,
374 P->paramtype, ctype, sqltype, precision, scale,
375 P->paramtype == SQL_PARAM_INPUT ?
376 (SQLPOINTER)param :
377 P->outbuf,
378 P->len,
379 &P->len
380 );
381
382 if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
383 return 1;
384 }
385 pdo_odbc_stmt_error("SQLBindParameter");
386 return 0;
387 }
388
389 case PDO_PARAM_EVT_EXEC_PRE:
390 P = param->driver_data;
391 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
392 if (Z_TYPE_P(param->parameter) == IS_RESOURCE) {
393 php_stream *stm;
394 php_stream_statbuf sb;
395
396 php_stream_from_zval_no_verify(stm, ¶m->parameter);
397
398 if (!stm) {
399 return 0;
400 }
401
402 if (0 == php_stream_stat(stm, &sb)) {
403 if (P->outbuf) {
404 int len, amount;
405 char *ptr = P->outbuf;
406 char *end = P->outbuf + P->len;
407
408 P->len = 0;
409 do {
410 amount = end - ptr;
411 if (amount == 0) {
412 break;
413 }
414 if (amount > 8192)
415 amount = 8192;
416 len = php_stream_read(stm, ptr, amount);
417 if (len == 0) {
418 break;
419 }
420 ptr += len;
421 P->len += len;
422 } while (1);
423
424 } else {
425 P->len = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size);
426 }
427 } else {
428 if (P->outbuf) {
429 P->len = 0;
430 } else {
431 P->len = SQL_LEN_DATA_AT_EXEC(0);
432 }
433 }
434 } else {
435 convert_to_string(param->parameter);
436 if (P->outbuf) {
437 P->len = Z_STRLEN_P(param->parameter);
438 memcpy(P->outbuf, Z_STRVAL_P(param->parameter), P->len);
439 } else {
440 P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(param->parameter));
441 }
442 }
443 } else if (Z_TYPE_P(param->parameter) == IS_NULL || PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL) {
444 P->len = SQL_NULL_DATA;
445 } else {
446 convert_to_string(param->parameter);
447 if (P->outbuf) {
448 unsigned long ulen;
449 switch (pdo_odbc_utf82ucs2(stmt, P->is_unicode,
450 Z_STRVAL_P(param->parameter),
451 Z_STRLEN_P(param->parameter),
452 &ulen)) {
453 case PDO_ODBC_CONV_FAIL:
454 case PDO_ODBC_CONV_NOT_REQUIRED:
455 P->len = Z_STRLEN_P(param->parameter);
456 memcpy(P->outbuf, Z_STRVAL_P(param->parameter), P->len);
457 break;
458 case PDO_ODBC_CONV_OK:
459 P->len = ulen;
460 memcpy(P->outbuf, S->convbuf, P->len);
461 break;
462 }
463 } else {
464 P->len = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(param->parameter));
465 }
466 }
467 return 1;
468
469 case PDO_PARAM_EVT_EXEC_POST:
470 P = param->driver_data;
471 if (P->outbuf) {
472 if (P->outbuf) {
473 unsigned long ulen;
474 char *srcbuf;
475 unsigned long srclen = 0;
476
477 switch (P->len) {
478 case SQL_NULL_DATA:
479 zval_dtor(param->parameter);
480 ZVAL_NULL(param->parameter);
481 break;
482 default:
483 convert_to_string(param->parameter);
484 switch (pdo_odbc_ucs22utf8(stmt, P->is_unicode, P->outbuf, P->len, &ulen)) {
485 case PDO_ODBC_CONV_FAIL:
486 /* something fishy, but allow it to come back as binary */
487 case PDO_ODBC_CONV_NOT_REQUIRED:
488 srcbuf = P->outbuf;
489 srclen = P->len;
490 break;
491 case PDO_ODBC_CONV_OK:
492 srcbuf = S->convbuf;
493 srclen = ulen;
494 break;
495 }
496
497 Z_STRVAL_P(param->parameter) = erealloc(Z_STRVAL_P(param->parameter), srclen+1);
498 memcpy(Z_STRVAL_P(param->parameter), srcbuf, srclen);
499 Z_STRLEN_P(param->parameter) = srclen;
500 Z_STRVAL_P(param->parameter)[srclen] = '\0';
501 }
502 }
503 }
504 return 1;
505 }
506 }
507 return 1;
508 }
509
odbc_stmt_fetch(pdo_stmt_t * stmt,enum pdo_fetch_orientation ori,long offset TSRMLS_DC)510 static int odbc_stmt_fetch(pdo_stmt_t *stmt,
511 enum pdo_fetch_orientation ori, long offset TSRMLS_DC)
512 {
513 RETCODE rc;
514 SQLSMALLINT odbcori;
515 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
516
517 switch (ori) {
518 case PDO_FETCH_ORI_NEXT: odbcori = SQL_FETCH_NEXT; break;
519 case PDO_FETCH_ORI_PRIOR: odbcori = SQL_FETCH_PRIOR; break;
520 case PDO_FETCH_ORI_FIRST: odbcori = SQL_FETCH_FIRST; break;
521 case PDO_FETCH_ORI_LAST: odbcori = SQL_FETCH_LAST; break;
522 case PDO_FETCH_ORI_ABS: odbcori = SQL_FETCH_ABSOLUTE; break;
523 case PDO_FETCH_ORI_REL: odbcori = SQL_FETCH_RELATIVE; break;
524 default:
525 strcpy(stmt->error_code, "HY106");
526 return 0;
527 }
528 rc = SQLFetchScroll(S->stmt, odbcori, offset);
529
530 if (rc == SQL_SUCCESS) {
531 return 1;
532 }
533 if (rc == SQL_SUCCESS_WITH_INFO) {
534 pdo_odbc_stmt_error("SQLFetchScroll");
535 return 1;
536 }
537
538 if (rc == SQL_NO_DATA) {
539 /* pdo_odbc_stmt_error("SQLFetchScroll"); */
540 return 0;
541 }
542
543 pdo_odbc_stmt_error("SQLFetchScroll");
544
545 return 0;
546 }
547
odbc_stmt_describe(pdo_stmt_t * stmt,int colno TSRMLS_DC)548 static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC)
549 {
550 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
551 struct pdo_column_data *col = &stmt->columns[colno];
552 RETCODE rc;
553 SWORD colnamelen;
554 SQLULEN colsize;
555 SQLLEN displaysize;
556
557 rc = SQLDescribeCol(S->stmt, colno+1, S->cols[colno].colname,
558 sizeof(S->cols[colno].colname)-1, &colnamelen,
559 &S->cols[colno].coltype, &colsize, NULL, NULL);
560
561 /* This fixes a known issue with SQL Server and (max) lengths,
562 may affect others as well. If we are SQL_VARCHAR,
563 SQL_VARBINARY, or SQL_WVARCHAR (or any of the long variations)
564 and zero is returned from colsize then consider it long */
565 if (0 == colsize &&
566 (S->cols[colno].coltype == SQL_VARCHAR ||
567 S->cols[colno].coltype == SQL_LONGVARCHAR ||
568 #ifdef SQL_WVARCHAR
569 S->cols[colno].coltype == SQL_WVARCHAR ||
570 #endif
571 #ifdef SQL_WLONGVARCHAR
572 S->cols[colno].coltype == SQL_WLONGVARCHAR ||
573 #endif
574 S->cols[colno].coltype == SQL_VARBINARY ||
575 S->cols[colno].coltype == SQL_LONGVARBINARY)) {
576 S->going_long = 1;
577 }
578
579 if (rc != SQL_SUCCESS) {
580 pdo_odbc_stmt_error("SQLDescribeCol");
581 if (rc != SQL_SUCCESS_WITH_INFO) {
582 return 0;
583 }
584 }
585
586 rc = SQLColAttribute(S->stmt, colno+1,
587 SQL_DESC_DISPLAY_SIZE,
588 NULL, 0, NULL, &displaysize);
589
590 if (rc != SQL_SUCCESS) {
591 pdo_odbc_stmt_error("SQLColAttribute");
592 if (rc != SQL_SUCCESS_WITH_INFO) {
593 return 0;
594 }
595 }
596 colsize = displaysize;
597
598 col->maxlen = S->cols[colno].datalen = colsize;
599 col->namelen = colnamelen;
600 col->name = estrdup(S->cols[colno].colname);
601 S->cols[colno].is_unicode = pdo_odbc_sqltype_is_unicode(S, S->cols[colno].coltype);
602
603 /* returning data as a string */
604 col->param_type = PDO_PARAM_STR;
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_col(pdo_stmt_t * stmt,int colno,char ** ptr,unsigned long * len,int * caller_frees TSRMLS_DC)633 static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC)
634 {
635 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
636 pdo_odbc_column *C = &S->cols[colno];
637 unsigned long ulen;
638
639 /* if it is a column containing "long" data, perform late binding now */
640 if (C->is_long) {
641 unsigned long used = 0;
642 char *buf;
643 RETCODE rc;
644
645 /* fetch it into C->data, which is allocated with a length
646 * of 256 bytes; if there is more to be had, we then allocate
647 * bigger buffer for the caller to free */
648
649 rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data,
650 256, &C->fetched_len);
651
652 if (rc == SQL_SUCCESS) {
653 /* all the data fit into our little buffer;
654 * jump down to the generic bound data case */
655 goto in_data;
656 }
657
658 if (rc == SQL_SUCCESS_WITH_INFO) {
659 /* this is a 'long column'
660
661 read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks
662 in order into the output buffer
663
664 this loop has to work whether or not SQLGetData() provides the total column length.
665 calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read
666 for that size would be slower except maybe for extremely long columns.*/
667 char *buf2;
668
669 buf2 = emalloc(256);
670 buf = estrndup(C->data, 256);
671 used = 255; /* not 256; the driver NUL terminated the buffer */
672
673 do {
674 C->fetched_len = 0;
675 /* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */
676 rc = SQLGetData(S->stmt, colno+1, SQL_C_CHAR, buf2, 256, &C->fetched_len);
677
678 /* resize output buffer and reassemble block */
679 if (rc==SQL_SUCCESS_WITH_INFO) {
680 /* point 5, in section "Retrieving Data with SQLGetData" in http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx
681 states that if SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size)
682 (if a driver fails to follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */
683 buf = erealloc(buf, used + 255+1);
684 memcpy(buf + used, buf2, 255);
685 used = used + 255;
686 } else if (rc==SQL_SUCCESS) {
687 buf = erealloc(buf, used + C->fetched_len+1);
688 memcpy(buf + used, buf2, C->fetched_len);
689 used = used + C->fetched_len;
690 } else {
691 /* includes SQL_NO_DATA */
692 break;
693 }
694
695 } while (1);
696
697 efree(buf2);
698
699 /* NULL terminate the buffer once, when finished, for use with the rest of PHP */
700 buf[used] = '\0';
701
702 *ptr = buf;
703 *caller_frees = 1;
704 *len = used;
705 if (C->is_unicode) {
706 goto unicode_conv;
707 }
708 return 1;
709 }
710
711 /* something went caca */
712 *ptr = NULL;
713 *len = 0;
714 return 1;
715 }
716
717 in_data:
718 /* check the indicator to ensure that the data is intact */
719 if (C->fetched_len == SQL_NULL_DATA) {
720 /* A NULL value */
721 *ptr = NULL;
722 *len = 0;
723 return 1;
724 } else if (C->fetched_len >= 0) {
725 /* it was stored perfectly */
726 *ptr = C->data;
727 *len = C->fetched_len;
728 if (C->is_unicode) {
729 goto unicode_conv;
730 }
731 return 1;
732 } else {
733 /* no data? */
734 *ptr = NULL;
735 *len = 0;
736 return 1;
737 }
738
739 unicode_conv:
740 switch (pdo_odbc_ucs22utf8(stmt, C->is_unicode, *ptr, *len, &ulen)) {
741 case PDO_ODBC_CONV_FAIL:
742 /* oh well. They can have the binary version of it */
743 case PDO_ODBC_CONV_NOT_REQUIRED:
744 /* shouldn't happen... */
745 return 1;
746
747 case PDO_ODBC_CONV_OK:
748 if (*caller_frees) {
749 efree(*ptr);
750 }
751 *ptr = emalloc(ulen + 1);
752 *len = ulen;
753 memcpy(*ptr, S->convbuf, ulen+1);
754 *caller_frees = 1;
755 return 1;
756 }
757 return 1;
758 }
759
odbc_stmt_set_param(pdo_stmt_t * stmt,long attr,zval * val TSRMLS_DC)760 static int odbc_stmt_set_param(pdo_stmt_t *stmt, long attr, zval *val TSRMLS_DC)
761 {
762 SQLRETURN rc;
763 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
764
765 switch (attr) {
766 case PDO_ATTR_CURSOR_NAME:
767 convert_to_string(val);
768 rc = SQLSetCursorName(S->stmt, Z_STRVAL_P(val), Z_STRLEN_P(val));
769
770 if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
771 return 1;
772 }
773 pdo_odbc_stmt_error("SQLSetCursorName");
774 return 0;
775
776 case PDO_ODBC_ATTR_ASSUME_UTF8:
777 S->assume_utf8 = zval_is_true(val);
778 return 0;
779 default:
780 strcpy(S->einfo.last_err_msg, "Unknown Attribute");
781 S->einfo.what = "setAttribute";
782 strcpy(S->einfo.last_state, "IM001");
783 return -1;
784 }
785 }
786
odbc_stmt_get_attr(pdo_stmt_t * stmt,long attr,zval * val TSRMLS_DC)787 static int odbc_stmt_get_attr(pdo_stmt_t *stmt, long attr, zval *val TSRMLS_DC)
788 {
789 SQLRETURN rc;
790 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
791
792 switch (attr) {
793 case PDO_ATTR_CURSOR_NAME:
794 {
795 char buf[256];
796 SQLSMALLINT len = 0;
797 rc = SQLGetCursorName(S->stmt, buf, sizeof(buf), &len);
798
799 if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
800 ZVAL_STRINGL(val, buf, len, 1);
801 return 1;
802 }
803 pdo_odbc_stmt_error("SQLGetCursorName");
804 return 0;
805 }
806
807 case PDO_ODBC_ATTR_ASSUME_UTF8:
808 ZVAL_BOOL(val, S->assume_utf8 ? 1 : 0);
809 return 0;
810
811 default:
812 strcpy(S->einfo.last_err_msg, "Unknown Attribute");
813 S->einfo.what = "getAttribute";
814 strcpy(S->einfo.last_state, "IM001");
815 return -1;
816 }
817 }
818
odbc_stmt_next_rowset(pdo_stmt_t * stmt TSRMLS_DC)819 static int odbc_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC)
820 {
821 SQLRETURN rc;
822 SQLSMALLINT colcount;
823 pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
824
825 /* NOTE: can't guarantee that output or input/output parameters
826 * are set until this fella returns SQL_NO_DATA, according to
827 * MSDN ODBC docs */
828 rc = SQLMoreResults(S->stmt);
829
830 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
831 return 0;
832 }
833
834 free_cols(stmt, S TSRMLS_CC);
835 /* how many columns do we have ? */
836 SQLNumResultCols(S->stmt, &colcount);
837 stmt->column_count = (int)colcount;
838 S->cols = ecalloc(colcount, sizeof(pdo_odbc_column));
839 S->going_long = 0;
840
841 return 1;
842 }
843
844 struct pdo_stmt_methods odbc_stmt_methods = {
845 odbc_stmt_dtor,
846 odbc_stmt_execute,
847 odbc_stmt_fetch,
848 odbc_stmt_describe,
849 odbc_stmt_get_col,
850 odbc_stmt_param_hook,
851 odbc_stmt_set_param,
852 odbc_stmt_get_attr, /* get attr */
853 NULL, /* get column meta */
854 odbc_stmt_next_rowset
855 };
856
857 /*
858 * Local variables:
859 * tab-width: 4
860 * c-basic-offset: 4
861 * End:
862 * vim600: noet sw=4 ts=4 fdm=marker
863 * vim<600: noet sw=4 ts=4
864 */
865