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