[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( ×tamp, &gmdt );
+ strftime( timestring, sizeof( timestring ), "%T", &gmdt );
+ } else if( !( attr & DBI_DATETIME_TIME )) {
+ localtime_r( ×tamp, &gmdt );
+ strftime( timestring, sizeof( timestring ), "%F", &gmdt );
+ } else {
+ localtime_r( ×tamp, &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