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