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