]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/c-apps/oils_qstore.c
Add a new service, open-ils.qstore. It is not yet useful except for testing
[working/Evergreen.git] / Open-ILS / src / c-apps / oils_qstore.c
1 /**
2         @file oils_qstore.c
3         @brief As a server, perform database queries as defined in the database itself.
4 */
5
6 #include <stdlib.h>
7 #include <string.h>
8 #include <ctype.h>
9 #include <dbi/dbi.h>
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"
16
17 static dbi_conn dbhandle; /* our db connection */
18
19 static const char modulename[] = "open-ils.qstore";
20
21 int doPrepare( osrfMethodContext* ctx );
22 int doExecute( osrfMethodContext* ctx );
23 int doSql( osrfMethodContext* ctx );
24
25 /**
26         @brief Disconnect from the database.
27
28         This function is called when the server drone is about to terminate.
29 */
30 void osrfAppChildExit() {
31         osrfLogDebug( OSRF_LOG_MARK, "Child is exiting, disconnecting from database..." );
32
33         if ( dbhandle ) {
34                 dbi_conn_query( dbhandle, "ROLLBACK;" );
35                 dbi_conn_close( dbhandle );
36                 dbhandle = NULL;
37         }
38 }
39
40 /**
41         @brief Initialize the application.
42         @return Zero if successful, or non-zero if not.
43
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.
47
48         Register the functions for remote procedure calls.
49
50         This function is called when the registering the application, and is executed by the
51         listener before spawning the drones.
52 */
53 int osrfAppInitialize() {
54
55         osrfLogInfo( OSRF_LOG_MARK, "Initializing the QStore Server..." );
56         osrfLogInfo( OSRF_LOG_MARK, "Finding XML file..." );
57
58         if ( !oilsIDLInit( osrf_settings_host_value( "/IDL" )))
59                 return 1; /* return non-zero to indicate error */
60
61         growing_buffer* method_name = buffer_init( 64 );
62
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 );
67
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 );
73
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 );
79
80         return 0;
81 }
82
83 /**
84         @brief Initialize a server drone.
85         @return Zero if successful, -1 if not.
86
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.
89
90         This function is called by a server drone shortly after it is spawned by the listener.
91 */
92 int osrfAppChildInit() {
93
94         osrfLogDebug( OSRF_LOG_MARK, "Attempting to initialize libdbi..." );
95         dbi_initialize( NULL );
96         osrfLogDebug( OSRF_LOG_MARK, "... libdbi initialized." );
97
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 );
104
105         osrfLogDebug( OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver );
106         dbhandle = dbi_conn_new( driver );
107
108         if( !dbhandle ) {
109                 osrfLogError( OSRF_LOG_MARK, "Error loading database driver [%s]", driver );
110                 return -1;
111         }
112         osrfLogDebug( OSRF_LOG_MARK, "Database driver [%s] seems OK", driver );
113
114         osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database.  host=%s, "
115                         "port=%s, user=%s, db=%s", modulename, host, port, user, db );
116
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 );
122
123         free( user );
124         free( host );
125         free( port );
126         free( db );
127         free( pw );
128
129         const char* err;
130         if( dbi_conn_connect( dbhandle ) < 0 ) {
131                 sleep( 1 );
132                 if( dbi_conn_connect( dbhandle ) < 0 ) {
133                         dbi_conn_error( dbhandle, &err );
134                         osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err );
135                         return -1;
136                 }
137         }
138
139         oilsSetDBConnection( dbhandle );
140         osrfLogInfo( OSRF_LOG_MARK, "%s successfully connected to the database", modulename );
141
142         // Add datatypes from database to the fields in the IDL
143         if( oilsExtendIDL() ) {
144                 osrfLogError( OSRF_LOG_MARK, "Error extending the IDL" );
145                 return -1;
146         }
147         else
148                 return 0;
149 }
150
151 int doPrepare( osrfMethodContext* ctx ) {
152         if(osrfMethodVerifyContext( ctx )) {
153                 osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
154                 return -1;
155         }
156
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" );
162                 return -1;
163         }
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" );
168                 return -1;
169         }
170
171         osrfLogInfo( OSRF_LOG_MARK, "Building query for id # %d", query_id );
172
173         osrfAppRespondComplete( ctx, jsonNewObject( "build method not yet implemented" ));
174         return 0;
175 }
176
177 int doExecute( osrfMethodContext* ctx ) {
178         if(osrfMethodVerifyContext( ctx )) {
179                 osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
180                 return -1;
181         }
182
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" );
188                 return -1;
189         }
190         const char* token = jsonObjectGetString( token_obj );
191
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" );
197                 return -1;
198         }
199
200         osrfLogInfo( OSRF_LOG_MARK, "Executing query for token \"%s\"", token );
201
202         osrfAppRespondComplete( ctx, jsonNewObject( "execute method not yet implemented" ));
203         return 0;
204 }
205
206 int doSql( osrfMethodContext* ctx ) {
207         if(osrfMethodVerifyContext( ctx )) {
208                 osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
209                 return -1;
210         }
211
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" );
217                 return -1;
218         }
219         const char* token = jsonObjectGetString( token_obj );
220
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" );
226                 return -1;
227         }
228
229         osrfLogInfo( OSRF_LOG_MARK, "Returning SQL for token \"%s\"", token );
230
231         osrfAppRespondComplete( ctx, jsonNewObject( "sql method not yet implemented" ));
232         return 0;
233 }