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 );
1863 osrfLogError(OSRF_LOG_MARK, "%s: Error building condition for field transform", MODULENAME, value);
1864 free(field_transform);
1867 } else if ( value_obj->type == JSON_ARRAY ) {
1868 value = searchValueTransform( value_obj );
1869 } else if ( value_obj->type == JSON_HASH ) {
1870 value = searchWHERE( value_obj, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN, NULL );
1872 osrfLogError(OSRF_LOG_MARK, "%s: Error building predicate for field transform", MODULENAME, value);
1873 free(field_transform);
1876 } else if ( value_obj->type == JSON_NUMBER ) {
1877 value = jsonNumberToDBString( field, value_obj );
1878 } else if ( value_obj->type != JSON_NULL ) {
1879 if ( !strcmp( get_primitive( field ), "number") ) {
1880 value = jsonNumberToDBString( field, value_obj );
1882 value = jsonObjectToSimpleString( value_obj );
1883 if ( !dbi_conn_quote_string(dbhandle, &value) ) {
1884 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, value);
1886 free(field_transform);
1892 growing_buffer* sql_buf = buffer_init(32);
1903 free(field_transform);
1905 return buffer_release(sql_buf);
1908 static char* searchSimplePredicate (const char* op, const char* class,
1909 osrfHash* field, const jsonObject* node) {
1913 // Get the value to which we are comparing the specified column
1914 if (node->type != JSON_NULL) {
1915 if ( node->type == JSON_NUMBER ) {
1916 val = jsonNumberToDBString( field, node );
1917 } else if ( !strcmp( get_primitive( field ), "number" ) ) {
1918 val = jsonNumberToDBString( field, node );
1920 val = jsonObjectToSimpleString(node);
1925 if( JSON_NUMBER != node->type && strcmp( get_primitive( field ), "number") ) {
1926 // Value is not numeric; enclose it in quotes
1927 if ( !dbi_conn_quote_string( dbhandle, &val ) ) {
1928 osrfLogError( OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val );
1934 // Compare to a null value
1935 val = strdup( "NULL" );
1936 if (strcmp( op, "=" ))
1942 growing_buffer* sql_buf = buffer_init(32);
1943 buffer_fadd( sql_buf, "\"%s\".%s %s %s", class, osrfHashGet(field, "name"), op, val );
1944 char* pred = buffer_release( sql_buf );
1951 static char* searchBETWEENPredicate (const char* class, osrfHash* field, jsonObject* node) {
1956 if ( !strcmp( get_primitive( field ), "number") ) {
1957 x_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,0));
1958 y_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,1));
1961 x_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,0));
1962 y_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,1));
1963 if ( !(dbi_conn_quote_string(dbhandle, &x_string) && dbi_conn_quote_string(dbhandle, &y_string)) ) {
1964 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key strings [%s] and [%s]", MODULENAME, x_string, y_string);
1971 growing_buffer* sql_buf = buffer_init(32);
1972 buffer_fadd( sql_buf, "%s BETWEEN %s AND %s", osrfHashGet(field, "name"), x_string, y_string );
1976 return buffer_release(sql_buf);
1979 static char* searchPredicate ( const char* class, osrfHash* field,
1980 jsonObject* node, osrfMethodContext* ctx ) {
1983 if (node->type == JSON_ARRAY) { // equality IN search
1984 pred = searchINPredicate( class, field, node, NULL, ctx );
1985 } else if (node->type == JSON_HASH) { // non-equality search
1986 jsonObject* pred_node;
1987 jsonIterator* pred_itr = jsonNewIterator( node );
1988 while ( (pred_node = jsonIteratorNext( pred_itr )) ) {
1989 if ( !(strcasecmp( pred_itr->key,"between" )) )
1990 pred = searchBETWEENPredicate( class, field, pred_node );
1991 else if ( !(strcasecmp( pred_itr->key,"in" )) || !(strcasecmp( pred_itr->key,"not in" )) )
1992 pred = searchINPredicate( class, field, pred_node, pred_itr->key, ctx );
1993 else if ( pred_node->type == JSON_ARRAY )
1994 pred = searchFunctionPredicate( class, field, pred_node, pred_itr->key );
1995 else if ( pred_node->type == JSON_HASH )
1996 pred = searchFieldTransformPredicate( class, field, pred_node, pred_itr->key );
1998 pred = searchSimplePredicate( pred_itr->key, class, field, pred_node );
2002 jsonIteratorFree(pred_itr);
2003 } else if (node->type == JSON_NULL) { // IS NULL search
2004 growing_buffer* _p = buffer_init(64);
2007 "\"%s\".%s IS NULL",
2009 osrfHashGet(field, "name")
2011 pred = buffer_release(_p);
2012 } else { // equality search
2013 pred = searchSimplePredicate( "=", class, field, node );
2032 field : call_number,
2048 static char* searchJOIN ( const jsonObject* join_hash, osrfHash* leftmeta ) {
2050 const jsonObject* working_hash;
2051 jsonObject* freeable_hash = NULL;
2053 if (join_hash->type == JSON_STRING) {
2054 // create a wrapper around a copy of the original
2055 char* _tmp = jsonObjectToSimpleString( join_hash );
2056 freeable_hash = jsonNewObjectType(JSON_HASH);
2057 jsonObjectSetKey(freeable_hash, _tmp, NULL);
2059 working_hash = freeable_hash;
2062 if( join_hash->type != JSON_HASH ) {
2065 "%s: JOIN failed; expected JSON object type not found",
2070 working_hash = join_hash;
2073 growing_buffer* join_buf = buffer_init(128);
2074 const char* leftclass = osrfHashGet(leftmeta, "classname");
2076 jsonObject* snode = NULL;
2077 jsonIterator* search_itr = jsonNewIterator( working_hash );
2079 while ( (snode = jsonIteratorNext( search_itr )) ) {
2080 const char* class = search_itr->key;
2081 osrfHash* idlClass = osrfHashGet( oilsIDL(), class );
2085 "%s: JOIN failed. No class \"%s\" defined in IDL",
2089 jsonIteratorFree( search_itr );
2090 buffer_free( join_buf );
2092 jsonObjectFree( freeable_hash );
2096 char* fkey = jsonObjectToSimpleString( jsonObjectGetKeyConst( snode, "fkey" ) );
2097 char* field = jsonObjectToSimpleString( jsonObjectGetKeyConst( snode, "field" ) );
2099 if (field && !fkey) {
2100 fkey = (char*)oilsIDLFindPath("/%s/links/%s/key", class, field);
2104 "%s: JOIN failed. No link defined from %s.%s to %s",
2110 buffer_free(join_buf);
2112 jsonObjectFree(freeable_hash);
2114 jsonIteratorFree(search_itr);
2117 fkey = strdup( fkey );
2119 } else if (!field && fkey) {
2120 field = (char*)oilsIDLFindPath("/%s/links/%s/key", leftclass, fkey );
2124 "%s: JOIN failed. No link defined from %s.%s to %s",
2130 buffer_free(join_buf);
2132 jsonObjectFree(freeable_hash);
2134 jsonIteratorFree(search_itr);
2137 field = strdup( field );
2139 } else if (!field && !fkey) {
2140 osrfHash* _links = oilsIDL_links( leftclass );
2142 // For each link defined for the left class:
2143 // see if the link references the joined class
2144 osrfHashIterator* itr = osrfNewHashIterator( _links );
2145 osrfHash* curr_link = NULL;
2146 while( (curr_link = osrfHashIteratorNext( itr ) ) ) {
2147 const char* other_class = osrfHashGet( curr_link, "class" );
2148 if( other_class && !strcmp( other_class, class ) ) {
2150 // Found a link between the classes
2151 fkey = strdup( osrfHashIteratorKey( itr ) );
2152 const char* other_key = osrfHashGet( curr_link, "key" );
2153 field = other_key ? strdup( other_key ) : NULL;
2157 osrfHashIteratorFree( itr );
2159 if (!field || !fkey) {
2160 // Do another such search, with the classes reversed
2161 _links = oilsIDL_links( class );
2163 // For each link defined for the joined class:
2164 // see if the link references the left class
2165 osrfHashIterator* itr = osrfNewHashIterator( _links );
2166 osrfHash* curr_link = NULL;
2167 while( (curr_link = osrfHashIteratorNext( itr ) ) ) {
2168 const char* other_class = osrfHashGet( curr_link, "class" );
2169 if( other_class && !strcmp( other_class, leftclass ) ) {
2171 // Found a link between the classes
2172 fkey = strdup( osrfHashIteratorKey( itr ) );
2173 const char* other_key = osrfHashGet( curr_link, "key" );
2174 field = other_key ? strdup( other_key ) : NULL;
2178 osrfHashIteratorFree( itr );
2181 if (!field || !fkey) {
2184 "%s: JOIN failed. No link defined between %s and %s",
2191 buffer_free(join_buf);
2193 jsonObjectFree(freeable_hash);
2194 jsonIteratorFree(search_itr);
2200 char* type = jsonObjectToSimpleString( jsonObjectGetKeyConst( snode, "type" ) );
2202 if ( !strcasecmp(type,"left") ) {
2203 buffer_add(join_buf, " LEFT JOIN");
2204 } else if ( !strcasecmp(type,"right") ) {
2205 buffer_add(join_buf, " RIGHT JOIN");
2206 } else if ( !strcasecmp(type,"full") ) {
2207 buffer_add(join_buf, " FULL JOIN");
2209 buffer_add(join_buf, " INNER JOIN");
2212 buffer_add(join_buf, " INNER JOIN");
2216 char* table = getSourceDefinition(idlClass);
2220 jsonIteratorFree( search_itr );
2221 buffer_free( join_buf );
2223 jsonObjectFree( freeable_hash );
2227 buffer_fadd(join_buf, " %s AS \"%s\" ON ( \"%s\".%s = \"%s\".%s",
2228 table, class, class, field, leftclass, fkey);
2231 const jsonObject* filter = jsonObjectGetKeyConst( snode, "filter" );
2233 const char* filter_op = jsonObjectGetString( jsonObjectGetKeyConst( snode, "filter_op" ) );
2235 if (!strcasecmp("or",filter_op)) {
2236 buffer_add( join_buf, " OR " );
2238 buffer_add( join_buf, " AND " );
2241 buffer_add( join_buf, " AND " );
2244 char* jpred = searchWHERE( filter, idlClass, AND_OP_JOIN, NULL );
2246 OSRF_BUFFER_ADD_CHAR( join_buf, ' ' );
2247 OSRF_BUFFER_ADD( join_buf, jpred );
2252 "%s: JOIN failed. Invalid conditional expression.",
2257 jsonIteratorFree( search_itr );
2258 buffer_free( join_buf );
2260 jsonObjectFree( freeable_hash );
2265 buffer_add(join_buf, " ) ");
2267 const jsonObject* join_filter = jsonObjectGetKeyConst( snode, "join" );
2269 char* jpred = searchJOIN( join_filter, idlClass );
2270 OSRF_BUFFER_ADD_CHAR( join_buf, ' ' );
2271 OSRF_BUFFER_ADD( join_buf, jpred );
2280 jsonObjectFree(freeable_hash);
2281 jsonIteratorFree(search_itr);
2283 return buffer_release(join_buf);
2288 { +class : { -or|-and : { field : { op : value }, ... } ... }, ... }
2289 { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }
2290 [ { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }, ... ]
2292 Generate code to express a set of conditions, as for a WHERE clause. Parameters:
2294 search_hash is the JSON expression of the conditions.
2295 meta is the class definition from the IDL, for the relevant table.
2296 opjoin_type indicates whether multiple conditions, if present, should be
2297 connected by AND or OR.
2298 osrfMethodContext is loaded with all sorts of stuff, but all we do with it here is
2299 to pass it to other functions -- and all they do with it is to use the session
2300 and request members to send error messages back to the client.
2304 static char* searchWHERE ( const jsonObject* search_hash, osrfHash* meta, int opjoin_type, osrfMethodContext* ctx ) {
2308 "%s: Entering searchWHERE; search_hash addr = %p, meta addr = %p, opjoin_type = %d, ctx addr = %p",
2316 growing_buffer* sql_buf = buffer_init(128);
2318 jsonObject* node = NULL;
2321 if ( search_hash->type == JSON_ARRAY ) {
2322 osrfLogDebug(OSRF_LOG_MARK, "%s: In WHERE clause, condition type is JSON_ARRAY", MODULENAME);
2323 jsonIterator* search_itr = jsonNewIterator( search_hash );
2324 if( !jsonIteratorHasNext( search_itr ) ) {
2327 "%s: Invalid predicate structure: empty JSON array",
2330 jsonIteratorFree( search_itr );
2331 buffer_free( sql_buf );
2335 while ( (node = jsonIteratorNext( search_itr )) ) {
2339 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
2340 else buffer_add(sql_buf, " AND ");
2343 char* subpred = searchWHERE( node, meta, opjoin_type, ctx );
2345 buffer_fadd(sql_buf, "( %s )", subpred);
2348 jsonIteratorFree( search_itr );
2349 buffer_free( sql_buf );
2353 jsonIteratorFree(search_itr);
2355 } else if ( search_hash->type == JSON_HASH ) {
2356 osrfLogDebug(OSRF_LOG_MARK, "%s: In WHERE clause, condition type is JSON_HASH", MODULENAME);
2357 jsonIterator* search_itr = jsonNewIterator( search_hash );
2358 if( !jsonIteratorHasNext( search_itr ) ) {
2361 "%s: Invalid predicate structure: empty JSON object",
2364 jsonIteratorFree( search_itr );
2365 buffer_free( sql_buf );
2369 while ( (node = jsonIteratorNext( search_itr )) ) {
2374 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
2375 else buffer_add(sql_buf, " AND ");
2378 if ( '+' == search_itr->key[ 0 ] ) {
2379 if ( node->type == JSON_STRING ) {
2380 // Intended purpose; to allow reference to a Boolean column.
2381 // Verify that the string looks like an identifier.
2382 const char* subpred = jsonObjectGetString( node );
2383 if( ! is_identifier( subpred ) ) {
2386 "%s: Invalid boolean identifier in WHERE clause: \"%s\"",
2390 jsonIteratorFree( search_itr );
2391 buffer_free( sql_buf );
2394 buffer_fadd(sql_buf, " \"%s\".%s ", search_itr->key + 1, subpred);
2396 char* subpred = searchWHERE( node, osrfHashGet( oilsIDL(), search_itr->key + 1 ), AND_OP_JOIN, ctx );
2398 buffer_fadd(sql_buf, "( %s )", subpred);
2401 jsonIteratorFree( search_itr );
2402 buffer_free( sql_buf );
2406 } else if ( !strcasecmp("-or",search_itr->key) ) {
2407 char* subpred = searchWHERE( node, meta, OR_OP_JOIN, ctx );
2409 buffer_fadd(sql_buf, "( %s )", subpred);
2412 buffer_free( sql_buf );
2416 } else if ( !strcasecmp("-and",search_itr->key) ) {
2417 char* subpred = searchWHERE( node, meta, AND_OP_JOIN, ctx );
2419 buffer_fadd(sql_buf, "( %s )", subpred);
2422 buffer_free( sql_buf );
2425 } else if ( !strcasecmp("-exists",search_itr->key) ) {
2426 char* subpred = SELECT(
2428 jsonObjectGetKey( node, "select" ),
2429 jsonObjectGetKey( node, "from" ),
2430 jsonObjectGetKey( node, "where" ),
2431 jsonObjectGetKey( node, "having" ),
2432 jsonObjectGetKey( node, "order_by" ),
2433 jsonObjectGetKey( node, "limit" ),
2434 jsonObjectGetKey( node, "offset" ),
2438 buffer_fadd(sql_buf, "EXISTS ( %s )", subpred);
2440 } else if ( !strcasecmp("-not-exists",search_itr->key) ) {
2441 char* subpred = SELECT(
2443 jsonObjectGetKey( node, "select" ),
2444 jsonObjectGetKey( node, "from" ),
2445 jsonObjectGetKey( node, "where" ),
2446 jsonObjectGetKey( node, "having" ),
2447 jsonObjectGetKey( node, "order_by" ),
2448 jsonObjectGetKey( node, "limit" ),
2449 jsonObjectGetKey( node, "offset" ),
2453 buffer_fadd(sql_buf, "NOT EXISTS ( %s )", subpred);
2457 char* class = osrfHashGet(meta, "classname");
2458 osrfHash* fields = osrfHashGet(meta, "fields");
2459 osrfHash* field = osrfHashGet( fields, search_itr->key );
2463 char* table = getSourceDefinition(meta);
2465 table = strdup( "(?)" );
2468 "%s: Attempt to reference non-existent column %s on %s (%s)",
2474 buffer_free(sql_buf);
2476 jsonIteratorFree(search_itr);
2480 char* subpred = searchPredicate( class, field, node, ctx );
2481 buffer_add( sql_buf, subpred );
2485 jsonIteratorFree(search_itr);
2488 // ERROR ... only hash and array allowed at this level
2489 char* predicate_string = jsonObjectToJSON( search_hash );
2492 "%s: Invalid predicate structure: %s",
2496 buffer_free(sql_buf);
2497 free(predicate_string);
2502 return buffer_release(sql_buf);
2506 /* method context */ osrfMethodContext* ctx,
2508 /* SELECT */ jsonObject* selhash,
2509 /* FROM */ jsonObject* join_hash,
2510 /* WHERE */ jsonObject* search_hash,
2511 /* HAVING */ jsonObject* having_hash,
2512 /* ORDER BY */ jsonObject* order_hash,
2513 /* LIMIT */ jsonObject* limit,
2514 /* OFFSET */ jsonObject* offset,
2515 /* flags */ int flags
2517 const char* locale = osrf_message_get_last_locale();
2519 // in case we don't get a select list
2520 jsonObject* defaultselhash = NULL;
2522 // general tmp objects
2523 const jsonObject* tmp_const;
2524 jsonObject* selclass = NULL;
2525 jsonObject* selfield = NULL;
2526 jsonObject* snode = NULL;
2527 jsonObject* onode = NULL;
2529 char* string = NULL;
2530 int from_function = 0;
2535 // the core search class
2536 char* core_class = NULL;
2538 // metadata about the core search class
2539 osrfHash* core_meta = NULL;
2541 // punt if there's no core class
2542 if (!join_hash || ( join_hash->type == JSON_HASH && !join_hash->size )) {
2545 "%s: FROM clause is missing or empty",
2549 osrfAppSessionStatus(
2551 OSRF_STATUS_INTERNALSERVERERROR,
2552 "osrfMethodException",
2554 "FROM clause is missing or empty in JSON query"
2559 // get the core class -- the only key of the top level FROM clause, or a string
2560 if (join_hash->type == JSON_HASH) {
2561 jsonIterator* tmp_itr = jsonNewIterator( join_hash );
2562 snode = jsonIteratorNext( tmp_itr );
2564 core_class = strdup( tmp_itr->key );
2567 jsonObject* extra = jsonIteratorNext( tmp_itr );
2569 jsonIteratorFree( tmp_itr );
2572 // There shouldn't be more than one entry in join_hash
2576 "%s: Malformed FROM clause: extra entry in JSON_HASH",
2580 osrfAppSessionStatus(
2582 OSRF_STATUS_INTERNALSERVERERROR,
2583 "osrfMethodException",
2585 "Malformed FROM clause in JSON query"
2588 return NULL; // Malformed join_hash; extra entry
2590 } else if (join_hash->type == JSON_ARRAY) {
2592 core_class = jsonObjectToSimpleString( jsonObjectGetIndex(join_hash, 0) );
2595 } else if (join_hash->type == JSON_STRING) {
2596 core_class = jsonObjectToSimpleString( join_hash );
2602 "%s: FROM clause is unexpected JSON type: %s",
2604 json_type( join_hash->type )
2607 osrfAppSessionStatus(
2609 OSRF_STATUS_INTERNALSERVERERROR,
2610 "osrfMethodException",
2612 "Ill-formed FROM clause in JSON query"
2618 if (!from_function) {
2619 // Get the IDL class definition for the core class
2620 core_meta = osrfHashGet( oilsIDL(), core_class );
2621 if( !core_meta ) { // Didn't find it?
2624 "%s: SELECT clause references undefined class: \"%s\"",
2629 osrfAppSessionStatus(
2631 OSRF_STATUS_INTERNALSERVERERROR,
2632 "osrfMethodException",
2634 "SELECT clause references undefined class in JSON query"
2640 // Make sure the class isn't virtual
2641 if( str_is_true( osrfHashGet( core_meta, "virtual" ) ) ) {
2644 "%s: Core class is virtual: \"%s\"",
2649 osrfAppSessionStatus(
2651 OSRF_STATUS_INTERNALSERVERERROR,
2652 "osrfMethodException",
2654 "FROM clause references virtual class in JSON query"
2661 // if the select list is empty, or the core class field list is '*',
2662 // build the default select list ...
2664 selhash = defaultselhash = jsonNewObjectType(JSON_HASH);
2665 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2666 } else if( selhash->type != JSON_HASH ) {
2669 "%s: Expected JSON_HASH for SELECT clause; found %s",
2671 json_type( selhash->type )
2675 osrfAppSessionStatus(
2677 OSRF_STATUS_INTERNALSERVERERROR,
2678 "osrfMethodException",
2680 "Malformed SELECT clause in JSON query"
2684 } else if ( (tmp_const = jsonObjectGetKeyConst( selhash, core_class )) && tmp_const->type == JSON_STRING ) {
2685 char* _x = jsonObjectToSimpleString( tmp_const );
2686 if (!strncmp( "*", _x, 1 )) {
2687 jsonObjectRemoveKey( selhash, core_class );
2688 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2694 growing_buffer* sql_buf = buffer_init(128);
2696 // temp buffer for the SELECT list
2697 growing_buffer* select_buf = buffer_init(128);
2698 growing_buffer* order_buf = buffer_init(128);
2699 growing_buffer* group_buf = buffer_init(128);
2700 growing_buffer* having_buf = buffer_init(128);
2702 int aggregate_found = 0; // boolean
2704 // Build a select list
2705 if(from_function) // From a function we select everything
2706 OSRF_BUFFER_ADD_CHAR( select_buf, '*' );
2709 // If we need to build a default list, prepare to do so
2710 jsonObject* _tmp = jsonObjectGetKey( selhash, core_class );
2711 if ( _tmp && !_tmp->size ) {
2713 osrfHash* core_fields = osrfHashGet( core_meta, "fields" );
2715 osrfHashIterator* field_itr = osrfNewHashIterator( core_fields );
2716 osrfHash* field_def;
2717 while( ( field_def = osrfHashIteratorNext( field_itr ) ) ) {
2718 if( ! str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2719 // This field is not virtual, so add it to the list
2720 jsonObjectPush( _tmp, jsonNewObject( osrfHashIteratorKey( field_itr ) ) );
2723 osrfHashIteratorFree( field_itr );
2726 // Now build the actual select list
2730 jsonIterator* selclass_itr = jsonNewIterator( selhash );
2731 while ( (selclass = jsonIteratorNext( selclass_itr )) ) { // For each class
2733 // Make sure the class is defined in the IDL
2734 const char* cname = selclass_itr->key;
2735 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
2739 "%s: Selected class \"%s\" not defined in IDL",
2745 osrfAppSessionStatus(
2747 OSRF_STATUS_INTERNALSERVERERROR,
2748 "osrfMethodException",
2750 "Selected class is not defined"
2752 jsonIteratorFree( selclass_itr );
2753 buffer_free( sql_buf );
2754 buffer_free( select_buf );
2755 buffer_free( order_buf );
2756 buffer_free( group_buf );
2757 buffer_free( having_buf );
2758 if( defaultselhash ) jsonObjectFree( defaultselhash );
2763 // Make sure the target relation is in the join tree.
2765 // At this point join_hash is a step down from the join_hash we
2766 // received as a parameter. If the original was a JSON_STRING,
2767 // then json_hash is now NULL. If the original was a JSON_HASH,
2768 // then json_hash is now the first (and only) entry in it,
2769 // denoting the core class. We've already excluded the
2770 // possibility that the original was a JSON_ARRAY, because in
2771 // that case from_function would be non-NULL, and we wouldn't
2774 int class_in_from_clause; // boolean
2776 if ( ! strcmp( core_class, cname ))
2777 // This is the core class -- no problem
2778 class_in_from_clause = 1;
2781 // There's only one class in the FROM clause, and this isn't it
2782 class_in_from_clause = 0;
2783 else if (join_hash->type == JSON_STRING) {
2784 // There's only one class in the FROM clause
2785 string = jsonObjectToSimpleString(join_hash);
2786 if ( strcmp( string, cname ) )
2787 class_in_from_clause = 0; // This isn't it
2789 class_in_from_clause = 1; // This is it
2792 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", cname);
2793 if ( 0 == found->size )
2794 class_in_from_clause = 0; // Nowhere in the join tree
2796 class_in_from_clause = 1; // Found it
2797 jsonObjectFree( found );
2801 // If the class isn't in the FROM clause, bail out
2802 if( ! class_in_from_clause ) {
2805 "%s: SELECT clause references class not in FROM clause: \"%s\"",
2810 osrfAppSessionStatus(
2812 OSRF_STATUS_INTERNALSERVERERROR,
2813 "osrfMethodException",
2815 "Selected class not in FROM clause in JSON query"
2817 jsonIteratorFree( selclass_itr );
2818 buffer_free( sql_buf );
2819 buffer_free( select_buf );
2820 buffer_free( order_buf );
2821 buffer_free( group_buf );
2822 buffer_free( having_buf );
2823 if( defaultselhash ) jsonObjectFree( defaultselhash );
2828 // Look up some attributes of the current class, so that we
2829 // don't have to look them up again for each field
2830 osrfHash* class_field_set = osrfHashGet( idlClass, "fields" );
2831 const char* class_pkey = osrfHashGet( idlClass, "primarykey" );
2832 const char* class_tname = osrfHashGet( idlClass, "tablename" );
2834 // stitch together the column list ...
2835 jsonIterator* select_itr = jsonNewIterator( selclass );
2836 while ( (selfield = jsonIteratorNext( select_itr )) ) { // for each SELECT column
2838 // If we need a separator comma, add one
2842 OSRF_BUFFER_ADD_CHAR( select_buf, ',' );
2845 // ... if it's a string, just toss it on the pile
2846 if (selfield->type == JSON_STRING) {
2848 // Look up the field in the IDL
2849 const char* col_name = selfield->value.s;
2850 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
2852 // No such field in current class
2855 "%s: Selected column \"%s\" not defined in IDL for class \"%s\"",
2861 osrfAppSessionStatus(
2863 OSRF_STATUS_INTERNALSERVERERROR,
2864 "osrfMethodException",
2866 "Selected column not defined in JSON query"
2868 jsonIteratorFree( select_itr );
2869 jsonIteratorFree( selclass_itr );
2870 buffer_free( sql_buf );
2871 buffer_free( select_buf );
2872 buffer_free( order_buf );
2873 buffer_free( group_buf );
2874 buffer_free( having_buf );
2875 if( defaultselhash ) jsonObjectFree( defaultselhash );
2878 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2879 // Virtual field not allowed
2882 "%s: Selected column \"%s\" for class \"%s\" is virtual",
2888 osrfAppSessionStatus(
2890 OSRF_STATUS_INTERNALSERVERERROR,
2891 "osrfMethodException",
2893 "Selected column may not be virtual in JSON query"
2895 jsonIteratorFree( select_itr );
2896 jsonIteratorFree( selclass_itr );
2897 buffer_free( sql_buf );
2898 buffer_free( select_buf );
2899 buffer_free( order_buf );
2900 buffer_free( group_buf );
2901 buffer_free( having_buf );
2902 if( defaultselhash ) jsonObjectFree( defaultselhash );
2909 if (flags & DISABLE_I18N)
2912 i18n = osrfHashGet(field_def, "i18n");
2914 if( str_is_true( i18n ) ) {
2915 buffer_fadd( select_buf,
2916 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
2917 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, col_name );
2919 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
2922 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
2925 // ... but it could be an object, in which case we check for a Field Transform
2926 } else if (selfield->type == JSON_HASH) {
2928 char* col_name = jsonObjectToSimpleString( jsonObjectGetKeyConst( selfield, "column" ) );
2930 // Get the field definition from the IDL
2931 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
2933 // No such field in current class
2936 "%s: Selected column \"%s\" is not defined in IDL for class \"%s\"",
2942 osrfAppSessionStatus(
2944 OSRF_STATUS_INTERNALSERVERERROR,
2945 "osrfMethodException",
2947 "Selected column is not defined in JSON query"
2949 jsonIteratorFree( select_itr );
2950 jsonIteratorFree( selclass_itr );
2951 buffer_free( sql_buf );
2952 buffer_free( select_buf );
2953 buffer_free( order_buf );
2954 buffer_free( group_buf );
2955 buffer_free( having_buf );
2956 if( defaultselhash ) jsonObjectFree( defaultselhash );
2959 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2960 // No such field in current class
2963 "%s: Selected column \"%s\" is virtual for class \"%s\"",
2969 osrfAppSessionStatus(
2971 OSRF_STATUS_INTERNALSERVERERROR,
2972 "osrfMethodException",
2974 "Selected column is virtual in JSON query"
2976 jsonIteratorFree( select_itr );
2977 jsonIteratorFree( selclass_itr );
2978 buffer_free( sql_buf );
2979 buffer_free( select_buf );
2980 buffer_free( order_buf );
2981 buffer_free( group_buf );
2982 buffer_free( having_buf );
2983 if( defaultselhash ) jsonObjectFree( defaultselhash );
2988 // Decide what to use as a column alias
2990 if ((tmp_const = jsonObjectGetKeyConst( selfield, "alias" ))) {
2991 _alias = jsonObjectToSimpleString( tmp_const );
2992 } else { // Use field name as the alias
2996 if (jsonObjectGetKeyConst( selfield, "transform" )) {
2997 char* transform_str = searchFieldTransform(cname, field_def, selfield);
2998 if( transform_str ) {
2999 buffer_fadd(select_buf, " %s AS \"%s\"", transform_str, _alias);
3000 free(transform_str);
3003 osrfAppSessionStatus(
3005 OSRF_STATUS_INTERNALSERVERERROR,
3006 "osrfMethodException",
3008 "Unable to generate transform function in JSON query"
3010 jsonIteratorFree( select_itr );
3011 jsonIteratorFree( selclass_itr );
3012 buffer_free( sql_buf );
3013 buffer_free( select_buf );
3014 buffer_free( order_buf );
3015 buffer_free( group_buf );
3016 buffer_free( having_buf );
3017 if( defaultselhash ) jsonObjectFree( defaultselhash );
3025 if (flags & DISABLE_I18N)
3028 i18n = osrfHashGet(field_def, "i18n");
3030 if( str_is_true( i18n ) ) {
3031 buffer_fadd( select_buf,
3032 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
3033 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, _alias);
3035 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
3038 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
3042 if( _alias != col_name )
3049 "%s: Selected item is unexpected JSON type: %s",
3051 json_type( selfield->type )
3054 osrfAppSessionStatus(
3056 OSRF_STATUS_INTERNALSERVERERROR,
3057 "osrfMethodException",
3059 "Ill-formed SELECT item in JSON query"
3061 jsonIteratorFree( select_itr );
3062 jsonIteratorFree( selclass_itr );
3063 buffer_free( sql_buf );
3064 buffer_free( select_buf );
3065 buffer_free( order_buf );
3066 buffer_free( group_buf );
3067 buffer_free( having_buf );
3068 if( defaultselhash ) jsonObjectFree( defaultselhash );
3073 const jsonObject* agg_obj = jsonObjectGetKey( selfield, "aggregate" );
3074 if( obj_is_true( agg_obj ) )
3075 aggregate_found = 1;
3077 // Append a comma (except for the first one)
3078 // and add the column to a GROUP BY clause
3082 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3084 buffer_fadd(group_buf, " %d", sel_pos);
3088 if (is_agg->size || (flags & SELECT_DISTINCT)) {
3090 const jsonObject* aggregate_obj = jsonObjectGetKey( selfield, "aggregate" );
3091 if ( ! obj_is_true( aggregate_obj ) ) {
3095 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3098 buffer_fadd(group_buf, " %d", sel_pos);
3101 } else if (is_agg = jsonObjectGetKey( selfield, "having" )) {
3105 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3108 _column = searchFieldTransform(cname, field, selfield);
3109 OSRF_BUFFER_ADD_CHAR(group_buf, ' ');
3110 OSRF_BUFFER_ADD(group_buf, _column);
3111 _column = searchFieldTransform(cname, field, selfield);
3118 } // end while -- iterating across SELECT columns
3120 jsonIteratorFree(select_itr);
3121 } // end while -- iterating across classes
3123 jsonIteratorFree(selclass_itr);
3127 char* col_list = buffer_release(select_buf);
3129 if (from_function) table = searchValueTransform(join_hash);
3130 else table = getSourceDefinition(core_meta);
3134 osrfAppSessionStatus(
3136 OSRF_STATUS_INTERNALSERVERERROR,
3137 "osrfMethodException",
3139 "Unable to identify table for core class"
3142 buffer_free( sql_buf );
3143 buffer_free( order_buf );
3144 buffer_free( group_buf );
3145 buffer_free( having_buf );
3146 if( defaultselhash ) jsonObjectFree( defaultselhash );
3151 // Put it all together
3152 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\" ", col_list, table, core_class );
3156 if (!from_function) {
3157 // Now, walk the join tree and add that clause
3159 char* join_clause = searchJOIN( join_hash, core_meta );
3161 buffer_add(sql_buf, join_clause);
3165 osrfAppSessionStatus(
3167 OSRF_STATUS_INTERNALSERVERERROR,
3168 "osrfMethodException",
3170 "Unable to construct JOIN clause(s)"
3172 buffer_free( sql_buf );
3173 buffer_free( order_buf );
3174 buffer_free( group_buf );
3175 buffer_free( having_buf );
3176 if( defaultselhash ) jsonObjectFree( defaultselhash );
3182 // Build a WHERE clause, if there is one
3183 if ( search_hash ) {
3184 buffer_add(sql_buf, " WHERE ");
3186 // and it's on the WHERE clause
3187 char* pred = searchWHERE( search_hash, core_meta, AND_OP_JOIN, ctx );
3190 buffer_add(sql_buf, pred);
3194 osrfAppSessionStatus(
3196 OSRF_STATUS_INTERNALSERVERERROR,
3197 "osrfMethodException",
3199 "Severe query error in WHERE predicate -- see error log for more details"
3203 buffer_free(having_buf);
3204 buffer_free(group_buf);
3205 buffer_free(order_buf);
3206 buffer_free(sql_buf);
3207 if (defaultselhash) jsonObjectFree(defaultselhash);
3212 // Build a HAVING clause, if there is one
3213 if ( having_hash ) {
3214 buffer_add(sql_buf, " HAVING ");
3216 // and it's on the the WHERE clause
3217 char* pred = searchWHERE( having_hash, core_meta, AND_OP_JOIN, ctx );
3220 buffer_add(sql_buf, pred);
3224 osrfAppSessionStatus(
3226 OSRF_STATUS_INTERNALSERVERERROR,
3227 "osrfMethodException",
3229 "Severe query error in HAVING predicate -- see error log for more details"
3233 buffer_free(having_buf);
3234 buffer_free(group_buf);
3235 buffer_free(order_buf);
3236 buffer_free(sql_buf);
3237 if (defaultselhash) jsonObjectFree(defaultselhash);
3242 // Build an ORDER BY clause, if there is one
3244 jsonIterator* class_itr = jsonNewIterator( order_hash );
3245 while ( (snode = jsonIteratorNext( class_itr )) ) {
3247 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
3250 if ( snode->type == JSON_HASH ) {
3252 jsonIterator* order_itr = jsonNewIterator( snode );
3253 while ( (onode = jsonIteratorNext( order_itr )) ) {
3255 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ))
3258 char* direction = NULL;
3259 if ( onode->type == JSON_HASH ) {
3260 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3261 string = searchFieldTransform(
3263 oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ),
3267 growing_buffer* field_buf = buffer_init(16);
3268 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3269 string = buffer_release(field_buf);
3272 if ( (tmp_const = jsonObjectGetKeyConst( onode, "direction" )) ) {
3273 direction = jsonObjectToSimpleString(tmp_const);
3274 if (!strncasecmp(direction, "d", 1)) {
3276 direction = " DESC";
3284 string = strdup(order_itr->key);
3285 direction = jsonObjectToSimpleString(onode);
3286 if (!strncasecmp(direction, "d", 1)) {
3288 direction = " DESC";
3298 buffer_add(order_buf, ", ");
3301 buffer_add(order_buf, string);
3305 buffer_add(order_buf, direction);
3309 // jsonIteratorFree(order_itr);
3311 } else if ( snode->type == JSON_ARRAY ) {
3313 jsonIterator* order_itr = jsonNewIterator( snode );
3314 while ( (onode = jsonIteratorNext( order_itr )) ) {
3316 char* _f = jsonObjectToSimpleString( onode );
3318 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, _f))
3324 buffer_add(order_buf, ", ");
3327 buffer_add(order_buf, _f);
3331 // jsonIteratorFree(order_itr);
3334 // IT'S THE OOOOOOOOOOOLD STYLE!
3336 osrfLogError(OSRF_LOG_MARK, "%s: Possible SQL injection attempt; direct order by is not allowed", MODULENAME);
3338 osrfAppSessionStatus(
3340 OSRF_STATUS_INTERNALSERVERERROR,
3341 "osrfMethodException",
3343 "Severe query error -- see error log for more details"
3348 buffer_free(having_buf);
3349 buffer_free(group_buf);
3350 buffer_free(order_buf);
3351 buffer_free(sql_buf);
3352 if (defaultselhash) jsonObjectFree(defaultselhash);
3353 jsonIteratorFree(class_itr);
3358 // jsonIteratorFree(class_itr);
3362 string = buffer_release(group_buf);
3364 if ( *string && ( aggregate_found || (flags & SELECT_DISTINCT) ) ) {
3365 OSRF_BUFFER_ADD( sql_buf, " GROUP BY " );
3366 OSRF_BUFFER_ADD( sql_buf, string );
3371 string = buffer_release(having_buf);
3374 OSRF_BUFFER_ADD( sql_buf, " HAVING " );
3375 OSRF_BUFFER_ADD( sql_buf, string );
3380 string = buffer_release(order_buf);
3383 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3384 OSRF_BUFFER_ADD( sql_buf, string );
3390 string = jsonObjectToSimpleString(limit);
3391 buffer_fadd( sql_buf, " LIMIT %d", atoi(string) );
3396 string = jsonObjectToSimpleString(offset);
3397 buffer_fadd( sql_buf, " OFFSET %d", atoi(string) );
3401 if (!(flags & SUBSELECT)) OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3404 if (defaultselhash) jsonObjectFree(defaultselhash);
3406 return buffer_release(sql_buf);
3410 static char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
3412 const char* locale = osrf_message_get_last_locale();
3414 osrfHash* fields = osrfHashGet(meta, "fields");
3415 char* core_class = osrfHashGet(meta, "classname");
3417 const jsonObject* join_hash = jsonObjectGetKeyConst( order_hash, "join" );
3419 jsonObject* node = NULL;
3420 jsonObject* snode = NULL;
3421 jsonObject* onode = NULL;
3422 const jsonObject* _tmp = NULL;
3423 jsonObject* selhash = NULL;
3424 jsonObject* defaultselhash = NULL;
3426 growing_buffer* sql_buf = buffer_init(128);
3427 growing_buffer* select_buf = buffer_init(128);
3429 if ( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
3430 defaultselhash = jsonNewObjectType(JSON_HASH);
3431 selhash = defaultselhash;
3434 if ( !jsonObjectGetKeyConst(selhash,core_class) ) {
3435 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
3436 jsonObject* flist = jsonObjectGetKey( selhash, core_class );
3441 osrfStringArray* keys = osrfHashKeys( fields );
3442 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
3443 if( ! str_is_true( osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
3444 jsonObjectPush( flist, jsonNewObject( field ) );
3446 osrfStringArrayFree(keys);
3450 jsonIterator* class_itr = jsonNewIterator( selhash );
3451 while ( (snode = jsonIteratorNext( class_itr )) ) {
3453 char* cname = class_itr->key;
3454 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
3455 if (!idlClass) continue;
3457 if (strcmp(core_class,class_itr->key)) {
3458 if (!join_hash) continue;
3460 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", class_itr->key);
3462 jsonObjectFree(found);
3466 jsonObjectFree(found);
3469 jsonIterator* select_itr = jsonNewIterator( snode );
3470 while ( (node = jsonIteratorNext( select_itr )) ) {
3471 char* item_str = jsonObjectToSimpleString(node);
3472 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), item_str );
3474 char* fname = osrfHashGet(field, "name");
3476 if (!field) continue;
3481 OSRF_BUFFER_ADD_CHAR(select_buf, ',');
3486 const jsonObject* no_i18n_obj = jsonObjectGetKey( order_hash, "no_i18n" );
3487 if ( obj_is_true( no_i18n_obj ) ) // Suppress internationalization?
3490 i18n = osrfHashGet(field, "i18n");
3492 if( str_is_true( i18n ) ) {
3493 char* pkey = osrfHashGet(idlClass, "primarykey");
3494 char* tname = osrfHashGet(idlClass, "tablename");
3496 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);
3498 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3501 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3505 jsonIteratorFree(select_itr);
3508 jsonIteratorFree(class_itr);
3510 char* col_list = buffer_release(select_buf);
3511 char* table = getSourceDefinition(meta);
3513 table = strdup( "(null)" );
3515 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\"", col_list, table, core_class );
3520 char* join_clause = searchJOIN( join_hash, meta );
3521 OSRF_BUFFER_ADD_CHAR(sql_buf, ' ');
3522 OSRF_BUFFER_ADD(sql_buf, join_clause);
3526 osrfLogDebug(OSRF_LOG_MARK, "%s pre-predicate SQL = %s",
3527 MODULENAME, OSRF_BUFFER_C_STR(sql_buf));
3529 buffer_add(sql_buf, " WHERE ");
3531 char* pred = searchWHERE( search_hash, meta, AND_OP_JOIN, ctx );
3533 osrfAppSessionStatus(
3535 OSRF_STATUS_INTERNALSERVERERROR,
3536 "osrfMethodException",
3538 "Severe query error -- see error log for more details"
3540 buffer_free(sql_buf);
3541 if(defaultselhash) jsonObjectFree(defaultselhash);
3544 buffer_add(sql_buf, pred);
3549 char* string = NULL;
3550 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "order_by" )) ){
3552 growing_buffer* order_buf = buffer_init(128);
3555 jsonIterator* class_itr = jsonNewIterator( _tmp );
3556 while ( (snode = jsonIteratorNext( class_itr )) ) {
3558 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
3561 if ( snode->type == JSON_HASH ) {
3563 jsonIterator* order_itr = jsonNewIterator( snode );
3564 while ( (onode = jsonIteratorNext( order_itr )) ) {
3566 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ))
3569 char* direction = NULL;
3570 if ( onode->type == JSON_HASH ) {
3571 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3572 string = searchFieldTransform(
3574 oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ),
3578 growing_buffer* field_buf = buffer_init(16);
3579 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3580 string = buffer_release(field_buf);
3583 if ( (_tmp = jsonObjectGetKeyConst( onode, "direction" )) ) {
3584 direction = jsonObjectToSimpleString(_tmp);
3585 if (!strncasecmp(direction, "d", 1)) {
3587 direction = " DESC";
3595 string = strdup(order_itr->key);
3596 direction = jsonObjectToSimpleString(onode);
3597 if (!strncasecmp(direction, "d", 1)) {
3599 direction = " DESC";
3609 buffer_add(order_buf, ", ");
3612 buffer_add(order_buf, string);
3616 buffer_add(order_buf, direction);
3621 jsonIteratorFree(order_itr);
3624 string = jsonObjectToSimpleString(snode);
3625 buffer_add(order_buf, string);
3632 jsonIteratorFree(class_itr);
3634 string = buffer_release(order_buf);
3637 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3638 OSRF_BUFFER_ADD( sql_buf, string );
3644 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "limit" )) ){
3645 string = jsonObjectToSimpleString(_tmp);
3654 _tmp = jsonObjectGetKeyConst( order_hash, "offset" );
3656 string = jsonObjectToSimpleString(_tmp);
3666 if (defaultselhash) jsonObjectFree(defaultselhash);
3668 OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3669 return buffer_release(sql_buf);
3672 int doJSONSearch ( osrfMethodContext* ctx ) {
3673 if(osrfMethodVerifyContext( ctx )) {
3674 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
3678 osrfLogDebug(OSRF_LOG_MARK, "Recieved query request");
3683 dbhandle = writehandle;
3685 jsonObject* hash = jsonObjectGetIndex(ctx->params, 0);
3689 if ( obj_is_true( jsonObjectGetKey( hash, "distinct" ) ) )
3690 flags |= SELECT_DISTINCT;
3692 if ( obj_is_true( jsonObjectGetKey( hash, "no_i18n" ) ) )
3693 flags |= DISABLE_I18N;
3695 osrfLogDebug(OSRF_LOG_MARK, "Building SQL ...");
3698 jsonObjectGetKey( hash, "select" ),
3699 jsonObjectGetKey( hash, "from" ),
3700 jsonObjectGetKey( hash, "where" ),
3701 jsonObjectGetKey( hash, "having" ),
3702 jsonObjectGetKey( hash, "order_by" ),
3703 jsonObjectGetKey( hash, "limit" ),
3704 jsonObjectGetKey( hash, "offset" ),
3713 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
3714 dbi_result result = dbi_conn_query(dbhandle, sql);
3717 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
3719 if (dbi_result_first_row(result)) {
3720 /* JSONify the result */
3721 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
3724 jsonObject* return_val = oilsMakeJSONFromResult( result );
3725 osrfAppRespond( ctx, return_val );
3726 jsonObjectFree( return_val );
3727 } while (dbi_result_next_row(result));
3730 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
3733 osrfAppRespondComplete( ctx, NULL );
3735 /* clean up the query */
3736 dbi_result_free(result);
3740 osrfLogError(OSRF_LOG_MARK, "%s: Error with query [%s]", MODULENAME, sql);
3741 osrfAppSessionStatus(
3743 OSRF_STATUS_INTERNALSERVERERROR,
3744 "osrfMethodException",
3746 "Severe query error -- see error log for more details"
3754 static jsonObject* doFieldmapperSearch ( osrfMethodContext* ctx, osrfHash* meta,
3755 const jsonObject* params, int* err ) {
3758 dbhandle = writehandle;
3760 osrfHash* links = osrfHashGet(meta, "links");
3761 osrfHash* fields = osrfHashGet(meta, "fields");
3762 char* core_class = osrfHashGet(meta, "classname");
3763 char* pkey = osrfHashGet(meta, "primarykey");
3765 const jsonObject* _tmp;
3767 jsonObject* search_hash = jsonObjectGetIndex(params, 0);
3768 jsonObject* order_hash = jsonObjectGetIndex(params, 1);
3770 char* sql = buildSELECT( search_hash, order_hash, meta, ctx );
3772 osrfLogDebug(OSRF_LOG_MARK, "Problem building query, returning NULL");
3777 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
3779 dbi_result result = dbi_conn_query(dbhandle, sql);
3780 if( NULL == result ) {
3781 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]",
3782 MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
3783 osrfAppSessionStatus(
3785 OSRF_STATUS_INTERNALSERVERERROR,
3786 "osrfMethodException",
3788 "Severe query error -- see error log for more details"
3795 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
3798 jsonObject* res_list = jsonNewObjectType(JSON_ARRAY);
3799 osrfHash* dedup = osrfNewHash();
3801 if (dbi_result_first_row(result)) {
3802 /* JSONify the result */
3803 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
3805 obj = oilsMakeFieldmapperFromResult( result, meta );
3806 char* pkey_val = oilsFMGetString( obj, pkey );
3807 if ( osrfHashGet( dedup, pkey_val ) ) {
3808 jsonObjectFree(obj);
3811 osrfHashSet( dedup, pkey_val, pkey_val );
3812 jsonObjectPush(res_list, obj);
3814 } while (dbi_result_next_row(result));
3816 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s",
3820 osrfHashFree(dedup);
3821 /* clean up the query */
3822 dbi_result_free(result);
3825 if (res_list->size && order_hash) {
3826 _tmp = jsonObjectGetKeyConst( order_hash, "flesh" );
3828 int x = (int)jsonObjectGetNumber(_tmp);
3829 if (x == -1 || x > max_flesh_depth) x = max_flesh_depth;
3831 const jsonObject* temp_blob;
3832 if ((temp_blob = jsonObjectGetKeyConst( order_hash, "flesh_fields" )) && x > 0) {
3834 jsonObject* flesh_blob = jsonObjectClone( temp_blob );
3835 const jsonObject* flesh_fields = jsonObjectGetKeyConst( flesh_blob, core_class );
3837 osrfStringArray* link_fields = NULL;
3840 if (flesh_fields->size == 1) {
3841 char* _t = jsonObjectToSimpleString( jsonObjectGetIndex( flesh_fields, 0 ) );
3842 if (!strcmp(_t,"*")) link_fields = osrfHashKeys( links );
3848 link_fields = osrfNewStringArray(1);
3849 jsonIterator* _i = jsonNewIterator( flesh_fields );
3850 while ((_f = jsonIteratorNext( _i ))) {
3851 osrfStringArrayAdd( link_fields, jsonObjectToSimpleString( _f ) );
3853 jsonIteratorFree(_i);
3858 jsonIterator* itr = jsonNewIterator( res_list );
3859 while ((cur = jsonIteratorNext( itr ))) {
3864 while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
3866 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
3868 osrfHash* kid_link = osrfHashGet(links, link_field);
3869 if (!kid_link) continue;
3871 osrfHash* field = osrfHashGet(fields, link_field);
3872 if (!field) continue;
3874 osrfHash* value_field = field;
3876 osrfHash* kid_idl = osrfHashGet(oilsIDL(), osrfHashGet(kid_link, "class"));
3877 if (!kid_idl) continue;
3879 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
3880 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
3883 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
3884 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
3887 osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
3889 if (link_map->size > 0) {
3890 jsonObject* _kid_key = jsonNewObjectType(JSON_ARRAY);
3893 jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
3898 osrfHashGet(kid_link, "class"),
3905 "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
3906 osrfHashGet(kid_link, "field"),
3907 osrfHashGet(kid_link, "class"),
3908 osrfHashGet(kid_link, "key"),
3909 osrfHashGet(kid_link, "reltype")
3912 jsonObject* fake_params = jsonNewObjectType(JSON_ARRAY);
3913 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH)); // search hash
3914 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH)); // order/flesh hash
3916 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
3919 jsonObjectToSimpleString(
3922 atoi( osrfHashGet(value_field, "array_position") )
3927 osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
3932 jsonObjectGetIndex(fake_params, 0),
3933 osrfHashGet(kid_link, "key"),
3934 jsonNewObject( search_key )
3941 jsonObjectGetIndex(fake_params, 1),
3943 jsonNewNumberObject( (double)(x - 1 + link_map->size) )
3947 jsonObjectSetKey( jsonObjectGetIndex(fake_params, 1), "flesh_fields", jsonObjectClone(flesh_blob) );
3949 if (jsonObjectGetKeyConst(order_hash, "order_by")) {
3951 jsonObjectGetIndex(fake_params, 1),
3953 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "order_by"))
3957 if (jsonObjectGetKeyConst(order_hash, "select")) {
3959 jsonObjectGetIndex(fake_params, 1),
3961 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "select"))
3965 jsonObject* kids = doFieldmapperSearch(ctx, kid_idl, fake_params, err);
3968 jsonObjectFree( fake_params );
3969 osrfStringArrayFree(link_fields);
3970 jsonIteratorFree(itr);
3971 jsonObjectFree(res_list);
3972 jsonObjectFree(flesh_blob);
3976 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
3978 jsonObject* X = NULL;
3979 if ( link_map->size > 0 && kids->size > 0 ) {
3981 kids = jsonNewObjectType(JSON_ARRAY);
3983 jsonObject* _k_node;
3984 jsonIterator* _k = jsonNewIterator( X );
3985 while ((_k_node = jsonIteratorNext( _k ))) {
3991 (unsigned long)atoi(
3997 osrfHashGet(kid_link, "class")
4001 osrfStringArrayGetString( link_map, 0 )
4010 jsonIteratorFree(_k);
4013 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" )) || !(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) {
4014 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
4017 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
4018 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
4022 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
4023 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
4026 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
4027 jsonObjectClone( kids )
4032 jsonObjectFree(kids);
4036 jsonObjectFree( kids );
4037 jsonObjectFree( fake_params );
4039 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
4040 osrfLogDebug(OSRF_LOG_MARK, "%s", jsonObjectToJSON(cur));
4044 jsonObjectFree( flesh_blob );
4045 osrfStringArrayFree(link_fields);
4046 jsonIteratorFree(itr);
4055 static jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
4057 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4059 jsonObject* target = jsonObjectGetIndex( ctx->params, 1 );
4061 jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
4064 if (!verifyObjectClass(ctx, target)) {
4069 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4070 osrfAppSessionStatus(
4072 OSRF_STATUS_BADREQUEST,
4073 "osrfMethodException",
4075 "No active transaction -- required for UPDATE"
4081 // The following test is harmless but redundant. If a class is
4082 // readonly, we don't register an update method for it.
4083 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4084 osrfAppSessionStatus(
4086 OSRF_STATUS_BADREQUEST,
4087 "osrfMethodException",
4089 "Cannot UPDATE readonly class"
4095 dbhandle = writehandle;
4097 char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
4099 // Set the last_xact_id
4100 int index = oilsIDL_ntop( target->classname, "last_xact_id" );
4102 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
4103 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
4106 char* pkey = osrfHashGet(meta, "primarykey");
4107 osrfHash* fields = osrfHashGet(meta, "fields");
4109 char* id = oilsFMGetString( target, pkey );
4113 "%s updating %s object with %s = %s",
4115 osrfHashGet(meta, "fieldmapper"),
4120 growing_buffer* sql = buffer_init(128);
4121 buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
4126 osrfStringArray* field_list = osrfHashKeys( fields );
4127 while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
4129 osrfHash* field = osrfHashGet( fields, field_name );
4131 if(!( strcmp( field_name, pkey ) )) continue;
4132 if( str_is_true( osrfHashGet(osrfHashGet(fields,field_name), "virtual") ) )
4135 const jsonObject* field_object = oilsFMGetObject( target, field_name );
4137 int value_is_numeric = 0; // boolean
4139 if (field_object && field_object->classname) {
4140 value = oilsFMGetString(
4142 (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
4145 value = jsonObjectToSimpleString( field_object );
4146 if( field_object && JSON_NUMBER == field_object->type )
4147 value_is_numeric = 1;
4150 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
4152 if (!field_object || field_object->type == JSON_NULL) {
4153 if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
4154 if (first) first = 0;
4155 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4156 buffer_fadd( sql, " %s = NULL", field_name );
4159 } else if ( value_is_numeric || !strcmp( get_primitive( field ), "number") ) {
4160 if (first) first = 0;
4161 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4163 const char* numtype = get_datatype( field );
4164 if ( !strncmp( numtype, "INT", 3 ) ) {
4165 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
4166 } else if ( !strcmp( numtype, "NUMERIC" ) ) {
4167 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
4170 osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, numtype );
4173 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4174 if (first) first = 0;
4175 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4176 buffer_fadd( sql, " %s = %s", field_name, value );
4179 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4180 osrfAppSessionStatus(
4182 OSRF_STATUS_INTERNALSERVERERROR,
4183 "osrfMethodException",
4185 "Error quoting string -- please see the error log for more details"
4199 jsonObject* obj = jsonNewObject(id);
4201 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4202 dbi_conn_quote_string(dbhandle, &id);
4204 buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
4206 char* query = buffer_release(sql);
4207 osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
4209 dbi_result result = dbi_conn_query(dbhandle, query);
4213 jsonObjectFree(obj);
4214 obj = jsonNewObject(NULL);
4217 "%s ERROR updating %s object with %s = %s",
4219 osrfHashGet(meta, "fieldmapper"),
4230 static jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
4232 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4234 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4235 osrfAppSessionStatus(
4237 OSRF_STATUS_BADREQUEST,
4238 "osrfMethodException",
4240 "No active transaction -- required for DELETE"
4246 // The following test is harmless but redundant. If a class is
4247 // readonly, we don't register a delete method for it.
4248 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4249 osrfAppSessionStatus(
4251 OSRF_STATUS_BADREQUEST,
4252 "osrfMethodException",
4254 "Cannot DELETE readonly class"
4260 dbhandle = writehandle;
4264 char* pkey = osrfHashGet(meta, "primarykey");
4272 if (jsonObjectGetIndex(ctx->params, _obj_pos)->classname) {
4273 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, _obj_pos ))) {
4278 id = oilsFMGetString( jsonObjectGetIndex(ctx->params, _obj_pos), pkey );
4281 if (!verifyObjectPCRUD( ctx, NULL )) {
4286 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, _obj_pos));
4291 "%s deleting %s object with %s = %s",
4293 osrfHashGet(meta, "fieldmapper"),
4298 obj = jsonNewObject(id);
4300 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4301 dbi_conn_quote_string(writehandle, &id);
4303 dbi_result result = dbi_conn_queryf(writehandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
4306 jsonObjectFree(obj);
4307 obj = jsonNewObject(NULL);
4310 "%s ERROR deleting %s object with %s = %s",
4312 osrfHashGet(meta, "fieldmapper"),
4325 static jsonObject* oilsMakeFieldmapperFromResult( dbi_result result, osrfHash* meta) {
4326 if(!(result && meta)) return jsonNULL;
4328 jsonObject* object = jsonNewObject(NULL);
4329 jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
4331 osrfHash* fields = osrfHashGet(meta, "fields");
4333 osrfLogInternal(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
4337 char dt_string[256];
4341 int columnIndex = 1;
4343 unsigned short type;
4344 const char* columnName;
4346 /* cycle through the column list */
4347 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4349 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4351 fmIndex = -1; // reset the position
4353 /* determine the field type and storage attributes */
4354 type = dbi_result_get_field_type(result, columnName);
4355 attr = dbi_result_get_field_attribs(result, columnName);
4357 /* fetch the fieldmapper index */
4358 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
4360 if ( str_is_true( osrfHashGet(_f, "virtual") ) )
4363 const char* pos = (char*)osrfHashGet(_f, "array_position");
4364 if ( !pos ) continue;
4366 fmIndex = atoi( pos );
4367 osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
4372 if (dbi_result_field_is_null(result, columnName)) {
4373 jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
4378 case DBI_TYPE_INTEGER :
4380 if( attr & DBI_INTEGER_SIZE8 )
4381 jsonObjectSetIndex( object, fmIndex,
4382 jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
4384 jsonObjectSetIndex( object, fmIndex,
4385 jsonNewNumberObject(dbi_result_get_int(result, columnName)));
4389 case DBI_TYPE_DECIMAL :
4390 jsonObjectSetIndex( object, fmIndex,
4391 jsonNewNumberObject(dbi_result_get_double(result, columnName)));
4394 case DBI_TYPE_STRING :
4400 jsonNewObject( dbi_result_get_string(result, columnName) )
4405 case DBI_TYPE_DATETIME :
4407 memset(dt_string, '\0', sizeof(dt_string));
4408 memset(&gmdt, '\0', sizeof(gmdt));
4410 _tmp_dt = dbi_result_get_datetime(result, columnName);
4413 if (!(attr & DBI_DATETIME_DATE)) {
4414 gmtime_r( &_tmp_dt, &gmdt );
4415 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4416 } else if (!(attr & DBI_DATETIME_TIME)) {
4417 localtime_r( &_tmp_dt, &gmdt );
4418 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4420 localtime_r( &_tmp_dt, &gmdt );
4421 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4424 jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
4428 case DBI_TYPE_BINARY :
4429 osrfLogError( OSRF_LOG_MARK,
4430 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4438 static jsonObject* oilsMakeJSONFromResult( dbi_result result ) {
4439 if(!result) return jsonNULL;
4441 jsonObject* object = jsonNewObject(NULL);
4444 char dt_string[256];
4448 int columnIndex = 1;
4450 unsigned short type;
4451 const char* columnName;
4453 /* cycle through the column list */
4454 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4456 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4458 fmIndex = -1; // reset the position
4460 /* determine the field type and storage attributes */
4461 type = dbi_result_get_field_type(result, columnName);
4462 attr = dbi_result_get_field_attribs(result, columnName);
4464 if (dbi_result_field_is_null(result, columnName)) {
4465 jsonObjectSetKey( object, columnName, jsonNewObject(NULL) );
4470 case DBI_TYPE_INTEGER :
4472 if( attr & DBI_INTEGER_SIZE8 )
4473 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_longlong(result, columnName)) );
4475 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_int(result, columnName)) );
4478 case DBI_TYPE_DECIMAL :
4479 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_double(result, columnName)) );
4482 case DBI_TYPE_STRING :
4483 jsonObjectSetKey( object, columnName, jsonNewObject(dbi_result_get_string(result, columnName)) );
4486 case DBI_TYPE_DATETIME :
4488 memset(dt_string, '\0', sizeof(dt_string));
4489 memset(&gmdt, '\0', sizeof(gmdt));
4491 _tmp_dt = dbi_result_get_datetime(result, columnName);
4494 if (!(attr & DBI_DATETIME_DATE)) {
4495 gmtime_r( &_tmp_dt, &gmdt );
4496 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4497 } else if (!(attr & DBI_DATETIME_TIME)) {
4498 localtime_r( &_tmp_dt, &gmdt );
4499 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4501 localtime_r( &_tmp_dt, &gmdt );
4502 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4505 jsonObjectSetKey( object, columnName, jsonNewObject(dt_string) );
4508 case DBI_TYPE_BINARY :
4509 osrfLogError( OSRF_LOG_MARK,
4510 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4518 // Interpret a string as true or false
4519 static int str_is_true( const char* str ) {
4520 if( NULL == str || strcasecmp( str, "true" ) )
4526 // Interpret a jsonObject as true or false
4527 static int obj_is_true( const jsonObject* obj ) {
4530 else switch( obj->type )
4538 if( strcasecmp( obj->value.s, "true" ) )
4542 case JSON_NUMBER : // Support 1/0 for perl's sake
4543 if( jsonObjectGetNumber( obj ) == 1.0 )
4552 // Translate a numeric code into a text string identifying a type of
4553 // jsonObject. To be used for building error messages.
4554 static const char* json_type( int code ) {
4560 return "JSON_ARRAY";
4562 return "JSON_STRING";
4564 return "JSON_NUMBER";
4570 return "(unrecognized)";
4574 // Extract the "primitive" attribute from an IDL field definition.
4575 // If we haven't initialized the app, then we must be running in
4576 // some kind of testbed. In that case, default to "string".
4577 static const char* get_primitive( osrfHash* field ) {
4578 const char* s = osrfHashGet( field, "primitive" );
4580 if( child_initialized )
4583 "%s ERROR No \"datatype\" attribute for field \"%s\"",
4585 osrfHashGet( field, "name" )
4593 // Extract the "datatype" attribute from an IDL field definition.
4594 // If we haven't initialized the app, then we must be running in
4595 // some kind of testbed. In that case, default to to NUMERIC,
4596 // since we look at the datatype only for numbers.
4597 static const char* get_datatype( osrfHash* field ) {
4598 const char* s = osrfHashGet( field, "datatype" );
4600 if( child_initialized )
4603 "%s ERROR No \"datatype\" attribute for field \"%s\"",
4605 osrfHashGet( field, "name" )
4614 If the input string is potentially a valid SQL identifier, return 1.
4617 Purpose: to prevent certain kinds of SQL injection. To that end we
4618 don't necessarily need to follow all the rules exactly, such as requiring
4619 that the first character not be a digit.
4621 We allow leading and trailing white space. In between, we do not allow
4622 punctuation (except for underscores and dollar signs), control
4623 characters, or embedded white space.
4625 More pedantically we should allow quoted identifiers containing arbitrary
4626 characters, but for the foreseeable future such quoted identifiers are not
4627 likely to be an issue.
4629 static int is_identifier( const char* s) {
4633 // Skip leading white space
4634 while( isspace( (unsigned char) *s ) )
4638 return 0; // Nothing but white space? Not okay.
4640 // Check each character until we reach white space or
4641 // end-of-string. Letters, digits, underscores, and
4642 // dollar signs are okay. Control characters and other
4643 // punctuation characters are not okay. Anything else
4644 // is okay -- it could for example be part of a multibyte
4645 // UTF8 character such as a letter with diacritical marks,
4646 // and those are allowed.
4648 if( isalnum( (unsigned char) *s )
4651 ; // Fine; keep going
4652 else if( ispunct( (unsigned char) *s )
4653 || iscntrl( (unsigned char) *s ) )
4656 } while( *s && ! isspace( (unsigned char) *s ) );
4658 // If we found any white space in the above loop,
4659 // the rest had better be all white space.
4661 while( isspace( (unsigned char) *s ) )
4665 return 0; // White space was embedded within non-white space