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