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 if( !jsonIteratorHasNext( search_itr ) ) {
2303 "%s: Invalid predicate structure: empty JSON array",
2306 jsonIteratorFree( search_itr );
2307 buffer_free( sql_buf );
2311 while ( (node = jsonIteratorNext( search_itr )) ) {
2315 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
2316 else buffer_add(sql_buf, " AND ");
2319 char* subpred = searchWHERE( node, meta, opjoin_type, ctx );
2320 buffer_fadd(sql_buf, "( %s )", subpred);
2323 jsonIteratorFree(search_itr);
2325 } else if ( search_hash->type == JSON_HASH ) {
2326 osrfLogDebug(OSRF_LOG_MARK, "%s: In WHERE clause, condition type is JSON_HASH", MODULENAME);
2327 jsonIterator* search_itr = jsonNewIterator( search_hash );
2328 if( !jsonIteratorHasNext( search_itr ) ) {
2331 "%s: Invalid predicate structure: empty JSON object",
2334 jsonIteratorFree( search_itr );
2335 buffer_free( sql_buf );
2339 while ( (node = jsonIteratorNext( search_itr )) ) {
2344 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
2345 else buffer_add(sql_buf, " AND ");
2348 if ( '+' == search_itr->key[ 0 ] ) {
2349 if ( node->type == JSON_STRING ) {
2350 // Intended purpose; to allow reference to a Boolean column.
2351 // Verify that the string looks like an identifier.
2352 const char* subpred = jsonObjectGetString( node );
2353 if( ! is_identifier( subpred ) ) {
2356 "%s: Invalid boolean identifier in WHERE clause: \"%s\"",
2360 jsonIteratorFree( search_itr );
2361 buffer_free( sql_buf );
2364 buffer_fadd(sql_buf, " \"%s\".%s ", search_itr->key + 1, subpred);
2366 char* subpred = searchWHERE( node, osrfHashGet( oilsIDL(), search_itr->key + 1 ), AND_OP_JOIN, ctx );
2367 buffer_fadd(sql_buf, "( %s )", subpred);
2370 } else if ( !strcasecmp("-or",search_itr->key) ) {
2371 char* subpred = searchWHERE( node, meta, OR_OP_JOIN, ctx );
2372 buffer_fadd(sql_buf, "( %s )", subpred);
2374 } else if ( !strcasecmp("-and",search_itr->key) ) {
2375 char* subpred = searchWHERE( node, meta, AND_OP_JOIN, ctx );
2376 buffer_fadd(sql_buf, "( %s )", subpred);
2378 } else if ( !strcasecmp("-exists",search_itr->key) ) {
2379 char* subpred = SELECT(
2381 jsonObjectGetKey( node, "select" ),
2382 jsonObjectGetKey( node, "from" ),
2383 jsonObjectGetKey( node, "where" ),
2384 jsonObjectGetKey( node, "having" ),
2385 jsonObjectGetKey( node, "order_by" ),
2386 jsonObjectGetKey( node, "limit" ),
2387 jsonObjectGetKey( node, "offset" ),
2391 buffer_fadd(sql_buf, "EXISTS ( %s )", subpred);
2393 } else if ( !strcasecmp("-not-exists",search_itr->key) ) {
2394 char* subpred = SELECT(
2396 jsonObjectGetKey( node, "select" ),
2397 jsonObjectGetKey( node, "from" ),
2398 jsonObjectGetKey( node, "where" ),
2399 jsonObjectGetKey( node, "having" ),
2400 jsonObjectGetKey( node, "order_by" ),
2401 jsonObjectGetKey( node, "limit" ),
2402 jsonObjectGetKey( node, "offset" ),
2406 buffer_fadd(sql_buf, "NOT EXISTS ( %s )", subpred);
2410 char* class = osrfHashGet(meta, "classname");
2411 osrfHash* fields = osrfHashGet(meta, "fields");
2412 osrfHash* field = osrfHashGet( fields, search_itr->key );
2416 char* table = getSourceDefinition(meta);
2418 table = strdup( "(?)" );
2421 "%s: Attempt to reference non-existent column %s on %s (%s)",
2427 buffer_free(sql_buf);
2429 jsonIteratorFree(search_itr);
2433 char* subpred = searchPredicate( class, field, node, ctx );
2434 buffer_add( sql_buf, subpred );
2438 jsonIteratorFree(search_itr);
2441 // ERROR ... only hash and array allowed at this level
2442 char* predicate_string = jsonObjectToJSON( search_hash );
2445 "%s: Invalid predicate structure: %s",
2449 buffer_free(sql_buf);
2450 free(predicate_string);
2455 return buffer_release(sql_buf);
2459 /* method context */ osrfMethodContext* ctx,
2461 /* SELECT */ jsonObject* selhash,
2462 /* FROM */ jsonObject* join_hash,
2463 /* WHERE */ jsonObject* search_hash,
2464 /* HAVING */ jsonObject* having_hash,
2465 /* ORDER BY */ jsonObject* order_hash,
2466 /* LIMIT */ jsonObject* limit,
2467 /* OFFSET */ jsonObject* offset,
2468 /* flags */ int flags
2470 const char* locale = osrf_message_get_last_locale();
2472 // in case we don't get a select list
2473 jsonObject* defaultselhash = NULL;
2475 // general tmp objects
2476 const jsonObject* tmp_const;
2477 jsonObject* selclass = NULL;
2478 jsonObject* selfield = NULL;
2479 jsonObject* snode = NULL;
2480 jsonObject* onode = NULL;
2482 char* string = NULL;
2483 int from_function = 0;
2488 // the core search class
2489 char* core_class = NULL;
2491 // metadata about the core search class
2492 osrfHash* core_meta = NULL;
2494 // punt if there's no core class
2495 if (!join_hash || ( join_hash->type == JSON_HASH && !join_hash->size )) {
2498 "%s: FROM clause is missing or empty",
2502 osrfAppSessionStatus(
2504 OSRF_STATUS_INTERNALSERVERERROR,
2505 "osrfMethodException",
2507 "FROM clause is missing or empty in JSON query"
2512 // get the core class -- the only key of the top level FROM clause, or a string
2513 if (join_hash->type == JSON_HASH) {
2514 jsonIterator* tmp_itr = jsonNewIterator( join_hash );
2515 snode = jsonIteratorNext( tmp_itr );
2517 core_class = strdup( tmp_itr->key );
2520 jsonObject* extra = jsonIteratorNext( tmp_itr );
2522 jsonIteratorFree( tmp_itr );
2525 // There shouldn't be more than one entry in join_hash
2529 "%s: Malformed FROM clause: extra entry in JSON_HASH",
2533 osrfAppSessionStatus(
2535 OSRF_STATUS_INTERNALSERVERERROR,
2536 "osrfMethodException",
2538 "Malformed FROM clause in JSON query"
2541 return NULL; // Malformed join_hash; extra entry
2543 } else if (join_hash->type == JSON_ARRAY) {
2545 core_class = jsonObjectToSimpleString( jsonObjectGetIndex(join_hash, 0) );
2548 } else if (join_hash->type == JSON_STRING) {
2549 core_class = jsonObjectToSimpleString( join_hash );
2555 "%s: FROM clause is unexpected JSON type: %s",
2557 json_type( join_hash->type )
2560 osrfAppSessionStatus(
2562 OSRF_STATUS_INTERNALSERVERERROR,
2563 "osrfMethodException",
2565 "Ill-formed FROM clause in JSON query"
2571 if (!from_function) {
2572 // Get the IDL class definition for the core class
2573 core_meta = osrfHashGet( oilsIDL(), core_class );
2574 if( !core_meta ) { // Didn't find it?
2577 "%s: SELECT clause references undefined class: \"%s\"",
2582 osrfAppSessionStatus(
2584 OSRF_STATUS_INTERNALSERVERERROR,
2585 "osrfMethodException",
2587 "SELECT clause references undefined class in JSON query"
2593 // Make sure the class isn't virtual
2594 if( str_is_true( osrfHashGet( core_meta, "virtual" ) ) ) {
2597 "%s: Core class is virtual: \"%s\"",
2602 osrfAppSessionStatus(
2604 OSRF_STATUS_INTERNALSERVERERROR,
2605 "osrfMethodException",
2607 "FROM clause references virtual class in JSON query"
2614 // if the select list is empty, or the core class field list is '*',
2615 // build the default select list ...
2617 selhash = defaultselhash = jsonNewObjectType(JSON_HASH);
2618 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2619 } else if( selhash->type != JSON_HASH ) {
2622 "%s: Expected JSON_HASH for SELECT clause; found %s",
2624 json_type( selhash->type )
2628 osrfAppSessionStatus(
2630 OSRF_STATUS_INTERNALSERVERERROR,
2631 "osrfMethodException",
2633 "Malformed SELECT clause in JSON query"
2637 } else if ( (tmp_const = jsonObjectGetKeyConst( selhash, core_class )) && tmp_const->type == JSON_STRING ) {
2638 char* _x = jsonObjectToSimpleString( tmp_const );
2639 if (!strncmp( "*", _x, 1 )) {
2640 jsonObjectRemoveKey( selhash, core_class );
2641 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2647 growing_buffer* sql_buf = buffer_init(128);
2649 // temp buffer for the SELECT list
2650 growing_buffer* select_buf = buffer_init(128);
2651 growing_buffer* order_buf = buffer_init(128);
2652 growing_buffer* group_buf = buffer_init(128);
2653 growing_buffer* having_buf = buffer_init(128);
2655 int aggregate_found = 0; // boolean
2657 // Build a select list
2658 if(from_function) // From a function we select everything
2659 OSRF_BUFFER_ADD_CHAR( select_buf, '*' );
2662 // If we need to build a default list, prepare to do so
2663 jsonObject* _tmp = jsonObjectGetKey( selhash, core_class );
2664 if ( _tmp && !_tmp->size ) {
2666 osrfHash* core_fields = osrfHashGet( core_meta, "fields" );
2668 osrfHashIterator* field_itr = osrfNewHashIterator( core_fields );
2669 osrfHash* field_def;
2670 while( ( field_def = osrfHashIteratorNext( field_itr ) ) ) {
2671 if( ! str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2672 // This field is not virtual, so add it to the list
2673 jsonObjectPush( _tmp, jsonNewObject( osrfHashIteratorKey( field_itr ) ) );
2676 osrfHashIteratorFree( field_itr );
2679 // Now build the actual select list
2683 jsonIterator* selclass_itr = jsonNewIterator( selhash );
2684 while ( (selclass = jsonIteratorNext( selclass_itr )) ) { // For each class
2686 // Make sure the class is defined in the IDL
2687 const char* cname = selclass_itr->key;
2688 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
2692 "%s: Selected class \"%s\" not defined in IDL",
2698 osrfAppSessionStatus(
2700 OSRF_STATUS_INTERNALSERVERERROR,
2701 "osrfMethodException",
2703 "Selected class is not defined"
2705 jsonIteratorFree( selclass_itr );
2706 buffer_free( sql_buf );
2707 buffer_free( select_buf );
2708 buffer_free( order_buf );
2709 buffer_free( group_buf );
2710 buffer_free( having_buf );
2711 if( defaultselhash ) jsonObjectFree( defaultselhash );
2716 // Make sure the target relation is in the join tree.
2718 // At this point join_hash is a step down from the join_hash we
2719 // received as a parameter. If the original was a JSON_STRING,
2720 // then json_hash is now NULL. If the original was a JSON_HASH,
2721 // then json_hash is now the first (and only) entry in it,
2722 // denoting the core class. We've already excluded the
2723 // possibility that the original was a JSON_ARRAY, because in
2724 // that case from_function would be non-NULL, and we wouldn't
2727 int class_in_from_clause; // boolean
2729 if ( ! strcmp( core_class, cname ))
2730 // This is the core class -- no problem
2731 class_in_from_clause = 1;
2734 // There's only one class in the FROM clause, and this isn't it
2735 class_in_from_clause = 0;
2736 else if (join_hash->type == JSON_STRING) {
2737 // There's only one class in the FROM clause
2738 string = jsonObjectToSimpleString(join_hash);
2739 if ( strcmp( string, cname ) )
2740 class_in_from_clause = 0; // This isn't it
2742 class_in_from_clause = 1; // This is it
2745 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", cname);
2746 if ( 0 == found->size )
2747 class_in_from_clause = 0; // Nowhere in the join tree
2749 class_in_from_clause = 1; // Found it
2750 jsonObjectFree( found );
2754 // If the class isn't in the FROM clause, bail out
2755 if( ! class_in_from_clause ) {
2758 "%s: SELECT clause references class not in FROM clause: \"%s\"",
2763 osrfAppSessionStatus(
2765 OSRF_STATUS_INTERNALSERVERERROR,
2766 "osrfMethodException",
2768 "Selected class not in FROM clause in JSON query"
2770 jsonIteratorFree( selclass_itr );
2771 buffer_free( sql_buf );
2772 buffer_free( select_buf );
2773 buffer_free( order_buf );
2774 buffer_free( group_buf );
2775 buffer_free( having_buf );
2776 if( defaultselhash ) jsonObjectFree( defaultselhash );
2781 // Look up some attributes of the current class, so that we
2782 // don't have to look them up again for each field
2783 osrfHash* class_field_set = osrfHashGet( idlClass, "fields" );
2784 const char* class_pkey = osrfHashGet( idlClass, "primarykey" );
2785 const char* class_tname = osrfHashGet( idlClass, "tablename" );
2787 // stitch together the column list ...
2788 jsonIterator* select_itr = jsonNewIterator( selclass );
2789 while ( (selfield = jsonIteratorNext( select_itr )) ) { // for each SELECT column
2791 // If we need a separator comma, add one
2795 OSRF_BUFFER_ADD_CHAR( select_buf, ',' );
2798 // ... if it's a string, just toss it on the pile
2799 if (selfield->type == JSON_STRING) {
2801 // Look up the field in the IDL
2802 const char* col_name = selfield->value.s;
2803 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
2805 // No such field in current class
2808 "%s: Selected column \"%s\" not defined in IDL for class \"%s\"",
2814 osrfAppSessionStatus(
2816 OSRF_STATUS_INTERNALSERVERERROR,
2817 "osrfMethodException",
2819 "Selected column not defined in JSON query"
2821 jsonIteratorFree( select_itr );
2822 jsonIteratorFree( selclass_itr );
2823 buffer_free( sql_buf );
2824 buffer_free( select_buf );
2825 buffer_free( order_buf );
2826 buffer_free( group_buf );
2827 buffer_free( having_buf );
2828 if( defaultselhash ) jsonObjectFree( defaultselhash );
2831 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2832 // Virtual field not allowed
2835 "%s: Selected column \"%s\" for class \"%s\" is virtual",
2841 osrfAppSessionStatus(
2843 OSRF_STATUS_INTERNALSERVERERROR,
2844 "osrfMethodException",
2846 "Selected column may not be virtual in JSON query"
2848 jsonIteratorFree( select_itr );
2849 jsonIteratorFree( selclass_itr );
2850 buffer_free( sql_buf );
2851 buffer_free( select_buf );
2852 buffer_free( order_buf );
2853 buffer_free( group_buf );
2854 buffer_free( having_buf );
2855 if( defaultselhash ) jsonObjectFree( defaultselhash );
2862 if (flags & DISABLE_I18N)
2865 i18n = osrfHashGet(field_def, "i18n");
2867 if( str_is_true( i18n ) ) {
2868 buffer_fadd( select_buf,
2869 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
2870 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, col_name );
2872 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
2875 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
2878 // ... but it could be an object, in which case we check for a Field Transform
2879 } else if (selfield->type == JSON_HASH) {
2881 char* col_name = jsonObjectToSimpleString( jsonObjectGetKeyConst( selfield, "column" ) );
2883 // Get the field definition from the IDL
2884 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
2886 // No such field in current class
2889 "%s: Selected column \"%s\" is not defined in IDL for class \"%s\"",
2895 osrfAppSessionStatus(
2897 OSRF_STATUS_INTERNALSERVERERROR,
2898 "osrfMethodException",
2900 "Selected column is not defined in JSON query"
2902 jsonIteratorFree( select_itr );
2903 jsonIteratorFree( selclass_itr );
2904 buffer_free( sql_buf );
2905 buffer_free( select_buf );
2906 buffer_free( order_buf );
2907 buffer_free( group_buf );
2908 buffer_free( having_buf );
2909 if( defaultselhash ) jsonObjectFree( defaultselhash );
2912 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2913 // No such field in current class
2916 "%s: Selected column \"%s\" is virtual for class \"%s\"",
2922 osrfAppSessionStatus(
2924 OSRF_STATUS_INTERNALSERVERERROR,
2925 "osrfMethodException",
2927 "Selected column is virtual in JSON query"
2929 jsonIteratorFree( select_itr );
2930 jsonIteratorFree( selclass_itr );
2931 buffer_free( sql_buf );
2932 buffer_free( select_buf );
2933 buffer_free( order_buf );
2934 buffer_free( group_buf );
2935 buffer_free( having_buf );
2936 if( defaultselhash ) jsonObjectFree( defaultselhash );
2941 // Decide what to use as a column alias
2943 if ((tmp_const = jsonObjectGetKeyConst( selfield, "alias" ))) {
2944 _alias = jsonObjectToSimpleString( tmp_const );
2945 } else { // Use field name as the alias
2949 if (jsonObjectGetKeyConst( selfield, "transform" )) {
2950 char* transform_str = searchFieldTransform(cname, field_def, selfield);
2951 if( transform_str ) {
2952 buffer_fadd(select_buf, " %s AS \"%s\"", transform_str, _alias);
2953 free(transform_str);
2956 osrfAppSessionStatus(
2958 OSRF_STATUS_INTERNALSERVERERROR,
2959 "osrfMethodException",
2961 "Unable to generate transform function in JSON query"
2963 jsonIteratorFree( select_itr );
2964 jsonIteratorFree( selclass_itr );
2965 buffer_free( sql_buf );
2966 buffer_free( select_buf );
2967 buffer_free( order_buf );
2968 buffer_free( group_buf );
2969 buffer_free( having_buf );
2970 if( defaultselhash ) jsonObjectFree( defaultselhash );
2978 if (flags & DISABLE_I18N)
2981 i18n = osrfHashGet(field_def, "i18n");
2983 if( str_is_true( i18n ) ) {
2984 buffer_fadd( select_buf,
2985 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
2986 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, _alias);
2988 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
2991 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
2995 if( _alias != col_name )
3002 "%s: Selected item is unexpected JSON type: %s",
3004 json_type( selfield->type )
3007 osrfAppSessionStatus(
3009 OSRF_STATUS_INTERNALSERVERERROR,
3010 "osrfMethodException",
3012 "Ill-formed SELECT item in JSON query"
3014 jsonIteratorFree( select_itr );
3015 jsonIteratorFree( selclass_itr );
3016 buffer_free( sql_buf );
3017 buffer_free( select_buf );
3018 buffer_free( order_buf );
3019 buffer_free( group_buf );
3020 buffer_free( having_buf );
3021 if( defaultselhash ) jsonObjectFree( defaultselhash );
3026 const jsonObject* agg_obj = jsonObjectGetKey( selfield, "aggregate" );
3027 if( obj_is_true( agg_obj ) )
3028 aggregate_found = 1;
3030 // Append a comma (except for the first one)
3031 // and add the column to a GROUP BY clause
3035 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3037 buffer_fadd(group_buf, " %d", sel_pos);
3041 if (is_agg->size || (flags & SELECT_DISTINCT)) {
3043 const jsonObject* aggregate_obj = jsonObjectGetKey( selfield, "aggregate" );
3044 if ( ! obj_is_true( aggregate_obj ) ) {
3048 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3051 buffer_fadd(group_buf, " %d", sel_pos);
3054 } else if (is_agg = jsonObjectGetKey( selfield, "having" )) {
3058 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3061 _column = searchFieldTransform(cname, field, selfield);
3062 OSRF_BUFFER_ADD_CHAR(group_buf, ' ');
3063 OSRF_BUFFER_ADD(group_buf, _column);
3064 _column = searchFieldTransform(cname, field, selfield);
3071 } // end while -- iterating across SELECT columns
3073 jsonIteratorFree(select_itr);
3074 } // end while -- iterating across classes
3076 jsonIteratorFree(selclass_itr);
3080 char* col_list = buffer_release(select_buf);
3082 if (from_function) table = searchValueTransform(join_hash);
3083 else table = getSourceDefinition(core_meta);
3087 osrfAppSessionStatus(
3089 OSRF_STATUS_INTERNALSERVERERROR,
3090 "osrfMethodException",
3092 "Unable to identify table for core class"
3095 buffer_free( sql_buf );
3096 buffer_free( order_buf );
3097 buffer_free( group_buf );
3098 buffer_free( having_buf );
3099 if( defaultselhash ) jsonObjectFree( defaultselhash );
3104 // Put it all together
3105 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\" ", col_list, table, core_class );
3109 if (!from_function) {
3110 // Now, walk the join tree and add that clause
3112 char* join_clause = searchJOIN( join_hash, core_meta );
3114 buffer_add(sql_buf, join_clause);
3118 osrfAppSessionStatus(
3120 OSRF_STATUS_INTERNALSERVERERROR,
3121 "osrfMethodException",
3123 "Unable to construct JOIN clause(s)"
3125 buffer_free( sql_buf );
3126 buffer_free( order_buf );
3127 buffer_free( group_buf );
3128 buffer_free( having_buf );
3129 if( defaultselhash ) jsonObjectFree( defaultselhash );
3135 // Build a WHERE clause, if there is one
3136 if ( search_hash ) {
3137 buffer_add(sql_buf, " WHERE ");
3139 // and it's on the WHERE clause
3140 char* pred = searchWHERE( search_hash, core_meta, AND_OP_JOIN, ctx );
3143 buffer_add(sql_buf, pred);
3147 osrfAppSessionStatus(
3149 OSRF_STATUS_INTERNALSERVERERROR,
3150 "osrfMethodException",
3152 "Severe query error in WHERE predicate -- see error log for more details"
3156 buffer_free(having_buf);
3157 buffer_free(group_buf);
3158 buffer_free(order_buf);
3159 buffer_free(sql_buf);
3160 if (defaultselhash) jsonObjectFree(defaultselhash);
3165 // Build a HAVING clause, if there is one
3166 if ( having_hash ) {
3167 buffer_add(sql_buf, " HAVING ");
3169 // and it's on the the WHERE clause
3170 char* pred = searchWHERE( having_hash, core_meta, AND_OP_JOIN, ctx );
3173 buffer_add(sql_buf, pred);
3177 osrfAppSessionStatus(
3179 OSRF_STATUS_INTERNALSERVERERROR,
3180 "osrfMethodException",
3182 "Severe query error in HAVING predicate -- see error log for more details"
3186 buffer_free(having_buf);
3187 buffer_free(group_buf);
3188 buffer_free(order_buf);
3189 buffer_free(sql_buf);
3190 if (defaultselhash) jsonObjectFree(defaultselhash);
3195 // Build an ORDER BY clause, if there is one
3197 jsonIterator* class_itr = jsonNewIterator( order_hash );
3198 while ( (snode = jsonIteratorNext( class_itr )) ) {
3200 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
3203 if ( snode->type == JSON_HASH ) {
3205 jsonIterator* order_itr = jsonNewIterator( snode );
3206 while ( (onode = jsonIteratorNext( order_itr )) ) {
3208 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ))
3211 char* direction = NULL;
3212 if ( onode->type == JSON_HASH ) {
3213 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3214 string = searchFieldTransform(
3216 oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ),
3220 growing_buffer* field_buf = buffer_init(16);
3221 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3222 string = buffer_release(field_buf);
3225 if ( (tmp_const = jsonObjectGetKeyConst( onode, "direction" )) ) {
3226 direction = jsonObjectToSimpleString(tmp_const);
3227 if (!strncasecmp(direction, "d", 1)) {
3229 direction = " DESC";
3237 string = strdup(order_itr->key);
3238 direction = jsonObjectToSimpleString(onode);
3239 if (!strncasecmp(direction, "d", 1)) {
3241 direction = " DESC";
3251 buffer_add(order_buf, ", ");
3254 buffer_add(order_buf, string);
3258 buffer_add(order_buf, direction);
3262 // jsonIteratorFree(order_itr);
3264 } else if ( snode->type == JSON_ARRAY ) {
3266 jsonIterator* order_itr = jsonNewIterator( snode );
3267 while ( (onode = jsonIteratorNext( order_itr )) ) {
3269 char* _f = jsonObjectToSimpleString( onode );
3271 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, _f))
3277 buffer_add(order_buf, ", ");
3280 buffer_add(order_buf, _f);
3284 // jsonIteratorFree(order_itr);
3287 // IT'S THE OOOOOOOOOOOLD STYLE!
3289 osrfLogError(OSRF_LOG_MARK, "%s: Possible SQL injection attempt; direct order by is not allowed", MODULENAME);
3291 osrfAppSessionStatus(
3293 OSRF_STATUS_INTERNALSERVERERROR,
3294 "osrfMethodException",
3296 "Severe query error -- see error log for more details"
3301 buffer_free(having_buf);
3302 buffer_free(group_buf);
3303 buffer_free(order_buf);
3304 buffer_free(sql_buf);
3305 if (defaultselhash) jsonObjectFree(defaultselhash);
3306 jsonIteratorFree(class_itr);
3311 // jsonIteratorFree(class_itr);
3315 string = buffer_release(group_buf);
3317 if ( *string && ( aggregate_found || (flags & SELECT_DISTINCT) ) ) {
3318 OSRF_BUFFER_ADD( sql_buf, " GROUP BY " );
3319 OSRF_BUFFER_ADD( sql_buf, string );
3324 string = buffer_release(having_buf);
3327 OSRF_BUFFER_ADD( sql_buf, " HAVING " );
3328 OSRF_BUFFER_ADD( sql_buf, string );
3333 string = buffer_release(order_buf);
3336 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3337 OSRF_BUFFER_ADD( sql_buf, string );
3343 string = jsonObjectToSimpleString(limit);
3344 buffer_fadd( sql_buf, " LIMIT %d", atoi(string) );
3349 string = jsonObjectToSimpleString(offset);
3350 buffer_fadd( sql_buf, " OFFSET %d", atoi(string) );
3354 if (!(flags & SUBSELECT)) OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3357 if (defaultselhash) jsonObjectFree(defaultselhash);
3359 return buffer_release(sql_buf);
3363 static char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
3365 const char* locale = osrf_message_get_last_locale();
3367 osrfHash* fields = osrfHashGet(meta, "fields");
3368 char* core_class = osrfHashGet(meta, "classname");
3370 const jsonObject* join_hash = jsonObjectGetKeyConst( order_hash, "join" );
3372 jsonObject* node = NULL;
3373 jsonObject* snode = NULL;
3374 jsonObject* onode = NULL;
3375 const jsonObject* _tmp = NULL;
3376 jsonObject* selhash = NULL;
3377 jsonObject* defaultselhash = NULL;
3379 growing_buffer* sql_buf = buffer_init(128);
3380 growing_buffer* select_buf = buffer_init(128);
3382 if ( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
3383 defaultselhash = jsonNewObjectType(JSON_HASH);
3384 selhash = defaultselhash;
3387 if ( !jsonObjectGetKeyConst(selhash,core_class) ) {
3388 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
3389 jsonObject* flist = jsonObjectGetKey( selhash, core_class );
3394 osrfStringArray* keys = osrfHashKeys( fields );
3395 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
3396 if( ! str_is_true( osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
3397 jsonObjectPush( flist, jsonNewObject( field ) );
3399 osrfStringArrayFree(keys);
3403 jsonIterator* class_itr = jsonNewIterator( selhash );
3404 while ( (snode = jsonIteratorNext( class_itr )) ) {
3406 char* cname = class_itr->key;
3407 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
3408 if (!idlClass) continue;
3410 if (strcmp(core_class,class_itr->key)) {
3411 if (!join_hash) continue;
3413 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", class_itr->key);
3415 jsonObjectFree(found);
3419 jsonObjectFree(found);
3422 jsonIterator* select_itr = jsonNewIterator( snode );
3423 while ( (node = jsonIteratorNext( select_itr )) ) {
3424 char* item_str = jsonObjectToSimpleString(node);
3425 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), item_str );
3427 char* fname = osrfHashGet(field, "name");
3429 if (!field) continue;
3434 OSRF_BUFFER_ADD_CHAR(select_buf, ',');
3439 const jsonObject* no_i18n_obj = jsonObjectGetKey( order_hash, "no_i18n" );
3440 if ( obj_is_true( no_i18n_obj ) ) // Suppress internationalization?
3443 i18n = osrfHashGet(field, "i18n");
3445 if( str_is_true( i18n ) ) {
3446 char* pkey = osrfHashGet(idlClass, "primarykey");
3447 char* tname = osrfHashGet(idlClass, "tablename");
3449 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);
3451 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3454 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3458 jsonIteratorFree(select_itr);
3461 jsonIteratorFree(class_itr);
3463 char* col_list = buffer_release(select_buf);
3464 char* table = getSourceDefinition(meta);
3466 table = strdup( "(null)" );
3468 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\"", col_list, table, core_class );
3473 char* join_clause = searchJOIN( join_hash, meta );
3474 OSRF_BUFFER_ADD_CHAR(sql_buf, ' ');
3475 OSRF_BUFFER_ADD(sql_buf, join_clause);
3479 osrfLogDebug(OSRF_LOG_MARK, "%s pre-predicate SQL = %s",
3480 MODULENAME, OSRF_BUFFER_C_STR(sql_buf));
3482 buffer_add(sql_buf, " WHERE ");
3484 char* pred = searchWHERE( search_hash, meta, AND_OP_JOIN, ctx );
3486 osrfAppSessionStatus(
3488 OSRF_STATUS_INTERNALSERVERERROR,
3489 "osrfMethodException",
3491 "Severe query error -- see error log for more details"
3493 buffer_free(sql_buf);
3494 if(defaultselhash) jsonObjectFree(defaultselhash);
3497 buffer_add(sql_buf, pred);
3502 char* string = NULL;
3503 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "order_by" )) ){
3505 growing_buffer* order_buf = buffer_init(128);
3508 jsonIterator* class_itr = jsonNewIterator( _tmp );
3509 while ( (snode = jsonIteratorNext( class_itr )) ) {
3511 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
3514 if ( snode->type == JSON_HASH ) {
3516 jsonIterator* order_itr = jsonNewIterator( snode );
3517 while ( (onode = jsonIteratorNext( order_itr )) ) {
3519 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ))
3522 char* direction = NULL;
3523 if ( onode->type == JSON_HASH ) {
3524 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3525 string = searchFieldTransform(
3527 oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ),
3531 growing_buffer* field_buf = buffer_init(16);
3532 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3533 string = buffer_release(field_buf);
3536 if ( (_tmp = jsonObjectGetKeyConst( onode, "direction" )) ) {
3537 direction = jsonObjectToSimpleString(_tmp);
3538 if (!strncasecmp(direction, "d", 1)) {
3540 direction = " DESC";
3548 string = strdup(order_itr->key);
3549 direction = jsonObjectToSimpleString(onode);
3550 if (!strncasecmp(direction, "d", 1)) {
3552 direction = " DESC";
3562 buffer_add(order_buf, ", ");
3565 buffer_add(order_buf, string);
3569 buffer_add(order_buf, direction);
3574 jsonIteratorFree(order_itr);
3577 string = jsonObjectToSimpleString(snode);
3578 buffer_add(order_buf, string);
3585 jsonIteratorFree(class_itr);
3587 string = buffer_release(order_buf);
3590 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3591 OSRF_BUFFER_ADD( sql_buf, string );
3597 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "limit" )) ){
3598 string = jsonObjectToSimpleString(_tmp);
3607 _tmp = jsonObjectGetKeyConst( order_hash, "offset" );
3609 string = jsonObjectToSimpleString(_tmp);
3619 if (defaultselhash) jsonObjectFree(defaultselhash);
3621 OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3622 return buffer_release(sql_buf);
3625 int doJSONSearch ( osrfMethodContext* ctx ) {
3626 if(osrfMethodVerifyContext( ctx )) {
3627 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
3631 osrfLogDebug(OSRF_LOG_MARK, "Recieved query request");
3636 dbhandle = writehandle;
3638 jsonObject* hash = jsonObjectGetIndex(ctx->params, 0);
3642 if ( obj_is_true( jsonObjectGetKey( hash, "distinct" ) ) )
3643 flags |= SELECT_DISTINCT;
3645 if ( obj_is_true( jsonObjectGetKey( hash, "no_i18n" ) ) )
3646 flags |= DISABLE_I18N;
3648 osrfLogDebug(OSRF_LOG_MARK, "Building SQL ...");
3651 jsonObjectGetKey( hash, "select" ),
3652 jsonObjectGetKey( hash, "from" ),
3653 jsonObjectGetKey( hash, "where" ),
3654 jsonObjectGetKey( hash, "having" ),
3655 jsonObjectGetKey( hash, "order_by" ),
3656 jsonObjectGetKey( hash, "limit" ),
3657 jsonObjectGetKey( hash, "offset" ),
3666 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
3667 dbi_result result = dbi_conn_query(dbhandle, sql);
3670 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
3672 if (dbi_result_first_row(result)) {
3673 /* JSONify the result */
3674 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
3677 jsonObject* return_val = oilsMakeJSONFromResult( result );
3678 osrfAppRespond( ctx, return_val );
3679 jsonObjectFree( return_val );
3680 } while (dbi_result_next_row(result));
3683 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
3686 osrfAppRespondComplete( ctx, NULL );
3688 /* clean up the query */
3689 dbi_result_free(result);
3693 osrfLogError(OSRF_LOG_MARK, "%s: Error with query [%s]", MODULENAME, sql);
3694 osrfAppSessionStatus(
3696 OSRF_STATUS_INTERNALSERVERERROR,
3697 "osrfMethodException",
3699 "Severe query error -- see error log for more details"
3707 static jsonObject* doFieldmapperSearch ( osrfMethodContext* ctx, osrfHash* meta,
3708 const jsonObject* params, int* err ) {
3711 dbhandle = writehandle;
3713 osrfHash* links = osrfHashGet(meta, "links");
3714 osrfHash* fields = osrfHashGet(meta, "fields");
3715 char* core_class = osrfHashGet(meta, "classname");
3716 char* pkey = osrfHashGet(meta, "primarykey");
3718 const jsonObject* _tmp;
3720 jsonObject* search_hash = jsonObjectGetIndex(params, 0);
3721 jsonObject* order_hash = jsonObjectGetIndex(params, 1);
3723 char* sql = buildSELECT( search_hash, order_hash, meta, ctx );
3725 osrfLogDebug(OSRF_LOG_MARK, "Problem building query, returning NULL");
3730 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
3732 dbi_result result = dbi_conn_query(dbhandle, sql);
3733 if( NULL == result ) {
3734 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]",
3735 MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
3736 osrfAppSessionStatus(
3738 OSRF_STATUS_INTERNALSERVERERROR,
3739 "osrfMethodException",
3741 "Severe query error -- see error log for more details"
3748 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
3751 jsonObject* res_list = jsonNewObjectType(JSON_ARRAY);
3752 osrfHash* dedup = osrfNewHash();
3754 if (dbi_result_first_row(result)) {
3755 /* JSONify the result */
3756 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
3758 obj = oilsMakeFieldmapperFromResult( result, meta );
3759 char* pkey_val = oilsFMGetString( obj, pkey );
3760 if ( osrfHashGet( dedup, pkey_val ) ) {
3761 jsonObjectFree(obj);
3764 osrfHashSet( dedup, pkey_val, pkey_val );
3765 jsonObjectPush(res_list, obj);
3767 } while (dbi_result_next_row(result));
3769 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s",
3773 osrfHashFree(dedup);
3774 /* clean up the query */
3775 dbi_result_free(result);
3778 if (res_list->size && order_hash) {
3779 _tmp = jsonObjectGetKeyConst( order_hash, "flesh" );
3781 int x = (int)jsonObjectGetNumber(_tmp);
3782 if (x == -1 || x > max_flesh_depth) x = max_flesh_depth;
3784 const jsonObject* temp_blob;
3785 if ((temp_blob = jsonObjectGetKeyConst( order_hash, "flesh_fields" )) && x > 0) {
3787 jsonObject* flesh_blob = jsonObjectClone( temp_blob );
3788 const jsonObject* flesh_fields = jsonObjectGetKeyConst( flesh_blob, core_class );
3790 osrfStringArray* link_fields = NULL;
3793 if (flesh_fields->size == 1) {
3794 char* _t = jsonObjectToSimpleString( jsonObjectGetIndex( flesh_fields, 0 ) );
3795 if (!strcmp(_t,"*")) link_fields = osrfHashKeys( links );
3801 link_fields = osrfNewStringArray(1);
3802 jsonIterator* _i = jsonNewIterator( flesh_fields );
3803 while ((_f = jsonIteratorNext( _i ))) {
3804 osrfStringArrayAdd( link_fields, jsonObjectToSimpleString( _f ) );
3806 jsonIteratorFree(_i);
3811 jsonIterator* itr = jsonNewIterator( res_list );
3812 while ((cur = jsonIteratorNext( itr ))) {
3817 while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
3819 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
3821 osrfHash* kid_link = osrfHashGet(links, link_field);
3822 if (!kid_link) continue;
3824 osrfHash* field = osrfHashGet(fields, link_field);
3825 if (!field) continue;
3827 osrfHash* value_field = field;
3829 osrfHash* kid_idl = osrfHashGet(oilsIDL(), osrfHashGet(kid_link, "class"));
3830 if (!kid_idl) continue;
3832 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
3833 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
3836 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
3837 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
3840 osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
3842 if (link_map->size > 0) {
3843 jsonObject* _kid_key = jsonNewObjectType(JSON_ARRAY);
3846 jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
3851 osrfHashGet(kid_link, "class"),
3858 "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
3859 osrfHashGet(kid_link, "field"),
3860 osrfHashGet(kid_link, "class"),
3861 osrfHashGet(kid_link, "key"),
3862 osrfHashGet(kid_link, "reltype")
3865 jsonObject* fake_params = jsonNewObjectType(JSON_ARRAY);
3866 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH)); // search hash
3867 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH)); // order/flesh hash
3869 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
3872 jsonObjectToSimpleString(
3875 atoi( osrfHashGet(value_field, "array_position") )
3880 osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
3885 jsonObjectGetIndex(fake_params, 0),
3886 osrfHashGet(kid_link, "key"),
3887 jsonNewObject( search_key )
3894 jsonObjectGetIndex(fake_params, 1),
3896 jsonNewNumberObject( (double)(x - 1 + link_map->size) )
3900 jsonObjectSetKey( jsonObjectGetIndex(fake_params, 1), "flesh_fields", jsonObjectClone(flesh_blob) );
3902 if (jsonObjectGetKeyConst(order_hash, "order_by")) {
3904 jsonObjectGetIndex(fake_params, 1),
3906 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "order_by"))
3910 if (jsonObjectGetKeyConst(order_hash, "select")) {
3912 jsonObjectGetIndex(fake_params, 1),
3914 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "select"))
3918 jsonObject* kids = doFieldmapperSearch(ctx, kid_idl, fake_params, err);
3921 jsonObjectFree( fake_params );
3922 osrfStringArrayFree(link_fields);
3923 jsonIteratorFree(itr);
3924 jsonObjectFree(res_list);
3925 jsonObjectFree(flesh_blob);
3929 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
3931 jsonObject* X = NULL;
3932 if ( link_map->size > 0 && kids->size > 0 ) {
3934 kids = jsonNewObjectType(JSON_ARRAY);
3936 jsonObject* _k_node;
3937 jsonIterator* _k = jsonNewIterator( X );
3938 while ((_k_node = jsonIteratorNext( _k ))) {
3944 (unsigned long)atoi(
3950 osrfHashGet(kid_link, "class")
3954 osrfStringArrayGetString( link_map, 0 )
3963 jsonIteratorFree(_k);
3966 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" )) || !(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) {
3967 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
3970 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
3971 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
3975 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
3976 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
3979 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
3980 jsonObjectClone( kids )
3985 jsonObjectFree(kids);
3989 jsonObjectFree( kids );
3990 jsonObjectFree( fake_params );
3992 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
3993 osrfLogDebug(OSRF_LOG_MARK, "%s", jsonObjectToJSON(cur));
3997 jsonObjectFree( flesh_blob );
3998 osrfStringArrayFree(link_fields);
3999 jsonIteratorFree(itr);
4008 static jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
4010 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4012 jsonObject* target = jsonObjectGetIndex( ctx->params, 1 );
4014 jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
4017 if (!verifyObjectClass(ctx, target)) {
4022 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4023 osrfAppSessionStatus(
4025 OSRF_STATUS_BADREQUEST,
4026 "osrfMethodException",
4028 "No active transaction -- required for UPDATE"
4034 // The following test is harmless but redundant. If a class is
4035 // readonly, we don't register an update method for it.
4036 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4037 osrfAppSessionStatus(
4039 OSRF_STATUS_BADREQUEST,
4040 "osrfMethodException",
4042 "Cannot UPDATE readonly class"
4048 dbhandle = writehandle;
4050 char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
4052 // Set the last_xact_id
4053 int index = oilsIDL_ntop( target->classname, "last_xact_id" );
4055 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
4056 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
4059 char* pkey = osrfHashGet(meta, "primarykey");
4060 osrfHash* fields = osrfHashGet(meta, "fields");
4062 char* id = oilsFMGetString( target, pkey );
4066 "%s updating %s object with %s = %s",
4068 osrfHashGet(meta, "fieldmapper"),
4073 growing_buffer* sql = buffer_init(128);
4074 buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
4079 osrfStringArray* field_list = osrfHashKeys( fields );
4080 while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
4082 osrfHash* field = osrfHashGet( fields, field_name );
4084 if(!( strcmp( field_name, pkey ) )) continue;
4085 if( str_is_true( osrfHashGet(osrfHashGet(fields,field_name), "virtual") ) )
4088 const jsonObject* field_object = oilsFMGetObject( target, field_name );
4090 int value_is_numeric = 0; // boolean
4092 if (field_object && field_object->classname) {
4093 value = oilsFMGetString(
4095 (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
4098 value = jsonObjectToSimpleString( field_object );
4099 if( field_object && JSON_NUMBER == field_object->type )
4100 value_is_numeric = 1;
4103 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
4105 if (!field_object || field_object->type == JSON_NULL) {
4106 if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
4107 if (first) first = 0;
4108 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4109 buffer_fadd( sql, " %s = NULL", field_name );
4112 } else if ( value_is_numeric || !strcmp( get_primitive( field ), "number") ) {
4113 if (first) first = 0;
4114 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4116 const char* numtype = get_datatype( field );
4117 if ( !strncmp( numtype, "INT", 3 ) ) {
4118 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
4119 } else if ( !strcmp( numtype, "NUMERIC" ) ) {
4120 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
4123 osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, numtype );
4126 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4127 if (first) first = 0;
4128 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4129 buffer_fadd( sql, " %s = %s", field_name, value );
4132 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4133 osrfAppSessionStatus(
4135 OSRF_STATUS_INTERNALSERVERERROR,
4136 "osrfMethodException",
4138 "Error quoting string -- please see the error log for more details"
4152 jsonObject* obj = jsonNewObject(id);
4154 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4155 dbi_conn_quote_string(dbhandle, &id);
4157 buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
4159 char* query = buffer_release(sql);
4160 osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
4162 dbi_result result = dbi_conn_query(dbhandle, query);
4166 jsonObjectFree(obj);
4167 obj = jsonNewObject(NULL);
4170 "%s ERROR updating %s object with %s = %s",
4172 osrfHashGet(meta, "fieldmapper"),
4183 static jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
4185 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4187 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4188 osrfAppSessionStatus(
4190 OSRF_STATUS_BADREQUEST,
4191 "osrfMethodException",
4193 "No active transaction -- required for DELETE"
4199 // The following test is harmless but redundant. If a class is
4200 // readonly, we don't register a delete method for it.
4201 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4202 osrfAppSessionStatus(
4204 OSRF_STATUS_BADREQUEST,
4205 "osrfMethodException",
4207 "Cannot DELETE readonly class"
4213 dbhandle = writehandle;
4217 char* pkey = osrfHashGet(meta, "primarykey");
4225 if (jsonObjectGetIndex(ctx->params, _obj_pos)->classname) {
4226 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, _obj_pos ))) {
4231 id = oilsFMGetString( jsonObjectGetIndex(ctx->params, _obj_pos), pkey );
4234 if (!verifyObjectPCRUD( ctx, NULL )) {
4239 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, _obj_pos));
4244 "%s deleting %s object with %s = %s",
4246 osrfHashGet(meta, "fieldmapper"),
4251 obj = jsonNewObject(id);
4253 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4254 dbi_conn_quote_string(writehandle, &id);
4256 dbi_result result = dbi_conn_queryf(writehandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
4259 jsonObjectFree(obj);
4260 obj = jsonNewObject(NULL);
4263 "%s ERROR deleting %s object with %s = %s",
4265 osrfHashGet(meta, "fieldmapper"),
4278 static jsonObject* oilsMakeFieldmapperFromResult( dbi_result result, osrfHash* meta) {
4279 if(!(result && meta)) return jsonNULL;
4281 jsonObject* object = jsonNewObject(NULL);
4282 jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
4284 osrfHash* fields = osrfHashGet(meta, "fields");
4286 osrfLogInternal(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
4290 char dt_string[256];
4294 int columnIndex = 1;
4296 unsigned short type;
4297 const char* columnName;
4299 /* cycle through the column list */
4300 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4302 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4304 fmIndex = -1; // reset the position
4306 /* determine the field type and storage attributes */
4307 type = dbi_result_get_field_type(result, columnName);
4308 attr = dbi_result_get_field_attribs(result, columnName);
4310 /* fetch the fieldmapper index */
4311 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
4313 if ( str_is_true( osrfHashGet(_f, "virtual") ) )
4316 const char* pos = (char*)osrfHashGet(_f, "array_position");
4317 if ( !pos ) continue;
4319 fmIndex = atoi( pos );
4320 osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
4325 if (dbi_result_field_is_null(result, columnName)) {
4326 jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
4331 case DBI_TYPE_INTEGER :
4333 if( attr & DBI_INTEGER_SIZE8 )
4334 jsonObjectSetIndex( object, fmIndex,
4335 jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
4337 jsonObjectSetIndex( object, fmIndex,
4338 jsonNewNumberObject(dbi_result_get_int(result, columnName)));
4342 case DBI_TYPE_DECIMAL :
4343 jsonObjectSetIndex( object, fmIndex,
4344 jsonNewNumberObject(dbi_result_get_double(result, columnName)));
4347 case DBI_TYPE_STRING :
4353 jsonNewObject( dbi_result_get_string(result, columnName) )
4358 case DBI_TYPE_DATETIME :
4360 memset(dt_string, '\0', sizeof(dt_string));
4361 memset(&gmdt, '\0', sizeof(gmdt));
4363 _tmp_dt = dbi_result_get_datetime(result, columnName);
4366 if (!(attr & DBI_DATETIME_DATE)) {
4367 gmtime_r( &_tmp_dt, &gmdt );
4368 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4369 } else if (!(attr & DBI_DATETIME_TIME)) {
4370 localtime_r( &_tmp_dt, &gmdt );
4371 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4373 localtime_r( &_tmp_dt, &gmdt );
4374 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4377 jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
4381 case DBI_TYPE_BINARY :
4382 osrfLogError( OSRF_LOG_MARK,
4383 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4391 static jsonObject* oilsMakeJSONFromResult( dbi_result result ) {
4392 if(!result) return jsonNULL;
4394 jsonObject* object = jsonNewObject(NULL);
4397 char dt_string[256];
4401 int columnIndex = 1;
4403 unsigned short type;
4404 const char* columnName;
4406 /* cycle through the column list */
4407 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4409 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4411 fmIndex = -1; // reset the position
4413 /* determine the field type and storage attributes */
4414 type = dbi_result_get_field_type(result, columnName);
4415 attr = dbi_result_get_field_attribs(result, columnName);
4417 if (dbi_result_field_is_null(result, columnName)) {
4418 jsonObjectSetKey( object, columnName, jsonNewObject(NULL) );
4423 case DBI_TYPE_INTEGER :
4425 if( attr & DBI_INTEGER_SIZE8 )
4426 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_longlong(result, columnName)) );
4428 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_int(result, columnName)) );
4431 case DBI_TYPE_DECIMAL :
4432 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_double(result, columnName)) );
4435 case DBI_TYPE_STRING :
4436 jsonObjectSetKey( object, columnName, jsonNewObject(dbi_result_get_string(result, columnName)) );
4439 case DBI_TYPE_DATETIME :
4441 memset(dt_string, '\0', sizeof(dt_string));
4442 memset(&gmdt, '\0', sizeof(gmdt));
4444 _tmp_dt = dbi_result_get_datetime(result, columnName);
4447 if (!(attr & DBI_DATETIME_DATE)) {
4448 gmtime_r( &_tmp_dt, &gmdt );
4449 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4450 } else if (!(attr & DBI_DATETIME_TIME)) {
4451 localtime_r( &_tmp_dt, &gmdt );
4452 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4454 localtime_r( &_tmp_dt, &gmdt );
4455 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4458 jsonObjectSetKey( object, columnName, jsonNewObject(dt_string) );
4461 case DBI_TYPE_BINARY :
4462 osrfLogError( OSRF_LOG_MARK,
4463 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4471 // Interpret a string as true or false
4472 static int str_is_true( const char* str ) {
4473 if( NULL == str || strcasecmp( str, "true" ) )
4479 // Interpret a jsonObject as true or false
4480 static int obj_is_true( const jsonObject* obj ) {
4483 else switch( obj->type )
4491 if( strcasecmp( obj->value.s, "true" ) )
4495 case JSON_NUMBER : // Support 1/0 for perl's sake
4496 if( jsonObjectGetNumber( obj ) == 1.0 )
4505 // Translate a numeric code into a text string identifying a type of
4506 // jsonObject. To be used for building error messages.
4507 static const char* json_type( int code ) {
4513 return "JSON_ARRAY";
4515 return "JSON_STRING";
4517 return "JSON_NUMBER";
4523 return "(unrecognized)";
4527 // Extract the "primitive" attribute from an IDL field definition.
4528 // If we haven't initialized the app, then we must be running in
4529 // some kind of testbed. In that case, default to "string".
4530 static const char* get_primitive( osrfHash* field ) {
4531 const char* s = osrfHashGet( field, "primitive" );
4533 if( child_initialized )
4536 "%s ERROR No \"datatype\" attribute for field \"%s\"",
4538 osrfHashGet( field, "name" )
4546 // Extract the "datatype" attribute from an IDL field definition.
4547 // If we haven't initialized the app, then we must be running in
4548 // some kind of testbed. In that case, default to to NUMERIC,
4549 // since we look at the datatype only for numbers.
4550 static const char* get_datatype( osrfHash* field ) {
4551 const char* s = osrfHashGet( field, "datatype" );
4553 if( child_initialized )
4556 "%s ERROR No \"datatype\" attribute for field \"%s\"",
4558 osrfHashGet( field, "name" )
4567 If the input string is potentially a valid SQL identifier, return 1.
4570 Purpose: to prevent certain kinds of SQL injection. To that end we
4571 don't necessarily need to follow all the rules exactly, such as requiring
4572 that the first character not be a digit.
4574 We allow leading and trailing white space. In between, we do not allow
4575 punctuation (except for underscores and dollar signs), control
4576 characters, or embedded white space.
4578 More pedantically we should allow quoted identifiers containing arbitrary
4579 characters, but for the foreseeable future such quoted identifiers are not
4580 likely to be an issue.
4582 static int is_identifier( const char* s) {
4586 // Skip leading white space
4587 while( isspace( (unsigned char) *s ) )
4591 return 0; // Nothing but white space? Not okay.
4593 // Check each character until we reach white space or
4594 // end-of-string. Letters, digits, underscores, and
4595 // dollar signs are okay. Control characters and other
4596 // punctuation characters are not okay. Anything else
4597 // is okay -- it could for example be part of a multibyte
4598 // UTF8 character such as a letter with diacritical marks,
4599 // and those are allowed.
4601 if( isalnum( (unsigned char) *s )
4604 ; // Fine; keep going
4605 else if( ispunct( (unsigned char) *s )
4606 || iscntrl( (unsigned char) *s ) )
4609 } while( *s && ! isspace( (unsigned char) *s ) );
4611 // If we found any white space in the above loop,
4612 // the rest had better be all white space.
4614 while( isspace( (unsigned char) *s ) )
4618 return 0; // White space was embedded within non-white space