From: scottmk Date: Thu, 20 May 2010 01:25:15 +0000 (+0000) Subject: Implement new param_list method, which returns a list of X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=3bd89b1b4c3ff11033167e21b9a3915859cd1335;p=evergreen%2Fbjwebb.git Implement new param_list method, which returns a list of bind variables so that the client can populate them. M Open-ILS/include/openils/oils_buildq.h M Open-ILS/src/c-apps/oils_qstore.c M Open-ILS/src/c-apps/buildSQL.c git-svn-id: svn://svn.open-ils.org/ILS/trunk@16455 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- diff --git a/Open-ILS/include/openils/oils_buildq.h b/Open-ILS/include/openils/oils_buildq.h index 5ddc7b786..1d18fc57a 100644 --- a/Open-ILS/include/openils/oils_buildq.h +++ b/Open-ILS/include/openils/oils_buildq.h @@ -224,6 +224,10 @@ jsonObject* oilsFirstRow( BuildSQLState* state ); jsonObject* oilsNextRow( BuildSQLState* state ); +jsonObject* oilsBindVarList( osrfHash* bindvar_list ); + +int oilsApplyBindValues( BuildSQLState* state, jsonObject* bindings ); + #ifdef __cplusplus } #endif diff --git a/Open-ILS/src/c-apps/buildSQL.c b/Open-ILS/src/c-apps/buildSQL.c index aeb35eaca..34b277a85 100644 --- a/Open-ILS/src/c-apps/buildSQL.c +++ b/Open-ILS/src/c-apps/buildSQL.c @@ -31,6 +31,78 @@ static inline void incr_indent( BuildSQLState* state ); static inline void decr_indent( BuildSQLState* state ); /** + @brief Create a jsonObject representing the current list of bind variables. + @param bindvar_list Pointer to the bindvar_list member of a BuildSQLState. + @return Pointer to the newly created jsonObject. + + The returned jsonObject is a (possibly empty) JSON_HASH, keyed on the names of the bind + variables. The data for each is another level of JSON_HASH with a fixed set of tags: + - "label" + - "type" + - "description" + - "default_value" (as a jsonObject) + - "actual_value" (as a jsonObject) + + Any non-existent values are represented as JSON_NULLs. + + The calling code is responsible for freeing the returned jsonOjbect by calling + jsonObjectFree(). +*/ +jsonObject* oilsBindVarList( osrfHash* bindvar_list ) { + jsonObject* list = jsonNewObjectType( JSON_HASH ); + + if( bindvar_list && osrfHashGetCount( bindvar_list )) { + // Traverse our internal list of bind variables + BindVar* bind = NULL; + osrfHashIterator* iter = osrfNewHashIterator( bindvar_list ); + while(( bind = osrfHashIteratorNext( iter ))) { + // Create an hash to represent the bind variable + jsonObject* bind_obj = jsonNewObjectType( JSON_HASH ); + + // Add an entry for each attribute + jsonObject* attr = jsonNewObject( bind->label ); + jsonObjectSetKey( bind_obj, "label", attr ); + + const char* type = NULL; + switch( bind->type ) { + case BIND_STR : + type = "string"; + break; + case BIND_NUM : + type = "number"; + break; + case BIND_STR_LIST : + type = "string_list"; + break; + case BIND_NUM_LIST : + type = "number_list"; + break; + default : + type = "(invalid)"; + break; + } + attr = jsonNewObject( type ); + jsonObjectSetKey( bind_obj, "type", attr ); + + attr = jsonNewObject( bind->description ); + jsonObjectSetKey( bind_obj, "description", attr ); + + attr = jsonObjectClone( bind->default_value ); + jsonObjectSetKey( bind_obj, "default_value", attr ); + + attr = jsonObjectClone( bind->actual_value ); + jsonObjectSetKey( bind_obj, "actual_value", attr ); + + // Add the bind variable to the list + jsonObjectSetKey( list, osrfHashIteratorKey( iter ), bind_obj ); + } + osrfHashIteratorFree( iter ); + } + + return list; +} + +/** @brief Apply values to bind variables, overriding the defaults, if any. @param state Pointer to the query-building context. @param bindings A JSON_HASH of values. @@ -60,7 +132,10 @@ int oilsApplyBindValues( BuildSQLState* state, jsonObject* bindings ) { const char* var_name = iter->key; BindVar* bind = osrfHashGet( state->bindvar_list, var_name ); if( bind ) { - ; + // Apply or replace the value for the specified variable + if( bind->actual_value ) + jsonObjectFree( bind->actual_value ); + bind->actual_value = jsonObjectClone( value ); } else { osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, "Can't assign value to bind variable \"%s\": no such variable", var_name )); @@ -216,9 +291,9 @@ static void buildSelect( BuildSQLState* state, StoredQ* query ) { decr_indent( state ); } - // To do: build GROUP BY clause, if there is one + // To do: build GROUP BY clause, if there is one - // Build HAVING clause, if there is one + // Build HAVING clause, if there is one if( query->having_clause ) { add_newline( state ); buffer_add( state->sql, "HAVING" ); @@ -232,7 +307,7 @@ static void buildSelect( BuildSQLState* state, StoredQ* query ) { } decr_indent( state ); } - + // Build ORDER BY clause, if there is one if( query->order_by_list ) { buildOrderBy( state, query->order_by_list ); diff --git a/Open-ILS/src/c-apps/oils_qstore.c b/Open-ILS/src/c-apps/oils_qstore.c index 01ab89144..546b97347 100644 --- a/Open-ILS/src/c-apps/oils_qstore.c +++ b/Open-ILS/src/c-apps/oils_qstore.c @@ -86,43 +86,49 @@ int osrfAppInitialize() { OSRF_BUFFER_ADD( method_name, modulename ); OSRF_BUFFER_ADD( method_name, ".prepare" ); osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ), - "doPrepare", "", 1, 0 ); + "doPrepare", "", 1, 0 ); buffer_reset( method_name ); OSRF_BUFFER_ADD( method_name, modulename ); OSRF_BUFFER_ADD( method_name, ".columns" ); osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ), - "doColumns", "", 1, 0 ); + "doColumns", "", 1, 0 ); + + buffer_reset( method_name ); + OSRF_BUFFER_ADD( method_name, modulename ); + OSRF_BUFFER_ADD( method_name, ".param_list" ); + osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ), + "doParamList", "", 1, 0 ); buffer_reset( method_name ); OSRF_BUFFER_ADD( method_name, modulename ); OSRF_BUFFER_ADD( method_name, ".bind_param" ); osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ), - "doBindParam", "", 2, 0 ); + "doBindParam", "", 2, 0 ); buffer_reset( method_name ); OSRF_BUFFER_ADD( method_name, modulename ); OSRF_BUFFER_ADD( method_name, ".execute" ); osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ), - "doExecute", "", 1, OSRF_METHOD_STREAMING ); + "doExecute", "", 1, OSRF_METHOD_STREAMING ); buffer_reset( method_name ); OSRF_BUFFER_ADD( method_name, modulename ); OSRF_BUFFER_ADD( method_name, ".sql" ); osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ), - "doSql", "", 1, OSRF_METHOD_STREAMING ); + "doSql", "", 1, OSRF_METHOD_STREAMING ); buffer_reset( method_name ); OSRF_BUFFER_ADD( method_name, modulename ); OSRF_BUFFER_ADD( method_name, ".finish" ); osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ), - "doFinish", "", 1, 0 ); + "doFinish", "", 1, 0 ); buffer_reset( method_name ); OSRF_BUFFER_ADD( method_name, modulename ); OSRF_BUFFER_ADD( method_name, ".messages" ); osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ), - "doMessages", "", 1, 0 ); + "doMessages", "", 1, 0 ); return 0; } @@ -256,6 +262,46 @@ int doColumns( osrfMethodContext* ctx ) { } } +int doParamList( osrfMethodContext* ctx ) { + if(osrfMethodVerifyContext( ctx )) { + osrfLogError( OSRF_LOG_MARK, "Invalid method context" ); + return -1; + } + + // Get the query token from a method parameter + const jsonObject* token_obj = jsonObjectGetIndex( ctx->params, 0 ); + if( token_obj->type != JSON_STRING ) { + osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException", + ctx->request, "Invalid parameter; query token must be a string" ); + return -1; + } + const char* token = jsonObjectGetString( token_obj ); + + // Look up the query token in the session-level userData + CachedQuery* query = search_token( ctx, token ); + if( !query ) { + osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException", + ctx->request, "Invalid query token" ); + return -1; + } + + osrfLogInfo( OSRF_LOG_MARK, "Returning list of bind variables for token %s", token ); + + osrfAppRespondComplete( ctx, oilsBindVarList( query->state->bindvar_list ) ); + return 0; +} + +/** + @brief Implement the bind_param method. + @param ctx Pointer to the current method context. + @return Zero if successful, or -1 if not. + + Method parameters: + - query token, as previously returned by the .prepare method. + - hash of bind variable values, keyed on bind variable names. + + Returns: Nothing. +*/ int doBindParam( osrfMethodContext* ctx ) { if(osrfMethodVerifyContext( ctx )) { osrfLogError( OSRF_LOG_MARK, "Invalid method context" );