]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/c-apps/oils_cstore.c
bugfixes and adding table aliases to where clause
[Evergreen.git] / Open-ILS / src / c-apps / oils_cstore.c
1 #include "opensrf/osrf_application.h"
2 #include "opensrf/osrf_settings.h"
3 #include "opensrf/utils.h"
4 #include "objson/object.h"
5 #include "opensrf/log.h"
6 #include "oils_utils.h"
7 #include "oils_constants.h"
8 #include "oils_event.h"
9 #include "oils_idl.h"
10 #include <dbi/dbi.h>
11
12 #include <time.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16 #ifdef RSTORE
17 #  define MODULENAME "open-ils.reporter-store"
18 #else
19 #  define MODULENAME "open-ils.cstore"
20 #endif
21
22 #define PERSIST_NS "http://open-ils.org/spec/opensrf/IDL/persistance/v1"
23 #define OBJECT_NS "http://open-ils.org/spec/opensrf/IDL/objects/v1"
24 #define BASE_NS "http://opensrf.org/spec/IDL/base/v1"
25
26 int osrfAppChildInit();
27 int osrfAppInitialize();
28
29 int verifyObjectClass ( osrfMethodContext*, jsonObject* );
30
31 int beginTransaction ( osrfMethodContext* );
32 int commitTransaction ( osrfMethodContext* );
33 int rollbackTransaction ( osrfMethodContext* );
34
35 int setSavepoint ( osrfMethodContext* );
36 int releaseSavepoint ( osrfMethodContext* );
37 int rollbackSavepoint ( osrfMethodContext* );
38
39 int dispatchCRUDMethod ( osrfMethodContext* );
40 jsonObject* doCreate ( osrfMethodContext*, int* );
41 jsonObject* doRetrieve ( osrfMethodContext*, int* );
42 jsonObject* doUpdate ( osrfMethodContext*, int* );
43 jsonObject* doDelete ( osrfMethodContext*, int* );
44 jsonObject* doSearch ( osrfMethodContext*, osrfHash*, jsonObject*, int* );
45 jsonObject* oilsMakeJSONFromResult( dbi_result, osrfHash* );
46
47 char* searchWriteSimplePredicate ( const char*, osrfHash*, const char*, const char*, const char* );
48 char* searchSimplePredicate ( const char*, const char*, osrfHash*, jsonObject* );
49 char* searchFunctionPredicate ( const char*, osrfHash*, jsonObjectNode* );
50 char* searchFieldTransform (const char*, osrfHash*, jsonObject*);
51 char* searchFieldTransformPredicate ( const char*, osrfHash*, jsonObjectNode* );
52 char* searchBETWEENPredicate ( const char*, osrfHash*, jsonObject* );
53 char* searchINPredicate ( const char*, osrfHash*, jsonObject*, const char* );
54 char* searchPredicate ( const char*, osrfHash*, jsonObject* );
55 char* searchJOIN ( jsonObject*, osrfHash* );
56 char* searchWHERE ( jsonObject*, osrfHash*, int );
57 char* buildSELECT ( jsonObject*, jsonObject*, osrfHash*, osrfMethodContext* );
58
59 void userDataFree( void* );
60 void sessionDataFree( char*, void* );
61
62 dbi_conn writehandle; /* our MASTER db connection */
63 dbi_conn dbhandle; /* our CURRENT db connection */
64 osrfHash readHandles;
65 jsonObject* jsonNULL = NULL; // 
66
67
68 /* parse and store the IDL here */
69 osrfHash* idl;
70
71 int osrfAppInitialize() {
72         growing_buffer* method_name;
73
74         osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
75         osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
76
77         char* idl_filename = osrf_settings_host_value("/apps/%s/app_settings/IDL", MODULENAME);
78         osrfLogInfo(OSRF_LOG_MARK, "Found file:");
79         osrfLogInfo(OSRF_LOG_MARK, idl_filename);
80
81         idl = oilsIDLInit( idl_filename );
82
83         if (!idl) {
84                 osrfLogError(OSRF_LOG_MARK, "Problem loading the IDL.  Seacrest out!");
85                 exit(1);
86         }
87
88         // first we register all the transaction and savepoint methods
89         method_name =  buffer_init(64);
90         buffer_fadd(method_name, "%s.transaction.begin", MODULENAME);
91         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "beginTransaction", "", 0, 0 );
92
93         buffer_reset(method_name);
94         buffer_fadd(method_name, "%s.transaction.commit", MODULENAME);
95         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "commitTransaction", "", 0, 0 );
96
97         buffer_reset(method_name);
98         buffer_fadd(method_name, "%s.transaction.rollback", MODULENAME);
99         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "rollbackTransaction", "", 0, 0 );
100
101
102         buffer_reset(method_name);
103         buffer_fadd(method_name, "%s.savepoint.set", MODULENAME);
104         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "setSavepoint", "", 1, 0 );
105
106         buffer_reset(method_name);
107         buffer_fadd(method_name, "%s.savepoint.release", MODULENAME);
108         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "releaseSavepoint", "", 1, 0 );
109
110         buffer_reset(method_name);
111         buffer_fadd(method_name, "%s.savepoint.rollback", MODULENAME);
112         osrfAppRegisterMethod( MODULENAME, buffer_data(method_name), "rollbackSavepoint", "", 1, 0 );
113
114         osrfStringArray* global_methods = osrfNewStringArray(6);
115
116         osrfStringArrayAdd( global_methods, "create" );
117         osrfStringArrayAdd( global_methods, "retrieve" );
118         osrfStringArrayAdd( global_methods, "update" );
119         osrfStringArrayAdd( global_methods, "delete" );
120         osrfStringArrayAdd( global_methods, "search" );
121         osrfStringArrayAdd( global_methods, "id_list" );
122
123         int c_index = 0; 
124         char* classname;
125         osrfStringArray* classes = osrfHashKeys( idl );
126         osrfLogDebug(OSRF_LOG_MARK, "%d classes loaded", classes->size );
127         osrfLogDebug(OSRF_LOG_MARK, "At least %d methods will be generated", classes->size * global_methods->size);
128         
129         while ( (classname = osrfStringArrayGetString(classes, c_index++)) ) {
130                 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
131                 
132                 osrfHash* idlClass = osrfHashGet(idl, classname);
133
134                 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), MODULENAME )) {
135                         osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on", MODULENAME, classname);
136                         continue;
137                 }
138
139                 char* virt = osrfHashGet(idlClass, "virtual");
140                 if (virt && !strcmp( virt, "true")) {
141                         osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
142                         continue;
143                 }
144
145                 int i = 0; 
146                 char* method_type;
147                 char* st_tmp;
148                 char* _fm;
149                 char* part;
150                 osrfHash* method_meta;
151                 while ( (method_type = osrfStringArrayGetString(global_methods, i++)) ) {
152                         osrfLogDebug(OSRF_LOG_MARK, "Using files to build %s class methods for %s", method_type, classname);
153
154                         if (!osrfHashGet(idlClass, "fieldmapper")) continue;
155
156                         method_meta = osrfNewHash();
157                         osrfHashSet(method_meta, idlClass, "class");
158
159                         _fm = strdup( (char*)osrfHashGet(idlClass, "fieldmapper") );
160                         part = strtok_r(_fm, ":", &st_tmp);
161
162                         growing_buffer* method_name =  buffer_init(64);
163                         buffer_fadd(method_name, "%s.direct.%s", MODULENAME, part);
164
165                         while ((part = strtok_r(NULL, ":", &st_tmp))) {
166                                 buffer_fadd(method_name, ".%s", part);
167                         }
168                         buffer_fadd(method_name, ".%s", method_type);
169
170
171                         char* method = buffer_data(method_name);
172                         buffer_free(method_name);
173                         free(_fm);
174
175                         osrfHashSet( method_meta, method, "methodname" );
176                         osrfHashSet( method_meta, method_type, "methodtype" );
177
178                         int flags = 0;
179                         if (!(strcmp( method_type, "search" )) || !(strcmp( method_type, "id_list" ))) {
180                                 flags = flags | OSRF_METHOD_STREAMING;
181                         }
182
183                         osrfAppRegisterExtendedMethod(
184                                 MODULENAME,
185                                 method,
186                                 "dispatchCRUDMethod",
187                                 "",
188                                 1,
189                                 flags,
190                                 (void*)method_meta
191                         );
192                 }
193         }
194
195         return 0;
196 }
197
198 /**
199  * Connects to the database 
200  */
201 int osrfAppChildInit() {
202
203         osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
204         dbi_initialize(NULL);
205         osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
206
207         char* driver    = osrf_settings_host_value("/apps/%s/app_settings/driver", MODULENAME);
208         char* user      = osrf_settings_host_value("/apps/%s/app_settings/database/user", MODULENAME);
209         char* host      = osrf_settings_host_value("/apps/%s/app_settings/database/host", MODULENAME);
210         char* port      = osrf_settings_host_value("/apps/%s/app_settings/database/port", MODULENAME);
211         char* db        = osrf_settings_host_value("/apps/%s/app_settings/database/db", MODULENAME);
212         char* pw        = osrf_settings_host_value("/apps/%s/app_settings/database/pw", MODULENAME);
213
214         osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
215         writehandle = dbi_conn_new(driver);
216
217         if(!writehandle) {
218                 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
219                 return -1;
220         }
221         osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
222
223         osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database.  host=%s, "
224                 "port=%s, user=%s, pw=%s, db=%s", MODULENAME, host, port, user, pw, db );
225
226         if(host) dbi_conn_set_option(writehandle, "host", host );
227         if(port) dbi_conn_set_option_numeric( writehandle, "port", atoi(port) );
228         if(user) dbi_conn_set_option(writehandle, "username", user);
229         if(pw) dbi_conn_set_option(writehandle, "password", pw );
230         if(db) dbi_conn_set_option(writehandle, "dbname", db );
231
232         free(user);
233         free(host);
234         free(port);
235         free(db);
236         free(pw);
237
238         const char* err;
239         if (dbi_conn_connect(writehandle) < 0) {
240                 dbi_conn_error(writehandle, &err);
241                 osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
242                 return -1;
243         }
244
245         osrfLogInfo(OSRF_LOG_MARK, "%s successfully connected to the database", MODULENAME);
246
247         int attr;
248         unsigned short type;
249         int i = 0; 
250         char* classname;
251         osrfStringArray* classes = osrfHashKeys( idl );
252         
253         while ( (classname = osrfStringArrayGetString(classes, i++)) ) {
254                 osrfHash* class = osrfHashGet( idl, classname );
255                 osrfHash* fields = osrfHashGet( class, "fields" );
256
257                 char* virt = osrfHashGet(class, "virtual");
258                 if (virt && !strcmp( virt, "true")) {
259                         osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
260                         continue;
261                 }
262
263         
264                 growing_buffer* sql_buf = buffer_init(32);
265                 buffer_fadd( sql_buf, "SELECT * FROM %s WHERE 1=0;", osrfHashGet(class, "tablename") );
266
267                 char* sql = buffer_data(sql_buf);
268                 buffer_free(sql_buf);
269                 osrfLogDebug(OSRF_LOG_MARK, "%s Investigatory SQL = %s", MODULENAME, sql);
270
271                 dbi_result result = dbi_conn_query(writehandle, sql);
272                 free(sql);
273
274                 if (result) {
275
276                         int columnIndex = 1;
277                         const char* columnName;
278                         osrfHash* _f;
279                         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
280
281                                 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
282
283                                 /* fetch the fieldmapper index */
284                                 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
285
286                                         osrfLogDebug(OSRF_LOG_MARK, "Found [%s] in IDL hash...", (char*)columnName);
287
288                                         /* determine the field type and storage attributes */
289                                         type = dbi_result_get_field_type(result, columnName);
290                                         attr = dbi_result_get_field_attribs(result, columnName);
291
292                                         switch( type ) {
293
294                                                 case DBI_TYPE_INTEGER :
295
296                                                         if ( !osrfHashGet(_f, "primitive") )
297                                                                 osrfHashSet(_f,"number", "primitive");
298
299                                                         if( attr & DBI_INTEGER_SIZE8 ) 
300                                                                 osrfHashSet(_f,"INT8", "datatype");
301                                                         else 
302                                                                 osrfHashSet(_f,"INT", "datatype");
303                                                         break;
304
305                                                 case DBI_TYPE_DECIMAL :
306                                                         if ( !osrfHashGet(_f, "primitive") )
307                                                                 osrfHashSet(_f,"number", "primitive");
308
309                                                         osrfHashSet(_f,"NUMERIC", "datatype");
310                                                         break;
311
312                                                 case DBI_TYPE_STRING :
313                                                         if ( !osrfHashGet(_f, "primitive") )
314                                                                 osrfHashSet(_f,"string", "primitive");
315                                                         osrfHashSet(_f,"TEXT", "datatype");
316                                                         break;
317
318                                                 case DBI_TYPE_DATETIME :
319                                                         if ( !osrfHashGet(_f, "primitive") )
320                                                                 osrfHashSet(_f,"string", "primitive");
321
322                                                         osrfHashSet(_f,"TIMESTAMP", "datatype");
323                                                         break;
324
325                                                 case DBI_TYPE_BINARY :
326                                                         if ( !osrfHashGet(_f, "primitive") )
327                                                                 osrfHashSet(_f,"string", "primitive");
328
329                                                         osrfHashSet(_f,"BYTEA", "datatype");
330                                         }
331
332                                         osrfLogDebug(
333                                                 OSRF_LOG_MARK,
334                                                 "Setting [%s] to primitive [%s] and datatype [%s]...",
335                                                 (char*)columnName,
336                                                 osrfHashGet(_f, "primitive"),
337                                                 osrfHashGet(_f, "datatype")
338                                         );
339                                 }
340                         }
341                         dbi_result_free(result);
342                 } else {
343                         osrfLogDebug(OSRF_LOG_MARK, "No data found for class [%s]...", (char*)classname);
344                 }
345         }
346
347         osrfStringArrayFree(classes);
348
349         return 0;
350 }
351
352 void userDataFree( void* blob ) {
353         osrfHashFree( (osrfHash*)blob );
354         return;
355 }
356
357 void sessionDataFree( char* key, void* item ) {
358         if (!(strcmp(key,"xact_id"))) {
359                 if (writehandle)
360                         dbi_conn_query(writehandle, "ROLLBACK;");
361                 free(item);
362         }
363
364         return;
365 }
366
367 int beginTransaction ( osrfMethodContext* ctx ) {
368         OSRF_METHOD_VERIFY_CONTEXT(ctx);
369
370         dbi_result result = dbi_conn_query(writehandle, "START TRANSACTION;");
371         if (!result) {
372                 osrfLogError(OSRF_LOG_MARK, "%s: Error starting transaction", MODULENAME );
373                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error starting transaction" );
374                 return -1;
375         } else {
376                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
377                 osrfAppRespondComplete( ctx, ret );
378                 jsonObjectFree(ret);
379                 
380                 if (!ctx->session->userData) {
381                         ctx->session->userData = osrfNewHash();
382                         ((osrfHash*)ctx->session->userData)->freeItem = &sessionDataFree;
383                 }
384
385                 osrfHashSet( (osrfHash*)ctx->session->userData, strdup( ctx->session->session_id ), "xact_id" );
386                 ctx->session->userDataFree = &userDataFree;
387                 
388         }
389         return 0;
390 }
391
392 int setSavepoint ( osrfMethodContext* ctx ) {
393         OSRF_METHOD_VERIFY_CONTEXT(ctx);
394
395         char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
396
397         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
398                 osrfAppSessionStatus(
399                         ctx->session,
400                         OSRF_STATUS_INTERNALSERVERERROR,
401                         "osrfMethodException",
402                         ctx->request,
403                         "No active transaction -- required for savepoints"
404                 );
405                 return -1;
406         }
407
408         dbi_result result = dbi_conn_queryf(writehandle, "SAVEPOINT \"%s\";", spName);
409         if (!result) {
410                 osrfLogError(
411                         OSRF_LOG_MARK,
412                         "%s: Error creating savepoint %s in transaction %s",
413                         MODULENAME,
414                         spName,
415                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
416                 );
417                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error creating savepoint" );
418                 return -1;
419         } else {
420                 jsonObject* ret = jsonNewObject(spName);
421                 osrfAppRespondComplete( ctx, ret );
422                 jsonObjectFree(ret);
423         }
424         return 0;
425 }
426
427 int releaseSavepoint ( osrfMethodContext* ctx ) {
428         OSRF_METHOD_VERIFY_CONTEXT(ctx);
429
430         char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
431
432         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
433                 osrfAppSessionStatus(
434                         ctx->session,
435                         OSRF_STATUS_INTERNALSERVERERROR,
436                         "osrfMethodException",
437                         ctx->request,
438                         "No active transaction -- required for savepoints"
439                 );
440                 return -1;
441         }
442
443         dbi_result result = dbi_conn_queryf(writehandle, "RELEASE SAVEPOINT \"%s\";", spName);
444         if (!result) {
445                 osrfLogError(
446                         OSRF_LOG_MARK,
447                         "%s: Error releasing savepoint %s in transaction %s",
448                         MODULENAME,
449                         spName,
450                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
451                 );
452                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error releasing savepoint" );
453                 return -1;
454         } else {
455                 jsonObject* ret = jsonNewObject(spName);
456                 osrfAppRespondComplete( ctx, ret );
457                 jsonObjectFree(ret);
458         }
459         return 0;
460 }
461
462 int rollbackSavepoint ( osrfMethodContext* ctx ) {
463         OSRF_METHOD_VERIFY_CONTEXT(ctx);
464
465         char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
466
467         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
468                 osrfAppSessionStatus(
469                         ctx->session,
470                         OSRF_STATUS_INTERNALSERVERERROR,
471                         "osrfMethodException",
472                         ctx->request,
473                         "No active transaction -- required for savepoints"
474                 );
475                 return -1;
476         }
477
478         dbi_result result = dbi_conn_queryf(writehandle, "ROLLBACK TO SAVEPOINT \"%s\";", spName);
479         if (!result) {
480                 osrfLogError(
481                         OSRF_LOG_MARK,
482                         "%s: Error rolling back savepoint %s in transaction %s",
483                         MODULENAME,
484                         spName,
485                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
486                 );
487                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error rolling back savepoint" );
488                 return -1;
489         } else {
490                 jsonObject* ret = jsonNewObject(spName);
491                 osrfAppRespondComplete( ctx, ret );
492                 jsonObjectFree(ret);
493         }
494         return 0;
495 }
496
497 int commitTransaction ( osrfMethodContext* ctx ) {
498         OSRF_METHOD_VERIFY_CONTEXT(ctx);
499
500         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
501                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to commit" );
502                 return -1;
503         }
504
505         dbi_result result = dbi_conn_query(writehandle, "COMMIT;");
506         if (!result) {
507                 osrfLogError(OSRF_LOG_MARK, "%s: Error committing transaction", MODULENAME );
508                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error committing transaction" );
509                 return -1;
510         } else {
511                 osrfHashRemove(ctx->session->userData, "xact_id");
512                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
513                 osrfAppRespondComplete( ctx, ret );
514                 jsonObjectFree(ret);
515         }
516         return 0;
517 }
518
519 int rollbackTransaction ( osrfMethodContext* ctx ) {
520         OSRF_METHOD_VERIFY_CONTEXT(ctx);
521
522         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
523                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to roll back" );
524                 return -1;
525         }
526
527         dbi_result result = dbi_conn_query(writehandle, "ROLLBACK;");
528         if (!result) {
529                 osrfLogError(OSRF_LOG_MARK, "%s: Error rolling back transaction", MODULENAME );
530                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error rolling back transaction" );
531                 return -1;
532         } else {
533                 osrfHashRemove(ctx->session->userData, "xact_id");
534                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
535                 osrfAppRespondComplete( ctx, ret );
536                 jsonObjectFree(ret);
537         }
538         return 0;
539 }
540
541 int dispatchCRUDMethod ( osrfMethodContext* ctx ) {
542         OSRF_METHOD_VERIFY_CONTEXT(ctx);
543
544         osrfHash* meta = (osrfHash*) ctx->method->userData;
545         osrfHash* class_obj = osrfHashGet( meta, "class" );
546         
547         int err = 0;
548
549         jsonObject * obj = NULL;
550         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "create"))
551                 obj = doCreate(ctx, &err);
552
553         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "retrieve"))
554                 obj = doRetrieve(ctx, &err);
555
556         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "update"))
557                 obj = doUpdate(ctx, &err);
558
559         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "delete"))
560                 obj = doDelete(ctx, &err);
561
562         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "search")) {
563
564                 obj = doSearch(ctx, class_obj, ctx->params, &err);
565                 if(err) return err;
566
567                 jsonObjectNode* cur;
568                 jsonObjectIterator* itr = jsonNewObjectIterator( obj );
569                 while ((cur = jsonObjectIteratorNext( itr ))) {
570                         osrfAppRespond( ctx, jsonObjectClone(cur->item) );
571                 }
572                 jsonObjectIteratorFree(itr);
573                 osrfAppRespondComplete( ctx, NULL );
574
575         } else if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "id_list")) {
576
577                 jsonObject* _p = jsonObjectClone( ctx->params );
578                 if (jsonObjectGetIndex( _p, 1 )) {
579                         jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "flesh" );
580                         jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "flesh_columns" );
581                 } else {
582                         jsonObjectSetIndex( _p, 1, jsonParseString("{}") );
583                 }
584
585                 growing_buffer* sel_list = buffer_init(16);
586                 buffer_fadd(sel_list, "{ \"%s\":[\"%s\"] }", osrfHashGet( class_obj, "classname" ), osrfHashGet( class_obj, "primarykey" ));
587                 char* _s = buffer_data(sel_list);
588                 buffer_free(sel_list);
589
590                 jsonObjectSetKey( jsonObjectGetIndex( _p, 1 ), "select", jsonParseString(_s) );
591                 osrfLogDebug(OSRF_LOG_MARK, "%s: Select qualifer set to [%s]", MODULENAME, _s);
592                 free(_s);
593
594                 obj = doSearch(ctx, class_obj, _p, &err);
595                 if(err) return err;
596
597                 jsonObjectNode* cur;
598                 jsonObjectIterator* itr = jsonNewObjectIterator( obj );
599                 while ((cur = jsonObjectIteratorNext( itr ))) {
600                         osrfAppRespond(
601                                 ctx,
602                                 jsonObjectClone(
603                                         jsonObjectGetIndex(
604                                                 cur->item,
605                                                 atoi(
606                                                         osrfHashGet(
607                                                                 osrfHashGet(
608                                                                         osrfHashGet( class_obj, "fields" ),
609                                                                         osrfHashGet( class_obj, "primarykey")
610                                                                 ),
611                                                                 "array_position"
612                                                         )
613                                                 )
614                                         )
615                                 )
616                         );
617                 }
618                 jsonObjectIteratorFree(itr);
619                 osrfAppRespondComplete( ctx, NULL );
620                 
621         } else {
622                 osrfAppRespondComplete( ctx, obj );
623         }
624
625         jsonObjectFree(obj);
626
627         return err;
628 }
629
630 int verifyObjectClass ( osrfMethodContext* ctx, jsonObject* param ) {
631         
632         osrfHash* meta = (osrfHash*) ctx->method->userData;
633         osrfHash* class = osrfHashGet( meta, "class" );
634         
635         if ((strcmp( osrfHashGet(class, "classname"), param->classname ))) {
636
637                 growing_buffer* msg = buffer_init(128);
638                 buffer_fadd(
639                         msg,
640                         "%s: %s method for type %s was passed a %s",
641                         MODULENAME,
642                         osrfHashGet(meta, "methodtype"),
643                         osrfHashGet(class, "classname"),
644                         param->classname
645                 );
646
647                 char* m = buffer_data(msg);
648                 osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException", ctx->request, m );
649
650                 buffer_free(msg);
651                 free(m);
652
653                 return 0;
654         }
655         return 1;
656 }
657
658 jsonObject* doCreate(osrfMethodContext* ctx, int* err ) {
659
660         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
661         jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
662         jsonObject* options = jsonObjectGetIndex( ctx->params, 1 );
663
664         if (!verifyObjectClass(ctx, target)) {
665                 *err = -1;
666                 return jsonNULL;
667         }
668
669         osrfLogDebug( OSRF_LOG_MARK, "Object seems to be of the correct type" );
670
671         if (!ctx->session || !ctx->session->userData || !osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
672                 osrfLogError( OSRF_LOG_MARK, "No active transaction -- required for CREATE" );
673
674                 osrfAppSessionStatus(
675                         ctx->session,
676                         OSRF_STATUS_BADREQUEST,
677                         "osrfMethodException",
678                         ctx->request,
679                         "No active transaction -- required for CREATE"
680                 );
681                 *err = -1;
682                 return jsonNULL;
683         }
684
685         char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
686
687         // Set the last_xact_id
688         osrfHash* last_xact_id;
689         if ((last_xact_id = oilsIDLFindPath("/%s/fields/last_xact_id", target->classname))) {
690                 int index = atoi( osrfHashGet(last_xact_id, "array_position") );
691                 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
692                 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
693         }       
694
695         osrfLogDebug( OSRF_LOG_MARK, "There is a transaction running..." );
696
697         dbhandle = writehandle;
698
699         osrfHash* fields = osrfHashGet(meta, "fields");
700         char* pkey = osrfHashGet(meta, "primarykey");
701         char* seq = osrfHashGet(meta, "sequence");
702
703         growing_buffer* table_buf = buffer_init(128);
704         growing_buffer* col_buf = buffer_init(128);
705         growing_buffer* val_buf = buffer_init(128);
706
707         buffer_fadd(table_buf,"INSERT INTO %s", osrfHashGet(meta, "tablename"));
708         buffer_add(col_buf,"(");
709         buffer_add(val_buf,"VALUES (");
710
711
712         int i = 0;
713         int first = 1;
714         char* field_name;
715         osrfStringArray* field_list = osrfHashKeys( fields );
716         while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
717
718                 osrfHash* field = osrfHashGet( fields, field_name );
719
720                 if(!( strcmp( osrfHashGet(osrfHashGet(fields,field_name), "virtual"), "true" ) )) continue;
721
722                 jsonObject* field_object = jsonObjectGetIndex( target, atoi(osrfHashGet(field, "array_position")) );
723
724                 char* value;
725                 if (field_object && field_object->classname) {
726                         value = jsonObjectToSimpleString(
727                                         jsonObjectGetIndex(
728                                                 field_object,
729                                                 atoi(
730                                                         osrfHashGet(
731                                                                 osrfHashGet(
732                                                                         oilsIDLFindPath("/%s/fields", field_object->classname),
733                                                                         (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
734                                                                 ),
735                                                                 "array_position"
736                                                         )
737                                                 )
738                                         )
739                                 );
740
741                 } else {
742                         value = jsonObjectToSimpleString( field_object );
743                 }
744
745
746                 if (first) {
747                         first = 0;
748                 } else {
749                         buffer_add(col_buf, ",");
750                         buffer_add(val_buf, ",");
751                 }
752
753                 buffer_add(col_buf, field_name);
754
755                 if (!field_object || field_object->type == JSON_NULL) {
756                         buffer_add( val_buf, "DEFAULT" );
757                         
758                 } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
759                         if ( !strcmp(osrfHashGet(field, "datatype"), "INT8") ) {
760                                 buffer_fadd( val_buf, "%lld", atol(value) );
761                                 
762                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "INT") ) {
763                                 buffer_fadd( val_buf, "%ld", atoll(value) );
764                                 
765                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
766                                 buffer_fadd( val_buf, "%f", atof(value) );
767                         }
768                 } else {
769                         if ( dbi_conn_quote_string(writehandle, &value) ) {
770                                 buffer_fadd( val_buf, "%s", value );
771
772                         } else {
773                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
774                                 osrfAppSessionStatus(
775                                         ctx->session,
776                                         OSRF_STATUS_INTERNALSERVERERROR,
777                                         "osrfMethodException",
778                                         ctx->request,
779                                         "Error quoting string -- please see the error log for more details"
780                                 );
781                                 free(value);
782                                 buffer_free(table_buf);
783                                 buffer_free(col_buf);
784                                 buffer_free(val_buf);
785                                 *err = -1;
786                                 return jsonNULL;
787                         }
788                 }
789
790                 free(value);
791                 
792         }
793
794
795         buffer_add(col_buf,")");
796         buffer_add(val_buf,")");
797
798         growing_buffer* sql = buffer_init(128);
799         buffer_fadd(
800                 sql,
801                 "%s %s %s;",
802                 buffer_data(table_buf),
803                 buffer_data(col_buf),
804                 buffer_data(val_buf)
805         );
806         buffer_free(table_buf);
807         buffer_free(col_buf);
808         buffer_free(val_buf);
809
810         char* query = buffer_data(sql);
811         buffer_free(sql);
812
813         osrfLogDebug(OSRF_LOG_MARK, "%s: Insert SQL [%s]", MODULENAME, query);
814
815         
816         dbi_result result = dbi_conn_query(writehandle, query);
817
818         jsonObject* obj = NULL;
819
820         if (!result) {
821                 obj = jsonNewObject(NULL);
822                 osrfLogError(
823                         OSRF_LOG_MARK,
824                         "%s ERROR inserting %s object using query [%s]",
825                         MODULENAME,
826                         osrfHashGet(meta, "fieldmapper"),
827                         query
828                 );
829                 osrfAppSessionStatus(
830                         ctx->session,
831                         OSRF_STATUS_INTERNALSERVERERROR,
832                         "osrfMethodException",
833                         ctx->request,
834                         "INSERT error -- please see the error log for more details"
835                 );
836                 *err = -1;
837         } else {
838
839                 int pos = atoi(osrfHashGet( osrfHashGet(fields, pkey), "array_position" ));
840                 char* id = jsonObjectToSimpleString(jsonObjectGetIndex(target, pos));
841                 if (!id) {
842                         unsigned long long new_id = dbi_conn_sequence_last(writehandle, seq);
843                         growing_buffer* _id = buffer_init(10);
844                         buffer_fadd(_id, "%lld", new_id);
845                         id = buffer_data(_id);
846                         buffer_free(_id);
847                 }
848
849                 if (    !options
850                         || !jsonObjectGetKey( options, "quiet")
851                         || strcmp( jsonObjectToSimpleString(jsonObjectGetKey( options, "quiet")), "true" )
852                 ) {
853
854                         jsonObject* fake_params = jsonParseString("[]");
855                         jsonObjectPush(fake_params, jsonParseString("{}"));
856
857                         jsonObjectSetKey(
858                                 jsonObjectGetIndex(fake_params, 0),
859                                 osrfHashGet(meta, "primarykey"),
860                                 jsonNewObject(id)
861                         );
862
863                         jsonObject* list = doSearch( ctx,meta, fake_params, err);
864
865                         if(*err) {
866                                 jsonObjectFree( fake_params );
867                                 obj = jsonNULL;
868                         } else {
869                                 obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
870                         }
871
872                         jsonObjectFree( list );
873                         jsonObjectFree( fake_params );
874
875                 } else {
876                         obj = jsonNewObject(id);
877                 }
878
879         }
880
881         free(query);
882
883         return obj;
884
885 }
886
887
888 jsonObject* doRetrieve(osrfMethodContext* ctx, int* err ) {
889
890         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
891
892         jsonObject* obj;
893
894         char* id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
895         jsonObject* order_hash = jsonObjectGetIndex(ctx->params, 1);
896
897         osrfLogDebug(
898                 OSRF_LOG_MARK,
899                 "%s retrieving %s object with id %s",
900                 MODULENAME,
901                 osrfHashGet(meta, "fieldmapper"),
902                 id
903         );
904
905         jsonObject* fake_params = jsonParseString("[]");
906         jsonObjectPush(fake_params, jsonParseString("{}"));
907
908         jsonObjectSetKey(
909                 jsonObjectGetIndex(fake_params, 0),
910                 osrfHashGet(meta, "primarykey"),
911                 jsonParseString(id)
912         );
913
914         if (order_hash) jsonObjectPush(fake_params, jsonObjectClone(order_hash) );
915
916         jsonObject* list = doSearch( ctx,meta, fake_params, err);
917
918         if(*err) {
919                 jsonObjectFree( fake_params );
920                 return jsonNULL;
921         }
922
923         obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
924
925         jsonObjectFree( list );
926         jsonObjectFree( fake_params );
927
928         return obj;
929 }
930
931 char* jsonNumberToDBString ( osrfHash* field, jsonObject* value ) {
932         growing_buffer* val_buf = buffer_init(32);
933
934         if ( !strncmp(osrfHashGet(field, "datatype"), "INT", 3) ) {
935                 if (value->type == JSON_NUMBER) buffer_fadd( val_buf, "%ld", (long)jsonObjectGetNumber(value) );
936                 else buffer_fadd( val_buf, "%ld", atol(jsonObjectToSimpleString(value)) );
937
938         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
939                 if (value->type == JSON_NUMBER) buffer_fadd( val_buf, "%f",  jsonObjectGetNumber(value) );
940                 else buffer_fadd( val_buf, "%f", atof(jsonObjectToSimpleString(value)) );
941         }
942
943         char* pred = buffer_data(val_buf);
944         buffer_free(val_buf);
945
946         return pred;
947 }
948
949 char* searchINPredicate (const char* class, osrfHash* field, jsonObject* node, const char* op) {
950         growing_buffer* sql_buf = buffer_init(32);
951         
952         buffer_fadd(
953                 sql_buf,
954                 "\"%s\".%s ",
955                 class,
956                 osrfHashGet(field, "name")
957         );
958
959         if (!op) {
960                 buffer_add(sql_buf, "IN (");
961         } else if (!(strcasecmp(op,"not in"))) {
962                 buffer_add(sql_buf, "NOT IN (");
963         } else {
964                 buffer_add(sql_buf, "IN (");
965         }
966
967         int in_item_index = 0;
968         int in_item_first = 1;
969         jsonObject* in_item;
970         while ( (in_item = jsonObjectGetIndex(node, in_item_index++)) ) {
971
972                 if (in_item_first)
973                         in_item_first = 0;
974                 else
975                         buffer_add(sql_buf, ", ");
976
977                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
978                         char* val = jsonNumberToDBString( field, in_item );
979                         buffer_fadd( sql_buf, "%s", val );
980                         free(val);
981
982                 } else {
983                         char* key_string = jsonObjectToSimpleString(in_item);
984                         if ( dbi_conn_quote_string(dbhandle, &key_string) ) {
985                                 buffer_fadd( sql_buf, "%s", key_string );
986                                 free(key_string);
987                         } else {
988                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string);
989                                 free(key_string);
990                                 buffer_free(sql_buf);
991                                 return NULL;
992                         }
993                 }
994         }
995
996         buffer_add(
997                 sql_buf,
998                 ")"
999         );
1000
1001         char* pred = buffer_data(sql_buf);
1002         buffer_free(sql_buf);
1003
1004         return pred;
1005 }
1006
1007 char* searchValueTransform( jsonObject* array ) {
1008         growing_buffer* sql_buf = buffer_init(32);
1009
1010         char* val = NULL;
1011         int func_item_index = 0;
1012         int func_item_first = 2;
1013         jsonObject* func_item;
1014         while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1015
1016                 val = jsonObjectToSimpleString(func_item);
1017
1018                 if (func_item_first == 2) {
1019                         buffer_fadd(sql_buf, "%s( ", val);
1020                         free(val);
1021                         func_item_first--;
1022                         continue;
1023                 }
1024
1025                 if (func_item_first)
1026                         func_item_first--;
1027                 else
1028                         buffer_add(sql_buf, ", ");
1029
1030                 if ( dbi_conn_quote_string(dbhandle, &val) ) {
1031                         buffer_fadd( sql_buf, "%s", val );
1032                         free(val);
1033                 } else {
1034                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1035                         free(val);
1036                         buffer_free(sql_buf);
1037                         return NULL;
1038                 }
1039         }
1040
1041         buffer_add(
1042                 sql_buf,
1043                 " )"
1044         );
1045
1046         char* pred = buffer_data(sql_buf);
1047         buffer_free(sql_buf);
1048
1049         return pred;
1050 }
1051
1052 char* searchFunctionPredicate (const char* class, osrfHash* field, jsonObjectNode* node) {
1053         growing_buffer* sql_buf = buffer_init(32);
1054
1055         char* val = searchValueTransform(node->item);
1056         
1057         buffer_fadd(
1058                 sql_buf,
1059                 "\"%s\".%s %s %s",
1060                 class,
1061                 osrfHashGet(field, "name"),
1062                 node->key,
1063                 val
1064         );
1065
1066         char* pred = buffer_data(sql_buf);
1067         buffer_free(sql_buf);
1068         free(val);
1069
1070         return pred;
1071 }
1072
1073 char* searchFieldTransform (const char* class, osrfHash* field, jsonObject* node) {
1074         growing_buffer* sql_buf = buffer_init(32);
1075         
1076         char* field_transform = jsonObjectToSimpleString( jsonObjectGetKey( node, "transform" ) );
1077
1078         buffer_fadd(
1079                 sql_buf,
1080                 "%s(\"%s\".%s)",
1081                 class,
1082                 field_transform,
1083                 osrfHashGet(field, "name")
1084         );
1085
1086
1087
1088         char* pred = buffer_data(sql_buf);
1089         buffer_free(sql_buf);
1090         free(field_transform);
1091
1092         return pred;
1093 }
1094
1095 char* searchFieldTransformPredicate (const char* class, osrfHash* field, jsonObjectNode* node) {
1096         growing_buffer* sql_buf = buffer_init(32);
1097         
1098         char* field_transform = searchFieldTransform( class, field, node->item );
1099         char* value = NULL;
1100
1101         if (jsonObjectGetKey( node->item, "value" )->type == JSON_ARRAY) {
1102                 value = searchValueTransform(jsonObjectGetKey( node->item, "value" ));
1103         } else if (jsonObjectGetKey( node->item, "value" )->type != JSON_NULL) {
1104                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1105                         value = jsonNumberToDBString( field, jsonObjectGetKey( node->item, "value" ) );
1106                 } else {
1107                         value = jsonObjectToSimpleString(jsonObjectGetKey( node->item, "value" ));
1108                         if ( !dbi_conn_quote_string(dbhandle, &value) ) {
1109                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, value);
1110                                 free(value);
1111                                 return NULL;
1112                         }
1113                 }
1114         }
1115
1116         buffer_fadd(
1117                 sql_buf,
1118                 "%s %s %s",
1119                 field_transform,
1120                 node->key,
1121                 value
1122         );
1123
1124         char* pred = buffer_data(sql_buf);
1125         buffer_free(sql_buf);
1126         free(field_transform);
1127
1128         return pred;
1129 }
1130
1131 char* searchSimplePredicate (const char* orig_op, const char* class, osrfHash* field, jsonObject* node) {
1132
1133         char* val = NULL;
1134
1135         if (node->type != JSON_NULL) {
1136                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1137                         val = jsonNumberToDBString( field, node );
1138                 } else {
1139                         val = jsonObjectToSimpleString(node);
1140                 }
1141         }
1142
1143         char* pred = searchWriteSimplePredicate( class, field, osrfHashGet(field, "name"), orig_op, val );
1144
1145         if (val) free(val);
1146
1147         return pred;
1148 }
1149
1150 char* searchWriteSimplePredicate ( const char* class, osrfHash* field, const char* left, const char* orig_op, const char* right ) {
1151
1152         char* val = NULL;
1153         char* op = NULL;
1154         if (right == NULL) {
1155                 val = strdup("NULL");
1156
1157                 if (strcmp( orig_op, "=" ))
1158                         op = strdup("IS NOT");
1159                 else
1160                         op = strdup("IS");
1161
1162         } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1163                 val = strdup(right);
1164                 op = strdup(orig_op);
1165
1166         } else {
1167                 val = strdup(right);
1168                 if ( !dbi_conn_quote_string(dbhandle, &val) ) {
1169                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1170                         free(val);
1171                         return NULL;
1172                 }
1173                 op = strdup(orig_op);
1174         }
1175
1176         growing_buffer* sql_buf = buffer_init(16);
1177         buffer_fadd( sql_buf, "\"%s\".%s %s %s", class, left, op, val );
1178         free(val);
1179         free(op);
1180
1181         char* pred = buffer_data(sql_buf);
1182         buffer_free(sql_buf);
1183
1184         return pred;
1185
1186 }
1187
1188 char* searchBETWEENPredicate (const char* class, osrfHash* field, jsonObject* node) {
1189
1190         char* x_string;
1191         char* y_string;
1192
1193         if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1194                 x_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,0));
1195                 y_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,1));
1196
1197         } else {
1198                 x_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,0));
1199                 y_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,1));
1200                 if ( !(dbi_conn_quote_string(dbhandle, &x_string) && dbi_conn_quote_string(dbhandle, &y_string)) ) {
1201                         osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key strings [%s] and [%s]", MODULENAME, x_string, y_string);
1202                         free(x_string);
1203                         free(y_string);
1204                         return NULL;
1205                 }
1206         }
1207
1208         growing_buffer* sql_buf = buffer_init(32);
1209         buffer_fadd( sql_buf, "%s BETWEEN %s AND %s", osrfHashGet(field, "name"), x_string, y_string );
1210         free(x_string);
1211         free(y_string);
1212
1213         char* pred = buffer_data(sql_buf);
1214         buffer_free(sql_buf);
1215
1216         return pred;
1217 }
1218
1219 char* searchPredicate ( const char* class, osrfHash* field, jsonObject* node ) {
1220
1221         char* pred = NULL;
1222         if (node->type == JSON_ARRAY) { // equality IN search
1223                 pred = searchINPredicate( class, field, node, NULL );
1224         } else if (node->type == JSON_HASH) { // non-equality search
1225                 jsonObjectNode* pred_node;
1226                 jsonObjectIterator* pred_itr = jsonNewObjectIterator( node );
1227                 while ( (pred_node = jsonObjectIteratorNext( pred_itr )) ) {
1228                         if ( !(strcasecmp( pred_node->key,"between" )) )
1229                                 pred = searchBETWEENPredicate( class, field, pred_node->item );
1230                         else if ( !(strcasecmp( pred_node->key,"in" )) || !(strcasecmp( pred_node->key,"not in" )) )
1231                                 pred = searchINPredicate( class, field, pred_node->item, pred_node->key );
1232                         else if ( pred_node->item->type == JSON_ARRAY )
1233                                 pred = searchFunctionPredicate( class, field, pred_node );
1234                         else if ( pred_node->item->type == JSON_HASH )
1235                                 pred = searchFieldTransformPredicate( class, field, pred_node );
1236                         else 
1237                                 pred = searchSimplePredicate( pred_node->key, class, field, pred_node->item );
1238
1239                         break;
1240                 }
1241         } else if (node->type == JSON_NULL) { // IS NULL search
1242                 growing_buffer* _p = buffer_init(16);
1243                 buffer_fadd(
1244                         _p,
1245                         "\"%s\".%s IS NULL",
1246                         class,
1247                         osrfHashGet(field, "name")
1248                 );
1249                 pred = buffer_data(_p);
1250                 buffer_free(_p);
1251         } else { // equality search
1252                 pred = searchSimplePredicate( "=", class, field, node );
1253         }
1254
1255         return pred;
1256
1257 }
1258
1259
1260 /*
1261
1262 join_filter : {
1263         acn : {
1264                 field : record,
1265                 fkey : id
1266                 type : left
1267                 filter : { ... },
1268                 join_filter : {
1269                         acp : {
1270                                 field : call_number,
1271                                 fkey : id,
1272                                 filter : { ... },
1273                         },
1274                 },
1275         },
1276         mrd : {
1277                 field : record,
1278                 type : inner
1279                 fkey : id,
1280                 filter : { ... },
1281         }
1282 }
1283
1284 */
1285
1286 char* searchJOIN ( jsonObject* join_hash, osrfHash* leftmeta ) {
1287
1288         char* leftclass = osrfHashGet(leftmeta, "classname");
1289
1290         growing_buffer* join_buf = buffer_init(128);
1291
1292         jsonObjectNode* snode = NULL;
1293         jsonObjectIterator* search_itr = jsonNewObjectIterator( join_hash );
1294         while ( (snode = jsonObjectIteratorNext( search_itr )) ) {
1295                 osrfHash* idlClass = osrfHashGet( idl, snode->key );
1296
1297                 char* class = osrfHashGet(idlClass, "classname");
1298                 char* table = osrfHashGet(idlClass, "tablename");
1299
1300                 char* type = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "type" ) );
1301                 char* fkey = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "fkey" ) );
1302                 char* field = jsonObjectToSimpleString( jsonObjectGetKey( snode->item, "field" ) );
1303
1304                 jsonObject* filter = jsonObjectGetKey( snode->item, "filter" );
1305                 jsonObject* join_filter = jsonObjectGetKey( snode->item, "join_filter" );
1306
1307                 if (type) {
1308                         if ( !strcasecmp(type,"left") ) {
1309                                 buffer_add(join_buf, " LEFT JOIN");
1310                         } else if ( !strcasecmp(type,"right") ) {
1311                                 buffer_add(join_buf, " RIGHT JOIN");
1312                         } else if ( !strcasecmp(type,"full") ) {
1313                                 buffer_add(join_buf, " FULL JOIN");
1314                         } else {
1315                                 buffer_add(join_buf, " INNER JOIN");
1316                         }
1317                 } else {
1318                         buffer_add(join_buf, " INNER JOIN");
1319                 }
1320
1321                 buffer_fadd(join_buf, " %s AS \"%s\" ON ( \"%s\".%s = \"%s\".%s", table, class, class, field, leftclass, fkey);
1322
1323                 if (filter) {
1324                         char* jpred = searchWHERE( filter, idlClass, 0 );
1325                         buffer_fadd( join_buf, " AND %s", jpred );
1326                         free(jpred);
1327                 }
1328
1329                 buffer_add(join_buf, " ) ");
1330                 
1331                 if (join_filter) {
1332                         char* jpred = searchJOIN( join_filter, idlClass );
1333                         buffer_fadd( join_buf, " %s", jpred );
1334                         free(jpred);
1335                 }
1336
1337         }
1338
1339         char* join = buffer_data(join_buf);
1340         buffer_free(join_buf);
1341
1342         return join;
1343 }
1344
1345 /*
1346
1347 { -or|-and : { field : { op : value }, ... }, ... }
1348
1349 */
1350 char* searchWHERE ( jsonObject* search_hash, osrfHash* meta, int opjoin_type ) {
1351
1352         osrfHash* fields = osrfHashGet(meta, "fields");
1353         char* class = osrfHashGet(meta, "classname");
1354         growing_buffer* sql_buf = buffer_init(128);
1355
1356         jsonObjectNode* node = NULL;
1357
1358         int first = 1;
1359         jsonObjectIterator* search_itr = jsonNewObjectIterator( search_hash );
1360         while ( (node = jsonObjectIteratorNext( search_itr )) ) {
1361
1362                 if (first) {
1363                         first = 0;
1364                 } else {
1365                         if (opjoin_type == 1) buffer_add(sql_buf, " OR ");
1366                         else buffer_add(sql_buf, " AND ");
1367                 }
1368
1369                 if ( !strcasecmp("-or",node->key) ) {
1370                         char* subpred = searchWHERE( node->item, meta, 1);
1371                         buffer_fadd(sql_buf, "( %s )", subpred);
1372                         free(subpred);
1373                 } else if ( !strcasecmp("-and",node->key) ) {
1374                         char* subpred = searchWHERE( node->item, meta, 0);
1375                         buffer_fadd(sql_buf, "( %s )", subpred);
1376                         free(subpred);
1377                 } else {
1378
1379                         osrfHash* field = osrfHashGet( fields, node->key );
1380
1381                         if (!field) {
1382                                 osrfLogError(
1383                                         OSRF_LOG_MARK,
1384                                         "%s: Attempt to reference non-existant column %s on table %s",
1385                                         MODULENAME,
1386                                         node->key,
1387                                         osrfHashGet(meta, "tablename")
1388                                 );
1389                                 buffer_free(sql_buf);
1390                                 return NULL;
1391                         }
1392
1393                         char* subpred = searchPredicate( class, field, node->item );
1394                         buffer_fadd( sql_buf, "%s", subpred );
1395                         free(subpred);
1396                 }
1397         }
1398
1399         jsonObjectIteratorFree(search_itr);
1400
1401         char* pred = buffer_data(sql_buf);
1402         buffer_free(sql_buf);
1403
1404         return pred;
1405 }
1406
1407 char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
1408
1409         char* core_class = osrfHashGet(meta, "classname");
1410         char* pkey = osrfHashGet(meta, "primarykey");
1411         jsonObject* join_hash = jsonObjectGetKey( order_hash, "join_filter" );
1412
1413         jsonObjectNode* node = NULL;
1414         jsonObjectNode* snode = NULL;
1415         jsonObject* _tmp;
1416
1417         growing_buffer* sql_buf = buffer_init(128);
1418
1419         buffer_fadd(sql_buf, "SELECT DISTINCT ON (\"%s\".%s)", core_class, pkey);
1420
1421         int first = 1;
1422         if ( (_tmp = jsonObjectGetKey( order_hash, "select" )) ) {
1423
1424                 jsonObjectIterator* class_itr = jsonNewObjectIterator( _tmp );
1425                 while ( (snode = jsonObjectIteratorNext( class_itr )) ) {
1426
1427                         osrfHash* idlClass = osrfHashGet( idl, snode->key );
1428                         if (!idlClass) continue;
1429                         char* cname = osrfHashGet(idlClass, "classname");
1430
1431                         if (strcmp(core_class,snode->key)) continue;
1432
1433                         jsonObjectIterator* select_itr = jsonNewObjectIterator( snode->item );
1434                         while ( (node = jsonObjectIteratorNext( select_itr )) ) {
1435                                 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), jsonObjectToSimpleString(node->item) );
1436                                 char* fname = osrfHashGet(field, "name");
1437
1438                                 if (!field) continue;
1439
1440                                 if (first) {
1441                                         first = 0;
1442                                 } else {
1443                                         buffer_add(sql_buf, ",");
1444                                 }
1445
1446                                 buffer_fadd(sql_buf, " \"%s\".%s", cname, fname);
1447                         }
1448                 }
1449
1450                 if (first) buffer_add(sql_buf, " *");
1451
1452         } else {
1453                 buffer_add(sql_buf, " *");
1454         }
1455
1456         buffer_fadd(sql_buf, " FROM %s AS \"%s\"", osrfHashGet(meta, "tablename"), core_class );
1457
1458         if ( join_hash ) {
1459                 char* join_clause = searchJOIN( join_hash, meta );
1460                 buffer_fadd(sql_buf, " %s", join_clause);
1461                 free(join_clause);
1462         }
1463
1464         buffer_fadd(sql_buf, " WHERE ", osrfHashGet(meta, "tablename"), core_class );
1465
1466         char* pred = searchWHERE( search_hash, meta, 0 );
1467         if (!pred) {
1468                 osrfAppSessionStatus(
1469                         ctx->session,
1470                         OSRF_STATUS_INTERNALSERVERERROR,
1471                                 "osrfMethodException",
1472                                 ctx->request,
1473                                 "Severe query error -- see error log for more details"
1474                         );
1475                 buffer_free(sql_buf);
1476                 return NULL;
1477         } else {
1478                 buffer_add(sql_buf, pred);
1479                 free(pred);
1480         }
1481
1482         if (order_hash) {
1483                 char* string;
1484                 if ( (_tmp = jsonObjectGetKey( jsonObjectGetKey( order_hash, "order_by" ), core_class ) ) ){
1485                         string = jsonObjectToSimpleString(_tmp);
1486                         buffer_fadd(
1487                                 sql_buf,
1488                                 " ORDER BY %s",
1489                                 string
1490                         );
1491                         free(string);
1492                 }
1493
1494                 if ( (_tmp = jsonObjectGetKey( order_hash, "limit" )) ){
1495                         string = jsonObjectToSimpleString(_tmp);
1496                         buffer_fadd(
1497                                 sql_buf,
1498                                 " LIMIT %d",
1499                                 atoi(string)
1500                         );
1501                         free(string);
1502                 }
1503
1504                 _tmp = jsonObjectGetKey( order_hash, "offset" );
1505                 if (_tmp) {
1506                         string = jsonObjectToSimpleString(_tmp);
1507                         buffer_fadd(
1508                                 sql_buf,
1509                                 " OFFSET %d",
1510                                 atoi(string)
1511                         );
1512                         free(string);
1513                 }
1514         }
1515
1516         buffer_add(sql_buf, ";");
1517
1518         char* sql = buffer_data(sql_buf);
1519         buffer_free(sql_buf);
1520
1521         return sql;
1522 }
1523
1524 jsonObject* doSearch ( osrfMethodContext* ctx, osrfHash* meta, jsonObject* params, int* err ) {
1525
1526         // XXX for now...
1527         dbhandle = writehandle;
1528
1529         osrfHash* links = osrfHashGet(meta, "links");
1530         osrfHash* fields = osrfHashGet(meta, "fields");
1531         char* core_class = osrfHashGet(meta, "classname");
1532
1533         jsonObject* _tmp;
1534         jsonObject* obj;
1535         jsonObject* search_hash = jsonObjectGetIndex(params, 0);
1536         jsonObject* order_hash = jsonObjectGetIndex(params, 1);
1537
1538         char* sql = buildSELECT( search_hash, order_hash, meta, ctx );
1539         if (!sql) {
1540                 *err = -1;
1541                 return NULL;
1542         }
1543         
1544         osrfLogDebug(OSRF_LOG_MARK, "%s SQL =  %s", MODULENAME, sql);
1545         dbi_result result = dbi_conn_query(dbhandle, sql);
1546
1547         jsonObject* res_list = jsonParseString("[]");
1548         if(result) {
1549                 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
1550
1551                 if (dbi_result_first_row(result)) {
1552                         /* JSONify the result */
1553                         osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
1554                         do {
1555                                 obj = oilsMakeJSONFromResult( result, meta );
1556                                 jsonObjectPush(res_list, obj);
1557                         } while (dbi_result_next_row(result));
1558                 } else {
1559                         osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
1560                 }
1561
1562                 /* clean up the query */
1563                 dbi_result_free(result); 
1564
1565         } else {
1566                 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]", MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
1567                 osrfAppSessionStatus(
1568                         ctx->session,
1569                         OSRF_STATUS_INTERNALSERVERERROR,
1570                         "osrfMethodException",
1571                         ctx->request,
1572                         "Severe query error -- see error log for more details"
1573                 );
1574                 *err = -1;
1575                 free(sql);
1576                 jsonObjectFree(res_list);
1577                 return jsonNULL;
1578
1579         }
1580
1581         free(sql);
1582
1583         if (res_list->size && order_hash) {
1584                 _tmp = jsonObjectGetKey( order_hash, "flesh" );
1585                 if (_tmp) {
1586                         int x = (int)jsonObjectGetNumber(_tmp);
1587
1588                         jsonObject* flesh_blob = NULL;
1589                         if ((flesh_blob = jsonObjectGetKey( order_hash, "flesh_fields" )) && x > 0) {
1590
1591                                 flesh_blob = jsonObjectClone( flesh_blob );
1592                                 jsonObject* flesh_fields = jsonObjectGetKey( flesh_blob, core_class );
1593
1594                                 osrfStringArray* link_fields = NULL;
1595
1596                                 if (flesh_fields) {
1597                                         if (flesh_fields->size == 1) {
1598                                                 char* _t = jsonObjectToSimpleString( jsonObjectGetIndex( flesh_fields, 0 ) );
1599                                                 if (!strcmp(_t,"*")) link_fields = osrfHashKeys( links );
1600                                                 free(_t);
1601                                         }
1602
1603                                         if (!link_fields) {
1604                                                 jsonObjectNode* _f;
1605                                                 link_fields = osrfNewStringArray(1);
1606                                                 jsonObjectIterator* _i = jsonNewObjectIterator( flesh_fields );
1607                                                 while ((_f = jsonObjectIteratorNext( _i ))) {
1608                                                         osrfStringArrayAdd( link_fields, jsonObjectToSimpleString( _f->item ) );
1609                                                 }
1610                                         }
1611                                 }
1612
1613                                 jsonObjectNode* cur;
1614                                 jsonObjectIterator* itr = jsonNewObjectIterator( res_list );
1615                                 while ((cur = jsonObjectIteratorNext( itr ))) {
1616
1617                                         int i = 0;
1618                                         char* link_field;
1619                                         
1620                                         while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
1621
1622                                                 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
1623
1624                                                 osrfHash* kid_link = osrfHashGet(links, link_field);
1625                                                 if (!kid_link) continue;
1626
1627                                                 osrfHash* field = osrfHashGet(fields, link_field);
1628                                                 if (!field) continue;
1629
1630                                                 osrfHash* value_field = field;
1631
1632                                                 osrfHash* kid_idl = osrfHashGet(idl, osrfHashGet(kid_link, "class"));
1633                                                 if (!kid_idl) continue;
1634
1635                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
1636                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
1637                                                 }
1638                                                         
1639                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
1640                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
1641                                                 }
1642
1643                                                 osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
1644
1645                                                 if (link_map->size > 0) {
1646                                                         jsonObject* _kid_key = jsonParseString("[]");
1647                                                         jsonObjectPush(
1648                                                                 _kid_key,
1649                                                                 jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
1650                                                         );
1651
1652                                                         jsonObjectSetKey(
1653                                                                 flesh_blob,
1654                                                                 osrfHashGet(kid_link, "class"),
1655                                                                 _kid_key
1656                                                         );
1657                                                 };
1658
1659                                                 osrfLogDebug(
1660                                                         OSRF_LOG_MARK,
1661                                                         "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
1662                                                         osrfHashGet(kid_link, "field"),
1663                                                         osrfHashGet(kid_link, "class"),
1664                                                         osrfHashGet(kid_link, "key"),
1665                                                         osrfHashGet(kid_link, "reltype")
1666                                                 );
1667
1668                                                 jsonObject* fake_params = jsonParseString("[]");
1669                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // search hash
1670                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // order/flesh hash
1671
1672                                                 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
1673
1674                                                 char* search_key =
1675                                                 jsonObjectToSimpleString(
1676                                                         jsonObjectGetIndex(
1677                                                                 cur->item,
1678                                                                 atoi( osrfHashGet(value_field, "array_position") )
1679                                                         )
1680                                                 );
1681
1682                                                 if (!search_key) {
1683                                                         osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
1684                                                         continue;
1685                                                 }
1686                                                         
1687                                                 jsonObjectSetKey(
1688                                                         jsonObjectGetIndex(fake_params, 0),
1689                                                         osrfHashGet(kid_link, "key"),
1690                                                         jsonNewObject( search_key )
1691                                                 );
1692
1693                                                 free(search_key);
1694
1695
1696                                                 jsonObjectSetKey(
1697                                                         jsonObjectGetIndex(fake_params, 1),
1698                                                         "flesh",
1699                                                         jsonNewNumberObject( (double)(x - 1 + link_map->size) )
1700                                                 );
1701
1702                                                 if (flesh_blob)
1703                                                         jsonObjectSetKey( jsonObjectGetIndex(fake_params, 1), "flesh_fields", jsonObjectClone(flesh_blob) );
1704
1705                                                 if (jsonObjectGetKey(order_hash, "order_by")) {
1706                                                         jsonObjectSetKey(
1707                                                                 jsonObjectGetIndex(fake_params, 1),
1708                                                                 "order_by",
1709                                                                 jsonObjectClone(jsonObjectGetKey(order_hash, "order_by"))
1710                                                         );
1711                                                 }
1712
1713                                                 if (jsonObjectGetKey(order_hash, "select")) {
1714                                                         jsonObjectSetKey(
1715                                                                 jsonObjectGetIndex(fake_params, 1),
1716                                                                 "select",
1717                                                                 jsonObjectClone(jsonObjectGetKey(order_hash, "select"))
1718                                                         );
1719                                                 }
1720
1721                                                 jsonObject* kids = doSearch(ctx, kid_idl, fake_params, err);
1722
1723                                                 if(*err) {
1724                                                         jsonObjectFree( fake_params );
1725                                                         osrfStringArrayFree(link_fields);
1726                                                         jsonObjectIteratorFree(itr);
1727                                                         jsonObjectFree(res_list);
1728                                                         return jsonNULL;
1729                                                 }
1730
1731                                                 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
1732
1733                                                 jsonObject* X = NULL;
1734                                                 if ( link_map->size > 0 && kids->size > 0 ) {
1735                                                         X = kids;
1736                                                         kids = jsonParseString("[]");
1737
1738                                                         jsonObjectNode* _k_node;
1739                                                         jsonObjectIterator* _k = jsonNewObjectIterator( X );
1740                                                         while ((_k_node = jsonObjectIteratorNext( _k ))) {
1741                                                                 jsonObjectPush(
1742                                                                         kids,
1743                                                                         jsonObjectClone(
1744                                                                                 jsonObjectGetIndex(
1745                                                                                         _k_node->item,
1746                                                                                         (unsigned long)atoi(
1747                                                                                                 osrfHashGet(
1748                                                                                                         osrfHashGet(
1749                                                                                                                 osrfHashGet(
1750                                                                                                                         osrfHashGet(
1751                                                                                                                                 idl,
1752                                                                                                                                 osrfHashGet(kid_link, "class")
1753                                                                                                                         ),
1754                                                                                                                         "fields"
1755                                                                                                                 ),
1756                                                                                                                 osrfStringArrayGetString( link_map, 0 )
1757                                                                                                         ),
1758                                                                                                         "array_position"
1759                                                                                                 )
1760                                                                                         )
1761                                                                                 )
1762                                                                         )
1763                                                                 );
1764                                                         }
1765                                                 }
1766
1767                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" )) || !(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) {
1768                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
1769                                                         jsonObjectSetIndex(
1770                                                                 cur->item,
1771                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
1772                                                                 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
1773                                                         );
1774                                                 }
1775
1776                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
1777                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
1778                                                         jsonObjectSetIndex(
1779                                                                 cur->item,
1780                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
1781                                                                 jsonObjectClone( kids )
1782                                                         );
1783                                                 }
1784
1785                                                 if (X) {
1786                                                         jsonObjectFree(kids);
1787                                                         kids = X;
1788                                                 }
1789
1790                                                 jsonObjectFree( kids );
1791                                                 jsonObjectFree( fake_params );
1792
1793                                                 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
1794                                                 osrfLogDebug(OSRF_LOG_MARK, "%s", jsonObjectToJSON(cur->item));
1795
1796                                         }
1797                                 }
1798                                 jsonObjectFree( flesh_blob );
1799                                 osrfStringArrayFree(link_fields);
1800                                 jsonObjectIteratorFree(itr);
1801                         }
1802                 }
1803         }
1804
1805         return res_list;
1806 }
1807
1808
1809 jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
1810
1811         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
1812         jsonObject* target = jsonObjectGetIndex(ctx->params, 0);
1813
1814         if (!verifyObjectClass(ctx, target)) {
1815                 *err = -1;
1816                 return jsonNULL;
1817         }
1818
1819         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
1820                 osrfAppSessionStatus(
1821                         ctx->session,
1822                         OSRF_STATUS_BADREQUEST,
1823                         "osrfMethodException",
1824                         ctx->request,
1825                         "No active transaction -- required for UPDATE"
1826                 );
1827                 *err = -1;
1828                 return jsonNULL;
1829         }
1830
1831         dbhandle = writehandle;
1832
1833         char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
1834
1835         // Set the last_xact_id
1836         osrfHash* last_xact_id;
1837         if ((last_xact_id = oilsIDLFindPath("/%s/fields/last_xact_id", target->classname))) {
1838                 int index = atoi( osrfHashGet(last_xact_id, "array_position") );
1839                 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
1840                 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
1841         }       
1842
1843         char* pkey = osrfHashGet(meta, "primarykey");
1844         osrfHash* fields = osrfHashGet(meta, "fields");
1845
1846         char* id =
1847                 jsonObjectToSimpleString(
1848                         jsonObjectGetIndex(
1849                                 target,
1850                                 atoi( osrfHashGet( osrfHashGet( fields, pkey ), "array_position" ) )
1851                         )
1852                 );
1853
1854         osrfLogDebug(
1855                 OSRF_LOG_MARK,
1856                 "%s updating %s object with %s = %s",
1857                 MODULENAME,
1858                 osrfHashGet(meta, "fieldmapper"),
1859                 pkey,
1860                 id
1861         );
1862
1863         growing_buffer* sql = buffer_init(128);
1864         buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
1865
1866         int i = 0;
1867         int first = 1;
1868         char* field_name;
1869         osrfStringArray* field_list = osrfHashKeys( fields );
1870         while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
1871
1872                 osrfHash* field = osrfHashGet( fields, field_name );
1873
1874                 if(!( strcmp( field_name, pkey ) )) continue;
1875                 if(!( strcmp( osrfHashGet(osrfHashGet(fields,field_name), "virtual"), "true" ) )) continue;
1876
1877                 jsonObject* field_object = jsonObjectGetIndex( target, atoi(osrfHashGet(field, "array_position")) );
1878
1879                 char* value;
1880                 if (field_object && field_object->classname) {
1881                         value = jsonObjectToSimpleString(
1882                                         jsonObjectGetIndex(
1883                                                 field_object,
1884                                                 atoi(
1885                                                         osrfHashGet(
1886                                                                 osrfHashGet(
1887                                                                         oilsIDLFindPath("/%s/fields", field_object->classname),
1888                                                                         (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
1889                                                                 ),
1890                                                                 "array_position"
1891                                                         )
1892                                                 )
1893                                         )
1894                                 );
1895
1896                 } else {
1897                         value = jsonObjectToSimpleString( field_object );
1898                 }
1899
1900                 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
1901
1902                 if (!field_object || field_object->type == JSON_NULL) {
1903                         if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
1904                                 if (first) first = 0;
1905                                 else buffer_add(sql, ",");
1906                                 buffer_fadd( sql, " %s = NULL", field_name );
1907                         }
1908                         
1909                 } else if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
1910                         if (first) first = 0;
1911                         else buffer_add(sql, ",");
1912
1913                         if ( !strncmp(osrfHashGet(field, "datatype"), "INT", (size_t)3) ) {
1914                                 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
1915                         } else if ( !strcmp(osrfHashGet(field, "datatype"), "NUMERIC") ) {
1916                                 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
1917                         }
1918
1919                         osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, osrfHashGet(field, "datatype"));
1920
1921                 } else {
1922                         if ( dbi_conn_quote_string(dbhandle, &value) ) {
1923                                 if (first) first = 0;
1924                                 else buffer_add(sql, ",");
1925                                 buffer_fadd( sql, " %s = %s", field_name, value );
1926
1927                         } else {
1928                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
1929                                 osrfAppSessionStatus(
1930                                         ctx->session,
1931                                         OSRF_STATUS_INTERNALSERVERERROR,
1932                                         "osrfMethodException",
1933                                         ctx->request,
1934                                         "Error quoting string -- please see the error log for more details"
1935                                 );
1936                                 free(value);
1937                                 free(id);
1938                                 buffer_free(sql);
1939                                 *err = -1;
1940                                 return jsonNULL;
1941                         }
1942                 }
1943
1944                 free(value);
1945                 
1946         }
1947
1948         jsonObject* obj = jsonParseString(id);
1949
1950         if ( strcmp( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "primitive" ), "number" ) )
1951                 dbi_conn_quote_string(dbhandle, &id);
1952
1953         buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
1954
1955         char* query = buffer_data(sql);
1956         buffer_free(sql);
1957
1958         osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
1959
1960         dbi_result result = dbi_conn_query(dbhandle, query);
1961         free(query);
1962
1963         if (!result) {
1964                 jsonObjectFree(obj);
1965                 obj = jsonNewObject(NULL);
1966                 osrfLogError(
1967                         OSRF_LOG_MARK,
1968                         "%s ERROR updating %s object with %s = %s",
1969                         MODULENAME,
1970                         osrfHashGet(meta, "fieldmapper"),
1971                         pkey,
1972                         id
1973                 );
1974         }
1975
1976         free(id);
1977
1978         return obj;
1979 }
1980
1981 jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
1982
1983         osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
1984
1985         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
1986                 osrfAppSessionStatus(
1987                         ctx->session,
1988                         OSRF_STATUS_BADREQUEST,
1989                         "osrfMethodException",
1990                         ctx->request,
1991                         "No active transaction -- required for DELETE"
1992                 );
1993                 *err = -1;
1994                 return jsonNULL;
1995         }
1996
1997         dbhandle = writehandle;
1998
1999         jsonObject* obj;
2000
2001         char* pkey = osrfHashGet(meta, "primarykey");
2002
2003         char* id;
2004         if (jsonObjectGetIndex(ctx->params, 0)->classname) {
2005                 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, 0 ))) {
2006                         *err = -1;
2007                         return jsonNULL;
2008                 }
2009
2010                 id = jsonObjectToSimpleString(
2011                         jsonObjectGetIndex(
2012                                 jsonObjectGetIndex(ctx->params, 0),
2013                                 atoi( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "array_position") )
2014                         )
2015                 );
2016         } else {
2017                 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
2018         }
2019
2020         osrfLogDebug(
2021                 OSRF_LOG_MARK,
2022                 "%s deleting %s object with %s = %s",
2023                 MODULENAME,
2024                 osrfHashGet(meta, "fieldmapper"),
2025                 pkey,
2026                 id
2027         );
2028
2029         obj = jsonParseString(id);
2030
2031         if ( strcmp( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "primitive" ), "number" ) )
2032                 dbi_conn_quote_string(writehandle, &id);
2033
2034         dbi_result result = dbi_conn_queryf(writehandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
2035
2036         if (!result) {
2037                 jsonObjectFree(obj);
2038                 obj = jsonNewObject(NULL);
2039                 osrfLogError(
2040                         OSRF_LOG_MARK,
2041                         "%s ERROR deleting %s object with %s = %s",
2042                         MODULENAME,
2043                         osrfHashGet(meta, "fieldmapper"),
2044                         pkey,
2045                         id
2046                 );
2047         }
2048
2049         free(id);
2050
2051         return obj;
2052
2053 }
2054
2055
2056 jsonObject* oilsMakeJSONFromResult( dbi_result result, osrfHash* meta) {
2057         if(!(result && meta)) return jsonNULL;
2058
2059         jsonObject* object = jsonParseString("[]");
2060         jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
2061
2062         osrfHash* fields = osrfHashGet(meta, "fields");
2063
2064         osrfLogInternal(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
2065
2066         osrfHash* _f;
2067         time_t _tmp_dt;
2068         char dt_string[256];
2069         struct tm gmdt;
2070
2071         int fmIndex;
2072         int columnIndex = 1;
2073         int attr;
2074         unsigned short type;
2075         const char* columnName;
2076
2077         /* cycle through the column list */
2078         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
2079
2080                 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
2081
2082                 fmIndex = -1; // reset the position
2083                 
2084                 /* determine the field type and storage attributes */
2085                 type = dbi_result_get_field_type(result, columnName);
2086                 attr = dbi_result_get_field_attribs(result, columnName);
2087
2088                 /* fetch the fieldmapper index */
2089                 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
2090                         char* virt = (char*)osrfHashGet(_f, "virtual");
2091                         char* pos = (char*)osrfHashGet(_f, "array_position");
2092
2093                         if ( !virt || !pos || !(strcmp( virt, "true" )) ) continue;
2094
2095                         fmIndex = atoi( pos );
2096                         osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
2097                 } else {
2098                         continue;
2099                 }
2100
2101                 if (dbi_result_field_is_null(result, columnName)) {
2102                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
2103                 } else {
2104
2105                         switch( type ) {
2106
2107                                 case DBI_TYPE_INTEGER :
2108
2109                                         if( attr & DBI_INTEGER_SIZE8 ) 
2110                                                 jsonObjectSetIndex( object, fmIndex, 
2111                                                         jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
2112                                         else 
2113                                                 jsonObjectSetIndex( object, fmIndex, 
2114                                                         jsonNewNumberObject(dbi_result_get_long(result, columnName)));
2115
2116                                         break;
2117
2118                                 case DBI_TYPE_DECIMAL :
2119                                         jsonObjectSetIndex( object, fmIndex, 
2120                                                         jsonNewNumberObject(dbi_result_get_double(result, columnName)));
2121                                         break;
2122
2123                                 case DBI_TYPE_STRING :
2124
2125
2126                                         jsonObjectSetIndex(
2127                                                 object,
2128                                                 fmIndex,
2129                                                 jsonNewObject( dbi_result_get_string(result, columnName) )
2130                                         );
2131
2132                                         break;
2133
2134                                 case DBI_TYPE_DATETIME :
2135
2136                                         memset(dt_string, '\0', 256);
2137                                         memset(&gmdt, '\0', sizeof(gmdt));
2138                                         memset(&_tmp_dt, '\0', sizeof(_tmp_dt));
2139
2140                                         _tmp_dt = dbi_result_get_datetime(result, columnName);
2141
2142                                         localtime_r( &_tmp_dt, &gmdt );
2143
2144                                         if (!(attr & DBI_DATETIME_DATE)) {
2145                                                 strftime(dt_string, 255, "%T", &gmdt);
2146                                         } else if (!(attr & DBI_DATETIME_TIME)) {
2147                                                 strftime(dt_string, 255, "%F", &gmdt);
2148                                         } else {
2149                                                 strftime(dt_string, 255, "%FT%T%z", &gmdt);
2150                                         }
2151
2152                                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
2153
2154                                         break;
2155
2156                                 case DBI_TYPE_BINARY :
2157                                         osrfLogError( OSRF_LOG_MARK, 
2158                                                 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
2159                         }
2160                 }
2161         }
2162
2163         return object;
2164 }
2165