[open-ils-commits] r16328 - in trunk/Open-ILS: include/openils src/c-apps (scottmk)

svn at svn.open-ils.org svn at svn.open-ils.org
Tue Apr 27 23:41:59 EDT 2010


Author: scottmk
Date: 2010-04-27 23:41:56 -0400 (Tue, 27 Apr 2010)
New Revision: 16328

Added:
   trunk/Open-ILS/src/c-apps/oils_execsql.c
Modified:
   trunk/Open-ILS/include/openils/oils_buildq.h
   trunk/Open-ILS/src/c-apps/Makefile.am
   trunk/Open-ILS/src/c-apps/oils_qstore.c
Log:
Implement .execute method of qstore server.

M    Open-ILS/include/openils/oils_buildq.h
M    Open-ILS/src/c-apps/oils_qstore.c
M    Open-ILS/src/c-apps/Makefile.am
A    Open-ILS/src/c-apps/oils_execsql.c


Modified: trunk/Open-ILS/include/openils/oils_buildq.h
===================================================================
--- trunk/Open-ILS/include/openils/oils_buildq.h	2010-04-28 03:36:33 UTC (rev 16327)
+++ trunk/Open-ILS/include/openils/oils_buildq.h	2010-04-28 03:41:56 UTC (rev 16328)
@@ -6,6 +6,8 @@
 #ifndef OILS_BUILDQ_H
 #define OILS_BUILDQ_H
 
+#include "opensrf/osrf_json.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -37,20 +39,21 @@
 /**
 	@brief Stores various things related to the construction of an SQL query.
 	
-	This struct carries around various bits and scraps of context for constructing an SQL
-	query.  It also provides a way for buildSQLQuery() to return more than one kind of thing
-	to its caller.  In particular it can return a status code, a list of error messages, and
-	(if there is no error) an SQL string.
+	This struct carries around various bits and scraps of context for constructing and
+	executing an SQL query.  It also provides a way for buildSQLQuery() to return more than
+	one kind of thing to its caller.  In particular it can return a status code, a list of
+	error messages, and (if there is no error) an SQL string.
 */
 struct BuildSQLState_ {
 	dbi_conn dbhandle;            /**< Handle for the database connection */
+	dbi_result result;            /**< Reference to current row or result set */
 	int error;                    /**< Boolean; true if an error has occurred */
 	osrfStringArray* error_msgs;  /**< Descriptions of errors, if any */
 	growing_buffer* sql;          /**< To hold the constructed query */
 	IdNode* query_stack;          /**< For avoiding infinite recursion of nested queries */
 	IdNode* expr_stack;           /**< For avoiding infinite recursion of nested expressions */
 	IdNode* from_stack;           /**< For avoiding infinite recursion of from clauses */
-	int indent;                   /**< For prettifying output: level of indentation */
+	int indent;                   /**< For prettifying SQL output: level of indentation */
 };
 
 typedef enum {
@@ -189,6 +192,12 @@
 
 void oilsStoredQSetVerbose( void );
 
+jsonObject* oilsExecSql( BuildSQLState* state );
+
+jsonObject* oilsFirstRow( BuildSQLState* state );
+
+jsonObject* oilsNextRow( BuildSQLState* state );
+
 #ifdef __cplusplus
 }
 #endif

Modified: trunk/Open-ILS/src/c-apps/Makefile.am
===================================================================
--- trunk/Open-ILS/src/c-apps/Makefile.am	2010-04-28 03:36:33 UTC (rev 16327)
+++ trunk/Open-ILS/src/c-apps/Makefile.am	2010-04-28 03:41:56 UTC (rev 16328)
@@ -31,7 +31,7 @@
 oils_cstore_la_LDFLAGS = $(AM_LDFLAGS) -loils_idl -ldbi -ldbdpgsql -loils_utils -module
 oils_cstore_la_DEPENDENCIES = liboils_idl.la liboils_idl.la
 
-oils_qstore_la_SOURCES = oils_qstore.c oils_sql.c oils_storedq.c oils_buildq.c buildSQL.c
+oils_qstore_la_SOURCES = oils_qstore.c oils_sql.c oils_storedq.c oils_buildq.c buildSQL.c oils_execsql.c
 oils_qstore_la_LDFLAGS = $(AM_LDFLAGS) -loils_idl -ldbi -ldbdpgsql -loils_utils -module
 oils_qstore_la_DEPENDENCIES = liboils_idl.la liboils_idl.la
 

Added: trunk/Open-ILS/src/c-apps/oils_execsql.c
===================================================================
--- trunk/Open-ILS/src/c-apps/oils_execsql.c	                        (rev 0)
+++ trunk/Open-ILS/src/c-apps/oils_execsql.c	2010-04-28 03:41:56 UTC (rev 16328)
@@ -0,0 +1,172 @@
+/**
+	@file oils_execsql.c
+	@brief Excecute a specified SQL query and return the results.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <dbi/dbi.h>
+#include "opensrf/utils.h"
+#include "opensrf/log.h"
+#include "opensrf/string_array.h"
+#include "opensrf/osrf_json.h"
+#include "openils/oils_buildq.h"
+
+static jsonObject* get_row( BuildSQLState* state );
+static jsonObject* get_date_column( dbi_result result, int col_idx );
+
+jsonObject* oilsExecSql( BuildSQLState* state ) {
+
+	if( !state )
+		return NULL;
+
+	// Execute the query
+	dbi_result result = dbi_conn_query( state->dbhandle, OSRF_BUFFER_C_STR( state->sql ));
+	if( !result ) {
+		state->error = 1;
+		const char* msg;
+		(void) dbi_conn_error( state->dbhandle, &msg );
+		osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
+			"Unable to execute query: %s",msg ? msg : "No description available" ));
+		return NULL;
+	}
+
+	if( !dbi_result_first_row( result ) )
+		return NULL;         // No rows returned
+
+	jsonObject* result_set = jsonNewObjectType( JSON_ARRAY );
+
+	do {
+		jsonObject* row = get_row( state );
+		if( row )
+			jsonObjectPush( result_set, row );
+	} while( dbi_result_next_row( result ));
+
+	dbi_result_free( result );
+	return result_set;
+}
+
+jsonObject* oilsFirstRow( BuildSQLState* state ) {
+
+	if( !state )
+		return NULL;
+
+	if( state->result )
+		dbi_result_free( state->result );
+
+	// Execute the query
+	state->result = dbi_conn_query( state->dbhandle, OSRF_BUFFER_C_STR( state->sql ));
+	if( !state->result ) {
+		state->error = 1;
+		const char* msg;
+		(void) dbi_conn_error( state->dbhandle, &msg );
+		osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state,
+			"Unable to execute query: %s",msg ? msg : "No description available" ));
+		return NULL;
+	}
+
+	// Get the first row
+	if( dbi_result_first_row( state->result ))
+		return get_row( state );
+	else {
+		dbi_result_free( state->result );
+		state->result = NULL;
+		return NULL;         // No rows returned
+	}
+}
+
+jsonObject* oilsNextRow( BuildSQLState* state ) {
+
+	if( !state || !state->result )
+		return NULL;
+
+	// Get the next row
+	if( dbi_result_next_row( state->result ))
+		return get_row( state );
+	else {
+		dbi_result_free( state->result );
+		state->result = NULL;
+		return NULL;         // No next row returned
+	}
+}
+
+static jsonObject* get_row( BuildSQLState* state  ) {
+	unsigned int col_count = dbi_result_get_numfields( state->result );
+	jsonObject* row = jsonNewObjectType( JSON_ARRAY );
+
+	unsigned int i = 1;
+	for( i = 1; i <= col_count; ++i ) {
+
+		if( dbi_result_field_is_null_idx( state->result, i )) {
+			jsonObjectPush( row, jsonNewObjectType( JSON_NULL ));
+			continue;       // Column is null
+		}
+
+		jsonObject* col_value = NULL;
+		int type = dbi_result_get_field_type_idx( state->result, i );
+		switch( type ) {
+			case DBI_TYPE_INTEGER : {
+				long long value = dbi_result_get_longlong_idx( state->result, i );
+				col_value = jsonNewNumberObject( (double) value );
+				break;
+			}
+			case DBI_TYPE_DECIMAL : {
+				double value = dbi_result_get_double_idx( state->result, i );
+				col_value = jsonNewNumberObject( value );
+				break;
+			}
+			case DBI_TYPE_STRING : {
+				const char* value = dbi_result_get_string_idx( state->result, i );
+				col_value = jsonNewObject( value );
+				break;
+			}
+			case DBI_TYPE_BINARY : {
+				osrfLogError( OSRF_LOG_MARK, "Binary types not supported; column set to null" );
+				col_value = jsonNewObjectType( JSON_NULL );
+				break;
+			}
+			case DBI_TYPE_DATETIME : {
+				col_value = get_date_column( state->result, i );
+				break;
+			}
+			default :
+				osrfLogError( OSRF_LOG_MARK, 
+					"Unrecognized column type %d; column set to null", type );
+				col_value = jsonNewObjectType( JSON_NULL );
+				break;
+		}
+		jsonObjectPush( row, col_value );
+	}
+
+	return row;
+}
+
+/**
+	@brief Translate a date column into a string.
+	@param result Reference to the current returned row.
+	@param col_idx Column number (starting with 1) within the row.
+	@return Pointer to a newly-allocated JSON_STRING containing a formatted date string.
+
+	The calling code is responsible for freeing the returned jsonObject by calling 
+	jsonObjectFree().
+*/
+static jsonObject* get_date_column( dbi_result result, int col_idx ) {
+
+	time_t timestamp = dbi_result_get_datetime_idx( result, col_idx );
+	char timestring[ 256 ] = "";
+	int attr = dbi_result_get_field_attribs_idx( result, col_idx );
+	struct tm gmdt;
+
+	if( !( attr & DBI_DATETIME_DATE )) {
+		gmtime_r( &timestamp, &gmdt );
+		strftime( timestring, sizeof( timestring ), "%T", &gmdt );
+	} else if( !( attr & DBI_DATETIME_TIME )) {
+		localtime_r( &timestamp, &gmdt );
+		strftime( timestring, sizeof( timestring ), "%F", &gmdt );
+	} else {
+		localtime_r( &timestamp, &gmdt );
+		strftime( timestring, sizeof( timestring ), "%FT%T%z", &gmdt );
+	}
+
+	return jsonNewObject( timestring );
+}

Modified: trunk/Open-ILS/src/c-apps/oils_qstore.c
===================================================================
--- trunk/Open-ILS/src/c-apps/oils_qstore.c	2010-04-28 03:36:33 UTC (rev 16327)
+++ trunk/Open-ILS/src/c-apps/oils_qstore.c	2010-04-28 03:41:56 UTC (rev 16328)
@@ -285,8 +285,35 @@
 	}
 
 	osrfLogInfo( OSRF_LOG_MARK, "Executing query for token \"%s\"", token );
+	if( query->state->error ) {
+		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( query->state,
+			"No valid prepared query available for query id # %d", query->query->id ));
+		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
+							  ctx->request, "No valid prepared query available" );
+		return -1;
+	} else if( buildSQL( query->state, query->query )) {
+		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( query->state,
+			"Unable to build SQL statement for query id # %d", query->query->id ));
+		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
+			ctx->request, "Unable to build SQL statement" );
+		return -1;
+	}
 
-	osrfAppRespondComplete( ctx, jsonNewObject( "execute method not yet implemented" ));
+	jsonObject* row = oilsFirstRow( query->state );
+	while( row ) {
+		osrfAppRespond( ctx, row );
+		row = oilsNextRow( query->state );
+	}
+
+	if( query->state->error ) {
+		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( query->state,
+			"Unable to execute SQL statement for query id # %d", query->query->id ));
+		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
+			ctx->request, "Unable to execute SQL statement" );
+		return -1;
+	}
+
+	osrfAppRespondComplete( ctx, NULL );
 	return 0;
 }
 
@@ -315,14 +342,14 @@
 
 	osrfLogInfo( OSRF_LOG_MARK, "Returning SQL for token \"%s\"", token );
 	if( query->state->error ) {
-		osrfLogWarning( OSRF_LOG_MARK, "No valid prepared query available for query id # %d",
-			query->query->id );
+		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( query->state,
+			"No valid prepared query available for query id # %d", query->query->id ));
 		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
 			ctx->request, "No valid prepared query available" );
 		return -1;
 	} else if( buildSQL( query->state, query->query )) {
-		osrfLogWarning( OSRF_LOG_MARK, "Unable to build SQL statement for query id # %d",
-			query->query->id );
+		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( query->state,
+			"Unable to build SQL statement for query id # %d", query->query->id ));
 		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
 			ctx->request, "Unable to build SQL statement" );
 		return -1;
@@ -332,6 +359,16 @@
 	return 0;
 }
 
+/**
+	@brief Discard a previously stored query, as identified by a token.
+	@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.
+
+	Returns: Nothing.
+*/
 int doFinish( osrfMethodContext* ctx ) {
 	if(osrfMethodVerifyContext( ctx )) {
 		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );



More information about the open-ils-commits mailing list