]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/c-apps/oils_cstore.c
moving around itr definitions ... something has made json_query unhappy recently
[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, 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                 jsonObjectFree(_p);
665                 if(err) return err;
666
667                 jsonObjectNode* cur;
668                 jsonObjectIterator* itr = jsonNewObjectIterator( obj );
669                 while ((cur = jsonObjectIteratorNext( itr ))) {
670                         osrfAppRespond(
671                                 ctx,
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                 jsonObjectIteratorFree(itr);
687                 osrfAppRespondComplete( ctx, NULL );
688                 
689         } else {
690                 osrfAppRespondComplete( ctx, obj );
691         }
692
693         jsonObjectFree(obj);
694
695         return err;
696 }
697
698 static int verifyObjectClass ( osrfMethodContext* ctx, const jsonObject* param ) {
699         
700         osrfHash* meta = (osrfHash*) ctx->method->userData;
701         osrfHash* class = osrfHashGet( meta, "class" );
702         
703         if (!param->classname || (strcmp( osrfHashGet(class, "classname"), param->classname ))) {
704
705                 growing_buffer* msg = buffer_init(128);
706                 buffer_fadd(
707                         msg,
708                         "%s: %s method for type %s was passed a %s",
709                         MODULENAME,
710                         osrfHashGet(meta, "methodtype"),
711                         osrfHashGet(class, "classname"),
712                         param->classname
713                 );
714
715                 char* m = buffer_release(msg);
716                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException", ctx->request, m );
717
718                 free(m);
719
720                 return 0;
721         }
722         return 1;
723 }
724
725 static jsonObject* doCreate(osrfMethodContext* ctx, int* err ) {
726
727         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
728         jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
729         jsonObject* options = jsonObjectGetIndex( ctx->params, 1 );
730
731         if (!verifyObjectClass(ctx, target)) {
732                 *err = -1;
733                 return jsonNULL;
734         }
735
736         osrfLogDebug( OSRF_LOG_MARK, "Object seems to be of the correct type" );
737
738         if (!ctx->session || !ctx->session->userData || !osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
739                 osrfLogError( OSRF_LOG_MARK, "No active transaction -- required for CREATE" );
740
741                 osrfAppSessionStatus(
742                         ctx->session,
743                         OSRF_STATUS_BADREQUEST,
744                         "osrfMethodException",
745                         ctx->request,
746                         "No active transaction -- required for CREATE"
747                 );
748                 *err = -1;
749                 return jsonNULL;
750         }
751
752         if (osrfHashGet( meta, "readonly" ) && strncasecmp("true", osrfHashGet( meta, "readonly" ), 4)) {
753                 osrfAppSessionStatus(
754                         ctx->session,
755                         OSRF_STATUS_BADREQUEST,
756                         "osrfMethodException",
757                         ctx->request,
758                         "Cannot INSERT readonly class"
759                 );
760                 *err = -1;
761                 return jsonNULL;
762         }
763
764
765         char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
766
767         // Set the last_xact_id
768         osrfHash* last_xact_id;
769         if ((last_xact_id = oilsIDLFindPath("/%s/fields/last_xact_id", target->classname))) {
770                 int index = atoi( osrfHashGet(last_xact_id, "array_position") );
771                 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
772                 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
773         }       
774
775         osrfLogDebug( OSRF_LOG_MARK, "There is a transaction running..." );
776
777         dbhandle = writehandle;
778
779         osrfHash* fields = osrfHashGet(meta, "fields");
780         char* pkey = osrfHashGet(meta, "primarykey");
781         char* seq = osrfHashGet(meta, "sequence");
782
783         growing_buffer* table_buf = buffer_init(128);
784         growing_buffer* col_buf = buffer_init(128);
785         growing_buffer* val_buf = buffer_init(128);
786
787         buffer_fadd(table_buf,"INSERT INTO %s", osrfHashGet(meta, "tablename"));
788         buffer_add(col_buf,"(");
789         buffer_add(val_buf,"VALUES (");
790
791
792         int i = 0;
793         int first = 1;
794         char* field_name;
795         osrfStringArray* field_list = osrfHashKeys( fields );
796         while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
797
798                 osrfHash* field = osrfHashGet( fields, field_name );
799
800                 if(!( strcmp( osrfHashGet(osrfHashGet(fields,field_name), "virtual"), "true" ) )) continue;
801
802                 jsonObject* field_object = jsonObjectGetIndex( target, atoi(osrfHashGet(field, "array_position")) );
803
804                 char* value;
805                 if (field_object && field_object->classname) {
806                         value = jsonObjectToSimpleString(
807                                         jsonObjectGetIndex(
808                                                 field_object,
809                                                 atoi(
810                                                         osrfHashGet(
811                                                                 osrfHashGet(
812                                                                         oilsIDLFindPath("/%s/fields", field_object->classname),
813                                                                         (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
814                                                                 ),
815                                                                 "array_position"
816                                                         )
817                                                 )
818                                         )
819                                 );
820
821                 } else {
822                         value = jsonObjectToSimpleString( field_object );
823                 }
824
825
826                 if (first) {
827                         first = 0;
828                 } else {
829                         buffer_add(col_buf, ",");
830                         buffer_add(val_buf, ",");
831                 }
832
833                 buffer_add(col_buf, field_name);
834
835                 if (!field_object || field_object->type == JSON_NULL) {
836                         buffer_add( val_buf, "DEFAULT" );
837                         
838                 } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
839                         if ( !strcmp(osrfHashGet(field, "datatype"), "INT8") ) {
840                                 buffer_fadd( val_buf, "%lld", atoll(value) );
841                                 
842                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "INT") ) {
843                                 buffer_fadd( val_buf, "%d", atoi(value) );
844                                 
845                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
846                                 buffer_fadd( val_buf, "%f", atof(value) );
847                         }
848                 } else {
849                         if ( dbi_conn_quote_string(writehandle, &value) ) {
850                                 buffer_fadd( val_buf, "%s", value );
851
852                         } else {
853                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
854                                 osrfAppSessionStatus(
855                                         ctx->session,
856                                         OSRF_STATUS_INTERNALSERVERERROR,
857                                         "osrfMethodException",
858                                         ctx->request,
859                                         "Error quoting string -- please see the error log for more details"
860                                 );
861                                 free(value);
862                                 buffer_free(table_buf);
863                                 buffer_free(col_buf);
864                                 buffer_free(val_buf);
865                                 *err = -1;
866                                 return jsonNULL;
867                         }
868                 }
869
870                 free(value);
871                 
872         }
873
874
875         buffer_add(col_buf,")");
876         buffer_add(val_buf,")");
877
878         char* table_str = buffer_release(table_buf);
879         char* col_str   = buffer_release(col_buf);
880         char* val_str   = buffer_release(val_buf);
881         growing_buffer* sql = buffer_init(128);
882         buffer_fadd( sql, "%s %s %s;", table_str, col_str, val_str );
883         free(table_str);
884         free(col_str);
885         free(val_str);
886
887         char* query = buffer_release(sql);
888
889         osrfLogDebug(OSRF_LOG_MARK, "%s: Insert SQL [%s]", MODULENAME, query);
890
891         
892         dbi_result result = dbi_conn_query(writehandle, query);
893
894         jsonObject* obj = NULL;
895
896         if (!result) {
897                 obj = jsonNewObject(NULL);
898                 osrfLogError(
899                         OSRF_LOG_MARK,
900                         "%s ERROR inserting %s object using query [%s]",
901                         MODULENAME,
902                         osrfHashGet(meta, "fieldmapper"),
903                         query
904                 );
905                 osrfAppSessionStatus(
906                         ctx->session,
907                         OSRF_STATUS_INTERNALSERVERERROR,
908                         "osrfMethodException",
909                         ctx->request,
910                         "INSERT error -- please see the error log for more details"
911                 );
912                 *err = -1;
913         } else {
914
915                 int pos = atoi(osrfHashGet( osrfHashGet(fields, pkey), "array_position" ));
916                 char* id = jsonObjectToSimpleString(jsonObjectGetIndex(target, pos));
917                 if (!id) {
918                         unsigned long long new_id = dbi_conn_sequence_last(writehandle, seq);
919                         growing_buffer* _id = buffer_init(10);
920                         buffer_fadd(_id, "%lld", new_id);
921                         id = buffer_release(_id);
922                 }
923
924                 if (    !options
925                         || !jsonObjectGetKeyConst( options, "quiet")
926                         || strcmp( jsonObjectToSimpleString(jsonObjectGetKeyConst( options, "quiet")), "true" )
927                 ) {
928
929                         jsonObject* fake_params = jsonParseString("[]");
930                         jsonObjectPush(fake_params, jsonParseString("{}"));
931
932                         jsonObjectSetKey(
933                                 jsonObjectGetIndex(fake_params, 0),
934                                 osrfHashGet(meta, "primarykey"),
935                                 jsonNewObject(id)
936                         );
937
938                         jsonObject* list = doFieldmapperSearch( ctx,meta, fake_params, err);
939
940                         if(*err) {
941                                 jsonObjectFree( fake_params );
942                                 obj = jsonNULL;
943                         } else {
944                                 obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
945                         }
946
947                         jsonObjectFree( list );
948                         jsonObjectFree( fake_params );
949
950                 } else {
951                         obj = jsonNewObject(id);
952                 }
953
954                 free(id);
955         }
956
957         free(query);
958
959         return obj;
960
961 }
962
963
964 static jsonObject* doRetrieve(osrfMethodContext* ctx, int* err ) {
965
966         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
967
968         jsonObject* obj;
969
970         char* id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
971         jsonObject* order_hash = jsonObjectGetIndex(ctx->params, 1);
972
973         osrfLogDebug(
974                 OSRF_LOG_MARK,
975                 "%s retrieving %s object with id %s",
976                 MODULENAME,
977                 osrfHashGet(meta, "fieldmapper"),
978                 id
979         );
980
981         jsonObject* fake_params = jsonParseString("[]");
982         jsonObjectPush(fake_params, jsonParseString("{}"));
983
984         jsonObjectSetKey(
985                 jsonObjectGetIndex(fake_params, 0),
986                 osrfHashGet(meta, "primarykey"),
987                 jsonParseString(id)
988         );
989
990         if (order_hash) jsonObjectPush(fake_params, jsonObjectClone(order_hash) );
991
992         jsonObject* list = doFieldmapperSearch( ctx,meta, fake_params, err);
993
994         if(*err) {
995                 jsonObjectFree( fake_params );
996                 return jsonNULL;
997         }
998
999         obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
1000
1001         jsonObjectFree( list );
1002         jsonObjectFree( fake_params );
1003
1004         return obj;
1005 }
1006
1007 static char* jsonNumberToDBString ( osrfHash* field, const jsonObject* value ) {
1008         growing_buffer* val_buf = buffer_init(32);
1009
1010         if ( !strncmp(osrfHashGet(field, "datatype"), "INT", (size_t)3) ) {
1011                 if (value->type == JSON_NUMBER) buffer_fadd( val_buf, "%ld", (long)jsonObjectGetNumber(value) );
1012                 else buffer_fadd( val_buf, "%ld", atol(jsonObjectToSimpleString(value)) );
1013
1014         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
1015                 if (value->type == JSON_NUMBER) buffer_fadd( val_buf, "%f",  jsonObjectGetNumber(value) );
1016                 else buffer_fadd( val_buf, "%f", atof(jsonObjectToSimpleString(value)) );
1017         }
1018
1019         return buffer_release(val_buf);
1020 }
1021
1022 static char* searchINPredicate (const char* class, osrfHash* field,
1023                 const jsonObject* node, const char* op) {
1024         growing_buffer* sql_buf = buffer_init(32);
1025         
1026         buffer_fadd(
1027                 sql_buf,
1028                 "\"%s\".%s ",
1029                 class,
1030                 osrfHashGet(field, "name")
1031         );
1032
1033         if (!op) {
1034                 buffer_add(sql_buf, "IN (");
1035         } else if (!(strcasecmp(op,"not in"))) {
1036                 buffer_add(sql_buf, "NOT IN (");
1037         } else {
1038                 buffer_add(sql_buf, "IN (");
1039         }
1040
1041         int in_item_index = 0;
1042         int in_item_first = 1;
1043         jsonObject* in_item;
1044         while ( (in_item = jsonObjectGetIndex(node, in_item_index++)) ) {
1045
1046                 if (in_item_first)
1047                         in_item_first = 0;
1048                 else
1049                         buffer_add(sql_buf, ", ");
1050
1051                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1052                         char* val = jsonNumberToDBString( field, in_item );
1053                         buffer_fadd( sql_buf, "%s", val );
1054                         free(val);
1055
1056                 } else {
1057                         char* key_string = jsonObjectToSimpleString(in_item);
1058                         if ( dbi_conn_quote_string(dbhandle, &key_string) ) {
1059                                 buffer_fadd( sql_buf, "%s", key_string );
1060                                 free(key_string);
1061                         } else {
1062                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string);
1063                                 free(key_string);
1064                                 buffer_free(sql_buf);
1065                                 return NULL;
1066                         }
1067                 }
1068         }
1069
1070         buffer_add(
1071                 sql_buf,
1072                 ")"
1073         );
1074
1075         return buffer_release(sql_buf);
1076 }
1077
1078 static char* searchValueTransform( const jsonObject* array ) {
1079         growing_buffer* sql_buf = buffer_init(32);
1080
1081         char* val = NULL;
1082         int func_item_index = 0;
1083         int func_item_first = 2;
1084         jsonObject* func_item;
1085         while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1086
1087                 val = jsonObjectToSimpleString(func_item);
1088
1089                 if (func_item_first == 2) {
1090                         buffer_fadd(sql_buf, "%s( ", val);
1091                         free(val);
1092                         func_item_first--;
1093                         continue;
1094                 }
1095
1096                 if (func_item_first)
1097                         func_item_first--;
1098                 else
1099                         buffer_add(sql_buf, ", ");
1100
1101                 if ( dbi_conn_quote_string(dbhandle, &val) ) {
1102                         buffer_fadd( sql_buf, "%s", val );
1103                         free(val);
1104                 } else {
1105                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1106                         free(val);
1107                         buffer_free(sql_buf);
1108                         return NULL;
1109                 }
1110         }
1111
1112         buffer_add(
1113                 sql_buf,
1114                 " )"
1115         );
1116
1117         return buffer_release(sql_buf);
1118 }
1119
1120 static char* searchFunctionPredicate (const char* class, osrfHash* field,
1121                 const jsonObjectNode* node) {
1122         growing_buffer* sql_buf = buffer_init(32);
1123
1124         char* val = searchValueTransform(node->item);
1125         
1126         buffer_fadd(
1127                 sql_buf,
1128                 "\"%s\".%s %s %s",
1129                 class,
1130                 osrfHashGet(field, "name"),
1131                 node->key,
1132                 val
1133         );
1134
1135         free(val);
1136
1137         return buffer_release(sql_buf);
1138 }
1139
1140 static char* searchFieldTransform (const char* class, osrfHash* field, const jsonObject* node) {
1141         growing_buffer* sql_buf = buffer_init(32);
1142         
1143         char* field_transform = jsonObjectToSimpleString( jsonObjectGetKeyConst( node, "transform" ) );
1144
1145         if (field_transform) {
1146                 buffer_fadd( sql_buf, "%s(\"%s\".%s", field_transform, class, osrfHashGet(field, "name"));
1147             const jsonObject* array = jsonObjectGetKeyConst( node, "params" );
1148
1149         if (array) {
1150                 int func_item_index = 0;
1151                 jsonObject* func_item;
1152                 while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1153
1154                         char* val = jsonObjectToSimpleString(func_item);
1155
1156                     if ( dbi_conn_quote_string(dbhandle, &val) ) {
1157                             buffer_fadd( sql_buf, ",%s", val );
1158                         } else {
1159                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1160                             free(field_transform);
1161                                 buffer_free(sql_buf);
1162                                 return NULL;
1163                 }
1164             }
1165
1166         }
1167
1168         buffer_add(
1169                 sql_buf,
1170                 " )"
1171         );
1172  
1173         } else {
1174                 buffer_fadd( sql_buf, "\"%s\".%s", class, osrfHashGet(field, "name"));
1175     }
1176
1177         if (field_transform) free(field_transform);
1178
1179         return buffer_release(sql_buf);
1180 }
1181
1182 static char* searchFieldTransformPredicate (const char* class, osrfHash* field, jsonObjectNode* node) {
1183         growing_buffer* sql_buf = buffer_init(32);
1184         
1185         char* field_transform = searchFieldTransform( class, field, node->item );
1186         char* value = NULL;
1187
1188         if (!jsonObjectGetKeyConst( node->item, "value" )) {
1189                 value = searchWHERE( node->item, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN );
1190         } else if (jsonObjectGetKeyConst( node->item, "value" )->type == JSON_ARRAY) {
1191                 value = searchValueTransform(jsonObjectGetKeyConst( node->item, "value" ));
1192         } else if (jsonObjectGetKeyConst( node->item, "value" )->type == JSON_HASH) {
1193                 value = searchWHERE( jsonObjectGetKeyConst( node->item, "value" ), osrfHashGet( oilsIDL(), class ), AND_OP_JOIN );
1194         } else if (jsonObjectGetKeyConst( node->item, "value" )->type != JSON_NULL) {
1195                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1196                         value = jsonNumberToDBString( field, jsonObjectGetKeyConst( node->item, "value" ) );
1197                 } else {
1198                         value = jsonObjectToSimpleString(jsonObjectGetKeyConst( node->item, "value" ));
1199                         if ( !dbi_conn_quote_string(dbhandle, &value) ) {
1200                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, value);
1201                                 free(value);
1202                                 return NULL;
1203                         }
1204                 }
1205         }
1206
1207         buffer_fadd(
1208                 sql_buf,
1209                 "%s %s %s",
1210                 field_transform,
1211                 node->key,
1212                 value
1213         );
1214
1215         free(field_transform);
1216
1217         return buffer_release(sql_buf);
1218 }
1219
1220 static char* searchSimplePredicate (const char* orig_op, const char* class,
1221                 osrfHash* field, const jsonObject* node) {
1222
1223         char* val = NULL;
1224
1225         if (node->type != JSON_NULL) {
1226                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1227                         val = jsonNumberToDBString( field, node );
1228                 } else {
1229                         val = jsonObjectToSimpleString(node);
1230                 }
1231         }
1232
1233         char* pred = searchWriteSimplePredicate( class, field, osrfHashGet(field, "name"), orig_op, val );
1234
1235         if (val) free(val);
1236
1237         return pred;
1238 }
1239
1240 static char* searchWriteSimplePredicate ( const char* class, osrfHash* field,
1241         const char* left, const char* orig_op, const char* right ) {
1242
1243         char* val = NULL;
1244         char* op = NULL;
1245         if (right == NULL) {
1246                 val = strdup("NULL");
1247
1248                 if (strcmp( orig_op, "=" ))
1249                         op = strdup("IS NOT");
1250                 else
1251                         op = strdup("IS");
1252
1253         } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1254                 val = strdup(right);
1255                 op = strdup(orig_op);
1256
1257         } else {
1258                 val = strdup(right);
1259                 if ( !dbi_conn_quote_string(dbhandle, &val) ) {
1260                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1261                         free(val);
1262                         return NULL;
1263                 }
1264                 op = strdup(orig_op);
1265         }
1266
1267         growing_buffer* sql_buf = buffer_init(16);
1268         buffer_fadd( sql_buf, "\"%s\".%s %s %s", class, left, op, val );
1269         free(val);
1270         free(op);
1271
1272         return buffer_release(sql_buf);
1273 }
1274
1275 static char* searchBETWEENPredicate (const char* class, osrfHash* field, jsonObject* node) {
1276
1277         char* x_string;
1278         char* y_string;
1279
1280         if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1281                 x_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,0));
1282                 y_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,1));
1283
1284         } else {
1285                 x_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,0));
1286                 y_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,1));
1287                 if ( !(dbi_conn_quote_string(dbhandle, &x_string) && dbi_conn_quote_string(dbhandle, &y_string)) ) {
1288                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key strings [%s] and [%s]", MODULENAME, x_string, y_string);
1289                         free(x_string);
1290                         free(y_string);
1291                         return NULL;
1292                 }
1293         }
1294
1295         growing_buffer* sql_buf = buffer_init(32);
1296         buffer_fadd( sql_buf, "%s BETWEEN %s AND %s", osrfHashGet(field, "name"), x_string, y_string );
1297         free(x_string);
1298         free(y_string);
1299
1300         return buffer_release(sql_buf);
1301 }
1302
1303 static char* searchPredicate ( const char* class, osrfHash* field, jsonObject* node ) {
1304
1305         char* pred = NULL;
1306         if (node->type == JSON_ARRAY) { // equality IN search
1307                 pred = searchINPredicate( class, field, node, NULL );
1308         } else if (node->type == JSON_HASH) { // non-equality search
1309                 jsonObjectNode* pred_node;
1310                 jsonObjectIterator* pred_itr = jsonNewObjectIterator( node );
1311                 while ( (pred_node = jsonObjectIteratorNext( pred_itr )) ) {
1312                         if ( !(strcasecmp( pred_node->key,"between" )) )
1313                                 pred = searchBETWEENPredicate( class, field, pred_node->item );
1314                         else if ( !(strcasecmp( pred_node->key,"in" )) || !(strcasecmp( pred_node->key,"not in" )) )
1315                                 pred = searchINPredicate( class, field, pred_node->item, pred_node->key );
1316                         else if ( pred_node->item->type == JSON_ARRAY )
1317                                 pred = searchFunctionPredicate( class, field, pred_node );
1318                         else if ( pred_node->item->type == JSON_HASH )
1319                                 pred = searchFieldTransformPredicate( class, field, pred_node );
1320                         else 
1321                                 pred = searchSimplePredicate( pred_node->key, class, field, pred_node->item );
1322
1323                         break;
1324                 }
1325         jsonObjectIteratorFree(pred_itr);
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     jsonObjectIteratorFree(search_itr);
1536
1537         return buffer_release(join_buf);
1538 }
1539
1540 /*
1541
1542 { +class : { -or|-and : { field : { op : value }, ... } ... }, ... }
1543 { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }
1544 [ { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }, ... ]
1545
1546 */
1547 static char* searchWHERE ( const jsonObject* search_hash, osrfHash* meta, int opjoin_type ) {
1548
1549         growing_buffer* sql_buf = buffer_init(128);
1550
1551         jsonObjectNode* node = NULL;
1552
1553     int first = 1;
1554     if ( search_hash->type == JSON_ARRAY ) {
1555         jsonObjectIterator* search_itr = jsonNewObjectIterator( search_hash );
1556         while ( (node = jsonObjectIteratorNext( search_itr )) ) {
1557             if (first) {
1558                 first = 0;
1559             } else {
1560                 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
1561                 else buffer_add(sql_buf, " AND ");
1562             }
1563
1564             char* subpred = searchWHERE( node->item, meta, opjoin_type );
1565             buffer_fadd(sql_buf, "( %s )", subpred);
1566             free(subpred);
1567         }
1568         jsonObjectIteratorFree(search_itr);
1569
1570     } else if ( search_hash->type == JSON_HASH ) {
1571         jsonObjectIterator* search_itr = jsonNewObjectIterator( search_hash );
1572         while ( (node = jsonObjectIteratorNext( search_itr )) ) {
1573
1574             if (first) {
1575                 first = 0;
1576             } else {
1577                 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
1578                 else buffer_add(sql_buf, " AND ");
1579             }
1580
1581             if ( !strncmp("+",node->key,1) ) {
1582                 if ( node->item->type == JSON_STRING ) {
1583                     char* subpred = jsonObjectToSimpleString( node->item );
1584                     buffer_fadd(sql_buf, " \"%s\".%s ", node->key + 1, subpred);
1585                     free(subpred);
1586                 } else {
1587                     char* subpred = searchWHERE( node->item, osrfHashGet( oilsIDL(), node->key + 1 ), AND_OP_JOIN );
1588                     buffer_fadd(sql_buf, "( %s )", subpred);
1589                     free(subpred);
1590                 }
1591             } else if ( !strcasecmp("-or",node->key) ) {
1592                 char* subpred = searchWHERE( node->item, meta, OR_OP_JOIN );
1593                 buffer_fadd(sql_buf, "( %s )", subpred);
1594                 free(subpred);
1595             } else if ( !strcasecmp("-and",node->key) ) {
1596                 char* subpred = searchWHERE( node->item, meta, AND_OP_JOIN );
1597                 buffer_fadd(sql_buf, "( %s )", subpred);
1598                 free(subpred);
1599             } else {
1600
1601                 char* class = osrfHashGet(meta, "classname");
1602                 osrfHash* fields = osrfHashGet(meta, "fields");
1603                 osrfHash* field = osrfHashGet( fields, node->key );
1604
1605
1606                 if (!field) {
1607                     char* table = getSourceDefinition(meta);
1608                     osrfLogError(
1609                         OSRF_LOG_MARK,
1610                         "%s: Attempt to reference non-existant column %s on %s (%s)",
1611                         MODULENAME,
1612                         node->key,
1613                         table,
1614                         class
1615                     );
1616                     buffer_free(sql_buf);
1617                     free(table);
1618                     return NULL;
1619                 }
1620
1621                 char* subpred = searchPredicate( class, field, node->item );
1622                 buffer_add( sql_buf, subpred );
1623                 free(subpred);
1624             }
1625         }
1626             jsonObjectIteratorFree(search_itr);
1627
1628     } else {
1629         // ERROR ... only hash and array allowed at this level
1630         char* predicate_string = jsonObjectToJSON( search_hash );
1631         osrfLogError(
1632             OSRF_LOG_MARK,
1633             "%s: Invalid predicate structure: %s",
1634             MODULENAME,
1635             predicate_string
1636         );
1637         buffer_free(sql_buf);
1638         free(predicate_string);
1639         return NULL;
1640     }
1641
1642
1643         return buffer_release(sql_buf);
1644 }
1645
1646 static char* SELECT (
1647                 /* method context */ osrfMethodContext* ctx,
1648                 
1649                 /* SELECT   */ jsonObject* selhash,
1650                 /* FROM     */ jsonObject* join_hash,
1651                 /* WHERE    */ jsonObject* search_hash,
1652                 /* HAVING   */ jsonObject* having_hash,
1653                 /* ORDER BY */ jsonObject* order_hash,
1654                 /* LIMIT    */ jsonObject* limit,
1655                 /* OFFSET   */ jsonObject* offset,
1656                 /* flags    */ int flags
1657 ) {
1658         const char* locale = osrf_message_get_last_locale();
1659
1660         // in case we don't get a select list
1661         jsonObject* defaultselhash = NULL;
1662
1663         // general tmp objects
1664         const jsonObject* tmp_const;
1665         jsonObject* _tmp = NULL;
1666         jsonObjectNode* selclass = NULL;
1667         jsonObjectNode* selfield = NULL;
1668         jsonObjectNode* snode = NULL;
1669         jsonObjectNode* onode = NULL;
1670         jsonObject* found = NULL;
1671
1672         char* string = NULL;
1673         int first = 1;
1674         int gfirst = 1;
1675         //int hfirst = 1;
1676
1677         // the core search class
1678         char* core_class = NULL;
1679
1680         // metadata about the core search class
1681         osrfHash* core_meta = NULL;
1682         osrfHash* core_fields = NULL;
1683         osrfHash* idlClass = NULL;
1684
1685         // punt if there's no core class
1686         if (!join_hash || ( join_hash->type == JSON_HASH && !join_hash->size ))
1687                 return NULL;
1688
1689         // get the core class -- the only key of the top level FROM clause, or a string
1690         if (join_hash->type == JSON_HASH) {
1691                 jsonObjectIterator* tmp_itr = jsonNewObjectIterator( join_hash );
1692                 snode = jsonObjectIteratorNext( tmp_itr );
1693                 
1694                 core_class = strdup( snode->key );
1695                 join_hash = snode->item;
1696
1697                 jsonObjectIteratorFree( tmp_itr );
1698                 snode = NULL;
1699
1700         } else if (join_hash->type == JSON_STRING) {
1701                 core_class = jsonObjectToSimpleString( join_hash );
1702                 join_hash = NULL;
1703         }
1704
1705         // punt if we don't know about the core class
1706         if (!(core_meta = osrfHashGet( oilsIDL(), core_class )))
1707                 return NULL;
1708
1709         // if the select list is empty, or the core class field list is '*',
1710         // build the default select list ...
1711         if (!selhash) {
1712                 selhash = defaultselhash = jsonParseString( "{}" );
1713                 jsonObjectSetKey( selhash, core_class, jsonParseString( "[]" ) );
1714         } else if ( (tmp_const = jsonObjectGetKeyConst( selhash, core_class )) && tmp_const->type == JSON_STRING ) {
1715                 char* _x = jsonObjectToSimpleString( tmp_const );
1716                 if (!strncmp( "*", _x, 1 )) {
1717                         jsonObjectRemoveKey( selhash, core_class );
1718                         jsonObjectSetKey( selhash, core_class, jsonParseString( "[]" ) );
1719                 }
1720                 free(_x);
1721         }
1722
1723         // the query buffer
1724         growing_buffer* sql_buf = buffer_init(128);
1725
1726         // temp buffer for the SELECT list
1727         growing_buffer* select_buf = buffer_init(128);
1728         growing_buffer* order_buf = buffer_init(128);
1729         growing_buffer* group_buf = buffer_init(128);
1730         growing_buffer* having_buf = buffer_init(128);
1731
1732         core_fields = osrfHashGet(core_meta, "fields");
1733
1734         // ... and if we /are/ building the default list, do that
1735         if ( (_tmp = jsonObjectGetKey(selhash,core_class)) && !_tmp->size ) {
1736                 
1737                 int i = 0;
1738                 char* field;
1739
1740                 osrfStringArray* keys = osrfHashKeys( core_fields );
1741                 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
1742                         if ( strncasecmp( "true", osrfHashGet( osrfHashGet( core_fields, field ), "virtual" ), 4 ) )
1743                                 jsonObjectPush( _tmp, jsonNewObject( field ) );
1744                 }
1745                 osrfStringArrayFree(keys);
1746         }
1747
1748         // Now we build the actual select list
1749         int sel_pos = 1;
1750         jsonObject* is_agg = jsonObjectFindPath(selhash, "//aggregate");
1751         first = 1;
1752         gfirst = 1;
1753         jsonObjectIterator* selclass_itr = jsonNewObjectIterator( selhash );
1754         while ( (selclass = jsonObjectIteratorNext( selclass_itr )) ) {
1755
1756                 // round trip through the idl, just to be safe
1757                 idlClass = osrfHashGet( oilsIDL(), selclass->key );
1758                 if (!idlClass) continue;
1759                 char* cname = osrfHashGet(idlClass, "classname");
1760
1761                 // make sure the target relation is in the join tree
1762                 if (strcmp(core_class,cname)) {
1763                         if (!join_hash) continue;
1764
1765                         if (join_hash->type == JSON_STRING) {
1766                                 string = jsonObjectToSimpleString(join_hash);
1767                                 found = strcmp(string,cname) ? NULL : jsonParseString("{\"1\":\"1\"}");
1768                                 free(string);
1769                         } else {
1770                                 found = jsonObjectFindPath(join_hash, "//%s", cname);
1771                         }
1772
1773                         if (!found->size) {
1774                                 jsonObjectFree(found);
1775                                 continue;
1776                         }
1777
1778                         jsonObjectFree(found);
1779                 }
1780
1781                 // stitch together the column list ...
1782                 jsonObjectIterator* select_itr = jsonNewObjectIterator( selclass->item );
1783                 while ( (selfield = jsonObjectIteratorNext( select_itr )) ) {
1784
1785                         char* __column = NULL;
1786                         char* __alias = NULL;
1787
1788                         // ... if it's a sstring, just toss it on the pile
1789                         if (selfield->item->type == JSON_STRING) {
1790
1791                                 // again, just to be safe
1792                                 char* _requested_col = jsonObjectToSimpleString(selfield->item);
1793                                 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), _requested_col );
1794                                 free(_requested_col);
1795
1796                                 if (!field) continue;
1797                                 __column = strdup(osrfHashGet(field, "name"));
1798
1799                                 if (first) {
1800                                         first = 0;
1801                                 } else {
1802                                         buffer_add(select_buf, ",");
1803                                 }
1804
1805                 if (locale) {
1806                         char* i18n = osrfHashGet(field, "i18n");
1807                                 if (flags & DISABLE_I18N)
1808                         i18n = NULL;
1809
1810                         if ( i18n && !strncasecmp("true", i18n, 4)) {
1811                             char* pkey = osrfHashGet(idlClass, "primarykey");
1812                             char* tname = osrfHashGet(idlClass, "tablename");
1813
1814                         buffer_fadd(select_buf, " oils_i18n_xlate('%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"", tname, __column, pkey, cname, pkey, locale, __column);
1815                     } else {
1816                                         buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, __column, __column);
1817                     }
1818                 } else {
1819                                     buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, __column, __column);
1820                 }
1821
1822                         // ... but it could be an object, in which case we check for a Field Transform
1823                         } else {
1824
1825                                 __column = jsonObjectToSimpleString( jsonObjectGetKeyConst( selfield->item, "column" ) );
1826
1827                                 // again, just to be safe
1828                                 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), __column );
1829                                 if (!field) continue;
1830                                 const char* fname = osrfHashGet(field, "name");
1831
1832                                 if (first) {
1833                                         first = 0;
1834                                 } else {
1835                                         buffer_add(select_buf, ",");
1836                                 }
1837
1838                                 if ((tmp_const = jsonObjectGetKeyConst( selfield->item, "alias" ))) {
1839                                         __alias = jsonObjectToSimpleString( tmp_const );
1840                                 } else {
1841                                         __alias = strdup(__column);
1842                                 }
1843
1844                                 if (jsonObjectGetKeyConst( selfield->item, "transform" )) {
1845                                         free(__column);
1846                                         __column = searchFieldTransform(cname, field, selfield->item);
1847                                         buffer_fadd(select_buf, " %s AS \"%s\"", __column, __alias);
1848                                 } else {
1849                     if (locale) {
1850                                 char* i18n = osrfHashGet(field, "i18n");
1851                                     if (flags & DISABLE_I18N)
1852                             i18n = NULL;
1853
1854                                 if ( i18n && !strncasecmp("true", i18n, 4)) {
1855                                 char* pkey = osrfHashGet(idlClass, "primarykey");
1856                                 char* tname = osrfHashGet(idlClass, "tablename");
1857
1858                             buffer_fadd(select_buf, " oils_i18n_xlate('%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"", tname, fname, pkey, cname, pkey, locale, __alias);
1859                         } else {
1860                                                 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, fname, __alias);
1861                         }
1862                     } else {
1863                                             buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, fname, __alias);
1864                     }
1865                                 }
1866                         }
1867
1868                         if (is_agg->size || (flags & SELECT_DISTINCT)) {
1869
1870                                 if (!jsonBoolIsTrue( jsonObjectGetKey( selfield->item, "aggregate" ) )) {
1871                                         if (gfirst) {
1872                                                 gfirst = 0;
1873                                         } else {
1874                                                 buffer_add(group_buf, ",");
1875                                         }
1876
1877                                         buffer_fadd(group_buf, " %d", sel_pos);
1878                                 /*
1879                                 } else if (is_agg = jsonObjectGetKey( selfield->item, "having" )) {
1880                                         if (gfirst) {
1881                                                 gfirst = 0;
1882                                         } else {
1883                                                 buffer_add(group_buf, ",");
1884                                         }
1885
1886                                         __column = searchFieldTransform(cname, field, selfield->item);
1887                                         buffer_fadd(group_buf, " %s", __column);
1888                                         __column = searchFieldTransform(cname, field, selfield->item);
1889                                 */
1890                                 }
1891                         }
1892
1893                         if (__column) free(__column);
1894                         if (__alias) free(__alias);
1895
1896                         sel_pos++;
1897                 }
1898         jsonObjectIteratorFree(select_itr);
1899         }
1900
1901     jsonObjectIteratorFree(selclass_itr);
1902
1903         if (is_agg) jsonObjectFree(is_agg);
1904
1905         char* col_list = buffer_release(select_buf);
1906         char* table = getSourceDefinition(core_meta);
1907
1908         // Put it all together
1909         buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\" ", col_list, table, core_class );
1910         free(col_list);
1911         free(table);
1912
1913         // Now, walk the join tree and add that clause
1914         if ( join_hash ) {
1915                 char* join_clause = searchJOIN( join_hash, core_meta );
1916                 buffer_add(sql_buf, join_clause);
1917                 free(join_clause);
1918         }
1919
1920         if ( search_hash ) {
1921                 buffer_add(sql_buf, " WHERE ");
1922
1923                 // and it's on the the WHERE clause
1924                 char* pred = searchWHERE( search_hash, core_meta, AND_OP_JOIN );
1925
1926                 if (!pred) {
1927                         osrfAppSessionStatus(
1928                                 ctx->session,
1929                                 OSRF_STATUS_INTERNALSERVERERROR,
1930                                 "osrfMethodException",
1931                                 ctx->request,
1932                                 "Severe query error in WHERE predicate -- see error log for more details"
1933                         );
1934                         free(core_class);
1935                         buffer_free(having_buf);
1936                         buffer_free(group_buf);
1937                         buffer_free(order_buf);
1938                         buffer_free(sql_buf);
1939                         if (defaultselhash) jsonObjectFree(defaultselhash);
1940                         return NULL;
1941                 } else {
1942                         buffer_add(sql_buf, pred);
1943                         free(pred);
1944                 }
1945     }
1946
1947         if ( having_hash ) {
1948                 buffer_add(sql_buf, " HAVING ");
1949
1950                 // and it's on the the WHERE clause
1951                 char* pred = searchWHERE( having_hash, core_meta, AND_OP_JOIN );
1952
1953                 if (!pred) {
1954                         osrfAppSessionStatus(
1955                                 ctx->session,
1956                                 OSRF_STATUS_INTERNALSERVERERROR,
1957                                 "osrfMethodException",
1958                                 ctx->request,
1959                                 "Severe query error in HAVING predicate -- see error log for more details"
1960                         );
1961                         free(core_class);
1962                         buffer_free(having_buf);
1963                         buffer_free(group_buf);
1964                         buffer_free(order_buf);
1965                         buffer_free(sql_buf);
1966                         if (defaultselhash) jsonObjectFree(defaultselhash);
1967                         return NULL;
1968                 } else {
1969                         buffer_add(sql_buf, pred);
1970                         free(pred);
1971                 }
1972         }
1973
1974         first = 1;
1975         jsonObjectIterator* class_itr = jsonNewObjectIterator( order_hash );
1976         while ( (snode = jsonObjectIteratorNext( class_itr )) ) {
1977
1978                 if (!jsonObjectGetKeyConst(selhash,snode->key))
1979                         continue;
1980
1981                 jsonObjectIterator* order_itr = jsonNewObjectIterator( snode->item );
1982                 if ( snode->item->type == JSON_HASH ) {
1983
1984                         while ( (onode = jsonObjectIteratorNext( order_itr )) ) {
1985
1986                                 if (!oilsIDLFindPath( "/%s/fields/%s", snode->key, onode->key ))
1987                                         continue;
1988
1989                                 char* direction = NULL;
1990                                 if ( onode->item->type == JSON_HASH ) {
1991                                         if ( jsonObjectGetKeyConst( onode->item, "transform" ) ) {
1992                                                 string = searchFieldTransform(
1993                                                         snode->key,
1994                                                         oilsIDLFindPath( "/%s/fields/%s", snode->key, onode->key ),
1995                                                         onode->item
1996                                                 );
1997                                         } else {
1998                                                 growing_buffer* field_buf = buffer_init(16);
1999                                                 buffer_fadd(field_buf, "\"%s\".%s", snode->key, onode->key);
2000                                                 string = buffer_release(field_buf);
2001                                         }
2002
2003                                         if ( (tmp_const = jsonObjectGetKeyConst( onode->item, "direction" )) ) {
2004                                                 direction = jsonObjectToSimpleString(tmp_const);
2005                                                 if (!strncasecmp(direction, "d", 1)) {
2006                                                         free(direction);
2007                                                         direction = " DESC";
2008                                                 } else {
2009                                                         free(direction);
2010                                                         direction = " ASC";
2011                                                 }
2012                                         }
2013
2014                                 } else {
2015                                         string = strdup(onode->key);
2016                                         direction = jsonObjectToSimpleString(onode->item);
2017                                         if (!strncasecmp(direction, "d", 1)) {
2018                                                 free(direction);
2019                                                 direction = " DESC";
2020                                         } else {
2021                                                 free(direction);
2022                                                 direction = " ASC";
2023                                         }
2024                                 }
2025
2026                                 if (first) {
2027                                         first = 0;
2028                                 } else {
2029                                         buffer_add(order_buf, ", ");
2030                                 }
2031
2032                                 buffer_add(order_buf, string);
2033                                 free(string);
2034
2035                                 if (direction) {
2036                                         buffer_add(order_buf, direction);
2037                                 }
2038
2039                         }
2040
2041                 } else if ( snode->item->type == JSON_ARRAY ) {
2042
2043                         while ( (onode = jsonObjectIteratorNext( order_itr )) ) {
2044
2045                                 char* _f = jsonObjectToSimpleString( onode->item );
2046
2047                                 if (!oilsIDLFindPath( "/%s/fields/%s", snode->key, _f))
2048                                         continue;
2049
2050                                 if (first) {
2051                                         first = 0;
2052                                 } else {
2053                                         buffer_add(order_buf, ", ");
2054                                 }
2055
2056                                 buffer_add(order_buf, _f);
2057                                 free(_f);
2058
2059                         }
2060
2061
2062                 // IT'S THE OOOOOOOOOOOLD STYLE!
2063                 } else {
2064                         osrfLogError(OSRF_LOG_MARK, "%s: Possible SQL injection attempt; direct order by is not allowed", MODULENAME);
2065                         osrfAppSessionStatus(
2066                                 ctx->session,
2067                                 OSRF_STATUS_INTERNALSERVERERROR,
2068                                 "osrfMethodException",
2069                                 ctx->request,
2070                                 "Severe query error -- see error log for more details"
2071                         );
2072
2073                         free(core_class);
2074                         buffer_free(having_buf);
2075                         buffer_free(group_buf);
2076                         buffer_free(order_buf);
2077                         buffer_free(sql_buf);
2078             jsonObjectIteratorFree(order_itr);
2079                         if (defaultselhash) jsonObjectFree(defaultselhash);
2080                         return NULL;
2081                 }
2082
2083         jsonObjectIteratorFree(order_itr);
2084
2085         }
2086
2087     jsonObjectIteratorFree(class_itr);
2088
2089         string = buffer_release(group_buf);
2090
2091         if (strlen(string)) {
2092                 buffer_fadd(
2093                         sql_buf,
2094                         " GROUP BY %s",
2095                         string
2096                 );
2097         }
2098
2099         free(string);
2100
2101         string = buffer_release(having_buf);
2102  
2103         if (strlen(string)) {
2104                 buffer_fadd(
2105                         sql_buf,
2106                         " HAVING %s",
2107                         string
2108                 );
2109         }
2110
2111         free(string);
2112
2113         string = buffer_release(order_buf);
2114
2115         if (strlen(string)) {
2116                 buffer_fadd(
2117                         sql_buf,
2118                         " ORDER BY %s",
2119                         string
2120                 );
2121         }
2122
2123         free(string);
2124
2125         if ( limit ){
2126                 string = jsonObjectToSimpleString(limit);
2127                 buffer_fadd( sql_buf, " LIMIT %d", atoi(string) );
2128                 free(string);
2129         }
2130
2131         if (offset) {
2132                 string = jsonObjectToSimpleString(offset);
2133                 buffer_fadd( sql_buf, " OFFSET %d", atoi(string) );
2134                 free(string);
2135         }
2136
2137         buffer_add(sql_buf, ";");
2138
2139         free(core_class);
2140         if (defaultselhash) jsonObjectFree(defaultselhash);
2141
2142         return buffer_release(sql_buf);
2143
2144 }
2145
2146 static char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
2147
2148         const char* locale = osrf_message_get_last_locale();
2149
2150         osrfHash* fields = osrfHashGet(meta, "fields");
2151         char* core_class = osrfHashGet(meta, "classname");
2152
2153         const jsonObject* join_hash = jsonObjectGetKeyConst( order_hash, "join" );
2154
2155         jsonObjectNode* node = NULL;
2156         jsonObjectNode* snode = NULL;
2157         jsonObjectNode* onode = NULL;
2158         const jsonObject* _tmp = NULL;
2159         jsonObject* selhash = NULL;
2160         jsonObject* defaultselhash = NULL;
2161
2162         growing_buffer* sql_buf = buffer_init(128);
2163         growing_buffer* select_buf = buffer_init(128);
2164
2165         if ( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
2166                 defaultselhash = jsonParseString( "{}" );
2167                 selhash = defaultselhash;
2168         }
2169         
2170         if ( !jsonObjectGetKeyConst(selhash,core_class) ) {
2171                 jsonObjectSetKey( selhash, core_class, jsonParseString( "[]" ) );
2172                 jsonObject* flist = jsonObjectGetKey( selhash, core_class );
2173                 
2174                 int i = 0;
2175                 char* field;
2176
2177                 osrfStringArray* keys = osrfHashKeys( fields );
2178                 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
2179                         if ( strcasecmp( "true", osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
2180                                 jsonObjectPush( flist, jsonNewObject( field ) );
2181                 }
2182                 osrfStringArrayFree(keys);
2183         }
2184
2185         int first = 1;
2186         jsonObjectIterator* class_itr = jsonNewObjectIterator( selhash );
2187         while ( (snode = jsonObjectIteratorNext( class_itr )) ) {
2188
2189                 osrfHash* idlClass = osrfHashGet( oilsIDL(), snode->key );
2190                 if (!idlClass) continue;
2191                 char* cname = osrfHashGet(idlClass, "classname");
2192
2193                 if (strcmp(core_class,snode->key)) {
2194                         if (!join_hash) continue;
2195
2196                         jsonObject* found =  jsonObjectFindPath(join_hash, "//%s", snode->key);
2197                         if (!found->size) {
2198                                 jsonObjectFree(found);
2199                                 continue;
2200                         }
2201
2202                         jsonObjectFree(found);
2203                 }
2204
2205                 jsonObjectIterator* select_itr = jsonNewObjectIterator( snode->item );
2206                 while ( (node = jsonObjectIteratorNext( select_itr )) ) {
2207                         osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), jsonObjectToSimpleString(node->item) );
2208                         char* fname = osrfHashGet(field, "name");
2209
2210                         if (!field) continue;
2211
2212                         if (first) {
2213                                 first = 0;
2214                         } else {
2215                                 buffer_add(select_buf, ",");
2216                         }
2217
2218             if (locale) {
2219                         char* i18n = osrfHashGet(field, "i18n");
2220                     if (jsonBoolIsTrue(jsonObjectGetKey( order_hash, "no_i18n" )))
2221                     i18n = NULL;
2222
2223                         if ( i18n && !strncasecmp("true", i18n, 4)) {
2224                         char* pkey = osrfHashGet(idlClass, "primarykey");
2225                         char* tname = osrfHashGet(idlClass, "tablename");
2226
2227                     buffer_fadd(select_buf, " oils_i18n_xlate('%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"", tname, fname, pkey, cname, pkey, locale, fname);
2228                 } else {
2229                                 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
2230                 }
2231             } else {
2232                             buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
2233             }
2234                 }
2235
2236         jsonObjectIteratorFree(select_itr);
2237         }
2238
2239     jsonObjectIteratorFree(class_itr);
2240
2241         char* col_list = buffer_release(select_buf);
2242         char* table = getSourceDefinition(meta);
2243
2244         buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\"", col_list, table, core_class );
2245         free(col_list);
2246         free(table);
2247
2248         if ( join_hash ) {
2249                 char* join_clause = searchJOIN( join_hash, meta );
2250                 buffer_fadd(sql_buf, " %s", join_clause);
2251                 free(join_clause);
2252         }
2253
2254         char* tmpsql = buffer_data(sql_buf);
2255         osrfLogDebug(OSRF_LOG_MARK, "%s pre-predicate SQL =  %s", MODULENAME, tmpsql);
2256         free(tmpsql);
2257
2258         buffer_add(sql_buf, " WHERE ");
2259
2260         char* pred = searchWHERE( search_hash, meta, AND_OP_JOIN );
2261         if (!pred) {
2262                 osrfAppSessionStatus(
2263                         ctx->session,
2264                         OSRF_STATUS_INTERNALSERVERERROR,
2265                                 "osrfMethodException",
2266                                 ctx->request,
2267                                 "Severe query error -- see error log for more details"
2268                         );
2269                 buffer_free(sql_buf);
2270                 return NULL;
2271         } else {
2272                 buffer_add(sql_buf, pred);
2273                 free(pred);
2274         }
2275
2276         if (order_hash) {
2277                 char* string = NULL;
2278                 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "order_by" )) ){
2279
2280                         growing_buffer* order_buf = buffer_init(128);
2281
2282                         first = 1;
2283                         jsonObjectIterator* class_itr = jsonNewObjectIterator( _tmp );
2284                         while ( (snode = jsonObjectIteratorNext( class_itr )) ) {
2285
2286                                 if (!jsonObjectGetKeyConst(selhash,snode->key))
2287                                         continue;
2288
2289                                 if ( snode->item->type == JSON_HASH ) {
2290
2291                                         jsonObjectIterator* order_itr = jsonNewObjectIterator( snode->item );
2292                                         while ( (onode = jsonObjectIteratorNext( order_itr )) ) {
2293
2294                                                 if (!oilsIDLFindPath( "/%s/fields/%s", snode->key, onode->key ))
2295                                                         continue;
2296
2297                                                 char* direction = NULL;
2298                                                 if ( onode->item->type == JSON_HASH ) {
2299                                                         if ( jsonObjectGetKeyConst( onode->item, "transform" ) ) {
2300                                                                 string = searchFieldTransform(
2301                                                                         snode->key,
2302                                                                         oilsIDLFindPath( "/%s/fields/%s", snode->key, onode->key ),
2303                                                                         onode->item
2304                                                                 );
2305                                                         } else {
2306                                                                 growing_buffer* field_buf = buffer_init(16);
2307                                                                 buffer_fadd(field_buf, "\"%s\".%s", snode->key, onode->key);
2308                                                                 string = buffer_release(field_buf);
2309                                                         }
2310
2311                                                         if ( (_tmp = jsonObjectGetKeyConst( onode->item, "direction" )) ) {
2312                                                                 direction = jsonObjectToSimpleString(_tmp);
2313                                                                 if (!strncasecmp(direction, "d", 1)) {
2314                                                                         free(direction);
2315                                                                         direction = " DESC";
2316                                                                 } else {
2317                                                                         free(direction);
2318                                                                         direction = " ASC";
2319                                                                 }
2320                                                         }
2321
2322                                                 } else {
2323                                                         string = strdup(onode->key);
2324                                                         direction = jsonObjectToSimpleString(onode->item);
2325                                                         if (!strncasecmp(direction, "d", 1)) {
2326                                                                 free(direction);
2327                                                                 direction = " DESC";
2328                                                         } else {
2329                                                                 free(direction);
2330                                                                 direction = " ASC";
2331                                                         }
2332                                                 }
2333
2334                                                 if (first) {
2335                                                         first = 0;
2336                                                 } else {
2337                                                         buffer_add(order_buf, ", ");
2338                                                 }
2339
2340                                                 buffer_add(order_buf, string);
2341                                                 free(string);
2342
2343                                                 if (direction) {
2344                                                         buffer_add(order_buf, direction);
2345                                                 }
2346
2347                                         }
2348
2349                     jsonObjectIteratorFree(order_itr);
2350
2351                                 } else {
2352                                         string = jsonObjectToSimpleString(snode->item);
2353                                         buffer_add(order_buf, string);
2354                                         free(string);
2355                                         break;
2356                                 }
2357
2358                         }
2359
2360             jsonObjectIteratorFree(class_itr);
2361
2362                         string = buffer_release(order_buf);
2363
2364                         if (strlen(string)) {
2365                                 buffer_fadd(
2366                                         sql_buf,
2367                                         " ORDER BY %s",
2368                                         string
2369                                 );
2370                         }
2371
2372                         free(string);
2373                 }
2374
2375                 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "limit" )) ){
2376                         string = jsonObjectToSimpleString(_tmp);
2377                         buffer_fadd(
2378                                 sql_buf,
2379                                 " LIMIT %d",
2380                                 atoi(string)
2381                         );
2382                         free(string);
2383                 }
2384
2385                 _tmp = jsonObjectGetKeyConst( order_hash, "offset" );
2386                 if (_tmp) {
2387                         string = jsonObjectToSimpleString(_tmp);
2388                         buffer_fadd(
2389                                 sql_buf,
2390                                 " OFFSET %d",
2391                                 atoi(string)
2392                         );
2393                         free(string);
2394                 }
2395         }
2396
2397         if (defaultselhash) jsonObjectFree(defaultselhash);
2398
2399         buffer_add(sql_buf, ";");
2400         return buffer_release(sql_buf);
2401 }
2402
2403 int doJSONSearch ( osrfMethodContext* ctx ) {
2404         OSRF_METHOD_VERIFY_CONTEXT(ctx);
2405         osrfLogDebug(OSRF_LOG_MARK, "Recieved query request");
2406
2407         int err = 0;
2408
2409         // XXX for now...
2410         dbhandle = writehandle;
2411
2412         jsonObject* hash = jsonObjectGetIndex(ctx->params, 0);
2413
2414     int flags = 0;
2415
2416         if (jsonBoolIsTrue(jsonObjectGetKey( hash, "distinct" )))
2417          flags |= SELECT_DISTINCT;
2418
2419         if (jsonBoolIsTrue(jsonObjectGetKey( hash, "no_i18n" )))
2420          flags |= DISABLE_I18N;
2421
2422         osrfLogDebug(OSRF_LOG_MARK, "Building SQL ...");
2423         char* sql = SELECT(
2424                         ctx,
2425                         jsonObjectGetKey( hash, "select" ),
2426                         jsonObjectGetKey( hash, "from" ),
2427                         jsonObjectGetKey( hash, "where" ),
2428                         jsonObjectGetKey( hash, "having" ),
2429                         jsonObjectGetKey( hash, "order_by" ),
2430                         jsonObjectGetKey( hash, "limit" ),
2431                         jsonObjectGetKey( hash, "offset" ),
2432                         flags
2433         );
2434
2435         if (!sql) {
2436                 err = -1;
2437                 return err;
2438         }
2439         
2440         osrfLogDebug(OSRF_LOG_MARK, "%s SQL =  %s", MODULENAME, sql);
2441         dbi_result result = dbi_conn_query(dbhandle, sql);
2442
2443         if(result) {
2444                 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
2445
2446                 if (dbi_result_first_row(result)) {
2447                         /* JSONify the result */
2448                         osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
2449
2450                         do {
2451                                 jsonObject* return_val = oilsMakeJSONFromResult( result );
2452                                 osrfAppRespond( ctx, return_val );
2453                 jsonObjectFree( return_val );
2454                         } while (dbi_result_next_row(result));
2455
2456                 } else {
2457                         osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
2458                 }
2459
2460                 osrfAppRespondComplete( ctx, NULL );
2461
2462                 /* clean up the query */
2463                 dbi_result_free(result); 
2464
2465         } else {
2466                 err = -1;
2467                 osrfLogError(OSRF_LOG_MARK, "%s: Error with query [%s]", MODULENAME, sql);
2468                 osrfAppSessionStatus(
2469                         ctx->session,
2470                         OSRF_STATUS_INTERNALSERVERERROR,
2471                         "osrfMethodException",
2472                         ctx->request,
2473                         "Severe query error -- see error log for more details"
2474                 );
2475         }
2476
2477         free(sql);
2478         return err;
2479 }
2480
2481 static jsonObject* doFieldmapperSearch ( osrfMethodContext* ctx, osrfHash* meta,
2482                 const jsonObject* params, int* err ) {
2483
2484         // XXX for now...
2485         dbhandle = writehandle;
2486
2487         osrfHash* links = osrfHashGet(meta, "links");
2488         osrfHash* fields = osrfHashGet(meta, "fields");
2489         char* core_class = osrfHashGet(meta, "classname");
2490         char* pkey = osrfHashGet(meta, "primarykey");
2491
2492         const jsonObject* _tmp;
2493         jsonObject* obj;
2494         jsonObject* search_hash = jsonObjectGetIndex(params, 0);
2495         jsonObject* order_hash = jsonObjectGetIndex(params, 1);
2496
2497         char* sql = buildSELECT( search_hash, order_hash, meta, ctx );
2498         if (!sql) {
2499                 *err = -1;
2500                 return NULL;
2501         }
2502         
2503         osrfLogDebug(OSRF_LOG_MARK, "%s SQL =  %s", MODULENAME, sql);
2504         dbi_result result = dbi_conn_query(dbhandle, sql);
2505
2506         osrfHash* dedup = osrfNewHash();
2507         jsonObject* res_list = jsonParseString("[]");
2508         if(result) {
2509                 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
2510
2511                 if (dbi_result_first_row(result)) {
2512                         /* JSONify the result */
2513                         osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
2514                         do {
2515                                 obj = oilsMakeFieldmapperFromResult( result, meta );
2516                                 int pkey_pos = atoi( osrfHashGet( osrfHashGet( fields, pkey ), "array_position" ) );
2517                                 char* pkey_val = jsonObjectToSimpleString( jsonObjectGetIndex( obj, pkey_pos ) );
2518                                 if ( osrfHashGet( dedup, pkey_val ) ) {
2519                                         jsonObjectFree(obj);
2520                                 } else {
2521                                         osrfHashSet( dedup, pkey_val, pkey_val );
2522                                         jsonObjectPush(res_list, obj);
2523                                 }
2524                         } while (dbi_result_next_row(result));
2525                 } else {
2526                         osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
2527                 }
2528
2529                 /* clean up the query */
2530                 dbi_result_free(result); 
2531
2532         } else {
2533                 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]", MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
2534                 osrfAppSessionStatus(
2535                         ctx->session,
2536                         OSRF_STATUS_INTERNALSERVERERROR,
2537                         "osrfMethodException",
2538                         ctx->request,
2539                         "Severe query error -- see error log for more details"
2540                 );
2541                 *err = -1;
2542                 free(sql);
2543                 jsonObjectFree(res_list);
2544                 return jsonNULL;
2545
2546         }
2547
2548         free(sql);
2549
2550         if (res_list->size && order_hash) {
2551                 _tmp = jsonObjectGetKeyConst( order_hash, "flesh" );
2552                 if (_tmp) {
2553                         int x = (int)jsonObjectGetNumber(_tmp);
2554                         if (x == -1 || x > max_flesh_depth) x = max_flesh_depth;
2555
2556                         const jsonObject* temp_blob;
2557                         if ((temp_blob = jsonObjectGetKeyConst( order_hash, "flesh_fields" )) && x > 0) {
2558
2559                                 jsonObject* flesh_blob = jsonObjectClone( temp_blob );
2560                                 const jsonObject* flesh_fields = jsonObjectGetKeyConst( flesh_blob, core_class );
2561
2562                                 osrfStringArray* link_fields = NULL;
2563
2564                                 if (flesh_fields) {
2565                                         if (flesh_fields->size == 1) {
2566                                                 char* _t = jsonObjectToSimpleString( jsonObjectGetIndex( flesh_fields, 0 ) );
2567                                                 if (!strcmp(_t,"*")) link_fields = osrfHashKeys( links );
2568                                                 free(_t);
2569                                         }
2570
2571                                         if (!link_fields) {
2572                                                 jsonObjectNode* _f;
2573                                                 link_fields = osrfNewStringArray(1);
2574                                                 jsonObjectIterator* _i = jsonNewObjectIterator( flesh_fields );
2575                                                 while ((_f = jsonObjectIteratorNext( _i ))) {
2576                                                         osrfStringArrayAdd( link_fields, jsonObjectToSimpleString( _f->item ) );
2577                                                 }
2578                         jsonObjectIteratorFree(_i);
2579                                         }
2580                                 }
2581
2582                                 jsonObjectNode* cur;
2583                                 jsonObjectIterator* itr = jsonNewObjectIterator( res_list );
2584                                 while ((cur = jsonObjectIteratorNext( itr ))) {
2585
2586                                         int i = 0;
2587                                         char* link_field;
2588                                         
2589                                         while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
2590
2591                                                 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
2592
2593                                                 osrfHash* kid_link = osrfHashGet(links, link_field);
2594                                                 if (!kid_link) continue;
2595
2596                                                 osrfHash* field = osrfHashGet(fields, link_field);
2597                                                 if (!field) continue;
2598
2599                                                 osrfHash* value_field = field;
2600
2601                                                 osrfHash* kid_idl = osrfHashGet(oilsIDL(), osrfHashGet(kid_link, "class"));
2602                                                 if (!kid_idl) continue;
2603
2604                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
2605                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
2606                                                 }
2607                                                         
2608                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
2609                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
2610                                                 }
2611
2612                                                 osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
2613
2614                                                 if (link_map->size > 0) {
2615                                                         jsonObject* _kid_key = jsonParseString("[]");
2616                                                         jsonObjectPush(
2617                                                                 _kid_key,
2618                                                                 jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
2619                                                         );
2620
2621                                                         jsonObjectSetKey(
2622                                                                 flesh_blob,
2623                                                                 osrfHashGet(kid_link, "class"),
2624                                                                 _kid_key
2625                                                         );
2626                                                 };
2627
2628                                                 osrfLogDebug(
2629                                                         OSRF_LOG_MARK,
2630                                                         "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
2631                                                         osrfHashGet(kid_link, "field"),
2632                                                         osrfHashGet(kid_link, "class"),
2633                                                         osrfHashGet(kid_link, "key"),
2634                                                         osrfHashGet(kid_link, "reltype")
2635                                                 );
2636
2637                                                 jsonObject* fake_params = jsonParseString("[]");
2638                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // search hash
2639                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // order/flesh hash
2640
2641                                                 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
2642
2643                                                 char* search_key =
2644                                                 jsonObjectToSimpleString(
2645                                                         jsonObjectGetIndex(
2646                                                                 cur->item,
2647                                                                 atoi( osrfHashGet(value_field, "array_position") )
2648                                                         )
2649                                                 );
2650
2651                                                 if (!search_key) {
2652                                                         osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
2653                                                         continue;
2654                                                 }
2655                                                         
2656                                                 jsonObjectSetKey(
2657                                                         jsonObjectGetIndex(fake_params, 0),
2658                                                         osrfHashGet(kid_link, "key"),
2659                                                         jsonNewObject( search_key )
2660                                                 );
2661
2662                                                 free(search_key);
2663
2664
2665                                                 jsonObjectSetKey(
2666                                                         jsonObjectGetIndex(fake_params, 1),
2667                                                         "flesh",
2668                                                         jsonNewNumberObject( (double)(x - 1 + link_map->size) )
2669                                                 );
2670
2671                                                 if (flesh_blob)
2672                                                         jsonObjectSetKey( jsonObjectGetIndex(fake_params, 1), "flesh_fields", jsonObjectClone(flesh_blob) );
2673
2674                                                 if (jsonObjectGetKeyConst(order_hash, "order_by")) {
2675                                                         jsonObjectSetKey(
2676                                                                 jsonObjectGetIndex(fake_params, 1),
2677                                                                 "order_by",
2678                                                                 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "order_by"))
2679                                                         );
2680                                                 }
2681
2682                                                 if (jsonObjectGetKeyConst(order_hash, "select")) {
2683                                                         jsonObjectSetKey(
2684                                                                 jsonObjectGetIndex(fake_params, 1),
2685                                                                 "select",
2686                                                                 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "select"))
2687                                                         );
2688                                                 }
2689
2690                                                 jsonObject* kids = doFieldmapperSearch(ctx, kid_idl, fake_params, err);
2691
2692                                                 if(*err) {
2693                                                         jsonObjectFree( fake_params );
2694                                                         osrfStringArrayFree(link_fields);
2695                                                         jsonObjectIteratorFree(itr);
2696                                                         jsonObjectFree(res_list);
2697                                                         return jsonNULL;
2698                                                 }
2699
2700                                                 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
2701
2702                                                 jsonObject* X = NULL;
2703                                                 if ( link_map->size > 0 && kids->size > 0 ) {
2704                                                         X = kids;
2705                                                         kids = jsonParseString("[]");
2706
2707                                                         jsonObjectNode* _k_node;
2708                                                         jsonObjectIterator* _k = jsonNewObjectIterator( X );
2709                                                         while ((_k_node = jsonObjectIteratorNext( _k ))) {
2710                                                                 jsonObjectPush(
2711                                                                         kids,
2712                                                                         jsonObjectClone(
2713                                                                                 jsonObjectGetIndex(
2714                                                                                         _k_node->item,
2715                                                                                         (unsigned long)atoi(
2716                                                                                                 osrfHashGet(
2717                                                                                                         osrfHashGet(
2718                                                                                                                 osrfHashGet(
2719                                                                                                                         osrfHashGet(
2720                                                                                                                                 oilsIDL(),
2721                                                                                                                                 osrfHashGet(kid_link, "class")
2722                                                                                                                         ),
2723                                                                                                                         "fields"
2724                                                                                                                 ),
2725                                                                                                                 osrfStringArrayGetString( link_map, 0 )
2726                                                                                                         ),
2727                                                                                                         "array_position"
2728                                                                                                 )
2729                                                                                         )
2730                                                                                 )
2731                                                                         )
2732                                                                 );
2733                                                         }
2734                                                 }
2735
2736                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" )) || !(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) {
2737                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
2738                                                         jsonObjectSetIndex(
2739                                                                 cur->item,
2740                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
2741                                                                 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
2742                                                         );
2743                                                 }
2744
2745                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
2746                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
2747                                                         jsonObjectSetIndex(
2748                                                                 cur->item,
2749                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
2750                                                                 jsonObjectClone( kids )
2751                                                         );
2752                                                 }
2753
2754                                                 if (X) {
2755                                                         jsonObjectFree(kids);
2756                                                         kids = X;
2757                                                 }
2758
2759                                                 jsonObjectFree( kids );
2760                                                 jsonObjectFree( fake_params );
2761
2762                                                 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
2763                                                 osrfLogDebug(OSRF_LOG_MARK, "%s", jsonObjectToJSON(cur->item));
2764
2765                                         }
2766                                 }
2767                                 jsonObjectFree( flesh_blob );
2768                                 osrfStringArrayFree(link_fields);
2769                                 jsonObjectIteratorFree(itr);
2770                         }
2771                 }
2772         }
2773
2774         return res_list;
2775 }
2776
2777
2778 static jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
2779
2780         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
2781         jsonObject* target = jsonObjectGetIndex(ctx->params, 0);
2782
2783         if (!verifyObjectClass(ctx, target)) {
2784                 *err = -1;
2785                 return jsonNULL;
2786         }
2787
2788         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
2789                 osrfAppSessionStatus(
2790                         ctx->session,
2791                         OSRF_STATUS_BADREQUEST,
2792                         "osrfMethodException",
2793                         ctx->request,
2794                         "No active transaction -- required for UPDATE"
2795                 );
2796                 *err = -1;
2797                 return jsonNULL;
2798         }
2799
2800         if (osrfHashGet( meta, "readonly" ) && strncasecmp("true", osrfHashGet( meta, "readonly" ), 4)) {
2801                 osrfAppSessionStatus(
2802                         ctx->session,
2803                         OSRF_STATUS_BADREQUEST,
2804                         "osrfMethodException",
2805                         ctx->request,
2806                         "Cannot UPDATE readonly class"
2807                 );
2808                 *err = -1;
2809                 return jsonNULL;
2810         }
2811
2812         dbhandle = writehandle;
2813
2814         char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
2815
2816         // Set the last_xact_id
2817         osrfHash* last_xact_id;
2818         if ((last_xact_id = oilsIDLFindPath("/%s/fields/last_xact_id", target->classname))) {
2819                 int index = atoi( osrfHashGet(last_xact_id, "array_position") );
2820                 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
2821                 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
2822         }       
2823
2824         char* pkey = osrfHashGet(meta, "primarykey");
2825         osrfHash* fields = osrfHashGet(meta, "fields");
2826
2827         char* id =
2828                 jsonObjectToSimpleString(
2829                         jsonObjectGetIndex(
2830                                 target,
2831                                 atoi( osrfHashGet( osrfHashGet( fields, pkey ), "array_position" ) )
2832                         )
2833                 );
2834
2835         osrfLogDebug(
2836                 OSRF_LOG_MARK,
2837                 "%s updating %s object with %s = %s",
2838                 MODULENAME,
2839                 osrfHashGet(meta, "fieldmapper"),
2840                 pkey,
2841                 id
2842         );
2843
2844         growing_buffer* sql = buffer_init(128);
2845         buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
2846
2847         int i = 0;
2848         int first = 1;
2849         char* field_name;
2850         osrfStringArray* field_list = osrfHashKeys( fields );
2851         while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
2852
2853                 osrfHash* field = osrfHashGet( fields, field_name );
2854
2855                 if(!( strcmp( field_name, pkey ) )) continue;
2856                 if(!( strcmp( osrfHashGet(osrfHashGet(fields,field_name), "virtual"), "true" ) )) continue;
2857
2858                 jsonObject* field_object = jsonObjectGetIndex( target, atoi(osrfHashGet(field, "array_position")) );
2859
2860                 char* value;
2861                 if (field_object && field_object->classname) {
2862                         value = jsonObjectToSimpleString(
2863                                         jsonObjectGetIndex(
2864                                                 field_object,
2865                                                 atoi(
2866                                                         osrfHashGet(
2867                                                                 osrfHashGet(
2868                                                                         oilsIDLFindPath("/%s/fields", field_object->classname),
2869                                                                         (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
2870                                                                 ),
2871                                                                 "array_position"
2872                                                         )
2873                                                 )
2874                                         )
2875                                 );
2876
2877                 } else {
2878                         value = jsonObjectToSimpleString( field_object );
2879                 }
2880
2881                 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
2882
2883                 if (!field_object || field_object->type == JSON_NULL) {
2884                         if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
2885                                 if (first) first = 0;
2886                                 else buffer_add(sql, ",");
2887                                 buffer_fadd( sql, " %s = NULL", field_name );
2888                         }
2889                         
2890                 } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
2891                         if (first) first = 0;
2892                         else buffer_add(sql, ",");
2893
2894                         if ( !strncmp(osrfHashGet(field, "datatype"), "INT", (size_t)3) ) {
2895                                 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
2896                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
2897                                 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
2898                         }
2899
2900                         osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, osrfHashGet(field, "datatype"));
2901
2902                 } else {
2903                         if ( dbi_conn_quote_string(dbhandle, &value) ) {
2904                                 if (first) first = 0;
2905                                 else buffer_add(sql, ",");
2906                                 buffer_fadd( sql, " %s = %s", field_name, value );
2907
2908                         } else {
2909                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
2910                                 osrfAppSessionStatus(
2911                                         ctx->session,
2912                                         OSRF_STATUS_INTERNALSERVERERROR,
2913                                         "osrfMethodException",
2914                                         ctx->request,
2915                                         "Error quoting string -- please see the error log for more details"
2916                                 );
2917                                 free(value);
2918                                 free(id);
2919                                 buffer_free(sql);
2920                                 *err = -1;
2921                                 return jsonNULL;
2922                         }
2923                 }
2924
2925                 free(value);
2926                 
2927         }
2928
2929         jsonObject* obj = jsonParseString(id);
2930
2931         if ( strcmp( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "primitive" ), "number" ) )
2932                 dbi_conn_quote_string(dbhandle, &id);
2933
2934         buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
2935
2936         char* query = buffer_release(sql);
2937         osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
2938
2939         dbi_result result = dbi_conn_query(dbhandle, query);
2940         free(query);
2941
2942         if (!result) {
2943                 jsonObjectFree(obj);
2944                 obj = jsonNewObject(NULL);
2945                 osrfLogError(
2946                         OSRF_LOG_MARK,
2947                         "%s ERROR updating %s object with %s = %s",
2948                         MODULENAME,
2949                         osrfHashGet(meta, "fieldmapper"),
2950                         pkey,
2951                         id
2952                 );
2953         }
2954
2955         free(id);
2956
2957         return obj;
2958 }
2959
2960 static jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
2961
2962         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
2963
2964         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
2965                 osrfAppSessionStatus(
2966                         ctx->session,
2967                         OSRF_STATUS_BADREQUEST,
2968                         "osrfMethodException",
2969                         ctx->request,
2970                         "No active transaction -- required for DELETE"
2971                 );
2972                 *err = -1;
2973                 return jsonNULL;
2974         }
2975
2976         if (osrfHashGet( meta, "readonly" ) && strncasecmp("true", osrfHashGet( meta, "readonly" ), 4)) {
2977                 osrfAppSessionStatus(
2978                         ctx->session,
2979                         OSRF_STATUS_BADREQUEST,
2980                         "osrfMethodException",
2981                         ctx->request,
2982                         "Cannot DELETE readonly class"
2983                 );
2984                 *err = -1;
2985                 return jsonNULL;
2986         }
2987
2988         dbhandle = writehandle;
2989
2990         jsonObject* obj;
2991
2992         char* pkey = osrfHashGet(meta, "primarykey");
2993
2994         char* id;
2995         if (jsonObjectGetIndex(ctx->params, 0)->classname) {
2996                 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, 0 ))) {
2997                         *err = -1;
2998                         return jsonNULL;
2999                 }
3000
3001                 id = jsonObjectToSimpleString(
3002                         jsonObjectGetIndex(
3003                                 jsonObjectGetIndex(ctx->params, 0),
3004                                 atoi( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "array_position") )
3005                         )
3006                 );
3007         } else {
3008                 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
3009         }
3010
3011         osrfLogDebug(
3012                 OSRF_LOG_MARK,
3013                 "%s deleting %s object with %s = %s",
3014                 MODULENAME,
3015                 osrfHashGet(meta, "fieldmapper"),
3016                 pkey,
3017                 id
3018         );
3019
3020         obj = jsonParseString(id);
3021
3022         if ( strcmp( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "primitive" ), "number" ) )
3023                 dbi_conn_quote_string(writehandle, &id);
3024
3025         dbi_result result = dbi_conn_queryf(writehandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
3026
3027         if (!result) {
3028                 jsonObjectFree(obj);
3029                 obj = jsonNewObject(NULL);
3030                 osrfLogError(
3031                         OSRF_LOG_MARK,
3032                         "%s ERROR deleting %s object with %s = %s",
3033                         MODULENAME,
3034                         osrfHashGet(meta, "fieldmapper"),
3035                         pkey,
3036                         id
3037                 );
3038         }
3039
3040         free(id);
3041
3042         return obj;
3043
3044 }
3045
3046
3047 static jsonObject* oilsMakeFieldmapperFromResult( dbi_result result, osrfHash* meta) {
3048         if(!(result && meta)) return jsonNULL;
3049
3050         jsonObject* object = jsonNewObject(NULL);
3051         jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
3052
3053         osrfHash* fields = osrfHashGet(meta, "fields");
3054
3055         osrfLogInternal(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
3056
3057         osrfHash* _f;
3058         time_t _tmp_dt;
3059         char dt_string[256];
3060         struct tm gmdt;
3061
3062         int fmIndex;
3063         int columnIndex = 1;
3064         int attr;
3065         unsigned short type;
3066         const char* columnName;
3067
3068         /* cycle through the column list */
3069         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
3070
3071                 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
3072
3073                 fmIndex = -1; // reset the position
3074                 
3075                 /* determine the field type and storage attributes */
3076                 type = dbi_result_get_field_type(result, columnName);
3077                 attr = dbi_result_get_field_attribs(result, columnName);
3078
3079                 /* fetch the fieldmapper index */
3080                 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
3081                         char* virt = (char*)osrfHashGet(_f, "virtual");
3082                         char* pos = (char*)osrfHashGet(_f, "array_position");
3083
3084                         if ( !virt || !pos || !(strcmp( virt, "true" )) ) continue;
3085
3086                         fmIndex = atoi( pos );
3087                         osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
3088                 } else {
3089                         continue;
3090                 }
3091
3092                 if (dbi_result_field_is_null(result, columnName)) {
3093                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
3094                 } else {
3095
3096                         switch( type ) {
3097
3098                                 case DBI_TYPE_INTEGER :
3099
3100                                         if( attr & DBI_INTEGER_SIZE8 ) 
3101                                                 jsonObjectSetIndex( object, fmIndex, 
3102                                                         jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
3103                                         else 
3104                                                 jsonObjectSetIndex( object, fmIndex, 
3105                                                         jsonNewNumberObject(dbi_result_get_long(result, columnName)));
3106
3107                                         break;
3108
3109                                 case DBI_TYPE_DECIMAL :
3110                                         jsonObjectSetIndex( object, fmIndex, 
3111                                                         jsonNewNumberObject(dbi_result_get_double(result, columnName)));
3112                                         break;
3113
3114                                 case DBI_TYPE_STRING :
3115
3116
3117                                         jsonObjectSetIndex(
3118                                                 object,
3119                                                 fmIndex,
3120                                                 jsonNewObject( dbi_result_get_string(result, columnName) )
3121                                         );
3122
3123                                         break;
3124
3125                                 case DBI_TYPE_DATETIME :
3126
3127                                         memset(dt_string, '\0', sizeof(dt_string));
3128                                         memset(&gmdt, '\0', sizeof(gmdt));
3129                                         memset(&_tmp_dt, '\0', sizeof(_tmp_dt));
3130
3131                                         _tmp_dt = dbi_result_get_datetime(result, columnName);
3132
3133                                         localtime_r( &_tmp_dt, &gmdt );
3134
3135                                         if (!(attr & DBI_DATETIME_DATE)) {
3136                                                 strftime(dt_string, 255, "%T", &gmdt);
3137                                         } else if (!(attr & DBI_DATETIME_TIME)) {
3138                                                 strftime(dt_string, 255, "%F", &gmdt);
3139                                         } else {
3140                                                 strftime(dt_string, 255, "%FT%T%z", &gmdt);
3141                                         }
3142
3143                                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
3144
3145                                         break;
3146
3147                                 case DBI_TYPE_BINARY :
3148                                         osrfLogError( OSRF_LOG_MARK, 
3149                                                 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
3150                         }
3151                 }
3152         }
3153
3154         return object;
3155 }
3156
3157 static jsonObject* oilsMakeJSONFromResult( dbi_result result ) {
3158         if(!result) return jsonNULL;
3159
3160         jsonObject* object = jsonNewObject(NULL);
3161
3162         time_t _tmp_dt;
3163         char dt_string[256];
3164         struct tm gmdt;
3165
3166         int fmIndex;
3167         int columnIndex = 1;
3168         int attr;
3169         unsigned short type;
3170         const char* columnName;
3171
3172         /* cycle through the column list */
3173         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
3174
3175                 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
3176
3177                 fmIndex = -1; // reset the position
3178                 
3179                 /* determine the field type and storage attributes */
3180                 type = dbi_result_get_field_type(result, columnName);
3181                 attr = dbi_result_get_field_attribs(result, columnName);
3182
3183                 if (dbi_result_field_is_null(result, columnName)) {
3184                         jsonObjectSetKey( object, columnName, jsonNewObject(NULL) );
3185                 } else {
3186
3187                         switch( type ) {
3188
3189                                 case DBI_TYPE_INTEGER :
3190
3191                                         if( attr & DBI_INTEGER_SIZE8 ) 
3192                                                 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_longlong(result, columnName)) );
3193                                         else 
3194                                                 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_long(result, columnName)) );
3195                                         break;
3196
3197                                 case DBI_TYPE_DECIMAL :
3198                                         jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_double(result, columnName)) );
3199                                         break;
3200
3201                                 case DBI_TYPE_STRING :
3202                                         jsonObjectSetKey( object, columnName, jsonNewObject(dbi_result_get_string(result, columnName)) );
3203                                         break;
3204
3205                                 case DBI_TYPE_DATETIME :
3206
3207                                         memset(dt_string, '\0', sizeof(dt_string));
3208                                         memset(&gmdt, '\0', sizeof(gmdt));
3209                                         memset(&_tmp_dt, '\0', sizeof(_tmp_dt));
3210
3211                                         _tmp_dt = dbi_result_get_datetime(result, columnName);
3212
3213                                         localtime_r( &_tmp_dt, &gmdt );
3214
3215                                         if (!(attr & DBI_DATETIME_DATE)) {
3216                                                 strftime(dt_string, 255, "%T", &gmdt);
3217                                         } else if (!(attr & DBI_DATETIME_TIME)) {
3218                                                 strftime(dt_string, 255, "%F", &gmdt);
3219                                         } else {
3220                                                 strftime(dt_string, 255, "%FT%T%z", &gmdt);
3221                                         }
3222
3223                                         jsonObjectSetKey( object, columnName, jsonNewObject(dt_string) );
3224                                         break;
3225
3226                                 case DBI_TYPE_BINARY :
3227                                         osrfLogError( OSRF_LOG_MARK, 
3228                                                 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
3229                         }
3230                 }
3231         }
3232
3233         return object;
3234 }
3235