]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/c-apps/oils_cstore.c
initial test of in-xml views for reporting
[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/utils.h"
4 #include "objson/object.h"
5 #include "opensrf/log.h"
6 #include "oils_idl.h"
7 #include <dbi/dbi.h>
8
9 #include <time.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13
14 #ifdef RSTORE
15 #  define MODULENAME "open-ils.reporter-store"
16 #else
17 #  define MODULENAME "open-ils.cstore"
18 #endif
19
20 #define SELECT_DISTINCT 1
21 #define AND_OP_JOIN     0
22 #define OR_OP_JOIN      1
23
24 int osrfAppChildInit();
25 int osrfAppInitialize();
26 void osrfAppChildExit();
27
28 int verifyObjectClass ( osrfMethodContext*, jsonObject* );
29
30 int beginTransaction ( osrfMethodContext* );
31 int commitTransaction ( osrfMethodContext* );
32 int rollbackTransaction ( osrfMethodContext* );
33
34 int setSavepoint ( osrfMethodContext* );
35 int releaseSavepoint ( osrfMethodContext* );
36 int rollbackSavepoint ( osrfMethodContext* );
37
38 int doJSONSearch ( osrfMethodContext* );
39
40 int dispatchCRUDMethod ( osrfMethodContext* );
41 jsonObject* doCreate ( osrfMethodContext*, int* );
42 jsonObject* doRetrieve ( osrfMethodContext*, int* );
43 jsonObject* doUpdate ( osrfMethodContext*, int* );
44 jsonObject* doDelete ( osrfMethodContext*, int* );
45 jsonObject* doFieldmapperSearch ( osrfMethodContext*, osrfHash*, jsonObject*, int* );
46 jsonObject* oilsMakeFieldmapperFromResult( dbi_result, osrfHash* );
47 jsonObject* oilsMakeJSONFromResult( dbi_result );
48
49 char* searchWriteSimplePredicate ( const char*, osrfHash*, const char*, const char*, const char* );
50 char* searchSimplePredicate ( const char*, const char*, osrfHash*, jsonObject* );
51 char* searchFunctionPredicate ( const char*, osrfHash*, jsonObjectNode* );
52 char* searchFieldTransform (const char*, osrfHash*, jsonObject*);
53 char* searchFieldTransformPredicate ( const char*, osrfHash*, jsonObjectNode* );
54 char* searchBETWEENPredicate ( const char*, osrfHash*, jsonObject* );
55 char* searchINPredicate ( const char*, osrfHash*, jsonObject*, const char* );
56 char* searchPredicate ( const char*, osrfHash*, jsonObject* );
57 char* searchJOIN ( jsonObject*, osrfHash* );
58 char* searchWHERE ( jsonObject*, osrfHash*, int );
59 char* buildSELECT ( jsonObject*, jsonObject*, osrfHash*, osrfMethodContext* );
60
61 char* SELECT ( osrfMethodContext*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, int );
62
63 void userDataFree( void* );
64 void sessionDataFree( char*, void* );
65
66 dbi_conn writehandle; /* our MASTER db connection */
67 dbi_conn dbhandle; /* our CURRENT db connection */
68 osrfHash readHandles;
69 jsonObject* jsonNULL = NULL; // 
70 static int max_flesh_depth = 100;
71
72 /* called when this process is about to exit */
73 void osrfAppChildExit() {
74         osrfLogDebug(OSRF_LOG_MARK, "Child is exiting, disconnecting from database...");
75
76         if (writehandle) {
77                 dbi_conn_query(writehandle, "ROLLBACK;");
78                 dbi_conn_close(writehandle);
79                 writehandle = NULL;
80         }
81
82         if (dbhandle)
83                 dbi_conn_close(dbhandle);
84
85         // XXX add cleanup of readHandles whenever that gets used
86
87         return;
88 }
89
90 int osrfAppInitialize() {
91         growing_buffer* method_name;
92
93         osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
94         osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
95
96         if (!oilsIDLInit( osrf_settings_host_value("/IDL") )) return 1; /* return non-zero to indicate error */
97
98         // Generic search thingy
99         method_name =  buffer_init(64);
100         buffer_fadd(method_name, "%s.json_query", MODULENAME);
101         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "doJSONSearch", "", 1, OSRF_METHOD_STREAMING );
102
103         // first we register all the transaction and savepoint methods
104         buffer_reset(method_name);
105         buffer_fadd(method_name, "%s.transaction.begin", MODULENAME);
106         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "beginTransaction", "", 0, 0 );
107
108         buffer_reset(method_name);
109         buffer_fadd(method_name, "%s.transaction.commit", MODULENAME);
110         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "commitTransaction", "", 0, 0 );
111
112         buffer_reset(method_name);
113         buffer_fadd(method_name, "%s.transaction.rollback", MODULENAME);
114         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "rollbackTransaction", "", 0, 0 );
115
116
117         buffer_reset(method_name);
118         buffer_fadd(method_name, "%s.savepoint.set", MODULENAME);
119         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "setSavepoint", "", 1, 0 );
120
121         buffer_reset(method_name);
122         buffer_fadd(method_name, "%s.savepoint.release", MODULENAME);
123         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "releaseSavepoint", "", 1, 0 );
124
125         buffer_reset(method_name);
126         buffer_fadd(method_name, "%s.savepoint.rollback", MODULENAME);
127         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "rollbackSavepoint", "", 1, 0 );
128
129         osrfStringArray* global_methods = osrfNewStringArray(6);
130
131         osrfStringArrayAdd( global_methods, "create" );
132         osrfStringArrayAdd( global_methods, "retrieve" );
133         osrfStringArrayAdd( global_methods, "update" );
134         osrfStringArrayAdd( global_methods, "delete" );
135         osrfStringArrayAdd( global_methods, "search" );
136         osrfStringArrayAdd( global_methods, "id_list" );
137
138         int c_index = 0; 
139         char* classname;
140         osrfStringArray* classes = osrfHashKeys( oilsIDL() );
141         osrfLogDebug(OSRF_LOG_MARK, "%d classes loaded", classes->size );
142         osrfLogDebug(OSRF_LOG_MARK, "At least %d methods will be generated", classes->size * global_methods->size);
143         
144         while ( (classname = osrfStringArrayGetString(classes, c_index++)) ) {
145                 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
146                 
147                 osrfHash* idlClass = osrfHashGet(oilsIDL(), classname);
148
149                 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), MODULENAME )) {
150                         osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on", MODULENAME, classname);
151                         continue;
152                 }
153
154                 char* virt = osrfHashGet(idlClass, "virtual");
155                 if (virt && !strcmp( virt, "true")) {
156                         osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
157                         continue;
158                 }
159
160                 int i = 0; 
161                 char* method_type;
162                 char* st_tmp;
163                 char* _fm;
164                 char* part;
165                 osrfHash* method_meta;
166                 while ( (method_type = osrfStringArrayGetString(global_methods, i++)) ) {
167                         osrfLogDebug(OSRF_LOG_MARK, "Using files to build %s class methods for %s", method_type, classname);
168
169                         if (!osrfHashGet(idlClass, "fieldmapper")) continue;
170
171                         method_meta = osrfNewHash();
172                         osrfHashSet(method_meta, idlClass, "class");
173
174                         _fm = strdup( (char*)osrfHashGet(idlClass, "fieldmapper") );
175                         part = strtok_r(_fm, ":", &st_tmp);
176
177                         growing_buffer* method_name =  buffer_init(64);
178                         buffer_fadd(method_name, "%s.direct.%s", MODULENAME, part);
179
180                         while ((part = strtok_r(NULL, ":", &st_tmp))) {
181                                 buffer_fadd(method_name, ".%s", part);
182                         }
183                         buffer_fadd(method_name, ".%s", method_type);
184
185
186                         char* method = buffer_data(method_name);
187                         buffer_free(method_name);
188                         free(_fm);
189
190                         osrfHashSet( method_meta, method, "methodname" );
191                         osrfHashSet( method_meta, method_type, "methodtype" );
192
193                         int flags = 0;
194                         if (!(strcmp( method_type, "search" )) || !(strcmp( method_type, "id_list" ))) {
195                                 flags = flags | OSRF_METHOD_STREAMING;
196                         }
197
198                         osrfAppRegisterExtendedMethod(
199                                 MODULENAME,
200                                 method,
201                                 "dispatchCRUDMethod",
202                                 "",
203                                 1,
204                                 flags,
205                                 (void*)method_meta
206                         );
207                 }
208         }
209
210         return 0;
211 }
212
213 /**
214  * Connects to the database 
215  */
216 int osrfAppChildInit() {
217
218         osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
219         dbi_initialize(NULL);
220         osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
221
222         char* driver    = osrf_settings_host_value("/apps/%s/app_settings/driver", MODULENAME);
223         char* user      = osrf_settings_host_value("/apps/%s/app_settings/database/user", MODULENAME);
224         char* host      = osrf_settings_host_value("/apps/%s/app_settings/database/host", MODULENAME);
225         char* port      = osrf_settings_host_value("/apps/%s/app_settings/database/port", MODULENAME);
226         char* db        = osrf_settings_host_value("/apps/%s/app_settings/database/db", MODULENAME);
227         char* pw        = osrf_settings_host_value("/apps/%s/app_settings/database/pw", MODULENAME);
228         char* md        = osrf_settings_host_value("/apps/%s/app_settings/max_query_recursion", MODULENAME);
229
230         osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
231         writehandle = dbi_conn_new(driver);
232
233         if(!writehandle) {
234                 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
235                 return -1;
236         }
237         osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
238
239         osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database.  host=%s, "
240                 "port=%s, user=%s, pw=%s, db=%s", MODULENAME, host, port, user, pw, db );
241
242         if(host) dbi_conn_set_option(writehandle, "host", host );
243         if(port) dbi_conn_set_option_numeric( writehandle, "port", atoi(port) );
244         if(user) dbi_conn_set_option(writehandle, "username", user);
245         if(pw) dbi_conn_set_option(writehandle, "password", pw );
246         if(db) dbi_conn_set_option(writehandle, "dbname", db );
247
248         if(md) max_flesh_depth = atoi(md);
249         if(max_flesh_depth < 0) max_flesh_depth = 1;
250         if(max_flesh_depth > 1000) max_flesh_depth = 1000;
251
252         free(user);
253         free(host);
254         free(port);
255         free(db);
256         free(pw);
257
258         const char* err;
259         if (dbi_conn_connect(writehandle) < 0) {
260                 sleep(1);
261                 if (dbi_conn_connect(writehandle) < 0) {
262                         dbi_conn_error(writehandle, &err);
263                         osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
264                         return -1;
265                 }
266         }
267
268         osrfLogInfo(OSRF_LOG_MARK, "%s successfully connected to the database", MODULENAME);
269
270         int attr;
271         unsigned short type;
272         int i = 0; 
273         char* classname;
274         osrfStringArray* classes = osrfHashKeys( oilsIDL() );
275         
276         while ( (classname = osrfStringArrayGetString(classes, i++)) ) {
277                 osrfHash* class = osrfHashGet( oilsIDL(), classname );
278                 osrfHash* fields = osrfHashGet( class, "fields" );
279
280                 char* virt = osrfHashGet(class, "virtual");
281                 if (virt && !strcmp( virt, "true")) {
282                         osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
283                         continue;
284                 }
285
286                 growing_buffer* tablebuf = buffer_init(128);
287                 char* tabledef = osrfHashGet(class, "tablename");
288                 if (!tabledef) {
289                         tabledef = osrfHashGet(class, "source_definition");
290                         buffer_fadd( tablebuf, "(%s)x", tabledef );
291                 } else {
292                         buffer_add( tablebuf, tabledef );
293                 }
294
295                 free(tabledef);
296                 tabledef = buffer_data(tablebuf);
297                 buffer_free(tablebuf);
298
299                 growing_buffer* sql_buf = buffer_init(32);
300                 buffer_fadd( sql_buf, "SELECT * FROM %s WHERE 1=0;", tabledef );
301
302                 free(tabledef);
303
304                 char* sql = buffer_data(sql_buf);
305                 buffer_free(sql_buf);
306                 osrfLogDebug(OSRF_LOG_MARK, "%s Investigatory SQL = %s", MODULENAME, sql);
307
308                 dbi_result result = dbi_conn_query(writehandle, sql);
309                 free(sql);
310
311                 if (result) {
312
313                         int columnIndex = 1;
314                         const char* columnName;
315                         osrfHash* _f;
316                         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
317
318                                 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
319
320                                 /* fetch the fieldmapper index */
321                                 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
322
323                                         osrfLogDebug(OSRF_LOG_MARK, "Found [%s] in IDL hash...", (char*)columnName);
324
325                                         /* determine the field type and storage attributes */
326                                         type = dbi_result_get_field_type(result, columnName);
327                                         attr = dbi_result_get_field_attribs(result, columnName);
328
329                                         switch( type ) {
330
331                                                 case DBI_TYPE_INTEGER :
332
333                                                         if ( !osrfHashGet(_f, "primitive") )
334                                                                 osrfHashSet(_f,"number", "primitive");
335
336                                                         if( attr & DBI_INTEGER_SIZE8 ) 
337                                                                 osrfHashSet(_f,"INT8", "datatype");
338                                                         else 
339                                                                 osrfHashSet(_f,"INT", "datatype");
340                                                         break;
341
342                                                 case DBI_TYPE_DECIMAL :
343                                                         if ( !osrfHashGet(_f, "primitive") )
344                                                                 osrfHashSet(_f,"number", "primitive");
345
346                                                         osrfHashSet(_f,"NUMERIC", "datatype");
347                                                         break;
348
349                                                 case DBI_TYPE_STRING :
350                                                         if ( !osrfHashGet(_f, "primitive") )
351                                                                 osrfHashSet(_f,"string", "primitive");
352                                                         osrfHashSet(_f,"TEXT", "datatype");
353                                                         break;
354
355                                                 case DBI_TYPE_DATETIME :
356                                                         if ( !osrfHashGet(_f, "primitive") )
357                                                                 osrfHashSet(_f,"string", "primitive");
358
359                                                         osrfHashSet(_f,"TIMESTAMP", "datatype");
360                                                         break;
361
362                                                 case DBI_TYPE_BINARY :
363                                                         if ( !osrfHashGet(_f, "primitive") )
364                                                                 osrfHashSet(_f,"string", "primitive");
365
366                                                         osrfHashSet(_f,"BYTEA", "datatype");
367                                         }
368
369                                         osrfLogDebug(
370                                                 OSRF_LOG_MARK,
371                                                 "Setting [%s] to primitive [%s] and datatype [%s]...",
372                                                 (char*)columnName,
373                                                 osrfHashGet(_f, "primitive"),
374                                                 osrfHashGet(_f, "datatype")
375                                         );
376                                 }
377                         }
378                         dbi_result_free(result);
379                 } else {
380                         osrfLogDebug(OSRF_LOG_MARK, "No data found for class [%s]...", (char*)classname);
381                 }
382         }
383
384         osrfStringArrayFree(classes);
385
386         return 0;
387 }
388
389 void userDataFree( void* blob ) {
390         osrfHashFree( (osrfHash*)blob );
391         return;
392 }
393
394 void sessionDataFree( char* key, void* item ) {
395         if (!(strcmp(key,"xact_id"))) {
396                 if (writehandle)
397                         dbi_conn_query(writehandle, "ROLLBACK;");
398                 free(item);
399         }
400
401         return;
402 }
403
404 int beginTransaction ( osrfMethodContext* ctx ) {
405         OSRF_METHOD_VERIFY_CONTEXT(ctx);
406
407         dbi_result result = dbi_conn_query(writehandle, "START TRANSACTION;");
408         if (!result) {
409                 osrfLogError(OSRF_LOG_MARK, "%s: Error starting transaction", MODULENAME );
410                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error starting transaction" );
411                 return -1;
412         } else {
413                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
414                 osrfAppRespondComplete( ctx, ret );
415                 jsonObjectFree(ret);
416                 
417                 if (!ctx->session->userData) {
418                         ctx->session->userData = osrfNewHash();
419                         ((osrfHash*)ctx->session->userData)->freeItem = &sessionDataFree;
420                 }
421
422                 osrfHashSet( (osrfHash*)ctx->session->userData, strdup( ctx->session->session_id ), "xact_id" );
423                 ctx->session->userDataFree = &userDataFree;
424                 
425         }
426         return 0;
427 }
428
429 int setSavepoint ( osrfMethodContext* ctx ) {
430         OSRF_METHOD_VERIFY_CONTEXT(ctx);
431
432         char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
433
434         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
435                 osrfAppSessionStatus(
436                         ctx->session,
437                         OSRF_STATUS_INTERNALSERVERERROR,
438                         "osrfMethodException",
439                         ctx->request,
440                         "No active transaction -- required for savepoints"
441                 );
442                 return -1;
443         }
444
445         dbi_result result = dbi_conn_queryf(writehandle, "SAVEPOINT \"%s\";", spName);
446         if (!result) {
447                 osrfLogError(
448                         OSRF_LOG_MARK,
449                         "%s: Error creating savepoint %s in transaction %s",
450                         MODULENAME,
451                         spName,
452                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
453                 );
454                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error creating savepoint" );
455                 return -1;
456         } else {
457                 jsonObject* ret = jsonNewObject(spName);
458                 osrfAppRespondComplete( ctx, ret );
459                 jsonObjectFree(ret);
460         }
461         return 0;
462 }
463
464 int releaseSavepoint ( osrfMethodContext* ctx ) {
465         OSRF_METHOD_VERIFY_CONTEXT(ctx);
466
467         char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
468
469         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
470                 osrfAppSessionStatus(
471                         ctx->session,
472                         OSRF_STATUS_INTERNALSERVERERROR,
473                         "osrfMethodException",
474                         ctx->request,
475                         "No active transaction -- required for savepoints"
476                 );
477                 return -1;
478         }
479
480         dbi_result result = dbi_conn_queryf(writehandle, "RELEASE SAVEPOINT \"%s\";", spName);
481         if (!result) {
482                 osrfLogError(
483                         OSRF_LOG_MARK,
484                         "%s: Error releasing savepoint %s in transaction %s",
485                         MODULENAME,
486                         spName,
487                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
488                 );
489                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error releasing savepoint" );
490                 return -1;
491         } else {
492                 jsonObject* ret = jsonNewObject(spName);
493                 osrfAppRespondComplete( ctx, ret );
494                 jsonObjectFree(ret);
495         }
496         return 0;
497 }
498
499 int rollbackSavepoint ( osrfMethodContext* ctx ) {
500         OSRF_METHOD_VERIFY_CONTEXT(ctx);
501
502         char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
503
504         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
505                 osrfAppSessionStatus(
506                         ctx->session,
507                         OSRF_STATUS_INTERNALSERVERERROR,
508                         "osrfMethodException",
509                         ctx->request,
510                         "No active transaction -- required for savepoints"
511                 );
512                 return -1;
513         }
514
515         dbi_result result = dbi_conn_queryf(writehandle, "ROLLBACK TO SAVEPOINT \"%s\";", spName);
516         if (!result) {
517                 osrfLogError(
518                         OSRF_LOG_MARK,
519                         "%s: Error rolling back savepoint %s in transaction %s",
520                         MODULENAME,
521                         spName,
522                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
523                 );
524                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error rolling back savepoint" );
525                 return -1;
526         } else {
527                 jsonObject* ret = jsonNewObject(spName);
528                 osrfAppRespondComplete( ctx, ret );
529                 jsonObjectFree(ret);
530         }
531         return 0;
532 }
533
534 int commitTransaction ( osrfMethodContext* ctx ) {
535         OSRF_METHOD_VERIFY_CONTEXT(ctx);
536
537         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
538                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to commit" );
539                 return -1;
540         }
541
542         dbi_result result = dbi_conn_query(writehandle, "COMMIT;");
543         if (!result) {
544                 osrfLogError(OSRF_LOG_MARK, "%s: Error committing transaction", MODULENAME );
545                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error committing transaction" );
546                 return -1;
547         } else {
548                 osrfHashRemove(ctx->session->userData, "xact_id");
549                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
550                 osrfAppRespondComplete( ctx, ret );
551                 jsonObjectFree(ret);
552         }
553         return 0;
554 }
555
556 int rollbackTransaction ( osrfMethodContext* ctx ) {
557         OSRF_METHOD_VERIFY_CONTEXT(ctx);
558
559         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
560                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to roll back" );
561                 return -1;
562         }
563
564         dbi_result result = dbi_conn_query(writehandle, "ROLLBACK;");
565         if (!result) {
566                 osrfLogError(OSRF_LOG_MARK, "%s: Error rolling back transaction", MODULENAME );
567                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error rolling back transaction" );
568                 return -1;
569         } else {
570                 osrfHashRemove(ctx->session->userData, "xact_id");
571                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
572                 osrfAppRespondComplete( ctx, ret );
573                 jsonObjectFree(ret);
574         }
575         return 0;
576 }
577
578 int dispatchCRUDMethod ( osrfMethodContext* ctx ) {
579         OSRF_METHOD_VERIFY_CONTEXT(ctx);
580
581         osrfHash* meta = (osrfHash*) ctx->method->userData;
582         osrfHash* class_obj = osrfHashGet( meta, "class" );
583         
584         int err = 0;
585
586         jsonObject * obj = NULL;
587         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "create"))
588                 obj = doCreate(ctx, &err);
589
590         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "retrieve"))
591                 obj = doRetrieve(ctx, &err);
592
593         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "update"))
594                 obj = doUpdate(ctx, &err);
595
596         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "delete"))
597                 obj = doDelete(ctx, &err);
598
599         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "search")) {
600
601                 obj = doFieldmapperSearch(ctx, class_obj, ctx->params, &err);
602                 if(err) return err;
603
604                 jsonObjectNode* cur;
605                 jsonObjectIterator* itr = jsonNewObjectIterator( obj );
606                 while ((cur = jsonObjectIteratorNext( itr ))) {
607                         osrfAppRespond( ctx, jsonObjectClone(cur->item) );
608                 }
609                 jsonObjectIteratorFree(itr);
610                 osrfAppRespondComplete( ctx, NULL );
611
612         } else if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "id_list")) {
613
614                 jsonObject* _p = jsonObjectClone( ctx->params );
615                 if (jsonObjectGetIndex( _p, 1 )) {
616                         jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "flesh" );
617                         jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "flesh_columns" );
618                 } else {
619                         jsonObjectSetIndex( _p, 1, jsonParseString("{}") );
620                 }
621
622                 growing_buffer* sel_list = buffer_init(16);
623                 buffer_fadd(sel_list, "{ \"%s\":[\"%s\"] }", osrfHashGet( class_obj, "classname" ), osrfHashGet( class_obj, "primarykey" ));
624                 char* _s = buffer_data(sel_list);
625                 buffer_free(sel_list);
626
627                 jsonObjectSetKey( jsonObjectGetIndex( _p, 1 ), "select", jsonParseString(_s) );
628                 osrfLogDebug(OSRF_LOG_MARK, "%s: Select qualifer set to [%s]", MODULENAME, _s);
629                 free(_s);
630
631                 obj = doFieldmapperSearch(ctx, class_obj, _p, &err);
632                 if(err) return err;
633
634                 jsonObjectNode* cur;
635                 jsonObjectIterator* itr = jsonNewObjectIterator( obj );
636                 while ((cur = jsonObjectIteratorNext( itr ))) {
637                         osrfAppRespond(
638                                 ctx,
639                                 jsonObjectClone(
640                                         jsonObjectGetIndex(
641                                                 cur->item,
642                                                 atoi(
643                                                         osrfHashGet(
644                                                                 osrfHashGet(
645                                                                         osrfHashGet( class_obj, "fields" ),
646                                                                         osrfHashGet( class_obj, "primarykey")
647                                                                 ),
648                                                                 "array_position"
649                                                         )
650                                                 )
651                                         )
652                                 )
653                         );
654                 }
655                 jsonObjectIteratorFree(itr);
656                 osrfAppRespondComplete( ctx, NULL );
657                 
658         } else {
659                 osrfAppRespondComplete( ctx, obj );
660         }
661
662         jsonObjectFree(obj);
663
664         return err;
665 }
666
667 int verifyObjectClass ( osrfMethodContext* ctx, jsonObject* param ) {
668         
669         osrfHash* meta = (osrfHash*) ctx->method->userData;
670         osrfHash* class = osrfHashGet( meta, "class" );
671         
672         if (!param->classname || (strcmp( osrfHashGet(class, "classname"), param->classname ))) {
673
674                 growing_buffer* msg = buffer_init(128);
675                 buffer_fadd(
676                         msg,
677                         "%s: %s method for type %s was passed a %s",
678                         MODULENAME,
679                         osrfHashGet(meta, "methodtype"),
680                         osrfHashGet(class, "classname"),
681                         param->classname
682                 );
683
684                 char* m = buffer_data(msg);
685                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException", ctx->request, m );
686
687                 buffer_free(msg);
688                 free(m);
689
690                 return 0;
691         }
692         return 1;
693 }
694
695 jsonObject* doCreate(osrfMethodContext* ctx, int* err ) {
696
697         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
698         jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
699         jsonObject* options = jsonObjectGetIndex( ctx->params, 1 );
700
701         if (!verifyObjectClass(ctx, target)) {
702                 *err = -1;
703                 return jsonNULL;
704         }
705
706         osrfLogDebug( OSRF_LOG_MARK, "Object seems to be of the correct type" );
707
708         if (!ctx->session || !ctx->session->userData || !osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
709                 osrfLogError( OSRF_LOG_MARK, "No active transaction -- required for CREATE" );
710
711                 osrfAppSessionStatus(
712                         ctx->session,
713                         OSRF_STATUS_BADREQUEST,
714                         "osrfMethodException",
715                         ctx->request,
716                         "No active transaction -- required for CREATE"
717                 );
718                 *err = -1;
719                 return jsonNULL;
720         }
721
722         if (osrfHashGet( meta, "readonly" ) && strncasecmp("true", osrfHashGet( meta, "readonly" ), 4)) {
723                 osrfAppSessionStatus(
724                         ctx->session,
725                         OSRF_STATUS_BADREQUEST,
726                         "osrfMethodException",
727                         ctx->request,
728                         "Cannot INSERT readonly class"
729                 );
730                 *err = -1;
731                 return jsonNULL;
732         }
733
734
735         char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
736
737         // Set the last_xact_id
738         osrfHash* last_xact_id;
739         if ((last_xact_id = oilsIDLFindPath("/%s/fields/last_xact_id", target->classname))) {
740                 int index = atoi( osrfHashGet(last_xact_id, "array_position") );
741                 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
742                 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
743         }       
744
745         osrfLogDebug( OSRF_LOG_MARK, "There is a transaction running..." );
746
747         dbhandle = writehandle;
748
749         osrfHash* fields = osrfHashGet(meta, "fields");
750         char* pkey = osrfHashGet(meta, "primarykey");
751         char* seq = osrfHashGet(meta, "sequence");
752
753         growing_buffer* table_buf = buffer_init(128);
754         growing_buffer* col_buf = buffer_init(128);
755         growing_buffer* val_buf = buffer_init(128);
756
757         buffer_fadd(table_buf,"INSERT INTO %s", osrfHashGet(meta, "tablename"));
758         buffer_add(col_buf,"(");
759         buffer_add(val_buf,"VALUES (");
760
761
762         int i = 0;
763         int first = 1;
764         char* field_name;
765         osrfStringArray* field_list = osrfHashKeys( fields );
766         while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
767
768                 osrfHash* field = osrfHashGet( fields, field_name );
769
770                 if(!( strcmp( osrfHashGet(osrfHashGet(fields,field_name), "virtual"), "true" ) )) continue;
771
772                 jsonObject* field_object = jsonObjectGetIndex( target, atoi(osrfHashGet(field, "array_position")) );
773
774                 char* value;
775                 if (field_object && field_object->classname) {
776                         value = jsonObjectToSimpleString(
777                                         jsonObjectGetIndex(
778                                                 field_object,
779                                                 atoi(
780                                                         osrfHashGet(
781                                                                 osrfHashGet(
782                                                                         oilsIDLFindPath("/%s/fields", field_object->classname),
783                                                                         (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
784                                                                 ),
785                                                                 "array_position"
786                                                         )
787                                                 )
788                                         )
789                                 );
790
791                 } else {
792                         value = jsonObjectToSimpleString( field_object );
793                 }
794
795
796                 if (first) {
797                         first = 0;
798                 } else {
799                         buffer_add(col_buf, ",");
800                         buffer_add(val_buf, ",");
801                 }
802
803                 buffer_add(col_buf, field_name);
804
805                 if (!field_object || field_object->type == JSON_NULL) {
806                         buffer_add( val_buf, "DEFAULT" );
807                         
808                 } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
809                         if ( !strcmp(osrfHashGet(field, "datatype"), "INT8") ) {
810                                 buffer_fadd( val_buf, "%lld", atoll(value) );
811                                 
812                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "INT") ) {
813                                 buffer_fadd( val_buf, "%d", atoi(value) );
814                                 
815                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
816                                 buffer_fadd( val_buf, "%f", atof(value) );
817                         }
818                 } else {
819                         if ( dbi_conn_quote_string(writehandle, &value) ) {
820                                 buffer_fadd( val_buf, "%s", value );
821
822                         } else {
823                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
824                                 osrfAppSessionStatus(
825                                         ctx->session,
826                                         OSRF_STATUS_INTERNALSERVERERROR,
827                                         "osrfMethodException",
828                                         ctx->request,
829                                         "Error quoting string -- please see the error log for more details"
830                                 );
831                                 free(value);
832                                 buffer_free(table_buf);
833                                 buffer_free(col_buf);
834                                 buffer_free(val_buf);
835                                 *err = -1;
836                                 return jsonNULL;
837                         }
838                 }
839
840                 free(value);
841                 
842         }
843
844
845         buffer_add(col_buf,")");
846         buffer_add(val_buf,")");
847
848         growing_buffer* sql = buffer_init(128);
849         buffer_fadd(
850                 sql,
851                 "%s %s %s;",
852                 buffer_data(table_buf),
853                 buffer_data(col_buf),
854                 buffer_data(val_buf)
855         );
856         buffer_free(table_buf);
857         buffer_free(col_buf);
858         buffer_free(val_buf);
859
860         char* query = buffer_data(sql);
861         buffer_free(sql);
862
863         osrfLogDebug(OSRF_LOG_MARK, "%s: Insert SQL [%s]", MODULENAME, query);
864
865         
866         dbi_result result = dbi_conn_query(writehandle, query);
867
868         jsonObject* obj = NULL;
869
870         if (!result) {
871                 obj = jsonNewObject(NULL);
872                 osrfLogError(
873                         OSRF_LOG_MARK,
874                         "%s ERROR inserting %s object using query [%s]",
875                         MODULENAME,
876                         osrfHashGet(meta, "fieldmapper"),
877                         query
878                 );
879                 osrfAppSessionStatus(
880                         ctx->session,
881                         OSRF_STATUS_INTERNALSERVERERROR,
882                         "osrfMethodException",
883                         ctx->request,
884                         "INSERT error -- please see the error log for more details"
885                 );
886                 *err = -1;
887         } else {
888
889                 int pos = atoi(osrfHashGet( osrfHashGet(fields, pkey), "array_position" ));
890                 char* id = jsonObjectToSimpleString(jsonObjectGetIndex(target, pos));
891                 if (!id) {
892                         unsigned long long new_id = dbi_conn_sequence_last(writehandle, seq);
893                         growing_buffer* _id = buffer_init(10);
894                         buffer_fadd(_id, "%lld", new_id);
895                         id = buffer_data(_id);
896                         buffer_free(_id);
897                 }
898
899                 if (    !options
900                         || !jsonObjectGetKey( options, "quiet")
901                         || strcmp( jsonObjectToSimpleString(jsonObjectGetKey( options, "quiet")), "true" )
902                 ) {
903
904                         jsonObject* fake_params = jsonParseString("[]");
905                         jsonObjectPush(fake_params, jsonParseString("{}"));
906
907                         jsonObjectSetKey(
908                                 jsonObjectGetIndex(fake_params, 0),
909                                 osrfHashGet(meta, "primarykey"),
910                                 jsonNewObject(id)
911                         );
912
913                         jsonObject* list = doFieldmapperSearch( ctx,meta, fake_params, err);
914
915                         if(*err) {
916                                 jsonObjectFree( fake_params );
917                                 obj = jsonNULL;
918                         } else {
919                                 obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
920                         }
921
922                         jsonObjectFree( list );
923                         jsonObjectFree( fake_params );
924
925                 } else {
926                         obj = jsonNewObject(id);
927                 }
928
929         }
930
931         free(query);
932
933         return obj;
934
935 }
936
937
938 jsonObject* doRetrieve(osrfMethodContext* ctx, int* err ) {
939
940         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
941
942         jsonObject* obj;
943
944         char* id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
945         jsonObject* order_hash = jsonObjectGetIndex(ctx->params, 1);
946
947         osrfLogDebug(
948                 OSRF_LOG_MARK,
949                 "%s retrieving %s object with id %s",
950                 MODULENAME,
951                 osrfHashGet(meta, "fieldmapper"),
952                 id
953         );
954
955         jsonObject* fake_params = jsonParseString("[]");
956         jsonObjectPush(fake_params, jsonParseString("{}"));
957
958         jsonObjectSetKey(
959                 jsonObjectGetIndex(fake_params, 0),
960                 osrfHashGet(meta, "primarykey"),
961                 jsonParseString(id)
962         );
963
964         if (order_hash) jsonObjectPush(fake_params, jsonObjectClone(order_hash) );
965
966         jsonObject* list = doFieldmapperSearch( ctx,meta, fake_params, err);
967
968         if(*err) {
969                 jsonObjectFree( fake_params );
970                 return jsonNULL;
971         }
972
973         obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
974
975         jsonObjectFree( list );
976         jsonObjectFree( fake_params );
977
978         return obj;
979 }
980
981 char* jsonNumberToDBString ( osrfHash* field, jsonObject* value ) {
982         growing_buffer* val_buf = buffer_init(32);
983
984         if ( !strncmp(osrfHashGet(field, "datatype"), "INT", (size_t)3) ) {
985                 if (value->type == JSON_NUMBER) buffer_fadd( val_buf, "%ld", (long)jsonObjectGetNumber(value) );
986                 else buffer_fadd( val_buf, "%ld", atol(jsonObjectToSimpleString(value)) );
987
988         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
989                 if (value->type == JSON_NUMBER) buffer_fadd( val_buf, "%f",  jsonObjectGetNumber(value) );
990                 else buffer_fadd( val_buf, "%f", atof(jsonObjectToSimpleString(value)) );
991         }
992
993         char* pred = buffer_data(val_buf);
994         buffer_free(val_buf);
995
996         return pred;
997 }
998
999 char* searchINPredicate (const char* class, osrfHash* field, jsonObject* node, const char* op) {
1000         growing_buffer* sql_buf = buffer_init(32);
1001         
1002         buffer_fadd(
1003                 sql_buf,
1004                 "\"%s\".%s ",
1005                 class,
1006                 osrfHashGet(field, "name")
1007         );
1008
1009         if (!op) {
1010                 buffer_add(sql_buf, "IN (");
1011         } else if (!(strcasecmp(op,"not in"))) {
1012                 buffer_add(sql_buf, "NOT IN (");
1013         } else {
1014                 buffer_add(sql_buf, "IN (");
1015         }
1016
1017         int in_item_index = 0;
1018         int in_item_first = 1;
1019         jsonObject* in_item;
1020         while ( (in_item = jsonObjectGetIndex(node, in_item_index++)) ) {
1021
1022                 if (in_item_first)
1023                         in_item_first = 0;
1024                 else
1025                         buffer_add(sql_buf, ", ");
1026
1027                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1028                         char* val = jsonNumberToDBString( field, in_item );
1029                         buffer_fadd( sql_buf, "%s", val );
1030                         free(val);
1031
1032                 } else {
1033                         char* key_string = jsonObjectToSimpleString(in_item);
1034                         if ( dbi_conn_quote_string(dbhandle, &key_string) ) {
1035                                 buffer_fadd( sql_buf, "%s", key_string );
1036                                 free(key_string);
1037                         } else {
1038                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string);
1039                                 free(key_string);
1040                                 buffer_free(sql_buf);
1041                                 return NULL;
1042                         }
1043                 }
1044         }
1045
1046         buffer_add(
1047                 sql_buf,
1048                 ")"
1049         );
1050
1051         char* pred = buffer_data(sql_buf);
1052         buffer_free(sql_buf);
1053
1054         return pred;
1055 }
1056
1057 char* searchValueTransform( jsonObject* array ) {
1058         growing_buffer* sql_buf = buffer_init(32);
1059
1060         char* val = NULL;
1061         int func_item_index = 0;
1062         int func_item_first = 2;
1063         jsonObject* func_item;
1064         while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1065
1066                 val = jsonObjectToSimpleString(func_item);
1067
1068                 if (func_item_first == 2) {
1069                         buffer_fadd(sql_buf, "%s( ", val);
1070                         free(val);
1071                         func_item_first--;
1072                         continue;
1073                 }
1074
1075                 if (func_item_first)
1076                         func_item_first--;
1077                 else
1078                         buffer_add(sql_buf, ", ");
1079
1080                 if ( dbi_conn_quote_string(dbhandle, &val) ) {
1081                         buffer_fadd( sql_buf, "%s", val );
1082                         free(val);
1083                 } else {
1084                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1085                         free(val);
1086                         buffer_free(sql_buf);
1087                         return NULL;
1088                 }
1089         }
1090
1091         buffer_add(
1092                 sql_buf,
1093                 " )"
1094         );
1095
1096         char* pred = buffer_data(sql_buf);
1097         buffer_free(sql_buf);
1098
1099         return pred;
1100 }
1101
1102 char* searchFunctionPredicate (const char* class, osrfHash* field, jsonObjectNode* node) {
1103         growing_buffer* sql_buf = buffer_init(32);
1104
1105         char* val = searchValueTransform(node->item);
1106         
1107         buffer_fadd(
1108                 sql_buf,
1109                 "\"%s\".%s %s %s",
1110                 class,
1111                 osrfHashGet(field, "name"),
1112                 node->key,
1113                 val
1114         );
1115
1116         char* pred = buffer_data(sql_buf);
1117         buffer_free(sql_buf);
1118         free(val);
1119
1120         return pred;
1121 }
1122
1123 char* searchFieldTransform (const char* class, osrfHash* field, jsonObject* node) {
1124         growing_buffer* sql_buf = buffer_init(32);
1125         
1126         char* field_transform = jsonObjectToSimpleString( jsonObjectGetKey( node, "transform" ) );
1127
1128         if (field_transform)
1129                 buffer_fadd( sql_buf, "%s(\"%s\".%s)", field_transform, class, osrfHashGet(field, "name"));
1130         else
1131                 buffer_fadd( sql_buf, "\"%s\".%s", class, osrfHashGet(field, "name"));
1132
1133         char* pred = buffer_data(sql_buf);
1134         buffer_free(sql_buf);
1135
1136         if (field_transform) free(field_transform);
1137
1138         return pred;
1139 }
1140
1141 char* searchFieldTransformPredicate (const char* class, osrfHash* field, jsonObjectNode* node) {
1142         growing_buffer* sql_buf = buffer_init(32);
1143         
1144         char* field_transform = searchFieldTransform( class, field, node->item );
1145         char* value = NULL;
1146
1147         if (!jsonObjectGetKey( node->item, "value" )) {
1148                 value = searchWHERE( node->item, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN );
1149         } else if (jsonObjectGetKey( node->item, "value" )->type == JSON_ARRAY) {
1150                 value = searchValueTransform(jsonObjectGetKey( node->item, "value" ));
1151         } else if (jsonObjectGetKey( node->item, "value" )->type == JSON_HASH) {
1152                 value = searchWHERE( jsonObjectGetKey( node->item, "value" ), osrfHashGet( oilsIDL(), class ), AND_OP_JOIN );
1153         } else if (jsonObjectGetKey( node->item, "value" )->type != JSON_NULL) {
1154                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1155                         value = jsonNumberToDBString( field, jsonObjectGetKey( node->item, "value" ) );
1156                 } else {
1157                         value = jsonObjectToSimpleString(jsonObjectGetKey( node->item, "value" ));
1158                         if ( !dbi_conn_quote_string(dbhandle, &value) ) {
1159                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, value);
1160                                 free(value);
1161                                 return NULL;
1162                         }
1163                 }
1164         }
1165
1166         buffer_fadd(
1167                 sql_buf,
1168                 "%s %s %s",
1169                 field_transform,
1170                 node->key,
1171                 value
1172         );
1173
1174         char* pred = buffer_data(sql_buf);
1175         buffer_free(sql_buf);
1176         free(field_transform);
1177
1178         return pred;
1179 }
1180
1181 char* searchSimplePredicate (const char* orig_op, const char* class, osrfHash* field, jsonObject* node) {
1182
1183         char* val = NULL;
1184
1185         if (node->type != JSON_NULL) {
1186                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1187                         val = jsonNumberToDBString( field, node );
1188                 } else {
1189                         val = jsonObjectToSimpleString(node);
1190                 }
1191         }
1192
1193         char* pred = searchWriteSimplePredicate( class, field, osrfHashGet(field, "name"), orig_op, val );
1194
1195         if (val) free(val);
1196
1197         return pred;
1198 }
1199
1200 char* searchWriteSimplePredicate ( const char* class, osrfHash* field, const char* left, const char* orig_op, const char* right ) {
1201
1202         char* val = NULL;
1203         char* op = NULL;
1204         if (right == NULL) {
1205                 val = strdup("NULL");
1206
1207                 if (strcmp( orig_op, "=" ))
1208                         op = strdup("IS NOT");
1209                 else
1210                         op = strdup("IS");
1211
1212         } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1213                 val = strdup(right);
1214                 op = strdup(orig_op);
1215
1216         } else {
1217                 val = strdup(right);
1218                 if ( !dbi_conn_quote_string(dbhandle, &val) ) {
1219                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1220                         free(val);
1221                         return NULL;
1222                 }
1223                 op = strdup(orig_op);
1224         }
1225
1226         growing_buffer* sql_buf = buffer_init(16);
1227         buffer_fadd( sql_buf, "\"%s\".%s %s %s", class, left, op, val );
1228         free(val);
1229         free(op);
1230
1231         char* pred = buffer_data(sql_buf);
1232         buffer_free(sql_buf);
1233
1234         return pred;
1235
1236 }
1237
1238 char* searchBETWEENPredicate (const char* class, osrfHash* field, jsonObject* node) {
1239
1240         char* x_string;
1241         char* y_string;
1242
1243         if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1244                 x_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,0));
1245                 y_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,1));
1246
1247         } else {
1248                 x_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,0));
1249                 y_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,1));
1250                 if ( !(dbi_conn_quote_string(dbhandle, &x_string) && dbi_conn_quote_string(dbhandle, &y_string)) ) {
1251                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key strings [%s] and [%s]", MODULENAME, x_string, y_string);
1252                         free(x_string);
1253                         free(y_string);
1254                         return NULL;
1255                 }
1256         }
1257
1258         growing_buffer* sql_buf = buffer_init(32);
1259         buffer_fadd( sql_buf, "%s BETWEEN %s AND %s", osrfHashGet(field, "name"), x_string, y_string );
1260         free(x_string);
1261         free(y_string);
1262
1263         char* pred = buffer_data(sql_buf);
1264         buffer_free(sql_buf);
1265
1266         return pred;
1267 }
1268
1269 char* searchPredicate ( const char* class, osrfHash* field, jsonObject* node ) {
1270
1271         char* pred = NULL;
1272         if (node->type == JSON_ARRAY) { // equality IN search
1273                 pred = searchINPredicate( class, field, node, NULL );
1274         } else if (node->type == JSON_HASH) { // non-equality search
1275                 jsonObjectNode* pred_node;
1276                 jsonObjectIterator* pred_itr = jsonNewObjectIterator( node );
1277                 while ( (pred_node = jsonObjectIteratorNext( pred_itr )) ) {
1278                         if ( !(strcasecmp( pred_node->key,"between" )) )
1279                                 pred = searchBETWEENPredicate( class, field, pred_node->item );
1280                         else if ( !(strcasecmp( pred_node->key,"in" )) || !(strcasecmp( pred_node->key,"not in" )) )
1281                                 pred = searchINPredicate( class, field, pred_node->item, pred_node->key );
1282                         else if ( pred_node->item->type == JSON_ARRAY )
1283                                 pred = searchFunctionPredicate( class, field, pred_node );
1284                         else if ( pred_node->item->type == JSON_HASH )
1285                                 pred = searchFieldTransformPredicate( class, field, pred_node );
1286                         else 
1287                                 pred = searchSimplePredicate( pred_node->key, class, field, pred_node->item );
1288
1289                         break;
1290                 }
1291         } else if (node->type == JSON_NULL) { // IS NULL search
1292                 growing_buffer* _p = buffer_init(16);
1293                 buffer_fadd(
1294                         _p,
1295                         "\"%s\".%s IS NULL",
1296                         class,
1297                         osrfHashGet(field, "name")
1298                 );
1299                 pred = buffer_data(_p);
1300                 buffer_free(_p);
1301         } else { // equality search
1302                 pred = searchSimplePredicate( "=", class, field, node );
1303         }
1304
1305         return pred;
1306
1307 }
1308
1309
1310 /*
1311
1312 join : {
1313         acn : {
1314                 field : record,
1315                 fkey : id
1316                 type : left
1317                 filter_op : or
1318                 filter : { ... },
1319                 join : {
1320                         acp : {
1321                                 field : call_number,
1322                                 fkey : id,
1323                                 filter : { ... },
1324                         },
1325                 },
1326         },
1327         mrd : {
1328                 field : record,
1329                 type : inner
1330                 fkey : id,
1331                 filter : { ... },
1332         }
1333 }
1334
1335 */
1336
1337 char* searchJOIN ( jsonObject* join_hash, osrfHash* leftmeta ) {
1338
1339         if (join_hash->type == JSON_STRING) {
1340                 char* __tmp = jsonObjectToSimpleString( join_hash );
1341                 join_hash = jsonParseString("{}");
1342                 jsonObjectSetKey(join_hash, __tmp, NULL);
1343                 free(__tmp);
1344         }
1345
1346         growing_buffer* join_buf = buffer_init(128);
1347         char* leftclass = osrfHashGet(leftmeta, "classname");
1348
1349         jsonObjectNode* snode = NULL;
1350         jsonObjectIterator* search_itr = jsonNewObjectIterator( join_hash );
1351         while ( (snode = jsonObjectIteratorNext( search_itr )) ) {
1352                 osrfHash* idlClass = osrfHashGet( oilsIDL(), snode->key );
1353
1354                 char* class = osrfHashGet(idlClass, "classname");
1355
1356                 growing_buffer* tablebuf = buffer_init(128);
1357                 char* table = osrfHashGet(idlClass, "tablename");
1358                 if (!table) {
1359                         table = osrfHashGet(idlClass, "source_definition");
1360                         buffer_fadd( tablebuf, "(%s)", table );
1361                         free(table);
1362                         table = buffer_data(tablebuf);
1363                         buffer_free(tablebuf);
1364                 }
1365
1366                 char* type = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "type" ) );
1367                 char* filter_op = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "filter_op" ) );
1368                 char* fkey = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "fkey" ) );
1369                 char* field = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "field" ) );
1370
1371                 jsonObject* filter = jsonObjectGetKey( snode->item, "filter" );
1372                 jsonObject* join_filter = jsonObjectGetKey( snode->item, "join" );
1373
1374                 if (field && !fkey) {
1375                         fkey = (char*)oilsIDLFindPath("/%s/links/%s/key", class, field);
1376                         if (!fkey) {
1377                                 osrfLogError(
1378                                         OSRF_LOG_MARK,
1379                                         "%s: JOIN failed.  No link defined from %s.%s to %s",
1380                                         MODULENAME,
1381                                         class,
1382                                         field,
1383                                         leftclass
1384                                 );
1385                                 buffer_free(join_buf);
1386                                 return NULL;
1387                         }
1388                         fkey = strdup( fkey );
1389
1390                 } else if (!field && fkey) {
1391                         field = (char*)oilsIDLFindPath("/%s/links/%s/key", leftclass, fkey );
1392                         if (!field) {
1393                                 osrfLogError(
1394                                         OSRF_LOG_MARK,
1395                                         "%s: JOIN failed.  No link defined from %s.%s to %s",
1396                                         MODULENAME,
1397                                         leftclass,
1398                                         fkey,
1399                                         class
1400                                 );
1401                                 buffer_free(join_buf);
1402                                 return NULL;
1403                         }
1404                         field = strdup( field );
1405
1406                 } else if (!field && !fkey) {
1407                         osrfHash* _links = oilsIDLFindPath("/%s/links", leftclass);
1408
1409                         int i = 0;
1410                         osrfStringArray* keys = osrfHashKeys( _links );
1411                         while ( (fkey = osrfStringArrayGetString(keys, i++)) ) {
1412                                 fkey = strdup(osrfStringArrayGetString(keys, i++));
1413                                 if ( !strcmp( (char*)oilsIDLFindPath("/%s/links/%s/class", leftclass, fkey), class) ) {
1414                                         field = strdup( (char*)oilsIDLFindPath("/%s/links/%s/key", leftclass, fkey) );
1415                                         break;
1416                                 } else {
1417                                         free(fkey);
1418                                 }
1419                         }
1420                         osrfStringArrayFree(keys);
1421
1422                         if (!field && !fkey) {
1423                                 _links = oilsIDLFindPath("/%s/links", class);
1424
1425                                 i = 0;
1426                                 keys = osrfHashKeys( _links );
1427                                 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
1428                                         field = strdup(osrfStringArrayGetString(keys, i++));
1429                                         if ( !strcmp( (char*)oilsIDLFindPath("/%s/links/%s/class", class, field), class) ) {
1430                                                 fkey = strdup( (char*)oilsIDLFindPath("/%s/links/%s/key", class, field) );
1431                                                 break;
1432                                         } else {
1433                                                 free(field);
1434                                         }
1435                                 }
1436                                 osrfStringArrayFree(keys);
1437                         }
1438
1439                         if (!field && !fkey) {
1440                                 osrfLogError(
1441                                         OSRF_LOG_MARK,
1442                                         "%s: JOIN failed.  No link defined between %s and %s",
1443                                         MODULENAME,
1444                                         leftclass,
1445                                         class
1446                                 );
1447                                 buffer_free(join_buf);
1448                                 return NULL;
1449                         }
1450
1451                 }
1452
1453                 if (type) {
1454                         if ( !strcasecmp(type,"left") ) {
1455                                 buffer_add(join_buf, " LEFT JOIN");
1456                         } else if ( !strcasecmp(type,"right") ) {
1457                                 buffer_add(join_buf, " RIGHT JOIN");
1458                         } else if ( !strcasecmp(type,"full") ) {
1459                                 buffer_add(join_buf, " FULL JOIN");
1460                         } else {
1461                                 buffer_add(join_buf, " INNER JOIN");
1462                         }
1463                 } else {
1464                         buffer_add(join_buf, " INNER JOIN");
1465                 }
1466
1467                 buffer_fadd(join_buf, " %s AS \"%s\" ON ( \"%s\".%s = \"%s\".%s", table, class, class, field, leftclass, fkey);
1468
1469                 if (filter) {
1470                         if (filter_op) {
1471                                 if (!strcasecmp("or",filter_op)) {
1472                                         buffer_add( join_buf, " OR " );
1473                                 } else {
1474                                         buffer_add( join_buf, " AND " );
1475                                 }
1476                         } else {
1477                                 buffer_add( join_buf, " AND " );
1478                         }
1479
1480                         char* jpred = searchWHERE( filter, idlClass, AND_OP_JOIN );
1481                         buffer_fadd( join_buf, " %s", jpred );
1482                         free(jpred);
1483                 }
1484
1485                 buffer_add(join_buf, " ) ");
1486                 
1487                 if (join_filter) {
1488                         char* jpred = searchJOIN( join_filter, idlClass );
1489                         buffer_fadd( join_buf, " %s", jpred );
1490                         free(jpred);
1491                 }
1492
1493                 free(type);
1494                 free(filter_op);
1495                 free(fkey);
1496                 free(field);
1497         }
1498
1499         char* join_string = buffer_data(join_buf);
1500         buffer_free(join_buf);
1501         return join_string;
1502 }
1503
1504 /*
1505
1506 { +class : { -or|-and : { field : { op : value }, ... }, ... }, ... }
1507
1508 */
1509 char* searchWHERE ( jsonObject* search_hash, osrfHash* meta, int opjoin_type ) {
1510
1511         growing_buffer* sql_buf = buffer_init(128);
1512
1513         jsonObjectNode* node = NULL;
1514
1515         int first = 1;
1516         jsonObjectIterator* search_itr = jsonNewObjectIterator( search_hash );
1517         while ( (node = jsonObjectIteratorNext( search_itr )) ) {
1518
1519                 if (first) {
1520                         first = 0;
1521                 } else {
1522                         if (opjoin_type == 1) buffer_add(sql_buf, " OR ");
1523                         else buffer_add(sql_buf, " AND ");
1524                 }
1525
1526                 if ( !strncmp("+",node->key,1) ) {
1527                         if ( node->item->type == JSON_STRING ) {
1528                                 char* subpred = jsonObjectToSimpleString( node->item );
1529                                 buffer_fadd(sql_buf, " \"%s\".%s ", node->key + 1, subpred);
1530                                 free(subpred);
1531                         } else {
1532                                 char* subpred = searchWHERE( node->item, osrfHashGet( oilsIDL(), node->key + 1 ), AND_OP_JOIN );
1533                                 buffer_fadd(sql_buf, "( %s )", subpred);
1534                                 free(subpred);
1535                         }
1536                 } else if ( !strcasecmp("-or",node->key) ) {
1537                         char* subpred = searchWHERE( node->item, meta, OR_OP_JOIN );
1538                         buffer_fadd(sql_buf, "( %s )", subpred);
1539                         free(subpred);
1540                 } else if ( !strcasecmp("-and",node->key) ) {
1541                         char* subpred = searchWHERE( node->item, meta, AND_OP_JOIN );
1542                         buffer_fadd(sql_buf, "( %s )", subpred);
1543                         free(subpred);
1544                 } else {
1545
1546                         char* class = osrfHashGet(meta, "classname");
1547                         osrfHash* fields = osrfHashGet(meta, "fields");
1548                         osrfHash* field = osrfHashGet( fields, node->key );
1549
1550                         char* table = osrfHashGet(meta, "tablename");
1551                         if (!table) table = "[CUSTOM RESULT SOURCE]";
1552
1553                         if (!field) {
1554                                 osrfLogError(
1555                                         OSRF_LOG_MARK,
1556                                         "%s: Attempt to reference non-existant column %s on %s (%s)",
1557                                         MODULENAME,
1558                                         node->key,
1559                                         table,
1560                                         class
1561                                 );
1562                                 buffer_free(sql_buf);
1563                                 return NULL;
1564                         }
1565
1566                         char* subpred = searchPredicate( class, field, node->item );
1567                         buffer_add( sql_buf, subpred );
1568                         free(subpred);
1569                 }
1570         }
1571
1572         jsonObjectIteratorFree(search_itr);
1573
1574         char* pred = buffer_data(sql_buf);
1575         buffer_free(sql_buf);
1576
1577         return pred;
1578 }
1579
1580 char* SELECT (
1581                 /* method context */ osrfMethodContext* ctx,
1582                 
1583                 /* SELECT   */ jsonObject* selhash,
1584                 /* FROM     */ jsonObject* join_hash,
1585                 /* WHERE    */ jsonObject* search_hash,
1586                 /* ORDER BY */ jsonObject* order_hash,
1587                 /* LIMIT    */ jsonObject* limit,
1588                 /* OFFSET   */ jsonObject* offset,
1589                 /* flags    */ int flags
1590 ) {
1591         // in case we don't get a select list
1592         jsonObject* defaultselhash = NULL;
1593
1594         // general tmp objects
1595         jsonObject* __tmp = NULL;
1596         jsonObjectNode* selclass = NULL;
1597         jsonObjectNode* selfield = NULL;
1598         jsonObjectNode* snode = NULL;
1599         jsonObjectNode* onode = NULL;
1600         jsonObject* found = NULL;
1601
1602         char* string = NULL;
1603         int first = 1;
1604         int gfirst = 1;
1605         //int hfirst = 1;
1606
1607         // return variable for the SQL
1608         char* sql = NULL;
1609
1610         // the core search class
1611         char* core_class = NULL;
1612
1613         // metadata about the core search class
1614         osrfHash* core_meta = NULL;
1615         osrfHash* core_fields = NULL;
1616         osrfHash* idlClass = NULL;
1617
1618         // the query buffer
1619         growing_buffer* sql_buf = buffer_init(128);
1620
1621         // temp buffer for the SELECT list
1622         growing_buffer* select_buf = buffer_init(128);
1623         growing_buffer* order_buf = buffer_init(128);
1624         growing_buffer* group_buf = buffer_init(128);
1625         growing_buffer* having_buf = buffer_init(128);
1626
1627         // punt if there's no core class
1628         if (!join_hash || ( join_hash->type == JSON_HASH && !join_hash->size ))
1629                 return NULL;
1630
1631         // get the core class -- the only key of the top level FROM clause, or a string
1632         if (join_hash->type == JSON_HASH) {
1633                 jsonObjectIterator* tmp_itr = jsonNewObjectIterator( join_hash );
1634                 snode = jsonObjectIteratorNext( tmp_itr );
1635                 
1636                 core_class = strdup( snode->key );
1637                 join_hash = snode->item;
1638
1639                 jsonObjectIteratorFree( tmp_itr );
1640                 snode = NULL;
1641
1642         } else if (join_hash->type == JSON_STRING) {
1643                 core_class = jsonObjectToSimpleString( join_hash );
1644                 join_hash = NULL;
1645         }
1646
1647         // punt if we don't know about the core class
1648         if (!(core_meta = osrfHashGet( oilsIDL(), core_class )))
1649                 return NULL;
1650
1651         core_fields = osrfHashGet(core_meta, "fields");
1652
1653         // if the select list is empty, or the core class field list is '*',
1654         // build the default select list ...
1655         if (!selhash) {
1656                 selhash = defaultselhash = jsonParseString( "{}" );
1657                 jsonObjectSetKey( selhash, core_class, jsonParseString( "[]" ) );
1658         } else if ( (__tmp = jsonObjectGetKey( selhash, core_class )) && __tmp->type == JSON_STRING ) {
1659                 char* __x = jsonObjectToSimpleString( __tmp );
1660                 if (!strncmp( "*", __x, 1 )) {
1661                         jsonObjectRemoveKey( selhash, core_class );
1662                         jsonObjectSetKey( selhash, core_class, jsonParseString( "[]" ) );
1663                 }
1664                 free(__x);
1665         }
1666
1667         // ... and if we /are/ building the default list, do that
1668         if ( (__tmp = jsonObjectGetKey(selhash,core_class)) && !__tmp->size ) {
1669                 
1670                 int i = 0;
1671                 char* field;
1672
1673                 osrfStringArray* keys = osrfHashKeys( core_fields );
1674                 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
1675                         if ( strncasecmp( "true", osrfHashGet( osrfHashGet( core_fields, field ), "virtual" ), 4 ) )
1676                                 jsonObjectPush( __tmp, jsonNewObject( field ) );
1677                 }
1678                 osrfStringArrayFree(keys);
1679         }
1680
1681         // Now we build the acutal select list
1682         int sel_pos = 1;
1683         jsonObject* is_agg = jsonObjectFindPath(selhash, "//aggregate");
1684         first = 1;
1685         gfirst = 1;
1686         jsonObjectIterator* selclass_itr = jsonNewObjectIterator( selhash );
1687         while ( (selclass = jsonObjectIteratorNext( selclass_itr )) ) {
1688
1689                 // round trip through the idl, just to be safe
1690                 idlClass = osrfHashGet( oilsIDL(), selclass->key );
1691                 if (!idlClass) continue;
1692                 char* cname = osrfHashGet(idlClass, "classname");
1693
1694                 // make sure the target relation is in the join tree
1695                 if (strcmp(core_class,cname)) {
1696                         if (!join_hash) continue;
1697
1698                         if (join_hash->type == JSON_STRING) {
1699                                 string = jsonObjectToSimpleString(join_hash);
1700                                 found = strcmp(string,cname) ? NULL : jsonParseString("{\"1\":\"1\"}");
1701                                 free(string);
1702                         } else {
1703                                 found = jsonObjectFindPath(join_hash, "//%s", cname);
1704                         }
1705
1706                         if (!found->size) {
1707                                 jsonObjectFree(found);
1708                                 continue;
1709                         }
1710
1711                         jsonObjectFree(found);
1712                 }
1713
1714                 // stitch together the column list ...
1715                 jsonObjectIterator* select_itr = jsonNewObjectIterator( selclass->item );
1716                 while ( (selfield = jsonObjectIteratorNext( select_itr )) ) {
1717
1718                         char* __column = NULL;
1719                         char* __alias = NULL;
1720
1721                         // ... if it's a sstring, just toss it on the pile
1722                         if (selfield->item->type == JSON_STRING) {
1723
1724                                 // again, just to be safe
1725                                 char* _requested_col = jsonObjectToSimpleString(selfield->item);
1726                                 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), _requested_col );
1727                                 free(_requested_col);
1728
1729                                 if (!field) continue;
1730                                 __column = strdup(osrfHashGet(field, "name"));
1731
1732                                 if (first) {
1733                                         first = 0;
1734                                 } else {
1735                                         buffer_add(select_buf, ",");
1736                                 }
1737
1738                                 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, __column, __column);
1739
1740                         // ... but it could be an object, in which case we check for a Field Transform
1741                         } else {
1742
1743                                 __column = jsonObjectToSimpleString( jsonObjectGetKey( selfield->item, "column" ) );
1744
1745                                 // again, just to be safe
1746                                 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), __column );
1747                                 if (!field) continue;
1748                                 char* fname = osrfHashGet(field, "name");
1749
1750                                 if (first) {
1751                                         first = 0;
1752                                 } else {
1753                                         buffer_add(select_buf, ",");
1754                                 }
1755
1756                                 if ((__tmp = jsonObjectGetKey( selfield->item, "alias" ))) {
1757                                         __alias = jsonObjectToSimpleString( __tmp );
1758                                 } else {
1759                                         __alias = strdup(__column);
1760                                 }
1761
1762                                 if (jsonObjectGetKey( selfield->item, "transform" )) {
1763                                         free(__column);
1764                                         __column = searchFieldTransform(cname, field, selfield->item);
1765                                         buffer_fadd(select_buf, " %s AS \"%s\"", __column, __alias);
1766                                 } else {
1767                                         buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, fname, __alias);
1768                                 }
1769                         }
1770
1771                         if (is_agg->size || (flags & SELECT_DISTINCT)) {
1772
1773                                 if (!jsonBoolIsTrue( jsonObjectGetKey( selfield->item, "aggregate" ) )) {
1774                                         if (gfirst) {
1775                                                 gfirst = 0;
1776                                         } else {
1777                                                 buffer_add(group_buf, ",");
1778                                         }
1779
1780                                         buffer_fadd(group_buf, " %d", sel_pos);
1781                                 /*
1782                                 } else if (is_agg = jsonObjectGetKey( selfield->item, "having" )) {
1783                                         if (gfirst) {
1784                                                 gfirst = 0;
1785                                         } else {
1786                                                 buffer_add(group_buf, ",");
1787                                         }
1788
1789                                         __column = searchFieldTransform(cname, field, selfield->item);
1790                                         buffer_fadd(group_buf, " %s", __column);
1791                                         __column = searchFieldTransform(cname, field, selfield->item);
1792                                 */
1793                                 }
1794                         }
1795
1796                         if (__column) free(__column);
1797                         if (__alias) free(__alias);
1798
1799                         sel_pos++;
1800                 }
1801         }
1802
1803         if (is_agg) jsonObjectFree(is_agg);
1804
1805         char* col_list = buffer_data(select_buf);
1806         buffer_free(select_buf);
1807
1808         growing_buffer* tablebuf = buffer_init(128);
1809         char* table = osrfHashGet(core_meta, "tablename");
1810         if (!table) {
1811                 table = osrfHashGet(core_meta, "source_definition");
1812                 buffer_fadd( tablebuf, "(%s)", table );
1813                 free(table);
1814                 table = buffer_data(tablebuf);
1815                 buffer_free(tablebuf);
1816         }
1817
1818         // Put it all together
1819         buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\" ", col_list, table, core_class );
1820         free(col_list);
1821
1822         // Now, walk the join tree and add that clause
1823         if ( join_hash ) {
1824                 char* join_clause = searchJOIN( join_hash, core_meta );
1825                 buffer_add(sql_buf, join_clause);
1826                 free(join_clause);
1827         }
1828
1829         if ( search_hash ) {
1830                 buffer_add(sql_buf, " WHERE ");
1831
1832                 // and it's on the the WHERE clause
1833                 char* pred = searchWHERE( search_hash, core_meta, AND_OP_JOIN );
1834                 if (!pred) {
1835                         osrfAppSessionStatus(
1836                                 ctx->session,
1837                                 OSRF_STATUS_INTERNALSERVERERROR,
1838                                 "osrfMethodException",
1839                                 ctx->request,
1840                                 "Severe query error -- see error log for more details"
1841                         );
1842                         free(core_class);
1843                         buffer_free(sql_buf);
1844                         if (defaultselhash) jsonObjectFree(defaultselhash);
1845                         return NULL;
1846                 } else {
1847                         buffer_add(sql_buf, pred);
1848                         free(pred);
1849                 }
1850         }
1851
1852         first = 1;
1853         jsonObjectIterator* class_itr = jsonNewObjectIterator( order_hash );
1854         while ( (snode = jsonObjectIteratorNext( class_itr )) ) {
1855
1856                 if (!jsonObjectGetKey(selhash,snode->key))
1857                         continue;
1858
1859                 if ( snode->item->type == JSON_HASH ) {
1860
1861                         jsonObjectIterator* order_itr = jsonNewObjectIterator( snode->item );
1862                         while ( (onode = jsonObjectIteratorNext( order_itr )) ) {
1863
1864                                 if (!oilsIDLFindPath( "/%s/fields/%s", snode->key, onode->key ))
1865                                         continue;
1866
1867                                 char* direction = NULL;
1868                                 if ( onode->item->type == JSON_HASH ) {
1869                                         if ( jsonObjectGetKey( onode->item, "transform" ) ) {
1870                                                 string = searchFieldTransform(
1871                                                         snode->key,
1872                                                         oilsIDLFindPath( "/%s/fields/%s", snode->key, onode->key ),
1873                                                         onode->item
1874                                                 );
1875                                         } else {
1876                                                 growing_buffer* field_buf = buffer_init(16);
1877                                                 buffer_fadd(field_buf, "\"%s\".%s", snode->key, onode->key);
1878                                                 string = buffer_data(field_buf);
1879                                                 buffer_free(field_buf);
1880                                         }
1881
1882                                         if ( (__tmp = jsonObjectGetKey( onode->item, "direction" )) ) {
1883                                                 direction = jsonObjectToSimpleString(__tmp);
1884                                                 if (!strncasecmp(direction, "d", 1)) {
1885                                                         free(direction);
1886                                                         direction = " DESC";
1887                                                 } else {
1888                                                         free(direction);
1889                                                         direction = " ASC";
1890                                                 }
1891                                         }
1892
1893                                 } else {
1894                                         string = strdup(onode->key);
1895                                         direction = jsonObjectToSimpleString(onode->item);
1896                                         if (!strncasecmp(direction, "d", 1)) {
1897                                                 free(direction);
1898                                                 direction = " DESC";
1899                                         } else {
1900                                                 free(direction);
1901                                                 direction = " ASC";
1902                                         }
1903                                 }
1904
1905                                 if (first) {
1906                                         first = 0;
1907                                 } else {
1908                                         buffer_add(order_buf, ", ");
1909                                 }
1910
1911                                 buffer_add(order_buf, string);
1912                                 free(string);
1913
1914                                 if (direction) {
1915                                         buffer_add(order_buf, direction);
1916                                 }
1917
1918                         }
1919
1920                 } else if ( snode->item->type == JSON_ARRAY ) {
1921
1922                         jsonObjectIterator* order_itr = jsonNewObjectIterator( snode->item );
1923                         while ( (onode = jsonObjectIteratorNext( order_itr )) ) {
1924
1925                                 char* _f = jsonObjectToSimpleString( onode->item );
1926
1927                                 if (!oilsIDLFindPath( "/%s/fields/%s", snode->key, _f))
1928                                         continue;
1929
1930                                 if (first) {
1931                                         first = 0;
1932                                 } else {
1933                                         buffer_add(order_buf, ", ");
1934                                 }
1935
1936                                 buffer_add(order_buf, _f);
1937                                 free(_f);
1938
1939                         }
1940
1941                 // IT'S THE OOOOOOOOOOOLD STYLE!
1942                 } else {
1943                         osrfLogError(OSRF_LOG_MARK, "%s: Possible SQL injection attempt; direct order by is not allowed", MODULENAME);
1944                         osrfAppSessionStatus(
1945                                 ctx->session,
1946                                 OSRF_STATUS_INTERNALSERVERERROR,
1947                                 "osrfMethodException",
1948                                 ctx->request,
1949                                 "Severe query error -- see error log for more details"
1950                         );
1951
1952                         free(core_class);
1953                         buffer_free(order_buf);
1954                         buffer_free(sql_buf);
1955                         if (defaultselhash) jsonObjectFree(defaultselhash);
1956                         return NULL;
1957                 }
1958
1959         }
1960
1961         string = buffer_data(group_buf);
1962         buffer_free(group_buf);
1963
1964         if (strlen(string)) {
1965                 buffer_fadd(
1966                         sql_buf,
1967                         " GROUP BY %s",
1968                         string
1969                 );
1970         }
1971
1972         free(string);
1973
1974         string = buffer_data(having_buf);
1975         buffer_free(having_buf);
1976
1977         if (strlen(string)) {
1978                 buffer_fadd(
1979                         sql_buf,
1980                         " HAVING %s",
1981                         string
1982                 );
1983         }
1984
1985         free(string);
1986
1987         string = buffer_data(order_buf);
1988         buffer_free(order_buf);
1989
1990         if (strlen(string)) {
1991                 buffer_fadd(
1992                         sql_buf,
1993                         " ORDER BY %s",
1994                         string
1995                 );
1996         }
1997
1998         free(string);
1999
2000         if ( limit ){
2001                 string = jsonObjectToSimpleString(limit);
2002                 buffer_fadd( sql_buf, " LIMIT %d", atoi(string) );
2003                 free(string);
2004         }
2005
2006         if (offset) {
2007                 string = jsonObjectToSimpleString(offset);
2008                 buffer_fadd( sql_buf, " OFFSET %d", atoi(string) );
2009                 free(string);
2010         }
2011
2012         buffer_add(sql_buf, ";");
2013
2014         sql = buffer_data(sql_buf);
2015
2016         free(core_class);
2017         buffer_free(sql_buf);
2018         if (defaultselhash) jsonObjectFree(defaultselhash);
2019
2020         return sql;
2021
2022 }
2023
2024 char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
2025
2026         osrfHash* fields = osrfHashGet(meta, "fields");
2027         char* core_class = osrfHashGet(meta, "classname");
2028
2029         jsonObject* join_hash = jsonObjectGetKey( order_hash, "join" );
2030
2031         jsonObjectNode* node = NULL;
2032         jsonObjectNode* snode = NULL;
2033         jsonObjectNode* onode = NULL;
2034         jsonObject* _tmp = NULL;
2035         jsonObject* selhash = NULL;
2036         jsonObject* defaultselhash = NULL;
2037
2038         growing_buffer* sql_buf = buffer_init(128);
2039         growing_buffer* select_buf = buffer_init(128);
2040
2041         if ( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
2042                 defaultselhash = jsonParseString( "{}" );
2043                 selhash = defaultselhash;
2044         }
2045         
2046         if ( !jsonObjectGetKey(selhash,core_class) ) {
2047                 jsonObjectSetKey( selhash, core_class, jsonParseString( "[]" ) );
2048                 jsonObject* flist = jsonObjectGetKey( selhash, core_class );
2049                 
2050                 int i = 0;
2051                 char* field;
2052
2053                 osrfStringArray* keys = osrfHashKeys( fields );
2054                 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
2055                         if ( strcasecmp( "true", osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
2056                                 jsonObjectPush( flist, jsonNewObject( field ) );
2057                 }
2058                 osrfStringArrayFree(keys);
2059         }
2060
2061         int first = 1;
2062         jsonObjectIterator* class_itr = jsonNewObjectIterator( selhash );
2063         while ( (snode = jsonObjectIteratorNext( class_itr )) ) {
2064
2065                 osrfHash* idlClass = osrfHashGet( oilsIDL(), snode->key );
2066                 if (!idlClass) continue;
2067                 char* cname = osrfHashGet(idlClass, "classname");
2068
2069                 if (strcmp(core_class,snode->key)) {
2070                         if (!join_hash) continue;
2071
2072                         jsonObject* found =  jsonObjectFindPath(join_hash, "//%s", snode->key);
2073                         if (!found->size) {
2074                                 jsonObjectFree(found);
2075                                 continue;
2076                         }
2077
2078                         jsonObjectFree(found);
2079                 }
2080
2081                 jsonObjectIterator* select_itr = jsonNewObjectIterator( snode->item );
2082                 while ( (node = jsonObjectIteratorNext( select_itr )) ) {
2083                         osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), jsonObjectToSimpleString(node->item) );
2084                         char* fname = osrfHashGet(field, "name");
2085
2086                         if (!field) continue;
2087
2088                         if (first) {
2089                                 first = 0;
2090                         } else {
2091                                 buffer_add(select_buf, ",");
2092                         }
2093
2094                         buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
2095                 }
2096         }
2097
2098         char* col_list = buffer_data(select_buf);
2099         buffer_free(select_buf);
2100
2101         growing_buffer* tablebuf = buffer_init(128);
2102         char* table = osrfHashGet(meta, "tablename");
2103         if (!table) {
2104                 table = osrfHashGet(meta, "source_definition");
2105                 buffer_fadd( tablebuf, "(%s)", table );
2106                 free(table);
2107                 table = buffer_data(tablebuf);
2108                 buffer_free(tablebuf);
2109         }
2110
2111         buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\"", col_list, table, core_class );
2112
2113         if ( join_hash ) {
2114                 char* join_clause = searchJOIN( join_hash, meta );
2115                 buffer_fadd(sql_buf, " %s", join_clause);
2116                 free(join_clause);
2117         }
2118
2119         buffer_add(sql_buf, " WHERE ");
2120
2121         char* pred = searchWHERE( search_hash, meta, AND_OP_JOIN );
2122         if (!pred) {
2123                 osrfAppSessionStatus(
2124                         ctx->session,
2125                         OSRF_STATUS_INTERNALSERVERERROR,
2126                                 "osrfMethodException",
2127                                 ctx->request,
2128                                 "Severe query error -- see error log for more details"
2129                         );
2130                 buffer_free(sql_buf);
2131                 return NULL;
2132         } else {
2133                 buffer_add(sql_buf, pred);
2134                 free(pred);
2135         }
2136
2137         if (order_hash) {
2138                 char* string = NULL;
2139                 if ( (_tmp = jsonObjectGetKey( order_hash, "order_by" )) ){
2140
2141                         growing_buffer* order_buf = buffer_init(128);
2142
2143                         first = 1;
2144                         jsonObjectIterator* class_itr = jsonNewObjectIterator( _tmp );
2145                         while ( (snode = jsonObjectIteratorNext( class_itr )) ) {
2146
2147                                 if (!jsonObjectGetKey(selhash,snode->key))
2148                                         continue;
2149
2150                                 if ( snode->item->type == JSON_HASH ) {
2151
2152                                         jsonObjectIterator* order_itr = jsonNewObjectIterator( snode->item );
2153                                         while ( (onode = jsonObjectIteratorNext( order_itr )) ) {
2154
2155                                                 if (!oilsIDLFindPath( "/%s/fields/%s", snode->key, onode->key ))
2156                                                         continue;
2157
2158                                                 char* direction = NULL;
2159                                                 if ( onode->item->type == JSON_HASH ) {
2160                                                         if ( jsonObjectGetKey( onode->item, "transform" ) ) {
2161                                                                 string = searchFieldTransform(
2162                                                                         snode->key,
2163                                                                         oilsIDLFindPath( "/%s/fields/%s", snode->key, onode->key ),
2164                                                                         onode->item
2165                                                                 );
2166                                                         } else {
2167                                                                 growing_buffer* field_buf = buffer_init(16);
2168                                                                 buffer_fadd(field_buf, "\"%s\".%s", snode->key, onode->key);
2169                                                                 string = buffer_data(field_buf);
2170                                                                 buffer_free(field_buf);
2171                                                         }
2172
2173                                                         if ( (_tmp = jsonObjectGetKey( onode->item, "direction" )) ) {
2174                                                                 direction = jsonObjectToSimpleString(_tmp);
2175                                                                 if (!strncasecmp(direction, "d", 1)) {
2176                                                                         free(direction);
2177                                                                         direction = " DESC";
2178                                                                 } else {
2179                                                                         free(direction);
2180                                                                         direction = " ASC";
2181                                                                 }
2182                                                         }
2183
2184                                                 } else {
2185                                                         string = strdup(onode->key);
2186                                                         direction = jsonObjectToSimpleString(onode->item);
2187                                                         if (!strncasecmp(direction, "d", 1)) {
2188                                                                 free(direction);
2189                                                                 direction = " DESC";
2190                                                         } else {
2191                                                                 free(direction);
2192                                                                 direction = " ASC";
2193                                                         }
2194                                                 }
2195
2196                                                 if (first) {
2197                                                         first = 0;
2198                                                 } else {
2199                                                         buffer_add(order_buf, ", ");
2200                                                 }
2201
2202                                                 buffer_add(order_buf, string);
2203                                                 free(string);
2204
2205                                                 if (direction) {
2206                                                         buffer_add(order_buf, direction);
2207                                                 }
2208
2209                                         }
2210
2211                                 } else {
2212                                         string = jsonObjectToSimpleString(snode->item);
2213                                         buffer_add(order_buf, string);
2214                                         free(string);
2215                                         break;
2216                                 }
2217
2218                         }
2219
2220                         string = buffer_data(order_buf);
2221                         buffer_free(order_buf);
2222
2223                         if (strlen(string)) {
2224                                 buffer_fadd(
2225                                         sql_buf,
2226                                         " ORDER BY %s",
2227                                         string
2228                                 );
2229                         }
2230
2231                         free(string);
2232                 }
2233
2234                 if ( (_tmp = jsonObjectGetKey( order_hash, "limit" )) ){
2235                         string = jsonObjectToSimpleString(_tmp);
2236                         buffer_fadd(
2237                                 sql_buf,
2238                                 " LIMIT %d",
2239                                 atoi(string)
2240                         );
2241                         free(string);
2242                 }
2243
2244                 _tmp = jsonObjectGetKey( order_hash, "offset" );
2245                 if (_tmp) {
2246                         string = jsonObjectToSimpleString(_tmp);
2247                         buffer_fadd(
2248                                 sql_buf,
2249                                 " OFFSET %d",
2250                                 atoi(string)
2251                         );
2252                         free(string);
2253                 }
2254         }
2255
2256         buffer_add(sql_buf, ";");
2257
2258         char* sql = buffer_data(sql_buf);
2259         buffer_free(sql_buf);
2260         if (defaultselhash) jsonObjectFree(defaultselhash);
2261
2262         return sql;
2263 }
2264
2265 int doJSONSearch ( osrfMethodContext* ctx ) {
2266         OSRF_METHOD_VERIFY_CONTEXT(ctx);
2267         osrfLogDebug(OSRF_LOG_MARK, "Recieved query request");
2268
2269         int err = 0;
2270
2271         // XXX for now...
2272         dbhandle = writehandle;
2273
2274         jsonObject* hash = jsonObjectGetIndex(ctx->params, 0);
2275
2276         osrfLogDebug(OSRF_LOG_MARK, "Building SQL ...");
2277         char* sql = SELECT(
2278                         ctx,
2279                         jsonObjectGetKey( hash, "select" ),
2280                         jsonObjectGetKey( hash, "from" ),
2281                         jsonObjectGetKey( hash, "where" ),
2282                         jsonObjectGetKey( hash, "order_by" ),
2283                         jsonObjectGetKey( hash, "limit" ),
2284                         jsonObjectGetKey( hash, "offset" ),
2285                         jsonBoolIsTrue(jsonObjectGetKey( hash, "distinct" )) ? SELECT_DISTINCT : 0
2286         );
2287
2288         if (!sql) {
2289                 err = -1;
2290                 return err;
2291         }
2292         
2293         osrfLogDebug(OSRF_LOG_MARK, "%s SQL =  %s", MODULENAME, sql);
2294         dbi_result result = dbi_conn_query(dbhandle, sql);
2295
2296         if(result) {
2297                 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
2298
2299                 if (dbi_result_first_row(result)) {
2300                         /* JSONify the result */
2301                         osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
2302
2303                         do {
2304                                 osrfAppRespond( ctx, oilsMakeJSONFromResult( result ) );
2305                         } while (dbi_result_next_row(result));
2306
2307                 } else {
2308                         osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
2309                 }
2310
2311                 osrfAppRespondComplete( ctx, NULL );
2312
2313                 /* clean up the query */
2314                 dbi_result_free(result); 
2315
2316         } else {
2317                 err = -1;
2318                 osrfLogError(OSRF_LOG_MARK, "%s: Error with query [%s]", MODULENAME, sql);
2319                 osrfAppSessionStatus(
2320                         ctx->session,
2321                         OSRF_STATUS_INTERNALSERVERERROR,
2322                         "osrfMethodException",
2323                         ctx->request,
2324                         "Severe query error -- see error log for more details"
2325                 );
2326         }
2327
2328         free(sql);
2329         return err;
2330 }
2331
2332 jsonObject* doFieldmapperSearch ( osrfMethodContext* ctx, osrfHash* meta, jsonObject* params, int* err ) {
2333
2334         // XXX for now...
2335         dbhandle = writehandle;
2336
2337         osrfHash* links = osrfHashGet(meta, "links");
2338         osrfHash* fields = osrfHashGet(meta, "fields");
2339         char* core_class = osrfHashGet(meta, "classname");
2340         char* pkey = osrfHashGet(meta, "primarykey");
2341
2342         jsonObject* _tmp;
2343         jsonObject* obj;
2344         jsonObject* search_hash = jsonObjectGetIndex(params, 0);
2345         jsonObject* order_hash = jsonObjectGetIndex(params, 1);
2346
2347         char* sql = buildSELECT( search_hash, order_hash, meta, ctx );
2348         if (!sql) {
2349                 *err = -1;
2350                 return NULL;
2351         }
2352         
2353         osrfLogDebug(OSRF_LOG_MARK, "%s SQL =  %s", MODULENAME, sql);
2354         dbi_result result = dbi_conn_query(dbhandle, sql);
2355
2356         osrfHash* dedup = osrfNewHash();
2357         jsonObject* res_list = jsonParseString("[]");
2358         if(result) {
2359                 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
2360
2361                 if (dbi_result_first_row(result)) {
2362                         /* JSONify the result */
2363                         osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
2364                         do {
2365                                 obj = oilsMakeFieldmapperFromResult( result, meta );
2366                                 int pkey_pos = atoi( osrfHashGet( osrfHashGet( fields, pkey ), "array_position" ) );
2367                                 char* pkey_val = jsonObjectToSimpleString( jsonObjectGetIndex( obj, pkey_pos ) );
2368                                 if ( osrfHashGet( dedup, pkey_val ) ) {
2369                                         jsonObjectFree(obj);
2370                                 } else {
2371                                         osrfHashSet( dedup, pkey_val, pkey_val );
2372                                         jsonObjectPush(res_list, obj);
2373                                 }
2374                         } while (dbi_result_next_row(result));
2375                 } else {
2376                         osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
2377                 }
2378
2379                 /* clean up the query */
2380                 dbi_result_free(result); 
2381
2382         } else {
2383                 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]", MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
2384                 osrfAppSessionStatus(
2385                         ctx->session,
2386                         OSRF_STATUS_INTERNALSERVERERROR,
2387                         "osrfMethodException",
2388                         ctx->request,
2389                         "Severe query error -- see error log for more details"
2390                 );
2391                 *err = -1;
2392                 free(sql);
2393                 jsonObjectFree(res_list);
2394                 return jsonNULL;
2395
2396         }
2397
2398         free(sql);
2399
2400         if (res_list->size && order_hash) {
2401                 _tmp = jsonObjectGetKey( order_hash, "flesh" );
2402                 if (_tmp) {
2403                         int x = (int)jsonObjectGetNumber(_tmp);
2404                         if (x == -1 || x > max_flesh_depth) x = max_flesh_depth;
2405
2406                         jsonObject* flesh_blob = NULL;
2407                         if ((flesh_blob = jsonObjectGetKey( order_hash, "flesh_fields" )) && x > 0) {
2408
2409                                 flesh_blob = jsonObjectClone( flesh_blob );
2410                                 jsonObject* flesh_fields = jsonObjectGetKey( flesh_blob, core_class );
2411
2412                                 osrfStringArray* link_fields = NULL;
2413
2414                                 if (flesh_fields) {
2415                                         if (flesh_fields->size == 1) {
2416                                                 char* _t = jsonObjectToSimpleString( jsonObjectGetIndex( flesh_fields, 0 ) );
2417                                                 if (!strcmp(_t,"*")) link_fields = osrfHashKeys( links );
2418                                                 free(_t);
2419                                         }
2420
2421                                         if (!link_fields) {
2422                                                 jsonObjectNode* _f;
2423                                                 link_fields = osrfNewStringArray(1);
2424                                                 jsonObjectIterator* _i = jsonNewObjectIterator( flesh_fields );
2425                                                 while ((_f = jsonObjectIteratorNext( _i ))) {
2426                                                         osrfStringArrayAdd( link_fields, jsonObjectToSimpleString( _f->item ) );
2427                                                 }
2428                                         }
2429                                 }
2430
2431                                 jsonObjectNode* cur;
2432                                 jsonObjectIterator* itr = jsonNewObjectIterator( res_list );
2433                                 while ((cur = jsonObjectIteratorNext( itr ))) {
2434
2435                                         int i = 0;
2436                                         char* link_field;
2437                                         
2438                                         while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
2439
2440                                                 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
2441
2442                                                 osrfHash* kid_link = osrfHashGet(links, link_field);
2443                                                 if (!kid_link) continue;
2444
2445                                                 osrfHash* field = osrfHashGet(fields, link_field);
2446                                                 if (!field) continue;
2447
2448                                                 osrfHash* value_field = field;
2449
2450                                                 osrfHash* kid_idl = osrfHashGet(oilsIDL(), osrfHashGet(kid_link, "class"));
2451                                                 if (!kid_idl) continue;
2452
2453                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
2454                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
2455                                                 }
2456                                                         
2457                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
2458                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
2459                                                 }
2460
2461                                                 osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
2462
2463                                                 if (link_map->size > 0) {
2464                                                         jsonObject* _kid_key = jsonParseString("[]");
2465                                                         jsonObjectPush(
2466                                                                 _kid_key,
2467                                                                 jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
2468                                                         );
2469
2470                                                         jsonObjectSetKey(
2471                                                                 flesh_blob,
2472                                                                 osrfHashGet(kid_link, "class"),
2473                                                                 _kid_key
2474                                                         );
2475                                                 };
2476
2477                                                 osrfLogDebug(
2478                                                         OSRF_LOG_MARK,
2479                                                         "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
2480                                                         osrfHashGet(kid_link, "field"),
2481                                                         osrfHashGet(kid_link, "class"),
2482                                                         osrfHashGet(kid_link, "key"),
2483                                                         osrfHashGet(kid_link, "reltype")
2484                                                 );
2485
2486                                                 jsonObject* fake_params = jsonParseString("[]");
2487                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // search hash
2488                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // order/flesh hash
2489
2490                                                 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
2491
2492                                                 char* search_key =
2493                                                 jsonObjectToSimpleString(
2494                                                         jsonObjectGetIndex(
2495                                                                 cur->item,
2496                                                                 atoi( osrfHashGet(value_field, "array_position") )
2497                                                         )
2498                                                 );
2499
2500                                                 if (!search_key) {
2501                                                         osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
2502                                                         continue;
2503                                                 }
2504                                                         
2505                                                 jsonObjectSetKey(
2506                                                         jsonObjectGetIndex(fake_params, 0),
2507                                                         osrfHashGet(kid_link, "key"),
2508                                                         jsonNewObject( search_key )
2509                                                 );
2510
2511                                                 free(search_key);
2512
2513
2514                                                 jsonObjectSetKey(
2515                                                         jsonObjectGetIndex(fake_params, 1),
2516                                                         "flesh",
2517                                                         jsonNewNumberObject( (double)(x - 1 + link_map->size) )
2518                                                 );
2519
2520                                                 if (flesh_blob)
2521                                                         jsonObjectSetKey( jsonObjectGetIndex(fake_params, 1), "flesh_fields", jsonObjectClone(flesh_blob) );
2522
2523                                                 if (jsonObjectGetKey(order_hash, "order_by")) {
2524                                                         jsonObjectSetKey(
2525                                                                 jsonObjectGetIndex(fake_params, 1),
2526                                                                 "order_by",
2527                                                                 jsonObjectClone(jsonObjectGetKey(order_hash, "order_by"))
2528                                                         );
2529                                                 }
2530
2531                                                 if (jsonObjectGetKey(order_hash, "select")) {
2532                                                         jsonObjectSetKey(
2533                                                                 jsonObjectGetIndex(fake_params, 1),
2534                                                                 "select",
2535                                                                 jsonObjectClone(jsonObjectGetKey(order_hash, "select"))
2536                                                         );
2537                                                 }
2538
2539                                                 jsonObject* kids = doFieldmapperSearch(ctx, kid_idl, fake_params, err);
2540
2541                                                 if(*err) {
2542                                                         jsonObjectFree( fake_params );
2543                                                         osrfStringArrayFree(link_fields);
2544                                                         jsonObjectIteratorFree(itr);
2545                                                         jsonObjectFree(res_list);
2546                                                         return jsonNULL;
2547                                                 }
2548
2549                                                 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
2550
2551                                                 jsonObject* X = NULL;
2552                                                 if ( link_map->size > 0 && kids->size > 0 ) {
2553                                                         X = kids;
2554                                                         kids = jsonParseString("[]");
2555
2556                                                         jsonObjectNode* _k_node;
2557                                                         jsonObjectIterator* _k = jsonNewObjectIterator( X );
2558                                                         while ((_k_node = jsonObjectIteratorNext( _k ))) {
2559                                                                 jsonObjectPush(
2560                                                                         kids,
2561                                                                         jsonObjectClone(
2562                                                                                 jsonObjectGetIndex(
2563                                                                                         _k_node->item,
2564                                                                                         (unsigned long)atoi(
2565                                                                                                 osrfHashGet(
2566                                                                                                         osrfHashGet(
2567                                                                                                                 osrfHashGet(
2568                                                                                                                         osrfHashGet(
2569                                                                                                                                 oilsIDL(),
2570                                                                                                                                 osrfHashGet(kid_link, "class")
2571                                                                                                                         ),
2572                                                                                                                         "fields"
2573                                                                                                                 ),
2574                                                                                                                 osrfStringArrayGetString( link_map, 0 )
2575                                                                                                         ),
2576                                                                                                         "array_position"
2577                                                                                                 )
2578                                                                                         )
2579                                                                                 )
2580                                                                         )
2581                                                                 );
2582                                                         }
2583                                                 }
2584
2585                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" )) || !(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) {
2586                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
2587                                                         jsonObjectSetIndex(
2588                                                                 cur->item,
2589                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
2590                                                                 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
2591                                                         );
2592                                                 }
2593
2594                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
2595                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
2596                                                         jsonObjectSetIndex(
2597                                                                 cur->item,
2598                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
2599                                                                 jsonObjectClone( kids )
2600                                                         );
2601                                                 }
2602
2603                                                 if (X) {
2604                                                         jsonObjectFree(kids);
2605                                                         kids = X;
2606                                                 }
2607
2608                                                 jsonObjectFree( kids );
2609                                                 jsonObjectFree( fake_params );
2610
2611                                                 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
2612                                                 osrfLogDebug(OSRF_LOG_MARK, "%s", jsonObjectToJSON(cur->item));
2613
2614                                         }
2615                                 }
2616                                 jsonObjectFree( flesh_blob );
2617                                 osrfStringArrayFree(link_fields);
2618                                 jsonObjectIteratorFree(itr);
2619                         }
2620                 }
2621         }
2622
2623         return res_list;
2624 }
2625
2626
2627 jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
2628
2629         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
2630         jsonObject* target = jsonObjectGetIndex(ctx->params, 0);
2631
2632         if (!verifyObjectClass(ctx, target)) {
2633                 *err = -1;
2634                 return jsonNULL;
2635         }
2636
2637         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
2638                 osrfAppSessionStatus(
2639                         ctx->session,
2640                         OSRF_STATUS_BADREQUEST,
2641                         "osrfMethodException",
2642                         ctx->request,
2643                         "No active transaction -- required for UPDATE"
2644                 );
2645                 *err = -1;
2646                 return jsonNULL;
2647         }
2648
2649         if (osrfHashGet( meta, "readonly" ) && strncasecmp("true", osrfHashGet( meta, "readonly" ), 4)) {
2650                 osrfAppSessionStatus(
2651                         ctx->session,
2652                         OSRF_STATUS_BADREQUEST,
2653                         "osrfMethodException",
2654                         ctx->request,
2655                         "Cannot UPDATE readonly class"
2656                 );
2657                 *err = -1;
2658                 return jsonNULL;
2659         }
2660
2661         dbhandle = writehandle;
2662
2663         char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
2664
2665         // Set the last_xact_id
2666         osrfHash* last_xact_id;
2667         if ((last_xact_id = oilsIDLFindPath("/%s/fields/last_xact_id", target->classname))) {
2668                 int index = atoi( osrfHashGet(last_xact_id, "array_position") );
2669                 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
2670                 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
2671         }       
2672
2673         char* pkey = osrfHashGet(meta, "primarykey");
2674         osrfHash* fields = osrfHashGet(meta, "fields");
2675
2676         char* id =
2677                 jsonObjectToSimpleString(
2678                         jsonObjectGetIndex(
2679                                 target,
2680                                 atoi( osrfHashGet( osrfHashGet( fields, pkey ), "array_position" ) )
2681                         )
2682                 );
2683
2684         osrfLogDebug(
2685                 OSRF_LOG_MARK,
2686                 "%s updating %s object with %s = %s",
2687                 MODULENAME,
2688                 osrfHashGet(meta, "fieldmapper"),
2689                 pkey,
2690                 id
2691         );
2692
2693         growing_buffer* sql = buffer_init(128);
2694         buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
2695
2696         int i = 0;
2697         int first = 1;
2698         char* field_name;
2699         osrfStringArray* field_list = osrfHashKeys( fields );
2700         while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
2701
2702                 osrfHash* field = osrfHashGet( fields, field_name );
2703
2704                 if(!( strcmp( field_name, pkey ) )) continue;
2705                 if(!( strcmp( osrfHashGet(osrfHashGet(fields,field_name), "virtual"), "true" ) )) continue;
2706
2707                 jsonObject* field_object = jsonObjectGetIndex( target, atoi(osrfHashGet(field, "array_position")) );
2708
2709                 char* value;
2710                 if (field_object && field_object->classname) {
2711                         value = jsonObjectToSimpleString(
2712                                         jsonObjectGetIndex(
2713                                                 field_object,
2714                                                 atoi(
2715                                                         osrfHashGet(
2716                                                                 osrfHashGet(
2717                                                                         oilsIDLFindPath("/%s/fields", field_object->classname),
2718                                                                         (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
2719                                                                 ),
2720                                                                 "array_position"
2721                                                         )
2722                                                 )
2723                                         )
2724                                 );
2725
2726                 } else {
2727                         value = jsonObjectToSimpleString( field_object );
2728                 }
2729
2730                 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
2731
2732                 if (!field_object || field_object->type == JSON_NULL) {
2733                         if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
2734                                 if (first) first = 0;
2735                                 else buffer_add(sql, ",");
2736                                 buffer_fadd( sql, " %s = NULL", field_name );
2737                         }
2738                         
2739                 } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
2740                         if (first) first = 0;
2741                         else buffer_add(sql, ",");
2742
2743                         if ( !strncmp(osrfHashGet(field, "datatype"), "INT", (size_t)3) ) {
2744                                 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
2745                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
2746                                 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
2747                         }
2748
2749                         osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, osrfHashGet(field, "datatype"));
2750
2751                 } else {
2752                         if ( dbi_conn_quote_string(dbhandle, &value) ) {
2753                                 if (first) first = 0;
2754                                 else buffer_add(sql, ",");
2755                                 buffer_fadd( sql, " %s = %s", field_name, value );
2756
2757                         } else {
2758                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
2759                                 osrfAppSessionStatus(
2760                                         ctx->session,
2761                                         OSRF_STATUS_INTERNALSERVERERROR,
2762                                         "osrfMethodException",
2763                                         ctx->request,
2764                                         "Error quoting string -- please see the error log for more details"
2765                                 );
2766                                 free(value);
2767                                 free(id);
2768                                 buffer_free(sql);
2769                                 *err = -1;
2770                                 return jsonNULL;
2771                         }
2772                 }
2773
2774                 free(value);
2775                 
2776         }
2777
2778         jsonObject* obj = jsonParseString(id);
2779
2780         if ( strcmp( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "primitive" ), "number" ) )
2781                 dbi_conn_quote_string(dbhandle, &id);
2782
2783         buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
2784
2785         char* query = buffer_data(sql);
2786         buffer_free(sql);
2787
2788         osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
2789
2790         dbi_result result = dbi_conn_query(dbhandle, query);
2791         free(query);
2792
2793         if (!result) {
2794                 jsonObjectFree(obj);
2795                 obj = jsonNewObject(NULL);
2796                 osrfLogError(
2797                         OSRF_LOG_MARK,
2798                         "%s ERROR updating %s object with %s = %s",
2799                         MODULENAME,
2800                         osrfHashGet(meta, "fieldmapper"),
2801                         pkey,
2802                         id
2803                 );
2804         }
2805
2806         free(id);
2807
2808         return obj;
2809 }
2810
2811 jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
2812
2813         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
2814
2815         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
2816                 osrfAppSessionStatus(
2817                         ctx->session,
2818                         OSRF_STATUS_BADREQUEST,
2819                         "osrfMethodException",
2820                         ctx->request,
2821                         "No active transaction -- required for DELETE"
2822                 );
2823                 *err = -1;
2824                 return jsonNULL;
2825         }
2826
2827         if (osrfHashGet( meta, "readonly" ) && strncasecmp("true", osrfHashGet( meta, "readonly" ), 4)) {
2828                 osrfAppSessionStatus(
2829                         ctx->session,
2830                         OSRF_STATUS_BADREQUEST,
2831                         "osrfMethodException",
2832                         ctx->request,
2833                         "Cannot DELETE readonly class"
2834                 );
2835                 *err = -1;
2836                 return jsonNULL;
2837         }
2838
2839         dbhandle = writehandle;
2840
2841         jsonObject* obj;
2842
2843         char* pkey = osrfHashGet(meta, "primarykey");
2844
2845         char* id;
2846         if (jsonObjectGetIndex(ctx->params, 0)->classname) {
2847                 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, 0 ))) {
2848                         *err = -1;
2849                         return jsonNULL;
2850                 }
2851
2852                 id = jsonObjectToSimpleString(
2853                         jsonObjectGetIndex(
2854                                 jsonObjectGetIndex(ctx->params, 0),
2855                                 atoi( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "array_position") )
2856                         )
2857                 );
2858         } else {
2859                 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
2860         }
2861
2862         osrfLogDebug(
2863                 OSRF_LOG_MARK,
2864                 "%s deleting %s object with %s = %s",
2865                 MODULENAME,
2866                 osrfHashGet(meta, "fieldmapper"),
2867                 pkey,
2868                 id
2869         );
2870
2871         obj = jsonParseString(id);
2872
2873         if ( strcmp( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "primitive" ), "number" ) )
2874                 dbi_conn_quote_string(writehandle, &id);
2875
2876         dbi_result result = dbi_conn_queryf(writehandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
2877
2878         if (!result) {
2879                 jsonObjectFree(obj);
2880                 obj = jsonNewObject(NULL);
2881                 osrfLogError(
2882                         OSRF_LOG_MARK,
2883                         "%s ERROR deleting %s object with %s = %s",
2884                         MODULENAME,
2885                         osrfHashGet(meta, "fieldmapper"),
2886                         pkey,
2887                         id
2888                 );
2889         }
2890
2891         free(id);
2892
2893         return obj;
2894
2895 }
2896
2897
2898 jsonObject* oilsMakeFieldmapperFromResult( dbi_result result, osrfHash* meta) {
2899         if(!(result && meta)) return jsonNULL;
2900
2901         jsonObject* object = jsonParseString("[]");
2902         jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
2903
2904         osrfHash* fields = osrfHashGet(meta, "fields");
2905
2906         osrfLogInternal(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
2907
2908         osrfHash* _f;
2909         time_t _tmp_dt;
2910         char dt_string[256];
2911         struct tm gmdt;
2912
2913         int fmIndex;
2914         int columnIndex = 1;
2915         int attr;
2916         unsigned short type;
2917         const char* columnName;
2918
2919         /* cycle through the column list */
2920         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
2921
2922                 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
2923
2924                 fmIndex = -1; // reset the position
2925                 
2926                 /* determine the field type and storage attributes */
2927                 type = dbi_result_get_field_type(result, columnName);
2928                 attr = dbi_result_get_field_attribs(result, columnName);
2929
2930                 /* fetch the fieldmapper index */
2931                 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
2932                         char* virt = (char*)osrfHashGet(_f, "virtual");
2933                         char* pos = (char*)osrfHashGet(_f, "array_position");
2934
2935                         if ( !virt || !pos || !(strcmp( virt, "true" )) ) continue;
2936
2937                         fmIndex = atoi( pos );
2938                         osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
2939                 } else {
2940                         continue;
2941                 }
2942
2943                 if (dbi_result_field_is_null(result, columnName)) {
2944                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
2945                 } else {
2946
2947                         switch( type ) {
2948
2949                                 case DBI_TYPE_INTEGER :
2950
2951                                         if( attr & DBI_INTEGER_SIZE8 ) 
2952                                                 jsonObjectSetIndex( object, fmIndex, 
2953                                                         jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
2954                                         else 
2955                                                 jsonObjectSetIndex( object, fmIndex, 
2956                                                         jsonNewNumberObject(dbi_result_get_long(result, columnName)));
2957
2958                                         break;
2959
2960                                 case DBI_TYPE_DECIMAL :
2961                                         jsonObjectSetIndex( object, fmIndex, 
2962                                                         jsonNewNumberObject(dbi_result_get_double(result, columnName)));
2963                                         break;
2964
2965                                 case DBI_TYPE_STRING :
2966
2967
2968                                         jsonObjectSetIndex(
2969                                                 object,
2970                                                 fmIndex,
2971                                                 jsonNewObject( dbi_result_get_string(result, columnName) )
2972                                         );
2973
2974                                         break;
2975
2976                                 case DBI_TYPE_DATETIME :
2977
2978                                         memset(dt_string, '\0', 256);
2979                                         memset(&gmdt, '\0', sizeof(gmdt));
2980                                         memset(&_tmp_dt, '\0', sizeof(_tmp_dt));
2981
2982                                         _tmp_dt = dbi_result_get_datetime(result, columnName);
2983
2984                                         localtime_r( &_tmp_dt, &gmdt );
2985
2986                                         if (!(attr & DBI_DATETIME_DATE)) {
2987                                                 strftime(dt_string, 255, "%T", &gmdt);
2988                                         } else if (!(attr & DBI_DATETIME_TIME)) {
2989                                                 strftime(dt_string, 255, "%F", &gmdt);
2990                                         } else {
2991                                                 strftime(dt_string, 255, "%FT%T%z", &gmdt);
2992                                         }
2993
2994                                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
2995
2996                                         break;
2997
2998                                 case DBI_TYPE_BINARY :
2999                                         osrfLogError( OSRF_LOG_MARK, 
3000                                                 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
3001                         }
3002                 }
3003         }
3004
3005         return object;
3006 }
3007 jsonObject* oilsMakeJSONFromResult( dbi_result result ) {
3008         if(!result) return jsonNULL;
3009
3010         jsonObject* object = jsonParseString("{}");
3011
3012         time_t _tmp_dt;
3013         char dt_string[256];
3014         struct tm gmdt;
3015
3016         int fmIndex;
3017         int columnIndex = 1;
3018         int attr;
3019         unsigned short type;
3020         const char* columnName;
3021
3022         /* cycle through the column list */
3023         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
3024
3025                 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
3026
3027                 fmIndex = -1; // reset the position
3028                 
3029                 /* determine the field type and storage attributes */
3030                 type = dbi_result_get_field_type(result, columnName);
3031                 attr = dbi_result_get_field_attribs(result, columnName);
3032
3033                 if (dbi_result_field_is_null(result, columnName)) {
3034                         jsonObjectSetKey( object, columnName, jsonNewObject(NULL) );
3035                 } else {
3036
3037                         switch( type ) {
3038
3039                                 case DBI_TYPE_INTEGER :
3040
3041                                         if( attr & DBI_INTEGER_SIZE8 ) 
3042                                                 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_longlong(result, columnName)) );
3043                                         else 
3044                                                 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_long(result, columnName)) );
3045                                         break;
3046
3047                                 case DBI_TYPE_DECIMAL :
3048                                         jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_double(result, columnName)) );
3049                                         break;
3050
3051                                 case DBI_TYPE_STRING :
3052                                         jsonObjectSetKey( object, columnName, jsonNewObject(dbi_result_get_string(result, columnName)) );
3053                                         break;
3054
3055                                 case DBI_TYPE_DATETIME :
3056
3057                                         memset(dt_string, '\0', 256);
3058                                         memset(&gmdt, '\0', sizeof(gmdt));
3059                                         memset(&_tmp_dt, '\0', sizeof(_tmp_dt));
3060
3061                                         _tmp_dt = dbi_result_get_datetime(result, columnName);
3062
3063                                         localtime_r( &_tmp_dt, &gmdt );
3064
3065                                         if (!(attr & DBI_DATETIME_DATE)) {
3066                                                 strftime(dt_string, 255, "%T", &gmdt);
3067                                         } else if (!(attr & DBI_DATETIME_TIME)) {
3068                                                 strftime(dt_string, 255, "%F", &gmdt);
3069                                         } else {
3070                                                 strftime(dt_string, 255, "%FT%T%z", &gmdt);
3071                                         }
3072
3073                                         jsonObjectSetKey( object, columnName, jsonNewObject(dt_string) );
3074                                         break;
3075
3076                                 case DBI_TYPE_BINARY :
3077                                         osrfLogError( OSRF_LOG_MARK, 
3078                                                 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
3079                         }
3080                 }
3081         }
3082
3083         return object;
3084 }
3085