]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/c-apps/oils_cstore.c
added transactions methods, not yet registering them
[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 <time.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <libxml/globals.h>
15 #include <libxml/xmlerror.h>
16 #include <libxml/parser.h>
17 #include <libxml/tree.h>
18 #include <libxml/debugXML.h>
19 #include <libxml/xmlmemory.h>
20
21 #define OILS_AUTH_CACHE_PRFX "oils_cstore_"
22 #define MODULENAME "open-ils.cstore"
23 #define PERSIST_NS "http://open-ils.org/spec/opensrf/IDL/persistance/v1"
24 #define OBJECT_NS "http://open-ils.org/spec/opensrf/IDL/objects/v1"
25 #define BASE_NS "http://opensrf.org/spec/IDL/base/v1"
26
27 int osrfAppChildInit();
28 int osrfAppInitialize();
29
30 int beginTransaction ( osrfMethodContext* );
31 int commitTransaction ( osrfMethodContext* );
32 int rollbackTransaction ( osrfMethodContext* );
33
34 int dispatchCRUDMethod ( osrfMethodContext* );
35 jsonObject* doCreate ( osrfHash*, jsonObject* );
36 jsonObject* doRetrieve ( osrfHash*, jsonObject* );
37 jsonObject* doUpdate ( osrfHash*, jsonObject* );
38 jsonObject* doDelete ( osrfHash*, jsonObject* );
39 jsonObject* doSearch ( osrfHash*, jsonObject* );
40 jsonObject* oilsMakeJSONFromResult( dbi_result, osrfHash* );
41
42
43 void userDataFree( void* );
44
45 dbi_conn dbhandle; /* our db connection */
46 xmlDocPtr idlDoc = NULL; // parse and store the IDL here
47
48
49 /* parse and store the IDL here */
50 osrfHash* idlHash;
51
52 int osrfAppInitialize() {
53
54         idlHash = osrfNewHash();
55         osrfHash* usrData = NULL;
56
57         osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
58         osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
59
60         char * idl_filename = osrf_settings_host_value("/apps/%s/app_settings/IDL", MODULENAME);
61         osrfLogInfo(OSRF_LOG_MARK, "Found file:");
62         osrfLogInfo(OSRF_LOG_MARK, idl_filename);
63
64         osrfLogInfo(OSRF_LOG_MARK, "Parsing the IDL XML...");
65         idlDoc = xmlReadFile( idl_filename, NULL, XML_PARSE_XINCLUDE );
66         
67         if (!idlDoc) {
68                 osrfLogError(OSRF_LOG_MARK, "Could not load or parse the IDL XML file!");
69                 exit(1);
70         }
71
72         osrfLogInfo(OSRF_LOG_MARK, "...IDL XML parsed");
73
74         osrfStringArray* global_methods = osrfNewStringArray(1);
75
76         //osrfStringArrayAdd( global_methods, "create" );
77         osrfStringArrayAdd( global_methods, "retrieve" );
78         //osrfStringArrayAdd( global_methods, "update" );
79         //osrfStringArrayAdd( global_methods, "delete" );
80         osrfStringArrayAdd( global_methods, "search" );
81
82         xmlNodePtr docRoot = xmlDocGetRootElement(idlDoc);
83         xmlNodePtr kid = docRoot->children;
84         while (kid) {
85                 if (!strcmp( (char*)kid->name, "class" )) {
86
87                         usrData = osrfNewHash();
88                         osrfHashSet( usrData, xmlGetProp(kid, "id"), "classname");
89                         osrfHashSet( usrData, xmlGetNsProp(kid, "tablename", PERSIST_NS), "tablename");
90                         osrfHashSet( usrData, xmlGetNsProp(kid, "fieldmapper", OBJECT_NS), "fieldmapper");
91
92                         osrfHashSet( idlHash, usrData, (char*)osrfHashGet(usrData, "classname") );
93
94                         osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", osrfHashGet(usrData, "fieldmapper") );
95
96                         osrfHash* _tmp;
97                         osrfHash* links = osrfNewHash();
98                         osrfHash* fields = osrfNewHash();
99
100                         osrfHashSet( usrData, fields, "fields" );
101                         osrfHashSet( usrData, links, "links" );
102
103                         xmlNodePtr _cur = kid->children;
104
105                         while (_cur) {
106                                 char* string_tmp = NULL;
107
108                                 if (!strcmp( (char*)_cur->name, "fields" )) {
109
110                                         if( (string_tmp = (char*)xmlGetNsProp(_cur, "primary", PERSIST_NS)) ) {
111                                                 osrfHashSet(
112                                                         usrData,
113                                                         strdup( string_tmp ),
114                                                         "primarykey"
115                                                 );
116                                         }
117                                         string_tmp = NULL;
118
119                                         xmlNodePtr _f = _cur->children;
120
121                                         while(_f) {
122                                                 if (strcmp( (char*)_f->name, "field" )) {
123                                                         _f = _f->next;
124                                                         continue;
125                                                 }
126
127                                                 _tmp = osrfNewHash();
128
129                                                 if( (string_tmp = (char*)xmlGetNsProp(_f, "array_position", OBJECT_NS)) ) {
130                                                         osrfHashSet(
131                                                                 _tmp,
132                                                                 strdup( string_tmp ),
133                                                                 "array_position"
134                                                         );
135                                                 }
136                                                 string_tmp = NULL;
137
138                                                 if( (string_tmp = (char*)xmlGetNsProp(_f, "virtual", PERSIST_NS)) ) {
139                                                         osrfHashSet(
140                                                                 _tmp,
141                                                                 strdup( string_tmp ),
142                                                                 "virtual"
143                                                         );
144                                                 }
145                                                 string_tmp = NULL;
146
147                                                 if( (string_tmp = (char*)xmlGetProp(_f, "name")) ) {
148                                                         osrfHashSet(
149                                                                 _tmp,
150                                                                 strdup( string_tmp ),
151                                                                 "name"
152                                                         );
153                                                 }
154
155                                                 osrfLogInfo(OSRF_LOG_MARK, "Found field %s for class %s", string_tmp, osrfHashGet(usrData, "classname") );
156
157                                                 osrfHashSet(
158                                                         fields,
159                                                         _tmp,
160                                                         strdup( string_tmp )
161                                                 );
162                                                 _f = _f->next;
163                                         }
164                                 }
165
166                                 if (!strcmp( (char*)_cur->name, "links" )) {
167                                         xmlNodePtr _l = _cur->children;
168
169                                         while(_l) {
170                                                 if (strcmp( (char*)_l->name, "link" )) {
171                                                         _l = _l->next;
172                                                         continue;
173                                                 }
174
175                                                 _tmp = osrfNewHash();
176
177                                                 if( (string_tmp = (char*)xmlGetProp(_l, "reltype")) ) {
178                                                         osrfHashSet(
179                                                                 _tmp,
180                                                                 strdup( string_tmp ),
181                                                                 "reltype"
182                                                         );
183                                                 }
184                                                 osrfLogInfo(OSRF_LOG_MARK, "Adding link with reltype %s", string_tmp );
185                                                 string_tmp = NULL;
186
187                                                 if( (string_tmp = (char*)xmlGetProp(_l, "key")) ) {
188                                                         osrfHashSet(
189                                                                 _tmp,
190                                                                 strdup( string_tmp ),
191                                                                 "key"
192                                                         );
193                                                 }
194                                                 osrfLogInfo(OSRF_LOG_MARK, "Link fkey is %s", string_tmp );
195                                                 string_tmp = NULL;
196
197                                                 if( (string_tmp = (char*)xmlGetProp(_l, "class")) ) {
198                                                         osrfHashSet(
199                                                                 _tmp,
200                                                                 strdup( string_tmp ),
201                                                                 "class"
202                                                         );
203                                                 }
204                                                 osrfLogInfo(OSRF_LOG_MARK, "Link fclass is %s", string_tmp );
205                                                 string_tmp = NULL;
206
207                                                 osrfStringArray* map = osrfNewStringArray(0);
208
209                                                 if( (string_tmp = (char*)xmlGetProp(_l, "map") )) {
210                                                         char* map_list = strdup( string_tmp );
211                                                         osrfLogInfo(OSRF_LOG_MARK, "Link mapping list is %s", string_tmp );
212
213                                                         if (strlen( map_list ) > 0) {
214                                                                 char* st_tmp;
215                                                                 char* _map_class = strtok_r(map_list, " ", &st_tmp);
216                                                                 osrfStringArrayAdd(map, strdup(_map_class));
217                                                 
218                                                                 while ((_map_class = strtok_r(NULL, " ", &st_tmp))) {
219                                                                         osrfStringArrayAdd(map, strdup(_map_class));
220                                                                 }
221                                                         }
222                                                 }
223                                                 osrfHashSet( _tmp, map, "map");
224
225                                                 if( (string_tmp = (char*)xmlGetProp(_l, "field")) ) {
226                                                         osrfHashSet(
227                                                                 _tmp,
228                                                                 strdup( string_tmp ),
229                                                                 "field"
230                                                         );
231                                                 }
232
233                                                 osrfHashSet(
234                                                         links,
235                                                         _tmp,
236                                                         strdup( string_tmp )
237                                                 );
238
239                                                 osrfLogInfo(OSRF_LOG_MARK, "Found link %s for class %s", string_tmp, osrfHashGet(usrData, "classname") );
240
241                                                 _l = _l->next;
242                                         }
243                                 }
244
245                                 _cur = _cur->next;
246                         }
247
248                         int i = 0; 
249                         char* method_type;
250                         char* st_tmp;
251                         char* _fm;
252                         char* part;
253                         osrfHash* method_meta;
254                         while ( (method_type = osrfStringArrayGetString(global_methods, i++)) ) {
255
256                                 if (!osrfHashGet(usrData, "fieldmapper")) continue;
257
258                                 method_meta = osrfNewHash();
259                                 osrfHashSet(method_meta, usrData, "class");
260
261                                 _fm = strdup( (char*)osrfHashGet(usrData, "fieldmapper") );
262                                 part = strtok_r(_fm, ":", &st_tmp);
263
264                                 growing_buffer* method_name =  buffer_init(64);
265                                 buffer_fadd(method_name, "%s.direct.%s", MODULENAME, part);
266
267                                 while ((part = strtok_r(NULL, ":", &st_tmp))) {
268                                         buffer_fadd(method_name, ".%s", part);
269                                 }
270                                 buffer_fadd(method_name, ".%s", method_type);
271
272
273                                 char* method = buffer_data(method_name);
274                                 buffer_free(method_name);
275                                 free(_fm);
276
277                                 osrfHashSet( method_meta, method, "methodname" );
278                                 osrfHashSet( method_meta, method_type, "methodtype" );
279
280                                 int flags = 0;
281                                 if (!(strcmp( method_type, "search" ))) {
282                                         flags = flags | OSRF_METHOD_STREAMING;
283                                 }
284
285                                 osrfAppRegisterExtendedMethod(
286                                         MODULENAME,
287                                         method,
288                                         "dispatchCRUDMethod",
289                                         "",
290                                         1,
291                                         flags,
292                                         (void*)method_meta
293                                 );
294                         }
295                 }
296                 kid = kid->next;
297         }
298
299         return 0;
300 }
301
302 /**
303  * Connects to the database 
304  */
305 int osrfAppChildInit() {
306
307         osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
308         dbi_initialize(NULL);
309         osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
310
311         char* driver    = osrf_settings_host_value("/apps/%s/app_settings/driver", MODULENAME);
312         char* user      = osrf_settings_host_value("/apps/%s/app_settings/database/user", MODULENAME);
313         char* host      = osrf_settings_host_value("/apps/%s/app_settings/database/host", MODULENAME);
314         char* port      = osrf_settings_host_value("/apps/%s/app_settings/database/port", MODULENAME);
315         char* db        = osrf_settings_host_value("/apps/%s/app_settings/database/db", MODULENAME);
316         char* pw        = osrf_settings_host_value("/apps/%s/app_settings/database/pw", MODULENAME);
317
318         osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
319         dbhandle = dbi_conn_new(driver);
320
321         if(!dbhandle) {
322                 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
323                 return -1;
324         }
325         osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
326
327         osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database.  host=%s, "
328                 "port=%s, user=%s, pw=%s, db=%s", MODULENAME, host, port, user, pw, db );
329
330         if(host) dbi_conn_set_option(dbhandle, "host", host );
331         if(port) dbi_conn_set_option_numeric( dbhandle, "port", atoi(port) );
332         if(user) dbi_conn_set_option(dbhandle, "username", user);
333         if(pw) dbi_conn_set_option(dbhandle, "password", pw );
334         if(db) dbi_conn_set_option(dbhandle, "dbname", db );
335
336         free(user);
337         free(host);
338         free(port);
339         free(db);
340         free(pw);
341
342         const char* err;
343         if (dbi_conn_connect(dbhandle) < 0) {
344                 dbi_conn_error(dbhandle, &err);
345                 osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
346                 return -1;
347         }
348
349         osrfLogInfo(OSRF_LOG_MARK, "%s successfully connected to the database", MODULENAME);
350
351         int attr;
352         unsigned short type;
353         int i = 0; 
354         char* classname;
355         osrfStringArray* classes = osrfHashKeys( idlHash );
356         
357         while ( (classname = osrfStringArrayGetString(classes, i++)) ) {
358                 osrfHash* class = osrfHashGet( idlHash, classname );
359                 osrfHash* fields = osrfHashGet( class, "fields" );
360                 
361                 growing_buffer* sql_buf = buffer_init(32);
362                 buffer_fadd( sql_buf, "SELECT * FROM %s WHERE 1=0;", osrfHashGet(class, "tablename") );
363
364                 char* sql = buffer_data(sql_buf);
365                 buffer_free(sql_buf);
366                 osrfLogDebug(OSRF_LOG_MARK, "%s Investigatory SQL = %s", MODULENAME, sql);
367
368                 dbi_result result = dbi_conn_query(dbhandle, sql);
369                 free(sql);
370
371                 if (result) {
372
373                         int columnIndex = 1;
374                         const char* columnName;
375                         osrfHash* _f;
376                         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
377
378                                 osrfLogDebug(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
379
380                                 /* fetch the fieldmapper index */
381                                 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
382
383                                         osrfLogDebug(OSRF_LOG_MARK, "Found [%s] in IDL hash...", (char*)columnName);
384
385                                         /* determine the field type and storage attributes */
386                                         type = dbi_result_get_field_type(result, columnName);
387                                         attr = dbi_result_get_field_attribs(result, columnName);
388
389                                         switch( type ) {
390
391                                                 case DBI_TYPE_INTEGER :
392
393                                                         osrfHashSet(_f,"number", "primitive");
394
395                                                         if( attr & DBI_INTEGER_SIZE8 ) 
396                                                                 osrfHashSet(_f,"INT8", "datatype");
397                                                         else 
398                                                                 osrfHashSet(_f,"INT", "datatype");
399                                                         break;
400
401                                                 case DBI_TYPE_DECIMAL :
402                                                         osrfHashSet(_f,"number", "primitive");
403                                                         osrfHashSet(_f,"NUMERIC", "datatype");
404                                                         break;
405
406                                                 case DBI_TYPE_STRING :
407                                                         osrfHashSet(_f,"string", "primitive");
408                                                         osrfHashSet(_f,"TEXT", "datatype");
409                                                         break;
410
411                                                 case DBI_TYPE_DATETIME :
412                                                         osrfHashSet(_f,"string", "primitive");
413                                                         osrfHashSet(_f,"TIMESTAMP", "datatype");
414                                                         break;
415
416                                                 case DBI_TYPE_BINARY :
417                                                         osrfHashSet(_f,"string", "primitive");
418                                                         osrfHashSet(_f,"BYTEA", "datatype");
419                                         }
420
421                                         osrfLogDebug(
422                                                 OSRF_LOG_MARK,
423                                                 "Setting [%s] to primitive [%s] and datatype [%s]...",
424                                                 (char*)columnName,
425                                                 osrfHashGet(_f, "primitive"),
426                                                 osrfHashGet(_f, "datatype")
427                                         );
428                                 }
429                         }
430                         dbi_result_free(result);
431                 } else {
432                         osrfLogDebug(OSRF_LOG_MARK, "No data found for class [%s]...", (char*)classname);
433                 }
434         }
435
436         osrfStringArrayFree(classes);
437
438         return 0;
439 }
440
441 void userDataFree( void* blob ) {
442         osrfHashFree( (osrfHash*)blob );
443         return;
444 }
445
446 int beginTransaction ( osrfMethodContext* ctx ) {
447         OSRF_METHOD_VERIFY_CONTEXT(ctx);
448
449         dbi_result result = dbi_conn_query(dbhandle, "START TRANSACTION;");
450         if (!result) {
451                 osrfLogDebug(OSRF_LOG_MARK, "%s: Error starting transaction", MODULENAME );
452                 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: Error starting transaction", MODULENAME );
453                 return 1;
454         } else {
455                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
456                 osrfAppRespondComplete( ctx, ret );
457                 jsonObjectFree(ret);
458                 
459                 if (!ctx->session->userData) ctx->session->userData = osrfNewHash();
460
461                 osrfHashSet( ctx->session->userData, strdup( ctx->session->session_id ), "xact_id" );
462                 ctx->session->userDataFree = &userDataFree;
463                 
464         }
465         return 0;
466 }
467
468 int commitTransaction ( osrfMethodContext* ctx ) {
469         OSRF_METHOD_VERIFY_CONTEXT(ctx);
470
471         dbi_result result = dbi_conn_query(dbhandle, "COMMIT;");
472         if (!result) {
473                 osrfLogDebug(OSRF_LOG_MARK, "%s: Error committing transaction", MODULENAME );
474                 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: Error committing transaction", MODULENAME );
475                 return 1;
476         } else {
477                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
478                 osrfAppRespondComplete( ctx, ret );
479                 jsonObjectFree(ret);
480         }
481         return 0;
482 }
483
484 int rollbackTransaction ( osrfMethodContext* ctx ) {
485         OSRF_METHOD_VERIFY_CONTEXT(ctx);
486
487         dbi_result result = dbi_conn_query(dbhandle, "ROLLBACK;");
488         if (!result) {
489                 osrfLogDebug(OSRF_LOG_MARK, "%s: Error rolling back transaction", MODULENAME );
490                 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: Error rolling back transaction", MODULENAME );
491                 return 1;
492         } else {
493                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
494                 osrfAppRespondComplete( ctx, ret );
495                 jsonObjectFree(ret);
496         }
497         return 0;
498 }
499
500 int dispatchCRUDMethod ( osrfMethodContext* ctx ) {
501         OSRF_METHOD_VERIFY_CONTEXT(ctx);
502
503         osrfHash* meta = (osrfHash*) ctx->method->userData;
504         osrfHash* class_obj = osrfHashGet( meta, "class" );
505
506         jsonObject * obj = NULL;
507         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "create"))
508                 obj = doCreate(class_obj, ctx->params);
509
510         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "retrieve"))
511                 obj = doRetrieve(class_obj, ctx->params);
512
513         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "update"))
514                 obj = doUpdate(class_obj, ctx->params);
515
516         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "delete"))
517                 obj = doDelete(class_obj, ctx->params);
518
519         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "search")) {
520                 jsonObjectNode* cur;
521                 obj = doSearch(class_obj, ctx->params);
522                 jsonObjectIterator* itr = jsonNewObjectIterator( obj );
523                 while ((cur = jsonObjectIteratorNext( itr ))) {
524                         osrfAppRespond( ctx, jsonObjectClone(cur->item) );
525                 }
526                 jsonObjectIteratorFree(itr);
527                 osrfAppRespondComplete( ctx, NULL );
528                 
529         } else {
530                 osrfAppRespondComplete( ctx, obj );
531         }
532
533         jsonObjectFree(obj);
534
535         return 0;
536 }
537
538 jsonObject* doCreate( osrfHash* meta, jsonObject* params ) { return NULL; }
539
540 jsonObject* doRetrieve( osrfHash* meta, jsonObject* params ) {
541
542         jsonObject* obj;
543
544         char* id = jsonObjectToSimpleString(jsonObjectGetIndex(params, 0));
545         jsonObject* order_hash = jsonObjectGetIndex(params, 1);
546
547         osrfLogDebug(
548                 OSRF_LOG_MARK,
549                 "%s retrieving %s object with id %s",
550                 MODULENAME,
551                 osrfHashGet(meta, "fieldmapper"),
552                 id
553         );
554
555         jsonObject* fake_params = jsonParseString("[]");
556         jsonObjectPush(fake_params, jsonParseString("{}"));
557
558         jsonObjectSetKey(
559                 jsonObjectGetIndex(fake_params, 0),
560                 osrfHashGet(meta, "primarykey"),
561                 jsonNewObject(id)
562         );
563
564         if (order_hash) jsonObjectPush(fake_params, jsonObjectClone(order_hash) );
565
566         jsonObject* list = doSearch(meta, fake_params);
567         obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
568
569         jsonObjectFree( list );
570         jsonObjectFree( fake_params );
571
572         return obj;
573 }
574
575 jsonObject* doSearch( osrfHash* meta, jsonObject* params ) {
576
577         jsonObject* _tmp;
578         jsonObject* obj;
579         jsonObject* search_hash = jsonObjectGetIndex(params, 0);
580         jsonObject* order_hash = jsonObjectGetIndex(params, 1);
581
582         growing_buffer* sql_buf = buffer_init(128);
583         buffer_fadd(sql_buf, "SELECT * FROM %s WHERE ", osrfHashGet(meta, "tablename") );
584
585         jsonObjectNode* node = NULL;
586         jsonObjectIterator* search_itr = jsonNewObjectIterator( search_hash );
587
588         int first = 1;
589         while ( (node = jsonObjectIteratorNext( search_itr )) ) {
590                 osrfHash* field = osrfHashGet( osrfHashGet(meta, "fields"), node->key );
591
592                 if (!field) continue;
593
594                 if (first) {
595                         first = 0;
596                 } else {
597                         buffer_add(sql_buf, " AND ");
598                 }
599
600                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
601                         buffer_fadd(
602                                 sql_buf,
603                                 "%s = %d",
604                                 osrfHashGet(field, "name"),
605                                 atoi( jsonObjectToSimpleString(node->item) )
606                         );
607                 } else {
608                         char* key_string = jsonObjectToSimpleString(node->item);
609                         if ( dbi_conn_quote_string(dbhandle, &key_string) ) {
610                                 buffer_fadd(
611                                         sql_buf,
612                                         "%s = %s",
613                                         osrfHashGet(field, "name"),
614                                         key_string
615                                 );
616                                 free(key_string);
617                         } else {
618                                 osrfLogDebug(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string);
619                                 free(key_string);
620                         }
621                 }
622         }
623
624         jsonObjectIteratorFree(search_itr);
625
626         if (order_hash) {
627                 char* string;
628                 _tmp = jsonObjectGetKey( order_hash, "order_by" );
629                 if (_tmp) {
630                         string = jsonObjectToSimpleString(_tmp);
631                         buffer_fadd(
632                                 sql_buf,
633                                 " ORDER BY %s",
634                                 string
635                         );
636                         free(string);
637                 }
638
639                 _tmp = jsonObjectGetKey( order_hash, "limit" );
640                 if (_tmp) {
641                         string = jsonObjectToSimpleString(_tmp);
642                         buffer_fadd(
643                                 sql_buf,
644                                 " LIMIT %d",
645                                 atoi(string)
646                         );
647                         free(string);
648                 }
649
650                 _tmp = jsonObjectGetKey( order_hash, "offset" );
651                 if (_tmp) {
652                         string = jsonObjectToSimpleString(_tmp);
653                         buffer_fadd(
654                                 sql_buf,
655                                 " OFFSET %d",
656                                 atoi(string)
657                         );
658                         free(string);
659                 }
660         }
661
662         buffer_add(sql_buf, ";");
663
664         char* sql = buffer_data(sql_buf);
665         buffer_free(sql_buf);
666         
667         osrfLogDebug(OSRF_LOG_MARK, "%s SQL =  %s", MODULENAME, sql);
668         dbi_result result = dbi_conn_query(dbhandle, sql);
669
670         jsonObject* res_list = jsonParseString("[]");
671         if(result) {
672                 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
673
674                 /* there should be one row at the most  */
675                 if (dbi_result_first_row(result)) {
676                         /* JSONify the result */
677                         osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
678                         do {
679                                 obj = oilsMakeJSONFromResult( result, meta );
680                                 jsonObjectPush(res_list, obj);
681                         } while (dbi_result_next_row(result));
682                 } else {
683                         osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
684                 }
685
686                 /* clean up the query */
687                 dbi_result_free(result); 
688
689         } else {
690                 osrfLogDebug(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]", MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
691         }
692
693         free(sql);
694
695         if (order_hash) {
696                 _tmp = jsonObjectGetKey( order_hash, "flesh" );
697                 if (_tmp) {
698                         double x = jsonObjectGetNumber(_tmp);
699
700                         if ((int)x > 0) {
701
702                                 jsonObjectNode* cur;
703                                 jsonObjectIterator* itr = jsonNewObjectIterator( res_list );
704                                 while ((cur = jsonObjectIteratorNext( itr ))) {
705
706                                         osrfHash* links = osrfHashGet(meta, "links");
707                                         osrfHash* fields = osrfHashGet(meta, "fields");
708
709                                         int i = 0;
710                                         char* link_field;
711                                         osrfStringArray* link_fields;
712                                         
713                                         jsonObject* flesh_fields = jsonObjectGetKey( order_hash, "flesh_fields" );
714                                         if (flesh_fields) {
715                                                 jsonObjectNode* _f;
716                                                 jsonObjectIterator* _i = jsonNewObjectIterator( flesh_fields );
717                                                 link_fields = osrfNewStringArray(1);
718                                                 while ((_f = jsonObjectIteratorNext( _i ))) {
719                                                         osrfStringArrayAdd( link_fields, jsonObjectToSimpleString( _f->item ) );
720                                                 }
721                                         } else {
722                                                 link_fields = osrfHashKeys( links );
723                                         }
724
725                                         while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
726
727                                                 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
728
729                                                 osrfHash* kid_link = osrfHashGet(links, link_field);
730                                                 if (!kid_link) continue;
731
732                                                 osrfHash* field = osrfHashGet(fields, link_field);
733                                                 if (!field) continue;
734
735                                                 osrfHash* value_field = field;
736
737                                                 osrfHash* kid_idl = osrfHashGet(idlHash, osrfHashGet(kid_link, "class"));
738                                                 if (!kid_idl) continue;
739
740                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
741                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
742                                                 }
743                                                         
744                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
745                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
746                                                 }
747
748                                                 osrfLogDebug(
749                                                         OSRF_LOG_MARK,
750                                                         "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
751                                                         osrfHashGet(kid_link, "field"),
752                                                         osrfHashGet(kid_link, "class"),
753                                                         osrfHashGet(kid_link, "key"),
754                                                         osrfHashGet(kid_link, "reltype")
755                                                 );
756
757                                                 jsonObject* fake_params = jsonParseString("[]");
758                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // search hash
759                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // order/flesh hash
760
761                                                 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
762
763                                                 char* search_key =
764                                                 jsonObjectToSimpleString(
765                                                         jsonObjectGetIndex(
766                                                                 cur->item,
767                                                                 atoi( osrfHashGet(value_field, "array_position") )
768                                                         )
769                                                 );
770
771                                                 if (!search_key) {
772                                                         osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
773                                                         continue;
774                                                 }
775                                                         
776                                                 jsonObjectSetKey(
777                                                         jsonObjectGetIndex(fake_params, 0),
778                                                         osrfHashGet(kid_link, "key"),
779                                                         jsonNewObject( search_key )
780                                                 );
781
782                                                 free(search_key);
783
784
785                                                 jsonObjectSetKey(
786                                                         jsonObjectGetIndex(fake_params, 1),
787                                                         "flesh",
788                                                         jsonNewNumberObject( (double)((int)x - 1) )
789                                                 );
790
791                                                 if (flesh_fields) {
792                                                         jsonObjectSetKey(
793                                                                 jsonObjectGetIndex(fake_params, 1),
794                                                                 "flesh_fields",
795                                                                 jsonObjectClone( flesh_fields )
796                                                         );
797                                                 }
798
799                                                 if (jsonObjectGetKey(order_hash, "order_by")) {
800                                                         jsonObjectSetKey(
801                                                                 jsonObjectGetIndex(fake_params, 1),
802                                                                 "order_by",
803                                                                 jsonObjectClone(jsonObjectGetKey(order_hash, "order_by"))
804                                                         );
805                                                 }
806
807                                                 jsonObject* kids = doSearch(kid_idl, fake_params);
808
809                                                 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
810                                                 
811                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" ))) {
812                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
813                                                         jsonObjectSetIndex(
814                                                                 cur->item,
815                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
816                                                                 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
817                                                         );
818                                                         jsonObjectFree( kids );
819                                                 }
820
821                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
822                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
823                                                         jsonObjectSetIndex(
824                                                                 cur->item,
825                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
826                                                                 kids
827                                                         );
828                                                 }
829
830                                                 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
831
832                                                 jsonObjectFree( fake_params );
833                                         }
834                                         if (flesh_fields) osrfStringArrayFree(link_fields);
835                                 }
836                                 jsonObjectIteratorFree(itr);
837                         }
838                 }
839         }
840
841         return res_list;
842 }
843
844
845 jsonObject* doUpdate( osrfHash* meta, jsonObject* params ) { return NULL; }
846
847 jsonObject* doDelete( osrfHash* meta, jsonObject* params ) { return NULL; }
848
849
850 jsonObject* oilsMakeJSONFromResult( dbi_result result, osrfHash* meta) {
851         if(!(result && meta)) return NULL;
852
853         jsonObject* object = jsonParseString("[]");
854         jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
855
856         osrfHash* fields = osrfHashGet(meta, "fields");
857
858         osrfLogDebug(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
859
860         osrfHash* _f;
861         time_t _tmp_dt;
862         char dt_string[256];
863         struct tm gmdt;
864
865         int fmIndex;
866         int columnIndex = 1;
867         int attr;
868         unsigned short type;
869         const char* columnName;
870
871         /* cycle through the column list */
872         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
873
874                 osrfLogDebug(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
875
876                 fmIndex = -1; // reset the position
877                 
878                 /* determine the field type and storage attributes */
879                 type = dbi_result_get_field_type(result, columnName);
880                 attr = dbi_result_get_field_attribs(result, columnName);
881
882                 /* fetch the fieldmapper index */
883                 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
884                         char* virt = (char*)osrfHashGet(_f, "virtual");
885                         char* pos = (char*)osrfHashGet(_f, "array_position");
886
887                         if ( !virt || !pos || !(strcmp( virt, "true" )) ) continue;
888
889                         fmIndex = atoi( pos );
890                         osrfLogDebug(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
891                 }
892
893                 if (dbi_result_field_is_null(result, columnName)) {
894                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
895                 } else {
896
897                         switch( type ) {
898
899                                 case DBI_TYPE_INTEGER :
900
901                                         if( attr & DBI_INTEGER_SIZE8 ) 
902                                                 jsonObjectSetIndex( object, fmIndex, 
903                                                         jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
904                                         else 
905                                                 jsonObjectSetIndex( object, fmIndex, 
906                                                         jsonNewNumberObject(dbi_result_get_long(result, columnName)));
907
908                                         break;
909
910                                 case DBI_TYPE_DECIMAL :
911                                         jsonObjectSetIndex( object, fmIndex, 
912                                                         jsonNewNumberObject(dbi_result_get_double(result, columnName)));
913                                         break;
914
915                                 case DBI_TYPE_STRING :
916
917
918                                         jsonObjectSetIndex(
919                                                 object,
920                                                 fmIndex,
921                                                 jsonNewObject( dbi_result_get_string(result, columnName) )
922                                         );
923
924                                         break;
925
926                                 case DBI_TYPE_DATETIME :
927
928                                         memset(dt_string, '\0', 256);
929                                         memset(&gmdt, '\0', sizeof(gmdt));
930                                         memset(&_tmp_dt, '\0', sizeof(_tmp_dt));
931
932                                         _tmp_dt = dbi_result_get_datetime(result, columnName);
933
934                                         localtime_r( &_tmp_dt, &gmdt );
935
936                                         if (!(attr & DBI_DATETIME_DATE)) {
937                                                 strftime(dt_string, 255, "%T", &gmdt);
938                                         } else if (!(attr & DBI_DATETIME_TIME)) {
939                                                 strftime(dt_string, 255, "%F", &gmdt);
940                                         } else {
941                                                 /* XXX ARG! My eyes! The goggles, they do nothing! */
942
943                                                 strftime(dt_string, 255, "%FT%T%z", &gmdt);
944                                         }
945
946                                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
947
948                                         break;
949
950                                 case DBI_TYPE_BINARY :
951                                         osrfLogError( OSRF_LOG_MARK, 
952                                                 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
953                         }
954                 }
955         }
956
957         return object;
958 }
959