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