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