]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/c-apps/oils_cstore.c
protecting class name check from objects with NULL classname
[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_idl.h"
7 #include <dbi/dbi.h>
8
9 #include <time.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13
14 #ifdef RSTORE
15 #  define MODULENAME "open-ils.reporter-store"
16 #else
17 #  define MODULENAME "open-ils.cstore"
18 #endif
19
20 #define SELECT_DISTINCT 1
21 #define AND_OP_JOIN     0
22 #define OR_OP_JOIN      1
23
24 int osrfAppChildInit();
25 int osrfAppInitialize();
26 void osrfAppChildExit();
27
28 int verifyObjectClass ( osrfMethodContext*, jsonObject* );
29
30 int beginTransaction ( osrfMethodContext* );
31 int commitTransaction ( osrfMethodContext* );
32 int rollbackTransaction ( osrfMethodContext* );
33
34 int setSavepoint ( osrfMethodContext* );
35 int releaseSavepoint ( osrfMethodContext* );
36 int rollbackSavepoint ( osrfMethodContext* );
37
38 int doJSONSearch ( osrfMethodContext* );
39
40 int dispatchCRUDMethod ( osrfMethodContext* );
41 jsonObject* doCreate ( osrfMethodContext*, int* );
42 jsonObject* doRetrieve ( osrfMethodContext*, int* );
43 jsonObject* doUpdate ( osrfMethodContext*, int* );
44 jsonObject* doDelete ( osrfMethodContext*, int* );
45 jsonObject* doFieldmapperSearch ( osrfMethodContext*, osrfHash*, jsonObject*, int* );
46 jsonObject* oilsMakeFieldmapperFromResult( dbi_result, osrfHash* );
47 jsonObject* oilsMakeJSONFromResult( dbi_result );
48
49 char* searchWriteSimplePredicate ( const char*, osrfHash*, const char*, const char*, const char* );
50 char* searchSimplePredicate ( const char*, const char*, osrfHash*, jsonObject* );
51 char* searchFunctionPredicate ( const char*, osrfHash*, jsonObjectNode* );
52 char* searchFieldTransform (const char*, osrfHash*, jsonObject*);
53 char* searchFieldTransformPredicate ( const char*, osrfHash*, jsonObjectNode* );
54 char* searchBETWEENPredicate ( const char*, osrfHash*, jsonObject* );
55 char* searchINPredicate ( const char*, osrfHash*, jsonObject*, const char* );
56 char* searchPredicate ( const char*, osrfHash*, jsonObject* );
57 char* searchJOIN ( jsonObject*, osrfHash* );
58 char* searchWHERE ( jsonObject*, osrfHash*, int );
59 char* buildSELECT ( jsonObject*, jsonObject*, osrfHash*, osrfMethodContext* );
60
61 char* SELECT ( osrfMethodContext*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, int );
62
63 void userDataFree( void* );
64 void sessionDataFree( char*, void* );
65
66 dbi_conn writehandle; /* our MASTER db connection */
67 dbi_conn dbhandle; /* our CURRENT db connection */
68 osrfHash readHandles;
69 jsonObject* jsonNULL = NULL; // 
70 static int max_flesh_depth = 100;
71
72 /* called when this process is about to exit */
73 void osrfAppChildExit() {
74         osrfLogDebug(OSRF_LOG_MARK, "Child is exiting, disconnecting from database...");
75
76         if (writehandle) {
77                 dbi_conn_query(writehandle, "ROLLBACK;");
78                 dbi_conn_close(writehandle);
79                 writehandle = NULL;
80         }
81
82         if (dbhandle)
83                 dbi_conn_close(dbhandle);
84
85         // XXX add cleanup of readHandles whenever that gets used
86
87         return;
88 }
89
90 int osrfAppInitialize() {
91         growing_buffer* method_name;
92
93         osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
94         osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
95
96         if (!oilsIDLInit( osrf_settings_host_value("/IDL") )) return 1; /* return non-zero to indicate error */
97
98         // Generic search thingy
99         method_name =  buffer_init(64);
100         buffer_fadd(method_name, "%s.json_query", MODULENAME);
101         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "doJSONSearch", "", 1, OSRF_METHOD_STREAMING );
102
103         // first we register all the transaction and savepoint methods
104         buffer_reset(method_name);
105         buffer_fadd(method_name, "%s.transaction.begin", MODULENAME);
106         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "beginTransaction", "", 0, 0 );
107
108         buffer_reset(method_name);
109         buffer_fadd(method_name, "%s.transaction.commit", MODULENAME);
110         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "commitTransaction", "", 0, 0 );
111
112         buffer_reset(method_name);
113         buffer_fadd(method_name, "%s.transaction.rollback", MODULENAME);
114         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "rollbackTransaction", "", 0, 0 );
115
116
117         buffer_reset(method_name);
118         buffer_fadd(method_name, "%s.savepoint.set", MODULENAME);
119         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "setSavepoint", "", 1, 0 );
120
121         buffer_reset(method_name);
122         buffer_fadd(method_name, "%s.savepoint.release", MODULENAME);
123         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "releaseSavepoint", "", 1, 0 );
124
125         buffer_reset(method_name);
126         buffer_fadd(method_name, "%s.savepoint.rollback", MODULENAME);
127         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "rollbackSavepoint", "", 1, 0 );
128
129         osrfStringArray* global_methods = osrfNewStringArray(6);
130
131         osrfStringArrayAdd( global_methods, "create" );
132         osrfStringArrayAdd( global_methods, "retrieve" );
133         osrfStringArrayAdd( global_methods, "update" );
134         osrfStringArrayAdd( global_methods, "delete" );
135         osrfStringArrayAdd( global_methods, "search" );
136         osrfStringArrayAdd( global_methods, "id_list" );
137
138         int c_index = 0; 
139         char* classname;
140         osrfStringArray* classes = osrfHashKeys( oilsIDL() );
141         osrfLogDebug(OSRF_LOG_MARK, "%d classes loaded", classes->size );
142         osrfLogDebug(OSRF_LOG_MARK, "At least %d methods will be generated", classes->size * global_methods->size);
143         
144         while ( (classname = osrfStringArrayGetString(classes, c_index++)) ) {
145                 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
146                 
147                 osrfHash* idlClass = osrfHashGet(oilsIDL(), classname);
148
149                 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), MODULENAME )) {
150                         osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on", MODULENAME, classname);
151                         continue;
152                 }
153
154                 char* virt = osrfHashGet(idlClass, "virtual");
155                 if (virt && !strcmp( virt, "true")) {
156                         osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
157                         continue;
158                 }
159
160                 int i = 0; 
161                 char* method_type;
162                 char* st_tmp;
163                 char* _fm;
164                 char* part;
165                 osrfHash* method_meta;
166                 while ( (method_type = osrfStringArrayGetString(global_methods, i++)) ) {
167                         osrfLogDebug(OSRF_LOG_MARK, "Using files to build %s class methods for %s", method_type, classname);
168
169                         if (!osrfHashGet(idlClass, "fieldmapper")) continue;
170
171                         method_meta = osrfNewHash();
172                         osrfHashSet(method_meta, idlClass, "class");
173
174                         _fm = strdup( (char*)osrfHashGet(idlClass, "fieldmapper") );
175                         part = strtok_r(_fm, ":", &st_tmp);
176
177                         growing_buffer* method_name =  buffer_init(64);
178                         buffer_fadd(method_name, "%s.direct.%s", MODULENAME, part);
179
180                         while ((part = strtok_r(NULL, ":", &st_tmp))) {
181                                 buffer_fadd(method_name, ".%s", part);
182                         }
183                         buffer_fadd(method_name, ".%s", method_type);
184
185
186                         char* method = buffer_data(method_name);
187                         buffer_free(method_name);
188                         free(_fm);
189
190                         osrfHashSet( method_meta, method, "methodname" );
191                         osrfHashSet( method_meta, method_type, "methodtype" );
192
193                         int flags = 0;
194                         if (!(strcmp( method_type, "search" )) || !(strcmp( method_type, "id_list" ))) {
195                                 flags = flags | OSRF_METHOD_STREAMING;
196                         }
197
198                         osrfAppRegisterExtendedMethod(
199                                 MODULENAME,
200                                 method,
201                                 "dispatchCRUDMethod",
202                                 "",
203                                 1,
204                                 flags,
205                                 (void*)method_meta
206                         );
207                 }
208         }
209
210         return 0;
211 }
212
213 /**
214  * Connects to the database 
215  */
216 int osrfAppChildInit() {
217
218         osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
219         dbi_initialize(NULL);
220         osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
221
222         char* driver    = osrf_settings_host_value("/apps/%s/app_settings/driver", MODULENAME);
223         char* user      = osrf_settings_host_value("/apps/%s/app_settings/database/user", MODULENAME);
224         char* host      = osrf_settings_host_value("/apps/%s/app_settings/database/host", MODULENAME);
225         char* port      = osrf_settings_host_value("/apps/%s/app_settings/database/port", MODULENAME);
226         char* db        = osrf_settings_host_value("/apps/%s/app_settings/database/db", MODULENAME);
227         char* pw        = osrf_settings_host_value("/apps/%s/app_settings/database/pw", MODULENAME);
228         char* md        = osrf_settings_host_value("/apps/%s/app_settings/max_query_recursion", MODULENAME);
229
230         osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
231         writehandle = dbi_conn_new(driver);
232
233         if(!writehandle) {
234                 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
235                 return -1;
236         }
237         osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
238
239         osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database.  host=%s, "
240                 "port=%s, user=%s, pw=%s, db=%s", MODULENAME, host, port, user, pw, db );
241
242         if(host) dbi_conn_set_option(writehandle, "host", host );
243         if(port) dbi_conn_set_option_numeric( writehandle, "port", atoi(port) );
244         if(user) dbi_conn_set_option(writehandle, "username", user);
245         if(pw) dbi_conn_set_option(writehandle, "password", pw );
246         if(db) dbi_conn_set_option(writehandle, "dbname", db );
247
248         if(md) max_flesh_depth = atoi(md);
249         if(max_flesh_depth < 0) max_flesh_depth = 1;
250         if(max_flesh_depth > 1000) max_flesh_depth = 1000;
251
252         free(user);
253         free(host);
254         free(port);
255         free(db);
256         free(pw);
257
258         const char* err;
259         if (dbi_conn_connect(writehandle) < 0) {
260                 sleep(1);
261                 if (dbi_conn_connect(writehandle) < 0) {
262                         dbi_conn_error(writehandle, &err);
263                         osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
264                         return -1;
265                 }
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 (!param->classname || (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", atoll(value) );
784                                 
785                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "INT") ) {
786                                 buffer_fadd( val_buf, "%d", atoi(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", (size_t)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         if (field_transform)
1102                 buffer_fadd( sql_buf, "%s(\"%s\".%s)", field_transform, class, osrfHashGet(field, "name"));
1103         else
1104                 buffer_fadd( sql_buf, "\"%s\".%s", class, osrfHashGet(field, "name"));
1105
1106         char* pred = buffer_data(sql_buf);
1107         buffer_free(sql_buf);
1108
1109         if (field_transform) free(field_transform);
1110
1111         return pred;
1112 }
1113
1114 char* searchFieldTransformPredicate (const char* class, osrfHash* field, jsonObjectNode* node) {
1115         growing_buffer* sql_buf = buffer_init(32);
1116         
1117         char* field_transform = searchFieldTransform( class, field, node->item );
1118         char* value = NULL;
1119
1120         if (!jsonObjectGetKey( node->item, "value" )) {
1121                 value = searchWHERE( node->item, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN );
1122         } else if (jsonObjectGetKey( node->item, "value" )->type == JSON_ARRAY) {
1123                 value = searchValueTransform(jsonObjectGetKey( node->item, "value" ));
1124         } else if (jsonObjectGetKey( node->item, "value" )->type == JSON_HASH) {
1125                 value = searchWHERE( jsonObjectGetKey( node->item, "value" ), osrfHashGet( oilsIDL(), class ), AND_OP_JOIN );
1126         } else if (jsonObjectGetKey( node->item, "value" )->type != JSON_NULL) {
1127                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1128                         value = jsonNumberToDBString( field, jsonObjectGetKey( node->item, "value" ) );
1129                 } else {
1130                         value = jsonObjectToSimpleString(jsonObjectGetKey( node->item, "value" ));
1131                         if ( !dbi_conn_quote_string(dbhandle, &value) ) {
1132                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, value);
1133                                 free(value);
1134                                 return NULL;
1135                         }
1136                 }
1137         }
1138
1139         buffer_fadd(
1140                 sql_buf,
1141                 "%s %s %s",
1142                 field_transform,
1143                 node->key,
1144                 value
1145         );
1146
1147         char* pred = buffer_data(sql_buf);
1148         buffer_free(sql_buf);
1149         free(field_transform);
1150
1151         return pred;
1152 }
1153
1154 char* searchSimplePredicate (const char* orig_op, const char* class, osrfHash* field, jsonObject* node) {
1155
1156         char* val = NULL;
1157
1158         if (node->type != JSON_NULL) {
1159                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1160                         val = jsonNumberToDBString( field, node );
1161                 } else {
1162                         val = jsonObjectToSimpleString(node);
1163                 }
1164         }
1165
1166         char* pred = searchWriteSimplePredicate( class, field, osrfHashGet(field, "name"), orig_op, val );
1167
1168         if (val) free(val);
1169
1170         return pred;
1171 }
1172
1173 char* searchWriteSimplePredicate ( const char* class, osrfHash* field, const char* left, const char* orig_op, const char* right ) {
1174
1175         char* val = NULL;
1176         char* op = NULL;
1177         if (right == NULL) {
1178                 val = strdup("NULL");
1179
1180                 if (strcmp( orig_op, "=" ))
1181                         op = strdup("IS NOT");
1182                 else
1183                         op = strdup("IS");
1184
1185         } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1186                 val = strdup(right);
1187                 op = strdup(orig_op);
1188
1189         } else {
1190                 val = strdup(right);
1191                 if ( !dbi_conn_quote_string(dbhandle, &val) ) {
1192                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1193                         free(val);
1194                         return NULL;
1195                 }
1196                 op = strdup(orig_op);
1197         }
1198
1199         growing_buffer* sql_buf = buffer_init(16);
1200         buffer_fadd( sql_buf, "\"%s\".%s %s %s", class, left, op, val );
1201         free(val);
1202         free(op);
1203
1204         char* pred = buffer_data(sql_buf);
1205         buffer_free(sql_buf);
1206
1207         return pred;
1208
1209 }
1210
1211 char* searchBETWEENPredicate (const char* class, osrfHash* field, jsonObject* node) {
1212
1213         char* x_string;
1214         char* y_string;
1215
1216         if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1217                 x_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,0));
1218                 y_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,1));
1219
1220         } else {
1221                 x_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,0));
1222                 y_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,1));
1223                 if ( !(dbi_conn_quote_string(dbhandle, &x_string) && dbi_conn_quote_string(dbhandle, &y_string)) ) {
1224                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key strings [%s] and [%s]", MODULENAME, x_string, y_string);
1225                         free(x_string);
1226                         free(y_string);
1227                         return NULL;
1228                 }
1229         }
1230
1231         growing_buffer* sql_buf = buffer_init(32);
1232         buffer_fadd( sql_buf, "%s BETWEEN %s AND %s", osrfHashGet(field, "name"), x_string, y_string );
1233         free(x_string);
1234         free(y_string);
1235
1236         char* pred = buffer_data(sql_buf);
1237         buffer_free(sql_buf);
1238
1239         return pred;
1240 }
1241
1242 char* searchPredicate ( const char* class, osrfHash* field, jsonObject* node ) {
1243
1244         char* pred = NULL;
1245         if (node->type == JSON_ARRAY) { // equality IN search
1246                 pred = searchINPredicate( class, field, node, NULL );
1247         } else if (node->type == JSON_HASH) { // non-equality search
1248                 jsonObjectNode* pred_node;
1249                 jsonObjectIterator* pred_itr = jsonNewObjectIterator( node );
1250                 while ( (pred_node = jsonObjectIteratorNext( pred_itr )) ) {
1251                         if ( !(strcasecmp( pred_node->key,"between" )) )
1252                                 pred = searchBETWEENPredicate( class, field, pred_node->item );
1253                         else if ( !(strcasecmp( pred_node->key,"in" )) || !(strcasecmp( pred_node->key,"not in" )) )
1254                                 pred = searchINPredicate( class, field, pred_node->item, pred_node->key );
1255                         else if ( pred_node->item->type == JSON_ARRAY )
1256                                 pred = searchFunctionPredicate( class, field, pred_node );
1257                         else if ( pred_node->item->type == JSON_HASH )
1258                                 pred = searchFieldTransformPredicate( class, field, pred_node );
1259                         else 
1260                                 pred = searchSimplePredicate( pred_node->key, class, field, pred_node->item );
1261
1262                         break;
1263                 }
1264         } else if (node->type == JSON_NULL) { // IS NULL search
1265                 growing_buffer* _p = buffer_init(16);
1266                 buffer_fadd(
1267                         _p,
1268                         "\"%s\".%s IS NULL",
1269                         class,
1270                         osrfHashGet(field, "name")
1271                 );
1272                 pred = buffer_data(_p);
1273                 buffer_free(_p);
1274         } else { // equality search
1275                 pred = searchSimplePredicate( "=", class, field, node );
1276         }
1277
1278         return pred;
1279
1280 }
1281
1282
1283 /*
1284
1285 join : {
1286         acn : {
1287                 field : record,
1288                 fkey : id
1289                 type : left
1290                 filter_op : or
1291                 filter : { ... },
1292                 join : {
1293                         acp : {
1294                                 field : call_number,
1295                                 fkey : id,
1296                                 filter : { ... },
1297                         },
1298                 },
1299         },
1300         mrd : {
1301                 field : record,
1302                 type : inner
1303                 fkey : id,
1304                 filter : { ... },
1305         }
1306 }
1307
1308 */
1309
1310 char* searchJOIN ( jsonObject* join_hash, osrfHash* leftmeta ) {
1311
1312         if (join_hash->type == JSON_STRING) {
1313                 char* __tmp = jsonObjectToSimpleString( join_hash );
1314                 join_hash = jsonParseString("{}");
1315                 jsonObjectSetKey(join_hash, __tmp, NULL);
1316                 free(__tmp);
1317         }
1318
1319         growing_buffer* join_buf = buffer_init(128);
1320         char* leftclass = osrfHashGet(leftmeta, "classname");
1321
1322         jsonObjectNode* snode = NULL;
1323         jsonObjectIterator* search_itr = jsonNewObjectIterator( join_hash );
1324         while ( (snode = jsonObjectIteratorNext( search_itr )) ) {
1325                 osrfHash* idlClass = osrfHashGet( oilsIDL(), snode->key );
1326
1327                 char* class = osrfHashGet(idlClass, "classname");
1328                 char* table = osrfHashGet(idlClass, "tablename");
1329
1330                 char* type = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "type" ) );
1331                 char* filter_op = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "filter_op" ) );
1332                 char* fkey = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "fkey" ) );
1333                 char* field = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "field" ) );
1334
1335                 jsonObject* filter = jsonObjectGetKey( snode->item, "filter" );
1336                 jsonObject* join_filter = jsonObjectGetKey( snode->item, "join" );
1337
1338                 if (field && !fkey) {
1339                         fkey = (char*)oilsIDLFindPath("/%s/links/%s/key", class, field);
1340                         if (!fkey) {
1341                                 osrfLogError(
1342                                         OSRF_LOG_MARK,
1343                                         "%s: JOIN failed.  No link defined from %s.%s to %s",
1344                                         MODULENAME,
1345                                         class,
1346                                         field,
1347                                         leftclass
1348                                 );
1349                                 buffer_free(join_buf);
1350                                 return NULL;
1351                         }
1352                         fkey = strdup( fkey );
1353
1354                 } else if (!field && fkey) {
1355                         field = (char*)oilsIDLFindPath("/%s/links/%s/key", leftclass, fkey );
1356                         if (!field) {
1357                                 osrfLogError(
1358                                         OSRF_LOG_MARK,
1359                                         "%s: JOIN failed.  No link defined from %s.%s to %s",
1360                                         MODULENAME,
1361                                         leftclass,
1362                                         fkey,
1363                                         class
1364                                 );
1365                                 buffer_free(join_buf);
1366                                 return NULL;
1367                         }
1368                         field = strdup( field );
1369
1370                 } else if (!field && !fkey) {
1371                         osrfHash* _links = oilsIDLFindPath("/%s/links", leftclass);
1372
1373                         int i = 0;
1374                         osrfStringArray* keys = osrfHashKeys( _links );
1375                         while ( (fkey = osrfStringArrayGetString(keys, i++)) ) {
1376                                 fkey = strdup(osrfStringArrayGetString(keys, i++));
1377                                 if ( !strcmp( (char*)oilsIDLFindPath("/%s/links/%s/class", leftclass, fkey), class) ) {
1378                                         field = strdup( (char*)oilsIDLFindPath("/%s/links/%s/key", leftclass, fkey) );
1379                                         break;
1380                                 } else {
1381                                         free(fkey);
1382                                 }
1383                         }
1384                         osrfStringArrayFree(keys);
1385
1386                         if (!field && !fkey) {
1387                                 _links = oilsIDLFindPath("/%s/links", class);
1388
1389                                 i = 0;
1390                                 keys = osrfHashKeys( _links );
1391                                 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
1392                                         field = strdup(osrfStringArrayGetString(keys, i++));
1393                                         if ( !strcmp( (char*)oilsIDLFindPath("/%s/links/%s/class", class, field), class) ) {
1394                                                 fkey = strdup( (char*)oilsIDLFindPath("/%s/links/%s/key", class, field) );
1395                                                 break;
1396                                         } else {
1397                                                 free(field);
1398                                         }
1399                                 }
1400                                 osrfStringArrayFree(keys);
1401                         }
1402
1403                         if (!field && !fkey) {
1404                                 osrfLogError(
1405                                         OSRF_LOG_MARK,
1406                                         "%s: JOIN failed.  No link defined between %s and %s",
1407                                         MODULENAME,
1408                                         leftclass,
1409                                         class
1410                                 );
1411                                 buffer_free(join_buf);
1412                                 return NULL;
1413                         }
1414
1415                 }
1416
1417                 if (type) {
1418                         if ( !strcasecmp(type,"left") ) {
1419                                 buffer_add(join_buf, " LEFT JOIN");
1420                         } else if ( !strcasecmp(type,"right") ) {
1421                                 buffer_add(join_buf, " RIGHT JOIN");
1422                         } else if ( !strcasecmp(type,"full") ) {
1423                                 buffer_add(join_buf, " FULL JOIN");
1424                         } else {
1425                                 buffer_add(join_buf, " INNER JOIN");
1426                         }
1427                 } else {
1428                         buffer_add(join_buf, " INNER JOIN");
1429                 }
1430
1431                 buffer_fadd(join_buf, " %s AS \"%s\" ON ( \"%s\".%s = \"%s\".%s", table, class, class, field, leftclass, fkey);
1432
1433                 if (filter) {
1434                         if (filter_op) {
1435                                 if (!strcasecmp("or",filter_op)) {
1436                                         buffer_add( join_buf, " OR " );
1437                                 } else {
1438                                         buffer_add( join_buf, " AND " );
1439                                 }
1440                         } else {
1441                                 buffer_add( join_buf, " AND " );
1442                         }
1443
1444                         char* jpred = searchWHERE( filter, idlClass, AND_OP_JOIN );
1445                         buffer_fadd( join_buf, " %s", jpred );
1446                         free(jpred);
1447                 }
1448
1449                 buffer_add(join_buf, " ) ");
1450                 
1451                 if (join_filter) {
1452                         char* jpred = searchJOIN( join_filter, idlClass );
1453                         buffer_fadd( join_buf, " %s", jpred );
1454                         free(jpred);
1455                 }
1456
1457                 free(type);
1458                 free(filter_op);
1459                 free(fkey);
1460                 free(field);
1461         }
1462
1463         char* join_string = buffer_data(join_buf);
1464         buffer_free(join_buf);
1465         return join_string;
1466 }
1467
1468 /*
1469
1470 { +class : { -or|-and : { field : { op : value }, ... }, ... }, ... }
1471
1472 */
1473 char* searchWHERE ( jsonObject* search_hash, osrfHash* meta, int opjoin_type ) {
1474
1475         growing_buffer* sql_buf = buffer_init(128);
1476
1477         jsonObjectNode* node = NULL;
1478
1479         int first = 1;
1480         jsonObjectIterator* search_itr = jsonNewObjectIterator( search_hash );
1481         while ( (node = jsonObjectIteratorNext( search_itr )) ) {
1482
1483                 if (first) {
1484                         first = 0;
1485                 } else {
1486                         if (opjoin_type == 1) buffer_add(sql_buf, " OR ");
1487                         else buffer_add(sql_buf, " AND ");
1488                 }
1489
1490                 if ( !strncmp("+",node->key,1) ) {
1491                         if ( node->item->type == JSON_STRING ) {
1492                                 char* subpred = jsonObjectToSimpleString( node->item );
1493                                 buffer_fadd(sql_buf, " \"%s\".%s ", node->key + 1, subpred);
1494                                 free(subpred);
1495                         } else {
1496                                 char* subpred = searchWHERE( node->item, osrfHashGet( oilsIDL(), node->key + 1 ), AND_OP_JOIN );
1497                                 buffer_fadd(sql_buf, "( %s )", subpred);
1498                                 free(subpred);
1499                         }
1500                 } else if ( !strcasecmp("-or",node->key) ) {
1501                         char* subpred = searchWHERE( node->item, meta, OR_OP_JOIN );
1502                         buffer_fadd(sql_buf, "( %s )", subpred);
1503                         free(subpred);
1504                 } else if ( !strcasecmp("-and",node->key) ) {
1505                         char* subpred = searchWHERE( node->item, meta, AND_OP_JOIN );
1506                         buffer_fadd(sql_buf, "( %s )", subpred);
1507                         free(subpred);
1508                 } else {
1509
1510                         char* class = osrfHashGet(meta, "classname");
1511                         osrfHash* fields = osrfHashGet(meta, "fields");
1512                         osrfHash* field = osrfHashGet( fields, node->key );
1513
1514                         if (!field) {
1515                                 osrfLogError(
1516                                         OSRF_LOG_MARK,
1517                                         "%s: Attempt to reference non-existant column %s on table %s",
1518                                         MODULENAME,
1519                                         node->key,
1520                                         osrfHashGet(meta, "tablename")
1521                                 );
1522                                 buffer_free(sql_buf);
1523                                 return NULL;
1524                         }
1525
1526                         char* subpred = searchPredicate( class, field, node->item );
1527                         buffer_add( sql_buf, subpred );
1528                         free(subpred);
1529                 }
1530         }
1531
1532         jsonObjectIteratorFree(search_itr);
1533
1534         char* pred = buffer_data(sql_buf);
1535         buffer_free(sql_buf);
1536
1537         return pred;
1538 }
1539
1540 char* SELECT (
1541                 /* method context */ osrfMethodContext* ctx,
1542                 
1543                 /* SELECT   */ jsonObject* selhash,
1544                 /* FROM     */ jsonObject* join_hash,
1545                 /* WHERE    */ jsonObject* search_hash,
1546                 /* ORDER BY */ jsonObject* order_hash,
1547                 /* LIMIT    */ jsonObject* limit,
1548                 /* OFFSET   */ jsonObject* offset,
1549                 /* flags    */ int flags
1550 ) {
1551         // in case we don't get a select list
1552         jsonObject* defaultselhash = NULL;
1553
1554         // general tmp objects
1555         jsonObject* __tmp = NULL;
1556         jsonObjectNode* selclass = NULL;
1557         jsonObjectNode* selfield = NULL;
1558         jsonObjectNode* snode = NULL;
1559         jsonObjectNode* onode = NULL;
1560         jsonObject* found = NULL;
1561
1562         char* string = NULL;
1563         int first = 1;
1564         int gfirst = 1;
1565         //int hfirst = 1;
1566
1567         // return variable for the SQL
1568         char* sql = NULL;
1569
1570         // the core search class
1571         char* core_class = NULL;
1572
1573         // metadata about the core search class
1574         osrfHash* core_meta = NULL;
1575         osrfHash* core_fields = NULL;
1576         osrfHash* idlClass = NULL;
1577
1578         // the query buffer
1579         growing_buffer* sql_buf = buffer_init(128);
1580
1581         // temp buffer for the SELECT list
1582         growing_buffer* select_buf = buffer_init(128);
1583         growing_buffer* order_buf = buffer_init(128);
1584         growing_buffer* group_buf = buffer_init(128);
1585         growing_buffer* having_buf = buffer_init(128);
1586
1587         // punt if there's no core class
1588         if (!join_hash || ( join_hash->type == JSON_HASH && !join_hash->size ))
1589                 return NULL;
1590
1591         // get the core class -- the only key of the top level FROM clause, or a string
1592         if (join_hash->type == JSON_HASH) {
1593                 jsonObjectIterator* tmp_itr = jsonNewObjectIterator( join_hash );
1594                 snode = jsonObjectIteratorNext( tmp_itr );
1595                 
1596                 core_class = strdup( snode->key );
1597                 join_hash = snode->item;
1598
1599                 jsonObjectIteratorFree( tmp_itr );
1600                 snode = NULL;
1601
1602         } else if (join_hash->type == JSON_STRING) {
1603                 core_class = jsonObjectToSimpleString( join_hash );
1604                 join_hash = NULL;
1605         }
1606
1607         // punt if we don't know about the core class
1608         if (!(core_meta = osrfHashGet( oilsIDL(), core_class )))
1609                 return NULL;
1610
1611         core_fields = osrfHashGet(core_meta, "fields");
1612
1613         // if the select list is empty, or the core class field list is '*',
1614         // build the default select list ...
1615         if (!selhash) {
1616                 selhash = defaultselhash = jsonParseString( "{}" );
1617                 jsonObjectSetKey( selhash, core_class, jsonParseString( "[]" ) );
1618         } else if ( (__tmp = jsonObjectGetKey( selhash, core_class )) && __tmp->type == JSON_STRING ) {
1619                 char* __x = jsonObjectToSimpleString( __tmp );
1620                 if (!strncmp( "*", __x, 1 )) {
1621                         jsonObjectRemoveKey( selhash, core_class );
1622                         jsonObjectSetKey( selhash, core_class, jsonParseString( "[]" ) );
1623                 }
1624                 free(__x);
1625         }
1626
1627         // ... and if we /are/ building the default list, do that
1628         if ( (__tmp = jsonObjectGetKey(selhash,core_class)) && !__tmp->size ) {
1629                 
1630                 int i = 0;
1631                 char* field;
1632
1633                 osrfStringArray* keys = osrfHashKeys( core_fields );
1634                 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
1635                         if ( strncasecmp( "true", osrfHashGet( osrfHashGet( core_fields, field ), "virtual" ), 4 ) )
1636                                 jsonObjectPush( __tmp, jsonNewObject( field ) );
1637                 }
1638                 osrfStringArrayFree(keys);
1639         }
1640
1641         // Now we build the acutal select list
1642         int sel_pos = 1;
1643         jsonObject* is_agg = jsonObjectFindPath(selhash, "//aggregate");
1644         first = 1;
1645         gfirst = 1;
1646         jsonObjectIterator* selclass_itr = jsonNewObjectIterator( selhash );
1647         while ( (selclass = jsonObjectIteratorNext( selclass_itr )) ) {
1648
1649                 // round trip through the idl, just to be safe
1650                 idlClass = osrfHashGet( oilsIDL(), selclass->key );
1651                 if (!idlClass) continue;
1652                 char* cname = osrfHashGet(idlClass, "classname");
1653
1654                 // make sure the target relation is in the join tree
1655                 if (strcmp(core_class,cname)) {
1656                         if (!join_hash) continue;
1657
1658                         if (join_hash->type == JSON_STRING) {
1659                                 string = jsonObjectToSimpleString(join_hash);
1660                                 found = strcmp(string,cname) ? NULL : jsonParseString("{\"1\":\"1\"}");
1661                                 free(string);
1662                         } else {
1663                                 found = jsonObjectFindPath(join_hash, "//%s", cname);
1664                         }
1665
1666                         if (!found->size) {
1667                                 jsonObjectFree(found);
1668                                 continue;
1669                         }
1670
1671                         jsonObjectFree(found);
1672                 }
1673
1674                 // stitch together the column list ...
1675                 jsonObjectIterator* select_itr = jsonNewObjectIterator( selclass->item );
1676                 while ( (selfield = jsonObjectIteratorNext( select_itr )) ) {
1677
1678                         char* __column = NULL;
1679                         char* __alias = NULL;
1680
1681                         // ... if it's a sstring, just toss it on the pile
1682                         if (selfield->item->type == JSON_STRING) {
1683
1684                                 // again, just to be safe
1685                                 char* _requested_col = jsonObjectToSimpleString(selfield->item);
1686                                 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), _requested_col );
1687                                 free(_requested_col);
1688
1689                                 if (!field) continue;
1690                                 __column = strdup(osrfHashGet(field, "name"));
1691
1692                                 if (first) {
1693                                         first = 0;
1694                                 } else {
1695                                         buffer_add(select_buf, ",");
1696                                 }
1697
1698                                 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, __column, __column);
1699
1700                         // ... but it could be an object, in which case we check for a Field Transform
1701                         } else {
1702
1703                                 __column = jsonObjectToSimpleString( jsonObjectGetKey( selfield->item, "column" ) );
1704
1705                                 // again, just to be safe
1706                                 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), __column );
1707                                 if (!field) continue;
1708                                 char* fname = osrfHashGet(field, "name");
1709
1710                                 if (first) {
1711                                         first = 0;
1712                                 } else {
1713                                         buffer_add(select_buf, ",");
1714                                 }
1715
1716                                 if ((__tmp = jsonObjectGetKey( selfield->item, "alias" ))) {
1717                                         __alias = jsonObjectToSimpleString( __tmp );
1718                                 } else {
1719                                         __alias = strdup(__column);
1720                                 }
1721
1722                                 if (jsonObjectGetKey( selfield->item, "transform" )) {
1723                                         free(__column);
1724                                         __column = searchFieldTransform(cname, field, selfield->item);
1725                                         buffer_fadd(select_buf, " %s AS \"%s\"", __column, __alias);
1726                                 } else {
1727                                         buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, fname, __alias);
1728                                 }
1729                         }
1730
1731                         if (is_agg->size || (flags & SELECT_DISTINCT)) {
1732
1733                                 if (!jsonBoolIsTrue( jsonObjectGetKey( selfield->item, "aggregate" ) )) {
1734                                         if (gfirst) {
1735                                                 gfirst = 0;
1736                                         } else {
1737                                                 buffer_add(group_buf, ",");
1738                                         }
1739
1740                                         buffer_fadd(group_buf, " %d", sel_pos);
1741                                 /*
1742                                 } else if (is_agg = jsonObjectGetKey( selfield->item, "having" )) {
1743                                         if (gfirst) {
1744                                                 gfirst = 0;
1745                                         } else {
1746                                                 buffer_add(group_buf, ",");
1747                                         }
1748
1749                                         __column = searchFieldTransform(cname, field, selfield->item);
1750                                         buffer_fadd(group_buf, " %s", __column);
1751                                         __column = searchFieldTransform(cname, field, selfield->item);
1752                                 */
1753                                 }
1754                         }
1755
1756                         if (__column) free(__column);
1757                         if (__alias) free(__alias);
1758
1759                         sel_pos++;
1760                 }
1761         }
1762
1763         if (is_agg) jsonObjectFree(is_agg);
1764
1765         char* col_list = buffer_data(select_buf);
1766         buffer_free(select_buf);
1767
1768         // Put it all together
1769         buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\" ", col_list, osrfHashGet(core_meta, "tablename"), core_class );
1770         free(col_list);
1771
1772         // Now, walk the join tree and add that clause
1773         if ( join_hash ) {
1774                 char* join_clause = searchJOIN( join_hash, core_meta );
1775                 buffer_add(sql_buf, join_clause);
1776                 free(join_clause);
1777         }
1778
1779         if ( search_hash ) {
1780                 buffer_add(sql_buf, " WHERE ");
1781
1782                 // and it's on the the WHERE clause
1783                 char* pred = searchWHERE( search_hash, core_meta, AND_OP_JOIN );
1784                 if (!pred) {
1785                         osrfAppSessionStatus(
1786                                 ctx->session,
1787                                 OSRF_STATUS_INTERNALSERVERERROR,
1788                                 "osrfMethodException",
1789                                 ctx->request,
1790                                 "Severe query error -- see error log for more details"
1791                         );
1792                         free(core_class);
1793                         buffer_free(sql_buf);
1794                         if (defaultselhash) jsonObjectFree(defaultselhash);
1795                         return NULL;
1796                 } else {
1797                         buffer_add(sql_buf, pred);
1798                         free(pred);
1799                 }
1800         }
1801
1802         first = 1;
1803         jsonObjectIterator* class_itr = jsonNewObjectIterator( order_hash );
1804         while ( (snode = jsonObjectIteratorNext( class_itr )) ) {
1805
1806                 if (!jsonObjectGetKey(selhash,snode->key))
1807                         continue;
1808
1809                 if ( snode->item->type == JSON_HASH ) {
1810
1811                         jsonObjectIterator* order_itr = jsonNewObjectIterator( snode->item );
1812                         while ( (onode = jsonObjectIteratorNext( order_itr )) ) {
1813
1814                                 if (!oilsIDLFindPath( "/%s/fields/%s", snode->key, onode->key ))
1815                                         continue;
1816
1817                                 char* direction = NULL;
1818                                 if ( onode->item->type == JSON_HASH ) {
1819                                         if ( jsonObjectGetKey( onode->item, "transform" ) ) {
1820                                                 string = searchFieldTransform(
1821                                                         snode->key,
1822                                                         oilsIDLFindPath( "/%s/fields/%s", snode->key, onode->key ),
1823                                                         onode->item
1824                                                 );
1825                                         } else {
1826                                                 growing_buffer* field_buf = buffer_init(16);
1827                                                 buffer_fadd(field_buf, "\"%s\".%s", snode->key, onode->key);
1828                                                 string = buffer_data(field_buf);
1829                                                 buffer_free(field_buf);
1830                                         }
1831
1832                                         if ( (__tmp = jsonObjectGetKey( onode->item, "direction" )) ) {
1833                                                 direction = jsonObjectToSimpleString(__tmp);
1834                                                 if (!strncasecmp(direction, "d", 1)) {
1835                                                         free(direction);
1836                                                         direction = " DESC";
1837                                                 } else {
1838                                                         free(direction);
1839                                                         direction = " ASC";
1840                                                 }
1841                                         }
1842
1843                                 } else {
1844                                         string = strdup(onode->key);
1845                                         direction = jsonObjectToSimpleString(onode->item);
1846                                         if (!strncasecmp(direction, "d", 1)) {
1847                                                 free(direction);
1848                                                 direction = " DESC";
1849                                         } else {
1850                                                 free(direction);
1851                                                 direction = " ASC";
1852                                         }
1853                                 }
1854
1855                                 if (first) {
1856                                         first = 0;
1857                                 } else {
1858                                         buffer_add(order_buf, ", ");
1859                                 }
1860
1861                                 buffer_add(order_buf, string);
1862                                 free(string);
1863
1864                                 if (direction) {
1865                                         buffer_add(order_buf, direction);
1866                                 }
1867
1868                         }
1869
1870                 } else if ( snode->item->type == JSON_ARRAY ) {
1871
1872                         jsonObjectIterator* order_itr = jsonNewObjectIterator( snode->item );
1873                         while ( (onode = jsonObjectIteratorNext( order_itr )) ) {
1874
1875                                 char* _f = jsonObjectToSimpleString( onode->item );
1876
1877                                 if (!oilsIDLFindPath( "/%s/fields/%s", snode->key, _f))
1878                                         continue;
1879
1880                                 if (first) {
1881                                         first = 0;
1882                                 } else {
1883                                         buffer_add(order_buf, ", ");
1884                                 }
1885
1886                                 buffer_add(order_buf, _f);
1887                                 free(_f);
1888
1889                         }
1890
1891                 // IT'S THE OOOOOOOOOOOLD STYLE!
1892                 } else {
1893                         osrfLogError(OSRF_LOG_MARK, "%s: Possible SQL injection attempt; direct order by is not allowed", MODULENAME);
1894                         osrfAppSessionStatus(
1895                                 ctx->session,
1896                                 OSRF_STATUS_INTERNALSERVERERROR,
1897                                 "osrfMethodException",
1898                                 ctx->request,
1899                                 "Severe query error -- see error log for more details"
1900                         );
1901
1902                         free(core_class);
1903                         buffer_free(order_buf);
1904                         buffer_free(sql_buf);
1905                         if (defaultselhash) jsonObjectFree(defaultselhash);
1906                         return NULL;
1907                 }
1908
1909         }
1910
1911         string = buffer_data(group_buf);
1912         buffer_free(group_buf);
1913
1914         if (strlen(string)) {
1915                 buffer_fadd(
1916                         sql_buf,
1917                         " GROUP BY %s",
1918                         string
1919                 );
1920         }
1921
1922         free(string);
1923
1924         string = buffer_data(having_buf);
1925         buffer_free(having_buf);
1926
1927         if (strlen(string)) {
1928                 buffer_fadd(
1929                         sql_buf,
1930                         " HAVING %s",
1931                         string
1932                 );
1933         }
1934
1935         free(string);
1936
1937         string = buffer_data(order_buf);
1938         buffer_free(order_buf);
1939
1940         if (strlen(string)) {
1941                 buffer_fadd(
1942                         sql_buf,
1943                         " ORDER BY %s",
1944                         string
1945                 );
1946         }
1947
1948         free(string);
1949
1950         if ( limit ){
1951                 string = jsonObjectToSimpleString(limit);
1952                 buffer_fadd( sql_buf, " LIMIT %d", atoi(string) );
1953                 free(string);
1954         }
1955
1956         if (offset) {
1957                 string = jsonObjectToSimpleString(offset);
1958                 buffer_fadd( sql_buf, " OFFSET %d", atoi(string) );
1959                 free(string);
1960         }
1961
1962         buffer_add(sql_buf, ";");
1963
1964         sql = buffer_data(sql_buf);
1965
1966         free(core_class);
1967         buffer_free(sql_buf);
1968         if (defaultselhash) jsonObjectFree(defaultselhash);
1969
1970         return sql;
1971
1972 }
1973
1974 char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
1975
1976         osrfHash* fields = osrfHashGet(meta, "fields");
1977         char* core_class = osrfHashGet(meta, "classname");
1978
1979         jsonObject* join_hash = jsonObjectGetKey( order_hash, "join" );
1980
1981         jsonObjectNode* node = NULL;
1982         jsonObjectNode* snode = NULL;
1983         jsonObjectNode* onode = NULL;
1984         jsonObject* _tmp = NULL;
1985         jsonObject* selhash = NULL;
1986         jsonObject* defaultselhash = NULL;
1987
1988         growing_buffer* sql_buf = buffer_init(128);
1989         growing_buffer* select_buf = buffer_init(128);
1990
1991         if ( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
1992                 defaultselhash = jsonParseString( "{}" );
1993                 selhash = defaultselhash;
1994         }
1995         
1996         if ( !jsonObjectGetKey(selhash,core_class) ) {
1997                 jsonObjectSetKey( selhash, core_class, jsonParseString( "[]" ) );
1998                 jsonObject* flist = jsonObjectGetKey( selhash, core_class );
1999                 
2000                 int i = 0;
2001                 char* field;
2002
2003                 osrfStringArray* keys = osrfHashKeys( fields );
2004                 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
2005                         if ( strcasecmp( "true", osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
2006                                 jsonObjectPush( flist, jsonNewObject( field ) );
2007                 }
2008                 osrfStringArrayFree(keys);
2009         }
2010
2011         int first = 1;
2012         jsonObjectIterator* class_itr = jsonNewObjectIterator( selhash );
2013         while ( (snode = jsonObjectIteratorNext( class_itr )) ) {
2014
2015                 osrfHash* idlClass = osrfHashGet( oilsIDL(), snode->key );
2016                 if (!idlClass) continue;
2017                 char* cname = osrfHashGet(idlClass, "classname");
2018
2019                 if (strcmp(core_class,snode->key)) {
2020                         if (!join_hash) continue;
2021
2022                         jsonObject* found =  jsonObjectFindPath(join_hash, "//%s", snode->key);
2023                         if (!found->size) {
2024                                 jsonObjectFree(found);
2025                                 continue;
2026                         }
2027
2028                         jsonObjectFree(found);
2029                 }
2030
2031                 jsonObjectIterator* select_itr = jsonNewObjectIterator( snode->item );
2032                 while ( (node = jsonObjectIteratorNext( select_itr )) ) {
2033                         osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), jsonObjectToSimpleString(node->item) );
2034                         char* fname = osrfHashGet(field, "name");
2035
2036                         if (!field) continue;
2037
2038                         if (first) {
2039                                 first = 0;
2040                         } else {
2041                                 buffer_add(select_buf, ",");
2042                         }
2043
2044                         buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
2045                 }
2046         }
2047
2048         char* col_list = buffer_data(select_buf);
2049         buffer_free(select_buf);
2050
2051         buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\"", col_list, osrfHashGet(meta, "tablename"), core_class );
2052
2053         if ( join_hash ) {
2054                 char* join_clause = searchJOIN( join_hash, meta );
2055                 buffer_fadd(sql_buf, " %s", join_clause);
2056                 free(join_clause);
2057         }
2058
2059         buffer_add(sql_buf, " WHERE ");
2060
2061         char* pred = searchWHERE( search_hash, meta, AND_OP_JOIN );
2062         if (!pred) {
2063                 osrfAppSessionStatus(
2064                         ctx->session,
2065                         OSRF_STATUS_INTERNALSERVERERROR,
2066                                 "osrfMethodException",
2067                                 ctx->request,
2068                                 "Severe query error -- see error log for more details"
2069                         );
2070                 buffer_free(sql_buf);
2071                 return NULL;
2072         } else {
2073                 buffer_add(sql_buf, pred);
2074                 free(pred);
2075         }
2076
2077         if (order_hash) {
2078                 char* string = NULL;
2079                 if ( (_tmp = jsonObjectGetKey( order_hash, "order_by" )) ){
2080
2081                         growing_buffer* order_buf = buffer_init(128);
2082
2083                         first = 1;
2084                         jsonObjectIterator* class_itr = jsonNewObjectIterator( _tmp );
2085                         while ( (snode = jsonObjectIteratorNext( class_itr )) ) {
2086
2087                                 if (!jsonObjectGetKey(selhash,snode->key))
2088                                         continue;
2089
2090                                 if ( snode->item->type == JSON_HASH ) {
2091
2092                                         jsonObjectIterator* order_itr = jsonNewObjectIterator( snode->item );
2093                                         while ( (onode = jsonObjectIteratorNext( order_itr )) ) {
2094
2095                                                 if (!oilsIDLFindPath( "/%s/fields/%s", snode->key, onode->key ))
2096                                                         continue;
2097
2098                                                 char* direction = NULL;
2099                                                 if ( onode->item->type == JSON_HASH ) {
2100                                                         if ( jsonObjectGetKey( onode->item, "transform" ) ) {
2101                                                                 string = searchFieldTransform(
2102                                                                         snode->key,
2103                                                                         oilsIDLFindPath( "/%s/fields/%s", snode->key, onode->key ),
2104                                                                         onode->item
2105                                                                 );
2106                                                         } else {
2107                                                                 growing_buffer* field_buf = buffer_init(16);
2108                                                                 buffer_fadd(field_buf, "\"%s\".%s", snode->key, onode->key);
2109                                                                 string = buffer_data(field_buf);
2110                                                                 buffer_free(field_buf);
2111                                                         }
2112
2113                                                         if ( (_tmp = jsonObjectGetKey( onode->item, "direction" )) ) {
2114                                                                 direction = jsonObjectToSimpleString(_tmp);
2115                                                                 if (!strncasecmp(direction, "d", 1)) {
2116                                                                         free(direction);
2117                                                                         direction = " DESC";
2118                                                                 } else {
2119                                                                         free(direction);
2120                                                                         direction = " ASC";
2121                                                                 }
2122                                                         }
2123
2124                                                 } else {
2125                                                         string = strdup(onode->key);
2126                                                         direction = jsonObjectToSimpleString(onode->item);
2127                                                         if (!strncasecmp(direction, "d", 1)) {
2128                                                                 free(direction);
2129                                                                 direction = " DESC";
2130                                                         } else {
2131                                                                 free(direction);
2132                                                                 direction = " ASC";
2133                                                         }
2134                                                 }
2135
2136                                                 if (first) {
2137                                                         first = 0;
2138                                                 } else {
2139                                                         buffer_add(order_buf, ", ");
2140                                                 }
2141
2142                                                 buffer_add(order_buf, string);
2143                                                 free(string);
2144
2145                                                 if (direction) {
2146                                                         buffer_add(order_buf, direction);
2147                                                 }
2148
2149                                         }
2150
2151                                 } else {
2152                                         string = jsonObjectToSimpleString(snode->item);
2153                                         buffer_add(order_buf, string);
2154                                         free(string);
2155                                         break;
2156                                 }
2157
2158                         }
2159
2160                         string = buffer_data(order_buf);
2161                         buffer_free(order_buf);
2162
2163                         if (strlen(string)) {
2164                                 buffer_fadd(
2165                                         sql_buf,
2166                                         " ORDER BY %s",
2167                                         string
2168                                 );
2169                         }
2170
2171                         free(string);
2172                 }
2173
2174                 if ( (_tmp = jsonObjectGetKey( order_hash, "limit" )) ){
2175                         string = jsonObjectToSimpleString(_tmp);
2176                         buffer_fadd(
2177                                 sql_buf,
2178                                 " LIMIT %d",
2179                                 atoi(string)
2180                         );
2181                         free(string);
2182                 }
2183
2184                 _tmp = jsonObjectGetKey( order_hash, "offset" );
2185                 if (_tmp) {
2186                         string = jsonObjectToSimpleString(_tmp);
2187                         buffer_fadd(
2188                                 sql_buf,
2189                                 " OFFSET %d",
2190                                 atoi(string)
2191                         );
2192                         free(string);
2193                 }
2194         }
2195
2196         buffer_add(sql_buf, ";");
2197
2198         char* sql = buffer_data(sql_buf);
2199         buffer_free(sql_buf);
2200         if (defaultselhash) jsonObjectFree(defaultselhash);
2201
2202         return sql;
2203 }
2204
2205 int doJSONSearch ( osrfMethodContext* ctx ) {
2206         OSRF_METHOD_VERIFY_CONTEXT(ctx);
2207         osrfLogDebug(OSRF_LOG_MARK, "Recieved query request");
2208
2209         int err = 0;
2210
2211         // XXX for now...
2212         dbhandle = writehandle;
2213
2214         jsonObject* hash = jsonObjectGetIndex(ctx->params, 0);
2215
2216         osrfLogDebug(OSRF_LOG_MARK, "Building SQL ...");
2217         char* sql = SELECT(
2218                         ctx,
2219                         jsonObjectGetKey( hash, "select" ),
2220                         jsonObjectGetKey( hash, "from" ),
2221                         jsonObjectGetKey( hash, "where" ),
2222                         jsonObjectGetKey( hash, "order_by" ),
2223                         jsonObjectGetKey( hash, "limit" ),
2224                         jsonObjectGetKey( hash, "offset" ),
2225                         jsonBoolIsTrue(jsonObjectGetKey( hash, "distinct" )) ? SELECT_DISTINCT : 0
2226         );
2227
2228         if (!sql) {
2229                 err = -1;
2230                 return err;
2231         }
2232         
2233         osrfLogDebug(OSRF_LOG_MARK, "%s SQL =  %s", MODULENAME, sql);
2234         dbi_result result = dbi_conn_query(dbhandle, sql);
2235
2236         if(result) {
2237                 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
2238
2239                 if (dbi_result_first_row(result)) {
2240                         /* JSONify the result */
2241                         osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
2242
2243                         do {
2244                                 osrfAppRespond( ctx, oilsMakeJSONFromResult( result ) );
2245                         } while (dbi_result_next_row(result));
2246
2247                 } else {
2248                         osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
2249                 }
2250
2251                 osrfAppRespondComplete( ctx, NULL );
2252
2253                 /* clean up the query */
2254                 dbi_result_free(result); 
2255
2256         } else {
2257                 err = -1;
2258                 osrfLogError(OSRF_LOG_MARK, "%s: Error with query [%s]", MODULENAME, sql);
2259                 osrfAppSessionStatus(
2260                         ctx->session,
2261                         OSRF_STATUS_INTERNALSERVERERROR,
2262                         "osrfMethodException",
2263                         ctx->request,
2264                         "Severe query error -- see error log for more details"
2265                 );
2266         }
2267
2268         free(sql);
2269         return err;
2270 }
2271
2272 jsonObject* doFieldmapperSearch ( osrfMethodContext* ctx, osrfHash* meta, jsonObject* params, int* err ) {
2273
2274         // XXX for now...
2275         dbhandle = writehandle;
2276
2277         osrfHash* links = osrfHashGet(meta, "links");
2278         osrfHash* fields = osrfHashGet(meta, "fields");
2279         char* core_class = osrfHashGet(meta, "classname");
2280         char* pkey = osrfHashGet(meta, "primarykey");
2281
2282         jsonObject* _tmp;
2283         jsonObject* obj;
2284         jsonObject* search_hash = jsonObjectGetIndex(params, 0);
2285         jsonObject* order_hash = jsonObjectGetIndex(params, 1);
2286
2287         char* sql = buildSELECT( search_hash, order_hash, meta, ctx );
2288         if (!sql) {
2289                 *err = -1;
2290                 return NULL;
2291         }
2292         
2293         osrfLogDebug(OSRF_LOG_MARK, "%s SQL =  %s", MODULENAME, sql);
2294         dbi_result result = dbi_conn_query(dbhandle, sql);
2295
2296         osrfHash* dedup = osrfNewHash();
2297         jsonObject* res_list = jsonParseString("[]");
2298         if(result) {
2299                 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
2300
2301                 if (dbi_result_first_row(result)) {
2302                         /* JSONify the result */
2303                         osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
2304                         do {
2305                                 obj = oilsMakeFieldmapperFromResult( result, meta );
2306                                 int pkey_pos = atoi( osrfHashGet( osrfHashGet( fields, pkey ), "array_position" ) );
2307                                 char* pkey_val = jsonObjectToSimpleString( jsonObjectGetIndex( obj, pkey_pos ) );
2308                                 if ( osrfHashGet( dedup, pkey_val ) ) {
2309                                         jsonObjectFree(obj);
2310                                 } else {
2311                                         osrfHashSet( dedup, pkey_val, pkey_val );
2312                                         jsonObjectPush(res_list, obj);
2313                                 }
2314                         } while (dbi_result_next_row(result));
2315                 } else {
2316                         osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
2317                 }
2318
2319                 /* clean up the query */
2320                 dbi_result_free(result); 
2321
2322         } else {
2323                 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]", MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
2324                 osrfAppSessionStatus(
2325                         ctx->session,
2326                         OSRF_STATUS_INTERNALSERVERERROR,
2327                         "osrfMethodException",
2328                         ctx->request,
2329                         "Severe query error -- see error log for more details"
2330                 );
2331                 *err = -1;
2332                 free(sql);
2333                 jsonObjectFree(res_list);
2334                 return jsonNULL;
2335
2336         }
2337
2338         free(sql);
2339
2340         if (res_list->size && order_hash) {
2341                 _tmp = jsonObjectGetKey( order_hash, "flesh" );
2342                 if (_tmp) {
2343                         int x = (int)jsonObjectGetNumber(_tmp);
2344                         if (x == -1 || x > max_flesh_depth) x = max_flesh_depth;
2345
2346                         jsonObject* flesh_blob = NULL;
2347                         if ((flesh_blob = jsonObjectGetKey( order_hash, "flesh_fields" )) && x > 0) {
2348
2349                                 flesh_blob = jsonObjectClone( flesh_blob );
2350                                 jsonObject* flesh_fields = jsonObjectGetKey( flesh_blob, core_class );
2351
2352                                 osrfStringArray* link_fields = NULL;
2353
2354                                 if (flesh_fields) {
2355                                         if (flesh_fields->size == 1) {
2356                                                 char* _t = jsonObjectToSimpleString( jsonObjectGetIndex( flesh_fields, 0 ) );
2357                                                 if (!strcmp(_t,"*")) link_fields = osrfHashKeys( links );
2358                                                 free(_t);
2359                                         }
2360
2361                                         if (!link_fields) {
2362                                                 jsonObjectNode* _f;
2363                                                 link_fields = osrfNewStringArray(1);
2364                                                 jsonObjectIterator* _i = jsonNewObjectIterator( flesh_fields );
2365                                                 while ((_f = jsonObjectIteratorNext( _i ))) {
2366                                                         osrfStringArrayAdd( link_fields, jsonObjectToSimpleString( _f->item ) );
2367                                                 }
2368                                         }
2369                                 }
2370
2371                                 jsonObjectNode* cur;
2372                                 jsonObjectIterator* itr = jsonNewObjectIterator( res_list );
2373                                 while ((cur = jsonObjectIteratorNext( itr ))) {
2374
2375                                         int i = 0;
2376                                         char* link_field;
2377                                         
2378                                         while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
2379
2380                                                 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
2381
2382                                                 osrfHash* kid_link = osrfHashGet(links, link_field);
2383                                                 if (!kid_link) continue;
2384
2385                                                 osrfHash* field = osrfHashGet(fields, link_field);
2386                                                 if (!field) continue;
2387
2388                                                 osrfHash* value_field = field;
2389
2390                                                 osrfHash* kid_idl = osrfHashGet(oilsIDL(), osrfHashGet(kid_link, "class"));
2391                                                 if (!kid_idl) continue;
2392
2393                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
2394                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
2395                                                 }
2396                                                         
2397                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
2398                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
2399                                                 }
2400
2401                                                 osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
2402
2403                                                 if (link_map->size > 0) {
2404                                                         jsonObject* _kid_key = jsonParseString("[]");
2405                                                         jsonObjectPush(
2406                                                                 _kid_key,
2407                                                                 jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
2408                                                         );
2409
2410                                                         jsonObjectSetKey(
2411                                                                 flesh_blob,
2412                                                                 osrfHashGet(kid_link, "class"),
2413                                                                 _kid_key
2414                                                         );
2415                                                 };
2416
2417                                                 osrfLogDebug(
2418                                                         OSRF_LOG_MARK,
2419                                                         "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
2420                                                         osrfHashGet(kid_link, "field"),
2421                                                         osrfHashGet(kid_link, "class"),
2422                                                         osrfHashGet(kid_link, "key"),
2423                                                         osrfHashGet(kid_link, "reltype")
2424                                                 );
2425
2426                                                 jsonObject* fake_params = jsonParseString("[]");
2427                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // search hash
2428                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // order/flesh hash
2429
2430                                                 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
2431
2432                                                 char* search_key =
2433                                                 jsonObjectToSimpleString(
2434                                                         jsonObjectGetIndex(
2435                                                                 cur->item,
2436                                                                 atoi( osrfHashGet(value_field, "array_position") )
2437                                                         )
2438                                                 );
2439
2440                                                 if (!search_key) {
2441                                                         osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
2442                                                         continue;
2443                                                 }
2444                                                         
2445                                                 jsonObjectSetKey(
2446                                                         jsonObjectGetIndex(fake_params, 0),
2447                                                         osrfHashGet(kid_link, "key"),
2448                                                         jsonNewObject( search_key )
2449                                                 );
2450
2451                                                 free(search_key);
2452
2453
2454                                                 jsonObjectSetKey(
2455                                                         jsonObjectGetIndex(fake_params, 1),
2456                                                         "flesh",
2457                                                         jsonNewNumberObject( (double)(x - 1 + link_map->size) )
2458                                                 );
2459
2460                                                 if (flesh_blob)
2461                                                         jsonObjectSetKey( jsonObjectGetIndex(fake_params, 1), "flesh_fields", jsonObjectClone(flesh_blob) );
2462
2463                                                 if (jsonObjectGetKey(order_hash, "order_by")) {
2464                                                         jsonObjectSetKey(
2465                                                                 jsonObjectGetIndex(fake_params, 1),
2466                                                                 "order_by",
2467                                                                 jsonObjectClone(jsonObjectGetKey(order_hash, "order_by"))
2468                                                         );
2469                                                 }
2470
2471                                                 if (jsonObjectGetKey(order_hash, "select")) {
2472                                                         jsonObjectSetKey(
2473                                                                 jsonObjectGetIndex(fake_params, 1),
2474                                                                 "select",
2475                                                                 jsonObjectClone(jsonObjectGetKey(order_hash, "select"))
2476                                                         );
2477                                                 }
2478
2479                                                 jsonObject* kids = doFieldmapperSearch(ctx, kid_idl, fake_params, err);
2480
2481                                                 if(*err) {
2482                                                         jsonObjectFree( fake_params );
2483                                                         osrfStringArrayFree(link_fields);
2484                                                         jsonObjectIteratorFree(itr);
2485                                                         jsonObjectFree(res_list);
2486                                                         return jsonNULL;
2487                                                 }
2488
2489                                                 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
2490
2491                                                 jsonObject* X = NULL;
2492                                                 if ( link_map->size > 0 && kids->size > 0 ) {
2493                                                         X = kids;
2494                                                         kids = jsonParseString("[]");
2495
2496                                                         jsonObjectNode* _k_node;
2497                                                         jsonObjectIterator* _k = jsonNewObjectIterator( X );
2498                                                         while ((_k_node = jsonObjectIteratorNext( _k ))) {
2499                                                                 jsonObjectPush(
2500                                                                         kids,
2501                                                                         jsonObjectClone(
2502                                                                                 jsonObjectGetIndex(
2503                                                                                         _k_node->item,
2504                                                                                         (unsigned long)atoi(
2505                                                                                                 osrfHashGet(
2506                                                                                                         osrfHashGet(
2507                                                                                                                 osrfHashGet(
2508                                                                                                                         osrfHashGet(
2509                                                                                                                                 oilsIDL(),
2510                                                                                                                                 osrfHashGet(kid_link, "class")
2511                                                                                                                         ),
2512                                                                                                                         "fields"
2513                                                                                                                 ),
2514                                                                                                                 osrfStringArrayGetString( link_map, 0 )
2515                                                                                                         ),
2516                                                                                                         "array_position"
2517                                                                                                 )
2518                                                                                         )
2519                                                                                 )
2520                                                                         )
2521                                                                 );
2522                                                         }
2523                                                 }
2524
2525                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" )) || !(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) {
2526                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
2527                                                         jsonObjectSetIndex(
2528                                                                 cur->item,
2529                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
2530                                                                 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
2531                                                         );
2532                                                 }
2533
2534                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
2535                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
2536                                                         jsonObjectSetIndex(
2537                                                                 cur->item,
2538                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
2539                                                                 jsonObjectClone( kids )
2540                                                         );
2541                                                 }
2542
2543                                                 if (X) {
2544                                                         jsonObjectFree(kids);
2545                                                         kids = X;
2546                                                 }
2547
2548                                                 jsonObjectFree( kids );
2549                                                 jsonObjectFree( fake_params );
2550
2551                                                 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
2552                                                 osrfLogDebug(OSRF_LOG_MARK, "%s", jsonObjectToJSON(cur->item));
2553
2554                                         }
2555                                 }
2556                                 jsonObjectFree( flesh_blob );
2557                                 osrfStringArrayFree(link_fields);
2558                                 jsonObjectIteratorFree(itr);
2559                         }
2560                 }
2561         }
2562
2563         return res_list;
2564 }
2565
2566
2567 jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
2568
2569         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
2570         jsonObject* target = jsonObjectGetIndex(ctx->params, 0);
2571
2572         if (!verifyObjectClass(ctx, target)) {
2573                 *err = -1;
2574                 return jsonNULL;
2575         }
2576
2577         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
2578                 osrfAppSessionStatus(
2579                         ctx->session,
2580                         OSRF_STATUS_BADREQUEST,
2581                         "osrfMethodException",
2582                         ctx->request,
2583                         "No active transaction -- required for UPDATE"
2584                 );
2585                 *err = -1;
2586                 return jsonNULL;
2587         }
2588
2589         dbhandle = writehandle;
2590
2591         char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
2592
2593         // Set the last_xact_id
2594         osrfHash* last_xact_id;
2595         if ((last_xact_id = oilsIDLFindPath("/%s/fields/last_xact_id", target->classname))) {
2596                 int index = atoi( osrfHashGet(last_xact_id, "array_position") );
2597                 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
2598                 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
2599         }       
2600
2601         char* pkey = osrfHashGet(meta, "primarykey");
2602         osrfHash* fields = osrfHashGet(meta, "fields");
2603
2604         char* id =
2605                 jsonObjectToSimpleString(
2606                         jsonObjectGetIndex(
2607                                 target,
2608                                 atoi( osrfHashGet( osrfHashGet( fields, pkey ), "array_position" ) )
2609                         )
2610                 );
2611
2612         osrfLogDebug(
2613                 OSRF_LOG_MARK,
2614                 "%s updating %s object with %s = %s",
2615                 MODULENAME,
2616                 osrfHashGet(meta, "fieldmapper"),
2617                 pkey,
2618                 id
2619         );
2620
2621         growing_buffer* sql = buffer_init(128);
2622         buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
2623
2624         int i = 0;
2625         int first = 1;
2626         char* field_name;
2627         osrfStringArray* field_list = osrfHashKeys( fields );
2628         while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
2629
2630                 osrfHash* field = osrfHashGet( fields, field_name );
2631
2632                 if(!( strcmp( field_name, pkey ) )) continue;
2633                 if(!( strcmp( osrfHashGet(osrfHashGet(fields,field_name), "virtual"), "true" ) )) continue;
2634
2635                 jsonObject* field_object = jsonObjectGetIndex( target, atoi(osrfHashGet(field, "array_position")) );
2636
2637                 char* value;
2638                 if (field_object && field_object->classname) {
2639                         value = jsonObjectToSimpleString(
2640                                         jsonObjectGetIndex(
2641                                                 field_object,
2642                                                 atoi(
2643                                                         osrfHashGet(
2644                                                                 osrfHashGet(
2645                                                                         oilsIDLFindPath("/%s/fields", field_object->classname),
2646                                                                         (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
2647                                                                 ),
2648                                                                 "array_position"
2649                                                         )
2650                                                 )
2651                                         )
2652                                 );
2653
2654                 } else {
2655                         value = jsonObjectToSimpleString( field_object );
2656                 }
2657
2658                 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
2659
2660                 if (!field_object || field_object->type == JSON_NULL) {
2661                         if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
2662                                 if (first) first = 0;
2663                                 else buffer_add(sql, ",");
2664                                 buffer_fadd( sql, " %s = NULL", field_name );
2665                         }
2666                         
2667                 } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
2668                         if (first) first = 0;
2669                         else buffer_add(sql, ",");
2670
2671                         if ( !strncmp(osrfHashGet(field, "datatype"), "INT", (size_t)3) ) {
2672                                 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
2673                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
2674                                 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
2675                         }
2676
2677                         osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, osrfHashGet(field, "datatype"));
2678
2679                 } else {
2680                         if ( dbi_conn_quote_string(dbhandle, &value) ) {
2681                                 if (first) first = 0;
2682                                 else buffer_add(sql, ",");
2683                                 buffer_fadd( sql, " %s = %s", field_name, value );
2684
2685                         } else {
2686                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
2687                                 osrfAppSessionStatus(
2688                                         ctx->session,
2689                                         OSRF_STATUS_INTERNALSERVERERROR,
2690                                         "osrfMethodException",
2691                                         ctx->request,
2692                                         "Error quoting string -- please see the error log for more details"
2693                                 );
2694                                 free(value);
2695                                 free(id);
2696                                 buffer_free(sql);
2697                                 *err = -1;
2698                                 return jsonNULL;
2699                         }
2700                 }
2701
2702                 free(value);
2703                 
2704         }
2705
2706         jsonObject* obj = jsonParseString(id);
2707
2708         if ( strcmp( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "primitive" ), "number" ) )
2709                 dbi_conn_quote_string(dbhandle, &id);
2710
2711         buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
2712
2713         char* query = buffer_data(sql);
2714         buffer_free(sql);
2715
2716         osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
2717
2718         dbi_result result = dbi_conn_query(dbhandle, query);
2719         free(query);
2720
2721         if (!result) {
2722                 jsonObjectFree(obj);
2723                 obj = jsonNewObject(NULL);
2724                 osrfLogError(
2725                         OSRF_LOG_MARK,
2726                         "%s ERROR updating %s object with %s = %s",
2727                         MODULENAME,
2728                         osrfHashGet(meta, "fieldmapper"),
2729                         pkey,
2730                         id
2731                 );
2732         }
2733
2734         free(id);
2735
2736         return obj;
2737 }
2738
2739 jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
2740
2741         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
2742
2743         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
2744                 osrfAppSessionStatus(
2745                         ctx->session,
2746                         OSRF_STATUS_BADREQUEST,
2747                         "osrfMethodException",
2748                         ctx->request,
2749                         "No active transaction -- required for DELETE"
2750                 );
2751                 *err = -1;
2752                 return jsonNULL;
2753         }
2754
2755         dbhandle = writehandle;
2756
2757         jsonObject* obj;
2758
2759         char* pkey = osrfHashGet(meta, "primarykey");
2760
2761         char* id;
2762         if (jsonObjectGetIndex(ctx->params, 0)->classname) {
2763                 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, 0 ))) {
2764                         *err = -1;
2765                         return jsonNULL;
2766                 }
2767
2768                 id = jsonObjectToSimpleString(
2769                         jsonObjectGetIndex(
2770                                 jsonObjectGetIndex(ctx->params, 0),
2771                                 atoi( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "array_position") )
2772                         )
2773                 );
2774         } else {
2775                 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
2776         }
2777
2778         osrfLogDebug(
2779                 OSRF_LOG_MARK,
2780                 "%s deleting %s object with %s = %s",
2781                 MODULENAME,
2782                 osrfHashGet(meta, "fieldmapper"),
2783                 pkey,
2784                 id
2785         );
2786
2787         obj = jsonParseString(id);
2788
2789         if ( strcmp( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "primitive" ), "number" ) )
2790                 dbi_conn_quote_string(writehandle, &id);
2791
2792         dbi_result result = dbi_conn_queryf(writehandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
2793
2794         if (!result) {
2795                 jsonObjectFree(obj);
2796                 obj = jsonNewObject(NULL);
2797                 osrfLogError(
2798                         OSRF_LOG_MARK,
2799                         "%s ERROR deleting %s object with %s = %s",
2800                         MODULENAME,
2801                         osrfHashGet(meta, "fieldmapper"),
2802                         pkey,
2803                         id
2804                 );
2805         }
2806
2807         free(id);
2808
2809         return obj;
2810
2811 }
2812
2813
2814 jsonObject* oilsMakeFieldmapperFromResult( dbi_result result, osrfHash* meta) {
2815         if(!(result && meta)) return jsonNULL;
2816
2817         jsonObject* object = jsonParseString("[]");
2818         jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
2819
2820         osrfHash* fields = osrfHashGet(meta, "fields");
2821
2822         osrfLogInternal(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
2823
2824         osrfHash* _f;
2825         time_t _tmp_dt;
2826         char dt_string[256];
2827         struct tm gmdt;
2828
2829         int fmIndex;
2830         int columnIndex = 1;
2831         int attr;
2832         unsigned short type;
2833         const char* columnName;
2834
2835         /* cycle through the column list */
2836         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
2837
2838                 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
2839
2840                 fmIndex = -1; // reset the position
2841                 
2842                 /* determine the field type and storage attributes */
2843                 type = dbi_result_get_field_type(result, columnName);
2844                 attr = dbi_result_get_field_attribs(result, columnName);
2845
2846                 /* fetch the fieldmapper index */
2847                 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
2848                         char* virt = (char*)osrfHashGet(_f, "virtual");
2849                         char* pos = (char*)osrfHashGet(_f, "array_position");
2850
2851                         if ( !virt || !pos || !(strcmp( virt, "true" )) ) continue;
2852
2853                         fmIndex = atoi( pos );
2854                         osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
2855                 } else {
2856                         continue;
2857                 }
2858
2859                 if (dbi_result_field_is_null(result, columnName)) {
2860                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
2861                 } else {
2862
2863                         switch( type ) {
2864
2865                                 case DBI_TYPE_INTEGER :
2866
2867                                         if( attr & DBI_INTEGER_SIZE8 ) 
2868                                                 jsonObjectSetIndex( object, fmIndex, 
2869                                                         jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
2870                                         else 
2871                                                 jsonObjectSetIndex( object, fmIndex, 
2872                                                         jsonNewNumberObject(dbi_result_get_long(result, columnName)));
2873
2874                                         break;
2875
2876                                 case DBI_TYPE_DECIMAL :
2877                                         jsonObjectSetIndex( object, fmIndex, 
2878                                                         jsonNewNumberObject(dbi_result_get_double(result, columnName)));
2879                                         break;
2880
2881                                 case DBI_TYPE_STRING :
2882
2883
2884                                         jsonObjectSetIndex(
2885                                                 object,
2886                                                 fmIndex,
2887                                                 jsonNewObject( dbi_result_get_string(result, columnName) )
2888                                         );
2889
2890                                         break;
2891
2892                                 case DBI_TYPE_DATETIME :
2893
2894                                         memset(dt_string, '\0', 256);
2895                                         memset(&gmdt, '\0', sizeof(gmdt));
2896                                         memset(&_tmp_dt, '\0', sizeof(_tmp_dt));
2897
2898                                         _tmp_dt = dbi_result_get_datetime(result, columnName);
2899
2900                                         localtime_r( &_tmp_dt, &gmdt );
2901
2902                                         if (!(attr & DBI_DATETIME_DATE)) {
2903                                                 strftime(dt_string, 255, "%T", &gmdt);
2904                                         } else if (!(attr & DBI_DATETIME_TIME)) {
2905                                                 strftime(dt_string, 255, "%F", &gmdt);
2906                                         } else {
2907                                                 strftime(dt_string, 255, "%FT%T%z", &gmdt);
2908                                         }
2909
2910                                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
2911
2912                                         break;
2913
2914                                 case DBI_TYPE_BINARY :
2915                                         osrfLogError( OSRF_LOG_MARK, 
2916                                                 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
2917                         }
2918                 }
2919         }
2920
2921         return object;
2922 }
2923 jsonObject* oilsMakeJSONFromResult( dbi_result result ) {
2924         if(!result) return jsonNULL;
2925
2926         jsonObject* object = jsonParseString("{}");
2927
2928         time_t _tmp_dt;
2929         char dt_string[256];
2930         struct tm gmdt;
2931
2932         int fmIndex;
2933         int columnIndex = 1;
2934         int attr;
2935         unsigned short type;
2936         const char* columnName;
2937
2938         /* cycle through the column list */
2939         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
2940
2941                 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
2942
2943                 fmIndex = -1; // reset the position
2944                 
2945                 /* determine the field type and storage attributes */
2946                 type = dbi_result_get_field_type(result, columnName);
2947                 attr = dbi_result_get_field_attribs(result, columnName);
2948
2949                 if (dbi_result_field_is_null(result, columnName)) {
2950                         jsonObjectSetKey( object, columnName, jsonNewObject(NULL) );
2951                 } else {
2952
2953                         switch( type ) {
2954
2955                                 case DBI_TYPE_INTEGER :
2956
2957                                         if( attr & DBI_INTEGER_SIZE8 ) 
2958                                                 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_longlong(result, columnName)) );
2959                                         else 
2960                                                 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_long(result, columnName)) );
2961                                         break;
2962
2963                                 case DBI_TYPE_DECIMAL :
2964                                         jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_double(result, columnName)) );
2965                                         break;
2966
2967                                 case DBI_TYPE_STRING :
2968                                         jsonObjectSetKey( object, columnName, jsonNewObject(dbi_result_get_string(result, columnName)) );
2969                                         break;
2970
2971                                 case DBI_TYPE_DATETIME :
2972
2973                                         memset(dt_string, '\0', 256);
2974                                         memset(&gmdt, '\0', sizeof(gmdt));
2975                                         memset(&_tmp_dt, '\0', sizeof(_tmp_dt));
2976
2977                                         _tmp_dt = dbi_result_get_datetime(result, columnName);
2978
2979                                         localtime_r( &_tmp_dt, &gmdt );
2980
2981                                         if (!(attr & DBI_DATETIME_DATE)) {
2982                                                 strftime(dt_string, 255, "%T", &gmdt);
2983                                         } else if (!(attr & DBI_DATETIME_TIME)) {
2984                                                 strftime(dt_string, 255, "%F", &gmdt);
2985                                         } else {
2986                                                 strftime(dt_string, 255, "%FT%T%z", &gmdt);
2987                                         }
2988
2989                                         jsonObjectSetKey( object, columnName, jsonNewObject(dt_string) );
2990                                         break;
2991
2992                                 case DBI_TYPE_BINARY :
2993                                         osrfLogError( OSRF_LOG_MARK, 
2994                                                 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
2995                         }
2996                 }
2997         }
2998
2999         return object;
3000 }
3001