]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/c-apps/oils_cstore.c
first cut at delete
[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 beginTransaction ( osrfMethodContext* );
31 int commitTransaction ( osrfMethodContext* );
32 int rollbackTransaction ( osrfMethodContext* );
33
34 int setSavepoint ( osrfMethodContext* );
35 int releaseSavepoint ( osrfMethodContext* );
36 int rollbackSavepoint ( osrfMethodContext* );
37
38 int dispatchCRUDMethod ( osrfMethodContext* );
39 jsonObject* doCreate ( osrfMethodContext*, osrfHash*, jsonObject* );
40 jsonObject* doRetrieve ( osrfMethodContext*, osrfHash*, jsonObject* );
41 jsonObject* doUpdate ( osrfMethodContext*, osrfHash*, jsonObject* );
42 jsonObject* doDelete ( osrfMethodContext*, osrfHash*, jsonObject* );
43 jsonObject* doSearch ( osrfMethodContext*, osrfHash*, jsonObject* );
44 jsonObject* oilsMakeJSONFromResult( dbi_result, osrfHash* );
45
46
47 void userDataFree( void* );
48 void sessionDataFree( char*, void* );
49
50 dbi_conn dbhandle; /* our db connection */
51 xmlDocPtr idlDoc = NULL; // parse and store the IDL here
52
53
54 /* parse and store the IDL here */
55 osrfHash* idlHash;
56
57 int osrfAppInitialize() {
58
59         // first we register all the transaction and savepoint methods
60         osrfAppRegisterMethod( MODULENAME, "open-ils.cstore.transaction.begin", "beginTransaction", "", 1, 0 );
61         osrfAppRegisterMethod( MODULENAME, "open-ils.cstore.transaction.commit", "commitTransaction", "", 1, 0 );
62         osrfAppRegisterMethod( MODULENAME, "open-ils.cstore.transaction.rollback", "rollbackTransaction", "", 1, 0 );
63
64         osrfAppRegisterMethod( MODULENAME, "open-ils.cstore.savepoint.set", "setSavepoint", "", 1, 0 );
65         osrfAppRegisterMethod( MODULENAME, "open-ils.cstore.savepoint.release", "releaseSavepoint", "", 1, 0 );
66         osrfAppRegisterMethod( MODULENAME, "open-ils.cstore.savepoint.rollback", "rollbackSavepoint", "", 1, 0 );
67
68
69         idlHash = osrfNewHash();
70         osrfHash* usrData = NULL;
71
72         osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
73         osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
74
75         char * idl_filename = osrf_settings_host_value("/apps/%s/app_settings/IDL", MODULENAME);
76         osrfLogInfo(OSRF_LOG_MARK, "Found file:");
77         osrfLogInfo(OSRF_LOG_MARK, idl_filename);
78
79         osrfLogInfo(OSRF_LOG_MARK, "Parsing the IDL XML...");
80         idlDoc = xmlReadFile( idl_filename, NULL, XML_PARSE_XINCLUDE );
81         
82         if (!idlDoc) {
83                 osrfLogError(OSRF_LOG_MARK, "Could not load or parse the IDL XML file!");
84                 exit(1);
85         }
86
87         osrfLogInfo(OSRF_LOG_MARK, "...IDL XML parsed");
88
89         osrfStringArray* global_methods = osrfNewStringArray(1);
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
97         xmlNodePtr docRoot = xmlDocGetRootElement(idlDoc);
98         xmlNodePtr kid = docRoot->children;
99         while (kid) {
100                 if (!strcmp( (char*)kid->name, "class" )) {
101
102                         usrData = osrfNewHash();
103                         osrfHashSet( usrData, xmlGetProp(kid, "id"), "classname");
104                         osrfHashSet( usrData, xmlGetNsProp(kid, "tablename", PERSIST_NS), "tablename");
105                         osrfHashSet( usrData, xmlGetNsProp(kid, "fieldmapper", OBJECT_NS), "fieldmapper");
106
107                         osrfHashSet( idlHash, usrData, (char*)osrfHashGet(usrData, "classname") );
108
109                         osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", osrfHashGet(usrData, "fieldmapper") );
110
111                         osrfHash* _tmp;
112                         osrfHash* links = osrfNewHash();
113                         osrfHash* fields = osrfNewHash();
114
115                         osrfHashSet( usrData, fields, "fields" );
116                         osrfHashSet( usrData, links, "links" );
117
118                         xmlNodePtr _cur = kid->children;
119
120                         while (_cur) {
121                                 char* string_tmp = NULL;
122
123                                 if (!strcmp( (char*)_cur->name, "fields" )) {
124
125                                         if( (string_tmp = (char*)xmlGetNsProp(_cur, "primary", PERSIST_NS)) ) {
126                                                 osrfHashSet(
127                                                         usrData,
128                                                         strdup( string_tmp ),
129                                                         "primarykey"
130                                                 );
131                                         }
132                                         string_tmp = NULL;
133
134                                         xmlNodePtr _f = _cur->children;
135
136                                         while(_f) {
137                                                 if (strcmp( (char*)_f->name, "field" )) {
138                                                         _f = _f->next;
139                                                         continue;
140                                                 }
141
142                                                 _tmp = osrfNewHash();
143
144                                                 if( (string_tmp = (char*)xmlGetNsProp(_f, "array_position", OBJECT_NS)) ) {
145                                                         osrfHashSet(
146                                                                 _tmp,
147                                                                 strdup( string_tmp ),
148                                                                 "array_position"
149                                                         );
150                                                 }
151                                                 string_tmp = NULL;
152
153                                                 if( (string_tmp = (char*)xmlGetNsProp(_f, "virtual", PERSIST_NS)) ) {
154                                                         osrfHashSet(
155                                                                 _tmp,
156                                                                 strdup( string_tmp ),
157                                                                 "virtual"
158                                                         );
159                                                 }
160                                                 string_tmp = NULL;
161
162                                                 if( (string_tmp = (char*)xmlGetProp(_f, "name")) ) {
163                                                         osrfHashSet(
164                                                                 _tmp,
165                                                                 strdup( string_tmp ),
166                                                                 "name"
167                                                         );
168                                                 }
169
170                                                 osrfLogInfo(OSRF_LOG_MARK, "Found field %s for class %s", string_tmp, osrfHashGet(usrData, "classname") );
171
172                                                 osrfHashSet(
173                                                         fields,
174                                                         _tmp,
175                                                         strdup( string_tmp )
176                                                 );
177                                                 _f = _f->next;
178                                         }
179                                 }
180
181                                 if (!strcmp( (char*)_cur->name, "links" )) {
182                                         xmlNodePtr _l = _cur->children;
183
184                                         while(_l) {
185                                                 if (strcmp( (char*)_l->name, "link" )) {
186                                                         _l = _l->next;
187                                                         continue;
188                                                 }
189
190                                                 _tmp = osrfNewHash();
191
192                                                 if( (string_tmp = (char*)xmlGetProp(_l, "reltype")) ) {
193                                                         osrfHashSet(
194                                                                 _tmp,
195                                                                 strdup( string_tmp ),
196                                                                 "reltype"
197                                                         );
198                                                 }
199                                                 osrfLogInfo(OSRF_LOG_MARK, "Adding link with reltype %s", string_tmp );
200                                                 string_tmp = NULL;
201
202                                                 if( (string_tmp = (char*)xmlGetProp(_l, "key")) ) {
203                                                         osrfHashSet(
204                                                                 _tmp,
205                                                                 strdup( string_tmp ),
206                                                                 "key"
207                                                         );
208                                                 }
209                                                 osrfLogInfo(OSRF_LOG_MARK, "Link fkey is %s", string_tmp );
210                                                 string_tmp = NULL;
211
212                                                 if( (string_tmp = (char*)xmlGetProp(_l, "class")) ) {
213                                                         osrfHashSet(
214                                                                 _tmp,
215                                                                 strdup( string_tmp ),
216                                                                 "class"
217                                                         );
218                                                 }
219                                                 osrfLogInfo(OSRF_LOG_MARK, "Link fclass is %s", string_tmp );
220                                                 string_tmp = NULL;
221
222                                                 osrfStringArray* map = osrfNewStringArray(0);
223
224                                                 if( (string_tmp = (char*)xmlGetProp(_l, "map") )) {
225                                                         char* map_list = strdup( string_tmp );
226                                                         osrfLogInfo(OSRF_LOG_MARK, "Link mapping list is %s", string_tmp );
227
228                                                         if (strlen( map_list ) > 0) {
229                                                                 char* st_tmp;
230                                                                 char* _map_class = strtok_r(map_list, " ", &st_tmp);
231                                                                 osrfStringArrayAdd(map, strdup(_map_class));
232                                                 
233                                                                 while ((_map_class = strtok_r(NULL, " ", &st_tmp))) {
234                                                                         osrfStringArrayAdd(map, strdup(_map_class));
235                                                                 }
236                                                         }
237                                                 }
238                                                 osrfHashSet( _tmp, map, "map");
239
240                                                 if( (string_tmp = (char*)xmlGetProp(_l, "field")) ) {
241                                                         osrfHashSet(
242                                                                 _tmp,
243                                                                 strdup( string_tmp ),
244                                                                 "field"
245                                                         );
246                                                 }
247
248                                                 osrfHashSet(
249                                                         links,
250                                                         _tmp,
251                                                         strdup( string_tmp )
252                                                 );
253
254                                                 osrfLogInfo(OSRF_LOG_MARK, "Found link %s for class %s", string_tmp, osrfHashGet(usrData, "classname") );
255
256                                                 _l = _l->next;
257                                         }
258                                 }
259
260                                 _cur = _cur->next;
261                         }
262
263                         int i = 0; 
264                         char* method_type;
265                         char* st_tmp;
266                         char* _fm;
267                         char* part;
268                         osrfHash* method_meta;
269                         while ( (method_type = osrfStringArrayGetString(global_methods, i++)) ) {
270
271                                 if (!osrfHashGet(usrData, "fieldmapper")) continue;
272
273                                 method_meta = osrfNewHash();
274                                 osrfHashSet(method_meta, usrData, "class");
275
276                                 _fm = strdup( (char*)osrfHashGet(usrData, "fieldmapper") );
277                                 part = strtok_r(_fm, ":", &st_tmp);
278
279                                 growing_buffer* method_name =  buffer_init(64);
280                                 buffer_fadd(method_name, "%s.direct.%s", MODULENAME, part);
281
282                                 while ((part = strtok_r(NULL, ":", &st_tmp))) {
283                                         buffer_fadd(method_name, ".%s", part);
284                                 }
285                                 buffer_fadd(method_name, ".%s", method_type);
286
287
288                                 char* method = buffer_data(method_name);
289                                 buffer_free(method_name);
290                                 free(_fm);
291
292                                 osrfHashSet( method_meta, method, "methodname" );
293                                 osrfHashSet( method_meta, method_type, "methodtype" );
294
295                                 int flags = 0;
296                                 if (!(strcmp( method_type, "search" ))) {
297                                         flags = flags | OSRF_METHOD_STREAMING;
298                                 }
299
300                                 osrfAppRegisterExtendedMethod(
301                                         MODULENAME,
302                                         method,
303                                         "dispatchCRUDMethod",
304                                         "",
305                                         1,
306                                         flags,
307                                         (void*)method_meta
308                                 );
309                         }
310                 }
311                 kid = kid->next;
312         }
313
314         return 0;
315 }
316
317 /**
318  * Connects to the database 
319  */
320 int osrfAppChildInit() {
321
322         osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
323         dbi_initialize(NULL);
324         osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
325
326         char* driver    = osrf_settings_host_value("/apps/%s/app_settings/driver", MODULENAME);
327         char* user      = osrf_settings_host_value("/apps/%s/app_settings/database/user", MODULENAME);
328         char* host      = osrf_settings_host_value("/apps/%s/app_settings/database/host", MODULENAME);
329         char* port      = osrf_settings_host_value("/apps/%s/app_settings/database/port", MODULENAME);
330         char* db        = osrf_settings_host_value("/apps/%s/app_settings/database/db", MODULENAME);
331         char* pw        = osrf_settings_host_value("/apps/%s/app_settings/database/pw", MODULENAME);
332
333         osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
334         dbhandle = dbi_conn_new(driver);
335
336         if(!dbhandle) {
337                 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
338                 return -1;
339         }
340         osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
341
342         osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database.  host=%s, "
343                 "port=%s, user=%s, pw=%s, db=%s", MODULENAME, host, port, user, pw, db );
344
345         if(host) dbi_conn_set_option(dbhandle, "host", host );
346         if(port) dbi_conn_set_option_numeric( dbhandle, "port", atoi(port) );
347         if(user) dbi_conn_set_option(dbhandle, "username", user);
348         if(pw) dbi_conn_set_option(dbhandle, "password", pw );
349         if(db) dbi_conn_set_option(dbhandle, "dbname", db );
350
351         free(user);
352         free(host);
353         free(port);
354         free(db);
355         free(pw);
356
357         const char* err;
358         if (dbi_conn_connect(dbhandle) < 0) {
359                 dbi_conn_error(dbhandle, &err);
360                 osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
361                 return -1;
362         }
363
364         osrfLogInfo(OSRF_LOG_MARK, "%s successfully connected to the database", MODULENAME);
365
366         int attr;
367         unsigned short type;
368         int i = 0; 
369         char* classname;
370         osrfStringArray* classes = osrfHashKeys( idlHash );
371         
372         while ( (classname = osrfStringArrayGetString(classes, i++)) ) {
373                 osrfHash* class = osrfHashGet( idlHash, classname );
374                 osrfHash* fields = osrfHashGet( class, "fields" );
375                 
376                 growing_buffer* sql_buf = buffer_init(32);
377                 buffer_fadd( sql_buf, "SELECT * FROM %s WHERE 1=0;", osrfHashGet(class, "tablename") );
378
379                 char* sql = buffer_data(sql_buf);
380                 buffer_free(sql_buf);
381                 osrfLogDebug(OSRF_LOG_MARK, "%s Investigatory SQL = %s", MODULENAME, sql);
382
383                 dbi_result result = dbi_conn_query(dbhandle, sql);
384                 free(sql);
385
386                 if (result) {
387
388                         int columnIndex = 1;
389                         const char* columnName;
390                         osrfHash* _f;
391                         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
392
393                                 osrfLogDebug(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
394
395                                 /* fetch the fieldmapper index */
396                                 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
397
398                                         osrfLogDebug(OSRF_LOG_MARK, "Found [%s] in IDL hash...", (char*)columnName);
399
400                                         /* determine the field type and storage attributes */
401                                         type = dbi_result_get_field_type(result, columnName);
402                                         attr = dbi_result_get_field_attribs(result, columnName);
403
404                                         switch( type ) {
405
406                                                 case DBI_TYPE_INTEGER :
407
408                                                         osrfHashSet(_f,"number", "primitive");
409
410                                                         if( attr & DBI_INTEGER_SIZE8 ) 
411                                                                 osrfHashSet(_f,"INT8", "datatype");
412                                                         else 
413                                                                 osrfHashSet(_f,"INT", "datatype");
414                                                         break;
415
416                                                 case DBI_TYPE_DECIMAL :
417                                                         osrfHashSet(_f,"number", "primitive");
418                                                         osrfHashSet(_f,"NUMERIC", "datatype");
419                                                         break;
420
421                                                 case DBI_TYPE_STRING :
422                                                         osrfHashSet(_f,"string", "primitive");
423                                                         osrfHashSet(_f,"TEXT", "datatype");
424                                                         break;
425
426                                                 case DBI_TYPE_DATETIME :
427                                                         osrfHashSet(_f,"string", "primitive");
428                                                         osrfHashSet(_f,"TIMESTAMP", "datatype");
429                                                         break;
430
431                                                 case DBI_TYPE_BINARY :
432                                                         osrfHashSet(_f,"string", "primitive");
433                                                         osrfHashSet(_f,"BYTEA", "datatype");
434                                         }
435
436                                         osrfLogDebug(
437                                                 OSRF_LOG_MARK,
438                                                 "Setting [%s] to primitive [%s] and datatype [%s]...",
439                                                 (char*)columnName,
440                                                 osrfHashGet(_f, "primitive"),
441                                                 osrfHashGet(_f, "datatype")
442                                         );
443                                 }
444                         }
445                         dbi_result_free(result);
446                 } else {
447                         osrfLogDebug(OSRF_LOG_MARK, "No data found for class [%s]...", (char*)classname);
448                 }
449         }
450
451         osrfStringArrayFree(classes);
452
453         return 0;
454 }
455
456 void userDataFree( void* blob ) {
457         osrfHashFree( (osrfHash*)blob );
458         return;
459 }
460
461 void sessionDataFree( char* key, void* item ) {
462         if (!(strcmp(key,"xact_id")))
463                 free(item);
464
465         return;
466 }
467
468 int beginTransaction ( osrfMethodContext* ctx ) {
469         OSRF_METHOD_VERIFY_CONTEXT(ctx);
470
471         dbi_result result = dbi_conn_query(dbhandle, "START TRANSACTION;");
472         if (!result) {
473                 osrfLogError(OSRF_LOG_MARK, "%s: Error starting transaction", MODULENAME );
474                 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: Error starting transaction", MODULENAME );
475                 return 1;
476         } else {
477                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
478                 osrfAppRespondComplete( ctx, ret );
479                 jsonObjectFree(ret);
480                 
481                 if (!ctx->session->userData) {
482                         ctx->session->userData = osrfNewHash();
483                         ((osrfHash*)ctx->session->userData)->freeItem = &sessionDataFree;
484                 }
485
486                 osrfHashSet( (osrfHash*)ctx->session->userData, strdup( ctx->session->session_id ), "xact_id" );
487                 ctx->session->userDataFree = &userDataFree;
488                 
489         }
490         return 0;
491 }
492
493 int setSavepoint ( osrfMethodContext* ctx ) {
494         OSRF_METHOD_VERIFY_CONTEXT(ctx);
495
496         char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
497
498         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
499                 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: No active transaction, required for savepoints", MODULENAME );
500                 return 1;
501         }
502
503         dbi_result result = dbi_conn_queryf(dbhandle, "SAVEPOINT \"%s\";", spName);
504         if (!result) {
505                 osrfLogError(
506                         OSRF_LOG_MARK,
507                         "%s: Error creating savepoint %s in transaction %s",
508                         MODULENAME,
509                         spName,
510                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
511                 );
512                 osrfAppRequestRespondException(
513                         ctx->session,
514                         ctx->request,
515                         "%s: Error creating savepoint %s in transaction %s",
516                         MODULENAME,
517                         spName,
518                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
519                 );
520                 return 1;
521         } else {
522                 jsonObject* ret = jsonNewObject(spName);
523                 osrfAppRespondComplete( ctx, ret );
524                 jsonObjectFree(ret);
525         }
526         return 0;
527 }
528
529 int releaseSavepoint ( osrfMethodContext* ctx ) {
530         OSRF_METHOD_VERIFY_CONTEXT(ctx);
531
532         char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
533
534         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
535                 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: No active transaction, required for savepoints", MODULENAME );
536                 return 1;
537         }
538
539         dbi_result result = dbi_conn_queryf(dbhandle, "RELEASE SAVEPOINT \"%s\";", spName);
540         if (!result) {
541                 osrfLogError(
542                         OSRF_LOG_MARK,
543                         "%s: Error releasing savepoint %s in transaction %s",
544                         MODULENAME,
545                         spName,
546                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
547                 );
548                 osrfAppRequestRespondException(
549                         ctx->session,
550                         ctx->request,
551                         "%s: Error releasing savepoint %s in transaction %s",
552                         MODULENAME,
553                         spName,
554                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
555                 );
556                 return 1;
557         } else {
558                 jsonObject* ret = jsonNewObject(spName);
559                 osrfAppRespondComplete( ctx, ret );
560                 jsonObjectFree(ret);
561         }
562         return 0;
563 }
564
565 int rollbackSavepoint ( osrfMethodContext* ctx ) {
566         OSRF_METHOD_VERIFY_CONTEXT(ctx);
567
568         char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
569
570         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
571                 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: No active transaction, required for savepoints", MODULENAME );
572                 return 1;
573         }
574
575         dbi_result result = dbi_conn_queryf(dbhandle, "ROLLBACK TO SAVEPOINT \"%s\";", spName);
576         if (!result) {
577                 osrfLogError(
578                         OSRF_LOG_MARK,
579                         "%s: Error rolling back savepoint %s in transaction %s",
580                         MODULENAME,
581                         spName,
582                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
583                 );
584                 osrfAppRequestRespondException(
585                         ctx->session,
586                         ctx->request,
587                         "%s: Error rolling back savepoint %s in transaction %s",
588                         MODULENAME,
589                         spName,
590                         osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
591                 );
592                 return 1;
593         } else {
594                 jsonObject* ret = jsonNewObject(spName);
595                 osrfAppRespondComplete( ctx, ret );
596                 jsonObjectFree(ret);
597         }
598         return 0;
599 }
600
601 int commitTransaction ( osrfMethodContext* ctx ) {
602         OSRF_METHOD_VERIFY_CONTEXT(ctx);
603
604         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
605                 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: No active transaction to commit", MODULENAME );
606                 return 1;
607         }
608
609         dbi_result result = dbi_conn_query(dbhandle, "COMMIT;");
610         if (!result) {
611                 osrfLogError(OSRF_LOG_MARK, "%s: Error committing transaction", MODULENAME );
612                 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: Error committing transaction", MODULENAME );
613                 return 1;
614         } else {
615                 osrfHashRemove(ctx->session->userData, "xact_id");
616                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
617                 osrfAppRespondComplete( ctx, ret );
618                 jsonObjectFree(ret);
619         }
620         return 0;
621 }
622
623 int rollbackTransaction ( osrfMethodContext* ctx ) {
624         OSRF_METHOD_VERIFY_CONTEXT(ctx);
625
626         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
627                 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: No active transaction to roll back", MODULENAME );
628                 return 1;
629         }
630
631         dbi_result result = dbi_conn_query(dbhandle, "ROLLBACK;");
632         if (!result) {
633                 osrfLogError(OSRF_LOG_MARK, "%s: Error rolling back transaction", MODULENAME );
634                 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: Error rolling back transaction", MODULENAME );
635                 return 1;
636         } else {
637                 osrfHashRemove(ctx->session->userData, "xact_id");
638                 jsonObject* ret = jsonNewObject(ctx->session->session_id);
639                 osrfAppRespondComplete( ctx, ret );
640                 jsonObjectFree(ret);
641         }
642         return 0;
643 }
644
645 int dispatchCRUDMethod ( osrfMethodContext* ctx ) {
646         OSRF_METHOD_VERIFY_CONTEXT(ctx);
647
648         osrfHash* meta = (osrfHash*) ctx->method->userData;
649         osrfHash* class_obj = osrfHashGet( meta, "class" );
650
651         jsonObject * obj = NULL;
652         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "create"))
653                 obj = doCreate(ctx, class_obj, ctx->params);
654
655         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "retrieve"))
656                 obj = doRetrieve(ctx, class_obj, ctx->params);
657
658         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "update"))
659                 obj = doUpdate(ctx, class_obj, ctx->params);
660
661         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "delete"))
662                 obj = doDelete(ctx, class_obj, ctx->params);
663
664         if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "search")) {
665                 jsonObjectNode* cur;
666                 obj = doSearch(ctx, class_obj, ctx->params);
667                 jsonObjectIterator* itr = jsonNewObjectIterator( obj );
668                 while ((cur = jsonObjectIteratorNext( itr ))) {
669                         osrfAppRespond( ctx, jsonObjectClone(cur->item) );
670                 }
671                 jsonObjectIteratorFree(itr);
672                 osrfAppRespondComplete( ctx, NULL );
673                 
674         } else {
675                 osrfAppRespondComplete( ctx, obj );
676         }
677
678         jsonObjectFree(obj);
679
680         return 0;
681 }
682
683 jsonObject* doCreate(osrfMethodContext* ctx, osrfHash* meta, jsonObject* params ) {
684
685         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
686                 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: No active transaction to roll back", MODULENAME );
687                 return 1;
688         }
689
690         return NULL;
691 }
692
693
694 jsonObject* doRetrieve(osrfMethodContext* ctx, osrfHash* meta, jsonObject* params ) {
695
696         jsonObject* obj;
697
698         char* id = jsonObjectToSimpleString(jsonObjectGetIndex(params, 0));
699         jsonObject* order_hash = jsonObjectGetIndex(params, 1);
700
701         osrfLogDebug(
702                 OSRF_LOG_MARK,
703                 "%s retrieving %s object with id %s",
704                 MODULENAME,
705                 osrfHashGet(meta, "fieldmapper"),
706                 id
707         );
708
709         jsonObject* fake_params = jsonParseString("[]");
710         jsonObjectPush(fake_params, jsonParseString("{}"));
711
712         jsonObjectSetKey(
713                 jsonObjectGetIndex(fake_params, 0),
714                 osrfHashGet(meta, "primarykey"),
715                 jsonNewObject(id)
716         );
717
718         if (order_hash) jsonObjectPush(fake_params, jsonObjectClone(order_hash) );
719
720         jsonObject* list = doSearch( ctx,meta, fake_params);
721         obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
722
723         jsonObjectFree( list );
724         jsonObjectFree( fake_params );
725
726         return obj;
727 }
728
729 jsonObject* doSearch(osrfMethodContext* ctx, osrfHash* meta, jsonObject* params ) {
730
731         jsonObject* _tmp;
732         jsonObject* obj;
733         jsonObject* search_hash = jsonObjectGetIndex(params, 0);
734         jsonObject* order_hash = jsonObjectGetIndex(params, 1);
735
736         growing_buffer* sql_buf = buffer_init(128);
737         buffer_fadd(sql_buf, "SELECT * FROM %s WHERE ", osrfHashGet(meta, "tablename") );
738
739         jsonObjectNode* node = NULL;
740         jsonObjectIterator* search_itr = jsonNewObjectIterator( search_hash );
741
742         int first = 1;
743         while ( (node = jsonObjectIteratorNext( search_itr )) ) {
744                 osrfHash* field = osrfHashGet( osrfHashGet(meta, "fields"), node->key );
745
746                 if (!field) continue;
747
748                 if (first) {
749                         first = 0;
750                 } else {
751                         buffer_add(sql_buf, " AND ");
752                 }
753
754                 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
755                         buffer_fadd(
756                                 sql_buf,
757                                 "%s = %d",
758                                 osrfHashGet(field, "name"),
759                                 atoi( jsonObjectToSimpleString(node->item) )
760                         );
761                 } else {
762                         char* key_string = jsonObjectToSimpleString(node->item);
763                         if ( dbi_conn_quote_string(dbhandle, &key_string) ) {
764                                 buffer_fadd(
765                                         sql_buf,
766                                         "%s = %s",
767                                         osrfHashGet(field, "name"),
768                                         key_string
769                                 );
770                                 free(key_string);
771                         } else {
772                                 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string);
773                                 free(key_string);
774                         }
775                 }
776         }
777
778         jsonObjectIteratorFree(search_itr);
779
780         if (order_hash) {
781                 char* string;
782                 _tmp = jsonObjectGetKey( order_hash, "order_by" );
783                 if (_tmp) {
784                         string = jsonObjectToSimpleString(_tmp);
785                         buffer_fadd(
786                                 sql_buf,
787                                 " ORDER BY %s",
788                                 string
789                         );
790                         free(string);
791                 }
792
793                 _tmp = jsonObjectGetKey( order_hash, "limit" );
794                 if (_tmp) {
795                         string = jsonObjectToSimpleString(_tmp);
796                         buffer_fadd(
797                                 sql_buf,
798                                 " LIMIT %d",
799                                 atoi(string)
800                         );
801                         free(string);
802                 }
803
804                 _tmp = jsonObjectGetKey( order_hash, "offset" );
805                 if (_tmp) {
806                         string = jsonObjectToSimpleString(_tmp);
807                         buffer_fadd(
808                                 sql_buf,
809                                 " OFFSET %d",
810                                 atoi(string)
811                         );
812                         free(string);
813                 }
814         }
815
816         buffer_add(sql_buf, ";");
817
818         char* sql = buffer_data(sql_buf);
819         buffer_free(sql_buf);
820         
821         osrfLogDebug(OSRF_LOG_MARK, "%s SQL =  %s", MODULENAME, sql);
822         dbi_result result = dbi_conn_query(dbhandle, sql);
823
824         jsonObject* res_list = jsonParseString("[]");
825         if(result) {
826                 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
827
828                 /* there should be one row at the most  */
829                 if (dbi_result_first_row(result)) {
830                         /* JSONify the result */
831                         osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
832                         do {
833                                 obj = oilsMakeJSONFromResult( result, meta );
834                                 jsonObjectPush(res_list, obj);
835                         } while (dbi_result_next_row(result));
836                 } else {
837                         osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
838                 }
839
840                 /* clean up the query */
841                 dbi_result_free(result); 
842
843         } else {
844                 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]", MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
845         }
846
847         free(sql);
848
849         if (order_hash) {
850                 _tmp = jsonObjectGetKey( order_hash, "flesh" );
851                 if (_tmp) {
852                         double x = jsonObjectGetNumber(_tmp);
853
854                         if ((int)x > 0) {
855
856                                 jsonObjectNode* cur;
857                                 jsonObjectIterator* itr = jsonNewObjectIterator( res_list );
858                                 while ((cur = jsonObjectIteratorNext( itr ))) {
859
860                                         osrfHash* links = osrfHashGet(meta, "links");
861                                         osrfHash* fields = osrfHashGet(meta, "fields");
862
863                                         int i = 0;
864                                         char* link_field;
865                                         osrfStringArray* link_fields;
866                                         
867                                         jsonObject* flesh_fields = jsonObjectGetKey( order_hash, "flesh_fields" );
868                                         if (flesh_fields) {
869                                                 jsonObjectNode* _f;
870                                                 jsonObjectIterator* _i = jsonNewObjectIterator( flesh_fields );
871                                                 link_fields = osrfNewStringArray(1);
872                                                 while ((_f = jsonObjectIteratorNext( _i ))) {
873                                                         osrfStringArrayAdd( link_fields, jsonObjectToSimpleString( _f->item ) );
874                                                 }
875                                         } else {
876                                                 link_fields = osrfHashKeys( links );
877                                         }
878
879                                         while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
880
881                                                 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
882
883                                                 osrfHash* kid_link = osrfHashGet(links, link_field);
884                                                 if (!kid_link) continue;
885
886                                                 osrfHash* field = osrfHashGet(fields, link_field);
887                                                 if (!field) continue;
888
889                                                 osrfHash* value_field = field;
890
891                                                 osrfHash* kid_idl = osrfHashGet(idlHash, osrfHashGet(kid_link, "class"));
892                                                 if (!kid_idl) continue;
893
894                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
895                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
896                                                 }
897                                                         
898                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
899                                                         value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
900                                                 }
901
902                                                 osrfLogDebug(
903                                                         OSRF_LOG_MARK,
904                                                         "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
905                                                         osrfHashGet(kid_link, "field"),
906                                                         osrfHashGet(kid_link, "class"),
907                                                         osrfHashGet(kid_link, "key"),
908                                                         osrfHashGet(kid_link, "reltype")
909                                                 );
910
911                                                 jsonObject* fake_params = jsonParseString("[]");
912                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // search hash
913                                                 jsonObjectPush(fake_params, jsonParseString("{}")); // order/flesh hash
914
915                                                 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
916
917                                                 char* search_key =
918                                                 jsonObjectToSimpleString(
919                                                         jsonObjectGetIndex(
920                                                                 cur->item,
921                                                                 atoi( osrfHashGet(value_field, "array_position") )
922                                                         )
923                                                 );
924
925                                                 if (!search_key) {
926                                                         osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
927                                                         continue;
928                                                 }
929                                                         
930                                                 jsonObjectSetKey(
931                                                         jsonObjectGetIndex(fake_params, 0),
932                                                         osrfHashGet(kid_link, "key"),
933                                                         jsonNewObject( search_key )
934                                                 );
935
936                                                 free(search_key);
937
938
939                                                 jsonObjectSetKey(
940                                                         jsonObjectGetIndex(fake_params, 1),
941                                                         "flesh",
942                                                         jsonNewNumberObject( (double)((int)x - 1) )
943                                                 );
944
945                                                 if (flesh_fields) {
946                                                         jsonObjectSetKey(
947                                                                 jsonObjectGetIndex(fake_params, 1),
948                                                                 "flesh_fields",
949                                                                 jsonObjectClone( flesh_fields )
950                                                         );
951                                                 }
952
953                                                 if (jsonObjectGetKey(order_hash, "order_by")) {
954                                                         jsonObjectSetKey(
955                                                                 jsonObjectGetIndex(fake_params, 1),
956                                                                 "order_by",
957                                                                 jsonObjectClone(jsonObjectGetKey(order_hash, "order_by"))
958                                                         );
959                                                 }
960
961                                                 jsonObject* kids = doSearch(ctx, kid_idl, fake_params);
962
963                                                 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
964                                                 
965                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" ))) {
966                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
967                                                         jsonObjectSetIndex(
968                                                                 cur->item,
969                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
970                                                                 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
971                                                         );
972                                                         jsonObjectFree( kids );
973                                                 }
974
975                                                 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
976                                                         osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
977                                                         jsonObjectSetIndex(
978                                                                 cur->item,
979                                                                 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
980                                                                 kids
981                                                         );
982                                                 }
983
984                                                 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
985
986                                                 jsonObjectFree( fake_params );
987                                         }
988                                         if (flesh_fields) osrfStringArrayFree(link_fields);
989                                 }
990                                 jsonObjectIteratorFree(itr);
991                         }
992                 }
993         }
994
995         return res_list;
996 }
997
998
999 jsonObject* doUpdate(osrfMethodContext* ctx, osrfHash* meta, jsonObject* params ) {
1000
1001         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
1002                 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: No active transaction to roll back", MODULENAME );
1003                 return 1;
1004         }
1005
1006         return NULL;
1007 }
1008
1009 jsonObject* doDelete(osrfMethodContext* ctx, osrfHash* meta, jsonObject* params ) {
1010
1011         if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
1012                 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: No active transaction -- required for delete", MODULENAME );
1013                 return jsonNewObject(NULL);
1014         }
1015
1016         jsonObject* obj;
1017
1018         char* pkey = osrfHashGet(meta, "primarykey");
1019
1020         char* id;
1021         if (jsonObjectGetIndex(params, 0)->classname) {
1022                 id = jsonObjectToSimpleString(
1023                         jsonObjectGetIndex(
1024                                 jsonObjectGetIndex(params, 0),
1025                                 atoi( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "array_position") )
1026                         )
1027                 );
1028         } else {
1029                 id = jsonObjectToSimpleString(jsonObjectGetIndex(params, 0));
1030         }
1031
1032         osrfLogDebug(
1033                 OSRF_LOG_MARK,
1034                 "%s deleting %s object with %s = %s",
1035                 MODULENAME,
1036                 osrfHashGet(meta, "fieldmapper"),
1037                 pkey,
1038                 id
1039         );
1040
1041         obj = jsonParseString(id);
1042
1043         if ( strcmp( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "primitive" ), "number" ) )
1044                 dbi_conn_quote_string(dbhandle, &id);
1045
1046         dbi_result result = dbi_conn_queryf(dbhandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
1047
1048         if (!result) {
1049                 jsonObjectFree(obj);
1050                 obj = jsonNewObject(NULL);
1051                 osrfLogError(
1052                         OSRF_LOG_MARK,
1053                         "%s ERROR deleting %s object with %s = %s",
1054                         MODULENAME,
1055                         osrfHashGet(meta, "fieldmapper"),
1056                         pkey,
1057                         id
1058                 );
1059         }
1060
1061         free(id);
1062
1063         return obj;
1064
1065 }
1066
1067
1068 jsonObject* oilsMakeJSONFromResult( dbi_result result, osrfHash* meta) {
1069         if(!(result && meta)) return NULL;
1070
1071         jsonObject* object = jsonParseString("[]");
1072         jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
1073
1074         osrfHash* fields = osrfHashGet(meta, "fields");
1075
1076         osrfLogDebug(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
1077
1078         osrfHash* _f;
1079         time_t _tmp_dt;
1080         char dt_string[256];
1081         struct tm gmdt;
1082
1083         int fmIndex;
1084         int columnIndex = 1;
1085         int attr;
1086         unsigned short type;
1087         const char* columnName;
1088
1089         /* cycle through the column list */
1090         while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
1091
1092                 osrfLogDebug(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
1093
1094                 fmIndex = -1; // reset the position
1095                 
1096                 /* determine the field type and storage attributes */
1097                 type = dbi_result_get_field_type(result, columnName);
1098                 attr = dbi_result_get_field_attribs(result, columnName);
1099
1100                 /* fetch the fieldmapper index */
1101                 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
1102                         char* virt = (char*)osrfHashGet(_f, "virtual");
1103                         char* pos = (char*)osrfHashGet(_f, "array_position");
1104
1105                         if ( !virt || !pos || !(strcmp( virt, "true" )) ) continue;
1106
1107                         fmIndex = atoi( pos );
1108                         osrfLogDebug(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
1109                 }
1110
1111                 if (dbi_result_field_is_null(result, columnName)) {
1112                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
1113                 } else {
1114
1115                         switch( type ) {
1116
1117                                 case DBI_TYPE_INTEGER :
1118
1119                                         if( attr & DBI_INTEGER_SIZE8 ) 
1120                                                 jsonObjectSetIndex( object, fmIndex, 
1121                                                         jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
1122                                         else 
1123                                                 jsonObjectSetIndex( object, fmIndex, 
1124                                                         jsonNewNumberObject(dbi_result_get_long(result, columnName)));
1125
1126                                         break;
1127
1128                                 case DBI_TYPE_DECIMAL :
1129                                         jsonObjectSetIndex( object, fmIndex, 
1130                                                         jsonNewNumberObject(dbi_result_get_double(result, columnName)));
1131                                         break;
1132
1133                                 case DBI_TYPE_STRING :
1134
1135
1136                                         jsonObjectSetIndex(
1137                                                 object,
1138                                                 fmIndex,
1139                                                 jsonNewObject( dbi_result_get_string(result, columnName) )
1140                                         );
1141
1142                                         break;
1143
1144                                 case DBI_TYPE_DATETIME :
1145
1146                                         memset(dt_string, '\0', 256);
1147                                         memset(&gmdt, '\0', sizeof(gmdt));
1148                                         memset(&_tmp_dt, '\0', sizeof(_tmp_dt));
1149
1150                                         _tmp_dt = dbi_result_get_datetime(result, columnName);
1151
1152                                         localtime_r( &_tmp_dt, &gmdt );
1153
1154                                         if (!(attr & DBI_DATETIME_DATE)) {
1155                                                 strftime(dt_string, 255, "%T", &gmdt);
1156                                         } else if (!(attr & DBI_DATETIME_TIME)) {
1157                                                 strftime(dt_string, 255, "%F", &gmdt);
1158                                         } else {
1159                                                 /* XXX ARG! My eyes! The goggles, they do nothing! */
1160
1161                                                 strftime(dt_string, 255, "%FT%T%z", &gmdt);
1162                                         }
1163
1164                                         jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
1165
1166                                         break;
1167
1168                                 case DBI_TYPE_BINARY :
1169                                         osrfLogError( OSRF_LOG_MARK, 
1170                                                 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
1171                         }
1172                 }
1173         }
1174
1175         return object;
1176 }
1177