]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/c-apps/oils_cstore.c
8c5dc65de5bdfe4d2f94162ee9e997632c794cef
[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/osrf_message.h"
4 #include "opensrf/utils.h"
5 #include "objson/object.h"
6 #include "opensrf/log.h"
7 #include "openils/oils_idl.h"
8 #include <dbi/dbi.h>
9
10 #include <time.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14
15 #ifdef RSTORE
16 #  define MODULENAME "open-ils.reporter-store"
17 #else
18 #  define MODULENAME "open-ils.cstore"
19 #endif
20
21 #define DISABLE_I18N    2
22 #define SELECT_DISTINCT 1
23 #define AND_OP_JOIN     0
24 #define OR_OP_JOIN      1
25
26 int osrfAppChildInit();
27 int osrfAppInitialize();
28 void osrfAppChildExit();
29
30 static int verifyObjectClass ( osrfMethodContext*, const jsonObject* );
31
32 int beginTransaction ( osrfMethodContext* );
33 int commitTransaction ( osrfMethodContext* );
34 int rollbackTransaction ( osrfMethodContext* );
35
36 int setSavepoint ( osrfMethodContext* );
37 int releaseSavepoint ( osrfMethodContext* );
38 int rollbackSavepoint ( osrfMethodContext* );
39
40 int doJSONSearch ( osrfMethodContext* );
41
42 int dispatchCRUDMethod ( osrfMethodContext* );
43 static jsonObject* doCreate ( osrfMethodContext*, int* );
44 static jsonObject* doRetrieve ( osrfMethodContext*, int* );
45 static jsonObject* doUpdate ( osrfMethodContext*, int* );
46 static jsonObject* doDelete ( osrfMethodContext*, int* );
47 static jsonObject* doFieldmapperSearch ( osrfMethodContext*, osrfHash*,
48                 const jsonObject*, int* );
49 static jsonObject* oilsMakeFieldmapperFromResult( dbi_result, osrfHash* );
50 static jsonObject* oilsMakeJSONFromResult( dbi_result );
51
52 static char* searchWriteSimplePredicate ( const char*, osrfHash*,
53         const char*, const char*, const char* );
54 static char* searchSimplePredicate ( const char*, const char*, osrfHash*, const jsonObject* );
55 static char* searchFunctionPredicate ( const char*, osrfHash*, const jsonObjectNode* );
56 static char* searchFieldTransform ( const char*, osrfHash*, const jsonObject*);
57 static char* searchFieldTransformPredicate ( const char*, osrfHash*, jsonObjectNode* );
58 static char* searchBETWEENPredicate ( const char*, osrfHash*, jsonObject* );
59 static char* searchINPredicate ( const char*, osrfHash*, const jsonObject*, const char* );
60 static char* searchPredicate ( const char*, osrfHash*, jsonObject* );
61 static char* searchJOIN ( const jsonObject*, osrfHash* );
62 static char* searchWHERE ( const jsonObject*, osrfHash*, int );
63 static char* buildSELECT ( jsonObject*, jsonObject*, osrfHash*, osrfMethodContext* );
64
65 static char* SELECT ( osrfMethodContext*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, int );
66
67 void userDataFree( void* );
68 static void sessionDataFree( char*, void* );
69 static char* getSourceDefinition( osrfHash* );
70
71 static dbi_conn writehandle; /* our MASTER db connection */
72 static dbi_conn dbhandle; /* our CURRENT db connection */
73 osrfHash readHandles;
74 static jsonObject* jsonNULL = NULL; // 
75 static int max_flesh_depth = 100;
76
77 /* called when this process is about to exit */
78 void osrfAppChildExit() {
79         osrfLogDebug(OSRF_LOG_MARK, "Child is exiting, disconnecting from database...");
80
81         if (writehandle) {
82                 dbi_conn_query(writehandle, "ROLLBACK;");
83                 dbi_conn_close(writehandle);
84                 writehandle = NULL;
85         }
86
87         if (dbhandle)
88                 dbi_conn_close(dbhandle);
89
90         // XXX add cleanup of readHandles whenever that gets used
91
92         return;
93 }
94
95 int osrfAppInitialize() {
96         growing_buffer* method_name;
97
98         osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
99         osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
100
101         if (!oilsIDLInit( osrf_settings_host_value("/IDL") )) return 1; /* return non-zero to indicate error */
102
103         // Generic search thingy
104         method_name =  buffer_init(64);
105         buffer_fadd(method_name, "%s.json_query", MODULENAME);
106         char* method_str = buffer_data(method_name);
107         osrfAppRegisterMethod( MODULENAME, method_str, "doJSONSearch", "", 1, OSRF_METHOD_STREAMING );
108         free(method_str);
109
110         // first we register all the transaction and savepoint methods
111         buffer_reset(method_name);
112         buffer_fadd(method_name, "%s.transaction.begin", MODULENAME);
113         method_str = buffer_data(method_name);
114         osrfAppRegisterMethod( MODULENAME, method_str, "beginTransaction", "", 0, 0 );
115         free(method_str);
116
117         buffer_reset(method_name);
118         buffer_fadd(method_name, "%s.transaction.commit", MODULENAME);
119         method_str = buffer_data(method_name);
120         osrfAppRegisterMethod( MODULENAME, method_str, "commitTransaction", "", 0, 0 );
121         free(method_str);
122
123         buffer_reset(method_name);
124         buffer_fadd(method_name, "%s.transaction.rollback", MODULENAME);
125         method_str = buffer_data(method_name);
126         osrfAppRegisterMethod( MODULENAME, method_str, "rollbackTransaction", "", 0, 0 );
127         free(method_str);
128
129         buffer_reset(method_name);
130         buffer_fadd(method_name, "%s.savepoint.set", MODULENAME);
131         method_str = buffer_data(method_name);
132         osrfAppRegisterMethod( MODULENAME, method_str, "setSavepoint", "", 1, 0 );
133         free(method_str);
134
135         buffer_reset(method_name);
136         buffer_fadd(method_name, "%s.savepoint.release", MODULENAME);
137         method_str = buffer_data(method_name);
138         osrfAppRegisterMethod( MODULENAME, method_str, "releaseSavepoint", "", 1, 0 );
139         free(method_str);
140
141         buffer_reset(method_name);
142         buffer_fadd(method_name, "%s.savepoint.rollback", MODULENAME);
143         method_str = buffer_data(method_name);
144         osrfAppRegisterMethod( MODULENAME, method_str, "rollbackSavepoint", "", 1, 0 );
145         free(method_str);
146
147         buffer_free(method_name);
148
149         osrfStringArray* global_methods = osrfNewStringArray(6);
150
151         osrfStringArrayAdd( global_methods, "create" );
152         osrfStringArrayAdd( global_methods, "retrieve" );
153         osrfStringArrayAdd( global_methods, "update" );
154         osrfStringArrayAdd( global_methods, "delete" );
155         osrfStringArrayAdd( global_methods, "search" );
156         osrfStringArrayAdd( global_methods, "id_list" );
157
158         int c_index = 0; 
159         char* classname;
160         osrfStringArray* classes = osrfHashKeys( oilsIDL() );
161         osrfLogDebug(OSRF_LOG_MARK, "%d classes loaded", classes->size );
162         osrfLogDebug(OSRF_LOG_MARK, "At least %d methods will be generated", classes->size * global_methods->size);
163         
164         while ( (classname = osrfStringArrayGetString(classes, c_index++)) ) {
165                 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
166                 
167                 osrfHash* idlClass = osrfHashGet(oilsIDL(), classname);
168
169                 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), MODULENAME )) {
170                         osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on", MODULENAME, classname);
171                         continue;
172                 }
173
174                 char* virt = osrfHashGet(idlClass, "virtual");
175                 if (virt && !strcmp( virt, "true")) {
176                         osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
177                         continue;
178                 }
179
180                 int i = 0; 
181                 char* method_type;
182                 char* st_tmp;
183                 char* _fm;
184                 char* part;
185                 osrfHash* method_meta;
186                 while ( (method_type = osrfStringArrayGetString(global_methods, i++)) ) {
187                         osrfLogDebug(OSRF_LOG_MARK, "Using files to build %s class methods for %s", method_type, classname);
188
189                         if (!osrfHashGet(idlClass, "fieldmapper")) continue;
190
191                         char* readonly = osrfHashGet(idlClass, "readonly");
192                         if (    readonly &&
193                                 !strncasecmp( "true", readonly, 4) &&
194                                 ( *method_type == 's' || *method_type == 'r' || *method_type == 'i')
195                         ) continue;
196
197                         method_meta = osrfNewHash();
198                         osrfHashSet(method_meta, idlClass, "class");
199
200                         _fm = strdup( (char*)osrfHashGet(idlClass, "fieldmapper") );
201                         part = strtok_r(_fm, ":", &st_tmp);
202
203                         growing_buffer* method_name =  buffer_init(64);
204                         buffer_fadd(method_name, "%s.direct.%s", MODULENAME, part);
205
206                         while ((part = strtok_r(NULL, ":", &st_tmp))) {
207                                 buffer_fadd(method_name, ".%s", part);
208                         }
209                         buffer_fadd(method_name, ".%s", method_type);
210
211
212                         char* method = buffer_release(method_name);
213                         free(_fm);
214
215                         osrfHashSet( method_meta, method, "methodname" );
216                         osrfHashSet( method_meta, method_type, "methodtype" );
217
218                         int flags = 0;
219                         if (!(strcmp( method_type, "search" )) || !(strcmp( method_type, "id_list" ))) {
220                                 flags = flags | OSRF_METHOD_STREAMING;
221                         }
222
223                         osrfAppRegisterExtendedMethod(
224                                 MODULENAME,
225                                 method,
226                                 "dispatchCRUDMethod",
227                                 "",
228                                 1,
229                                 flags,
230                                 (void*)method_meta
231                         );
232
233                         free(method);
234                 }
235         }
236
237         return 0;
238 }
239
240 static char* getSourceDefinition( osrfHash* class ) {
241
242         char* tabledef = osrfHashGet(class, "tablename");
243
244         if (!tabledef) {
245                 growing_buffer* tablebuf = buffer_init(128);
246                 tabledef = osrfHashGet(class, "source_definition");
247                 if( !tabledef )
248                         tabledef = "(null)";
249                 buffer_fadd( tablebuf, "(%s)", tabledef );
250                 tabledef = buffer_release(tablebuf);
251         } else {
252                 tabledef = strdup(tabledef);
253         }
254
255         return tabledef;
256 }
257
258 /**
259  * Connects to the database 
260  */
261 int osrfAppChildInit() {
262
263         osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
264         dbi_initialize(NULL);
265         osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
266
267         char* driver    = osrf_settings_host_value("/apps/%s/app_settings/driver", MODULENAME);
268         char* user      = osrf_settings_host_value("/apps/%s/app_settings/database/user", MODULENAME);
269         char* host      = osrf_settings_host_value("/apps/%s/app_settings/database/host", MODULENAME);
270         char* port      = osrf_settings_host_value("/apps/%s/app_settings/database/port", MODULENAME);
271         char* db        = osrf_settings_host_value("/apps/%s/app_settings/database/db", MODULENAME);
272         char* pw        = osrf_settings_host_value("/apps/%s/app_settings/database/pw", MODULENAME);
273         char* md        = osrf_settings_host_value("/apps/%s/app_settings/max_query_recursion", MODULENAME);
274
275         osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
276         writehandle = dbi_conn_new(driver);
277
278         if(!writehandle) {
279                 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
280                 return -1;
281         }
282         osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
283
284         osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database.  host=%s, "
285                 "port=%s, user=%s, pw=%s, db=%s", MODULENAME, host, port, user, pw, db );
286
287         if(host) dbi_conn_set_option(writehandle, "host", host );
288         if(port) dbi_conn_set_option_numeric( writehandle, "port", atoi(port) );
289         if(user) dbi_conn_set_option(writehandle, "username", user);
290         if(pw) dbi_conn_set_option(writehandle, "password", pw );
291         if(db) dbi_conn_set_option(writehandle, "dbname", db );
292
293         if(md) max_flesh_depth = atoi(md);
294         if(max_flesh_depth < 0) max_flesh_depth = 1;
295         if(max_flesh_depth > 1000) max_flesh_depth = 1000;
296
297         free(user);
298         free(host);
299         free(port);
300         free(db);
301         free(pw);
302
303         const char* err;
304         if (dbi_conn_connect(writehandle) < 0) {
305                 sleep(1);
306                 if (dbi_conn_connect(writehandle) < 0) {
307                         dbi_conn_error(writehandle, &err);
308                         osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
309                         return -1;
310                 }
311         }
312
313         osrfLogInfo(OSRF_LOG_MARK, "%s successfully connected to the database", MODULENAME);
314
315         int attr;
316         unsigned short type;
317         int i = 0; 
318         char* classname;
319         osrfStringArray* classes = osrfHashKeys( oilsIDL() );
320         
321         while ( (classname = osrfStringArrayGetString(classes, i++)) ) {
322                 osrfHash* class = osrfHashGet( oilsIDL(), classname );
323                 osrfHash* fields = osrfHashGet( class, "fields" );
324
325                 char* virt = osrfHashGet(class, "virtual");
326                 if (virt && !strcmp( virt, "true")) {
327                         osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
328                         continue;
329                 }
330
331                 char* tabledef = getSourceDefinition(class);
332
333                 growing_buffer* sql_buf = buffer_init(32);
334                 buffer_fadd( sql_buf, "SELECT * FROM %s AS x WHERE 1=0;", tabledef );
335
336                 free(tabledef);
337
338                 char* sql = buffer_release(sql_buf);
339                 osrfLogDebug(OSRF_LOG_MARK, "%s Investigatory SQL = %s", MODULENAME, sql);
340
341                 dbi_result result = dbi_conn_query(writehandle, sql);
342                 free(sql);
343
344                 if (result) {
345
346                         int columnIndex = 1;
347                         const char* columnName;
348                         osrfHash* _f;
349                         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
350
351                                 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
352
353                                 /* fetch the fieldmapper index */
354                                 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
355
356                                         osrfLogDebug(OSRF_LOG_MARK, "Found [%s] in IDL hash...", (char*)columnName);
357
358                                         /* determine the field type and storage attributes */
359                                         type = dbi_result_get_field_type(result, columnName);
360                                         attr = dbi_result_get_field_attribs(result, columnName);
361
362                                         switch( type ) {
363
364                                                 case DBI_TYPE_INTEGER :
365
366                                                         if ( !osrfHashGet(_f, "primitive") )
367                                                                 osrfHashSet(_f,"number", "primitive");
368
369                                                         if( attr & DBI_INTEGER_SIZE8 ) 
370                                                                 osrfHashSet(_f,"INT8", "datatype");
371                                                         else 
372                                                                 osrfHashSet(_f,"INT", "datatype");
373                                                         break;
374
375                                                 case DBI_TYPE_DECIMAL :
376                                                         if ( !osrfHashGet(_f, "primitive") )
377                                                                 osrfHashSet(_f,"number", "primitive");
378
379                                                         osrfHashSet(_f,"NUMERIC", "datatype");
380                                                         break;
381
382                                                 case DBI_TYPE_STRING :
383                                                         if ( !osrfHashGet(_f, "primitive") )
384                                                                 osrfHashSet(_f,"string", "primitive");
385                                                         osrfHashSet(_f,"TEXT", "datatype");
386                                                         break;
387
388                                                 case DBI_TYPE_DATETIME :
389                                                         if ( !osrfHashGet(_f, "primitive") )
390                                                                 osrfHashSet(_f,"string", "primitive");
391
392                                                         osrfHashSet(_f,"TIMESTAMP", "datatype");
393                                                         break;
394
395                                                 case DBI_TYPE_BINARY :
396                                                         if ( !osrfHashGet(_f, "primitive") )
397                                                                 osrfHashSet(_f,"string", "primitive");
398
399                                                         osrfHashSet(_f,"BYTEA", "datatype");
400                                         }
401
402                                         osrfLogDebug(
403                                                 OSRF_LOG_MARK,
404                                                 "Setting [%s] to primitive [%s] and datatype [%s]...",
405                                                 (char*)columnName,
406                                                 osrfHashGet(_f, "primitive"),
407                                                 osrfHashGet(_f, "datatype")
408                                         );
409                                 }
410                         }
411                         dbi_result_free(result);
412                 } else {
413                         osrfLogDebug(OSRF_LOG_MARK, "No data found for class [%s]...", (char*)classname);
414                 }
415         }
416
417         osrfStringArrayFree(classes);
418
419         return 0;
420 }
421
422 void userDataFree( void* blob ) {
423         osrfHashFree( (osrfHash*)blob );
424         return;
425 }
426
427 static void sessionDataFree( char* key, void* item ) {
428         if (!(strcmp(key,"xact_id"))) {
429                 if (writehandle)
430                         dbi_conn_query(writehandle, "ROLLBACK;");
431                 free(item);
432         }
433
434         return;
435 }
436
437 int beginTransaction ( osrfMethodContext* ctx ) {
438         OSRF_METHOD_VERIFY_CONTEXT(ctx);
439
440         dbi_result result = dbi_conn_query(writehandle, "START TRANSACTION;");
441         if (!result) {
442                 osrfLogError(OSRF_LOG_MARK, "%s: Error starting transaction", MODULENAME );
443                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error starting transaction" );
444                 return -1;
445         } else {
446                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
447                 osrfAppRespondComplete( ctx, ret );
448                 jsonObjectFree(ret);
449                 
450                 if (!ctx->session->userData) {
451                         ctx->session->userData = osrfNewHash();
452                         ((osrfHash*)ctx->session->userData)->freeItem = &sessionDataFree;
453                 }
454
455                 osrfHashSet( (osrfHash*)ctx->session->userData, strdup( ctx->session->session_id ), "xact_id" );
456                 ctx->session->userDataFree = &userDataFree;
457                 
458         }
459         return 0;
460 }
461
462 int setSavepoint ( osrfMethodContext* ctx ) {
463         OSRF_METHOD_VERIFY_CONTEXT(ctx);
464
465         char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
466
467         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
468                 osrfAppSessionStatus(
469                         ctx->session,
470                         OSRF_STATUS_INTERNALSERVERERROR,
471                         "osrfMethodException",
472                         ctx->request,
473                         "No active transaction -- required for savepoints"
474                 );
475                 return -1;
476         }
477
478         dbi_result result = dbi_conn_queryf(writehandle, "SAVEPOINT \"%s\";", spName);
479         if (!result) {
480                 osrfLogError(
481                         OSRF_LOG_MARK,
482                         "%s: Error creating savepoint %s in transaction %s",
483                         MODULENAME,
484                         spName,
485                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
486                 );
487                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error creating savepoint" );
488                 return -1;
489         } else {
490                 jsonObject* ret = jsonNewObject(spName);
491                 osrfAppRespondComplete( ctx, ret );
492                 jsonObjectFree(ret);
493         }
494         return 0;
495 }
496
497 int releaseSavepoint ( osrfMethodContext* ctx ) {
498         OSRF_METHOD_VERIFY_CONTEXT(ctx);
499
500         char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
501
502         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
503                 osrfAppSessionStatus(
504                         ctx->session,
505                         OSRF_STATUS_INTERNALSERVERERROR,
506                         "osrfMethodException",
507                         ctx->request,
508                         "No active transaction -- required for savepoints"
509                 );
510                 return -1;
511         }
512
513         dbi_result result = dbi_conn_queryf(writehandle, "RELEASE SAVEPOINT \"%s\";", spName);
514         if (!result) {
515                 osrfLogError(
516                         OSRF_LOG_MARK,
517                         "%s: Error releasing savepoint %s in transaction %s",
518                         MODULENAME,
519                         spName,
520                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
521                 );
522                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error releasing savepoint" );
523                 return -1;
524         } else {
525                 jsonObject* ret = jsonNewObject(spName);
526                 osrfAppRespondComplete( ctx, ret );
527                 jsonObjectFree(ret);
528         }
529         return 0;
530 }
531
532 int rollbackSavepoint ( osrfMethodContext* ctx ) {
533         OSRF_METHOD_VERIFY_CONTEXT(ctx);
534
535         char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
536
537         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
538                 osrfAppSessionStatus(
539                         ctx->session,
540                         OSRF_STATUS_INTERNALSERVERERROR,
541                         "osrfMethodException",
542                         ctx->request,
543                         "No active transaction -- required for savepoints"
544                 );
545                 return -1;
546         }
547
548         dbi_result result = dbi_conn_queryf(writehandle, "ROLLBACK TO SAVEPOINT \"%s\";", spName);
549         if (!result) {
550                 osrfLogError(
551                         OSRF_LOG_MARK,
552                         "%s: Error rolling back savepoint %s in transaction %s",
553                         MODULENAME,
554                         spName,
555                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
556                 );
557                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error rolling back savepoint" );
558                 return -1;
559         } else {
560                 jsonObject* ret = jsonNewObject(spName);
561                 osrfAppRespondComplete( ctx, ret );
562                 jsonObjectFree(ret);
563         }
564         return 0;
565 }
566
567 int commitTransaction ( osrfMethodContext* ctx ) {
568         OSRF_METHOD_VERIFY_CONTEXT(ctx);
569
570         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
571                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to commit" );
572                 return -1;
573         }
574
575         dbi_result result = dbi_conn_query(writehandle, "COMMIT;");
576         if (!result) {
577                 osrfLogError(OSRF_LOG_MARK, "%s: Error committing transaction", MODULENAME );
578                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error committing transaction" );
579                 return -1;
580         } else {
581                 osrfHashRemove(ctx->session->userData, "xact_id");
582                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
583                 osrfAppRespondComplete( ctx, ret );
584                 jsonObjectFree(ret);
585         }
586         return 0;
587 }
588
589 int rollbackTransaction ( osrfMethodContext* ctx ) {
590         OSRF_METHOD_VERIFY_CONTEXT(ctx);
591
592         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
593                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to roll back" );
594                 return -1;
595         }
596
597         dbi_result result = dbi_conn_query(writehandle, "ROLLBACK;");
598         if (!result) {
599                 osrfLogError(OSRF_LOG_MARK, "%s: Error rolling back transaction", MODULENAME );
600                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error rolling back transaction" );
601                 return -1;
602         } else {
603                 osrfHashRemove(ctx->session->userData, "xact_id");
604                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
605                 osrfAppRespondComplete( ctx, ret );
606                 jsonObjectFree(ret);
607         }
608         return 0;
609 }
610
611 int dispatchCRUDMethod ( osrfMethodContext* ctx ) {
612         OSRF_METHOD_VERIFY_CONTEXT(ctx);
613
614         osrfHash* meta = (osrfHash*) ctx->method->userData;
615         osrfHash* class_obj = osrfHashGet( meta, "class" );
616         
617         int err = 0;
618
619         jsonObject * obj = NULL;
620         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "create"))
621                 obj = doCreate(ctx, &err);
622
623         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "retrieve"))
624                 obj = doRetrieve(ctx, &err);
625
626         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "update"))
627                 obj = doUpdate(ctx, &err);
628
629         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "delete"))
630                 obj = doDelete(ctx, &err);
631
632         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "search")) {
633
634                 obj = doFieldmapperSearch(ctx, class_obj, ctx->params, &err);
635                 if(err) return err;
636
637                 jsonObjectNode* cur;
638                 jsonObjectIterator* itr = jsonNewObjectIterator( obj );
639                 while ((cur = jsonObjectIteratorNext( itr ))) {
640                         osrfAppRespond( ctx, jsonObjectClone(cur->item) );
641                 }
642                 jsonObjectIteratorFree(itr);
643                 osrfAppRespondComplete( ctx, NULL );
644
645         } else if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "id_list")) {
646
647                 jsonObject* _p = jsonObjectClone( ctx->params );
648                 if (jsonObjectGetIndex( _p, 1 )) {
649                         jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "flesh" );
650                         jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "flesh_columns" );
651                 } else {
652                         jsonObjectSetIndex( _p, 1, jsonParseString("{}") );
653                 }
654
655                 growing_buffer* sel_list = buffer_init(16);
656                 buffer_fadd(sel_list, "{ \"%s\":[\"%s\"] }", osrfHashGet( class_obj, "classname" ), osrfHashGet( class_obj, "primarykey" ));
657                 char* _s = buffer_release(sel_list);
658
659                 jsonObjectSetKey( jsonObjectGetIndex( _p, 1 ), "select", jsonParseString(_s) );
660                 osrfLogDebug(OSRF_LOG_MARK, "%s: Select qualifer set to [%s]", MODULENAME, _s);
661                 free(_s);
662
663                 obj = doFieldmapperSearch(ctx, class_obj, _p, &err);
664                 if(err) return err;
665
666                 jsonObjectNode* cur;
667                 jsonObjectIterator* itr = jsonNewObjectIterator( obj );
668                 while ((cur = jsonObjectIteratorNext( itr ))) {
669                         osrfAppRespond(
670                                 ctx,
671                                 jsonObjectClone(
672                                         jsonObjectGetIndex(
673                                                 cur->item,
674                                                 atoi(
675                                                         osrfHashGet(
676                                                                 osrfHashGet(
677                                                                         osrfHashGet( class_obj, "fields" ),
678                                                                         osrfHashGet( class_obj, "primarykey")
679                                                                 ),
680                                                                 "array_position"
681                                                         )
682                                                 )
683                                         )
684                                 )
685                         );
686                 }
687                 jsonObjectIteratorFree(itr);
688                 osrfAppRespondComplete( ctx, NULL );
689                 
690         } else {
691                 osrfAppRespondComplete( ctx, obj );
692         }
693
694         jsonObjectFree(obj);
695
696         return err;
697 }
698
699 static int verifyObjectClass ( osrfMethodContext* ctx, const jsonObject* param ) {
700         
701         osrfHash* meta = (osrfHash*) ctx->method->userData;
702         osrfHash* class = osrfHashGet( meta, "class" );
703         
704         if (!param->classname || (strcmp( osrfHashGet(class, "classname"), param->classname ))) {
705
706                 growing_buffer* msg = buffer_init(128);
707                 buffer_fadd(
708                         msg,
709                         "%s: %s method for type %s was passed a %s",
710                         MODULENAME,
711                         osrfHashGet(meta, "methodtype"),
712                         osrfHashGet(class, "classname"),
713                         param->classname
714                 );
715
716                 char* m = buffer_release(msg);
717                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException", ctx->request, m );
718
719                 free(m);
720
721                 return 0;
722         }
723         return 1;
724 }
725
726 static jsonObject* doCreate(osrfMethodContext* ctx, int* err ) {
727
728         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
729         jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
730         jsonObject* options = jsonObjectGetIndex( ctx->params, 1 );
731
732         if (!verifyObjectClass(ctx, target)) {
733                 *err = -1;
734                 return jsonNULL;
735         }
736
737         osrfLogDebug( OSRF_LOG_MARK, "Object seems to be of the correct type" );
738
739         if (!ctx->session || !ctx->session->userData || !osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
740                 osrfLogError( OSRF_LOG_MARK, "No active transaction -- required for CREATE" );
741
742                 osrfAppSessionStatus(
743                         ctx->session,
744                         OSRF_STATUS_BADREQUEST,
745                         "osrfMethodException",
746                         ctx->request,
747                         "No active transaction -- required for CREATE"
748                 );
749                 *err = -1;
750                 return jsonNULL;
751         }
752
753         if (osrfHashGet( meta, "readonly" ) && strncasecmp("true", osrfHashGet( meta, "readonly" ), 4)) {
754                 osrfAppSessionStatus(
755                         ctx->session,
756                         OSRF_STATUS_BADREQUEST,
757                         "osrfMethodException",
758                         ctx->request,
759                         "Cannot INSERT readonly class"
760                 );
761                 *err = -1;
762                 return jsonNULL;
763         }
764
765
766         char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
767
768         // Set the last_xact_id
769         osrfHash* last_xact_id;
770         if ((last_xact_id = oilsIDLFindPath("/%s/fields/last_xact_id", target->classname))) {
771                 int index = atoi( osrfHashGet(last_xact_id, "array_position") );
772                 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
773                 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
774         }       
775
776         osrfLogDebug( OSRF_LOG_MARK, "There is a transaction running..." );
777
778         dbhandle = writehandle;
779
780         osrfHash* fields = osrfHashGet(meta, "fields");
781         char* pkey = osrfHashGet(meta, "primarykey");
782         char* seq = osrfHashGet(meta, "sequence");
783
784         growing_buffer* table_buf = buffer_init(128);
785         growing_buffer* col_buf = buffer_init(128);
786         growing_buffer* val_buf = buffer_init(128);
787
788         buffer_fadd(table_buf,"INSERT INTO %s", osrfHashGet(meta, "tablename"));
789         buffer_add(col_buf,"(");
790         buffer_add(val_buf,"VALUES (");
791
792
793         int i = 0;
794         int first = 1;
795         char* field_name;
796         osrfStringArray* field_list = osrfHashKeys( fields );
797         while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
798
799                 osrfHash* field = osrfHashGet( fields, field_name );
800
801                 if(!( strcmp( osrfHashGet(osrfHashGet(fields,field_name), "virtual"), "true" ) )) continue;
802
803                 jsonObject* field_object = jsonObjectGetIndex( target, atoi(osrfHashGet(field, "array_position")) );
804
805                 char* value;
806                 if (field_object && field_object->classname) {
807                         value = jsonObjectToSimpleString(
808                                         jsonObjectGetIndex(
809                                                 field_object,
810                                                 atoi(
811                                                         osrfHashGet(
812                                                                 osrfHashGet(
813                                                                         oilsIDLFindPath("/%s/fields", field_object->classname),
814                                                                         (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
815                                                                 ),
816                                                                 "array_position"
817                                                         )
818                                                 )
819                                         )
820                                 );
821
822                 } else {
823                         value = jsonObjectToSimpleString( field_object );
824                 }
825
826
827                 if (first) {
828                         first = 0;
829                 } else {
830                         buffer_add(col_buf, ",");
831                         buffer_add(val_buf, ",");
832                 }
833
834                 buffer_add(col_buf, field_name);
835
836                 if (!field_object || field_object->type == JSON_NULL) {
837                         buffer_add( val_buf, "DEFAULT" );
838                         
839                 } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
840                         if ( !strcmp(osrfHashGet(field, "datatype"), "INT8") ) {
841                                 buffer_fadd( val_buf, "%lld", atoll(value) );
842                                 
843                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "INT") ) {
844                                 buffer_fadd( val_buf, "%d", atoi(value) );
845                                 
846                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
847                                 buffer_fadd( val_buf, "%f", atof(value) );
848                         }
849                 } else {
850                         if ( dbi_conn_quote_string(writehandle, &value) ) {
851                                 buffer_fadd( val_buf, "%s", value );
852
853                         } else {
854                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
855                                 osrfAppSessionStatus(
856                                         ctx->session,
857                                         OSRF_STATUS_INTERNALSERVERERROR,
858                                         "osrfMethodException",
859                                         ctx->request,
860                                         "Error quoting string -- please see the error log for more details"
861                                 );
862                                 free(value);
863                                 buffer_free(table_buf);
864                                 buffer_free(col_buf);
865                                 buffer_free(val_buf);
866                                 *err = -1;
867                                 return jsonNULL;
868                         }
869                 }
870
871                 free(value);
872                 
873         }
874
875
876         buffer_add(col_buf,")");
877         buffer_add(val_buf,")");
878
879         char* table_str = buffer_release(table_buf);
880         char* col_str   = buffer_release(col_buf);
881         char* val_str   = buffer_release(val_buf);
882         growing_buffer* sql = buffer_init(128);
883         buffer_fadd( sql, "%s %s %s;", table_str, col_str, val_str );
884         free(table_str);
885         free(col_str);
886         free(val_str);
887
888         char* query = buffer_release(sql);
889
890         osrfLogDebug(OSRF_LOG_MARK, "%s: Insert SQL [%s]", MODULENAME, query);
891
892         
893         dbi_result result = dbi_conn_query(writehandle, query);
894
895         jsonObject* obj = NULL;
896
897         if (!result) {
898                 obj = jsonNewObject(NULL);
899                 osrfLogError(
900                         OSRF_LOG_MARK,
901                         "%s ERROR inserting %s object using query [%s]",
902                         MODULENAME,
903                         osrfHashGet(meta, "fieldmapper"),
904                         query
905                 );
906                 osrfAppSessionStatus(
907                         ctx->session,
908                         OSRF_STATUS_INTERNALSERVERERROR,
909                         "osrfMethodException",
910                         ctx->request,
911                         "INSERT error -- please see the error log for more details"
912                 );
913                 *err = -1;
914         } else {
915
916                 int pos = atoi(osrfHashGet( osrfHashGet(fields, pkey), "array_position" ));
917                 char* id = jsonObjectToSimpleString(jsonObjectGetIndex(target, pos));
918                 if (!id) {
919                         unsigned long long new_id = dbi_conn_sequence_last(writehandle, seq);
920                         growing_buffer* _id = buffer_init(10);
921                         buffer_fadd(_id, "%lld", new_id);
922                         id = buffer_release(_id);
923                 }
924
925                 if (    !options
926                         || !jsonObjectGetKeyConst( options, "quiet")
927                         || strcmp( jsonObjectToSimpleString(jsonObjectGetKeyConst( options, "quiet")), "true" )
928                 ) {
929
930                         jsonObject* fake_params = jsonParseString("[]");
931                         jsonObjectPush(fake_params, jsonParseString("{}"));
932
933                         jsonObjectSetKey(
934                                 jsonObjectGetIndex(fake_params, 0),
935                                 osrfHashGet(meta, "primarykey"),
936                                 jsonNewObject(id)
937                         );
938
939                         jsonObject* list = doFieldmapperSearch( ctx,meta, fake_params, err);
940
941                         if(*err) {
942                                 jsonObjectFree( fake_params );
943                                 obj = jsonNULL;
944                         } else {
945                                 obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
946                         }
947
948                         jsonObjectFree( list );
949                         jsonObjectFree( fake_params );
950
951                 } else {
952                         obj = jsonNewObject(id);
953                 }
954
955                 free(id);
956         }
957
958         free(query);
959
960         return obj;
961
962 }
963
964
965 static jsonObject* doRetrieve(osrfMethodContext* ctx, int* err ) {
966
967         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
968
969         jsonObject* obj;
970
971         char* id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
972         jsonObject* order_hash = jsonObjectGetIndex(ctx->params, 1);
973
974         osrfLogDebug(
975                 OSRF_LOG_MARK,
976                 "%s retrieving %s object with id %s",
977                 MODULENAME,
978                 osrfHashGet(meta, "fieldmapper"),
979                 id
980         );
981
982         jsonObject* fake_params = jsonParseString("[]");
983         jsonObjectPush(fake_params, jsonParseString("{}"));
984
985         jsonObjectSetKey(
986                 jsonObjectGetIndex(fake_params, 0),
987                 osrfHashGet(meta, "primarykey"),
988                 jsonParseString(id)
989         );
990
991         if (order_hash) jsonObjectPush(fake_params, jsonObjectClone(order_hash) );
992
993         jsonObject* list = doFieldmapperSearch( ctx,meta, fake_params, err);
994
995         if(*err) {
996                 jsonObjectFree( fake_params );
997                 return jsonNULL;
998         }
999
1000         obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
1001
1002         jsonObjectFree( list );
1003         jsonObjectFree( fake_params );
1004
1005         return obj;
1006 }
1007
1008 static char* jsonNumberToDBString ( osrfHash* field, const jsonObject* value ) {
1009         growing_buffer* val_buf = buffer_init(32);
1010
1011         if ( !strncmp(osrfHashGet(field, "datatype"), "INT", (size_t)3) ) {
1012                 if (value->type == JSON_NUMBER) buffer_fadd( val_buf, "%ld", (long)jsonObjectGetNumber(value) );
1013                 else buffer_fadd( val_buf, "%ld", atol(jsonObjectToSimpleString(value)) );
1014
1015         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
1016                 if (value->type == JSON_NUMBER) buffer_fadd( val_buf, "%f",  jsonObjectGetNumber(value) );
1017                 else buffer_fadd( val_buf, "%f", atof(jsonObjectToSimpleString(value)) );
1018         }
1019
1020         return buffer_release(val_buf);
1021 }
1022
1023 static char* searchINPredicate (const char* class, osrfHash* field,
1024                 const jsonObject* node, const char* op) {
1025         growing_buffer* sql_buf = buffer_init(32);
1026         
1027         buffer_fadd(
1028                 sql_buf,
1029                 "\"%s\".%s ",
1030                 class,
1031                 osrfHashGet(field, "name")
1032         );
1033
1034         if (!op) {
1035                 buffer_add(sql_buf, "IN (");
1036         } else if (!(strcasecmp(op,"not in"))) {
1037                 buffer_add(sql_buf, "NOT IN (");
1038         } else {
1039                 buffer_add(sql_buf, "IN (");
1040         }
1041
1042         int in_item_index = 0;
1043         int in_item_first = 1;
1044         jsonObject* in_item;
1045         while ( (in_item = jsonObjectGetIndex(node, in_item_index++)) ) {
1046
1047                 if (in_item_first)
1048                         in_item_first = 0;
1049                 else
1050                         buffer_add(sql_buf, ", ");
1051
1052                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1053                         char* val = jsonNumberToDBString( field, in_item );
1054                         buffer_fadd( sql_buf, "%s", val );
1055                         free(val);
1056
1057                 } else {
1058                         char* key_string = jsonObjectToSimpleString(in_item);
1059                         if ( dbi_conn_quote_string(dbhandle, &key_string) ) {
1060                                 buffer_fadd( sql_buf, "%s", key_string );
1061                                 free(key_string);
1062                         } else {
1063                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string);
1064                                 free(key_string);
1065                                 buffer_free(sql_buf);
1066                                 return NULL;
1067                         }
1068                 }
1069         }
1070
1071         buffer_add(
1072                 sql_buf,
1073                 ")"
1074         );
1075
1076         return buffer_release(sql_buf);
1077 }
1078
1079 static char* searchValueTransform( const jsonObject* array ) {
1080         growing_buffer* sql_buf = buffer_init(32);
1081
1082         char* val = NULL;
1083         int func_item_index = 0;
1084         int func_item_first = 2;
1085         jsonObject* func_item;
1086         while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1087
1088                 val = jsonObjectToSimpleString(func_item);
1089
1090                 if (func_item_first == 2) {
1091                         buffer_fadd(sql_buf, "%s( ", val);
1092                         free(val);
1093                         func_item_first--;
1094                         continue;
1095                 }
1096
1097                 if (func_item_first)
1098                         func_item_first--;
1099                 else
1100                         buffer_add(sql_buf, ", ");
1101
1102                 if ( dbi_conn_quote_string(dbhandle, &val) ) {
1103                         buffer_fadd( sql_buf, "%s", val );
1104                         free(val);
1105                 } else {
1106                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1107                         free(val);
1108                         buffer_free(sql_buf);
1109                         return NULL;
1110                 }
1111         }
1112
1113         buffer_add(
1114                 sql_buf,
1115                 " )"
1116         );
1117
1118         return buffer_release(sql_buf);
1119 }
1120
1121 static char* searchFunctionPredicate (const char* class, osrfHash* field,
1122                 const jsonObjectNode* node) {
1123         growing_buffer* sql_buf = buffer_init(32);
1124
1125         char* val = searchValueTransform(node->item);
1126         
1127         buffer_fadd(
1128                 sql_buf,
1129                 "\"%s\".%s %s %s",
1130                 class,
1131                 osrfHashGet(field, "name"),
1132                 node->key,
1133                 val
1134         );
1135
1136         free(val);
1137
1138         return buffer_release(sql_buf);
1139 }
1140
1141 static char* searchFieldTransform (const char* class, osrfHash* field, const jsonObject* node) {
1142         growing_buffer* sql_buf = buffer_init(32);
1143         
1144         char* field_transform = jsonObjectToSimpleString( jsonObjectGetKeyConst( node, "transform" ) );
1145
1146         if (field_transform) {
1147                 buffer_fadd( sql_buf, "%s(\"%s\".%s", field_transform, class, osrfHashGet(field, "name"));
1148             const jsonObject* array = jsonObjectGetKeyConst( node, "params" );
1149
1150         if (array) {
1151                 int func_item_index = 0;
1152                 jsonObject* func_item;
1153                 while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1154
1155                         char* val = jsonObjectToSimpleString(func_item);
1156
1157                     if ( dbi_conn_quote_string(dbhandle, &val) ) {
1158                             buffer_fadd( sql_buf, ",%s", val );
1159                         } else {
1160                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1161                             free(field_transform);
1162                                 buffer_free(sql_buf);
1163                                 return NULL;
1164                 }
1165             }
1166
1167         }
1168
1169         buffer_add(
1170                 sql_buf,
1171                 " )"
1172         );
1173  
1174         } else {
1175                 buffer_fadd( sql_buf, "\"%s\".%s", class, osrfHashGet(field, "name"));
1176     }
1177
1178         if (field_transform) free(field_transform);
1179
1180         return buffer_release(sql_buf);
1181 }
1182
1183 static char* searchFieldTransformPredicate (const char* class, osrfHash* field, jsonObjectNode* node) {
1184         growing_buffer* sql_buf = buffer_init(32);
1185         
1186         char* field_transform = searchFieldTransform( class, field, node->item );
1187         char* value = NULL;
1188
1189         if (!jsonObjectGetKeyConst( node->item, "value" )) {
1190                 value = searchWHERE( node->item, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN );
1191         } else if (jsonObjectGetKeyConst( node->item, "value" )->type == JSON_ARRAY) {
1192                 value = searchValueTransform(jsonObjectGetKeyConst( node->item, "value" ));
1193         } else if (jsonObjectGetKeyConst( node->item, "value" )->type == JSON_HASH) {
1194                 value = searchWHERE( jsonObjectGetKeyConst( node->item, "value" ), osrfHashGet( oilsIDL(), class ), AND_OP_JOIN );
1195         } else if (jsonObjectGetKeyConst( node->item, "value" )->type != JSON_NULL) {
1196                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1197                         value = jsonNumberToDBString( field, jsonObjectGetKeyConst( node->item, "value" ) );
1198                 } else {
1199                         value = jsonObjectToSimpleString(jsonObjectGetKeyConst( node->item, "value" ));
1200                         if ( !dbi_conn_quote_string(dbhandle, &value) ) {
1201                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, value);
1202                                 free(value);
1203                                 return NULL;
1204                         }
1205                 }
1206         }
1207
1208         buffer_fadd(
1209                 sql_buf,
1210                 "%s %s %s",
1211                 field_transform,
1212                 node->key,
1213                 value
1214         );
1215
1216         free(field_transform);
1217
1218         return buffer_release(sql_buf);
1219 }
1220
1221 static char* searchSimplePredicate (const char* orig_op, const char* class,
1222                 osrfHash* field, const jsonObject* node) {
1223
1224         char* val = NULL;
1225
1226         if (node->type != JSON_NULL) {
1227                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1228                         val = jsonNumberToDBString( field, node );
1229                 } else {
1230                         val = jsonObjectToSimpleString(node);
1231                 }
1232         }
1233
1234         char* pred = searchWriteSimplePredicate( class, field, osrfHashGet(field, "name"), orig_op, val );
1235
1236         if (val) free(val);
1237
1238         return pred;
1239 }
1240
1241 static char* searchWriteSimplePredicate ( const char* class, osrfHash* field,
1242         const char* left, const char* orig_op, const char* right ) {
1243
1244         char* val = NULL;
1245         char* op = NULL;
1246         if (right == NULL) {
1247                 val = strdup("NULL");
1248
1249                 if (strcmp( orig_op, "=" ))
1250                         op = strdup("IS NOT");
1251                 else
1252                         op = strdup("IS");
1253
1254         } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1255                 val = strdup(right);
1256                 op = strdup(orig_op);
1257
1258         } else {
1259                 val = strdup(right);
1260                 if ( !dbi_conn_quote_string(dbhandle, &val) ) {
1261                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1262                         free(val);
1263                         return NULL;
1264                 }
1265                 op = strdup(orig_op);
1266         }
1267
1268         growing_buffer* sql_buf = buffer_init(16);
1269         buffer_fadd( sql_buf, "\"%s\".%s %s %s", class, left, op, val );
1270         free(val);
1271         free(op);
1272
1273         return buffer_release(sql_buf);
1274 }
1275
1276 static char* searchBETWEENPredicate (const char* class, osrfHash* field, jsonObject* node) {
1277
1278         char* x_string;
1279         char* y_string;
1280
1281         if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1282                 x_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,0));
1283                 y_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,1));
1284
1285         } else {
1286                 x_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,0));
1287                 y_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,1));
1288                 if ( !(dbi_conn_quote_string(dbhandle, &x_string) && dbi_conn_quote_string(dbhandle, &y_string)) ) {
1289                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key strings [%s] and [%s]", MODULENAME, x_string, y_string);
1290                         free(x_string);
1291                         free(y_string);
1292                         return NULL;
1293                 }
1294         }
1295
1296         growing_buffer* sql_buf = buffer_init(32);
1297         buffer_fadd( sql_buf, "%s BETWEEN %s AND %s", osrfHashGet(field, "name"), x_string, y_string );
1298         free(x_string);
1299         free(y_string);
1300
1301         return buffer_release(sql_buf);
1302 }
1303
1304 static char* searchPredicate ( const char* class, osrfHash* field, jsonObject* node ) {
1305
1306         char* pred = NULL;
1307         if (node->type == JSON_ARRAY) { // equality IN search
1308                 pred = searchINPredicate( class, field, node, NULL );
1309         } else if (node->type == JSON_HASH) { // non-equality search
1310                 jsonObjectNode* pred_node;
1311                 jsonObjectIterator* pred_itr = jsonNewObjectIterator( node );
1312                 while ( (pred_node = jsonObjectIteratorNext( pred_itr )) ) {
1313                         if ( !(strcasecmp( pred_node->key,"between" )) )
1314                                 pred = searchBETWEENPredicate( class, field, pred_node->item );
1315                         else if ( !(strcasecmp( pred_node->key,"in" )) || !(strcasecmp( pred_node->key,"not in" )) )
1316                                 pred = searchINPredicate( class, field, pred_node->item, pred_node->key );
1317                         else if ( pred_node->item->type == JSON_ARRAY )
1318                                 pred = searchFunctionPredicate( class, field, pred_node );
1319                         else if ( pred_node->item->type == JSON_HASH )
1320                                 pred = searchFieldTransformPredicate( class, field, pred_node );
1321                         else 
1322                                 pred = searchSimplePredicate( pred_node->key, class, field, pred_node->item );
1323
1324                         break;
1325                 }
1326         } else if (node->type == JSON_NULL) { // IS NULL search
1327                 growing_buffer* _p = buffer_init(64);
1328                 buffer_fadd(
1329                         _p,
1330                         "\"%s\".%s IS NULL",
1331                         class,
1332                         osrfHashGet(field, "name")
1333                 );
1334                 pred = buffer_release(_p);
1335         } else { // equality search
1336                 pred = searchSimplePredicate( "=", class, field, node );
1337         }
1338
1339         return pred;
1340
1341 }
1342
1343
1344 /*
1345
1346 join : {
1347         acn : {
1348                 field : record,
1349                 fkey : id
1350                 type : left
1351                 filter_op : or
1352                 filter : { ... },
1353                 join : {
1354                         acp : {
1355                                 field : call_number,
1356                                 fkey : id,
1357                                 filter : { ... },
1358                         },
1359                 },
1360         },
1361         mrd : {
1362                 field : record,
1363                 type : inner
1364                 fkey : id,
1365                 filter : { ... },
1366         }
1367 }
1368
1369 */
1370
1371 static char* searchJOIN ( const jsonObject* join_hash, osrfHash* leftmeta ) {
1372
1373         const jsonObject* working_hash;
1374         jsonObject* freeable_hash = NULL;
1375
1376         if (join_hash->type == JSON_STRING) {
1377                 // create a wrapper around a copy of the original
1378                 char* _tmp = jsonObjectToSimpleString( join_hash );
1379                 freeable_hash = jsonParseString("{}");
1380                 jsonObjectSetKey(freeable_hash, _tmp, NULL);
1381                 free(_tmp);
1382                 working_hash = freeable_hash;
1383         }
1384         else
1385                 working_hash = join_hash;
1386
1387         growing_buffer* join_buf = buffer_init(128);
1388         char* leftclass = osrfHashGet(leftmeta, "classname");
1389
1390         jsonObjectNode* snode = NULL;
1391         jsonObjectIterator* search_itr = jsonNewObjectIterator( working_hash );
1392         if(freeable_hash)
1393                 jsonObjectFree(freeable_hash);
1394         
1395         while ( (snode = jsonObjectIteratorNext( search_itr )) ) {
1396                 osrfHash* idlClass = osrfHashGet( oilsIDL(), snode->key );
1397
1398                 char* class = osrfHashGet(idlClass, "classname");
1399
1400                 char* table = getSourceDefinition(idlClass);
1401                 char* type = jsonObjectToSimpleString( jsonObjectGetKeyConst( snode->item, "type" ) );
1402                 char* filter_op = jsonObjectToSimpleString( jsonObjectGetKeyConst( snode->item, "filter_op" ) );
1403                 char* fkey = jsonObjectToSimpleString( jsonObjectGetKeyConst( snode->item, "fkey" ) );
1404                 char* field = jsonObjectToSimpleString( jsonObjectGetKeyConst( snode->item, "field" ) );
1405
1406                 const jsonObject* filter = jsonObjectGetKeyConst( snode->item, "filter" );
1407                 const jsonObject* join_filter = jsonObjectGetKeyConst( snode->item, "join" );
1408
1409                 if (field && !fkey) {
1410                         fkey = (char*)oilsIDLFindPath("/%s/links/%s/key", class, field);
1411                         if (!fkey) {
1412                                 osrfLogError(
1413                                         OSRF_LOG_MARK,
1414                                         "%s: JOIN failed.  No link defined from %s.%s to %s",
1415                                         MODULENAME,
1416                                         class,
1417                                         field,
1418                                         leftclass
1419                                 );
1420                                 buffer_free(join_buf);
1421                                 return NULL;
1422                         }
1423                         fkey = strdup( fkey );
1424
1425                 } else if (!field && fkey) {
1426                         field = (char*)oilsIDLFindPath("/%s/links/%s/key", leftclass, fkey );
1427                         if (!field) {
1428                                 osrfLogError(
1429                                         OSRF_LOG_MARK,
1430                                         "%s: JOIN failed.  No link defined from %s.%s to %s",
1431                                         MODULENAME,
1432                                         leftclass,
1433                                         fkey,
1434                                         class
1435                                 );
1436                                 buffer_free(join_buf);
1437                                 return NULL;
1438                         }
1439                         field = strdup( field );
1440
1441                 } else if (!field && !fkey) {
1442                         osrfHash* _links = oilsIDLFindPath("/%s/links", leftclass);
1443
1444                         int i = 0;
1445                         osrfStringArray* keys = osrfHashKeys( _links );
1446                         while ( (fkey = osrfStringArrayGetString(keys, i++)) ) {
1447                                 fkey = strdup(osrfStringArrayGetString(keys, i++));
1448                                 if ( !strcmp( (char*)oilsIDLFindPath("/%s/links/%s/class", leftclass, fkey), class) ) {
1449                                         field = strdup( (char*)oilsIDLFindPath("/%s/links/%s/key", leftclass, fkey) );
1450                                         break;
1451                                 } else {
1452                                         free(fkey);
1453                                 }
1454                         }
1455                         osrfStringArrayFree(keys);
1456
1457                         if (!field && !fkey) {
1458                                 _links = oilsIDLFindPath("/%s/links", class);
1459
1460                                 i = 0;
1461                                 keys = osrfHashKeys( _links );
1462                                 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
1463                                         field = strdup(osrfStringArrayGetString(keys, i++));
1464                                         if ( !strcmp( (char*)oilsIDLFindPath("/%s/links/%s/class", class, field), class) ) {
1465                                                 fkey = strdup( (char*)oilsIDLFindPath("/%s/links/%s/key", class, field) );
1466                                                 break;
1467                                         } else {
1468                                                 free(field);
1469                                         }
1470                                 }
1471                                 osrfStringArrayFree(keys);
1472                         }
1473
1474                         if (!field && !fkey) {
1475                                 osrfLogError(
1476                                         OSRF_LOG_MARK,
1477                                         "%s: JOIN failed.  No link defined between %s and %s",
1478                                         MODULENAME,
1479                                         leftclass,
1480                                         class
1481                                 );
1482                                 buffer_free(join_buf);
1483                                 return NULL;
1484                         }
1485
1486                 }
1487
1488                 if (type) {
1489                         if ( !strcasecmp(type,"left") ) {
1490                                 buffer_add(join_buf, " LEFT JOIN");
1491                         } else if ( !strcasecmp(type,"right") ) {
1492                                 buffer_add(join_buf, " RIGHT JOIN");
1493                         } else if ( !strcasecmp(type,"full") ) {
1494                                 buffer_add(join_buf, " FULL JOIN");
1495                         } else {
1496                                 buffer_add(join_buf, " INNER JOIN");
1497                         }
1498                 } else {
1499                         buffer_add(join_buf, " INNER JOIN");
1500                 }
1501
1502                 buffer_fadd(join_buf, " %s AS \"%s\" ON ( \"%s\".%s = \"%s\".%s", table, class, class, field, leftclass, fkey);
1503                 free(table);
1504
1505                 if (filter) {
1506                         if (filter_op) {
1507                                 if (!strcasecmp("or",filter_op)) {
1508                                         buffer_add( join_buf, " OR " );
1509                                 } else {
1510                                         buffer_add( join_buf, " AND " );
1511                                 }
1512                         } else {
1513                                 buffer_add( join_buf, " AND " );
1514                         }
1515
1516                         char* jpred = searchWHERE( filter, idlClass, AND_OP_JOIN );
1517                         buffer_fadd( join_buf, " %s", jpred );
1518                         free(jpred);
1519                 }
1520
1521                 buffer_add(join_buf, " ) ");
1522                 
1523                 if (join_filter) {
1524                         char* jpred = searchJOIN( join_filter, idlClass );
1525                         buffer_fadd( join_buf, " %s", jpred );
1526                         free(jpred);
1527                 }
1528
1529                 free(type);
1530                 free(filter_op);
1531                 free(fkey);
1532                 free(field);
1533         }
1534
1535         return buffer_release(join_buf);
1536 }
1537
1538 /*
1539
1540 { +class : { -or|-and : { field : { op : value }, ... } ... }, ... }
1541 { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }
1542 [ { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }, ... ]
1543
1544 */
1545 static char* searchWHERE ( const jsonObject* search_hash, osrfHash* meta, int opjoin_type ) {
1546
1547         growing_buffer* sql_buf = buffer_init(128);
1548
1549         jsonObjectNode* node = NULL;
1550
1551     int first = 1;
1552     if ( search_hash->type == JSON_ARRAY ) {
1553         jsonObjectIterator* search_itr = jsonNewObjectIterator( search_hash );
1554         while ( (node = jsonObjectIteratorNext( search_itr )) ) {
1555             if (first) {
1556                 first = 0;
1557             } else {
1558                 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
1559                 else buffer_add(sql_buf, " AND ");
1560             }
1561
1562             char* subpred = searchWHERE( node->item, meta, opjoin_type );
1563             buffer_fadd(sql_buf, "( %s )", subpred);
1564             free(subpred);
1565         }
1566         jsonObjectIteratorFree(search_itr);
1567
1568     } else if ( search_hash->type == JSON_HASH ) {
1569         jsonObjectIterator* search_itr = jsonNewObjectIterator( search_hash );
1570         while ( (node = jsonObjectIteratorNext( search_itr )) ) {
1571
1572             if (first) {
1573                 first = 0;
1574             } else {
1575                 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
1576                 else buffer_add(sql_buf, " AND ");
1577             }
1578
1579             if ( !strncmp("+",node->key,1) ) {
1580                 if ( node->item->type == JSON_STRING ) {
1581                     char* subpred = jsonObjectToSimpleString( node->item );
1582                     buffer_fadd(sql_buf, " \"%s\".%s ", node->key + 1, subpred);
1583                     free(subpred);
1584                 } else {
1585                     char* subpred = searchWHERE( node->item, osrfHashGet( oilsIDL(), node->key + 1 ), AND_OP_JOIN );
1586                     buffer_fadd(sql_buf, "( %s )", subpred);
1587                     free(subpred);
1588                 }
1589             } else if ( !strcasecmp("-or",node->key) ) {
1590                 char* subpred = searchWHERE( node->item, meta, OR_OP_JOIN );
1591                 buffer_fadd(sql_buf, "( %s )", subpred);
1592                 free(subpred);
1593             } else if ( !strcasecmp("-and",node->key) ) {
1594                 char* subpred = searchWHERE( node->item, meta, AND_OP_JOIN );
1595                 buffer_fadd(sql_buf, "( %s )", subpred);
1596                 free(subpred);
1597             } else {
1598
1599                 char* class = osrfHashGet(meta, "classname");
1600                 osrfHash* fields = osrfHashGet(meta, "fields");
1601                 osrfHash* field = osrfHashGet( fields, node->key );
1602
1603
1604                 if (!field) {
1605                     char* table = getSourceDefinition(meta);
1606                     osrfLogError(
1607                         OSRF_LOG_MARK,
1608                         "%s: Attempt to reference non-existant column %s on %s (%s)",
1609                         MODULENAME,
1610                         node->key,
1611                         table,
1612                         class
1613                     );
1614                     buffer_free(sql_buf);
1615                     free(table);
1616                     return NULL;
1617                 }
1618
1619                 char* subpred = searchPredicate( class, field, node->item );
1620                 buffer_add( sql_buf, subpred );
1621                 free(subpred);
1622             }
1623         }
1624             jsonObjectIteratorFree(search_itr);
1625
1626     } else {
1627         // ERROR ... only hash and array allowed at this level
1628         char* predicate_string = jsonObjectToJSON( search_hash );
1629         osrfLogError(
1630             OSRF_LOG_MARK,
1631             "%s: Invalid predicate structure: %s",
1632             MODULENAME,
1633             predicate_string
1634         );
1635         buffer_free(sql_buf);
1636         free(predicate_string);
1637         return NULL;
1638     }
1639
1640
1641         return buffer_release(sql_buf);
1642 }
1643
1644 static char* SELECT (
1645                 /* method context */ osrfMethodContext* ctx,
1646                 
1647                 /* SELECT   */ jsonObject* selhash,
1648                 /* FROM     */ jsonObject* join_hash,
1649                 /* WHERE    */ jsonObject* search_hash,
1650                 /* HAVING   */ jsonObject* having_hash,
1651                 /* ORDER BY */ jsonObject* order_hash,
1652                 /* LIMIT    */ jsonObject* limit,
1653                 /* OFFSET   */ jsonObject* offset,
1654                 /* flags    */ int flags
1655 ) {
1656         const char* locale = osrf_message_get_last_locale();
1657
1658         // in case we don't get a select list
1659         jsonObject* defaultselhash = NULL;
1660
1661         // general tmp objects
1662         const jsonObject* tmp_const;
1663         jsonObject* _tmp = NULL;
1664         jsonObjectNode* selclass = NULL;
1665         jsonObjectNode* selfield = NULL;
1666         jsonObjectNode* snode = NULL;
1667         jsonObjectNode* onode = NULL;
1668         jsonObject* found = NULL;
1669
1670         char* string = NULL;
1671         int first = 1;
1672         int gfirst = 1;
1673         //int hfirst = 1;
1674
1675         // the core search class
1676         char* core_class = NULL;
1677
1678         // metadata about the core search class
1679         osrfHash* core_meta = NULL;
1680         osrfHash* core_fields = NULL;
1681         osrfHash* idlClass = NULL;
1682
1683         // punt if there's no core class
1684         if (!join_hash || ( join_hash->type == JSON_HASH && !join_hash->size ))
1685                 return NULL;
1686
1687         // get the core class -- the only key of the top level FROM clause, or a string
1688         if (join_hash->type == JSON_HASH) {
1689                 jsonObjectIterator* tmp_itr = jsonNewObjectIterator( join_hash );
1690                 snode = jsonObjectIteratorNext( tmp_itr );
1691                 
1692                 core_class = strdup( snode->key );
1693                 join_hash = snode->item;
1694
1695                 jsonObjectIteratorFree( tmp_itr );
1696                 snode = NULL;
1697
1698         } else if (join_hash->type == JSON_STRING) {
1699                 core_class = jsonObjectToSimpleString( join_hash );
1700                 join_hash = NULL;
1701         }
1702
1703         // punt if we don't know about the core class
1704         if (!(core_meta = osrfHashGet( oilsIDL(), core_class )))
1705                 return NULL;
1706
1707         // if the select list is empty, or the core class field list is '*',
1708         // build the default select list ...
1709         if (!selhash) {
1710                 selhash = defaultselhash = jsonParseString( "{}" );
1711                 jsonObjectSetKey( selhash, core_class, jsonParseString( "[]" ) );
1712         } else if ( (tmp_const = jsonObjectGetKeyConst( selhash, core_class )) && tmp_const->type == JSON_STRING ) {
1713                 char* _x = jsonObjectToSimpleString( tmp_const );
1714                 if (!strncmp( "*", _x, 1 )) {
1715                         jsonObjectRemoveKey( selhash, core_class );
1716                         jsonObjectSetKey( selhash, core_class, jsonParseString( "[]" ) );
1717                 }
1718                 free(_x);
1719         }
1720
1721         // the query buffer
1722         growing_buffer* sql_buf = buffer_init(128);
1723
1724         // temp buffer for the SELECT list
1725         growing_buffer* select_buf = buffer_init(128);
1726         growing_buffer* order_buf = buffer_init(128);
1727         growing_buffer* group_buf = buffer_init(128);
1728         growing_buffer* having_buf = buffer_init(128);
1729
1730         core_fields = osrfHashGet(core_meta, "fields");
1731
1732         // ... and if we /are/ building the default list, do that
1733         if ( (_tmp = jsonObjectGetKey(selhash,core_class)) && !_tmp->size ) {
1734                 
1735                 int i = 0;
1736                 char* field;
1737
1738                 osrfStringArray* keys = osrfHashKeys( core_fields );
1739                 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
1740                         if ( strncasecmp( "true", osrfHashGet( osrfHashGet( core_fields, field ), "virtual" ), 4 ) )
1741                                 jsonObjectPush( _tmp, jsonNewObject( field ) );
1742                 }
1743                 osrfStringArrayFree(keys);
1744         }
1745
1746         // Now we build the actual select list
1747         int sel_pos = 1;
1748         jsonObject* is_agg = jsonObjectFindPath(selhash, "//aggregate");
1749         first = 1;
1750         gfirst = 1;
1751         jsonObjectIterator* selclass_itr = jsonNewObjectIterator( selhash );
1752         while ( (selclass = jsonObjectIteratorNext( selclass_itr )) ) {
1753
1754                 // round trip through the idl, just to be safe
1755                 idlClass = osrfHashGet( oilsIDL(), selclass->key );
1756                 if (!idlClass) continue;
1757                 char* cname = osrfHashGet(idlClass, "classname");
1758
1759                 // make sure the target relation is in the join tree
1760                 if (strcmp(core_class,cname)) {
1761                         if (!join_hash) continue;
1762
1763                         if (join_hash->type == JSON_STRING) {
1764                                 string = jsonObjectToSimpleString(join_hash);
1765                                 found = strcmp(string,cname) ? NULL : jsonParseString("{\"1\":\"1\"}");
1766                                 free(string);
1767                         } else {
1768                                 found = jsonObjectFindPath(join_hash, "//%s", cname);
1769                         }
1770
1771                         if (!found->size) {
1772                                 jsonObjectFree(found);
1773                                 continue;
1774                         }
1775
1776                         jsonObjectFree(found);
1777                 }
1778
1779                 // stitch together the column list ...
1780                 jsonObjectIterator* select_itr = jsonNewObjectIterator( selclass->item );
1781                 while ( (selfield = jsonObjectIteratorNext( select_itr )) ) {
1782
1783                         char* __column = NULL;
1784                         char* __alias = NULL;
1785
1786                         // ... if it's a sstring, just toss it on the pile
1787                         if (selfield->item->type == JSON_STRING) {
1788
1789                                 // again, just to be safe
1790                                 char* _requested_col = jsonObjectToSimpleString(selfield->item);
1791                                 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), _requested_col );
1792                                 free(_requested_col);
1793
1794                                 if (!field) continue;
1795                                 __column = strdup(osrfHashGet(field, "name"));
1796
1797                                 if (first) {
1798                                         first = 0;
1799                                 } else {
1800                                         buffer_add(select_buf, ",");
1801                                 }
1802
1803                 if (locale) {
1804                         char* i18n = osrfHashGet(field, "i18n");
1805                                 if (flags & DISABLE_I18N)
1806                         i18n = NULL;
1807
1808                         if ( i18n && !strncasecmp("true", i18n, 4)) {
1809                             char* pkey = osrfHashGet(idlClass, "primarykey");
1810                             char* tname = osrfHashGet(idlClass, "tablename");
1811
1812                             buffer_fadd(select_buf, " oils_i18n_xlate('%s.%s', \"%s\".%s::TEXT, '%s') AS \"%s\"", tname, __column, cname, pkey, locale, __column);
1813                     } else {
1814                                         buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, __column, __column);
1815                     }
1816                 } else {
1817                                     buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, __column, __column);
1818                 }
1819
1820                         // ... but it could be an object, in which case we check for a Field Transform
1821                         } else {
1822
1823                                 __column = jsonObjectToSimpleString( jsonObjectGetKeyConst( selfield->item, "column" ) );
1824
1825                                 // again, just to be safe
1826                                 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), __column );
1827                                 if (!field) continue;
1828                                 const char* fname = osrfHashGet(field, "name");
1829
1830                                 if (first) {
1831                                         first = 0;
1832                                 } else {
1833                                         buffer_add(select_buf, ",");
1834                                 }
1835
1836                                 if ((tmp_const = jsonObjectGetKeyConst( selfield->item, "alias" ))) {
1837                                         __alias = jsonObjectToSimpleString( tmp_const );
1838                                 } else {
1839                                         __alias = strdup(__column);
1840                                 }
1841
1842                                 if (jsonObjectGetKeyConst( selfield->item, "transform" )) {
1843                                         free(__column);
1844                                         __column = searchFieldTransform(cname, field, selfield->item);
1845                                         buffer_fadd(select_buf, " %s AS \"%s\"", __column, __alias);
1846                                 } else {
1847                     if (locale) {
1848                                 char* i18n = osrfHashGet(field, "i18n");
1849                                     if (flags & DISABLE_I18N)
1850                             i18n = NULL;
1851
1852                                 if ( i18n && !strncasecmp("true", i18n, 4)) {
1853                                 char* pkey = osrfHashGet(idlClass, "primarykey");
1854                                 char* tname = osrfHashGet(idlClass, "tablename");
1855
1856                                 buffer_fadd(select_buf, " oils_i18n_xlate('%s.%s', \"%s\".%s::TEXT, '%s') AS \"%s\"", tname, fname, cname, pkey, locale, __alias);
1857                         } else {
1858                                                 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, fname, __alias);
1859                         }
1860                     } else {
1861                                             buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, fname, __alias);
1862                     }
1863                                 }
1864                         }
1865
1866                         if (is_agg->size || (flags & SELECT_DISTINCT)) {
1867
1868                                 if (!jsonBoolIsTrue( jsonObjectGetKey( selfield->item, "aggregate" ) )) {
1869                                         if (gfirst) {
1870                                                 gfirst = 0;
1871                                         } else {
1872                                                 buffer_add(group_buf, ",");
1873                                         }
1874
1875                                         buffer_fadd(group_buf, " %d", sel_pos);
1876                                 /*
1877                                 } else if (is_agg = jsonObjectGetKey( selfield->item, "having" )) {
1878                                         if (gfirst) {
1879                                                 gfirst = 0;
1880                                         } else {
1881                                                 buffer_add(group_buf, ",");
1882                                         }
1883
1884                                         __column = searchFieldTransform(cname, field, selfield->item);
1885                                         buffer_fadd(group_buf, " %s", __column);
1886                                         __column = searchFieldTransform(cname, field, selfield->item);
1887                                 */
1888                                 }
1889                         }
1890
1891                         if (__column) free(__column);
1892                         if (__alias) free(__alias);
1893
1894                         sel_pos++;
1895                 }
1896         }
1897
1898         if (is_agg) jsonObjectFree(is_agg);
1899
1900         char* col_list = buffer_release(select_buf);
1901         char* table = getSourceDefinition(core_meta);
1902
1903         // Put it all together
1904         buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\" ", col_list, table, core_class );
1905         free(col_list);
1906         free(table);
1907
1908         // Now, walk the join tree and add that clause
1909         if ( join_hash ) {
1910                 char* join_clause = searchJOIN( join_hash, core_meta );
1911                 buffer_add(sql_buf, join_clause);
1912                 free(join_clause);
1913         }
1914
1915         if ( search_hash ) {
1916                 buffer_add(sql_buf, " WHERE ");
1917
1918                 // and it's on the the WHERE clause
1919                 char* pred = searchWHERE( search_hash, core_meta, AND_OP_JOIN );
1920
1921                 if (!pred) {
1922                         osrfAppSessionStatus(
1923                                 ctx->session,
1924                                 OSRF_STATUS_INTERNALSERVERERROR,
1925                                 "osrfMethodException",
1926                                 ctx->request,
1927                                 "Severe query error in WHERE predicate -- see error log for more details"
1928                         );
1929                         free(core_class);
1930                         buffer_free(having_buf);
1931                         buffer_free(group_buf);
1932                         buffer_free(order_buf);
1933                         buffer_free(sql_buf);
1934                         if (defaultselhash) jsonObjectFree(defaultselhash);
1935                         return NULL;
1936                 } else {
1937                         buffer_add(sql_buf, pred);
1938                         free(pred);
1939                 }
1940     }
1941
1942         if ( having_hash ) {
1943                 buffer_add(sql_buf, " HAVING ");
1944
1945                 // and it's on the the WHERE clause
1946                 char* pred = searchWHERE( having_hash, core_meta, AND_OP_JOIN );
1947
1948                 if (!pred) {
1949                         osrfAppSessionStatus(
1950                                 ctx->session,
1951                                 OSRF_STATUS_INTERNALSERVERERROR,
1952                                 "osrfMethodException",
1953                                 ctx->request,
1954                                 "Severe query error in HAVING predicate -- see error log for more details"
1955                         );
1956                         free(core_class);
1957                         buffer_free(having_buf);
1958                         buffer_free(group_buf);
1959                         buffer_free(order_buf);
1960                         buffer_free(sql_buf);
1961                         if (defaultselhash) jsonObjectFree(defaultselhash);
1962                         return NULL;
1963                 } else {
1964                         buffer_add(sql_buf, pred);
1965                         free(pred);
1966                 }
1967         }
1968
1969         first = 1;
1970         jsonObjectIterator* class_itr = jsonNewObjectIterator( order_hash );
1971         while ( (snode = jsonObjectIteratorNext( class_itr )) ) {
1972
1973                 if (!jsonObjectGetKeyConst(selhash,snode->key))
1974                         continue;
1975
1976                 if ( snode->item->type == JSON_HASH ) {
1977
1978                         jsonObjectIterator* order_itr = jsonNewObjectIterator( snode->item );
1979                         while ( (onode = jsonObjectIteratorNext( order_itr )) ) {
1980
1981                                 if (!oilsIDLFindPath( "/%s/fields/%s", snode->key, onode->key ))
1982                                         continue;
1983
1984                                 char* direction = NULL;
1985                                 if ( onode->item->type == JSON_HASH ) {
1986                                         if ( jsonObjectGetKeyConst( onode->item, "transform" ) ) {
1987                                                 string = searchFieldTransform(
1988                                                         snode->key,
1989                                                         oilsIDLFindPath( "/%s/fields/%s", snode->key, onode->key ),
1990                                                         onode->item
1991                                                 );
1992                                         } else {
1993                                                 growing_buffer* field_buf = buffer_init(16);
1994                                                 buffer_fadd(field_buf, "\"%s\".%s", snode->key, onode->key);
1995                                                 string = buffer_release(field_buf);
1996                                         }
1997
1998                                         if ( (tmp_const = jsonObjectGetKeyConst( onode->item, "direction" )) ) {
1999                                                 direction = jsonObjectToSimpleString(tmp_const);
2000                                                 if (!strncasecmp(direction, "d", 1)) {
2001                                                         free(direction);
2002                                                         direction = " DESC";
2003                                                 } else {
2004                                                         free(direction);
2005                                                         direction = " ASC";
2006                                                 }
2007                                         }
2008
2009                                 } else {
2010                                         string = strdup(onode->key);
2011                                         direction = jsonObjectToSimpleString(onode->item);
2012                                         if (!strncasecmp(direction, "d", 1)) {
2013                                                 free(direction);
2014                                                 direction = " DESC";
2015                                         } else {
2016                                                 free(direction);
2017                                                 direction = " ASC";
2018                                         }
2019                                 }
2020
2021                                 if (first) {
2022                                         first = 0;
2023                                 } else {
2024                                         buffer_add(order_buf, ", ");
2025                                 }
2026
2027                                 buffer_add(order_buf, string);
2028                                 free(string);
2029
2030                                 if (direction) {
2031                                         buffer_add(order_buf, direction);
2032                                 }
2033
2034                         }
2035
2036                 } else if ( snode->item->type == JSON_ARRAY ) {
2037
2038                         jsonObjectIterator* order_itr = jsonNewObjectIterator( snode->item );
2039                         while ( (onode = jsonObjectIteratorNext( order_itr )) ) {
2040
2041                                 char* _f = jsonObjectToSimpleString( onode->item );
2042
2043                                 if (!oilsIDLFindPath( "/%s/fields/%s", snode->key, _f))
2044                                         continue;
2045
2046                                 if (first) {
2047                                         first = 0;
2048                                 } else {
2049                                         buffer_add(order_buf, ", ");
2050                                 }
2051
2052                                 buffer_add(order_buf, _f);
2053                                 free(_f);
2054
2055                         }
2056
2057                 // IT'S THE OOOOOOOOOOOLD STYLE!
2058                 } else {
2059                         osrfLogError(OSRF_LOG_MARK, "%s: Possible SQL injection attempt; direct order by is not allowed", MODULENAME);
2060                         osrfAppSessionStatus(
2061                                 ctx->session,
2062                                 OSRF_STATUS_INTERNALSERVERERROR,
2063                                 "osrfMethodException",
2064                                 ctx->request,
2065                                 "Severe query error -- see error log for more details"
2066                         );
2067
2068                         free(core_class);
2069                         buffer_free(having_buf);
2070                         buffer_free(group_buf);
2071                         buffer_free(order_buf);
2072                         buffer_free(sql_buf);
2073                         if (defaultselhash) jsonObjectFree(defaultselhash);
2074                         return NULL;
2075                 }
2076
2077         }
2078
2079         string = buffer_release(group_buf);
2080
2081         if (strlen(string)) {
2082                 buffer_fadd(
2083                         sql_buf,
2084                         " GROUP BY %s",
2085                         string
2086                 );
2087         }
2088
2089         free(string);
2090
2091         string = buffer_release(having_buf);
2092  
2093         if (strlen(string)) {
2094                 buffer_fadd(
2095                         sql_buf,
2096                         " HAVING %s",
2097                         string
2098                 );
2099         }
2100
2101         free(string);
2102
2103         string = buffer_release(order_buf);
2104
2105         if (strlen(string)) {
2106                 buffer_fadd(
2107                         sql_buf,
2108                         " ORDER BY %s",
2109                         string
2110                 );
2111         }
2112
2113         free(string);
2114
2115         if ( limit ){
2116                 string = jsonObjectToSimpleString(limit);
2117                 buffer_fadd( sql_buf, " LIMIT %d", atoi(string) );
2118                 free(string);
2119         }
2120
2121         if (offset) {
2122                 string = jsonObjectToSimpleString(offset);
2123                 buffer_fadd( sql_buf, " OFFSET %d", atoi(string) );
2124                 free(string);
2125         }
2126
2127         buffer_add(sql_buf, ";");
2128
2129         free(core_class);
2130         if (defaultselhash) jsonObjectFree(defaultselhash);
2131
2132         return buffer_release(sql_buf);
2133
2134 }
2135
2136 static char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
2137
2138         const char* locale = osrf_message_get_last_locale();
2139
2140         osrfHash* fields = osrfHashGet(meta, "fields");
2141         char* core_class = osrfHashGet(meta, "classname");
2142
2143         const jsonObject* join_hash = jsonObjectGetKeyConst( order_hash, "join" );
2144
2145         jsonObjectNode* node = NULL;
2146         jsonObjectNode* snode = NULL;
2147         jsonObjectNode* onode = NULL;
2148         const jsonObject* _tmp = NULL;
2149         jsonObject* selhash = NULL;
2150         jsonObject* defaultselhash = NULL;
2151
2152         growing_buffer* sql_buf = buffer_init(128);
2153         growing_buffer* select_buf = buffer_init(128);
2154
2155         if ( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
2156                 defaultselhash = jsonParseString( "{}" );
2157                 selhash = defaultselhash;
2158         }
2159         
2160         if ( !jsonObjectGetKeyConst(selhash,core_class) ) {
2161                 jsonObjectSetKey( selhash, core_class, jsonParseString( "[]" ) );
2162                 jsonObject* flist = jsonObjectGetKey( selhash, core_class );
2163                 
2164                 int i = 0;
2165                 char* field;
2166
2167                 osrfStringArray* keys = osrfHashKeys( fields );
2168                 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
2169                         if ( strcasecmp( "true", osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
2170                                 jsonObjectPush( flist, jsonNewObject( field ) );
2171                 }
2172                 osrfStringArrayFree(keys);
2173         }
2174
2175         int first = 1;
2176         jsonObjectIterator* class_itr = jsonNewObjectIterator( selhash );
2177         while ( (snode = jsonObjectIteratorNext( class_itr )) ) {
2178
2179                 osrfHash* idlClass = osrfHashGet( oilsIDL(), snode->key );
2180                 if (!idlClass) continue;
2181                 char* cname = osrfHashGet(idlClass, "classname");
2182
2183                 if (strcmp(core_class,snode->key)) {
2184                         if (!join_hash) continue;
2185
2186                         jsonObject* found =  jsonObjectFindPath(join_hash, "//%s", snode->key);
2187                         if (!found->size) {
2188                                 jsonObjectFree(found);
2189                                 continue;
2190                         }
2191
2192                         jsonObjectFree(found);
2193                 }
2194
2195                 jsonObjectIterator* select_itr = jsonNewObjectIterator( snode->item );
2196                 while ( (node = jsonObjectIteratorNext( select_itr )) ) {
2197                         osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), jsonObjectToSimpleString(node->item) );
2198                         char* fname = osrfHashGet(field, "name");
2199
2200                         if (!field) continue;
2201
2202                         if (first) {
2203                                 first = 0;
2204                         } else {
2205                                 buffer_add(select_buf, ",");
2206                         }
2207
2208             if (locale) {
2209                         char* i18n = osrfHashGet(field, "i18n");
2210                     if (jsonBoolIsTrue(jsonObjectGetKey( order_hash, "no_i18n" )))
2211                     i18n = NULL;
2212
2213                         if ( i18n && !strncasecmp("true", i18n, 4)) {
2214                         char* pkey = osrfHashGet(idlClass, "primarykey");
2215                         char* tname = osrfHashGet(idlClass, "tablename");
2216
2217                             buffer_fadd(select_buf, " oils_i18n_xlate('%s.%s', \"%s\".%s::TEXT, '%s') AS \"%s\"", tname, fname, cname, pkey, locale, fname);
2218                 } else {
2219                                 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
2220                 }
2221             } else {
2222                             buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
2223             }
2224                 }
2225         }
2226
2227         char* col_list = buffer_release(select_buf);
2228         char* table = getSourceDefinition(meta);
2229
2230         buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\"", col_list, table, core_class );
2231         free(col_list);
2232         free(table);
2233
2234         if ( join_hash ) {
2235                 char* join_clause = searchJOIN( join_hash, meta );
2236                 buffer_fadd(sql_buf, " %s", join_clause);
2237                 free(join_clause);
2238         }
2239
2240         char* tmpsql = buffer_data(sql_buf);
2241         osrfLogDebug(OSRF_LOG_MARK, "%s pre-predicate SQL =  %s", MODULENAME, tmpsql);
2242         free(tmpsql);
2243
2244         buffer_add(sql_buf, " WHERE ");
2245
2246         char* pred = searchWHERE( search_hash, meta, AND_OP_JOIN );
2247         if (!pred) {
2248                 osrfAppSessionStatus(
2249                         ctx->session,
2250                         OSRF_STATUS_INTERNALSERVERERROR,
2251                                 "osrfMethodException",
2252                                 ctx->request,
2253                                 "Severe query error -- see error log for more details"
2254                         );
2255                 buffer_free(sql_buf);
2256                 return NULL;
2257         } else {
2258                 buffer_add(sql_buf, pred);
2259                 free(pred);
2260         }
2261
2262         if (order_hash) {
2263                 char* string = NULL;
2264                 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "order_by" )) ){
2265
2266                         growing_buffer* order_buf = buffer_init(128);
2267
2268                         first = 1;
2269                         jsonObjectIterator* class_itr = jsonNewObjectIterator( _tmp );
2270                         while ( (snode = jsonObjectIteratorNext( class_itr )) ) {
2271
2272                                 if (!jsonObjectGetKeyConst(selhash,snode->key))
2273                                         continue;
2274
2275                                 if ( snode->item->type == JSON_HASH ) {
2276
2277                                         jsonObjectIterator* order_itr = jsonNewObjectIterator( snode->item );
2278                                         while ( (onode = jsonObjectIteratorNext( order_itr )) ) {
2279
2280                                                 if (!oilsIDLFindPath( "/%s/fields/%s", snode->key, onode->key ))
2281                                                         continue;
2282
2283                                                 char* direction = NULL;
2284                                                 if ( onode->item->type == JSON_HASH ) {
2285                                                         if ( jsonObjectGetKeyConst( onode->item, "transform" ) ) {
2286                                                                 string = searchFieldTransform(
2287                                                                         snode->key,
2288                                                                         oilsIDLFindPath( "/%s/fields/%s", snode->key, onode->key ),
2289                                                                         onode->item
2290                                                                 );
2291                                                         } else {
2292                                                                 growing_buffer* field_buf = buffer_init(16);
2293                                                                 buffer_fadd(field_buf, "\"%s\".%s", snode->key, onode->key);
2294                                                                 string = buffer_release(field_buf);
2295                                                         }
2296
2297                                                         if ( (_tmp = jsonObjectGetKeyConst( onode->item, "direction" )) ) {
2298                                                                 direction = jsonObjectToSimpleString(_tmp);
2299                                                                 if (!strncasecmp(direction, "d", 1)) {
2300                                                                         free(direction);
2301                                                                         direction = " DESC";
2302                                                                 } else {
2303                                                                         free(direction);
2304                                                                         direction = " ASC";
2305                                                                 }
2306                                                         }
2307
2308                                                 } else {
2309                                                         string = strdup(onode->key);
2310                                                         direction = jsonObjectToSimpleString(onode->item);
2311                                                         if (!strncasecmp(direction, "d", 1)) {
2312                                                                 free(direction);
2313                                                                 direction = " DESC";
2314                                                         } else {
2315                                                                 free(direction);
2316                                                                 direction = " ASC";
2317                                                         }
2318                                                 }
2319
2320                                                 if (first) {
2321                                                         first = 0;
2322                                                 } else {
2323                                                         buffer_add(order_buf, ", ");
2324                                                 }
2325
2326                                                 buffer_add(order_buf, string);
2327                                                 free(string);
2328
2329                                                 if (direction) {
2330                                                         buffer_add(order_buf, direction);
2331                                                 }
2332
2333                                         }
2334
2335                                 } else {
2336                                         string = jsonObjectToSimpleString(snode->item);
2337                                         buffer_add(order_buf, string);
2338                                         free(string);
2339                                         break;
2340                                 }
2341
2342                         }
2343
2344                         string = buffer_release(order_buf);
2345
2346                         if (strlen(string)) {
2347                                 buffer_fadd(
2348                                         sql_buf,
2349                                         " ORDER BY %s",
2350                                         string
2351                                 );
2352                         }
2353
2354                         free(string);
2355                 }
2356
2357                 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "limit" )) ){
2358                         string = jsonObjectToSimpleString(_tmp);
2359                         buffer_fadd(
2360                                 sql_buf,
2361                                 " LIMIT %d",
2362                                 atoi(string)
2363                         );
2364                         free(string);
2365                 }
2366
2367                 _tmp = jsonObjectGetKeyConst( order_hash, "offset" );
2368                 if (_tmp) {
2369                         string = jsonObjectToSimpleString(_tmp);
2370                         buffer_fadd(
2371                                 sql_buf,
2372                                 " OFFSET %d",
2373                                 atoi(string)
2374                         );
2375                         free(string);
2376                 }
2377         }
2378
2379         if (defaultselhash) jsonObjectFree(defaultselhash);
2380
2381         buffer_add(sql_buf, ";");
2382         return buffer_release(sql_buf);
2383 }
2384
2385 int doJSONSearch ( osrfMethodContext* ctx ) {
2386         OSRF_METHOD_VERIFY_CONTEXT(ctx);
2387         osrfLogDebug(OSRF_LOG_MARK, "Recieved query request");
2388
2389         int err = 0;
2390
2391         // XXX for now...
2392         dbhandle = writehandle;
2393
2394         jsonObject* hash = jsonObjectGetIndex(ctx->params, 0);
2395
2396     int flags = 0;
2397
2398         if (jsonBoolIsTrue(jsonObjectGetKey( hash, "distinct" )))
2399          flags |= SELECT_DISTINCT;
2400
2401         if (jsonBoolIsTrue(jsonObjectGetKey( hash, "no_i18n" )))
2402          flags |= DISABLE_I18N;
2403
2404         osrfLogDebug(OSRF_LOG_MARK, "Building SQL ...");
2405         char* sql = SELECT(
2406                         ctx,
2407                         jsonObjectGetKey( hash, "select" ),
2408                         jsonObjectGetKey( hash, "from" ),
2409                         jsonObjectGetKey( hash, "where" ),
2410                         jsonObjectGetKey( hash, "having" ),
2411                         jsonObjectGetKey( hash, "order_by" ),
2412                         jsonObjectGetKey( hash, "limit" ),
2413                         jsonObjectGetKey( hash, "offset" ),
2414                         flags
2415         );
2416
2417         if (!sql) {
2418                 err = -1;
2419                 return err;
2420         }
2421         
2422         osrfLogDebug(OSRF_LOG_MARK, "%s SQL =  %s", MODULENAME, sql);
2423         dbi_result result = dbi_conn_query(dbhandle, sql);
2424
2425         if(result) {
2426                 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
2427
2428                 if (dbi_result_first_row(result)) {
2429                         /* JSONify the result */
2430                         osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
2431
2432                         do {
2433                                 osrfAppRespond( ctx, oilsMakeJSONFromResult( result ) );
2434                         } while (dbi_result_next_row(result));
2435
2436                 } else {
2437                         osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
2438                 }
2439
2440                 osrfAppRespondComplete( ctx, NULL );
2441
2442                 /* clean up the query */
2443                 dbi_result_free(result); 
2444
2445         } else {
2446                 err = -1;
2447                 osrfLogError(OSRF_LOG_MARK, "%s: Error with query [%s]", MODULENAME, sql);
2448                 osrfAppSessionStatus(
2449                         ctx->session,
2450                         OSRF_STATUS_INTERNALSERVERERROR,
2451                         "osrfMethodException",
2452                         ctx->request,
2453                         "Severe query error -- see error log for more details"
2454                 );
2455         }
2456
2457         free(sql);
2458         return err;
2459 }
2460
2461 static jsonObject* doFieldmapperSearch ( osrfMethodContext* ctx, osrfHash* meta,
2462                 const jsonObject* params, int* err ) {
2463
2464         // XXX for now...
2465         dbhandle = writehandle;
2466
2467         osrfHash* links = osrfHashGet(meta, "links");
2468         osrfHash* fields = osrfHashGet(meta, "fields");
2469         char* core_class = osrfHashGet(meta, "classname");
2470         char* pkey = osrfHashGet(meta, "primarykey");
2471
2472         const jsonObject* _tmp;
2473         jsonObject* obj;
2474         jsonObject* search_hash = jsonObjectGetIndex(params, 0);
2475         jsonObject* order_hash = jsonObjectGetIndex(params, 1);
2476
2477         char* sql = buildSELECT( search_hash, order_hash, meta, ctx );
2478         if (!sql) {
2479                 *err = -1;
2480                 return NULL;
2481         }
2482         
2483         osrfLogDebug(OSRF_LOG_MARK, "%s SQL =  %s", MODULENAME, sql);
2484         dbi_result result = dbi_conn_query(dbhandle, sql);
2485
2486         osrfHash* dedup = osrfNewHash();
2487         jsonObject* res_list = jsonParseString("[]");
2488         if(result) {
2489                 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
2490
2491                 if (dbi_result_first_row(result)) {
2492                         /* JSONify the result */
2493                         osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
2494                         do {
2495                                 obj = oilsMakeFieldmapperFromResult( result, meta );
2496                                 int pkey_pos = atoi( osrfHashGet( osrfHashGet( fields, pkey ), "array_position" ) );
2497                                 char* pkey_val = jsonObjectToSimpleString( jsonObjectGetIndex( obj, pkey_pos ) );
2498                                 if ( osrfHashGet( dedup, pkey_val ) ) {
2499                                         jsonObjectFree(obj);
2500                                 } else {
2501                                         osrfHashSet( dedup, pkey_val, pkey_val );
2502                                         jsonObjectPush(res_list, obj);
2503                                 }
2504                         } while (dbi_result_next_row(result));
2505                 } else {
2506                         osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
2507                 }
2508
2509                 /* clean up the query */
2510                 dbi_result_free(result); 
2511
2512         } else {
2513                 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]", MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
2514                 osrfAppSessionStatus(
2515                         ctx->session,
2516                         OSRF_STATUS_INTERNALSERVERERROR,
2517                         "osrfMethodException",
2518                         ctx->request,
2519                         "Severe query error -- see error log for more details"
2520                 );
2521                 *err = -1;
2522                 free(sql);
2523                 jsonObjectFree(res_list);
2524                 return jsonNULL;
2525
2526         }
2527
2528         free(sql);
2529
2530         if (res_list->size && order_hash) {
2531                 _tmp = jsonObjectGetKeyConst( order_hash, "flesh" );
2532                 if (_tmp) {
2533                         int x = (int)jsonObjectGetNumber(_tmp);
2534                         if (x == -1 || x > max_flesh_depth) x = max_flesh_depth;
2535
2536                         const jsonObject* temp_blob;
2537                         if ((temp_blob = jsonObjectGetKeyConst( order_hash, "flesh_fields" )) && x > 0) {
2538
2539                                 jsonObject* flesh_blob = jsonObjectClone( temp_blob );
2540                                 const jsonObject* flesh_fields = jsonObjectGetKeyConst( flesh_blob, core_class );
2541
2542                                 osrfStringArray* link_fields = NULL;
2543
2544                                 if (flesh_fields) {
2545                                         if (flesh_fields->size == 1) {
2546                                                 char* _t = jsonObjectToSimpleString( jsonObjectGetIndex( flesh_fields, 0 ) );
2547                                                 if (!strcmp(_t,"*")) link_fields = osrfHashKeys( links );
2548                                                 free(_t);
2549                                         }
2550
2551                                         if (!link_fields) {
2552                                                 jsonObjectNode* _f;
2553                                                 link_fields = osrfNewStringArray(1);
2554                                                 jsonObjectIterator* _i = jsonNewObjectIterator( flesh_fields );
2555                                                 while ((_f = jsonObjectIteratorNext( _i ))) {
2556                                                         osrfStringArrayAdd( link_fields, jsonObjectToSimpleString( _f->item ) );
2557                                                 }
2558                                         }
2559                                 }
2560
2561                                 jsonObjectNode* cur;
2562                                 jsonObjectIterator* itr = jsonNewObjectIterator( res_list );
2563                                 while ((cur = jsonObjectIteratorNext( itr ))) {
2564
2565                                         int i = 0;
2566                                         char* link_field;
2567                                         
2568                                         while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
2569
2570                                                 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
2571
2572                                                 osrfHash* kid_link = osrfHashGet(links, link_field);
2573                                                 if (!kid_link) continue;
2574
2575                                                 osrfHash* field = osrfHashGet(fields, link_field);
2576                                                 if (!field) continue;
2577
2578                                                 osrfHash* value_field = field;
2579
2580                                                 osrfHash* kid_idl = osrfHashGet(oilsIDL(), osrfHashGet(kid_link, "class"));
2581                                                 if (!kid_idl) continue;
2582
2583                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
2584                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
2585                                                 }
2586                                                         
2587                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
2588                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
2589                                                 }
2590
2591                                                 osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
2592
2593                                                 if (link_map->size > 0) {
2594                                                         jsonObject* _kid_key = jsonParseString("[]");
2595                                                         jsonObjectPush(
2596                                                                 _kid_key,
2597                                                                 jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
2598                                                         );
2599
2600                                                         jsonObjectSetKey(
2601                                                                 flesh_blob,
2602                                                                 osrfHashGet(kid_link, "class"),
2603                                                                 _kid_key
2604                                                         );
2605                                                 };
2606
2607                                                 osrfLogDebug(
2608                                                         OSRF_LOG_MARK,
2609                                                         "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
2610                                                         osrfHashGet(kid_link, "field"),
2611                                                         osrfHashGet(kid_link, "class"),
2612                                                         osrfHashGet(kid_link, "key"),
2613                                                         osrfHashGet(kid_link, "reltype")
2614                                                 );
2615
2616                                                 jsonObject* fake_params = jsonParseString("[]");
2617                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // search hash
2618                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // order/flesh hash
2619
2620                                                 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
2621
2622                                                 char* search_key =
2623                                                 jsonObjectToSimpleString(
2624                                                         jsonObjectGetIndex(
2625                                                                 cur->item,
2626                                                                 atoi( osrfHashGet(value_field, "array_position") )
2627                                                         )
2628                                                 );
2629
2630                                                 if (!search_key) {
2631                                                         osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
2632                                                         continue;
2633                                                 }
2634                                                         
2635                                                 jsonObjectSetKey(
2636                                                         jsonObjectGetIndex(fake_params, 0),
2637                                                         osrfHashGet(kid_link, "key"),
2638                                                         jsonNewObject( search_key )
2639                                                 );
2640
2641                                                 free(search_key);
2642
2643
2644                                                 jsonObjectSetKey(
2645                                                         jsonObjectGetIndex(fake_params, 1),
2646                                                         "flesh",
2647                                                         jsonNewNumberObject( (double)(x - 1 + link_map->size) )
2648                                                 );
2649
2650                                                 if (flesh_blob)
2651                                                         jsonObjectSetKey( jsonObjectGetIndex(fake_params, 1), "flesh_fields", jsonObjectClone(flesh_blob) );
2652
2653                                                 if (jsonObjectGetKeyConst(order_hash, "order_by")) {
2654                                                         jsonObjectSetKey(
2655                                                                 jsonObjectGetIndex(fake_params, 1),
2656                                                                 "order_by",
2657                                                                 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "order_by"))
2658                                                         );
2659                                                 }
2660
2661                                                 if (jsonObjectGetKeyConst(order_hash, "select")) {
2662                                                         jsonObjectSetKey(
2663                                                                 jsonObjectGetIndex(fake_params, 1),
2664                                                                 "select",
2665                                                                 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "select"))
2666                                                         );
2667                                                 }
2668
2669                                                 jsonObject* kids = doFieldmapperSearch(ctx, kid_idl, fake_params, err);
2670
2671                                                 if(*err) {
2672                                                         jsonObjectFree( fake_params );
2673                                                         osrfStringArrayFree(link_fields);
2674                                                         jsonObjectIteratorFree(itr);
2675                                                         jsonObjectFree(res_list);
2676                                                         return jsonNULL;
2677                                                 }
2678
2679                                                 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
2680
2681                                                 jsonObject* X = NULL;
2682                                                 if ( link_map->size > 0 && kids->size > 0 ) {
2683                                                         X = kids;
2684                                                         kids = jsonParseString("[]");
2685
2686                                                         jsonObjectNode* _k_node;
2687                                                         jsonObjectIterator* _k = jsonNewObjectIterator( X );
2688                                                         while ((_k_node = jsonObjectIteratorNext( _k ))) {
2689                                                                 jsonObjectPush(
2690                                                                         kids,
2691                                                                         jsonObjectClone(
2692                                                                                 jsonObjectGetIndex(
2693                                                                                         _k_node->item,
2694                                                                                         (unsigned long)atoi(
2695                                                                                                 osrfHashGet(
2696                                                                                                         osrfHashGet(
2697                                                                                                                 osrfHashGet(
2698                                                                                                                         osrfHashGet(
2699                                                                                                                                 oilsIDL(),
2700                                                                                                                                 osrfHashGet(kid_link, "class")
2701                                                                                                                         ),
2702                                                                                                                         "fields"
2703                                                                                                                 ),
2704                                                                                                                 osrfStringArrayGetString( link_map, 0 )
2705                                                                                                         ),
2706                                                                                                         "array_position"
2707                                                                                                 )
2708                                                                                         )
2709                                                                                 )
2710                                                                         )
2711                                                                 );
2712                                                         }
2713                                                 }
2714
2715                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" )) || !(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) {
2716                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
2717                                                         jsonObjectSetIndex(
2718                                                                 cur->item,
2719                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
2720                                                                 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
2721                                                         );
2722                                                 }
2723
2724                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
2725                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
2726                                                         jsonObjectSetIndex(
2727                                                                 cur->item,
2728                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
2729                                                                 jsonObjectClone( kids )
2730                                                         );
2731                                                 }
2732
2733                                                 if (X) {
2734                                                         jsonObjectFree(kids);
2735                                                         kids = X;
2736                                                 }
2737
2738                                                 jsonObjectFree( kids );
2739                                                 jsonObjectFree( fake_params );
2740
2741                                                 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
2742                                                 osrfLogDebug(OSRF_LOG_MARK, "%s", jsonObjectToJSON(cur->item));
2743
2744                                         }
2745                                 }
2746                                 jsonObjectFree( flesh_blob );
2747                                 osrfStringArrayFree(link_fields);
2748                                 jsonObjectIteratorFree(itr);
2749                         }
2750                 }
2751         }
2752
2753         return res_list;
2754 }
2755
2756
2757 static jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
2758
2759         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
2760         jsonObject* target = jsonObjectGetIndex(ctx->params, 0);
2761
2762         if (!verifyObjectClass(ctx, target)) {
2763                 *err = -1;
2764                 return jsonNULL;
2765         }
2766
2767         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
2768                 osrfAppSessionStatus(
2769                         ctx->session,
2770                         OSRF_STATUS_BADREQUEST,
2771                         "osrfMethodException",
2772                         ctx->request,
2773                         "No active transaction -- required for UPDATE"
2774                 );
2775                 *err = -1;
2776                 return jsonNULL;
2777         }
2778
2779         if (osrfHashGet( meta, "readonly" ) && strncasecmp("true", osrfHashGet( meta, "readonly" ), 4)) {
2780                 osrfAppSessionStatus(
2781                         ctx->session,
2782                         OSRF_STATUS_BADREQUEST,
2783                         "osrfMethodException",
2784                         ctx->request,
2785                         "Cannot UPDATE readonly class"
2786                 );
2787                 *err = -1;
2788                 return jsonNULL;
2789         }
2790
2791         dbhandle = writehandle;
2792
2793         char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
2794
2795         // Set the last_xact_id
2796         osrfHash* last_xact_id;
2797         if ((last_xact_id = oilsIDLFindPath("/%s/fields/last_xact_id", target->classname))) {
2798                 int index = atoi( osrfHashGet(last_xact_id, "array_position") );
2799                 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
2800                 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
2801         }       
2802
2803         char* pkey = osrfHashGet(meta, "primarykey");
2804         osrfHash* fields = osrfHashGet(meta, "fields");
2805
2806         char* id =
2807                 jsonObjectToSimpleString(
2808                         jsonObjectGetIndex(
2809                                 target,
2810                                 atoi( osrfHashGet( osrfHashGet( fields, pkey ), "array_position" ) )
2811                         )
2812                 );
2813
2814         osrfLogDebug(
2815                 OSRF_LOG_MARK,
2816                 "%s updating %s object with %s = %s",
2817                 MODULENAME,
2818                 osrfHashGet(meta, "fieldmapper"),
2819                 pkey,
2820                 id
2821         );
2822
2823         growing_buffer* sql = buffer_init(128);
2824         buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
2825
2826         int i = 0;
2827         int first = 1;
2828         char* field_name;
2829         osrfStringArray* field_list = osrfHashKeys( fields );
2830         while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
2831
2832                 osrfHash* field = osrfHashGet( fields, field_name );
2833
2834                 if(!( strcmp( field_name, pkey ) )) continue;
2835                 if(!( strcmp( osrfHashGet(osrfHashGet(fields,field_name), "virtual"), "true" ) )) continue;
2836
2837                 jsonObject* field_object = jsonObjectGetIndex( target, atoi(osrfHashGet(field, "array_position")) );
2838
2839                 char* value;
2840                 if (field_object && field_object->classname) {
2841                         value = jsonObjectToSimpleString(
2842                                         jsonObjectGetIndex(
2843                                                 field_object,
2844                                                 atoi(
2845                                                         osrfHashGet(
2846                                                                 osrfHashGet(
2847                                                                         oilsIDLFindPath("/%s/fields", field_object->classname),
2848                                                                         (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
2849                                                                 ),
2850                                                                 "array_position"
2851                                                         )
2852                                                 )
2853                                         )
2854                                 );
2855
2856                 } else {
2857                         value = jsonObjectToSimpleString( field_object );
2858                 }
2859
2860                 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
2861
2862                 if (!field_object || field_object->type == JSON_NULL) {
2863                         if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
2864                                 if (first) first = 0;
2865                                 else buffer_add(sql, ",");
2866                                 buffer_fadd( sql, " %s = NULL", field_name );
2867                         }
2868                         
2869                 } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
2870                         if (first) first = 0;
2871                         else buffer_add(sql, ",");
2872
2873                         if ( !strncmp(osrfHashGet(field, "datatype"), "INT", (size_t)3) ) {
2874                                 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
2875                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
2876                                 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
2877                         }
2878
2879                         osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, osrfHashGet(field, "datatype"));
2880
2881                 } else {
2882                         if ( dbi_conn_quote_string(dbhandle, &value) ) {
2883                                 if (first) first = 0;
2884                                 else buffer_add(sql, ",");
2885                                 buffer_fadd( sql, " %s = %s", field_name, value );
2886
2887                         } else {
2888                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
2889                                 osrfAppSessionStatus(
2890                                         ctx->session,
2891                                         OSRF_STATUS_INTERNALSERVERERROR,
2892                                         "osrfMethodException",
2893                                         ctx->request,
2894                                         "Error quoting string -- please see the error log for more details"
2895                                 );
2896                                 free(value);
2897                                 free(id);
2898                                 buffer_free(sql);
2899                                 *err = -1;
2900                                 return jsonNULL;
2901                         }
2902                 }
2903
2904                 free(value);
2905                 
2906         }
2907
2908         jsonObject* obj = jsonParseString(id);
2909
2910         if ( strcmp( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "primitive" ), "number" ) )
2911                 dbi_conn_quote_string(dbhandle, &id);
2912
2913         buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
2914
2915         char* query = buffer_release(sql);
2916         osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
2917
2918         dbi_result result = dbi_conn_query(dbhandle, query);
2919         free(query);
2920
2921         if (!result) {
2922                 jsonObjectFree(obj);
2923                 obj = jsonNewObject(NULL);
2924                 osrfLogError(
2925                         OSRF_LOG_MARK,
2926                         "%s ERROR updating %s object with %s = %s",
2927                         MODULENAME,
2928                         osrfHashGet(meta, "fieldmapper"),
2929                         pkey,
2930                         id
2931                 );
2932         }
2933
2934         free(id);
2935
2936         return obj;
2937 }
2938
2939 static jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
2940
2941         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
2942
2943         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
2944                 osrfAppSessionStatus(
2945                         ctx->session,
2946                         OSRF_STATUS_BADREQUEST,
2947                         "osrfMethodException",
2948                         ctx->request,
2949                         "No active transaction -- required for DELETE"
2950                 );
2951                 *err = -1;
2952                 return jsonNULL;
2953         }
2954
2955         if (osrfHashGet( meta, "readonly" ) && strncasecmp("true", osrfHashGet( meta, "readonly" ), 4)) {
2956                 osrfAppSessionStatus(
2957                         ctx->session,
2958                         OSRF_STATUS_BADREQUEST,
2959                         "osrfMethodException",
2960                         ctx->request,
2961                         "Cannot DELETE readonly class"
2962                 );
2963                 *err = -1;
2964                 return jsonNULL;
2965         }
2966
2967         dbhandle = writehandle;
2968
2969         jsonObject* obj;
2970
2971         char* pkey = osrfHashGet(meta, "primarykey");
2972
2973         char* id;
2974         if (jsonObjectGetIndex(ctx->params, 0)->classname) {
2975                 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, 0 ))) {
2976                         *err = -1;
2977                         return jsonNULL;
2978                 }
2979
2980                 id = jsonObjectToSimpleString(
2981                         jsonObjectGetIndex(
2982                                 jsonObjectGetIndex(ctx->params, 0),
2983                                 atoi( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "array_position") )
2984                         )
2985                 );
2986         } else {
2987                 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
2988         }
2989
2990         osrfLogDebug(
2991                 OSRF_LOG_MARK,
2992                 "%s deleting %s object with %s = %s",
2993                 MODULENAME,
2994                 osrfHashGet(meta, "fieldmapper"),
2995                 pkey,
2996                 id
2997         );
2998
2999         obj = jsonParseString(id);
3000
3001         if ( strcmp( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "primitive" ), "number" ) )
3002                 dbi_conn_quote_string(writehandle, &id);
3003
3004         dbi_result result = dbi_conn_queryf(writehandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
3005
3006         if (!result) {
3007                 jsonObjectFree(obj);
3008                 obj = jsonNewObject(NULL);
3009                 osrfLogError(
3010                         OSRF_LOG_MARK,
3011                         "%s ERROR deleting %s object with %s = %s",
3012                         MODULENAME,
3013                         osrfHashGet(meta, "fieldmapper"),
3014                         pkey,
3015                         id
3016                 );
3017         }
3018
3019         free(id);
3020
3021         return obj;
3022
3023 }
3024
3025
3026 static jsonObject* oilsMakeFieldmapperFromResult( dbi_result result, osrfHash* meta) {
3027         if(!(result && meta)) return jsonNULL;
3028
3029         jsonObject* object = jsonParseString("[]");
3030         jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
3031
3032         osrfHash* fields = osrfHashGet(meta, "fields");
3033
3034         osrfLogInternal(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
3035
3036         osrfHash* _f;
3037         time_t _tmp_dt;
3038         char dt_string[256];
3039         struct tm gmdt;
3040
3041         int fmIndex;
3042         int columnIndex = 1;
3043         int attr;
3044         unsigned short type;
3045         const char* columnName;
3046
3047         /* cycle through the column list */
3048         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
3049
3050                 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
3051
3052                 fmIndex = -1; // reset the position
3053                 
3054                 /* determine the field type and storage attributes */
3055                 type = dbi_result_get_field_type(result, columnName);
3056                 attr = dbi_result_get_field_attribs(result, columnName);
3057
3058                 /* fetch the fieldmapper index */
3059                 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
3060                         char* virt = (char*)osrfHashGet(_f, "virtual");
3061                         char* pos = (char*)osrfHashGet(_f, "array_position");
3062
3063                         if ( !virt || !pos || !(strcmp( virt, "true" )) ) continue;
3064
3065                         fmIndex = atoi( pos );
3066                         osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
3067                 } else {
3068                         continue;
3069                 }
3070
3071                 if (dbi_result_field_is_null(result, columnName)) {
3072                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
3073                 } else {
3074
3075                         switch( type ) {
3076
3077                                 case DBI_TYPE_INTEGER :
3078
3079                                         if( attr & DBI_INTEGER_SIZE8 ) 
3080                                                 jsonObjectSetIndex( object, fmIndex, 
3081                                                         jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
3082                                         else 
3083                                                 jsonObjectSetIndex( object, fmIndex, 
3084                                                         jsonNewNumberObject(dbi_result_get_long(result, columnName)));
3085
3086                                         break;
3087
3088                                 case DBI_TYPE_DECIMAL :
3089                                         jsonObjectSetIndex( object, fmIndex, 
3090                                                         jsonNewNumberObject(dbi_result_get_double(result, columnName)));
3091                                         break;
3092
3093                                 case DBI_TYPE_STRING :
3094
3095
3096                                         jsonObjectSetIndex(
3097                                                 object,
3098                                                 fmIndex,
3099                                                 jsonNewObject( dbi_result_get_string(result, columnName) )
3100                                         );
3101
3102                                         break;
3103
3104                                 case DBI_TYPE_DATETIME :
3105
3106                                         memset(dt_string, '\0', sizeof(dt_string));
3107                                         memset(&gmdt, '\0', sizeof(gmdt));
3108                                         memset(&_tmp_dt, '\0', sizeof(_tmp_dt));
3109
3110                                         _tmp_dt = dbi_result_get_datetime(result, columnName);
3111
3112                                         localtime_r( &_tmp_dt, &gmdt );
3113
3114                                         if (!(attr & DBI_DATETIME_DATE)) {
3115                                                 strftime(dt_string, 255, "%T", &gmdt);
3116                                         } else if (!(attr & DBI_DATETIME_TIME)) {
3117                                                 strftime(dt_string, 255, "%F", &gmdt);
3118                                         } else {
3119                                                 strftime(dt_string, 255, "%FT%T%z", &gmdt);
3120                                         }
3121
3122                                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
3123
3124                                         break;
3125
3126                                 case DBI_TYPE_BINARY :
3127                                         osrfLogError( OSRF_LOG_MARK, 
3128                                                 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
3129                         }
3130                 }
3131         }
3132
3133         return object;
3134 }
3135
3136 static jsonObject* oilsMakeJSONFromResult( dbi_result result ) {
3137         if(!result) return jsonNULL;
3138
3139         jsonObject* object = jsonParseString("{}");
3140
3141         time_t _tmp_dt;
3142         char dt_string[256];
3143         struct tm gmdt;
3144
3145         int fmIndex;
3146         int columnIndex = 1;
3147         int attr;
3148         unsigned short type;
3149         const char* columnName;
3150
3151         /* cycle through the column list */
3152         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
3153
3154                 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
3155
3156                 fmIndex = -1; // reset the position
3157                 
3158                 /* determine the field type and storage attributes */
3159                 type = dbi_result_get_field_type(result, columnName);
3160                 attr = dbi_result_get_field_attribs(result, columnName);
3161
3162                 if (dbi_result_field_is_null(result, columnName)) {
3163                         jsonObjectSetKey( object, columnName, jsonNewObject(NULL) );
3164                 } else {
3165
3166                         switch( type ) {
3167
3168                                 case DBI_TYPE_INTEGER :
3169
3170                                         if( attr & DBI_INTEGER_SIZE8 ) 
3171                                                 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_longlong(result, columnName)) );
3172                                         else 
3173                                                 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_long(result, columnName)) );
3174                                         break;
3175
3176                                 case DBI_TYPE_DECIMAL :
3177                                         jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_double(result, columnName)) );
3178                                         break;
3179
3180                                 case DBI_TYPE_STRING :
3181                                         jsonObjectSetKey( object, columnName, jsonNewObject(dbi_result_get_string(result, columnName)) );
3182                                         break;
3183
3184                                 case DBI_TYPE_DATETIME :
3185
3186                                         memset(dt_string, '\0', sizeof(dt_string));
3187                                         memset(&gmdt, '\0', sizeof(gmdt));
3188                                         memset(&_tmp_dt, '\0', sizeof(_tmp_dt));
3189
3190                                         _tmp_dt = dbi_result_get_datetime(result, columnName);
3191
3192                                         localtime_r( &_tmp_dt, &gmdt );
3193
3194                                         if (!(attr & DBI_DATETIME_DATE)) {
3195                                                 strftime(dt_string, 255, "%T", &gmdt);
3196                                         } else if (!(attr & DBI_DATETIME_TIME)) {
3197                                                 strftime(dt_string, 255, "%F", &gmdt);
3198                                         } else {
3199                                                 strftime(dt_string, 255, "%FT%T%z", &gmdt);
3200                                         }
3201
3202                                         jsonObjectSetKey( object, columnName, jsonNewObject(dt_string) );
3203                                         break;
3204
3205                                 case DBI_TYPE_BINARY :
3206                                         osrfLogError( OSRF_LOG_MARK, 
3207                                                 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
3208                         }
3209                 }
3210         }
3211
3212         return object;
3213 }
3214