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