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