]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/c-apps/oils_cstore.c
getting data types for fields (for quoting and such)
[Evergreen.git] / Open-ILS / src / c-apps / oils_cstore.c
1 #include "opensrf/osrf_application.h"
2 #include "opensrf/osrf_settings.h"
3 #include "opensrf/utils.h"
4 #include "objson/object.h"
5 #include "opensrf/log.h"
6 #include "oils_utils.h"
7 #include "oils_constants.h"
8 #include "oils_event.h"
9 #include <dbi/dbi.h>
10
11 #include <string.h>
12 #include <libxml/globals.h>
13 #include <libxml/xmlerror.h>
14 #include <libxml/parser.h>
15 #include <libxml/tree.h>
16 #include <libxml/debugXML.h>
17 #include <libxml/xmlmemory.h>
18 //#include <openils/fieldmapper_lookup.h>
19
20 #define OILS_AUTH_CACHE_PRFX "oils_cstore_"
21 #define MODULENAME "open-ils.cstore"
22 #define PERSIST_NS "http://open-ils.org/spec/opensrf/IDL/persistance/v1"
23 #define OBJECT_NS "http://open-ils.org/spec/opensrf/IDL/objects/v1"
24 #define BASE_NS "http://opensrf.org/spec/IDL/base/v1"
25
26 int osrfAppChildInit();
27 int osrfAppInitialize();
28
29 int dispatchCRUDMethod ( osrfMethodContext* ctx );
30 jsonObject* doCreate ( osrfHash* metadata, jsonObject* params );
31 jsonObject* doRetrieve ( osrfHash* metadata, jsonObject* params );
32 jsonObject* doUpdate ( osrfHash* metadata, jsonObject* params );
33 jsonObject* doDelete ( osrfHash* metadata, jsonObject* params );
34 jsonObject* oilsMakeJSONFromResult( dbi_result result, osrfHash* meta);
35
36 dbi_conn dbhandle; /* our db connection */
37 xmlDocPtr idlDoc = NULL; // parse and store the IDL here
38
39
40 /* parse and store the IDL here */
41 osrfHash* idlHash;
42
43 /* handy NULL json object to have around */
44 static jsonObject* oilsNULL = NULL;
45
46 int osrfAppInitialize() {
47
48         idlHash = osrfNewHash();
49
50         osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
51         osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
52
53         char * idl_filename = osrf_settings_host_value("/apps/%s/app_settings/IDL", MODULENAME);
54         osrfLogInfo(OSRF_LOG_MARK, "Found file:");
55         osrfLogInfo(OSRF_LOG_MARK, idl_filename);
56
57         osrfLogInfo(OSRF_LOG_MARK, "Parsing the IDL XML...");
58         idlDoc = xmlReadFile( idl_filename, NULL, XML_PARSE_XINCLUDE );
59         
60         if (!idlDoc) {
61                 osrfLogError(OSRF_LOG_MARK, "Could not load or parse the IDL XML file!");
62                 exit(1);
63         }
64
65         osrfLogInfo(OSRF_LOG_MARK, "...IDL XML parsed");
66
67         osrfStringArray* global_methods = osrfNewStringArray(1);
68
69         //osrfStringArrayAdd( global_methods, "create" );
70         osrfStringArrayAdd( global_methods, "retrieve" );
71         //osrfStringArrayAdd( global_methods, "update" );
72         //osrfStringArrayAdd( global_methods, "delete" );
73
74         xmlNodePtr docRoot = xmlDocGetRootElement(idlDoc);
75         xmlNodePtr kid = docRoot->children;
76         while (kid) {
77                 if (!strcmp( (char*)kid->name, "class" )) {
78                         int i = 0; 
79                         char* method_type;
80                         while ( (method_type = osrfStringArrayGetString(global_methods, i++)) ) {
81
82                                 osrfHash * usrData = osrfNewHash();
83                                 osrfHashSet( usrData, xmlGetProp(kid, "id"), "classname");
84                                 osrfHashSet( usrData, xmlGetNsProp(kid, "tablename", PERSIST_NS), "tablename");
85                                 osrfHashSet( usrData, xmlGetNsProp(kid, "fieldmapper", OBJECT_NS), "fieldmapper");
86
87                                 if(!strcmp(method_type, "retrieve"))
88                                         osrfHashSet( idlHash, usrData, (char*)osrfHashGet(usrData, "classname") );
89
90                                 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", osrfHashGet(usrData, "fieldmapper") );
91
92                                 osrfHash* _tmp;
93                                 osrfHash* links = osrfNewHash();
94                                 osrfHash* fields = osrfNewHash();
95
96                                 osrfHashSet( usrData, fields, "fields" );
97                                 osrfHashSet( usrData, links, "links" );
98
99                                 xmlNodePtr _cur = kid->children;
100
101                                 while (_cur) {
102                                         char* string_tmp = NULL;
103
104                                         if (!strcmp( (char*)_cur->name, "fields" )) {
105
106                                                 if( (string_tmp = (char*)xmlGetNsProp(_cur, "primary", PERSIST_NS)) ) {
107                                                         osrfHashSet(
108                                                                 usrData,
109                                                                 strdup( string_tmp ),
110                                                                 "primarykey"
111                                                         );
112                                                 }
113                                                 string_tmp = NULL;
114
115                                                 xmlNodePtr _f = _cur->children;
116
117                                                 while(_f) {
118                                                         if (strcmp( (char*)_f->name, "field" )) {
119                                                                 _f = _f->next;
120                                                                 continue;
121                                                         }
122
123                                                         _tmp = osrfNewHash();
124
125                                                         if( (string_tmp = (char*)xmlGetNsProp(_f, "array_position", OBJECT_NS)) ) {
126                                                                 osrfHashSet(
127                                                                         _tmp,
128                                                                         strdup( string_tmp ),
129                                                                         "array_position"
130                                                                 );
131                                                         }
132                                                         string_tmp = NULL;
133
134                                                         if( (string_tmp = (char*)xmlGetNsProp(_f, "virtual", PERSIST_NS)) ) {
135                                                                 osrfHashSet(
136                                                                         _tmp,
137                                                                         strdup( string_tmp ),
138                                                                         "virtual"
139                                                                 );
140                                                         }
141                                                         string_tmp = NULL;
142
143                                                         if( (string_tmp = (char*)xmlGetProp(_f, "name")) ) {
144                                                                 osrfHashSet(
145                                                                         _tmp,
146                                                                         strdup( string_tmp ),
147                                                                         "name"
148                                                                 );
149                                                         }
150
151                                                         osrfLogInfo(OSRF_LOG_MARK, "Found field %s for class %s", string_tmp, osrfHashGet(usrData, "classname") );
152
153                                                         osrfHashSet(
154                                                                 fields,
155                                                                 _tmp,
156                                                                 strdup( string_tmp )
157                                                         );
158                                                         _f = _f->next;
159                                                 }
160                                         }
161
162                                         if (!strcmp( (char*)_cur->name, "links" )) {
163                                                 xmlNodePtr _l = _cur->children;
164
165                                                 while(_l) {
166                                                         if (strcmp( (char*)_l->name, "link" )) {
167                                                                 _l = _l->next;
168                                                                 continue;
169                                                         }
170
171                                                         _tmp = osrfNewHash();
172
173                                                         if( (string_tmp = (char*)xmlGetProp(_l, "reltype")) ) {
174                                                                 osrfHashSet(
175                                                                         _tmp,
176                                                                         strdup( string_tmp ),
177                                                                         "reltype"
178                                                                 );
179                                                         }
180                                                         osrfLogInfo(OSRF_LOG_MARK, "Adding link with reltype %s", string_tmp );
181                                                         string_tmp = NULL;
182
183                                                         if( (string_tmp = (char*)xmlGetProp(_l, "key")) ) {
184                                                                 osrfHashSet(
185                                                                         _tmp,
186                                                                         strdup( string_tmp ),
187                                                                         "key"
188                                                                 );
189                                                         }
190                                                         osrfLogInfo(OSRF_LOG_MARK, "Link fkey is %s", string_tmp );
191                                                         string_tmp = NULL;
192
193                                                         if( (string_tmp = (char*)xmlGetProp(_l, "class")) ) {
194                                                                 osrfHashSet(
195                                                                         _tmp,
196                                                                         strdup( string_tmp ),
197                                                                         "class"
198                                                                 );
199                                                         }
200                                                         osrfLogInfo(OSRF_LOG_MARK, "Link fclass is %s", string_tmp );
201                                                         string_tmp = NULL;
202
203                                                         osrfStringArray* map = osrfNewStringArray(0);
204
205                                                         if( (string_tmp = (char*)xmlGetProp(_l, "map") )) {
206                                                                 char* map_list = strdup( string_tmp );
207                                                                 osrfLogInfo(OSRF_LOG_MARK, "Link mapping list is %s", string_tmp );
208
209                                                                 if (strlen( map_list ) > 0) {
210                                                                         char* st_tmp;
211                                                                         char* _map_class = strtok_r(map_list, " ", &st_tmp);
212                                                                         osrfStringArrayAdd(map, strdup(_map_class));
213                                                         
214                                                                         while ((_map_class = strtok_r(NULL, " ", &st_tmp))) {
215                                                                                 osrfStringArrayAdd(map, strdup(_map_class));
216                                                                         }
217                                                                 }
218                                                         }
219                                                         osrfHashSet( _tmp, map, "map");
220
221                                                         if( (string_tmp = (char*)xmlGetProp(_l, "field")) ) {
222                                                                 osrfHashSet(
223                                                                         _tmp,
224                                                                         strdup( string_tmp ),
225                                                                         "field"
226                                                                 );
227                                                         }
228
229                                                         osrfHashSet(
230                                                                 links,
231                                                                 _tmp,
232                                                                 strdup( string_tmp )
233                                                         );
234
235                                                         osrfLogInfo(OSRF_LOG_MARK, "Found link %s for class %s", string_tmp, osrfHashGet(usrData, "classname") );
236
237                                                         _l = _l->next;
238                                                 }
239                                         }
240
241                                         _cur = _cur->next;
242                                 }
243
244
245                                 char* st_tmp;
246                                 char* _fm;
247                                 if (!osrfHashGet(usrData, "fieldmapper")) continue;
248
249                                 _fm = strdup( (char*)osrfHashGet(usrData, "fieldmapper") );
250                                 char* part = strtok_r(_fm, ":", &st_tmp);
251
252                                 growing_buffer* method_name =  buffer_init(64);
253                                 buffer_fadd(method_name, "%s.direct.%s", MODULENAME, part);
254
255                                 while ((part = strtok_r(NULL, ":", &st_tmp))) {
256                                         buffer_fadd(method_name, ".%s", part);
257                                 }
258                                 buffer_fadd(method_name, ".%s", method_type);
259
260                                 char* method = buffer_data(method_name);
261                                 buffer_free(method_name);
262
263                                 osrfHashSet( usrData, method, "methodname" );
264                                 osrfHashSet( usrData, strdup(method_type), "methodtype" );
265
266                                 osrfAppRegisterExtendedMethod(
267                                         MODULENAME,
268                                         method,
269                                         "dispatchCRUDMethod",
270                                         "",
271                                         1,
272                                         0,
273                                         (void*)usrData
274                                 );
275                         }
276                 }
277                 kid = kid->next;
278         }
279
280         return 0;
281 }
282
283 /**
284  * Connects to the database 
285  */
286 int osrfAppChildInit() {
287
288         osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
289         dbi_initialize(NULL);
290         osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
291
292         char* driver    = osrf_settings_host_value("/apps/%s/app_settings/driver", MODULENAME);
293         char* user      = osrf_settings_host_value("/apps/%s/app_settings/database/user", MODULENAME);
294         char* host      = osrf_settings_host_value("/apps/%s/app_settings/database/host", MODULENAME);
295         char* port      = osrf_settings_host_value("/apps/%s/app_settings/database/port", MODULENAME);
296         char* db        = osrf_settings_host_value("/apps/%s/app_settings/database/db", MODULENAME);
297         char* pw        = osrf_settings_host_value("/apps/%s/app_settings/database/pw", MODULENAME);
298
299         osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
300         dbhandle = dbi_conn_new(driver);
301
302         if(!dbhandle) {
303                 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
304                 return -1;
305         }
306         osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
307
308         osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database.  host=%s, "
309                 "port=%s, user=%s, pw=%s, db=%s", MODULENAME, host, port, user, pw, db );
310
311         if(host) dbi_conn_set_option(dbhandle, "host", host );
312         if(port) dbi_conn_set_option_numeric( dbhandle, "port", atoi(port) );
313         if(user) dbi_conn_set_option(dbhandle, "username", user);
314         if(pw) dbi_conn_set_option(dbhandle, "password", pw );
315         if(db) dbi_conn_set_option(dbhandle, "dbname", db );
316
317         free(user);
318         free(host);
319         free(port);
320         free(db);
321         free(pw);
322
323         const char* err;
324         if (dbi_conn_connect(dbhandle) < 0) {
325                 dbi_conn_error(dbhandle, &err);
326                 osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
327                 return -1;
328         }
329
330         osrfLogInfo(OSRF_LOG_MARK, "%s successfully connected to the database", MODULENAME);
331
332         int attr;
333         unsigned short type;
334         int i = 0; 
335         char* classname;
336         osrfStringArray* classes = osrfHashKeys( idlHash );
337         
338         while ( (classname = osrfStringArrayGetString(classes, i++)) ) {
339                 osrfHash* class = osrfHashGet( idlHash, classname );
340                 osrfHash* fields = osrfHashGet( class, "fields" );
341                 
342                 growing_buffer* sql_buf = buffer_init(32);
343                 buffer_fadd( sql_buf, "SELECT * FROM %s WHERE 1=0;", osrfHashGet(class, "tablename") );
344
345                 char* sql = buffer_data(sql_buf);
346                 buffer_free(sql_buf);
347                 osrfLogDebug(OSRF_LOG_MARK, "%s Investigatory SQL = %s", MODULENAME, sql);
348
349                 dbi_result result = dbi_conn_query(dbhandle, sql);
350                 free(sql);
351
352                 int columnIndex = 1;
353                 const char* columnName;
354                 osrfHash* _f;
355                 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
356
357                         osrfLogDebug(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
358
359                         /* fetch the fieldmapper index */
360                         if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
361
362                                 osrfLogDebug(OSRF_LOG_MARK, "Found [%s] in IDL hash...", (char*)columnName);
363
364                                 /* determine the field type and storage attributes */
365                                 type = dbi_result_get_field_type(result, columnName);
366                                 attr = dbi_result_get_field_attribs(result, columnName);
367
368                                 switch( type ) {
369
370                                         case DBI_TYPE_INTEGER :
371
372                                                 osrfHashSet(_f,"number", "primative");
373
374                                                 if( attr & DBI_INTEGER_SIZE8 ) 
375                                                         osrfHashSet(_f,"INT8", "datatype");
376                                                 else 
377                                                         osrfHashSet(_f,"INT", "datatype");
378                                                 break;
379
380                                         case DBI_TYPE_DECIMAL :
381                                                 osrfHashSet(_f,"number", "primative");
382                                                 osrfHashSet(_f,"NUMERIC", "datatype");
383                                                 break;
384
385                                         case DBI_TYPE_STRING :
386                                                 osrfHashSet(_f,"string", "primative");
387                                                 osrfHashSet(_f,"TEXT", "datatype");
388                                                 break;
389
390                                         case DBI_TYPE_DATETIME :
391                                                 osrfHashSet(_f,"string", "primative");
392                                                 osrfHashSet(_f,"TIMESTAMP", "datatype");
393                                                 break;
394
395                                         case DBI_TYPE_BINARY :
396                                                 osrfHashSet(_f,"string", "primative");
397                                                 osrfHashSet(_f,"BYTEA", "datatype");
398                                 }
399
400                                 osrfLogDebug(
401                                         OSRF_LOG_MARK,
402                                         "Setting [%s] to primitive [%s] and datatype [%s]...",
403                                         (char*)columnName,
404                                         osrfHashGet(_f, "primitive"),
405                                         osrfHashGet(_f, "datatype")
406                                 );
407                         }
408                 }
409
410                 dbi_result_free(result);
411         }
412
413         osrfStringArrayFree(classes);
414
415         return 0;
416 }
417
418
419 int dispatchCRUDMethod ( osrfMethodContext* ctx ) {
420         OSRF_METHOD_VERIFY_CONTEXT(ctx);
421
422         osrfHash* meta = (osrfHash*) ctx->method->userData;
423
424         jsonObject * obj = NULL;
425         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "create"))
426                 obj = doCreate(meta, ctx->params);
427
428         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "retrieve"))
429                 obj = doRetrieve(meta, ctx->params);
430
431         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "update"))
432                 obj = doUpdate(meta, ctx->params);
433
434         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "delete"))
435                 obj = doDelete(meta, ctx->params);
436
437         osrfAppRespondComplete( ctx, obj );
438
439         jsonObjectFree(obj);
440
441         return 0;
442 }
443
444 jsonObject* doCreate( osrfHash* meta, jsonObject* params ) { return NULL; }
445
446 jsonObject* doRetrieve( osrfHash* meta, jsonObject* params ) {
447
448         char* id        = jsonObjectToSimpleString(jsonObjectGetIndex(params, 0));
449
450         osrfLogDebug(
451                 OSRF_LOG_MARK,
452                 "%s retrieving %s object with id %s",
453                 MODULENAME,
454                 osrfHashGet(meta, "fieldmapper"),
455                 id
456         );
457
458
459
460         growing_buffer* sql_buf = buffer_init(128);
461         buffer_fadd(
462                 sql_buf,
463                 "SELECT * FROM %s WHERE %s = %d;",
464                 osrfHashGet(meta, "tablename"),
465                 osrfHashGet(meta, "primarykey"),
466                 atoi(id)
467         );
468
469         char* sql = buffer_data(sql_buf);
470         buffer_free(sql_buf);
471         
472         osrfLogDebug(OSRF_LOG_MARK, "%s SQL =  %s", MODULENAME, sql);
473
474         dbi_result result = dbi_conn_query(dbhandle, sql);
475
476         jsonObject* obj = NULL;
477         if(result) {
478
479                 /* there should be one row at the most  */
480                 if (!dbi_result_first_row(result)) {
481                         osrfLogDebug(OSRF_LOG_MARK, "%s: Error retrieving %s with key %s", MODULENAME, osrfHashGet(meta, "fieldmapper"), id);
482                         dbi_result_free(result); 
483                         return oilsNULL;
484                 }
485
486                 /* JSONify the result */
487                 obj = oilsMakeJSONFromResult( result, meta );
488
489                 /* clean up the query */
490                 dbi_result_free(result); 
491
492         } else {
493                 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
494         }
495
496         free(id);
497
498         return obj;
499 }
500
501
502 jsonObject* doUpdate( osrfHash* meta, jsonObject* params ) { return NULL; }
503
504 jsonObject* doDelete( osrfHash* meta, jsonObject* params ) { return NULL; }
505
506
507 jsonObject* oilsMakeJSONFromResult( dbi_result result, osrfHash* meta) {
508         if(!(result && meta)) return NULL;
509
510         jsonObject* object = jsonParseString("[]");
511         jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
512
513         osrfHash* fields = osrfHashGet(meta, "fields");
514
515         osrfLogDebug(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
516
517         osrfHash* _f;
518         int fmIndex;
519         int columnIndex = 1;
520         int attr;
521         unsigned short type;
522         const char* columnName;
523
524         /* cycle through the column list */
525         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
526
527                 osrfLogDebug(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
528
529                 fmIndex = -1; // reset the position
530                 
531                 /* determine the field type and storage attributes */
532                 type = dbi_result_get_field_type(result, columnName);
533                 attr = dbi_result_get_field_attribs(result, columnName);
534
535                 /* fetch the fieldmapper index */
536                 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
537                         char* virt = (char*)osrfHashGet(_f, "virtual");
538                         char* pos = (char*)osrfHashGet(_f, "array_position");
539
540                         if ( !virt || !pos || !(strcmp( virt, "true" )) ) continue;
541
542                         fmIndex = atoi( pos );
543                         osrfLogDebug(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
544                 }
545
546                 switch( type ) {
547
548                         case DBI_TYPE_INTEGER :
549
550                                 if( attr & DBI_INTEGER_SIZE8 ) 
551                                         jsonObjectSetIndex( object, fmIndex, 
552                                                 jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
553                                 else 
554                                         jsonObjectSetIndex( object, fmIndex, 
555                                                 jsonNewNumberObject(dbi_result_get_long(result, columnName)));
556
557                                 break;
558
559                         case DBI_TYPE_DECIMAL :
560                                 jsonObjectSetIndex( object, fmIndex, 
561                                                 jsonNewNumberObject(dbi_result_get_double(result, columnName)));
562                                 break;
563
564                         case DBI_TYPE_STRING :
565                                 jsonObjectSetIndex( object, fmIndex, 
566                                         jsonNewObject(dbi_result_get_string(result, columnName)));
567                                 break;
568
569                         case DBI_TYPE_DATETIME :
570                                 jsonObjectSetIndex( object, fmIndex, 
571                                         jsonNewNumberObject(dbi_result_get_datetime(result, columnName)));
572                                 break;
573
574                         case DBI_TYPE_BINARY :
575                                 osrfLogError( OSRF_LOG_MARK, 
576                                         "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
577                 }
578         }
579
580         return object;
581 }
582
583
584
585