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