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