@ -58,6 +58,11 @@ CreateVariableSpace(void)
return ptr ;
return ptr ;
}
}
/*
* Get string value of variable , or NULL if it ' s not defined .
*
* Note : result is valid until variable is next assigned to .
*/
const char *
const char *
GetVariable ( VariableSpace space , const char * name )
GetVariable ( VariableSpace space , const char * name )
{
{
@ -79,94 +84,121 @@ GetVariable(VariableSpace space, const char *name)
}
}
/*
/*
* Try to interpret " value " as boolean value .
* Try to interpret " value " as a boolean value , and if successful ,
* store it in * result . Otherwise don ' t clobber * result .
*
*
* Valid values are : true , false , yes , no , on , off , 1 , 0 ; as well as unique
* Valid values are : true , false , yes , no , on , off , 1 , 0 ; as well as unique
* prefixes thereof .
* prefixes thereof .
*
*
* " name " is the name of the variable we ' re assigning to , to use in error
* " name " is the name of the variable we ' re assigning to , to use in error
* report if any . Pass name = = NULL to suppress the error report .
* report if any . Pass name = = NULL to suppress the error report .
*
* Return true when " value " is syntactically valid , false otherwise .
*/
*/
bool
bool
ParseVariableBool ( const char * value , const char * name )
ParseVariableBool ( const char * value , const char * name , bool * result )
{
{
size_t len ;
size_t len ;
bool valid = true ;
if ( value = = NULL )
if ( value = = NULL )
return false ; /* not set -> assume "off" */
{
* result = false ; /* not set -> assume "off" */
return valid ;
}
len = strlen ( value ) ;
len = strlen ( value ) ;
if ( pg_strncasecmp ( value , " true " , len ) = = 0 )
if ( len > 0 & & pg_strncasecmp ( value , " true " , len ) = = 0 )
return true ;
* result = true ;
else if ( pg_strncasecmp ( value , " false " , len ) = = 0 )
else if ( len > 0 & & pg_strncasecmp ( value , " false " , len ) = = 0 )
return false ;
* result = false ;
else if ( pg_strncasecmp ( value , " yes " , len ) = = 0 )
else if ( len > 0 & & pg_strncasecmp ( value , " yes " , len ) = = 0 )
return true ;
* result = true ;
else if ( pg_strncasecmp ( value , " no " , len ) = = 0 )
else if ( len > 0 & & pg_strncasecmp ( value , " no " , len ) = = 0 )
return false ;
* result = false ;
/* 'o' is not unique enough */
/* 'o' is not unique enough */
else if ( pg_strncasecmp ( value , " on " , ( len > 2 ? len : 2 ) ) = = 0 )
else if ( pg_strncasecmp ( value , " on " , ( len > 2 ? len : 2 ) ) = = 0 )
return true ;
* result = true ;
else if ( pg_strncasecmp ( value , " off " , ( len > 2 ? len : 2 ) ) = = 0 )
else if ( pg_strncasecmp ( value , " off " , ( len > 2 ? len : 2 ) ) = = 0 )
return false ;
* result = false ;
else if ( pg_strcasecmp ( value , " 1 " ) = = 0 )
else if ( pg_strcasecmp ( value , " 1 " ) = = 0 )
return true ;
* result = true ;
else if ( pg_strcasecmp ( value , " 0 " ) = = 0 )
else if ( pg_strcasecmp ( value , " 0 " ) = = 0 )
return false ;
* result = false ;
else
else
{
{
/* NULL is treated as false, so a non-matching value is 'true' */
/* string is not recognized; don't clobber *result */
if ( name )
if ( name )
psql_error ( " unrecognized value \" %s \" for \" %s \" ; assuming \" %s \" \n " ,
psql_error ( " unrecognized value \" %s \" for \" %s \" : boolean expected \n " ,
value , name , " on " ) ;
value , name ) ;
return tru e;
valid = fals e;
}
}
return valid ;
}
}
/*
/*
* Read numeric variable , or defaultval if it is not set , or faultval if its
* Try to interpret " value " as an integer value , and if successful ,
* value is not a valid numeric string . If allowtrail is false , this will
* store it in * result . Otherwise don ' t clobber * result .
* include the case where there are trailing characters after the number .
*
* " name " is the name of the variable we ' re assigning to , to use in error
* report if any . Pass name = = NULL to suppress the error report .
*
* Return true when " value " is syntactically valid , false otherwise .
*/
*/
int
bool
ParseVariableNum ( const char * val ,
ParseVariableNum ( const char * value , const char * name , int * result )
int defaultval ,
int faultval ,
bool allowtrail )
{
{
int result ;
char * end ;
long numval ;
if ( ! val )
if ( value = = NULL )
result = defaultval ;
return false ;
else if ( ! val [ 0 ] )
errno = 0 ;
result = faultval ;
numval = strtol ( value , & end , 0 ) ;
if ( errno = = 0 & & * end = = ' \0 ' & & end ! = value & & numval = = ( int ) numval )
{
* result = ( int ) numval ;
return true ;
}
else
else
{
{
char * end ;
/* string is not recognized; don't clobber *result */
if ( name )
result = strtol ( val , & end , 0 ) ;
psql_error ( " invalid value \" %s \" for \" %s \" : integer expected \n " ,
if ( ! allowtrail & & * end )
value , name ) ;
result = faultval ;
return false ;
}
}
return result ;
}
}
/*
* Read integer value of the numeric variable named " name " .
*
* Return defaultval if it is not set , or faultval if its value is not a
* valid integer . ( No error message is issued . )
*/
int
int
GetVariableNum ( VariableSpace space ,
GetVariableNum ( VariableSpace space ,
const char * name ,
const char * name ,
int defaultval ,
int defaultval ,
int faultval ,
int faultval )
bool allowtrail )
{
{
const char * val ;
const char * val ;
int result ;
val = GetVariable ( space , name ) ;
val = GetVariable ( space , name ) ;
return ParseVariableNum ( val , defaultval , faultval , allowtrail ) ;
if ( ! val )
return defaultval ;
if ( ParseVariableNum ( val , NULL , & result ) )
return result ;
else
return faultval ;
}
}
/*
* Print values of all variables .
*/
void
void
PrintVariables ( VariableSpace space )
PrintVariables ( VariableSpace space )
{
{
@ -184,17 +216,28 @@ PrintVariables(VariableSpace space)
}
}
}
}
/*
* Set the variable named " name " to value " value " ,
* or delete it if " value " is NULL .
*
* Returns true if successful , false if not ; in the latter case a suitable
* error message has been printed , except for the unexpected case of
* space or name being NULL .
*/
bool
bool
SetVariable ( VariableSpace space , const char * name , const char * value )
SetVariable ( VariableSpace space , const char * name , const char * value )
{
{
struct _variable * current ,
struct _variable * current ,
* previous ;
* previous ;
if ( ! space )
if ( ! space | | ! name )
return false ;
return false ;
if ( ! valid_variable_name ( name ) )
if ( ! valid_variable_name ( name ) )
{
psql_error ( " invalid variable name: \" %s \" \n " , name ) ;
return false ;
return false ;
}
if ( ! value )
if ( ! value )
return DeleteVariable ( space , name ) ;
return DeleteVariable ( space , name ) ;
@ -205,13 +248,30 @@ SetVariable(VariableSpace space, const char *name, const char *value)
{
{
if ( strcmp ( current - > name , name ) = = 0 )
if ( strcmp ( current - > name , name ) = = 0 )
{
{
/* found entry, so update */
/*
if ( current - > value )
* Found entry , so update , unless hook returns false . The hook
free ( current - > value ) ;
* may need the passed value to have the same lifespan as the
current - > value = pg_strdup ( value ) ;
* variable , so allocate it right away , even though we ' ll have to
* free it again if the hook returns false .
*/
char * new_value = pg_strdup ( value ) ;
bool confirmed ;
if ( current - > assign_hook )
if ( current - > assign_hook )
( * current - > assign_hook ) ( current - > value ) ;
confirmed = ( * current - > assign_hook ) ( new_value ) ;
return true ;
else
confirmed = true ;
if ( confirmed )
{
if ( current - > value )
pg_free ( current - > value ) ;
current - > value = new_value ;
}
else
pg_free ( new_value ) ; /* current->value is left unchanged */
return confirmed ;
}
}
}
}
@ -226,19 +286,29 @@ SetVariable(VariableSpace space, const char *name, const char *value)
}
}
/*
/*
* This both sets a hook function , and calls it on the current value ( if any )
* Attach an assign hook function to the named variable .
*
* If the variable doesn ' t already exist , create it with value NULL ,
* just so we have a place to store the hook function . ( Externally ,
* this isn ' t different from it not being defined . )
*
* The hook is immediately called on the variable ' s current value . This is
* meant to let it update any derived psql state . If the hook doesn ' t like
* the current value , it will print a message to that effect , but we ' ll ignore
* it . Generally we do not expect any such failure here , because this should
* get called before any user - supplied value is assigned .
*/
*/
bool
void
SetVariableAssignHook ( VariableSpace space , const char * name , VariableAssignHook hook )
SetVariableAssignHook ( VariableSpace space , const char * name , VariableAssignHook hook )
{
{
struct _variable * current ,
struct _variable * current ,
* previous ;
* previous ;
if ( ! space )
if ( ! space | | ! name )
return false ;
return ;
if ( ! valid_variable_name ( name ) )
if ( ! valid_variable_name ( name ) )
return false ;
return ;
for ( previous = space , current = space - > next ;
for ( previous = space , current = space - > next ;
current ;
current ;
@ -248,8 +318,8 @@ SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook
{
{
/* found entry, so update */
/* found entry, so update */
current - > assign_hook = hook ;
current - > assign_hook = hook ;
( * hook ) ( current - > value ) ;
( void ) ( * hook ) ( current - > value ) ;
return true ;
return ;
}
}
}
}
@ -260,16 +330,24 @@ SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook
current - > assign_hook = hook ;
current - > assign_hook = hook ;
current - > next = NULL ;
current - > next = NULL ;
previous - > next = current ;
previous - > next = current ;
( * hook ) ( NULL ) ;
( void ) ( * hook ) ( NULL ) ;
return true ;
}
}
/*
* Convenience function to set a variable ' s value to " on " .
*/
bool
bool
SetVariableBool ( VariableSpace space , const char * name )
SetVariableBool ( VariableSpace space , const char * name )
{
{
return SetVariable ( space , name , " on " ) ;
return SetVariable ( space , name , " on " ) ;
}
}
/*
* Attempt to delete variable .
*
* If unsuccessful , print a message and return " false " .
* Deleting a nonexistent variable is not an error .
*/
bool
bool
DeleteVariable ( VariableSpace space , const char * name )
DeleteVariable ( VariableSpace space , const char * name )
{
{
@ -277,7 +355,7 @@ DeleteVariable(VariableSpace space, const char *name)
* previous ;
* previous ;
if ( ! space )
if ( ! space )
return fals e;
return tru e;
for ( previous = space , current = space - > next ;
for ( previous = space , current = space - > next ;
current ;
current ;
@ -285,14 +363,21 @@ DeleteVariable(VariableSpace space, const char *name)
{
{
if ( strcmp ( current - > name , name ) = = 0 )
if ( strcmp ( current - > name , name ) = = 0 )
{
{
if ( current - > value )
free ( current - > value ) ;
current - > value = NULL ;
/* Physically delete only if no hook function to remember */
if ( current - > assign_hook )
if ( current - > assign_hook )
( * current - > assign_hook ) ( NULL ) ;
{
/* Allow deletion only if hook is okay with NULL value */
if ( ! ( * current - > assign_hook ) ( NULL ) )
return false ; /* message printed by hook */
if ( current - > value )
free ( current - > value ) ;
current - > value = NULL ;
/* Don't delete entry, or we'd forget the hook function */
}
else
else
{
{
/* We can delete the entry as well as its value */
if ( current - > value )
free ( current - > value ) ;
previous - > next = current - > next ;
previous - > next = current - > next ;
free ( current - > name ) ;
free ( current - > name ) ;
free ( current ) ;
free ( current ) ;
@ -303,3 +388,16 @@ DeleteVariable(VariableSpace space, const char *name)
return true ;
return true ;
}
}
/*
* Emit error with suggestions for variables or commands
* accepting enum - style arguments .
* This function just exists to standardize the wording .
* suggestions should follow the format " fee, fi, fo, fum " .
*/
void
PsqlVarEnumError ( const char * name , const char * value , const char * suggestions )
{
psql_error ( " unrecognized value \" %s \" for \" %s \" \n Available values are: %s. \n " ,
value , name , suggestions ) ;
}