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