From 92c0d73dd55cedb14915f7fe3f083ce65aac34f1 Mon Sep 17 00:00:00 2001 From: scottmk Date: Mon, 19 Apr 2010 12:48:01 +0000 Subject: [PATCH] Add a new service, open-ils.qstore. It is not yet useful except for testing connectivity, because the methods are stubbed out. It will require changes to opensrf.core before a client can access it. A Open-ILS/src/c-apps/oils_qstore.c M Open-ILS/src/c-apps/Makefile.am git-svn-id: svn://svn.open-ils.org/ILS/trunk@16270 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- Open-ILS/src/c-apps/Makefile.am | 6 +- Open-ILS/src/c-apps/oils_qstore.c | 233 ++++++++++++++++++++++++++++++ 2 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 Open-ILS/src/c-apps/oils_qstore.c diff --git a/Open-ILS/src/c-apps/Makefile.am b/Open-ILS/src/c-apps/Makefile.am index 2892fd68b9..e63904cbc2 100644 --- a/Open-ILS/src/c-apps/Makefile.am +++ b/Open-ILS/src/c-apps/Makefile.am @@ -21,7 +21,7 @@ test_json_query_CFLAGS = $(AM_CFLAGS) test_json_query_LDFLAGS = $(AM_LDFLAGS) -loils_idl -loils_utils test_json_query_DEPENDENCIES = liboils_idl.la liboils_utils.la -lib_LTLIBRARIES = liboils_idl.la liboils_utils.la oils_cstore.la oils_rstore.la oils_pcrud.la oils_auth.la +lib_LTLIBRARIES = liboils_idl.la liboils_utils.la oils_cstore.la oils_qstore.la oils_rstore.la oils_pcrud.la oils_auth.la liboils_idl_la_SOURCES = oils_idl-core.c @@ -31,6 +31,10 @@ oils_cstore_la_SOURCES = oils_cstore.c oils_sql.c 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_qstore_la_LDFLAGS = $(AM_LDFLAGS) -loils_idl -ldbi -ldbdpgsql -loils_utils -module +oils_qstore_la_DEPENDENCIES = liboils_idl.la liboils_idl.la + oils_rstore_la_SOURCES = oils_rstore.c oils_sql.c oils_rstore_la_LDFLAGS = $(AM_LDFLAGS) -loils_idl -ldbi -ldbdpgsql -loils_utils -module oils_rstore_la_DEPENDENCIES = liboils_idl.la liboils_idl.la diff --git a/Open-ILS/src/c-apps/oils_qstore.c b/Open-ILS/src/c-apps/oils_qstore.c new file mode 100644 index 0000000000..e22e5d4e66 --- /dev/null +++ b/Open-ILS/src/c-apps/oils_qstore.c @@ -0,0 +1,233 @@ +/** + @file oils_qstore.c + @brief As a server, perform database queries as defined in the database itself. +*/ + +#include +#include +#include +#include +#include "opensrf/utils.h" +#include "opensrf/log.h" +#include "opensrf/osrf_json.h" +#include "opensrf/osrf_application.h" +#include "openils/oils_utils.h" +#include "openils/oils_sql.h" + +static dbi_conn dbhandle; /* our db connection */ + +static const char modulename[] = "open-ils.qstore"; + +int doPrepare( osrfMethodContext* ctx ); +int doExecute( osrfMethodContext* ctx ); +int doSql( osrfMethodContext* ctx ); + +/** + @brief Disconnect from the database. + + This function is called when the server drone is about to terminate. +*/ +void osrfAppChildExit() { + osrfLogDebug( OSRF_LOG_MARK, "Child is exiting, disconnecting from database..." ); + + if ( dbhandle ) { + dbi_conn_query( dbhandle, "ROLLBACK;" ); + dbi_conn_close( dbhandle ); + dbhandle = NULL; + } +} + +/** + @brief Initialize the application. + @return Zero if successful, or non-zero if not. + + Load the IDL file into an internal data structure for future reference. Each non-virtual + class in the IDL corresponds to a table or view in the database, or to a subquery defined + in the IDL. Ignore all virtual tables and virtual fields. + + Register the functions for remote procedure calls. + + This function is called when the registering the application, and is executed by the + listener before spawning the drones. +*/ +int osrfAppInitialize() { + + osrfLogInfo( OSRF_LOG_MARK, "Initializing the QStore Server..." ); + osrfLogInfo( OSRF_LOG_MARK, "Finding XML file..." ); + + if ( !oilsIDLInit( osrf_settings_host_value( "/IDL" ))) + return 1; /* return non-zero to indicate error */ + + growing_buffer* method_name = buffer_init( 64 ); + + OSRF_BUFFER_ADD( method_name, modulename ); + OSRF_BUFFER_ADD( method_name, ".prepare" ); + osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ), + "doBuild", "", 1, 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 ); + + 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 ); + + return 0; +} + +/** + @brief Initialize a server drone. + @return Zero if successful, -1 if not. + + Connect to the database. For each non-virtual class in the IDL, execute a dummy "SELECT * " + query to get the datatype of each column. Record the datatypes in the loaded IDL. + + This function is called by a server drone shortly after it is spawned by the listener. +*/ +int osrfAppChildInit() { + + osrfLogDebug( OSRF_LOG_MARK, "Attempting to initialize libdbi..." ); + dbi_initialize( NULL ); + osrfLogDebug( OSRF_LOG_MARK, "... libdbi initialized." ); + + char* driver = osrf_settings_host_value( "/apps/%s/app_settings/driver", modulename ); + char* user = osrf_settings_host_value( "/apps/%s/app_settings/database/user", modulename ); + char* host = osrf_settings_host_value( "/apps/%s/app_settings/database/host", modulename ); + char* port = osrf_settings_host_value( "/apps/%s/app_settings/database/port", modulename ); + char* db = osrf_settings_host_value( "/apps/%s/app_settings/database/db", modulename ); + char* pw = osrf_settings_host_value( "/apps/%s/app_settings/database/pw", modulename ); + + osrfLogDebug( OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver ); + dbhandle = dbi_conn_new( driver ); + + if( !dbhandle ) { + osrfLogError( OSRF_LOG_MARK, "Error loading database driver [%s]", driver ); + return -1; + } + osrfLogDebug( OSRF_LOG_MARK, "Database driver [%s] seems OK", driver ); + + osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database. host=%s, " + "port=%s, user=%s, db=%s", modulename, host, port, user, db ); + + if( host ) dbi_conn_set_option( dbhandle, "host", host ); + if( port ) dbi_conn_set_option_numeric( dbhandle, "port", atoi( port ) ); + if( user ) dbi_conn_set_option( dbhandle, "username", user ); + if( pw ) dbi_conn_set_option( dbhandle, "password", pw ); + if( db ) dbi_conn_set_option( dbhandle, "dbname", db ); + + free( user ); + free( host ); + free( port ); + free( db ); + free( pw ); + + const char* err; + if( dbi_conn_connect( dbhandle ) < 0 ) { + sleep( 1 ); + if( dbi_conn_connect( dbhandle ) < 0 ) { + dbi_conn_error( dbhandle, &err ); + osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err ); + return -1; + } + } + + oilsSetDBConnection( dbhandle ); + osrfLogInfo( OSRF_LOG_MARK, "%s successfully connected to the database", modulename ); + + // Add datatypes from database to the fields in the IDL + if( oilsExtendIDL() ) { + osrfLogError( OSRF_LOG_MARK, "Error extending the IDL" ); + return -1; + } + else + return 0; +} + +int doPrepare( osrfMethodContext* ctx ) { + if(osrfMethodVerifyContext( ctx )) { + osrfLogError( OSRF_LOG_MARK, "Invalid method context" ); + return -1; + } + + // Get the query id from a method parameter + const jsonObject* query_id_obj = jsonObjectGetIndex( ctx->params, 0 ); + if( query_id_obj->type != JSON_NUMBER ) { + osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException", + ctx->request, "Invalid parameter; query id must be a number" ); + return -1; + } + int query_id = atoi( jsonObjectGetString( query_id_obj )); + if( query_id <= 0 ) { + osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException", + ctx->request, "Invalid parameter: query id must be greater than zero" ); + return -1; + } + + osrfLogInfo( OSRF_LOG_MARK, "Building query for id # %d", query_id ); + + osrfAppRespondComplete( ctx, jsonNewObject( "build method not yet implemented" )); + return 0; +} + +int doExecute( osrfMethodContext* ctx ) { + if(osrfMethodVerifyContext( ctx )) { + osrfLogError( OSRF_LOG_MARK, "Invalid method context" ); + return -1; + } + + // Get the query token + 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 id must be a string" ); + return -1; + } + const char* token = jsonObjectGetString( token_obj ); + + // Get the list of bind variables, if there is one + jsonObject* bind_map = jsonObjectGetIndex( ctx->params, 1 ); + if( bind_map && bind_map->type != JSON_HASH ) { + osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException", + ctx->request, "Invalid parameter; bind map must be a JSON object" ); + return -1; + } + + osrfLogInfo( OSRF_LOG_MARK, "Executing query for token \"%s\"", token ); + + osrfAppRespondComplete( ctx, jsonNewObject( "execute method not yet implemented" )); + return 0; +} + +int doSql( osrfMethodContext* ctx ) { + if(osrfMethodVerifyContext( ctx )) { + osrfLogError( OSRF_LOG_MARK, "Invalid method context" ); + return -1; + } + + // Get the query token + 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 id must be a string" ); + return -1; + } + const char* token = jsonObjectGetString( token_obj ); + + // Get the list of bind variables, if there is one + jsonObject* bind_map = jsonObjectGetIndex( ctx->params, 1 ); + if( bind_map && bind_map->type != JSON_HASH ) { + osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException", + ctx->request, "Invalid parameter; bind map must be a JSON object" ); + return -1; + } + + osrfLogInfo( OSRF_LOG_MARK, "Returning SQL for token \"%s\"", token ); + + osrfAppRespondComplete( ctx, jsonNewObject( "sql method not yet implemented" )); + return 0; +} -- 2.43.2