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