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