1 /*-------
2 * Module: convert.c
3 *
4 * Description: This module contains routines related to
5 * converting parameters and columns into requested data types.
6 * Parameters are converted from their SQL_C data types into
7 * the appropriate postgres type. Columns are converted from
8 * their postgres type (SQL type) into the appropriate SQL_C
9 * data type.
10 *
11 * Classes: n/a
12 *
13 * API functions: none
14 *
15 * Comments: See "readme.txt" for copyright and license information.
16 *-------
17 */
18 /* Multibyte support Eiji Tokuya 2001-03-15 */
19
20 #include "convert.h"
21 #include "unicode_support.h"
22 #include "misc.h"
23 #ifdef WIN32
24 #include <float.h>
25 #define HAVE_LOCALE_H
26 #endif /* WIN32 */
27
28 #include <stdio.h>
29 #include <string.h>
30 #include <ctype.h>
31
32 #include "multibyte.h"
33
34 #include <time.h>
35 #ifdef HAVE_LOCALE_H
36 #include <locale.h>
37 #endif
38 #include <math.h>
39 #include <stdlib.h>
40 #include <limits.h>
41 #include "statement.h"
42 #include "qresult.h"
43 #include "bind.h"
44 #include "pgtypes.h"
45 #include "lobj.h"
46 #include "connection.h"
47 #include "catfunc.h"
48 #include "pgapifunc.h"
49
50 CSTR NAN_STRING = "NaN";
51 CSTR INFINITY_STRING = "Infinity";
52 CSTR MINFINITY_STRING = "-Infinity";
53
54 #if defined(WIN32) || defined(__CYGWIN__)
55 #define TIMEZONE_GLOBAL _timezone
56 #define TZNAME_GLOBAL _tzname
57 #define DAYLIGHT_GLOBAL _daylight
58 #elif defined(HAVE_INT_TIMEZONE)
59 #define TIMEZONE_GLOBAL timezone
60 #define TZNAME_GLOBAL tzname
61 #define DAYLIGHT_GLOBAL daylight
62 #endif
63
64 /*
65 * How to map ODBC scalar functions {fn func(args)} to Postgres.
66 * This is just a simple substitution. List augmented from:
67 * http://www.merant.com/datadirect/download/docs/odbc16/Odbcref/rappc.htm
68 * - thomas 2000-04-03
69 */
70 static const struct
71 {
72 /*
73 * There's a horrible hack in odbc_name field: if it begins with a %,
74 * the digit after the % indicates the number of arguments. Otherwise,
75 * the entry matches any number of args.
76 */
77 char *odbc_name;
78 char *pgsql_name;
79 } mapFuncs[] = {
80 /* { "ASCII", "ascii" }, built_in */
81 {"CHAR", "chr($*)" },
82 {"CONCAT", "concat($1::text, $2::text)" },
83 /* { "DIFFERENCE", "difference" }, how to ? */
84 {"INSERT", "substring($1 from 1 for $2 - 1) || $4 || substring($1 from $2 + $3)" },
85 {"LCASE", "lower($*)" },
86 /* {"LEFT", "left" }, built_in */
87 {"%2LOCATE", "strpos($2, $1)" }, /* 2 parameters */
88 {"%3LOCATE", "strpos(substring($2 from $3), $1) + $3 - 1" }, /* 3 parameters */
89 {"LENGTH", "char_length($*)"},
90 /* { "LTRIM", "ltrim" }, built_in */
91 /* {"RIGHT", "right" }, built_in */
92 {"SPACE", "repeat(' ', $1)" },
93 /* { "REPEAT", "repeat" }, built_in */
94 /* { "REPLACE", "replace" }, ??? */
95 /* { "RTRIM", "rtrim" }, built_in */
96 /* { "SOUNDEX", "soundex" }, how to ? */
97 {"SUBSTRING", "substr($*)" },
98 {"UCASE", "upper($*)" },
99
100 /* { "ABS", "abs" }, built_in */
101 /* { "ACOS", "acos" }, built_in */
102 /* { "ASIN", "asin" }, built_in */
103 /* { "ATAN", "atan" }, built_in */
104 /* { "ATAN2", "atan2" }, built_in */
105 {"CEILING", "ceil($*)" },
106 /* { "COS", "cos" }, built_in */
107 /* { "COT", "cot" }, built_in */
108 /* { "DEGREES", "degrees" }, built_in */
109 /* { "EXP", "exp" }, built_in */
110 /* { "FLOOR", "floor" }, built_in */
111 {"LOG", "ln($*)" },
112 {"LOG10", "log($*)" },
113 /* { "MOD", "mod" }, built_in */
114 /* { "PI", "pi" }, built_in */
115 /* {"POWER", "power" }, built_in */
116 /* { "RADIANS", "radians" }, built_in */
117 {"%0RAND", "random()" }, /* 0 parameters */
118 {"%1RAND", "(setseed($1) * .0 + random())" }, /* 1 parameters */
119 /* { "ROUND", "round" }, built_in */
120 /* { "SIGN", "sign" }, built_in */
121 /* { "SIN", "sin" }, built_in */
122 /* { "SQRT", "sqrt" }, built_in */
123 /* { "TAN", "tan" }, built_in */
124 {"TRUNCATE", "trunc($*)" },
125
126 {"CURRENT_DATE", "current_date" },
127 {"CURRENT_TIME", "current_time" },
128 {"CURRENT_TIMESTAMP", "current_timestamp" },
129 {"LOCALTIME", "localtime" },
130 {"LOCALTIMESTAMP", "localtimestamp" },
131 {"CURRENT_USER", "cast(current_user as text)" },
132 {"SESSION_USER", "cast(session_user as text)" },
133 {"CURDATE", "current_date" },
134 {"CURTIME", "current_time" },
135 {"DAYNAME", "to_char($1, 'Day')" },
136 {"DAYOFMONTH", "cast(extract(day from $1) as integer)" },
137 {"DAYOFWEEK", "(cast(extract(dow from $1) as integer) + 1)" },
138 {"DAYOFYEAR", "cast(extract(doy from $1) as integer)" },
139 {"HOUR", "cast(extract(hour from $1) as integer)" },
140 {"MINUTE", "cast(extract(minute from $1) as integer)" },
141 {"MONTH", "cast(extract(month from $1) as integer)" },
142 {"MONTHNAME", " to_char($1, 'Month')" },
143 /* { "NOW", "now" }, built_in */
144 {"QUARTER", "cast(extract(quarter from $1) as integer)" },
145 {"SECOND", "cast(extract(second from $1) as integer)" },
146 {"WEEK", "cast(extract(week from $1) as integer)" },
147 {"YEAR", "cast(extract(year from $1) as integer)" },
148 // TIMESTAMPADD()
149 {"TIMESTAMPADD(SQL_TSI_YEAR", "($3+make_interval(years := $2))" },
150 {"TIMESTAMPADD(SQL_TSI_MONTH", "($3+make_interval(months := $2))" },
151 {"TIMESTAMPADD(SQL_TSI_WEEK", "($3+make_interval(weeks := $2))" },
152 {"TIMESTAMPADD(SQL_TSI_DAY", "($3+make_interval(days := $2))" },
153 {"TIMESTAMPADD(SQL_TSI_HOUR", "($3+make_interval(hours := $2))" },
154 {"TIMESTAMPADD(SQL_TSI_MINUTE", "($3+make_interval(mins := $2))" },
155 {"TIMESTAMPADD(SQL_TSI_SECOND", "($3+make_interval(secs := $2))" },
156 {"TIMESTAMPADD(SQL_TSI_FRAC_SECOND", "($3+make_interval(secs := $2::float / 1000000))" },
157 // TIMESTAMPDIFF()
158 /* doesn't work properly?
159 {"TIMESTAMPDIFF(SQL_TSI_YEAR", "cast(extract(year from ($3 - $2)) as integer)" },
160 {"TIMESTAMPDIFF(SQL_TSI_MONTH", "cast(extract(month from ($3 - $2)) as integer)" },
161 {"TIMESTAMPDIFF(SQL_TSI_QUARTER", "cast(extract(quarter from ($3 - $2)) as integer)" },
162 {"TIMESTAMPDIFF(SQL_TSI_WEEK", "cast(extract(week from ($3 - $2)) as integer)" },
163 */
164 /*
165 {"TIMESTAMPDIFF(SQL_TSI_DAY", "cast(extract(day from ($3 - $2)) as integer)" },
166 {"TIMESTAMPDIFF(SQL_TSI_HOUR", "cast(extract(hour from ($3 - $2)) as integer)" },
167 {"TIMESTAMPDIFF(SQL_TSI_HOUR", "(extract(epoch from $3) - extract(epoch from $2)) / 3600" },
168 {"TIMESTAMPDIFF(SQL_TSI_MINUTE", "cast(extract(minute from ($3 - $2)) as integer)" },
169 {"TIMESTAMPDIFF(SQL_TSI_SECOND", "cast(extract(second from ($3 - $2)) as integer)" },
170 */
171 {"TIMESTAMPDIFF(SQL_TSI_DAY", "cast((extract(epoch from $3) - extract(epoch from $2)) / (24*60*60) as int)" },
172 {"TIMESTAMPDIFF(SQL_TSI_HOUR", "cast((extract(epoch from $3) - extract(epoch from $2)) / 3600 as int)" },
173 {"TIMESTAMPDIFF(SQL_TSI_MINUTE", "cast((extract(epoch from $3) - extract(epoch from $2)) / 60 as int)" },
174 {"TIMESTAMPDIFF(SQL_TSI_SECOND", "cast((extract(epoch from $3) - extract(epoch from $2)) as int)" },
175 {"TIMESTAMPDIFF(SQL_TSI_FRAC_SECOND", "mod(cast(extract(second from ($3 - $2)) as numeric), 1.0) * 1000000" },
176
177 /* { "EXTRACT", "extract" }, built_in */
178
179 /* { "DATABASE", "database" }, */
180 {"IFNULL", "coalesce($*)" },
181 {"USER", "cast(current_user as text)" },
182
183 {0, 0}
184 };
185
186 typedef struct
187 {
188 int infinity;
189 int m;
190 int d;
191 int y;
192 int hh;
193 int mm;
194 int ss;
195 int fr;
196 } SIMPLE_TIME;
197
198 static const char *mapFunction(const char *func, int param_count, const char * keyword);
199 static BOOL convert_money(const char *s, char *sout, size_t soutmax);
200 static char parse_datetime(const char *buf, SIMPLE_TIME *st);
201 size_t convert_linefeeds(const char *s, char *dst, size_t max, BOOL convlf, BOOL *changed);
202 static size_t convert_from_pgbinary(const char *value, char *rgbValue, SQLLEN cbValueMax);
203 static int convert_lo(StatementClass *stmt, const void *value, SQLSMALLINT fCType,
204 PTR rgbValue, SQLLEN cbValueMax, SQLLEN *pcbValue);
205 static int conv_from_octal(const char *s);
206 static SQLLEN pg_bin2hex(const char *src, char *dst, SQLLEN length);
207 #ifdef UNICODE_SUPPORT
208 static SQLLEN pg_bin2whex(const char *src, SQLWCHAR *dst, SQLLEN length);
209 #endif /* UNICODE_SUPPORT */
210
211 /*---------
212 * A Guide for date/time/timestamp conversions
213 *
214 * field_type fCType Output
215 * ---------- ------ ----------
216 * PG_TYPE_DATE SQL_C_DEFAULT SQL_C_DATE
217 * PG_TYPE_DATE SQL_C_DATE SQL_C_DATE
218 * PG_TYPE_DATE SQL_C_TIMESTAMP SQL_C_TIMESTAMP (time = 0 (midnight))
219 * PG_TYPE_TIME SQL_C_DEFAULT SQL_C_TIME
220 * PG_TYPE_TIME SQL_C_TIME SQL_C_TIME
221 * PG_TYPE_TIME SQL_C_TIMESTAMP SQL_C_TIMESTAMP (date = current date)
222 * PG_TYPE_ABSTIME SQL_C_DEFAULT SQL_C_TIMESTAMP
223 * PG_TYPE_ABSTIME SQL_C_DATE SQL_C_DATE (time is truncated)
224 * PG_TYPE_ABSTIME SQL_C_TIME SQL_C_TIME (date is truncated)
225 * PG_TYPE_ABSTIME SQL_C_TIMESTAMP SQL_C_TIMESTAMP
226 *---------
227 */
228
229
230 /*
231 * Macros for unsigned long handling.
232 */
233 #ifdef WIN32
234 #define ATOI32U(val) strtoul(val, NULL, 10)
235 #elif defined(HAVE_STRTOUL)
236 #define ATOI32U(val) strtoul(val, NULL, 10)
237 #else /* HAVE_STRTOUL */
238 #define ATOI32U atol
239 #endif /* WIN32 */
240
241 /*
242 * Macros for BIGINT handling.
243 */
244 #ifdef ODBCINT64
245 #ifdef WIN32
246 #define ATOI64(val) _strtoi64(val, NULL, 10)
247 #define ATOI64U(val) _strtoui64(val, NULL, 10)
248 #elif (SIZEOF_LONG == 8)
249 #define ATOI64(val) strtol(val, NULL, 10)
250 #define ATOI64U(val) strtoul(val, NULL, 10)
251 #else
252 #if defined(HAVE_STRTOLL)
253 #define ATOI64(val) strtoll(val, NULL, 10)
254 #define ATOI64U(val) strtoull(val, NULL, 10)
255 #else
ATOI64(const char * val)256 static ODBCINT64 ATOI64(const char *val)
257 {
258 ODBCINT64 ll;
259 sscanf(val, "%lld", &ll);
260 return ll;
261 }
ATOI64U(const char * val)262 static unsigned ODBCINT64 ATOI64U(const char *val)
263 {
264 unsigned ODBCINT64 ll;
265 sscanf(val, "%llu", &ll);
266 return ll;
267 }
268 #endif /* HAVE_STRTOLL */
269 #endif /* WIN32 */
270 #endif /* ODBCINT64 */
271
272 static void ResolveNumericParam(const SQL_NUMERIC_STRUCT *ns, char *chrform);
273 static void parse_to_numeric_struct(const char *wv, SQL_NUMERIC_STRUCT *ns, BOOL *overflow);
274
275 /*
276 * TIMESTAMP <-----> SIMPLE_TIME
277 * precision support since 7.2.
278 * time zone support is unavailable(the stuff is unreliable)
279 */
280 static BOOL
timestamp2stime(const char * str,SIMPLE_TIME * st,BOOL * bZone,int * zone)281 timestamp2stime(const char *str, SIMPLE_TIME *st, BOOL *bZone, int *zone)
282 {
283 char rest[64], bc[16],
284 *ptr;
285 int scnt,
286 i;
287 int y, m, d, hh, mm, ss;
288 #ifdef TIMEZONE_GLOBAL
289 long timediff;
290 #endif
291 BOOL withZone = *bZone;
292
293 *bZone = FALSE;
294 *zone = 0;
295 st->fr = 0;
296 st->infinity = 0;
297 rest[0] = '\0';
298 bc[0] = '\0';
299 if ((scnt = sscanf(str, "%4d-%2d-%2d %2d:%2d:%2d%31s %15s", &y, &m, &d, &hh, &mm, &ss, rest, bc)) < 6)
300 {
301 if (scnt == 3) /* date */
302 {
303 st->y = y;
304 st->m = m;
305 st->d = d;
306 st->hh = 0;
307 st->mm = 0;
308 st->ss = 0;
309 return TRUE;
310 }
311 if ((scnt = sscanf(str, "%2d:%2d:%2d%31s %15s", &hh, &mm, &ss, rest, bc)) < 3)
312 return FALSE;
313 else
314 {
315 st->hh = hh;
316 st->mm = mm;
317 st->ss = ss;
318 if (scnt == 3) /* time */
319 return TRUE;
320 }
321 }
322 else
323 {
324 st->y = y;
325 st->m = m;
326 st->d = d;
327 st->hh = hh;
328 st->mm = mm;
329 st->ss = ss;
330 if (scnt == 6)
331 return TRUE;
332 }
333 switch (rest[0])
334 {
335 case '+':
336 *bZone = TRUE;
337 *zone = atoi(&rest[1]);
338 break;
339 case '-':
340 *bZone = TRUE;
341 *zone = -atoi(&rest[1]);
342 break;
343 case '.':
344 if ((ptr = strchr(rest, '+')) != NULL)
345 {
346 *bZone = TRUE;
347 *zone = atoi(&ptr[1]);
348 *ptr = '\0';
349 }
350 else if ((ptr = strchr(rest, '-')) != NULL)
351 {
352 *bZone = TRUE;
353 *zone = -atoi(&ptr[1]);
354 *ptr = '\0';
355 }
356 for (i = 1; i < 10; i++)
357 {
358 if (!isdigit((UCHAR) rest[i]))
359 break;
360 }
361 for (; i < 10; i++)
362 rest[i] = '0';
363 rest[i] = '\0';
364 st->fr = atoi(&rest[1]);
365 break;
366 case 'B':
367 if (stricmp(rest, "BC") == 0)
368 st->y *= -1;
369 return TRUE;
370 default:
371 return TRUE;
372 }
373 if (stricmp(bc, "BC") == 0)
374 {
375 st->y *= -1;
376 }
377 if (!withZone || !*bZone || st->y < 1970)
378 return TRUE;
379 #ifdef TIMEZONE_GLOBAL
380 if (!TZNAME_GLOBAL[0] || !TZNAME_GLOBAL[0][0])
381 {
382 *bZone = FALSE;
383 return TRUE;
384 }
385 timediff = TIMEZONE_GLOBAL + (*zone) * 3600;
386 if (!DAYLIGHT_GLOBAL && timediff == 0) /* the same timezone */
387 return TRUE;
388 else
389 {
390 struct tm tm,
391 *tm2;
392 time_t time0;
393
394 *bZone = FALSE;
395 tm.tm_year = st->y - 1900;
396 tm.tm_mon = st->m - 1;
397 tm.tm_mday = st->d;
398 tm.tm_hour = st->hh;
399 tm.tm_min = st->mm;
400 tm.tm_sec = st->ss;
401 tm.tm_isdst = -1;
402 time0 = mktime(&tm);
403 if (time0 < 0)
404 return TRUE;
405 if (tm.tm_isdst > 0)
406 timediff -= 3600;
407 if (timediff == 0) /* the same time zone */
408 return TRUE;
409 time0 -= timediff;
410 #ifdef HAVE_LOCALTIME_R
411 if (time0 >= 0 && (tm2 = localtime_r(&time0, &tm)) != NULL)
412 #else
413 if (time0 >= 0 && (tm2 = localtime(&time0)) != NULL)
414 #endif /* HAVE_LOCALTIME_R */
415 {
416 st->y = tm2->tm_year + 1900;
417 st->m = tm2->tm_mon + 1;
418 st->d = tm2->tm_mday;
419 st->hh = tm2->tm_hour;
420 st->mm = tm2->tm_min;
421 st->ss = tm2->tm_sec;
422 *bZone = TRUE;
423 }
424 }
425 #endif /* TIMEZONE_GLOBAL */
426 return TRUE;
427 }
428
429 static int
stime2timestamp(const SIMPLE_TIME * st,char * str,size_t bufsize,BOOL bZone,int precision)430 stime2timestamp(const SIMPLE_TIME *st, char *str, size_t bufsize, BOOL bZone,
431 int precision)
432 {
433 char precstr[16],
434 zonestr[16];
435 int i;
436
437 precstr[0] = '\0';
438 if (st->infinity > 0)
439 {
440 return snprintf(str, bufsize, "%s", INFINITY_STRING);
441 }
442 else if (st->infinity < 0)
443 {
444 return snprintf(str, bufsize, "%s", MINFINITY_STRING);
445 }
446 if (precision > 0 && st->fr)
447 {
448 SPRINTF_FIXED(precstr, ".%09d", st->fr);
449 if (precision < 9)
450 precstr[precision + 1] = '\0';
451 else if (precision > 9)
452 precision = 9;
453 for (i = precision; i > 0; i--)
454 {
455 if (precstr[i] != '0')
456 break;
457 precstr[i] = '\0';
458 }
459 if (i == 0)
460 precstr[i] = '\0';
461 }
462 zonestr[0] = '\0';
463 #ifdef TIMEZONE_GLOBAL
464 if (bZone && TZNAME_GLOBAL[0] && TZNAME_GLOBAL[0][0] && st->y >= 1970)
465 {
466 long zoneint;
467 struct tm tm;
468 time_t time0;
469
470 zoneint = TIMEZONE_GLOBAL;
471 if (DAYLIGHT_GLOBAL && st->y >= 1900)
472 {
473 tm.tm_year = st->y - 1900;
474 tm.tm_mon = st->m - 1;
475 tm.tm_mday = st->d;
476 tm.tm_hour = st->hh;
477 tm.tm_min = st->mm;
478 tm.tm_sec = st->ss;
479 tm.tm_isdst = -1;
480 time0 = mktime(&tm);
481 if (time0 >= 0 && tm.tm_isdst > 0)
482 zoneint -= 3600;
483 }
484 if (zoneint > 0)
485 SPRINTF_FIXED(zonestr, "-%02d", (int) zoneint / 3600);
486 else
487 SPRINTF_FIXED(zonestr, "+%02d", -(int) zoneint / 3600);
488 }
489 #endif /* TIMEZONE_GLOBAL */
490 if (st->y < 0)
491 return snprintf(str, bufsize, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d%s%s BC", -st->y, st->m, st->d, st->hh, st->mm, st->ss, precstr, zonestr);
492 else
493 return snprintf(str, bufsize, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d%s%s", st->y, st->m, st->d, st->hh, st->mm, st->ss, precstr, zonestr);
494 }
495
496 static
interval2itype(SQLSMALLINT ctype)497 SQLINTERVAL interval2itype(SQLSMALLINT ctype)
498 {
499 SQLINTERVAL sqlitv = 0;
500
501 switch (ctype)
502 {
503 case SQL_C_INTERVAL_YEAR:
504 sqlitv = SQL_IS_YEAR;
505 break;
506 case SQL_C_INTERVAL_MONTH:
507 sqlitv = SQL_IS_MONTH;
508 break;
509 case SQL_C_INTERVAL_YEAR_TO_MONTH:
510 sqlitv = SQL_IS_YEAR_TO_MONTH;
511 break;
512 case SQL_C_INTERVAL_DAY:
513 sqlitv = SQL_IS_DAY;
514 break;
515 case SQL_C_INTERVAL_HOUR:
516 sqlitv = SQL_IS_HOUR;
517 break;
518 case SQL_C_INTERVAL_DAY_TO_HOUR:
519 sqlitv = SQL_IS_DAY_TO_HOUR;
520 break;
521 case SQL_C_INTERVAL_MINUTE:
522 sqlitv = SQL_IS_MINUTE;
523 break;
524 case SQL_C_INTERVAL_DAY_TO_MINUTE:
525 sqlitv = SQL_IS_DAY_TO_MINUTE;
526 break;
527 case SQL_C_INTERVAL_HOUR_TO_MINUTE:
528 sqlitv = SQL_IS_HOUR_TO_MINUTE;
529 break;
530 case SQL_C_INTERVAL_SECOND:
531 sqlitv = SQL_IS_SECOND;
532 break;
533 case SQL_C_INTERVAL_DAY_TO_SECOND:
534 sqlitv = SQL_IS_DAY_TO_SECOND;
535 break;
536 case SQL_C_INTERVAL_HOUR_TO_SECOND:
537 sqlitv = SQL_IS_HOUR_TO_SECOND;
538 break;
539 case SQL_C_INTERVAL_MINUTE_TO_SECOND:
540 sqlitv = SQL_IS_MINUTE_TO_SECOND;
541 break;
542 }
543 return sqlitv;
544 }
545
546 /*
547 * Interval data <-----> SQL_INTERVAL_STRUCT
548 */
549
getPrecisionPart(int precision,const char * precPart)550 static int getPrecisionPart(int precision, const char * precPart)
551 {
552 char fraction[] = "000000000";
553 int fracs = sizeof(fraction) - 1;
554 size_t cpys;
555
556 if (precision < 0)
557 precision = 6; /* default */
558 if (precision == 0)
559 return 0;
560 cpys = strlen(precPart);
561 if (cpys > fracs)
562 cpys = fracs;
563 memcpy(fraction, precPart, cpys);
564 fraction[precision] = '\0';
565
566 return atoi(fraction);
567 }
568
569 static BOOL
interval2istruct(SQLSMALLINT ctype,int precision,const char * str,SQL_INTERVAL_STRUCT * st)570 interval2istruct(SQLSMALLINT ctype, int precision, const char *str, SQL_INTERVAL_STRUCT *st)
571 {
572 char lit1[64], lit2[64];
573 int scnt, years, mons, days, hours, minutes, seconds;
574 BOOL sign;
575 SQLINTERVAL itype = interval2itype(ctype);
576
577 memset(st, 0, sizeof(SQL_INTERVAL_STRUCT));
578 if ((scnt = sscanf(str, "%d-%d", &years, &mons)) >=2)
579 {
580 if (SQL_IS_YEAR_TO_MONTH == itype)
581 {
582 sign = years < 0 ? SQL_TRUE : SQL_FALSE;
583 st->interval_type = itype;
584 st->interval_sign = sign;
585 st->intval.year_month.year = sign ? (-years) : years;
586 st->intval.year_month.month = mons;
587 return TRUE;
588 }
589 return FALSE;
590 }
591 else if (scnt = sscanf(str, "%d %02d:%02d:%02d.%09s", &days, &hours, &minutes, &seconds, lit2), 5 == scnt || 4 == scnt)
592 {
593 sign = days < 0 ? SQL_TRUE : SQL_FALSE;
594 st->interval_type = itype;
595 st->interval_sign = sign;
596 st->intval.day_second.day = sign ? (-days) : days;
597 st->intval.day_second.hour = hours;
598 st->intval.day_second.minute = minutes;
599 st->intval.day_second.second = seconds;
600 if (scnt > 4)
601 st->intval.day_second.fraction = getPrecisionPart(precision, lit2);
602 return TRUE;
603 }
604 else if ((scnt = sscanf(str, "%d %10s %d %10s", &years, lit1, &mons, lit2)) >=4)
605 {
606 if (strnicmp(lit1, "year", 4) == 0 &&
607 strnicmp(lit2, "mon", 2) == 0 &&
608 (SQL_IS_MONTH == itype ||
609 SQL_IS_YEAR_TO_MONTH == itype))
610 {
611 sign = years < 0 ? SQL_TRUE : SQL_FALSE;
612 st->interval_type = itype;
613 st->interval_sign = sign;
614 st->intval.year_month.year = sign ? (-years) : years;
615 st->intval.year_month.month = sign ? (-mons) : mons;
616 return TRUE;
617 }
618 return FALSE;
619 }
620 if ((scnt = sscanf(str, "%d %10s %d", &years, lit1, &days)) == 2)
621 {
622 sign = years < 0 ? SQL_TRUE : SQL_FALSE;
623 if (SQL_IS_YEAR == itype &&
624 (stricmp(lit1, "year") == 0 ||
625 stricmp(lit1, "years") == 0))
626 {
627 st->interval_type = itype;
628 st->interval_sign = sign;
629 st->intval.year_month.year = sign ? (-years) : years;
630 return TRUE;
631 }
632 if (SQL_IS_MONTH == itype &&
633 (stricmp(lit1, "mon") == 0 ||
634 stricmp(lit1, "mons") == 0))
635 {
636 st->interval_type = itype;
637 st->interval_sign = sign;
638 st->intval.year_month.month = sign ? (-years) : years;
639 return TRUE;
640 }
641 if (SQL_IS_DAY == itype &&
642 (stricmp(lit1, "day") == 0 ||
643 stricmp(lit1, "days") == 0))
644 {
645 st->interval_type = itype;
646 st->interval_sign = sign;
647 st->intval.day_second.day = sign ? (-years) : years;
648 return TRUE;
649 }
650 return FALSE;
651 }
652 if (itype == SQL_IS_YEAR || itype == SQL_IS_MONTH || itype == SQL_IS_YEAR_TO_MONTH)
653 {
654 /* these formats should've been handled above already */
655 return FALSE;
656 }
657 scnt = sscanf(str, "%d %10s %02d:%02d:%02d.%09s", &days, lit1, &hours, &minutes, &seconds, lit2);
658 if (scnt == 5 || scnt == 6)
659 {
660 if (strnicmp(lit1, "day", 3) != 0)
661 return FALSE;
662 sign = days < 0 ? SQL_TRUE : SQL_FALSE;
663
664 st->interval_type = itype;
665 st->interval_sign = sign;
666 st->intval.day_second.day = sign ? (-days) : days;
667 st->intval.day_second.hour = sign ? (-hours) : hours;
668 st->intval.day_second.minute = minutes;
669 st->intval.day_second.second = seconds;
670 if (scnt > 5)
671 st->intval.day_second.fraction = getPrecisionPart(precision, lit2);
672 return TRUE;
673 }
674 scnt = sscanf(str, "%02d:%02d:%02d.%09s", &hours, &minutes, &seconds, lit2);
675 if (scnt == 3 || scnt == 4)
676 {
677 sign = hours < 0 ? SQL_TRUE : SQL_FALSE;
678
679 st->interval_type = itype;
680 st->interval_sign = sign;
681 st->intval.day_second.hour = sign ? (-hours) : hours;
682 st->intval.day_second.minute = minutes;
683 st->intval.day_second.second = seconds;
684 if (scnt > 3)
685 st->intval.day_second.fraction = getPrecisionPart(precision, lit2);
686 return TRUE;
687 }
688
689 return FALSE;
690 }
691
692
693 #ifdef HAVE_LOCALE_H
694 /*
695 * Get the decimal point of the current locale.
696 *
697 * XXX: This isn't thread-safe, if another thread changes the locale with
698 * setlocale() concurrently. There are two problems with that:
699 *
700 * 1. The pointer returned by localeconv(), or the lc->decimal_point string,
701 * might be invalidated by calls in other threads. Until someone comes up
702 * with a thread-safe version of localeconv(), there isn't much we can do
703 * about that. (libc implementations that return a static buffer (like glibc)
704 * happen to be safe from the lconv struct being invalidated, but the
705 * decimal_point string might still not point to a static buffer).
706 *
707 * 2. The between the call to sprintf() and get_current_decimal_point(), the
708 * decimal point might change. That would cause set_server_decimal_point()
709 * to fail to recognize a decimal separator, and we might send a numeric
710 * string to the server that the server won't recognize. This would cause
711 * the query to fail in the server.
712 *
713 * XXX: we only take into account the first byte of the decimal separator.
714 */
get_current_decimal_point(void)715 static char get_current_decimal_point(void)
716 {
717 struct lconv *lc = localeconv();
718
719 return lc->decimal_point[0];
720 }
721
722 /*
723 * Modify the string representation of a numeric/float value, converting the
724 * decimal point from '.' to the correct decimal separator of the current
725 * locale.
726 */
set_server_decimal_point(char * num,SQLLEN len)727 static void set_server_decimal_point(char *num, SQLLEN len)
728 {
729 char current_decimal_point = get_current_decimal_point();
730 char *str;
731 SQLLEN i;
732
733 if ('.' == current_decimal_point)
734 return;
735 i = 0;
736 for (str = num; '\0' != *str; str++)
737 {
738 if (*str == current_decimal_point)
739 {
740 *str = '.';
741 break;
742 }
743
744 if (len != SQL_NTS && i++ >= len)
745 break;
746 }
747 }
748
749 /*
750 * Inverse of set_server_decimal_point.
751 */
set_client_decimal_point(char * num)752 static void set_client_decimal_point(char *num)
753 {
754 char current_decimal_point = get_current_decimal_point();
755 char *str;
756
757 if ('.' == current_decimal_point)
758 return;
759 for (str = num; '\0' != *str; str++)
760 {
761 if (*str == '.')
762 {
763 *str = current_decimal_point;
764 break;
765 }
766 }
767 }
768 #else
set_server_decimal_point(char * num)769 static void set_server_decimal_point(char *num) {}
set_client_decimal_point(char * num,BOOL)770 static void set_client_decimal_point(char *num, BOOL) {}
771 #endif /* HAVE_LOCALE_H */
772
773 /* This is called by SQLFetch() */
774 int
copy_and_convert_field_bindinfo(StatementClass * stmt,OID field_type,int atttypmod,void * value,int col)775 copy_and_convert_field_bindinfo(StatementClass *stmt, OID field_type, int atttypmod, void *value, int col)
776 {
777 ARDFields *opts = SC_get_ARDF(stmt);
778 BindInfoClass *bic;
779 SQLULEN offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0;
780
781 if (opts->allocated <= col)
782 extend_column_bindings(opts, col + 1);
783 bic = &(opts->bindings[col]);
784 SC_set_current_col(stmt, -1);
785 return copy_and_convert_field(stmt, field_type, atttypmod, value,
786 bic->returntype, bic->precision,
787 (PTR) (bic->buffer + offset), bic->buflen,
788 LENADDR_SHIFT(bic->used, offset), LENADDR_SHIFT(bic->indicator, offset));
789 }
790
791 /*
792 * Is 'str' a valid integer literal, consisting only of ASCII characters
793 * 0-9 ?
794 *
795 * Also, *negative is set to TRUE if the value was negative.
796 *
797 * We don't check for overflow here. This is just to decide if we need to
798 * quote the value.
799 */
800 static BOOL
valid_int_literal(const char * str,SQLLEN len,BOOL * negative)801 valid_int_literal(const char *str, SQLLEN len, BOOL *negative)
802 {
803 SQLLEN i = 0;
804
805 /* Check there is a minus sign in front */
806 if ((len == SQL_NTS || len > 0) && str[0] == '-')
807 {
808 i++;
809 *negative = TRUE;
810 }
811 else
812 *negative = FALSE;
813
814 /*
815 * Must begin with a digit. This also rejects empty strings and '-'.
816 */
817 if (i == len || !(str[i] >= '0' && str[i] <= '9'))
818 return FALSE;
819
820 for (; str[i] && (len == SQL_NTS || i < len); i++)
821 {
822 if (!(str[i] >= '0' && str[i] <= '9'))
823 return FALSE;
824 }
825
826 return TRUE;
827 }
828
get_double_value(const char * str)829 static double get_double_value(const char *str)
830 {
831 if (stricmp(str, NAN_STRING) == 0)
832 #ifdef NAN
833 return (double) NAN;
834 #else
835 {
836 double a = .0;
837 return .0 / a;
838 }
839 #endif /* NAN */
840 else if (stricmp(str, INFINITY_STRING) == 0)
841 #ifdef INFINITY
842 return (double) INFINITY;
843 #else
844 return (double) (HUGE_VAL * HUGE_VAL);
845 #endif /* INFINITY */
846 else if (stricmp(str, MINFINITY_STRING) == 0)
847 #ifdef INFINITY
848 return (double) -INFINITY;
849 #else
850 return (double) -(HUGE_VAL * HUGE_VAL);
851 #endif /* INFINITY */
852 return atof(str);
853 }
854
char2guid(const char * str,SQLGUID * g)855 static int char2guid(const char *str, SQLGUID *g)
856 {
857 /*
858 * SQLGUID.Data1 is an "unsigned long" on some platforms, and
859 * "unsigned int" on others. For format "%08X", it should be an
860 * "unsigned int", so use a temporary variable for it.
861 */
862 unsigned int Data1;
863 if (sscanf(str,
864 "%08X-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
865 &Data1,
866 &g->Data2, &g->Data3,
867 &g->Data4[0], &g->Data4[1], &g->Data4[2], &g->Data4[3],
868 &g->Data4[4], &g->Data4[5], &g->Data4[6], &g->Data4[7]) < 11)
869 return COPY_GENERAL_ERROR;
870 g->Data1 = Data1;
871 return COPY_OK;
872 }
873
effective_fraction(int fraction,int * width)874 static int effective_fraction(int fraction, int *width)
875 {
876 for (*width = 9; fraction % 10 == 0; (*width)--, fraction /= 10)
877 ;
878 return fraction;
879 }
880
881
882 static int
get_terminator_len(SQLSMALLINT fCType)883 get_terminator_len(SQLSMALLINT fCType)
884 {
885 switch (fCType)
886 {
887 #ifdef UNICODE_SUPPORT
888 case SQL_C_WCHAR:
889 return WCLEN;
890 #endif /* UNICODE_SUPPORT */
891 case SQL_C_BINARY:
892 return 0;
893 }
894
895 /* SQL_C_CHAR or INTERNAL_ASIS_TYPE */
896 return 1;
897 }
898
899 static SQLLEN
get_adjust_len(SQLSMALLINT fCType,SQLLEN len)900 get_adjust_len(SQLSMALLINT fCType, SQLLEN len)
901 {
902 switch (fCType)
903 {
904 #ifdef UNICODE_SUPPORT
905 case SQL_C_WCHAR:
906 return (len / WCLEN) * WCLEN;
907 #endif /* UNICODE_SUPPORT */
908 }
909
910 return len;
911 }
912
913 #define BYTEA_PROCESS_ESCAPE 1
914 #define BYTEA_PROCESS_BINARY 2
915
916 static int
setup_getdataclass(SQLLEN * const length_return,const char ** const ptr_return,int * needbuflen_return,GetDataClass * const pgdc,const char * neut_str,const OID field_type,const SQLSMALLINT fCType,const SQLLEN cbValueMax,const ConnectionClass * const conn)917 setup_getdataclass(SQLLEN * const length_return, const char ** const ptr_return,
918 int *needbuflen_return, GetDataClass * const pgdc, const char *neut_str,
919 const OID field_type, const SQLSMALLINT fCType,
920 const SQLLEN cbValueMax, const ConnectionClass * const conn)
921 {
922 SQLLEN len = (-2);
923 const char *ptr = NULL;
924 int needbuflen = 0;
925 int result = COPY_OK;
926
927 BOOL lf_conv = conn->connInfo.lf_conversion;
928 int bytea_process_kind = 0;
929 BOOL already_processed = FALSE;
930 BOOL changed = FALSE;
931 int len_for_wcs_term = 0;
932
933 #ifdef UNICODE_SUPPORT
934 char *allocbuf = NULL;
935 int unicode_count = -1;
936 BOOL localize_needed = FALSE;
937 BOOL hybrid = FALSE;
938 #endif /* UNICODE_SUPPORT */
939
940 if (PG_TYPE_BYTEA == field_type)
941 {
942 if (SQL_C_BINARY == fCType)
943 bytea_process_kind = BYTEA_PROCESS_BINARY;
944 else if (0 == strnicmp(neut_str, "\\x", 2)) /* hex format */
945 neut_str += 2;
946 else
947 bytea_process_kind = BYTEA_PROCESS_ESCAPE;
948 }
949
950 #ifdef UNICODE_SUPPORT
951 if (0 == bytea_process_kind)
952 {
953 if (get_convtype() > 0) /* coversion between the current locale is available */
954 {
955 BOOL wcs_debug = conn->connInfo.wcs_debug;
956 BOOL same_encoding = (conn->ccsc == pg_CS_code(conn->locale_encoding));
957 BOOL is_utf8 = (UTF8 == conn->ccsc);
958
959 switch (field_type)
960 {
961 case PG_TYPE_UNKNOWN:
962 case PG_TYPE_BPCHAR:
963 case PG_TYPE_VARCHAR:
964 case PG_TYPE_TEXT:
965 case PG_TYPE_BPCHARARRAY:
966 case PG_TYPE_VARCHARARRAY:
967 case PG_TYPE_TEXTARRAY:
968 if (SQL_C_CHAR == fCType || SQL_C_BINARY == fCType)
969 localize_needed = (!same_encoding || wcs_debug);
970 if (SQL_C_WCHAR == fCType)
971 hybrid = (!is_utf8 || (same_encoding && wcs_debug));
972 }
973 MYLOG(0, "localize=%d hybrid=%d is_utf8=%d same_encoding=%d wcs_debug=%d\n", localize_needed, hybrid, is_utf8, same_encoding, wcs_debug);
974 }
975 }
976 if (fCType == SQL_C_WCHAR)
977 {
978 if (BYTEA_PROCESS_ESCAPE == bytea_process_kind)
979 unicode_count = convert_from_pgbinary(neut_str, NULL, 0) * 2;
980 else if (hybrid)
981 {
982 MYLOG(0, "hybrid estimate\n");
983 if ((unicode_count = bindcol_hybrid_estimate(neut_str, lf_conv, &allocbuf)) < 0)
984 {
985 result = COPY_INVALID_STRING_CONVERSION;
986 goto cleanup;
987 }
988 }
989 else /* normally */
990 {
991 unicode_count = utf8_to_ucs2_lf(neut_str, SQL_NTS, lf_conv, NULL, 0, FALSE);
992 }
993 len = WCLEN * unicode_count;
994 already_processed = changed = TRUE;
995 }
996 else if (localize_needed)
997 {
998 if ((len = bindcol_localize_estimate(neut_str, lf_conv, &allocbuf)) < 0)
999 {
1000 result = COPY_INVALID_STRING_CONVERSION;
1001 goto cleanup;
1002 }
1003 already_processed = changed = TRUE;
1004 }
1005 #endif /* UNICODE_SUPPORT */
1006
1007 if (already_processed) /* skip */
1008 ;
1009 else if (0 != bytea_process_kind)
1010 {
1011 len = convert_from_pgbinary(neut_str, NULL, 0);
1012 if (BYTEA_PROCESS_BINARY != bytea_process_kind)
1013 len *= 2;
1014 changed = TRUE;
1015 }
1016 else
1017 /* convert linefeeds to carriage-return/linefeed */
1018 len = convert_linefeeds(neut_str, NULL, 0, lf_conv, &changed);
1019
1020 /* just returns length info */
1021 if (cbValueMax == 0)
1022 {
1023 result = COPY_RESULT_TRUNCATED;
1024 goto cleanup;
1025 }
1026
1027 if (!pgdc->ttlbuf)
1028 pgdc->ttlbuflen = 0;
1029 needbuflen = len + get_terminator_len(fCType);
1030 if (SQL_C_BINARY == fCType)
1031 {
1032 /*
1033 * Though Binary doesn't have NULL terminator,
1034 * bindcol_localize_exec() needs output buffer
1035 * for NULL terminator.
1036 */
1037 len_for_wcs_term = 1;
1038 }
1039 if (changed || needbuflen > cbValueMax)
1040 {
1041 if (needbuflen > (SQLLEN) pgdc->ttlbuflen)
1042 {
1043 pgdc->ttlbuf = realloc(pgdc->ttlbuf, needbuflen + len_for_wcs_term);
1044 pgdc->ttlbuflen = needbuflen;
1045 }
1046
1047 already_processed = FALSE;
1048 #ifdef UNICODE_SUPPORT
1049 if (fCType == SQL_C_WCHAR)
1050 {
1051 if (BYTEA_PROCESS_ESCAPE == bytea_process_kind)
1052 {
1053 len = convert_from_pgbinary(neut_str, pgdc->ttlbuf, pgdc->ttlbuflen);
1054 len = pg_bin2whex(pgdc->ttlbuf, (SQLWCHAR *) pgdc->ttlbuf, len);
1055 }
1056 else
1057 {
1058 if (!hybrid) /* normally */
1059 utf8_to_ucs2_lf(neut_str, SQL_NTS, lf_conv, (SQLWCHAR *) pgdc->ttlbuf, unicode_count, FALSE);
1060 else /* hybrid */
1061 {
1062 MYLOG(0, "hybrid convert\n");
1063 if (bindcol_hybrid_exec((SQLWCHAR *) pgdc->ttlbuf, neut_str, unicode_count + 1, lf_conv, &allocbuf) < 0)
1064 {
1065 result = COPY_INVALID_STRING_CONVERSION;
1066 goto cleanup;
1067 }
1068 }
1069 }
1070 already_processed = TRUE;
1071 }
1072 else if (localize_needed)
1073 {
1074 if (bindcol_localize_exec(pgdc->ttlbuf, len + 1, lf_conv, &allocbuf) < 0)
1075 {
1076 result = COPY_INVALID_STRING_CONVERSION;
1077 goto cleanup;
1078 }
1079 already_processed = TRUE;
1080 }
1081 #endif /* UNICODE_SUPPORT */
1082
1083 if (already_processed)
1084 ;
1085 else if (0 != bytea_process_kind)
1086 {
1087 len = convert_from_pgbinary(neut_str, pgdc->ttlbuf, pgdc->ttlbuflen);
1088 if (BYTEA_PROCESS_ESCAPE == bytea_process_kind)
1089 len = pg_bin2hex(pgdc->ttlbuf, pgdc->ttlbuf, len);
1090 }
1091 else
1092 convert_linefeeds(neut_str, pgdc->ttlbuf, pgdc->ttlbuflen, lf_conv, &changed);
1093 ptr = pgdc->ttlbuf;
1094 pgdc->ttlbufused = len;
1095 }
1096 else
1097 {
1098 if (pgdc->ttlbuf)
1099 {
1100 free(pgdc->ttlbuf);
1101 pgdc->ttlbuf = NULL;
1102 }
1103 ptr = neut_str;
1104 }
1105 cleanup:
1106 #ifdef UNICODE_SUPPORT
1107 if (allocbuf)
1108 free(allocbuf);
1109 #endif /* UNICODE_SUPPORT */
1110
1111 *length_return = len;
1112 *ptr_return = ptr;
1113 *needbuflen_return = needbuflen;
1114
1115 return result;
1116 }
1117
1118 /*
1119 gdata SC_get_GDTI(stmt)
1120 current_col stmt->current_col
1121 */
1122
1123 /*
1124 * fCType treated in the following function is
1125 *
1126 * SQL_C_CHAR, SQL_C_BINARY, SQL_C_WCHAR or INTERNAL_ASIS_TYPE
1127 */
1128 static int
convert_text_field_to_sql_c(GetDataInfo * const gdata,const int current_col,const char * const neut_str,const OID field_type,const SQLSMALLINT fCType,char * const rgbValueBindRow,const SQLLEN cbValueMax,const ConnectionClass * const conn,SQLLEN * const length_return)1129 convert_text_field_to_sql_c(GetDataInfo * const gdata, const int current_col,
1130 const char * const neut_str, const OID field_type,
1131 const SQLSMALLINT fCType, char * const rgbValueBindRow,
1132 const SQLLEN cbValueMax, const ConnectionClass * const conn,
1133 SQLLEN * const length_return)
1134 {
1135 int result = COPY_OK;
1136 SQLLEN len = (-2);
1137 GetDataClass *pgdc;
1138 int copy_len = 0, needbuflen = 0, i;
1139 const char *ptr;
1140
1141 MYLOG(0, "field_type=%u type=%d\n", field_type, fCType);
1142
1143 switch (field_type)
1144 {
1145 case PG_TYPE_FLOAT4:
1146 case PG_TYPE_FLOAT8:
1147 case PG_TYPE_NUMERIC:
1148 set_client_decimal_point((char *) neut_str);
1149 break;
1150 }
1151
1152 if (current_col < 0)
1153 {
1154 pgdc = &(gdata->fdata);
1155 pgdc->data_left = -1;
1156 }
1157 else
1158 pgdc = &gdata->gdata[current_col];
1159 if (pgdc->data_left < 0)
1160 {
1161 if (COPY_OK != (result = setup_getdataclass(&len, &ptr,
1162 &needbuflen, pgdc, neut_str, field_type,
1163 fCType, cbValueMax, conn)))
1164 goto cleanup;
1165 }
1166 else
1167 {
1168 ptr = pgdc->ttlbuf;
1169 len = pgdc->ttlbufused;
1170 }
1171
1172 MYLOG(0, "DEFAULT: len = " FORMAT_LEN ", ptr = '%.*s'\n", len, (int) len, ptr);
1173
1174 if (current_col >= 0)
1175 {
1176 if (pgdc->data_left > 0)
1177 {
1178 ptr += (len - pgdc->data_left);
1179 len = pgdc->data_left;
1180 needbuflen = len + (pgdc->ttlbuflen - pgdc->ttlbufused);
1181 }
1182 else
1183 pgdc->data_left = len;
1184 }
1185
1186 if (cbValueMax > 0)
1187 {
1188 BOOL already_copied = FALSE;
1189 int terminatorlen;
1190
1191 terminatorlen = get_terminator_len(fCType);
1192 if (terminatorlen >= cbValueMax)
1193 copy_len = 0;
1194 else if (len + terminatorlen > cbValueMax)
1195 copy_len = get_adjust_len(fCType, cbValueMax - terminatorlen);
1196 else
1197 copy_len = len;
1198
1199 if (!already_copied)
1200 {
1201 /* Copy the data */
1202 if (copy_len > 0)
1203 memcpy(rgbValueBindRow, ptr, copy_len);
1204 /* Add null terminator */
1205 for (i = 0; i < terminatorlen && copy_len + i < cbValueMax; i++)
1206 rgbValueBindRow[copy_len + i] = '\0';
1207 }
1208 /* Adjust data_left for next time */
1209 if (current_col >= 0)
1210 pgdc->data_left -= copy_len;
1211 }
1212
1213 /*
1214 * Finally, check for truncation so that proper status can
1215 * be returned
1216 */
1217 if (cbValueMax > 0 && needbuflen > cbValueMax)
1218 result = COPY_RESULT_TRUNCATED;
1219 else
1220 {
1221 if (pgdc->ttlbuf != NULL)
1222 {
1223 free(pgdc->ttlbuf);
1224 pgdc->ttlbuf = NULL;
1225 }
1226 }
1227
1228 #ifdef UNICODE_SUPPORT
1229 if (SQL_C_WCHAR == fCType)
1230 MYLOG(0, " SQL_C_WCHAR, default: len = " FORMAT_LEN ", cbValueMax = " FORMAT_LEN ", rgbValueBindRow = '%s'\n", len, cbValueMax, rgbValueBindRow);
1231 else
1232 #endif /* UNICODE_SUPPORT */
1233 if (SQL_C_BINARY == fCType)
1234 MYLOG(0, " SQL_C_BINARY, default: len = " FORMAT_LEN ", cbValueMax = " FORMAT_LEN ", rgbValueBindRow = '%.*s'\n", len, cbValueMax, copy_len, rgbValueBindRow);
1235 else
1236 MYLOG(0, " SQL_C_CHAR, default: len = " FORMAT_LEN ", cbValueMax = " FORMAT_LEN ", rgbValueBindRow = '%s'\n", len, cbValueMax, rgbValueBindRow);
1237
1238 cleanup:
1239 *length_return = len;
1240
1241 return result;
1242 }
1243
1244 /* This is called by SQLGetData() */
1245 int
copy_and_convert_field(StatementClass * stmt,OID field_type,int atttypmod,void * valuei,SQLSMALLINT fCType,int precision,PTR rgbValue,SQLLEN cbValueMax,SQLLEN * pcbValue,SQLLEN * pIndicator)1246 copy_and_convert_field(StatementClass *stmt,
1247 OID field_type, int atttypmod,
1248 void *valuei,
1249 SQLSMALLINT fCType, int precision,
1250 PTR rgbValue, SQLLEN cbValueMax,
1251 SQLLEN *pcbValue, SQLLEN *pIndicator)
1252 {
1253 CSTR func = "copy_and_convert_field";
1254 const char *value = valuei;
1255 ARDFields *opts = SC_get_ARDF(stmt);
1256 GetDataInfo *gdata = SC_get_GDTI(stmt);
1257 SQLLEN len = 0;
1258 SIMPLE_TIME std_time;
1259 #ifdef HAVE_LOCALTIME_R
1260 struct tm tm;
1261 #endif /* HAVE_LOCALTIME_R */
1262 SQLLEN pcbValueOffset,
1263 rgbValueOffset;
1264 char *rgbValueBindRow = NULL;
1265 SQLLEN *pcbValueBindRow = NULL, *pIndicatorBindRow = NULL;
1266 SQLSETPOSIROW bind_row = stmt->bind_row;
1267 int bind_size = opts->bind_size;
1268 int result = COPY_OK;
1269 const ConnectionClass *conn = SC_get_conn(stmt);
1270 BOOL text_bin_handling;
1271 const char *neut_str = value;
1272 char booltemp[3];
1273 char midtemp[64];
1274 GetDataClass *pgdc;
1275
1276 if (stmt->current_col >= 0)
1277 {
1278 if (stmt->current_col >= opts->allocated)
1279 {
1280 return SQL_ERROR;
1281 }
1282 if (gdata->allocated != opts->allocated)
1283 extend_getdata_info(gdata, opts->allocated, TRUE);
1284 pgdc = &gdata->gdata[stmt->current_col];
1285 if (pgdc->data_left == -2)
1286 pgdc->data_left = (cbValueMax > 0) ? 0 : -1; /* This seems to be *
1287 * needed by ADO ? */
1288 if (pgdc->data_left == 0)
1289 {
1290 if (pgdc->ttlbuf != NULL)
1291 {
1292 free(pgdc->ttlbuf);
1293 pgdc->ttlbuf = NULL;
1294 pgdc->ttlbuflen = 0;
1295 }
1296 pgdc->data_left = -2; /* needed by ADO ? */
1297 return COPY_NO_DATA_FOUND;
1298 }
1299 }
1300 /*---------
1301 * rgbValueOffset is *ONLY* for character and binary data.
1302 * pcbValueOffset is for computing any pcbValue location
1303 *---------
1304 */
1305
1306 if (bind_size > 0)
1307 pcbValueOffset = rgbValueOffset = (bind_size * bind_row);
1308 else
1309 {
1310 pcbValueOffset = bind_row * sizeof(SQLLEN);
1311 rgbValueOffset = bind_row * cbValueMax;
1312 }
1313 /*
1314 * The following is applicable in case bind_size > 0
1315 * or the fCType is of variable length.
1316 */
1317 if (rgbValue)
1318 rgbValueBindRow = (char *) rgbValue + rgbValueOffset;
1319 if (pcbValue)
1320 pcbValueBindRow = LENADDR_SHIFT(pcbValue, pcbValueOffset);
1321 if (pIndicator)
1322 {
1323 pIndicatorBindRow = LENADDR_SHIFT(pIndicator, pcbValueOffset);
1324 *pIndicatorBindRow = 0;
1325 }
1326
1327 memset(&std_time, 0, sizeof(SIMPLE_TIME));
1328
1329 MYLOG(0, "field_type = %d, fctype = %d, value = '%s', cbValueMax=" FORMAT_LEN "\n", field_type, fCType, (value == NULL) ? "<NULL>" : value, cbValueMax);
1330
1331 if (!value)
1332 {
1333 MYLOG(0, "null_cvt_date_string=%d\n", conn->connInfo.cvt_null_date_string);
1334 /* a speicial handling for FOXPRO NULL -> NULL_STRING */
1335 if (conn->connInfo.cvt_null_date_string > 0 &&
1336 (PG_TYPE_DATE == field_type ||
1337 PG_TYPE_DATETIME == field_type ||
1338 PG_TYPE_TIMESTAMP_NO_TMZONE == field_type) &&
1339 (SQL_C_CHAR == fCType ||
1340 #ifdef UNICODE_SUPPORT
1341 SQL_C_WCHAR == fCType ||
1342 #endif /* UNICODE_SUPPORT */
1343 SQL_C_DATE == fCType ||
1344 SQL_C_TYPE_DATE == fCType ||
1345 SQL_C_DEFAULT == fCType))
1346 {
1347 if (pcbValueBindRow)
1348 *pcbValueBindRow = 0;
1349 switch (fCType)
1350 {
1351 case SQL_C_CHAR:
1352 if (rgbValueBindRow && cbValueMax > 0)
1353 *rgbValueBindRow = '\0';
1354 else
1355 result = COPY_RESULT_TRUNCATED;
1356 break;
1357 case SQL_C_DATE:
1358 case SQL_C_TYPE_DATE:
1359 case SQL_C_DEFAULT:
1360 if (rgbValueBindRow && cbValueMax >= sizeof(DATE_STRUCT))
1361 {
1362 memset(rgbValueBindRow, 0, cbValueMax);
1363 if (pcbValueBindRow)
1364 *pcbValueBindRow = sizeof(DATE_STRUCT);
1365 }
1366 else
1367 result = COPY_RESULT_TRUNCATED;
1368 break;
1369 #ifdef UNICODE_SUPPORT
1370 case SQL_C_WCHAR:
1371 if (rgbValueBindRow && cbValueMax >= WCLEN)
1372 memset(rgbValueBindRow, 0, WCLEN);
1373 else
1374 result = COPY_RESULT_TRUNCATED;
1375 break;
1376 #endif /* UNICODE_SUPPORT */
1377 }
1378 return result;
1379 }
1380 /*
1381 * handle a null just by returning SQL_NULL_DATA in pcbValue, and
1382 * doing nothing to the buffer.
1383 */
1384 else if (pIndicator)
1385 {
1386 *pIndicatorBindRow = SQL_NULL_DATA;
1387 return COPY_OK;
1388 }
1389 else
1390 {
1391 SC_set_error(stmt, STMT_RETURN_NULL_WITHOUT_INDICATOR, "StrLen_or_IndPtr was a null pointer and NULL data was retrieved", func);
1392 return SQL_ERROR;
1393 }
1394 }
1395
1396 if (stmt->hdbc->DataSourceToDriver != NULL)
1397 {
1398 size_t length = strlen(value);
1399
1400 stmt->hdbc->DataSourceToDriver(stmt->hdbc->translation_option,
1401 SQL_CHAR, valuei, (SDWORD) length,
1402 valuei, (SDWORD) length, NULL,
1403 NULL, 0, NULL);
1404 }
1405
1406 /*
1407 * First convert any specific postgres types into more useable data.
1408 *
1409 * NOTE: Conversions from PG char/varchar of a date/time/timestamp value
1410 * to SQL_C_DATE,SQL_C_TIME, SQL_C_TIMESTAMP not supported
1411 */
1412 switch (field_type)
1413 {
1414 /*
1415 * $$$ need to add parsing for date/time/timestamp strings in
1416 * PG_TYPE_CHAR,VARCHAR $$$
1417 */
1418 case PG_TYPE_DATE:
1419 sscanf(value, "%4d-%2d-%2d", &std_time.y, &std_time.m, &std_time.d);
1420 break;
1421
1422 case PG_TYPE_TIME:
1423 {
1424
1425 BOOL bZone = FALSE; /* time zone stuff is unreliable */
1426 int zone;
1427 timestamp2stime(value, &std_time, &bZone, &zone);
1428 }
1429 break;
1430
1431 case PG_TYPE_ABSTIME:
1432 case PG_TYPE_DATETIME:
1433 case PG_TYPE_TIMESTAMP_NO_TMZONE:
1434 case PG_TYPE_TIMESTAMP:
1435 std_time.fr = 0;
1436 std_time.infinity = 0;
1437 if (strnicmp(value, INFINITY_STRING, 8) == 0)
1438 {
1439 std_time.infinity = 1;
1440 std_time.m = 12;
1441 std_time.d = 31;
1442 std_time.y = 9999;
1443 std_time.hh = 23;
1444 std_time.mm = 59;
1445 std_time.ss = 59;
1446 }
1447 if (strnicmp(value, MINFINITY_STRING, 9) == 0)
1448 {
1449 std_time.infinity = -1;
1450 std_time.m = 1;
1451 std_time.d = 1;
1452 // std_time.y = -4713;
1453 std_time.y = -9999;
1454 std_time.hh = 0;
1455 std_time.mm = 0;
1456 std_time.ss = 0;
1457 }
1458 if (strnicmp(value, "invalid", 7) != 0)
1459 {
1460 BOOL bZone = field_type != PG_TYPE_TIMESTAMP_NO_TMZONE;
1461 int zone;
1462
1463 /*
1464 * sscanf(value, "%4d-%2d-%2d %2d:%2d:%2d", &std_time.y, &std_time.m,
1465 * &std_time.d, &std_time.hh, &std_time.mm, &std_time.ss);
1466 */
1467 bZone = FALSE; /* time zone stuff is unreliable */
1468 timestamp2stime(value, &std_time, &bZone, &zone);
1469 MYLOG(DETAIL_LOG_LEVEL, "2stime fr=%d\n", std_time.fr);
1470 }
1471 else
1472 {
1473 /*
1474 * The timestamp is invalid so set something conspicuous,
1475 * like the epoch
1476 */
1477 struct tm *tim;
1478 time_t t = 0;
1479 #ifdef HAVE_LOCALTIME_R
1480 tim = localtime_r(&t, &tm);
1481 #else
1482 tim = localtime(&t);
1483 #endif /* HAVE_LOCALTIME_R */
1484 std_time.m = tim->tm_mon + 1;
1485 std_time.d = tim->tm_mday;
1486 std_time.y = tim->tm_year + 1900;
1487 std_time.hh = tim->tm_hour;
1488 std_time.mm = tim->tm_min;
1489 std_time.ss = tim->tm_sec;
1490 }
1491 break;
1492
1493 case PG_TYPE_BOOL:
1494 { /* change T/F to 1/0 */
1495 const ConnInfo *ci = &(conn->connInfo);
1496
1497 switch (((char *)value)[0])
1498 {
1499 case 'f':
1500 case 'F':
1501 case 'n':
1502 case 'N':
1503 case '0':
1504 STRCPY_FIXED(booltemp, "0");
1505 break;
1506 default:
1507 if (ci->true_is_minus1)
1508 STRCPY_FIXED(booltemp, "-1");
1509 else
1510 STRCPY_FIXED(booltemp, "1");
1511 }
1512 neut_str = booltemp;
1513 }
1514 break;
1515
1516 /* This is for internal use by SQLStatistics() */
1517 case PG_TYPE_INT2VECTOR:
1518 if (SQL_C_DEFAULT == fCType)
1519 {
1520 int i, nval, maxc;
1521 const char *vp;
1522 /* this is an array of eight integers */
1523 short *short_array = (short *) rgbValueBindRow, shortv;
1524
1525 maxc = 0;
1526 if (NULL != short_array)
1527 maxc = (int) cbValueMax / sizeof(short);
1528 vp = value;
1529 nval = 0;
1530 MYLOG(0, "index=(");
1531 for (i = 0;; i++)
1532 {
1533 if (sscanf(vp, "%hi", &shortv) != 1)
1534 break;
1535 MYPRINTF(0, " %hi", shortv);
1536 nval++;
1537 if (nval < maxc)
1538 short_array[i + 1] = shortv;
1539
1540 /* skip the current token */
1541 while (IS_NOT_SPACE(*vp))
1542 vp++;
1543 /* and skip the space to the next token */
1544 while ((*vp != '\0') && (isspace(*vp)))
1545 vp++;
1546 if (*vp == '\0')
1547 break;
1548 }
1549 MYPRINTF(0, ") nval = %i\n", nval);
1550 if (maxc > 0)
1551 short_array[0] = nval;
1552
1553 /* There is no corresponding fCType for this. */
1554 len = (nval + 1) * sizeof(short);
1555 if (pcbValue)
1556 *pcbValueBindRow = len;
1557
1558 if (len <= cbValueMax)
1559 return COPY_OK; /* dont go any further or the data will be
1560 * trashed */
1561 else
1562 return COPY_RESULT_TRUNCATED;
1563 }
1564 break;
1565
1566 /*
1567 * This is a large object OID, which is used to store
1568 * LONGVARBINARY objects.
1569 */
1570 case PG_TYPE_LO_UNDEFINED:
1571
1572 return convert_lo(stmt, value, fCType, rgbValueBindRow, cbValueMax, pcbValueBindRow);
1573
1574 case 0:
1575 break;
1576
1577 default:
1578
1579 if (field_type == stmt->hdbc->lobj_type /* hack until permanent type available */
1580 || (PG_TYPE_OID == field_type && SQL_C_BINARY == fCType && conn->lo_is_domain)
1581 )
1582 return convert_lo(stmt, value, fCType, rgbValueBindRow, cbValueMax, pcbValueBindRow);
1583 }
1584
1585 /* Change default into something useable */
1586 if (fCType == SQL_C_DEFAULT)
1587 {
1588 fCType = pgtype_attr_to_ctype(conn, field_type, atttypmod);
1589 #ifdef UNICODE_SUPPORT
1590 if (fCType == SQL_C_WCHAR
1591 && CC_default_is_c(conn))
1592 fCType = SQL_C_CHAR;
1593 #endif
1594
1595 MYLOG(0, ", SQL_C_DEFAULT: fCType = %d\n", fCType);
1596 }
1597
1598 text_bin_handling = FALSE;
1599 switch (fCType)
1600 {
1601 case INTERNAL_ASIS_TYPE:
1602 #ifdef UNICODE_SUPPORT
1603 case SQL_C_WCHAR:
1604 #endif /* UNICODE_SUPPORT */
1605 case SQL_C_CHAR:
1606 text_bin_handling = TRUE;
1607 break;
1608 case SQL_C_BINARY:
1609 switch (field_type)
1610 {
1611 case PG_TYPE_UNKNOWN:
1612 case PG_TYPE_BPCHAR:
1613 case PG_TYPE_VARCHAR:
1614 case PG_TYPE_TEXT:
1615 case PG_TYPE_XML:
1616 case PG_TYPE_BPCHARARRAY:
1617 case PG_TYPE_VARCHARARRAY:
1618 case PG_TYPE_TEXTARRAY:
1619 case PG_TYPE_XMLARRAY:
1620 case PG_TYPE_BYTEA:
1621 text_bin_handling = TRUE;
1622 break;
1623 }
1624 break;
1625 }
1626
1627 if (text_bin_handling)
1628 {
1629 BOOL pre_convert = TRUE;
1630 int midsize = sizeof(midtemp);
1631 int i;
1632
1633 /* Special character formatting as required */
1634
1635 /*
1636 * These really should return error if cbValueMax is not big
1637 * enough.
1638 */
1639 switch (field_type)
1640 {
1641 case PG_TYPE_DATE:
1642 len = SPRINTF_FIXED(midtemp, "%.4d-%.2d-%.2d", std_time.y, std_time.m, std_time.d);
1643 break;
1644
1645 case PG_TYPE_TIME:
1646 len = SPRINTF_FIXED(midtemp, "%.2d:%.2d:%.2d", std_time.hh, std_time.mm, std_time.ss);
1647 if (std_time.fr > 0)
1648 {
1649 int wdt;
1650 int fr = effective_fraction(std_time.fr, &wdt);
1651
1652 len = SPRINTFCAT_FIXED(midtemp, ".%0*d", wdt, fr);
1653 }
1654 break;
1655
1656 case PG_TYPE_ABSTIME:
1657 case PG_TYPE_DATETIME:
1658 case PG_TYPE_TIMESTAMP_NO_TMZONE:
1659 case PG_TYPE_TIMESTAMP:
1660 len = stime2timestamp(&std_time, midtemp, midsize, FALSE,
1661 (int) (midsize - 19 - 2) );
1662 break;
1663
1664 case PG_TYPE_UUID:
1665 len = strlen(neut_str);
1666 for (i = 0; i < len && i < midsize - 2; i++)
1667 midtemp[i] = toupper((UCHAR) neut_str[i]);
1668 midtemp[i] = '\0';
1669 MYLOG(0, "PG_TYPE_UUID: rgbValueBindRow = '%s'\n", rgbValueBindRow);
1670 break;
1671
1672 /*
1673 * Currently, data is SILENTLY TRUNCATED for BYTEA and
1674 * character data types if there is not enough room in
1675 * cbValueMax because the driver can't handle multiple
1676 * calls to SQLGetData for these, yet. Most likely, the
1677 * buffer passed in will be big enough to handle the
1678 * maximum limit of postgres, anyway.
1679 *
1680 * LongVarBinary types are handled correctly above, observing
1681 * truncation and all that stuff since there is
1682 * essentially no limit on the large object used to store
1683 * those.
1684 */
1685 case PG_TYPE_BYTEA:/* convert binary data to hex strings
1686 * (i.e, 255 = "FF") */
1687
1688 default:
1689 pre_convert = FALSE;
1690 }
1691 if (pre_convert)
1692 neut_str = midtemp;
1693 result = convert_text_field_to_sql_c(gdata, stmt->current_col, neut_str, field_type, fCType, rgbValueBindRow, cbValueMax, conn, &len);
1694 }
1695 else
1696 {
1697 SQLGUID g;
1698
1699 /*
1700 * for SQL_C_CHAR, it's probably ok to leave currency symbols in.
1701 * But to convert to numeric types, it is necessary to get rid of
1702 * those.
1703 */
1704 if (field_type == PG_TYPE_MONEY)
1705 {
1706 if (convert_money(neut_str, midtemp, sizeof(midtemp)))
1707 neut_str = midtemp;
1708 else
1709 {
1710 MYLOG(0, "couldn't convert money type to %d\n", fCType);
1711 return COPY_UNSUPPORTED_TYPE;
1712 }
1713 }
1714
1715 switch (fCType)
1716 {
1717 case SQL_C_DATE:
1718 case SQL_C_TYPE_DATE: /* 91 */
1719 len = 6;
1720 {
1721 DATE_STRUCT *ds;
1722 struct tm *tim;
1723
1724 if (bind_size > 0)
1725 ds = (DATE_STRUCT *) rgbValueBindRow;
1726 else
1727 ds = (DATE_STRUCT *) rgbValue + bind_row;
1728
1729 /*
1730 * Initialize date in case conversion destination
1731 * expects date part from this source time data.
1732 * A value may be partially set here, so do some
1733 * sanity checks on the existing values before
1734 * setting them.
1735 */
1736 tim = SC_get_localtime(stmt);
1737 if (std_time.m == 0)
1738 std_time.m = tim->tm_mon + 1;
1739 if (std_time.d == 0)
1740 std_time.d = tim->tm_mday;
1741 if (std_time.y == 0)
1742 std_time.y = tim->tm_year + 1900;
1743 ds->year = std_time.y;
1744 ds->month = std_time.m;
1745 ds->day = std_time.d;
1746 }
1747 break;
1748
1749 case SQL_C_TIME:
1750 case SQL_C_TYPE_TIME: /* 92 */
1751 len = 6;
1752 {
1753 TIME_STRUCT *ts;
1754
1755 if (bind_size > 0)
1756 ts = (TIME_STRUCT *) rgbValueBindRow;
1757 else
1758 ts = (TIME_STRUCT *) rgbValue + bind_row;
1759 ts->hour = std_time.hh;
1760 ts->minute = std_time.mm;
1761 ts->second = std_time.ss;
1762 }
1763 break;
1764
1765 case SQL_C_TIMESTAMP:
1766 case SQL_C_TYPE_TIMESTAMP: /* 93 */
1767 len = 16;
1768 {
1769 struct tm *tim;
1770 TIMESTAMP_STRUCT *ts;
1771
1772 if (bind_size > 0)
1773 ts = (TIMESTAMP_STRUCT *) rgbValueBindRow;
1774 else
1775 ts = (TIMESTAMP_STRUCT *) rgbValue + bind_row;
1776
1777 /*
1778 * Initialize date in case conversion destination
1779 * expects date part from this source time data.
1780 * A value may be partially set here, so do some
1781 * sanity checks on the existing values before
1782 * setting them.
1783 */
1784 tim = SC_get_localtime(stmt);
1785 if (std_time.m == 0)
1786 std_time.m = tim->tm_mon + 1;
1787 if (std_time.d == 0)
1788 std_time.d = tim->tm_mday;
1789 if (std_time.y == 0)
1790 std_time.y = tim->tm_year + 1900;
1791
1792 ts->year = std_time.y;
1793 ts->month = std_time.m;
1794 ts->day = std_time.d;
1795 ts->hour = std_time.hh;
1796 ts->minute = std_time.mm;
1797 ts->second = std_time.ss;
1798 ts->fraction = std_time.fr;
1799 }
1800 break;
1801
1802 case SQL_C_BIT:
1803 len = 1;
1804 if (bind_size > 0)
1805 *((UCHAR *) rgbValueBindRow) = atoi(neut_str);
1806 else
1807 *((UCHAR *) rgbValue + bind_row) = atoi(neut_str);
1808
1809 MYLOG(99, "SQL_C_BIT: bind_row = " FORMAT_POSIROW " val = %d, cb = " FORMAT_LEN ", rgb=%d\n",
1810 bind_row, atoi(neut_str), cbValueMax, *((UCHAR *)rgbValue));
1811 break;
1812
1813 case SQL_C_STINYINT:
1814 case SQL_C_TINYINT:
1815 len = 1;
1816 if (bind_size > 0)
1817 *((SCHAR *) rgbValueBindRow) = atoi(neut_str);
1818 else
1819 *((SCHAR *) rgbValue + bind_row) = atoi(neut_str);
1820 break;
1821
1822 case SQL_C_UTINYINT:
1823 len = 1;
1824 if (bind_size > 0)
1825 *((UCHAR *) rgbValueBindRow) = atoi(neut_str);
1826 else
1827 *((UCHAR *) rgbValue + bind_row) = atoi(neut_str);
1828 break;
1829
1830 case SQL_C_FLOAT:
1831 set_client_decimal_point((char *) neut_str);
1832 len = 4;
1833 if (bind_size > 0)
1834 *((SFLOAT *) rgbValueBindRow) = (float) get_double_value(neut_str);
1835 else
1836 *((SFLOAT *) rgbValue + bind_row) = (float) get_double_value(neut_str);
1837 break;
1838
1839 case SQL_C_DOUBLE:
1840 set_client_decimal_point((char *) neut_str);
1841 len = 8;
1842 if (bind_size > 0)
1843 *((SDOUBLE *) rgbValueBindRow) = get_double_value(neut_str);
1844 else
1845 *((SDOUBLE *) rgbValue + bind_row) = get_double_value(neut_str);
1846 break;
1847
1848 case SQL_C_NUMERIC:
1849 {
1850 SQL_NUMERIC_STRUCT *ns;
1851 BOOL overflowed;
1852
1853 if (bind_size > 0)
1854 ns = (SQL_NUMERIC_STRUCT *) rgbValueBindRow;
1855 else
1856 ns = (SQL_NUMERIC_STRUCT *) rgbValue + bind_row;
1857
1858 parse_to_numeric_struct(neut_str, ns, &overflowed);
1859 if (overflowed)
1860 result = COPY_RESULT_TRUNCATED;
1861 }
1862 break;
1863
1864 case SQL_C_SSHORT:
1865 case SQL_C_SHORT:
1866 len = 2;
1867 if (bind_size > 0)
1868 *((SQLSMALLINT *) rgbValueBindRow) = atoi(neut_str);
1869 else
1870 *((SQLSMALLINT *) rgbValue + bind_row) = atoi(neut_str);
1871 break;
1872
1873 case SQL_C_USHORT:
1874 len = 2;
1875 if (bind_size > 0)
1876 *((SQLUSMALLINT *) rgbValueBindRow) = atoi(neut_str);
1877 else
1878 *((SQLUSMALLINT *) rgbValue + bind_row) = atoi(neut_str);
1879 break;
1880
1881 case SQL_C_SLONG:
1882 case SQL_C_LONG:
1883 len = 4;
1884 if (bind_size > 0)
1885 *((SQLINTEGER *) rgbValueBindRow) = atol(neut_str);
1886 else
1887 *((SQLINTEGER *) rgbValue + bind_row) = atol(neut_str);
1888 break;
1889
1890 case SQL_C_ULONG:
1891 len = 4;
1892 if (bind_size > 0)
1893 *((SQLUINTEGER *) rgbValueBindRow) = ATOI32U(neut_str);
1894 else
1895 *((SQLUINTEGER *) rgbValue + bind_row) = ATOI32U(neut_str);
1896 break;
1897
1898 #ifdef ODBCINT64
1899 case SQL_C_SBIGINT:
1900 len = 8;
1901 if (bind_size > 0)
1902 *((SQLBIGINT *) rgbValueBindRow) = ATOI64(neut_str);
1903 else
1904 *((SQLBIGINT *) rgbValue + bind_row) = ATOI64(neut_str);
1905 break;
1906
1907 case SQL_C_UBIGINT:
1908 len = 8;
1909 if (bind_size > 0)
1910 *((SQLUBIGINT *) rgbValueBindRow) = ATOI64U(neut_str);
1911 else
1912 *((SQLUBIGINT *) rgbValue + bind_row) = ATOI64U(neut_str);
1913 break;
1914
1915 #endif /* ODBCINT64 */
1916 case SQL_C_BINARY:
1917 /* The following is for SQL_C_VARBOOKMARK */
1918 if (PG_TYPE_INT4 == field_type)
1919 {
1920 UInt4 ival = ATOI32U(neut_str);
1921
1922 MYLOG(DETAIL_LOG_LEVEL, "SQL_C_VARBOOKMARK value=%d\n", ival);
1923 if (pcbValue)
1924 *pcbValueBindRow = sizeof(ival);
1925 if (cbValueMax >= sizeof(ival))
1926 {
1927 memcpy(rgbValueBindRow, &ival, sizeof(ival));
1928 return COPY_OK;
1929 }
1930 else
1931 return COPY_RESULT_TRUNCATED;
1932 }
1933 else if (PG_TYPE_UUID == field_type)
1934 {
1935 int rtn = char2guid(neut_str, &g);
1936
1937 if (COPY_OK != rtn)
1938 return rtn;
1939 if (pcbValue)
1940 *pcbValueBindRow = sizeof(g);
1941 if (cbValueMax >= sizeof(g))
1942 {
1943 memcpy(rgbValueBindRow, &g, sizeof(g));
1944 return COPY_OK;
1945 }
1946 else
1947 return COPY_RESULT_TRUNCATED;
1948 }
1949 else
1950 {
1951 MYLOG(0, "couldn't convert the type %d to SQL_C_BINARY\n", field_type);
1952 return COPY_UNSUPPORTED_TYPE;
1953 }
1954 break;
1955 case SQL_C_GUID:
1956
1957 result = char2guid(neut_str, &g);
1958 if (COPY_OK != result)
1959 {
1960 MYLOG(0, "Could not convert to SQL_C_GUID\n");
1961 return COPY_UNSUPPORTED_TYPE;
1962 }
1963 len = sizeof(g);
1964 if (bind_size > 0)
1965 *((SQLGUID *) rgbValueBindRow) = g;
1966 else
1967 *((SQLGUID *) rgbValue + bind_row) = g;
1968 break;
1969 case SQL_C_INTERVAL_YEAR:
1970 case SQL_C_INTERVAL_MONTH:
1971 case SQL_C_INTERVAL_YEAR_TO_MONTH:
1972 case SQL_C_INTERVAL_DAY:
1973 case SQL_C_INTERVAL_HOUR:
1974 case SQL_C_INTERVAL_DAY_TO_HOUR:
1975 case SQL_C_INTERVAL_MINUTE:
1976 case SQL_C_INTERVAL_HOUR_TO_MINUTE:
1977 case SQL_C_INTERVAL_SECOND:
1978 case SQL_C_INTERVAL_DAY_TO_SECOND:
1979 case SQL_C_INTERVAL_HOUR_TO_SECOND:
1980 case SQL_C_INTERVAL_MINUTE_TO_SECOND:
1981 interval2istruct(fCType, precision, neut_str, bind_size > 0 ? (SQL_INTERVAL_STRUCT *) rgbValueBindRow : (SQL_INTERVAL_STRUCT *) rgbValue + bind_row);
1982 break;
1983
1984 default:
1985 MYLOG(0, "conversion to the type %d isn't supported\n", fCType);
1986 return COPY_UNSUPPORTED_TYPE;
1987 }
1988 }
1989
1990 /* store the length of what was copied, if there's a place for it */
1991 if (pcbValue)
1992 *pcbValueBindRow = len;
1993
1994 if (result == COPY_OK && stmt->current_col >= 0)
1995 gdata->gdata[stmt->current_col].data_left = 0;
1996 return result;
1997
1998 }
1999
2000
2001 /*--------------------------------------------------------------------
2002 * Functions/Macros to get rid of query size limit.
2003 *
2004 * I always used the follwoing macros to convert from
2005 * old_statement to new_statement. Please improve it
2006 * if you have a better way. Hiroshi 2001/05/22
2007 *--------------------------------------------------------------------
2008 */
2009
2010 #define FLGP_USING_CURSOR (1L << 1)
2011 #define FLGP_SELECT_INTO (1L << 2)
2012 #define FLGP_SELECT_FOR_UPDATE_OR_SHARE (1L << 3)
2013 #define FLGP_MULTIPLE_STATEMENT (1L << 5)
2014 #define FLGP_SELECT_FOR_READONLY (1L << 6)
2015 typedef struct _QueryParse {
2016 const char *statement;
2017 int statement_type;
2018 size_t opos;
2019 ssize_t from_pos;
2020 ssize_t where_pos;
2021 ssize_t stmt_len;
2022 int in_status;
2023 char escape_in_literal, prev_token_end;
2024 const char *dollar_tag;
2025 ssize_t taglen;
2026 char token_curr[64];
2027 int token_len;
2028 size_t declare_pos;
2029 UInt4 flags, comment_level;
2030 encoded_str encstr;
2031 } QueryParse;
2032
2033 static void
QP_initialize(QueryParse * q,const StatementClass * stmt)2034 QP_initialize(QueryParse *q, const StatementClass *stmt)
2035 {
2036 q->statement = stmt->statement;
2037 q->statement_type = stmt->statement_type;
2038 q->opos = 0;
2039 q->from_pos = -1;
2040 q->where_pos = -1;
2041 q->stmt_len = (q->statement) ? strlen(q->statement) : -1;
2042 q->in_status = 0;
2043 q->escape_in_literal = '\0';
2044 q->dollar_tag = NULL;
2045 q->taglen = -1;
2046 q->token_curr[0] = '\0';
2047 q->token_len = 0;
2048 q->prev_token_end = TRUE;
2049 q->declare_pos = 0;
2050 q->flags = 0;
2051 q->comment_level = 0;
2052 make_encoded_str(&q->encstr, SC_get_conn(stmt), q->statement);
2053 }
2054
2055 enum {
2056 QP_IN_IDENT_KEYWORD = 1L /* identifier or keyword */
2057 , QP_IN_DQUOTE_IDENTIFIER = (1L << 1) /* "" */
2058 , QP_IN_LITERAL = (1L << 2) /* '' */
2059 , QP_IN_ESCAPE = (1L << 3) /* \ in literal */
2060 , QP_IN_DOLLAR_QUOTE = (1L << 4) /* $...$ $...$ */
2061 , QP_IN_COMMENT_BLOCK = (1L << 5) /* slash asterisk */
2062 , QP_IN_LINE_COMMENT = (1L << 6) /* -- */
2063 };
2064
2065 #define QP_in_idle_status(qp) ((qp)->in_status == 0)
2066
2067 #define QP_is_in(qp, status) (((qp)->in_status & status) != 0)
2068 #define QP_enter(qp, status) ((qp)->in_status |= status)
2069 #define QP_exit(qp, status) ((qp)->in_status &= (~status))
2070
2071 /*
2072 * ResolveOneParam can be work in these four modes:
2073 *
2074 * RPM_REPLACE_PARAMS
2075 * Replace parameter markers with their values.
2076 *
2077 * RPM_FAKE_PARAMS
2078 * The query is going to be sent to the server only to be able to
2079 * Describe the result set. Parameter markers are replaced with NULL
2080 * literals if we don't know their real values yet.
2081 *
2082 * RPM_BUILDING_PREPARE_STATEMENT
2083 * Building a query suitable for PREPARE statement, or server-side Parse.
2084 * Parameter markers are replaced with $n-style parameter markers.
2085 *
2086 * RPM_BUILDING_BIND_REQUEST
2087 * Building the actual parameter values to send to the server in a Bind
2088 * request. Return an unescaped value that will be accepted by the
2089 * server's input function.
2090 */
2091 typedef enum
2092 {
2093 RPM_REPLACE_PARAMS,
2094 RPM_FAKE_PARAMS,
2095 RPM_BUILDING_PREPARE_STATEMENT,
2096 RPM_BUILDING_BIND_REQUEST
2097 } ResolveParamMode;
2098
2099 #define FLGB_INACCURATE_RESULT (1L << 4)
2100 #define FLGB_CREATE_KEYSET (1L << 5)
2101 #define FLGB_KEYSET_DRIVEN (1L << 6)
2102 #define FLGB_CONVERT_LF (1L << 7)
2103 #define FLGB_DISCARD_OUTPUT (1L << 8)
2104 #define FLGB_BINARY_AS_POSSIBLE (1L << 9)
2105 #define FLGB_LITERAL_EXTENSION (1L << 10)
2106 #define FLGB_HEX_BIN_FORMAT (1L << 11)
2107 #define FLGB_PARAM_CAST (1L << 12)
2108 typedef struct _QueryBuild {
2109 char *query_statement;
2110 size_t str_alsize;
2111 size_t npos;
2112 SQLLEN current_row;
2113 Int2 param_number;
2114 Int2 dollar_number;
2115 Int2 num_io_params;
2116 Int2 num_output_params;
2117 Int2 num_discard_params;
2118 Int2 proc_return;
2119 Int2 brace_level;
2120 char parenthesize_the_first;
2121 APDFields *apdopts;
2122 IPDFields *ipdopts;
2123 PutDataInfo *pdata;
2124 size_t load_stmt_len;
2125 size_t load_from_pos;
2126 ResolveParamMode param_mode;
2127 UInt4 flags;
2128 int ccsc;
2129 int errornumber;
2130 const char *errormsg;
2131
2132 ConnectionClass *conn; /* mainly needed for LO handling */
2133 StatementClass *stmt; /* needed to set error info in ENLARGE_.. */
2134 } QueryBuild;
2135
2136 #define INIT_MIN_ALLOC 4096
2137 static ssize_t
QB_initialize(QueryBuild * qb,size_t size,StatementClass * stmt,ResolveParamMode param_mode)2138 QB_initialize(QueryBuild *qb, size_t size, StatementClass *stmt, ResolveParamMode param_mode)
2139 {
2140 size_t newsize = 0;
2141
2142 qb->param_mode = param_mode;
2143 qb->flags = 0;
2144 qb->load_stmt_len = 0;
2145 qb->load_from_pos = 0;
2146 qb->stmt = stmt;
2147 qb->apdopts = NULL;
2148 qb->ipdopts = NULL;
2149 qb->pdata = NULL;
2150 qb->proc_return = 0;
2151 qb->num_io_params = 0;
2152 qb->num_output_params = 0;
2153 qb->num_discard_params = 0;
2154 qb->brace_level = 0;
2155 qb->parenthesize_the_first = FALSE;
2156
2157 /* Copy options from statement */
2158 qb->apdopts = SC_get_APDF(stmt);
2159 qb->ipdopts = SC_get_IPDF(stmt);
2160 qb->pdata = SC_get_PDTI(stmt);
2161 qb->conn = SC_get_conn(stmt);
2162 if (stmt->discard_output_params)
2163 qb->flags |= FLGB_DISCARD_OUTPUT;
2164 qb->num_io_params = CountParameters(stmt, NULL, NULL, &qb->num_output_params);
2165 qb->proc_return = stmt->proc_return;
2166 if (0 != (qb->flags & FLGB_DISCARD_OUTPUT))
2167 qb->num_discard_params = qb->num_output_params;
2168 if (qb->num_discard_params < qb->proc_return)
2169 qb->num_discard_params = qb->proc_return;
2170
2171 /* Copy options from connection */
2172 if (qb->conn->connInfo.lf_conversion)
2173 qb->flags |= FLGB_CONVERT_LF;
2174 qb->ccsc = qb->conn->ccsc;
2175 if (CC_get_escape(qb->conn) &&
2176 PG_VERSION_GE(qb->conn, 8.1))
2177 qb->flags |= FLGB_LITERAL_EXTENSION;
2178 if (PG_VERSION_GE(qb->conn, 9.0))
2179 qb->flags |= FLGB_HEX_BIN_FORMAT;
2180
2181 newsize = INIT_MIN_ALLOC;
2182 while (newsize <= size)
2183 newsize *= 2;
2184
2185 if ((qb->query_statement = malloc(newsize)) == NULL)
2186 {
2187 qb->str_alsize = 0;
2188 return -1;
2189 }
2190 qb->query_statement[0] = '\0';
2191 qb->str_alsize = newsize;
2192 qb->npos = 0;
2193 qb->current_row = stmt->exec_current_row < 0 ? 0 : stmt->exec_current_row;
2194 qb->param_number = -1;
2195 qb->dollar_number = 0;
2196 qb->errornumber = 0;
2197 qb->errormsg = NULL;
2198
2199 return newsize;
2200 }
2201
2202 static int
QB_initialize_copy(QueryBuild * qb_to,const QueryBuild * qb_from,UInt4 size)2203 QB_initialize_copy(QueryBuild *qb_to, const QueryBuild *qb_from, UInt4 size)
2204 {
2205 memcpy(qb_to, qb_from, sizeof(QueryBuild));
2206
2207 if ((qb_to->query_statement = malloc(size)) == NULL)
2208 {
2209 qb_to->str_alsize = 0;
2210 return -1;
2211 }
2212 qb_to->query_statement[0] = '\0';
2213 qb_to->str_alsize = size;
2214 qb_to->npos = 0;
2215
2216 return size;
2217 }
2218
2219 static void
QB_replace_SC_error(StatementClass * stmt,const QueryBuild * qb,const char * func)2220 QB_replace_SC_error(StatementClass *stmt, const QueryBuild *qb, const char *func)
2221 {
2222 int number;
2223
2224 if (0 == qb->errornumber) return;
2225 if ((number = SC_get_errornumber(stmt)) > 0) return;
2226 if (number < 0 && qb->errornumber < 0) return;
2227 SC_set_error(stmt, qb->errornumber, qb->errormsg, func);
2228 }
2229
2230 static void
QB_Destructor(QueryBuild * qb)2231 QB_Destructor(QueryBuild *qb)
2232 {
2233 if (qb->query_statement)
2234 {
2235 free(qb->query_statement);
2236 qb->query_statement = NULL;
2237 qb->str_alsize = 0;
2238 }
2239 }
2240
2241 /*
2242 * New macros (Aceto)
2243 *--------------------
2244 */
2245
2246 #define F_OldChar(qp) ((qp)->statement[(qp)->opos])
2247
2248 #define F_OldPtr(qp) ((qp)->statement + (qp)->opos)
2249
2250 #define F_OldNext(qp) (++(qp)->opos)
2251
2252 #define F_OldPrior(qp) (--(qp)->opos)
2253
2254 #define F_OldPos(qp) (qp)->opos
2255
2256 #define F_ExtractOldTo(qp, buf, ch, maxsize) \
2257 do { \
2258 size_t c = 0; \
2259 while ((qp)->statement[qp->opos] != '\0' && (qp)->statement[qp->opos] != ch) \
2260 { \
2261 if (c >= maxsize) \
2262 break; \
2263 buf[c++] = (qp)->statement[qp->opos++]; \
2264 } \
2265 if ((qp)->statement[qp->opos] == '\0') \
2266 {retval = SQL_ERROR; goto cleanup;} \
2267 buf[c] = '\0'; \
2268 } while (0)
2269
2270 #define F_NewChar(qb) (qb->query_statement[(qb)->npos])
2271
2272 #define F_NewPtr(qb) ((qb)->query_statement + (qb)->npos)
2273
2274 #define F_NewNext(qb) (++(qb)->npos)
2275
2276 #define F_NewPos(qb) ((qb)->npos)
2277
2278
2279 static int
2280 convert_escape(QueryParse *qp, QueryBuild *qb);
2281 static int
2282 inner_process_tokens(QueryParse *qp, QueryBuild *qb);
2283 static int
2284 ResolveOneParam(QueryBuild *qb, QueryParse *qp, BOOL *isnull, BOOL *usebinary,
2285 Oid *pgType);
2286 static int
2287 processParameters(QueryParse *qp, QueryBuild *qb,
2288 size_t *output_count, SQLLEN param_pos[][2]);
2289 static size_t
2290 convert_to_pgbinary(const char *in, char *out, size_t len, QueryBuild *qb);
2291 static BOOL convert_special_chars(QueryBuild *qb, const char *si, size_t used);
2292
2293 /*
2294 * Enlarge qb->query_statement buffer so that it is at least newsize + 1
2295 * in size.
2296 */
2297 static ssize_t
enlarge_query_statement(QueryBuild * qb,size_t newsize)2298 enlarge_query_statement(QueryBuild *qb, size_t newsize)
2299 {
2300 size_t newalsize = INIT_MIN_ALLOC;
2301 CSTR func = "enlarge_statement";
2302
2303 while (newalsize <= newsize)
2304 newalsize *= 2;
2305 if (!(qb->query_statement = realloc(qb->query_statement, newalsize)))
2306 {
2307 qb->str_alsize = 0;
2308 if (qb->stmt)
2309 {
2310 SC_set_error(qb->stmt, STMT_EXEC_ERROR, "Query buffer allocate error in copy_statement_with_parameters", func);
2311 }
2312 else
2313 {
2314 qb->errormsg = "Query buffer allocate error in copy_statement_with_parameters";
2315 qb->errornumber = STMT_EXEC_ERROR;
2316 }
2317 return 0;
2318 }
2319 qb->str_alsize = newalsize;
2320 return newalsize;
2321 }
2322
2323 /*----------
2324 * Enlarge stmt_with_params if necessary.
2325 *----------
2326 */
2327 #define ENLARGE_NEWSTATEMENT(qb, newpos) \
2328 do { \
2329 if ((newpos) >= (qb)->str_alsize) \
2330 { \
2331 if (enlarge_query_statement(qb, newpos) <= 0) \
2332 { \
2333 retval = SQL_ERROR; \
2334 goto cleanup; \
2335 } \
2336 } \
2337 } while(0)
2338
2339 /*----------
2340 * Terminate the stmt_with_params string with NULL.
2341 *----------
2342 */
2343 #define CVT_TERMINATE(qb) \
2344 do { \
2345 if (NULL == (qb)->query_statement) \
2346 { \
2347 retval = SQL_ERROR; \
2348 goto cleanup; \
2349 } \
2350 (qb)->query_statement[(qb)->npos] = '\0'; \
2351 } while (0)
2352
2353 /*----------
2354 * Append a data.
2355 *----------
2356 */
2357 #define CVT_APPEND_DATA(qb, s, len) \
2358 do { \
2359 size_t newpos = (qb)->npos + len; \
2360 ENLARGE_NEWSTATEMENT((qb), newpos); \
2361 memcpy(&(qb)->query_statement[(qb)->npos], s, len); \
2362 (qb)->npos = newpos; \
2363 (qb)->query_statement[newpos] = '\0'; \
2364 } while (0)
2365
2366 /*----------
2367 * Append a string.
2368 *----------
2369 */
2370 #define CVT_APPEND_STR(qb, s) \
2371 do { \
2372 size_t len = strlen(s); \
2373 CVT_APPEND_DATA(qb, s, len); \
2374 } while (0)
2375
2376 /*----------
2377 * Append a char.
2378 *----------
2379 */
2380 #define CVT_APPEND_CHAR(qb, c) \
2381 do { \
2382 ENLARGE_NEWSTATEMENT(qb, (qb)->npos + 1); \
2383 (qb)->query_statement[(qb)->npos++] = c; \
2384 } while (0)
2385
2386 /*----------
2387 * Append a binary data.
2388 * Newly required size may be overestimated currently.
2389 *----------
2390 */
2391 #define CVT_APPEND_BINARY(qb, buf, used) \
2392 do { \
2393 size_t newlimit; \
2394 \
2395 newlimit = (qb)->npos; \
2396 if (qb->flags & FLGB_HEX_BIN_FORMAT) \
2397 newlimit += 2 * used + 4; \
2398 else \
2399 newlimit += 5 * used; \
2400 ENLARGE_NEWSTATEMENT(qb, newlimit); \
2401 (qb)->npos += convert_to_pgbinary(buf, \
2402 &qb->query_statement[(qb)->npos], \
2403 used, qb); \
2404 } while (0)
2405
2406 /*----------
2407 *
2408 *----------
2409 */
2410 #define CVT_SPECIAL_CHARS(qb, buf, used) \
2411 do { \
2412 if (!convert_special_chars(qb, buf, used)) \
2413 { \
2414 retval = SQL_ERROR; \
2415 goto cleanup; \
2416 } \
2417 } while (0)
2418
2419
2420 static RETCODE
QB_start_brace(QueryBuild * qb)2421 QB_start_brace(QueryBuild *qb)
2422 {
2423 BOOL replace_by_parenthesis = TRUE;
2424 RETCODE retval = SQL_ERROR;
2425
2426 if (0 == qb->brace_level)
2427 {
2428 if (0 == F_NewPos(qb))
2429 {
2430 qb->parenthesize_the_first = FALSE;
2431 replace_by_parenthesis = FALSE;
2432 }
2433 else
2434 qb->parenthesize_the_first = TRUE;
2435 }
2436 if (replace_by_parenthesis)
2437 CVT_APPEND_CHAR(qb, '(');
2438 qb->brace_level++;
2439 retval = SQL_SUCCESS;
2440 cleanup:
2441 return retval;
2442 }
2443
2444 static RETCODE
QB_end_brace(QueryBuild * qb)2445 QB_end_brace(QueryBuild *qb)
2446 {
2447 BOOL replace_by_parenthesis = TRUE;
2448 RETCODE retval = SQL_ERROR;
2449
2450 if (qb->brace_level <= 1 &&
2451 !qb->parenthesize_the_first)
2452 replace_by_parenthesis = FALSE;
2453 if (replace_by_parenthesis)
2454 CVT_APPEND_CHAR(qb, ')');
2455 qb->brace_level--;
2456 retval = SQL_SUCCESS;
2457 cleanup:
2458 return retval;
2459 }
2460
QB_append_space_to_separate_identifiers(QueryBuild * qb,const QueryParse * qp)2461 static RETCODE QB_append_space_to_separate_identifiers(QueryBuild *qb, const QueryParse *qp)
2462 {
2463 unsigned char tchar = F_OldChar(qp);
2464 encoded_str encstr;
2465 BOOL add_space = FALSE;
2466 RETCODE retval = SQL_ERROR;
2467
2468 if (ODBC_ESCAPE_END != tchar)
2469 return SQL_SUCCESS;
2470 encoded_str_constr(&encstr, qb->ccsc, F_OldPtr(qp) + 1);
2471 tchar = encoded_nextchar(&encstr);
2472 if (MBCS_NON_ASCII(encstr))
2473 add_space = TRUE;
2474 else
2475 {
2476 if (isalnum(tchar))
2477 add_space = TRUE;
2478 else
2479 {
2480 switch (tchar)
2481 {
2482 case '_':
2483 case '$':
2484 add_space = TRUE;
2485 }
2486 }
2487 }
2488 if (add_space)
2489 CVT_APPEND_CHAR(qb, ' ');
2490 retval = SQL_SUCCESS;
2491 cleanup:
2492
2493 return retval;
2494 }
2495
2496 /*----------
2497 * Check if the statement is
2498 * SELECT ... INTO table FROM .....
2499 * This isn't really a strict check but ...
2500 *----------
2501 */
2502 static BOOL
into_table_from(const char * stmt)2503 into_table_from(const char *stmt)
2504 {
2505 if (strnicmp(stmt, "into", 4))
2506 return FALSE;
2507 stmt += 4;
2508 while (isspace((UCHAR) *stmt)) stmt++;
2509 switch (*stmt)
2510 {
2511 case '\0':
2512 case ',':
2513 case LITERAL_QUOTE:
2514 case DOLLAR_QUOTE:
2515 return FALSE;
2516 case '-':
2517 case '/':
2518 return TRUE;
2519 case IDENTIFIER_QUOTE: /* double quoted table name ? */
2520 do
2521 {
2522 do {
2523 ++stmt;
2524 } while (*stmt != IDENTIFIER_QUOTE && *stmt);
2525 if (*stmt)
2526 stmt++;
2527 }
2528 while (*stmt == IDENTIFIER_QUOTE);
2529 break;
2530 default:
2531 while (IS_NOT_SPACE(*stmt)) stmt++;
2532 break;
2533 }
2534 if (!*stmt)
2535 return FALSE;
2536 while (isspace((UCHAR) *stmt)) stmt++;
2537 if ('/' == *stmt ||
2538 '-' == *stmt)
2539 return TRUE;
2540 if (strnicmp(stmt, "from", 4))
2541 return FALSE;
2542 return TRUE;
2543 }
2544
2545 /*----------
2546 * Check if the statement is
2547 * SELECT ... FOR UPDATE .....
2548 * This isn't really a strict check but ...
2549 *----------
2550 */
2551 static UInt4
table_for_update_or_share(const char * stmt,size_t * endpos)2552 table_for_update_or_share(const char *stmt, size_t *endpos)
2553 {
2554 const char *wstmt = stmt;
2555 int advance;
2556 UInt4 flag = 0, zeroflag = 0;
2557
2558 while (isspace((UCHAR) *wstmt)) wstmt++;
2559 if (!*wstmt)
2560 return 0;
2561 if (0 == strnicmp(wstmt, "update", advance = 6))
2562 flag |= FLGP_SELECT_FOR_UPDATE_OR_SHARE;
2563 else if (0 == strnicmp(wstmt, "share", advance = 5))
2564 flag |= FLGP_SELECT_FOR_UPDATE_OR_SHARE;
2565 else if (0 == strnicmp(wstmt, "read", advance = 4))
2566 flag |= FLGP_SELECT_FOR_READONLY;
2567 else
2568 {
2569 flag |= FLGP_SELECT_FOR_UPDATE_OR_SHARE; /* maybe */
2570 return flag;
2571 }
2572 zeroflag = flag; /* returns flag anyway */
2573 wstmt += advance;
2574 if (IS_NOT_SPACE(wstmt[0]))
2575 return zeroflag;
2576 else if (0 != (flag & FLGP_SELECT_FOR_READONLY))
2577 {
2578 if (IS_NOT_SPACE(wstmt[0]))
2579 return zeroflag;
2580 while (isspace((UCHAR) *wstmt)) wstmt++;
2581 if (!*wstmt)
2582 return zeroflag;
2583 if (0 != strnicmp(wstmt, "only", advance = 4))
2584 return zeroflag;
2585 wstmt += advance;
2586 }
2587 if (IS_NOT_SPACE(wstmt[0]))
2588 return zeroflag;
2589 *endpos = wstmt - stmt;
2590 return flag;
2591 }
2592
2593 /*----------
2594 * Check if the statement has OUTER JOIN
2595 * This isn't really a strict check but ...
2596 *----------
2597 */
2598 static BOOL
check_join(StatementClass * stmt,const char * curptr,size_t curpos)2599 check_join(StatementClass *stmt, const char *curptr, size_t curpos)
2600 {
2601 const char *wstmt;
2602 ssize_t stapos, endpos, tokenwd;
2603 const int backstep = 4;
2604 BOOL outerj = TRUE;
2605
2606 for (endpos = curpos, wstmt = curptr; endpos >= 0 && isspace((UCHAR) *wstmt); endpos--, wstmt--)
2607 ;
2608 if (endpos < 0)
2609 return FALSE;
2610 for (endpos -= backstep, wstmt -= backstep; endpos >= 0 && isspace((UCHAR) *wstmt); endpos--, wstmt--)
2611 ;
2612 if (endpos < 0)
2613 return FALSE;
2614 for (stapos = endpos; stapos >= 0 && IS_NOT_SPACE(*wstmt); stapos--, wstmt--)
2615 ;
2616 if (stapos < 0 || 0 == *wstmt)
2617 return FALSE;
2618 wstmt++;
2619 switch (tokenwd = endpos - stapos)
2620 {
2621 case 4:
2622 if (strnicmp(wstmt, "FULL", tokenwd) == 0 ||
2623 strnicmp(wstmt, "LEFT", tokenwd) == 0)
2624 break;
2625 return FALSE;
2626 case 5:
2627 if (strnicmp(wstmt, "OUTER", tokenwd) == 0 ||
2628 strnicmp(wstmt, "RIGHT", tokenwd) == 0)
2629 break;
2630 if (strnicmp(wstmt, "INNER", tokenwd) == 0 ||
2631 strnicmp(wstmt, "CROSS", tokenwd) == 0)
2632 {
2633 outerj = FALSE;
2634 break;
2635 }
2636 return FALSE;
2637 default:
2638 return FALSE;
2639 }
2640 if (stmt)
2641 {
2642 if (outerj)
2643 SC_set_outer_join(stmt);
2644 else
2645 SC_set_inner_join(stmt);
2646 }
2647 return TRUE;
2648 }
2649
2650 /*----------
2651 * Check if the statement is
2652 * INSERT INTO ... () VALUES ()
2653 * This isn't really a strict check but ...
2654 *----------
2655 */
2656 static BOOL
insert_without_target(const char * stmt,size_t * endpos)2657 insert_without_target(const char *stmt, size_t *endpos)
2658 {
2659 const char *wstmt = stmt;
2660
2661 while (isspace((UCHAR) *wstmt)) wstmt++;
2662 if (!*wstmt)
2663 return FALSE;
2664 if (strnicmp(wstmt, "VALUES", 6))
2665 return FALSE;
2666 wstmt += 6;
2667 if (!wstmt[0] || IS_NOT_SPACE(wstmt[0]))
2668 return FALSE;
2669 while (isspace((UCHAR) *wstmt)) wstmt++;
2670 if (*wstmt != '(' || *(++wstmt) != ')')
2671 return FALSE;
2672 wstmt++;
2673 *endpos = wstmt - stmt;
2674 return !wstmt[0] || isspace((UCHAR) wstmt[0])
2675 || ';' == wstmt[0];
2676 }
2677
2678 static ProcessedStmt *
buildProcessedStmt(const char * srvquery,ssize_t endp,int num_params)2679 buildProcessedStmt(const char *srvquery, ssize_t endp, int num_params)
2680 {
2681 ProcessedStmt *pstmt;
2682 size_t qlen;
2683
2684 qlen = (endp == SQL_NTS) ? strlen(srvquery) : endp;
2685
2686 pstmt = malloc(sizeof(ProcessedStmt));
2687 if (!pstmt)
2688 return NULL;
2689
2690 pstmt->next = NULL;
2691 pstmt->query = malloc(qlen + 1);
2692 if (!pstmt->query)
2693 {
2694 free(pstmt);
2695 return NULL;
2696 }
2697 memcpy(pstmt->query, srvquery, qlen);
2698 pstmt->query[qlen] = '\0';
2699 pstmt->num_params = num_params;
2700
2701 return pstmt;
2702 }
2703
2704 /*
2705 * Process the original SQL query for execution using server-side prepared
2706 * statements.
2707 *
2708 * Split a possible multi-statement query into parts, and replace ?-style
2709 * parameter markers with $n. The resulting queries are stored in a linked
2710 * list in stmt->processed_statements.
2711 *
2712 * If 'fake_params' is true, we will replace ?-style parameter markers with
2713 * fake parameter values instead. This is used when a query's result columns
2714 * have to be described (SQLPrepare+SQLDescribeCol) before executing the
2715 * query, in UseServerSidePrepare=0 mode.
2716 */
2717 RETCODE
prepareParametersNoDesc(StatementClass * stmt,BOOL fake_params,BOOL param_cast)2718 prepareParametersNoDesc(StatementClass *stmt, BOOL fake_params, BOOL param_cast)
2719 {
2720 CSTR func = "process_statements";
2721 RETCODE retval;
2722 ConnectionClass *conn = SC_get_conn(stmt);
2723 char plan_name[32];
2724 po_ind_t multi;
2725 const char *orgquery = NULL, *srvquery = NULL;
2726 ssize_t endp1, endp2;
2727 SQLSMALLINT num_pa = 0, num_p1, num_p2;
2728 ProcessedStmt *pstmt;
2729 ProcessedStmt *last_pstmt;
2730 QueryParse query_org, *qp;
2731 QueryBuild query_crt, *qb;
2732
2733 MYLOG(DETAIL_LOG_LEVEL, "entering\n");
2734 qp = &query_org;
2735 QP_initialize(qp, stmt);
2736 qb = &query_crt;
2737 if (QB_initialize(qb, qp->stmt_len, stmt,
2738 fake_params ? RPM_FAKE_PARAMS : RPM_BUILDING_PREPARE_STATEMENT) < 0)
2739 {
2740 SC_set_errornumber(stmt, STMT_NO_MEMORY_ERROR);
2741 return SQL_ERROR;
2742 }
2743 if (param_cast)
2744 qb->flags |= FLGB_PARAM_CAST;
2745
2746 for (qp->opos = 0; qp->opos < qp->stmt_len; qp->opos++)
2747 {
2748 retval = inner_process_tokens(qp, qb);
2749 if (SQL_ERROR == retval)
2750 {
2751 QB_replace_SC_error(stmt, qb, func);
2752 QB_Destructor(qb);
2753 return retval;
2754 }
2755 }
2756 CVT_TERMINATE(qb);
2757
2758 retval = SQL_ERROR;
2759 #define return DONT_CALL_RETURN_FROM_HERE???
2760 if (NAMED_PARSE_REQUEST == SC_get_prepare_method(stmt))
2761 SPRINTF_FIXED(plan_name, "_PLAN%p", stmt);
2762 else
2763 plan_name[0] = '\0';
2764
2765 stmt->current_exec_param = 0;
2766 multi = stmt->multi_statement;
2767 orgquery = stmt->statement;
2768 srvquery = qb->query_statement;
2769
2770 SC_scanQueryAndCountParams(orgquery, conn, &endp1, &num_p1, &multi, NULL);
2771 SC_scanQueryAndCountParams(srvquery, conn, &endp2, NULL, NULL, NULL);
2772 MYLOG(0, "parsed for the first command length=" FORMAT_SSIZE_T "(" FORMAT_SSIZE_T ") num_p=%d\n", endp2, endp1, num_p1);
2773 pstmt = buildProcessedStmt(srvquery,
2774 endp2 < 0 ? SQL_NTS : endp2,
2775 fake_params ? 0 : num_p1);
2776 if (!pstmt)
2777 {
2778 SC_set_errornumber(stmt, STMT_NO_MEMORY_ERROR);
2779 goto cleanup;
2780 }
2781 stmt->processed_statements = last_pstmt = pstmt;
2782 while (multi > 0)
2783 {
2784 orgquery += (endp1 + 1);
2785 srvquery += (endp2 + 1);
2786 num_pa += num_p1;
2787 SC_scanQueryAndCountParams(orgquery, conn, &endp1, &num_p1, &multi, NULL);
2788 SC_scanQueryAndCountParams(srvquery, conn, &endp2, &num_p2, NULL, NULL);
2789 MYLOG(0, "parsed for the subsequent command length=" FORMAT_SSIZE_T "(" FORMAT_SSIZE_T ") num_p=%d\n", endp2, endp1, num_p1);
2790 pstmt = buildProcessedStmt(srvquery,
2791 endp2 < 0 ? SQL_NTS : endp2,
2792 fake_params ? 0 : num_p1);
2793 if (!pstmt)
2794 {
2795 SC_set_errornumber(stmt, STMT_NO_MEMORY_ERROR);
2796 goto cleanup;
2797 }
2798 last_pstmt->next = pstmt;
2799 last_pstmt = pstmt;
2800 }
2801
2802 SC_set_planname(stmt, plan_name);
2803 SC_set_prepared(stmt, plan_name[0] ? PREPARING_PERMANENTLY : PREPARING_TEMPORARILY);
2804
2805 retval = SQL_SUCCESS;
2806 cleanup:
2807 #undef return
2808 stmt->current_exec_param = -1;
2809 QB_Destructor(qb);
2810 return retval;
2811 }
2812
2813 /*
2814 * Describe the parameters and portal for given query.
2815 */
2816 static RETCODE
desc_params_and_sync(StatementClass * stmt)2817 desc_params_and_sync(StatementClass *stmt)
2818 {
2819 CSTR func = "desc_params_and_sync";
2820 RETCODE retval;
2821 ConnectionClass *conn = SC_get_conn(stmt);
2822 QResultClass *res;
2823 char *plan_name;
2824 int func_cs_count = 0;
2825 SQLSMALLINT num_pa = 0;
2826 ProcessedStmt *pstmt;
2827
2828 MYLOG(DETAIL_LOG_LEVEL, "entering\n");
2829
2830 retval = SQL_ERROR;
2831 #define return DONT_CALL_RETURN_FROM_HERE???
2832 ENTER_INNER_CONN_CS(conn, func_cs_count);
2833
2834 plan_name = stmt->plan_name ? stmt->plan_name : "";
2835
2836 pstmt = stmt->processed_statements;
2837
2838 stmt->current_exec_param = 0;
2839 res = ParseAndDescribeWithLibpq(stmt, plan_name, pstmt->query, pstmt->num_params, "prepare_and_describe", NULL);
2840 if (res == NULL)
2841 goto cleanup;
2842 // SC_set_Result(stmt, res);
2843 QR_Destructor(stmt->parsed);
2844 stmt->parsed = res;
2845 if (!QR_command_maybe_successful(res))
2846 {
2847 SC_set_error(stmt, STMT_EXEC_ERROR, "Error while preparing parameters", func);
2848 goto cleanup;
2849 }
2850 num_pa = pstmt->num_params;
2851 for (pstmt = pstmt->next; pstmt; pstmt = pstmt->next)
2852 {
2853 if (pstmt->num_params > 0)
2854 {
2855 stmt->current_exec_param = num_pa;
2856
2857 res = ParseAndDescribeWithLibpq(stmt, plan_name, pstmt->query, pstmt->num_params, "prepare_and_describe", NULL);
2858 if (res == NULL)
2859 goto cleanup;
2860 QR_Destructor(res);
2861 num_pa += pstmt->num_params;
2862 }
2863 }
2864 retval = SQL_SUCCESS;
2865 cleanup:
2866 #undef return
2867 CLEANUP_FUNC_CONN_CS(func_cs_count, conn);
2868 stmt->current_exec_param = -1;
2869 return retval;
2870 }
2871
2872 /*
2873 * Process the original SQL query, and and ask the server describe the
2874 * parameters.
2875 */
prepareParameters(StatementClass * stmt,BOOL fake_params)2876 RETCODE prepareParameters(StatementClass *stmt, BOOL fake_params)
2877 {
2878 ConnectionClass *conn = SC_get_conn(stmt);
2879
2880 switch (stmt->prepared)
2881 {
2882 case PREPARED_TEMPORARILY:
2883 if (conn->unnamed_prepared_stmt == stmt)
2884 return SQL_SUCCESS;
2885 else
2886 break;
2887 case NOT_YET_PREPARED:
2888 case PREPARING_PERMANENTLY:
2889 case PREPARING_TEMPORARILY:
2890 break;
2891 default:
2892 return SQL_SUCCESS;
2893 }
2894
2895 MYLOG(DETAIL_LOG_LEVEL, "calling prepareParameters\n");
2896
2897 if (prepareParametersNoDesc(stmt, fake_params, PARSE_PARAM_CAST) == SQL_ERROR)
2898 return SQL_ERROR;
2899 return desc_params_and_sync(stmt);
2900 }
2901
2902 /*
2903 * This function inserts parameters into an SQL statements.
2904 * It will also modify a SELECT statement for use with declare/fetch cursors.
2905 * This function does a dynamic memory allocation to get rid of query size limit!
2906 */
2907 int
copy_statement_with_parameters(StatementClass * stmt,BOOL buildPrepareStatement)2908 copy_statement_with_parameters(StatementClass *stmt, BOOL buildPrepareStatement)
2909 {
2910 CSTR func = "copy_statement_with_parameters";
2911 RETCODE retval;
2912 QueryParse query_org, *qp;
2913 QueryBuild query_crt, *qb;
2914
2915 char *new_statement;
2916
2917 ConnectionClass *conn = SC_get_conn(stmt);
2918 ConnInfo *ci = &(conn->connInfo);
2919 const char *bestitem = NULL;
2920
2921 MYLOG(DETAIL_LOG_LEVEL, "entering prepared=%d\n", stmt->prepared);
2922 if (!stmt->statement)
2923 {
2924 SC_set_error(stmt, STMT_INTERNAL_ERROR, "No statement string", func);
2925 return SQL_ERROR;
2926 }
2927
2928 qp = &query_org;
2929 QP_initialize(qp, stmt);
2930
2931 if (stmt->statement_type != STMT_TYPE_SELECT)
2932 {
2933 stmt->options.cursor_type = SQL_CURSOR_FORWARD_ONLY;
2934 stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
2935 }
2936 else if (stmt->options.cursor_type == SQL_CURSOR_FORWARD_ONLY)
2937 stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
2938 else if (stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
2939 {
2940 if (SQL_CURSOR_DYNAMIC == stmt->options.cursor_type &&
2941 0 == (ci->updatable_cursors & ALLOW_DYNAMIC_CURSORS))
2942 stmt->options.cursor_type = SQL_CURSOR_KEYSET_DRIVEN;
2943 if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type &&
2944 0 == (ci->updatable_cursors & ALLOW_KEYSET_DRIVEN_CURSORS))
2945 stmt->options.cursor_type = SQL_CURSOR_STATIC;
2946 switch (stmt->options.cursor_type)
2947 {
2948 case SQL_CURSOR_DYNAMIC:
2949 case SQL_CURSOR_KEYSET_DRIVEN:
2950 if (SC_update_not_ready(stmt))
2951 parse_statement(stmt, TRUE);
2952 if (SC_is_updatable(stmt) && stmt->ntab > 0)
2953 {
2954 if (bestitem = GET_NAME(stmt->ti[0]->bestitem), NULL == bestitem)
2955 stmt->options.cursor_type = SQL_CURSOR_STATIC;
2956 }
2957 break;
2958 }
2959 if (SQL_CURSOR_STATIC == stmt->options.cursor_type)
2960 {
2961 if (0 == (ci->updatable_cursors & ALLOW_STATIC_CURSORS))
2962 stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
2963 else if (SC_update_not_ready(stmt))
2964 parse_statement(stmt, TRUE);
2965 }
2966 if (SC_parsed_status(stmt) == STMT_PARSE_FATAL)
2967 {
2968 stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
2969 if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
2970 stmt->options.cursor_type = SQL_CURSOR_STATIC;
2971 }
2972 else if (!stmt->updatable)
2973 {
2974 stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
2975 stmt->options.cursor_type = SQL_CURSOR_STATIC;
2976 }
2977 else
2978 {
2979 qp->from_pos = stmt->from_pos;
2980 qp->where_pos = stmt->where_pos;
2981 }
2982 MYLOG(DETAIL_LOG_LEVEL, "type=" FORMAT_UINTEGER " concur=" FORMAT_UINTEGER "\n", stmt->options.cursor_type, stmt->options.scroll_concurrency);
2983 }
2984
2985 SC_miscinfo_clear(stmt);
2986 /* If the application hasn't set a cursor name, then generate one */
2987 if (!SC_cursor_is_valid(stmt))
2988 {
2989 char curname[32];
2990
2991 SPRINTF_FIXED(curname, "SQL_CUR%p", stmt);
2992 STRX_TO_NAME(stmt->cursor_name, curname);
2993 }
2994 if (stmt->stmt_with_params)
2995 {
2996 free(stmt->stmt_with_params);
2997 stmt->stmt_with_params = NULL;
2998 }
2999
3000 SC_no_fetchcursor(stmt);
3001 qb = &query_crt;
3002 qb->query_statement = NULL;
3003 if (PREPARED_PERMANENTLY == stmt->prepared)
3004 {
3005 /* already prepared */
3006 retval = SQL_SUCCESS;
3007 goto cleanup;
3008 }
3009
3010 /*
3011 * If it's a simple read-only cursor, we use extended query protocol to
3012 * Parse it.
3013 */
3014 if (buildPrepareStatement &&
3015 SQL_CONCUR_READ_ONLY == stmt->options.scroll_concurrency)
3016 {
3017 /* Nothing to do here. It will be prepared before execution. */
3018 char plan_name[32];
3019 if (NAMED_PARSE_REQUEST == SC_get_prepare_method(stmt))
3020 SPRINTF_FIXED(plan_name, "_PLAN%p", stmt);
3021 else
3022 plan_name[0] = '\0';
3023
3024 SC_set_planname(stmt, plan_name);
3025 SC_set_prepared(stmt, plan_name[0] ? PREPARING_PERMANENTLY : PREPARING_TEMPORARILY);
3026
3027 retval = SQL_SUCCESS;
3028
3029 goto cleanup;
3030 }
3031
3032 /* Otherwise... */
3033 if (QB_initialize(qb, qp->stmt_len, stmt, RPM_REPLACE_PARAMS) < 0)
3034 {
3035 retval = SQL_ERROR;
3036 goto cleanup;
3037 }
3038 if (SIMPLE_PARAM_CAST)
3039 qb->flags |= FLGB_PARAM_CAST;
3040 new_statement = qb->query_statement;
3041
3042 /* For selects, prepend a declare cursor to the statement */
3043 if (SC_may_use_cursor(stmt) && stmt->external)
3044 {
3045 const char *opt_scroll = NULL_STRING, *opt_hold = NULL_STRING;
3046
3047 if (ci->drivers.use_declarefetch
3048 /** && SQL_CONCUR_READ_ONLY == stmt->options.scroll_concurrency **/
3049 )
3050 {
3051 SC_set_fetchcursor(stmt);
3052 if (SC_is_with_hold(stmt))
3053 opt_hold = " with hold";
3054 if (SQL_CURSOR_FORWARD_ONLY != stmt->options.cursor_type)
3055 opt_scroll = " scroll";
3056 }
3057 if (SC_is_fetchcursor(stmt))
3058 {
3059 snprintfcat(new_statement, qb->str_alsize,
3060 "declare \"%s\"%s cursor%s for ",
3061 SC_cursor_name(stmt), opt_scroll, opt_hold);
3062 qb->npos = strlen(new_statement);
3063 qp->flags |= FLGP_USING_CURSOR;
3064 qp->declare_pos = qb->npos;
3065 }
3066 if (SQL_CONCUR_READ_ONLY != stmt->options.scroll_concurrency)
3067 {
3068 qb->flags |= FLGB_CREATE_KEYSET;
3069 if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type)
3070 qb->flags |= FLGB_KEYSET_DRIVEN;
3071 }
3072 }
3073
3074 for (qp->opos = 0; qp->opos < qp->stmt_len; qp->opos++)
3075 {
3076 retval = inner_process_tokens(qp, qb);
3077 if (SQL_ERROR == retval)
3078 {
3079 QB_replace_SC_error(stmt, qb, func);
3080 QB_Destructor(qb);
3081 return retval;
3082 }
3083 }
3084 /* make sure new_statement is always null-terminated */
3085 CVT_TERMINATE(qb);
3086
3087 new_statement = qb->query_statement;
3088 stmt->statement_type = qp->statement_type;
3089 if (0 == (qp->flags & FLGP_USING_CURSOR))
3090 SC_no_fetchcursor(stmt);
3091 #ifdef NOT_USED /* this seems problematic */
3092 else if (0 == (qp->flags & (FLGP_SELECT_FOR_UPDATE_OR_SHARE | FLGP_SELECT_FOR_READONLY)) &&
3093 0 == stmt->multi_statement &&
3094 PG_VERSION_GE(conn, 8.3))
3095 {
3096 BOOL semi_colon_found = FALSE;
3097 const UCHAR *ptr = NULL, semi_colon = ';';
3098 int npos;
3099
3100 if (npos = F_NewPos(qb) - 1, npos >= 0)
3101 ptr = F_NewPtr(qb) - 1;
3102 for (; npos >= 0 && isspace(*ptr); npos--, ptr--) ;
3103 if (npos >= 0 && semi_colon == *ptr)
3104 {
3105 qb->npos = npos;
3106 semi_colon_found = TRUE;
3107 }
3108 CVT_APPEND_STR(qb, " for read only");
3109 if (semi_colon_found)
3110 CVT_APPEND_CHAR(qb, semi_colon);
3111 CVT_TERMINATE(qb);
3112 }
3113 #endif /* NOT_USED */
3114 if (0 != (qp->flags & FLGP_SELECT_INTO) ||
3115 0 != (qp->flags & FLGP_MULTIPLE_STATEMENT))
3116 {
3117 stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
3118 }
3119 if (0 != (qp->flags & FLGP_SELECT_FOR_UPDATE_OR_SHARE))
3120 {
3121 stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
3122 }
3123
3124 if (conn->DriverToDataSource != NULL)
3125 {
3126 size_t length = strlen(new_statement);
3127
3128 conn->DriverToDataSource(conn->translation_option,
3129 SQL_CHAR, new_statement, (SDWORD) length,
3130 new_statement, (SDWORD) length, NULL,
3131 NULL, 0, NULL);
3132 }
3133
3134 if (!stmt->load_statement && qp->from_pos >= 0)
3135 {
3136 size_t npos = qb->load_stmt_len;
3137
3138 if (0 == npos)
3139 {
3140 npos = qb->npos;
3141 for (; npos > 0; npos--)
3142 {
3143 if (isspace((unsigned char) new_statement[npos - 1]))
3144 continue;
3145 if (';' != new_statement[npos - 1])
3146 break;
3147 }
3148 if (0 != (qb->flags & FLGB_KEYSET_DRIVEN))
3149 {
3150 qb->npos = npos;
3151 /* ----------
3152 * 1st query is for field information
3153 * 2nd query is keyset gathering
3154 */
3155 CVT_APPEND_STR(qb, " where ctid = '(0,0)';select \"ctid");
3156 if (bestitem)
3157 {
3158 CVT_APPEND_STR(qb, "\", \"");
3159 CVT_APPEND_STR(qb, bestitem);
3160 }
3161 CVT_APPEND_STR(qb, "\" from ");
3162 CVT_APPEND_DATA(qb, qp->statement + qp->from_pos + 5, npos - qp->from_pos - 5);
3163 }
3164 }
3165 npos -= qp->declare_pos;
3166 stmt->load_statement = malloc(npos + 1);
3167 if (!stmt->load_statement)
3168 {
3169 retval = SQL_ERROR;
3170 goto cleanup;
3171 }
3172 memcpy(stmt->load_statement, qb->query_statement + qp->declare_pos, npos);
3173 stmt->load_from_pos = qb->load_from_pos - qp->declare_pos;
3174 stmt->load_statement[npos] = '\0';
3175 }
3176
3177 stmt->stmt_with_params = qb->query_statement;
3178 retval = SQL_SUCCESS;
3179 cleanup:
3180 return retval;
3181 }
3182
3183 static void
remove_declare_cursor(QueryBuild * qb,QueryParse * qp)3184 remove_declare_cursor(QueryBuild *qb, QueryParse *qp)
3185 {
3186 qp->flags &= ~FLGP_USING_CURSOR;
3187 if (qp->declare_pos <= 0) return;
3188 memmove(qb->query_statement, qb->query_statement + qp->declare_pos, qb->npos - qp->declare_pos);
3189 qb->npos -= qp->declare_pos;
3190 qp->declare_pos = 0;
3191 }
3192
3193 /*
3194 * When 'tag' starts with dollar-quoted tag, e.g. "$foo$...", return
3195 * the length of the tag (e.g. 5, with the previous example). If there
3196 * is no end-dollar in the string, returns 0. The caller should've checked
3197 * that the string begins with a dollar.
3198 */
3199 size_t
findTag(const char * tag,int ccsc)3200 findTag(const char *tag, int ccsc)
3201 {
3202 size_t taglen = 0;
3203 encoded_str encstr;
3204 UCHAR tchar;
3205
3206 encoded_str_constr(&encstr, ccsc, tag + 1);
3207 for (tchar = encoded_nextchar(&encstr); tchar; tchar = encoded_nextchar(&encstr))
3208 {
3209 if (MBCS_NON_ASCII(encstr))
3210 continue;
3211 if (DOLLAR_QUOTE == tchar)
3212 {
3213 taglen = encstr.pos + 2;
3214 break;
3215 }
3216 if (!isalnum(tchar))
3217 break;
3218 }
3219 return taglen;
3220 }
3221
3222 int
findIdentifier(const UCHAR * str,int ccsc,const UCHAR ** next_token)3223 findIdentifier(const UCHAR *str, int ccsc, const UCHAR **next_token)
3224 {
3225 int slen = -1;
3226 encoded_str encstr;
3227 UCHAR tchar;
3228 BOOL dquote = FALSE;
3229
3230 *next_token = NULL;
3231 encoded_str_constr(&encstr, ccsc, (const char *) str);
3232 for (tchar = encoded_nextchar(&encstr); tchar; tchar = encoded_nextchar(&encstr))
3233 {
3234 if (MBCS_NON_ASCII(encstr))
3235 continue;
3236 if (encstr.pos == 0) /* the first character */
3237 {
3238 if (dquote = (IDENTIFIER_QUOTE == tchar), dquote)
3239 continue;
3240 if (!isalpha(tchar))
3241 {
3242 slen = 0;
3243 if (IS_NOT_SPACE(tchar))
3244 *next_token = ENCODE_PTR(encstr);
3245 break;
3246 }
3247 }
3248 if (dquote)
3249 {
3250 if (IDENTIFIER_QUOTE == tchar)
3251 {
3252 tchar = encoded_nextchar(&encstr);
3253 if (IDENTIFIER_QUOTE == tchar)
3254 continue;
3255 slen = encstr.pos;
3256 break;
3257 }
3258 }
3259 else
3260 {
3261 if (isalnum(tchar))
3262 continue;
3263 switch (tchar)
3264 {
3265 case '_':
3266 case DOLLAR_QUOTE:
3267 continue;
3268 }
3269 slen = encstr.pos;
3270 if (IS_NOT_SPACE(tchar))
3271 *next_token = ENCODE_PTR(encstr);
3272 break;
3273 }
3274 }
3275 if (slen < 0 && !dquote)
3276 slen = encstr.pos;
3277 if (NULL == *next_token)
3278 {
3279 for (; tchar; tchar = encoded_nextchar(&encstr))
3280 {
3281 if (IS_NOT_SPACE((UCHAR) tchar))
3282 {
3283 *next_token = ENCODE_PTR(encstr);
3284 break;
3285 }
3286 }
3287 }
3288 return slen;
3289 }
3290
lower_or_remove_dquote(pgNAME nm,const UCHAR * src,int srclen,int ccsc)3291 static pgNAME lower_or_remove_dquote(pgNAME nm, const UCHAR *src, int srclen, int ccsc)
3292 {
3293 int i, outlen;
3294 char *tc;
3295 UCHAR tchar;
3296 BOOL idQuote;
3297 encoded_str encstr;
3298
3299 if (nm.name)
3300 tc = realloc(nm.name, srclen + 1);
3301 else
3302 tc = malloc(srclen + 1);
3303 if (!tc)
3304 {
3305 NULL_THE_NAME(nm);
3306 return nm;
3307 }
3308 nm.name = tc;
3309 idQuote = (src[0] == IDENTIFIER_QUOTE);
3310 encoded_str_constr(&encstr, ccsc, (const char *) src);
3311 for (i = 0, tchar = encoded_nextchar(&encstr), outlen = 0; i < srclen; i++, tchar = encoded_nextchar(&encstr))
3312 {
3313 if (MBCS_NON_ASCII(encstr))
3314 {
3315 tc[outlen++] = tchar;
3316 continue;
3317 }
3318 if (idQuote)
3319 {
3320 if (IDENTIFIER_QUOTE == tchar)
3321 {
3322 if (0 == i)
3323 continue;
3324 if (i == srclen - 1)
3325 continue;
3326 i++;
3327 tchar = encoded_nextchar(&encstr);
3328 }
3329 tc[outlen++] = tchar;
3330 }
3331 else
3332 {
3333 tc[outlen++] = tolower(tchar);
3334 }
3335 }
3336 tc[outlen] = '\0';
3337 return nm;
3338 }
3339
3340 int
eatTableIdentifiers(const UCHAR * str,int ccsc,pgNAME * table,pgNAME * schema)3341 eatTableIdentifiers(const UCHAR *str, int ccsc, pgNAME *table, pgNAME *schema)
3342 {
3343 int len;
3344 const UCHAR *next_token;
3345 const UCHAR *tstr = str;
3346
3347 while (isspace(*tstr)) tstr++;
3348
3349 if ((len = findIdentifier(tstr, ccsc, &next_token)) <= 0)
3350 return len; /* table name doesn't exist */
3351 if (table)
3352 {
3353 if (IDENTIFIER_QUOTE == *tstr)
3354 *table = lower_or_remove_dquote(*table, tstr, len, ccsc);
3355 else
3356 STRN_TO_NAME(*table, tstr, len);
3357 }
3358 if (!next_token || '.' != *next_token || (int) (next_token - tstr) != len)
3359 return (int) (next_token - str); /* table only */
3360 tstr = next_token + 1;
3361 if ((len = findIdentifier(tstr, ccsc, &next_token)) <= 0)
3362 return -1;
3363 if (table)
3364 {
3365 if (schema)
3366 MOVE_NAME(*schema, *table);
3367 *table = lower_or_remove_dquote(*table, tstr, len, ccsc);
3368 }
3369 if (!next_token || '.' != *next_token || (int) (next_token - tstr) != len)
3370 return (int) (next_token - str); /* schema.table */
3371 tstr = next_token + 1;
3372 if ((len = findIdentifier(tstr, ccsc, &next_token)) <= 0)
3373 return -1;
3374 if (table)
3375 {
3376 if (schema)
3377 MOVE_NAME(*schema, *table);
3378 *table = lower_or_remove_dquote(*table, tstr, len, ccsc);
3379 }
3380 return (int) (next_token - str); /* catalog.schema.table */
3381 }
3382
token_start(QueryParse * qp,char oldchar)3383 static void token_start(QueryParse *qp, char oldchar)
3384 {
3385 qp->prev_token_end = FALSE;
3386 qp->token_curr[0] = oldchar;
3387 qp->token_len = 1;
3388 }
token_finish(QueryParse * qp,char oldchar,char * finished_token)3389 static int token_finish(QueryParse *qp, char oldchar, char *finished_token)
3390 {
3391 int ret = -1;
3392 if (!qp->prev_token_end)
3393 {
3394 if (oldchar && qp->token_len + 1 < sizeof(qp->token_curr))
3395 qp->token_curr[qp->token_len++] = oldchar;
3396 qp->prev_token_end = TRUE;
3397 qp->token_curr[qp->token_len] = '\0';
3398 strncpy_null(finished_token, qp->token_curr, sizeof(qp->token_curr));
3399 MYLOG(DETAIL_LOG_LEVEL, "finished token=%s\n", finished_token);
3400 ret = qp->token_len;
3401 }
3402 return ret;
3403 }
3404
token_restart(QueryParse * qp,char oldchar,char * finished_token)3405 static int token_restart(QueryParse *qp, char oldchar, char *finished_token)
3406 {
3407 int ret = token_finish(qp, 0, finished_token);
3408 if (IS_NOT_SPACE(oldchar))
3409 token_start(qp, oldchar);
3410
3411 return ret;
3412 }
3413
token_continue(QueryParse * qp,char oldchar)3414 static int token_continue(QueryParse *qp, char oldchar)
3415 {
3416 if (qp->prev_token_end)
3417 token_start(qp, oldchar);
3418 else if (qp->token_len + 1 < sizeof(qp->token_curr))
3419 qp->token_curr[qp->token_len++] = oldchar;
3420
3421 return qp->token_len;
3422 }
3423
3424 /*
3425 * ParseToken functions
3426 */
3427 typedef struct {
3428 QueryParse *qp;
3429 int token_len;
3430 BOOL curchar_processed;
3431 unsigned int in_status;
3432 char finished_token[sizeof(((QueryParse *) NULL)->token_curr)];
3433 } ParseToken;
3434
PT_initialize(ParseToken * pt,QueryParse * qp)3435 static void PT_initialize(ParseToken *pt, QueryParse *qp)
3436 {
3437 pt->qp = qp;
3438 pt->token_len = -1;
3439 pt->curchar_processed = FALSE;
3440 pt->in_status = 0;
3441 pt->finished_token[0] = '\0';
3442 }
3443
PT_token_finish(ParseToken * pt,char oldchar)3444 static int PT_token_finish(ParseToken *pt, char oldchar)
3445 {
3446 int token_len_tmp;
3447
3448 if (pt->curchar_processed)
3449 return pt->token_len;
3450 if ((token_len_tmp = token_finish(pt->qp, oldchar, pt->finished_token)) > 0)
3451 {
3452 pt->token_len = token_len_tmp;
3453 pt->in_status = pt->qp->in_status;
3454 }
3455 if (oldchar)
3456 pt->curchar_processed = TRUE;
3457 return pt->token_len;
3458 }
3459
PT_token_restart(ParseToken * pt,char oldchar)3460 static int PT_token_restart(ParseToken *pt, char oldchar)
3461 {
3462 int token_len_tmp;
3463 unsigned int in_status_save;
3464
3465 if (pt->curchar_processed)
3466 return pt->token_len;
3467 in_status_save = pt->qp->in_status;
3468 if ((token_len_tmp = token_restart(pt->qp, oldchar, pt->finished_token)) > 0)
3469 {
3470 pt->token_len = token_len_tmp;
3471 pt->in_status = in_status_save;
3472 }
3473 pt->curchar_processed = TRUE;
3474 return pt->token_len;
3475 }
3476
PT_token_continue(ParseToken * pt,char oldchar)3477 static int PT_token_continue(ParseToken *pt, char oldchar)
3478 {
3479 if (pt->curchar_processed)
3480 return pt->token_len;
3481 token_continue(pt->qp, oldchar);
3482 pt->curchar_processed = TRUE;
3483 return pt->token_len;
3484 }
3485 #define PT_TOKEN_IGNORE(pt) ((pt)->curchar_processed = TRUE)
3486
3487 static int
inner_process_tokens(QueryParse * qp,QueryBuild * qb)3488 inner_process_tokens(QueryParse *qp, QueryBuild *qb)
3489 {
3490 CSTR func = "inner_process_tokens";
3491 BOOL lf_conv = ((qb->flags & FLGB_CONVERT_LF) != 0);
3492 const char *bestitem = NULL;
3493
3494 RETCODE retval;
3495 Int4 opos;
3496 char oldchar;
3497 StatementClass *stmt = qb->stmt;
3498 BOOL isnull;
3499 BOOL isbinary;
3500 Oid dummy;
3501 ParseToken pts, *pt = &pts;
3502
3503 PT_initialize(pt, qp);
3504
3505 if (stmt->ntab > 0)
3506 bestitem = GET_NAME(stmt->ti[0]->bestitem);
3507 opos = (Int4) qp->opos;
3508 if (opos < 0)
3509 {
3510 qb->errornumber = STMT_SEQUENCE_ERROR;
3511 qb->errormsg = "Function call for inner_process_tokens sequence error";
3512 return SQL_ERROR;
3513 }
3514 if (qp->from_pos == opos)
3515 {
3516 if (0 == (qb->flags & FLGB_CREATE_KEYSET))
3517 {
3518 qb->errornumber = STMT_SEQUENCE_ERROR;
3519 qb->errormsg = "Should come here only when handling updatable cursors";
3520 return SQL_ERROR;
3521 }
3522 CVT_APPEND_STR(qb, ", \"ctid");
3523 if (bestitem)
3524 {
3525 CVT_APPEND_STR(qb, "\", \"");
3526 CVT_APPEND_STR(qb, bestitem);
3527 }
3528 CVT_APPEND_STR(qb, "\" ");
3529 qb->load_from_pos = qb->npos;
3530 }
3531 else if (qp->where_pos == opos)
3532 {
3533 qb->load_stmt_len = qb->npos;
3534 if (0 != (qb->flags & FLGB_KEYSET_DRIVEN))
3535 {
3536 CVT_APPEND_STR(qb, "where ctid = '(0,0)';select \"ctid");
3537 if (bestitem)
3538 {
3539 CVT_APPEND_STR(qb, "\", \"");
3540 CVT_APPEND_STR(qb, bestitem);
3541 }
3542 CVT_APPEND_STR(qb, "\" from ");
3543 CVT_APPEND_DATA(qb, qp->statement + qp->from_pos + 5, qp->where_pos - qp->from_pos - 5);
3544 }
3545 }
3546 oldchar = encoded_byte_check(&qp->encstr, qp->opos);
3547 if (MBCS_NON_ASCII(qp->encstr))
3548 {
3549 if (QP_in_idle_status(qp))
3550 {
3551 PT_token_restart(pt, oldchar); /* placed before QP_enter() */
3552 QP_enter(qp, QP_IN_IDENT_KEYWORD); /* identifier */
3553 }
3554 else if (qp->token_len > 0)
3555 PT_token_continue(pt, oldchar);
3556 CVT_APPEND_CHAR(qb, oldchar);
3557 return SQL_SUCCESS;
3558 }
3559
3560 /*
3561 * From here we are guaranteed to handle a 1-byte character.
3562 */
3563
3564 if (QP_is_in(qp, QP_IN_IDENT_KEYWORD)) /* identifier or keyword */
3565 {
3566 if (isalnum((UCHAR)oldchar) ||
3567 DOLLAR_QUOTE == oldchar ||
3568 '_' == oldchar)
3569 {
3570 CVT_APPEND_CHAR(qb, oldchar);
3571 PT_token_continue(pt, oldchar);
3572 return SQL_SUCCESS;
3573 }
3574 PT_token_finish(pt, 0); /* placed before QP_exit() */
3575 QP_exit(qp, QP_IN_IDENT_KEYWORD);
3576 }
3577
3578 if (QP_is_in(qp, QP_IN_ESCAPE)) /* escape in literal check */
3579 {
3580 QP_exit(qp, QP_IN_ESCAPE);
3581 CVT_APPEND_CHAR(qb, oldchar);
3582 return SQL_SUCCESS;
3583 }
3584 else if (QP_is_in(qp, QP_IN_DOLLAR_QUOTE)) /* dollar quote check */
3585 {
3586 if (oldchar == DOLLAR_QUOTE)
3587 {
3588 if (strncmp(F_OldPtr(qp), qp->dollar_tag, qp->taglen) == 0)
3589 {
3590 CVT_APPEND_DATA(qb, F_OldPtr(qp), qp->taglen);
3591 qp->opos += (qp->taglen - 1);
3592 QP_exit(qp, QP_IN_DOLLAR_QUOTE);
3593 qp->dollar_tag = NULL;
3594 qp->taglen = -1;
3595 return SQL_SUCCESS;
3596 }
3597 }
3598 CVT_APPEND_CHAR(qb, oldchar);
3599 return SQL_SUCCESS;
3600 }
3601 else if (QP_is_in(qp, QP_IN_LITERAL)) /* quote check */
3602 {
3603 if (oldchar == LITERAL_QUOTE)
3604 {
3605 PT_token_finish(pt, oldchar); /* placed before QP_exit() */
3606 QP_exit(qp, QP_IN_LITERAL);
3607 }
3608 else
3609 {
3610 PT_token_continue(pt, oldchar);
3611 if (oldchar == qp->escape_in_literal)
3612 QP_enter(qp, QP_IN_ESCAPE); /* escape in literal */
3613 }
3614 CVT_APPEND_CHAR(qb, oldchar);
3615 return SQL_SUCCESS;
3616 }
3617 else if (QP_is_in(qp, QP_IN_DQUOTE_IDENTIFIER)) /* double quote check */
3618 {
3619 if (oldchar == IDENTIFIER_QUOTE)
3620 {
3621 PT_token_finish(pt, oldchar); /* placed before QP_exit() */
3622 QP_exit(qp, QP_IN_DQUOTE_IDENTIFIER);
3623 }
3624 else
3625 PT_token_continue(pt, oldchar);
3626 CVT_APPEND_CHAR(qb, oldchar);
3627 return SQL_SUCCESS;
3628 }
3629 else if (QP_is_in(qp, QP_IN_COMMENT_BLOCK)) /* comment_level check */
3630 {
3631 if ('/' == oldchar &&
3632 '*' == F_OldPtr(qp)[1])
3633 {
3634 qp->comment_level++;
3635 QP_enter(qp, QP_IN_COMMENT_BLOCK);
3636 CVT_APPEND_CHAR(qb, oldchar);
3637 F_OldNext(qp);
3638 oldchar = F_OldChar(qp);
3639 }
3640 else if ('*' == oldchar &&
3641 '/' == F_OldPtr(qp)[1])
3642 {
3643 if (--qp->comment_level <= 0)
3644 QP_exit(qp, QP_IN_COMMENT_BLOCK);
3645 CVT_APPEND_CHAR(qb, oldchar);
3646 F_OldNext(qp);
3647 oldchar = F_OldChar(qp);
3648 }
3649 CVT_APPEND_CHAR(qb, oldchar);
3650 return SQL_SUCCESS;
3651 }
3652 else if (QP_is_in(qp, QP_IN_LINE_COMMENT)) /* line comment check */
3653 {
3654 if (PG_LINEFEED == oldchar)
3655 QP_exit(qp, QP_IN_LINE_COMMENT);
3656 CVT_APPEND_CHAR(qb, oldchar);
3657 return SQL_SUCCESS;
3658 }
3659
3660 if (!QP_in_idle_status(qp))
3661 {
3662 qb->errornumber = STMT_EXEC_ERROR;
3663 qb->errormsg = "logic error? not in QP_in_idle_status";
3664 return SQL_ERROR;
3665 }
3666
3667 /*
3668 * From here we are guaranteed to be in neither a literal_escape,
3669 * a LITREAL_QUOTE nor an IDENTIFIER_QUOTE.
3670 */
3671 /* Squeeze carriage-return/linefeed pairs to linefeed only */
3672 if (lf_conv &&
3673 PG_CARRIAGE_RETURN == oldchar &&
3674 qp->opos + 1 < qp->stmt_len &&
3675 PG_LINEFEED == qp->statement[qp->opos + 1])
3676 return SQL_SUCCESS;
3677
3678 /*
3679 * Handle literals (date, time, timestamp) and ODBC scalar
3680 * functions
3681 */
3682 if (oldchar == ODBC_ESCAPE_START)
3683 {
3684 PT_token_finish(pt, 0);
3685 if (SQL_ERROR == convert_escape(qp, qb))
3686 {
3687 if (0 == qb->errornumber)
3688 {
3689 qb->errornumber = STMT_EXEC_ERROR;
3690 qb->errormsg = "ODBC escape convert error";
3691 }
3692 MYLOG(0, "convert_escape error\n");
3693 return SQL_ERROR;
3694 }
3695 PT_TOKEN_IGNORE(pt);
3696 return SQL_SUCCESS;
3697 }
3698 /* End of an escape sequence */
3699 else if (oldchar == ODBC_ESCAPE_END)
3700 {
3701 PT_token_finish(pt, 0);
3702 PT_TOKEN_IGNORE(pt);
3703 return QB_end_brace(qb);
3704 }
3705 else if (oldchar == '@' &&
3706 strnicmp(F_OldPtr(qp), "@@identity", 10) == 0)
3707 {
3708 ConnectionClass *conn = SC_get_conn(stmt);
3709 BOOL converted = FALSE;
3710 COL_INFO *coli;
3711
3712 #ifdef NOT_USED /* lastval() isn't always appropriate */
3713 if (PG_VERSION_GE(conn, 8.1))
3714 {
3715 CVT_APPEND_STR(qb, "lastval()");
3716 converted = TRUE;
3717 }
3718 else
3719 #endif /* NOT_USED */
3720 if (NAME_IS_VALID(conn->tableIns))
3721 {
3722 TABLE_INFO ti, *pti = &ti;
3723
3724 memset(&ti, 0, sizeof(ti));
3725 NAME_TO_NAME(ti.schema_name, conn->schemaIns);
3726 NAME_TO_NAME(ti.table_name, conn->tableIns);
3727 getCOLIfromTI(func, conn, qb->stmt, 0, &pti);
3728 coli = ti.col_info;
3729 NULL_THE_NAME(ti.schema_name);
3730 NULL_THE_NAME(ti.table_name);
3731 if (NULL != coli)
3732 {
3733 int i, num_fields = QR_NumResultCols(coli->result);
3734 const char *auto_increment;
3735
3736 for (i = 0; i < num_fields; i++)
3737 {
3738 auto_increment = (const char *) QR_get_value_backend_text(coli->result, i, COLUMNS_AUTO_INCREMENT);
3739 if (auto_increment && auto_increment[0] == '1')
3740 {
3741 converted = TRUE;
3742 break;
3743 }
3744 }
3745 if (converted)
3746 {
3747 const char *column_def = (const char *) QR_get_value_backend_text(coli->result, i, COLUMNS_COLUMN_DEF);
3748 if (NULL != column_def &&
3749 strncmp(column_def, "nextval", 7) == 0)
3750 {
3751 CVT_APPEND_STR(qb, "curr");
3752 CVT_APPEND_STR(qb, column_def + 4);
3753 }
3754 else
3755 {
3756 char relcnv[128];
3757 const char *column_name = (const char *) QR_get_value_backend_text(coli->result, i, COLUMNS_COLUMN_NAME);
3758
3759 CVT_APPEND_STR(qb, "currval(pg_get_serial_sequence('");
3760 if (NAME_IS_VALID(conn->schemaIns))
3761 {
3762 CVT_APPEND_STR(qb, identifierEscape((const SQLCHAR *) SAFE_NAME(conn->schemaIns), SQL_NTS, conn, relcnv, sizeof(relcnv), TRUE));
3763 CVT_APPEND_STR(qb, ".");
3764 }
3765 CVT_APPEND_STR(qb, identifierEscape((const SQLCHAR *) SAFE_NAME(conn->tableIns), SQL_NTS, conn, relcnv, sizeof(relcnv), TRUE));
3766 CVT_APPEND_STR(qb, "','");
3767 if (NULL != column_name)
3768 CVT_APPEND_STR(qb, identifierEscape((const SQLCHAR *) column_name, SQL_NTS, conn, relcnv, sizeof(relcnv), FALSE));
3769 CVT_APPEND_STR(qb, "')::regclass)");
3770 }
3771 }
3772 }
3773 }
3774 if (!converted)
3775 CVT_APPEND_STR(qb, "NULL");
3776 qp->opos += 10;
3777 return SQL_SUCCESS;
3778 }
3779
3780 /*
3781 * Can you have parameter markers inside of quotes? I dont think
3782 * so. All the queries I've seen expect the driver to put quotes
3783 * if needed.
3784 */
3785 else if (oldchar != '?')
3786 {
3787 if (oldchar == DOLLAR_QUOTE)
3788 {
3789 PT_token_finish(pt, 0);
3790 qp->taglen = findTag(F_OldPtr(qp), qp->encstr.ccsc);
3791 if (qp->taglen > 0)
3792 {
3793 QP_enter(qp, QP_IN_DOLLAR_QUOTE);
3794 qp->dollar_tag = F_OldPtr(qp);
3795 CVT_APPEND_DATA(qb, F_OldPtr(qp), qp->taglen);
3796 qp->opos += (qp->taglen - 1);
3797 return SQL_SUCCESS;
3798 }
3799 }
3800 else if (oldchar == LITERAL_QUOTE)
3801 {
3802 PT_token_restart(pt, oldchar); /* placed before QP_enter() */
3803 QP_enter(qp, QP_IN_LITERAL);
3804 qp->escape_in_literal = CC_get_escape(qb->conn);
3805 if (!qp->escape_in_literal)
3806 {
3807 if (LITERAL_EXT == F_OldPtr(qp)[-1])
3808 qp->escape_in_literal = ESCAPE_IN_LITERAL;
3809 }
3810 }
3811 else if (oldchar == IDENTIFIER_QUOTE)
3812 {
3813 PT_token_restart(pt, oldchar); /* placed before QP_enter() */
3814 QP_enter(qp, QP_IN_DQUOTE_IDENTIFIER);
3815 }
3816 else if ('/' == oldchar &&
3817 '*' == F_OldPtr(qp)[1])
3818 {
3819 qp->comment_level++;
3820 PT_token_finish(pt, 0); /* comments are excluded */
3821 QP_enter(qp, QP_IN_COMMENT_BLOCK);
3822 PT_TOKEN_IGNORE(pt);
3823 }
3824 else if ('-' == oldchar &&
3825 '-' == F_OldPtr(qp)[1])
3826 {
3827 PT_token_finish(pt, 0); /* comments are excluded */
3828 QP_enter(qp, QP_IN_LINE_COMMENT);
3829 PT_TOKEN_IGNORE(pt);
3830 }
3831 else if (oldchar == ';')
3832 {
3833 PT_token_restart(pt, 0);
3834 /*
3835 * can't parse multiple statement using protocol V3.
3836 * reset the dollar number here in case it is divided
3837 * to parse.
3838 */
3839 qb->dollar_number = 0;
3840 if (0 != (qp->flags & FLGP_USING_CURSOR))
3841 {
3842 const char *vp = &(qp->statement[qp->opos + 1]);
3843
3844 while (*vp && isspace((unsigned char) *vp))
3845 vp++;
3846 if (*vp) /* multiple statement */
3847 {
3848 qp->flags |= FLGP_MULTIPLE_STATEMENT;
3849 qb->flags &= ~FLGB_KEYSET_DRIVEN;
3850 remove_declare_cursor(qb, qp);
3851 }
3852 }
3853 }
3854 else if (isalnum(oldchar))
3855 {
3856 PT_token_restart(pt, oldchar); /* placed before QP_enter() */
3857 QP_enter(qp, QP_IN_IDENT_KEYWORD); /* identifier or keyword */
3858 }
3859 else
3860 {
3861 /*
3862 * a hack to handle boolean items in VBA
3863 * with MS Access.
3864 * VBA seems to transform the where condition
3865 * a_boolean_item=True
3866 * into
3867 * ("a_boolean_item" = 1)
3868 * which causes an ERROR:Operator does not exist boolean = integer .
3869 * So transforms it into
3870 * ("a_boolean_item"='1')
3871 * here.
3872 */
3873 if (')' == oldchar &&
3874 qb->conn->ms_jet &&
3875 1 == qp->token_len &&
3876 '1' == qp->token_curr[0] &&
3877 8 <= F_OldPos(qp))
3878 {
3879 const char *oldptr = F_OldPtr(qp);
3880 int oldpos = F_OldPos(qp);
3881
3882 if (strncmp(oldptr - 5, "\" =", 3) == 0)
3883 {
3884 int i;
3885
3886 for (i = 6; i < oldpos - 1; i++)
3887 {
3888 if (oldptr[-i] == '"')
3889 {
3890 if (oldptr[-(i+1)] == '(')
3891 {
3892 F_NewPtr(qb)[-4] = '=';
3893 F_NewPtr(qb)[-3] = '\'';
3894 F_NewPtr(qb)[-2] = '1';
3895 F_NewPtr(qb)[-1] = '\'';
3896 }
3897 break;
3898 }
3899 }
3900 }
3901 }
3902 if (!isalnum((UCHAR) oldchar))
3903 {
3904 PT_token_restart(pt, oldchar);
3905 }
3906 else
3907 PT_token_continue(pt, oldchar);
3908 }
3909
3910 if (pt->token_len > 0)
3911 MYLOG(0, "token_len=%d status=%x token=%s\n", pt->token_len, pt->in_status, pt->finished_token);
3912 if (!pt->curchar_processed)
3913 {
3914 MYLOG(0, "Forgot to process ParseToken char=%c status=%u\n", oldchar, qp->in_status);
3915 #ifdef NOT_USED /* strict check for debugging */
3916 qb->errornumber = STMT_EXEC_ERROR;
3917 qb->errormsg = "Forget to process ParseToken";
3918 return SQL_ERROR;
3919 #endif /* NOT_USED */
3920 }
3921 switch (pt->token_len)
3922 {
3923 case 4:
3924 if (0 != (qp->flags & FLGP_USING_CURSOR) &&
3925 into_table_from(&qp->statement[qp->opos - pt->token_len]))
3926 {
3927 qp->flags |= FLGP_SELECT_INTO;
3928 qb->flags &= ~FLGB_KEYSET_DRIVEN;
3929 qp->statement_type = STMT_TYPE_CREATE;
3930 remove_declare_cursor(qb, qp);
3931 }
3932 else if (stricmp(pt->finished_token, "join") == 0)
3933 check_join(stmt, F_OldPtr(qp), F_OldPos(qp));
3934 break;
3935 case 3:
3936 if (0 != (qp->flags & FLGP_USING_CURSOR) &&
3937 strnicmp(pt->finished_token, "for", 3) == 0)
3938 {
3939 UInt4 flg;
3940 size_t endpos;
3941
3942 flg = table_for_update_or_share(F_OldPtr(qp), &endpos);
3943 if (0 != (FLGP_SELECT_FOR_UPDATE_OR_SHARE & flg))
3944 {
3945 qp->flags |= flg;
3946 remove_declare_cursor(qb, qp);
3947 }
3948 else
3949 qp->flags |= flg;
3950 }
3951 break;
3952 case 2:
3953 {
3954 size_t endpos;
3955
3956 if (STMT_TYPE_INSERT == qp->statement_type &&
3957 strnicmp(pt->finished_token, "()", 2) == 0 &&
3958 insert_without_target(F_OldPtr(qp), &endpos))
3959 {
3960 qb->npos -= 2;
3961 CVT_APPEND_STR(qb, "DEFAULT VALUES");
3962 qp->opos += endpos;
3963 return SQL_SUCCESS;
3964 }
3965 break;
3966 }
3967 case 1:
3968 {
3969 size_t endpos;
3970
3971 if (STMT_TYPE_INSERT == qp->statement_type &&
3972 pt->finished_token[0] == '(' &&
3973 oldchar == ')' &&
3974 insert_without_target(F_OldPtr(qp)+1, &endpos))
3975 {
3976 qb->npos --;
3977 CVT_APPEND_STR(qb, " DEFAULT VALUES");
3978 qp->opos += endpos;
3979 return SQL_SUCCESS;
3980 }
3981 break;
3982 }
3983 }
3984
3985 CVT_APPEND_CHAR(qb, oldchar);
3986 return SQL_SUCCESS;
3987 }
3988 else
3989 PT_token_restart(pt, oldchar);
3990
3991 /*
3992 * It's a '?' parameter alright
3993 */
3994 retval = ResolveOneParam(qb, qp, &isnull, &isbinary, &dummy);
3995 if (retval < 0)
3996 return retval;
3997
3998 if (SQL_SUCCESS_WITH_INFO == retval) /* means discarding output parameter */
3999 {
4000 }
4001 retval = SQL_SUCCESS;
4002 cleanup:
4003 return retval;
4004 }
4005
4006 #define MIN_ALC_SIZE 128
4007
4008 /*
4009 * Build an array of parameters to pass to libpq's PQexecPrepared
4010 * function.
4011 */
4012 BOOL
build_libpq_bind_params(StatementClass * stmt,int * nParams,OID ** paramTypes,char *** paramValues,int ** paramLengths,int ** paramFormats,int * resultFormat)4013 build_libpq_bind_params(StatementClass *stmt,
4014 int *nParams,
4015 OID **paramTypes,
4016 char ***paramValues,
4017 int **paramLengths,
4018 int **paramFormats,
4019 int *resultFormat)
4020 {
4021 CSTR func = "build_libpq_bind_params";
4022 QueryBuild qb;
4023 SQLSMALLINT num_p;
4024 int i, num_params;
4025 ConnectionClass *conn = SC_get_conn(stmt);
4026 BOOL ret = FALSE, discard_output;
4027 RETCODE retval;
4028 const IPDFields *ipdopts = SC_get_IPDF(stmt);
4029
4030 *paramTypes = NULL;
4031 *paramValues = NULL;
4032 *paramLengths = NULL;
4033 *paramFormats = NULL;
4034
4035 num_params = stmt->num_params;
4036 if (num_params < 0)
4037 {
4038 PGAPI_NumParams(stmt, &num_p);
4039 num_params = num_p;
4040 }
4041 if (ipdopts->allocated < num_params)
4042 {
4043 char tmp[100];
4044
4045 if (0 == ipdopts->allocated)
4046 STRCPY_FIXED(tmp, "Parameters exist but IPD isn't set. Please call SQLDescribeParam()");
4047 else
4048 SPRINTF_FIXED(tmp, "The # of IPD parameters %d < %d the # of parameter markers", ipdopts->allocated, num_params);
4049 MYLOG(0, "%s\n", tmp);
4050 SC_set_error(stmt, STMT_COUNT_FIELD_INCORRECT, tmp, func);
4051 return FALSE;
4052 }
4053
4054 if (QB_initialize(&qb, MIN_ALC_SIZE, stmt, RPM_BUILDING_BIND_REQUEST) < 0)
4055 return FALSE;
4056
4057 if (num_params > 0)
4058 {
4059 *paramTypes = malloc(sizeof(OID) * num_params);
4060 if (*paramTypes == NULL)
4061 goto cleanup;
4062 *paramValues = malloc(sizeof(char *) * num_params);
4063 if (*paramValues == NULL)
4064 goto cleanup;
4065 memset(*paramValues, 0, sizeof(char *) * num_params);
4066 *paramLengths = malloc(sizeof(int) * num_params);
4067 if (*paramLengths == NULL)
4068 goto cleanup;
4069 *paramFormats = malloc(sizeof(int) * num_params);
4070 if (*paramFormats == NULL)
4071 goto cleanup;
4072 }
4073
4074 qb.flags |= FLGB_BINARY_AS_POSSIBLE;
4075
4076 MYLOG(DETAIL_LOG_LEVEL, "num_params=%d proc_return=%d\n", num_params, stmt->proc_return);
4077 num_p = num_params - qb.num_discard_params;
4078 MYLOG(DETAIL_LOG_LEVEL, "num_p=%d\n", num_p);
4079 discard_output = (0 != (qb.flags & FLGB_DISCARD_OUTPUT));
4080 *nParams = 0;
4081 if (num_p > 0)
4082 {
4083 ParameterImplClass *parameters = ipdopts->parameters;
4084 int pno;
4085
4086 BOOL isnull;
4087 BOOL isbinary;
4088 char *val_copy;
4089 OID pgType;
4090
4091 /*
4092 * Now build the parameter values.
4093 */
4094 for (i = 0, pno = 0; i < stmt->num_params; i++)
4095 {
4096 qb.npos = 0;
4097 retval = ResolveOneParam(&qb, NULL, &isnull, &isbinary, &pgType);
4098 if (SQL_ERROR == retval)
4099 {
4100 QB_replace_SC_error(stmt, &qb, func);
4101 ret = FALSE;
4102 goto cleanup;
4103 }
4104
4105 MYLOG(DETAIL_LOG_LEVEL, "%dth parameter type oid is %u\n", i, PIC_dsp_pgtype(conn, parameters[i]));
4106
4107 if (i < qb.proc_return)
4108 continue;
4109 if (SQL_PARAM_OUTPUT == parameters[i].paramType)
4110 {
4111 if (discard_output)
4112 continue;
4113 (*paramTypes)[pno] = PG_TYPE_VOID;
4114 (*paramValues)[pno] = NULL;
4115 (*paramLengths)[pno] = 0;
4116 (*paramFormats)[pno] = 0;
4117 pno++;
4118 continue;
4119 }
4120 if (!isnull)
4121 {
4122 val_copy = malloc(qb.npos + 1);
4123 if (!val_copy)
4124 goto cleanup;
4125 memcpy(val_copy, qb.query_statement, qb.npos);
4126 val_copy[qb.npos] = '\0';
4127
4128 (*paramTypes)[pno] = pgType;
4129 (*paramValues)[pno] = val_copy;
4130 if (qb.npos > INT_MAX)
4131 goto cleanup;
4132 (*paramLengths)[pno] = (int) qb.npos;
4133 }
4134 else
4135 {
4136 (*paramTypes)[pno] = pgType;
4137 (*paramValues)[pno] = NULL;
4138 (*paramLengths)[pno] = 0;
4139 }
4140 if (isbinary)
4141 MYLOG(0, "%dth parameter is of binary format\n", pno);
4142 (*paramFormats)[pno] = isbinary ? 1 : 0;
4143
4144 pno++;
4145 }
4146 *nParams = pno;
4147 }
4148
4149 /* result format is text */
4150 *resultFormat = 0;
4151
4152 ret = TRUE;
4153
4154 cleanup:
4155 QB_Destructor(&qb);
4156
4157 return ret;
4158 }
4159
4160
4161 /*
4162 * With SQL_MAX_NUMERIC_LEN = 16, the highest representable number is
4163 * 2^128 - 1, which fits in 39 digits.
4164 */
4165 #define MAX_NUMERIC_DIGITS 39
4166
4167 /*
4168 * Convert a SQL_NUMERIC_STRUCT into string representation.
4169 */
4170 static void
ResolveNumericParam(const SQL_NUMERIC_STRUCT * ns,char * chrform)4171 ResolveNumericParam(const SQL_NUMERIC_STRUCT *ns, char *chrform)
4172 {
4173 Int4 i, vlen, len, newlen;
4174 const UCHAR *val = (const UCHAR *) ns->val;
4175 UCHAR vals[SQL_MAX_NUMERIC_LEN];
4176 int lastnonzero;
4177 UCHAR calv[MAX_NUMERIC_DIGITS];
4178 int precision;
4179
4180 MYLOG(DETAIL_LOG_LEVEL, "C_NUMERIC [prec=%d scale=%d]", ns->precision, ns->scale);
4181
4182 if (0 == ns->precision)
4183 {
4184 if (chrform)
4185 strncpy_null(chrform, "0", 2);
4186 return;
4187 }
4188
4189 precision = ns->precision;
4190 if (precision > MAX_NUMERIC_DIGITS)
4191 precision = MAX_NUMERIC_DIGITS;
4192
4193 /*
4194 * The representation in SQL_NUMERIC_STRUCT is 16 bytes with most
4195 * significant byte first. Make a working copy.
4196 */
4197 memcpy(vals, val, SQL_MAX_NUMERIC_LEN);
4198
4199 vlen = SQL_MAX_NUMERIC_LEN;
4200 len = 0;
4201 do
4202 {
4203 UInt2 d, r;
4204
4205 /*
4206 * Divide the number by 10, and output the reminder as the next digit.
4207 *
4208 * Begin from the most-significant byte (last in the array), and at
4209 * each step, carry the remainder to the prev byte.
4210 */
4211 r = 0;
4212 lastnonzero = -1;
4213 for (i = vlen - 1; i >= 0; i--)
4214 {
4215 UInt2 v;
4216
4217 v = ((UInt2) vals[i]) + (r << 8);
4218 d = v / 10; r = v % 10;
4219 vals[i] = (UCHAR) d;
4220
4221 if (d != 0 && lastnonzero == -1)
4222 lastnonzero = i;
4223 }
4224
4225 /* output the remainder */
4226 calv[len++] = (UCHAR) r;
4227
4228 vlen = lastnonzero + 1;
4229 } while(lastnonzero >= 0 && len < precision);
4230
4231 /*
4232 * calv now contains the digits in reverse order, i.e. least significant
4233 * digit is at calv[0]
4234 */
4235
4236 MYPRINTF(DETAIL_LOG_LEVEL, " len2=%d", len);
4237
4238 /* build the final output string. */
4239 newlen = 0;
4240 if (0 == ns->sign)
4241 chrform[newlen++] = '-';
4242
4243 i = len - 1;
4244 if (i < ns->scale)
4245 i = ns->scale;
4246 for (; i >= ns->scale; i--)
4247 {
4248 if (i >= len)
4249 chrform[newlen++] = '0';
4250 else
4251 chrform[newlen++] = calv[i] + '0';
4252 }
4253 if (ns->scale > 0)
4254 {
4255 chrform[newlen++] = '.';
4256 for (; i >= 0; i--)
4257 {
4258 if (i >= len)
4259 chrform[newlen++] = '0';
4260 else
4261 chrform[newlen++] = calv[i] + '0';
4262 }
4263 }
4264 if (0 == len)
4265 chrform[newlen++] = '0';
4266 chrform[newlen] = '\0';
4267 MYLOG(DETAIL_LOG_LEVEL, " convval(2) len=%d %s\n", newlen, chrform);
4268 }
4269
4270 /*
4271 * Convert a string representation of a numeric into SQL_NUMERIC_STRUCT.
4272 */
4273 static void
parse_to_numeric_struct(const char * wv,SQL_NUMERIC_STRUCT * ns,BOOL * overflow)4274 parse_to_numeric_struct(const char *wv, SQL_NUMERIC_STRUCT *ns, BOOL *overflow)
4275 {
4276 int i, nlen, dig;
4277 char calv[SQL_MAX_NUMERIC_LEN * 3];
4278 BOOL dot_exist;
4279
4280 *overflow = FALSE;
4281
4282 /* skip leading space */
4283 while (*wv && isspace((unsigned char) *wv))
4284 wv++;
4285
4286 /* sign */
4287 ns->sign = 1;
4288 if (*wv == '-')
4289 {
4290 ns->sign = 0;
4291 wv++;
4292 }
4293 else if (*wv == '+')
4294 wv++;
4295
4296 /* skip leading zeros */
4297 while (*wv == '0')
4298 wv++;
4299
4300 /* read the digits into calv */
4301 ns->precision = 0;
4302 ns->scale = 0;
4303 for (nlen = 0, dot_exist = FALSE;; wv++)
4304 {
4305 if (*wv == '.')
4306 {
4307 if (dot_exist)
4308 break;
4309 dot_exist = TRUE;
4310 }
4311 else if (*wv == '\0' || !isdigit((unsigned char) *wv))
4312 break;
4313 else
4314 {
4315 if (nlen >= sizeof(calv))
4316 {
4317 if (dot_exist)
4318 break;
4319 else
4320 {
4321 ns->scale--;
4322 *overflow = TRUE;
4323 continue;
4324 }
4325 }
4326 if (dot_exist)
4327 ns->scale++;
4328 calv[nlen++] = *wv;
4329 }
4330 }
4331 ns->precision = nlen;
4332
4333 /* Convert the decimal digits to binary */
4334 memset(ns->val, 0, sizeof(ns->val));
4335 for (dig = 0; dig < nlen; dig++)
4336 {
4337 UInt4 carry;
4338
4339 /* multiply the current value by 10, and add the next digit */
4340 carry = calv[dig] - '0';
4341 for (i = 0; i < sizeof(ns->val); i++)
4342 {
4343 UInt4 t;
4344
4345 t = ((UInt4) ns->val[i]) * 10 + carry;
4346 ns->val[i] = (unsigned char) (t & 0xFF);
4347 carry = (t >> 8);
4348 }
4349
4350 if (carry != 0)
4351 *overflow = TRUE;
4352 }
4353 }
4354
4355 static BOOL
parameter_is_with_cast(const QueryParse * qp)4356 parameter_is_with_cast(const QueryParse *qp)
4357 {
4358 const char *str = F_OldPtr(qp);
4359
4360 if ('?' != *str) return FALSE;
4361 while (isspace(*(++str))) ;
4362 if (strncmp(str, "::", 2) == 0)
4363 return TRUE;
4364 if (strnicmp(str, "as", 2) != 0)
4365 return FALSE;
4366 if (isspace(str[2]))
4367 return TRUE;
4368 return FALSE;
4369 }
4370
4371 #ifdef UNICODE_SUPPORT
4372 enum {
4373 ErrorOutConversionErrors /* error out conversion errors */
4374 , ReturnZeroLengthString /* simply returns zero length strings */
4375 };
4376 static int convert_err_flag =
4377 #ifdef WIN32
4378 ReturnZeroLengthString;
4379 #else
4380 ErrorOutConversionErrors;
4381 #endif /* WIN32 */
4382
4383 static BOOL
handle_lu_onvert_error(QueryBuild * qb,int flag,char * buffer,SQLLEN paralen)4384 handle_lu_onvert_error(QueryBuild *qb, int flag, char *buffer, SQLLEN paralen)
4385 {
4386 int blen = paralen;
4387
4388 if (!buffer)
4389 return FALSE;
4390 if (get_mylog() > 0 || ReturnZeroLengthString != flag)
4391 {
4392 const UCHAR *buf = (UCHAR *) buffer;
4393 int i;
4394 PQExpBufferData pbuf = {0};
4395
4396 if (SQL_NTS == blen)
4397 blen = strlen(buffer);
4398 initPQExpBuffer(&pbuf);
4399 appendPQExpBuffer(&pbuf, "Could not convert the current data '");
4400 for (i = 0; i < blen; i++)
4401 {
4402 if (buf[i] >= 0x80)
4403 appendPQExpBuffer(&pbuf, "\\%03o", buf[i]);
4404 else if ('\\' == buf[i])
4405 appendPQExpBuffer(&pbuf, "\\\\");
4406 else
4407 appendPQExpBuffer(&pbuf, "%c", buf[i]);
4408 }
4409 appendPQExpBuffer(&pbuf, "' to wide chars");
4410 MYLOG(0, "%s\n", pbuf.data);
4411 if (ReturnZeroLengthString != flag)
4412 {
4413 if (qb->stmt)
4414 SC_set_error(qb->stmt, STMT_EXEC_ERROR, pbuf.data, __FUNCTION__);
4415 else
4416 qb->errormsg = "could not convert the current data to wide chars";
4417 }
4418 termPQExpBuffer(&pbuf);
4419
4420 }
4421 switch (flag)
4422 {
4423 case ReturnZeroLengthString:
4424 if (qb->stmt)
4425 SC_set_error(qb->stmt, STMT_ERROR_IN_ROW, "conversion error to wide chars occured", __FUNCTION__);
4426 return TRUE;
4427 default:
4428 qb->errornumber = STMT_EXEC_ERROR;
4429 return FALSE;
4430 }
4431 }
4432 #endif /* UNICODE_SUPPORT */
4433
4434 /*
4435 * Resolve one parameter.
4436 *
4437 * *isnull is set to TRUE if it was NULL.
4438 * *isbinary is set to TRUE, if the binary output format was used. (binary
4439 * output is only produced if the FLGB_BINARY_AS_POSSIBLE flag is set)
4440 * *pgType is set to the PostgreSQL type OID that should be used when binding
4441 * (or 0, to let the server decide)
4442 */
4443 static int
ResolveOneParam(QueryBuild * qb,QueryParse * qp,BOOL * isnull,BOOL * isbinary,OID * pgType)4444 ResolveOneParam(QueryBuild *qb, QueryParse *qp, BOOL *isnull, BOOL *isbinary,
4445 OID *pgType)
4446 {
4447 ConnectionClass *conn = qb->conn;
4448 const APDFields *apdopts = qb->apdopts;
4449 const IPDFields *ipdopts = qb->ipdopts;
4450 PutDataInfo *pdata = qb->pdata;
4451
4452 int param_number;
4453 char param_string[150],
4454 tmp[256];
4455 char cbuf[PG_NUMERIC_MAX_PRECISION * 2]; /* seems big enough to handle the data in this function */
4456 OID param_pgtype;
4457 SQLSMALLINT param_ctype, param_sqltype;
4458 SIMPLE_TIME st;
4459 struct tm *tim;
4460 SQLLEN used;
4461 const char *send_buf;
4462
4463 char *buffer, *allocbuf = NULL, *lastadd = NULL;
4464 OID lobj_oid;
4465 int lobj_fd;
4466 SQLULEN offset = apdopts->param_offset_ptr ? *apdopts->param_offset_ptr : 0;
4467 size_t current_row = qb->current_row;
4468 BOOL handling_large_object = FALSE, req_bind;
4469 BOOL need_quotes = TRUE;
4470 BOOL add_parens = FALSE;
4471 BOOL negative;
4472 ParameterInfoClass *apara;
4473 ParameterImplClass *ipara;
4474 BOOL outputDiscard,
4475 valueOutput;
4476 SDOUBLE dbv;
4477 SFLOAT flv;
4478 SQL_INTERVAL_STRUCT *ivstruct;
4479 const char *ivsign;
4480 BOOL final_binary_convert = FALSE;
4481 RETCODE retval = SQL_ERROR;
4482
4483 *isnull = FALSE;
4484 *isbinary = FALSE;
4485 *pgType = 0;
4486
4487 outputDiscard = (0 != (qb->flags & FLGB_DISCARD_OUTPUT));
4488 valueOutput = (qb->param_mode != RPM_FAKE_PARAMS &&
4489 qb->param_mode != RPM_BUILDING_PREPARE_STATEMENT);
4490 req_bind = (qb->param_mode == RPM_BUILDING_BIND_REQUEST);
4491
4492 if (qb->proc_return < 0 && qb->stmt)
4493 qb->proc_return = qb->stmt->proc_return;
4494 /*
4495 * It's a '?' parameter alright
4496 */
4497 param_number = ++qb->param_number;
4498
4499 MYLOG(DETAIL_LOG_LEVEL, "para:%d(%d,%d)\n", param_number, ipdopts->allocated, apdopts->allocated);
4500 apara = NULL;
4501 ipara = NULL;
4502 if (param_number < apdopts->allocated)
4503 apara = apdopts->parameters + param_number;
4504 if (param_number < ipdopts->allocated)
4505 ipara = ipdopts->parameters + param_number;
4506 if ((!apara || !ipara) && valueOutput)
4507 {
4508 MYLOG(0, "The # of (A|I)PD parameters (%d, %d) < %d the # of parameter markers\n", apdopts->allocated, ipdopts->allocated, param_number);
4509 qb->errormsg = "The # of binded parameters < the # of parameter markers";
4510 qb->errornumber = STMT_COUNT_FIELD_INCORRECT;
4511 CVT_TERMINATE(qb); /* just in case */
4512 return SQL_ERROR;
4513 }
4514
4515 MYLOG(DETAIL_LOG_LEVEL, "ipara=%p paramName=%s paramType=%d %d proc_return=%d\n", ipara, ipara ? PRINT_NAME(ipara->paramName) : PRINT_NULL, ipara ? ipara->paramType : -1, PG_VERSION_LT(conn, 8.1), qb->proc_return);
4516 if (param_number < qb->proc_return)
4517 {
4518 if (ipara && SQL_PARAM_OUTPUT != ipara->paramType)
4519 {
4520 qb->errormsg = "The function return value isn't marked as output parameter";
4521 qb->errornumber = STMT_EXEC_ERROR;
4522 CVT_TERMINATE(qb); /* just in case */
4523 return SQL_ERROR;
4524 }
4525 return SQL_SUCCESS;
4526 }
4527 if (ipara && SQL_PARAM_OUTPUT == ipara->paramType)
4528 {
4529 if (PG_VERSION_LT(conn, 8.1))
4530 {
4531 qb->errormsg = "Output parameter isn't available before 8.1 version";
4532 qb->errornumber = STMT_INTERNAL_ERROR;
4533 CVT_TERMINATE(qb); /* just in case */
4534 return SQL_ERROR;
4535 }
4536 if (outputDiscard)
4537 {
4538 ssize_t npos = 0;
4539
4540 for (npos = qb->npos - 1; npos >= 0 && isspace((unsigned char) qb->query_statement[npos]) ; npos--) ;
4541 if (npos >= 0)
4542 {
4543 switch (qb->query_statement[npos])
4544 {
4545 case ',':
4546 qb->npos = npos;
4547 qb->query_statement[npos] = '\0';
4548 break;
4549 case '(':
4550 if (!qp)
4551 break;
4552 for (npos = qp->opos + 1; isspace((unsigned char) qp->statement[npos]); npos++) ;
4553 if (qp->statement[npos] == ',')
4554 qp->opos = npos;
4555 break;
4556 }
4557 }
4558 return SQL_SUCCESS_WITH_INFO;
4559 }
4560 }
4561 else
4562 {
4563 /* For procedures, use named notation if a parameter name is specified */
4564 if (!req_bind && ipara && NAME_IS_VALID(ipara->paramName) &&
4565 qp && qp->statement_type == STMT_TYPE_PROCCALL)
4566 {
4567 char named_notation[COLUMN_NAME_STORAGE_LEN + 7];
4568 SPRINTF_FIXED(named_notation, "\"%s\" := ", GET_NAME(ipara->paramName));
4569 CVT_APPEND_STR(qb, named_notation);
4570 }
4571 }
4572
4573 if ((!apara || !ipara) && qb->param_mode == RPM_FAKE_PARAMS)
4574 {
4575 CVT_APPEND_STR(qb, "NULL");
4576 qb->flags |= FLGB_INACCURATE_RESULT;
4577 return SQL_SUCCESS;
4578 }
4579 if (qb->param_mode == RPM_BUILDING_PREPARE_STATEMENT)
4580 {
4581 char pnum[16];
4582
4583 qb->dollar_number++;
4584 if (ipara &&
4585 SQL_PARAM_OUTPUT != ipara->paramType &&
4586 (qb->flags & FLGB_PARAM_CAST) != 0 &&
4587 !parameter_is_with_cast(qp))
4588 SPRINTF_FIXED(pnum, "$%d%s", qb->dollar_number, sqltype_to_pgcast(conn, ipara->SQLType));
4589 else
4590 SPRINTF_FIXED(pnum, "$%d", qb->dollar_number);
4591 CVT_APPEND_STR(qb, pnum);
4592 return SQL_SUCCESS;
4593 }
4594 /*
4595 * After this point, we can assume apara and ipara to be set. The only
4596 * cases where we allow them to be NULL is when param_mode is
4597 * RPM_FAKE_PARAMS or RPM_BUILDING_PREPARE_STATEMENT, and we've now handled
4598 * those cases.
4599 */
4600
4601 /* Assign correct buffers based on data at exec param or not */
4602 if (apara->data_at_exec)
4603 {
4604 if (pdata->allocated != apdopts->allocated)
4605 extend_putdata_info(pdata, apdopts->allocated, TRUE);
4606 used = pdata->pdata[param_number].EXEC_used ? *pdata->pdata[param_number].EXEC_used : SQL_NTS;
4607 buffer = pdata->pdata[param_number].EXEC_buffer;
4608 if (pdata->pdata[param_number].lobj_oid)
4609 handling_large_object = TRUE;
4610 }
4611 else
4612 {
4613 UInt4 bind_size = apdopts->param_bind_type;
4614 UInt4 ctypelen;
4615 BOOL bSetUsed = FALSE;
4616
4617 buffer = apara->buffer + offset;
4618 if (current_row > 0)
4619 {
4620 if (bind_size > 0)
4621 buffer += (bind_size * current_row);
4622 else if (ctypelen = ctype_length(apara->CType), ctypelen > 0)
4623 buffer += current_row * ctypelen;
4624 else
4625 buffer += current_row * apara->buflen;
4626 }
4627 if (apara->used || apara->indicator)
4628 {
4629 SQLULEN p_offset;
4630
4631 if (bind_size > 0)
4632 p_offset = offset + bind_size * current_row;
4633 else
4634 p_offset = offset + sizeof(SQLLEN) * current_row;
4635 if (apara->indicator)
4636 {
4637 used = *LENADDR_SHIFT(apara->indicator, p_offset);
4638 if (SQL_NULL_DATA == used)
4639 bSetUsed = TRUE;
4640 }
4641 if (!bSetUsed && apara->used)
4642 {
4643 used = *LENADDR_SHIFT(apara->used, p_offset);
4644 bSetUsed = TRUE;
4645 }
4646 }
4647 if (!bSetUsed)
4648 used = SQL_NTS;
4649 }
4650
4651 /* Handle DEFAULT_PARAM parameter data. Should be NULL ?
4652 if (used == SQL_DEFAULT_PARAM)
4653 {
4654 return SQL_SUCCESS;
4655 } */
4656
4657 param_ctype = apara->CType;
4658 param_sqltype = ipara->SQLType;
4659 param_pgtype = PIC_dsp_pgtype(qb->conn, *ipara);
4660
4661 /* XXX: should we use param_pgtype here instead? */
4662 *pgType = sqltype_to_bind_pgtype(conn, param_sqltype);
4663
4664 if (0 == param_sqltype) /* calling SQLSetStmtAttr(.., SQL_ATTR_APP_PARAM_DES, an ARD of another statement) may cause this */
4665 {
4666 if (0 != param_pgtype)
4667 {
4668 param_sqltype = pgtype_attr_to_concise_type(conn, param_pgtype, PG_ATP_UNSET, PG_ADT_UNSET, PG_UNKNOWNS_UNSET);
4669 MYLOG(0, "convert from pgtype(%u) to sqltype(%d)\n", param_pgtype, param_sqltype);
4670 }
4671 }
4672
4673 MYLOG(0, "from(fcType)=%d, to(fSqlType)=%d(%u), *pgType=%u\n",
4674 param_ctype, param_sqltype, param_pgtype, *pgType);
4675
4676 /* Handle NULL parameter data */
4677 if (SQL_PARAM_OUTPUT == ipara->paramType
4678 || used == SQL_NULL_DATA
4679 || used == SQL_DEFAULT_PARAM)
4680 {
4681 *isnull = TRUE;
4682 if (!req_bind)
4683 CVT_APPEND_STR(qb, "NULL");
4684 return SQL_SUCCESS;
4685 }
4686
4687 /*
4688 * If no buffer, and it's not null, then what the hell is it? Just
4689 * leave it alone then.
4690 */
4691 if (!buffer)
4692 {
4693 if (qb->param_mode == RPM_FAKE_PARAMS)
4694 {
4695 CVT_APPEND_STR(qb, "NULL");
4696 qb->flags |= FLGB_INACCURATE_RESULT;
4697 return SQL_SUCCESS;
4698 }
4699 else if (!handling_large_object)
4700 {
4701 /* shouldn't happen */
4702 qb->errormsg = "unexpected NULL parameter value";
4703 qb->errornumber = STMT_EXEC_ERROR;
4704 return SQL_ERROR;
4705 }
4706 }
4707
4708 /* replace DEFAULT with something we can use */
4709 if (param_ctype == SQL_C_DEFAULT)
4710 {
4711 param_ctype = sqltype_to_default_ctype(conn, param_sqltype);
4712 #ifdef UNICODE_SUPPORT
4713 if (param_ctype == SQL_C_WCHAR
4714 && CC_default_is_c(conn))
4715 param_ctype =SQL_C_CHAR;
4716 #endif
4717 }
4718
4719 allocbuf = NULL;
4720 send_buf = NULL;
4721 param_string[0] = '\0';
4722 cbuf[0] = '\0';
4723 memset(&st, 0, sizeof(st));
4724
4725 ivstruct = (SQL_INTERVAL_STRUCT *) buffer;
4726 /* Convert input C type to a neutral format */
4727 #ifdef UNICODE_SUPPORT
4728 if (get_convtype() > 0) /* coversion between the current locale is available */
4729 {
4730 BOOL wcs_debug = conn->connInfo.wcs_debug;
4731 BOOL is_utf8 = (UTF8 == conn->ccsc);
4732 BOOL same_encoding = (conn->ccsc == pg_CS_code(conn->locale_encoding));
4733
4734 switch (param_ctype)
4735 {
4736 case SQL_C_CHAR:
4737 if (!same_encoding || wcs_debug)
4738 {
4739 SQLLEN paralen = used;
4740
4741 MYLOG(0, "locale param convert\n");
4742 if ((used = bindpara_msg_to_utf8(buffer, &allocbuf, used)) < 0)
4743 {
4744 if (!handle_lu_onvert_error(qb, convert_err_flag, buffer, paralen))
4745 goto cleanup;
4746 send_buf = NULL_STRING;
4747 used = 0;
4748 }
4749 else
4750 send_buf = allocbuf;
4751 }
4752 break;
4753 case SQL_C_WCHAR:
4754 if (!is_utf8 || (same_encoding && wcs_debug))
4755 {
4756 MYLOG(0, "hybrid param convert\n");
4757 if ((used = bindpara_wchar_to_msg((SQLWCHAR *) buffer, &allocbuf, used)) < 0)
4758 {
4759 qb->errormsg = "Could not convert from wide characters to the current locale";
4760 qb->errornumber = STMT_EXEC_ERROR;
4761 goto cleanup;
4762 }
4763 send_buf = allocbuf;
4764 }
4765 break;
4766 }
4767 }
4768 #endif /* UNICODE_SUPPORT */
4769 switch (param_ctype)
4770 {
4771 case SQL_C_BINARY:
4772 send_buf = buffer;
4773 break;
4774 case SQL_C_CHAR:
4775 if (NULL == send_buf)
4776 send_buf = buffer;
4777 break;
4778
4779 #ifdef UNICODE_SUPPORT
4780 case SQL_C_WCHAR:
4781 MYLOG(0, " C_WCHAR=%d contents=%s(" FORMAT_LEN ")\n", param_ctype, buffer, used);
4782 if (NULL == send_buf)
4783 {
4784 allocbuf = ucs2_to_utf8((SQLWCHAR *) buffer, used > 0 ? used / WCLEN : used, &used, FALSE);
4785 send_buf = allocbuf;
4786 }
4787 break;
4788 #endif /* UNICODE_SUPPORT */
4789
4790 case SQL_C_DOUBLE:
4791 dbv = *((SDOUBLE *) buffer);
4792 #ifdef WIN32
4793 if (_finite(dbv))
4794 #endif /* WIN32 */
4795 {
4796 SPRINTF_FIXED(param_string, "%.*g", PG_DOUBLE_DIGITS, dbv);
4797 set_server_decimal_point(param_string, SQL_NTS);
4798 }
4799 #ifdef WIN32
4800 else if (_isnan(dbv))
4801 STRCPY_FIXED(param_string, NAN_STRING);
4802 else if (dbv < .0)
4803 STRCPY_FIXED(param_string, MINFINITY_STRING);
4804 else
4805 STRCPY_FIXED(param_string, INFINITY_STRING);
4806 #endif /* WIN32 */
4807 break;
4808
4809 case SQL_C_FLOAT:
4810 flv = *((SFLOAT *) buffer);
4811 #ifdef WIN32
4812 if (_finite(flv))
4813 #endif /* WIN32 */
4814 {
4815 SPRINTF_FIXED(param_string, "%.*g", PG_REAL_DIGITS, flv);
4816 set_server_decimal_point(param_string, SQL_NTS);
4817 }
4818 #ifdef WIN32
4819 else if (_isnan(flv))
4820 STRCPY_FIXED(param_string, NAN_STRING);
4821 else if (flv < .0)
4822 STRCPY_FIXED(param_string, MINFINITY_STRING);
4823 else
4824 STRCPY_FIXED(param_string, INFINITY_STRING);
4825 #endif /* WIN32 */
4826 break;
4827
4828 case SQL_C_SLONG:
4829 case SQL_C_LONG:
4830 SPRINTF_FIXED(param_string, FORMAT_INTEGER,
4831 *((SQLINTEGER *) buffer));
4832 break;
4833
4834 #ifdef ODBCINT64
4835 case SQL_C_SBIGINT:
4836 case SQL_BIGINT: /* Is this needed ? */
4837 SPRINTF_FIXED(param_string, FORMATI64,
4838 *((SQLBIGINT *) buffer));
4839 break;
4840
4841 case SQL_C_UBIGINT:
4842 SPRINTF_FIXED(param_string, FORMATI64U,
4843 *((SQLUBIGINT *) buffer));
4844 break;
4845
4846 #endif /* ODBCINT64 */
4847 case SQL_C_SSHORT:
4848 case SQL_C_SHORT:
4849 ITOA_FIXED(param_string, *((SQLSMALLINT *) buffer));
4850 break;
4851
4852 case SQL_C_STINYINT:
4853 case SQL_C_TINYINT:
4854 ITOA_FIXED(param_string, *((SCHAR *) buffer));
4855 break;
4856
4857 case SQL_C_ULONG:
4858 SPRINTF_FIXED(param_string, FORMAT_UINTEGER,
4859 *((SQLUINTEGER *) buffer));
4860 break;
4861
4862 case SQL_C_USHORT:
4863 SPRINTF_FIXED(param_string, "%u",
4864 *((SQLUSMALLINT *) buffer));
4865 break;
4866
4867 case SQL_C_UTINYINT:
4868 SPRINTF_FIXED(param_string, "%u",
4869 *((UCHAR *) buffer));
4870 break;
4871
4872 case SQL_C_BIT:
4873 {
4874 int i = *((UCHAR *) buffer);
4875
4876 ITOA_FIXED(param_string, i ? 1 : 0);
4877 break;
4878 }
4879
4880 case SQL_C_DATE:
4881 case SQL_C_TYPE_DATE: /* 91 */
4882 {
4883 DATE_STRUCT *ds = (DATE_STRUCT *) buffer;
4884
4885 st.m = ds->month;
4886 st.d = ds->day;
4887 st.y = ds->year;
4888
4889 break;
4890 }
4891
4892 case SQL_C_TIME:
4893 case SQL_C_TYPE_TIME: /* 92 */
4894 {
4895 TIME_STRUCT *ts = (TIME_STRUCT *) buffer;
4896
4897 st.hh = ts->hour;
4898 st.mm = ts->minute;
4899 st.ss = ts->second;
4900 /*
4901 * Initialize date in case conversion destination
4902 * expects date part from this source time data.
4903 */
4904 tim = SC_get_localtime(qb->stmt);
4905 st.m = tim->tm_mon + 1;
4906 st.d = tim->tm_mday;
4907 st.y = tim->tm_year + 1900;
4908 break;
4909 }
4910
4911 case SQL_C_TIMESTAMP:
4912 case SQL_C_TYPE_TIMESTAMP: /* 93 */
4913 {
4914 TIMESTAMP_STRUCT *tss = (TIMESTAMP_STRUCT *) buffer;
4915
4916 st.m = tss->month;
4917 st.d = tss->day;
4918 st.y = tss->year;
4919 st.hh = tss->hour;
4920 st.mm = tss->minute;
4921 st.ss = tss->second;
4922 st.fr = tss->fraction;
4923
4924 MYLOG(0, "m=%d,d=%d,y=%d,hh=%d,mm=%d,ss=%d\n", st.m, st.d, st.y, st.hh, st.mm, st.ss);
4925
4926 break;
4927
4928 }
4929 case SQL_C_NUMERIC:
4930 {
4931 ResolveNumericParam((SQL_NUMERIC_STRUCT *) buffer, param_string);
4932 break;
4933 }
4934 case SQL_C_INTERVAL_YEAR:
4935 ivsign = ivstruct->interval_sign ? "-" : "";
4936 SPRINTF_FIXED(param_string, "%s%u years", ivsign, (unsigned int) ivstruct->intval.year_month.year);
4937 break;
4938 case SQL_C_INTERVAL_MONTH:
4939 case SQL_C_INTERVAL_YEAR_TO_MONTH:
4940 ivsign = ivstruct->interval_sign ? "-" : "";
4941 SPRINTF_FIXED(param_string, "%s%u years %s%u mons", ivsign, (unsigned int) ivstruct->intval.year_month.year, ivsign, (unsigned int) ivstruct->intval.year_month.month);
4942 break;
4943 case SQL_C_INTERVAL_DAY:
4944 ivsign = ivstruct->interval_sign ? "-" : "";
4945 SPRINTF_FIXED(param_string, "%s%u days", ivsign, (unsigned int) ivstruct->intval.day_second.day);
4946 break;
4947 case SQL_C_INTERVAL_HOUR:
4948 case SQL_C_INTERVAL_DAY_TO_HOUR:
4949 ivsign = ivstruct->interval_sign ? "-" : "";
4950 SPRINTF_FIXED(param_string, "%s%u days %s%02u:00:00", ivsign, (unsigned int) ivstruct->intval.day_second.day, ivsign, (unsigned int) ivstruct->intval.day_second.hour);
4951 break;
4952 case SQL_C_INTERVAL_MINUTE:
4953 case SQL_C_INTERVAL_HOUR_TO_MINUTE:
4954 case SQL_C_INTERVAL_DAY_TO_MINUTE:
4955 ivsign = ivstruct->interval_sign ? "-" : "";
4956 SPRINTF_FIXED(param_string, "%s%u days %s%02u:%02u:00", ivsign, (unsigned int) ivstruct->intval.day_second.day, ivsign, (unsigned int) ivstruct->intval.day_second.hour, (unsigned int) ivstruct->intval.day_second.minute);
4957 break;
4958
4959 case SQL_C_INTERVAL_SECOND:
4960 case SQL_C_INTERVAL_DAY_TO_SECOND:
4961 case SQL_C_INTERVAL_HOUR_TO_SECOND:
4962 case SQL_C_INTERVAL_MINUTE_TO_SECOND:
4963 ivsign = ivstruct->interval_sign ? "-" : "";
4964 SPRINTF_FIXED(param_string, "%s%u days %s%02u:%02u:%02u",
4965 ivsign, (unsigned int) ivstruct->intval.day_second.day,
4966 ivsign, (unsigned int) ivstruct->intval.day_second.hour,
4967 (unsigned int) ivstruct->intval.day_second.minute,
4968 (unsigned int) ivstruct->intval.day_second.second);
4969 if (ivstruct->intval.day_second.fraction > 0)
4970 {
4971 int fraction = ivstruct->intval.day_second.fraction, prec = apara->precision;
4972
4973 while (fraction % 10 == 0)
4974 {
4975 fraction /= 10;
4976 prec--;
4977 }
4978 SPRINTFCAT_FIXED(param_string, ".%0*d", prec, fraction);
4979 }
4980 break;
4981 case SQL_C_GUID:
4982 {
4983 /*
4984 * SQLGUID.Data1 is an "unsigned long" on some platforms, and
4985 * "unsigned int" on others.
4986 */
4987 SQLGUID *g = (SQLGUID *) buffer;
4988 SPRINTF_FIXED (param_string,
4989 "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
4990 (unsigned int) g->Data1,
4991 g->Data2, g->Data3,
4992 g->Data4[0], g->Data4[1], g->Data4[2], g->Data4[3],
4993 g->Data4[4], g->Data4[5], g->Data4[6], g->Data4[7]);
4994 }
4995 break;
4996 default:
4997 /* error */
4998 qb->errormsg = "Unrecognized C_parameter type in copy_statement_with_parameters";
4999 qb->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
5000 CVT_TERMINATE(qb); /* just in case */
5001 goto cleanup;
5002 }
5003
5004 /*
5005 * Now that the input data is in a neutral format, convert it to
5006 * the desired output format (sqltype)
5007 */
5008
5009 /* Special handling NULL string For FOXPRO */
5010 MYLOG(0, "cvt_null_date_string=%d pgtype=%d send_buf=%p\n", conn->connInfo.cvt_null_date_string, param_pgtype, send_buf);
5011 if (conn->connInfo.cvt_null_date_string > 0 &&
5012 (PG_TYPE_DATE == param_pgtype ||
5013 PG_TYPE_DATETIME == param_pgtype ||
5014 PG_TYPE_TIMESTAMP_NO_TMZONE == param_pgtype) &&
5015 NULL != send_buf &&
5016 (
5017 (SQL_C_CHAR == param_ctype && '\0' == send_buf[0])
5018 #ifdef UNICODE_SUPPORT
5019 || (SQL_C_WCHAR ==param_ctype && '\0' == send_buf[0] && '\0' == send_buf[1])
5020 #endif /* UNICODE_SUPPORT */
5021 ))
5022 {
5023 *isnull = TRUE;
5024 if (!req_bind)
5025 CVT_APPEND_STR(qb, "NULL");
5026 retval = SQL_SUCCESS;
5027 goto cleanup;
5028 }
5029
5030 /*
5031 * We now have the value we want to print in one of these three canonical
5032 * formats:
5033 *
5034 * 1. As a string in 'send_buf', with length indicated by 'used' (can be
5035 * SQL_NTS).
5036 * 2. As a null-terminated string in 'param_string'.
5037 * 3. Time-related fields in 'st'.
5038 */
5039
5040 /*
5041 * For simplicity, fold the param_string representation into 'send_buf'.
5042 */
5043 if (!send_buf && param_string[0])
5044 {
5045 send_buf = param_string;
5046 used = SQL_NTS;
5047 }
5048
5049 /*
5050 * Do some further processing to create the final string we want to output.
5051 * This will use the fields in 'st' to create a string if it's a time/date
5052 * value, and do some other conversions.
5053 */
5054 switch (param_sqltype)
5055 {
5056 case SQL_CHAR:
5057 case SQL_VARCHAR:
5058 case SQL_LONGVARCHAR:
5059 #ifdef UNICODE_SUPPORT
5060 case SQL_WCHAR:
5061 case SQL_WVARCHAR:
5062 case SQL_WLONGVARCHAR:
5063 #endif /* UNICODE_SUPPORT */
5064 case SQL_BIT:
5065
5066 /* Special handling for some column types */
5067 switch (param_pgtype)
5068 {
5069 case PG_TYPE_BOOL:
5070 /*
5071 * consider True is -1 case.
5072 *
5073 * FIXME: This actually matches anything that begins
5074 * with -1, like "-1234" or "-1foobar". Is that
5075 * intentional?
5076 */
5077 if (NULL != send_buf && '-' == send_buf[0] && '1' == send_buf[1])
5078 {
5079 send_buf = "1";
5080 used = 1;
5081 }
5082 break;
5083 case PG_TYPE_FLOAT4:
5084 case PG_TYPE_FLOAT8:
5085 case PG_TYPE_NUMERIC:
5086 if (NULL != send_buf)
5087 set_server_decimal_point((char *) send_buf, used);
5088 break;
5089 }
5090 if (!send_buf)
5091 {
5092 /* it was date,time,timestamp -- use m,d,y,hh,mm,ss */
5093 SPRINTF_FIXED(tmp, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
5094 st.y, st.m, st.d, st.hh, st.mm, st.ss);
5095 send_buf = tmp;
5096 used = SQL_NTS;
5097 }
5098 break;
5099
5100 case SQL_DATE:
5101 case SQL_TYPE_DATE: /* 91 */
5102 if (send_buf)
5103 { /* copy char data to time */
5104 my_strcpy(cbuf, sizeof(cbuf), send_buf, used);
5105 parse_datetime(cbuf, &st);
5106 }
5107
5108 if (st.y < 0)
5109 SPRINTF_FIXED(tmp, "%.4d-%.2d-%.2d BC", -st.y, st.m, st.d);
5110 else
5111 SPRINTF_FIXED(tmp, "%.4d-%.2d-%.2d", st.y, st.m, st.d);
5112 lastadd = "::date";
5113 send_buf = tmp;
5114 used = SQL_NTS;
5115 break;
5116
5117 case SQL_TIME:
5118 case SQL_TYPE_TIME: /* 92 */
5119 if (send_buf)
5120 { /* copy char data to time */
5121 my_strcpy(cbuf, sizeof(cbuf), send_buf, used);
5122 parse_datetime(cbuf, &st);
5123 }
5124
5125 if (st.fr > 0)
5126 {
5127 int wdt;
5128 int fr = effective_fraction(st.fr, &wdt);
5129 SPRINTF_FIXED(tmp, "%.2d:%.2d:%.2d.%0*d", st.hh, st.mm, st.ss, wdt, fr);
5130 }
5131 else
5132 SPRINTF_FIXED(tmp, "%.2d:%.2d:%.2d", st.hh, st.mm, st.ss);
5133 lastadd = "::time";
5134 send_buf = tmp;
5135 used = SQL_NTS;
5136 break;
5137
5138 case SQL_TIMESTAMP:
5139 case SQL_TYPE_TIMESTAMP: /* 93 */
5140 if (send_buf)
5141 {
5142 my_strcpy(cbuf, sizeof(cbuf), send_buf, used);
5143 parse_datetime(cbuf, &st);
5144 }
5145
5146 /*
5147 * SPRINTF_FIXED(tmp, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d", st.y,
5148 * st.m, st.d, st.hh, st.mm, st.ss);
5149 */
5150 /* Time zone stuff is unreliable */
5151 stime2timestamp(&st, tmp, sizeof(tmp), USE_ZONE, 6);
5152 lastadd = "::timestamp";
5153 send_buf = tmp;
5154 used = SQL_NTS;
5155 break;
5156
5157 case SQL_BINARY:
5158 case SQL_VARBINARY:
5159 case SQL_LONGVARBINARY:
5160 switch (param_ctype)
5161 {
5162 case SQL_C_BINARY:
5163 break;
5164 case SQL_C_CHAR:
5165 #ifdef UNICODE_SUPPORT
5166 case SQL_C_WCHAR:
5167 #endif /* UNICODE_SUPPORT */
5168 switch (used)
5169 {
5170 case SQL_NTS:
5171 used = strlen(send_buf);
5172 break;
5173 }
5174 allocbuf = malloc(used / 2 + 1);
5175 if (allocbuf)
5176 {
5177 pg_hex2bin(send_buf, allocbuf, used);
5178 send_buf = allocbuf;
5179 used /= 2;
5180 }
5181 break;
5182 default:
5183 qb->errormsg = "Could not convert the ctype to binary type";
5184 qb->errornumber = STMT_EXEC_ERROR;
5185 goto cleanup;
5186 }
5187 if (param_pgtype == PG_TYPE_BYTEA)
5188 {
5189 if (0 != (qb->flags & FLGB_BINARY_AS_POSSIBLE))
5190 {
5191 MYLOG(0, "sending binary data leng=" FORMAT_LEN "\n", used);
5192 *isbinary = TRUE;
5193 }
5194 else
5195 {
5196 /* non-ascii characters should be
5197 * converted to octal
5198 */
5199 MYLOG(0, "SQL_VARBINARY: about to call convert_to_pgbinary, used = " FORMAT_LEN "\n", used);
5200 final_binary_convert = TRUE;
5201 }
5202 break;
5203 }
5204 if (PG_TYPE_OID == param_pgtype && conn->lo_is_domain)
5205 ;
5206 else if (param_pgtype != conn->lobj_type)
5207 {
5208 qb->errormsg = "Could not convert binary other than LO type";
5209 qb->errornumber = STMT_EXEC_ERROR;
5210 goto cleanup;
5211 }
5212
5213 if (apara->data_at_exec)
5214 lobj_oid = pdata->pdata[param_number].lobj_oid;
5215 else
5216 {
5217 BOOL is_in_trans_at_entry = CC_is_in_trans(conn);
5218 int write_result;
5219
5220 /* begin transaction if needed */
5221 if (!is_in_trans_at_entry)
5222 {
5223 if (!CC_begin(conn))
5224 {
5225 qb->errormsg = "Could not begin (in-line) a transaction";
5226 qb->errornumber = STMT_EXEC_ERROR;
5227 goto cleanup;
5228 }
5229 }
5230
5231 /* store the oid */
5232 lobj_oid = odbc_lo_creat(conn, INV_READ | INV_WRITE);
5233 if (lobj_oid == 0)
5234 {
5235 qb->errornumber = STMT_EXEC_ERROR;
5236 qb->errormsg = "Couldn't create (in-line) large object.";
5237 goto cleanup;
5238 }
5239
5240 /* store the fd */
5241 lobj_fd = odbc_lo_open(conn, lobj_oid, INV_WRITE);
5242 if (lobj_fd < 0)
5243 {
5244 qb->errornumber = STMT_EXEC_ERROR;
5245 qb->errormsg = "Couldn't open (in-line) large object for writing.";
5246 goto cleanup;
5247 }
5248
5249 write_result = odbc_lo_write(conn, lobj_fd, buffer, (Int4) used);
5250 if (write_result < 0)
5251 {
5252 qb->errornumber = STMT_EXEC_ERROR;
5253 qb->errormsg = "Couldn't write to (in-line) large object.";
5254 goto cleanup;
5255 }
5256
5257 odbc_lo_close(conn, lobj_fd);
5258
5259 /* commit transaction if needed */
5260 if (!is_in_trans_at_entry)
5261 {
5262 if (!CC_commit(conn))
5263 {
5264 qb->errormsg = "Could not commit (in-line) a transaction";
5265 qb->errornumber = STMT_EXEC_ERROR;
5266 goto cleanup;
5267 }
5268 }
5269 }
5270
5271 /*
5272 * the oid of the large object -- just put that in for the
5273 * parameter marker -- the data has already been sent to
5274 * the large object
5275 */
5276 SPRINTF_FIXED(param_string, "%u", lobj_oid);
5277 lastadd = "::lo";
5278 send_buf = param_string;
5279 used = SQL_NTS;
5280 break;
5281
5282 /*
5283 * because of no conversion operator for bool and int4,
5284 * SQL_BIT
5285 */
5286 /* must be quoted (0 or 1 is ok to use inside the quotes) */
5287
5288 case SQL_REAL:
5289 set_server_decimal_point((char *) send_buf, used);
5290 lastadd = "::float4";
5291 break;
5292 case SQL_FLOAT:
5293 case SQL_DOUBLE:
5294 set_server_decimal_point((char *) send_buf, used);
5295 lastadd = "::float8";
5296 break;
5297 case SQL_NUMERIC:
5298 break;
5299 /*
5300 * If it looks like a valid integer, we can pass it without quotes
5301 * and let the server interpret it. Arguably, it would always be
5302 * better to explicitly pass it as 'xxx'::integer or 'xxx'::smallint,
5303 * but historically we haven't done that, so let's avoid changing the
5304 * behaviour.
5305 *
5306 * If it's a negative number, we have to wrap it in parens. Otherwise
5307 * a query like "SELECT 0-?" would turn into "SELECT 0--123".
5308 */
5309 case SQL_INTEGER:
5310 if (valid_int_literal(send_buf, used, &negative))
5311 {
5312 need_quotes = FALSE;
5313 add_parens = negative;
5314 }
5315 else
5316 {
5317 /*
5318 * Doesn't look like a valid integer. The server will most
5319 * likely throw an error, unless it's in some format we don't
5320 * recognize but the server does.
5321 */
5322 lastadd = "::int4";
5323 }
5324 break;
5325 case SQL_SMALLINT:
5326 if (valid_int_literal(send_buf, used, &negative))
5327 {
5328 need_quotes = FALSE;
5329 add_parens = negative;
5330 }
5331 else
5332 lastadd = "::smallint";
5333 break;
5334 default: /* a numeric type or SQL_BIT */
5335 break;
5336 }
5337
5338 if (!send_buf)
5339 {
5340 qb->errormsg = "Could not convert parameter ctype to sqltype";
5341 qb->errornumber = STMT_EXEC_ERROR;
5342 goto cleanup;
5343 }
5344 if (used == SQL_NTS)
5345 used = strlen(send_buf);
5346
5347 /*
5348 * Ok, we now have the final string representation in 'send_buf', length 'used'.
5349 * We're ready to output the final string, with quotes and other
5350 * embellishments if necessary.
5351 *
5352 * In bind-mode, we don't need to do any quoting.
5353 */
5354 if (req_bind)
5355 CVT_APPEND_DATA(qb, send_buf, used);
5356 else
5357 {
5358 if (add_parens)
5359 CVT_APPEND_CHAR(qb, '(');
5360
5361 if (need_quotes)
5362 {
5363 if ((qb->flags & FLGB_LITERAL_EXTENSION) != 0)
5364 CVT_APPEND_CHAR(qb, LITERAL_EXT);
5365 CVT_APPEND_CHAR(qb, LITERAL_QUOTE);
5366
5367 if (final_binary_convert)
5368 CVT_APPEND_BINARY(qb, send_buf, used);
5369 else
5370 CVT_SPECIAL_CHARS(qb, send_buf, used);
5371
5372 CVT_APPEND_CHAR(qb, LITERAL_QUOTE);
5373 }
5374 else
5375 CVT_APPEND_DATA(qb, send_buf, used);
5376
5377 if (add_parens)
5378 CVT_APPEND_CHAR(qb, ')');
5379 if (lastadd && (FLGB_PARAM_CAST & qb->flags) != 0)
5380 CVT_APPEND_STR(qb, lastadd);
5381 }
5382
5383 retval = SQL_SUCCESS;
5384 cleanup:
5385 if (allocbuf)
5386 free(allocbuf);
5387 return retval;
5388 }
5389
5390
5391 static const char *
mapFunction(const char * func,int param_count,const char * keyword)5392 mapFunction(const char *func, int param_count, const char *keyword)
5393 {
5394 int i;
5395 const char *p1, *p2;
5396
5397 for (i = 0; (p1 = mapFuncs[i].odbc_name) != NULL; i++)
5398 {
5399 if (p1[0] == '%')
5400 {
5401 if (p1[1] - '0' == param_count &&
5402 !stricmp(p1 + 2, func))
5403 return mapFuncs[i].pgsql_name;
5404 }
5405 else if (!stricmp(p1, func))
5406 return mapFuncs[i].pgsql_name;
5407 else if (p2 = strchr(p1, (int) '('), NULL != p2)
5408 {
5409 int len = (int) (p2 - mapFuncs[i].odbc_name);
5410
5411 if (strlen(func) == len &&
5412 !strnicmp(p1, func, len) &&
5413 !stricmp(p2 + 1, keyword))
5414 return mapFuncs[i].pgsql_name;
5415 }
5416 }
5417
5418 return NULL;
5419 }
5420
5421 /*
5422 * processParameters()
5423 * Process function parameters and work with embedded escapes sequences.
5424 */
5425 static int
processParameters(QueryParse * qp,QueryBuild * qb,size_t * output_count,SQLLEN param_pos[][2])5426 processParameters(QueryParse *qp, QueryBuild *qb,
5427 size_t *output_count, SQLLEN param_pos[][2])
5428 {
5429 int retval, innerParenthesis, param_count;
5430 BOOL stop;
5431
5432 /* begin with outer '(' */
5433 innerParenthesis = 0;
5434 param_count = 0;
5435 if (NULL != output_count)
5436 *output_count = 0;
5437 stop = FALSE;
5438 for (; F_OldPos(qp) < qp->stmt_len; F_OldNext(qp))
5439 {
5440 retval = inner_process_tokens(qp, qb);
5441 if (retval == SQL_ERROR)
5442 return retval;
5443 if (MBCS_NON_ASCII(qp->encstr))
5444 continue;
5445 if (!QP_in_idle_status(qp))
5446 continue;
5447
5448 switch (F_OldChar(qp))
5449 {
5450 case ',':
5451 if (1 == innerParenthesis)
5452 {
5453 param_pos[param_count][1] = F_NewPos(qb) - 2;
5454 param_count++;
5455 param_pos[param_count][0] = F_NewPos(qb);
5456 param_pos[param_count][1] = -1;
5457 }
5458 break;
5459 case '(':
5460 if (0 == innerParenthesis)
5461 {
5462 param_pos[param_count][0] = F_NewPos(qb);
5463 param_pos[param_count][1] = -1;
5464 }
5465 innerParenthesis++;
5466 break;
5467
5468 case ')':
5469 innerParenthesis--;
5470 if (0 == innerParenthesis)
5471 {
5472 param_pos[param_count][1] = F_NewPos(qb) - 2;
5473 param_count++;
5474 param_pos[param_count][0] =
5475 param_pos[param_count][1] = -1;
5476 }
5477 if (output_count)
5478 *output_count = F_NewPos(qb);
5479 break;
5480
5481 case ODBC_ESCAPE_END:
5482 stop = (0 == innerParenthesis);
5483 break;
5484
5485 }
5486 if (stop) /* returns with the last } position */
5487 break;
5488 }
5489 if (param_pos[param_count][0] >= 0)
5490 {
5491 MYLOG(0, "closing ) not found %d\n", innerParenthesis);
5492 qb->errornumber = STMT_EXEC_ERROR;
5493 qb->errormsg = "processParameters closing ) not found";
5494 return SQL_ERROR;
5495 }
5496 else if (1 == param_count) /* the 1 parameter is really valid ? */
5497 {
5498 BOOL param_exist = FALSE;
5499 SQLLEN i;
5500
5501 for (i = param_pos[0][0]; i <= param_pos[0][1]; i++)
5502 {
5503 if (IS_NOT_SPACE(qb->query_statement[i]))
5504 {
5505 param_exist = TRUE;
5506 break;
5507 }
5508 }
5509 if (!param_exist)
5510 {
5511 param_pos[0][0] = param_pos[0][1] = -1;
5512 }
5513 }
5514
5515 return SQL_SUCCESS;
5516 }
5517
5518 /*
5519 * convert_escape()
5520 * This function doesn't return a pointer to static memory any longer !
5521 */
5522 static int
convert_escape(QueryParse * qp,QueryBuild * qb)5523 convert_escape(QueryParse *qp, QueryBuild *qb)
5524 {
5525 RETCODE retval = SQL_SUCCESS;
5526 char buf[1024], buf_small[128], key[65];
5527 UCHAR ucv;
5528 UInt4 prtlen;
5529
5530 QueryBuild nqb;
5531 BOOL nqb_is_valid = FALSE;
5532
5533 if (F_OldChar(qp) == ODBC_ESCAPE_START) /* skip the first { */
5534 F_OldNext(qp);
5535 /* Separate off the key, skipping leading and trailing whitespace */
5536 while ((ucv = F_OldChar(qp)) != '\0' && isspace(ucv))
5537 F_OldNext(qp);
5538 /*
5539 * procedure calls
5540 */
5541 /* '?=' to accept return values exists ? */
5542 if (F_OldChar(qp) == '?')
5543 {
5544 qb->param_number++;
5545 qb->proc_return = 1;
5546 if (qb->stmt)
5547 qb->stmt->proc_return = 1;
5548 while (isspace((UCHAR) qp->statement[++qp->opos]));
5549 if (F_OldChar(qp) != '=')
5550 {
5551 F_OldPrior(qp);
5552 return SQL_SUCCESS;
5553 }
5554 while (isspace((UCHAR) qp->statement[++qp->opos]));
5555 }
5556
5557 sscanf(F_OldPtr(qp), "%32s", key);
5558 while ((ucv = F_OldChar(qp)) != '\0' && (IS_NOT_SPACE(ucv)))
5559 F_OldNext(qp);
5560 while ((ucv = F_OldChar(qp)) != '\0' && isspace(ucv))
5561 F_OldNext(qp);
5562
5563 /* Avoid the concatenation of the function name with the previous word. Aceto */
5564
5565 if (stricmp(key, "call") == 0)
5566 {
5567 size_t funclen;
5568 const UCHAR *next_token;
5569
5570 if (SQL_ERROR == QB_start_brace(qb))
5571 {
5572 retval = SQL_ERROR;
5573 goto cleanup;
5574 }
5575 if (qb->num_io_params > 1 ||
5576 (0 == qb->proc_return))
5577 CVT_APPEND_STR(qb, "SELECT * FROM ");
5578 else
5579 CVT_APPEND_STR(qb, "SELECT ");
5580 funclen = findIdentifier((const UCHAR *) F_OldPtr(qp), qb->ccsc, &next_token);
5581 if (next_token && ODBC_ESCAPE_END == *next_token)
5582 {
5583 CVT_APPEND_DATA(qb, F_OldPtr(qp), funclen);
5584 CVT_APPEND_STR(qb, "()");
5585 if (SQL_ERROR == QB_end_brace(qb))
5586 {
5587 retval = SQL_ERROR;
5588 goto cleanup;
5589 }
5590 /* positioned at } */
5591 qp->opos += ((const char *) next_token - F_OldPtr(qp));
5592 }
5593 else
5594 {
5595 /* Continue at inner_process_tokens loop */
5596 F_OldPrior(qp);
5597 return SQL_SUCCESS;
5598 }
5599 }
5600 else if (stricmp(key, "d") == 0)
5601 {
5602 /* Literal; return the escape part adding type cast */
5603 F_ExtractOldTo(qp, buf_small, ODBC_ESCAPE_END, sizeof(buf_small));
5604 prtlen = SPRINTF_FIXED(buf, "%s::date", buf_small);
5605 CVT_APPEND_DATA(qb, buf, prtlen);
5606 retval = QB_append_space_to_separate_identifiers(qb, qp);
5607 }
5608 else if (stricmp(key, "t") == 0)
5609 {
5610 /* Literal; return the escape part adding type cast */
5611 F_ExtractOldTo(qp, buf_small, ODBC_ESCAPE_END, sizeof(buf_small));
5612 prtlen = SPRINTF_FIXED(buf, "%s::time", buf_small);
5613 CVT_APPEND_DATA(qb, buf, prtlen);
5614 retval = QB_append_space_to_separate_identifiers(qb, qp);
5615 }
5616 else if (stricmp(key, "ts") == 0)
5617 {
5618 /* Literal; return the escape part adding type cast */
5619 F_ExtractOldTo(qp, buf_small, ODBC_ESCAPE_END, sizeof(buf_small));
5620 prtlen = SPRINTF_FIXED(buf, "%s::timestamp", buf_small);
5621 CVT_APPEND_DATA(qb, buf, prtlen);
5622 retval = QB_append_space_to_separate_identifiers(qb, qp);
5623 }
5624 else if (stricmp(key, "oj") == 0) /* {oj syntax support for 7.1 * servers */
5625 {
5626 if (qb->stmt)
5627 SC_set_outer_join(qb->stmt);
5628 retval = QB_start_brace(qb);
5629 /* Continue at inner_process_tokens loop */
5630 F_OldPrior(qp);
5631 goto cleanup;
5632 }
5633 else if (stricmp(key, "escape") == 0) /* like escape syntax support for 7.1+ servers */
5634 {
5635 /* Literal; return the escape part adding type cast */
5636 F_ExtractOldTo(qp, buf_small, ODBC_ESCAPE_END, sizeof(buf_small));
5637 prtlen = SPRINTF_FIXED(buf, "%s %s", key, buf_small);
5638 CVT_APPEND_DATA(qb, buf, prtlen);
5639 retval = QB_append_space_to_separate_identifiers(qb, qp);
5640 }
5641 else if (stricmp(key, "fn") == 0)
5642 {
5643 const char *mapExpr;
5644 int i, param_count;
5645 SQLLEN from, to;
5646 size_t param_consumed;
5647 SQLLEN param_pos[16][2];
5648 BOOL cvt_func = FALSE;
5649
5650 /* Separate off the func name, skipping leading and trailing whitespace */
5651 i = 0;
5652 while ((ucv = F_OldChar(qp)) != '\0' && ucv != '(' &&
5653 (IS_NOT_SPACE(ucv)))
5654 {
5655 if (i < sizeof(key) - 1)
5656 key[i++] = ucv;
5657 F_OldNext(qp);
5658 }
5659 key[i] = '\0';
5660 while ((ucv = F_OldChar(qp)) != '\0' && isspace(ucv))
5661 F_OldNext(qp);
5662
5663 /*
5664 * We expect left parenthesis here, else return fn body as-is
5665 * since it is one of those "function constants".
5666 */
5667 if (F_OldChar(qp) != '(')
5668 {
5669 CVT_APPEND_STR(qb, key);
5670 goto cleanup;
5671 }
5672
5673 /*
5674 * Process parameter list and inner escape
5675 * sequences
5676 * Aceto 2002-01-29
5677 */
5678
5679 QB_initialize_copy(&nqb, qb, 1024);
5680 nqb_is_valid = TRUE;
5681 if (retval = processParameters(qp, &nqb, ¶m_consumed, param_pos), retval == SQL_ERROR)
5682 {
5683 qb->errornumber = nqb.errornumber;
5684 qb->errormsg = nqb.errormsg;
5685 goto cleanup;
5686 }
5687
5688 for (param_count = 0;; param_count++)
5689 {
5690 if (param_pos[param_count][0] < 0)
5691 break;
5692 }
5693 if (param_count == 1 &&
5694 param_pos[0][1] < param_pos[0][0])
5695 param_count = 0;
5696
5697 mapExpr = NULL;
5698 if (stricmp(key, "convert") == 0)
5699 cvt_func = TRUE;
5700 else
5701 {
5702 char keyword[64] = "";
5703
5704 if (param_count > 0)
5705 {
5706 int i, from, to;
5707 const char * p;
5708
5709 for (i = param_pos[0][0], p = nqb.query_statement + i; i <= param_pos[0][1] && isspace(*p); i++, p++)
5710 ;
5711 from = i;
5712 for (; i <= param_pos[0][1] && IS_NOT_SPACE(*p); i++, p++)
5713 ;
5714 to = i - 1;
5715 if (to >= from)
5716 {
5717 int len = to - from + 1;
5718
5719 if (len < sizeof(keyword))
5720 {
5721 memcpy(keyword, nqb.query_statement + from, len);
5722 keyword[len] = '\0';
5723 }
5724 }
5725 }
5726 mapExpr = mapFunction(key, param_count, keyword);
5727 }
5728 if (cvt_func)
5729 {
5730 if (2 == param_count)
5731 {
5732 BOOL add_cast = FALSE, add_quote = FALSE;
5733 const char *pptr;
5734
5735 from = param_pos[0][0];
5736 to = param_pos[0][1];
5737 for (pptr = nqb.query_statement + from; *pptr && isspace((unsigned char) *pptr); pptr++)
5738 ;
5739 if (LITERAL_QUOTE == *pptr)
5740 ;
5741 else if ('-' == *pptr)
5742 add_quote = TRUE;
5743 else if (isdigit((unsigned char) *pptr))
5744 add_quote = TRUE;
5745 else
5746 add_cast = TRUE;
5747 if (add_quote)
5748 CVT_APPEND_CHAR(qb, LITERAL_QUOTE);
5749 else if (add_cast)
5750 CVT_APPEND_CHAR(qb, '(');
5751 CVT_APPEND_DATA(qb, nqb.query_statement + from, to - from + 1);
5752 if (add_quote)
5753 CVT_APPEND_CHAR(qb, LITERAL_QUOTE);
5754 else if (add_cast)
5755 {
5756 const char *cast_form = NULL;
5757 char sqltype[32];
5758 int typel;
5759
5760 CVT_APPEND_CHAR(qb, ')');
5761 from = param_pos[1][0];
5762 to = param_pos[1][1];
5763 typel = to - from + 1;
5764 if (typel < sizeof(sqltype))
5765 {
5766 const char *type;
5767
5768 memcpy(sqltype, nqb.query_statement + from, typel);
5769 sqltype[typel] = '\0';
5770 MYLOG(0, FORMAT_LEN "-" FORMAT_LEN " SQLtype=%s SQL_BIT=%d\n", to, from, sqltype, SQL_BIT);
5771 for (type = sqltype; *type && isspace(*type); type++)
5772 ;
5773 if (strncmp(type, "SQL_", 4) == 0)
5774 {
5775 type += 4;
5776 if (strcmp(type, "INTEGER") == 0)
5777 cast_form = "int4";
5778 else if (strcmp(type, "CHAR") == 0)
5779 cast_form = "varchar";
5780 else if (strcmp(type, "VARCHAR") == 0)
5781 cast_form = "varchar";
5782 else if (strcmp(type, "LONGVARCHAR") == 0)
5783 cast_form = "text";
5784 else if (strcmp(type, "WCHAR") == 0)
5785 cast_form = "varchar";
5786 else if (strcmp(type, "WVARCHAR") == 0)
5787 cast_form = "varchar";
5788 else if (strcmp(type, "WLONGVARCHAR") == 0)
5789 cast_form = "text";
5790 else if (strcmp(type, "NUMERIC") == 0)
5791 cast_form = "numeric";
5792 else if (strcmp(type, "DOUBLE") == 0)
5793 cast_form = "float8";
5794 else if (strcmp(type, "FLOAT") == 0)
5795 cast_form = "float8";
5796 else if (strcmp(type, "REAL") == 0)
5797 cast_form = "float4";
5798 else if (strcmp(type, "BIGINT") == 0)
5799 cast_form = "int8";
5800 else if (strcmp(type, "DECIMAL") == 0)
5801 cast_form = "numeric";
5802 else if (strcmp(type, "SMALLINT") == 0)
5803 cast_form = "int2";
5804 else if (strcmp(type, "TYPE_DATE") == 0)
5805 cast_form = "date";
5806 else if (strcmp(type, "TYPE_TIME") == 0)
5807 cast_form = "time";
5808 else if (strcmp(type, "TYPE_TIMESTAMP") == 0)
5809 cast_form = "timestamp";
5810 else if (strcmp(type, "BIT") == 0)
5811 cast_form = "bit";
5812 }
5813 }
5814 if (NULL != cast_form)
5815 {
5816 CVT_APPEND_STR(qb, "::");
5817 CVT_APPEND_STR(qb, cast_form);
5818 }
5819 }
5820 }
5821 else
5822 {
5823 qb->errornumber = STMT_EXEC_ERROR;
5824 qb->errormsg = "convert param count must be 2";
5825 retval = SQL_ERROR;
5826 }
5827 }
5828 else if (mapExpr == NULL)
5829 {
5830 CVT_APPEND_STR(qb, key);
5831 CVT_APPEND_DATA(qb, nqb.query_statement, nqb.npos);
5832 }
5833 else
5834 {
5835 const char *mapptr;
5836 SQLLEN paramlen;
5837 int pidx;
5838
5839 for (mapptr = mapExpr; *mapptr; mapptr++)
5840 {
5841 if (*mapptr != '$')
5842 {
5843 CVT_APPEND_CHAR(qb, *mapptr);
5844 continue;
5845 }
5846 mapptr++;
5847 if (*mapptr == '*')
5848 {
5849 from = 1;
5850 to = param_consumed - 2;
5851 }
5852 else if (isdigit((unsigned char) *mapptr))
5853 {
5854 pidx = *mapptr - '0' - 1;
5855 if (pidx < 0 ||
5856 param_pos[pidx][0] < 0)
5857 {
5858 qb->errornumber = STMT_EXEC_ERROR;
5859 qb->errormsg = "param not found";
5860 MYLOG(0, "%dth param not found for the expression %s\n", pidx + 1, mapExpr);
5861 retval = SQL_ERROR;
5862 break;
5863 }
5864 from = param_pos[pidx][0];
5865 to = param_pos[pidx][1];
5866 }
5867 else
5868 {
5869 qb->errornumber = STMT_EXEC_ERROR;
5870 qb->errormsg = "internal expression error";
5871 MYLOG(0, "internal expression error %s\n", mapExpr);
5872 retval = SQL_ERROR;
5873 break;
5874 }
5875 paramlen = to - from + 1;
5876 if (paramlen > 0)
5877 CVT_APPEND_DATA(qb, nqb.query_statement + from, paramlen);
5878 }
5879 }
5880 if (0 == qb->errornumber)
5881 {
5882 qb->errornumber = nqb.errornumber;
5883 qb->errormsg = nqb.errormsg;
5884 }
5885 if (SQL_ERROR != retval)
5886 {
5887 qb->param_number = nqb.param_number;
5888 qb->dollar_number = nqb.dollar_number;
5889 qb->flags = nqb.flags;
5890 }
5891 }
5892 else
5893 {
5894 /* Bogus key, leave untranslated */
5895 retval = SQL_ERROR;
5896 }
5897
5898 cleanup:
5899 if (nqb_is_valid)
5900 QB_Destructor(&nqb);
5901 return retval;
5902 }
5903
5904 static BOOL
convert_money(const char * s,char * sout,size_t soutmax)5905 convert_money(const char *s, char *sout, size_t soutmax)
5906 {
5907 char in, decp = 0;
5908 size_t i = 0,
5909 out = 0;
5910 int num_in = -1, period_in = -1, comma_in = -1;
5911
5912 for (i = 0; s[i]; i++)
5913 {
5914 switch (in = s[i])
5915 {
5916 case '.':
5917 if (period_in < 0)
5918 period_in = i;
5919 break;
5920 case ',':
5921 if (comma_in < 0)
5922 comma_in = i;
5923 break;
5924 default:
5925 if ('0' <= in && '9' >= in)
5926 num_in = i;
5927 break;
5928 }
5929 }
5930 if (period_in > comma_in)
5931 {
5932 if ( period_in >= num_in - 2)
5933 decp = '.';
5934 }
5935 else if (comma_in >= 0 &&
5936 comma_in >= num_in - 2)
5937 decp = ',';
5938 for (i = 0; s[i] && out + 1 < soutmax; i++)
5939 {
5940 switch (in = s[i])
5941 {
5942 case '(':
5943 case '-':
5944 sout[out++] = '-';
5945 break;
5946 default:
5947 if (in >= '0' && in <= '9')
5948 sout[out++] = in;
5949 else if (in == decp)
5950 sout[out++] = '.';
5951 }
5952 }
5953 sout[out] = '\0';
5954 return TRUE;
5955 }
5956
5957
5958 /*
5959 * This function parses a character string for date/time info and fills in SIMPLE_TIME
5960 * It does not zero out SIMPLE_TIME in case it is desired to initialize it with a value
5961 */
5962 static char
parse_datetime(const char * buf,SIMPLE_TIME * st)5963 parse_datetime(const char *buf, SIMPLE_TIME *st)
5964 {
5965 int y,
5966 m,
5967 d,
5968 hh,
5969 mm,
5970 ss;
5971 int nf;
5972 BOOL bZone; int zone;
5973
5974 y = m = d = hh = mm = ss = 0;
5975 st->fr = 0;
5976 st->infinity = 0;
5977
5978 /*
5979 * Handle ODBC time/date/timestamp literals, e.g.
5980 * { d '2011-04-22' }
5981 * { t '12:34:56' }
5982 * { ts '2011-04-22 12:34:56' }
5983 */
5984 if (buf[0] == ODBC_ESCAPE_START)
5985 {
5986 while (*(++buf) && *buf != LITERAL_QUOTE);
5987 if (!(*buf))
5988 return FALSE;
5989 buf++;
5990 }
5991 bZone = FALSE;
5992 if (timestamp2stime(buf, st, &bZone, &zone))
5993 return TRUE;
5994 if (buf[4] == '-') /* year first */
5995 nf = sscanf(buf, "%4d-%2d-%2d %2d:%2d:%2d", &y, &m, &d, &hh, &mm, &ss);
5996 else
5997 nf = sscanf(buf, "%2d-%2d-%4d %2d:%2d:%2d", &m, &d, &y, &hh, &mm, &ss);
5998
5999 if (nf == 5 || nf == 6)
6000 {
6001 st->y = y;
6002 st->m = m;
6003 st->d = d;
6004 st->hh = hh;
6005 st->mm = mm;
6006 st->ss = ss;
6007
6008 return TRUE;
6009 }
6010
6011 if (buf[4] == '-') /* year first */
6012 nf = sscanf(buf, "%4d-%2d-%2d", &y, &m, &d);
6013 else
6014 nf = sscanf(buf, "%2d-%2d-%4d", &m, &d, &y);
6015
6016 if (nf == 3)
6017 {
6018 st->y = y;
6019 st->m = m;
6020 st->d = d;
6021
6022 return TRUE;
6023 }
6024
6025 nf = sscanf(buf, "%2d:%2d:%2d", &hh, &mm, &ss);
6026 if (nf == 2 || nf == 3)
6027 {
6028 st->hh = hh;
6029 st->mm = mm;
6030 st->ss = ss;
6031
6032 return TRUE;
6033 }
6034
6035 return FALSE;
6036 }
6037
6038
6039 /* Change linefeed to carriage-return/linefeed */
6040 size_t
convert_linefeeds(const char * si,char * dst,size_t max,BOOL convlf,BOOL * changed)6041 convert_linefeeds(const char *si, char *dst, size_t max, BOOL convlf, BOOL *changed)
6042 {
6043 size_t i = 0,
6044 out = 0;
6045
6046 if (max == 0)
6047 max = 0xffffffff;
6048 *changed = FALSE;
6049 for (i = 0; si[i] && out < max - 1; i++)
6050 {
6051 if (convlf && si[i] == '\n')
6052 {
6053 /* Only add the carriage-return if needed */
6054 if (i > 0 && PG_CARRIAGE_RETURN == si[i - 1])
6055 {
6056 if (dst)
6057 dst[out++] = si[i];
6058 else
6059 out++;
6060 continue;
6061 }
6062 *changed = TRUE;
6063
6064 if (dst)
6065 {
6066 dst[out++] = PG_CARRIAGE_RETURN;
6067 dst[out++] = '\n';
6068 }
6069 else
6070 out += 2;
6071 }
6072 else
6073 {
6074 if (dst)
6075 dst[out++] = si[i];
6076 else
6077 out++;
6078 }
6079 }
6080 if (dst)
6081 dst[out] = '\0';
6082 return out;
6083 }
6084
6085
6086 /*
6087 * Change carriage-return/linefeed to just linefeed
6088 * Plus, escape any special characters.
6089 */
6090 static BOOL
convert_special_chars(QueryBuild * qb,const char * si,size_t used)6091 convert_special_chars(QueryBuild *qb, const char *si, size_t used)
6092 {
6093 size_t i = 0,
6094 max;
6095 char tchar;
6096 encoded_str encstr;
6097 BOOL convlf = (0 != (qb->flags & FLGB_CONVERT_LF));
6098 BOOL double_special = (qb->param_mode != RPM_BUILDING_BIND_REQUEST);
6099 int ccsc = qb->ccsc;
6100 char escape_in_literal = CC_get_escape(qb->conn);
6101
6102 if (used == SQL_NTS)
6103 max = strlen(si);
6104 else
6105 max = used;
6106
6107 /*
6108 * Make sure there's room for the null-terminator, if the input is an
6109 * empty string. XXX: I don't think the null-termination is actually
6110 * even required, but better safe than sorry.
6111 */
6112 if (!enlarge_query_statement(qb, qb->npos + 1))
6113 return FALSE;
6114
6115 encoded_str_constr(&encstr, ccsc, si);
6116 for (i = 0; i < max && si[i]; i++)
6117 {
6118 tchar = encoded_nextchar(&encstr);
6119
6120 /*
6121 * Make sure there is room for three more bytes in the buffer. We
6122 * expand quotes to two bytes, plus null-terminate the end.
6123 */
6124 if (qb->npos + 3 >= qb->str_alsize)
6125 {
6126 if (!enlarge_query_statement(qb, qb->npos + 3))
6127 return FALSE;
6128 }
6129
6130 if (MBCS_NON_ASCII(encstr))
6131 {
6132 qb->query_statement[qb->npos++] = tchar;
6133 continue;
6134 }
6135 if (convlf && /* CR/LF -> LF */
6136 PG_CARRIAGE_RETURN == tchar &&
6137 PG_LINEFEED == si[i + 1])
6138 continue;
6139 else if (double_special && /* double special chars ? */
6140 (tchar == LITERAL_QUOTE ||
6141 tchar == escape_in_literal))
6142 {
6143 qb->query_statement[qb->npos++] = tchar;
6144 }
6145 qb->query_statement[qb->npos++] = tchar;
6146 }
6147
6148 qb->query_statement[qb->npos] = '\0';
6149
6150 return TRUE;
6151 }
6152
6153 static int
conv_from_octal(const char * s)6154 conv_from_octal(const char *s)
6155 {
6156 ssize_t i;
6157 int y = 0;
6158
6159 for (i = 1; i <= 3; i++)
6160 y += (s[i] - '0') << (3 * (3 - i));
6161
6162 return y;
6163 }
6164
6165
6166 /* convert octal escapes to bytes */
6167 static size_t
convert_from_pgbinary(const char * value,char * rgbValue,SQLLEN cbValueMax)6168 convert_from_pgbinary(const char *value, char *rgbValue, SQLLEN cbValueMax)
6169 {
6170 size_t i,
6171 ilen = strlen(value);
6172 size_t o = 0;
6173
6174 for (i = 0; i < ilen;)
6175 {
6176 if (value[i] == BYTEA_ESCAPE_CHAR)
6177 {
6178 if (value[i + 1] == BYTEA_ESCAPE_CHAR)
6179 {
6180 if (rgbValue)
6181 rgbValue[o] = value[i];
6182 o++;
6183 i += 2;
6184 }
6185 else if (value[i + 1] == 'x')
6186 {
6187 i += 2;
6188 if (i < ilen)
6189 {
6190 ilen -= i;
6191 if (rgbValue)
6192 pg_hex2bin(value + i, rgbValue + o, ilen);
6193 o += ilen / 2;
6194 }
6195 break;
6196 }
6197 else
6198 {
6199 if (rgbValue)
6200 rgbValue[o] = conv_from_octal(&value[i]);
6201 o++;
6202 i += 4;
6203 }
6204 }
6205 else
6206 {
6207 if (rgbValue)
6208 rgbValue[o] = value[i];
6209 o++;
6210 i++;
6211 }
6212 /** if (rgbValue)
6213 MYLOG(0, "i=%d, rgbValue[%d] = %d, %c\n", i, o, rgbValue[o], rgbValue[o]); ***/
6214 }
6215
6216 if (rgbValue)
6217 rgbValue[o] = '\0'; /* extra protection */
6218
6219 MYLOG(0, "in=" FORMAT_SIZE_T ", out = " FORMAT_SIZE_T "\n", ilen, o);
6220
6221 return o;
6222 }
6223
6224
6225 static UInt2
conv_to_octal(UCHAR val,char * octal,char escape_ch)6226 conv_to_octal(UCHAR val, char *octal, char escape_ch)
6227 {
6228 int i, pos = 0, len;
6229
6230 if (escape_ch)
6231 octal[pos++] = escape_ch;
6232 octal[pos] = BYTEA_ESCAPE_CHAR;
6233 len = 4 + pos;
6234 octal[len] = '\0';
6235
6236 for (i = len - 1; i > pos; i--)
6237 {
6238 octal[i] = (val & 7) + '0';
6239 val >>= 3;
6240 }
6241
6242 return (UInt2) len;
6243 }
6244
6245
6246 static char *
conv_to_octal2(UCHAR val,char * octal)6247 conv_to_octal2(UCHAR val, char *octal)
6248 {
6249 int i;
6250
6251 octal[0] = BYTEA_ESCAPE_CHAR;
6252 octal[4] = '\0';
6253
6254 for (i = 3; i > 0; i--)
6255 {
6256 octal[i] = (val & 7) + '0';
6257 val >>= 3;
6258 }
6259
6260 return octal;
6261 }
6262
6263
6264 /* convert non-ascii bytes to octal escape sequences */
6265 static size_t
convert_to_pgbinary(const char * in,char * out,size_t len,QueryBuild * qb)6266 convert_to_pgbinary(const char *in, char *out, size_t len, QueryBuild *qb)
6267 {
6268 UCHAR inc;
6269 size_t i, o = 0;
6270 char escape_in_literal = CC_get_escape(qb->conn);
6271 BOOL esc_double = (qb->param_mode != RPM_BUILDING_BIND_REQUEST &&
6272 0 != escape_in_literal);
6273
6274 /* use hex format for 9.0 or later servers */
6275 if (0 != (qb->flags & FLGB_HEX_BIN_FORMAT))
6276 {
6277 if (esc_double)
6278 out[o++] = escape_in_literal;
6279 out[o++] = '\\';
6280 out[o++] = 'x';
6281 o += pg_bin2hex(in, out + o, len);
6282 return o;
6283 }
6284 for (i = 0; i < len; i++)
6285 {
6286 inc = in[i];
6287 MYLOG(DETAIL_LOG_LEVEL, "in[" FORMAT_SIZE_T "] = %d, %c\n", i, inc, inc);
6288 if (inc < 128 && (isalnum(inc) || inc == ' '))
6289 out[o++] = inc;
6290 else
6291 {
6292 if (esc_double)
6293 {
6294 o += conv_to_octal(inc, &out[o], escape_in_literal);
6295 }
6296 else
6297 {
6298 conv_to_octal2(inc, &out[o]);
6299 o += 4;
6300 }
6301 }
6302 }
6303
6304 MYLOG(0, "leaving " FORMAT_SIZE_T ", out='%.*s'\n", o, (int) o, out);
6305
6306 return o;
6307 }
6308
6309
6310 static const char *hextbl = "0123456789ABCDEF";
6311
6312 #define def_bin2hex(type) \
6313 (const char *src, type *dst, SQLLEN length) \
6314 { \
6315 const char *src_wk; \
6316 UCHAR chr; \
6317 type *dst_wk; \
6318 BOOL backwards; \
6319 int i; \
6320 \
6321 backwards = FALSE; \
6322 if ((char *) dst < src) \
6323 { \
6324 if ((char *) (dst + 2 * (length - 1)) > src + length - 1) \
6325 return -1; \
6326 } \
6327 else if ((char *) dst < src + length) \
6328 backwards = TRUE; \
6329 if (backwards) \
6330 { \
6331 for (i = 0, src_wk = src + length - 1, dst_wk = dst + 2 * length - 1; i < length; i++, src_wk--) \
6332 { \
6333 chr = *src_wk; \
6334 *dst_wk-- = hextbl[chr % 16]; \
6335 *dst_wk-- = hextbl[chr >> 4]; \
6336 } \
6337 } \
6338 else \
6339 { \
6340 for (i = 0, src_wk = src, dst_wk = dst; i < length; i++, src_wk++) \
6341 { \
6342 chr = *src_wk; \
6343 *dst_wk++ = hextbl[chr >> 4]; \
6344 *dst_wk++ = hextbl[chr % 16]; \
6345 } \
6346 } \
6347 dst[2 * length] = '\0'; \
6348 return 2 * length * sizeof(type); \
6349 }
6350 #ifdef UNICODE_SUPPORT
6351 static SQLLEN
def_bin2hex(SQLWCHAR)6352 pg_bin2whex def_bin2hex(SQLWCHAR)
6353 #endif /* UNICODE_SUPPORT */
6354
6355 static SQLLEN
6356 pg_bin2hex def_bin2hex(char)
6357
6358 SQLLEN
6359 pg_hex2bin(const char *src, char *dst, SQLLEN length)
6360 {
6361 UCHAR chr;
6362 const char *src_wk;
6363 char *dst_wk;
6364 SQLLEN i;
6365 int val;
6366 BOOL HByte = TRUE;
6367
6368 for (i = 0, src_wk = src, dst_wk = dst; i < length; i++, src_wk++)
6369 {
6370 chr = *src_wk;
6371 if (!chr)
6372 break;
6373 if (chr >= 'a' && chr <= 'f')
6374 val = chr - 'a' + 10;
6375 else if (chr >= 'A' && chr <= 'F')
6376 val = chr - 'A' + 10;
6377 else
6378 val = chr - '0';
6379 if (HByte)
6380 *dst_wk = (val << 4);
6381 else
6382 {
6383 *dst_wk += val;
6384 dst_wk++;
6385 }
6386 HByte = !HByte;
6387 }
6388 *dst_wk = '\0';
6389 return length;
6390 }
6391
6392 /*-------
6393 * 1. get oid (from 'value')
6394 * 2. open the large object
6395 * 3. read from the large object (handle multiple GetData)
6396 * 4. close when read less than requested? -OR-
6397 * lseek/read each time
6398 * handle case where application receives truncated and
6399 * decides not to continue reading.
6400 *
6401 * CURRENTLY, ONLY LONGVARBINARY is handled, since that is the only
6402 * data type currently mapped to a PG_TYPE_LO. But, if any other types
6403 * are desired to map to a large object (PG_TYPE_LO), then that would
6404 * need to be handled here. For example, LONGVARCHAR could possibly be
6405 * mapped to PG_TYPE_LO someday, instead of PG_TYPE_TEXT as it is now.
6406 *-------
6407 */
6408 static int
convert_lo(StatementClass * stmt,const void * value,SQLSMALLINT fCType,PTR rgbValue,SQLLEN cbValueMax,SQLLEN * pcbValue)6409 convert_lo(StatementClass *stmt, const void *value, SQLSMALLINT fCType, PTR rgbValue,
6410 SQLLEN cbValueMax, SQLLEN *pcbValue)
6411 {
6412 CSTR func = "convert_lo";
6413 OID oid;
6414 int result;
6415 Int8 retval;
6416 Int8 left64 = -1;
6417 struct GetBlobDataClass *gdata_blob = NULL;
6418 ConnectionClass *conn = SC_get_conn(stmt);
6419 ConnInfo *ci = &(conn->connInfo);
6420 GetDataInfo *gdata_info = SC_get_GDTI(stmt);
6421 int factor;
6422
6423 oid = ATOI32U(value);
6424 if (0 == oid)
6425 {
6426 if (pcbValue)
6427 *pcbValue = SQL_NULL_DATA;
6428 return COPY_OK;
6429 }
6430 switch (fCType)
6431 {
6432 case SQL_C_CHAR:
6433 factor = 2;
6434 break;
6435 case SQL_C_BINARY:
6436 factor = 1;
6437 break;
6438 default:
6439 SC_set_error(stmt, STMT_EXEC_ERROR, "Could not convert lo to the c-type", func);
6440 return COPY_GENERAL_ERROR;
6441 }
6442 /* If using SQLGetData, then current_col will be set */
6443 if (stmt->current_col >= 0)
6444 {
6445 gdata_blob = &(gdata_info->gdata[stmt->current_col].blob);
6446 left64 = gdata_blob->data_left64;
6447 }
6448
6449 /*
6450 * if this is the first call for this column, open the large object
6451 * for reading
6452 */
6453
6454 if (!gdata_blob || gdata_blob->data_left64 == -1)
6455 {
6456 /* begin transaction if needed */
6457 if (!CC_is_in_trans(conn))
6458 {
6459 if (!CC_begin(conn))
6460 {
6461 SC_set_error(stmt, STMT_EXEC_ERROR, "Could not begin (in-line) a transaction", func);
6462 return COPY_GENERAL_ERROR;
6463 }
6464 }
6465
6466 stmt->lobj_fd = odbc_lo_open(conn, oid, INV_READ);
6467 if (stmt->lobj_fd < 0)
6468 {
6469 SC_set_error(stmt, STMT_EXEC_ERROR, "Couldnt open large object for reading.", func);
6470 return COPY_GENERAL_ERROR;
6471 }
6472
6473 /* Get the size */
6474 retval = odbc_lo_lseek64(conn, stmt->lobj_fd, 0L, SEEK_END);
6475 if (retval >= 0)
6476 {
6477 left64 = odbc_lo_tell64(conn, stmt->lobj_fd);
6478 if (gdata_blob)
6479 gdata_blob->data_left64 = left64;
6480
6481 /* return to beginning */
6482 odbc_lo_lseek64(conn, stmt->lobj_fd, 0L, SEEK_SET);
6483 }
6484 }
6485 else if (left64 == 0)
6486 return COPY_NO_DATA_FOUND;
6487 MYLOG(0, "lo data left = " FORMATI64 "\n", left64);
6488
6489 if (stmt->lobj_fd < 0)
6490 {
6491 SC_set_error(stmt, STMT_EXEC_ERROR, "Large object FD undefined for multiple read.", func);
6492 return COPY_GENERAL_ERROR;
6493 }
6494
6495 if (0 >= cbValueMax)
6496 retval = 0;
6497 else
6498 retval = (Int8) odbc_lo_read(conn, stmt->lobj_fd, (char *) rgbValue, (Int4) (factor > 1 ? (cbValueMax - 1) / factor : cbValueMax));
6499 if (retval < 0)
6500 {
6501 odbc_lo_close(conn, stmt->lobj_fd);
6502
6503 /* commit transaction if needed */
6504 if (!ci->drivers.use_declarefetch && CC_does_autocommit(conn))
6505 {
6506 if (!CC_commit(conn))
6507 {
6508 SC_set_error(stmt, STMT_EXEC_ERROR, "Could not commit (in-line) a transaction", func);
6509 return COPY_GENERAL_ERROR;
6510 }
6511 }
6512
6513 stmt->lobj_fd = -1;
6514
6515 SC_set_error(stmt, STMT_EXEC_ERROR, "Error reading from large object.", func);
6516 return COPY_GENERAL_ERROR;
6517 }
6518
6519 if (factor > 1)
6520 pg_bin2hex((char *) rgbValue, (char *) rgbValue, retval);
6521 if (retval < left64)
6522 result = COPY_RESULT_TRUNCATED;
6523 else
6524 result = COPY_OK;
6525
6526 if (pcbValue)
6527 {
6528 Int8 leftbytes = left64 * factor;
6529 *pcbValue = left64 < 0 ? SQL_NO_TOTAL : (leftbytes == (SQLLEN) leftbytes ? leftbytes : /* exceeds SQLLEN limit */ SQL_NO_TOTAL);
6530 }
6531
6532 if (gdata_blob && gdata_blob->data_left64 > 0)
6533 gdata_blob->data_left64 -= retval;
6534
6535 if (!gdata_blob || gdata_blob->data_left64 == 0)
6536 {
6537 odbc_lo_close(conn, stmt->lobj_fd);
6538
6539 /* commit transaction if needed */
6540 if (!ci->drivers.use_declarefetch && CC_does_autocommit(conn))
6541 {
6542 if (!CC_commit(conn))
6543 {
6544 SC_set_error(stmt, STMT_EXEC_ERROR, "Could not commit (in-line) a transaction", func);
6545 return COPY_GENERAL_ERROR;
6546 }
6547 }
6548
6549 stmt->lobj_fd = -1; /* prevent further reading */
6550 }
6551
6552 return result;
6553 }
6554