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