2 #include "opensrf/osrf_application.h"
3 #include "opensrf/osrf_settings.h"
4 #include "opensrf/osrf_message.h"
5 #include "opensrf/utils.h"
6 #include "opensrf/osrf_json.h"
7 #include "opensrf/log.h"
8 #include "openils/oils_utils.h"
17 # define MODULENAME "open-ils.reporter-store"
20 # define MODULENAME "open-ils.pcrud"
22 # define MODULENAME "open-ils.cstore"
27 #define DISABLE_I18N 2
28 #define SELECT_DISTINCT 1
32 int osrfAppChildInit();
33 int osrfAppInitialize();
34 void osrfAppChildExit();
36 static int verifyObjectClass ( osrfMethodContext*, const jsonObject* );
38 int beginTransaction ( osrfMethodContext* );
39 int commitTransaction ( osrfMethodContext* );
40 int rollbackTransaction ( osrfMethodContext* );
42 int setSavepoint ( osrfMethodContext* );
43 int releaseSavepoint ( osrfMethodContext* );
44 int rollbackSavepoint ( osrfMethodContext* );
46 int doJSONSearch ( osrfMethodContext* );
48 int dispatchCRUDMethod ( osrfMethodContext* );
49 static jsonObject* doCreate ( osrfMethodContext*, int* );
50 static jsonObject* doRetrieve ( osrfMethodContext*, int* );
51 static jsonObject* doUpdate ( osrfMethodContext*, int* );
52 static jsonObject* doDelete ( osrfMethodContext*, int* );
53 static jsonObject* doFieldmapperSearch ( osrfMethodContext*, osrfHash*,
54 const jsonObject*, int* );
55 static jsonObject* oilsMakeFieldmapperFromResult( dbi_result, osrfHash* );
56 static jsonObject* oilsMakeJSONFromResult( dbi_result );
58 static char* searchSimplePredicate ( const char* op, const char* class,
59 osrfHash* field, const jsonObject* node );
60 static char* searchFunctionPredicate ( const char*, osrfHash*, const jsonObject*, const char* );
61 static char* searchFieldTransform ( const char*, osrfHash*, const jsonObject*);
62 static char* searchFieldTransformPredicate ( const char*, osrfHash*, jsonObject*, const char* );
63 static char* searchBETWEENPredicate ( const char*, osrfHash*, jsonObject* );
64 static char* searchINPredicate ( const char*, osrfHash*,
65 jsonObject*, const char*, osrfMethodContext* );
66 static char* searchPredicate ( const char*, osrfHash*, jsonObject*, osrfMethodContext* );
67 static char* searchJOIN ( const jsonObject*, osrfHash* );
68 static char* searchWHERE ( const jsonObject*, osrfHash*, int, osrfMethodContext* );
69 static char* buildSELECT ( jsonObject*, jsonObject*, osrfHash*, osrfMethodContext* );
71 char* SELECT ( osrfMethodContext*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, int );
73 void userDataFree( void* );
74 static void sessionDataFree( char*, void* );
75 static char* getSourceDefinition( osrfHash* );
76 static int str_is_true( const char* str );
77 static int obj_is_true( const jsonObject* obj );
78 static const char* json_type( int code );
79 static const char* get_primitive( osrfHash* field );
80 static const char* get_datatype( osrfHash* field );
81 static int is_identifier( const char* s);
84 static jsonObject* verifyUserPCRUD( osrfMethodContext* );
85 static int verifyObjectPCRUD( osrfMethodContext*, const jsonObject* );
88 static int child_initialized = 0; /* boolean */
90 static dbi_conn writehandle; /* our MASTER db connection */
91 static dbi_conn dbhandle; /* our CURRENT db connection */
92 //static osrfHash * readHandles;
93 static jsonObject* jsonNULL = NULL; //
94 static int max_flesh_depth = 100;
96 /* called when this process is about to exit */
97 void osrfAppChildExit() {
98 osrfLogDebug(OSRF_LOG_MARK, "Child is exiting, disconnecting from database...");
101 if (writehandle == dbhandle) same = 1;
103 dbi_conn_query(writehandle, "ROLLBACK;");
104 dbi_conn_close(writehandle);
107 if (dbhandle && !same)
108 dbi_conn_close(dbhandle);
110 // XXX add cleanup of readHandles whenever that gets used
115 int osrfAppInitialize() {
117 osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
118 osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
120 if (!oilsIDLInit( osrf_settings_host_value("/IDL") )) return 1; /* return non-zero to indicate error */
122 growing_buffer* method_name = buffer_init(64);
124 // Generic search thingy
125 buffer_add(method_name, MODULENAME);
126 buffer_add(method_name, ".json_query");
127 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
128 "doJSONSearch", "", 1, OSRF_METHOD_STREAMING );
131 // first we register all the transaction and savepoint methods
132 buffer_reset(method_name);
133 OSRF_BUFFER_ADD(method_name, MODULENAME);
134 OSRF_BUFFER_ADD(method_name, ".transaction.begin");
135 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
136 "beginTransaction", "", 0, 0 );
138 buffer_reset(method_name);
139 OSRF_BUFFER_ADD(method_name, MODULENAME);
140 OSRF_BUFFER_ADD(method_name, ".transaction.commit");
141 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
142 "commitTransaction", "", 0, 0 );
144 buffer_reset(method_name);
145 OSRF_BUFFER_ADD(method_name, MODULENAME);
146 OSRF_BUFFER_ADD(method_name, ".transaction.rollback");
147 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
148 "rollbackTransaction", "", 0, 0 );
150 buffer_reset(method_name);
151 OSRF_BUFFER_ADD(method_name, MODULENAME);
152 OSRF_BUFFER_ADD(method_name, ".savepoint.set");
153 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
154 "setSavepoint", "", 1, 0 );
156 buffer_reset(method_name);
157 OSRF_BUFFER_ADD(method_name, MODULENAME);
158 OSRF_BUFFER_ADD(method_name, ".savepoint.release");
159 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
160 "releaseSavepoint", "", 1, 0 );
162 buffer_reset(method_name);
163 OSRF_BUFFER_ADD(method_name, MODULENAME);
164 OSRF_BUFFER_ADD(method_name, ".savepoint.rollback");
165 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
166 "rollbackSavepoint", "", 1, 0 );
168 buffer_free(method_name);
170 static const char* global_method[] = {
178 const int global_method_count
179 = sizeof( global_method ) / sizeof ( global_method[0] );
183 osrfStringArray* classes = osrfHashKeys( oilsIDL() );
184 osrfLogDebug(OSRF_LOG_MARK, "%d classes loaded", classes->size );
185 osrfLogDebug(OSRF_LOG_MARK,
186 "At least %d methods will be generated", classes->size * global_method_count);
188 while ( (classname = osrfStringArrayGetString(classes, c_index++)) ) {
189 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
191 osrfHash* idlClass = osrfHashGet(oilsIDL(), classname);
193 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), MODULENAME )) {
194 osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on", MODULENAME, classname);
198 if ( str_is_true( osrfHashGet(idlClass, "virtual") ) ) {
199 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
203 // Look up some other attributes of the current class
204 const char* idlClass_fieldmapper = osrfHashGet(idlClass, "fieldmapper");
205 const char* readonly = osrfHashGet(idlClass, "readonly");
207 osrfHash* idlClass_permacrud = osrfHashGet(idlClass, "permacrud");
211 for( i = 0; i < global_method_count; ++i ) {
212 const char* method_type = global_method[ i ];
213 osrfLogDebug(OSRF_LOG_MARK,
214 "Using files to build %s class methods for %s", method_type, classname);
216 if (!idlClass_fieldmapper) continue;
219 if (!idlClass_permacrud) continue;
221 const char* tmp_method = method_type;
222 if ( *tmp_method == 'i' || *tmp_method == 's') {
223 tmp_method = "retrieve";
225 if (!osrfHashGet( idlClass_permacrud, tmp_method )) continue;
228 if ( str_is_true( readonly ) &&
229 ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd')
232 osrfHash* method_meta = osrfNewHash();
233 osrfHashSet(method_meta, idlClass, "class");
235 method_name = buffer_init(64);
237 buffer_fadd(method_name, "%s.%s.%s", MODULENAME, method_type, classname);
241 char* _fm = strdup( idlClass_fieldmapper );
242 part = strtok_r(_fm, ":", &st_tmp);
244 buffer_fadd(method_name, "%s.direct.%s", MODULENAME, part);
246 while ((part = strtok_r(NULL, ":", &st_tmp))) {
247 OSRF_BUFFER_ADD_CHAR(method_name, '.');
248 OSRF_BUFFER_ADD(method_name, part);
250 OSRF_BUFFER_ADD_CHAR(method_name, '.');
251 OSRF_BUFFER_ADD(method_name, method_type);
255 char* method = buffer_release(method_name);
257 osrfHashSet( method_meta, method, "methodname" );
258 osrfHashSet( method_meta, strdup(method_type), "methodtype" );
261 if (*method_type == 'i' || *method_type == 's') {
262 flags = flags | OSRF_METHOD_STREAMING;
265 osrfAppRegisterExtendedMethod(
268 "dispatchCRUDMethod",
282 static char* getSourceDefinition( osrfHash* class ) {
284 char* tabledef = osrfHashGet(class, "tablename");
287 tabledef = strdup(tabledef);
289 tabledef = osrfHashGet(class, "source_definition");
291 growing_buffer* tablebuf = buffer_init(128);
292 buffer_fadd( tablebuf, "(%s)", tabledef );
293 tabledef = buffer_release(tablebuf);
295 const char* classname = osrfHashGet( class, "classname" );
300 "%s ERROR No tablename or source_definition for class \"%s\"",
311 * Connects to the database
313 int osrfAppChildInit() {
315 osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
316 dbi_initialize(NULL);
317 osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
319 char* driver = osrf_settings_host_value("/apps/%s/app_settings/driver", MODULENAME);
320 char* user = osrf_settings_host_value("/apps/%s/app_settings/database/user", MODULENAME);
321 char* host = osrf_settings_host_value("/apps/%s/app_settings/database/host", MODULENAME);
322 char* port = osrf_settings_host_value("/apps/%s/app_settings/database/port", MODULENAME);
323 char* db = osrf_settings_host_value("/apps/%s/app_settings/database/db", MODULENAME);
324 char* pw = osrf_settings_host_value("/apps/%s/app_settings/database/pw", MODULENAME);
325 char* md = osrf_settings_host_value("/apps/%s/app_settings/max_query_recursion", MODULENAME);
327 osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
328 writehandle = dbi_conn_new(driver);
331 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
334 osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
336 osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database. host=%s, "
337 "port=%s, user=%s, pw=%s, db=%s", MODULENAME, host, port, user, pw, db );
339 if(host) dbi_conn_set_option(writehandle, "host", host );
340 if(port) dbi_conn_set_option_numeric( writehandle, "port", atoi(port) );
341 if(user) dbi_conn_set_option(writehandle, "username", user);
342 if(pw) dbi_conn_set_option(writehandle, "password", pw );
343 if(db) dbi_conn_set_option(writehandle, "dbname", db );
345 if(md) max_flesh_depth = atoi(md);
346 if(max_flesh_depth < 0) max_flesh_depth = 1;
347 if(max_flesh_depth > 1000) max_flesh_depth = 1000;
356 if (dbi_conn_connect(writehandle) < 0) {
358 if (dbi_conn_connect(writehandle) < 0) {
359 dbi_conn_error(writehandle, &err);
360 osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
365 osrfLogInfo(OSRF_LOG_MARK, "%s successfully connected to the database", MODULENAME);
371 osrfStringArray* classes = osrfHashKeys( oilsIDL() );
373 while ( (classname = osrfStringArrayGetString(classes, i++)) ) {
374 osrfHash* class = osrfHashGet( oilsIDL(), classname );
375 osrfHash* fields = osrfHashGet( class, "fields" );
377 if( str_is_true( osrfHashGet(class, "virtual") ) ) {
378 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
382 char* tabledef = getSourceDefinition(class);
384 tabledef = strdup( "(null)" );
386 growing_buffer* sql_buf = buffer_init(32);
387 buffer_fadd( sql_buf, "SELECT * FROM %s AS x WHERE 1=0;", tabledef );
391 char* sql = buffer_release(sql_buf);
392 osrfLogDebug(OSRF_LOG_MARK, "%s Investigatory SQL = %s", MODULENAME, sql);
394 dbi_result result = dbi_conn_query(writehandle, sql);
400 const char* columnName;
402 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
404 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
406 /* fetch the fieldmapper index */
407 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
409 osrfLogDebug(OSRF_LOG_MARK, "Found [%s] in IDL hash...", (char*)columnName);
411 /* determine the field type and storage attributes */
412 type = dbi_result_get_field_type(result, columnName);
413 attr = dbi_result_get_field_attribs(result, columnName);
417 case DBI_TYPE_INTEGER :
419 if ( !osrfHashGet(_f, "primitive") )
420 osrfHashSet(_f,"number", "primitive");
422 if( attr & DBI_INTEGER_SIZE8 )
423 osrfHashSet(_f,"INT8", "datatype");
425 osrfHashSet(_f,"INT", "datatype");
428 case DBI_TYPE_DECIMAL :
429 if ( !osrfHashGet(_f, "primitive") )
430 osrfHashSet(_f,"number", "primitive");
432 osrfHashSet(_f,"NUMERIC", "datatype");
435 case DBI_TYPE_STRING :
436 if ( !osrfHashGet(_f, "primitive") )
437 osrfHashSet(_f,"string", "primitive");
438 osrfHashSet(_f,"TEXT", "datatype");
441 case DBI_TYPE_DATETIME :
442 if ( !osrfHashGet(_f, "primitive") )
443 osrfHashSet(_f,"string", "primitive");
445 osrfHashSet(_f,"TIMESTAMP", "datatype");
448 case DBI_TYPE_BINARY :
449 if ( !osrfHashGet(_f, "primitive") )
450 osrfHashSet(_f,"string", "primitive");
452 osrfHashSet(_f,"BYTEA", "datatype");
457 "Setting [%s] to primitive [%s] and datatype [%s]...",
459 osrfHashGet(_f, "primitive"),
460 osrfHashGet(_f, "datatype")
464 dbi_result_free(result);
466 osrfLogDebug(OSRF_LOG_MARK, "No data found for class [%s]...", (char*)classname);
470 osrfStringArrayFree(classes);
472 child_initialized = 1;
477 This function is a sleazy hack intended *only* for testing and
478 debugging. Any real server process should initialize the
479 database connection by calling osrfAppChildInit().
481 void set_cstore_dbi_conn( dbi_conn conn ) {
482 dbhandle = writehandle = conn;
485 void userDataFree( void* blob ) {
486 osrfHashFree( (osrfHash*)blob );
490 static void sessionDataFree( char* key, void* item ) {
491 if (!(strcmp(key,"xact_id"))) {
493 dbi_conn_query(writehandle, "ROLLBACK;");
500 int beginTransaction ( osrfMethodContext* ctx ) {
501 if(osrfMethodVerifyContext( ctx )) {
502 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
507 jsonObject* user = verifyUserPCRUD( ctx );
508 if (!user) return -1;
509 jsonObjectFree(user);
512 dbi_result result = dbi_conn_query(writehandle, "START TRANSACTION;");
514 osrfLogError(OSRF_LOG_MARK, "%s: Error starting transaction", MODULENAME );
515 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error starting transaction" );
518 jsonObject* ret = jsonNewObject(ctx->session->session_id);
519 osrfAppRespondComplete( ctx, ret );
522 if (!ctx->session->userData) {
523 ctx->session->userData = osrfNewHash();
524 osrfHashSetCallback((osrfHash*)ctx->session->userData, &sessionDataFree);
527 osrfHashSet( (osrfHash*)ctx->session->userData, strdup( ctx->session->session_id ), "xact_id" );
528 ctx->session->userDataFree = &userDataFree;
534 int setSavepoint ( osrfMethodContext* ctx ) {
535 if(osrfMethodVerifyContext( ctx )) {
536 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
543 jsonObject* user = verifyUserPCRUD( ctx );
544 if (!user) return -1;
545 jsonObjectFree(user);
548 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
549 osrfAppSessionStatus(
551 OSRF_STATUS_INTERNALSERVERERROR,
552 "osrfMethodException",
554 "No active transaction -- required for savepoints"
559 char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, spNamePos));
561 dbi_result result = dbi_conn_queryf(writehandle, "SAVEPOINT \"%s\";", spName);
565 "%s: Error creating savepoint %s in transaction %s",
568 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
570 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error creating savepoint" );
574 jsonObject* ret = jsonNewObject(spName);
575 osrfAppRespondComplete( ctx, ret );
582 int releaseSavepoint ( osrfMethodContext* ctx ) {
583 if(osrfMethodVerifyContext( ctx )) {
584 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
591 jsonObject* user = verifyUserPCRUD( ctx );
592 if (!user) return -1;
593 jsonObjectFree(user);
596 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
597 osrfAppSessionStatus(
599 OSRF_STATUS_INTERNALSERVERERROR,
600 "osrfMethodException",
602 "No active transaction -- required for savepoints"
607 char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, spNamePos));
609 dbi_result result = dbi_conn_queryf(writehandle, "RELEASE SAVEPOINT \"%s\";", spName);
613 "%s: Error releasing savepoint %s in transaction %s",
616 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
618 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error releasing savepoint" );
622 jsonObject* ret = jsonNewObject(spName);
623 osrfAppRespondComplete( ctx, ret );
630 int rollbackSavepoint ( osrfMethodContext* ctx ) {
631 if(osrfMethodVerifyContext( ctx )) {
632 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
639 jsonObject* user = verifyUserPCRUD( ctx );
640 if (!user) return -1;
641 jsonObjectFree(user);
644 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
645 osrfAppSessionStatus(
647 OSRF_STATUS_INTERNALSERVERERROR,
648 "osrfMethodException",
650 "No active transaction -- required for savepoints"
655 char* spName = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, spNamePos));
657 dbi_result result = dbi_conn_queryf(writehandle, "ROLLBACK TO SAVEPOINT \"%s\";", spName);
661 "%s: Error rolling back savepoint %s in transaction %s",
664 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
666 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error rolling back savepoint" );
670 jsonObject* ret = jsonNewObject(spName);
671 osrfAppRespondComplete( ctx, ret );
678 int commitTransaction ( osrfMethodContext* ctx ) {
679 if(osrfMethodVerifyContext( ctx )) {
680 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
685 jsonObject* user = verifyUserPCRUD( ctx );
686 if (!user) return -1;
687 jsonObjectFree(user);
690 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
691 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to commit" );
695 dbi_result result = dbi_conn_query(writehandle, "COMMIT;");
697 osrfLogError(OSRF_LOG_MARK, "%s: Error committing transaction", MODULENAME );
698 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error committing transaction" );
701 osrfHashRemove(ctx->session->userData, "xact_id");
702 jsonObject* ret = jsonNewObject(ctx->session->session_id);
703 osrfAppRespondComplete( ctx, ret );
709 int rollbackTransaction ( osrfMethodContext* ctx ) {
710 if(osrfMethodVerifyContext( ctx )) {
711 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
716 jsonObject* user = verifyUserPCRUD( ctx );
717 if (!user) return -1;
718 jsonObjectFree(user);
721 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
722 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to roll back" );
726 dbi_result result = dbi_conn_query(writehandle, "ROLLBACK;");
728 osrfLogError(OSRF_LOG_MARK, "%s: Error rolling back transaction", MODULENAME );
729 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error rolling back transaction" );
732 osrfHashRemove(ctx->session->userData, "xact_id");
733 jsonObject* ret = jsonNewObject(ctx->session->session_id);
734 osrfAppRespondComplete( ctx, ret );
740 int dispatchCRUDMethod ( osrfMethodContext* ctx ) {
741 if(osrfMethodVerifyContext( ctx )) {
742 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
746 osrfHash* meta = (osrfHash*) ctx->method->userData;
747 osrfHash* class_obj = osrfHashGet( meta, "class" );
751 const char* methodtype = osrfHashGet(meta, "methodtype");
752 jsonObject * obj = NULL;
754 if (!strcmp(methodtype, "create")) {
755 obj = doCreate(ctx, &err);
756 osrfAppRespondComplete( ctx, obj );
758 else if (!strcmp(methodtype, "retrieve")) {
759 obj = doRetrieve(ctx, &err);
760 osrfAppRespondComplete( ctx, obj );
762 else if (!strcmp(methodtype, "update")) {
763 obj = doUpdate(ctx, &err);
764 osrfAppRespondComplete( ctx, obj );
766 else if (!strcmp(methodtype, "delete")) {
767 obj = doDelete(ctx, &err);
768 osrfAppRespondComplete( ctx, obj );
770 else if (!strcmp(methodtype, "search")) {
772 jsonObject* _p = jsonObjectClone( ctx->params );
775 _p = jsonParseString("[]");
776 jsonObjectPush(_p, jsonObjectClone(jsonObjectGetIndex(ctx->params, 1)));
777 jsonObjectPush(_p, jsonObjectClone(jsonObjectGetIndex(ctx->params, 2)));
780 obj = doFieldmapperSearch(ctx, class_obj, _p, &err);
786 jsonIterator* itr = jsonNewIterator( obj );
787 while ((cur = jsonIteratorNext( itr ))) {
789 if(!verifyObjectPCRUD(ctx, cur)) continue;
791 osrfAppRespond( ctx, cur );
793 jsonIteratorFree(itr);
794 osrfAppRespondComplete( ctx, NULL );
796 } else if (!strcmp(methodtype, "id_list")) {
798 jsonObject* _p = jsonObjectClone( ctx->params );
801 _p = jsonParseString("[]");
802 jsonObjectPush(_p, jsonObjectClone(jsonObjectGetIndex(ctx->params, 1)));
803 jsonObjectPush(_p, jsonObjectClone(jsonObjectGetIndex(ctx->params, 2)));
806 if (jsonObjectGetIndex( _p, 1 )) {
807 jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "select" );
808 jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "no_i18n" );
809 jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "flesh" );
810 jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "flesh_columns" );
812 jsonObjectSetIndex( _p, 1, jsonNewObjectType(JSON_HASH) );
815 jsonObjectSetKey( jsonObjectGetIndex( _p, 1 ), "no_i18n", jsonNewBoolObject( 1 ) );
818 jsonObjectGetIndex( _p, 1 ),
821 "{ \"%s\":[\"%s\"] }",
822 osrfHashGet( class_obj, "classname" ),
823 osrfHashGet( class_obj, "primarykey" )
827 obj = doFieldmapperSearch(ctx, class_obj, _p, &err);
833 jsonIterator* itr = jsonNewIterator( obj );
834 while ((cur = jsonIteratorNext( itr ))) {
836 if(!verifyObjectPCRUD(ctx, cur)) continue;
840 oilsFMGetObject( cur, osrfHashGet( class_obj, "primarykey" ) )
843 jsonIteratorFree(itr);
844 osrfAppRespondComplete( ctx, NULL );
847 osrfAppRespondComplete( ctx, obj );
855 static int verifyObjectClass ( osrfMethodContext* ctx, const jsonObject* param ) {
858 osrfHash* meta = (osrfHash*) ctx->method->userData;
859 osrfHash* class = osrfHashGet( meta, "class" );
861 if (!param->classname || (strcmp( osrfHashGet(class, "classname"), param->classname ))) {
863 growing_buffer* msg = buffer_init(128);
866 "%s: %s method for type %s was passed a %s",
868 osrfHashGet(meta, "methodtype"),
869 osrfHashGet(class, "classname"),
873 char* m = buffer_release(msg);
874 osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException", ctx->request, m );
882 ret = verifyObjectPCRUD( ctx, param );
890 static jsonObject* verifyUserPCRUD( osrfMethodContext* ctx ) {
891 char* auth = jsonObjectToSimpleString( jsonObjectGetIndex( ctx->params, 0 ) );
892 jsonObject* auth_object = jsonNewObject(auth);
893 jsonObject* user = oilsUtilsQuickReq("open-ils.auth","open-ils.auth.session.retrieve", auth_object);
894 jsonObjectFree(auth_object);
896 if (!user->classname || strcmp(user->classname, "au")) {
898 growing_buffer* msg = buffer_init(128);
901 "%s: permacrud received a bad auth token: %s",
906 char* m = buffer_release(msg);
907 osrfAppSessionStatus( ctx->session, OSRF_STATUS_UNAUTHORIZED, "osrfMethodException", ctx->request, m );
910 jsonObjectFree(user);
919 static int verifyObjectPCRUD ( osrfMethodContext* ctx, const jsonObject* obj ) {
921 dbhandle = writehandle;
923 osrfHash* meta = (osrfHash*) ctx->method->userData;
924 osrfHash* class = osrfHashGet( meta, "class" );
925 char* method_type = strdup( osrfHashGet(meta, "methodtype") );
928 if ( ( *method_type == 's' || *method_type == 'i' ) ) {
930 method_type = strdup("retrieve"); // search and id_list are equivelant to retrieve for this
931 } else if ( *method_type == 'u' || *method_type == 'd' ) {
932 fetch = 1; // MUST go to the db for the object for update and delete
935 osrfHash* pcrud = osrfHashGet( osrfHashGet(class, "permacrud"), method_type );
939 // No permacrud for this method type on this class
941 growing_buffer* msg = buffer_init(128);
944 "%s: %s on class %s has no permacrud IDL entry",
946 osrfHashGet(meta, "methodtype"),
947 osrfHashGet(class, "classname")
950 char* m = buffer_release(msg);
951 osrfAppSessionStatus( ctx->session, OSRF_STATUS_FORBIDDEN, "osrfMethodException", ctx->request, m );
958 jsonObject* user = verifyUserPCRUD( ctx );
961 int userid = atoi( oilsFMGetString( user, "id" ) );
962 jsonObjectFree(user);
964 osrfStringArray* permission = osrfHashGet(pcrud, "permission");
965 osrfStringArray* local_context = osrfHashGet(pcrud, "local_context");
966 osrfHash* foreign_context = osrfHashGet(pcrud, "foreign_context");
968 osrfStringArray* context_org_array = osrfNewStringArray(1);
971 char* pkey_value = NULL;
972 if ( str_is_true( osrfHashGet(pcrud, "global_required") ) ) {
973 osrfLogDebug( OSRF_LOG_MARK, "global-level permissions required, fetching top of the org tree" );
975 // check for perm at top of org tree
976 jsonObject* _tmp_params = jsonParseString("[{\"parent_ou\":null}]");
977 jsonObject* _list = doFieldmapperSearch(ctx, osrfHashGet( oilsIDL(), "aou" ), _tmp_params, &err);
979 jsonObject* _tree_top = jsonObjectGetIndex(_list, 0);
982 jsonObjectFree(_tmp_params);
983 jsonObjectFree(_list);
985 growing_buffer* msg = buffer_init(128);
986 OSRF_BUFFER_ADD( msg, MODULENAME );
987 OSRF_BUFFER_ADD( msg,
988 ": Internal error, could not find the top of the org tree (parent_ou = NULL)" );
990 char* m = buffer_release(msg);
991 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, m );
997 osrfStringArrayAdd( context_org_array, oilsFMGetString( _tree_top, "id" ) );
998 osrfLogDebug( OSRF_LOG_MARK, "top of the org tree is %s", osrfStringArrayGetString(context_org_array, 0) );
1000 jsonObjectFree(_tmp_params);
1001 jsonObjectFree(_list);
1004 osrfLogDebug( OSRF_LOG_MARK, "global-level permissions not required, fetching context org ids" );
1005 char* pkey = osrfHashGet(class, "primarykey");
1006 jsonObject *param = NULL;
1008 if (obj->classname) {
1009 pkey_value = oilsFMGetString( obj, pkey );
1010 if (!fetch) param = jsonObjectClone(obj);
1011 osrfLogDebug( OSRF_LOG_MARK, "Object supplied, using primary key value of %s", pkey_value );
1013 pkey_value = jsonObjectToSimpleString( obj );
1015 osrfLogDebug( OSRF_LOG_MARK, "Object not supplied, using primary key value of %s and retrieving from the database", pkey_value );
1019 jsonObject* _tmp_params = jsonParseStringFmt("[{\"%s\":\"%s\"}]", pkey, pkey_value);
1020 jsonObject* _list = doFieldmapperSearch(
1027 param = jsonObjectClone(jsonObjectGetIndex(_list, 0));
1029 jsonObjectFree(_tmp_params);
1030 jsonObjectFree(_list);
1034 osrfLogDebug( OSRF_LOG_MARK, "Object not found in the database with primary key %s of %s", pkey, pkey_value );
1036 growing_buffer* msg = buffer_init(128);
1039 "%s: no object found with primary key %s of %s",
1045 char* m = buffer_release(msg);
1046 osrfAppSessionStatus(
1048 OSRF_STATUS_INTERNALSERVERERROR,
1049 "osrfMethodException",
1055 if (pkey_value) free(pkey_value);
1060 if (local_context->size > 0) {
1061 osrfLogDebug( OSRF_LOG_MARK, "%d class-local context field(s) specified", local_context->size);
1063 char* lcontext = NULL;
1064 while ( (lcontext = osrfStringArrayGetString(local_context, i++)) ) {
1065 osrfStringArrayAdd( context_org_array, oilsFMGetString( param, lcontext ) );
1068 "adding class-local field %s (value: %s) to the context org list",
1070 osrfStringArrayGetString(context_org_array, context_org_array->size - 1)
1075 osrfStringArray* class_list;
1077 if (foreign_context) {
1078 class_list = osrfHashKeys( foreign_context );
1079 osrfLogDebug( OSRF_LOG_MARK, "%d foreign context classes(s) specified", class_list->size);
1081 if (class_list->size > 0) {
1084 char* class_name = NULL;
1085 while ( (class_name = osrfStringArrayGetString(class_list, i++)) ) {
1086 osrfHash* fcontext = osrfHashGet(foreign_context, class_name);
1090 "%d foreign context fields(s) specified for class %s",
1091 ((osrfStringArray*)osrfHashGet(fcontext,"context"))->size,
1095 char* foreign_pkey = osrfHashGet(fcontext, "field");
1096 char* foreign_pkey_value = oilsFMGetString(param, osrfHashGet(fcontext, "fkey"));
1098 jsonObject* _tmp_params = jsonParseStringFmt(
1099 "[{\"%s\":\"%s\"}]",
1104 jsonObject* _list = doFieldmapperSearch(
1106 osrfHashGet( oilsIDL(), class_name ),
1111 jsonObject* _fparam = jsonObjectClone(jsonObjectGetIndex(_list, 0));
1112 jsonObjectFree(_tmp_params);
1113 jsonObjectFree(_list);
1115 osrfStringArray* jump_list = osrfHashGet(fcontext, "jump");
1117 if (_fparam && jump_list) {
1120 while ( (flink = osrfStringArrayGetString(jump_list, k++)) && _fparam ) {
1121 free(foreign_pkey_value);
1123 osrfHash* foreign_link_hash = oilsIDLFindPath( "/%s/links/%s", _fparam->classname, flink );
1125 foreign_pkey_value = oilsFMGetString(_fparam, flink);
1126 foreign_pkey = osrfHashGet( foreign_link_hash, "key" );
1128 _tmp_params = jsonParseStringFmt(
1129 "[{\"%s\":\"%s\"}]",
1134 _list = doFieldmapperSearch(
1136 osrfHashGet( oilsIDL(), osrfHashGet( foreign_link_hash, "class" ) ),
1141 _fparam = jsonObjectClone(jsonObjectGetIndex(_list, 0));
1142 jsonObjectFree(_tmp_params);
1143 jsonObjectFree(_list);
1150 growing_buffer* msg = buffer_init(128);
1153 "%s: no object found with primary key %s of %s",
1159 char* m = buffer_release(msg);
1160 osrfAppSessionStatus(
1162 OSRF_STATUS_INTERNALSERVERERROR,
1163 "osrfMethodException",
1169 osrfStringArrayFree(class_list);
1170 free(foreign_pkey_value);
1171 jsonObjectFree(param);
1176 free(foreign_pkey_value);
1179 char* foreign_field = NULL;
1180 while ( (foreign_field = osrfStringArrayGetString(osrfHashGet(fcontext,"context"), j++)) ) {
1181 osrfStringArrayAdd( context_org_array, oilsFMGetString( _fparam, foreign_field ) );
1184 "adding foreign class %s field %s (value: %s) to the context org list",
1187 osrfStringArrayGetString(context_org_array, context_org_array->size - 1)
1191 jsonObjectFree(_fparam);
1194 osrfStringArrayFree(class_list);
1198 jsonObjectFree(param);
1201 char* context_org = NULL;
1205 if (permission->size == 0) {
1206 osrfLogDebug( OSRF_LOG_MARK, "No permission specified for this action, passing through" );
1211 while ( (perm = osrfStringArrayGetString(permission, i++)) ) {
1213 while ( (context_org = osrfStringArrayGetString(context_org_array, j++)) ) {
1219 "Checking object permission [%s] for user %d on object %s (class %s) at org %d",
1223 osrfHashGet(class, "classname"),
1227 result = dbi_conn_queryf(
1229 "SELECT permission.usr_has_object_perm(%d, '%s', '%s', '%s', %d) AS has_perm;",
1232 osrfHashGet(class, "classname"),
1240 "Recieved a result for object permission [%s] for user %d on object %s (class %s) at org %d",
1244 osrfHashGet(class, "classname"),
1248 if (dbi_result_first_row(result)) {
1249 jsonObject* return_val = oilsMakeJSONFromResult( result );
1250 char* has_perm = jsonObjectToSimpleString( jsonObjectGetKeyConst(return_val, "has_perm") );
1254 "Status of object permission [%s] for user %d on object %s (class %s) at org %d is %s",
1258 osrfHashGet(class, "classname"),
1263 if ( *has_perm == 't' ) OK = 1;
1265 jsonObjectFree(return_val);
1268 dbi_result_free(result);
1273 osrfLogDebug( OSRF_LOG_MARK, "Checking non-object permission [%s] for user %d at org %d", perm, userid, atoi(context_org) );
1274 result = dbi_conn_queryf(
1276 "SELECT permission.usr_has_perm(%d, '%s', %d) AS has_perm;",
1283 osrfLogDebug( OSRF_LOG_MARK, "Received a result for permission [%s] for user %d at org %d", perm, userid, atoi(context_org) );
1284 if (dbi_result_first_row(result)) {
1285 jsonObject* return_val = oilsMakeJSONFromResult( result );
1286 char* has_perm = jsonObjectToSimpleString( jsonObjectGetKeyConst(return_val, "has_perm") );
1287 osrfLogDebug( OSRF_LOG_MARK, "Status of permission [%s] for user %d at org %d is [%s]", perm, userid, atoi(context_org), has_perm );
1288 if ( *has_perm == 't' ) OK = 1;
1290 jsonObjectFree(return_val);
1293 dbi_result_free(result);
1301 if (pkey_value) free(pkey_value);
1302 osrfStringArrayFree(context_org_array);
1309 static jsonObject* doCreate(osrfMethodContext* ctx, int* err ) {
1311 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
1313 jsonObject* target = jsonObjectGetIndex( ctx->params, 1 );
1314 jsonObject* options = jsonObjectGetIndex( ctx->params, 2 );
1316 jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
1317 jsonObject* options = jsonObjectGetIndex( ctx->params, 1 );
1320 if (!verifyObjectClass(ctx, target)) {
1325 osrfLogDebug( OSRF_LOG_MARK, "Object seems to be of the correct type" );
1327 char* trans_id = NULL;
1328 if( ctx->session && ctx->session->userData )
1329 trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
1332 osrfLogError( OSRF_LOG_MARK, "No active transaction -- required for CREATE" );
1334 osrfAppSessionStatus(
1336 OSRF_STATUS_BADREQUEST,
1337 "osrfMethodException",
1339 "No active transaction -- required for CREATE"
1345 // The following test is harmless but redundant. If a class is
1346 // readonly, we don't register a create method for it.
1347 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
1348 osrfAppSessionStatus(
1350 OSRF_STATUS_BADREQUEST,
1351 "osrfMethodException",
1353 "Cannot INSERT readonly class"
1359 // Set the last_xact_id
1360 int index = oilsIDL_ntop( target->classname, "last_xact_id" );
1362 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
1363 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
1366 osrfLogDebug( OSRF_LOG_MARK, "There is a transaction running..." );
1368 dbhandle = writehandle;
1370 osrfHash* fields = osrfHashGet(meta, "fields");
1371 char* pkey = osrfHashGet(meta, "primarykey");
1372 char* seq = osrfHashGet(meta, "sequence");
1374 growing_buffer* table_buf = buffer_init(128);
1375 growing_buffer* col_buf = buffer_init(128);
1376 growing_buffer* val_buf = buffer_init(128);
1378 OSRF_BUFFER_ADD(table_buf, "INSERT INTO ");
1379 OSRF_BUFFER_ADD(table_buf, osrfHashGet(meta, "tablename"));
1380 OSRF_BUFFER_ADD_CHAR( col_buf, '(' );
1381 buffer_add(val_buf,"VALUES (");
1387 osrfStringArray* field_list = osrfHashKeys( fields );
1388 while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
1390 osrfHash* field = osrfHashGet( fields, field_name );
1392 if( str_is_true( osrfHashGet( field, "virtual" ) ) )
1395 const jsonObject* field_object = oilsFMGetObject( target, field_name );
1398 if (field_object && field_object->classname) {
1399 value = oilsFMGetString(
1401 (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
1404 value = jsonObjectToSimpleString( field_object );
1411 OSRF_BUFFER_ADD_CHAR( col_buf, ',' );
1412 OSRF_BUFFER_ADD_CHAR( val_buf, ',' );
1415 buffer_add(col_buf, field_name);
1417 if (!field_object || field_object->type == JSON_NULL) {
1418 buffer_add( val_buf, "DEFAULT" );
1420 } else if ( !strcmp(get_primitive( field ), "number") ) {
1421 const char* numtype = get_datatype( field );
1422 if ( !strcmp( numtype, "INT8") ) {
1423 buffer_fadd( val_buf, "%lld", atoll(value) );
1425 } else if ( !strcmp( numtype, "INT") ) {
1426 buffer_fadd( val_buf, "%d", atoi(value) );
1428 } else if ( !strcmp( numtype, "NUMERIC") ) {
1429 buffer_fadd( val_buf, "%f", atof(value) );
1432 if ( dbi_conn_quote_string(writehandle, &value) ) {
1433 OSRF_BUFFER_ADD( val_buf, value );
1436 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
1437 osrfAppSessionStatus(
1439 OSRF_STATUS_INTERNALSERVERERROR,
1440 "osrfMethodException",
1442 "Error quoting string -- please see the error log for more details"
1445 buffer_free(table_buf);
1446 buffer_free(col_buf);
1447 buffer_free(val_buf);
1458 OSRF_BUFFER_ADD_CHAR( col_buf, ')' );
1459 OSRF_BUFFER_ADD_CHAR( val_buf, ')' );
1461 char* table_str = buffer_release(table_buf);
1462 char* col_str = buffer_release(col_buf);
1463 char* val_str = buffer_release(val_buf);
1464 growing_buffer* sql = buffer_init(128);
1465 buffer_fadd( sql, "%s %s %s;", table_str, col_str, val_str );
1470 char* query = buffer_release(sql);
1472 osrfLogDebug(OSRF_LOG_MARK, "%s: Insert SQL [%s]", MODULENAME, query);
1475 dbi_result result = dbi_conn_query(writehandle, query);
1477 jsonObject* obj = NULL;
1480 obj = jsonNewObject(NULL);
1483 "%s ERROR inserting %s object using query [%s]",
1485 osrfHashGet(meta, "fieldmapper"),
1488 osrfAppSessionStatus(
1490 OSRF_STATUS_INTERNALSERVERERROR,
1491 "osrfMethodException",
1493 "INSERT error -- please see the error log for more details"
1498 char* id = oilsFMGetString(target, pkey);
1500 unsigned long long new_id = dbi_conn_sequence_last(writehandle, seq);
1501 growing_buffer* _id = buffer_init(10);
1502 buffer_fadd(_id, "%lld", new_id);
1503 id = buffer_release(_id);
1506 // Find quietness specification, if present
1507 char* quiet_str = NULL;
1509 const jsonObject* quiet_obj = jsonObjectGetKeyConst( options, "quiet" );
1511 quiet_str = jsonObjectToSimpleString( quiet_obj );
1514 if( str_is_true( quiet_str ) ) { // if quietness is specified
1515 obj = jsonNewObject(id);
1519 jsonObject* fake_params = jsonNewObjectType(JSON_ARRAY);
1520 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH));
1523 jsonObjectGetIndex(fake_params, 0),
1528 jsonObject* list = doFieldmapperSearch( ctx,meta, fake_params, err);
1531 jsonObjectFree( fake_params );
1534 obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
1537 jsonObjectFree( list );
1538 jsonObjectFree( fake_params );
1541 if(quiet_str) free(quiet_str);
1552 static jsonObject* doRetrieve(osrfMethodContext* ctx, int* err ) {
1562 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
1564 char* id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, id_pos));
1565 jsonObject* order_hash = jsonObjectGetIndex(ctx->params, order_pos);
1569 "%s retrieving %s object with primary key value of %s",
1571 osrfHashGet(meta, "fieldmapper"),
1576 jsonObject* fake_params = jsonNewObjectType(JSON_ARRAY);
1577 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH));
1580 jsonObjectGetIndex(fake_params, 0),
1581 osrfHashGet(meta, "primarykey"),
1582 jsonObjectClone(jsonObjectGetIndex(ctx->params, id_pos))
1586 if (order_hash) jsonObjectPush(fake_params, jsonObjectClone(order_hash) );
1588 jsonObject* list = doFieldmapperSearch( ctx,meta, fake_params, err);
1591 jsonObjectFree( fake_params );
1595 jsonObject* obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
1597 jsonObjectFree( list );
1598 jsonObjectFree( fake_params );
1601 if(!verifyObjectPCRUD(ctx, obj)) {
1602 jsonObjectFree(obj);
1605 growing_buffer* msg = buffer_init(128);
1606 OSRF_BUFFER_ADD( msg, MODULENAME );
1607 OSRF_BUFFER_ADD( msg, ": Insufficient permissions to retrieve object" );
1609 char* m = buffer_release(msg);
1610 osrfAppSessionStatus( ctx->session, OSRF_STATUS_NOTALLOWED, "osrfMethodException", ctx->request, m );
1621 static char* jsonNumberToDBString ( osrfHash* field, const jsonObject* value ) {
1622 growing_buffer* val_buf = buffer_init(32);
1623 const char* numtype = get_datatype( field );
1625 if ( !strncmp( numtype, "INT", 3 ) ) {
1626 if (value->type == JSON_NUMBER) buffer_fadd( val_buf, "%ld", (long)jsonObjectGetNumber(value) );
1628 char* val_str = jsonObjectToSimpleString(value);
1629 buffer_fadd( val_buf, "%ld", atol(val_str) );
1633 } else if ( !strcmp( numtype, "NUMERIC" ) ) {
1634 if (value->type == JSON_NUMBER) buffer_fadd( val_buf, "%f", jsonObjectGetNumber(value) );
1636 char* val_str = jsonObjectToSimpleString(value);
1637 buffer_fadd( val_buf, "%f", atof(val_str) );
1642 return buffer_release(val_buf);
1645 static char* searchINPredicate (const char* class, osrfHash* field,
1646 jsonObject* node, const char* op, osrfMethodContext* ctx ) {
1647 growing_buffer* sql_buf = buffer_init(32);
1653 osrfHashGet(field, "name")
1657 buffer_add(sql_buf, "IN (");
1658 } else if (!(strcasecmp(op,"not in"))) {
1659 buffer_add(sql_buf, "NOT IN (");
1661 buffer_add(sql_buf, "IN (");
1664 if (node->type == JSON_HASH) {
1665 // subquery predicate
1666 char* subpred = SELECT(
1668 jsonObjectGetKey( node, "select" ),
1669 jsonObjectGetKey( node, "from" ),
1670 jsonObjectGetKey( node, "where" ),
1671 jsonObjectGetKey( node, "having" ),
1672 jsonObjectGetKey( node, "order_by" ),
1673 jsonObjectGetKey( node, "limit" ),
1674 jsonObjectGetKey( node, "offset" ),
1678 buffer_add(sql_buf, subpred);
1681 } else if (node->type == JSON_ARRAY) {
1682 // literal value list
1683 int in_item_index = 0;
1684 int in_item_first = 1;
1685 const jsonObject* in_item;
1686 while ( (in_item = jsonObjectGetIndex(node, in_item_index++)) ) {
1691 buffer_add(sql_buf, ", ");
1693 // Append the literal value -- quoted if not a number
1694 if ( JSON_NUMBER == in_item->type ) {
1695 char* val = jsonNumberToDBString( field, in_item );
1696 OSRF_BUFFER_ADD( sql_buf, val );
1699 } else if ( !strcmp( get_primitive( field ), "number") ) {
1700 char* val = jsonNumberToDBString( field, in_item );
1701 OSRF_BUFFER_ADD( sql_buf, val );
1705 char* key_string = jsonObjectToSimpleString(in_item);
1706 if ( dbi_conn_quote_string(dbhandle, &key_string) ) {
1707 OSRF_BUFFER_ADD( sql_buf, key_string );
1710 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string);
1712 buffer_free(sql_buf);
1719 OSRF_BUFFER_ADD_CHAR( sql_buf, ')' );
1721 return buffer_release(sql_buf);
1724 // Receive a JSON_ARRAY representing a function call. The first
1725 // entry in the array is the function name. The rest are parameters.
1726 static char* searchValueTransform( const jsonObject* array ) {
1727 growing_buffer* sql_buf = buffer_init(32);
1730 jsonObject* func_item;
1732 // Get the function name
1733 if( array->size > 0 ) {
1734 func_item = jsonObjectGetIndex( array, 0 );
1735 val = jsonObjectToSimpleString( func_item );
1736 OSRF_BUFFER_ADD( sql_buf, val );
1737 OSRF_BUFFER_ADD( sql_buf, "( " );
1741 // Get the parameters
1742 int func_item_index = 1; // We already grabbed the zeroth entry
1743 while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1745 // Add a separator comma, if we need one
1746 if( func_item_index > 2 )
1747 buffer_add( sql_buf, ", " );
1749 // Add the current parameter
1750 if (func_item->type == JSON_NULL) {
1751 buffer_add( sql_buf, "NULL" );
1753 val = jsonObjectToSimpleString(func_item);
1754 if ( dbi_conn_quote_string(dbhandle, &val) ) {
1755 OSRF_BUFFER_ADD( sql_buf, val );
1758 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1759 buffer_free(sql_buf);
1766 buffer_add( sql_buf, " )" );
1768 return buffer_release(sql_buf);
1771 static char* searchFunctionPredicate (const char* class, osrfHash* field,
1772 const jsonObject* node, const char* node_key) {
1773 growing_buffer* sql_buf = buffer_init(32);
1775 char* val = searchValueTransform(node);
1781 osrfHashGet(field, "name"),
1788 return buffer_release(sql_buf);
1791 // class is a class name
1792 // field is a field definition as stored in the IDL
1793 // node comes from the method parameter, and represents an entry in the SELECT list
1794 static char* searchFieldTransform (const char* class, osrfHash* field, const jsonObject* node) {
1795 growing_buffer* sql_buf = buffer_init(32);
1797 char* field_transform = jsonObjectToSimpleString( jsonObjectGetKeyConst( node, "transform" ) );
1798 char* transform_subcolumn = jsonObjectToSimpleString( jsonObjectGetKeyConst( node, "result_field" ) );
1800 if(transform_subcolumn)
1801 OSRF_BUFFER_ADD_CHAR( sql_buf, '(' ); // enclose transform in parentheses
1803 if (field_transform) {
1804 buffer_fadd( sql_buf, "%s(\"%s\".%s", field_transform, class, osrfHashGet(field, "name"));
1805 const jsonObject* array = jsonObjectGetKeyConst( node, "params" );
1808 if( array->type != JSON_ARRAY ) {
1809 osrfLogError( OSRF_LOG_MARK,
1810 "%s: Expected JSON_ARRAY for function params; found %s",
1811 MODULENAME, json_type( array->type ) );
1812 free( transform_subcolumn );
1813 free( field_transform );
1814 buffer_free( sql_buf );
1817 int func_item_index = 0;
1818 jsonObject* func_item;
1819 while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1821 char* val = jsonObjectToSimpleString(func_item);
1824 buffer_add( sql_buf, ",NULL" );
1825 } else if ( dbi_conn_quote_string(dbhandle, &val) ) {
1826 OSRF_BUFFER_ADD_CHAR( sql_buf, ',' );
1827 OSRF_BUFFER_ADD( sql_buf, val );
1829 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1830 free(transform_subcolumn);
1831 free(field_transform);
1833 buffer_free(sql_buf);
1840 buffer_add( sql_buf, " )" );
1843 buffer_fadd( sql_buf, "\"%s\".%s", class, osrfHashGet(field, "name"));
1846 if (transform_subcolumn)
1847 buffer_fadd( sql_buf, ").\"%s\"", transform_subcolumn );
1849 if (field_transform) free(field_transform);
1850 if (transform_subcolumn) free(transform_subcolumn);
1852 return buffer_release(sql_buf);
1855 static char* searchFieldTransformPredicate (const char* class, osrfHash* field, jsonObject* node, const char* node_key) {
1856 char* field_transform = searchFieldTransform( class, field, node );
1859 const jsonObject* value_obj = jsonObjectGetKeyConst( node, "value" );
1860 if ( ! value_obj ) {
1861 value = searchWHERE( node, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN, NULL );
1862 } else if ( value_obj->type == JSON_ARRAY ) {
1863 value = searchValueTransform( value_obj );
1864 } else if ( value_obj->type == JSON_HASH ) {
1865 value = searchWHERE( value_obj, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN, NULL );
1866 } else if ( value_obj->type == JSON_NUMBER ) {
1867 value = jsonNumberToDBString( field, value_obj );
1868 } else if ( value_obj->type != JSON_NULL ) {
1869 if ( !strcmp( get_primitive( field ), "number") ) {
1870 value = jsonNumberToDBString( field, value_obj );
1872 value = jsonObjectToSimpleString( value_obj );
1873 if ( !dbi_conn_quote_string(dbhandle, &value) ) {
1874 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, value);
1876 free(field_transform);
1882 growing_buffer* sql_buf = buffer_init(32);
1893 free(field_transform);
1895 return buffer_release(sql_buf);
1898 static char* searchSimplePredicate (const char* op, const char* class,
1899 osrfHash* field, const jsonObject* node) {
1903 // Get the value to which we are comparing the specified column
1904 if (node->type != JSON_NULL) {
1905 if ( node->type == JSON_NUMBER ) {
1906 val = jsonNumberToDBString( field, node );
1907 } else if ( !strcmp( get_primitive( field ), "number" ) ) {
1908 val = jsonNumberToDBString( field, node );
1910 val = jsonObjectToSimpleString(node);
1915 if( JSON_NUMBER != node->type && strcmp( get_primitive( field ), "number") ) {
1916 // Value is not numeric; enclose it in quotes
1917 if ( !dbi_conn_quote_string( dbhandle, &val ) ) {
1918 osrfLogError( OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val );
1924 // Compare to a null value
1925 val = strdup( "NULL" );
1926 if (strcmp( op, "=" ))
1932 growing_buffer* sql_buf = buffer_init(32);
1933 buffer_fadd( sql_buf, "\"%s\".%s %s %s", class, osrfHashGet(field, "name"), op, val );
1934 char* pred = buffer_release( sql_buf );
1941 static char* searchBETWEENPredicate (const char* class, osrfHash* field, jsonObject* node) {
1946 if ( !strcmp( get_primitive( field ), "number") ) {
1947 x_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,0));
1948 y_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,1));
1951 x_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,0));
1952 y_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,1));
1953 if ( !(dbi_conn_quote_string(dbhandle, &x_string) && dbi_conn_quote_string(dbhandle, &y_string)) ) {
1954 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key strings [%s] and [%s]", MODULENAME, x_string, y_string);
1961 growing_buffer* sql_buf = buffer_init(32);
1962 buffer_fadd( sql_buf, "%s BETWEEN %s AND %s", osrfHashGet(field, "name"), x_string, y_string );
1966 return buffer_release(sql_buf);
1969 static char* searchPredicate ( const char* class, osrfHash* field,
1970 jsonObject* node, osrfMethodContext* ctx ) {
1973 if (node->type == JSON_ARRAY) { // equality IN search
1974 pred = searchINPredicate( class, field, node, NULL, ctx );
1975 } else if (node->type == JSON_HASH) { // non-equality search
1976 jsonObject* pred_node;
1977 jsonIterator* pred_itr = jsonNewIterator( node );
1978 while ( (pred_node = jsonIteratorNext( pred_itr )) ) {
1979 if ( !(strcasecmp( pred_itr->key,"between" )) )
1980 pred = searchBETWEENPredicate( class, field, pred_node );
1981 else if ( !(strcasecmp( pred_itr->key,"in" )) || !(strcasecmp( pred_itr->key,"not in" )) )
1982 pred = searchINPredicate( class, field, pred_node, pred_itr->key, ctx );
1983 else if ( pred_node->type == JSON_ARRAY )
1984 pred = searchFunctionPredicate( class, field, pred_node, pred_itr->key );
1985 else if ( pred_node->type == JSON_HASH )
1986 pred = searchFieldTransformPredicate( class, field, pred_node, pred_itr->key );
1988 pred = searchSimplePredicate( pred_itr->key, class, field, pred_node );
1992 jsonIteratorFree(pred_itr);
1993 } else if (node->type == JSON_NULL) { // IS NULL search
1994 growing_buffer* _p = buffer_init(64);
1997 "\"%s\".%s IS NULL",
1999 osrfHashGet(field, "name")
2001 pred = buffer_release(_p);
2002 } else { // equality search
2003 pred = searchSimplePredicate( "=", class, field, node );
2022 field : call_number,
2038 static char* searchJOIN ( const jsonObject* join_hash, osrfHash* leftmeta ) {
2040 const jsonObject* working_hash;
2041 jsonObject* freeable_hash = NULL;
2043 if (join_hash->type == JSON_STRING) {
2044 // create a wrapper around a copy of the original
2045 char* _tmp = jsonObjectToSimpleString( join_hash );
2046 freeable_hash = jsonNewObjectType(JSON_HASH);
2047 jsonObjectSetKey(freeable_hash, _tmp, NULL);
2049 working_hash = freeable_hash;
2052 if( join_hash->type != JSON_HASH ) {
2055 "%s: JOIN failed; expected JSON object type not found",
2060 working_hash = join_hash;
2063 growing_buffer* join_buf = buffer_init(128);
2064 const char* leftclass = osrfHashGet(leftmeta, "classname");
2066 jsonObject* snode = NULL;
2067 jsonIterator* search_itr = jsonNewIterator( working_hash );
2069 while ( (snode = jsonIteratorNext( search_itr )) ) {
2070 const char* class = search_itr->key;
2071 osrfHash* idlClass = osrfHashGet( oilsIDL(), class );
2075 "%s: JOIN failed. No class \"%s\" defined in IDL",
2079 jsonIteratorFree( search_itr );
2080 buffer_free( join_buf );
2082 jsonObjectFree( freeable_hash );
2086 char* fkey = jsonObjectToSimpleString( jsonObjectGetKeyConst( snode, "fkey" ) );
2087 char* field = jsonObjectToSimpleString( jsonObjectGetKeyConst( snode, "field" ) );
2089 if (field && !fkey) {
2090 fkey = (char*)oilsIDLFindPath("/%s/links/%s/key", class, field);
2094 "%s: JOIN failed. No link defined from %s.%s to %s",
2100 buffer_free(join_buf);
2102 jsonObjectFree(freeable_hash);
2104 jsonIteratorFree(search_itr);
2107 fkey = strdup( fkey );
2109 } else if (!field && fkey) {
2110 field = (char*)oilsIDLFindPath("/%s/links/%s/key", leftclass, fkey );
2114 "%s: JOIN failed. No link defined from %s.%s to %s",
2120 buffer_free(join_buf);
2122 jsonObjectFree(freeable_hash);
2124 jsonIteratorFree(search_itr);
2127 field = strdup( field );
2129 } else if (!field && !fkey) {
2130 osrfHash* _links = oilsIDL_links( leftclass );
2132 // For each link defined for the left class:
2133 // see if the link references the joined class
2134 osrfHashIterator* itr = osrfNewHashIterator( _links );
2135 osrfHash* curr_link = NULL;
2136 while( (curr_link = osrfHashIteratorNext( itr ) ) ) {
2137 const char* other_class = osrfHashGet( curr_link, "class" );
2138 if( other_class && !strcmp( other_class, class ) ) {
2140 // Found a link between the classes
2141 fkey = strdup( osrfHashIteratorKey( itr ) );
2142 const char* other_key = osrfHashGet( curr_link, "key" );
2143 field = other_key ? strdup( other_key ) : NULL;
2147 osrfHashIteratorFree( itr );
2149 if (!field || !fkey) {
2150 // Do another such search, with the classes reversed
2151 _links = oilsIDL_links( class );
2153 // For each link defined for the joined class:
2154 // see if the link references the left class
2155 osrfHashIterator* itr = osrfNewHashIterator( _links );
2156 osrfHash* curr_link = NULL;
2157 while( (curr_link = osrfHashIteratorNext( itr ) ) ) {
2158 const char* other_class = osrfHashGet( curr_link, "class" );
2159 if( other_class && !strcmp( other_class, leftclass ) ) {
2161 // Found a link between the classes
2162 fkey = strdup( osrfHashIteratorKey( itr ) );
2163 const char* other_key = osrfHashGet( curr_link, "key" );
2164 field = other_key ? strdup( other_key ) : NULL;
2168 osrfHashIteratorFree( itr );
2171 if (!field || !fkey) {
2174 "%s: JOIN failed. No link defined between %s and %s",
2181 buffer_free(join_buf);
2183 jsonObjectFree(freeable_hash);
2184 jsonIteratorFree(search_itr);
2190 char* type = jsonObjectToSimpleString( jsonObjectGetKeyConst( snode, "type" ) );
2192 if ( !strcasecmp(type,"left") ) {
2193 buffer_add(join_buf, " LEFT JOIN");
2194 } else if ( !strcasecmp(type,"right") ) {
2195 buffer_add(join_buf, " RIGHT JOIN");
2196 } else if ( !strcasecmp(type,"full") ) {
2197 buffer_add(join_buf, " FULL JOIN");
2199 buffer_add(join_buf, " INNER JOIN");
2202 buffer_add(join_buf, " INNER JOIN");
2206 char* table = getSourceDefinition(idlClass);
2210 jsonIteratorFree( search_itr );
2211 buffer_free( join_buf );
2213 jsonObjectFree( freeable_hash );
2217 buffer_fadd(join_buf, " %s AS \"%s\" ON ( \"%s\".%s = \"%s\".%s",
2218 table, class, class, field, leftclass, fkey);
2221 const jsonObject* filter = jsonObjectGetKeyConst( snode, "filter" );
2223 char* filter_op = jsonObjectToSimpleString( jsonObjectGetKeyConst( snode, "filter_op" ) );
2225 if (!strcasecmp("or",filter_op)) {
2226 buffer_add( join_buf, " OR " );
2228 buffer_add( join_buf, " AND " );
2231 buffer_add( join_buf, " AND " );
2234 char* jpred = searchWHERE( filter, idlClass, AND_OP_JOIN, NULL );
2235 OSRF_BUFFER_ADD_CHAR( join_buf, ' ' );
2236 OSRF_BUFFER_ADD( join_buf, jpred );
2241 buffer_add(join_buf, " ) ");
2243 const jsonObject* join_filter = jsonObjectGetKeyConst( snode, "join" );
2245 char* jpred = searchJOIN( join_filter, idlClass );
2246 OSRF_BUFFER_ADD_CHAR( join_buf, ' ' );
2247 OSRF_BUFFER_ADD( join_buf, jpred );
2256 jsonObjectFree(freeable_hash);
2257 jsonIteratorFree(search_itr);
2259 return buffer_release(join_buf);
2264 { +class : { -or|-and : { field : { op : value }, ... } ... }, ... }
2265 { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }
2266 [ { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }, ... ]
2268 Generate code to express a set of conditions, as for a WHERE clause. Parameters:
2270 search_hash is the JSON expression of the conditions.
2271 meta is the class definition from the IDL, for the relevant table.
2272 opjoin_type indicates whether multiple conditions, if present, should be
2273 connected by AND or OR.
2274 osrfMethodContext is loaded with all sorts of stuff, but all we do with it here is
2275 to pass it to other functions -- and all they do with it is to use the session
2276 and request members to send error messages back to the client.
2280 static char* searchWHERE ( const jsonObject* search_hash, osrfHash* meta, int opjoin_type, osrfMethodContext* ctx ) {
2284 "%s: Entering searchWHERE; search_hash addr = %p, meta addr = %p, opjoin_type = %d, ctx addr = %p",
2292 growing_buffer* sql_buf = buffer_init(128);
2294 jsonObject* node = NULL;
2297 if ( search_hash->type == JSON_ARRAY ) {
2298 osrfLogDebug(OSRF_LOG_MARK, "%s: In WHERE clause, condition type is JSON_ARRAY", MODULENAME);
2299 jsonIterator* search_itr = jsonNewIterator( search_hash );
2300 while ( (node = jsonIteratorNext( search_itr )) ) {
2304 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
2305 else buffer_add(sql_buf, " AND ");
2308 char* subpred = searchWHERE( node, meta, opjoin_type, ctx );
2309 buffer_fadd(sql_buf, "( %s )", subpred);
2312 jsonIteratorFree(search_itr);
2314 } else if ( search_hash->type == JSON_HASH ) {
2315 osrfLogDebug(OSRF_LOG_MARK, "%s: In WHERE clause, condition type is JSON_HASH", MODULENAME);
2316 jsonIterator* search_itr = jsonNewIterator( search_hash );
2317 while ( (node = jsonIteratorNext( search_itr )) ) {
2322 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
2323 else buffer_add(sql_buf, " AND ");
2326 if ( '+' == search_itr->key[ 0 ] ) {
2327 if ( node->type == JSON_STRING ) {
2328 // Intended purpose; to allow reference to a Boolean column.
2329 // Verify that the string looks like an identifier.
2330 const char* subpred = jsonObjectGetString( node );
2331 if( ! is_identifier( subpred ) ) {
2334 "%s: Invalid boolean identifier in WHERE clause: \"%s\"",
2338 jsonIteratorFree( search_itr );
2339 buffer_free( sql_buf );
2342 buffer_fadd(sql_buf, " \"%s\".%s ", search_itr->key + 1, subpred);
2344 char* subpred = searchWHERE( node, osrfHashGet( oilsIDL(), search_itr->key + 1 ), AND_OP_JOIN, ctx );
2345 buffer_fadd(sql_buf, "( %s )", subpred);
2348 } else if ( !strcasecmp("-or",search_itr->key) ) {
2349 char* subpred = searchWHERE( node, meta, OR_OP_JOIN, ctx );
2350 buffer_fadd(sql_buf, "( %s )", subpred);
2352 } else if ( !strcasecmp("-and",search_itr->key) ) {
2353 char* subpred = searchWHERE( node, meta, AND_OP_JOIN, ctx );
2354 buffer_fadd(sql_buf, "( %s )", subpred);
2356 } else if ( !strcasecmp("-exists",search_itr->key) ) {
2357 char* subpred = SELECT(
2359 jsonObjectGetKey( node, "select" ),
2360 jsonObjectGetKey( node, "from" ),
2361 jsonObjectGetKey( node, "where" ),
2362 jsonObjectGetKey( node, "having" ),
2363 jsonObjectGetKey( node, "order_by" ),
2364 jsonObjectGetKey( node, "limit" ),
2365 jsonObjectGetKey( node, "offset" ),
2369 buffer_fadd(sql_buf, "EXISTS ( %s )", subpred);
2371 } else if ( !strcasecmp("-not-exists",search_itr->key) ) {
2372 char* subpred = SELECT(
2374 jsonObjectGetKey( node, "select" ),
2375 jsonObjectGetKey( node, "from" ),
2376 jsonObjectGetKey( node, "where" ),
2377 jsonObjectGetKey( node, "having" ),
2378 jsonObjectGetKey( node, "order_by" ),
2379 jsonObjectGetKey( node, "limit" ),
2380 jsonObjectGetKey( node, "offset" ),
2384 buffer_fadd(sql_buf, "NOT EXISTS ( %s )", subpred);
2388 char* class = osrfHashGet(meta, "classname");
2389 osrfHash* fields = osrfHashGet(meta, "fields");
2390 osrfHash* field = osrfHashGet( fields, search_itr->key );
2394 char* table = getSourceDefinition(meta);
2396 table = strdup( "(?)" );
2399 "%s: Attempt to reference non-existent column %s on %s (%s)",
2405 buffer_free(sql_buf);
2407 jsonIteratorFree(search_itr);
2411 char* subpred = searchPredicate( class, field, node, ctx );
2412 buffer_add( sql_buf, subpred );
2416 jsonIteratorFree(search_itr);
2419 // ERROR ... only hash and array allowed at this level
2420 char* predicate_string = jsonObjectToJSON( search_hash );
2423 "%s: Invalid predicate structure: %s",
2427 buffer_free(sql_buf);
2428 free(predicate_string);
2433 return buffer_release(sql_buf);
2437 /* method context */ osrfMethodContext* ctx,
2439 /* SELECT */ jsonObject* selhash,
2440 /* FROM */ jsonObject* join_hash,
2441 /* WHERE */ jsonObject* search_hash,
2442 /* HAVING */ jsonObject* having_hash,
2443 /* ORDER BY */ jsonObject* order_hash,
2444 /* LIMIT */ jsonObject* limit,
2445 /* OFFSET */ jsonObject* offset,
2446 /* flags */ int flags
2448 const char* locale = osrf_message_get_last_locale();
2450 // in case we don't get a select list
2451 jsonObject* defaultselhash = NULL;
2453 // general tmp objects
2454 const jsonObject* tmp_const;
2455 jsonObject* selclass = NULL;
2456 jsonObject* selfield = NULL;
2457 jsonObject* snode = NULL;
2458 jsonObject* onode = NULL;
2460 char* string = NULL;
2461 int from_function = 0;
2466 // the core search class
2467 char* core_class = NULL;
2469 // metadata about the core search class
2470 osrfHash* core_meta = NULL;
2472 // punt if there's no core class
2473 if (!join_hash || ( join_hash->type == JSON_HASH && !join_hash->size )) {
2476 "%s: FROM clause is missing or empty",
2480 osrfAppSessionStatus(
2482 OSRF_STATUS_INTERNALSERVERERROR,
2483 "osrfMethodException",
2485 "FROM clause is missing or empty in JSON query"
2490 // get the core class -- the only key of the top level FROM clause, or a string
2491 if (join_hash->type == JSON_HASH) {
2492 jsonIterator* tmp_itr = jsonNewIterator( join_hash );
2493 snode = jsonIteratorNext( tmp_itr );
2495 core_class = strdup( tmp_itr->key );
2498 jsonObject* extra = jsonIteratorNext( tmp_itr );
2500 jsonIteratorFree( tmp_itr );
2503 // There shouldn't be more than one entry in join_hash
2507 "%s: Malformed FROM clause: extra entry in JSON_HASH",
2511 osrfAppSessionStatus(
2513 OSRF_STATUS_INTERNALSERVERERROR,
2514 "osrfMethodException",
2516 "Malformed FROM clause in JSON query"
2519 return NULL; // Malformed join_hash; extra entry
2521 } else if (join_hash->type == JSON_ARRAY) {
2523 core_class = jsonObjectToSimpleString( jsonObjectGetIndex(join_hash, 0) );
2526 } else if (join_hash->type == JSON_STRING) {
2527 core_class = jsonObjectToSimpleString( join_hash );
2533 "%s: FROM clause is unexpected JSON type: %s",
2535 json_type( join_hash->type )
2538 osrfAppSessionStatus(
2540 OSRF_STATUS_INTERNALSERVERERROR,
2541 "osrfMethodException",
2543 "Ill-formed FROM clause in JSON query"
2549 if (!from_function) {
2550 // Get the IDL class definition for the core class
2551 core_meta = osrfHashGet( oilsIDL(), core_class );
2552 if( !core_meta ) { // Didn't find it?
2555 "%s: SELECT clause references undefined class: \"%s\"",
2560 osrfAppSessionStatus(
2562 OSRF_STATUS_INTERNALSERVERERROR,
2563 "osrfMethodException",
2565 "SELECT clause references undefined class in JSON query"
2571 // Make sure the class isn't virtual
2572 if( str_is_true( osrfHashGet( core_meta, "virtual" ) ) ) {
2575 "%s: Core class is virtual: \"%s\"",
2580 osrfAppSessionStatus(
2582 OSRF_STATUS_INTERNALSERVERERROR,
2583 "osrfMethodException",
2585 "FROM clause references virtual class in JSON query"
2592 // if the select list is empty, or the core class field list is '*',
2593 // build the default select list ...
2595 selhash = defaultselhash = jsonNewObjectType(JSON_HASH);
2596 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2597 } else if( selhash->type != JSON_HASH ) {
2600 "%s: Expected JSON_HASH for SELECT clause; found %s",
2602 json_type( selhash->type )
2606 osrfAppSessionStatus(
2608 OSRF_STATUS_INTERNALSERVERERROR,
2609 "osrfMethodException",
2611 "Malformed SELECT clause in JSON query"
2615 } else if ( (tmp_const = jsonObjectGetKeyConst( selhash, core_class )) && tmp_const->type == JSON_STRING ) {
2616 char* _x = jsonObjectToSimpleString( tmp_const );
2617 if (!strncmp( "*", _x, 1 )) {
2618 jsonObjectRemoveKey( selhash, core_class );
2619 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2625 growing_buffer* sql_buf = buffer_init(128);
2627 // temp buffer for the SELECT list
2628 growing_buffer* select_buf = buffer_init(128);
2629 growing_buffer* order_buf = buffer_init(128);
2630 growing_buffer* group_buf = buffer_init(128);
2631 growing_buffer* having_buf = buffer_init(128);
2633 int aggregate_found = 0; // boolean
2635 // Build a select list
2636 if(from_function) // From a function we select everything
2637 OSRF_BUFFER_ADD_CHAR( select_buf, '*' );
2640 // If we need to build a default list, prepare to do so
2641 jsonObject* _tmp = jsonObjectGetKey( selhash, core_class );
2642 if ( _tmp && !_tmp->size ) {
2644 osrfHash* core_fields = osrfHashGet( core_meta, "fields" );
2646 osrfHashIterator* field_itr = osrfNewHashIterator( core_fields );
2647 osrfHash* field_def;
2648 while( ( field_def = osrfHashIteratorNext( field_itr ) ) ) {
2649 if( ! str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2650 // This field is not virtual, so add it to the list
2651 jsonObjectPush( _tmp, jsonNewObject( osrfHashIteratorKey( field_itr ) ) );
2654 osrfHashIteratorFree( field_itr );
2657 // Now build the actual select list
2661 jsonIterator* selclass_itr = jsonNewIterator( selhash );
2662 while ( (selclass = jsonIteratorNext( selclass_itr )) ) { // For each class
2664 // Make sure the class is defined in the IDL
2665 const char* cname = selclass_itr->key;
2666 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
2670 "%s: Selected class \"%s\" not defined in IDL",
2676 osrfAppSessionStatus(
2678 OSRF_STATUS_INTERNALSERVERERROR,
2679 "osrfMethodException",
2681 "Selected class is not defined"
2683 jsonIteratorFree( selclass_itr );
2684 buffer_free( sql_buf );
2685 buffer_free( select_buf );
2686 buffer_free( order_buf );
2687 buffer_free( group_buf );
2688 buffer_free( having_buf );
2689 if( defaultselhash ) jsonObjectFree( defaultselhash );
2694 // Make sure the target relation is in the join tree.
2696 // At this point join_hash is a step down from the join_hash we
2697 // received as a parameter. If the original was a JSON_STRING,
2698 // then json_hash is now NULL. If the original was a JSON_HASH,
2699 // then json_hash is now the first (and only) entry in it,
2700 // denoting the core class. We've already excluded the
2701 // possibility that the original was a JSON_ARRAY, because in
2702 // that case from_function would be non-NULL, and we wouldn't
2705 int class_in_from_clause; // boolean
2707 if ( ! strcmp( core_class, cname ))
2708 // This is the core class -- no problem
2709 class_in_from_clause = 1;
2712 // There's only one class in the FROM clause, and this isn't it
2713 class_in_from_clause = 0;
2714 else if (join_hash->type == JSON_STRING) {
2715 // There's only one class in the FROM clause
2716 string = jsonObjectToSimpleString(join_hash);
2717 if ( strcmp( string, cname ) )
2718 class_in_from_clause = 0; // This isn't it
2720 class_in_from_clause = 1; // This is it
2723 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", cname);
2724 if ( 0 == found->size )
2725 class_in_from_clause = 0; // Nowhere in the join tree
2727 class_in_from_clause = 1; // Found it
2728 jsonObjectFree( found );
2732 // If the class isn't in the FROM clause, bail out
2733 if( ! class_in_from_clause ) {
2736 "%s: SELECT clause references class not in FROM clause: \"%s\"",
2741 osrfAppSessionStatus(
2743 OSRF_STATUS_INTERNALSERVERERROR,
2744 "osrfMethodException",
2746 "Selected class not in FROM clause in JSON query"
2748 jsonIteratorFree( selclass_itr );
2749 buffer_free( sql_buf );
2750 buffer_free( select_buf );
2751 buffer_free( order_buf );
2752 buffer_free( group_buf );
2753 buffer_free( having_buf );
2754 if( defaultselhash ) jsonObjectFree( defaultselhash );
2759 // Look up some attributes of the current class, so that we
2760 // don't have to look them up again for each field
2761 osrfHash* class_field_set = osrfHashGet( idlClass, "fields" );
2762 const char* class_pkey = osrfHashGet( idlClass, "primarykey" );
2763 const char* class_tname = osrfHashGet( idlClass, "tablename" );
2765 // stitch together the column list ...
2766 jsonIterator* select_itr = jsonNewIterator( selclass );
2767 while ( (selfield = jsonIteratorNext( select_itr )) ) { // for each SELECT column
2769 // If we need a separator comma, add one
2773 OSRF_BUFFER_ADD_CHAR( select_buf, ',' );
2776 // ... if it's a string, just toss it on the pile
2777 if (selfield->type == JSON_STRING) {
2779 // Look up the field in the IDL
2780 const char* col_name = selfield->value.s;
2781 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
2783 // No such field in current class
2786 "%s: Selected column \"%s\" not defined in IDL for class \"%s\"",
2792 osrfAppSessionStatus(
2794 OSRF_STATUS_INTERNALSERVERERROR,
2795 "osrfMethodException",
2797 "Selected column not defined in JSON query"
2799 jsonIteratorFree( select_itr );
2800 jsonIteratorFree( selclass_itr );
2801 buffer_free( sql_buf );
2802 buffer_free( select_buf );
2803 buffer_free( order_buf );
2804 buffer_free( group_buf );
2805 buffer_free( having_buf );
2806 if( defaultselhash ) jsonObjectFree( defaultselhash );
2809 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2810 // Virtual field not allowed
2813 "%s: Selected column \"%s\" for class \"%s\" is virtual",
2819 osrfAppSessionStatus(
2821 OSRF_STATUS_INTERNALSERVERERROR,
2822 "osrfMethodException",
2824 "Selected column may not be virtual in JSON query"
2826 jsonIteratorFree( select_itr );
2827 jsonIteratorFree( selclass_itr );
2828 buffer_free( sql_buf );
2829 buffer_free( select_buf );
2830 buffer_free( order_buf );
2831 buffer_free( group_buf );
2832 buffer_free( having_buf );
2833 if( defaultselhash ) jsonObjectFree( defaultselhash );
2840 if (flags & DISABLE_I18N)
2843 i18n = osrfHashGet(field_def, "i18n");
2845 if( str_is_true( i18n ) ) {
2846 buffer_fadd( select_buf,
2847 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
2848 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, col_name );
2850 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
2853 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
2856 // ... but it could be an object, in which case we check for a Field Transform
2857 } else if (selfield->type == JSON_HASH) {
2859 char* col_name = jsonObjectToSimpleString( jsonObjectGetKeyConst( selfield, "column" ) );
2861 // Get the field definition from the IDL
2862 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
2864 // No such field in current class
2867 "%s: Selected column \"%s\" is not defined in IDL for class \"%s\"",
2873 osrfAppSessionStatus(
2875 OSRF_STATUS_INTERNALSERVERERROR,
2876 "osrfMethodException",
2878 "Selected column is not defined in JSON query"
2880 jsonIteratorFree( select_itr );
2881 jsonIteratorFree( selclass_itr );
2882 buffer_free( sql_buf );
2883 buffer_free( select_buf );
2884 buffer_free( order_buf );
2885 buffer_free( group_buf );
2886 buffer_free( having_buf );
2887 if( defaultselhash ) jsonObjectFree( defaultselhash );
2890 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2891 // No such field in current class
2894 "%s: Selected column \"%s\" is virtual for class \"%s\"",
2900 osrfAppSessionStatus(
2902 OSRF_STATUS_INTERNALSERVERERROR,
2903 "osrfMethodException",
2905 "Selected column is virtual in JSON query"
2907 jsonIteratorFree( select_itr );
2908 jsonIteratorFree( selclass_itr );
2909 buffer_free( sql_buf );
2910 buffer_free( select_buf );
2911 buffer_free( order_buf );
2912 buffer_free( group_buf );
2913 buffer_free( having_buf );
2914 if( defaultselhash ) jsonObjectFree( defaultselhash );
2919 // Decide what to use as a column alias
2921 if ((tmp_const = jsonObjectGetKeyConst( selfield, "alias" ))) {
2922 _alias = jsonObjectToSimpleString( tmp_const );
2923 } else { // Use field name as the alias
2927 if (jsonObjectGetKeyConst( selfield, "transform" )) {
2928 char* transform_str = searchFieldTransform(cname, field_def, selfield);
2929 if( transform_str ) {
2930 buffer_fadd(select_buf, " %s AS \"%s\"", transform_str, _alias);
2931 free(transform_str);
2934 osrfAppSessionStatus(
2936 OSRF_STATUS_INTERNALSERVERERROR,
2937 "osrfMethodException",
2939 "Unable to generate transform function in JSON query"
2941 jsonIteratorFree( select_itr );
2942 jsonIteratorFree( selclass_itr );
2943 buffer_free( sql_buf );
2944 buffer_free( select_buf );
2945 buffer_free( order_buf );
2946 buffer_free( group_buf );
2947 buffer_free( having_buf );
2948 if( defaultselhash ) jsonObjectFree( defaultselhash );
2956 if (flags & DISABLE_I18N)
2959 i18n = osrfHashGet(field_def, "i18n");
2961 if( str_is_true( i18n ) ) {
2962 buffer_fadd( select_buf,
2963 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
2964 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, _alias);
2966 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
2969 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
2973 if( _alias != col_name )
2980 "%s: Selected item is unexpected JSON type: %s",
2982 json_type( selfield->type )
2985 osrfAppSessionStatus(
2987 OSRF_STATUS_INTERNALSERVERERROR,
2988 "osrfMethodException",
2990 "Ill-formed SELECT item in JSON query"
2992 jsonIteratorFree( select_itr );
2993 jsonIteratorFree( selclass_itr );
2994 buffer_free( sql_buf );
2995 buffer_free( select_buf );
2996 buffer_free( order_buf );
2997 buffer_free( group_buf );
2998 buffer_free( having_buf );
2999 if( defaultselhash ) jsonObjectFree( defaultselhash );
3004 const jsonObject* agg_obj = jsonObjectGetKey( selfield, "aggregate" );
3005 if( obj_is_true( agg_obj ) )
3006 aggregate_found = 1;
3008 // Append a comma (except for the first one)
3009 // and add the column to a GROUP BY clause
3013 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3015 buffer_fadd(group_buf, " %d", sel_pos);
3019 if (is_agg->size || (flags & SELECT_DISTINCT)) {
3021 const jsonObject* aggregate_obj = jsonObjectGetKey( selfield, "aggregate" );
3022 if ( ! obj_is_true( aggregate_obj ) ) {
3026 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3029 buffer_fadd(group_buf, " %d", sel_pos);
3032 } else if (is_agg = jsonObjectGetKey( selfield, "having" )) {
3036 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3039 _column = searchFieldTransform(cname, field, selfield);
3040 OSRF_BUFFER_ADD_CHAR(group_buf, ' ');
3041 OSRF_BUFFER_ADD(group_buf, _column);
3042 _column = searchFieldTransform(cname, field, selfield);
3049 } // end while -- iterating across SELECT columns
3051 jsonIteratorFree(select_itr);
3052 } // end while -- iterating across classes
3054 jsonIteratorFree(selclass_itr);
3058 char* col_list = buffer_release(select_buf);
3060 if (from_function) table = searchValueTransform(join_hash);
3061 else table = getSourceDefinition(core_meta);
3065 osrfAppSessionStatus(
3067 OSRF_STATUS_INTERNALSERVERERROR,
3068 "osrfMethodException",
3070 "Unable to identify table for core class"
3073 buffer_free( sql_buf );
3074 buffer_free( order_buf );
3075 buffer_free( group_buf );
3076 buffer_free( having_buf );
3077 if( defaultselhash ) jsonObjectFree( defaultselhash );
3082 // Put it all together
3083 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\" ", col_list, table, core_class );
3087 if (!from_function) {
3088 // Now, walk the join tree and add that clause
3090 char* join_clause = searchJOIN( join_hash, core_meta );
3092 buffer_add(sql_buf, join_clause);
3096 osrfAppSessionStatus(
3098 OSRF_STATUS_INTERNALSERVERERROR,
3099 "osrfMethodException",
3101 "Unable to construct JOIN clause(s)"
3103 buffer_free( sql_buf );
3104 buffer_free( order_buf );
3105 buffer_free( group_buf );
3106 buffer_free( having_buf );
3107 if( defaultselhash ) jsonObjectFree( defaultselhash );
3113 // Build a WHERE clause, if there is one
3114 if ( search_hash ) {
3115 buffer_add(sql_buf, " WHERE ");
3117 // and it's on the WHERE clause
3118 char* pred = searchWHERE( search_hash, core_meta, AND_OP_JOIN, ctx );
3121 buffer_add(sql_buf, pred);
3125 osrfAppSessionStatus(
3127 OSRF_STATUS_INTERNALSERVERERROR,
3128 "osrfMethodException",
3130 "Severe query error in WHERE predicate -- see error log for more details"
3134 buffer_free(having_buf);
3135 buffer_free(group_buf);
3136 buffer_free(order_buf);
3137 buffer_free(sql_buf);
3138 if (defaultselhash) jsonObjectFree(defaultselhash);
3143 // Build a HAVING clause, if there is one
3144 if ( having_hash ) {
3145 buffer_add(sql_buf, " HAVING ");
3147 // and it's on the the WHERE clause
3148 char* pred = searchWHERE( having_hash, core_meta, AND_OP_JOIN, ctx );
3151 buffer_add(sql_buf, pred);
3155 osrfAppSessionStatus(
3157 OSRF_STATUS_INTERNALSERVERERROR,
3158 "osrfMethodException",
3160 "Severe query error in HAVING predicate -- see error log for more details"
3164 buffer_free(having_buf);
3165 buffer_free(group_buf);
3166 buffer_free(order_buf);
3167 buffer_free(sql_buf);
3168 if (defaultselhash) jsonObjectFree(defaultselhash);
3173 // Build an ORDER BY clause, if there is one
3175 jsonIterator* class_itr = jsonNewIterator( order_hash );
3176 while ( (snode = jsonIteratorNext( class_itr )) ) {
3178 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
3181 if ( snode->type == JSON_HASH ) {
3183 jsonIterator* order_itr = jsonNewIterator( snode );
3184 while ( (onode = jsonIteratorNext( order_itr )) ) {
3186 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ))
3189 char* direction = NULL;
3190 if ( onode->type == JSON_HASH ) {
3191 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3192 string = searchFieldTransform(
3194 oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ),
3198 growing_buffer* field_buf = buffer_init(16);
3199 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3200 string = buffer_release(field_buf);
3203 if ( (tmp_const = jsonObjectGetKeyConst( onode, "direction" )) ) {
3204 direction = jsonObjectToSimpleString(tmp_const);
3205 if (!strncasecmp(direction, "d", 1)) {
3207 direction = " DESC";
3215 string = strdup(order_itr->key);
3216 direction = jsonObjectToSimpleString(onode);
3217 if (!strncasecmp(direction, "d", 1)) {
3219 direction = " DESC";
3229 buffer_add(order_buf, ", ");
3232 buffer_add(order_buf, string);
3236 buffer_add(order_buf, direction);
3240 // jsonIteratorFree(order_itr);
3242 } else if ( snode->type == JSON_ARRAY ) {
3244 jsonIterator* order_itr = jsonNewIterator( snode );
3245 while ( (onode = jsonIteratorNext( order_itr )) ) {
3247 char* _f = jsonObjectToSimpleString( onode );
3249 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, _f))
3255 buffer_add(order_buf, ", ");
3258 buffer_add(order_buf, _f);
3262 // jsonIteratorFree(order_itr);
3265 // IT'S THE OOOOOOOOOOOLD STYLE!
3267 osrfLogError(OSRF_LOG_MARK, "%s: Possible SQL injection attempt; direct order by is not allowed", MODULENAME);
3269 osrfAppSessionStatus(
3271 OSRF_STATUS_INTERNALSERVERERROR,
3272 "osrfMethodException",
3274 "Severe query error -- see error log for more details"
3279 buffer_free(having_buf);
3280 buffer_free(group_buf);
3281 buffer_free(order_buf);
3282 buffer_free(sql_buf);
3283 if (defaultselhash) jsonObjectFree(defaultselhash);
3284 jsonIteratorFree(class_itr);
3289 // jsonIteratorFree(class_itr);
3293 string = buffer_release(group_buf);
3295 if ( *string && ( aggregate_found || (flags & SELECT_DISTINCT) ) ) {
3296 OSRF_BUFFER_ADD( sql_buf, " GROUP BY " );
3297 OSRF_BUFFER_ADD( sql_buf, string );
3302 string = buffer_release(having_buf);
3305 OSRF_BUFFER_ADD( sql_buf, " HAVING " );
3306 OSRF_BUFFER_ADD( sql_buf, string );
3311 string = buffer_release(order_buf);
3314 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3315 OSRF_BUFFER_ADD( sql_buf, string );
3321 string = jsonObjectToSimpleString(limit);
3322 buffer_fadd( sql_buf, " LIMIT %d", atoi(string) );
3327 string = jsonObjectToSimpleString(offset);
3328 buffer_fadd( sql_buf, " OFFSET %d", atoi(string) );
3332 if (!(flags & SUBSELECT)) OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3335 if (defaultselhash) jsonObjectFree(defaultselhash);
3337 return buffer_release(sql_buf);
3341 static char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
3343 const char* locale = osrf_message_get_last_locale();
3345 osrfHash* fields = osrfHashGet(meta, "fields");
3346 char* core_class = osrfHashGet(meta, "classname");
3348 const jsonObject* join_hash = jsonObjectGetKeyConst( order_hash, "join" );
3350 jsonObject* node = NULL;
3351 jsonObject* snode = NULL;
3352 jsonObject* onode = NULL;
3353 const jsonObject* _tmp = NULL;
3354 jsonObject* selhash = NULL;
3355 jsonObject* defaultselhash = NULL;
3357 growing_buffer* sql_buf = buffer_init(128);
3358 growing_buffer* select_buf = buffer_init(128);
3360 if ( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
3361 defaultselhash = jsonNewObjectType(JSON_HASH);
3362 selhash = defaultselhash;
3365 if ( !jsonObjectGetKeyConst(selhash,core_class) ) {
3366 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
3367 jsonObject* flist = jsonObjectGetKey( selhash, core_class );
3372 osrfStringArray* keys = osrfHashKeys( fields );
3373 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
3374 if( ! str_is_true( osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
3375 jsonObjectPush( flist, jsonNewObject( field ) );
3377 osrfStringArrayFree(keys);
3381 jsonIterator* class_itr = jsonNewIterator( selhash );
3382 while ( (snode = jsonIteratorNext( class_itr )) ) {
3384 char* cname = class_itr->key;
3385 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
3386 if (!idlClass) continue;
3388 if (strcmp(core_class,class_itr->key)) {
3389 if (!join_hash) continue;
3391 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", class_itr->key);
3393 jsonObjectFree(found);
3397 jsonObjectFree(found);
3400 jsonIterator* select_itr = jsonNewIterator( snode );
3401 while ( (node = jsonIteratorNext( select_itr )) ) {
3402 char* item_str = jsonObjectToSimpleString(node);
3403 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), item_str );
3405 char* fname = osrfHashGet(field, "name");
3407 if (!field) continue;
3412 OSRF_BUFFER_ADD_CHAR(select_buf, ',');
3417 const jsonObject* no_i18n_obj = jsonObjectGetKey( order_hash, "no_i18n" );
3418 if ( obj_is_true( no_i18n_obj ) ) // Suppress internationalization?
3421 i18n = osrfHashGet(field, "i18n");
3423 if( str_is_true( i18n ) ) {
3424 char* pkey = osrfHashGet(idlClass, "primarykey");
3425 char* tname = osrfHashGet(idlClass, "tablename");
3427 buffer_fadd(select_buf, " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"", tname, cname, fname, pkey, cname, pkey, locale, fname);
3429 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3432 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3436 jsonIteratorFree(select_itr);
3439 jsonIteratorFree(class_itr);
3441 char* col_list = buffer_release(select_buf);
3442 char* table = getSourceDefinition(meta);
3444 table = strdup( "(null)" );
3446 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\"", col_list, table, core_class );
3451 char* join_clause = searchJOIN( join_hash, meta );
3452 OSRF_BUFFER_ADD_CHAR(sql_buf, ' ');
3453 OSRF_BUFFER_ADD(sql_buf, join_clause);
3457 osrfLogDebug(OSRF_LOG_MARK, "%s pre-predicate SQL = %s",
3458 MODULENAME, OSRF_BUFFER_C_STR(sql_buf));
3460 buffer_add(sql_buf, " WHERE ");
3462 char* pred = searchWHERE( search_hash, meta, AND_OP_JOIN, ctx );
3464 osrfAppSessionStatus(
3466 OSRF_STATUS_INTERNALSERVERERROR,
3467 "osrfMethodException",
3469 "Severe query error -- see error log for more details"
3471 buffer_free(sql_buf);
3472 if(defaultselhash) jsonObjectFree(defaultselhash);
3475 buffer_add(sql_buf, pred);
3480 char* string = NULL;
3481 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "order_by" )) ){
3483 growing_buffer* order_buf = buffer_init(128);
3486 jsonIterator* class_itr = jsonNewIterator( _tmp );
3487 while ( (snode = jsonIteratorNext( class_itr )) ) {
3489 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
3492 if ( snode->type == JSON_HASH ) {
3494 jsonIterator* order_itr = jsonNewIterator( snode );
3495 while ( (onode = jsonIteratorNext( order_itr )) ) {
3497 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ))
3500 char* direction = NULL;
3501 if ( onode->type == JSON_HASH ) {
3502 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3503 string = searchFieldTransform(
3505 oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ),
3509 growing_buffer* field_buf = buffer_init(16);
3510 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3511 string = buffer_release(field_buf);
3514 if ( (_tmp = jsonObjectGetKeyConst( onode, "direction" )) ) {
3515 direction = jsonObjectToSimpleString(_tmp);
3516 if (!strncasecmp(direction, "d", 1)) {
3518 direction = " DESC";
3526 string = strdup(order_itr->key);
3527 direction = jsonObjectToSimpleString(onode);
3528 if (!strncasecmp(direction, "d", 1)) {
3530 direction = " DESC";
3540 buffer_add(order_buf, ", ");
3543 buffer_add(order_buf, string);
3547 buffer_add(order_buf, direction);
3552 jsonIteratorFree(order_itr);
3555 string = jsonObjectToSimpleString(snode);
3556 buffer_add(order_buf, string);
3563 jsonIteratorFree(class_itr);
3565 string = buffer_release(order_buf);
3568 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3569 OSRF_BUFFER_ADD( sql_buf, string );
3575 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "limit" )) ){
3576 string = jsonObjectToSimpleString(_tmp);
3585 _tmp = jsonObjectGetKeyConst( order_hash, "offset" );
3587 string = jsonObjectToSimpleString(_tmp);
3597 if (defaultselhash) jsonObjectFree(defaultselhash);
3599 OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3600 return buffer_release(sql_buf);
3603 int doJSONSearch ( osrfMethodContext* ctx ) {
3604 if(osrfMethodVerifyContext( ctx )) {
3605 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
3609 osrfLogDebug(OSRF_LOG_MARK, "Recieved query request");
3614 dbhandle = writehandle;
3616 jsonObject* hash = jsonObjectGetIndex(ctx->params, 0);
3620 if ( obj_is_true( jsonObjectGetKey( hash, "distinct" ) ) )
3621 flags |= SELECT_DISTINCT;
3623 if ( obj_is_true( jsonObjectGetKey( hash, "no_i18n" ) ) )
3624 flags |= DISABLE_I18N;
3626 osrfLogDebug(OSRF_LOG_MARK, "Building SQL ...");
3629 jsonObjectGetKey( hash, "select" ),
3630 jsonObjectGetKey( hash, "from" ),
3631 jsonObjectGetKey( hash, "where" ),
3632 jsonObjectGetKey( hash, "having" ),
3633 jsonObjectGetKey( hash, "order_by" ),
3634 jsonObjectGetKey( hash, "limit" ),
3635 jsonObjectGetKey( hash, "offset" ),
3644 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
3645 dbi_result result = dbi_conn_query(dbhandle, sql);
3648 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
3650 if (dbi_result_first_row(result)) {
3651 /* JSONify the result */
3652 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
3655 jsonObject* return_val = oilsMakeJSONFromResult( result );
3656 osrfAppRespond( ctx, return_val );
3657 jsonObjectFree( return_val );
3658 } while (dbi_result_next_row(result));
3661 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
3664 osrfAppRespondComplete( ctx, NULL );
3666 /* clean up the query */
3667 dbi_result_free(result);
3671 osrfLogError(OSRF_LOG_MARK, "%s: Error with query [%s]", MODULENAME, sql);
3672 osrfAppSessionStatus(
3674 OSRF_STATUS_INTERNALSERVERERROR,
3675 "osrfMethodException",
3677 "Severe query error -- see error log for more details"
3685 static jsonObject* doFieldmapperSearch ( osrfMethodContext* ctx, osrfHash* meta,
3686 const jsonObject* params, int* err ) {
3689 dbhandle = writehandle;
3691 osrfHash* links = osrfHashGet(meta, "links");
3692 osrfHash* fields = osrfHashGet(meta, "fields");
3693 char* core_class = osrfHashGet(meta, "classname");
3694 char* pkey = osrfHashGet(meta, "primarykey");
3696 const jsonObject* _tmp;
3698 jsonObject* search_hash = jsonObjectGetIndex(params, 0);
3699 jsonObject* order_hash = jsonObjectGetIndex(params, 1);
3701 char* sql = buildSELECT( search_hash, order_hash, meta, ctx );
3703 osrfLogDebug(OSRF_LOG_MARK, "Problem building query, returning NULL");
3708 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
3710 dbi_result result = dbi_conn_query(dbhandle, sql);
3711 if( NULL == result ) {
3712 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]",
3713 MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
3714 osrfAppSessionStatus(
3716 OSRF_STATUS_INTERNALSERVERERROR,
3717 "osrfMethodException",
3719 "Severe query error -- see error log for more details"
3726 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
3729 jsonObject* res_list = jsonNewObjectType(JSON_ARRAY);
3730 osrfHash* dedup = osrfNewHash();
3732 if (dbi_result_first_row(result)) {
3733 /* JSONify the result */
3734 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
3736 obj = oilsMakeFieldmapperFromResult( result, meta );
3737 char* pkey_val = oilsFMGetString( obj, pkey );
3738 if ( osrfHashGet( dedup, pkey_val ) ) {
3739 jsonObjectFree(obj);
3742 osrfHashSet( dedup, pkey_val, pkey_val );
3743 jsonObjectPush(res_list, obj);
3745 } while (dbi_result_next_row(result));
3747 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s",
3751 osrfHashFree(dedup);
3752 /* clean up the query */
3753 dbi_result_free(result);
3756 if (res_list->size && order_hash) {
3757 _tmp = jsonObjectGetKeyConst( order_hash, "flesh" );
3759 int x = (int)jsonObjectGetNumber(_tmp);
3760 if (x == -1 || x > max_flesh_depth) x = max_flesh_depth;
3762 const jsonObject* temp_blob;
3763 if ((temp_blob = jsonObjectGetKeyConst( order_hash, "flesh_fields" )) && x > 0) {
3765 jsonObject* flesh_blob = jsonObjectClone( temp_blob );
3766 const jsonObject* flesh_fields = jsonObjectGetKeyConst( flesh_blob, core_class );
3768 osrfStringArray* link_fields = NULL;
3771 if (flesh_fields->size == 1) {
3772 char* _t = jsonObjectToSimpleString( jsonObjectGetIndex( flesh_fields, 0 ) );
3773 if (!strcmp(_t,"*")) link_fields = osrfHashKeys( links );
3779 link_fields = osrfNewStringArray(1);
3780 jsonIterator* _i = jsonNewIterator( flesh_fields );
3781 while ((_f = jsonIteratorNext( _i ))) {
3782 osrfStringArrayAdd( link_fields, jsonObjectToSimpleString( _f ) );
3784 jsonIteratorFree(_i);
3789 jsonIterator* itr = jsonNewIterator( res_list );
3790 while ((cur = jsonIteratorNext( itr ))) {
3795 while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
3797 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
3799 osrfHash* kid_link = osrfHashGet(links, link_field);
3800 if (!kid_link) continue;
3802 osrfHash* field = osrfHashGet(fields, link_field);
3803 if (!field) continue;
3805 osrfHash* value_field = field;
3807 osrfHash* kid_idl = osrfHashGet(oilsIDL(), osrfHashGet(kid_link, "class"));
3808 if (!kid_idl) continue;
3810 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
3811 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
3814 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
3815 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
3818 osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
3820 if (link_map->size > 0) {
3821 jsonObject* _kid_key = jsonNewObjectType(JSON_ARRAY);
3824 jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
3829 osrfHashGet(kid_link, "class"),
3836 "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
3837 osrfHashGet(kid_link, "field"),
3838 osrfHashGet(kid_link, "class"),
3839 osrfHashGet(kid_link, "key"),
3840 osrfHashGet(kid_link, "reltype")
3843 jsonObject* fake_params = jsonNewObjectType(JSON_ARRAY);
3844 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH)); // search hash
3845 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH)); // order/flesh hash
3847 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
3850 jsonObjectToSimpleString(
3853 atoi( osrfHashGet(value_field, "array_position") )
3858 osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
3863 jsonObjectGetIndex(fake_params, 0),
3864 osrfHashGet(kid_link, "key"),
3865 jsonNewObject( search_key )
3872 jsonObjectGetIndex(fake_params, 1),
3874 jsonNewNumberObject( (double)(x - 1 + link_map->size) )
3878 jsonObjectSetKey( jsonObjectGetIndex(fake_params, 1), "flesh_fields", jsonObjectClone(flesh_blob) );
3880 if (jsonObjectGetKeyConst(order_hash, "order_by")) {
3882 jsonObjectGetIndex(fake_params, 1),
3884 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "order_by"))
3888 if (jsonObjectGetKeyConst(order_hash, "select")) {
3890 jsonObjectGetIndex(fake_params, 1),
3892 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "select"))
3896 jsonObject* kids = doFieldmapperSearch(ctx, kid_idl, fake_params, err);
3899 jsonObjectFree( fake_params );
3900 osrfStringArrayFree(link_fields);
3901 jsonIteratorFree(itr);
3902 jsonObjectFree(res_list);
3903 jsonObjectFree(flesh_blob);
3907 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
3909 jsonObject* X = NULL;
3910 if ( link_map->size > 0 && kids->size > 0 ) {
3912 kids = jsonNewObjectType(JSON_ARRAY);
3914 jsonObject* _k_node;
3915 jsonIterator* _k = jsonNewIterator( X );
3916 while ((_k_node = jsonIteratorNext( _k ))) {
3922 (unsigned long)atoi(
3928 osrfHashGet(kid_link, "class")
3932 osrfStringArrayGetString( link_map, 0 )
3941 jsonIteratorFree(_k);
3944 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" )) || !(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) {
3945 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
3948 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
3949 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
3953 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
3954 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
3957 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
3958 jsonObjectClone( kids )
3963 jsonObjectFree(kids);
3967 jsonObjectFree( kids );
3968 jsonObjectFree( fake_params );
3970 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
3971 osrfLogDebug(OSRF_LOG_MARK, "%s", jsonObjectToJSON(cur));
3975 jsonObjectFree( flesh_blob );
3976 osrfStringArrayFree(link_fields);
3977 jsonIteratorFree(itr);
3986 static jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
3988 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
3990 jsonObject* target = jsonObjectGetIndex( ctx->params, 1 );
3992 jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
3995 if (!verifyObjectClass(ctx, target)) {
4000 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4001 osrfAppSessionStatus(
4003 OSRF_STATUS_BADREQUEST,
4004 "osrfMethodException",
4006 "No active transaction -- required for UPDATE"
4012 // The following test is harmless but redundant. If a class is
4013 // readonly, we don't register an update method for it.
4014 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4015 osrfAppSessionStatus(
4017 OSRF_STATUS_BADREQUEST,
4018 "osrfMethodException",
4020 "Cannot UPDATE readonly class"
4026 dbhandle = writehandle;
4028 char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
4030 // Set the last_xact_id
4031 int index = oilsIDL_ntop( target->classname, "last_xact_id" );
4033 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
4034 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
4037 char* pkey = osrfHashGet(meta, "primarykey");
4038 osrfHash* fields = osrfHashGet(meta, "fields");
4040 char* id = oilsFMGetString( target, pkey );
4044 "%s updating %s object with %s = %s",
4046 osrfHashGet(meta, "fieldmapper"),
4051 growing_buffer* sql = buffer_init(128);
4052 buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
4057 osrfStringArray* field_list = osrfHashKeys( fields );
4058 while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
4060 osrfHash* field = osrfHashGet( fields, field_name );
4062 if(!( strcmp( field_name, pkey ) )) continue;
4063 if( str_is_true( osrfHashGet(osrfHashGet(fields,field_name), "virtual") ) )
4066 const jsonObject* field_object = oilsFMGetObject( target, field_name );
4068 int value_is_numeric = 0; // boolean
4070 if (field_object && field_object->classname) {
4071 value = oilsFMGetString(
4073 (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
4076 value = jsonObjectToSimpleString( field_object );
4077 if( field_object && JSON_NUMBER == field_object->type )
4078 value_is_numeric = 1;
4081 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
4083 if (!field_object || field_object->type == JSON_NULL) {
4084 if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
4085 if (first) first = 0;
4086 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4087 buffer_fadd( sql, " %s = NULL", field_name );
4090 } else if ( value_is_numeric || !strcmp( get_primitive( field ), "number") ) {
4091 if (first) first = 0;
4092 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4094 const char* numtype = get_datatype( field );
4095 if ( !strncmp( numtype, "INT", 3 ) ) {
4096 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
4097 } else if ( !strcmp( numtype, "NUMERIC" ) ) {
4098 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
4101 osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, numtype );
4104 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4105 if (first) first = 0;
4106 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4107 buffer_fadd( sql, " %s = %s", field_name, value );
4110 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4111 osrfAppSessionStatus(
4113 OSRF_STATUS_INTERNALSERVERERROR,
4114 "osrfMethodException",
4116 "Error quoting string -- please see the error log for more details"
4130 jsonObject* obj = jsonNewObject(id);
4132 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4133 dbi_conn_quote_string(dbhandle, &id);
4135 buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
4137 char* query = buffer_release(sql);
4138 osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
4140 dbi_result result = dbi_conn_query(dbhandle, query);
4144 jsonObjectFree(obj);
4145 obj = jsonNewObject(NULL);
4148 "%s ERROR updating %s object with %s = %s",
4150 osrfHashGet(meta, "fieldmapper"),
4161 static jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
4163 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4165 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4166 osrfAppSessionStatus(
4168 OSRF_STATUS_BADREQUEST,
4169 "osrfMethodException",
4171 "No active transaction -- required for DELETE"
4177 // The following test is harmless but redundant. If a class is
4178 // readonly, we don't register a delete method for it.
4179 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4180 osrfAppSessionStatus(
4182 OSRF_STATUS_BADREQUEST,
4183 "osrfMethodException",
4185 "Cannot DELETE readonly class"
4191 dbhandle = writehandle;
4195 char* pkey = osrfHashGet(meta, "primarykey");
4203 if (jsonObjectGetIndex(ctx->params, _obj_pos)->classname) {
4204 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, _obj_pos ))) {
4209 id = oilsFMGetString( jsonObjectGetIndex(ctx->params, _obj_pos), pkey );
4212 if (!verifyObjectPCRUD( ctx, NULL )) {
4217 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, _obj_pos));
4222 "%s deleting %s object with %s = %s",
4224 osrfHashGet(meta, "fieldmapper"),
4229 obj = jsonNewObject(id);
4231 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4232 dbi_conn_quote_string(writehandle, &id);
4234 dbi_result result = dbi_conn_queryf(writehandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
4237 jsonObjectFree(obj);
4238 obj = jsonNewObject(NULL);
4241 "%s ERROR deleting %s object with %s = %s",
4243 osrfHashGet(meta, "fieldmapper"),
4256 static jsonObject* oilsMakeFieldmapperFromResult( dbi_result result, osrfHash* meta) {
4257 if(!(result && meta)) return jsonNULL;
4259 jsonObject* object = jsonNewObject(NULL);
4260 jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
4262 osrfHash* fields = osrfHashGet(meta, "fields");
4264 osrfLogInternal(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
4268 char dt_string[256];
4272 int columnIndex = 1;
4274 unsigned short type;
4275 const char* columnName;
4277 /* cycle through the column list */
4278 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4280 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4282 fmIndex = -1; // reset the position
4284 /* determine the field type and storage attributes */
4285 type = dbi_result_get_field_type(result, columnName);
4286 attr = dbi_result_get_field_attribs(result, columnName);
4288 /* fetch the fieldmapper index */
4289 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
4291 if ( str_is_true( osrfHashGet(_f, "virtual") ) )
4294 const char* pos = (char*)osrfHashGet(_f, "array_position");
4295 if ( !pos ) continue;
4297 fmIndex = atoi( pos );
4298 osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
4303 if (dbi_result_field_is_null(result, columnName)) {
4304 jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
4309 case DBI_TYPE_INTEGER :
4311 if( attr & DBI_INTEGER_SIZE8 )
4312 jsonObjectSetIndex( object, fmIndex,
4313 jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
4315 jsonObjectSetIndex( object, fmIndex,
4316 jsonNewNumberObject(dbi_result_get_int(result, columnName)));
4320 case DBI_TYPE_DECIMAL :
4321 jsonObjectSetIndex( object, fmIndex,
4322 jsonNewNumberObject(dbi_result_get_double(result, columnName)));
4325 case DBI_TYPE_STRING :
4331 jsonNewObject( dbi_result_get_string(result, columnName) )
4336 case DBI_TYPE_DATETIME :
4338 memset(dt_string, '\0', sizeof(dt_string));
4339 memset(&gmdt, '\0', sizeof(gmdt));
4341 _tmp_dt = dbi_result_get_datetime(result, columnName);
4344 if (!(attr & DBI_DATETIME_DATE)) {
4345 gmtime_r( &_tmp_dt, &gmdt );
4346 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4347 } else if (!(attr & DBI_DATETIME_TIME)) {
4348 localtime_r( &_tmp_dt, &gmdt );
4349 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4351 localtime_r( &_tmp_dt, &gmdt );
4352 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4355 jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
4359 case DBI_TYPE_BINARY :
4360 osrfLogError( OSRF_LOG_MARK,
4361 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4369 static jsonObject* oilsMakeJSONFromResult( dbi_result result ) {
4370 if(!result) return jsonNULL;
4372 jsonObject* object = jsonNewObject(NULL);
4375 char dt_string[256];
4379 int columnIndex = 1;
4381 unsigned short type;
4382 const char* columnName;
4384 /* cycle through the column list */
4385 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4387 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4389 fmIndex = -1; // reset the position
4391 /* determine the field type and storage attributes */
4392 type = dbi_result_get_field_type(result, columnName);
4393 attr = dbi_result_get_field_attribs(result, columnName);
4395 if (dbi_result_field_is_null(result, columnName)) {
4396 jsonObjectSetKey( object, columnName, jsonNewObject(NULL) );
4401 case DBI_TYPE_INTEGER :
4403 if( attr & DBI_INTEGER_SIZE8 )
4404 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_longlong(result, columnName)) );
4406 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_int(result, columnName)) );
4409 case DBI_TYPE_DECIMAL :
4410 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_double(result, columnName)) );
4413 case DBI_TYPE_STRING :
4414 jsonObjectSetKey( object, columnName, jsonNewObject(dbi_result_get_string(result, columnName)) );
4417 case DBI_TYPE_DATETIME :
4419 memset(dt_string, '\0', sizeof(dt_string));
4420 memset(&gmdt, '\0', sizeof(gmdt));
4422 _tmp_dt = dbi_result_get_datetime(result, columnName);
4425 if (!(attr & DBI_DATETIME_DATE)) {
4426 gmtime_r( &_tmp_dt, &gmdt );
4427 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4428 } else if (!(attr & DBI_DATETIME_TIME)) {
4429 localtime_r( &_tmp_dt, &gmdt );
4430 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4432 localtime_r( &_tmp_dt, &gmdt );
4433 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4436 jsonObjectSetKey( object, columnName, jsonNewObject(dt_string) );
4439 case DBI_TYPE_BINARY :
4440 osrfLogError( OSRF_LOG_MARK,
4441 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4449 // Interpret a string as true or false
4450 static int str_is_true( const char* str ) {
4451 if( NULL == str || strcasecmp( str, "true" ) )
4457 // Interpret a jsonObject as true or false
4458 static int obj_is_true( const jsonObject* obj ) {
4461 else switch( obj->type )
4469 if( strcasecmp( obj->value.s, "true" ) )
4473 case JSON_NUMBER : // Support 1/0 for perl's sake
4474 if( jsonObjectGetNumber( obj ) == 1.0 )
4483 // Translate a numeric code into a text string identifying a type of
4484 // jsonObject. To be used for building error messages.
4485 static const char* json_type( int code ) {
4491 return "JSON_ARRAY";
4493 return "JSON_STRING";
4495 return "JSON_NUMBER";
4501 return "(unrecognized)";
4505 // Extract the "primitive" attribute from an IDL field definition.
4506 // If we haven't initialized the app, then we must be running in
4507 // some kind of testbed. In that case, default to "string".
4508 static const char* get_primitive( osrfHash* field ) {
4509 const char* s = osrfHashGet( field, "primitive" );
4511 if( child_initialized )
4514 "%s ERROR No \"datatype\" attribute for field \"%s\"",
4516 osrfHashGet( field, "name" )
4524 // Extract the "datatype" attribute from an IDL field definition.
4525 // If we haven't initialized the app, then we must be running in
4526 // some kind of testbed. In that case, default to to NUMERIC,
4527 // since we look at the datatype only for numbers.
4528 static const char* get_datatype( osrfHash* field ) {
4529 const char* s = osrfHashGet( field, "datatype" );
4531 if( child_initialized )
4534 "%s ERROR No \"datatype\" attribute for field \"%s\"",
4536 osrfHashGet( field, "name" )
4545 If the input string is potentially a valid SQL identifier, return 1.
4548 Purpose: to prevent certain kinds of SQL injection. To that end we
4549 don't necessarily need to follow all the rules exactly, such as requiring
4550 that the first character not be a digit.
4552 We allow leading and trailing white space. In between, we do not allow
4553 punctuation (except for underscores and dollar signs), control
4554 characters, or embedded white space.
4556 More pedantically we should allow quoted identifiers containing arbitrary
4557 characters, but for the foreseeable future such quoted identifiers are not
4558 likely to be an issue.
4560 static int is_identifier( const char* s) {
4564 // Skip leading white space
4565 while( isspace( (unsigned char) *s ) )
4569 return 0; // Nothing but white space? Not okay.
4571 // Check each character until we reach white space or
4572 // end-of-string. Letters, digits, underscores, and
4573 // dollar signs are okay. Control characters and other
4574 // punctuation characters are not okay. Anything else
4575 // is okay -- it could for example be part of a multibyte
4576 // UTF8 character such as a letter with diacritical marks,
4577 // and those are allowed.
4579 if( isalnum( (unsigned char) *s )
4582 ; // Fine; keep going
4583 else if( ispunct( (unsigned char) *s )
4584 || iscntrl( (unsigned char) *s ) )
4587 } while( *s && ! isspace( (unsigned char) *s ) );
4589 // If we found any white space in the above loop,
4590 // the rest had better be all white space.
4592 while( isspace( (unsigned char) *s ) )
4596 return 0; // White space was embedded within non-white space