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