From 713cb2adc2d257b5a5d153994c47eeb477a83d04 Mon Sep 17 00:00:00 2001 From: scottmk Date: Thu, 1 Jul 2010 02:29:38 +0000 Subject: [PATCH] 1. Support the peculiar syntax of EXTRACT(). 2. Repair a regression to make SELECT * work again. M Open-ILS/src/c-apps/oils_storedq.c M Open-ILS/src/c-apps/buildSQL.c git-svn-id: svn://svn.open-ils.org/ILS/trunk@16842 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- Open-ILS/src/c-apps/buildSQL.c | 97 +++++++++++++++++++++++++++++++++++--- Open-ILS/src/c-apps/oils_storedq.c | 7 +-- 2 files changed, 91 insertions(+), 13 deletions(-) diff --git a/Open-ILS/src/c-apps/buildSQL.c b/Open-ILS/src/c-apps/buildSQL.c index ab9da2255e..859dbf5324 100644 --- a/Open-ILS/src/c-apps/buildSQL.c +++ b/Open-ILS/src/c-apps/buildSQL.c @@ -26,6 +26,7 @@ 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 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 ); static void buildScalar( BuildSQLState* state, int numeric, const jsonObject* obj ); @@ -954,15 +955,22 @@ static void buildFunction( BuildSQLState* state, const Expression* expr ) { if( expr->column_name ) buffer_add_char( state->sql, '(' ); - // We rely on the input side to ensure that the function name is available - buffer_add( state->sql, expr->function_name ); - buffer_add_char( state->sql, '(' ); + // 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" )) { + buildExtract( state, expr ); + } else { + // Not a special exception. + 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, ')' ); + // 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, ").\"" ); @@ -972,6 +980,81 @@ static void buildFunction( BuildSQLState* state, const Expression* expr ) { } /** + @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. +*/ +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; + return; + } else { + // check the first argument against a list of valid values + if( strcasecmp( arg->literal, "century" ) + && strcasecmp( arg->literal, "day" ) + && strcasecmp( arg->literal, "decade" ) + && strcasecmp( arg->literal, "dow" ) + && strcasecmp( arg->literal, "doy" ) + && strcasecmp( arg->literal, "epoch" ) + && strcasecmp( arg->literal, "hour" ) + && strcasecmp( arg->literal, "isodow" ) + && strcasecmp( arg->literal, "isoyear" ) + && strcasecmp( arg->literal, "microseconds" ) + && strcasecmp( arg->literal, "millennium" ) + && strcasecmp( arg->literal, "milliseconds" ) + && strcasecmp( arg->literal, "minute" ) + && strcasecmp( arg->literal, "month" ) + && strcasecmp( arg->literal, "quarter" ) + && strcasecmp( arg->literal, "second" ) + && strcasecmp( arg->literal, "timezone" ) + && strcasecmp( arg->literal, "timezone_hour" ) + && strcasecmp( arg->literal, "timezone_minute" ) + && strcasecmp( arg->literal, "week" ) + && strcasecmp( arg->literal, "year" )) { + sqlAddMsg( state, + "Invalid name \"%s\" as EXTRACT argument in expression # %d", + expr->literal, expr->id ); + state->error = 1; + } + } + + buffer_add( state->sql, "EXTRACT(" ); + buffer_add( state->sql, arg->literal ); + buffer_add( state->sql, " FROM " ); + + arg = arg->next; + if( !arg ) { + sqlAddMsg( state, + "Only one argument supplied to EXTRACT function in expression # %d", expr->id ); + state->error = 1; + return; + } + + // The second parameter must be of type timestamp, time, or interval. We don't have + // 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; + } +} + +/** @brief Build a series of expressions separated by a specified operator, or by commas. @param state Pointer to the query-building context. @param subexp_list Pointer to the first Expression in a linked list. diff --git a/Open-ILS/src/c-apps/oils_storedq.c b/Open-ILS/src/c-apps/oils_storedq.c index f83128874a..5258bff51f 100644 --- a/Open-ILS/src/c-apps/oils_storedq.c +++ b/Open-ILS/src/c-apps/oils_storedq.c @@ -1766,12 +1766,7 @@ static Expression* constructExpression( BuildSQLState* state, dbi_result result } } else if( EXP_COLUMN == type ) { - if( !column_name ) { - osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state, - "No column name for column expression # %d", id )); - state->error = 1; - return NULL; - } else if( !is_identifier( column_name )) { + if( column_name && !is_identifier( column_name )) { osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( state, "Column name \"%s\" is invalid identifier for expression # %d", column_name, id )); -- 2.11.0