]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/c-apps/oils_cstore.c
allow foreign table order-by and dedup search results on main table pkey
[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                 class,
1082                 field_transform,
1083                 osrfHashGet(field, "name")
1084         );
1085
1086
1087
1088         char* pred = buffer_data(sql_buf);
1089         buffer_free(sql_buf);
1090         free(field_transform);
1091
1092         return pred;
1093 }
1094
1095 char* searchFieldTransformPredicate (const char* class, osrfHash* field, jsonObjectNode* node) {
1096         growing_buffer* sql_buf = buffer_init(32);
1097         
1098         char* field_transform = searchFieldTransform( class, field, node->item );
1099         char* value = NULL;
1100
1101         if (jsonObjectGetKey( node->item, "value" )->type == JSON_ARRAY) {
1102                 value = searchValueTransform(jsonObjectGetKey( node->item, "value" ));
1103         } else if (jsonObjectGetKey( node->item, "value" )->type != JSON_NULL) {
1104                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1105                         value = jsonNumberToDBString( field, jsonObjectGetKey( node->item, "value" ) );
1106                 } else {
1107                         value = jsonObjectToSimpleString(jsonObjectGetKey( node->item, "value" ));
1108                         if ( !dbi_conn_quote_string(dbhandle, &value) ) {
1109                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, value);
1110                                 free(value);
1111                                 return NULL;
1112                         }
1113                 }
1114         }
1115
1116         buffer_fadd(
1117                 sql_buf,
1118                 "%s %s %s",
1119                 field_transform,
1120                 node->key,
1121                 value
1122         );
1123
1124         char* pred = buffer_data(sql_buf);
1125         buffer_free(sql_buf);
1126         free(field_transform);
1127
1128         return pred;
1129 }
1130
1131 char* searchSimplePredicate (const char* orig_op, const char* class, osrfHash* field, jsonObject* node) {
1132
1133         char* val = NULL;
1134
1135         if (node->type != JSON_NULL) {
1136                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1137                         val = jsonNumberToDBString( field, node );
1138                 } else {
1139                         val = jsonObjectToSimpleString(node);
1140                 }
1141         }
1142
1143         char* pred = searchWriteSimplePredicate( class, field, osrfHashGet(field, "name"), orig_op, val );
1144
1145         if (val) free(val);
1146
1147         return pred;
1148 }
1149
1150 char* searchWriteSimplePredicate ( const char* class, osrfHash* field, const char* left, const char* orig_op, const char* right ) {
1151
1152         char* val = NULL;
1153         char* op = NULL;
1154         if (right == NULL) {
1155                 val = strdup("NULL");
1156
1157                 if (strcmp( orig_op, "=" ))
1158                         op = strdup("IS NOT");
1159                 else
1160                         op = strdup("IS");
1161
1162         } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1163                 val = strdup(right);
1164                 op = strdup(orig_op);
1165
1166         } else {
1167                 val = strdup(right);
1168                 if ( !dbi_conn_quote_string(dbhandle, &val) ) {
1169                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1170                         free(val);
1171                         return NULL;
1172                 }
1173                 op = strdup(orig_op);
1174         }
1175
1176         growing_buffer* sql_buf = buffer_init(16);
1177         buffer_fadd( sql_buf, "\"%s\".%s %s %s", class, left, op, val );
1178         free(val);
1179         free(op);
1180
1181         char* pred = buffer_data(sql_buf);
1182         buffer_free(sql_buf);
1183
1184         return pred;
1185
1186 }
1187
1188 char* searchBETWEENPredicate (const char* class, osrfHash* field, jsonObject* node) {
1189
1190         char* x_string;
1191         char* y_string;
1192
1193         if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1194                 x_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,0));
1195                 y_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,1));
1196
1197         } else {
1198                 x_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,0));
1199                 y_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,1));
1200                 if ( !(dbi_conn_quote_string(dbhandle, &x_string) && dbi_conn_quote_string(dbhandle, &y_string)) ) {
1201                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key strings [%s] and [%s]", MODULENAME, x_string, y_string);
1202                         free(x_string);
1203                         free(y_string);
1204                         return NULL;
1205                 }
1206         }
1207
1208         growing_buffer* sql_buf = buffer_init(32);
1209         buffer_fadd( sql_buf, "%s BETWEEN %s AND %s", osrfHashGet(field, "name"), x_string, y_string );
1210         free(x_string);
1211         free(y_string);
1212
1213         char* pred = buffer_data(sql_buf);
1214         buffer_free(sql_buf);
1215
1216         return pred;
1217 }
1218
1219 char* searchPredicate ( const char* class, osrfHash* field, jsonObject* node ) {
1220
1221         char* pred = NULL;
1222         if (node->type == JSON_ARRAY) { // equality IN search
1223                 pred = searchINPredicate( class, field, node, NULL );
1224         } else if (node->type == JSON_HASH) { // non-equality search
1225                 jsonObjectNode* pred_node;
1226                 jsonObjectIterator* pred_itr = jsonNewObjectIterator( node );
1227                 while ( (pred_node = jsonObjectIteratorNext( pred_itr )) ) {
1228                         if ( !(strcasecmp( pred_node->key,"between" )) )
1229                                 pred = searchBETWEENPredicate( class, field, pred_node->item );
1230                         else if ( !(strcasecmp( pred_node->key,"in" )) || !(strcasecmp( pred_node->key,"not in" )) )
1231                                 pred = searchINPredicate( class, field, pred_node->item, pred_node->key );
1232                         else if ( pred_node->item->type == JSON_ARRAY )
1233                                 pred = searchFunctionPredicate( class, field, pred_node );
1234                         else if ( pred_node->item->type == JSON_HASH )
1235                                 pred = searchFieldTransformPredicate( class, field, pred_node );
1236                         else 
1237                                 pred = searchSimplePredicate( pred_node->key, class, field, pred_node->item );
1238
1239                         break;
1240                 }
1241         } else if (node->type == JSON_NULL) { // IS NULL search
1242                 growing_buffer* _p = buffer_init(16);
1243                 buffer_fadd(
1244                         _p,
1245                         "\"%s\".%s IS NULL",
1246                         class,
1247                         osrfHashGet(field, "name")
1248                 );
1249                 pred = buffer_data(_p);
1250                 buffer_free(_p);
1251         } else { // equality search
1252                 pred = searchSimplePredicate( "=", class, field, node );
1253         }
1254
1255         return pred;
1256
1257 }
1258
1259
1260 /*
1261
1262 join_filter : {
1263         acn : {
1264                 field : record,
1265                 fkey : id
1266                 type : left
1267                 filter_op : or
1268                 filter : { ... },
1269                 join_filter : {
1270                         acp : {
1271                                 field : call_number,
1272                                 fkey : id,
1273                                 filter : { ... },
1274                         },
1275                 },
1276         },
1277         mrd : {
1278                 field : record,
1279                 type : inner
1280                 fkey : id,
1281                 filter : { ... },
1282         }
1283 }
1284
1285 */
1286
1287 char* searchJOIN ( jsonObject* join_hash, osrfHash* leftmeta ) {
1288
1289         char* leftclass = osrfHashGet(leftmeta, "classname");
1290
1291         growing_buffer* join_buf = buffer_init(128);
1292
1293         jsonObjectNode* snode = NULL;
1294         jsonObjectIterator* search_itr = jsonNewObjectIterator( join_hash );
1295         while ( (snode = jsonObjectIteratorNext( search_itr )) ) {
1296                 osrfHash* idlClass = osrfHashGet( idl, snode->key );
1297
1298                 char* class = osrfHashGet(idlClass, "classname");
1299                 char* table = osrfHashGet(idlClass, "tablename");
1300
1301                 char* type = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "type" ) );
1302                 char* filter_op = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "filter_op" ) );
1303                 char* fkey = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "fkey" ) );
1304                 char* field = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "field" ) );
1305
1306                 jsonObject* filter = jsonObjectGetKey( snode->item, "filter" );
1307                 jsonObject* join_filter = jsonObjectGetKey( snode->item, "join_filter" );
1308
1309                 if (type) {
1310                         if ( !strcasecmp(type,"left") ) {
1311                                 buffer_add(join_buf, " LEFT JOIN");
1312                         } else if ( !strcasecmp(type,"right") ) {
1313                                 buffer_add(join_buf, " RIGHT JOIN");
1314                         } else if ( !strcasecmp(type,"full") ) {
1315                                 buffer_add(join_buf, " FULL JOIN");
1316                         } else {
1317                                 buffer_add(join_buf, " INNER JOIN");
1318                         }
1319                 } else {
1320                         buffer_add(join_buf, " INNER JOIN");
1321                 }
1322
1323                 buffer_fadd(join_buf, " %s AS \"%s\" ON ( \"%s\".%s = \"%s\".%s", table, class, class, field, leftclass, fkey);
1324
1325                 if (filter) {
1326                         if (filter_op) {
1327                                 if (!strcasecmp("or",filter_op)) {
1328                                         buffer_add( join_buf, " OR " );
1329                                 } else {
1330                                         buffer_add( join_buf, " AND " );
1331                                 }
1332                         } else {
1333                                 buffer_add( join_buf, " AND " );
1334                         }
1335
1336                         char* jpred = searchWHERE( filter, idlClass, 0 );
1337                         buffer_fadd( join_buf, " %s", jpred );
1338                         free(jpred);
1339                 }
1340
1341                 buffer_add(join_buf, " ) ");
1342                 
1343                 if (join_filter) {
1344                         char* jpred = searchJOIN( join_filter, idlClass );
1345                         buffer_fadd( join_buf, " %s", jpred );
1346                         free(jpred);
1347                 }
1348
1349         }
1350
1351         char* join = buffer_data(join_buf);
1352         buffer_free(join_buf);
1353
1354         return join;
1355 }
1356
1357 /*
1358
1359 { -or|-and : { field : { op : value }, ... }, ... }
1360
1361 */
1362 char* searchWHERE ( jsonObject* search_hash, osrfHash* meta, int opjoin_type ) {
1363
1364         osrfHash* fields = osrfHashGet(meta, "fields");
1365         char* class = osrfHashGet(meta, "classname");
1366         growing_buffer* sql_buf = buffer_init(128);
1367
1368         jsonObjectNode* node = NULL;
1369
1370         int first = 1;
1371         jsonObjectIterator* search_itr = jsonNewObjectIterator( search_hash );
1372         while ( (node = jsonObjectIteratorNext( search_itr )) ) {
1373
1374                 if (first) {
1375                         first = 0;
1376                 } else {
1377                         if (opjoin_type == 1) buffer_add(sql_buf, " OR ");
1378                         else buffer_add(sql_buf, " AND ");
1379                 }
1380
1381                 if ( !strcasecmp("-or",node->key) ) {
1382                         char* subpred = searchWHERE( node->item, meta, 1);
1383                         buffer_fadd(sql_buf, "( %s )", subpred);
1384                         free(subpred);
1385                 } else if ( !strcasecmp("-and",node->key) ) {
1386                         char* subpred = searchWHERE( node->item, meta, 0);
1387                         buffer_fadd(sql_buf, "( %s )", subpred);
1388                         free(subpred);
1389                 } else {
1390
1391                         osrfHash* field = osrfHashGet( fields, node->key );
1392
1393                         if (!field) {
1394                                 osrfLogError(
1395                                         OSRF_LOG_MARK,
1396                                         "%s: Attempt to reference non-existant column %s on table %s",
1397                                         MODULENAME,
1398                                         node->key,
1399                                         osrfHashGet(meta, "tablename")
1400                                 );
1401                                 buffer_free(sql_buf);
1402                                 return NULL;
1403                         }
1404
1405                         char* subpred = searchPredicate( class, field, node->item );
1406                         buffer_fadd( sql_buf, "%s", subpred );
1407                         free(subpred);
1408                 }
1409         }
1410
1411         jsonObjectIteratorFree(search_itr);
1412
1413         char* pred = buffer_data(sql_buf);
1414         buffer_free(sql_buf);
1415
1416         return pred;
1417 }
1418
1419 char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
1420
1421         osrfHash* fields = osrfHashGet(meta, "fields");
1422         char* core_class = osrfHashGet(meta, "classname");
1423
1424         jsonObject* join_hash = jsonObjectGetKey( order_hash, "join_filter" );
1425
1426         jsonObjectNode* node = NULL;
1427         jsonObjectNode* snode = NULL;
1428         jsonObject* _tmp = NULL;
1429         jsonObject* selhash = NULL;
1430         jsonObject* defaultselhash = NULL;
1431
1432         growing_buffer* sql_buf = buffer_init(128);
1433         growing_buffer* select_buf = buffer_init(128);
1434
1435         if ( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
1436                 defaultselhash = jsonParseString( "{}" );
1437                 selhash = defaultselhash;
1438         }
1439         
1440         if ( !jsonObjectGetKey(selhash,core_class) ) {
1441                 jsonObjectSetKey( selhash, core_class, jsonParseString( "[]" ) );
1442                 jsonObject* flist = jsonObjectGetKey( selhash, core_class );
1443                 
1444                 int i = 0;
1445                 char* field;
1446
1447                 osrfStringArray* keys = osrfHashKeys( fields );
1448                 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
1449                         if ( strcasecmp( "true", osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
1450                                 jsonObjectPush( flist, jsonNewObject( field ) );
1451                 }
1452                 osrfStringArrayFree(keys);
1453         }
1454
1455         int first = 1;
1456         jsonObjectIterator* class_itr = jsonNewObjectIterator( selhash );
1457         while ( (snode = jsonObjectIteratorNext( class_itr )) ) {
1458
1459                 osrfHash* idlClass = osrfHashGet( idl, snode->key );
1460                 if (!idlClass) continue;
1461                 char* cname = osrfHashGet(idlClass, "classname");
1462
1463                 //if (strcmp(core_class,snode->key)) continue;
1464
1465                 jsonObjectIterator* select_itr = jsonNewObjectIterator( snode->item );
1466                 while ( (node = jsonObjectIteratorNext( select_itr )) ) {
1467                         osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), jsonObjectToSimpleString(node->item) );
1468                         char* fname = osrfHashGet(field, "name");
1469
1470                         if (!field) continue;
1471
1472                         if (first) {
1473                                 first = 0;
1474                         } else {
1475                                 buffer_add(select_buf, ",");
1476                         }
1477
1478                         buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
1479                 }
1480         }
1481
1482         char* col_list = buffer_data(select_buf);
1483         buffer_free(select_buf);
1484         if (defaultselhash) jsonObjectFree(defaultselhash);
1485
1486         buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\"", col_list, osrfHashGet(meta, "tablename"), core_class );
1487
1488         if ( join_hash ) {
1489                 char* join_clause = searchJOIN( join_hash, meta );
1490                 buffer_fadd(sql_buf, " %s", join_clause);
1491                 free(join_clause);
1492         }
1493
1494         buffer_add(sql_buf, " WHERE ");
1495
1496         char* pred = searchWHERE( search_hash, meta, 0 );
1497         if (!pred) {
1498                 osrfAppSessionStatus(
1499                         ctx->session,
1500                         OSRF_STATUS_INTERNALSERVERERROR,
1501                                 "osrfMethodException",
1502                                 ctx->request,
1503                                 "Severe query error -- see error log for more details"
1504                         );
1505                 buffer_free(sql_buf);
1506                 return NULL;
1507         } else {
1508                 buffer_add(sql_buf, pred);
1509                 free(pred);
1510         }
1511
1512         if (order_hash) {
1513                 char* string;
1514                 if ( (_tmp = jsonObjectGetKey( order_hash, "order_by" )) ){
1515                         jsonObjectIterator* class_itr = jsonNewObjectIterator( _tmp );
1516                         while ( (snode = jsonObjectIteratorNext( class_itr )) ) {
1517                                 string = jsonObjectToSimpleString(snode->item);
1518                                 buffer_fadd(
1519                                         sql_buf,
1520                                         " ORDER BY \"%s\".%s",
1521                                         snode->key,
1522                                         string
1523                                 );
1524                                 free(string);
1525                         }
1526                 }
1527
1528                 if ( (_tmp = jsonObjectGetKey( order_hash, "limit" )) ){
1529                         string = jsonObjectToSimpleString(_tmp);
1530                         buffer_fadd(
1531                                 sql_buf,
1532                                 " LIMIT %d",
1533                                 atoi(string)
1534                         );
1535                         free(string);
1536                 }
1537
1538                 _tmp = jsonObjectGetKey( order_hash, "offset" );
1539                 if (_tmp) {
1540                         string = jsonObjectToSimpleString(_tmp);
1541                         buffer_fadd(
1542                                 sql_buf,
1543                                 " OFFSET %d",
1544                                 atoi(string)
1545                         );
1546                         free(string);
1547                 }
1548         }
1549
1550         buffer_add(sql_buf, ";");
1551
1552         char* sql = buffer_data(sql_buf);
1553         buffer_free(sql_buf);
1554
1555         return sql;
1556 }
1557
1558 jsonObject* doSearch ( osrfMethodContext* ctx, osrfHash* meta, jsonObject* params, int* err ) {
1559
1560         // XXX for now...
1561         dbhandle = writehandle;
1562
1563         osrfHash* links = osrfHashGet(meta, "links");
1564         osrfHash* fields = osrfHashGet(meta, "fields");
1565         char* core_class = osrfHashGet(meta, "classname");
1566         char* pkey = osrfHashGet(meta, "primarykey");
1567
1568         jsonObject* _tmp;
1569         jsonObject* obj;
1570         jsonObject* search_hash = jsonObjectGetIndex(params, 0);
1571         jsonObject* order_hash = jsonObjectGetIndex(params, 1);
1572
1573         char* sql = buildSELECT( search_hash, order_hash, meta, ctx );
1574         if (!sql) {
1575                 *err = -1;
1576                 return NULL;
1577         }
1578         
1579         osrfLogDebug(OSRF_LOG_MARK, "%s SQL =  %s", MODULENAME, sql);
1580         dbi_result result = dbi_conn_query(dbhandle, sql);
1581
1582         osrfHash* dedup = osrfNewHash();
1583         jsonObject* res_list = jsonParseString("[]");
1584         if(result) {
1585                 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
1586
1587                 if (dbi_result_first_row(result)) {
1588                         /* JSONify the result */
1589                         osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
1590                         do {
1591                                 obj = oilsMakeJSONFromResult( result, meta );
1592                                 int pkey_pos = atoi( osrfHashGet( osrfHashGet( fields, pkey ), "array_position" ) );
1593                                 char* pkey_val = jsonObjectToSimpleString( jsonObjectGetIndex( obj, pkey_pos ) );
1594                                 if ( osrfHashGet( dedup, pkey_val ) ) {
1595                                         jsonObjectFree(obj);
1596                                 } else {
1597                                         osrfHashSet( dedup, pkey_val, pkey_val );
1598                                         jsonObjectPush(res_list, obj);
1599                                 }
1600                         } while (dbi_result_next_row(result));
1601                 } else {
1602                         osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
1603                 }
1604
1605                 /* clean up the query */
1606                 dbi_result_free(result); 
1607
1608         } else {
1609                 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]", MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
1610                 osrfAppSessionStatus(
1611                         ctx->session,
1612                         OSRF_STATUS_INTERNALSERVERERROR,
1613                         "osrfMethodException",
1614                         ctx->request,
1615                         "Severe query error -- see error log for more details"
1616                 );
1617                 *err = -1;
1618                 free(sql);
1619                 jsonObjectFree(res_list);
1620                 return jsonNULL;
1621
1622         }
1623
1624         free(sql);
1625
1626         if (res_list->size && order_hash) {
1627                 _tmp = jsonObjectGetKey( order_hash, "flesh" );
1628                 if (_tmp) {
1629                         int x = (int)jsonObjectGetNumber(_tmp);
1630
1631                         jsonObject* flesh_blob = NULL;
1632                         if ((flesh_blob = jsonObjectGetKey( order_hash, "flesh_fields" )) && x > 0) {
1633
1634                                 flesh_blob = jsonObjectClone( flesh_blob );
1635                                 jsonObject* flesh_fields = jsonObjectGetKey( flesh_blob, core_class );
1636
1637                                 osrfStringArray* link_fields = NULL;
1638
1639                                 if (flesh_fields) {
1640                                         if (flesh_fields->size == 1) {
1641                                                 char* _t = jsonObjectToSimpleString( jsonObjectGetIndex( flesh_fields, 0 ) );
1642                                                 if (!strcmp(_t,"*")) link_fields = osrfHashKeys( links );
1643                                                 free(_t);
1644                                         }
1645
1646                                         if (!link_fields) {
1647                                                 jsonObjectNode* _f;
1648                                                 link_fields = osrfNewStringArray(1);
1649                                                 jsonObjectIterator* _i = jsonNewObjectIterator( flesh_fields );
1650                                                 while ((_f = jsonObjectIteratorNext( _i ))) {
1651                                                         osrfStringArrayAdd( link_fields, jsonObjectToSimpleString( _f->item ) );
1652                                                 }
1653                                         }
1654                                 }
1655
1656                                 jsonObjectNode* cur;
1657                                 jsonObjectIterator* itr = jsonNewObjectIterator( res_list );
1658                                 while ((cur = jsonObjectIteratorNext( itr ))) {
1659
1660                                         int i = 0;
1661                                         char* link_field;
1662                                         
1663                                         while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
1664
1665                                                 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
1666
1667                                                 osrfHash* kid_link = osrfHashGet(links, link_field);
1668                                                 if (!kid_link) continue;
1669
1670                                                 osrfHash* field = osrfHashGet(fields, link_field);
1671                                                 if (!field) continue;
1672
1673                                                 osrfHash* value_field = field;
1674
1675                                                 osrfHash* kid_idl = osrfHashGet(idl, osrfHashGet(kid_link, "class"));
1676                                                 if (!kid_idl) continue;
1677
1678                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
1679                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
1680                                                 }
1681                                                         
1682                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
1683                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
1684                                                 }
1685
1686                                                 osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
1687
1688                                                 if (link_map->size > 0) {
1689                                                         jsonObject* _kid_key = jsonParseString("[]");
1690                                                         jsonObjectPush(
1691                                                                 _kid_key,
1692                                                                 jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
1693                                                         );
1694
1695                                                         jsonObjectSetKey(
1696                                                                 flesh_blob,
1697                                                                 osrfHashGet(kid_link, "class"),
1698                                                                 _kid_key
1699                                                         );
1700                                                 };
1701
1702                                                 osrfLogDebug(
1703                                                         OSRF_LOG_MARK,
1704                                                         "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
1705                                                         osrfHashGet(kid_link, "field"),
1706                                                         osrfHashGet(kid_link, "class"),
1707                                                         osrfHashGet(kid_link, "key"),
1708                                                         osrfHashGet(kid_link, "reltype")
1709                                                 );
1710
1711                                                 jsonObject* fake_params = jsonParseString("[]");
1712                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // search hash
1713                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // order/flesh hash
1714
1715                                                 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
1716
1717                                                 char* search_key =
1718                                                 jsonObjectToSimpleString(
1719                                                         jsonObjectGetIndex(
1720                                                                 cur->item,
1721                                                                 atoi( osrfHashGet(value_field, "array_position") )
1722                                                         )
1723                                                 );
1724
1725                                                 if (!search_key) {
1726                                                         osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
1727                                                         continue;
1728                                                 }
1729                                                         
1730                                                 jsonObjectSetKey(
1731                                                         jsonObjectGetIndex(fake_params, 0),
1732                                                         osrfHashGet(kid_link, "key"),
1733                                                         jsonNewObject( search_key )
1734                                                 );
1735
1736                                                 free(search_key);
1737
1738
1739                                                 jsonObjectSetKey(
1740                                                         jsonObjectGetIndex(fake_params, 1),
1741                                                         "flesh",
1742                                                         jsonNewNumberObject( (double)(x - 1 + link_map->size) )
1743                                                 );
1744
1745                                                 if (flesh_blob)
1746                                                         jsonObjectSetKey( jsonObjectGetIndex(fake_params, 1), "flesh_fields", jsonObjectClone(flesh_blob) );
1747
1748                                                 if (jsonObjectGetKey(order_hash, "order_by")) {
1749                                                         jsonObjectSetKey(
1750                                                                 jsonObjectGetIndex(fake_params, 1),
1751                                                                 "order_by",
1752                                                                 jsonObjectClone(jsonObjectGetKey(order_hash, "order_by"))
1753                                                         );
1754                                                 }
1755
1756                                                 if (jsonObjectGetKey(order_hash, "select")) {
1757                                                         jsonObjectSetKey(
1758                                                                 jsonObjectGetIndex(fake_params, 1),
1759                                                                 "select",
1760                                                                 jsonObjectClone(jsonObjectGetKey(order_hash, "select"))
1761                                                         );
1762                                                 }
1763
1764                                                 jsonObject* kids = doSearch(ctx, kid_idl, fake_params, err);
1765
1766                                                 if(*err) {
1767                                                         jsonObjectFree( fake_params );
1768                                                         osrfStringArrayFree(link_fields);
1769                                                         jsonObjectIteratorFree(itr);
1770                                                         jsonObjectFree(res_list);
1771                                                         return jsonNULL;
1772                                                 }
1773
1774                                                 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
1775
1776                                                 jsonObject* X = NULL;
1777                                                 if ( link_map->size > 0 && kids->size > 0 ) {
1778                                                         X = kids;
1779                                                         kids = jsonParseString("[]");
1780
1781                                                         jsonObjectNode* _k_node;
1782                                                         jsonObjectIterator* _k = jsonNewObjectIterator( X );
1783                                                         while ((_k_node = jsonObjectIteratorNext( _k ))) {
1784                                                                 jsonObjectPush(
1785                                                                         kids,
1786                                                                         jsonObjectClone(
1787                                                                                 jsonObjectGetIndex(
1788                                                                                         _k_node->item,
1789                                                                                         (unsigned long)atoi(
1790                                                                                                 osrfHashGet(
1791                                                                                                         osrfHashGet(
1792                                                                                                                 osrfHashGet(
1793                                                                                                                         osrfHashGet(
1794                                                                                                                                 idl,
1795                                                                                                                                 osrfHashGet(kid_link, "class")
1796                                                                                                                         ),
1797                                                                                                                         "fields"
1798                                                                                                                 ),
1799                                                                                                                 osrfStringArrayGetString( link_map, 0 )
1800                                                                                                         ),
1801                                                                                                         "array_position"
1802                                                                                                 )
1803                                                                                         )
1804                                                                                 )
1805                                                                         )
1806                                                                 );
1807                                                         }
1808                                                 }
1809
1810                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" )) || !(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) {
1811                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
1812                                                         jsonObjectSetIndex(
1813                                                                 cur->item,
1814                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
1815                                                                 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
1816                                                         );
1817                                                 }
1818
1819                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
1820                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
1821                                                         jsonObjectSetIndex(
1822                                                                 cur->item,
1823                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
1824                                                                 jsonObjectClone( kids )
1825                                                         );
1826                                                 }
1827
1828                                                 if (X) {
1829                                                         jsonObjectFree(kids);
1830                                                         kids = X;
1831                                                 }
1832
1833                                                 jsonObjectFree( kids );
1834                                                 jsonObjectFree( fake_params );
1835
1836                                                 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
1837                                                 osrfLogDebug(OSRF_LOG_MARK, "%s", jsonObjectToJSON(cur->item));
1838
1839                                         }
1840                                 }
1841                                 jsonObjectFree( flesh_blob );
1842                                 osrfStringArrayFree(link_fields);
1843                                 jsonObjectIteratorFree(itr);
1844                         }
1845                 }
1846         }
1847
1848         return res_list;
1849 }
1850
1851
1852 jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
1853
1854         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
1855         jsonObject* target = jsonObjectGetIndex(ctx->params, 0);
1856
1857         if (!verifyObjectClass(ctx, target)) {
1858                 *err = -1;
1859                 return jsonNULL;
1860         }
1861
1862         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
1863                 osrfAppSessionStatus(
1864                         ctx->session,
1865                         OSRF_STATUS_BADREQUEST,
1866                         "osrfMethodException",
1867                         ctx->request,
1868                         "No active transaction -- required for UPDATE"
1869                 );
1870                 *err = -1;
1871                 return jsonNULL;
1872         }
1873
1874         dbhandle = writehandle;
1875
1876         char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
1877
1878         // Set the last_xact_id
1879         osrfHash* last_xact_id;
1880         if ((last_xact_id = oilsIDLFindPath("/%s/fields/last_xact_id", target->classname))) {
1881                 int index = atoi( osrfHashGet(last_xact_id, "array_position") );
1882                 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
1883                 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
1884         }       
1885
1886         char* pkey = osrfHashGet(meta, "primarykey");
1887         osrfHash* fields = osrfHashGet(meta, "fields");
1888
1889         char* id =
1890                 jsonObjectToSimpleString(
1891                         jsonObjectGetIndex(
1892                                 target,
1893                                 atoi( osrfHashGet( osrfHashGet( fields, pkey ), "array_position" ) )
1894                         )
1895                 );
1896
1897         osrfLogDebug(
1898                 OSRF_LOG_MARK,
1899                 "%s updating %s object with %s = %s",
1900                 MODULENAME,
1901                 osrfHashGet(meta, "fieldmapper"),
1902                 pkey,
1903                 id
1904         );
1905
1906         growing_buffer* sql = buffer_init(128);
1907         buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
1908
1909         int i = 0;
1910         int first = 1;
1911         char* field_name;
1912         osrfStringArray* field_list = osrfHashKeys( fields );
1913         while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
1914
1915                 osrfHash* field = osrfHashGet( fields, field_name );
1916
1917                 if(!( strcmp( field_name, pkey ) )) continue;
1918                 if(!( strcmp( osrfHashGet(osrfHashGet(fields,field_name), "virtual"), "true" ) )) continue;
1919
1920                 jsonObject* field_object = jsonObjectGetIndex( target, atoi(osrfHashGet(field, "array_position")) );
1921
1922                 char* value;
1923                 if (field_object && field_object->classname) {
1924                         value = jsonObjectToSimpleString(
1925                                         jsonObjectGetIndex(
1926                                                 field_object,
1927                                                 atoi(
1928                                                         osrfHashGet(
1929                                                                 osrfHashGet(
1930                                                                         oilsIDLFindPath("/%s/fields", field_object->classname),
1931                                                                         (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
1932                                                                 ),
1933                                                                 "array_position"
1934                                                         )
1935                                                 )
1936                                         )
1937                                 );
1938
1939                 } else {
1940                         value = jsonObjectToSimpleString( field_object );
1941                 }
1942
1943                 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
1944
1945                 if (!field_object || field_object->type == JSON_NULL) {
1946                         if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
1947                                 if (first) first = 0;
1948                                 else buffer_add(sql, ",");
1949                                 buffer_fadd( sql, " %s = NULL", field_name );
1950                         }
1951                         
1952                 } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1953                         if (first) first = 0;
1954                         else buffer_add(sql, ",");
1955
1956                         if ( !strncmp(osrfHashGet(field, "datatype"), "INT", (size_t)3) ) {
1957                                 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
1958                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
1959                                 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
1960                         }
1961
1962                         osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, osrfHashGet(field, "datatype"));
1963
1964                 } else {
1965                         if ( dbi_conn_quote_string(dbhandle, &value) ) {
1966                                 if (first) first = 0;
1967                                 else buffer_add(sql, ",");
1968                                 buffer_fadd( sql, " %s = %s", field_name, value );
1969
1970                         } else {
1971                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
1972                                 osrfAppSessionStatus(
1973                                         ctx->session,
1974                                         OSRF_STATUS_INTERNALSERVERERROR,
1975                                         "osrfMethodException",
1976                                         ctx->request,
1977                                         "Error quoting string -- please see the error log for more details"
1978                                 );
1979                                 free(value);
1980                                 free(id);
1981                                 buffer_free(sql);
1982                                 *err = -1;
1983                                 return jsonNULL;
1984                         }
1985                 }
1986
1987                 free(value);
1988                 
1989         }
1990
1991         jsonObject* obj = jsonParseString(id);
1992
1993         if ( strcmp( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "primitive" ), "number" ) )
1994                 dbi_conn_quote_string(dbhandle, &id);
1995
1996         buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
1997
1998         char* query = buffer_data(sql);
1999         buffer_free(sql);
2000
2001         osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
2002
2003         dbi_result result = dbi_conn_query(dbhandle, query);
2004         free(query);
2005
2006         if (!result) {
2007                 jsonObjectFree(obj);
2008                 obj = jsonNewObject(NULL);
2009                 osrfLogError(
2010                         OSRF_LOG_MARK,
2011                         "%s ERROR updating %s object with %s = %s",
2012                         MODULENAME,
2013                         osrfHashGet(meta, "fieldmapper"),
2014                         pkey,
2015                         id
2016                 );
2017         }
2018
2019         free(id);
2020
2021         return obj;
2022 }
2023
2024 jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
2025
2026         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
2027
2028         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
2029                 osrfAppSessionStatus(
2030                         ctx->session,
2031                         OSRF_STATUS_BADREQUEST,
2032                         "osrfMethodException",
2033                         ctx->request,
2034                         "No active transaction -- required for DELETE"
2035                 );
2036                 *err = -1;
2037                 return jsonNULL;
2038         }
2039
2040         dbhandle = writehandle;
2041
2042         jsonObject* obj;
2043
2044         char* pkey = osrfHashGet(meta, "primarykey");
2045
2046         char* id;
2047         if (jsonObjectGetIndex(ctx->params, 0)->classname) {
2048                 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, 0 ))) {
2049                         *err = -1;
2050                         return jsonNULL;
2051                 }
2052
2053                 id = jsonObjectToSimpleString(
2054                         jsonObjectGetIndex(
2055                                 jsonObjectGetIndex(ctx->params, 0),
2056                                 atoi( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "array_position") )
2057                         )
2058                 );
2059         } else {
2060                 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
2061         }
2062
2063         osrfLogDebug(
2064                 OSRF_LOG_MARK,
2065                 "%s deleting %s object with %s = %s",
2066                 MODULENAME,
2067                 osrfHashGet(meta, "fieldmapper"),
2068                 pkey,
2069                 id
2070         );
2071
2072         obj = jsonParseString(id);
2073
2074         if ( strcmp( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "primitive" ), "number" ) )
2075                 dbi_conn_quote_string(writehandle, &id);
2076
2077         dbi_result result = dbi_conn_queryf(writehandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
2078
2079         if (!result) {
2080                 jsonObjectFree(obj);
2081                 obj = jsonNewObject(NULL);
2082                 osrfLogError(
2083                         OSRF_LOG_MARK,
2084                         "%s ERROR deleting %s object with %s = %s",
2085                         MODULENAME,
2086                         osrfHashGet(meta, "fieldmapper"),
2087                         pkey,
2088                         id
2089                 );
2090         }
2091
2092         free(id);
2093
2094         return obj;
2095
2096 }
2097
2098
2099 jsonObject* oilsMakeJSONFromResult( dbi_result result, osrfHash* meta) {
2100         if(!(result && meta)) return jsonNULL;
2101
2102         jsonObject* object = jsonParseString("[]");
2103         jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
2104
2105         osrfHash* fields = osrfHashGet(meta, "fields");
2106
2107         osrfLogInternal(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
2108
2109         osrfHash* _f;
2110         time_t _tmp_dt;
2111         char dt_string[256];
2112         struct tm gmdt;
2113
2114         int fmIndex;
2115         int columnIndex = 1;
2116         int attr;
2117         unsigned short type;
2118         const char* columnName;
2119
2120         /* cycle through the column list */
2121         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
2122
2123                 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
2124
2125                 fmIndex = -1; // reset the position
2126                 
2127                 /* determine the field type and storage attributes */
2128                 type = dbi_result_get_field_type(result, columnName);
2129                 attr = dbi_result_get_field_attribs(result, columnName);
2130
2131                 /* fetch the fieldmapper index */
2132                 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
2133                         char* virt = (char*)osrfHashGet(_f, "virtual");
2134                         char* pos = (char*)osrfHashGet(_f, "array_position");
2135
2136                         if ( !virt || !pos || !(strcmp( virt, "true" )) ) continue;
2137
2138                         fmIndex = atoi( pos );
2139                         osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
2140                 } else {
2141                         continue;
2142                 }
2143
2144                 if (dbi_result_field_is_null(result, columnName)) {
2145                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
2146                 } else {
2147
2148                         switch( type ) {
2149
2150                                 case DBI_TYPE_INTEGER :
2151
2152                                         if( attr & DBI_INTEGER_SIZE8 ) 
2153                                                 jsonObjectSetIndex( object, fmIndex, 
2154                                                         jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
2155                                         else 
2156                                                 jsonObjectSetIndex( object, fmIndex, 
2157                                                         jsonNewNumberObject(dbi_result_get_long(result, columnName)));
2158
2159                                         break;
2160
2161                                 case DBI_TYPE_DECIMAL :
2162                                         jsonObjectSetIndex( object, fmIndex, 
2163                                                         jsonNewNumberObject(dbi_result_get_double(result, columnName)));
2164                                         break;
2165
2166                                 case DBI_TYPE_STRING :
2167
2168
2169                                         jsonObjectSetIndex(
2170                                                 object,
2171                                                 fmIndex,
2172                                                 jsonNewObject( dbi_result_get_string(result, columnName) )
2173                                         );
2174
2175                                         break;
2176
2177                                 case DBI_TYPE_DATETIME :
2178
2179                                         memset(dt_string, '\0', 256);
2180                                         memset(&gmdt, '\0', sizeof(gmdt));
2181                                         memset(&_tmp_dt, '\0', sizeof(_tmp_dt));
2182
2183                                         _tmp_dt = dbi_result_get_datetime(result, columnName);
2184
2185                                         localtime_r( &_tmp_dt, &gmdt );
2186
2187                                         if (!(attr & DBI_DATETIME_DATE)) {
2188                                                 strftime(dt_string, 255, "%T", &gmdt);
2189                                         } else if (!(attr & DBI_DATETIME_TIME)) {
2190                                                 strftime(dt_string, 255, "%F", &gmdt);
2191                                         } else {
2192                                                 strftime(dt_string, 255, "%FT%T%z", &gmdt);
2193                                         }
2194
2195                                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
2196
2197                                         break;
2198
2199                                 case DBI_TYPE_BINARY :
2200                                         osrfLogError( OSRF_LOG_MARK, 
2201                                                 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
2202                         }
2203                 }
2204         }
2205
2206         return object;
2207 }
2208