]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/c-apps/oils_cstore.c
making order_by injection-safe
[Evergreen.git] / Open-ILS / src / c-apps / oils_cstore.c
1 #include "opensrf/osrf_application.h"
2 #include "opensrf/osrf_settings.h"
3 #include "opensrf/utils.h"
4 #include "objson/object.h"
5 #include "opensrf/log.h"
6 #include "oils_utils.h"
7 #include "oils_constants.h"
8 #include "oils_event.h"
9 #include "oils_idl.h"
10 #include <dbi/dbi.h>
11
12 #include <time.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16 #ifdef RSTORE
17 #  define MODULENAME "open-ils.reporter-store"
18 #else
19 #  define MODULENAME "open-ils.cstore"
20 #endif
21
22 #define PERSIST_NS "http://open-ils.org/spec/opensrf/IDL/persistance/v1"
23 #define OBJECT_NS "http://open-ils.org/spec/opensrf/IDL/objects/v1"
24 #define BASE_NS "http://opensrf.org/spec/IDL/base/v1"
25
26 int osrfAppChildInit();
27 int osrfAppInitialize();
28
29 int verifyObjectClass ( osrfMethodContext*, jsonObject* );
30
31 int beginTransaction ( osrfMethodContext* );
32 int commitTransaction ( osrfMethodContext* );
33 int rollbackTransaction ( osrfMethodContext* );
34
35 int setSavepoint ( osrfMethodContext* );
36 int releaseSavepoint ( osrfMethodContext* );
37 int rollbackSavepoint ( osrfMethodContext* );
38
39 int dispatchCRUDMethod ( osrfMethodContext* );
40 jsonObject* doCreate ( osrfMethodContext*, int* );
41 jsonObject* doRetrieve ( osrfMethodContext*, int* );
42 jsonObject* doUpdate ( osrfMethodContext*, int* );
43 jsonObject* doDelete ( osrfMethodContext*, int* );
44 jsonObject* doSearch ( osrfMethodContext*, osrfHash*, jsonObject*, int* );
45 jsonObject* oilsMakeJSONFromResult( dbi_result, osrfHash* );
46
47 char* searchWriteSimplePredicate ( const char*, osrfHash*, const char*, const char*, const char* );
48 char* searchSimplePredicate ( const char*, const char*, osrfHash*, jsonObject* );
49 char* searchFunctionPredicate ( const char*, osrfHash*, jsonObjectNode* );
50 char* searchFieldTransform (const char*, osrfHash*, jsonObject*);
51 char* searchFieldTransformPredicate ( const char*, osrfHash*, jsonObjectNode* );
52 char* searchBETWEENPredicate ( const char*, osrfHash*, jsonObject* );
53 char* searchINPredicate ( const char*, osrfHash*, jsonObject*, const char* );
54 char* searchPredicate ( const char*, osrfHash*, jsonObject* );
55 char* searchJOIN ( jsonObject*, osrfHash* );
56 char* searchWHERE ( jsonObject*, osrfHash*, int );
57 char* buildSELECT ( jsonObject*, jsonObject*, osrfHash*, osrfMethodContext* );
58
59 void userDataFree( void* );
60 void sessionDataFree( char*, void* );
61
62 dbi_conn writehandle; /* our MASTER db connection */
63 dbi_conn dbhandle; /* our CURRENT db connection */
64 osrfHash readHandles;
65 jsonObject* jsonNULL = NULL; // 
66
67
68 /* parse and store the IDL here */
69 osrfHash* idl;
70
71 int osrfAppInitialize() {
72         growing_buffer* method_name;
73
74         osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
75         osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
76
77         char* idl_filename = osrf_settings_host_value("/apps/%s/app_settings/IDL", MODULENAME);
78         osrfLogInfo(OSRF_LOG_MARK, "Found file:");
79         osrfLogInfo(OSRF_LOG_MARK, idl_filename);
80
81         idl = oilsIDLInit( idl_filename );
82
83         if (!idl) {
84                 osrfLogError(OSRF_LOG_MARK, "Problem loading the IDL.  Seacrest out!");
85                 exit(1);
86         }
87
88         // first we register all the transaction and savepoint methods
89         method_name =  buffer_init(64);
90         buffer_fadd(method_name, "%s.transaction.begin", MODULENAME);
91         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "beginTransaction", "", 0, 0 );
92
93         buffer_reset(method_name);
94         buffer_fadd(method_name, "%s.transaction.commit", MODULENAME);
95         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "commitTransaction", "", 0, 0 );
96
97         buffer_reset(method_name);
98         buffer_fadd(method_name, "%s.transaction.rollback", MODULENAME);
99         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "rollbackTransaction", "", 0, 0 );
100
101
102         buffer_reset(method_name);
103         buffer_fadd(method_name, "%s.savepoint.set", MODULENAME);
104         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "setSavepoint", "", 1, 0 );
105
106         buffer_reset(method_name);
107         buffer_fadd(method_name, "%s.savepoint.release", MODULENAME);
108         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "releaseSavepoint", "", 1, 0 );
109
110         buffer_reset(method_name);
111         buffer_fadd(method_name, "%s.savepoint.rollback", MODULENAME);
112         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "rollbackSavepoint", "", 1, 0 );
113
114         osrfStringArray* global_methods = osrfNewStringArray(6);
115
116         osrfStringArrayAdd( global_methods, "create" );
117         osrfStringArrayAdd( global_methods, "retrieve" );
118         osrfStringArrayAdd( global_methods, "update" );
119         osrfStringArrayAdd( global_methods, "delete" );
120         osrfStringArrayAdd( global_methods, "search" );
121         osrfStringArrayAdd( global_methods, "id_list" );
122
123         int c_index = 0; 
124         char* classname;
125         osrfStringArray* classes = osrfHashKeys( idl );
126         osrfLogDebug(OSRF_LOG_MARK, "%d classes loaded", classes->size );
127         osrfLogDebug(OSRF_LOG_MARK, "At least %d methods will be generated", classes->size * global_methods->size);
128         
129         while ( (classname = osrfStringArrayGetString(classes, c_index++)) ) {
130                 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
131                 
132                 osrfHash* idlClass = osrfHashGet(idl, classname);
133
134                 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), MODULENAME )) {
135                         osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on", MODULENAME, classname);
136                         continue;
137                 }
138
139                 char* virt = osrfHashGet(idlClass, "virtual");
140                 if (virt && !strcmp( virt, "true")) {
141                         osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
142                         continue;
143                 }
144
145                 int i = 0; 
146                 char* method_type;
147                 char* st_tmp;
148                 char* _fm;
149                 char* part;
150                 osrfHash* method_meta;
151                 while ( (method_type = osrfStringArrayGetString(global_methods, i++)) ) {
152                         osrfLogDebug(OSRF_LOG_MARK, "Using files to build %s class methods for %s", method_type, classname);
153
154                         if (!osrfHashGet(idlClass, "fieldmapper")) continue;
155
156                         method_meta = osrfNewHash();
157                         osrfHashSet(method_meta, idlClass, "class");
158
159                         _fm = strdup( (char*)osrfHashGet(idlClass, "fieldmapper") );
160                         part = strtok_r(_fm, ":", &st_tmp);
161
162                         growing_buffer* method_name =  buffer_init(64);
163                         buffer_fadd(method_name, "%s.direct.%s", MODULENAME, part);
164
165                         while ((part = strtok_r(NULL, ":", &st_tmp))) {
166                                 buffer_fadd(method_name, ".%s", part);
167                         }
168                         buffer_fadd(method_name, ".%s", method_type);
169
170
171                         char* method = buffer_data(method_name);
172                         buffer_free(method_name);
173                         free(_fm);
174
175                         osrfHashSet( method_meta, method, "methodname" );
176                         osrfHashSet( method_meta, method_type, "methodtype" );
177
178                         int flags = 0;
179                         if (!(strcmp( method_type, "search" )) || !(strcmp( method_type, "id_list" ))) {
180                                 flags = flags | OSRF_METHOD_STREAMING;
181                         }
182
183                         osrfAppRegisterExtendedMethod(
184                                 MODULENAME,
185                                 method,
186                                 "dispatchCRUDMethod",
187                                 "",
188                                 1,
189                                 flags,
190                                 (void*)method_meta
191                         );
192                 }
193         }
194
195         return 0;
196 }
197
198 /**
199  * Connects to the database 
200  */
201 int osrfAppChildInit() {
202
203         osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
204         dbi_initialize(NULL);
205         osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
206
207         char* driver    = osrf_settings_host_value("/apps/%s/app_settings/driver", MODULENAME);
208         char* user      = osrf_settings_host_value("/apps/%s/app_settings/database/user", MODULENAME);
209         char* host      = osrf_settings_host_value("/apps/%s/app_settings/database/host", MODULENAME);
210         char* port      = osrf_settings_host_value("/apps/%s/app_settings/database/port", MODULENAME);
211         char* db        = osrf_settings_host_value("/apps/%s/app_settings/database/db", MODULENAME);
212         char* pw        = osrf_settings_host_value("/apps/%s/app_settings/database/pw", MODULENAME);
213
214         osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
215         writehandle = dbi_conn_new(driver);
216
217         if(!writehandle) {
218                 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
219                 return -1;
220         }
221         osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
222
223         osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database.  host=%s, "
224                 "port=%s, user=%s, pw=%s, db=%s", MODULENAME, host, port, user, pw, db );
225
226         if(host) dbi_conn_set_option(writehandle, "host", host );
227         if(port) dbi_conn_set_option_numeric( writehandle, "port", atoi(port) );
228         if(user) dbi_conn_set_option(writehandle, "username", user);
229         if(pw) dbi_conn_set_option(writehandle, "password", pw );
230         if(db) dbi_conn_set_option(writehandle, "dbname", db );
231
232         free(user);
233         free(host);
234         free(port);
235         free(db);
236         free(pw);
237
238         const char* err;
239         if (dbi_conn_connect(writehandle) < 0) {
240                 dbi_conn_error(writehandle, &err);
241                 osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
242                 return -1;
243         }
244
245         osrfLogInfo(OSRF_LOG_MARK, "%s successfully connected to the database", MODULENAME);
246
247         int attr;
248         unsigned short type;
249         int i = 0; 
250         char* classname;
251         osrfStringArray* classes = osrfHashKeys( idl );
252         
253         while ( (classname = osrfStringArrayGetString(classes, i++)) ) {
254                 osrfHash* class = osrfHashGet( idl, classname );
255                 osrfHash* fields = osrfHashGet( class, "fields" );
256
257                 char* virt = osrfHashGet(class, "virtual");
258                 if (virt && !strcmp( virt, "true")) {
259                         osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
260                         continue;
261                 }
262
263         
264                 growing_buffer* sql_buf = buffer_init(32);
265                 buffer_fadd( sql_buf, "SELECT * FROM %s WHERE 1=0;", osrfHashGet(class, "tablename") );
266
267                 char* sql = buffer_data(sql_buf);
268                 buffer_free(sql_buf);
269                 osrfLogDebug(OSRF_LOG_MARK, "%s Investigatory SQL = %s", MODULENAME, sql);
270
271                 dbi_result result = dbi_conn_query(writehandle, sql);
272                 free(sql);
273
274                 if (result) {
275
276                         int columnIndex = 1;
277                         const char* columnName;
278                         osrfHash* _f;
279                         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
280
281                                 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
282
283                                 /* fetch the fieldmapper index */
284                                 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
285
286                                         osrfLogDebug(OSRF_LOG_MARK, "Found [%s] in IDL hash...", (char*)columnName);
287
288                                         /* determine the field type and storage attributes */
289                                         type = dbi_result_get_field_type(result, columnName);
290                                         attr = dbi_result_get_field_attribs(result, columnName);
291
292                                         switch( type ) {
293
294                                                 case DBI_TYPE_INTEGER :
295
296                                                         if ( !osrfHashGet(_f, "primitive") )
297                                                                 osrfHashSet(_f,"number", "primitive");
298
299                                                         if( attr & DBI_INTEGER_SIZE8 ) 
300                                                                 osrfHashSet(_f,"INT8", "datatype");
301                                                         else 
302                                                                 osrfHashSet(_f,"INT", "datatype");
303                                                         break;
304
305                                                 case DBI_TYPE_DECIMAL :
306                                                         if ( !osrfHashGet(_f, "primitive") )
307                                                                 osrfHashSet(_f,"number", "primitive");
308
309                                                         osrfHashSet(_f,"NUMERIC", "datatype");
310                                                         break;
311
312                                                 case DBI_TYPE_STRING :
313                                                         if ( !osrfHashGet(_f, "primitive") )
314                                                                 osrfHashSet(_f,"string", "primitive");
315                                                         osrfHashSet(_f,"TEXT", "datatype");
316                                                         break;
317
318                                                 case DBI_TYPE_DATETIME :
319                                                         if ( !osrfHashGet(_f, "primitive") )
320                                                                 osrfHashSet(_f,"string", "primitive");
321
322                                                         osrfHashSet(_f,"TIMESTAMP", "datatype");
323                                                         break;
324
325                                                 case DBI_TYPE_BINARY :
326                                                         if ( !osrfHashGet(_f, "primitive") )
327                                                                 osrfHashSet(_f,"string", "primitive");
328
329                                                         osrfHashSet(_f,"BYTEA", "datatype");
330                                         }
331
332                                         osrfLogDebug(
333                                                 OSRF_LOG_MARK,
334                                                 "Setting [%s] to primitive [%s] and datatype [%s]...",
335                                                 (char*)columnName,
336                                                 osrfHashGet(_f, "primitive"),
337                                                 osrfHashGet(_f, "datatype")
338                                         );
339                                 }
340                         }
341                         dbi_result_free(result);
342                 } else {
343                         osrfLogDebug(OSRF_LOG_MARK, "No data found for class [%s]...", (char*)classname);
344                 }
345         }
346
347         osrfStringArrayFree(classes);
348
349         return 0;
350 }
351
352 void userDataFree( void* blob ) {
353         osrfHashFree( (osrfHash*)blob );
354         return;
355 }
356
357 void sessionDataFree( char* key, void* item ) {
358         if (!(strcmp(key,"xact_id"))) {
359                 if (writehandle)
360                         dbi_conn_query(writehandle, "ROLLBACK;");
361                 free(item);
362         }
363
364         return;
365 }
366
367 int beginTransaction ( osrfMethodContext* ctx ) {
368         OSRF_METHOD_VERIFY_CONTEXT(ctx);
369
370         dbi_result result = dbi_conn_query(writehandle, "START TRANSACTION;");
371         if (!result) {
372                 osrfLogError(OSRF_LOG_MARK, "%s: Error starting transaction", MODULENAME );
373                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error starting transaction" );
374                 return -1;
375         } else {
376                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
377                 osrfAppRespondComplete( ctx, ret );
378                 jsonObjectFree(ret);
379                 
380                 if (!ctx->session->userData) {
381                         ctx->session->userData = osrfNewHash();
382                         ((osrfHash*)ctx->session->userData)->freeItem = &sessionDataFree;
383                 }
384
385                 osrfHashSet( (osrfHash*)ctx->session->userData, strdup( ctx->session->session_id ), "xact_id" );
386                 ctx->session->userDataFree = &userDataFree;
387                 
388         }
389         return 0;
390 }
391
392 int setSavepoint ( osrfMethodContext* ctx ) {
393         OSRF_METHOD_VERIFY_CONTEXT(ctx);
394
395         char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
396
397         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
398                 osrfAppSessionStatus(
399                         ctx->session,
400                         OSRF_STATUS_INTERNALSERVERERROR,
401                         "osrfMethodException",
402                         ctx->request,
403                         "No active transaction -- required for savepoints"
404                 );
405                 return -1;
406         }
407
408         dbi_result result = dbi_conn_queryf(writehandle, "SAVEPOINT \"%s\";", spName);
409         if (!result) {
410                 osrfLogError(
411                         OSRF_LOG_MARK,
412                         "%s: Error creating savepoint %s in transaction %s",
413                         MODULENAME,
414                         spName,
415                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
416                 );
417                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error creating savepoint" );
418                 return -1;
419         } else {
420                 jsonObject* ret = jsonNewObject(spName);
421                 osrfAppRespondComplete( ctx, ret );
422                 jsonObjectFree(ret);
423         }
424         return 0;
425 }
426
427 int releaseSavepoint ( osrfMethodContext* ctx ) {
428         OSRF_METHOD_VERIFY_CONTEXT(ctx);
429
430         char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
431
432         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
433                 osrfAppSessionStatus(
434                         ctx->session,
435                         OSRF_STATUS_INTERNALSERVERERROR,
436                         "osrfMethodException",
437                         ctx->request,
438                         "No active transaction -- required for savepoints"
439                 );
440                 return -1;
441         }
442
443         dbi_result result = dbi_conn_queryf(writehandle, "RELEASE SAVEPOINT \"%s\";", spName);
444         if (!result) {
445                 osrfLogError(
446                         OSRF_LOG_MARK,
447                         "%s: Error releasing savepoint %s in transaction %s",
448                         MODULENAME,
449                         spName,
450                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
451                 );
452                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error releasing savepoint" );
453                 return -1;
454         } else {
455                 jsonObject* ret = jsonNewObject(spName);
456                 osrfAppRespondComplete( ctx, ret );
457                 jsonObjectFree(ret);
458         }
459         return 0;
460 }
461
462 int rollbackSavepoint ( osrfMethodContext* ctx ) {
463         OSRF_METHOD_VERIFY_CONTEXT(ctx);
464
465         char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
466
467         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
468                 osrfAppSessionStatus(
469                         ctx->session,
470                         OSRF_STATUS_INTERNALSERVERERROR,
471                         "osrfMethodException",
472                         ctx->request,
473                         "No active transaction -- required for savepoints"
474                 );
475                 return -1;
476         }
477
478         dbi_result result = dbi_conn_queryf(writehandle, "ROLLBACK TO SAVEPOINT \"%s\";", spName);
479         if (!result) {
480                 osrfLogError(
481                         OSRF_LOG_MARK,
482                         "%s: Error rolling back savepoint %s in transaction %s",
483                         MODULENAME,
484                         spName,
485                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
486                 );
487                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error rolling back savepoint" );
488                 return -1;
489         } else {
490                 jsonObject* ret = jsonNewObject(spName);
491                 osrfAppRespondComplete( ctx, ret );
492                 jsonObjectFree(ret);
493         }
494         return 0;
495 }
496
497 int commitTransaction ( osrfMethodContext* ctx ) {
498         OSRF_METHOD_VERIFY_CONTEXT(ctx);
499
500         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
501                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to commit" );
502                 return -1;
503         }
504
505         dbi_result result = dbi_conn_query(writehandle, "COMMIT;");
506         if (!result) {
507                 osrfLogError(OSRF_LOG_MARK, "%s: Error committing transaction", MODULENAME );
508                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error committing transaction" );
509                 return -1;
510         } else {
511                 osrfHashRemove(ctx->session->userData, "xact_id");
512                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
513                 osrfAppRespondComplete( ctx, ret );
514                 jsonObjectFree(ret);
515         }
516         return 0;
517 }
518
519 int rollbackTransaction ( osrfMethodContext* ctx ) {
520         OSRF_METHOD_VERIFY_CONTEXT(ctx);
521
522         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
523                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to roll back" );
524                 return -1;
525         }
526
527         dbi_result result = dbi_conn_query(writehandle, "ROLLBACK;");
528         if (!result) {
529                 osrfLogError(OSRF_LOG_MARK, "%s: Error rolling back transaction", MODULENAME );
530                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error rolling back transaction" );
531                 return -1;
532         } else {
533                 osrfHashRemove(ctx->session->userData, "xact_id");
534                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
535                 osrfAppRespondComplete( ctx, ret );
536                 jsonObjectFree(ret);
537         }
538         return 0;
539 }
540
541 int dispatchCRUDMethod ( osrfMethodContext* ctx ) {
542         OSRF_METHOD_VERIFY_CONTEXT(ctx);
543
544         osrfHash* meta = (osrfHash*) ctx->method->userData;
545         osrfHash* class_obj = osrfHashGet( meta, "class" );
546         
547         int err = 0;
548
549         jsonObject * obj = NULL;
550         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "create"))
551                 obj = doCreate(ctx, &err);
552
553         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "retrieve"))
554                 obj = doRetrieve(ctx, &err);
555
556         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "update"))
557                 obj = doUpdate(ctx, &err);
558
559         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "delete"))
560                 obj = doDelete(ctx, &err);
561
562         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "search")) {
563
564                 obj = doSearch(ctx, class_obj, ctx->params, &err);
565                 if(err) return err;
566
567                 jsonObjectNode* cur;
568                 jsonObjectIterator* itr = jsonNewObjectIterator( obj );
569                 while ((cur = jsonObjectIteratorNext( itr ))) {
570                         osrfAppRespond( ctx, jsonObjectClone(cur->item) );
571                 }
572                 jsonObjectIteratorFree(itr);
573                 osrfAppRespondComplete( ctx, NULL );
574
575         } else if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "id_list")) {
576
577                 jsonObject* _p = jsonObjectClone( ctx->params );
578                 if (jsonObjectGetIndex( _p, 1 )) {
579                         jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "flesh" );
580                         jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "flesh_columns" );
581                 } else {
582                         jsonObjectSetIndex( _p, 1, jsonParseString("{}") );
583                 }
584
585                 growing_buffer* sel_list = buffer_init(16);
586                 buffer_fadd(sel_list, "{ \"%s\":[\"%s\"] }", osrfHashGet( class_obj, "classname" ), osrfHashGet( class_obj, "primarykey" ));
587                 char* _s = buffer_data(sel_list);
588                 buffer_free(sel_list);
589
590                 jsonObjectSetKey( jsonObjectGetIndex( _p, 1 ), "select", jsonParseString(_s) );
591                 osrfLogDebug(OSRF_LOG_MARK, "%s: Select qualifer set to [%s]", MODULENAME, _s);
592                 free(_s);
593
594                 obj = doSearch(ctx, class_obj, _p, &err);
595                 if(err) return err;
596
597                 jsonObjectNode* cur;
598                 jsonObjectIterator* itr = jsonNewObjectIterator( obj );
599                 while ((cur = jsonObjectIteratorNext( itr ))) {
600                         osrfAppRespond(
601                                 ctx,
602                                 jsonObjectClone(
603                                         jsonObjectGetIndex(
604                                                 cur->item,
605                                                 atoi(
606                                                         osrfHashGet(
607                                                                 osrfHashGet(
608                                                                         osrfHashGet( class_obj, "fields" ),
609                                                                         osrfHashGet( class_obj, "primarykey")
610                                                                 ),
611                                                                 "array_position"
612                                                         )
613                                                 )
614                                         )
615                                 )
616                         );
617                 }
618                 jsonObjectIteratorFree(itr);
619                 osrfAppRespondComplete( ctx, NULL );
620                 
621         } else {
622                 osrfAppRespondComplete( ctx, obj );
623         }
624
625         jsonObjectFree(obj);
626
627         return err;
628 }
629
630 int verifyObjectClass ( osrfMethodContext* ctx, jsonObject* param ) {
631         
632         osrfHash* meta = (osrfHash*) ctx->method->userData;
633         osrfHash* class = osrfHashGet( meta, "class" );
634         
635         if ((strcmp( osrfHashGet(class, "classname"), param->classname ))) {
636
637                 growing_buffer* msg = buffer_init(128);
638                 buffer_fadd(
639                         msg,
640                         "%s: %s method for type %s was passed a %s",
641                         MODULENAME,
642                         osrfHashGet(meta, "methodtype"),
643                         osrfHashGet(class, "classname"),
644                         param->classname
645                 );
646
647                 char* m = buffer_data(msg);
648                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException", ctx->request, m );
649
650                 buffer_free(msg);
651                 free(m);
652
653                 return 0;
654         }
655         return 1;
656 }
657
658 jsonObject* doCreate(osrfMethodContext* ctx, int* err ) {
659
660         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
661         jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
662         jsonObject* options = jsonObjectGetIndex( ctx->params, 1 );
663
664         if (!verifyObjectClass(ctx, target)) {
665                 *err = -1;
666                 return jsonNULL;
667         }
668
669         osrfLogDebug( OSRF_LOG_MARK, "Object seems to be of the correct type" );
670
671         if (!ctx->session || !ctx->session->userData || !osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
672                 osrfLogError( OSRF_LOG_MARK, "No active transaction -- required for CREATE" );
673
674                 osrfAppSessionStatus(
675                         ctx->session,
676                         OSRF_STATUS_BADREQUEST,
677                         "osrfMethodException",
678                         ctx->request,
679                         "No active transaction -- required for CREATE"
680                 );
681                 *err = -1;
682                 return jsonNULL;
683         }
684
685         char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
686
687         // Set the last_xact_id
688         osrfHash* last_xact_id;
689         if ((last_xact_id = oilsIDLFindPath("/%s/fields/last_xact_id", target->classname))) {
690                 int index = atoi( osrfHashGet(last_xact_id, "array_position") );
691                 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
692                 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
693         }       
694
695         osrfLogDebug( OSRF_LOG_MARK, "There is a transaction running..." );
696
697         dbhandle = writehandle;
698
699         osrfHash* fields = osrfHashGet(meta, "fields");
700         char* pkey = osrfHashGet(meta, "primarykey");
701         char* seq = osrfHashGet(meta, "sequence");
702
703         growing_buffer* table_buf = buffer_init(128);
704         growing_buffer* col_buf = buffer_init(128);
705         growing_buffer* val_buf = buffer_init(128);
706
707         buffer_fadd(table_buf,"INSERT INTO %s", osrfHashGet(meta, "tablename"));
708         buffer_add(col_buf,"(");
709         buffer_add(val_buf,"VALUES (");
710
711
712         int i = 0;
713         int first = 1;
714         char* field_name;
715         osrfStringArray* field_list = osrfHashKeys( fields );
716         while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
717
718                 osrfHash* field = osrfHashGet( fields, field_name );
719
720                 if(!( strcmp( osrfHashGet(osrfHashGet(fields,field_name), "virtual"), "true" ) )) continue;
721
722                 jsonObject* field_object = jsonObjectGetIndex( target, atoi(osrfHashGet(field, "array_position")) );
723
724                 char* value;
725                 if (field_object && field_object->classname) {
726                         value = jsonObjectToSimpleString(
727                                         jsonObjectGetIndex(
728                                                 field_object,
729                                                 atoi(
730                                                         osrfHashGet(
731                                                                 osrfHashGet(
732                                                                         oilsIDLFindPath("/%s/fields", field_object->classname),
733                                                                         (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
734                                                                 ),
735                                                                 "array_position"
736                                                         )
737                                                 )
738                                         )
739                                 );
740
741                 } else {
742                         value = jsonObjectToSimpleString( field_object );
743                 }
744
745
746                 if (first) {
747                         first = 0;
748                 } else {
749                         buffer_add(col_buf, ",");
750                         buffer_add(val_buf, ",");
751                 }
752
753                 buffer_add(col_buf, field_name);
754
755                 if (!field_object || field_object->type == JSON_NULL) {
756                         buffer_add( val_buf, "DEFAULT" );
757                         
758                 } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
759                         if ( !strcmp(osrfHashGet(field, "datatype"), "INT8") ) {
760                                 buffer_fadd( val_buf, "%lld", atol(value) );
761                                 
762                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "INT") ) {
763                                 buffer_fadd( val_buf, "%ld", atoll(value) );
764                                 
765                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
766                                 buffer_fadd( val_buf, "%f", atof(value) );
767                         }
768                 } else {
769                         if ( dbi_conn_quote_string(writehandle, &value) ) {
770                                 buffer_fadd( val_buf, "%s", value );
771
772                         } else {
773                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
774                                 osrfAppSessionStatus(
775                                         ctx->session,
776                                         OSRF_STATUS_INTERNALSERVERERROR,
777                                         "osrfMethodException",
778                                         ctx->request,
779                                         "Error quoting string -- please see the error log for more details"
780                                 );
781                                 free(value);
782                                 buffer_free(table_buf);
783                                 buffer_free(col_buf);
784                                 buffer_free(val_buf);
785                                 *err = -1;
786                                 return jsonNULL;
787                         }
788                 }
789
790                 free(value);
791                 
792         }
793
794
795         buffer_add(col_buf,")");
796         buffer_add(val_buf,")");
797
798         growing_buffer* sql = buffer_init(128);
799         buffer_fadd(
800                 sql,
801                 "%s %s %s;",
802                 buffer_data(table_buf),
803                 buffer_data(col_buf),
804                 buffer_data(val_buf)
805         );
806         buffer_free(table_buf);
807         buffer_free(col_buf);
808         buffer_free(val_buf);
809
810         char* query = buffer_data(sql);
811         buffer_free(sql);
812
813         osrfLogDebug(OSRF_LOG_MARK, "%s: Insert SQL [%s]", MODULENAME, query);
814
815         
816         dbi_result result = dbi_conn_query(writehandle, query);
817
818         jsonObject* obj = NULL;
819
820         if (!result) {
821                 obj = jsonNewObject(NULL);
822                 osrfLogError(
823                         OSRF_LOG_MARK,
824                         "%s ERROR inserting %s object using query [%s]",
825                         MODULENAME,
826                         osrfHashGet(meta, "fieldmapper"),
827                         query
828                 );
829                 osrfAppSessionStatus(
830                         ctx->session,
831                         OSRF_STATUS_INTERNALSERVERERROR,
832                         "osrfMethodException",
833                         ctx->request,
834                         "INSERT error -- please see the error log for more details"
835                 );
836                 *err = -1;
837         } else {
838
839                 int pos = atoi(osrfHashGet( osrfHashGet(fields, pkey), "array_position" ));
840                 char* id = jsonObjectToSimpleString(jsonObjectGetIndex(target, pos));
841                 if (!id) {
842                         unsigned long long new_id = dbi_conn_sequence_last(writehandle, seq);
843                         growing_buffer* _id = buffer_init(10);
844                         buffer_fadd(_id, "%lld", new_id);
845                         id = buffer_data(_id);
846                         buffer_free(_id);
847                 }
848
849                 if (    !options
850                         || !jsonObjectGetKey( options, "quiet")
851                         || strcmp( jsonObjectToSimpleString(jsonObjectGetKey( options, "quiet")), "true" )
852                 ) {
853
854                         jsonObject* fake_params = jsonParseString("[]");
855                         jsonObjectPush(fake_params, jsonParseString("{}"));
856
857                         jsonObjectSetKey(
858                                 jsonObjectGetIndex(fake_params, 0),
859                                 osrfHashGet(meta, "primarykey"),
860                                 jsonNewObject(id)
861                         );
862
863                         jsonObject* list = doSearch( ctx,meta, fake_params, err);
864
865                         if(*err) {
866                                 jsonObjectFree( fake_params );
867                                 obj = jsonNULL;
868                         } else {
869                                 obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
870                         }
871
872                         jsonObjectFree( list );
873                         jsonObjectFree( fake_params );
874
875                 } else {
876                         obj = jsonNewObject(id);
877                 }
878
879         }
880
881         free(query);
882
883         return obj;
884
885 }
886
887
888 jsonObject* doRetrieve(osrfMethodContext* ctx, int* err ) {
889
890         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
891
892         jsonObject* obj;
893
894         char* id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
895         jsonObject* order_hash = jsonObjectGetIndex(ctx->params, 1);
896
897         osrfLogDebug(
898                 OSRF_LOG_MARK,
899                 "%s retrieving %s object with id %s",
900                 MODULENAME,
901                 osrfHashGet(meta, "fieldmapper"),
902                 id
903         );
904
905         jsonObject* fake_params = jsonParseString("[]");
906         jsonObjectPush(fake_params, jsonParseString("{}"));
907
908         jsonObjectSetKey(
909                 jsonObjectGetIndex(fake_params, 0),
910                 osrfHashGet(meta, "primarykey"),
911                 jsonParseString(id)
912         );
913
914         if (order_hash) jsonObjectPush(fake_params, jsonObjectClone(order_hash) );
915
916         jsonObject* list = doSearch( ctx,meta, fake_params, err);
917
918         if(*err) {
919                 jsonObjectFree( fake_params );
920                 return jsonNULL;
921         }
922
923         obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
924
925         jsonObjectFree( list );
926         jsonObjectFree( fake_params );
927
928         return obj;
929 }
930
931 char* jsonNumberToDBString ( osrfHash* field, jsonObject* value ) {
932         growing_buffer* val_buf = buffer_init(32);
933
934         if ( !strncmp(osrfHashGet(field, "datatype"), "INT", 3) ) {
935                 if (value->type == JSON_NUMBER) buffer_fadd( val_buf, "%ld", (long)jsonObjectGetNumber(value) );
936                 else buffer_fadd( val_buf, "%ld", atol(jsonObjectToSimpleString(value)) );
937
938         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
939                 if (value->type == JSON_NUMBER) buffer_fadd( val_buf, "%f",  jsonObjectGetNumber(value) );
940                 else buffer_fadd( val_buf, "%f", atof(jsonObjectToSimpleString(value)) );
941         }
942
943         char* pred = buffer_data(val_buf);
944         buffer_free(val_buf);
945
946         return pred;
947 }
948
949 char* searchINPredicate (const char* class, osrfHash* field, jsonObject* node, const char* op) {
950         growing_buffer* sql_buf = buffer_init(32);
951         
952         buffer_fadd(
953                 sql_buf,
954                 "\"%s\".%s ",
955                 class,
956                 osrfHashGet(field, "name")
957         );
958
959         if (!op) {
960                 buffer_add(sql_buf, "IN (");
961         } else if (!(strcasecmp(op,"not in"))) {
962                 buffer_add(sql_buf, "NOT IN (");
963         } else {
964                 buffer_add(sql_buf, "IN (");
965         }
966
967         int in_item_index = 0;
968         int in_item_first = 1;
969         jsonObject* in_item;
970         while ( (in_item = jsonObjectGetIndex(node, in_item_index++)) ) {
971
972                 if (in_item_first)
973                         in_item_first = 0;
974                 else
975                         buffer_add(sql_buf, ", ");
976
977                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
978                         char* val = jsonNumberToDBString( field, in_item );
979                         buffer_fadd( sql_buf, "%s", val );
980                         free(val);
981
982                 } else {
983                         char* key_string = jsonObjectToSimpleString(in_item);
984                         if ( dbi_conn_quote_string(dbhandle, &key_string) ) {
985                                 buffer_fadd( sql_buf, "%s", key_string );
986                                 free(key_string);
987                         } else {
988                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string);
989                                 free(key_string);
990                                 buffer_free(sql_buf);
991                                 return NULL;
992                         }
993                 }
994         }
995
996         buffer_add(
997                 sql_buf,
998                 ")"
999         );
1000
1001         char* pred = buffer_data(sql_buf);
1002         buffer_free(sql_buf);
1003
1004         return pred;
1005 }
1006
1007 char* searchValueTransform( jsonObject* array ) {
1008         growing_buffer* sql_buf = buffer_init(32);
1009
1010         char* val = NULL;
1011         int func_item_index = 0;
1012         int func_item_first = 2;
1013         jsonObject* func_item;
1014         while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1015
1016                 val = jsonObjectToSimpleString(func_item);
1017
1018                 if (func_item_first == 2) {
1019                         buffer_fadd(sql_buf, "%s( ", val);
1020                         free(val);
1021                         func_item_first--;
1022                         continue;
1023                 }
1024
1025                 if (func_item_first)
1026                         func_item_first--;
1027                 else
1028                         buffer_add(sql_buf, ", ");
1029
1030                 if ( dbi_conn_quote_string(dbhandle, &val) ) {
1031                         buffer_fadd( sql_buf, "%s", val );
1032                         free(val);
1033                 } else {
1034                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1035                         free(val);
1036                         buffer_free(sql_buf);
1037                         return NULL;
1038                 }
1039         }
1040
1041         buffer_add(
1042                 sql_buf,
1043                 " )"
1044         );
1045
1046         char* pred = buffer_data(sql_buf);
1047         buffer_free(sql_buf);
1048
1049         return pred;
1050 }
1051
1052 char* searchFunctionPredicate (const char* class, osrfHash* field, jsonObjectNode* node) {
1053         growing_buffer* sql_buf = buffer_init(32);
1054
1055         char* val = searchValueTransform(node->item);
1056         
1057         buffer_fadd(
1058                 sql_buf,
1059                 "\"%s\".%s %s %s",
1060                 class,
1061                 osrfHashGet(field, "name"),
1062                 node->key,
1063                 val
1064         );
1065
1066         char* pred = buffer_data(sql_buf);
1067         buffer_free(sql_buf);
1068         free(val);
1069
1070         return pred;
1071 }
1072
1073 char* searchFieldTransform (const char* class, osrfHash* field, jsonObject* node) {
1074         growing_buffer* sql_buf = buffer_init(32);
1075         
1076         char* field_transform = jsonObjectToSimpleString( jsonObjectGetKey( node, "transform" ) );
1077
1078         buffer_fadd(
1079                 sql_buf,
1080                 "%s(\"%s\".%s)",
1081                 field_transform,
1082                 class,
1083                 osrfHashGet(field, "name")
1084         );
1085
1086         char* pred = buffer_data(sql_buf);
1087         buffer_free(sql_buf);
1088         free(field_transform);
1089
1090         return pred;
1091 }
1092
1093 char* searchFieldTransformPredicate (const char* class, osrfHash* field, jsonObjectNode* node) {
1094         growing_buffer* sql_buf = buffer_init(32);
1095         
1096         char* field_transform = searchFieldTransform( class, field, node->item );
1097         char* value = NULL;
1098
1099         if (jsonObjectGetKey( node->item, "value" )->type == JSON_ARRAY) {
1100                 value = searchValueTransform(jsonObjectGetKey( node->item, "value" ));
1101         } else if (jsonObjectGetKey( node->item, "value" )->type != JSON_NULL) {
1102                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1103                         value = jsonNumberToDBString( field, jsonObjectGetKey( node->item, "value" ) );
1104                 } else {
1105                         value = jsonObjectToSimpleString(jsonObjectGetKey( node->item, "value" ));
1106                         if ( !dbi_conn_quote_string(dbhandle, &value) ) {
1107                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, value);
1108                                 free(value);
1109                                 return NULL;
1110                         }
1111                 }
1112         }
1113
1114         buffer_fadd(
1115                 sql_buf,
1116                 "%s %s %s",
1117                 field_transform,
1118                 node->key,
1119                 value
1120         );
1121
1122         char* pred = buffer_data(sql_buf);
1123         buffer_free(sql_buf);
1124         free(field_transform);
1125
1126         return pred;
1127 }
1128
1129 char* searchSimplePredicate (const char* orig_op, const char* class, osrfHash* field, jsonObject* node) {
1130
1131         char* val = NULL;
1132
1133         if (node->type != JSON_NULL) {
1134                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1135                         val = jsonNumberToDBString( field, node );
1136                 } else {
1137                         val = jsonObjectToSimpleString(node);
1138                 }
1139         }
1140
1141         char* pred = searchWriteSimplePredicate( class, field, osrfHashGet(field, "name"), orig_op, val );
1142
1143         if (val) free(val);
1144
1145         return pred;
1146 }
1147
1148 char* searchWriteSimplePredicate ( const char* class, osrfHash* field, const char* left, const char* orig_op, const char* right ) {
1149
1150         char* val = NULL;
1151         char* op = NULL;
1152         if (right == NULL) {
1153                 val = strdup("NULL");
1154
1155                 if (strcmp( orig_op, "=" ))
1156                         op = strdup("IS NOT");
1157                 else
1158                         op = strdup("IS");
1159
1160         } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1161                 val = strdup(right);
1162                 op = strdup(orig_op);
1163
1164         } else {
1165                 val = strdup(right);
1166                 if ( !dbi_conn_quote_string(dbhandle, &val) ) {
1167                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1168                         free(val);
1169                         return NULL;
1170                 }
1171                 op = strdup(orig_op);
1172         }
1173
1174         growing_buffer* sql_buf = buffer_init(16);
1175         buffer_fadd( sql_buf, "\"%s\".%s %s %s", class, left, op, val );
1176         free(val);
1177         free(op);
1178
1179         char* pred = buffer_data(sql_buf);
1180         buffer_free(sql_buf);
1181
1182         return pred;
1183
1184 }
1185
1186 char* searchBETWEENPredicate (const char* class, osrfHash* field, jsonObject* node) {
1187
1188         char* x_string;
1189         char* y_string;
1190
1191         if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1192                 x_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,0));
1193                 y_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,1));
1194
1195         } else {
1196                 x_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,0));
1197                 y_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,1));
1198                 if ( !(dbi_conn_quote_string(dbhandle, &x_string) && dbi_conn_quote_string(dbhandle, &y_string)) ) {
1199                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key strings [%s] and [%s]", MODULENAME, x_string, y_string);
1200                         free(x_string);
1201                         free(y_string);
1202                         return NULL;
1203                 }
1204         }
1205
1206         growing_buffer* sql_buf = buffer_init(32);
1207         buffer_fadd( sql_buf, "%s BETWEEN %s AND %s", osrfHashGet(field, "name"), x_string, y_string );
1208         free(x_string);
1209         free(y_string);
1210
1211         char* pred = buffer_data(sql_buf);
1212         buffer_free(sql_buf);
1213
1214         return pred;
1215 }
1216
1217 char* searchPredicate ( const char* class, osrfHash* field, jsonObject* node ) {
1218
1219         char* pred = NULL;
1220         if (node->type == JSON_ARRAY) { // equality IN search
1221                 pred = searchINPredicate( class, field, node, NULL );
1222         } else if (node->type == JSON_HASH) { // non-equality search
1223                 jsonObjectNode* pred_node;
1224                 jsonObjectIterator* pred_itr = jsonNewObjectIterator( node );
1225                 while ( (pred_node = jsonObjectIteratorNext( pred_itr )) ) {
1226                         if ( !(strcasecmp( pred_node->key,"between" )) )
1227                                 pred = searchBETWEENPredicate( class, field, pred_node->item );
1228                         else if ( !(strcasecmp( pred_node->key,"in" )) || !(strcasecmp( pred_node->key,"not in" )) )
1229                                 pred = searchINPredicate( class, field, pred_node->item, pred_node->key );
1230                         else if ( pred_node->item->type == JSON_ARRAY )
1231                                 pred = searchFunctionPredicate( class, field, pred_node );
1232                         else if ( pred_node->item->type == JSON_HASH )
1233                                 pred = searchFieldTransformPredicate( class, field, pred_node );
1234                         else 
1235                                 pred = searchSimplePredicate( pred_node->key, class, field, pred_node->item );
1236
1237                         break;
1238                 }
1239         } else if (node->type == JSON_NULL) { // IS NULL search
1240                 growing_buffer* _p = buffer_init(16);
1241                 buffer_fadd(
1242                         _p,
1243                         "\"%s\".%s IS NULL",
1244                         class,
1245                         osrfHashGet(field, "name")
1246                 );
1247                 pred = buffer_data(_p);
1248                 buffer_free(_p);
1249         } else { // equality search
1250                 pred = searchSimplePredicate( "=", class, field, node );
1251         }
1252
1253         return pred;
1254
1255 }
1256
1257
1258 /*
1259
1260 join_filter : {
1261         acn : {
1262                 field : record,
1263                 fkey : id
1264                 type : left
1265                 filter_op : or
1266                 filter : { ... },
1267                 join_filter : {
1268                         acp : {
1269                                 field : call_number,
1270                                 fkey : id,
1271                                 filter : { ... },
1272                         },
1273                 },
1274         },
1275         mrd : {
1276                 field : record,
1277                 type : inner
1278                 fkey : id,
1279                 filter : { ... },
1280         }
1281 }
1282
1283 */
1284
1285 char* searchJOIN ( jsonObject* join_hash, osrfHash* leftmeta ) {
1286
1287         char* leftclass = osrfHashGet(leftmeta, "classname");
1288
1289         growing_buffer* join_buf = buffer_init(128);
1290
1291         jsonObjectNode* snode = NULL;
1292         jsonObjectIterator* search_itr = jsonNewObjectIterator( join_hash );
1293         while ( (snode = jsonObjectIteratorNext( search_itr )) ) {
1294                 osrfHash* idlClass = osrfHashGet( idl, snode->key );
1295
1296                 char* class = osrfHashGet(idlClass, "classname");
1297                 char* table = osrfHashGet(idlClass, "tablename");
1298
1299                 char* type = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "type" ) );
1300                 char* filter_op = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "filter_op" ) );
1301                 char* fkey = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "fkey" ) );
1302                 char* field = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "field" ) );
1303
1304                 jsonObject* filter = jsonObjectGetKey( snode->item, "filter" );
1305                 jsonObject* join_filter = jsonObjectGetKey( snode->item, "join_filter" );
1306
1307                 if (type) {
1308                         if ( !strcasecmp(type,"left") ) {
1309                                 buffer_add(join_buf, " LEFT JOIN");
1310                         } else if ( !strcasecmp(type,"right") ) {
1311                                 buffer_add(join_buf, " RIGHT JOIN");
1312                         } else if ( !strcasecmp(type,"full") ) {
1313                                 buffer_add(join_buf, " FULL JOIN");
1314                         } else {
1315                                 buffer_add(join_buf, " INNER JOIN");
1316                         }
1317                 } else {
1318                         buffer_add(join_buf, " INNER JOIN");
1319                 }
1320
1321                 buffer_fadd(join_buf, " %s AS \"%s\" ON ( \"%s\".%s = \"%s\".%s", table, class, class, field, leftclass, fkey);
1322
1323                 if (filter) {
1324                         if (filter_op) {
1325                                 if (!strcasecmp("or",filter_op)) {
1326                                         buffer_add( join_buf, " OR " );
1327                                 } else {
1328                                         buffer_add( join_buf, " AND " );
1329                                 }
1330                         } else {
1331                                 buffer_add( join_buf, " AND " );
1332                         }
1333
1334                         char* jpred = searchWHERE( filter, idlClass, 0 );
1335                         buffer_fadd( join_buf, " %s", jpred );
1336                         free(jpred);
1337                 }
1338
1339                 buffer_add(join_buf, " ) ");
1340                 
1341                 if (join_filter) {
1342                         char* jpred = searchJOIN( join_filter, idlClass );
1343                         buffer_fadd( join_buf, " %s", jpred );
1344                         free(jpred);
1345                 }
1346
1347         }
1348
1349         char* join = buffer_data(join_buf);
1350         buffer_free(join_buf);
1351
1352         return join;
1353 }
1354
1355 /*
1356
1357 { -or|-and : { field : { op : value }, ... }, ... }
1358
1359 */
1360 char* searchWHERE ( jsonObject* search_hash, osrfHash* meta, int opjoin_type ) {
1361
1362         osrfHash* fields = osrfHashGet(meta, "fields");
1363         char* class = osrfHashGet(meta, "classname");
1364         growing_buffer* sql_buf = buffer_init(128);
1365
1366         jsonObjectNode* node = NULL;
1367
1368         int first = 1;
1369         jsonObjectIterator* search_itr = jsonNewObjectIterator( search_hash );
1370         while ( (node = jsonObjectIteratorNext( search_itr )) ) {
1371
1372                 if (first) {
1373                         first = 0;
1374                 } else {
1375                         if (opjoin_type == 1) buffer_add(sql_buf, " OR ");
1376                         else buffer_add(sql_buf, " AND ");
1377                 }
1378
1379                 if ( !strcasecmp("-or",node->key) ) {
1380                         char* subpred = searchWHERE( node->item, meta, 1);
1381                         buffer_fadd(sql_buf, "( %s )", subpred);
1382                         free(subpred);
1383                 } else if ( !strcasecmp("-and",node->key) ) {
1384                         char* subpred = searchWHERE( node->item, meta, 0);
1385                         buffer_fadd(sql_buf, "( %s )", subpred);
1386                         free(subpred);
1387                 } else {
1388
1389                         osrfHash* field = osrfHashGet( fields, node->key );
1390
1391                         if (!field) {
1392                                 osrfLogError(
1393                                         OSRF_LOG_MARK,
1394                                         "%s: Attempt to reference non-existant column %s on table %s",
1395                                         MODULENAME,
1396                                         node->key,
1397                                         osrfHashGet(meta, "tablename")
1398                                 );
1399                                 buffer_free(sql_buf);
1400                                 return NULL;
1401                         }
1402
1403                         char* subpred = searchPredicate( class, field, node->item );
1404                         buffer_fadd( sql_buf, "%s", subpred );
1405                         free(subpred);
1406                 }
1407         }
1408
1409         jsonObjectIteratorFree(search_itr);
1410
1411         char* pred = buffer_data(sql_buf);
1412         buffer_free(sql_buf);
1413
1414         return pred;
1415 }
1416
1417 char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
1418
1419         osrfHash* fields = osrfHashGet(meta, "fields");
1420         char* core_class = osrfHashGet(meta, "classname");
1421
1422         jsonObject* join_hash = jsonObjectGetKey( order_hash, "join_filter" );
1423
1424         jsonObjectNode* node = NULL;
1425         jsonObjectNode* snode = NULL;
1426         jsonObjectNode* onode = NULL;
1427         jsonObject* _tmp = NULL;
1428         jsonObject* selhash = NULL;
1429         jsonObject* defaultselhash = NULL;
1430
1431         growing_buffer* sql_buf = buffer_init(128);
1432         growing_buffer* select_buf = buffer_init(128);
1433
1434         if ( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
1435                 defaultselhash = jsonParseString( "{}" );
1436                 selhash = defaultselhash;
1437         }
1438         
1439         if ( !jsonObjectGetKey(selhash,core_class) ) {
1440                 jsonObjectSetKey( selhash, core_class, jsonParseString( "[]" ) );
1441                 jsonObject* flist = jsonObjectGetKey( selhash, core_class );
1442                 
1443                 int i = 0;
1444                 char* field;
1445
1446                 osrfStringArray* keys = osrfHashKeys( fields );
1447                 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
1448                         if ( strcasecmp( "true", osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
1449                                 jsonObjectPush( flist, jsonNewObject( field ) );
1450                 }
1451                 osrfStringArrayFree(keys);
1452         }
1453
1454         int first = 1;
1455         jsonObjectIterator* class_itr = jsonNewObjectIterator( selhash );
1456         while ( (snode = jsonObjectIteratorNext( class_itr )) ) {
1457
1458                 osrfHash* idlClass = osrfHashGet( idl, snode->key );
1459                 if (!idlClass) continue;
1460                 char* cname = osrfHashGet(idlClass, "classname");
1461
1462                 //if (strcmp(core_class,snode->key)) continue;
1463
1464                 jsonObjectIterator* select_itr = jsonNewObjectIterator( snode->item );
1465                 while ( (node = jsonObjectIteratorNext( select_itr )) ) {
1466                         osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), jsonObjectToSimpleString(node->item) );
1467                         char* fname = osrfHashGet(field, "name");
1468
1469                         if (!field) continue;
1470
1471                         if (first) {
1472                                 first = 0;
1473                         } else {
1474                                 buffer_add(select_buf, ",");
1475                         }
1476
1477                         buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
1478                 }
1479         }
1480
1481         char* col_list = buffer_data(select_buf);
1482         buffer_free(select_buf);
1483
1484         buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\"", col_list, osrfHashGet(meta, "tablename"), core_class );
1485
1486         if ( join_hash ) {
1487                 char* join_clause = searchJOIN( join_hash, meta );
1488                 buffer_fadd(sql_buf, " %s", join_clause);
1489                 free(join_clause);
1490         }
1491
1492         buffer_add(sql_buf, " WHERE ");
1493
1494         char* pred = searchWHERE( search_hash, meta, 0 );
1495         if (!pred) {
1496                 osrfAppSessionStatus(
1497                         ctx->session,
1498                         OSRF_STATUS_INTERNALSERVERERROR,
1499                                 "osrfMethodException",
1500                                 ctx->request,
1501                                 "Severe query error -- see error log for more details"
1502                         );
1503                 buffer_free(sql_buf);
1504                 return NULL;
1505         } else {
1506                 buffer_add(sql_buf, pred);
1507                 free(pred);
1508         }
1509
1510         if (order_hash) {
1511                 char* string = NULL;
1512                 if ( (_tmp = jsonObjectGetKey( order_hash, "order_by" )) ){
1513
1514                         growing_buffer* order_buf = buffer_init(128);
1515
1516                         first = 1;
1517                         jsonObjectIterator* class_itr = jsonNewObjectIterator( _tmp );
1518                         while ( (snode = jsonObjectIteratorNext( class_itr )) ) {
1519
1520                                 if (!jsonObjectGetKey(selhash,snode->key))
1521                                         continue;
1522
1523                                 if ( snode->item->type == JSON_HASH ) {
1524
1525                                         jsonObjectIterator* order_itr = jsonNewObjectIterator( snode->item );
1526                                         while ( (onode = jsonObjectIteratorNext( order_itr )) ) {
1527
1528                                                 if (!oilsIDLFindPath( "/%s/fields/%s", snode->key, onode->key ))
1529                                                         continue;
1530
1531                                                 char* direction = NULL;
1532                                                 if ( onode->item->type == JSON_HASH ) {
1533                                                         if ( jsonObjectGetKey( onode->item, "transform" ) ) {
1534                                                                 string = searchFieldTransform(
1535                                                                         snode->key,
1536                                                                         oilsIDLFindPath( "/%s/fields/%s", snode->key, onode->key ),
1537                                                                         onode->item
1538                                                                 );
1539                                                         } else {
1540                                                                 growing_buffer* field_buf = buffer_init(16);
1541                                                                 buffer_fadd(field_buf, "\"%s\".%s", snode->key, onode->key);
1542                                                                 string = buffer_data(field_buf);
1543                                                                 buffer_free(field_buf);
1544                                                         }
1545
1546                                                         if ( (_tmp = jsonObjectGetKey( onode->item, "direction" )) ) {
1547                                                                 direction = jsonObjectToSimpleString(_tmp);
1548                                                                 if (!strncasecmp(direction, "d", 1)) {
1549                                                                         free(direction);
1550                                                                         direction = " DESC";
1551                                                                 } else {
1552                                                                         free(direction);
1553                                                                         direction = " ASC";
1554                                                                 }
1555                                                         }
1556
1557                                                 } else {
1558                                                         string = strdup(onode->key);
1559                                                         direction = jsonObjectToSimpleString(onode->item);
1560                                                         if (!strncasecmp(direction, "d", 1)) {
1561                                                                 free(direction);
1562                                                                 direction = " DESC";
1563                                                         } else {
1564                                                                 free(direction);
1565                                                                 direction = " ASC";
1566                                                         }
1567                                                 }
1568
1569                                                 if (first) {
1570                                                         first = 0;
1571                                                 } else {
1572                                                         buffer_add(order_buf, ", ");
1573                                                 }
1574
1575                                                 buffer_add(order_buf, string);
1576                                                 free(string);
1577
1578                                                 if (direction) {
1579                                                         buffer_add(order_buf, direction);
1580                                                 }
1581
1582                                         }
1583
1584                                 } else {
1585                                         string = jsonObjectToSimpleString(snode->item);
1586                                         buffer_add(order_buf, string);
1587                                         free(string);
1588                                         break;
1589                                 }
1590
1591                         }
1592
1593                         string = buffer_data(order_buf);
1594                         buffer_free(order_buf);
1595
1596                         if (strlen(string)) {
1597                                 buffer_fadd(
1598                                         sql_buf,
1599                                         " ORDER BY %s",
1600                                         string
1601                                 );
1602                         }
1603
1604                         free(string);
1605                 }
1606
1607                 if ( (_tmp = jsonObjectGetKey( order_hash, "limit" )) ){
1608                         string = jsonObjectToSimpleString(_tmp);
1609                         buffer_fadd(
1610                                 sql_buf,
1611                                 " LIMIT %d",
1612                                 atoi(string)
1613                         );
1614                         free(string);
1615                 }
1616
1617                 _tmp = jsonObjectGetKey( order_hash, "offset" );
1618                 if (_tmp) {
1619                         string = jsonObjectToSimpleString(_tmp);
1620                         buffer_fadd(
1621                                 sql_buf,
1622                                 " OFFSET %d",
1623                                 atoi(string)
1624                         );
1625                         free(string);
1626                 }
1627         }
1628
1629         buffer_add(sql_buf, ";");
1630
1631         char* sql = buffer_data(sql_buf);
1632         buffer_free(sql_buf);
1633         if (defaultselhash) jsonObjectFree(defaultselhash);
1634
1635         return sql;
1636 }
1637
1638 jsonObject* doSearch ( osrfMethodContext* ctx, osrfHash* meta, jsonObject* params, int* err ) {
1639
1640         // XXX for now...
1641         dbhandle = writehandle;
1642
1643         osrfHash* links = osrfHashGet(meta, "links");
1644         osrfHash* fields = osrfHashGet(meta, "fields");
1645         char* core_class = osrfHashGet(meta, "classname");
1646         char* pkey = osrfHashGet(meta, "primarykey");
1647
1648         jsonObject* _tmp;
1649         jsonObject* obj;
1650         jsonObject* search_hash = jsonObjectGetIndex(params, 0);
1651         jsonObject* order_hash = jsonObjectGetIndex(params, 1);
1652
1653         char* sql = buildSELECT( search_hash, order_hash, meta, ctx );
1654         if (!sql) {
1655                 *err = -1;
1656                 return NULL;
1657         }
1658         
1659         osrfLogDebug(OSRF_LOG_MARK, "%s SQL =  %s", MODULENAME, sql);
1660         dbi_result result = dbi_conn_query(dbhandle, sql);
1661
1662         osrfHash* dedup = osrfNewHash();
1663         jsonObject* res_list = jsonParseString("[]");
1664         if(result) {
1665                 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
1666
1667                 if (dbi_result_first_row(result)) {
1668                         /* JSONify the result */
1669                         osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
1670                         do {
1671                                 obj = oilsMakeJSONFromResult( result, meta );
1672                                 int pkey_pos = atoi( osrfHashGet( osrfHashGet( fields, pkey ), "array_position" ) );
1673                                 char* pkey_val = jsonObjectToSimpleString( jsonObjectGetIndex( obj, pkey_pos ) );
1674                                 if ( osrfHashGet( dedup, pkey_val ) ) {
1675                                         jsonObjectFree(obj);
1676                                 } else {
1677                                         osrfHashSet( dedup, pkey_val, pkey_val );
1678                                         jsonObjectPush(res_list, obj);
1679                                 }
1680                         } while (dbi_result_next_row(result));
1681                 } else {
1682                         osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
1683                 }
1684
1685                 /* clean up the query */
1686                 dbi_result_free(result); 
1687
1688         } else {
1689                 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]", MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
1690                 osrfAppSessionStatus(
1691                         ctx->session,
1692                         OSRF_STATUS_INTERNALSERVERERROR,
1693                         "osrfMethodException",
1694                         ctx->request,
1695                         "Severe query error -- see error log for more details"
1696                 );
1697                 *err = -1;
1698                 free(sql);
1699                 jsonObjectFree(res_list);
1700                 return jsonNULL;
1701
1702         }
1703
1704         free(sql);
1705
1706         if (res_list->size && order_hash) {
1707                 _tmp = jsonObjectGetKey( order_hash, "flesh" );
1708                 if (_tmp) {
1709                         int x = (int)jsonObjectGetNumber(_tmp);
1710
1711                         jsonObject* flesh_blob = NULL;
1712                         if ((flesh_blob = jsonObjectGetKey( order_hash, "flesh_fields" )) && x > 0) {
1713
1714                                 flesh_blob = jsonObjectClone( flesh_blob );
1715                                 jsonObject* flesh_fields = jsonObjectGetKey( flesh_blob, core_class );
1716
1717                                 osrfStringArray* link_fields = NULL;
1718
1719                                 if (flesh_fields) {
1720                                         if (flesh_fields->size == 1) {
1721                                                 char* _t = jsonObjectToSimpleString( jsonObjectGetIndex( flesh_fields, 0 ) );
1722                                                 if (!strcmp(_t,"*")) link_fields = osrfHashKeys( links );
1723                                                 free(_t);
1724                                         }
1725
1726                                         if (!link_fields) {
1727                                                 jsonObjectNode* _f;
1728                                                 link_fields = osrfNewStringArray(1);
1729                                                 jsonObjectIterator* _i = jsonNewObjectIterator( flesh_fields );
1730                                                 while ((_f = jsonObjectIteratorNext( _i ))) {
1731                                                         osrfStringArrayAdd( link_fields, jsonObjectToSimpleString( _f->item ) );
1732                                                 }
1733                                         }
1734                                 }
1735
1736                                 jsonObjectNode* cur;
1737                                 jsonObjectIterator* itr = jsonNewObjectIterator( res_list );
1738                                 while ((cur = jsonObjectIteratorNext( itr ))) {
1739
1740                                         int i = 0;
1741                                         char* link_field;
1742                                         
1743                                         while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
1744
1745                                                 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
1746
1747                                                 osrfHash* kid_link = osrfHashGet(links, link_field);
1748                                                 if (!kid_link) continue;
1749
1750                                                 osrfHash* field = osrfHashGet(fields, link_field);
1751                                                 if (!field) continue;
1752
1753                                                 osrfHash* value_field = field;
1754
1755                                                 osrfHash* kid_idl = osrfHashGet(idl, osrfHashGet(kid_link, "class"));
1756                                                 if (!kid_idl) continue;
1757
1758                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
1759                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
1760                                                 }
1761                                                         
1762                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
1763                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
1764                                                 }
1765
1766                                                 osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
1767
1768                                                 if (link_map->size > 0) {
1769                                                         jsonObject* _kid_key = jsonParseString("[]");
1770                                                         jsonObjectPush(
1771                                                                 _kid_key,
1772                                                                 jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
1773                                                         );
1774
1775                                                         jsonObjectSetKey(
1776                                                                 flesh_blob,
1777                                                                 osrfHashGet(kid_link, "class"),
1778                                                                 _kid_key
1779                                                         );
1780                                                 };
1781
1782                                                 osrfLogDebug(
1783                                                         OSRF_LOG_MARK,
1784                                                         "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
1785                                                         osrfHashGet(kid_link, "field"),
1786                                                         osrfHashGet(kid_link, "class"),
1787                                                         osrfHashGet(kid_link, "key"),
1788                                                         osrfHashGet(kid_link, "reltype")
1789                                                 );
1790
1791                                                 jsonObject* fake_params = jsonParseString("[]");
1792                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // search hash
1793                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // order/flesh hash
1794
1795                                                 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
1796
1797                                                 char* search_key =
1798                                                 jsonObjectToSimpleString(
1799                                                         jsonObjectGetIndex(
1800                                                                 cur->item,
1801                                                                 atoi( osrfHashGet(value_field, "array_position") )
1802                                                         )
1803                                                 );
1804
1805                                                 if (!search_key) {
1806                                                         osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
1807                                                         continue;
1808                                                 }
1809                                                         
1810                                                 jsonObjectSetKey(
1811                                                         jsonObjectGetIndex(fake_params, 0),
1812                                                         osrfHashGet(kid_link, "key"),
1813                                                         jsonNewObject( search_key )
1814                                                 );
1815
1816                                                 free(search_key);
1817
1818
1819                                                 jsonObjectSetKey(
1820                                                         jsonObjectGetIndex(fake_params, 1),
1821                                                         "flesh",
1822                                                         jsonNewNumberObject( (double)(x - 1 + link_map->size) )
1823                                                 );
1824
1825                                                 if (flesh_blob)
1826                                                         jsonObjectSetKey( jsonObjectGetIndex(fake_params, 1), "flesh_fields", jsonObjectClone(flesh_blob) );
1827
1828                                                 if (jsonObjectGetKey(order_hash, "order_by")) {
1829                                                         jsonObjectSetKey(
1830                                                                 jsonObjectGetIndex(fake_params, 1),
1831                                                                 "order_by",
1832                                                                 jsonObjectClone(jsonObjectGetKey(order_hash, "order_by"))
1833                                                         );
1834                                                 }
1835
1836                                                 if (jsonObjectGetKey(order_hash, "select")) {
1837                                                         jsonObjectSetKey(
1838                                                                 jsonObjectGetIndex(fake_params, 1),
1839                                                                 "select",
1840                                                                 jsonObjectClone(jsonObjectGetKey(order_hash, "select"))
1841                                                         );
1842                                                 }
1843
1844                                                 jsonObject* kids = doSearch(ctx, kid_idl, fake_params, err);
1845
1846                                                 if(*err) {
1847                                                         jsonObjectFree( fake_params );
1848                                                         osrfStringArrayFree(link_fields);
1849                                                         jsonObjectIteratorFree(itr);
1850                                                         jsonObjectFree(res_list);
1851                                                         return jsonNULL;
1852                                                 }
1853
1854                                                 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
1855
1856                                                 jsonObject* X = NULL;
1857                                                 if ( link_map->size > 0 && kids->size > 0 ) {
1858                                                         X = kids;
1859                                                         kids = jsonParseString("[]");
1860
1861                                                         jsonObjectNode* _k_node;
1862                                                         jsonObjectIterator* _k = jsonNewObjectIterator( X );
1863                                                         while ((_k_node = jsonObjectIteratorNext( _k ))) {
1864                                                                 jsonObjectPush(
1865                                                                         kids,
1866                                                                         jsonObjectClone(
1867                                                                                 jsonObjectGetIndex(
1868                                                                                         _k_node->item,
1869                                                                                         (unsigned long)atoi(
1870                                                                                                 osrfHashGet(
1871                                                                                                         osrfHashGet(
1872                                                                                                                 osrfHashGet(
1873                                                                                                                         osrfHashGet(
1874                                                                                                                                 idl,
1875                                                                                                                                 osrfHashGet(kid_link, "class")
1876                                                                                                                         ),
1877                                                                                                                         "fields"
1878                                                                                                                 ),
1879                                                                                                                 osrfStringArrayGetString( link_map, 0 )
1880                                                                                                         ),
1881                                                                                                         "array_position"
1882                                                                                                 )
1883                                                                                         )
1884                                                                                 )
1885                                                                         )
1886                                                                 );
1887                                                         }
1888                                                 }
1889
1890                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" )) || !(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) {
1891                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
1892                                                         jsonObjectSetIndex(
1893                                                                 cur->item,
1894                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
1895                                                                 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
1896                                                         );
1897                                                 }
1898
1899                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
1900                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
1901                                                         jsonObjectSetIndex(
1902                                                                 cur->item,
1903                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
1904                                                                 jsonObjectClone( kids )
1905                                                         );
1906                                                 }
1907
1908                                                 if (X) {
1909                                                         jsonObjectFree(kids);
1910                                                         kids = X;
1911                                                 }
1912
1913                                                 jsonObjectFree( kids );
1914                                                 jsonObjectFree( fake_params );
1915
1916                                                 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
1917                                                 osrfLogDebug(OSRF_LOG_MARK, "%s", jsonObjectToJSON(cur->item));
1918
1919                                         }
1920                                 }
1921                                 jsonObjectFree( flesh_blob );
1922                                 osrfStringArrayFree(link_fields);
1923                                 jsonObjectIteratorFree(itr);
1924                         }
1925                 }
1926         }
1927
1928         return res_list;
1929 }
1930
1931
1932 jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
1933
1934         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
1935         jsonObject* target = jsonObjectGetIndex(ctx->params, 0);
1936
1937         if (!verifyObjectClass(ctx, target)) {
1938                 *err = -1;
1939                 return jsonNULL;
1940         }
1941
1942         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
1943                 osrfAppSessionStatus(
1944                         ctx->session,
1945                         OSRF_STATUS_BADREQUEST,
1946                         "osrfMethodException",
1947                         ctx->request,
1948                         "No active transaction -- required for UPDATE"
1949                 );
1950                 *err = -1;
1951                 return jsonNULL;
1952         }
1953
1954         dbhandle = writehandle;
1955
1956         char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
1957
1958         // Set the last_xact_id
1959         osrfHash* last_xact_id;
1960         if ((last_xact_id = oilsIDLFindPath("/%s/fields/last_xact_id", target->classname))) {
1961                 int index = atoi( osrfHashGet(last_xact_id, "array_position") );
1962                 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
1963                 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
1964         }       
1965
1966         char* pkey = osrfHashGet(meta, "primarykey");
1967         osrfHash* fields = osrfHashGet(meta, "fields");
1968
1969         char* id =
1970                 jsonObjectToSimpleString(
1971                         jsonObjectGetIndex(
1972                                 target,
1973                                 atoi( osrfHashGet( osrfHashGet( fields, pkey ), "array_position" ) )
1974                         )
1975                 );
1976
1977         osrfLogDebug(
1978                 OSRF_LOG_MARK,
1979                 "%s updating %s object with %s = %s",
1980                 MODULENAME,
1981                 osrfHashGet(meta, "fieldmapper"),
1982                 pkey,
1983                 id
1984         );
1985
1986         growing_buffer* sql = buffer_init(128);
1987         buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
1988
1989         int i = 0;
1990         int first = 1;
1991         char* field_name;
1992         osrfStringArray* field_list = osrfHashKeys( fields );
1993         while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
1994
1995                 osrfHash* field = osrfHashGet( fields, field_name );
1996
1997                 if(!( strcmp( field_name, pkey ) )) continue;
1998                 if(!( strcmp( osrfHashGet(osrfHashGet(fields,field_name), "virtual"), "true" ) )) continue;
1999
2000                 jsonObject* field_object = jsonObjectGetIndex( target, atoi(osrfHashGet(field, "array_position")) );
2001
2002                 char* value;
2003                 if (field_object && field_object->classname) {
2004                         value = jsonObjectToSimpleString(
2005                                         jsonObjectGetIndex(
2006                                                 field_object,
2007                                                 atoi(
2008                                                         osrfHashGet(
2009                                                                 osrfHashGet(
2010                                                                         oilsIDLFindPath("/%s/fields", field_object->classname),
2011                                                                         (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
2012                                                                 ),
2013                                                                 "array_position"
2014                                                         )
2015                                                 )
2016                                         )
2017                                 );
2018
2019                 } else {
2020                         value = jsonObjectToSimpleString( field_object );
2021                 }
2022
2023                 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
2024
2025                 if (!field_object || field_object->type == JSON_NULL) {
2026                         if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
2027                                 if (first) first = 0;
2028                                 else buffer_add(sql, ",");
2029                                 buffer_fadd( sql, " %s = NULL", field_name );
2030                         }
2031                         
2032                 } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
2033                         if (first) first = 0;
2034                         else buffer_add(sql, ",");
2035
2036                         if ( !strncmp(osrfHashGet(field, "datatype"), "INT", (size_t)3) ) {
2037                                 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
2038                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
2039                                 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
2040                         }
2041
2042                         osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, osrfHashGet(field, "datatype"));
2043
2044                 } else {
2045                         if ( dbi_conn_quote_string(dbhandle, &value) ) {
2046                                 if (first) first = 0;
2047                                 else buffer_add(sql, ",");
2048                                 buffer_fadd( sql, " %s = %s", field_name, value );
2049
2050                         } else {
2051                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
2052                                 osrfAppSessionStatus(
2053                                         ctx->session,
2054                                         OSRF_STATUS_INTERNALSERVERERROR,
2055                                         "osrfMethodException",
2056                                         ctx->request,
2057                                         "Error quoting string -- please see the error log for more details"
2058                                 );
2059                                 free(value);
2060                                 free(id);
2061                                 buffer_free(sql);
2062                                 *err = -1;
2063                                 return jsonNULL;
2064                         }
2065                 }
2066
2067                 free(value);
2068                 
2069         }
2070
2071         jsonObject* obj = jsonParseString(id);
2072
2073         if ( strcmp( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "primitive" ), "number" ) )
2074                 dbi_conn_quote_string(dbhandle, &id);
2075
2076         buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
2077
2078         char* query = buffer_data(sql);
2079         buffer_free(sql);
2080
2081         osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
2082
2083         dbi_result result = dbi_conn_query(dbhandle, query);
2084         free(query);
2085
2086         if (!result) {
2087                 jsonObjectFree(obj);
2088                 obj = jsonNewObject(NULL);
2089                 osrfLogError(
2090                         OSRF_LOG_MARK,
2091                         "%s ERROR updating %s object with %s = %s",
2092                         MODULENAME,
2093                         osrfHashGet(meta, "fieldmapper"),
2094                         pkey,
2095                         id
2096                 );
2097         }
2098
2099         free(id);
2100
2101         return obj;
2102 }
2103
2104 jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
2105
2106         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
2107
2108         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
2109                 osrfAppSessionStatus(
2110                         ctx->session,
2111                         OSRF_STATUS_BADREQUEST,
2112                         "osrfMethodException",
2113                         ctx->request,
2114                         "No active transaction -- required for DELETE"
2115                 );
2116                 *err = -1;
2117                 return jsonNULL;
2118         }
2119
2120         dbhandle = writehandle;
2121
2122         jsonObject* obj;
2123
2124         char* pkey = osrfHashGet(meta, "primarykey");
2125
2126         char* id;
2127         if (jsonObjectGetIndex(ctx->params, 0)->classname) {
2128                 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, 0 ))) {
2129                         *err = -1;
2130                         return jsonNULL;
2131                 }
2132
2133                 id = jsonObjectToSimpleString(
2134                         jsonObjectGetIndex(
2135                                 jsonObjectGetIndex(ctx->params, 0),
2136                                 atoi( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "array_position") )
2137                         )
2138                 );
2139         } else {
2140                 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
2141         }
2142
2143         osrfLogDebug(
2144                 OSRF_LOG_MARK,
2145                 "%s deleting %s object with %s = %s",
2146                 MODULENAME,
2147                 osrfHashGet(meta, "fieldmapper"),
2148                 pkey,
2149                 id
2150         );
2151
2152         obj = jsonParseString(id);
2153
2154         if ( strcmp( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "primitive" ), "number" ) )
2155                 dbi_conn_quote_string(writehandle, &id);
2156
2157         dbi_result result = dbi_conn_queryf(writehandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
2158
2159         if (!result) {
2160                 jsonObjectFree(obj);
2161                 obj = jsonNewObject(NULL);
2162                 osrfLogError(
2163                         OSRF_LOG_MARK,
2164                         "%s ERROR deleting %s object with %s = %s",
2165                         MODULENAME,
2166                         osrfHashGet(meta, "fieldmapper"),
2167                         pkey,
2168                         id
2169                 );
2170         }
2171
2172         free(id);
2173
2174         return obj;
2175
2176 }
2177
2178
2179 jsonObject* oilsMakeJSONFromResult( dbi_result result, osrfHash* meta) {
2180         if(!(result && meta)) return jsonNULL;
2181
2182         jsonObject* object = jsonParseString("[]");
2183         jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
2184
2185         osrfHash* fields = osrfHashGet(meta, "fields");
2186
2187         osrfLogInternal(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
2188
2189         osrfHash* _f;
2190         time_t _tmp_dt;
2191         char dt_string[256];
2192         struct tm gmdt;
2193
2194         int fmIndex;
2195         int columnIndex = 1;
2196         int attr;
2197         unsigned short type;
2198         const char* columnName;
2199
2200         /* cycle through the column list */
2201         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
2202
2203                 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
2204
2205                 fmIndex = -1; // reset the position
2206                 
2207                 /* determine the field type and storage attributes */
2208                 type = dbi_result_get_field_type(result, columnName);
2209                 attr = dbi_result_get_field_attribs(result, columnName);
2210
2211                 /* fetch the fieldmapper index */
2212                 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
2213                         char* virt = (char*)osrfHashGet(_f, "virtual");
2214                         char* pos = (char*)osrfHashGet(_f, "array_position");
2215
2216                         if ( !virt || !pos || !(strcmp( virt, "true" )) ) continue;
2217
2218                         fmIndex = atoi( pos );
2219                         osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
2220                 } else {
2221                         continue;
2222                 }
2223
2224                 if (dbi_result_field_is_null(result, columnName)) {
2225                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
2226                 } else {
2227
2228                         switch( type ) {
2229
2230                                 case DBI_TYPE_INTEGER :
2231
2232                                         if( attr & DBI_INTEGER_SIZE8 ) 
2233                                                 jsonObjectSetIndex( object, fmIndex, 
2234                                                         jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
2235                                         else 
2236                                                 jsonObjectSetIndex( object, fmIndex, 
2237                                                         jsonNewNumberObject(dbi_result_get_long(result, columnName)));
2238
2239                                         break;
2240
2241                                 case DBI_TYPE_DECIMAL :
2242                                         jsonObjectSetIndex( object, fmIndex, 
2243                                                         jsonNewNumberObject(dbi_result_get_double(result, columnName)));
2244                                         break;
2245
2246                                 case DBI_TYPE_STRING :
2247
2248
2249                                         jsonObjectSetIndex(
2250                                                 object,
2251                                                 fmIndex,
2252                                                 jsonNewObject( dbi_result_get_string(result, columnName) )
2253                                         );
2254
2255                                         break;
2256
2257                                 case DBI_TYPE_DATETIME :
2258
2259                                         memset(dt_string, '\0', 256);
2260                                         memset(&gmdt, '\0', sizeof(gmdt));
2261                                         memset(&_tmp_dt, '\0', sizeof(_tmp_dt));
2262
2263                                         _tmp_dt = dbi_result_get_datetime(result, columnName);
2264
2265                                         localtime_r( &_tmp_dt, &gmdt );
2266
2267                                         if (!(attr & DBI_DATETIME_DATE)) {
2268                                                 strftime(dt_string, 255, "%T", &gmdt);
2269                                         } else if (!(attr & DBI_DATETIME_TIME)) {
2270                                                 strftime(dt_string, 255, "%F", &gmdt);
2271                                         } else {
2272                                                 strftime(dt_string, 255, "%FT%T%z", &gmdt);
2273                                         }
2274
2275                                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
2276
2277                                         break;
2278
2279                                 case DBI_TYPE_BINARY :
2280                                         osrfLogError( OSRF_LOG_MARK, 
2281                                                 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
2282                         }
2283                 }
2284         }
2285
2286         return object;
2287 }
2288