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