-
Notifications
You must be signed in to change notification settings - Fork 14
/
ibm_statement.c
1958 lines (1804 loc) · 53.4 KB
/
ibm_statement.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
+----------------------------------------------------------------------+
| (C) Copyright IBM Corporation 2006. |
+----------------------------------------------------------------------+
| |
| Licensed under the Apache License, Version 2.0 (the "License"); you |
| may not use this file except in compliance with the License. You may |
| obtain a copy of the License at |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| implied. See the License for the specific language governing |
| permissions and limitations under the License. |
+----------------------------------------------------------------------+
| Authors: Rick McGuire, Dan Scott, Krishna Raman, Kellen Bombardier, |
| Ambrish Bhargava, Rahul Priyadarshi |
+----------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "pdo/php_pdo.h"
#include "pdo/php_pdo_driver.h"
#include "php_pdo_ibm.h"
#include "php_pdo_ibm_int.h"
#ifdef PASE
# define SQL_LEN_DATA_AT_EXEC(length) length
#else
# if !defined(SQL_LEN_DATA_AT_EXEC) && !defined(SQL_LEN_DATA_AT_EXEC_OFFSET)
# define SQL_LEN_DATA_AT_EXEC_OFFSET (-100)
# define SQL_LEN_DATA_AT_EXEC(length) (-(length)+SQL_LEN_DATA_AT_EXEC_OFFSET)
# endif
#endif
struct lob_stream_data
{
stmt_handle *stmt_res;
pdo_stmt_t *stmt;
int colno;
};
#ifdef PASE
static int get_lob_length(pdo_stmt_t *stmt, column_data *col_res)
{
SQLRETURN rc = 0;
SQLHANDLE new_hstmt;
conn_handle *conn_res = (conn_handle *)stmt->dbh->driver_data;
rc = SQLAllocHandle(SQL_HANDLE_STMT, conn_res->hdbc, &new_hstmt);
if (rc != SQL_SUCCESS) {
return rc;
}
rc = SQLGetLength((SQLHSTMT)new_hstmt,
col_res->loc_type,
col_res->lob_loc,
&col_res->lob_data_length,
&col_res->loc_ind);
check_stmt_error(rc, "SQLGetLength");
if (rc != SQL_SUCCESS) {
col_res->lob_data_length=0;
}
SQLFreeHandle(SQL_HANDLE_STMT, new_hstmt);
return rc;
}
static int get_lob_substring(pdo_stmt_t *stmt, column_data *col_res,
SQLSMALLINT ctype, SQLINTEGER *out_length)
{
SQLRETURN rc = 0;
SQLHANDLE new_hstmt;
conn_handle *conn_res = (conn_handle *)stmt->dbh->driver_data;
*out_length=0;
col_res->lob_data_offset = 0;
col_res->lob_data[col_res->lob_data_length]='\0';
rc = SQLAllocHandle(SQL_HANDLE_STMT, conn_res->hdbc, &new_hstmt);
if (rc != SQL_SUCCESS) {
return rc;
}
rc = SQLGetSubString(
(SQLHSTMT)new_hstmt,
col_res->loc_type,
col_res->lob_loc,
1,
col_res->lob_data_length,
ctype,
col_res->lob_data,
col_res->lob_data_length+1,
out_length,
&col_res->loc_ind);
check_stmt_error(rc, "SQLGetSubString");
col_res->lob_data[col_res->lob_data_length]='\0';
SQLFreeHandle(SQL_HANDLE_STMT, new_hstmt);
return rc;
}
#endif
STREAM_RETURN_TYPE lob_stream_read(php_stream *stream, char *buf, size_t count)
{
SQLINTEGER readBytes = 0;
struct lob_stream_data *data = stream->abstract;
column_data *col_res = &data->stmt_res->columns[data->colno];
stmt_handle *stmt_res = data->stmt_res;
pdo_stmt_t *stmt = data->stmt;
int ctype = 0;
SQLRETURN rc = 0;
long sLength;
switch (col_res->data_type) {
case SQL_LONGVARBINARY:
case SQL_VARBINARY:
case SQL_BINARY:
case SQL_BLOB:
#ifndef PASE /* i5os string type required for the ascii->ebcdic conversion */
case SQL_CLOB:
#endif /* PASE */
case SQL_XML:
ctype = SQL_C_BINARY;
break;
default:
ctype = SQL_C_CHAR;
break;
}
#ifdef PASE
if (buf == NULL) {
rc = get_lob_length(stmt, col_res);
if (rc != SQL_ERROR && col_res->lob_data_length > 0) {
if (col_res->lob_data) {
col_res->lob_data = erealloc(col_res->lob_data, col_res->lob_data_length+1);
} else {
col_res->lob_data = emalloc(col_res->lob_data_length+1);
}
rc = get_lob_substring(stmt, col_res, ctype, &readBytes);
}
} else {
readBytes = MIN(count, col_res->lob_data_length - col_res->lob_data_offset);
if (readBytes > 0) {
memcpy( buf, col_res->lob_data + col_res->lob_data_offset, readBytes);
col_res->lob_data_offset +=readBytes;
}
}
if (readBytes <= 0) {
readBytes = -1;
if (buf != NULL) {
/* EOF reached */
stream->eof = 1;
}
}
#else
rc = SQLGetData(stmt_res->hstmt, data->colno + 1, ctype, buf, count, &readBytes);
/* custom error handling for possible ssize_t */
if (rc == SQL_ERROR) {
RAISE_STMT_ERROR("SQLGetData");
return (STREAM_RETURN_TYPE)-1;
}
if (readBytes == -1) { /*For NULL CLOB/BLOB values */
return (STREAM_RETURN_TYPE) readBytes;
}
if (readBytes > count) {
if (col_res->data_type == SQL_LONGVARCHAR) { /*Dont return the NULL at end of CLOB buffer */
readBytes = count - 1;
} else {
readBytes = count;
}
}
#endif
return (STREAM_RETURN_TYPE) readBytes;
}
STREAM_RETURN_TYPE lob_stream_write(php_stream *stream, const char *buf, size_t count)
{
return 0;
}
int lob_stream_flush(php_stream *stream)
{
return 0;
}
int lob_stream_close(php_stream *stream, int close_handle)
{
struct lob_stream_data *data = stream->abstract;
efree(data);
return 0;
}
php_stream_ops lob_stream_ops = {
lob_stream_write, /* Write */
lob_stream_read, /* Read */
lob_stream_close, /* Close */
lob_stream_flush, /* Flush */
"ibm PDO Lob stream",
NULL, /* Seek */
NULL, /* GetS */
NULL, /* Cast */
NULL /* Stat */
};
php_stream* create_lob_stream( pdo_stmt_t *stmt , stmt_handle *stmt_res , int colno )
{
struct lob_stream_data *data;
column_data *col_res;
php_stream *retval;
data = emalloc(sizeof(struct lob_stream_data));
data->stmt_res = stmt_res;
data->stmt = stmt;
data->colno = colno;
col_res = &data->stmt_res->columns[data->colno];
retval = (php_stream *) php_stream_alloc(&lob_stream_ops, data, NULL, "r");
/* Find out if the column contains NULL data */
if (lob_stream_read(retval, NULL, 0) == SQL_NULL_DATA) {
php_stream_close(retval);
return NULL;
} else
return retval;
}
/*
* Clear up our column descriptors. This is done either from
* the statement constructors or whenever we traverse from one
* result set to the next.
*/
static void stmt_free_column_descriptors(pdo_stmt_t *stmt)
{
stmt_handle *stmt_res = (stmt_handle *) stmt->driver_data;
if (stmt_res->columns != NULL) {
int i;
/* see if any of the columns have attached storage too. */
for (i = 0; i < stmt->column_count; i++) {
/*
* Was this a string form? We have an allocated string
* buffer that also needs releasing.
*/
if (stmt_res->columns[i].returned_type == PDO_PARAM_STR) {
efree(stmt_res->columns[i].data.str_val);
}
if (stmt_res->columns[i].returned_type == PDO_PARAM_LOB && (stmt_res->columns[i].lob_data != NULL)) {
efree(stmt_res->columns[i].lob_data);
}
}
/* free the entire column list. */
efree(stmt_res->columns);
stmt_res->columns = NULL;
}
}
/*
* Cleanup any driver-allocated control blocks attached to a statement
* instance. This cleans up the driver_data control block, as
* well as any temporary allocations used during execution.
*/
void stmt_cleanup(pdo_stmt_t *stmt)
{
stmt_handle *stmt_res = (stmt_handle *) stmt->driver_data;
if (stmt_res != NULL) {
if (stmt_res->converted_statement != NULL) {
#if PHP_8_1_OR_HIGHER
zend_string_release(stmt_res->converted_statement);
#else
efree(stmt_res->converted_statement);
#endif
}
if (stmt_res->lob_buffer != NULL) {
stmt_res->lob_buffer = NULL;
}
/* free any descriptors we're keeping active */
stmt_free_column_descriptors(stmt);
efree(stmt_res);
}
stmt->driver_data = NULL;
}
/* get the parameter description information for a positional bound parameter. */
static int stmt_get_parameter_info(pdo_stmt_t * stmt, struct pdo_bound_param_data *param)
{
param_node *param_res = (param_node *) param->driver_data;
stmt_handle *stmt_res = NULL;
int rc = 0;
/* do we have the parameter information yet? */
if (param_res == NULL) {
/* allocate a new one and attach to the PDO param structure */
param_res = (param_node *) emalloc(sizeof(param_node));
check_stmt_allocation(param_res, "stmt_get_parameter",
"Unable to allocate parameter driver data");
/* get the statement specifics */
stmt_res = (stmt_handle *) stmt->driver_data;
/* this is only used if a string is interned for an OUT */
param_res->tmp_binding_buffer = NULL;
/*
* NB: The PDO parameter numbers are origin zero, but the
* SQLDescribeParam() ones start with 1.
*/
rc = SQLDescribeParam((SQLHSTMT)stmt_res->hstmt, (SQLUSMALLINT)param->paramno + 1, ¶m_res->data_type,
¶m_res->param_size, ¶m_res->scale, ¶m_res->nullable);
/* Free the memory if SQLDescribeParam failed */
if (rc == SQL_ERROR) {
efree(param_res);
param_res = NULL;
}
check_stmt_error(rc, "SQLDescribeParam");
/* only attach this if we succeed */
param->driver_data = param_res;
/*
* but see if we need to alter this for binary forms or
* can optimize numerics a little.
*/
switch (param_res->data_type) {
/*
* The binary forms need to be transferred as binary
* data, not as char data. */
case SQL_BINARY:
case SQL_BLOB:
#ifndef PASE /* i5/OS CLOB is char not binary (default) */
case SQL_CLOB:
#endif
case SQL_XML:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
param_res->ctype = SQL_C_BINARY;
break;
/*
* Numeric forms we can map directly to a long
* int value
*/
case SQL_BOOLEAN:
case SQL_BIT:
case SQL_SMALLINT:
case SQL_INTEGER:
param_res->ctype = SQL_C_LONG;
break;
/* everything else will transfer as character */
default:
/* by default, we transfer as character data */
param_res->ctype = SQL_C_CHAR;
break;
}
}
return TRUE;
}
static int db2_inout_parm_numeric(param_node *param_res) {
/* need character to number padding */
switch ( param_res->data_type ) {
case SQL_BOOLEAN:
case SQL_BIT:
case SQL_SMALLINT:
case SQL_INTEGER:
case SQL_REAL:
case SQL_FLOAT:
case SQL_DOUBLE:
case SQL_DECFLOAT:
case SQL_BIGINT:
case SQL_DECIMAL:
case SQL_NUMERIC:
return 1;
default:
break;
}
return 0;
}
static int db2_inout_parm_pad_len(param_node *param_res) {
/* length full char pad for number expanded */
switch ( param_res->data_type ) {
case SQL_REAL:
case SQL_FLOAT:
case SQL_DOUBLE:
case SQL_DECFLOAT:
case SQL_BIGINT:
return 20;
break;
case SQL_DECIMAL:
return param_res->param_size * 2 + 1;
break;
case SQL_NUMERIC:
/* length full char pad same as SQL column length */
default:
break;
}
return param_res->param_size;
}
static void db2_inout_parm_pad_data(param_node *param_res, struct pdo_bound_param_data *curr) {
char *front = NULL;
char *back = NULL;
char *here = NULL;
int isNeg = 0;
int isNum = 0;
int len = 0;
int need = 0;
#ifdef PASE /* i5/OS - RPG, COBOL, expect space pad */
char pad = 0x20;
#else /* LUW - c programming null pad (0) */
char pad = 0x00;
#endif /* PASE */
zval *parameter;
if (Z_ISREF(curr->parameter)) {
parameter = Z_REFVAL(curr->parameter);
} else {
parameter = &curr->parameter;
}
len = Z_STRLEN_P(parameter);
front = Z_STRVAL_P(parameter);
here=back= Z_STRVAL_P(parameter) + len - 1;
/*
* HACK: Purge the buffer of any leftovers, but keep a copy of what we
* need from it we can blit back in. Otherwise, we tend to get some
* garbage from previous uses that can get used. (CB 20200903)
*/
char *tmp_buf = estrdup(front);
if (tmp_buf) {
memset(front, 0, len);
strncpy(front, tmp_buf, strlen(tmp_buf));
efree(tmp_buf);
}
/*
* character cast to number pad
* Example:
* BIGINT<implicit>CHAR works consistently if 0x30 pad left,
* '2' becomes '000000000000000000002'
*/
switch ( param_res->data_type ) {
case SQL_BOOLEAN:
case SQL_BIT:
case SQL_SMALLINT:
case SQL_INTEGER:
case SQL_REAL:
case SQL_FLOAT:
case SQL_DOUBLE:
case SQL_DECFLOAT:
case SQL_BIGINT:
case SQL_DECIMAL:
case SQL_NUMERIC:
for (;here>=front;here--) {
if (*here == '-') {
isNeg = 1;
}
if ((*here>='0' && *here<='9') || *here == '.') {
isNum = 1;
*back = *here;
back--;
} else if (isNum) {
*back = '0';
back--;
}
}
for (;back>=front;back--) {
*back = '0';
}
if (isNeg==1) {
*front = '-';
back--;
}
break;
default:
/*
* character pad (platform dependent)
* IBM i (PASE):
* 'hi' becomes 'hi' + 0x2020202020(00)
* LUW:
* 'hi' becomes 'hi' + 0x0000000000(00)
*/
len = strlen(front);
here = front + len;
back--;
for (;back>=here;back--) {
*back = pad;
}
break;
}
}
static void db2_inout_parm_pad_return(param_node *param_res, struct pdo_bound_param_data *curr) {
char *front = NULL;
char *back = NULL;
char *here = NULL;
int len = param_res->transfer_length;
zval *parameter;
if (Z_ISREF(curr->parameter)) {
parameter = Z_REFVAL(curr->parameter);
} else {
parameter = &curr->parameter;
}
front = Z_STRVAL_P(parameter);
here=back= Z_STRVAL_P(parameter) + len - 1;
/* trim trailing 'junk' */
for (;back>=front;back--) {
#ifdef PASE /* i5/OS - also annoying trailing ebcdic space (0x40) */
if (*back == 0x00 || *back == 0x20 || *back == 0x40)
#else /* LUW -- assume trim also wanted */
if (*back == 0x00 || *back == 0x20)
#endif /* PASE */
{
param_res->transfer_length--;
*back = '0';
} else {
break;
}
}
}
/*
* Bind a statement parameter to the PHP value supplying or receiving the
* parameter data.
*/
int stmt_bind_parameter(pdo_stmt_t *stmt, struct pdo_bound_param_data *curr)
{
int rc, is_num = 0, is_null = 0, is_empty = 0;
stmt_handle *stmt_res = (stmt_handle *) stmt->driver_data;
param_node *param_res = NULL;
SQLSMALLINT inputOutputType = 0;
SQLSMALLINT switchInputOutputType = 0;
int origlen = 0;
void *data_buf = NULL;
int how_to_handle_empty_string = 0;
zval *parameter;
if (Z_ISREF(curr->parameter)) {
parameter = Z_REFVAL(curr->parameter);
} else {
parameter = &curr->parameter;
}
/* make sure we have current description information. */
if (stmt_get_parameter_info(stmt, curr) == FALSE) {
return FALSE;
}
param_res = (param_node *) curr->driver_data;
/*
* Now figure out the parameter type so we can tell the database code
* how to handle this.
* this is rare, really only used for stored procedures.
*/
if ((unsigned)((unsigned)curr->param_type & (unsigned)PDO_PARAM_INPUT_OUTPUT) == (unsigned)PDO_PARAM_INPUT_OUTPUT) {
inputOutputType = SQL_PARAM_INPUT_OUTPUT;
}
/*
* If this is a non-positive length, we can't assign a value,
* so this is by definition an INPUT param.
*/
else if (curr->max_value_len <= 0) {
inputOutputType = SQL_PARAM_INPUT;
} else {
/* everything else is output. */
inputOutputType = SQL_PARAM_OUTPUT;
}
/* IS_NULL special handling (before any alterations) */
if (Z_TYPE_P(parameter) == IS_NULL) {
is_null = 1;
}
/* IS_EMPTY special handling (before any alterations) */
if (Z_TYPE_P(parameter) == IS_STRING && !Z_STRLEN_P(parameter)) {
is_empty = 1;
}
/*
* Now do the actual binding, which is controlled by the
* PDO supplied type.
*/
switch (PDO_PARAM_TYPE(curr->param_type)) {
/* not implemented yet */
case PDO_PARAM_STMT:
RAISE_IBM_STMT_ERROR("IM001", "param_hook",
"Driver does not support statement parameters");
return FALSE;
/* this is a long value for PHP (or user forced) */
case PDO_PARAM_BOOL:
case PDO_PARAM_INT:
/*
* If the parameter type is a numeric type,
* we'll bind this directly
* (param_res->ctype db2 provided field description)
*/
if (param_res->ctype == SQL_C_LONG) {
/* promote to null */
if (is_null || is_empty) {
is_null = 1;
} else {
convert_to_long(parameter);
data_buf = &Z_LVAL_P(parameter);
/*
* SQL_BIGINT works as the C-side type
* and will bind as a 64-bit integer,
* because XPF long is 32-bit and PASE
* long is 64-bit (under 64-bit PASE).
*/
rc = SQLBindParameter(stmt_res->hstmt,
curr->paramno + 1,
inputOutputType,
#if defined(PASE) && defined(__LP64__)
SQL_BIGINT,
#else
SQL_C_LONG,
#endif
param_res->data_type,
param_res->param_size,
param_res->scale,
data_buf,
0,
NULL);
check_stmt_error(rc, "SQLBindParameter");
return TRUE;
}
}
/*
* NOTE: We fall through from above if there is a
* type mismatch.
*/
/* a string value (very common) */
case PDO_PARAM_STR:
/*
* SQL_PARAM_INPUT -- null remain untouched (read)
* non-null convert string (update)
* SQL_PARAM_OUTPUT -- force big enough out buffer (write)
* SQL_PARAM_INPUT_OUTPUT -- force big enough out buffer (write)
*/
switch(inputOutputType) {
case SQL_PARAM_INPUT:
/*
* IS_NULL special handling
* php variable remain untouched (goal)
*/
if (is_null) {
data_buf = &Z_LVAL_P(parameter);
break;
}
/* fall through to force string */
case SQL_PARAM_INPUT_OUTPUT:
case SQL_PARAM_OUTPUT:
default:
/* force this to be a real string value */
param_res->ctype = SQL_C_CHAR;
convert_to_string(parameter);
/* buffer and size before alterations */
data_buf = Z_STRVAL_P(parameter);
origlen = Z_STRLEN_P(parameter);
/*
* The transfer length to zero now...this
* gets updated at EXEC_PRE time.
*/
param_res->transfer_length = 0;
/*
* empty string is common ($var=''),
* but what to do with DB2 implicit CAST error
* inserting to a database 'numeric' field
* (CREATE TABLE animals (id INTEGER))?
*/
how_to_handle_empty_string = 2;
if (inputOutputType == SQL_PARAM_INPUT && !is_null && !origlen) {
switch (how_to_handle_empty_string) {
case 1:
/*
* tough luck! Let DB2 throw a CAST error
* (cruel database expert)
*/
break;
case 2:
/*
* empty string to promote to null
* (kind database expert)
*/
switchInputOutputType = db2_inout_parm_numeric(param_res);
if (switchInputOutputType) {
switchInputOutputType = 0;
is_null = 1;
}
break;
case 3:
default:
/*
* empty string to promote to zero
* (simple mutable php)
*/
switchInputOutputType = db2_inout_parm_numeric(param_res);
if (switchInputOutputType) {
inputOutputType = SQL_PARAM_OUTPUT;
curr->max_value_len = 1;
}
break;
}
}
break;
} /* switch */
/*
* SQL_PARAM_INPUT -- any size buffer
* SQL_PARAM_OUTPUT -- need max size buffer
* SQL_PARAM_INPUT_OUTPUT -- need max size buffer
*/
switch (inputOutputType) {
case SQL_PARAM_INPUT:
/* IS_NULL special handling */
if (is_null) {
switchInputOutputType = db2_inout_parm_numeric(param_res);
if (switchInputOutputType) {
param_res->ctype = SQL_C_LONG;
switchInputOutputType = 0;
}
curr->max_value_len = 0;
param_res->transfer_length = SQL_NULL_DATA;
rc = SQLBindParameter(stmt_res->hstmt,
curr->paramno + 1,
inputOutputType,
param_res->ctype,
param_res->data_type,
param_res->param_size,
param_res->scale,
data_buf,
curr->max_value_len <=
0 ? 0 : curr->max_value_len,
¶m_res->transfer_length);
check_stmt_error(rc, "SQLBindParameter");
return TRUE;
}
break;
case SQL_PARAM_INPUT_OUTPUT:
case SQL_PARAM_OUTPUT:
default:
/*
* IS_INTERNED() macro to check if a given char* is interned or regular string
* each string (or atom) is allocated once and never changed (immutable)
* aka, not useful for INOUT and OUT paramters obviously
*/
if (IS_INTERNED(Z_STR_P(parameter))) {
Z_STR_P(parameter) = zend_string_init(Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), 0);
/* free at event hook */
param_res->tmp_binding_buffer = (parameter);
}
/*
* Now we need to make sure the string buffer
* is large enough to receive a new value if
* this is an output or in/out parameter
*/
curr->max_value_len = db2_inout_parm_pad_len(param_res);
if (curr->max_value_len > Z_STRLEN_P(parameter)) {
/* reallocate this to the new size */
Z_STR_P(parameter) = zend_string_extend(Z_STR_P(parameter), curr->max_value_len, 0);
check_stmt_allocation(Z_STRVAL_P(parameter),
"stmt_bind_parameter",
"Unable to allocate bound parameter");
}
/* correct format of string data implicit CAST (if needed) */
db2_inout_parm_pad_data(param_res, curr);
/* switch type for CAST help (kind zero way) */
if (switchInputOutputType) {
inputOutputType = SQL_PARAM_INPUT;
}
break;
} /* switch */
/* data buf (after alterations) */
data_buf = Z_STRVAL_P(parameter);
/* GH-12: Set +1 to avoid a possible CHAR(0). Db2i
* reacts violently (-902) to it. zend_string is
* null terminated, so it should be safe.
*/
param_res->param_size = Z_STRLEN_P(parameter) + 1;
/* binary */
switch(param_res->data_type) {
case SQL_BINARY:
case SQL_LONGVARBINARY:
case SQL_VARBINARY:
case SQL_BLOB:
param_res->ctype = SQL_C_BINARY;
break;
default:
param_res->ctype = SQL_C_CHAR;
break;
}
/* bind as char */
rc = SQLBindParameter(stmt_res->hstmt,
curr->paramno + 1,
inputOutputType,
param_res->ctype,
param_res->data_type,
param_res->param_size,
param_res->scale,
data_buf,
curr->max_value_len <=
0 ? 0 : curr->max_value_len,
¶m_res->transfer_length);
check_stmt_error(rc, "SQLBindParameter");
return TRUE;
/*
* This is either a string, or, if the length is zero,
* then this is a pointer to a PHP stream.
*/
case PDO_PARAM_LOB:
if (inputOutputType != SQL_PARAM_INPUT) {
inputOutputType = SQL_PARAM_INPUT;
}
/* have we bound a LOB to a long type for some reason? */
if (param_res->ctype == SQL_C_LONG) {
/* transfer this as character data. */
param_res->ctype = SQL_C_CHAR;
}
/*
* CLOB, DBCLOB = CHAR
* BLOB, XML, other = BINARY
*/
switch(param_res->data_type) {
case SQL_CLOB:
#ifdef PASE /* i5/OS - include DBCLOB */
case SQL_DBCLOB:
#endif /* PASE */
param_res->ctype = SQL_C_CHAR;
break;
default:
param_res->ctype = SQL_C_BINARY;
break;
}
/* indicate we're going to transfer the data at exec time. */
param_res->transfer_length = SQL_DATA_AT_EXEC;
data_buf = curr;
#ifdef PASE /* i5/OS - to cause a conversion to ebcdic */
if (Z_TYPE_P(parameter) != IS_RESOURCE) {
param_res->transfer_length = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter));
/* get the pointer to the string data */
data_buf = Z_STRVAL_P(parameter);
}
#endif /* PASE */
/*
* We can't bind LOBs at this point...we process all
* of this at execute time. However, we set the value
* data to the PDO binding control block and set the
* SQL_DATA_AT_EXEC value to cause it to prompt us for
* the data at execute time. The pointer is recoverable
* at that time by using SQLParamData(), and we can
* then process the request.
*/
rc = SQLBindParameter(stmt_res->hstmt, curr->paramno + 1,
inputOutputType, param_res->ctype,
param_res->data_type,
param_res->param_size, param_res->scale,
data_buf,
4096,
¶m_res->transfer_length);
check_stmt_error(rc, "SQLBindParameter");
return TRUE;
/* this is an unknown type */
default:
RAISE_IBM_STMT_ERROR( "IM001", "SQLBindParameter", "Unknown parameter type" );
return FALSE;
}
return TRUE;
}
/* handle the pre-execution phase for bound parameters. */
static int stmt_parameter_pre_execute(pdo_stmt_t *stmt, struct pdo_bound_param_data *curr)
{
stmt_handle *stmt_res = (stmt_handle *) stmt->driver_data;
param_node *param_res = (param_node *) curr->driver_data;
/*
* Now we need to prepare the parameter binding information
* for execution. If this is a LOB, then we need to ensure
* the LOB data is going to be available and make sure
* the binding is tagged to provide the data at exec time.
*/
zval *parameter;
if (Z_ISREF(curr->parameter)) {
parameter = Z_REFVAL(curr->parameter);
} else {
parameter = &curr->parameter;
}
if (PDO_PARAM_TYPE(curr->param_type) == PDO_PARAM_LOB) {
/*
* If the LOB data is a stream, we need to make sure it is
* really there.
*/
if (Z_TYPE_P(parameter) == IS_RESOURCE) {
php_stream *stm;
php_stream_statbuf sb;
/* make sure we have a stream to work with */
php_stream_from_zval_no_verify(stm, parameter);
if (stm == NULL) {
RAISE_IBM_STMT_ERROR( "HY000" , "SQLBindParameter" ,
"PDO_PARAM_LOB file stream is invalid");
}
/*
* Now see if we can retrieve length information from
* the stream
*/
if (php_stream_stat(stm, &sb) == 0) {
/*
* Yes, we're able to give the statement some
* hints about the size.
*/
#ifndef PASE
param_res->transfer_length = SQL_LEN_DATA_AT_EXEC(sb.sb.st_size);
#endif
} else {
/*
* Still unknown...we'll have to do everything
* at execute size.
*/
param_res->transfer_length = SQL_LEN_DATA_AT_EXEC(0);
}
} else {
/*
* Convert this to a string value now. We bound the
* data pointer to our parameter descriptor, so we
* can't just supply this directly yet, but we can
* at least give the size hint information.
*/
convert_to_string(parameter);
param_res->transfer_length = SQL_LEN_DATA_AT_EXEC(Z_STRLEN_P(parameter));
}
} else {
if (Z_TYPE_P(parameter) != IS_NULL && param_res != NULL) {
/*
* if we're processing this as string or binary data,
* then directly update the length to the real value.
*/
if (param_res->ctype == SQL_C_LONG) {
/* make sure this is a long value */
convert_to_long(parameter);
} else {
/*
* Make sure this is a string value...it might
* have been changed between the bind and the
* execute
*/
convert_to_string(parameter);
param_res->transfer_length = Z_STRLEN_P(parameter);
}
}
}
return TRUE;
}
/* post-execution bound parameter handling. */
static int stmt_parameter_post_execute(pdo_stmt_t *stmt, struct pdo_bound_param_data *curr)
{
param_node *param_res = (param_node *) curr->driver_data;
/*
* If the type of the parameter is a string, we need to update the
* string length and make sure that these are null terminated.
* Values returned from the DB are just copied directly into the bound
* locations, so we need to update the PHP control blocks so that the
* data is processed correctly.
*/
zval *parameter;
if (Z_ISREF(curr->parameter)) {
parameter = Z_REFVAL(curr->parameter);
} else {
parameter = &curr->parameter;
}