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