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* ctx, osrfHash* meta,
54 jsonObject* where_hash, jsonObject* query_hash, int* err );
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*, const jsonObject*, const char* );
63 static char* searchBETWEENPredicate ( const char*, osrfHash*, const 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);
82 static int is_good_operator( const char* op );
85 static jsonObject* verifyUserPCRUD( osrfMethodContext* );
86 static int verifyObjectPCRUD( osrfMethodContext*, const jsonObject* );
87 static jsonObject* single_hash( const char* key, const char* value );
90 static int child_initialized = 0; /* boolean */
92 static dbi_conn writehandle; /* our MASTER db connection */
93 static dbi_conn dbhandle; /* our CURRENT db connection */
94 //static osrfHash * readHandles;
95 static jsonObject* jsonNULL = NULL; //
96 static int max_flesh_depth = 100;
98 /* called when this process is about to exit */
99 void osrfAppChildExit() {
100 osrfLogDebug(OSRF_LOG_MARK, "Child is exiting, disconnecting from database...");
103 if (writehandle == dbhandle) same = 1;
105 dbi_conn_query(writehandle, "ROLLBACK;");
106 dbi_conn_close(writehandle);
109 if (dbhandle && !same)
110 dbi_conn_close(dbhandle);
112 // XXX add cleanup of readHandles whenever that gets used
117 int osrfAppInitialize() {
119 osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
120 osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
122 if (!oilsIDLInit( osrf_settings_host_value("/IDL") )) return 1; /* return non-zero to indicate error */
124 growing_buffer* method_name = buffer_init(64);
126 // Generic search thingy
127 buffer_add(method_name, MODULENAME);
128 buffer_add(method_name, ".json_query");
129 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
130 "doJSONSearch", "", 1, OSRF_METHOD_STREAMING );
133 // first we register all the transaction and savepoint methods
134 buffer_reset(method_name);
135 OSRF_BUFFER_ADD(method_name, MODULENAME);
136 OSRF_BUFFER_ADD(method_name, ".transaction.begin");
137 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
138 "beginTransaction", "", 0, 0 );
140 buffer_reset(method_name);
141 OSRF_BUFFER_ADD(method_name, MODULENAME);
142 OSRF_BUFFER_ADD(method_name, ".transaction.commit");
143 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
144 "commitTransaction", "", 0, 0 );
146 buffer_reset(method_name);
147 OSRF_BUFFER_ADD(method_name, MODULENAME);
148 OSRF_BUFFER_ADD(method_name, ".transaction.rollback");
149 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
150 "rollbackTransaction", "", 0, 0 );
152 buffer_reset(method_name);
153 OSRF_BUFFER_ADD(method_name, MODULENAME);
154 OSRF_BUFFER_ADD(method_name, ".savepoint.set");
155 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
156 "setSavepoint", "", 1, 0 );
158 buffer_reset(method_name);
159 OSRF_BUFFER_ADD(method_name, MODULENAME);
160 OSRF_BUFFER_ADD(method_name, ".savepoint.release");
161 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
162 "releaseSavepoint", "", 1, 0 );
164 buffer_reset(method_name);
165 OSRF_BUFFER_ADD(method_name, MODULENAME);
166 OSRF_BUFFER_ADD(method_name, ".savepoint.rollback");
167 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
168 "rollbackSavepoint", "", 1, 0 );
170 buffer_free(method_name);
172 static const char* global_method[] = {
180 const int global_method_count
181 = sizeof( global_method ) / sizeof ( global_method[0] );
185 osrfStringArray* classes = osrfHashKeys( oilsIDL() );
186 osrfLogDebug(OSRF_LOG_MARK, "%d classes loaded", classes->size );
187 osrfLogDebug(OSRF_LOG_MARK,
188 "At least %d methods will be generated", classes->size * global_method_count);
190 while ( (classname = osrfStringArrayGetString(classes, c_index++)) ) {
191 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
193 osrfHash* idlClass = osrfHashGet(oilsIDL(), classname);
195 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), MODULENAME )) {
196 osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on", MODULENAME, classname);
200 if ( str_is_true( osrfHashGet(idlClass, "virtual") ) ) {
201 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
205 // Look up some other attributes of the current class
206 const char* idlClass_fieldmapper = osrfHashGet(idlClass, "fieldmapper");
207 const char* readonly = osrfHashGet(idlClass, "readonly");
209 osrfHash* idlClass_permacrud = osrfHashGet(idlClass, "permacrud");
213 for( i = 0; i < global_method_count; ++i ) {
214 const char* method_type = global_method[ i ];
215 osrfLogDebug(OSRF_LOG_MARK,
216 "Using files to build %s class methods for %s", method_type, classname);
218 if (!idlClass_fieldmapper) continue;
221 if (!idlClass_permacrud) continue;
223 const char* tmp_method = method_type;
224 if ( *tmp_method == 'i' || *tmp_method == 's') {
225 tmp_method = "retrieve";
227 if (!osrfHashGet( idlClass_permacrud, tmp_method )) continue;
230 if ( str_is_true( readonly ) &&
231 ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd')
234 osrfHash* method_meta = osrfNewHash();
235 osrfHashSet(method_meta, idlClass, "class");
237 method_name = buffer_init(64);
239 buffer_fadd(method_name, "%s.%s.%s", MODULENAME, method_type, classname);
243 char* _fm = strdup( idlClass_fieldmapper );
244 part = strtok_r(_fm, ":", &st_tmp);
246 buffer_fadd(method_name, "%s.direct.%s", MODULENAME, part);
248 while ((part = strtok_r(NULL, ":", &st_tmp))) {
249 OSRF_BUFFER_ADD_CHAR(method_name, '.');
250 OSRF_BUFFER_ADD(method_name, part);
252 OSRF_BUFFER_ADD_CHAR(method_name, '.');
253 OSRF_BUFFER_ADD(method_name, method_type);
257 char* method = buffer_release(method_name);
259 osrfHashSet( method_meta, method, "methodname" );
260 osrfHashSet( method_meta, strdup(method_type), "methodtype" );
263 if (*method_type == 'i' || *method_type == 's') {
264 flags = flags | OSRF_METHOD_STREAMING;
267 osrfAppRegisterExtendedMethod(
270 "dispatchCRUDMethod",
284 static char* getSourceDefinition( osrfHash* class ) {
286 char* tabledef = osrfHashGet(class, "tablename");
289 tabledef = strdup(tabledef);
291 tabledef = osrfHashGet(class, "source_definition");
293 growing_buffer* tablebuf = buffer_init(128);
294 buffer_fadd( tablebuf, "(%s)", tabledef );
295 tabledef = buffer_release(tablebuf);
297 const char* classname = osrfHashGet( class, "classname" );
302 "%s ERROR No tablename or source_definition for class \"%s\"",
313 * Connects to the database
315 int osrfAppChildInit() {
317 osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
318 dbi_initialize(NULL);
319 osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
321 char* driver = osrf_settings_host_value("/apps/%s/app_settings/driver", MODULENAME);
322 char* user = osrf_settings_host_value("/apps/%s/app_settings/database/user", MODULENAME);
323 char* host = osrf_settings_host_value("/apps/%s/app_settings/database/host", MODULENAME);
324 char* port = osrf_settings_host_value("/apps/%s/app_settings/database/port", MODULENAME);
325 char* db = osrf_settings_host_value("/apps/%s/app_settings/database/db", MODULENAME);
326 char* pw = osrf_settings_host_value("/apps/%s/app_settings/database/pw", MODULENAME);
327 char* md = osrf_settings_host_value("/apps/%s/app_settings/max_query_recursion", MODULENAME);
329 osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
330 writehandle = dbi_conn_new(driver);
333 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
336 osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
338 osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database. host=%s, "
339 "port=%s, user=%s, pw=%s, db=%s", MODULENAME, host, port, user, pw, db );
341 if(host) dbi_conn_set_option(writehandle, "host", host );
342 if(port) dbi_conn_set_option_numeric( writehandle, "port", atoi(port) );
343 if(user) dbi_conn_set_option(writehandle, "username", user);
344 if(pw) dbi_conn_set_option(writehandle, "password", pw );
345 if(db) dbi_conn_set_option(writehandle, "dbname", db );
347 if(md) max_flesh_depth = atoi(md);
348 if(max_flesh_depth < 0) max_flesh_depth = 1;
349 if(max_flesh_depth > 1000) max_flesh_depth = 1000;
358 if (dbi_conn_connect(writehandle) < 0) {
360 if (dbi_conn_connect(writehandle) < 0) {
361 dbi_conn_error(writehandle, &err);
362 osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
367 osrfLogInfo(OSRF_LOG_MARK, "%s successfully connected to the database", MODULENAME);
372 osrfStringArray* classes = osrfHashKeys( oilsIDL() );
374 while ( (classname = osrfStringArrayGetString(classes, i++)) ) {
375 osrfHash* class = osrfHashGet( oilsIDL(), classname );
376 osrfHash* fields = osrfHashGet( class, "fields" );
378 if( str_is_true( osrfHashGet(class, "virtual") ) ) {
379 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
383 char* tabledef = getSourceDefinition(class);
385 tabledef = strdup( "(null)" );
387 growing_buffer* sql_buf = buffer_init(32);
388 buffer_fadd( sql_buf, "SELECT * FROM %s AS x WHERE 1=0;", tabledef );
392 char* sql = buffer_release(sql_buf);
393 osrfLogDebug(OSRF_LOG_MARK, "%s Investigatory SQL = %s", MODULENAME, sql);
395 dbi_result result = dbi_conn_query(writehandle, sql);
401 const char* columnName;
403 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
405 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
407 /* fetch the fieldmapper index */
408 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
410 osrfLogDebug(OSRF_LOG_MARK, "Found [%s] in IDL hash...", (char*)columnName);
412 /* determine the field type and storage attributes */
413 type = dbi_result_get_field_type(result, columnName);
417 case DBI_TYPE_INTEGER : {
419 if ( !osrfHashGet(_f, "primitive") )
420 osrfHashSet(_f,"number", "primitive");
422 int attr = dbi_result_get_field_attribs(result, columnName);
423 if( attr & DBI_INTEGER_SIZE8 )
424 osrfHashSet(_f,"INT8", "datatype");
426 osrfHashSet(_f,"INT", "datatype");
429 case DBI_TYPE_DECIMAL :
430 if ( !osrfHashGet(_f, "primitive") )
431 osrfHashSet(_f,"number", "primitive");
433 osrfHashSet(_f,"NUMERIC", "datatype");
436 case DBI_TYPE_STRING :
437 if ( !osrfHashGet(_f, "primitive") )
438 osrfHashSet(_f,"string", "primitive");
439 osrfHashSet(_f,"TEXT", "datatype");
442 case DBI_TYPE_DATETIME :
443 if ( !osrfHashGet(_f, "primitive") )
444 osrfHashSet(_f,"string", "primitive");
446 osrfHashSet(_f,"TIMESTAMP", "datatype");
449 case DBI_TYPE_BINARY :
450 if ( !osrfHashGet(_f, "primitive") )
451 osrfHashSet(_f,"string", "primitive");
453 osrfHashSet(_f,"BYTEA", "datatype");
458 "Setting [%s] to primitive [%s] and datatype [%s]...",
460 osrfHashGet(_f, "primitive"),
461 osrfHashGet(_f, "datatype")
465 dbi_result_free(result);
467 osrfLogDebug(OSRF_LOG_MARK, "No data found for class [%s]...", (char*)classname);
471 osrfStringArrayFree(classes);
473 child_initialized = 1;
478 This function is a sleazy hack intended *only* for testing and
479 debugging. Any real server process should initialize the
480 database connection by calling osrfAppChildInit().
482 void set_cstore_dbi_conn( dbi_conn conn ) {
483 dbhandle = writehandle = conn;
486 void userDataFree( void* blob ) {
487 osrfHashFree( (osrfHash*)blob );
491 static void sessionDataFree( char* key, void* item ) {
492 if (!(strcmp(key,"xact_id"))) {
494 dbi_conn_query(writehandle, "ROLLBACK;");
501 int beginTransaction ( osrfMethodContext* ctx ) {
502 if(osrfMethodVerifyContext( ctx )) {
503 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
508 jsonObject* user = verifyUserPCRUD( ctx );
509 if (!user) return -1;
510 jsonObjectFree(user);
513 dbi_result result = dbi_conn_query(writehandle, "START TRANSACTION;");
515 osrfLogError(OSRF_LOG_MARK, "%s: Error starting transaction", MODULENAME );
516 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error starting transaction" );
519 jsonObject* ret = jsonNewObject(ctx->session->session_id);
520 osrfAppRespondComplete( ctx, ret );
523 if (!ctx->session->userData) {
524 ctx->session->userData = osrfNewHash();
525 osrfHashSetCallback((osrfHash*)ctx->session->userData, &sessionDataFree);
528 osrfHashSet( (osrfHash*)ctx->session->userData, strdup( ctx->session->session_id ), "xact_id" );
529 ctx->session->userDataFree = &userDataFree;
535 int setSavepoint ( osrfMethodContext* ctx ) {
536 if(osrfMethodVerifyContext( ctx )) {
537 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
544 jsonObject* user = verifyUserPCRUD( ctx );
545 if (!user) return -1;
546 jsonObjectFree(user);
549 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
550 osrfAppSessionStatus(
552 OSRF_STATUS_INTERNALSERVERERROR,
553 "osrfMethodException",
555 "No active transaction -- required for savepoints"
560 const char* spName = jsonObjectGetString(jsonObjectGetIndex(ctx->params, spNamePos));
562 dbi_result result = dbi_conn_queryf(writehandle, "SAVEPOINT \"%s\";", spName);
566 "%s: Error creating savepoint %s in transaction %s",
569 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
571 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR,
572 "osrfMethodException", ctx->request, "Error creating savepoint" );
575 jsonObject* ret = jsonNewObject(spName);
576 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 const char* spName = jsonObjectGetString( 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,
619 "osrfMethodException", ctx->request, "Error releasing savepoint" );
622 jsonObject* ret = jsonNewObject(spName);
623 osrfAppRespondComplete( ctx, ret );
629 int rollbackSavepoint ( osrfMethodContext* ctx ) {
630 if(osrfMethodVerifyContext( ctx )) {
631 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
638 jsonObject* user = verifyUserPCRUD( ctx );
639 if (!user) return -1;
640 jsonObjectFree(user);
643 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
644 osrfAppSessionStatus(
646 OSRF_STATUS_INTERNALSERVERERROR,
647 "osrfMethodException",
649 "No active transaction -- required for savepoints"
654 const char* spName = jsonObjectGetString( jsonObjectGetIndex(ctx->params, spNamePos) );
656 dbi_result result = dbi_conn_queryf(writehandle, "ROLLBACK TO SAVEPOINT \"%s\";", spName);
660 "%s: Error rolling back savepoint %s in transaction %s",
663 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
665 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR,
666 "osrfMethodException", ctx->request, "Error rolling back savepoint" );
669 jsonObject* ret = jsonNewObject(spName);
670 osrfAppRespondComplete( ctx, ret );
676 int commitTransaction ( osrfMethodContext* ctx ) {
677 if(osrfMethodVerifyContext( ctx )) {
678 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
683 jsonObject* user = verifyUserPCRUD( ctx );
684 if (!user) return -1;
685 jsonObjectFree(user);
688 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
689 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to commit" );
693 dbi_result result = dbi_conn_query(writehandle, "COMMIT;");
695 osrfLogError(OSRF_LOG_MARK, "%s: Error committing transaction", MODULENAME );
696 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error committing transaction" );
699 osrfHashRemove(ctx->session->userData, "xact_id");
700 jsonObject* ret = jsonNewObject(ctx->session->session_id);
701 osrfAppRespondComplete( ctx, ret );
707 int rollbackTransaction ( osrfMethodContext* ctx ) {
708 if(osrfMethodVerifyContext( ctx )) {
709 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
714 jsonObject* user = verifyUserPCRUD( ctx );
715 if (!user) return -1;
716 jsonObjectFree(user);
719 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
720 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to roll back" );
724 dbi_result result = dbi_conn_query(writehandle, "ROLLBACK;");
726 osrfLogError(OSRF_LOG_MARK, "%s: Error rolling back transaction", MODULENAME );
727 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error rolling back transaction" );
730 osrfHashRemove(ctx->session->userData, "xact_id");
731 jsonObject* ret = jsonNewObject(ctx->session->session_id);
732 osrfAppRespondComplete( ctx, ret );
738 int dispatchCRUDMethod ( osrfMethodContext* ctx ) {
739 if(osrfMethodVerifyContext( ctx )) {
740 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
744 osrfHash* meta = (osrfHash*) ctx->method->userData;
745 osrfHash* class_obj = osrfHashGet( meta, "class" );
749 const char* methodtype = osrfHashGet(meta, "methodtype");
750 jsonObject * obj = NULL;
752 if (!strcmp(methodtype, "create")) {
753 obj = doCreate(ctx, &err);
754 osrfAppRespondComplete( ctx, obj );
756 else if (!strcmp(methodtype, "retrieve")) {
757 obj = doRetrieve(ctx, &err);
758 osrfAppRespondComplete( ctx, obj );
760 else if (!strcmp(methodtype, "update")) {
761 obj = doUpdate(ctx, &err);
762 osrfAppRespondComplete( ctx, obj );
764 else if (!strcmp(methodtype, "delete")) {
765 obj = doDelete(ctx, &err);
766 osrfAppRespondComplete( ctx, obj );
768 else if (!strcmp(methodtype, "search")) {
770 jsonObject* where_clause;
771 jsonObject* rest_of_query;
774 where_clause = jsonObjectGetIndex( ctx->params, 1 );
775 rest_of_query = jsonObjectGetIndex( ctx->params, 2 );
777 where_clause = jsonObjectGetIndex( ctx->params, 0 );
778 rest_of_query = jsonObjectGetIndex( ctx->params, 1 );
781 obj = doFieldmapperSearch( ctx, class_obj, where_clause, rest_of_query, &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* where_clause;
799 jsonObject* rest_of_query;
801 // We use the where clause without change. But we need
802 // to massage the rest of the query, so we work with a copy
803 // of it instead of modifying the original.
805 where_clause = jsonObjectGetIndex( ctx->params, 1 );
806 rest_of_query = jsonObjectClone( jsonObjectGetIndex( ctx->params, 2 ) );
808 where_clause = jsonObjectGetIndex( ctx->params, 0 );
809 rest_of_query = jsonObjectClone( jsonObjectGetIndex( ctx->params, 1 ) );
812 if ( rest_of_query ) {
813 jsonObjectRemoveKey( rest_of_query, "select" );
814 jsonObjectRemoveKey( rest_of_query, "no_i18n" );
815 jsonObjectRemoveKey( rest_of_query, "flesh" );
816 jsonObjectRemoveKey( rest_of_query, "flesh_columns" );
818 rest_of_query = jsonNewObjectType( JSON_HASH );
821 jsonObjectSetKey( rest_of_query, "no_i18n", jsonNewBoolObject( 1 ) );
827 "{ \"%s\":[\"%s\"] }",
828 osrfHashGet( class_obj, "classname" ),
829 osrfHashGet( class_obj, "primarykey" )
833 obj = doFieldmapperSearch( ctx, class_obj, where_clause, rest_of_query, &err );
835 jsonObjectFree( rest_of_query );
839 jsonIterator* itr = jsonNewIterator( obj );
840 while ((cur = jsonIteratorNext( itr ))) {
842 if(!verifyObjectPCRUD(ctx, cur)) continue;
846 oilsFMGetObject( cur, osrfHashGet( class_obj, "primarykey" ) )
849 jsonIteratorFree(itr);
850 osrfAppRespondComplete( ctx, NULL );
853 osrfAppRespondComplete( ctx, obj );
861 static int verifyObjectClass ( osrfMethodContext* ctx, const jsonObject* param ) {
864 osrfHash* meta = (osrfHash*) ctx->method->userData;
865 osrfHash* class = osrfHashGet( meta, "class" );
867 if (!param->classname || (strcmp( osrfHashGet(class, "classname"), param->classname ))) {
869 growing_buffer* msg = buffer_init(128);
872 "%s: %s method for type %s was passed a %s",
874 osrfHashGet(meta, "methodtype"),
875 osrfHashGet(class, "classname"),
879 char* m = buffer_release(msg);
880 osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException", ctx->request, m );
888 ret = verifyObjectPCRUD( ctx, param );
896 static jsonObject* verifyUserPCRUD( osrfMethodContext* ctx ) {
897 const char* auth = jsonObjectGetString( jsonObjectGetIndex( ctx->params, 0 ) );
898 jsonObject* auth_object = jsonNewObject(auth);
899 jsonObject* user = oilsUtilsQuickReq("open-ils.auth","open-ils.auth.session.retrieve", auth_object);
900 jsonObjectFree(auth_object);
902 if (!user->classname || strcmp(user->classname, "au")) {
904 growing_buffer* msg = buffer_init(128);
907 "%s: permacrud received a bad auth token: %s",
912 char* m = buffer_release(msg);
913 osrfAppSessionStatus( ctx->session, OSRF_STATUS_UNAUTHORIZED, "osrfMethodException", ctx->request, m );
916 jsonObjectFree(user);
924 static int verifyObjectPCRUD ( osrfMethodContext* ctx, const jsonObject* obj ) {
926 dbhandle = writehandle;
928 osrfHash* meta = (osrfHash*) ctx->method->userData;
929 osrfHash* class = osrfHashGet( meta, "class" );
930 char* method_type = strdup( osrfHashGet(meta, "methodtype") );
933 if ( ( *method_type == 's' || *method_type == 'i' ) ) {
935 method_type = strdup("retrieve"); // search and id_list are equivelant to retrieve for this
936 } else if ( *method_type == 'u' || *method_type == 'd' ) {
937 fetch = 1; // MUST go to the db for the object for update and delete
940 osrfHash* pcrud = osrfHashGet( osrfHashGet(class, "permacrud"), method_type );
944 // No permacrud for this method type on this class
946 growing_buffer* msg = buffer_init(128);
949 "%s: %s on class %s has no permacrud IDL entry",
951 osrfHashGet(meta, "methodtype"),
952 osrfHashGet(class, "classname")
955 char* m = buffer_release(msg);
956 osrfAppSessionStatus( ctx->session, OSRF_STATUS_FORBIDDEN, "osrfMethodException", ctx->request, m );
963 jsonObject* user = verifyUserPCRUD( ctx );
966 int userid = atoi( oilsFMGetString( user, "id" ) );
967 jsonObjectFree(user);
969 osrfStringArray* permission = osrfHashGet(pcrud, "permission");
970 osrfStringArray* local_context = osrfHashGet(pcrud, "local_context");
971 osrfHash* foreign_context = osrfHashGet(pcrud, "foreign_context");
973 osrfStringArray* context_org_array = osrfNewStringArray(1);
976 char* pkey_value = NULL;
977 if ( str_is_true( osrfHashGet(pcrud, "global_required") ) ) {
978 osrfLogDebug( OSRF_LOG_MARK, "global-level permissions required, fetching top of the org tree" );
980 // check for perm at top of org tree
981 jsonObject* _tmp_params = jsonParseString("{\"parent_ou\":null}");
982 jsonObject* _list = doFieldmapperSearch(ctx, osrfHashGet( oilsIDL(), "aou" ),
983 _tmp_params, NULL, &err);
985 jsonObject* _tree_top = jsonObjectGetIndex(_list, 0);
988 jsonObjectFree(_tmp_params);
989 jsonObjectFree(_list);
991 growing_buffer* msg = buffer_init(128);
992 OSRF_BUFFER_ADD( msg, MODULENAME );
993 OSRF_BUFFER_ADD( msg,
994 ": Internal error, could not find the top of the org tree (parent_ou = NULL)" );
996 char* m = buffer_release(msg);
997 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, m );
1003 osrfStringArrayAdd( context_org_array, oilsFMGetString( _tree_top, "id" ) );
1004 osrfLogDebug( OSRF_LOG_MARK, "top of the org tree is %s", osrfStringArrayGetString(context_org_array, 0) );
1006 jsonObjectFree(_tmp_params);
1007 jsonObjectFree(_list);
1010 osrfLogDebug( OSRF_LOG_MARK, "global-level permissions not required, fetching context org ids" );
1011 char* pkey = osrfHashGet(class, "primarykey");
1012 jsonObject *param = NULL;
1014 if (obj->classname) {
1015 pkey_value = oilsFMGetString( obj, pkey );
1016 if (!fetch) param = jsonObjectClone(obj);
1017 osrfLogDebug( OSRF_LOG_MARK, "Object supplied, using primary key value of %s", pkey_value );
1019 pkey_value = jsonObjectToSimpleString( obj );
1021 osrfLogDebug( OSRF_LOG_MARK, "Object not supplied, using primary key value of %s and retrieving from the database", pkey_value );
1025 jsonObject* _tmp_params = single_hash( pkey, pkey_value );
1026 jsonObject* _list = doFieldmapperSearch(
1027 ctx, class, _tmp_params, NULL, &err );
1029 param = jsonObjectClone(jsonObjectGetIndex(_list, 0));
1031 jsonObjectFree(_tmp_params);
1032 jsonObjectFree(_list);
1036 osrfLogDebug( OSRF_LOG_MARK, "Object not found in the database with primary key %s of %s", pkey, pkey_value );
1038 growing_buffer* msg = buffer_init(128);
1041 "%s: no object found with primary key %s of %s",
1047 char* m = buffer_release(msg);
1048 osrfAppSessionStatus(
1050 OSRF_STATUS_INTERNALSERVERERROR,
1051 "osrfMethodException",
1057 if (pkey_value) free(pkey_value);
1062 if (local_context->size > 0) {
1063 osrfLogDebug( OSRF_LOG_MARK, "%d class-local context field(s) specified", local_context->size);
1065 char* lcontext = NULL;
1066 while ( (lcontext = osrfStringArrayGetString(local_context, i++)) ) {
1067 osrfStringArrayAdd( context_org_array, oilsFMGetString( param, lcontext ) );
1070 "adding class-local field %s (value: %s) to the context org list",
1072 osrfStringArrayGetString(context_org_array, context_org_array->size - 1)
1077 osrfStringArray* class_list;
1079 if (foreign_context) {
1080 class_list = osrfHashKeys( foreign_context );
1081 osrfLogDebug( OSRF_LOG_MARK, "%d foreign context classes(s) specified", class_list->size);
1083 if (class_list->size > 0) {
1086 char* class_name = NULL;
1087 while ( (class_name = osrfStringArrayGetString(class_list, i++)) ) {
1088 osrfHash* fcontext = osrfHashGet(foreign_context, class_name);
1092 "%d foreign context fields(s) specified for class %s",
1093 ((osrfStringArray*)osrfHashGet(fcontext,"context"))->size,
1097 char* foreign_pkey = osrfHashGet(fcontext, "field");
1098 char* foreign_pkey_value = oilsFMGetString(param, osrfHashGet(fcontext, "fkey"));
1100 jsonObject* _tmp_params = single_hash( foreign_pkey, foreign_pkey_value );
1102 jsonObject* _list = doFieldmapperSearch(
1103 ctx, osrfHashGet( oilsIDL(), class_name ), _tmp_params, NULL, &err );
1105 jsonObject* _fparam = jsonObjectClone(jsonObjectGetIndex(_list, 0));
1106 jsonObjectFree(_tmp_params);
1107 jsonObjectFree(_list);
1109 osrfStringArray* jump_list = osrfHashGet(fcontext, "jump");
1111 if (_fparam && jump_list) {
1114 while ( (flink = osrfStringArrayGetString(jump_list, k++)) && _fparam ) {
1115 free(foreign_pkey_value);
1117 osrfHash* foreign_link_hash = oilsIDLFindPath( "/%s/links/%s", _fparam->classname, flink );
1119 foreign_pkey_value = oilsFMGetString(_fparam, flink);
1120 foreign_pkey = osrfHashGet( foreign_link_hash, "key" );
1122 _tmp_params = single_hash( foreign_pkey, foreign_pkey_value );
1124 _list = doFieldmapperSearch(
1126 osrfHashGet( oilsIDL(), osrfHashGet( foreign_link_hash, "class" ) ),
1132 _fparam = jsonObjectClone(jsonObjectGetIndex(_list, 0));
1133 jsonObjectFree(_tmp_params);
1134 jsonObjectFree(_list);
1141 growing_buffer* msg = buffer_init(128);
1144 "%s: no object found with primary key %s of %s",
1150 char* m = buffer_release(msg);
1151 osrfAppSessionStatus(
1153 OSRF_STATUS_INTERNALSERVERERROR,
1154 "osrfMethodException",
1160 osrfStringArrayFree(class_list);
1161 free(foreign_pkey_value);
1162 jsonObjectFree(param);
1167 free(foreign_pkey_value);
1170 char* foreign_field = NULL;
1171 while ( (foreign_field = osrfStringArrayGetString(osrfHashGet(fcontext,"context"), j++)) ) {
1172 osrfStringArrayAdd( context_org_array, oilsFMGetString( _fparam, foreign_field ) );
1175 "adding foreign class %s field %s (value: %s) to the context org list",
1178 osrfStringArrayGetString(context_org_array, context_org_array->size - 1)
1182 jsonObjectFree(_fparam);
1185 osrfStringArrayFree(class_list);
1189 jsonObjectFree(param);
1192 char* context_org = NULL;
1196 if (permission->size == 0) {
1197 osrfLogDebug( OSRF_LOG_MARK, "No permission specified for this action, passing through" );
1202 while ( (perm = osrfStringArrayGetString(permission, i++)) ) {
1204 while ( (context_org = osrfStringArrayGetString(context_org_array, j++)) ) {
1210 "Checking object permission [%s] for user %d on object %s (class %s) at org %d",
1214 osrfHashGet(class, "classname"),
1218 result = dbi_conn_queryf(
1220 "SELECT permission.usr_has_object_perm(%d, '%s', '%s', '%s', %d) AS has_perm;",
1223 osrfHashGet(class, "classname"),
1231 "Recieved a result for object permission [%s] for user %d on object %s (class %s) at org %d",
1235 osrfHashGet(class, "classname"),
1239 if (dbi_result_first_row(result)) {
1240 jsonObject* return_val = oilsMakeJSONFromResult( result );
1241 const char* has_perm = jsonObjectGetString( jsonObjectGetKeyConst(return_val, "has_perm") );
1245 "Status of object permission [%s] for user %d on object %s (class %s) at org %d is %s",
1249 osrfHashGet(class, "classname"),
1254 if ( *has_perm == 't' ) OK = 1;
1255 jsonObjectFree(return_val);
1258 dbi_result_free(result);
1263 osrfLogDebug( OSRF_LOG_MARK, "Checking non-object permission [%s] for user %d at org %d", perm, userid, atoi(context_org) );
1264 result = dbi_conn_queryf(
1266 "SELECT permission.usr_has_perm(%d, '%s', %d) AS has_perm;",
1273 osrfLogDebug( OSRF_LOG_MARK, "Received a result for permission [%s] for user %d at org %d",
1274 perm, userid, atoi(context_org) );
1275 if ( dbi_result_first_row(result) ) {
1276 jsonObject* return_val = oilsMakeJSONFromResult( result );
1277 const char* has_perm = jsonObjectGetString( jsonObjectGetKeyConst(return_val, "has_perm") );
1278 osrfLogDebug( OSRF_LOG_MARK, "Status of permission [%s] for user %d at org %d is [%s]",
1279 perm, userid, atoi(context_org), has_perm );
1280 if ( *has_perm == 't' ) OK = 1;
1281 jsonObjectFree(return_val);
1284 dbi_result_free(result);
1292 if (pkey_value) free(pkey_value);
1293 osrfStringArrayFree(context_org_array);
1299 Utility function: create a JSON_HASH with a single key/value pair.
1300 This function is equivalent to:
1302 jsonParseStringFmt( "{\"%s\":\"%s\"}", key, value )
1304 ...but faster because it doesn't create and parse a JSON string.
1306 static jsonObject* single_hash( const char* key, const char* value ) {
1308 if( ! key ) key = "";
1309 if( ! value ) value = "";
1311 jsonObject* hash = jsonNewObjectType( JSON_HASH );
1312 jsonObjectSetKey( hash, key, jsonNewObject( value ) );
1318 static jsonObject* doCreate(osrfMethodContext* ctx, int* err ) {
1320 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
1322 jsonObject* target = jsonObjectGetIndex( ctx->params, 1 );
1323 jsonObject* options = jsonObjectGetIndex( ctx->params, 2 );
1325 jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
1326 jsonObject* options = jsonObjectGetIndex( ctx->params, 1 );
1329 if (!verifyObjectClass(ctx, target)) {
1334 osrfLogDebug( OSRF_LOG_MARK, "Object seems to be of the correct type" );
1336 char* trans_id = NULL;
1337 if( ctx->session && ctx->session->userData )
1338 trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
1341 osrfLogError( OSRF_LOG_MARK, "No active transaction -- required for CREATE" );
1343 osrfAppSessionStatus(
1345 OSRF_STATUS_BADREQUEST,
1346 "osrfMethodException",
1348 "No active transaction -- required for CREATE"
1354 // The following test is harmless but redundant. If a class is
1355 // readonly, we don't register a create method for it.
1356 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
1357 osrfAppSessionStatus(
1359 OSRF_STATUS_BADREQUEST,
1360 "osrfMethodException",
1362 "Cannot INSERT readonly class"
1368 // Set the last_xact_id
1369 int index = oilsIDL_ntop( target->classname, "last_xact_id" );
1371 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
1372 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
1375 osrfLogDebug( OSRF_LOG_MARK, "There is a transaction running..." );
1377 dbhandle = writehandle;
1379 osrfHash* fields = osrfHashGet(meta, "fields");
1380 char* pkey = osrfHashGet(meta, "primarykey");
1381 char* seq = osrfHashGet(meta, "sequence");
1383 growing_buffer* table_buf = buffer_init(128);
1384 growing_buffer* col_buf = buffer_init(128);
1385 growing_buffer* val_buf = buffer_init(128);
1387 OSRF_BUFFER_ADD(table_buf, "INSERT INTO ");
1388 OSRF_BUFFER_ADD(table_buf, osrfHashGet(meta, "tablename"));
1389 OSRF_BUFFER_ADD_CHAR( col_buf, '(' );
1390 buffer_add(val_buf,"VALUES (");
1396 osrfStringArray* field_list = osrfHashKeys( fields );
1397 while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
1399 osrfHash* field = osrfHashGet( fields, field_name );
1401 if( str_is_true( osrfHashGet( field, "virtual" ) ) )
1404 const jsonObject* field_object = oilsFMGetObject( target, field_name );
1407 if (field_object && field_object->classname) {
1408 value = oilsFMGetString(
1410 (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
1413 value = jsonObjectToSimpleString( field_object );
1420 OSRF_BUFFER_ADD_CHAR( col_buf, ',' );
1421 OSRF_BUFFER_ADD_CHAR( val_buf, ',' );
1424 buffer_add(col_buf, field_name);
1426 if (!field_object || field_object->type == JSON_NULL) {
1427 buffer_add( val_buf, "DEFAULT" );
1429 } else if ( !strcmp(get_primitive( field ), "number") ) {
1430 const char* numtype = get_datatype( field );
1431 if ( !strcmp( numtype, "INT8") ) {
1432 buffer_fadd( val_buf, "%lld", atoll(value) );
1434 } else if ( !strcmp( numtype, "INT") ) {
1435 buffer_fadd( val_buf, "%d", atoi(value) );
1437 } else if ( !strcmp( numtype, "NUMERIC") ) {
1438 buffer_fadd( val_buf, "%f", atof(value) );
1441 if ( dbi_conn_quote_string(writehandle, &value) ) {
1442 OSRF_BUFFER_ADD( val_buf, value );
1445 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
1446 osrfAppSessionStatus(
1448 OSRF_STATUS_INTERNALSERVERERROR,
1449 "osrfMethodException",
1451 "Error quoting string -- please see the error log for more details"
1454 buffer_free(table_buf);
1455 buffer_free(col_buf);
1456 buffer_free(val_buf);
1467 OSRF_BUFFER_ADD_CHAR( col_buf, ')' );
1468 OSRF_BUFFER_ADD_CHAR( val_buf, ')' );
1470 char* table_str = buffer_release(table_buf);
1471 char* col_str = buffer_release(col_buf);
1472 char* val_str = buffer_release(val_buf);
1473 growing_buffer* sql = buffer_init(128);
1474 buffer_fadd( sql, "%s %s %s;", table_str, col_str, val_str );
1479 char* query = buffer_release(sql);
1481 osrfLogDebug(OSRF_LOG_MARK, "%s: Insert SQL [%s]", MODULENAME, query);
1484 dbi_result result = dbi_conn_query(writehandle, query);
1486 jsonObject* obj = NULL;
1489 obj = jsonNewObject(NULL);
1492 "%s ERROR inserting %s object using query [%s]",
1494 osrfHashGet(meta, "fieldmapper"),
1497 osrfAppSessionStatus(
1499 OSRF_STATUS_INTERNALSERVERERROR,
1500 "osrfMethodException",
1502 "INSERT error -- please see the error log for more details"
1507 char* id = oilsFMGetString(target, pkey);
1509 unsigned long long new_id = dbi_conn_sequence_last(writehandle, seq);
1510 growing_buffer* _id = buffer_init(10);
1511 buffer_fadd(_id, "%lld", new_id);
1512 id = buffer_release(_id);
1515 // Find quietness specification, if present
1516 const char* quiet_str = NULL;
1518 const jsonObject* quiet_obj = jsonObjectGetKeyConst( options, "quiet" );
1520 quiet_str = jsonObjectGetString( quiet_obj );
1523 if( str_is_true( quiet_str ) ) { // if quietness is specified
1524 obj = jsonNewObject(id);
1528 jsonObject* where_clause = jsonNewObjectType( JSON_HASH );
1529 jsonObjectSetKey( where_clause, pkey, jsonNewObject(id) );
1531 jsonObject* list = doFieldmapperSearch( ctx, meta, where_clause, NULL, err );
1533 jsonObjectFree( where_clause );
1538 obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
1541 jsonObjectFree( list );
1554 static jsonObject* doRetrieve(osrfMethodContext* ctx, int* err ) {
1564 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
1566 const char* id = jsonObjectGetString(jsonObjectGetIndex(ctx->params, id_pos));
1567 jsonObject* order_hash = jsonObjectGetIndex(ctx->params, order_pos);
1571 "%s retrieving %s object with primary key value of %s",
1573 osrfHashGet(meta, "fieldmapper"),
1577 jsonObject* key_param = jsonNewObjectType( JSON_HASH );
1580 osrfHashGet( meta, "primarykey" ),
1581 jsonObjectClone( jsonObjectGetIndex(ctx->params, id_pos) )
1584 jsonObject* list = doFieldmapperSearch( ctx, meta, key_param, order_hash, err );
1587 jsonObjectFree( key_param );
1591 jsonObject* obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
1593 jsonObjectFree( list );
1594 jsonObjectFree( key_param );
1597 if(!verifyObjectPCRUD(ctx, obj)) {
1598 jsonObjectFree(obj);
1601 growing_buffer* msg = buffer_init(128);
1602 OSRF_BUFFER_ADD( msg, MODULENAME );
1603 OSRF_BUFFER_ADD( msg, ": Insufficient permissions to retrieve object" );
1605 char* m = buffer_release(msg);
1606 osrfAppSessionStatus( ctx->session, OSRF_STATUS_NOTALLOWED, "osrfMethodException", ctx->request, m );
1617 static char* jsonNumberToDBString ( osrfHash* field, const jsonObject* value ) {
1618 growing_buffer* val_buf = buffer_init(32);
1619 const char* numtype = get_datatype( field );
1621 if ( !strncmp( numtype, "INT", 3 ) ) {
1622 if (value->type == JSON_NUMBER)
1623 //buffer_fadd( val_buf, "%ld", (long)jsonObjectGetNumber(value) );
1624 buffer_fadd( val_buf, jsonObjectGetString( value ) );
1626 //const char* val_str = jsonObjectGetString( value );
1627 //buffer_fadd( val_buf, "%ld", atol(val_str) );
1628 buffer_fadd( val_buf, jsonObjectGetString( value ) );
1631 } else if ( !strcmp( numtype, "NUMERIC" ) ) {
1632 if (value->type == JSON_NUMBER)
1633 //buffer_fadd( val_buf, "%f", jsonObjectGetNumber(value) );
1634 buffer_fadd( val_buf, jsonObjectGetString( value ) );
1636 //const char* val_str = jsonObjectGetString( value );
1637 //buffer_fadd( val_buf, "%f", atof(val_str) );
1638 buffer_fadd( val_buf, jsonObjectGetString( value ) );
1642 // Presumably this was really intended ot be a string, so quote it
1643 char* str = jsonObjectToSimpleString( value );
1644 if ( dbi_conn_quote_string(dbhandle, &str) ) {
1645 OSRF_BUFFER_ADD( val_buf, str );
1648 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, str);
1650 buffer_free(val_buf);
1655 return buffer_release(val_buf);
1658 static char* searchINPredicate (const char* class, osrfHash* field,
1659 jsonObject* node, const char* op, osrfMethodContext* ctx ) {
1660 growing_buffer* sql_buf = buffer_init(32);
1666 osrfHashGet(field, "name")
1670 buffer_add(sql_buf, "IN (");
1671 } else if (!(strcasecmp(op,"not in"))) {
1672 buffer_add(sql_buf, "NOT IN (");
1674 buffer_add(sql_buf, "IN (");
1677 if (node->type == JSON_HASH) {
1678 // subquery predicate
1679 char* subpred = SELECT(
1681 jsonObjectGetKey( node, "select" ),
1682 jsonObjectGetKey( node, "from" ),
1683 jsonObjectGetKey( node, "where" ),
1684 jsonObjectGetKey( node, "having" ),
1685 jsonObjectGetKey( node, "order_by" ),
1686 jsonObjectGetKey( node, "limit" ),
1687 jsonObjectGetKey( node, "offset" ),
1692 buffer_add(sql_buf, subpred);
1695 buffer_free( sql_buf );
1699 } else if (node->type == JSON_ARRAY) {
1700 // literal value list
1701 int in_item_index = 0;
1702 int in_item_first = 1;
1703 const jsonObject* in_item;
1704 while ( (in_item = jsonObjectGetIndex(node, in_item_index++)) ) {
1709 buffer_add(sql_buf, ", ");
1712 if ( in_item->type != JSON_STRING && in_item->type != JSON_NUMBER ) {
1713 osrfLogError(OSRF_LOG_MARK, "%s: Expected string or number within IN list; found %s",
1714 MODULENAME, json_type( in_item->type ) );
1715 buffer_free(sql_buf);
1719 // Append the literal value -- quoted if not a number
1720 if ( JSON_NUMBER == in_item->type ) {
1721 char* val = jsonNumberToDBString( field, in_item );
1722 OSRF_BUFFER_ADD( sql_buf, val );
1725 } else if ( !strcmp( get_primitive( field ), "number") ) {
1726 char* val = jsonNumberToDBString( field, in_item );
1727 OSRF_BUFFER_ADD( sql_buf, val );
1731 char* key_string = jsonObjectToSimpleString(in_item);
1732 if ( dbi_conn_quote_string(dbhandle, &key_string) ) {
1733 OSRF_BUFFER_ADD( sql_buf, key_string );
1736 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string);
1738 buffer_free(sql_buf);
1744 if( in_item_first ) {
1745 osrfLogError(OSRF_LOG_MARK, "%s: Empty IN list", MODULENAME );
1746 buffer_free( sql_buf );
1750 osrfLogError(OSRF_LOG_MARK, "%s: Expected object or array for IN clause; found %s",
1751 MODULENAME, json_type( node->type ) );
1752 buffer_free(sql_buf);
1756 OSRF_BUFFER_ADD_CHAR( sql_buf, ')' );
1758 return buffer_release(sql_buf);
1761 // Receive a JSON_ARRAY representing a function call. The first
1762 // entry in the array is the function name. The rest are parameters.
1763 static char* searchValueTransform( const jsonObject* array ) {
1765 if( array->size < 1 ) {
1766 osrfLogError(OSRF_LOG_MARK, "%s: Empty array for value transform", MODULENAME);
1770 // Get the function name
1771 jsonObject* func_item = jsonObjectGetIndex( array, 0 );
1772 if( func_item->type != JSON_STRING ) {
1773 osrfLogError(OSRF_LOG_MARK, "%s: Error: expected function name, found %s",
1774 MODULENAME, json_type( func_item->type ) );
1778 growing_buffer* sql_buf = buffer_init(32);
1780 OSRF_BUFFER_ADD( sql_buf, jsonObjectGetString( func_item ) );
1781 OSRF_BUFFER_ADD( sql_buf, "( " );
1783 // Get the parameters
1784 int func_item_index = 1; // We already grabbed the zeroth entry
1785 while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1787 // Add a separator comma, if we need one
1788 if( func_item_index > 2 )
1789 buffer_add( sql_buf, ", " );
1791 // Add the current parameter
1792 if (func_item->type == JSON_NULL) {
1793 buffer_add( sql_buf, "NULL" );
1795 char* val = jsonObjectToSimpleString(func_item);
1796 if ( dbi_conn_quote_string(dbhandle, &val) ) {
1797 OSRF_BUFFER_ADD( sql_buf, val );
1800 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1801 buffer_free(sql_buf);
1808 buffer_add( sql_buf, " )" );
1810 return buffer_release(sql_buf);
1813 static char* searchFunctionPredicate (const char* class, osrfHash* field,
1814 const jsonObject* node, const char* op) {
1816 if( ! is_good_operator( op ) ) {
1817 osrfLogError( OSRF_LOG_MARK, "%s: Invalid operator [%s]", MODULENAME, op );
1821 char* val = searchValueTransform(node);
1825 growing_buffer* sql_buf = buffer_init(32);
1830 osrfHashGet(field, "name"),
1837 return buffer_release(sql_buf);
1840 // class is a class name
1841 // field is a field definition as stored in the IDL
1842 // node comes from the method parameter, and may represent an entry in the SELECT list
1843 static char* searchFieldTransform (const char* class, osrfHash* field, const jsonObject* node) {
1844 growing_buffer* sql_buf = buffer_init(32);
1846 const char* field_transform = jsonObjectGetString( jsonObjectGetKeyConst( node, "transform" ) );
1847 const char* transform_subcolumn = jsonObjectGetString( jsonObjectGetKeyConst( node, "result_field" ) );
1849 if(transform_subcolumn) {
1850 if( ! is_identifier( transform_subcolumn ) ) {
1851 osrfLogError( OSRF_LOG_MARK, "%s: Invalid subfield name: \"%s\"\n",
1852 MODULENAME, transform_subcolumn );
1853 buffer_free( sql_buf );
1856 OSRF_BUFFER_ADD_CHAR( sql_buf, '(' ); // enclose transform in parentheses
1859 if (field_transform) {
1861 if( ! is_identifier( field_transform ) ) {
1862 osrfLogError( OSRF_LOG_MARK, "%s: Expected function name, found \"%s\"\n",
1863 MODULENAME, field_transform );
1864 buffer_free( sql_buf );
1868 buffer_fadd( sql_buf, "%s(\"%s\".%s", field_transform, class, osrfHashGet(field, "name"));
1869 const jsonObject* array = jsonObjectGetKeyConst( node, "params" );
1872 if( array->type != JSON_ARRAY ) {
1873 osrfLogError( OSRF_LOG_MARK,
1874 "%s: Expected JSON_ARRAY for function params; found %s",
1875 MODULENAME, json_type( array->type ) );
1876 buffer_free( sql_buf );
1879 int func_item_index = 0;
1880 jsonObject* func_item;
1881 while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1883 char* val = jsonObjectToSimpleString(func_item);
1886 buffer_add( sql_buf, ",NULL" );
1887 } else if ( dbi_conn_quote_string(dbhandle, &val) ) {
1888 OSRF_BUFFER_ADD_CHAR( sql_buf, ',' );
1889 OSRF_BUFFER_ADD( sql_buf, val );
1891 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1893 buffer_free(sql_buf);
1900 buffer_add( sql_buf, " )" );
1903 buffer_fadd( sql_buf, "\"%s\".%s", class, osrfHashGet(field, "name"));
1906 if (transform_subcolumn)
1907 buffer_fadd( sql_buf, ").\"%s\"", transform_subcolumn );
1909 return buffer_release(sql_buf);
1912 static char* searchFieldTransformPredicate (const char* class, osrfHash* field,
1913 const jsonObject* node, const char* op ) {
1915 if( ! is_good_operator( op ) ) {
1916 osrfLogError(OSRF_LOG_MARK, "%s: Error: Invalid operator %s", MODULENAME, op);
1920 char* field_transform = searchFieldTransform( class, field, node );
1921 if( ! field_transform )
1924 int extra_parens = 0; // boolean
1926 const jsonObject* value_obj = jsonObjectGetKeyConst( node, "value" );
1927 if ( ! value_obj ) {
1928 value = searchWHERE( node, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN, NULL );
1930 osrfLogError(OSRF_LOG_MARK, "%s: Error building condition for field transform", MODULENAME);
1931 free(field_transform);
1935 } else if ( value_obj->type == JSON_ARRAY ) {
1936 value = searchValueTransform( value_obj );
1938 osrfLogError(OSRF_LOG_MARK, "%s: Error building value transform for field transform", MODULENAME);
1939 free( field_transform );
1942 } else if ( value_obj->type == JSON_HASH ) {
1943 value = searchWHERE( value_obj, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN, NULL );
1945 osrfLogError(OSRF_LOG_MARK, "%s: Error building predicate for field transform", MODULENAME);
1946 free(field_transform);
1950 } else if ( value_obj->type == JSON_NUMBER ) {
1951 value = jsonNumberToDBString( field, value_obj );
1952 } else if ( value_obj->type == JSON_NULL ) {
1953 osrfLogError(OSRF_LOG_MARK, "%s: Error building predicate for field transform: null value", MODULENAME);
1954 free(field_transform);
1956 } else if ( value_obj->type == JSON_BOOL ) {
1957 osrfLogError(OSRF_LOG_MARK, "%s: Error building predicate for field transform: boolean value", MODULENAME);
1958 free(field_transform);
1961 if ( !strcmp( get_primitive( field ), "number") ) {
1962 value = jsonNumberToDBString( field, value_obj );
1964 value = jsonObjectToSimpleString( value_obj );
1965 if ( !dbi_conn_quote_string(dbhandle, &value) ) {
1966 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, value);
1968 free(field_transform);
1974 const char* left_parens = "";
1975 const char* right_parens = "";
1977 if( extra_parens ) {
1982 growing_buffer* sql_buf = buffer_init(32);
1986 "%s%s %s %s %s %s%s",
1997 free(field_transform);
1999 return buffer_release(sql_buf);
2002 static char* searchSimplePredicate (const char* op, const char* class,
2003 osrfHash* field, const jsonObject* node) {
2005 if( ! is_good_operator( op ) ) {
2006 osrfLogError( OSRF_LOG_MARK, "%s: Invalid operator [%s]", MODULENAME, op );
2012 // Get the value to which we are comparing the specified column
2013 if (node->type != JSON_NULL) {
2014 if ( node->type == JSON_NUMBER ) {
2015 val = jsonNumberToDBString( field, node );
2016 } else if ( !strcmp( get_primitive( field ), "number" ) ) {
2017 val = jsonNumberToDBString( field, node );
2019 val = jsonObjectToSimpleString(node);
2024 if( JSON_NUMBER != node->type && strcmp( get_primitive( field ), "number") ) {
2025 // Value is not numeric; enclose it in quotes
2026 if ( !dbi_conn_quote_string( dbhandle, &val ) ) {
2027 osrfLogError( OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val );
2033 // Compare to a null value
2034 val = strdup( "NULL" );
2035 if (strcmp( op, "=" ))
2041 growing_buffer* sql_buf = buffer_init(32);
2042 buffer_fadd( sql_buf, "\"%s\".%s %s %s", class, osrfHashGet(field, "name"), op, val );
2043 char* pred = buffer_release( sql_buf );
2050 static char* searchBETWEENPredicate (const char* class, osrfHash* field, const jsonObject* node) {
2052 const jsonObject* x_node = jsonObjectGetIndex( node, 0 );
2053 const jsonObject* y_node = jsonObjectGetIndex( node, 1 );
2055 if( NULL == y_node ) {
2056 osrfLogError( OSRF_LOG_MARK, "%s: Not enough operands for BETWEEN operator", MODULENAME );
2059 else if( NULL != jsonObjectGetIndex( node, 2 ) ) {
2060 osrfLogError( OSRF_LOG_MARK, "%s: Too many operands for BETWEEN operator", MODULENAME );
2067 if ( !strcmp( get_primitive( field ), "number") ) {
2068 x_string = jsonNumberToDBString(field, x_node);
2069 y_string = jsonNumberToDBString(field, y_node);
2072 x_string = jsonObjectToSimpleString(x_node);
2073 y_string = jsonObjectToSimpleString(y_node);
2074 if ( !(dbi_conn_quote_string(dbhandle, &x_string) && dbi_conn_quote_string(dbhandle, &y_string)) ) {
2075 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key strings [%s] and [%s]",
2076 MODULENAME, x_string, y_string);
2083 growing_buffer* sql_buf = buffer_init(32);
2084 buffer_fadd( sql_buf, "%s BETWEEN %s AND %s", osrfHashGet(field, "name"), x_string, y_string );
2088 return buffer_release(sql_buf);
2091 static char* searchPredicate ( const char* class, osrfHash* field,
2092 jsonObject* node, osrfMethodContext* ctx ) {
2095 if (node->type == JSON_ARRAY) { // equality IN search
2096 pred = searchINPredicate( class, field, node, NULL, ctx );
2097 } else if (node->type == JSON_HASH) { // other search
2098 jsonIterator* pred_itr = jsonNewIterator( node );
2099 if( !jsonIteratorHasNext( pred_itr ) ) {
2100 osrfLogError( OSRF_LOG_MARK, "%s: Empty predicate for field \"%s\"",
2101 MODULENAME, osrfHashGet(field, "name") );
2103 jsonObject* pred_node = jsonIteratorNext( pred_itr );
2105 // Verify that there are no additional predicates
2106 if( jsonIteratorHasNext( pred_itr ) ) {
2107 osrfLogError( OSRF_LOG_MARK, "%s: Multiple predicates for field \"%s\"",
2108 MODULENAME, osrfHashGet(field, "name") );
2109 } else if ( !(strcasecmp( pred_itr->key,"between" )) )
2110 pred = searchBETWEENPredicate( class, field, pred_node );
2111 else if ( !(strcasecmp( pred_itr->key,"in" )) || !(strcasecmp( pred_itr->key,"not in" )) )
2112 pred = searchINPredicate( class, field, pred_node, pred_itr->key, ctx );
2113 else if ( pred_node->type == JSON_ARRAY )
2114 pred = searchFunctionPredicate( class, field, pred_node, pred_itr->key );
2115 else if ( pred_node->type == JSON_HASH )
2116 pred = searchFieldTransformPredicate( class, field, pred_node, pred_itr->key );
2118 pred = searchSimplePredicate( pred_itr->key, class, field, pred_node );
2120 jsonIteratorFree(pred_itr);
2122 } else if (node->type == JSON_NULL) { // IS NULL search
2123 growing_buffer* _p = buffer_init(64);
2126 "\"%s\".%s IS NULL",
2128 osrfHashGet(field, "name")
2130 pred = buffer_release(_p);
2131 } else { // equality search
2132 pred = searchSimplePredicate( "=", class, field, node );
2151 field : call_number,
2167 static char* searchJOIN ( const jsonObject* join_hash, osrfHash* leftmeta ) {
2169 const jsonObject* working_hash;
2170 jsonObject* freeable_hash = NULL;
2172 if (join_hash->type == JSON_STRING) {
2173 // create a wrapper around a copy of the original
2174 const char* _tmp = jsonObjectGetString( join_hash );
2175 freeable_hash = jsonNewObjectType(JSON_HASH);
2176 jsonObjectSetKey(freeable_hash, _tmp, NULL);
2177 working_hash = freeable_hash;
2180 if( join_hash->type != JSON_HASH ) {
2183 "%s: JOIN failed; expected JSON object type not found",
2188 working_hash = join_hash;
2191 growing_buffer* join_buf = buffer_init(128);
2192 const char* leftclass = osrfHashGet(leftmeta, "classname");
2194 jsonObject* snode = NULL;
2195 jsonIterator* search_itr = jsonNewIterator( working_hash );
2197 while ( (snode = jsonIteratorNext( search_itr )) ) {
2198 const char* class = search_itr->key;
2199 osrfHash* idlClass = osrfHashGet( oilsIDL(), class );
2203 "%s: JOIN failed. No class \"%s\" defined in IDL",
2207 jsonIteratorFree( search_itr );
2208 buffer_free( join_buf );
2210 jsonObjectFree( freeable_hash );
2214 const char* fkey = jsonObjectGetString( jsonObjectGetKeyConst( snode, "fkey" ) );
2215 const char* field = jsonObjectGetString( jsonObjectGetKeyConst( snode, "field" ) );
2217 if (field && !fkey) {
2218 // Look up the corresponding join column in the IDL.
2219 // The link must be defined in the child table,
2220 // and point to the right parent table.
2221 osrfHash* idl_link = (osrfHash*) oilsIDLFindPath( "/%s/links/%s", class, field );
2222 const char* reltype = NULL;
2223 const char* other_class = NULL;
2224 reltype = osrfHashGet( idl_link, "reltype" );
2225 if( reltype && strcmp( reltype, "has_many" ) )
2226 other_class = osrfHashGet( idl_link, "class" );
2227 if( other_class && !strcmp( other_class, leftclass ) )
2228 fkey = osrfHashGet( idl_link, "key" );
2232 "%s: JOIN failed. No link defined from %s.%s to %s",
2238 buffer_free(join_buf);
2240 jsonObjectFree(freeable_hash);
2241 jsonIteratorFree(search_itr);
2245 } else if (!field && fkey) {
2246 // Look up the corresponding join column in the IDL.
2247 // The link must be defined in the child table,
2248 // and point to the right parent table.
2249 osrfHash* idl_link = (osrfHash*) oilsIDLFindPath( "/%s/links/%s", leftclass, fkey );
2250 const char* reltype = NULL;
2251 const char* other_class = NULL;
2252 reltype = osrfHashGet( idl_link, "reltype" );
2253 if( reltype && strcmp( reltype, "has_many" ) )
2254 other_class = osrfHashGet( idl_link, "class" );
2255 if( other_class && !strcmp( other_class, class ) )
2256 field = osrfHashGet( idl_link, "key" );
2260 "%s: JOIN failed. No link defined from %s.%s to %s",
2266 buffer_free(join_buf);
2268 jsonObjectFree(freeable_hash);
2269 jsonIteratorFree(search_itr);
2273 } else if (!field && !fkey) {
2274 osrfHash* _links = oilsIDL_links( leftclass );
2276 // For each link defined for the left class:
2277 // see if the link references the joined class
2278 osrfHashIterator* itr = osrfNewHashIterator( _links );
2279 osrfHash* curr_link = NULL;
2280 while( (curr_link = osrfHashIteratorNext( itr ) ) ) {
2281 const char* other_class = osrfHashGet( curr_link, "class" );
2282 if( other_class && !strcmp( other_class, class ) ) {
2284 // In the IDL, the parent class doesn't know then names of the child
2285 // columns that are pointing to it, so don't use that end of the link
2286 const char* reltype = osrfHashGet( curr_link, "reltype" );
2287 if( reltype && strcmp( reltype, "has_many" ) ) {
2288 // Found a link between the classes
2289 fkey = osrfHashIteratorKey( itr );
2290 field = osrfHashGet( curr_link, "key" );
2295 osrfHashIteratorFree( itr );
2297 if (!field || !fkey) {
2298 // Do another such search, with the classes reversed
2299 _links = oilsIDL_links( class );
2301 // For each link defined for the joined class:
2302 // see if the link references the left class
2303 osrfHashIterator* itr = osrfNewHashIterator( _links );
2304 osrfHash* curr_link = NULL;
2305 while( (curr_link = osrfHashIteratorNext( itr ) ) ) {
2306 const char* other_class = osrfHashGet( curr_link, "class" );
2307 if( other_class && !strcmp( other_class, leftclass ) ) {
2309 // In the IDL, the parent class doesn't know then names of the child
2310 // columns that are pointing to it, so don't use that end of the link
2311 const char* reltype = osrfHashGet( curr_link, "reltype" );
2312 if( reltype && strcmp( reltype, "has_many" ) ) {
2313 // Found a link between the classes
2314 field = osrfHashIteratorKey( itr );
2315 fkey = osrfHashGet( curr_link, "key" );
2320 osrfHashIteratorFree( itr );
2323 if (!field || !fkey) {
2326 "%s: JOIN failed. No link defined between %s and %s",
2331 buffer_free(join_buf);
2333 jsonObjectFree(freeable_hash);
2334 jsonIteratorFree(search_itr);
2340 const char* type = jsonObjectGetString( jsonObjectGetKeyConst( snode, "type" ) );
2342 if ( !strcasecmp(type,"left") ) {
2343 buffer_add(join_buf, " LEFT JOIN");
2344 } else if ( !strcasecmp(type,"right") ) {
2345 buffer_add(join_buf, " RIGHT JOIN");
2346 } else if ( !strcasecmp(type,"full") ) {
2347 buffer_add(join_buf, " FULL JOIN");
2349 buffer_add(join_buf, " INNER JOIN");
2352 buffer_add(join_buf, " INNER JOIN");
2355 char* table = getSourceDefinition(idlClass);
2357 jsonIteratorFree( search_itr );
2358 buffer_free( join_buf );
2360 jsonObjectFree( freeable_hash );
2364 buffer_fadd(join_buf, " %s AS \"%s\" ON ( \"%s\".%s = \"%s\".%s",
2365 table, class, class, field, leftclass, fkey);
2368 const jsonObject* filter = jsonObjectGetKeyConst( snode, "filter" );
2370 const char* filter_op = jsonObjectGetString( jsonObjectGetKeyConst( snode, "filter_op" ) );
2371 if ( filter_op && !strcasecmp("or",filter_op) ) {
2372 buffer_add( join_buf, " OR " );
2374 buffer_add( join_buf, " AND " );
2377 char* jpred = searchWHERE( filter, idlClass, AND_OP_JOIN, NULL );
2379 OSRF_BUFFER_ADD_CHAR( join_buf, ' ' );
2380 OSRF_BUFFER_ADD( join_buf, jpred );
2385 "%s: JOIN failed. Invalid conditional expression.",
2388 jsonIteratorFree( search_itr );
2389 buffer_free( join_buf );
2391 jsonObjectFree( freeable_hash );
2396 buffer_add(join_buf, " ) ");
2398 const jsonObject* join_filter = jsonObjectGetKeyConst( snode, "join" );
2400 char* jpred = searchJOIN( join_filter, idlClass );
2402 OSRF_BUFFER_ADD_CHAR( join_buf, ' ' );
2403 OSRF_BUFFER_ADD( join_buf, jpred );
2406 osrfLogError( OSRF_LOG_MARK, "%s: Invalid nested join.", MODULENAME );
2407 jsonIteratorFree( search_itr );
2408 buffer_free( join_buf );
2410 jsonObjectFree( freeable_hash );
2417 jsonObjectFree(freeable_hash);
2418 jsonIteratorFree(search_itr);
2420 return buffer_release(join_buf);
2425 { +class : { -or|-and : { field : { op : value }, ... } ... }, ... }
2426 { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }
2427 [ { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }, ... ]
2429 Generate code to express a set of conditions, as for a WHERE clause. Parameters:
2431 search_hash is the JSON expression of the conditions.
2432 meta is the class definition from the IDL, for the relevant table.
2433 opjoin_type indicates whether multiple conditions, if present, should be
2434 connected by AND or OR.
2435 osrfMethodContext is loaded with all sorts of stuff, but all we do with it here is
2436 to pass it to other functions -- and all they do with it is to use the session
2437 and request members to send error messages back to the client.
2441 static char* searchWHERE ( const jsonObject* search_hash, osrfHash* meta, int opjoin_type, osrfMethodContext* ctx ) {
2445 "%s: Entering searchWHERE; search_hash addr = %p, meta addr = %p, opjoin_type = %d, ctx addr = %p",
2453 growing_buffer* sql_buf = buffer_init(128);
2455 jsonObject* node = NULL;
2458 if ( search_hash->type == JSON_ARRAY ) {
2459 osrfLogDebug(OSRF_LOG_MARK, "%s: In WHERE clause, condition type is JSON_ARRAY", MODULENAME);
2460 jsonIterator* search_itr = jsonNewIterator( search_hash );
2461 if( !jsonIteratorHasNext( search_itr ) ) {
2464 "%s: Invalid predicate structure: empty JSON array",
2467 jsonIteratorFree( search_itr );
2468 buffer_free( sql_buf );
2472 while ( (node = jsonIteratorNext( search_itr )) ) {
2476 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
2477 else buffer_add(sql_buf, " AND ");
2480 char* subpred = searchWHERE( node, meta, opjoin_type, ctx );
2482 buffer_fadd(sql_buf, "( %s )", subpred);
2485 jsonIteratorFree( search_itr );
2486 buffer_free( sql_buf );
2490 jsonIteratorFree(search_itr);
2492 } else if ( search_hash->type == JSON_HASH ) {
2493 osrfLogDebug(OSRF_LOG_MARK, "%s: In WHERE clause, condition type is JSON_HASH", MODULENAME);
2494 jsonIterator* search_itr = jsonNewIterator( search_hash );
2495 if( !jsonIteratorHasNext( search_itr ) ) {
2498 "%s: Invalid predicate structure: empty JSON object",
2501 jsonIteratorFree( search_itr );
2502 buffer_free( sql_buf );
2506 while ( (node = jsonIteratorNext( search_itr )) ) {
2511 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
2512 else buffer_add(sql_buf, " AND ");
2515 if ( '+' == search_itr->key[ 0 ] ) {
2516 if ( node->type == JSON_STRING ) {
2517 // Intended purpose; to allow reference to a Boolean column
2519 // Verify that the class alias is not empty
2520 if( '\0' == search_itr->key[ 1 ] ) {
2523 "%s: Table alias is empty",
2526 jsonIteratorFree( search_itr );
2527 buffer_free( sql_buf );
2531 // Verify that the string looks like an identifier.
2532 const char* subpred = jsonObjectGetString( node );
2533 if( ! is_identifier( subpred ) ) {
2536 "%s: Invalid boolean identifier in WHERE clause: \"%s\"",
2540 jsonIteratorFree( search_itr );
2541 buffer_free( sql_buf );
2545 buffer_fadd(sql_buf, " \"%s\".%s ", search_itr->key + 1, subpred);
2547 char* subpred = searchWHERE( node, osrfHashGet( oilsIDL(), search_itr->key + 1 ), AND_OP_JOIN, ctx );
2549 buffer_fadd(sql_buf, "( %s )", subpred);
2552 jsonIteratorFree( search_itr );
2553 buffer_free( sql_buf );
2557 } else if ( !strcasecmp("-or",search_itr->key) ) {
2558 char* subpred = searchWHERE( node, meta, OR_OP_JOIN, ctx );
2560 buffer_fadd(sql_buf, "( %s )", subpred);
2563 buffer_free( sql_buf );
2566 } else if ( !strcasecmp("-and",search_itr->key) ) {
2567 char* subpred = searchWHERE( node, meta, AND_OP_JOIN, ctx );
2569 buffer_fadd(sql_buf, "( %s )", subpred);
2572 buffer_free( sql_buf );
2575 } else if ( !strcasecmp("-not",search_itr->key) ) {
2576 char* subpred = searchWHERE( node, meta, AND_OP_JOIN, ctx );
2578 buffer_fadd(sql_buf, " NOT ( %s )", subpred);
2581 buffer_free( sql_buf );
2584 } else if ( !strcasecmp("-exists",search_itr->key) ) {
2585 char* subpred = SELECT(
2587 jsonObjectGetKey( node, "select" ),
2588 jsonObjectGetKey( node, "from" ),
2589 jsonObjectGetKey( node, "where" ),
2590 jsonObjectGetKey( node, "having" ),
2591 jsonObjectGetKey( node, "order_by" ),
2592 jsonObjectGetKey( node, "limit" ),
2593 jsonObjectGetKey( node, "offset" ),
2598 buffer_fadd(sql_buf, "EXISTS ( %s )", subpred);
2601 buffer_free( sql_buf );
2604 } else if ( !strcasecmp("-not-exists",search_itr->key) ) {
2605 char* subpred = SELECT(
2607 jsonObjectGetKey( node, "select" ),
2608 jsonObjectGetKey( node, "from" ),
2609 jsonObjectGetKey( node, "where" ),
2610 jsonObjectGetKey( node, "having" ),
2611 jsonObjectGetKey( node, "order_by" ),
2612 jsonObjectGetKey( node, "limit" ),
2613 jsonObjectGetKey( node, "offset" ),
2618 buffer_fadd(sql_buf, "NOT EXISTS ( %s )", subpred);
2621 buffer_free( sql_buf );
2627 char* class = osrfHashGet(meta, "classname");
2628 osrfHash* fields = osrfHashGet(meta, "fields");
2629 osrfHash* field = osrfHashGet( fields, search_itr->key );
2633 char* table = getSourceDefinition(meta);
2635 table = strdup( "(?)" );
2638 "%s: Attempt to reference non-existent column \"%s\" on %s (%s)",
2644 buffer_free(sql_buf);
2646 jsonIteratorFree(search_itr);
2650 char* subpred = searchPredicate( class, field, node, ctx );
2652 buffer_add( sql_buf, subpred );
2655 buffer_free(sql_buf);
2656 jsonIteratorFree(search_itr);
2661 jsonIteratorFree(search_itr);
2664 // ERROR ... only hash and array allowed at this level
2665 char* predicate_string = jsonObjectToJSON( search_hash );
2668 "%s: Invalid predicate structure: %s",
2672 buffer_free(sql_buf);
2673 free(predicate_string);
2677 return buffer_release(sql_buf);
2680 // Return 1 if the class is in the FROM clause, or 0 if not
2681 static int is_joined( jsonObject* from_clause, const char* class ) {
2685 else if( from_clause->type == JSON_STRING ) {
2686 if( strcmp( class, jsonObjectGetString( from_clause ) ) )
2690 } else if( from_clause->type != JSON_HASH ) {
2692 } else { // Look at any subjoins
2693 jsonIterator* class_itr = jsonNewIterator( from_clause );
2694 jsonObject* curr_class;
2695 int rc = 0; // return code
2696 while ( ( curr_class = jsonIteratorNext( class_itr ) ) ) {
2697 if( ! strcmp( class_itr->key, class ) ) {
2701 jsonObject* subjoin = jsonObjectGetKey( curr_class, "join" );
2702 if( subjoin && is_joined( subjoin, class ) ) {
2708 jsonIteratorFree( class_itr );
2715 /* method context */ osrfMethodContext* ctx,
2717 /* SELECT */ jsonObject* selhash,
2718 /* FROM */ jsonObject* join_hash,
2719 /* WHERE */ jsonObject* search_hash,
2720 /* HAVING */ jsonObject* having_hash,
2721 /* ORDER BY */ jsonObject* order_hash,
2722 /* LIMIT */ jsonObject* limit,
2723 /* OFFSET */ jsonObject* offset,
2724 /* flags */ int flags
2726 const char* locale = osrf_message_get_last_locale();
2728 // in case we don't get a select list
2729 jsonObject* defaultselhash = NULL;
2731 // general tmp objects
2732 const jsonObject* tmp_const;
2733 jsonObject* selclass = NULL;
2734 jsonObject* selfield = NULL;
2735 jsonObject* snode = NULL;
2736 jsonObject* onode = NULL;
2738 char* string = NULL;
2739 int from_function = 0;
2744 // the core search class
2745 char* core_class = NULL;
2747 // metadata about the core search class
2748 osrfHash* core_meta = NULL;
2750 osrfLogDebug(OSRF_LOG_MARK, "cstore SELECT locale: %s", locale);
2752 // punt if there's no core class
2753 if (!join_hash || ( join_hash->type == JSON_HASH && !join_hash->size )) {
2756 "%s: FROM clause is missing or empty",
2760 osrfAppSessionStatus(
2762 OSRF_STATUS_INTERNALSERVERERROR,
2763 "osrfMethodException",
2765 "FROM clause is missing or empty in JSON query"
2770 // get the core class -- the only key of the top level FROM clause, or a string
2771 if (join_hash->type == JSON_HASH) {
2772 jsonIterator* tmp_itr = jsonNewIterator( join_hash );
2773 snode = jsonIteratorNext( tmp_itr );
2775 core_class = strdup( tmp_itr->key );
2778 jsonObject* extra = jsonIteratorNext( tmp_itr );
2780 jsonIteratorFree( tmp_itr );
2783 // There shouldn't be more than one entry in join_hash
2787 "%s: Malformed FROM clause: extra entry in JSON_HASH",
2791 osrfAppSessionStatus(
2793 OSRF_STATUS_INTERNALSERVERERROR,
2794 "osrfMethodException",
2796 "Malformed FROM clause in JSON query"
2799 return NULL; // Malformed join_hash; extra entry
2801 } else if (join_hash->type == JSON_ARRAY) {
2803 core_class = jsonObjectToSimpleString( jsonObjectGetIndex(join_hash, 0) );
2806 } else if (join_hash->type == JSON_STRING) {
2807 core_class = jsonObjectToSimpleString( join_hash );
2813 "%s: FROM clause is unexpected JSON type: %s",
2815 json_type( join_hash->type )
2818 osrfAppSessionStatus(
2820 OSRF_STATUS_INTERNALSERVERERROR,
2821 "osrfMethodException",
2823 "Ill-formed FROM clause in JSON query"
2829 if (!from_function) {
2830 // Get the IDL class definition for the core class
2831 core_meta = osrfHashGet( oilsIDL(), core_class );
2832 if( !core_meta ) { // Didn't find it?
2835 "%s: SELECT clause references undefined class: \"%s\"",
2840 osrfAppSessionStatus(
2842 OSRF_STATUS_INTERNALSERVERERROR,
2843 "osrfMethodException",
2845 "SELECT clause references undefined class in JSON query"
2851 // Make sure the class isn't virtual
2852 if( str_is_true( osrfHashGet( core_meta, "virtual" ) ) ) {
2855 "%s: Core class is virtual: \"%s\"",
2860 osrfAppSessionStatus(
2862 OSRF_STATUS_INTERNALSERVERERROR,
2863 "osrfMethodException",
2865 "FROM clause references virtual class in JSON query"
2872 // if the select list is empty, or the core class field list is '*',
2873 // build the default select list ...
2875 selhash = defaultselhash = jsonNewObjectType(JSON_HASH);
2876 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2877 } else if( selhash->type != JSON_HASH ) {
2880 "%s: Expected JSON_HASH for SELECT clause; found %s",
2882 json_type( selhash->type )
2886 osrfAppSessionStatus(
2888 OSRF_STATUS_INTERNALSERVERERROR,
2889 "osrfMethodException",
2891 "Malformed SELECT clause in JSON query"
2895 } else if ( (tmp_const = jsonObjectGetKeyConst( selhash, core_class )) && tmp_const->type == JSON_STRING ) {
2896 const char* _x = jsonObjectGetString( tmp_const );
2897 if (!strncmp( "*", _x, 1 )) {
2898 jsonObjectRemoveKey( selhash, core_class );
2899 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2904 growing_buffer* sql_buf = buffer_init(128);
2906 // temp buffers for the SELECT list and GROUP BY clause
2907 growing_buffer* select_buf = buffer_init(128);
2908 growing_buffer* group_buf = buffer_init(128);
2910 int aggregate_found = 0; // boolean
2912 // Build a select list
2913 if(from_function) // From a function we select everything
2914 OSRF_BUFFER_ADD_CHAR( select_buf, '*' );
2917 // If we need to build a default list, prepare to do so
2918 jsonObject* _tmp = jsonObjectGetKey( selhash, core_class );
2919 if ( _tmp && !_tmp->size ) {
2921 osrfHash* core_fields = osrfHashGet( core_meta, "fields" );
2923 osrfHashIterator* field_itr = osrfNewHashIterator( core_fields );
2924 osrfHash* field_def;
2925 while( ( field_def = osrfHashIteratorNext( field_itr ) ) ) {
2926 if( ! str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2927 // This field is not virtual, so add it to the list
2928 jsonObjectPush( _tmp, jsonNewObject( osrfHashIteratorKey( field_itr ) ) );
2931 osrfHashIteratorFree( field_itr );
2934 // Now build the actual select list
2938 jsonIterator* selclass_itr = jsonNewIterator( selhash );
2939 while ( (selclass = jsonIteratorNext( selclass_itr )) ) { // For each class
2941 const char* cname = selclass_itr->key;
2943 // Make sure the target relation is in the FROM clause.
2945 // At this point join_hash is a step down from the join_hash we
2946 // received as a parameter. If the original was a JSON_STRING,
2947 // then json_hash is now NULL. If the original was a JSON_HASH,
2948 // then json_hash is now the first (and only) entry in it,
2949 // denoting the core class. We've already excluded the
2950 // possibility that the original was a JSON_ARRAY, because in
2951 // that case from_function would be non-NULL, and we wouldn't
2954 // If the current class isn't the core class
2955 // and it isn't in the join tree, bail out
2956 if ( strcmp( core_class, cname ) && ! is_joined( join_hash, cname ) ) {
2959 "%s: SELECT clause references class not in FROM clause: \"%s\"",
2964 osrfAppSessionStatus(
2966 OSRF_STATUS_INTERNALSERVERERROR,
2967 "osrfMethodException",
2969 "Selected class not in FROM clause in JSON query"
2971 jsonIteratorFree( selclass_itr );
2972 buffer_free( sql_buf );
2973 buffer_free( select_buf );
2974 buffer_free( group_buf );
2975 if( defaultselhash ) jsonObjectFree( defaultselhash );
2980 // Look up some attributes of the current class, so that we
2981 // don't have to look them up again for each field
2982 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
2983 osrfHash* class_field_set = osrfHashGet( idlClass, "fields" );
2984 const char* class_pkey = osrfHashGet( idlClass, "primarykey" );
2985 const char* class_tname = osrfHashGet( idlClass, "tablename" );
2987 // stitch together the column list ...
2988 jsonIterator* select_itr = jsonNewIterator( selclass );
2989 while ( (selfield = jsonIteratorNext( select_itr )) ) { // for each SELECT column
2991 // If we need a separator comma, add one
2995 OSRF_BUFFER_ADD_CHAR( select_buf, ',' );
2998 // ... if it's a string, just toss it on the pile
2999 if (selfield->type == JSON_STRING) {
3001 // Look up the field in the IDL
3002 const char* col_name = jsonObjectGetString( selfield );
3003 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
3005 // No such field in current class
3008 "%s: Selected column \"%s\" not defined in IDL for class \"%s\"",
3014 osrfAppSessionStatus(
3016 OSRF_STATUS_INTERNALSERVERERROR,
3017 "osrfMethodException",
3019 "Selected column not defined in JSON query"
3021 jsonIteratorFree( select_itr );
3022 jsonIteratorFree( selclass_itr );
3023 buffer_free( sql_buf );
3024 buffer_free( select_buf );
3025 buffer_free( group_buf );
3026 if( defaultselhash ) jsonObjectFree( defaultselhash );
3029 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
3030 // Virtual field not allowed
3033 "%s: Selected column \"%s\" for class \"%s\" is virtual",
3039 osrfAppSessionStatus(
3041 OSRF_STATUS_INTERNALSERVERERROR,
3042 "osrfMethodException",
3044 "Selected column may not be virtual in JSON query"
3046 jsonIteratorFree( select_itr );
3047 jsonIteratorFree( selclass_itr );
3048 buffer_free( sql_buf );
3049 buffer_free( select_buf );
3050 buffer_free( group_buf );
3051 if( defaultselhash ) jsonObjectFree( defaultselhash );
3058 if (flags & DISABLE_I18N)
3061 i18n = osrfHashGet(field_def, "i18n");
3063 if( str_is_true( i18n ) ) {
3064 buffer_fadd( select_buf,
3065 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
3066 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, col_name );
3068 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
3071 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
3074 // ... but it could be an object, in which case we check for a Field Transform
3075 } else if (selfield->type == JSON_HASH) {
3077 const char* col_name = jsonObjectGetString( jsonObjectGetKeyConst( selfield, "column" ) );
3079 // Get the field definition from the IDL
3080 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
3082 // No such field in current class
3085 "%s: Selected column \"%s\" is not defined in IDL for class \"%s\"",
3091 osrfAppSessionStatus(
3093 OSRF_STATUS_INTERNALSERVERERROR,
3094 "osrfMethodException",
3096 "Selected column is not defined in JSON query"
3098 jsonIteratorFree( select_itr );
3099 jsonIteratorFree( selclass_itr );
3100 buffer_free( sql_buf );
3101 buffer_free( select_buf );
3102 buffer_free( group_buf );
3103 if( defaultselhash ) jsonObjectFree( defaultselhash );
3106 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
3107 // No such field in current class
3110 "%s: Selected column \"%s\" is virtual for class \"%s\"",
3116 osrfAppSessionStatus(
3118 OSRF_STATUS_INTERNALSERVERERROR,
3119 "osrfMethodException",
3121 "Selected column is virtual in JSON query"
3123 jsonIteratorFree( select_itr );
3124 jsonIteratorFree( selclass_itr );
3125 buffer_free( sql_buf );
3126 buffer_free( select_buf );
3127 buffer_free( group_buf );
3128 if( defaultselhash ) jsonObjectFree( defaultselhash );
3133 // Decide what to use as a column alias
3135 if ((tmp_const = jsonObjectGetKeyConst( selfield, "alias" ))) {
3136 _alias = jsonObjectGetString( tmp_const );
3137 } else { // Use field name as the alias
3141 if (jsonObjectGetKeyConst( selfield, "transform" )) {
3142 char* transform_str = searchFieldTransform(cname, field_def, selfield);
3143 if( transform_str ) {
3144 buffer_fadd(select_buf, " %s AS \"%s\"", transform_str, _alias);
3145 free(transform_str);
3148 osrfAppSessionStatus(
3150 OSRF_STATUS_INTERNALSERVERERROR,
3151 "osrfMethodException",
3153 "Unable to generate transform function in JSON query"
3155 jsonIteratorFree( select_itr );
3156 jsonIteratorFree( selclass_itr );
3157 buffer_free( sql_buf );
3158 buffer_free( select_buf );
3159 buffer_free( group_buf );
3160 if( defaultselhash ) jsonObjectFree( defaultselhash );
3168 if (flags & DISABLE_I18N)
3171 i18n = osrfHashGet(field_def, "i18n");
3173 if( str_is_true( i18n ) ) {
3174 buffer_fadd( select_buf,
3175 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
3176 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, _alias);
3178 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
3181 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
3188 "%s: Selected item is unexpected JSON type: %s",
3190 json_type( selfield->type )
3193 osrfAppSessionStatus(
3195 OSRF_STATUS_INTERNALSERVERERROR,
3196 "osrfMethodException",
3198 "Ill-formed SELECT item in JSON query"
3200 jsonIteratorFree( select_itr );
3201 jsonIteratorFree( selclass_itr );
3202 buffer_free( sql_buf );
3203 buffer_free( select_buf );
3204 buffer_free( group_buf );
3205 if( defaultselhash ) jsonObjectFree( defaultselhash );
3210 const jsonObject* agg_obj = jsonObjectGetKey( selfield, "aggregate" );
3211 if( obj_is_true( agg_obj ) )
3212 aggregate_found = 1;
3214 // Append a comma (except for the first one)
3215 // and add the column to a GROUP BY clause
3219 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3221 buffer_fadd(group_buf, " %d", sel_pos);
3225 if (is_agg->size || (flags & SELECT_DISTINCT)) {
3227 const jsonObject* aggregate_obj = jsonObjectGetKey( selfield, "aggregate" );
3228 if ( ! obj_is_true( aggregate_obj ) ) {
3232 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3235 buffer_fadd(group_buf, " %d", sel_pos);
3238 } else if (is_agg = jsonObjectGetKey( selfield, "having" )) {
3242 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3245 _column = searchFieldTransform(cname, field, selfield);
3246 OSRF_BUFFER_ADD_CHAR(group_buf, ' ');
3247 OSRF_BUFFER_ADD(group_buf, _column);
3248 _column = searchFieldTransform(cname, field, selfield);
3255 } // end while -- iterating across SELECT columns
3257 jsonIteratorFree(select_itr);
3258 } // end while -- iterating across classes
3260 jsonIteratorFree(selclass_itr);
3264 char* col_list = buffer_release(select_buf);
3266 if (from_function) table = searchValueTransform(join_hash);
3267 else table = getSourceDefinition(core_meta);
3271 osrfAppSessionStatus(
3273 OSRF_STATUS_INTERNALSERVERERROR,
3274 "osrfMethodException",
3276 "Unable to identify table for core class"
3279 buffer_free( sql_buf );
3280 buffer_free( group_buf );
3281 if( defaultselhash ) jsonObjectFree( defaultselhash );
3286 // Put it all together
3287 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\" ", col_list, table, core_class );
3291 char* order_by_list = NULL;
3292 char* having_buf = NULL;
3294 if (!from_function) {
3296 // Now, walk the join tree and add that clause
3298 char* join_clause = searchJOIN( join_hash, core_meta );
3300 buffer_add(sql_buf, join_clause);
3304 osrfAppSessionStatus(
3306 OSRF_STATUS_INTERNALSERVERERROR,
3307 "osrfMethodException",
3309 "Unable to construct JOIN clause(s)"
3311 buffer_free( sql_buf );
3312 buffer_free( group_buf );
3313 if( defaultselhash ) jsonObjectFree( defaultselhash );
3319 // Build a WHERE clause, if there is one
3320 if ( search_hash ) {
3321 buffer_add(sql_buf, " WHERE ");
3323 // and it's on the WHERE clause
3324 char* pred = searchWHERE( search_hash, core_meta, AND_OP_JOIN, ctx );
3327 buffer_add(sql_buf, pred);
3331 osrfAppSessionStatus(
3333 OSRF_STATUS_INTERNALSERVERERROR,
3334 "osrfMethodException",
3336 "Severe query error in WHERE predicate -- see error log for more details"
3340 buffer_free(group_buf);
3341 buffer_free(sql_buf);
3342 if (defaultselhash) jsonObjectFree(defaultselhash);
3347 // Build a HAVING clause, if there is one
3348 if ( having_hash ) {
3350 // and it's on the the WHERE clause
3351 having_buf = searchWHERE( having_hash, core_meta, AND_OP_JOIN, ctx );
3353 if( ! having_buf ) {
3355 osrfAppSessionStatus(
3357 OSRF_STATUS_INTERNALSERVERERROR,
3358 "osrfMethodException",
3360 "Severe query error in HAVING predicate -- see error log for more details"
3364 buffer_free(group_buf);
3365 buffer_free(sql_buf);
3366 if (defaultselhash) jsonObjectFree(defaultselhash);
3371 growing_buffer* order_buf = NULL; // to collect ORDER BY list
3373 // Build an ORDER BY clause, if there is one
3374 if( NULL == order_hash )
3375 ; // No ORDER BY? do nothing
3376 else if( JSON_ARRAY == order_hash->type ) {
3377 // Array of field specifications, each specification being a
3378 // hash to define the class, field, and other details
3380 jsonObject* order_spec;
3381 while( (order_spec = jsonObjectGetIndex( order_hash, order_idx++ ) ) ) {
3383 if( JSON_HASH != order_spec->type ) {
3384 osrfLogError(OSRF_LOG_MARK,
3385 "%s: Malformed field specification in ORDER BY clause; expected JSON_HASH, found %s",
3386 MODULENAME, json_type( order_spec->type ) );
3388 osrfAppSessionStatus(
3390 OSRF_STATUS_INTERNALSERVERERROR,
3391 "osrfMethodException",
3393 "Malformed ORDER BY clause -- see error log for more details"
3395 buffer_free( order_buf );
3398 buffer_free(group_buf);
3399 buffer_free(sql_buf);
3400 if (defaultselhash) jsonObjectFree(defaultselhash);
3405 jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "class" ) );
3407 jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "field" ) );
3410 OSRF_BUFFER_ADD(order_buf, ", ");
3412 order_buf = buffer_init(128);
3414 if( !field || !class ) {
3415 osrfLogError(OSRF_LOG_MARK,
3416 "%s: Missing class or field name in field specification of ORDER BY clause",
3419 osrfAppSessionStatus(
3421 OSRF_STATUS_INTERNALSERVERERROR,
3422 "osrfMethodException",
3424 "Malformed ORDER BY clause -- see error log for more details"
3426 buffer_free( order_buf );
3429 buffer_free(group_buf);
3430 buffer_free(sql_buf);
3431 if (defaultselhash) jsonObjectFree(defaultselhash);
3435 if ( ! jsonObjectGetKeyConst( selhash, class )
3436 && strcmp( core_class, class )
3437 && ! is_joined( join_hash, class ) ) {
3438 osrfLogError(OSRF_LOG_MARK, "%s: ORDER BY clause references class \"%s\" "
3439 "not in FROM clause", MODULENAME, class );
3441 osrfAppSessionStatus(
3443 OSRF_STATUS_INTERNALSERVERERROR,
3444 "osrfMethodException",
3446 "Invalid class referenced in ORDER BY clause -- see error log for more details"
3450 buffer_free(group_buf);
3451 buffer_free(sql_buf);
3452 if (defaultselhash) jsonObjectFree(defaultselhash);
3456 osrfHash* field_def = oilsIDLFindPath( "/%s/fields/%s", class, field );
3458 osrfLogError(OSRF_LOG_MARK, "%s: Invalid field \"%s\".%s referenced in ORDER BY clause",
3459 MODULENAME, class, field );
3461 osrfAppSessionStatus(
3463 OSRF_STATUS_INTERNALSERVERERROR,
3464 "osrfMethodException",
3466 "Invalid field referenced in ORDER BY clause -- see error log for more details"
3470 buffer_free(group_buf);
3471 buffer_free(sql_buf);
3472 if (defaultselhash) jsonObjectFree(defaultselhash);
3474 } else if( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
3475 osrfLogError(OSRF_LOG_MARK, "%s: Virtual field \"%s\" in ORDER BY clause",
3476 MODULENAME, field );
3478 osrfAppSessionStatus(
3480 OSRF_STATUS_INTERNALSERVERERROR,
3481 "osrfMethodException",
3483 "Virtual field in ORDER BY clause -- see error log for more details"
3485 buffer_free( order_buf );
3488 buffer_free(group_buf);
3489 buffer_free(sql_buf);
3490 if (defaultselhash) jsonObjectFree(defaultselhash);
3494 if( jsonObjectGetKeyConst( order_spec, "transform" ) ) {
3495 char* transform_str = searchFieldTransform( class, field_def, order_spec );
3496 if( ! transform_str ) {
3498 osrfAppSessionStatus(
3500 OSRF_STATUS_INTERNALSERVERERROR,
3501 "osrfMethodException",
3503 "Severe query error in ORDER BY clause -- see error log for more details"
3505 buffer_free( order_buf );
3508 buffer_free(group_buf);
3509 buffer_free(sql_buf);
3510 if (defaultselhash) jsonObjectFree(defaultselhash);
3514 OSRF_BUFFER_ADD( order_buf, transform_str );
3515 free( transform_str );
3518 buffer_fadd( order_buf, "\"%s\".%s", class, field );
3520 const char* direction =
3521 jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "direction" ) );
3523 if( direction[ 0 ] || 'D' == direction[ 0 ] )
3524 OSRF_BUFFER_ADD( order_buf, " DESC" );
3526 OSRF_BUFFER_ADD( order_buf, " ASC" );
3529 } else if( JSON_HASH == order_hash->type ) {
3530 // This hash is keyed on class name. Each class has either
3531 // an array of field names or a hash keyed on field name.
3532 jsonIterator* class_itr = jsonNewIterator( order_hash );
3533 while ( (snode = jsonIteratorNext( class_itr )) ) {
3535 if ( ! jsonObjectGetKeyConst( selhash,class_itr->key )
3536 && strcmp( core_class, class_itr->key )
3537 && ! is_joined( join_hash, class_itr->key ) ) {
3538 osrfLogError(OSRF_LOG_MARK, "%s: Invalid class \"%s\" referenced in ORDER BY clause",
3539 MODULENAME, class_itr->key );
3541 osrfAppSessionStatus(
3543 OSRF_STATUS_INTERNALSERVERERROR,
3544 "osrfMethodException",
3546 "Invalid class referenced in ORDER BY clause -- see error log for more details"
3548 jsonIteratorFree( class_itr );
3549 buffer_free( order_buf );
3552 buffer_free(group_buf);
3553 buffer_free(sql_buf);
3554 if (defaultselhash) jsonObjectFree(defaultselhash);
3558 osrfHash* field_list_def = oilsIDLFindPath( "/%s/fields", class_itr->key );
3560 if ( snode->type == JSON_HASH ) {
3562 // Hash is keyed on field names from the current class. For each field
3563 // there is another layer of hash to define the sorting details, if any,
3564 // or a string to indicate direction of sorting.
3565 jsonIterator* order_itr = jsonNewIterator( snode );
3566 while ( (onode = jsonIteratorNext( order_itr )) ) {
3568 osrfHash* field_def = osrfHashGet( field_list_def, order_itr->key );
3570 osrfLogError(OSRF_LOG_MARK, "%s: Invalid field \"%s\" in ORDER BY clause",
3571 MODULENAME, order_itr->key );
3573 osrfAppSessionStatus(
3575 OSRF_STATUS_INTERNALSERVERERROR,
3576 "osrfMethodException",
3578 "Invalid field in ORDER BY clause -- see error log for more details"
3580 jsonIteratorFree( order_itr );
3581 jsonIteratorFree( class_itr );
3582 buffer_free( order_buf );
3585 buffer_free(group_buf);
3586 buffer_free(sql_buf);
3587 if (defaultselhash) jsonObjectFree(defaultselhash);
3589 } else if( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
3590 osrfLogError(OSRF_LOG_MARK, "%s: Virtual field \"%s\" in ORDER BY clause",
3591 MODULENAME, order_itr->key );
3593 osrfAppSessionStatus(
3595 OSRF_STATUS_INTERNALSERVERERROR,
3596 "osrfMethodException",
3598 "Virtual field in ORDER BY clause -- see error log for more details"
3600 jsonIteratorFree( order_itr );
3601 jsonIteratorFree( class_itr );
3602 buffer_free( order_buf );
3605 buffer_free(group_buf);
3606 buffer_free(sql_buf);
3607 if (defaultselhash) jsonObjectFree(defaultselhash);
3611 const char* direction = NULL;
3612 if ( onode->type == JSON_HASH ) {
3613 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3614 string = searchFieldTransform(
3616 osrfHashGet( field_list_def, order_itr->key ),
3620 if( ctx ) osrfAppSessionStatus(
3622 OSRF_STATUS_INTERNALSERVERERROR,
3623 "osrfMethodException",
3625 "Severe query error in ORDER BY clause -- see error log for more details"
3627 jsonIteratorFree( order_itr );
3628 jsonIteratorFree( class_itr );
3631 buffer_free(group_buf);
3632 buffer_free(order_buf);
3633 buffer_free(sql_buf);
3634 if (defaultselhash) jsonObjectFree(defaultselhash);
3638 growing_buffer* field_buf = buffer_init(16);
3639 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3640 string = buffer_release(field_buf);
3643 if ( (tmp_const = jsonObjectGetKeyConst( onode, "direction" )) ) {
3644 const char* dir = jsonObjectGetString(tmp_const);
3645 if (!strncasecmp(dir, "d", 1)) {
3646 direction = " DESC";
3652 } else if ( JSON_NULL == onode->type || JSON_ARRAY == onode->type ) {
3653 osrfLogError( OSRF_LOG_MARK,
3654 "%s: Expected JSON_STRING in ORDER BY clause; found %s",
3655 MODULENAME, json_type( onode->type ) );
3657 osrfAppSessionStatus(
3659 OSRF_STATUS_INTERNALSERVERERROR,
3660 "osrfMethodException",
3662 "Malformed ORDER BY clause -- see error log for more details"
3664 jsonIteratorFree( order_itr );
3665 jsonIteratorFree( class_itr );
3668 buffer_free(group_buf);
3669 buffer_free(order_buf);
3670 buffer_free(sql_buf);
3671 if (defaultselhash) jsonObjectFree(defaultselhash);
3675 string = strdup(order_itr->key);
3676 const char* dir = jsonObjectGetString(onode);
3677 if (!strncasecmp(dir, "d", 1)) {
3678 direction = " DESC";
3685 OSRF_BUFFER_ADD(order_buf, ", ");
3687 order_buf = buffer_init(128);
3689 OSRF_BUFFER_ADD(order_buf, string);
3693 OSRF_BUFFER_ADD(order_buf, direction);
3697 jsonIteratorFree(order_itr);
3699 } else if ( snode->type == JSON_ARRAY ) {
3701 // Array is a list of fields from the current class
3702 unsigned long order_idx = 0;
3703 while(( onode = jsonObjectGetIndex( snode, order_idx++ ) )) {
3705 const char* _f = jsonObjectGetString( onode );
3707 osrfHash* field_def = osrfHashGet( field_list_def, _f );
3709 osrfLogError(OSRF_LOG_MARK, "%s: Invalid field \"%s\" in ORDER BY clause",
3712 osrfAppSessionStatus(
3714 OSRF_STATUS_INTERNALSERVERERROR,
3715 "osrfMethodException",
3717 "Invalid field in ORDER BY clause -- see error log for more details"
3719 jsonIteratorFree( class_itr );
3720 buffer_free( order_buf );
3723 buffer_free(group_buf);
3724 buffer_free(sql_buf);
3725 if (defaultselhash) jsonObjectFree(defaultselhash);
3727 } else if( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
3728 osrfLogError(OSRF_LOG_MARK, "%s: Virtual field \"%s\" in ORDER BY clause",
3731 osrfAppSessionStatus(
3733 OSRF_STATUS_INTERNALSERVERERROR,
3734 "osrfMethodException",
3736 "Virtual field in ORDER BY clause -- see error log for more details"
3738 jsonIteratorFree( class_itr );
3739 buffer_free( order_buf );
3742 buffer_free(group_buf);
3743 buffer_free(sql_buf);
3744 if (defaultselhash) jsonObjectFree(defaultselhash);
3749 OSRF_BUFFER_ADD(order_buf, ", ");
3751 order_buf = buffer_init(128);
3753 buffer_fadd( order_buf, "\"%s\".%s", class_itr->key, _f);
3757 // IT'S THE OOOOOOOOOOOLD STYLE!
3759 osrfLogError(OSRF_LOG_MARK,
3760 "%s: Possible SQL injection attempt; direct order by is not allowed", MODULENAME);
3762 osrfAppSessionStatus(
3764 OSRF_STATUS_INTERNALSERVERERROR,
3765 "osrfMethodException",
3767 "Severe query error -- see error log for more details"
3773 buffer_free(group_buf);
3774 buffer_free(order_buf);
3775 buffer_free(sql_buf);
3776 if (defaultselhash) jsonObjectFree(defaultselhash);
3777 jsonIteratorFree(class_itr);
3781 jsonIteratorFree( class_itr );
3783 osrfLogError(OSRF_LOG_MARK,
3784 "%s: Malformed ORDER BY clause; expected JSON_HASH or JSON_ARRAY, found %s",
3785 MODULENAME, json_type( order_hash->type ) );
3787 osrfAppSessionStatus(
3789 OSRF_STATUS_INTERNALSERVERERROR,
3790 "osrfMethodException",
3792 "Malformed ORDER BY clause -- see error log for more details"
3794 buffer_free( order_buf );
3797 buffer_free(group_buf);
3798 buffer_free(sql_buf);
3799 if (defaultselhash) jsonObjectFree(defaultselhash);
3804 order_by_list = buffer_release( order_buf );
3808 string = buffer_release(group_buf);
3810 if ( *string && ( aggregate_found || (flags & SELECT_DISTINCT) ) ) {
3811 OSRF_BUFFER_ADD( sql_buf, " GROUP BY " );
3812 OSRF_BUFFER_ADD( sql_buf, string );
3817 if( having_buf && *having_buf ) {
3818 OSRF_BUFFER_ADD( sql_buf, " HAVING " );
3819 OSRF_BUFFER_ADD( sql_buf, having_buf );
3823 if( order_by_list ) {
3825 if ( *order_by_list ) {
3826 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3827 OSRF_BUFFER_ADD( sql_buf, order_by_list );
3830 free( order_by_list );
3834 const char* str = jsonObjectGetString(limit);
3835 buffer_fadd( sql_buf, " LIMIT %d", atoi(str) );
3839 const char* str = jsonObjectGetString(offset);
3840 buffer_fadd( sql_buf, " OFFSET %d", atoi(str) );
3843 if (!(flags & SUBSELECT)) OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3846 if (defaultselhash) jsonObjectFree(defaultselhash);
3848 return buffer_release(sql_buf);
3852 static char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
3854 const char* locale = osrf_message_get_last_locale();
3856 osrfHash* fields = osrfHashGet(meta, "fields");
3857 char* core_class = osrfHashGet(meta, "classname");
3859 const jsonObject* join_hash = jsonObjectGetKeyConst( order_hash, "join" );
3861 jsonObject* node = NULL;
3862 jsonObject* snode = NULL;
3863 jsonObject* onode = NULL;
3864 const jsonObject* _tmp = NULL;
3865 jsonObject* selhash = NULL;
3866 jsonObject* defaultselhash = NULL;
3868 growing_buffer* sql_buf = buffer_init(128);
3869 growing_buffer* select_buf = buffer_init(128);
3871 if ( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
3872 defaultselhash = jsonNewObjectType(JSON_HASH);
3873 selhash = defaultselhash;
3876 if ( !jsonObjectGetKeyConst(selhash,core_class) ) {
3877 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
3878 jsonObject* flist = jsonObjectGetKey( selhash, core_class );
3883 osrfStringArray* keys = osrfHashKeys( fields );
3884 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
3885 if( ! str_is_true( osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
3886 jsonObjectPush( flist, jsonNewObject( field ) );
3888 osrfStringArrayFree(keys);
3892 jsonIterator* class_itr = jsonNewIterator( selhash );
3893 while ( (snode = jsonIteratorNext( class_itr )) ) {
3895 char* cname = class_itr->key;
3896 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
3897 if (!idlClass) continue;
3899 if (strcmp(core_class,class_itr->key)) {
3900 if (!join_hash) continue;
3902 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", class_itr->key);
3904 jsonObjectFree(found);
3908 jsonObjectFree(found);
3911 jsonIterator* select_itr = jsonNewIterator( snode );
3912 while ( (node = jsonIteratorNext( select_itr )) ) {
3913 const char* item_str = jsonObjectGetString( node );
3914 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), item_str );
3915 char* fname = osrfHashGet(field, "name");
3917 if (!field) continue;
3922 OSRF_BUFFER_ADD_CHAR(select_buf, ',');
3927 const jsonObject* no_i18n_obj = jsonObjectGetKey( order_hash, "no_i18n" );
3928 if ( obj_is_true( no_i18n_obj ) ) // Suppress internationalization?
3931 i18n = osrfHashGet(field, "i18n");
3933 if( str_is_true( i18n ) ) {
3934 char* pkey = osrfHashGet(idlClass, "primarykey");
3935 char* tname = osrfHashGet(idlClass, "tablename");
3937 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);
3939 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3942 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3946 jsonIteratorFree(select_itr);
3949 jsonIteratorFree(class_itr);
3951 char* col_list = buffer_release(select_buf);
3952 char* table = getSourceDefinition(meta);
3954 table = strdup( "(null)" );
3956 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\"", col_list, table, core_class );
3961 char* join_clause = searchJOIN( join_hash, meta );
3962 OSRF_BUFFER_ADD_CHAR(sql_buf, ' ');
3963 OSRF_BUFFER_ADD(sql_buf, join_clause);
3967 osrfLogDebug(OSRF_LOG_MARK, "%s pre-predicate SQL = %s",
3968 MODULENAME, OSRF_BUFFER_C_STR(sql_buf));
3970 OSRF_BUFFER_ADD(sql_buf, " WHERE ");
3972 char* pred = searchWHERE( search_hash, meta, AND_OP_JOIN, ctx );
3974 osrfAppSessionStatus(
3976 OSRF_STATUS_INTERNALSERVERERROR,
3977 "osrfMethodException",
3979 "Severe query error -- see error log for more details"
3981 buffer_free(sql_buf);
3982 if(defaultselhash) jsonObjectFree(defaultselhash);
3985 buffer_add(sql_buf, pred);
3990 char* string = NULL;
3991 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "order_by" )) ){
3993 growing_buffer* order_buf = buffer_init(128);
3996 jsonIterator* class_itr = jsonNewIterator( _tmp );
3997 while ( (snode = jsonIteratorNext( class_itr )) ) {
3999 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
4002 if ( snode->type == JSON_HASH ) {
4004 jsonIterator* order_itr = jsonNewIterator( snode );
4005 while ( (onode = jsonIteratorNext( order_itr )) ) {
4007 osrfHash* field_def = oilsIDLFindPath( "/%s/fields/%s",
4008 class_itr->key, order_itr->key );
4012 char* direction = NULL;
4013 if ( onode->type == JSON_HASH ) {
4014 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
4015 string = searchFieldTransform( class_itr->key, field_def, onode );
4017 osrfAppSessionStatus(
4019 OSRF_STATUS_INTERNALSERVERERROR,
4020 "osrfMethodException",
4022 "Severe query error in ORDER BY clause -- see error log for more details"
4024 jsonIteratorFree( order_itr );
4025 jsonIteratorFree( class_itr );
4026 buffer_free( order_buf );
4027 buffer_free( sql_buf );
4028 if( defaultselhash ) jsonObjectFree( defaultselhash );
4032 growing_buffer* field_buf = buffer_init(16);
4033 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
4034 string = buffer_release(field_buf);
4037 if ( (_tmp = jsonObjectGetKeyConst( onode, "direction" )) ) {
4038 const char* dir = jsonObjectGetString(_tmp);
4039 if (!strncasecmp(dir, "d", 1)) {
4040 direction = " DESC";
4047 string = strdup(order_itr->key);
4048 const char* dir = jsonObjectGetString(onode);
4049 if (!strncasecmp(dir, "d", 1)) {
4050 direction = " DESC";
4059 buffer_add(order_buf, ", ");
4062 buffer_add(order_buf, string);
4066 buffer_add(order_buf, direction);
4071 jsonIteratorFree(order_itr);
4074 const char* str = jsonObjectGetString(snode);
4075 buffer_add(order_buf, str);
4081 jsonIteratorFree(class_itr);
4083 string = buffer_release(order_buf);
4086 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
4087 OSRF_BUFFER_ADD( sql_buf, string );
4093 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "limit" )) ){
4094 const char* str = jsonObjectGetString(_tmp);
4102 _tmp = jsonObjectGetKeyConst( order_hash, "offset" );
4104 const char* str = jsonObjectGetString(_tmp);
4113 if (defaultselhash) jsonObjectFree(defaultselhash);
4115 OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
4116 return buffer_release(sql_buf);
4119 int doJSONSearch ( osrfMethodContext* ctx ) {
4120 if(osrfMethodVerifyContext( ctx )) {
4121 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
4125 osrfLogDebug(OSRF_LOG_MARK, "Recieved query request");
4130 dbhandle = writehandle;
4132 jsonObject* hash = jsonObjectGetIndex(ctx->params, 0);
4136 if ( obj_is_true( jsonObjectGetKey( hash, "distinct" ) ) )
4137 flags |= SELECT_DISTINCT;
4139 if ( obj_is_true( jsonObjectGetKey( hash, "no_i18n" ) ) )
4140 flags |= DISABLE_I18N;
4142 osrfLogDebug(OSRF_LOG_MARK, "Building SQL ...");
4145 jsonObjectGetKey( hash, "select" ),
4146 jsonObjectGetKey( hash, "from" ),
4147 jsonObjectGetKey( hash, "where" ),
4148 jsonObjectGetKey( hash, "having" ),
4149 jsonObjectGetKey( hash, "order_by" ),
4150 jsonObjectGetKey( hash, "limit" ),
4151 jsonObjectGetKey( hash, "offset" ),
4160 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
4161 dbi_result result = dbi_conn_query(dbhandle, sql);
4164 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
4166 if (dbi_result_first_row(result)) {
4167 /* JSONify the result */
4168 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
4171 jsonObject* return_val = oilsMakeJSONFromResult( result );
4172 osrfAppRespond( ctx, return_val );
4173 jsonObjectFree( return_val );
4174 } while (dbi_result_next_row(result));
4177 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
4180 osrfAppRespondComplete( ctx, NULL );
4182 /* clean up the query */
4183 dbi_result_free(result);
4187 osrfLogError(OSRF_LOG_MARK, "%s: Error with query [%s]", MODULENAME, sql);
4188 osrfAppSessionStatus(
4190 OSRF_STATUS_INTERNALSERVERERROR,
4191 "osrfMethodException",
4193 "Severe query error -- see error log for more details"
4201 static jsonObject* doFieldmapperSearch ( osrfMethodContext* ctx, osrfHash* meta,
4202 jsonObject* where_hash, jsonObject* query_hash, int* err ) {
4205 dbhandle = writehandle;
4207 osrfHash* links = osrfHashGet(meta, "links");
4208 osrfHash* fields = osrfHashGet(meta, "fields");
4209 char* core_class = osrfHashGet(meta, "classname");
4210 char* pkey = osrfHashGet(meta, "primarykey");
4212 const jsonObject* _tmp;
4215 char* sql = buildSELECT( where_hash, query_hash, meta, ctx );
4217 osrfLogDebug(OSRF_LOG_MARK, "Problem building query, returning NULL");
4222 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
4224 dbi_result result = dbi_conn_query(dbhandle, sql);
4225 if( NULL == result ) {
4226 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]",
4227 MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
4228 osrfAppSessionStatus(
4230 OSRF_STATUS_INTERNALSERVERERROR,
4231 "osrfMethodException",
4233 "Severe query error -- see error log for more details"
4240 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
4243 jsonObject* res_list = jsonNewObjectType(JSON_ARRAY);
4244 osrfHash* dedup = osrfNewHash();
4246 if (dbi_result_first_row(result)) {
4247 /* JSONify the result */
4248 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
4250 obj = oilsMakeFieldmapperFromResult( result, meta );
4251 char* pkey_val = oilsFMGetString( obj, pkey );
4252 if ( osrfHashGet( dedup, pkey_val ) ) {
4253 jsonObjectFree(obj);
4256 osrfHashSet( dedup, pkey_val, pkey_val );
4257 jsonObjectPush(res_list, obj);
4259 } while (dbi_result_next_row(result));
4261 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s",
4265 osrfHashFree(dedup);
4266 /* clean up the query */
4267 dbi_result_free(result);
4270 if (res_list->size && query_hash) {
4271 _tmp = jsonObjectGetKeyConst( query_hash, "flesh" );
4273 int x = (int)jsonObjectGetNumber(_tmp);
4274 if (x == -1 || x > max_flesh_depth) x = max_flesh_depth;
4276 const jsonObject* temp_blob;
4277 if ((temp_blob = jsonObjectGetKeyConst( query_hash, "flesh_fields" )) && x > 0) {
4279 jsonObject* flesh_blob = jsonObjectClone( temp_blob );
4280 const jsonObject* flesh_fields = jsonObjectGetKeyConst( flesh_blob, core_class );
4282 osrfStringArray* link_fields = NULL;
4285 if (flesh_fields->size == 1) {
4286 const char* _t = jsonObjectGetString( jsonObjectGetIndex( flesh_fields, 0 ) );
4287 if (!strcmp(_t,"*")) link_fields = osrfHashKeys( links );
4292 link_fields = osrfNewStringArray(1);
4293 jsonIterator* _i = jsonNewIterator( flesh_fields );
4294 while ((_f = jsonIteratorNext( _i ))) {
4295 osrfStringArrayAdd( link_fields, jsonObjectGetString( _f ) );
4297 jsonIteratorFree(_i);
4302 jsonIterator* itr = jsonNewIterator( res_list );
4303 while ((cur = jsonIteratorNext( itr ))) {
4308 while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
4310 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
4312 osrfHash* kid_link = osrfHashGet(links, link_field);
4313 if (!kid_link) continue;
4315 osrfHash* field = osrfHashGet(fields, link_field);
4316 if (!field) continue;
4318 osrfHash* value_field = field;
4320 osrfHash* kid_idl = osrfHashGet(oilsIDL(), osrfHashGet(kid_link, "class"));
4321 if (!kid_idl) continue;
4323 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
4324 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
4327 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
4328 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
4331 osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
4333 if (link_map->size > 0) {
4334 jsonObject* _kid_key = jsonNewObjectType(JSON_ARRAY);
4337 jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
4342 osrfHashGet(kid_link, "class"),
4349 "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
4350 osrfHashGet(kid_link, "field"),
4351 osrfHashGet(kid_link, "class"),
4352 osrfHashGet(kid_link, "key"),
4353 osrfHashGet(kid_link, "reltype")
4356 const char* search_key = jsonObjectGetString(
4359 atoi( osrfHashGet(value_field, "array_position") )
4364 osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
4368 osrfLogDebug(OSRF_LOG_MARK, "Creating param objects...");
4370 // construct WHERE clause
4371 jsonObject* where_clause = jsonNewObjectType(JSON_HASH);
4374 osrfHashGet(kid_link, "key"),
4375 jsonNewObject( search_key )
4378 // construct the rest of the query
4379 jsonObject* rest_of_query = jsonNewObjectType(JSON_HASH);
4380 jsonObjectSetKey( rest_of_query, "flesh",
4381 jsonNewNumberObject( (double)(x - 1 + link_map->size) )
4385 jsonObjectSetKey( rest_of_query, "flesh_fields", jsonObjectClone(flesh_blob) );
4387 if (jsonObjectGetKeyConst(query_hash, "order_by")) {
4388 jsonObjectSetKey( rest_of_query, "order_by",
4389 jsonObjectClone(jsonObjectGetKeyConst(query_hash, "order_by"))
4393 if (jsonObjectGetKeyConst(query_hash, "select")) {
4394 jsonObjectSetKey( rest_of_query, "select",
4395 jsonObjectClone(jsonObjectGetKeyConst(query_hash, "select"))
4399 jsonObject* kids = doFieldmapperSearch( ctx, kid_idl,
4400 where_clause, rest_of_query, err);
4402 jsonObjectFree( where_clause );
4403 jsonObjectFree( rest_of_query );
4406 osrfStringArrayFree(link_fields);
4407 jsonIteratorFree(itr);
4408 jsonObjectFree(res_list);
4409 jsonObjectFree(flesh_blob);
4413 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
4415 jsonObject* X = NULL;
4416 if ( link_map->size > 0 && kids->size > 0 ) {
4418 kids = jsonNewObjectType(JSON_ARRAY);
4420 jsonObject* _k_node;
4421 jsonIterator* _k = jsonNewIterator( X );
4422 while ((_k_node = jsonIteratorNext( _k ))) {
4428 (unsigned long)atoi(
4434 osrfHashGet(kid_link, "class")
4438 osrfStringArrayGetString( link_map, 0 )
4447 jsonIteratorFree(_k);
4450 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" )) || !(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) {
4451 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
4454 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
4455 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
4459 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
4460 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
4463 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
4464 jsonObjectClone( kids )
4469 jsonObjectFree(kids);
4473 jsonObjectFree( kids );
4475 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
4476 osrfLogDebug(OSRF_LOG_MARK, "%s", jsonObjectToJSON(cur));
4480 jsonObjectFree( flesh_blob );
4481 osrfStringArrayFree(link_fields);
4482 jsonIteratorFree(itr);
4491 static jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
4493 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4495 jsonObject* target = jsonObjectGetIndex( ctx->params, 1 );
4497 jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
4500 if (!verifyObjectClass(ctx, target)) {
4505 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4506 osrfAppSessionStatus(
4508 OSRF_STATUS_BADREQUEST,
4509 "osrfMethodException",
4511 "No active transaction -- required for UPDATE"
4517 // The following test is harmless but redundant. If a class is
4518 // readonly, we don't register an update method for it.
4519 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4520 osrfAppSessionStatus(
4522 OSRF_STATUS_BADREQUEST,
4523 "osrfMethodException",
4525 "Cannot UPDATE readonly class"
4531 dbhandle = writehandle;
4533 char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
4535 // Set the last_xact_id
4536 int index = oilsIDL_ntop( target->classname, "last_xact_id" );
4538 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
4539 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
4542 char* pkey = osrfHashGet(meta, "primarykey");
4543 osrfHash* fields = osrfHashGet(meta, "fields");
4545 char* id = oilsFMGetString( target, pkey );
4549 "%s updating %s object with %s = %s",
4551 osrfHashGet(meta, "fieldmapper"),
4556 growing_buffer* sql = buffer_init(128);
4557 buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
4562 osrfStringArray* field_list = osrfHashKeys( fields );
4563 while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
4565 osrfHash* field = osrfHashGet( fields, field_name );
4567 if(!( strcmp( field_name, pkey ) )) continue;
4568 if( str_is_true( osrfHashGet(osrfHashGet(fields,field_name), "virtual") ) )
4571 const jsonObject* field_object = oilsFMGetObject( target, field_name );
4573 int value_is_numeric = 0; // boolean
4575 if (field_object && field_object->classname) {
4576 value = oilsFMGetString(
4578 (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
4581 value = jsonObjectToSimpleString( field_object );
4582 if( field_object && JSON_NUMBER == field_object->type )
4583 value_is_numeric = 1;
4586 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
4588 if (!field_object || field_object->type == JSON_NULL) {
4589 if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
4590 if (first) first = 0;
4591 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4592 buffer_fadd( sql, " %s = NULL", field_name );
4595 } else if ( value_is_numeric || !strcmp( get_primitive( field ), "number") ) {
4596 if (first) first = 0;
4597 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4599 const char* numtype = get_datatype( field );
4600 if ( !strncmp( numtype, "INT", 3 ) ) {
4601 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
4602 } else if ( !strcmp( numtype, "NUMERIC" ) ) {
4603 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
4605 // Must really be intended as a string, so quote it
4606 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4607 buffer_fadd( sql, " %s = %s", field_name, value );
4609 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4610 osrfAppSessionStatus(
4612 OSRF_STATUS_INTERNALSERVERERROR,
4613 "osrfMethodException",
4615 "Error quoting string -- please see the error log for more details"
4625 osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, numtype );
4628 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4629 if (first) first = 0;
4630 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4631 buffer_fadd( sql, " %s = %s", field_name, value );
4634 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4635 osrfAppSessionStatus(
4637 OSRF_STATUS_INTERNALSERVERERROR,
4638 "osrfMethodException",
4640 "Error quoting string -- please see the error log for more details"
4654 jsonObject* obj = jsonNewObject(id);
4656 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4657 dbi_conn_quote_string(dbhandle, &id);
4659 buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
4661 char* query = buffer_release(sql);
4662 osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
4664 dbi_result result = dbi_conn_query(dbhandle, query);
4668 jsonObjectFree(obj);
4669 obj = jsonNewObject(NULL);
4672 "%s ERROR updating %s object with %s = %s",
4674 osrfHashGet(meta, "fieldmapper"),
4685 static jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
4687 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4689 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4690 osrfAppSessionStatus(
4692 OSRF_STATUS_BADREQUEST,
4693 "osrfMethodException",
4695 "No active transaction -- required for DELETE"
4701 // The following test is harmless but redundant. If a class is
4702 // readonly, we don't register a delete method for it.
4703 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4704 osrfAppSessionStatus(
4706 OSRF_STATUS_BADREQUEST,
4707 "osrfMethodException",
4709 "Cannot DELETE readonly class"
4715 dbhandle = writehandle;
4719 char* pkey = osrfHashGet(meta, "primarykey");
4727 if (jsonObjectGetIndex(ctx->params, _obj_pos)->classname) {
4728 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, _obj_pos ))) {
4733 id = oilsFMGetString( jsonObjectGetIndex(ctx->params, _obj_pos), pkey );
4736 if (!verifyObjectPCRUD( ctx, NULL )) {
4741 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, _obj_pos));
4746 "%s deleting %s object with %s = %s",
4748 osrfHashGet(meta, "fieldmapper"),
4753 obj = jsonNewObject(id);
4755 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4756 dbi_conn_quote_string(writehandle, &id);
4758 dbi_result result = dbi_conn_queryf(writehandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
4761 jsonObjectFree(obj);
4762 obj = jsonNewObject(NULL);
4765 "%s ERROR deleting %s object with %s = %s",
4767 osrfHashGet(meta, "fieldmapper"),
4780 static jsonObject* oilsMakeFieldmapperFromResult( dbi_result result, osrfHash* meta) {
4781 if(!(result && meta)) return jsonNULL;
4783 jsonObject* object = jsonNewObject(NULL);
4784 jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
4786 osrfHash* fields = osrfHashGet(meta, "fields");
4788 osrfLogInternal(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
4792 char dt_string[256];
4796 int columnIndex = 1;
4798 unsigned short type;
4799 const char* columnName;
4801 /* cycle through the column list */
4802 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4804 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4806 fmIndex = -1; // reset the position
4808 /* determine the field type and storage attributes */
4809 type = dbi_result_get_field_type(result, columnName);
4810 attr = dbi_result_get_field_attribs(result, columnName);
4812 /* fetch the fieldmapper index */
4813 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
4815 if ( str_is_true( osrfHashGet(_f, "virtual") ) )
4818 const char* pos = (char*)osrfHashGet(_f, "array_position");
4819 if ( !pos ) continue;
4821 fmIndex = atoi( pos );
4822 osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
4827 if (dbi_result_field_is_null(result, columnName)) {
4828 jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
4833 case DBI_TYPE_INTEGER :
4835 if( attr & DBI_INTEGER_SIZE8 )
4836 jsonObjectSetIndex( object, fmIndex,
4837 jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
4839 jsonObjectSetIndex( object, fmIndex,
4840 jsonNewNumberObject(dbi_result_get_int(result, columnName)));
4844 case DBI_TYPE_DECIMAL :
4845 jsonObjectSetIndex( object, fmIndex,
4846 jsonNewNumberObject(dbi_result_get_double(result, columnName)));
4849 case DBI_TYPE_STRING :
4855 jsonNewObject( dbi_result_get_string(result, columnName) )
4860 case DBI_TYPE_DATETIME :
4862 memset(dt_string, '\0', sizeof(dt_string));
4863 memset(&gmdt, '\0', sizeof(gmdt));
4865 _tmp_dt = dbi_result_get_datetime(result, columnName);
4868 if (!(attr & DBI_DATETIME_DATE)) {
4869 gmtime_r( &_tmp_dt, &gmdt );
4870 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4871 } else if (!(attr & DBI_DATETIME_TIME)) {
4872 localtime_r( &_tmp_dt, &gmdt );
4873 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4875 localtime_r( &_tmp_dt, &gmdt );
4876 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4879 jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
4883 case DBI_TYPE_BINARY :
4884 osrfLogError( OSRF_LOG_MARK,
4885 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4893 static jsonObject* oilsMakeJSONFromResult( dbi_result result ) {
4894 if(!result) return jsonNULL;
4896 jsonObject* object = jsonNewObject(NULL);
4899 char dt_string[256];
4903 int columnIndex = 1;
4905 unsigned short type;
4906 const char* columnName;
4908 /* cycle through the column list */
4909 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4911 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4913 fmIndex = -1; // reset the position
4915 /* determine the field type and storage attributes */
4916 type = dbi_result_get_field_type(result, columnName);
4917 attr = dbi_result_get_field_attribs(result, columnName);
4919 if (dbi_result_field_is_null(result, columnName)) {
4920 jsonObjectSetKey( object, columnName, jsonNewObject(NULL) );
4925 case DBI_TYPE_INTEGER :
4927 if( attr & DBI_INTEGER_SIZE8 )
4928 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_longlong(result, columnName)) );
4930 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_int(result, columnName)) );
4933 case DBI_TYPE_DECIMAL :
4934 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_double(result, columnName)) );
4937 case DBI_TYPE_STRING :
4938 jsonObjectSetKey( object, columnName, jsonNewObject(dbi_result_get_string(result, columnName)) );
4941 case DBI_TYPE_DATETIME :
4943 memset(dt_string, '\0', sizeof(dt_string));
4944 memset(&gmdt, '\0', sizeof(gmdt));
4946 _tmp_dt = dbi_result_get_datetime(result, columnName);
4949 if (!(attr & DBI_DATETIME_DATE)) {
4950 gmtime_r( &_tmp_dt, &gmdt );
4951 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4952 } else if (!(attr & DBI_DATETIME_TIME)) {
4953 localtime_r( &_tmp_dt, &gmdt );
4954 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4956 localtime_r( &_tmp_dt, &gmdt );
4957 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4960 jsonObjectSetKey( object, columnName, jsonNewObject(dt_string) );
4963 case DBI_TYPE_BINARY :
4964 osrfLogError( OSRF_LOG_MARK,
4965 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4973 // Interpret a string as true or false
4974 static int str_is_true( const char* str ) {
4975 if( NULL == str || strcasecmp( str, "true" ) )
4981 // Interpret a jsonObject as true or false
4982 static int obj_is_true( const jsonObject* obj ) {
4985 else switch( obj->type )
4993 if( strcasecmp( obj->value.s, "true" ) )
4997 case JSON_NUMBER : // Support 1/0 for perl's sake
4998 if( jsonObjectGetNumber( obj ) == 1.0 )
5007 // Translate a numeric code into a text string identifying a type of
5008 // jsonObject. To be used for building error messages.
5009 static const char* json_type( int code ) {
5015 return "JSON_ARRAY";
5017 return "JSON_STRING";
5019 return "JSON_NUMBER";
5025 return "(unrecognized)";
5029 // Extract the "primitive" attribute from an IDL field definition.
5030 // If we haven't initialized the app, then we must be running in
5031 // some kind of testbed. In that case, default to "string".
5032 static const char* get_primitive( osrfHash* field ) {
5033 const char* s = osrfHashGet( field, "primitive" );
5035 if( child_initialized )
5038 "%s ERROR No \"datatype\" attribute for field \"%s\"",
5040 osrfHashGet( field, "name" )
5048 // Extract the "datatype" attribute from an IDL field definition.
5049 // If we haven't initialized the app, then we must be running in
5050 // some kind of testbed. In that case, default to to NUMERIC,
5051 // since we look at the datatype only for numbers.
5052 static const char* get_datatype( osrfHash* field ) {
5053 const char* s = osrfHashGet( field, "datatype" );
5055 if( child_initialized )
5058 "%s ERROR No \"datatype\" attribute for field \"%s\"",
5060 osrfHashGet( field, "name" )
5069 If the input string is potentially a valid SQL identifier, return 1.
5072 Purpose: to prevent certain kinds of SQL injection. To that end we
5073 don't necessarily need to follow all the rules exactly, such as requiring
5074 that the first character not be a digit.
5076 We allow leading and trailing white space. In between, we do not allow
5077 punctuation (except for underscores and dollar signs), control
5078 characters, or embedded white space.
5080 More pedantically we should allow quoted identifiers containing arbitrary
5081 characters, but for the foreseeable future such quoted identifiers are not
5082 likely to be an issue.
5084 static int is_identifier( const char* s) {
5088 // Skip leading white space
5089 while( isspace( (unsigned char) *s ) )
5093 return 0; // Nothing but white space? Not okay.
5095 // Check each character until we reach white space or
5096 // end-of-string. Letters, digits, underscores, and
5097 // dollar signs are okay. With the exception of periods
5098 // (as in schema.identifier), control characters and other
5099 // punctuation characters are not okay. Anything else
5100 // is okay -- it could for example be part of a multibyte
5101 // UTF8 character such as a letter with diacritical marks,
5102 // and those are allowed.
5104 if( isalnum( (unsigned char) *s )
5108 ; // Fine; keep going
5109 else if( ispunct( (unsigned char) *s )
5110 || iscntrl( (unsigned char) *s ) )
5113 } while( *s && ! isspace( (unsigned char) *s ) );
5115 // If we found any white space in the above loop,
5116 // the rest had better be all white space.
5118 while( isspace( (unsigned char) *s ) )
5122 return 0; // White space was embedded within non-white space
5128 Determine whether to accept a character string as a comparison operator.
5129 Return 1 if it's good, or 0 if it's bad.
5131 We don't validate it for real. We just make sure that it doesn't contain
5132 any semicolons or white space (with a special exception for the
5133 "SIMILAR TO" operator). The idea is to block certain kinds of SQL
5134 injection. If it has no semicolons or white space but it's still not a
5135 valid operator, then the database will complain.
5137 Another approach would be to compare the string against a short list of
5138 approved operators. We don't do that because we want to allow custom
5139 operators like ">100*", which would be difficult or impossible to
5140 express otherwise in a JSON query.
5142 static int is_good_operator( const char* op ) {
5143 if( !op ) return 0; // Sanity check
5147 if( isspace( (unsigned char) *s ) ) {
5148 // Special exception for SIMILAR TO. Someday we might make
5149 // exceptions for IS DISTINCT FROM and IS NOT DISTINCT FROM.
5150 if( !strcasecmp( op, "similar to" ) )
5155 else if( ';' == *s )