From a5ec6f2f0b324f0d124b24174e20a8ff54a91406 Mon Sep 17 00:00:00 2001 From: scottmk Date: Thu, 1 Jul 2010 14:14:42 +0000 Subject: [PATCH] Support the following built-in functions with peculiar syntax. They take no parameters and don't even accept empty parentheses: current_date current_time current_timestamp localtime localtimestamp M Open-ILS/src/c-apps/buildSQL.c git-svn-id: svn://svn.open-ils.org/ILS/trunk@16843 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- Open-ILS/src/c-apps/buildSQL.c | 95 ++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 33 deletions(-) diff --git a/Open-ILS/src/c-apps/buildSQL.c b/Open-ILS/src/c-apps/buildSQL.c index 859dbf5324..e19d9fcb55 100644 --- a/Open-ILS/src/c-apps/buildSQL.c +++ b/Open-ILS/src/c-apps/buildSQL.c @@ -26,6 +26,8 @@ static void buildOrderBy( BuildSQLState* state, const OrderItem* ord_list ); static void buildCase( BuildSQLState* state, const Expression* expr ); static void buildExpression( BuildSQLState* state, const Expression* expr ); static void buildFunction( BuildSQLState* state, const Expression* exp ); +static int subexp_count( const Expression* expr ); +static void buildTypicalFunction( BuildSQLState* state, const Expression* expr ); static void buildExtract( BuildSQLState* state, const Expression* expr ); static void buildSeries( BuildSQLState* state, const Expression* subexp_list, const char* op ); static void buildBindVar( BuildSQLState* state, const BindVar* bind ); @@ -938,13 +940,9 @@ static void buildCase( BuildSQLState* state, const Expression* expr ) { } /** - @brief Build a function call. + @brief Build a function call, with a subfield if specified. @param state Pointer to the query-building context. @param exp Pointer to an Expression representing a function call. - - This function does not currently accommodate certain functions with idiosyncratic - syntax, such as the absence of parentheses, or the use of certain keywords in - in the parameter list. */ static void buildFunction( BuildSQLState* state, const Expression* expr ) { if( expr->negate ) @@ -958,19 +956,21 @@ static void buildFunction( BuildSQLState* state, const Expression* expr ) { // First, check for some specific functions with peculiar syntax, and treat them // as special exceptions. We rely on the input side to ensure that the function // name is available. - if( !strcasecmp( expr->function_name, "EXTRACT" )) { + if( !strcasecmp( expr->function_name, "EXTRACT" )) buildExtract( state, expr ); - } else { - // Not a special exception. - buffer_add( state->sql, expr->function_name ); - buffer_add_char( state->sql, '(' ); + else if( !strcasecmp( expr->function_name, "CURRENT_DATE" ) && ! expr->subexp_list ) + buffer_add( state->sql, "CURRENT_DATE " ); + else if( !strcasecmp( expr->function_name, "CURRENT_TIME" ) && ! expr->subexp_list ) + buffer_add( state->sql, "CURRENT_TIME " ); + else if( !strcasecmp( expr->function_name, "CURRENT_TIMESTAMP" ) && ! expr->subexp_list ) + buffer_add( state->sql, "CURRENT_TIMESTAMP " ); + else if( !strcasecmp( expr->function_name, "LOCALTIME" ) && ! expr->subexp_list ) + buffer_add( state->sql, "LOCALTIME " ); + else if( !strcasecmp( expr->function_name, "LOCALTIMESTAMP" ) && ! expr->subexp_list ) + buffer_add( state->sql, "LOCALTIMESTAMP " ); + else + buildTypicalFunction( state, expr ); // Not a special exception. - // Add the parameters, if any - buildSeries( state, expr->subexp_list, NULL ); - - buffer_add_char( state->sql, ')' ); - } - if( expr->column_name ) { // Add the name of the subfield buffer_add( state->sql, ").\"" ); @@ -979,25 +979,59 @@ static void buildFunction( BuildSQLState* state, const Expression* expr ) { } } +/** + @brief Count the number of subexpressions attached to a given Expression. + @param expr Pointer to the Expression whose subexpressions are to be counted. + @return The number of subexpressions. +*/ +static int subexp_count( const Expression* expr ) { + int count = 0; + const Expression* sub = expr->subexp_list; + while( sub ) { + ++count; + sub = sub->next; + } + return count; +} + +/** + @brief Build an ordinary function call, i.e. one with no special syntax, + @param state Pointer to the query-building context. + @param exp Pointer to an Expression representing a function call. + + Emit the parameters as a comma-separated list of expressions. +*/ +static void buildTypicalFunction( BuildSQLState* state, const Expression* expr ) { + buffer_add( state->sql, expr->function_name ); + buffer_add_char( state->sql, '(' ); + + // Add the parameters, if any + buildSeries( state, expr->subexp_list, NULL ); + + buffer_add_char( state->sql, ')' ); +} + /** @brief Build a call to the EXTRACT function, with its peculiar syntax. @param state Pointer to the query-building context. @param exp Pointer to an Expression representing an EXTRACT call. + + If there are not exactly two parameters, or if the first parameter is not a string, + then assume it is an ordinary function overloading on the same name. We don't try to + check the type of the second parameter. Hence it is possible for a legitimately + overloaded function to be uncallable. + + The first parameter of EXTRACT() must be one of a short list of names for some fragment + of a date or time. Here we accept that parameter in the form of a string. We don't + surround it with quotes in the output, although PostgreSQL wouldn't mind if we did. */ static void buildExtract( BuildSQLState* state, const Expression* expr ) { const Expression* arg = expr->subexp_list; - // Sanity checks - if( !arg ) { - sqlAddMsg( state, - "No arguments supplied to EXTRACT function in expression # %d", expr->id ); - state->error = 1; - return; - } else if( arg->type != EXP_STRING ) { - sqlAddMsg( state, - "First argument to EXTRACT is not a string in expression # %d", expr->id ); - state->error = 1; + // See if this is the special form of EXTRACT(), so far as we can tell + if( subexp_count( expr ) != 2 || arg->type != EXP_STRING ) { + buildTypicalFunction( state, expr ); return; } else { // check the first argument against a list of valid values @@ -1022,6 +1056,8 @@ static void buildExtract( BuildSQLState* state, const Expression* expr ) { && strcasecmp( arg->literal, "timezone_minute" ) && strcasecmp( arg->literal, "week" ) && strcasecmp( arg->literal, "year" )) { + // This *could* be an ordinary function, overloading on the name. However it's + // more likely that the user misspelled one of the names expected by EXTRACT(). sqlAddMsg( state, "Invalid name \"%s\" as EXTRACT argument in expression # %d", expr->literal, expr->id ); @@ -1045,13 +1081,6 @@ static void buildExtract( BuildSQLState* state, const Expression* expr ) { // a good way of checking it here, so we rely on PostgreSQL to complain if necessary. buildExpression( state, arg ); buffer_add_char( state->sql, ')' ); - - if( arg->next ) { - sqlAddMsg( state, - "Too many one arguments supplied to EXTRACT function in expression # %d", expr->id ); - state->error = 1; - return; - } } /** -- 2.43.2