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