@ -189,19 +189,20 @@ const char *progname;
volatile bool timer_exceeded = false ; /* flag from signal handler */
/*
* Variable definitions . If a variable has a string value , " value " is that
* value , is_numeric is false , and num_value is undefined . If the value is
* known to be numeric , is_numeric is true and num_value contains the value
* ( in any permitted numeric variant ) . In this case " value " contains the
* string equivalent of the number , if we ' ve had occasion to compute that ,
* or NULL if we haven ' t .
* Variable definitions .
*
* If a variable only has a string value , " svalue " is that value , and value is
* " not set " . If the value is known , " value " contains the value ( in any
* variant ) .
*
* In this case " svalue " contains the string equivalent of the value , if we ' ve
* had occasion to compute that , or NULL if we haven ' t .
*/
typedef struct
{
char * name ; /* variable's name */
char * value ; /* its value in string form, if known */
bool is_numeric ; /* is numeric value known? */
PgBenchValue num_value ; /* variable's value in numeric form */
char * svalue ; /* its value in string form, if known */
PgBenchValue value ; /* actual variable's value */
} Variable ;
# define MAX_SCRIPTS 128 /* max number of SQL scripts allowed */
@ -488,6 +489,8 @@ static const BuiltinScript builtin_script[] =
/* Function prototypes */
static void setNullValue ( PgBenchValue * pv ) ;
static void setBoolValue ( PgBenchValue * pv , bool bval ) ;
static void setIntValue ( PgBenchValue * pv , int64 ival ) ;
static void setDoubleValue ( PgBenchValue * pv , double dval ) ;
static bool evaluateExpr ( TState * , CState * , PgBenchExpr * , PgBenchValue * ) ;
@ -1146,50 +1149,82 @@ getVariable(CState *st, char *name)
if ( var = = NULL )
return NULL ; /* not found */
if ( var - > value )
return var - > value ; /* we have it in string form */
if ( var - > s value)
return var - > s value; /* we have it in string form */
/* We need to produce a string equivalent of the numeric value */
Assert ( var - > is_numeric ) ;
if ( var - > num_value . type = = PGBT_INT )
/* We need to produce a string equivalent of the value */
Assert ( var - > value . type ! = PGBT_NO_VALUE ) ;
if ( var - > value . type = = PGBT_NULL )
snprintf ( stringform , sizeof ( stringform ) , " NULL " ) ;
else if ( var - > value . type = = PGBT_BOOLEAN )
snprintf ( stringform , sizeof ( stringform ) ,
INT64_FORMAT , var - > num_value . u . ival ) ;
else
{
Assert ( var - > num_value . type = = PGBT_DOUBLE ) ;
" %s " , var - > value . u . bval ? " true " : " false " ) ;
else if ( var - > value . type = = PGBT_INT )
snprintf ( stringform , sizeof ( stringform ) ,
" %.*g " , DBL_DIG , var - > num_value . u . dval ) ;
}
var - > value = pg_strdup ( stringform ) ;
return var - > value ;
INT64_FORMAT , var - > value . u . ival ) ;
else if ( var - > value . type = = PGBT_DOUBLE )
snprintf ( stringform , sizeof ( stringform ) ,
" %.*g " , DBL_DIG , var - > value . u . dval ) ;
else /* internal error, unexpected type */
Assert ( 0 ) ;
var - > svalue = pg_strdup ( stringform ) ;
return var - > svalue ;
}
/* Try to convert variable to numeric form; return false on failure */
/* Try to convert variable to a value ; return false on failure */
static bool
makeVariableNumeric ( Variable * var )
makeVariableValue ( Variable * var )
{
if ( var - > is_numeric )
size_t slen ;
if ( var - > value . type ! = PGBT_NO_VALUE )
return true ; /* no work */
if ( is_an_int ( var - > value ) )
slen = strlen ( var - > svalue ) ;
if ( slen = = 0 )
/* what should it do on ""? */
return false ;
if ( pg_strcasecmp ( var - > svalue , " null " ) = = 0 )
{
setIntValue ( & var - > num_value , strtoint64 ( var - > value ) ) ;
var - > is_numeric = true ;
setNullValue ( & var - > value ) ;
}
/*
* accept prefixes such as y , ye , n , no . . . but not for " o " .
* 0 / 1 are recognized later as an int , which is converted
* to bool if needed .
*/
else if ( pg_strncasecmp ( var - > svalue , " true " , slen ) = = 0 | |
pg_strncasecmp ( var - > svalue , " yes " , slen ) = = 0 | |
pg_strcasecmp ( var - > svalue , " on " ) = = 0 )
{
setBoolValue ( & var - > value , true ) ;
}
else if ( pg_strncasecmp ( var - > svalue , " false " , slen ) = = 0 | |
pg_strncasecmp ( var - > svalue , " no " , slen ) = = 0 | |
pg_strcasecmp ( var - > svalue , " off " ) = = 0 | |
pg_strcasecmp ( var - > svalue , " of " ) = = 0 )
{
setBoolValue ( & var - > value , false ) ;
}
else if ( is_an_int ( var - > svalue ) )
{
setIntValue ( & var - > value , strtoint64 ( var - > svalue ) ) ;
}
else /* type should be double */
{
double dv ;
char xs ;
if ( sscanf ( var - > value , " %lf%c " , & dv , & xs ) ! = 1 )
if ( sscanf ( var - > s value, " %lf%c " , & dv , & xs ) ! = 1 )
{
fprintf ( stderr ,
" malformed variable \" %s \" value: \" %s \" \n " ,
var - > name , var - > value ) ;
var - > name , var - > s value) ;
return false ;
}
setDoubleValue ( & var - > num_value , dv ) ;
var - > is_numeric = true ;
setDoubleValue ( & var - > value , dv ) ;
}
return true ;
}
@ -1266,7 +1301,7 @@ lookupCreateVariable(CState *st, const char *context, char *name)
var = & newvars [ st - > nvariables ] ;
var - > name = pg_strdup ( name ) ;
var - > value = NULL ;
var - > s value = NULL ;
/* caller is expected to initialize remaining fields */
st - > nvariables + + ;
@ -1292,18 +1327,18 @@ putVariable(CState *st, const char *context, char *name, const char *value)
/* dup then free, in case value is pointing at this variable */
val = pg_strdup ( value ) ;
if ( var - > value )
free ( var - > value ) ;
var - > value = val ;
var - > is_numeric = false ;
if ( var - > s value)
free ( var - > s value) ;
var - > s value = val ;
var - > value . type = PGBT_NO_VALUE ;
return true ;
}
/* Assign a numeric value to a variable, creating it if need be */
/* Assign a value to a variable, creating it if need be */
/* Returns false on failure (bad name) */
static bool
putVariableNumber ( CState * st , const char * context , char * name ,
putVariableValue ( CState * st , const char * context , char * name ,
const PgBenchValue * value )
{
Variable * var ;
@ -1312,11 +1347,10 @@ putVariableNumber(CState *st, const char *context, char *name,
if ( ! var )
return false ;
if ( var - > value )
free ( var - > value ) ;
var - > value = NULL ;
var - > is_numeric = true ;
var - > num_value = * value ;
if ( var - > svalue )
free ( var - > svalue ) ;
var - > svalue = NULL ;
var - > value = * value ;
return true ;
}
@ -1329,7 +1363,7 @@ putVariableInt(CState *st, const char *context, char *name, int64 value)
PgBenchValue val ;
setIntValue ( & val , value ) ;
return putVariableNumber ( st , context , name , & val ) ;
return putVariableValue ( st , context , name , & val ) ;
}
/*
@ -1428,6 +1462,67 @@ getQueryParams(CState *st, const Command *command, const char **params)
params [ i ] = getVariable ( st , command - > argv [ i + 1 ] ) ;
}
static char *
valueTypeName ( PgBenchValue * pval )
{
if ( pval - > type = = PGBT_NO_VALUE )
return " none " ;
else if ( pval - > type = = PGBT_NULL )
return " null " ;
else if ( pval - > type = = PGBT_INT )
return " int " ;
else if ( pval - > type = = PGBT_DOUBLE )
return " double " ;
else if ( pval - > type = = PGBT_BOOLEAN )
return " boolean " ;
else
{
/* internal error, should never get there */
Assert ( false ) ;
return NULL ;
}
}
/* get a value as a boolean, or tell if there is a problem */
static bool
coerceToBool ( PgBenchValue * pval , bool * bval )
{
if ( pval - > type = = PGBT_BOOLEAN )
{
* bval = pval - > u . bval ;
return true ;
}
else /* NULL, INT or DOUBLE */
{
fprintf ( stderr , " cannot coerce %s to boolean \n " , valueTypeName ( pval ) ) ;
return false ;
}
}
/*
* Return true or false from an expression for conditional purposes .
* Non zero numerical values are true , zero and NULL are false .
*/
static bool
valueTruth ( PgBenchValue * pval )
{
switch ( pval - > type )
{
case PGBT_NULL :
return false ;
case PGBT_BOOLEAN :
return pval - > u . bval ;
case PGBT_INT :
return pval - > u . ival ! = 0 ;
case PGBT_DOUBLE :
return pval - > u . dval ! = 0.0 ;
default :
/* internal error, unexpected type */
Assert ( 0 ) ;
return false ;
}
}
/* get a value as an int, tell if there is a problem */
static bool
coerceToInt ( PgBenchValue * pval , int64 * ival )
@ -1437,11 +1532,10 @@ coerceToInt(PgBenchValue *pval, int64 *ival)
* ival = pval - > u . ival ;
return true ;
}
else
else if ( pval - > type = = PGBT_DOUBLE )
{
double dval = pval - > u . dval ;
Assert ( pval - > type = = PGBT_DOUBLE ) ;
if ( dval < PG_INT64_MIN | | PG_INT64_MAX < dval )
{
fprintf ( stderr , " double to int overflow for %f \n " , dval ) ;
@ -1450,6 +1544,11 @@ coerceToInt(PgBenchValue *pval, int64 *ival)
* ival = ( int64 ) dval ;
return true ;
}
else /* BOOLEAN or NULL */
{
fprintf ( stderr , " cannot coerce %s to int \n " , valueTypeName ( pval ) ) ;
return false ;
}
}
/* get a value as a double, or tell if there is a problem */
@ -1461,12 +1560,32 @@ coerceToDouble(PgBenchValue *pval, double *dval)
* dval = pval - > u . dval ;
return true ;
}
else
else if ( pval - > type = = PGBT_INT )
{
Assert ( pval - > type = = PGBT_INT ) ;
* dval = ( double ) pval - > u . ival ;
return true ;
}
else /* BOOLEAN or NULL */
{
fprintf ( stderr , " cannot coerce %s to double \n " , valueTypeName ( pval ) ) ;
return false ;
}
}
/* assign a null value */
static void
setNullValue ( PgBenchValue * pv )
{
pv - > type = PGBT_NULL ;
pv - > u . ival = 0 ;
}
/* assign a boolean value */
static void
setBoolValue ( PgBenchValue * pv , bool bval )
{
pv - > type = PGBT_BOOLEAN ;
pv - > u . bval = bval ;
}
/* assign an integer value */
static void
@ -1484,24 +1603,144 @@ setDoubleValue(PgBenchValue *pv, double dval)
pv - > u . dval = dval ;
}
static bool isLazyFunc ( PgBenchFunction func )
{
return func = = PGBENCH_AND | | func = = PGBENCH_OR | | func = = PGBENCH_CASE ;
}
/* lazy evaluation of some functions */
static bool
evalLazyFunc ( TState * thread , CState * st ,
PgBenchFunction func , PgBenchExprLink * args , PgBenchValue * retval )
{
PgBenchValue a1 , a2 ;
bool ba1 , ba2 ;
Assert ( isLazyFunc ( func ) & & args ! = NULL & & args - > next ! = NULL ) ;
/* args points to first condition */
if ( ! evaluateExpr ( thread , st , args - > expr , & a1 ) )
return false ;
/* second condition for AND/OR and corresponding branch for CASE */
args = args - > next ;
switch ( func )
{
case PGBENCH_AND :
if ( a1 . type = = PGBT_NULL )
{
setNullValue ( retval ) ;
return true ;
}
if ( ! coerceToBool ( & a1 , & ba1 ) )
return false ;
if ( ! ba1 )
{
setBoolValue ( retval , false ) ;
return true ;
}
if ( ! evaluateExpr ( thread , st , args - > expr , & a2 ) )
return false ;
if ( a2 . type = = PGBT_NULL )
{
setNullValue ( retval ) ;
return true ;
}
else if ( ! coerceToBool ( & a2 , & ba2 ) )
return false ;
else
{
setBoolValue ( retval , ba2 ) ;
return true ;
}
return true ;
case PGBENCH_OR :
if ( a1 . type = = PGBT_NULL )
{
setNullValue ( retval ) ;
return true ;
}
if ( ! coerceToBool ( & a1 , & ba1 ) )
return false ;
if ( ba1 )
{
setBoolValue ( retval , true ) ;
return true ;
}
if ( ! evaluateExpr ( thread , st , args - > expr , & a2 ) )
return false ;
if ( a2 . type = = PGBT_NULL )
{
setNullValue ( retval ) ;
return true ;
}
else if ( ! coerceToBool ( & a2 , & ba2 ) )
return false ;
else
{
setBoolValue ( retval , ba2 ) ;
return true ;
}
case PGBENCH_CASE :
/* when true, execute branch */
if ( valueTruth ( & a1 ) )
return evaluateExpr ( thread , st , args - > expr , retval ) ;
/* now args contains next condition or final else expression */
args = args - > next ;
/* final else case? */
if ( args - > next = = NULL )
return evaluateExpr ( thread , st , args - > expr , retval ) ;
/* no, another when, proceed */
return evalLazyFunc ( thread , st , PGBENCH_CASE , args , retval ) ;
default :
/* internal error, cannot get here */
Assert ( 0 ) ;
break ;
}
return false ;
}
/* maximum number of function arguments */
# define MAX_FARGS 16
/*
* Recursive evaluation of functions
* Recursive evaluation of standard functions ,
* which do not require lazy evaluation .
*/
static bool
evalFunc ( TState * thread , CState * st ,
PgBenchFunction func , PgBenchExprLink * args , PgBenchValue * retval )
evalStandardFunc (
TState * thread , CState * st ,
PgBenchFunction func , PgBenchExprLink * args , PgBenchValue * retval )
{
/* evaluate all function arguments */
int nargs = 0 ;
PgBenchValue vargs [ MAX_FARGS ] ;
int nargs = 0 ;
PgBenchValue vargs [ MAX_FARGS ] ;
PgBenchExprLink * l = args ;
bool has_null = false ;
for ( nargs = 0 ; nargs < MAX_FARGS & & l ! = NULL ; nargs + + , l = l - > next )
{
if ( ! evaluateExpr ( thread , st , l - > expr , & vargs [ nargs ] ) )
return false ;
has_null | = vargs [ nargs ] . type = = PGBT_NULL ;
}
if ( l ! = NULL )
{
@ -1510,6 +1749,13 @@ evalFunc(TState *thread, CState *st,
return false ;
}
/* NULL arguments */
if ( has_null & & func ! = PGBENCH_IS & & func ! = PGBENCH_DEBUG )
{
setNullValue ( retval ) ;
return true ;
}
/* then evaluate function */
switch ( func )
{
@ -1519,6 +1765,10 @@ evalFunc(TState *thread, CState *st,
case PGBENCH_MUL :
case PGBENCH_DIV :
case PGBENCH_MOD :
case PGBENCH_EQ :
case PGBENCH_NE :
case PGBENCH_LE :
case PGBENCH_LT :
{
PgBenchValue * lval = & vargs [ 0 ] ,
* rval = & vargs [ 1 ] ;
@ -1554,6 +1804,22 @@ evalFunc(TState *thread, CState *st,
setDoubleValue ( retval , ld / rd ) ;
return true ;
case PGBENCH_EQ :
setBoolValue ( retval , ld = = rd ) ;
return true ;
case PGBENCH_NE :
setBoolValue ( retval , ld ! = rd ) ;
return true ;
case PGBENCH_LE :
setBoolValue ( retval , ld < = rd ) ;
return true ;
case PGBENCH_LT :
setBoolValue ( retval , ld < rd ) ;
return true ;
default :
/* cannot get here */
Assert ( 0 ) ;
@ -1582,6 +1848,22 @@ evalFunc(TState *thread, CState *st,
setIntValue ( retval , li * ri ) ;
return true ;
case PGBENCH_EQ :
setBoolValue ( retval , li = = ri ) ;
return true ;
case PGBENCH_NE :
setBoolValue ( retval , li ! = ri ) ;
return true ;
case PGBENCH_LE :
setBoolValue ( retval , li < = ri ) ;
return true ;
case PGBENCH_LT :
setBoolValue ( retval , li < ri ) ;
return true ;
case PGBENCH_DIV :
case PGBENCH_MOD :
if ( ri = = 0 )
@ -1622,6 +1904,45 @@ evalFunc(TState *thread, CState *st,
}
}
/* integer bitwise operators */
case PGBENCH_BITAND :
case PGBENCH_BITOR :
case PGBENCH_BITXOR :
case PGBENCH_LSHIFT :
case PGBENCH_RSHIFT :
{
int64 li , ri ;
if ( ! coerceToInt ( & vargs [ 0 ] , & li ) | | ! coerceToInt ( & vargs [ 1 ] , & ri ) )
return false ;
if ( func = = PGBENCH_BITAND )
setIntValue ( retval , li & ri ) ;
else if ( func = = PGBENCH_BITOR )
setIntValue ( retval , li | ri ) ;
else if ( func = = PGBENCH_BITXOR )
setIntValue ( retval , li ^ ri ) ;
else if ( func = = PGBENCH_LSHIFT )
setIntValue ( retval , li < < ri ) ;
else if ( func = = PGBENCH_RSHIFT )
setIntValue ( retval , li > > ri ) ;
else /* cannot get here */
Assert ( 0 ) ;
return true ;
}
/* logical operators */
case PGBENCH_NOT :
{
bool b ;
if ( ! coerceToBool ( & vargs [ 0 ] , & b ) )
return false ;
setBoolValue ( retval , ! b ) ;
return true ;
}
/* no arguments */
case PGBENCH_PI :
setDoubleValue ( retval , M_PI ) ;
@ -1660,13 +1981,16 @@ evalFunc(TState *thread, CState *st,
fprintf ( stderr , " debug(script=%d,command=%d): " ,
st - > use_file , st - > command + 1 ) ;
if ( varg - > type = = PGBT_INT )
if ( varg - > type = = PGBT_NULL )
fprintf ( stderr , " null \n " ) ;
else if ( varg - > type = = PGBT_BOOLEAN )
fprintf ( stderr , " boolean %s \n " , varg - > u . bval ? " true " : " false " ) ;
else if ( varg - > type = = PGBT_INT )
fprintf ( stderr , " int " INT64_FORMAT " \n " , varg - > u . ival ) ;
else
{
Assert ( varg - > type = = PGBT_DOUBLE ) ;
else if ( varg - > type = = PGBT_DOUBLE )
fprintf ( stderr , " double %.*g \n " , DBL_DIG , varg - > u . dval ) ;
}
else /* internal error, unexpected type */
Assert ( 0 ) ;
* retval = * varg ;
@ -1676,6 +2000,8 @@ evalFunc(TState *thread, CState *st,
/* 1 double argument */
case PGBENCH_DOUBLE :
case PGBENCH_SQRT :
case PGBENCH_LN :
case PGBENCH_EXP :
{
double dval ;
@ -1686,6 +2012,11 @@ evalFunc(TState *thread, CState *st,
if ( func = = PGBENCH_SQRT )
dval = sqrt ( dval ) ;
else if ( func = = PGBENCH_LN )
dval = log ( dval ) ;
else if ( func = = PGBENCH_EXP )
dval = exp ( dval ) ;
/* else is cast: do nothing */
setDoubleValue ( retval , dval ) ;
return true ;
@ -1868,6 +2199,16 @@ evalFunc(TState *thread, CState *st,
return true ;
}
case PGBENCH_IS :
{
Assert ( nargs = = 2 ) ;
/* note: this simple implementation is more permissive than SQL */
setBoolValue ( retval ,
vargs [ 0 ] . type = = vargs [ 1 ] . type & &
vargs [ 0 ] . u . bval = = vargs [ 1 ] . u . bval ) ;
return true ;
}
default :
/* cannot get here */
Assert ( 0 ) ;
@ -1876,6 +2217,17 @@ evalFunc(TState *thread, CState *st,
}
}
/* evaluate some function */
static bool
evalFunc ( TState * thread , CState * st ,
PgBenchFunction func , PgBenchExprLink * args , PgBenchValue * retval )
{
if ( isLazyFunc ( func ) )
return evalLazyFunc ( thread , st , func , args , retval ) ;
else
return evalStandardFunc ( thread , st , func , args , retval ) ;
}
/*
* Recursive evaluation of an expression in a pgbench script
* using the current state of variables .
@ -1904,10 +2256,10 @@ evaluateExpr(TState *thread, CState *st, PgBenchExpr *expr, PgBenchValue *retval
return false ;
}
if ( ! makeVariableNumeric ( var ) )
if ( ! makeVariableValue ( var ) )
return false ;
* retval = var - > num_ value;
* retval = var - > value ;
return true ;
}
@ -2479,7 +2831,7 @@ doCustom(TState *thread, CState *st, StatsData *agg)
break ;
}
if ( ! putVariableNumber ( st , argv [ 0 ] , argv [ 1 ] , & result ) )
if ( ! putVariableValue ( st , argv [ 0 ] , argv [ 1 ] , & result ) )
{
commandFailed ( st , " assignment of meta-command 'set' failed " ) ;
st - > state = CSTATE_ABORTED ;
@ -4582,16 +4934,16 @@ main(int argc, char **argv)
{
Variable * var = & state [ 0 ] . variables [ j ] ;
if ( var - > is_numeric )
if ( var - > value . type ! = PGBT_NO_VALUE )
{
if ( ! putVariableNumber ( & state [ i ] , " startup " ,
var - > name , & var - > num_ value) )
if ( ! putVariableValue ( & state [ i ] , " startup " ,
var - > name , & var - > value ) )
exit ( 1 ) ;
}
else
{
if ( ! putVariable ( & state [ i ] , " startup " ,
var - > name , var - > value ) )
var - > name , var - > s value) )
exit ( 1 ) ;
}
}