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