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