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 "notice.txt" for copyright and license information.
16 *
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <ctype.h>
27
28 #include "psqlodbc.h"
29
30 #ifndef WIN32
31 #include "isql.h"
32 #include "isqlext.h"
33 #else
34 #include <windows.h>
35 #include <sql.h>
36 #include <sqlext.h>
37 #endif
38
39 #include <time.h>
40 #ifdef HAVE_LOCALE_H
41 #include <locale.h>
42 #endif
43 #include <math.h>
44 #include "convert.h"
45 #include "statement.h"
46 #include "qresult.h"
47 #include "bind.h"
48 #include "pgtypes.h"
49 #include "lobj.h"
50 #include "connection.h"
51
52 #ifndef WIN32
53 #ifndef HAVE_STRICMP
54 #define stricmp(s1,s2) strcasecmp(s1,s2)
55 #define strnicmp(s1,s2,n) strncasecmp(s1,s2,n)
56 #endif
57 #endif
58
59 extern GLOBAL_VALUES globals;
60
61 /* How to map ODBC scalar functions {fn func(args)} to Postgres
62 * This is just a simple substitution
63 * List augmented from
64 * http://www.merant.com/datadirect/download/docs/odbc16/Odbcref/rappc.htm
65 * - thomas 2000-04-03
66 */
67 char* const mapFuncs[][2] = {
68 /* { "ASCII", "ascii" }, */
69 { "CHAR", "chr" },
70 { "CONCAT", "textcat" },
71 /* { "DIFFERENCE", "difference" }, */
72 /* { "INSERT", "insert" }, */
73 { "LCASE", "lower" },
74 { "LEFT", "ltrunc" },
75 { "LOCATE", "strpos" },
76 { "LENGTH", "char_length"},
77 /* { "LTRIM", "ltrim" }, */
78 { "RIGHT", "rtrunc" },
79 /* { "REPEAT", "repeat" }, */
80 /* { "REPLACE", "replace" }, */
81 /* { "RTRIM", "rtrim" }, */
82 /* { "SOUNDEX", "soundex" }, */
83 { "SUBSTRING", "substr" },
84 { "UCASE", "upper" },
85
86 /* { "ABS", "abs" }, */
87 /* { "ACOS", "acos" }, */
88 /* { "ASIN", "asin" }, */
89 /* { "ATAN", "atan" }, */
90 /* { "ATAN2", "atan2" }, */
91 { "CEILING", "ceil" },
92 /* { "COS", "cos" }, */
93 /* { "COT", "cot" }, */
94 /* { "DEGREES", "degrees" }, */
95 /* { "EXP", "exp" }, */
96 /* { "FLOOR", "floor" }, */
97 { "LOG", "ln" },
98 { "LOG10", "log" },
99 /* { "MOD", "mod" }, */
100 /* { "PI", "pi" }, */
101 { "POWER", "pow" },
102 /* { "RADIANS", "radians" }, */
103 { "RAND", "random" },
104 /* { "ROUND", "round" }, */
105 /* { "SIGN", "sign" }, */
106 /* { "SIN", "sin" }, */
107 /* { "SQRT", "sqrt" }, */
108 /* { "TAN", "tan" }, */
109 { "TRUNCATE", "trunc" },
110
111 /* { "CURDATE", "curdate" }, */
112 /* { "CURTIME", "curtime" }, */
113 /* { "DAYNAME", "dayname" }, */
114 /* { "DAYOFMONTH", "dayofmonth" }, */
115 /* { "DAYOFWEEK", "dayofweek" }, */
116 /* { "DAYOFYEAR", "dayofyear" }, */
117 /* { "HOUR", "hour" }, */
118 /* { "MINUTE", "minute" }, */
119 /* { "MONTH", "month" }, */
120 /* { "MONTHNAME", "monthname" }, */
121 /* { "NOW", "now" }, */
122 /* { "QUARTER", "quarter" }, */
123 /* { "SECOND", "second" }, */
124 /* { "WEEK", "week" }, */
125 /* { "YEAR", "year" }, */
126
127 /* { "DATABASE", "database" }, */
128 { "IFNULL", "coalesce" },
129 { "USER", "odbc_user" },
130 { 0, 0 }
131 };
132
133 char *mapFunction(char *func);
134 unsigned int conv_from_octal(unsigned char *s);
135 unsigned int conv_from_hex(unsigned char *s);
136 char *conv_to_octal(unsigned char val);
137
138 /******** A Guide for date/time/timestamp conversions **************
139
140 field_type fCType Output
141 ---------- ------ ----------
142 PG_TYPE_DATE SQL_C_DEFAULT SQL_C_DATE
143 PG_TYPE_DATE SQL_C_DATE SQL_C_DATE
144 PG_TYPE_DATE SQL_C_TIMESTAMP SQL_C_TIMESTAMP (time = 0 (midnight))
145 PG_TYPE_TIME SQL_C_DEFAULT SQL_C_TIME
146 PG_TYPE_TIME SQL_C_TIME SQL_C_TIME
147 PG_TYPE_TIME SQL_C_TIMESTAMP SQL_C_TIMESTAMP (date = current date)
148 PG_TYPE_ABSTIME SQL_C_DEFAULT SQL_C_TIMESTAMP
149 PG_TYPE_ABSTIME SQL_C_DATE SQL_C_DATE (time is truncated)
150 PG_TYPE_ABSTIME SQL_C_TIME SQL_C_TIME (date is truncated)
151 PG_TYPE_ABSTIME SQL_C_TIMESTAMP SQL_C_TIMESTAMP
152 ******************************************************************************/
153
154
155 /* This is called by SQLFetch() */
156 int
copy_and_convert_field_bindinfo(StatementClass * stmt,Int4 field_type,void * value,int col)157 copy_and_convert_field_bindinfo(StatementClass *stmt, Int4 field_type, void *value, int col)
158 {
159 BindInfoClass *bic = &(stmt->bindings[col]);
160
161 return copy_and_convert_field(stmt, field_type, value, (Int2)bic->returntype, (PTR)bic->buffer,
162 (SDWORD)bic->buflen, (SQLLEN *)bic->used);
163 }
164
setup_ts(SIMPLE_TIME st)165 static void setup_ts( SIMPLE_TIME st )
166 {
167 time_t t = time( NULL );
168 struct tm *tim;
169
170 #ifdef HAVE_LOCALTIME_R
171 struct tm tp;
172 #endif
173
174 #ifdef HAVE_LOCALTIME_R
175 tim = localtime_r(&t, &tp);
176 #else
177 tim = localtime(&t);
178 #endif
179 st.m = tim->tm_mon + 1;
180 st.d = tim->tm_mday;
181 st.y = tim->tm_year + 1900;
182 st.hh = tim->tm_hour;
183 st.mm = tim->tm_min;
184 st.ss = tim->tm_sec;
185 }
186
187 /* This is called by SQLGetData() */
188 int
copy_and_convert_field(StatementClass * stmt,Int4 field_type,void * value,Int2 fCType,PTR rgbValue,SDWORD cbValueMax,SQLLEN * pcbValue)189 copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 fCType,
190 PTR rgbValue, SDWORD cbValueMax, SQLLEN *pcbValue)
191 {
192 Int4 len = 0, copy_len = 0;
193 SIMPLE_TIME st;
194 time_t t;
195 struct tm *tim;
196 int pcbValueOffset, rgbValueOffset;
197 char *rgbValueBindRow, *ptr;
198 int bind_row = stmt->bind_row;
199 int bind_size = stmt->options.bind_size;
200 int result = COPY_OK;
201 /* char tempBuf[TEXT_FIELD_SIZE+5]; */
202 char *tempBuf;
203 #ifdef HAVE_LOCALE_H
204 char saved_locale[256];
205 #endif
206 #ifdef HAVE_LOCALTIME_R
207 struct tm tp;
208 #endif
209
210 /* rgbValueOffset is *ONLY* for character and binary data */
211 /* pcbValueOffset is for computing any pcbValue location */
212 tempBuf = (char *) malloc(TEXT_FIELD_SIZE+5);
213 if (bind_size > 0) {
214
215 pcbValueOffset = rgbValueOffset = (bind_size * bind_row);
216 }
217 else {
218
219 pcbValueOffset = bind_row * sizeof(SDWORD);
220 rgbValueOffset = bind_row * cbValueMax;
221
222 }
223
224 memset(&st, 0, sizeof(SIMPLE_TIME));
225
226 mylog("copy_and_convert: field_type = %d, fctype = %d, value = '%s', cbValueMax=%d\n", field_type, fCType, (value==NULL)?"<NULL>":value, cbValueMax);
227
228 if ( ! value) {
229 /* handle a null just by returning SQL_NULL_DATA in pcbValue, */
230 /* and doing nothing to the buffer. */
231 if(pcbValue) {
232 *(SDWORD *) ((char *) pcbValue + pcbValueOffset) = SQL_NULL_DATA;
233 }
234 free(tempBuf);
235 return COPY_OK;
236 }
237
238
239 if (stmt->hdbc->DataSourceToDriver != NULL) {
240 int length = strlen (value);
241 stmt->hdbc->DataSourceToDriver (stmt->hdbc->translation_option,
242 SQL_CHAR,
243 value, length,
244 value, length, NULL,
245 NULL, 0, NULL);
246 }
247
248
249 /********************************************************************
250 First convert any specific postgres types into more
251 useable data.
252
253 NOTE: Conversions from PG char/varchar of a date/time/timestamp
254 value to SQL_C_DATE,SQL_C_TIME, SQL_C_TIMESTAMP not supported
255 *********************************************************************/
256 switch(field_type) {
257 /* $$$ need to add parsing for date/time/timestamp strings in PG_TYPE_CHAR,VARCHAR $$$ */
258 case PG_TYPE_DATE:
259 setup_ts( st );
260 sscanf(value, "%4d-%2d-%2d", &st.y, &st.m, &st.d);
261 break;
262
263 case PG_TYPE_TIME:
264 setup_ts( st );
265 sscanf(value, "%2d:%2d:%2d", &st.hh, &st.mm, &st.ss);
266 break;
267
268 case PG_TYPE_ABSTIME:
269 case PG_TYPE_DATETIME:
270 case PG_TYPE_TIMESTAMP:
271 case PG_TYPE_TIMESTAMP_NO_TMZONE:
272 setup_ts( st );
273 if (strnicmp(value, "invalid", 7) != 0) {
274 sscanf(value, "%4d-%2d-%2d %2d:%2d:%2d", &st.y, &st.m, &st.d, &st.hh, &st.mm, &st.ss);
275
276 } else { /* The timestamp is invalid so set something conspicuous, like the epoch */
277 t = 0;
278 #ifdef HAVE_LOCALTIME_R
279 tim = localtime_r(&t, &tp);
280 #else
281 tim = localtime(&t);
282 #endif
283 st.m = tim->tm_mon + 1;
284 st.d = tim->tm_mday;
285 st.y = tim->tm_year + 1900;
286 st.hh = tim->tm_hour;
287 st.mm = tim->tm_min;
288 st.ss = tim->tm_sec;
289 }
290 break;
291
292 case PG_TYPE_BOOL: { /* change T/F to 1/0 */
293 char *s = (char *) value;
294 if (s[0] == 'T' || s[0] == 't')
295 s[0] = '1';
296 else
297 s[0] = '0';
298 }
299 break;
300
301 /* This is for internal use by SQLStatistics() */
302 case PG_TYPE_INT2VECTOR: {
303 int nval, i;
304 char *vp;
305 /* this is an array of eight integers */
306 short *short_array = (short *) ( (char *) rgbValue + rgbValueOffset);
307
308 len = 16;
309 vp = value;
310 nval = 0;
311 for (i = 0; i < 8; i++)
312 {
313 if (sscanf(vp, "%hd", &short_array[i]) != 1)
314 break;
315
316 nval++;
317
318 /* skip the current token */
319 while ((*vp != '\0') && (! isspace((unsigned char) *vp))) vp++;
320 /* and skip the space to the next token */
321 while ((*vp != '\0') && (isspace((unsigned char) *vp))) vp++;
322 if (*vp == '\0')
323 break;
324 }
325
326 for (i = nval; i < 8; i++)
327 {
328 short_array[i] = 0;
329 }
330
331 #if 0
332 sscanf(value, "%hd %hd %hd %hd %hd %hd %hd %hd",
333 &short_array[0],
334 &short_array[1],
335 &short_array[2],
336 &short_array[3],
337 &short_array[4],
338 &short_array[5],
339 &short_array[6],
340 &short_array[7]);
341 #endif
342
343 /* There is no corresponding fCType for this. */
344 if(pcbValue)
345 *(SDWORD *) ((char *) pcbValue + pcbValueOffset) = len;
346 free(tempBuf);
347 return COPY_OK; /* dont go any further or the data will be trashed */
348 }
349
350 /* This is a large object OID, which is used to store LONGVARBINARY objects. */
351 case PG_TYPE_LO:
352 free(tempBuf);
353 return convert_lo( stmt, value, fCType, ((char *) rgbValue + rgbValueOffset), cbValueMax, (SDWORD *) ((char *) pcbValue + pcbValueOffset));
354
355 default:
356
357 if (field_type == stmt->hdbc->lobj_type) /* hack until permanent type available */
358 { free(tempBuf);
359 return convert_lo( stmt, value, fCType, ((char *) rgbValue + rgbValueOffset), cbValueMax, (SDWORD *) ((char *) pcbValue + pcbValueOffset));
360 }
361 }
362
363 /* Change default into something useable */
364 if (fCType == SQL_C_DEFAULT) {
365 fCType = pgtype_to_ctype(stmt, field_type);
366
367 mylog("copy_and_convert, SQL_C_DEFAULT: fCType = %d\n", fCType);
368 }
369
370
371 rgbValueBindRow = (char *) rgbValue + rgbValueOffset;
372
373 if(fCType == SQL_C_CHAR) {
374
375
376 /* Special character formatting as required */
377 /* These really should return error if cbValueMax is not big enough. */
378 switch(field_type) {
379 case PG_TYPE_DATE:
380 len = 10;
381 if (cbValueMax > len)
382 sprintf(rgbValueBindRow, "%.4d-%.2d-%.2d", st.y, st.m, st.d);
383 break;
384
385 case PG_TYPE_TIME:
386 len = 8;
387 if (cbValueMax > len)
388 sprintf(rgbValueBindRow, "%.2d:%.2d:%.2d", st.hh, st.mm, st.ss);
389 break;
390
391 case PG_TYPE_ABSTIME:
392 case PG_TYPE_DATETIME:
393 case PG_TYPE_TIMESTAMP:
394 case PG_TYPE_TIMESTAMP_NO_TMZONE:
395 len = 19;
396 if (cbValueMax > len)
397 sprintf(rgbValueBindRow, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
398 st.y, st.m, st.d, st.hh, st.mm, st.ss);
399 break;
400
401 case PG_TYPE_BOOL:
402 len = 1;
403 if (cbValueMax > len) {
404 strcpy(rgbValueBindRow, value);
405 mylog("PG_TYPE_BOOL: rgbValueBindRow = '%s'\n", rgbValueBindRow);
406 }
407 break;
408
409 /* Currently, data is SILENTLY TRUNCATED for BYTEA and character data
410 types if there is not enough room in cbValueMax because the driver
411 can't handle multiple calls to SQLGetData for these, yet. Most likely,
412 the buffer passed in will be big enough to handle the maximum limit of
413 postgres, anyway.
414
415 LongVarBinary types are handled correctly above, observing truncation
416 and all that stuff since there is essentially no limit on the large
417 object used to store those.
418 */
419 case PG_TYPE_BYTEA: /* convert binary data to hex strings (i.e, 255 = "FF") */
420 len = convert_pgbinary_to_char(value, rgbValueBindRow, cbValueMax);
421
422 /***** THIS IS NOT PROPERLY IMPLEMENTED *****/
423 break;
424
425 default:
426 /* convert linefeeds to carriage-return/linefeed */
427 len = convert_linefeeds(value, tempBuf, TEXT_FIELD_SIZE+5);
428 ptr = tempBuf;
429
430 mylog("DEFAULT: len = %d, ptr = '%s'\n", len, ptr);
431
432 if (stmt->current_col >= 0) {
433 if (stmt->bindings[stmt->current_col].data_left == 0) {
434 free( tempBuf );
435 return COPY_NO_DATA_FOUND;
436 }
437 else if (stmt->bindings[stmt->current_col].data_left > 0) {
438 ptr += len - stmt->bindings[stmt->current_col].data_left;
439 len = stmt->bindings[stmt->current_col].data_left;
440 }
441 else
442 stmt->bindings[stmt->current_col].data_left = strlen(ptr);
443 }
444
445 if (cbValueMax > 0) {
446
447 copy_len = (len >= cbValueMax) ? cbValueMax -1 : len;
448
449 #ifdef HAVE_LOCALE_H
450 switch (field_type) {
451 case PG_TYPE_FLOAT4:
452 case PG_TYPE_FLOAT8:
453 case PG_TYPE_NUMERIC:
454 {
455 struct lconv *lc;
456 char *new_string;
457 int i, j, dplen;
458
459 new_string = malloc( cbValueMax );
460 lc = localeconv();
461 dplen = strlen(lc->decimal_point);
462 for (i = 0, j = 0; (j < cbValueMax - 1) && ptr[i]; i++)
463 if (ptr[i] == '.') {
464 if ((j + dplen) <= (cbValueMax - 1)) {
465 strncpy(&new_string[j], lc->decimal_point, dplen);
466 j += dplen;
467 } else
468 break;
469 } else
470 new_string[j++] = ptr[i];
471 new_string[j] = '\0';
472 strncpy_null(rgbValueBindRow, new_string, copy_len + 1);
473 free(new_string);
474 break;
475 }
476 default:
477 /* Copy the data */
478 strncpy_null(rgbValueBindRow, ptr, copy_len + 1);
479 }
480 #else
481 /* Copy the data */
482 strncpy_null(rgbValueBindRow, ptr, copy_len + 1);
483 #endif
484
485 /* Adjust data_left for next time */
486 if (stmt->current_col >= 0) {
487 stmt->bindings[stmt->current_col].data_left -= copy_len;
488 }
489 }
490
491 /* Finally, check for truncation so that proper status can be returned */
492 if ( len >= cbValueMax)
493 result = COPY_RESULT_TRUNCATED;
494
495
496 mylog(" SQL_C_CHAR, default: len = %d, cbValueMax = %d, rgbValueBindRow = '%s'\n", len, cbValueMax, rgbValueBindRow);
497 break;
498 }
499
500
501 } else {
502
503 /* for SQL_C_CHAR, it's probably ok to leave currency symbols in. But
504 to convert to numeric types, it is necessary to get rid of those.
505 */
506 if (field_type == PG_TYPE_MONEY)
507 convert_money(value);
508
509 switch(fCType) {
510 case SQL_C_DATE:
511 len = 6;
512 {
513 DATE_STRUCT *ds;
514
515 if (bind_size > 0) {
516 ds = (DATE_STRUCT *) ((char *) rgbValue + (bind_row * bind_size));
517 } else {
518 ds = (DATE_STRUCT *) rgbValue + bind_row;
519 }
520 ds->year = st.y;
521 ds->month = st.m;
522 ds->day = st.d;
523 }
524 break;
525
526 case SQL_C_TIME:
527 len = 6;
528 {
529 TIME_STRUCT *ts;
530
531 if (bind_size > 0) {
532 ts = (TIME_STRUCT *) ((char *) rgbValue + (bind_row * bind_size));
533 } else {
534 ts = (TIME_STRUCT *) rgbValue + bind_row;
535 }
536 ts->hour = st.hh;
537 ts->minute = st.mm;
538 ts->second = st.ss;
539 }
540 break;
541
542 case SQL_C_TIMESTAMP:
543 len = 16;
544 {
545 TIMESTAMP_STRUCT *ts;
546 if (bind_size > 0) {
547 ts = (TIMESTAMP_STRUCT *) ((char *) rgbValue + (bind_row * bind_size));
548 } else {
549 ts = (TIMESTAMP_STRUCT *) rgbValue + bind_row;
550 }
551 ts->year = st.y;
552 ts->month = st.m;
553 ts->day = st.d;
554 ts->hour = st.hh;
555 ts->minute = st.mm;
556 ts->second = st.ss;
557 ts->fraction = 0;
558 }
559 break;
560
561 case SQL_C_BIT:
562 len = 1;
563 if (bind_size > 0) {
564 *(UCHAR *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(value);
565 } else {
566 *((UCHAR *)rgbValue + bind_row) = atoi(value);
567 }
568 /* mylog("SQL_C_BIT: val = %d, cb = %d, rgb=%d\n", atoi(value), cbValueMax, *((UCHAR *)rgbValue)); */
569 break;
570
571 case SQL_C_STINYINT:
572 case SQL_C_TINYINT:
573 len = 1;
574 if (bind_size > 0) {
575 *(SCHAR *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(value);
576 } else {
577 *((SCHAR *) rgbValue + bind_row) = atoi(value);
578 }
579 break;
580
581 case SQL_C_UTINYINT:
582 len = 1;
583 if (bind_size > 0) {
584 *(UCHAR *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(value);
585 } else {
586 *((UCHAR *) rgbValue + bind_row) = atoi(value);
587 }
588 break;
589
590 case SQL_C_FLOAT:
591 #ifdef HAVE_LOCALE_H
592 strcpy(saved_locale,setlocale(LC_ALL, NULL));
593 setlocale(LC_ALL, "C");
594 #endif
595 len = 4;
596 if (bind_size > 0) {
597 *(SFLOAT *) ((char *) rgbValue + (bind_row * bind_size)) = (float) atof(value);
598 } else {
599 *((SFLOAT *)rgbValue + bind_row) = (float) atof(value);
600 }
601 #ifdef HAVE_LOCALE_H
602 setlocale(LC_ALL, saved_locale);
603 #endif
604 break;
605
606 case SQL_C_DOUBLE:
607 #ifdef HAVE_LOCALE_H
608 strcpy(saved_locale, setlocale(LC_ALL, NULL));
609 setlocale(LC_ALL, "C");
610 #endif
611 len = 8;
612 if (bind_size > 0) {
613 *(SDOUBLE *) ((char *) rgbValue + (bind_row * bind_size)) = atof(value);
614 } else {
615 *((SDOUBLE *)rgbValue + bind_row) = atof(value);
616 }
617 #ifdef HAVE_LOCALE_H
618 setlocale(LC_ALL, saved_locale);
619 #endif
620 break;
621
622 case SQL_C_SSHORT:
623 case SQL_C_SHORT:
624 len = 2;
625 if (bind_size > 0) {
626 *(SWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(value);
627 } else {
628 *((SWORD *)rgbValue + bind_row) = atoi(value);
629 }
630 break;
631
632 case SQL_C_USHORT:
633 len = 2;
634 if (bind_size > 0) {
635 *(UWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(value);
636 } else {
637 *((UWORD *)rgbValue + bind_row) = atoi(value);
638 }
639 break;
640
641 case SQL_C_SLONG:
642 case SQL_C_LONG:
643 len = 4;
644 if (bind_size > 0) {
645 *(SDWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atol(value);
646 } else {
647 *((SDWORD *)rgbValue + bind_row) = atol(value);
648 }
649 break;
650
651 case SQL_C_ULONG:
652 len = 4;
653 if (bind_size > 0) {
654 *(UDWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atol(value);
655 } else {
656 *((UDWORD *)rgbValue + bind_row) = atol(value);
657 }
658 break;
659
660 #ifdef HAVE_LONG_LONG
661 case SQL_BIGINT:
662 {
663 long long lv;
664
665 len = 8;
666
667 #ifdef HAVE_ATOLL
668 lv = atoll( value );
669 #elif HAVE_STROLL
670 lv = strtoll( value, NULL, 10 );
671 #else
672 lv = atol( value );
673 #endif
674
675 if (bind_size > 0) {
676 *(long long *) ((char *) rgbValue + (bind_row * bind_size)) = lv;
677 } else {
678 *((long long *)rgbValue + bind_row) = lv;
679 }
680 }
681 break;
682 #endif
683
684 case SQL_C_BINARY:
685
686 /* truncate if necessary */
687 /* convert octal escapes to bytes */
688
689 len = convert_from_pgbinary(value, (SQLCHAR*)tempBuf, TEXT_FIELD_SIZE+5);
690 ptr = tempBuf;
691
692 if (stmt->current_col >= 0) {
693
694 /* No more data left for this column */
695 if (stmt->bindings[stmt->current_col].data_left == 0)
696 {
697 free(tempBuf);
698 return COPY_NO_DATA_FOUND;
699 }
700
701 /* Second (or more) call to SQLGetData so move the pointer */
702 else if (stmt->bindings[stmt->current_col].data_left > 0) {
703 ptr += len - stmt->bindings[stmt->current_col].data_left;
704 len = stmt->bindings[stmt->current_col].data_left;
705 }
706
707 /* First call to SQLGetData so initialize data_left */
708 else
709 stmt->bindings[stmt->current_col].data_left = len;
710
711 }
712
713 if (cbValueMax > 0) {
714 copy_len = (len > cbValueMax) ? cbValueMax : len;
715
716 /* Copy the data */
717 memcpy(rgbValueBindRow, ptr, copy_len);
718
719 /* Adjust data_left for next time */
720 if (stmt->current_col >= 0) {
721 stmt->bindings[stmt->current_col].data_left -= copy_len;
722 }
723 }
724
725 /* Finally, check for truncation so that proper status can be returned */
726 if ( len > cbValueMax)
727 result = COPY_RESULT_TRUNCATED;
728
729 mylog("SQL_C_BINARY: len = %d, copy_len = %d\n", len, copy_len);
730 break;
731
732 default:
733 free(tempBuf);
734 return COPY_UNSUPPORTED_TYPE;
735 }
736 }
737
738 /* store the length of what was copied, if there's a place for it */
739 if(pcbValue) {
740 *(SDWORD *) ((char *)pcbValue + pcbValueOffset) = len;
741 }
742 free(tempBuf);
743 return result;
744
745 }
746
747
748 /* This function inserts parameters into an SQL statements.
749 It will also modify a SELECT statement for use with declare/fetch cursors.
750 This function no longer does any dynamic memory allocation!
751 */
752 int
copy_statement_with_parameters(StatementClass * stmt)753 copy_statement_with_parameters(StatementClass *stmt)
754 {
755 static char* const func="copy_statement_with_parameters";
756 unsigned int opos, npos, oldstmtlen;
757 char param_string[1024], tmp[256];
758 char * cbuf; /* [TEXT_FIELD_SIZE+5]; */
759 int param_number;
760 Int2 param_ctype, param_sqltype;
761 char *old_statement = stmt->statement;
762 char *new_statement = stmt->stmt_with_params;
763 SIMPLE_TIME st;
764 time_t t = time(NULL);
765 struct tm *tim;
766 SDWORD used;
767 char *buffer, *buf;
768 char in_quote = FALSE;
769 Oid lobj_oid;
770 int lobj_fd, retval;
771
772 stmt -> reexecute = 0;
773 cbuf=(char *)malloc(TEXT_FIELD_SIZE+5);
774 if ( ! old_statement) {
775 SC_log_error(func, "No statement string", stmt);
776 free(cbuf);
777 return SQL_ERROR;
778 }
779
780 memset(&st, 0, sizeof(SIMPLE_TIME));
781
782 /* If the application hasn't set a cursor name, then generate one */
783 if ( stmt->cursor_name[0] == '\0')
784 sprintf(stmt->cursor_name, "SQL_CUR%p", stmt);
785
786 /* For selects, prepend a declare cursor to the statement */
787 if (stmt->statement_type == STMT_TYPE_SELECT && globals.use_declarefetch) {
788 sprintf(new_statement, "declare %s cursor for ", stmt->cursor_name);
789 npos = strlen(new_statement);
790 }
791 else {
792 new_statement[0] = '0';
793 npos = 0;
794 }
795
796 param_number = -1;
797
798 oldstmtlen = strlen(old_statement);
799
800 for (opos = 0; opos < oldstmtlen; opos++) {
801
802 /* Squeeze carriage-return/linefeed pairs to linefeed only */
803 if (old_statement[opos] == '\r' && opos+1 < oldstmtlen &&
804 old_statement[opos+1] == '\n') {
805 continue;
806 }
807
808 /* Handle literals (date, time, timestamp) and ODBC scalar functions */
809 else if (old_statement[opos] == '{') {
810 char *esc;
811 char *begin = &old_statement[opos + 1];
812 char *end = strchr(begin, '}');
813
814 if ( ! end)
815 continue;
816
817 *end = '\0';
818
819 esc = convert_escape(begin);
820 if (esc) {
821 memcpy(&new_statement[npos], esc, strlen(esc));
822 npos += strlen(esc);
823 }
824 else { /* it's not a valid literal so just copy */
825 *end = '}';
826 new_statement[npos++] = old_statement[opos];
827 continue;
828 }
829
830 opos += end - begin + 1;
831
832 *end = '}';
833
834 continue;
835 }
836
837 /* Can you have parameter markers inside of quotes? I dont think so.
838 All the queries I've seen expect the driver to put quotes if needed.
839 */
840 else if (old_statement[opos] == '?' && !in_quote)
841 ; /* ok */
842 else {
843 if (old_statement[opos] == '\'')
844 in_quote = (in_quote ? FALSE : TRUE);
845
846 new_statement[npos++] = old_statement[opos];
847 continue;
848 }
849
850
851
852 /****************************************************/
853 /* Its a '?' parameter alright */
854 /****************************************************/
855
856 param_number++;
857
858 if (param_number >= stmt->parameters_allocated)
859 {
860 strcpy(&new_statement[npos], "NULL");
861 npos += strlen("NULL");
862 stmt -> reexecute = 1;
863 continue;
864 }
865
866 /* Assign correct buffers based on data at exec param or not */
867 if ( stmt->parameters[param_number].data_at_exec) {
868 used = stmt->parameters[param_number].EXEC_used ? *stmt->parameters[param_number].EXEC_used : SQL_NTS;
869 buffer = stmt->parameters[param_number].EXEC_buffer;
870 }
871 else {
872 used = stmt->parameters[param_number].used ? *stmt->parameters[param_number].used : SQL_NTS;
873 buffer = stmt->parameters[param_number].buffer;
874 }
875
876 /* Handle NULL parameter data */
877 if (used == SQL_NULL_DATA) {
878 strcpy(&new_statement[npos], "NULL");
879 npos += 4;
880 continue;
881 }
882
883 /* If no buffer, and it's not null, then what the hell is it?
884 Just leave it alone then.
885 */
886 if ( ! buffer) {
887 new_statement[npos++] = '?';
888 continue;
889 }
890
891 param_ctype = stmt->parameters[param_number].CType;
892 param_sqltype = stmt->parameters[param_number].SQLType;
893
894 mylog("copy_statement_with_params: from(fcType)=%d, to(fSqlType)=%d\n", param_ctype, param_sqltype);
895
896 /* replace DEFAULT with something we can use */
897 if(param_ctype == SQL_C_DEFAULT)
898 param_ctype = sqltype_to_default_ctype(param_sqltype);
899
900 buf = NULL;
901 param_string[0] = '\0';
902 cbuf[0] = '\0';
903
904 /* Convert input C type to a neutral format */
905 switch(param_ctype) {
906 case SQL_C_BINARY:
907 buf = buffer;
908 break;
909
910 #ifdef HAVE_LOCALE_H
911 case SQL_C_CHAR:
912 if (param_sqltype == SQL_NUMERIC ||
913 param_sqltype == SQL_DECIMAL ||
914 param_sqltype == SQL_FLOAT ||
915 param_sqltype == SQL_REAL ||
916 param_sqltype == SQL_DOUBLE) {
917 struct lconv *lc;
918 int i, j, dplen;
919
920 lc = localeconv();
921 dplen = strlen(lc->decimal_point);
922 for (i = 0, j = 0; j < sizeof(param_string)-1 && buffer[i]; )
923 if (!strncmp(&buffer[i], lc->decimal_point, dplen)) {
924 param_string[j++] = '.';
925 i += dplen;
926 } else
927 param_string[j++] = buffer[i++];
928 param_string[j] = '\0';
929 break;
930 } else
931 buf = buffer;
932 break;
933 #else
934 case SQL_C_CHAR:
935 buf = buffer;
936 break;
937 #endif
938
939 case SQL_C_DOUBLE:
940 #ifdef HAVE_LOCALE_H
941 strcpy(tmp, setlocale(LC_ALL, NULL));
942 setlocale(LC_ALL, "C");
943 #endif
944 sprintf(param_string, "%g",
945 *((SDOUBLE *) buffer));
946 #ifdef HAVE_LOCALE_H
947 setlocale(LC_ALL, tmp);
948 #endif
949 break;
950
951 case SQL_C_FLOAT:
952 #ifdef HAVE_LOCALE_H
953 strcpy(tmp, setlocale(LC_ALL, NULL));
954 setlocale(LC_ALL, "C");
955 #endif
956 sprintf(param_string, "%g",
957 *((SFLOAT *) buffer));
958 #ifdef HAVE_LOCALE_H
959 setlocale(LC_ALL, tmp);
960 #endif
961 break;
962
963 case SQL_C_SLONG:
964 case SQL_C_LONG:
965 #if (SIZEOF_LONG_INT == 4)
966 sprintf(param_string, "%ld",
967 #else
968 sprintf(param_string, "%d",
969 #endif
970 *((SDWORD *) buffer));
971 break;
972
973 case SQL_C_SSHORT:
974 case SQL_C_SHORT:
975 sprintf(param_string, "%d",
976 *((SWORD *) buffer));
977 break;
978
979 case SQL_C_STINYINT:
980 case SQL_C_TINYINT:
981 sprintf(param_string, "%d",
982 *((SCHAR *) buffer));
983 break;
984
985 case SQL_C_ULONG:
986 #if (SIZEOF_LONG_INT == 4)
987 sprintf(param_string, "%lu",
988 #else
989 sprintf(param_string, "%u",
990 #endif
991 *((UDWORD *) buffer));
992 break;
993
994 case SQL_C_USHORT:
995 sprintf(param_string, "%u",
996 *((UWORD *) buffer));
997 break;
998
999 case SQL_C_UTINYINT:
1000 sprintf(param_string, "%u",
1001 *((UCHAR *) buffer));
1002 break;
1003
1004 case SQL_C_BIT: {
1005 int i = *((UCHAR *) buffer);
1006
1007 sprintf(param_string, "%d", i ? 1 : 0);
1008 break;
1009 }
1010
1011 case SQL_C_DATE: {
1012 DATE_STRUCT *ds = (DATE_STRUCT *) buffer;
1013 setup_ts( st );
1014 st.m = ds->month;
1015 st.d = ds->day;
1016 st.y = ds->year;
1017
1018 break;
1019 }
1020
1021 case SQL_C_TIME: {
1022 TIME_STRUCT *ts = (TIME_STRUCT *) buffer;
1023 setup_ts( st );
1024 st.hh = ts->hour;
1025 st.mm = ts->minute;
1026 st.ss = ts->second;
1027
1028 break;
1029 }
1030
1031 case SQL_C_TIMESTAMP: {
1032 TIMESTAMP_STRUCT *tss = (TIMESTAMP_STRUCT *) buffer;
1033 setup_ts( st );
1034 st.m = tss->month;
1035 st.d = tss->day;
1036 st.y = tss->year;
1037 st.hh = tss->hour;
1038 st.mm = tss->minute;
1039 st.ss = tss->second;
1040
1041 mylog("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);
1042
1043 break;
1044
1045 }
1046 default:
1047 /* error */
1048 SC_set_error(stmt, STMT_NOT_IMPLEMENTED_ERROR, "Unrecognized C_parameter type in copy_statement_with_parameters");
1049 new_statement[npos] = '\0'; /* just in case */
1050 SC_log_error(func, "", stmt);
1051 free(cbuf);
1052 return SQL_ERROR;
1053 }
1054
1055 /* Now that the input data is in a neutral format, convert it to
1056 the desired output format (sqltype)
1057 */
1058
1059 switch(param_sqltype) {
1060 case SQL_CHAR:
1061 case SQL_VARCHAR:
1062 case SQL_LONGVARCHAR:
1063
1064 new_statement[npos++] = '\''; /* Open Quote */
1065
1066 /* it was a SQL_C_CHAR */
1067 if (buf) {
1068 convert_special_chars(buf, &new_statement[npos], used);
1069 npos += strlen(&new_statement[npos]);
1070 }
1071
1072 /* it was a numeric type */
1073 else if (param_string[0] != '\0') {
1074 strcpy(&new_statement[npos], param_string);
1075 npos += strlen(param_string);
1076 }
1077
1078 /* it was date,time,timestamp -- use m,d,y,hh,mm,ss */
1079 else {
1080 setup_ts( st );
1081
1082 sprintf(tmp, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
1083 st.y, st.m, st.d, st.hh, st.mm, st.ss);
1084
1085 strcpy(&new_statement[npos], tmp);
1086 npos += strlen(tmp);
1087 }
1088
1089 new_statement[npos++] = '\''; /* Close Quote */
1090
1091 break;
1092
1093 case SQL_DATE:
1094 if (buf) { /* copy char data to time */
1095 my_strcpy(cbuf, TEXT_FIELD_SIZE+5, buf, used);
1096 parse_datetime(cbuf, &st);
1097 }
1098
1099 sprintf(tmp, "'%.4d-%.2d-%.2d'", st.y, st.m, st.d);
1100
1101 strcpy(&new_statement[npos], tmp);
1102 npos += strlen(tmp);
1103 break;
1104
1105 case SQL_TIME:
1106 if (buf) { /* copy char data to time */
1107 my_strcpy(cbuf, TEXT_FIELD_SIZE+5, buf, used);
1108 parse_datetime(cbuf, &st);
1109 }
1110
1111 sprintf(tmp, "'%.2d:%.2d:%.2d'", st.hh, st.mm, st.ss);
1112
1113 strcpy(&new_statement[npos], tmp);
1114 npos += strlen(tmp);
1115 break;
1116
1117 case SQL_TIMESTAMP:
1118
1119 if (buf) {
1120 my_strcpy(cbuf, TEXT_FIELD_SIZE+5, buf, used);
1121 parse_datetime(cbuf, &st);
1122 }
1123
1124 sprintf(tmp, "'%.4d-%.2d-%.2d %.2d:%.2d:%.2d'",
1125 st.y, st.m, st.d, st.hh, st.mm, st.ss);
1126
1127 strcpy(&new_statement[npos], tmp);
1128 npos += strlen(tmp);
1129
1130 break;
1131
1132 case SQL_BINARY:
1133 case SQL_VARBINARY: /* non-ascii characters should be converted to octal */
1134 new_statement[npos++] = '\''; /* Open Quote */
1135
1136 mylog("SQL_VARBINARY: about to call convert_to_pgbinary, used = %d\n", used);
1137
1138 npos += convert_to_pgbinary((SQLCHAR*)buf, &new_statement[npos], used);
1139
1140 new_statement[npos++] = '\''; /* Close Quote */
1141
1142 break;
1143
1144 case SQL_LONGVARBINARY:
1145
1146 if ( stmt->parameters[param_number].data_at_exec) {
1147
1148 lobj_oid = stmt->parameters[param_number].lobj_oid;
1149
1150 }
1151 else {
1152
1153 /* begin transaction if needed */
1154 if(!CC_is_in_trans(stmt->hdbc)) {
1155 QResultClass *res;
1156 char ok;
1157
1158 res = CC_send_query(stmt->hdbc, "BEGIN", NULL);
1159 if (!res) {
1160 SC_set_error(stmt, STMT_EXEC_ERROR, "Could not begin (in-line) a transaction");
1161 SC_log_error(func, "", stmt);
1162 free(cbuf);
1163 return SQL_ERROR;
1164 }
1165 ok = QR_command_successful(res);
1166 QR_Destructor(res);
1167 if (!ok) {
1168 SC_set_error(stmt, STMT_EXEC_ERROR, "Could not begin (in-line) a transaction");
1169 SC_log_error(func, "", stmt);
1170 free(cbuf);
1171 return SQL_ERROR;
1172 }
1173
1174 CC_set_in_trans(stmt->hdbc);
1175 }
1176
1177 /* store the oid */
1178 lobj_oid = odbc_lo_creat(stmt->hdbc, INV_READ | INV_WRITE);
1179 if (lobj_oid == 0) {
1180 SC_set_error(stmt, STMT_EXEC_ERROR, "Couldnt create (in-line) large object.");
1181 SC_log_error(func, "", stmt);
1182 free(cbuf);
1183 return SQL_ERROR;
1184 }
1185
1186 /* store the fd */
1187 lobj_fd = odbc_lo_open(stmt->hdbc, lobj_oid, INV_WRITE);
1188 if ( lobj_fd < 0) {
1189 SC_set_error(stmt, STMT_EXEC_ERROR, "Couldnt open (in-line) large object for writing.");
1190 SC_log_error(func, "", stmt);
1191 free(cbuf);
1192 return SQL_ERROR;
1193 }
1194
1195 retval = odbc_lo_write(stmt->hdbc, lobj_fd, buffer, used);
1196
1197 odbc_lo_close(stmt->hdbc, lobj_fd);
1198
1199 /* commit transaction if needed */
1200 if (!globals.use_declarefetch && CC_is_in_autocommit(stmt->hdbc)) {
1201 QResultClass *res;
1202 char ok;
1203
1204 res = CC_send_query(stmt->hdbc, "COMMIT", NULL);
1205 if (!res) {
1206 SC_set_error(stmt, STMT_EXEC_ERROR, "Could not commit (in-line) a transaction");
1207 SC_log_error(func, "", stmt);
1208 free(cbuf);
1209 return SQL_ERROR;
1210 }
1211 ok = QR_command_successful(res);
1212 QR_Destructor(res);
1213 if (!ok) {
1214 SC_set_error(stmt, STMT_EXEC_ERROR, "Could not commit (in-line) a transaction");
1215 SC_log_error(func, "", stmt);
1216 free(cbuf);
1217 return SQL_ERROR;
1218 }
1219
1220 CC_set_no_trans(stmt->hdbc);
1221 }
1222 }
1223
1224 /* the oid of the large object -- just put that in for the
1225 parameter marker -- the data has already been sent to the large object
1226 */
1227 sprintf(param_string, "'%d'", lobj_oid);
1228 strcpy(&new_statement[npos], param_string);
1229 npos += strlen(param_string);
1230
1231 break;
1232
1233 /* because of no conversion operator for bool and int4, SQL_BIT */
1234 /* must be quoted (0 or 1 is ok to use inside the quotes) */
1235
1236 case SQL_REAL:
1237 if (buf)
1238 my_strcpy(param_string, sizeof(param_string), buf, used);
1239 sprintf(tmp, "'%s'::float4", param_string);
1240 strcpy(&new_statement[npos], tmp);
1241 npos += strlen(tmp);
1242 break;
1243 case SQL_FLOAT:
1244 case SQL_DOUBLE:
1245 if (buf)
1246 my_strcpy(param_string, sizeof(param_string), buf, used);
1247 sprintf(tmp, "'%s'::float8", param_string);
1248 strcpy(&new_statement[npos], tmp);
1249 npos += strlen(tmp);
1250 break;
1251 case SQL_NUMERIC:
1252 if (buf)
1253 {
1254 cbuf[0] = '\'';
1255 my_strcpy(cbuf + 1, TEXT_FIELD_SIZE+5 - 12, buf, used); /* 12 = 1('\'') + strlen("'::numeric") + 1('\0') */
1256 strcat(cbuf, "'::numeric");
1257 }
1258 else
1259 sprintf(cbuf, "'%s'::numeric", param_string);
1260 my_strcpy(&new_statement[npos], sizeof(stmt->stmt_with_params) - npos - 1, cbuf, strlen(cbuf));
1261 npos += strlen(&new_statement[npos]);
1262 break;
1263 default: /* a numeric type or SQL_BIT */
1264 if (param_sqltype == SQL_BIT)
1265 new_statement[npos++] = '\''; /* Open Quote */
1266
1267 if (buf) {
1268 my_strcpy(&new_statement[npos], sizeof(stmt->stmt_with_params) - npos, buf, used);
1269 npos += strlen(&new_statement[npos]);
1270 }
1271 else {
1272 strcpy(&new_statement[npos], param_string);
1273 npos += strlen(param_string);
1274 }
1275
1276 if (param_sqltype == SQL_BIT)
1277 new_statement[npos++] = '\''; /* Close Quote */
1278
1279 break;
1280
1281 }
1282
1283 } /* end, for */
1284
1285 /* make sure new_statement is always null-terminated */
1286 new_statement[npos] = '\0';
1287
1288
1289 if(stmt->hdbc->DriverToDataSource != NULL) {
1290 int length = strlen (new_statement);
1291 stmt->hdbc->DriverToDataSource (stmt->hdbc->translation_option,
1292 SQL_CHAR,
1293 new_statement, length,
1294 new_statement, length, NULL,
1295 NULL, 0, NULL);
1296 }
1297
1298 free(cbuf);
1299
1300 return SQL_SUCCESS;
1301 }
1302
1303 char *
mapFunction(char * func)1304 mapFunction(char *func)
1305 {
1306 int i;
1307
1308 for (i = 0; mapFuncs[i][0]; i++)
1309 if ( ! stricmp(mapFuncs[i][0], func))
1310 return mapFuncs[i][1];
1311
1312 return NULL;
1313 }
1314
1315 /* convert_escape()
1316 * This function returns a pointer to static memory!
1317 */
1318 char *
convert_escape(char * value)1319 convert_escape(char *value)
1320 {
1321 static char escape[1024];
1322 char key[33];
1323
1324 /* Separate off the key, skipping leading and trailing whitespace */
1325 while ((*value != '\0') && isspace((unsigned char) *value)) value++;
1326 sscanf(value, "%32s", key);
1327 while ((*value != '\0') && (! isspace((unsigned char) *value))) value++;
1328 while ((*value != '\0') && isspace((unsigned char) *value)) value++;
1329
1330 mylog("convert_escape: key='%s', val='%s'\n", key, value);
1331
1332 if ( (strcmp(key, "d") == 0) ||
1333 (strcmp(key, "t") == 0) ||
1334 (strcmp(key, "ts") == 0) ||
1335 (stricmp(key, "oj") == 0)) {
1336 /* Literal; return the escape part as-is */
1337 strncpy(escape, value, sizeof(escape)-1);
1338 }
1339 else if (strcmp(key, "fn") == 0) {
1340 /* Function invocation
1341 * Separate off the func name,
1342 * skipping trailing whitespace.
1343 */
1344 char *funcEnd = value;
1345 char svchar;
1346 char *mapFunc;
1347
1348 while ((*funcEnd != '\0') && (*funcEnd != '(') &&
1349 (! isspace((unsigned char) *funcEnd)))
1350 funcEnd++;
1351 svchar = *funcEnd;
1352 *funcEnd = '\0';
1353 sscanf(value, "%32s", key);
1354 *funcEnd = svchar;
1355 while ((*funcEnd != '\0') && isspace((unsigned char) *funcEnd))
1356 funcEnd++;
1357
1358 /* We expect left parenthesis here,
1359 * else return fn body as-is since it is
1360 * one of those "function constants".
1361 */
1362 if (*funcEnd != '(') {
1363 strncpy(escape, value, sizeof(escape)-1);
1364 return escape;
1365 }
1366 mapFunc = mapFunction(key);
1367 /* We could have mapFunction() return key if not in table...
1368 * - thomas 2000-04-03
1369 */
1370 if (mapFunc == NULL) {
1371 /* If unrecognized function name, return fn body as-is */
1372 strncpy(escape, value, sizeof(escape)-1);
1373 return escape;
1374 }
1375 /* copy mapped name and remaining input string */
1376 strcpy(escape, mapFunc);
1377 strncat(escape, funcEnd, sizeof(escape)-1-strlen(mapFunc));
1378 }
1379 else {
1380 /* Bogus key, leave untranslated */
1381 return NULL;
1382 }
1383
1384 return escape;
1385
1386 }
1387
1388
1389 char *
convert_money(char * s)1390 convert_money(char *s)
1391 {
1392 size_t i = 0, out = 0, slen=strlen(s);
1393
1394 for (i = 0; i < slen; i++) {
1395 if (s[i] == '$' || s[i] == ',' || s[i] == ')')
1396 ; /* skip these characters */
1397 else if (s[i] == '(')
1398 s[out++] = '-';
1399 else
1400 s[out++] = s[i];
1401 }
1402 s[out] = '\0';
1403 return s;
1404 }
1405
1406
1407
1408 /* This function parses a character string for date/time info and fills in SIMPLE_TIME */
1409 /* It does not zero out SIMPLE_TIME in case it is desired to initialize it with a value */
1410 char
parse_datetime(char * buf,SIMPLE_TIME * st)1411 parse_datetime(char *buf, SIMPLE_TIME *st)
1412 {
1413 int y,m,d,hh,mm,ss;
1414 int nf;
1415
1416 y = m = d = hh = mm = ss = 0;
1417
1418 if (buf[4] == '-') /* year first */
1419 nf = sscanf(buf, "%4d-%2d-%2d %2d:%2d:%2d", &y,&m,&d,&hh,&mm,&ss);
1420 else
1421 nf = sscanf(buf, "%2d-%2d-%4d %2d:%2d:%2d", &m,&d,&y,&hh,&mm,&ss);
1422
1423 if (nf == 5 || nf == 6) {
1424 st->y = y;
1425 st->m = m;
1426 st->d = d;
1427 st->hh = hh;
1428 st->mm = mm;
1429 st->ss = ss;
1430
1431 return TRUE;
1432 }
1433
1434 if (buf[4] == '-') /* year first */
1435 nf = sscanf(buf, "%4d-%2d-%2d", &y, &m, &d);
1436 else
1437 nf = sscanf(buf, "%2d-%2d-%4d", &m, &d, &y);
1438
1439 if (nf == 3) {
1440 st->y = y;
1441 st->m = m;
1442 st->d = d;
1443
1444 return TRUE;
1445 }
1446
1447 nf = sscanf(buf, "%2d:%2d:%2d", &hh, &mm, &ss);
1448 if (nf == 2 || nf == 3) {
1449 st->hh = hh;
1450 st->mm = mm;
1451 st->ss = ss;
1452
1453 return TRUE;
1454 }
1455
1456 return FALSE;
1457 }
1458
1459 /* Change linefeed to carriage-return/linefeed */
1460 int
convert_linefeeds(char * si,char * dst,size_t max)1461 convert_linefeeds(char *si, char *dst, size_t max)
1462 {
1463 size_t i = 0, out = 0;
1464
1465 for (i = 0; si[ i ] && out < max - 1; i++) {
1466 if (si[i] == '\n') {
1467 /* Only add the carriage-return if needed */
1468 if (i > 0 && si[i-1] == '\r') {
1469 dst[out++] = si[i];
1470 continue;
1471 }
1472
1473 dst[out++] = '\r';
1474 dst[out++] = '\n';
1475 }
1476 else
1477 dst[out++] = si[i];
1478 }
1479 dst[out] = '\0';
1480 return out;
1481 }
1482
1483 /* Change carriage-return/linefeed to just linefeed
1484 Plus, escape any special characters.
1485 */
1486 char *
convert_special_chars(char * si,char * dst,int used)1487 convert_special_chars(char *si, char *dst, int used)
1488 {
1489 size_t i = 0, out = 0, max;
1490 /*static char sout[TEXT_FIELD_SIZE+5];*/
1491 char *p;
1492 int in_len = strlen( si );
1493
1494 if (dst)
1495 p = dst;
1496 else
1497 /*
1498 p = sout;
1499 */
1500 {
1501 printf("BUG !!! convert_special_chars\n");
1502 exit(0);
1503 }
1504 p[0] = '\0';
1505
1506 if (used == SQL_NTS)
1507 max = strlen(si);
1508 else
1509 max = used;
1510
1511 for (i = 0; i < max; i++) {
1512 if (si[i] == '\r' && i+1 < in_len && si[i+1] == '\n')
1513 continue;
1514 else if (si[i] == '\'' || si[i] == '\\')
1515 p[out++] = '\\';
1516
1517 p[out++] = si[i];
1518 }
1519 p[out] = '\0';
1520 return p;
1521 }
1522
1523 /* !!! Need to implement this function !!! */
1524 int
convert_pgbinary_to_char(char * value,char * rgbValue,int cbValueMax)1525 convert_pgbinary_to_char(char *value, char *rgbValue, int cbValueMax)
1526 {
1527 mylog("convert_pgbinary_to_char: value = '%s'\n", value);
1528
1529 strncpy_null(rgbValue, value, cbValueMax);
1530 return 0;
1531 }
1532
1533 unsigned int
conv_from_octal(unsigned char * s)1534 conv_from_octal(unsigned char *s)
1535 {
1536 int i, y=0;
1537
1538 for (i = 1; i <= 3; i++) {
1539 y += (s[i] - 48) * (int) pow(8, 3-i);
1540 }
1541
1542 return y;
1543
1544 }
1545
1546 unsigned int
conv_from_hex(unsigned char * s)1547 conv_from_hex(unsigned char *s)
1548 {
1549 int i, y=0, val;
1550
1551 for (i = 1; i <= 2; i++) {
1552
1553 if (s[i] >= 'a' && s[i] <= 'f')
1554 val = s[i] - 'a' + 10;
1555 else if (s[i] >= 'A' && s[i] <= 'F')
1556 val = s[i] - 'A' + 10;
1557 else
1558 val = s[i] - '0';
1559
1560 y += val * (int) pow(16, 2-i);
1561 }
1562
1563 return y;
1564 }
1565
1566 /* convert octal escapes to bytes */
1567 int
convert_from_pgbinary(unsigned char * value,unsigned char * rgbValue,int cbValueMax)1568 convert_from_pgbinary(unsigned char *value, unsigned char *rgbValue, int cbValueMax)
1569 {
1570 int o=0;
1571 size_t i, valen=strlen((char*)value);;
1572
1573
1574 for (i = 0; i < valen && o < cbValueMax; )
1575 {
1576 if (value[i] == '\\')
1577 {
1578 rgbValue[o] = conv_from_octal(&value[i]);
1579 i += 4;
1580 }
1581 else
1582 {
1583 rgbValue[o] = value[i++];
1584 }
1585 mylog("convert_from_pgbinary: i=%d, rgbValue[%d] = %d, %c\n", i, o, rgbValue[o], rgbValue[o]);
1586 o++;
1587 }
1588
1589 rgbValue[o] = '\0'; /* extra protection */
1590
1591 return o;
1592 }
1593
1594
1595 char *
conv_to_octal(unsigned char val)1596 conv_to_octal(unsigned char val)
1597 {
1598 int i;
1599 static char x[6];
1600
1601 x[0] = '\\';
1602 x[1] = '\\';
1603 x[5] = '\0';
1604
1605 for (i = 4; i > 1; i--) {
1606 x[i] = (val & 7) + 48;
1607 val >>= 3;
1608 }
1609
1610 return x;
1611 }
1612
1613 /* convert non-ascii bytes to octal escape sequences */
1614 int
convert_to_pgbinary(unsigned char * in,char * out,int len)1615 convert_to_pgbinary(unsigned char *in, char *out, int len)
1616 {
1617 int i, o=0;
1618
1619
1620 for (i = 0; i < len; i++) {
1621 mylog("convert_to_pgbinary: in[%d] = %d, %c\n", i, in[i], in[i]);
1622 if ( isalnum(in[i]) || in[i] == ' ') {
1623 out[o++] = in[i];
1624 }
1625 else {
1626 strcpy(&out[o], conv_to_octal(in[i]));
1627 o += 5;
1628 }
1629
1630 }
1631
1632 mylog("convert_to_pgbinary: returning %d, out='%.*s'\n", o, o, out);
1633
1634 return o;
1635 }
1636
1637
1638 void
encode(char * in,char * out)1639 encode(char *in, char *out)
1640 {
1641 unsigned int i, o = 0;
1642 size_t inlen=strlen(in);
1643
1644 for (i = 0; i < inlen; i++) {
1645 if ( in[i] == '+') {
1646 sprintf(&out[o], "%%2B");
1647 o += 3;
1648 }
1649 else if ( isspace((unsigned char) in[i])) {
1650 out[o++] = '+';
1651 }
1652 else if ( ! isalnum((unsigned char) in[i])) {
1653 sprintf(&out[o], "%%%02x", (unsigned char) in[i]);
1654 o += 3;
1655 }
1656 else
1657 out[o++] = in[i];
1658 }
1659 out[o++] = '\0';
1660 }
1661
1662
1663 void
decode(char * in,char * out)1664 decode(char *in, char *out)
1665 {
1666 unsigned int i, o = 0;
1667 size_t stlen=strlen(in);
1668
1669 for(i=0; i < stlen; i++) {
1670 if (in[i] == '+')
1671 out[o++] = ' ';
1672 else if (in[i] == '%') {
1673 sprintf(&out[o++], "%c", conv_from_hex((SQLCHAR*)&in[i]));
1674 i+=2;
1675 }
1676 else
1677 out[o++] = in[i];
1678 }
1679 out[o++] = '\0';
1680 }
1681
1682
1683
1684 /* 1. get oid (from 'value')
1685 2. open the large object
1686 3. read from the large object (handle multiple GetData)
1687 4. close when read less than requested? -OR-
1688 lseek/read each time
1689 handle case where application receives truncated and
1690 decides not to continue reading.
1691
1692 CURRENTLY, ONLY LONGVARBINARY is handled, since that is the only
1693 data type currently mapped to a PG_TYPE_LO. But, if any other types
1694 are desired to map to a large object (PG_TYPE_LO), then that would
1695 need to be handled here. For example, LONGVARCHAR could possibly be
1696 mapped to PG_TYPE_LO someday, instead of PG_TYPE_TEXT as it is now.
1697 */
1698 int
convert_lo(StatementClass * stmt,void * value,Int2 fCType,PTR rgbValue,SDWORD cbValueMax,SDWORD * pcbValue)1699 convert_lo(StatementClass *stmt, void *value, Int2 fCType, PTR rgbValue,
1700 SDWORD cbValueMax, SDWORD *pcbValue)
1701 {
1702 Oid oid;
1703 int retval, result, left = -1;
1704 BindInfoClass *bindInfo = NULL;
1705
1706
1707 /* If using SQLGetData, then current_col will be set */
1708 if (stmt->current_col >= 0) {
1709 bindInfo = &stmt->bindings[stmt->current_col];
1710 left = bindInfo->data_left;
1711 }
1712
1713 /* if this is the first call for this column,
1714 open the large object for reading
1715 */
1716
1717 if ( ! bindInfo || bindInfo->data_left == -1) {
1718
1719 /* begin transaction if needed */
1720 if(!CC_is_in_trans(stmt->hdbc)) {
1721 QResultClass *res;
1722 char ok;
1723
1724 res = CC_send_query(stmt->hdbc, "BEGIN", NULL);
1725 if (!res) {
1726 SC_set_error(stmt, STMT_EXEC_ERROR, "Could not begin (in-line) a transaction");
1727 return COPY_GENERAL_ERROR;
1728 }
1729 ok = QR_command_successful(res);
1730 QR_Destructor(res);
1731 if (!ok) {
1732 SC_set_error(stmt, STMT_EXEC_ERROR, "Could not begin (in-line) a transaction");
1733 return COPY_GENERAL_ERROR;
1734 }
1735
1736 CC_set_in_trans(stmt->hdbc);
1737 }
1738
1739 oid = atoi(value);
1740 stmt->lobj_fd = odbc_lo_open(stmt->hdbc, oid, INV_READ);
1741 if (stmt->lobj_fd < 0) {
1742 SC_set_error(stmt, STMT_EXEC_ERROR, "Couldnt open large object for reading.");
1743 return COPY_GENERAL_ERROR;
1744 }
1745
1746 /* Get the size */
1747 retval = odbc_lo_lseek(stmt->hdbc, stmt->lobj_fd, 0L, SEEK_END);
1748 if (retval >= 0) {
1749
1750 left = odbc_lo_tell(stmt->hdbc, stmt->lobj_fd);
1751 if (bindInfo)
1752 bindInfo->data_left = left;
1753
1754 /* return to beginning */
1755 odbc_lo_lseek(stmt->hdbc, stmt->lobj_fd, 0L, SEEK_SET);
1756 }
1757 }
1758
1759 if (left == 0) {
1760 return COPY_NO_DATA_FOUND;
1761 }
1762
1763 if (stmt->lobj_fd < 0) {
1764 SC_set_error(stmt, STMT_EXEC_ERROR, "Large object FD undefined for multiple read.");
1765 return COPY_GENERAL_ERROR;
1766 }
1767
1768 retval = odbc_lo_read(stmt->hdbc, stmt->lobj_fd, (char *) rgbValue, cbValueMax);
1769 if (retval < 0) {
1770 odbc_lo_close(stmt->hdbc, stmt->lobj_fd);
1771
1772 /* commit transaction if needed */
1773 if (!globals.use_declarefetch && CC_is_in_autocommit(stmt->hdbc)) {
1774 QResultClass *res;
1775 char ok;
1776
1777 res = CC_send_query(stmt->hdbc, "COMMIT", NULL);
1778 if (!res) {
1779 SC_set_error(stmt, STMT_EXEC_ERROR, "Could not commit (in-line) a transaction");
1780 return COPY_GENERAL_ERROR;
1781 }
1782 ok = QR_command_successful(res);
1783 QR_Destructor(res);
1784 if (!ok) {
1785 SC_set_error(stmt, STMT_EXEC_ERROR, "Could not commit (in-line) a transaction");
1786 return COPY_GENERAL_ERROR;
1787 }
1788
1789 CC_set_no_trans(stmt->hdbc);
1790 }
1791
1792 stmt->lobj_fd = -1;
1793
1794 SC_set_error(stmt, STMT_EXEC_ERROR, "Error reading from large object.");
1795 return COPY_GENERAL_ERROR;
1796 }
1797
1798 if (retval < left)
1799 result = COPY_RESULT_TRUNCATED;
1800 else
1801 result = COPY_OK;
1802
1803 if (pcbValue)
1804 *pcbValue = left < 0 ? SQL_NO_TOTAL : left;
1805
1806
1807 if (bindInfo && bindInfo->data_left > 0)
1808 bindInfo->data_left -= retval;
1809
1810
1811 if (! bindInfo || bindInfo->data_left == 0) {
1812 odbc_lo_close(stmt->hdbc, stmt->lobj_fd);
1813
1814 /* commit transaction if needed */
1815 if (!globals.use_declarefetch && CC_is_in_autocommit(stmt->hdbc)) {
1816 QResultClass *res;
1817 char ok;
1818
1819 res = CC_send_query(stmt->hdbc, "COMMIT", NULL);
1820 if (!res) {
1821 SC_set_error(stmt, STMT_EXEC_ERROR, "Could not commit (in-line) a transaction");
1822 return COPY_GENERAL_ERROR;
1823 }
1824 ok = QR_command_successful(res);
1825 QR_Destructor(res);
1826 if (!ok) {
1827 SC_set_error(stmt, STMT_EXEC_ERROR, "Could not commit (in-line) a transaction");
1828 return COPY_GENERAL_ERROR;
1829 }
1830
1831 CC_set_no_trans(stmt->hdbc);
1832 }
1833
1834 stmt->lobj_fd = -1; /* prevent further reading */
1835 }
1836
1837
1838 return result;
1839
1840 }
1841