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