/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/data.c,v 1.49 2010/02/04 09:41:34 meskes Exp $ */
# define POSTGRES_ECPG_INTERNAL
# include "postgres_fe.h"
# include <stdlib.h>
# include <string.h>
# include <math.h>
# include "ecpgtype.h"
# include "ecpglib.h"
# include "ecpgerrno.h"
# include "extern.h"
# include "sqlca.h"
# include "pgtypes_numeric.h"
# include "pgtypes_date.h"
# include "pgtypes_timestamp.h"
# include "pgtypes_interval.h"
/* returns true if character c is a delimiter for the given array type */
static bool
array_delimiter ( enum ARRAY_TYPE isarray , char c )
{
if ( isarray = = ECPG_ARRAY_ARRAY & & c = = ' , ' )
return true ;
if ( isarray = = ECPG_ARRAY_VECTOR & & c = = ' ' )
return true ;
return false ;
}
/* returns true if character c marks the boundary for the given array type */
static bool
array_boundary ( enum ARRAY_TYPE isarray , char c )
{
if ( isarray = = ECPG_ARRAY_ARRAY & & c = = ' } ' )
return true ;
if ( isarray = = ECPG_ARRAY_VECTOR & & c = = ' \0 ' )
return true ;
return false ;
}
/* returns true if some garbage is found at the end of the scanned string */
static bool
garbage_left ( enum ARRAY_TYPE isarray , char * scan_length , enum COMPAT_MODE compat )
{
/*
* INFORMIX allows for selecting a numeric into an int , the result is
* truncated
*/
if ( isarray = = ECPG_ARRAY_NONE )
{
if ( INFORMIX_MODE ( compat ) & & * scan_length = = ' . ' )
return false ;
if ( * scan_length ! = ' ' & & * scan_length ! = ' \0 ' )
return true ;
}
else if ( ECPG_IS_ARRAY ( isarray ) & & ! array_delimiter ( isarray , * scan_length ) & & ! array_boundary ( isarray , * scan_length ) )
return true ;
return false ;
}
/* stolen code from src/backend/utils/adt/float.c */
# if defined(WIN32) && !defined(NAN)
static const uint32 nan [ 2 ] = { 0xffffffff , 0x7fffffff } ;
# define NAN (*(const double *) nan)
# endif
static double
get_float8_infinity ( void )
{
# ifdef INFINITY
return ( double ) INFINITY ;
# else
return ( double ) ( HUGE_VAL * HUGE_VAL ) ;
# endif
}
static double
get_float8_nan ( void )
{
# ifdef NAN
return ( double ) NAN ;
# else
return ( double ) ( 0.0 / 0.0 ) ;
# endif
}
static bool
check_special_value ( char * ptr , double * retval , char * * endptr )
{
if ( ! pg_strncasecmp ( ptr , " NaN " , 3 ) )
{
* retval = get_float8_nan ( ) ;
* endptr = ptr + 3 ;
return true ;
}
else if ( ! pg_strncasecmp ( ptr , " Infinity " , 8 ) )
{
* retval = get_float8_infinity ( ) ;
* endptr = ptr + 8 ;
return true ;
}
else if ( ! pg_strncasecmp ( ptr , " -Infinity " , 9 ) )
{
* retval = - get_float8_infinity ( ) ;
* endptr = ptr + 9 ;
return true ;
}
return false ;
}
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 )
{
struct sqlca_t * sqlca = ECPGget_sqlca ( ) ;
char * pval = ( char * ) PQgetvalue ( results , act_tuple , act_field ) ;
int binary = PQfformat ( results , act_field ) ;
int size = PQgetlength ( results , act_tuple , act_field ) ;
int value_for_indicator = 0 ;
long log_offset ;
/*
* If we are running in a regression test , do not log the offset variable ,
* it depends on the machine ' s alignment .
*/
if ( ecpg_internal_regression_mode )
log_offset = - 1 ;
else
log_offset = offset ;
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 " ) ;
/* pval is a pointer to the value */
if ( ! pval )
{
/*
* This should never happen because we already checked that we
* found at least one tuple , but let ' s play it safe .
*/
ecpg_raise ( lineno , ECPG_NOT_FOUND , ECPG_SQLSTATE_NO_DATA , NULL ) ;
return ( false ) ;
}
/* We will have to decode the value */
/*
* check for null value and set indicator accordingly , i . e . - 1 if NULL and
* 0 if not
*/
if ( PQgetisnull ( results , act_tuple , act_field ) )
value_for_indicator = - 1 ;
switch ( ind_type )
{
case ECPGt_short :
case ECPGt_unsigned_short :
* ( ( short * ) ( ind + ind_offset * act_tuple ) ) = value_for_indicator ;
break ;
case ECPGt_int :
case ECPGt_unsigned_int :
* ( ( int * ) ( ind + ind_offset * act_tuple ) ) = value_for_indicator ;
break ;
case ECPGt_long :
case ECPGt_unsigned_long :
* ( ( long * ) ( ind + ind_offset * act_tuple ) ) = value_for_indicator ;
break ;
# ifdef HAVE_LONG_LONG_INT_64
case ECPGt_long_long :
case ECPGt_unsigned_long_long :
* ( ( long long int * ) ( ind + ind_offset * act_tuple ) ) = value_for_indicator ;
break ;
# endif /* HAVE_LONG_LONG_INT_64 */
case ECPGt_NO_INDICATOR :
if ( value_for_indicator = = - 1 )
{
if ( force_indicator = = false )
{
/*
* Informix has an additional way to specify NULLs note
* that this uses special values to denote NULL
*/
ECPGset_noind_null ( type , var + offset * act_tuple ) ;
}
else
{
ecpg_raise ( lineno , ECPG_MISSING_INDICATOR ,
ECPG_SQLSTATE_NULL_VALUE_NO_INDICATOR_PARAMETER ,
NULL ) ;
return ( false ) ;
}
}
break ;
default :
ecpg_raise ( lineno , ECPG_UNSUPPORTED ,
ECPG_SQLSTATE_ECPG_INTERNAL_ERROR ,
ecpg_type_name ( ind_type ) ) ;
return ( false ) ;
break ;
}
if ( value_for_indicator = = - 1 )
return ( true ) ;
/* let's check if it really is an array if it should be one */
if ( isarray = = ECPG_ARRAY_ARRAY )
{
if ( * pval ! = ' { ' )
{
ecpg_raise ( lineno , ECPG_DATA_NOT_ARRAY ,
ECPG_SQLSTATE_DATATYPE_MISMATCH , NULL ) ;
return ( false ) ;
}
switch ( type )
{
case ECPGt_char :
case ECPGt_unsigned_char :
case ECPGt_varchar :
case ECPGt_string :
break ;
default :
pval + + ;
break ;
}
}
do
{
if ( binary )
{
if ( varcharsize = = 0 | | varcharsize * offset > = size )
memcpy ( var + offset * act_tuple , pval , size ) ;
else
{
memcpy ( var + offset * act_tuple , pval , varcharsize * offset ) ;
if ( varcharsize * offset < size )
{
/* truncation */
switch ( ind_type )
{
case ECPGt_short :
case ECPGt_unsigned_short :
* ( ( short * ) ( ind + ind_offset * act_tuple ) ) = size ;
break ;
case ECPGt_int :
case ECPGt_unsigned_int :
* ( ( int * ) ( ind + ind_offset * act_tuple ) ) = size ;
break ;
case ECPGt_long :
case ECPGt_unsigned_long :
* ( ( long * ) ( ind + ind_offset * act_tuple ) ) = size ;
break ;
# ifdef HAVE_LONG_LONG_INT_64
case ECPGt_long_long :
case ECPGt_unsigned_long_long :
* ( ( long long int * ) ( ind + ind_offset * act_tuple ) ) = size ;
break ;
# endif /* HAVE_LONG_LONG_INT_64 */
default :
break ;
}
sqlca - > sqlwarn [ 0 ] = sqlca - > sqlwarn [ 1 ] = ' W ' ;
}
}
pval + = size ;
}
else
{
switch ( type )
{
long res ;
unsigned long ures ;
double dres ;
char * scan_length ;
numeric * nres ;
date ddres ;
timestamp tres ;
interval * ires ;
case ECPGt_short :
case ECPGt_int :
case ECPGt_long :
res = strtol ( pval , & scan_length , 10 ) ;
if ( garbage_left ( isarray , scan_length , compat ) )
{
ecpg_raise ( lineno , ECPG_INT_FORMAT ,
ECPG_SQLSTATE_DATATYPE_MISMATCH , pval ) ;
return ( false ) ;
}
pval = scan_length ;
switch ( type )
{
case ECPGt_short :
* ( ( short * ) ( var + offset * act_tuple ) ) = ( short ) res ;
break ;
case ECPGt_int :
* ( ( int * ) ( var + offset * act_tuple ) ) = ( int ) res ;
break ;
case ECPGt_long :
* ( ( long * ) ( var + offset * act_tuple ) ) = ( long ) res ;
break ;
default :
/* Cannot happen */
break ;
}
break ;
case ECPGt_unsigned_short :
case ECPGt_unsigned_int :
case ECPGt_unsigned_long :
ures = strtoul ( pval , & scan_length , 10 ) ;
if ( garbage_left ( isarray , scan_length , compat ) )
{
ecpg_raise ( lineno , ECPG_UINT_FORMAT ,
ECPG_SQLSTATE_DATATYPE_MISMATCH , pval ) ;
return ( false ) ;
}
pval = scan_length ;
switch ( type )
{
case ECPGt_unsigned_short :
* ( ( unsigned short * ) ( var + offset * act_tuple ) ) = ( unsigned short ) ures ;
break ;
case ECPGt_unsigned_int :
* ( ( unsigned int * ) ( var + offset * act_tuple ) ) = ( unsigned int ) ures ;
break ;
case ECPGt_unsigned_long :
* ( ( unsigned long * ) ( var + offset * act_tuple ) ) = ( unsigned long ) ures ;
break ;
default :
/* Cannot happen */
break ;
}
break ;
# ifdef HAVE_LONG_LONG_INT_64
# ifdef HAVE_STRTOLL
case ECPGt_long_long :
* ( ( long long int * ) ( var + offset * act_tuple ) ) = strtoll ( pval , & scan_length , 10 ) ;
if ( garbage_left ( isarray , scan_length , compat ) )
{
ecpg_raise ( lineno , ECPG_INT_FORMAT , ECPG_SQLSTATE_DATATYPE_MISMATCH , pval ) ;
return ( false ) ;
}
pval = scan_length ;
break ;
# endif /* HAVE_STRTOLL */
# ifdef HAVE_STRTOULL
case ECPGt_unsigned_long_long :
* ( ( unsigned long long int * ) ( var + offset * act_tuple ) ) = strtoull ( pval , & scan_length , 10 ) ;
if ( ( isarray & & * scan_length ! = ' , ' & & * scan_length ! = ' } ' )
| | ( ! isarray & & ! ( INFORMIX_MODE ( compat ) & & * scan_length = = ' . ' ) & & * scan_length ! = ' \0 ' & & * scan_length ! = ' ' ) ) /* Garbage left */
{
ecpg_raise ( lineno , ECPG_UINT_FORMAT , ECPG_SQLSTATE_DATATYPE_MISMATCH , pval ) ;
return ( false ) ;
}
pval = scan_length ;
break ;
# endif /* HAVE_STRTOULL */
# endif /* HAVE_LONG_LONG_INT_64 */
case ECPGt_float :
case ECPGt_double :
if ( isarray & & * pval = = ' " ' )
pval + + ;
if ( ! check_special_value ( pval , & dres , & scan_length ) )
dres = strtod ( pval , & scan_length ) ;
if ( isarray & & * scan_length = = ' " ' )
scan_length + + ;
if ( garbage_left ( isarray , scan_length , compat ) )
{
ecpg_raise ( lineno , ECPG_FLOAT_FORMAT ,
ECPG_SQLSTATE_DATATYPE_MISMATCH , pval ) ;
return ( false ) ;
}
pval = scan_length ;
switch ( type )
{
case ECPGt_float :
* ( ( float * ) ( var + offset * act_tuple ) ) = dres ;
break ;
case ECPGt_double :
* ( ( double * ) ( var + offset * act_tuple ) ) = dres ;
break ;
default :
/* Cannot happen */
break ;
}
break ;
case ECPGt_bool :
if ( pval [ 0 ] = = ' f ' & & pval [ 1 ] = = ' \0 ' )
{
if ( offset = = sizeof ( char ) )
* ( ( char * ) ( var + offset * act_tuple ) ) = false ;
else if ( offset = = sizeof ( int ) )
* ( ( int * ) ( var + offset * act_tuple ) ) = false ;
else
ecpg_raise ( lineno , ECPG_CONVERT_BOOL ,
ECPG_SQLSTATE_DATATYPE_MISMATCH ,
NULL ) ;
break ;
}
else if ( pval [ 0 ] = = ' t ' & & pval [ 1 ] = = ' \0 ' )
{
if ( offset = = sizeof ( char ) )
* ( ( char * ) ( var + offset * act_tuple ) ) = true ;
else if ( offset = = sizeof ( int ) )
* ( ( int * ) ( var + offset * act_tuple ) ) = true ;
else
ecpg_raise ( lineno , ECPG_CONVERT_BOOL ,
ECPG_SQLSTATE_DATATYPE_MISMATCH ,
NULL ) ;
break ;
}
else if ( pval [ 0 ] = = ' \0 ' & & PQgetisnull ( results , act_tuple , act_field ) )
{
/* NULL is valid */
break ;
}
ecpg_raise ( lineno , ECPG_CONVERT_BOOL ,
ECPG_SQLSTATE_DATATYPE_MISMATCH , pval ) ;
return ( false ) ;
break ;
case ECPGt_char :
case ECPGt_unsigned_char :
case ECPGt_string :
{
char * str = ( char * ) ( var + offset * act_tuple ) ;
if ( varcharsize = = 0 | | varcharsize > size )
{
strncpy ( str , pval , size + 1 ) ;
/* do the rtrim() */
if ( type = = ECPGt_string )
{
char * last = str + size ;
while ( last > str & & ( * last = = ' ' | | * last = = ' \0 ' ) )
{
* last = ' \0 ' ;
last - - ;
}
}
}
else
{
strncpy ( str , pval , varcharsize ) ;
if ( varcharsize < size )
{
/* truncation */
switch ( ind_type )
{
case ECPGt_short :
case ECPGt_unsigned_short :
* ( ( short * ) ( ind + ind_offset * act_tuple ) ) = size ;
break ;
case ECPGt_int :
case ECPGt_unsigned_int :
* ( ( int * ) ( ind + ind_offset * act_tuple ) ) = size ;
break ;
case ECPGt_long :
case ECPGt_unsigned_long :
* ( ( long * ) ( ind + ind_offset * act_tuple ) ) = size ;
break ;
# ifdef HAVE_LONG_LONG_INT_64
case ECPGt_long_long :
case ECPGt_unsigned_long_long :
* ( ( long long int * ) ( ind + ind_offset * act_tuple ) ) = size ;
break ;
# endif /* HAVE_LONG_LONG_INT_64 */
default :
break ;
}
sqlca - > sqlwarn [ 0 ] = sqlca - > sqlwarn [ 1 ] = ' W ' ;
}
}
pval + = size ;
}
break ;
case ECPGt_varchar :
{
struct ECPGgeneric_varchar * variable =
( struct ECPGgeneric_varchar * ) ( var + offset * act_tuple ) ;
variable - > len = size ;
if ( varcharsize = = 0 )
strncpy ( variable - > arr , pval , variable - > len ) ;
else
{
strncpy ( variable - > arr , pval , varcharsize ) ;
if ( variable - > len > varcharsize )
{
/* truncation */
switch ( ind_type )
{
case ECPGt_short :
case ECPGt_unsigned_short :
* ( ( short * ) ( ind + offset * act_tuple ) ) = variable - > len ;
break ;
case ECPGt_int :
case ECPGt_unsigned_int :
* ( ( int * ) ( ind + offset * act_tuple ) ) = variable - > len ;
break ;
case ECPGt_long :
case ECPGt_unsigned_long :
* ( ( long * ) ( ind + offset * act_tuple ) ) = variable - > len ;
break ;
# ifdef HAVE_LONG_LONG_INT_64
case ECPGt_long_long :
case ECPGt_unsigned_long_long :
* ( ( long long int * ) ( ind + ind_offset * act_tuple ) ) = variable - > len ;
break ;
# endif /* HAVE_LONG_LONG_INT_64 */
default :
break ;
}
sqlca - > sqlwarn [ 0 ] = sqlca - > sqlwarn [ 1 ] = ' W ' ;
variable - > len = varcharsize ;
}
}
pval + = size ;
}
break ;
case ECPGt_decimal :
case ECPGt_numeric :
if ( isarray & & * pval = = ' " ' )
nres = PGTYPESnumeric_from_asc ( pval + 1 , & scan_length ) ;
else
nres = PGTYPESnumeric_from_asc ( pval , & scan_length ) ;
/* did we get an error? */
if ( nres = = NULL )
{
ecpg_log ( " ecpg_get_data on line %d: RESULT %s; errno %d \n " ,
lineno , pval ? pval : " " , errno ) ;
if ( INFORMIX_MODE ( compat ) )
{
/*
* Informix wants its own NULL value here
* instead of an error
*/
nres = PGTYPESnumeric_new ( ) ;
if ( nres )
ECPGset_noind_null ( ECPGt_numeric , nres ) ;
else
{
ecpg_raise ( lineno , ECPG_OUT_OF_MEMORY ,
ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY , NULL ) ;
return ( false ) ;
}
}
else
{
ecpg_raise ( lineno , ECPG_NUMERIC_FORMAT ,
ECPG_SQLSTATE_DATATYPE_MISMATCH , pval ) ;
return ( false ) ;
}
}
else
{
if ( isarray & & * scan_length = = ' " ' )
scan_length + + ;
if ( garbage_left ( isarray , scan_length , compat ) )
{
free ( nres ) ;
ecpg_raise ( lineno , ECPG_NUMERIC_FORMAT ,
ECPG_SQLSTATE_DATATYPE_MISMATCH , pval ) ;
return ( false ) ;
}
}
pval = scan_length ;
if ( type = = ECPGt_numeric )
PGTYPESnumeric_copy ( nres , ( numeric * ) ( var + offset * act_tuple ) ) ;
else
PGTYPESnumeric_to_decimal ( nres , ( decimal * ) ( var + offset * act_tuple ) ) ;
PGTYPESnumeric_free ( nres ) ;
break ;
case ECPGt_interval :
if ( isarray & & * pval = = ' " ' )
ires = PGTYPESinterval_from_asc ( pval + 1 , & scan_length ) ;
else
ires = PGTYPESinterval_from_asc ( pval , & scan_length ) ;
/* did we get an error? */
if ( ires = = NULL )
{
ecpg_log ( " ecpg_get_data on line %d: RESULT %s; errno %d \n " ,
lineno , pval ? pval : " " , errno ) ;
if ( INFORMIX_MODE ( compat ) )
{
/*
* Informix wants its own NULL value here
* instead of an error
*/
ires = ( interval * ) ecpg_alloc ( sizeof ( interval ) , lineno ) ;
if ( ! ires )
return ( false ) ;
ECPGset_noind_null ( ECPGt_interval , ires ) ;
}
else
{
ecpg_raise ( lineno , ECPG_INTERVAL_FORMAT ,
ECPG_SQLSTATE_DATATYPE_MISMATCH , pval ) ;
return ( false ) ;
}
}
else
{
if ( isarray & & * scan_length = = ' " ' )
scan_length + + ;
if ( garbage_left ( isarray , scan_length , compat ) )
{
free ( ires ) ;
ecpg_raise ( lineno , ECPG_INTERVAL_FORMAT ,
ECPG_SQLSTATE_DATATYPE_MISMATCH , pval ) ;
return ( false ) ;
}
}
pval = scan_length ;
PGTYPESinterval_copy ( ires , ( interval * ) ( var + offset * act_tuple ) ) ;
free ( ires ) ;
break ;
case ECPGt_date :
if ( isarray & & * pval = = ' " ' )
ddres = PGTYPESdate_from_asc ( pval + 1 , & scan_length ) ;
else
ddres = PGTYPESdate_from_asc ( pval , & scan_length ) ;
/* did we get an error? */
if ( errno ! = 0 )
{
ecpg_log ( " ecpg_get_data on line %d: RESULT %s; errno %d \n " ,
lineno , pval ? pval : " " , errno ) ;
if ( INFORMIX_MODE ( compat ) )
{
/*
* Informix wants its own NULL value here
* instead of an error
*/
ECPGset_noind_null ( ECPGt_date , & ddres ) ;
}
else
{
ecpg_raise ( lineno , ECPG_DATE_FORMAT ,
ECPG_SQLSTATE_DATATYPE_MISMATCH , pval ) ;
return ( false ) ;
}
}
else
{
if ( isarray & & * scan_length = = ' " ' )
scan_length + + ;
if ( garbage_left ( isarray , scan_length , compat ) )
{
ecpg_raise ( lineno , ECPG_DATE_FORMAT ,
ECPG_SQLSTATE_DATATYPE_MISMATCH , pval ) ;
return ( false ) ;
}
}
* ( ( date * ) ( var + offset * act_tuple ) ) = ddres ;
pval = scan_length ;
break ;
case ECPGt_timestamp :
if ( isarray & & * pval = = ' " ' )
tres = PGTYPEStimestamp_from_asc ( pval + 1 , & scan_length ) ;
else
tres = PGTYPEStimestamp_from_asc ( pval , & scan_length ) ;
/* did we get an error? */
if ( errno ! = 0 )
{
ecpg_log ( " ecpg_get_data on line %d: RESULT %s; errno %d \n " ,
lineno , pval ? pval : " " , errno ) ;
if ( INFORMIX_MODE ( compat ) )
{
/*
* Informix wants its own NULL value here
* instead of an error
*/
ECPGset_noind_null ( ECPGt_timestamp , & tres ) ;
}
else
{
ecpg_raise ( lineno , ECPG_TIMESTAMP_FORMAT ,
ECPG_SQLSTATE_DATATYPE_MISMATCH , pval ) ;
return ( false ) ;
}
}
else
{
if ( isarray & & * scan_length = = ' " ' )
scan_length + + ;
if ( garbage_left ( isarray , scan_length , compat ) )
{
ecpg_raise ( lineno , ECPG_TIMESTAMP_FORMAT ,
ECPG_SQLSTATE_DATATYPE_MISMATCH , pval ) ;
return ( false ) ;
}
}
* ( ( timestamp * ) ( var + offset * act_tuple ) ) = tres ;
pval = scan_length ;
break ;
default :
ecpg_raise ( lineno , ECPG_UNSUPPORTED ,
ECPG_SQLSTATE_ECPG_INTERNAL_ERROR ,
ecpg_type_name ( type ) ) ;
return ( false ) ;
break ;
}
if ( ECPG_IS_ARRAY ( isarray ) )
{
bool string = false ;
/* set array to next entry */
+ + act_tuple ;
/* set pval to the next entry */
/* *pval != '\0' should not be needed, but is used as a safety guard */
for ( ; * pval ! = ' \0 ' & & ( string | | ( ! array_delimiter ( isarray , * pval ) & & ! array_boundary ( isarray , * pval ) ) ) ; + + pval )
if ( * pval = = ' " ' )
string = string ? false : true ;
if ( array_delimiter ( isarray , * pval ) )
+ + pval ;
}
}
} while ( * pval ! = ' \0 ' & & array_boundary ( isarray , * pval ) ) ;
return ( true ) ;
}