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