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