From: scottmk <scottmk@dcc99617-32d9-48b4-a31d-7c20da2025e4>
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=contrib%2FConifer.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 5ddc7b7864..1d18fc57a3 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 aeb35eacaa..34b277a851 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 01ab891442..546b973478 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" );