1 /* src/interfaces/ecpg/ecpglib/data.c */
2 
3 #define POSTGRES_ECPG_INTERNAL
4 #include "postgres_fe.h"
5 
6 #include <stdlib.h>
7 #include <string.h>
8 #include <float.h>
9 #include <math.h>
10 
11 #include "ecpgtype.h"
12 #include "ecpglib.h"
13 #include "ecpgerrno.h"
14 #include "extern.h"
15 #include "sqlca.h"
16 #include "pgtypes_numeric.h"
17 #include "pgtypes_date.h"
18 #include "pgtypes_timestamp.h"
19 #include "pgtypes_interval.h"
20 
21 /* returns true if character c is a delimiter for the given array type */
22 static bool
array_delimiter(enum ARRAY_TYPE isarray,char c)23 array_delimiter(enum ARRAY_TYPE isarray, char c)
24 {
25 	if (isarray == ECPG_ARRAY_ARRAY && c == ',')
26 		return true;
27 
28 	if (isarray == ECPG_ARRAY_VECTOR && c == ' ')
29 		return true;
30 
31 	return false;
32 }
33 
34 /* returns true if character c marks the boundary for the given array type */
35 static bool
array_boundary(enum ARRAY_TYPE isarray,char c)36 array_boundary(enum ARRAY_TYPE isarray, char c)
37 {
38 	if (isarray == ECPG_ARRAY_ARRAY && c == '}')
39 		return true;
40 
41 	if (isarray == ECPG_ARRAY_VECTOR && c == '\0')
42 		return true;
43 
44 	return false;
45 }
46 
47 /* returns true if some garbage is found at the end of the scanned string */
48 static bool
garbage_left(enum ARRAY_TYPE isarray,char ** scan_length,enum COMPAT_MODE compat)49 garbage_left(enum ARRAY_TYPE isarray, char **scan_length, enum COMPAT_MODE compat)
50 {
51 	/*
52 	 * INFORMIX allows for selecting a numeric into an int, the result is
53 	 * truncated
54 	 */
55 	if (isarray == ECPG_ARRAY_NONE)
56 	{
57 		if (INFORMIX_MODE(compat) && **scan_length == '.')
58 		{
59 			/* skip invalid characters */
60 			do {
61 				(*scan_length)++;
62 			} while (isdigit((unsigned char) **scan_length));
63 		}
64 
65 		if (**scan_length != ' ' && **scan_length != '\0')
66 			return true;
67 	}
68 	else if (ECPG_IS_ARRAY(isarray) && !array_delimiter(isarray, **scan_length) && !array_boundary(isarray, **scan_length))
69 		return true;
70 
71 	return false;
72 }
73 
74 /* stolen code from src/backend/utils/adt/float.c */
75 #if defined(WIN32) && !defined(NAN)
76 static const uint32 nan[2] = {0xffffffff, 0x7fffffff};
77 
78 #define NAN (*(const double *) nan)
79 #endif
80 
81 static double
get_float8_infinity(void)82 get_float8_infinity(void)
83 {
84 #ifdef INFINITY
85 	return (double) INFINITY;
86 #else
87 	return (double) (HUGE_VAL * HUGE_VAL);
88 #endif
89 }
90 
91 static double
get_float8_nan(void)92 get_float8_nan(void)
93 {
94 	/* (double) NAN doesn't work on some NetBSD/MIPS releases */
95 #if defined(NAN) && !(defined(__NetBSD__) && defined(__mips__))
96 	return (double) NAN;
97 #else
98 	return (double) (0.0 / 0.0);
99 #endif
100 }
101 
102 static bool
check_special_value(char * ptr,double * retval,char ** endptr)103 check_special_value(char *ptr, double *retval, char **endptr)
104 {
105 	if (pg_strncasecmp(ptr, "NaN", 3) == 0)
106 	{
107 		*retval = get_float8_nan();
108 		*endptr = ptr + 3;
109 		return true;
110 	}
111 	else if (pg_strncasecmp(ptr, "Infinity", 8) == 0)
112 	{
113 		*retval = get_float8_infinity();
114 		*endptr = ptr + 8;
115 		return true;
116 	}
117 	else if (pg_strncasecmp(ptr, "-Infinity", 9) == 0)
118 	{
119 		*retval = -get_float8_infinity();
120 		*endptr = ptr + 9;
121 		return true;
122 	}
123 
124 	return false;
125 }
126 
127 bool
ecpg_get_data(const PGresult * results,int act_tuple,int act_field,int lineno,enum ECPGttype type,enum ECPGttype ind_type,char * var,char * ind,long varcharsize,long offset,long ind_offset,enum ARRAY_TYPE isarray,enum COMPAT_MODE compat,bool force_indicator)128 ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
129 			  enum ECPGttype type, enum ECPGttype ind_type,
130 			  char *var, char *ind, long varcharsize, long offset,
131 			  long ind_offset, enum ARRAY_TYPE isarray, enum COMPAT_MODE compat, bool force_indicator)
132 {
133 	struct sqlca_t *sqlca = ECPGget_sqlca();
134 	char	   *pval = (char *) PQgetvalue(results, act_tuple, act_field);
135 	int			binary = PQfformat(results, act_field);
136 	int			size = PQgetlength(results, act_tuple, act_field);
137 	int			value_for_indicator = 0;
138 	long		log_offset;
139 
140 	if (sqlca == NULL)
141 	{
142 		ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
143 				   ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
144 		return (false);
145 	}
146 
147 	/*
148 	 * If we are running in a regression test, do not log the offset variable,
149 	 * it depends on the machine's alignment.
150 	 */
151 	if (ecpg_internal_regression_mode)
152 		log_offset = -1;
153 	else
154 		log_offset = offset;
155 
156 	ecpg_log("ecpg_get_data on line %d: RESULT: %s offset: %ld; array: %s\n", lineno, pval ? (binary ? "BINARY" : pval) : "EMPTY", log_offset, ECPG_IS_ARRAY(isarray) ? "yes" : "no");
157 
158 	/* pval is a pointer to the value */
159 	if (!pval)
160 	{
161 		/*
162 		 * This should never happen because we already checked that we found
163 		 * at least one tuple, but let's play it safe.
164 		 */
165 		ecpg_raise(lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL);
166 		return (false);
167 	}
168 
169 	/* We will have to decode the value */
170 
171 	/*
172 	 * check for null value and set indicator accordingly, i.e. -1 if NULL and
173 	 * 0 if not
174 	 */
175 	if (PQgetisnull(results, act_tuple, act_field))
176 		value_for_indicator = -1;
177 
178 	switch (ind_type)
179 	{
180 		case ECPGt_short:
181 		case ECPGt_unsigned_short:
182 			*((short *) (ind + ind_offset * act_tuple)) = value_for_indicator;
183 			break;
184 		case ECPGt_int:
185 		case ECPGt_unsigned_int:
186 			*((int *) (ind + ind_offset * act_tuple)) = value_for_indicator;
187 			break;
188 		case ECPGt_long:
189 		case ECPGt_unsigned_long:
190 			*((long *) (ind + ind_offset * act_tuple)) = value_for_indicator;
191 			break;
192 #ifdef HAVE_LONG_LONG_INT
193 		case ECPGt_long_long:
194 		case ECPGt_unsigned_long_long:
195 			*((long long int *) (ind + ind_offset * act_tuple)) = value_for_indicator;
196 			break;
197 #endif   /* HAVE_LONG_LONG_INT */
198 		case ECPGt_NO_INDICATOR:
199 			if (value_for_indicator == -1)
200 			{
201 				if (force_indicator == false)
202 				{
203 					/*
204 					 * Informix has an additional way to specify NULLs note
205 					 * that this uses special values to denote NULL
206 					 */
207 					ECPGset_noind_null(type, var + offset * act_tuple);
208 				}
209 				else
210 				{
211 					ecpg_raise(lineno, ECPG_MISSING_INDICATOR,
212 							 ECPG_SQLSTATE_NULL_VALUE_NO_INDICATOR_PARAMETER,
213 							   NULL);
214 					return (false);
215 				}
216 			}
217 			break;
218 		default:
219 			ecpg_raise(lineno, ECPG_UNSUPPORTED,
220 					   ECPG_SQLSTATE_ECPG_INTERNAL_ERROR,
221 					   ecpg_type_name(ind_type));
222 			return (false);
223 			break;
224 	}
225 
226 	if (value_for_indicator == -1)
227 		return (true);
228 
229 	/* let's check if it really is an array if it should be one */
230 	if (isarray == ECPG_ARRAY_ARRAY)
231 	{
232 		if (*pval != '{')
233 		{
234 			ecpg_raise(lineno, ECPG_DATA_NOT_ARRAY,
235 					   ECPG_SQLSTATE_DATATYPE_MISMATCH, NULL);
236 			return (false);
237 		}
238 
239 		switch (type)
240 		{
241 			case ECPGt_char:
242 			case ECPGt_unsigned_char:
243 			case ECPGt_varchar:
244 			case ECPGt_string:
245 				break;
246 
247 			default:
248 				pval++;
249 				break;
250 		}
251 	}
252 
253 	do
254 	{
255 		if (binary)
256 		{
257 			if (varcharsize == 0 || varcharsize * offset >= size)
258 				memcpy(var + offset * act_tuple, pval, size);
259 			else
260 			{
261 				memcpy(var + offset * act_tuple, pval, varcharsize * offset);
262 
263 				if (varcharsize * offset < size)
264 				{
265 					/* truncation */
266 					switch (ind_type)
267 					{
268 						case ECPGt_short:
269 						case ECPGt_unsigned_short:
270 							*((short *) (ind + ind_offset * act_tuple)) = size;
271 							break;
272 						case ECPGt_int:
273 						case ECPGt_unsigned_int:
274 							*((int *) (ind + ind_offset * act_tuple)) = size;
275 							break;
276 						case ECPGt_long:
277 						case ECPGt_unsigned_long:
278 							*((long *) (ind + ind_offset * act_tuple)) = size;
279 							break;
280 #ifdef HAVE_LONG_LONG_INT
281 						case ECPGt_long_long:
282 						case ECPGt_unsigned_long_long:
283 							*((long long int *) (ind + ind_offset * act_tuple)) = size;
284 							break;
285 #endif   /* HAVE_LONG_LONG_INT */
286 						default:
287 							break;
288 					}
289 					sqlca->sqlwarn[0] = sqlca->sqlwarn[1] = 'W';
290 				}
291 			}
292 			pval += size;
293 		}
294 		else
295 		{
296 			switch (type)
297 			{
298 					long		res;
299 					unsigned long ures;
300 					double		dres;
301 					char	   *scan_length;
302 					numeric    *nres;
303 					date		ddres;
304 					timestamp	tres;
305 					interval   *ires;
306 					char	   *endptr,
307 								endchar;
308 
309 				case ECPGt_short:
310 				case ECPGt_int:
311 				case ECPGt_long:
312 					res = strtol(pval, &scan_length, 10);
313 					if (garbage_left(isarray, &scan_length, compat))
314 					{
315 						ecpg_raise(lineno, ECPG_INT_FORMAT,
316 								   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
317 						return (false);
318 					}
319 					pval = scan_length;
320 
321 					switch (type)
322 					{
323 						case ECPGt_short:
324 							*((short *) (var + offset * act_tuple)) = (short) res;
325 							break;
326 						case ECPGt_int:
327 							*((int *) (var + offset * act_tuple)) = (int) res;
328 							break;
329 						case ECPGt_long:
330 							*((long *) (var + offset * act_tuple)) = (long) res;
331 							break;
332 						default:
333 							/* Cannot happen */
334 							break;
335 					}
336 					break;
337 
338 				case ECPGt_unsigned_short:
339 				case ECPGt_unsigned_int:
340 				case ECPGt_unsigned_long:
341 					ures = strtoul(pval, &scan_length, 10);
342 					if (garbage_left(isarray, &scan_length, compat))
343 					{
344 						ecpg_raise(lineno, ECPG_UINT_FORMAT,
345 								   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
346 						return (false);
347 					}
348 					pval = scan_length;
349 
350 					switch (type)
351 					{
352 						case ECPGt_unsigned_short:
353 							*((unsigned short *) (var + offset * act_tuple)) = (unsigned short) ures;
354 							break;
355 						case ECPGt_unsigned_int:
356 							*((unsigned int *) (var + offset * act_tuple)) = (unsigned int) ures;
357 							break;
358 						case ECPGt_unsigned_long:
359 							*((unsigned long *) (var + offset * act_tuple)) = (unsigned long) ures;
360 							break;
361 						default:
362 							/* Cannot happen */
363 							break;
364 					}
365 					break;
366 
367 #ifdef HAVE_LONG_LONG_INT
368 #ifdef HAVE_STRTOLL
369 				case ECPGt_long_long:
370 					*((long long int *) (var + offset * act_tuple)) = strtoll(pval, &scan_length, 10);
371 					if (garbage_left(isarray, &scan_length, compat))
372 					{
373 						ecpg_raise(lineno, ECPG_INT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
374 						return (false);
375 					}
376 					pval = scan_length;
377 
378 					break;
379 #endif   /* HAVE_STRTOLL */
380 #ifdef HAVE_STRTOULL
381 				case ECPGt_unsigned_long_long:
382 					*((unsigned long long int *) (var + offset * act_tuple)) = strtoull(pval, &scan_length, 10);
383 					if (garbage_left(isarray, &scan_length, compat))
384 					{
385 						ecpg_raise(lineno, ECPG_UINT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
386 						return (false);
387 					}
388 					pval = scan_length;
389 
390 					break;
391 #endif   /* HAVE_STRTOULL */
392 #endif   /* HAVE_LONG_LONG_INT */
393 
394 				case ECPGt_float:
395 				case ECPGt_double:
396 					if (isarray && *pval == '"')
397 						pval++;
398 
399 					if (!check_special_value(pval, &dres, &scan_length))
400 						dres = strtod(pval, &scan_length);
401 
402 					if (isarray && *scan_length == '"')
403 						scan_length++;
404 
405 					/* no special INFORMIX treatment for floats */
406 					if (garbage_left(isarray, &scan_length, ECPG_COMPAT_PGSQL))
407 					{
408 						ecpg_raise(lineno, ECPG_FLOAT_FORMAT,
409 								   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
410 						return (false);
411 					}
412 					pval = scan_length;
413 
414 					switch (type)
415 					{
416 						case ECPGt_float:
417 							*((float *) (var + offset * act_tuple)) = dres;
418 							break;
419 						case ECPGt_double:
420 							*((double *) (var + offset * act_tuple)) = dres;
421 							break;
422 						default:
423 							/* Cannot happen */
424 							break;
425 					}
426 					break;
427 
428 				case ECPGt_bool:
429 					if (pval[0] == 'f' && pval[1] == '\0')
430 					{
431 						*((bool *) (var + offset * act_tuple)) = false;
432 						pval++;
433 						break;
434 					}
435 					else if (pval[0] == 't' && pval[1] == '\0')
436 					{
437 						*((bool *) (var + offset * act_tuple)) = true;
438 						pval++;
439 						break;
440 					}
441 					else if (pval[0] == '\0' && PQgetisnull(results, act_tuple, act_field))
442 					{
443 						/* NULL is valid */
444 						break;
445 					}
446 
447 					ecpg_raise(lineno, ECPG_CONVERT_BOOL,
448 							   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
449 					return (false);
450 					break;
451 
452 				case ECPGt_char:
453 				case ECPGt_unsigned_char:
454 				case ECPGt_string:
455 					{
456 						char	   *str = (char *) (var + offset * act_tuple);
457 
458 						/*
459 						 * If varcharsize is unknown and the offset is that of
460 						 * char *, then this variable represents the array of
461 						 * character pointers. So, use extra indirection.
462 						 */
463 						if (varcharsize == 0 && offset == sizeof(char *))
464 							str = *(char **) str;
465 
466 						if (varcharsize == 0 || varcharsize > size)
467 						{
468 							strncpy(str, pval, size + 1);
469 							/* do the rtrim() */
470 							if (type == ECPGt_string)
471 							{
472 								char	   *last = str + size;
473 
474 								while (last > str && (*last == ' ' || *last == '\0'))
475 								{
476 									*last = '\0';
477 									last--;
478 								}
479 							}
480 						}
481 						else
482 						{
483 							strncpy(str, pval, varcharsize);
484 
485 							if (varcharsize < size)
486 							{
487 								/* truncation */
488 								switch (ind_type)
489 								{
490 									case ECPGt_short:
491 									case ECPGt_unsigned_short:
492 										*((short *) (ind + ind_offset * act_tuple)) = size;
493 										break;
494 									case ECPGt_int:
495 									case ECPGt_unsigned_int:
496 										*((int *) (ind + ind_offset * act_tuple)) = size;
497 										break;
498 									case ECPGt_long:
499 									case ECPGt_unsigned_long:
500 										*((long *) (ind + ind_offset * act_tuple)) = size;
501 										break;
502 #ifdef HAVE_LONG_LONG_INT
503 									case ECPGt_long_long:
504 									case ECPGt_unsigned_long_long:
505 										*((long long int *) (ind + ind_offset * act_tuple)) = size;
506 										break;
507 #endif   /* HAVE_LONG_LONG_INT */
508 									default:
509 										break;
510 								}
511 								sqlca->sqlwarn[0] = sqlca->sqlwarn[1] = 'W';
512 							}
513 						}
514 						pval += size;
515 					}
516 					break;
517 
518 				case ECPGt_varchar:
519 					{
520 						struct ECPGgeneric_varchar *variable =
521 						(struct ECPGgeneric_varchar *) (var + offset * act_tuple);
522 
523 						variable->len = size;
524 						if (varcharsize == 0)
525 							strncpy(variable->arr, pval, variable->len);
526 						else
527 						{
528 							strncpy(variable->arr, pval, varcharsize);
529 
530 							if (variable->len > varcharsize)
531 							{
532 								/* truncation */
533 								switch (ind_type)
534 								{
535 									case ECPGt_short:
536 									case ECPGt_unsigned_short:
537 										*((short *) (ind + ind_offset * act_tuple)) = variable->len;
538 										break;
539 									case ECPGt_int:
540 									case ECPGt_unsigned_int:
541 										*((int *) (ind + ind_offset * act_tuple)) = variable->len;
542 										break;
543 									case ECPGt_long:
544 									case ECPGt_unsigned_long:
545 										*((long *) (ind + ind_offset * act_tuple)) = variable->len;
546 										break;
547 #ifdef HAVE_LONG_LONG_INT
548 									case ECPGt_long_long:
549 									case ECPGt_unsigned_long_long:
550 										*((long long int *) (ind + ind_offset * act_tuple)) = variable->len;
551 										break;
552 #endif   /* HAVE_LONG_LONG_INT */
553 									default:
554 										break;
555 								}
556 								sqlca->sqlwarn[0] = sqlca->sqlwarn[1] = 'W';
557 
558 								variable->len = varcharsize;
559 							}
560 						}
561 						pval += size;
562 					}
563 					break;
564 
565 				case ECPGt_decimal:
566 				case ECPGt_numeric:
567 					for (endptr = pval; *endptr && *endptr != ',' && *endptr != '}'; endptr++);
568 					endchar = *endptr;
569 					*endptr = '\0';
570 					nres = PGTYPESnumeric_from_asc(pval, &scan_length);
571 					*endptr = endchar;
572 
573 					/* did we get an error? */
574 					if (nres == NULL)
575 					{
576 						ecpg_log("ecpg_get_data on line %d: RESULT %s; errno %d\n",
577 								 lineno, pval, errno);
578 
579 						if (INFORMIX_MODE(compat))
580 						{
581 							/*
582 							 * Informix wants its own NULL value here instead
583 							 * of an error
584 							 */
585 							nres = PGTYPESnumeric_new();
586 							if (nres)
587 								ECPGset_noind_null(ECPGt_numeric, nres);
588 							else
589 							{
590 								ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
591 									 ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
592 								return (false);
593 							}
594 						}
595 						else
596 						{
597 							ecpg_raise(lineno, ECPG_NUMERIC_FORMAT,
598 									   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
599 							return (false);
600 						}
601 					}
602 					else
603 					{
604 						if (!isarray && garbage_left(isarray, &scan_length, compat))
605 						{
606 							free(nres);
607 							ecpg_raise(lineno, ECPG_NUMERIC_FORMAT,
608 									   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
609 							return (false);
610 						}
611 					}
612 					pval = scan_length;
613 
614 					if (type == ECPGt_numeric)
615 						PGTYPESnumeric_copy(nres, (numeric *) (var + offset * act_tuple));
616 					else
617 						PGTYPESnumeric_to_decimal(nres, (decimal *) (var + offset * act_tuple));
618 
619 					PGTYPESnumeric_free(nres);
620 					break;
621 
622 				case ECPGt_interval:
623 					if (*pval == '"')
624 						pval++;
625 
626 					for (endptr = pval; *endptr && *endptr != ',' && *endptr != '"' && *endptr != '}'; endptr++);
627 					endchar = *endptr;
628 					*endptr = '\0';
629 					ires = PGTYPESinterval_from_asc(pval, &scan_length);
630 					*endptr = endchar;
631 
632 					/* did we get an error? */
633 					if (ires == NULL)
634 					{
635 						ecpg_log("ecpg_get_data on line %d: RESULT %s; errno %d\n",
636 								 lineno, pval, errno);
637 
638 						if (INFORMIX_MODE(compat))
639 						{
640 							/*
641 							 * Informix wants its own NULL value here instead
642 							 * of an error
643 							 */
644 							ires = (interval *) ecpg_alloc(sizeof(interval), lineno);
645 							if (!ires)
646 								return (false);
647 
648 							ECPGset_noind_null(ECPGt_interval, ires);
649 						}
650 						else
651 						{
652 							ecpg_raise(lineno, ECPG_INTERVAL_FORMAT,
653 									   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
654 							return (false);
655 						}
656 					}
657 					else
658 					{
659 						if (*scan_length == '"')
660 							scan_length++;
661 
662 						if (!isarray && garbage_left(isarray, &scan_length, compat))
663 						{
664 							free(ires);
665 							ecpg_raise(lineno, ECPG_INTERVAL_FORMAT,
666 									   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
667 							return (false);
668 						}
669 					}
670 					pval = scan_length;
671 
672 					PGTYPESinterval_copy(ires, (interval *) (var + offset * act_tuple));
673 					free(ires);
674 					break;
675 
676 				case ECPGt_date:
677 					if (*pval == '"')
678 						pval++;
679 
680 					for (endptr = pval; *endptr && *endptr != ',' && *endptr != '"' && *endptr != '}'; endptr++);
681 					endchar = *endptr;
682 					*endptr = '\0';
683 					ddres = PGTYPESdate_from_asc(pval, &scan_length);
684 					*endptr = endchar;
685 
686 					/* did we get an error? */
687 					if (errno != 0)
688 					{
689 						ecpg_log("ecpg_get_data on line %d: RESULT %s; errno %d\n",
690 								 lineno, pval, errno);
691 
692 						if (INFORMIX_MODE(compat))
693 						{
694 							/*
695 							 * Informix wants its own NULL value here instead
696 							 * of an error
697 							 */
698 							ECPGset_noind_null(ECPGt_date, &ddres);
699 						}
700 						else
701 						{
702 							ecpg_raise(lineno, ECPG_DATE_FORMAT,
703 									   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
704 							return (false);
705 						}
706 					}
707 					else
708 					{
709 						if (*scan_length == '"')
710 							scan_length++;
711 
712 						if (!isarray && garbage_left(isarray, &scan_length, compat))
713 						{
714 							ecpg_raise(lineno, ECPG_DATE_FORMAT,
715 									   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
716 							return (false);
717 						}
718 					}
719 
720 					*((date *) (var + offset * act_tuple)) = ddres;
721 					pval = scan_length;
722 					break;
723 
724 				case ECPGt_timestamp:
725 					if (*pval == '"')
726 						pval++;
727 
728 					for (endptr = pval; *endptr && *endptr != ',' && *endptr != '"' && *endptr != '}'; endptr++);
729 					endchar = *endptr;
730 					*endptr = '\0';
731 					tres = PGTYPEStimestamp_from_asc(pval, &scan_length);
732 					*endptr = endchar;
733 
734 					/* did we get an error? */
735 					if (errno != 0)
736 					{
737 						ecpg_log("ecpg_get_data on line %d: RESULT %s; errno %d\n",
738 								 lineno, pval, errno);
739 
740 						if (INFORMIX_MODE(compat))
741 						{
742 							/*
743 							 * Informix wants its own NULL value here instead
744 							 * of an error
745 							 */
746 							ECPGset_noind_null(ECPGt_timestamp, &tres);
747 						}
748 						else
749 						{
750 							ecpg_raise(lineno, ECPG_TIMESTAMP_FORMAT,
751 									   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
752 							return (false);
753 						}
754 					}
755 					else
756 					{
757 						if (*scan_length == '"')
758 							scan_length++;
759 
760 						if (!isarray && garbage_left(isarray, &scan_length, compat))
761 						{
762 							ecpg_raise(lineno, ECPG_TIMESTAMP_FORMAT,
763 									   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
764 							return (false);
765 						}
766 					}
767 
768 					*((timestamp *) (var + offset * act_tuple)) = tres;
769 					pval = scan_length;
770 					break;
771 
772 				default:
773 					ecpg_raise(lineno, ECPG_UNSUPPORTED,
774 							   ECPG_SQLSTATE_ECPG_INTERNAL_ERROR,
775 							   ecpg_type_name(type));
776 					return (false);
777 					break;
778 			}
779 			if (ECPG_IS_ARRAY(isarray))
780 			{
781 				bool		string = false;
782 
783 				/* set array to next entry */
784 				++act_tuple;
785 
786 				/* set pval to the next entry */
787 
788 				/*
789 				 * *pval != '\0' should not be needed, but is used as a safety
790 				 * guard
791 				 */
792 				for (; *pval != '\0' && (string || (!array_delimiter(isarray, *pval) && !array_boundary(isarray, *pval))); ++pval)
793 					if (*pval == '"')
794 						string = string ? false : true;
795 
796 				if (array_delimiter(isarray, *pval))
797 					++pval;
798 			}
799 		}
800 	} while (*pval != '\0' && !array_boundary(isarray, *pval));
801 
802 	return (true);
803 }
804