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