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