From 402766a42ef0c095c7e205e0a6fd53e708490509 Mon Sep 17 00:00:00 2001 From: scottmk Date: Thu, 6 Aug 2009 22:06:19 +0000 Subject: [PATCH] Tightened the rules for defining SELECT clauses for a JSON query. With two exceptions as noted below, the SELECT list for every class must be encoded as an array. Anything else is an error. The exceptions: null and "*". For the core class only, these encodings request a default SELECT list. For the core class: an empty array requests a default SELECT list. For a non-core class: an empty array results in an error message, but not an error. git-svn-id: svn://svn.open-ils.org/ILS/trunk@13799 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- Open-ILS/src/c-apps/oils_cstore.c | 143 ++++++++++++++++++++++++++++++-------- 1 file changed, 114 insertions(+), 29 deletions(-) diff --git a/Open-ILS/src/c-apps/oils_cstore.c b/Open-ILS/src/c-apps/oils_cstore.c index 848c85f76..9c5fce8c1 100644 --- a/Open-ILS/src/c-apps/oils_cstore.c +++ b/Open-ILS/src/c-apps/oils_cstore.c @@ -2790,6 +2790,38 @@ static char* searchWHERE ( const jsonObject* search_hash, const ClassInfo* class return buffer_release(sql_buf); } +/* Build a JSON_ARRAY of field names for a given table alias + */ +static jsonObject* defaultSelectList( const char* table_alias ) { + + if( ! table_alias ) + table_alias = ""; + + ClassInfo* class_info = search_all_alias( table_alias ); + if( ! class_info ) { + osrfLogError( + OSRF_LOG_MARK, + "%s: Can't build default SELECT clause for \"%s\"; no such table alias", + MODULENAME, + table_alias + ); + return NULL; + } + + jsonObject* array = jsonNewObjectType( JSON_ARRAY ); + osrfHash* field_def = NULL; + osrfHashIterator* field_itr = osrfNewHashIterator( class_info->fields ); + while( ( field_def = osrfHashIteratorNext( field_itr ) ) ) { + const char* field_name = osrfHashIteratorKey( field_itr ); + if( ! str_is_true( osrfHashGet( field_def, "virtual" ) ) ) { + jsonObjectPush( array, jsonNewObject( field_name ) ); + } + } + osrfHashIteratorFree( field_itr ); + + return array; +} + char* SELECT ( /* method context */ osrfMethodContext* ctx, @@ -2950,12 +2982,29 @@ char* SELECT ( // For in case we don't get a select list jsonObject* defaultselhash = NULL; - // if the select list is empty, or the core class field list is '*', - // build the default select list ... + // if there is no select list, build a default select list ... if (!selhash) { + jsonObject* default_list = defaultSelectList( core_class ); + if( ! default_list ) { + if (ctx) { + osrfAppSessionStatus( + ctx->session, + OSRF_STATUS_INTERNALSERVERERROR, + "osrfMethodException", + ctx->request, + "Unable to build default SELECT clause in JSON query" + ); + free( join_clause ); + return NULL; + } + } + selhash = defaultselhash = jsonNewObjectType(JSON_HASH); - jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) ); - } else if( selhash->type != JSON_HASH ) { + jsonObjectSetKey( selhash, core_class, default_list ); + } + + // The SELECT clause can be encoded only by a hash + if( selhash->type != JSON_HASH ) { osrfLogError( OSRF_LOG_MARK, "%s: Expected JSON_HASH for SELECT clause; found %s", @@ -2973,11 +3022,39 @@ char* SELECT ( ); free( join_clause ); return NULL; - } else if ( (tmp_const = jsonObjectGetKeyConst( selhash, core_class )) && tmp_const->type == JSON_STRING ) { - const char* _x = jsonObjectGetString( tmp_const ); - if (!strncmp( "*", _x, 1 )) { - jsonObjectRemoveKey( selhash, core_class ); - jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) ); + } + + // If you see a null or wild card specifier for the core class, or an + // empty array, replace it with a default SELECT list + tmp_const = jsonObjectGetKeyConst( selhash, core_class ); + if ( tmp_const ) { + int default_needed = 0; // boolean + if( JSON_STRING == tmp_const->type + && !strcmp( "*", jsonObjectGetString( tmp_const ) )) + default_needed = 1; + else if( JSON_NULL == tmp_const->type ) + default_needed = 1; + else if( JSON_ARRAY == tmp_const->type && 0 == tmp_const->size ) + default_needed = 1; + + if( default_needed ) { + // Build a default SELECT list + jsonObject* default_list = defaultSelectList( core_class ); + if( ! default_list ) { + if (ctx) { + osrfAppSessionStatus( + ctx->session, + OSRF_STATUS_INTERNALSERVERERROR, + "osrfMethodException", + ctx->request, + "Can't build default SELECT clause in JSON query" + ); + free( join_clause ); + return NULL; + } + } + + jsonObjectSetKey( selhash, core_class, default_list ); } } @@ -2992,24 +3069,7 @@ char* SELECT ( OSRF_BUFFER_ADD_CHAR( select_buf, '*' ); else { - // If we need to build a default list, prepare to do so - jsonObject* _tmp = jsonObjectGetKey( selhash, core_class ); - if ( _tmp && !_tmp->size ) { - - osrfHash* core_fields = curr_query->core.fields; - - osrfHashIterator* field_itr = osrfNewHashIterator( core_fields ); - osrfHash* field_def; - while( ( field_def = osrfHashIteratorNext( field_itr ) ) ) { - if( ! str_is_true( osrfHashGet( field_def, "virtual" ) ) ) { - // This field is not virtual, so add it to the list - jsonObjectPush( _tmp, jsonNewObject( osrfHashIteratorKey( field_itr ) ) ); - } - } - osrfHashIteratorFree( field_itr ); - } - - // Now build the actual select list + // Build the SELECT list as SQL int sel_pos = 1; first = 1; gfirst = 1; @@ -3055,6 +3115,30 @@ char* SELECT ( return NULL; } + if( selclass->type != JSON_ARRAY ) { + osrfLogError( + OSRF_LOG_MARK, + "%s: Malformed SELECT list for class \"%s\"; not an array", + MODULENAME, + cname + ); + if( ctx ) + osrfAppSessionStatus( + ctx->session, + OSRF_STATUS_INTERNALSERVERERROR, + "osrfMethodException", + ctx->request, + "Selected class not in FROM clause in JSON query" + ); + + jsonIteratorFree( selclass_itr ); + buffer_free( select_buf ); + buffer_free( group_buf ); + if( defaultselhash ) jsonObjectFree( defaultselhash ); + free( join_clause ); + return NULL; + } + // Look up some attributes of the current class osrfHash* idlClass = class_info->class_def; osrfHash* class_field_set = class_info->fields; @@ -3343,10 +3427,11 @@ char* SELECT ( char* col_list = buffer_release(select_buf); - // Make sure the SELECT list isn't empty. This can happen if we try to - // build a default SELECT clause from a non-core table. + // Make sure the SELECT list isn't empty. This can happen, for example, + // if we try to build a default SELECT clause from a non-core table. if( ! *col_list ) { + osrfLogError(OSRF_LOG_MARK, "%s: SELECT clause is empty", MODULENAME ); if (ctx) osrfAppSessionStatus( ctx->session, -- 2.11.0