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