3 @brief As a server, perform database queries as defined in the database itself.
10 #include "opensrf/utils.h"
11 #include "opensrf/log.h"
12 #include "opensrf/osrf_json.h"
13 #include "opensrf/osrf_application.h"
14 #include "openils/oils_utils.h"
15 #include "openils/oils_sql.h"
17 static dbi_conn dbhandle; /* our db connection */
19 static const char modulename[] = "open-ils.qstore";
21 int doPrepare( osrfMethodContext* ctx );
22 int doExecute( osrfMethodContext* ctx );
23 int doSql( osrfMethodContext* ctx );
26 @brief Disconnect from the database.
28 This function is called when the server drone is about to terminate.
30 void osrfAppChildExit() {
31 osrfLogDebug( OSRF_LOG_MARK, "Child is exiting, disconnecting from database..." );
34 dbi_conn_query( dbhandle, "ROLLBACK;" );
35 dbi_conn_close( dbhandle );
41 @brief Initialize the application.
42 @return Zero if successful, or non-zero if not.
44 Load the IDL file into an internal data structure for future reference. Each non-virtual
45 class in the IDL corresponds to a table or view in the database, or to a subquery defined
46 in the IDL. Ignore all virtual tables and virtual fields.
48 Register the functions for remote procedure calls.
50 This function is called when the registering the application, and is executed by the
51 listener before spawning the drones.
53 int osrfAppInitialize() {
55 osrfLogInfo( OSRF_LOG_MARK, "Initializing the QStore Server..." );
56 osrfLogInfo( OSRF_LOG_MARK, "Finding XML file..." );
58 if ( !oilsIDLInit( osrf_settings_host_value( "/IDL" )))
59 return 1; /* return non-zero to indicate error */
61 growing_buffer* method_name = buffer_init( 64 );
63 OSRF_BUFFER_ADD( method_name, modulename );
64 OSRF_BUFFER_ADD( method_name, ".prepare" );
65 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ),
66 "doBuild", "", 1, 0 );
68 buffer_reset( method_name );
69 OSRF_BUFFER_ADD( method_name, modulename );
70 OSRF_BUFFER_ADD( method_name, ".execute" );
71 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ),
72 "doExecute", "", 1, OSRF_METHOD_STREAMING );
74 buffer_reset( method_name );
75 OSRF_BUFFER_ADD( method_name, modulename );
76 OSRF_BUFFER_ADD( method_name, ".sql" );
77 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ),
78 "doSql", "", 1, OSRF_METHOD_STREAMING );
84 @brief Initialize a server drone.
85 @return Zero if successful, -1 if not.
87 Connect to the database. For each non-virtual class in the IDL, execute a dummy "SELECT * "
88 query to get the datatype of each column. Record the datatypes in the loaded IDL.
90 This function is called by a server drone shortly after it is spawned by the listener.
92 int osrfAppChildInit() {
94 osrfLogDebug( OSRF_LOG_MARK, "Attempting to initialize libdbi..." );
95 dbi_initialize( NULL );
96 osrfLogDebug( OSRF_LOG_MARK, "... libdbi initialized." );
98 char* driver = osrf_settings_host_value( "/apps/%s/app_settings/driver", modulename );
99 char* user = osrf_settings_host_value( "/apps/%s/app_settings/database/user", modulename );
100 char* host = osrf_settings_host_value( "/apps/%s/app_settings/database/host", modulename );
101 char* port = osrf_settings_host_value( "/apps/%s/app_settings/database/port", modulename );
102 char* db = osrf_settings_host_value( "/apps/%s/app_settings/database/db", modulename );
103 char* pw = osrf_settings_host_value( "/apps/%s/app_settings/database/pw", modulename );
105 osrfLogDebug( OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver );
106 dbhandle = dbi_conn_new( driver );
109 osrfLogError( OSRF_LOG_MARK, "Error loading database driver [%s]", driver );
112 osrfLogDebug( OSRF_LOG_MARK, "Database driver [%s] seems OK", driver );
114 osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database. host=%s, "
115 "port=%s, user=%s, db=%s", modulename, host, port, user, db );
117 if( host ) dbi_conn_set_option( dbhandle, "host", host );
118 if( port ) dbi_conn_set_option_numeric( dbhandle, "port", atoi( port ) );
119 if( user ) dbi_conn_set_option( dbhandle, "username", user );
120 if( pw ) dbi_conn_set_option( dbhandle, "password", pw );
121 if( db ) dbi_conn_set_option( dbhandle, "dbname", db );
130 if( dbi_conn_connect( dbhandle ) < 0 ) {
132 if( dbi_conn_connect( dbhandle ) < 0 ) {
133 dbi_conn_error( dbhandle, &err );
134 osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err );
139 oilsSetDBConnection( dbhandle );
140 osrfLogInfo( OSRF_LOG_MARK, "%s successfully connected to the database", modulename );
142 // Add datatypes from database to the fields in the IDL
143 if( oilsExtendIDL() ) {
144 osrfLogError( OSRF_LOG_MARK, "Error extending the IDL" );
151 int doPrepare( osrfMethodContext* ctx ) {
152 if(osrfMethodVerifyContext( ctx )) {
153 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
157 // Get the query id from a method parameter
158 const jsonObject* query_id_obj = jsonObjectGetIndex( ctx->params, 0 );
159 if( query_id_obj->type != JSON_NUMBER ) {
160 osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
161 ctx->request, "Invalid parameter; query id must be a number" );
164 int query_id = atoi( jsonObjectGetString( query_id_obj ));
165 if( query_id <= 0 ) {
166 osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
167 ctx->request, "Invalid parameter: query id must be greater than zero" );
171 osrfLogInfo( OSRF_LOG_MARK, "Building query for id # %d", query_id );
173 osrfAppRespondComplete( ctx, jsonNewObject( "build method not yet implemented" ));
177 int doExecute( osrfMethodContext* ctx ) {
178 if(osrfMethodVerifyContext( ctx )) {
179 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
183 // Get the query token
184 const jsonObject* token_obj = jsonObjectGetIndex( ctx->params, 0 );
185 if( token_obj->type != JSON_STRING ) {
186 osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
187 ctx->request, "Invalid parameter; query id must be a string" );
190 const char* token = jsonObjectGetString( token_obj );
192 // Get the list of bind variables, if there is one
193 jsonObject* bind_map = jsonObjectGetIndex( ctx->params, 1 );
194 if( bind_map && bind_map->type != JSON_HASH ) {
195 osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
196 ctx->request, "Invalid parameter; bind map must be a JSON object" );
200 osrfLogInfo( OSRF_LOG_MARK, "Executing query for token \"%s\"", token );
202 osrfAppRespondComplete( ctx, jsonNewObject( "execute method not yet implemented" ));
206 int doSql( osrfMethodContext* ctx ) {
207 if(osrfMethodVerifyContext( ctx )) {
208 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
212 // Get the query token
213 const jsonObject* token_obj = jsonObjectGetIndex( ctx->params, 0 );
214 if( token_obj->type != JSON_STRING ) {
215 osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
216 ctx->request, "Invalid parameter; query id must be a string" );
219 const char* token = jsonObjectGetString( token_obj );
221 // Get the list of bind variables, if there is one
222 jsonObject* bind_map = jsonObjectGetIndex( ctx->params, 1 );
223 if( bind_map && bind_map->type != JSON_HASH ) {
224 osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
225 ctx->request, "Invalid parameter; bind map must be a JSON object" );
229 osrfLogInfo( OSRF_LOG_MARK, "Returning SQL for token \"%s\"", token );
231 osrfAppRespondComplete( ctx, jsonNewObject( "sql method not yet implemented" ));