Implement new param_list method, which returns a list of
authorscottmk <scottmk@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Thu, 20 May 2010 01:25:15 +0000 (01:25 +0000)
committerscottmk <scottmk@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Thu, 20 May 2010 01:25:15 +0000 (01:25 +0000)
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

Open-ILS/include/openils/oils_buildq.h
Open-ILS/src/c-apps/buildSQL.c
Open-ILS/src/c-apps/oils_qstore.c

index 5ddc7b7..1d18fc5 100644 (file)
@@ -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
index aeb35ea..34b277a 100644 (file)
@@ -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 );
index 01ab891..546b973 100644 (file)
@@ -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" );