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"
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>
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"
27 int osrfAppChildInit();
28 int osrfAppInitialize();
30 int beginTransaction ( osrfMethodContext* );
31 int commitTransaction ( osrfMethodContext* );
32 int rollbackTransaction ( osrfMethodContext* );
34 int setSavepoint ( osrfMethodContext* );
35 int releaseSavepoint ( osrfMethodContext* );
36 int rollbackSavepoint ( osrfMethodContext* );
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* );
47 void userDataFree( void* );
48 void sessionDataFree( char*, void* );
50 dbi_conn dbhandle; /* our db connection */
51 xmlDocPtr idlDoc = NULL; // parse and store the IDL here
54 /* parse and store the IDL here */
57 int osrfAppInitialize() {
59 // first we register all the transaction and savepoint methods
60 osrfAppRegisterMethod( MODULENAME, "open-ils.cstore.transaction.begin", "beginTransaction", "", 0, 0 );
61 osrfAppRegisterMethod( MODULENAME, "open-ils.cstore.transaction.commit", "commitTransaction", "", 0, 0 );
62 osrfAppRegisterMethod( MODULENAME, "open-ils.cstore.transaction.rollback", "rollbackTransaction", "", 0, 0 );
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 );
69 idlHash = osrfNewHash();
70 osrfHash* usrData = NULL;
72 osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
73 osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
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);
79 osrfLogInfo(OSRF_LOG_MARK, "Parsing the IDL XML...");
80 idlDoc = xmlReadFile( idl_filename, NULL, XML_PARSE_XINCLUDE );
83 osrfLogError(OSRF_LOG_MARK, "Could not load or parse the IDL XML file!");
87 osrfLogInfo(OSRF_LOG_MARK, "...IDL XML parsed");
89 osrfStringArray* global_methods = osrfNewStringArray(1);
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" );
97 xmlNodePtr docRoot = xmlDocGetRootElement(idlDoc);
98 xmlNodePtr kid = docRoot->children;
100 if (!strcmp( (char*)kid->name, "class" )) {
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");
107 osrfHashSet( idlHash, usrData, (char*)osrfHashGet(usrData, "classname") );
109 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", osrfHashGet(usrData, "fieldmapper") );
112 osrfHash* links = osrfNewHash();
113 osrfHash* fields = osrfNewHash();
115 osrfHashSet( usrData, fields, "fields" );
116 osrfHashSet( usrData, links, "links" );
118 xmlNodePtr _cur = kid->children;
121 char* string_tmp = NULL;
123 if (!strcmp( (char*)_cur->name, "fields" )) {
125 if( (string_tmp = (char*)xmlGetNsProp(_cur, "primary", PERSIST_NS)) ) {
128 strdup( string_tmp ),
134 xmlNodePtr _f = _cur->children;
137 if (strcmp( (char*)_f->name, "field" )) {
142 _tmp = osrfNewHash();
144 if( (string_tmp = (char*)xmlGetNsProp(_f, "array_position", OBJECT_NS)) ) {
147 strdup( string_tmp ),
153 if( (string_tmp = (char*)xmlGetNsProp(_f, "virtual", PERSIST_NS)) ) {
156 strdup( string_tmp ),
162 if( (string_tmp = (char*)xmlGetProp(_f, "name")) ) {
165 strdup( string_tmp ),
170 osrfLogInfo(OSRF_LOG_MARK, "Found field %s for class %s", string_tmp, osrfHashGet(usrData, "classname") );
181 if (!strcmp( (char*)_cur->name, "links" )) {
182 xmlNodePtr _l = _cur->children;
185 if (strcmp( (char*)_l->name, "link" )) {
190 _tmp = osrfNewHash();
192 if( (string_tmp = (char*)xmlGetProp(_l, "reltype")) ) {
195 strdup( string_tmp ),
199 osrfLogInfo(OSRF_LOG_MARK, "Adding link with reltype %s", string_tmp );
202 if( (string_tmp = (char*)xmlGetProp(_l, "key")) ) {
205 strdup( string_tmp ),
209 osrfLogInfo(OSRF_LOG_MARK, "Link fkey is %s", string_tmp );
212 if( (string_tmp = (char*)xmlGetProp(_l, "class")) ) {
215 strdup( string_tmp ),
219 osrfLogInfo(OSRF_LOG_MARK, "Link fclass is %s", string_tmp );
222 osrfStringArray* map = osrfNewStringArray(0);
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 );
228 if (strlen( map_list ) > 0) {
230 char* _map_class = strtok_r(map_list, " ", &st_tmp);
231 osrfStringArrayAdd(map, strdup(_map_class));
233 while ((_map_class = strtok_r(NULL, " ", &st_tmp))) {
234 osrfStringArrayAdd(map, strdup(_map_class));
238 osrfHashSet( _tmp, map, "map");
240 if( (string_tmp = (char*)xmlGetProp(_l, "field")) ) {
243 strdup( string_tmp ),
254 osrfLogInfo(OSRF_LOG_MARK, "Found link %s for class %s", string_tmp, osrfHashGet(usrData, "classname") );
268 osrfHash* method_meta;
269 while ( (method_type = osrfStringArrayGetString(global_methods, i++)) ) {
271 if (!osrfHashGet(usrData, "fieldmapper")) continue;
273 method_meta = osrfNewHash();
274 osrfHashSet(method_meta, usrData, "class");
276 _fm = strdup( (char*)osrfHashGet(usrData, "fieldmapper") );
277 part = strtok_r(_fm, ":", &st_tmp);
279 growing_buffer* method_name = buffer_init(64);
280 buffer_fadd(method_name, "%s.direct.%s", MODULENAME, part);
282 while ((part = strtok_r(NULL, ":", &st_tmp))) {
283 buffer_fadd(method_name, ".%s", part);
285 buffer_fadd(method_name, ".%s", method_type);
288 char* method = buffer_data(method_name);
289 buffer_free(method_name);
292 osrfHashSet( method_meta, method, "methodname" );
293 osrfHashSet( method_meta, method_type, "methodtype" );
296 if (!(strcmp( method_type, "search" ))) {
297 flags = flags | OSRF_METHOD_STREAMING;
300 osrfAppRegisterExtendedMethod(
303 "dispatchCRUDMethod",
318 * Connects to the database
320 int osrfAppChildInit() {
322 osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
323 dbi_initialize(NULL);
324 osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
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);
333 osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
334 dbhandle = dbi_conn_new(driver);
337 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
340 osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
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 );
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 );
358 if (dbi_conn_connect(dbhandle) < 0) {
359 dbi_conn_error(dbhandle, &err);
360 osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
364 osrfLogInfo(OSRF_LOG_MARK, "%s successfully connected to the database", MODULENAME);
370 osrfStringArray* classes = osrfHashKeys( idlHash );
372 while ( (classname = osrfStringArrayGetString(classes, i++)) ) {
373 osrfHash* class = osrfHashGet( idlHash, classname );
374 osrfHash* fields = osrfHashGet( class, "fields" );
376 growing_buffer* sql_buf = buffer_init(32);
377 buffer_fadd( sql_buf, "SELECT * FROM %s WHERE 1=0;", osrfHashGet(class, "tablename") );
379 char* sql = buffer_data(sql_buf);
380 buffer_free(sql_buf);
381 osrfLogDebug(OSRF_LOG_MARK, "%s Investigatory SQL = %s", MODULENAME, sql);
383 dbi_result result = dbi_conn_query(dbhandle, sql);
389 const char* columnName;
391 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
393 osrfLogDebug(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
395 /* fetch the fieldmapper index */
396 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
398 osrfLogDebug(OSRF_LOG_MARK, "Found [%s] in IDL hash...", (char*)columnName);
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);
406 case DBI_TYPE_INTEGER :
408 osrfHashSet(_f,"number", "primitive");
410 if( attr & DBI_INTEGER_SIZE8 )
411 osrfHashSet(_f,"INT8", "datatype");
413 osrfHashSet(_f,"INT", "datatype");
416 case DBI_TYPE_DECIMAL :
417 osrfHashSet(_f,"number", "primitive");
418 osrfHashSet(_f,"NUMERIC", "datatype");
421 case DBI_TYPE_STRING :
422 osrfHashSet(_f,"string", "primitive");
423 osrfHashSet(_f,"TEXT", "datatype");
426 case DBI_TYPE_DATETIME :
427 osrfHashSet(_f,"string", "primitive");
428 osrfHashSet(_f,"TIMESTAMP", "datatype");
431 case DBI_TYPE_BINARY :
432 osrfHashSet(_f,"string", "primitive");
433 osrfHashSet(_f,"BYTEA", "datatype");
438 "Setting [%s] to primitive [%s] and datatype [%s]...",
440 osrfHashGet(_f, "primitive"),
441 osrfHashGet(_f, "datatype")
445 dbi_result_free(result);
447 osrfLogDebug(OSRF_LOG_MARK, "No data found for class [%s]...", (char*)classname);
451 osrfStringArrayFree(classes);
456 void userDataFree( void* blob ) {
457 osrfHashFree( (osrfHash*)blob );
461 void sessionDataFree( char* key, void* item ) {
462 if (!(strcmp(key,"xact_id")))
468 int beginTransaction ( osrfMethodContext* ctx ) {
469 OSRF_METHOD_VERIFY_CONTEXT(ctx);
471 dbi_result result = dbi_conn_query(dbhandle, "START TRANSACTION;");
473 osrfLogError(OSRF_LOG_MARK, "%s: Error starting transaction", MODULENAME );
474 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: Error starting transaction", MODULENAME );
477 jsonObject* ret = jsonNewObject(ctx->session->session_id);
478 osrfAppRespondComplete( ctx, ret );
481 if (!ctx->session->userData) {
482 ctx->session->userData = osrfNewHash();
483 ((osrfHash*)ctx->session->userData)->freeItem = &sessionDataFree;
486 osrfHashSet( (osrfHash*)ctx->session->userData, strdup( ctx->session->session_id ), "xact_id" );
487 ctx->session->userDataFree = &userDataFree;
493 int setSavepoint ( osrfMethodContext* ctx ) {
494 OSRF_METHOD_VERIFY_CONTEXT(ctx);
496 char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
498 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
499 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: No active transaction, required for savepoints", MODULENAME );
503 dbi_result result = dbi_conn_queryf(dbhandle, "SAVEPOINT \"%s\";", spName);
507 "%s: Error creating savepoint %s in transaction %s",
510 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
512 osrfAppRequestRespondException(
515 "%s: Error creating savepoint %s in transaction %s",
518 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
522 jsonObject* ret = jsonNewObject(spName);
523 osrfAppRespondComplete( ctx, ret );
529 int releaseSavepoint ( osrfMethodContext* ctx ) {
530 OSRF_METHOD_VERIFY_CONTEXT(ctx);
532 char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
534 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
535 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: No active transaction, required for savepoints", MODULENAME );
539 dbi_result result = dbi_conn_queryf(dbhandle, "RELEASE SAVEPOINT \"%s\";", spName);
543 "%s: Error releasing savepoint %s in transaction %s",
546 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
548 osrfAppRequestRespondException(
551 "%s: Error releasing savepoint %s in transaction %s",
554 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
558 jsonObject* ret = jsonNewObject(spName);
559 osrfAppRespondComplete( ctx, ret );
565 int rollbackSavepoint ( osrfMethodContext* ctx ) {
566 OSRF_METHOD_VERIFY_CONTEXT(ctx);
568 char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0));
570 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
571 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: No active transaction, required for savepoints", MODULENAME );
575 dbi_result result = dbi_conn_queryf(dbhandle, "ROLLBACK TO SAVEPOINT \"%s\";", spName);
579 "%s: Error rolling back savepoint %s in transaction %s",
582 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
584 osrfAppRequestRespondException(
587 "%s: Error rolling back savepoint %s in transaction %s",
590 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
594 jsonObject* ret = jsonNewObject(spName);
595 osrfAppRespondComplete( ctx, ret );
601 int commitTransaction ( osrfMethodContext* ctx ) {
602 OSRF_METHOD_VERIFY_CONTEXT(ctx);
604 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
605 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: No active transaction to commit", MODULENAME );
609 dbi_result result = dbi_conn_query(dbhandle, "COMMIT;");
611 osrfLogError(OSRF_LOG_MARK, "%s: Error committing transaction", MODULENAME );
612 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: Error committing transaction", MODULENAME );
615 osrfHashRemove(ctx->session->userData, "xact_id");
616 jsonObject* ret = jsonNewObject(ctx->session->session_id);
617 osrfAppRespondComplete( ctx, ret );
623 int rollbackTransaction ( osrfMethodContext* ctx ) {
624 OSRF_METHOD_VERIFY_CONTEXT(ctx);
626 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
627 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: No active transaction to roll back", MODULENAME );
631 dbi_result result = dbi_conn_query(dbhandle, "ROLLBACK;");
633 osrfLogError(OSRF_LOG_MARK, "%s: Error rolling back transaction", MODULENAME );
634 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: Error rolling back transaction", MODULENAME );
637 osrfHashRemove(ctx->session->userData, "xact_id");
638 jsonObject* ret = jsonNewObject(ctx->session->session_id);
639 osrfAppRespondComplete( ctx, ret );
645 int dispatchCRUDMethod ( osrfMethodContext* ctx ) {
646 OSRF_METHOD_VERIFY_CONTEXT(ctx);
648 osrfHash* meta = (osrfHash*) ctx->method->userData;
649 osrfHash* class_obj = osrfHashGet( meta, "class" );
651 jsonObject * obj = NULL;
652 if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "create"))
653 obj = doCreate(ctx, class_obj, ctx->params);
655 if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "retrieve"))
656 obj = doRetrieve(ctx, class_obj, ctx->params);
658 if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "update"))
659 obj = doUpdate(ctx, class_obj, ctx->params);
661 if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "delete"))
662 obj = doDelete(ctx, class_obj, ctx->params);
664 if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "search")) {
666 obj = doSearch(ctx, class_obj, ctx->params);
667 jsonObjectIterator* itr = jsonNewObjectIterator( obj );
668 while ((cur = jsonObjectIteratorNext( itr ))) {
669 osrfAppRespond( ctx, jsonObjectClone(cur->item) );
671 jsonObjectIteratorFree(itr);
672 osrfAppRespondComplete( ctx, NULL );
675 osrfAppRespondComplete( ctx, obj );
683 jsonObject* doCreate(osrfMethodContext* ctx, osrfHash* meta, jsonObject* params ) {
685 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
686 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: No active transaction to roll back", MODULENAME );
694 jsonObject* doRetrieve(osrfMethodContext* ctx, osrfHash* meta, jsonObject* params ) {
698 char* id = jsonObjectToSimpleString(jsonObjectGetIndex(params, 0));
699 jsonObject* order_hash = jsonObjectGetIndex(params, 1);
703 "%s retrieving %s object with id %s",
705 osrfHashGet(meta, "fieldmapper"),
709 jsonObject* fake_params = jsonParseString("[]");
710 jsonObjectPush(fake_params, jsonParseString("{}"));
713 jsonObjectGetIndex(fake_params, 0),
714 osrfHashGet(meta, "primarykey"),
718 if (order_hash) jsonObjectPush(fake_params, jsonObjectClone(order_hash) );
720 jsonObject* list = doSearch( ctx,meta, fake_params);
721 obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
723 jsonObjectFree( list );
724 jsonObjectFree( fake_params );
729 jsonObject* doSearch(osrfMethodContext* ctx, osrfHash* meta, jsonObject* params ) {
733 jsonObject* search_hash = jsonObjectGetIndex(params, 0);
734 jsonObject* order_hash = jsonObjectGetIndex(params, 1);
736 growing_buffer* sql_buf = buffer_init(128);
737 buffer_fadd(sql_buf, "SELECT * FROM %s WHERE ", osrfHashGet(meta, "tablename") );
739 jsonObjectNode* node = NULL;
740 jsonObjectIterator* search_itr = jsonNewObjectIterator( search_hash );
743 while ( (node = jsonObjectIteratorNext( search_itr )) ) {
744 osrfHash* field = osrfHashGet( osrfHashGet(meta, "fields"), node->key );
746 if (!field) continue;
751 buffer_add(sql_buf, " AND ");
754 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
758 osrfHashGet(field, "name"),
759 atoi( jsonObjectToSimpleString(node->item) )
762 char* key_string = jsonObjectToSimpleString(node->item);
763 if ( dbi_conn_quote_string(dbhandle, &key_string) ) {
767 osrfHashGet(field, "name"),
772 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string);
778 jsonObjectIteratorFree(search_itr);
782 _tmp = jsonObjectGetKey( order_hash, "order_by" );
784 string = jsonObjectToSimpleString(_tmp);
793 _tmp = jsonObjectGetKey( order_hash, "limit" );
795 string = jsonObjectToSimpleString(_tmp);
804 _tmp = jsonObjectGetKey( order_hash, "offset" );
806 string = jsonObjectToSimpleString(_tmp);
816 buffer_add(sql_buf, ";");
818 char* sql = buffer_data(sql_buf);
819 buffer_free(sql_buf);
821 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
822 dbi_result result = dbi_conn_query(dbhandle, sql);
824 jsonObject* res_list = jsonParseString("[]");
826 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
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");
833 obj = oilsMakeJSONFromResult( result, meta );
834 jsonObjectPush(res_list, obj);
835 } while (dbi_result_next_row(result));
837 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
840 /* clean up the query */
841 dbi_result_free(result);
844 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]", MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
850 _tmp = jsonObjectGetKey( order_hash, "flesh" );
852 double x = jsonObjectGetNumber(_tmp);
857 jsonObjectIterator* itr = jsonNewObjectIterator( res_list );
858 while ((cur = jsonObjectIteratorNext( itr ))) {
860 osrfHash* links = osrfHashGet(meta, "links");
861 osrfHash* fields = osrfHashGet(meta, "fields");
865 osrfStringArray* link_fields;
867 jsonObject* flesh_fields = jsonObjectGetKey( order_hash, "flesh_fields" );
870 jsonObjectIterator* _i = jsonNewObjectIterator( flesh_fields );
871 link_fields = osrfNewStringArray(1);
872 while ((_f = jsonObjectIteratorNext( _i ))) {
873 osrfStringArrayAdd( link_fields, jsonObjectToSimpleString( _f->item ) );
876 link_fields = osrfHashKeys( links );
879 while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
881 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
883 osrfHash* kid_link = osrfHashGet(links, link_field);
884 if (!kid_link) continue;
886 osrfHash* field = osrfHashGet(fields, link_field);
887 if (!field) continue;
889 osrfHash* value_field = field;
891 osrfHash* kid_idl = osrfHashGet(idlHash, osrfHashGet(kid_link, "class"));
892 if (!kid_idl) continue;
894 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
895 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
898 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
899 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
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")
911 jsonObject* fake_params = jsonParseString("[]");
912 jsonObjectPush(fake_params, jsonParseString("{}")); // search hash
913 jsonObjectPush(fake_params, jsonParseString("{}")); // order/flesh hash
915 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
918 jsonObjectToSimpleString(
921 atoi( osrfHashGet(value_field, "array_position") )
926 osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
931 jsonObjectGetIndex(fake_params, 0),
932 osrfHashGet(kid_link, "key"),
933 jsonNewObject( search_key )
940 jsonObjectGetIndex(fake_params, 1),
942 jsonNewNumberObject( (double)((int)x - 1) )
947 jsonObjectGetIndex(fake_params, 1),
949 jsonObjectClone( flesh_fields )
953 if (jsonObjectGetKey(order_hash, "order_by")) {
955 jsonObjectGetIndex(fake_params, 1),
957 jsonObjectClone(jsonObjectGetKey(order_hash, "order_by"))
961 jsonObject* kids = doSearch(ctx, kid_idl, fake_params);
963 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
965 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" ))) {
966 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
969 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
970 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
972 jsonObjectFree( kids );
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"));
979 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
984 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
986 jsonObjectFree( fake_params );
988 if (flesh_fields) osrfStringArrayFree(link_fields);
990 jsonObjectIteratorFree(itr);
999 jsonObject* doUpdate(osrfMethodContext* ctx, osrfHash* meta, jsonObject* params ) {
1001 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
1002 osrfAppRequestRespondException( ctx->session, ctx->request, "%s: No active transaction to roll back", MODULENAME );
1009 jsonObject* doDelete(osrfMethodContext* ctx, osrfHash* meta, jsonObject* params ) {
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);
1018 char* pkey = osrfHashGet(meta, "primarykey");
1021 if (jsonObjectGetIndex(params, 0)->classname) {
1022 id = jsonObjectToSimpleString(
1024 jsonObjectGetIndex(params, 0),
1025 atoi( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "array_position") )
1029 id = jsonObjectToSimpleString(jsonObjectGetIndex(params, 0));
1034 "%s deleting %s object with %s = %s",
1036 osrfHashGet(meta, "fieldmapper"),
1041 obj = jsonParseString(id);
1043 if ( strcmp( osrfHashGet( osrfHashGet( osrfHashGet(meta, "fields"), pkey ), "primitive" ), "number" ) )
1044 dbi_conn_quote_string(dbhandle, &id);
1046 dbi_result result = dbi_conn_queryf(dbhandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
1049 jsonObjectFree(obj);
1050 obj = jsonNewObject(NULL);
1053 "%s ERROR deleting %s object with %s = %s",
1055 osrfHashGet(meta, "fieldmapper"),
1068 jsonObject* oilsMakeJSONFromResult( dbi_result result, osrfHash* meta) {
1069 if(!(result && meta)) return NULL;
1071 jsonObject* object = jsonParseString("[]");
1072 jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
1074 osrfHash* fields = osrfHashGet(meta, "fields");
1076 osrfLogDebug(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
1080 char dt_string[256];
1084 int columnIndex = 1;
1086 unsigned short type;
1087 const char* columnName;
1089 /* cycle through the column list */
1090 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
1092 osrfLogDebug(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
1094 fmIndex = -1; // reset the position
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);
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");
1105 if ( !virt || !pos || !(strcmp( virt, "true" )) ) continue;
1107 fmIndex = atoi( pos );
1108 osrfLogDebug(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
1111 if (dbi_result_field_is_null(result, columnName)) {
1112 jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
1117 case DBI_TYPE_INTEGER :
1119 if( attr & DBI_INTEGER_SIZE8 )
1120 jsonObjectSetIndex( object, fmIndex,
1121 jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
1123 jsonObjectSetIndex( object, fmIndex,
1124 jsonNewNumberObject(dbi_result_get_long(result, columnName)));
1128 case DBI_TYPE_DECIMAL :
1129 jsonObjectSetIndex( object, fmIndex,
1130 jsonNewNumberObject(dbi_result_get_double(result, columnName)));
1133 case DBI_TYPE_STRING :
1139 jsonNewObject( dbi_result_get_string(result, columnName) )
1144 case DBI_TYPE_DATETIME :
1146 memset(dt_string, '\0', 256);
1147 memset(&gmdt, '\0', sizeof(gmdt));
1148 memset(&_tmp_dt, '\0', sizeof(_tmp_dt));
1150 _tmp_dt = dbi_result_get_datetime(result, columnName);
1152 localtime_r( &_tmp_dt, &gmdt );
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);
1159 /* XXX ARG! My eyes! The goggles, they do nothing! */
1161 strftime(dt_string, 255, "%FT%T%z", &gmdt);
1164 jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
1168 case DBI_TYPE_BINARY :
1169 osrfLogError( OSRF_LOG_MARK,
1170 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);