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