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* const 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 static const char* global_method[] = {
178 const int global_method_count
179 = sizeof( global_method ) / sizeof ( global_method[0] );
183 osrfStringArray* classes = osrfHashKeys( oilsIDL() );
184 osrfLogDebug(OSRF_LOG_MARK, "%d classes loaded", classes->size );
185 osrfLogDebug(OSRF_LOG_MARK,
186 "At most %d methods will be generated", classes->size * global_method_count);
188 // For each class in IDL...
189 while ( (classname = osrfStringArrayGetString(classes, c_index++)) ) {
190 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
192 osrfHash* idlClass = osrfHashGet(oilsIDL(), classname);
194 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), MODULENAME )) {
195 osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on", MODULENAME, classname);
199 if ( str_is_true( osrfHashGet(idlClass, "virtual") ) ) {
200 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
204 // Look up some other attributes of the current class
205 const char* idlClass_fieldmapper = osrfHashGet(idlClass, "fieldmapper");
206 if( !idlClass_fieldmapper ) {
207 osrfLogDebug( OSRF_LOG_MARK, "Skipping class \"%s\"; no fieldmapper in IDL", classname );
212 osrfHash* idlClass_permacrud = osrfHashGet(idlClass, "permacrud");
213 if (!idlClass_permacrud) {
214 osrfLogDebug( OSRF_LOG_MARK, "Skipping class \"%s\"; no permacrud in IDL", classname );
218 const char* readonly = osrfHashGet(idlClass, "readonly");
221 for( i = 0; i < global_method_count; ++i ) { // for each global method
222 const char* method_type = global_method[ i ];
223 osrfLogDebug(OSRF_LOG_MARK,
224 "Using files to build %s class methods for %s", method_type, classname);
227 const char* tmp_method = method_type;
228 if ( *tmp_method == 'i' || *tmp_method == 's') {
229 tmp_method = "retrieve";
231 if (!osrfHashGet( idlClass_permacrud, tmp_method )) continue;
234 if ( str_is_true( readonly ) &&
235 ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd')
238 buffer_reset( method_name );
240 buffer_fadd(method_name, "%s.%s.%s", MODULENAME, method_type, classname);
244 char* _fm = strdup( idlClass_fieldmapper );
245 part = strtok_r(_fm, ":", &st_tmp);
247 buffer_fadd(method_name, "%s.direct.%s", MODULENAME, part);
249 while ((part = strtok_r(NULL, ":", &st_tmp))) {
250 OSRF_BUFFER_ADD_CHAR(method_name, '.');
251 OSRF_BUFFER_ADD(method_name, part);
253 OSRF_BUFFER_ADD_CHAR(method_name, '.');
254 OSRF_BUFFER_ADD(method_name, method_type);
258 char* method = buffer_data(method_name);
261 if (*method_type == 'i' || *method_type == 's') {
262 flags = flags | OSRF_METHOD_STREAMING;
265 osrfHash* method_meta = osrfNewHash();
266 osrfHashSet( method_meta, idlClass, "class");
267 osrfHashSet( method_meta, method, "methodname" );
268 osrfHashSet( method_meta, strdup(method_type), "methodtype" );
270 osrfAppRegisterExtendedMethod(
273 "dispatchCRUDMethod",
281 } // end for each global method
282 } // end for each class in IDL
284 buffer_free( method_name );
285 osrfStringArrayFree( classes );
290 static char* getSourceDefinition( osrfHash* class ) {
292 char* tabledef = osrfHashGet(class, "tablename");
295 tabledef = strdup(tabledef);
297 tabledef = osrfHashGet(class, "source_definition");
299 growing_buffer* tablebuf = buffer_init(128);
300 buffer_fadd( tablebuf, "(%s)", tabledef );
301 tabledef = buffer_release(tablebuf);
303 const char* classname = osrfHashGet( class, "classname" );
308 "%s ERROR No tablename or source_definition for class \"%s\"",
319 * Connects to the database
321 int osrfAppChildInit() {
323 osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
324 dbi_initialize(NULL);
325 osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
327 char* driver = osrf_settings_host_value("/apps/%s/app_settings/driver", MODULENAME);
328 char* user = osrf_settings_host_value("/apps/%s/app_settings/database/user", MODULENAME);
329 char* host = osrf_settings_host_value("/apps/%s/app_settings/database/host", MODULENAME);
330 char* port = osrf_settings_host_value("/apps/%s/app_settings/database/port", MODULENAME);
331 char* db = osrf_settings_host_value("/apps/%s/app_settings/database/db", MODULENAME);
332 char* pw = osrf_settings_host_value("/apps/%s/app_settings/database/pw", MODULENAME);
333 char* md = osrf_settings_host_value("/apps/%s/app_settings/max_query_recursion", MODULENAME);
335 osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
336 writehandle = dbi_conn_new(driver);
339 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
342 osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
344 osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database. host=%s, "
345 "port=%s, user=%s, pw=%s, db=%s", MODULENAME, host, port, user, pw, db );
347 if(host) dbi_conn_set_option(writehandle, "host", host );
348 if(port) dbi_conn_set_option_numeric( writehandle, "port", atoi(port) );
349 if(user) dbi_conn_set_option(writehandle, "username", user);
350 if(pw) dbi_conn_set_option(writehandle, "password", pw );
351 if(db) dbi_conn_set_option(writehandle, "dbname", db );
353 if(md) max_flesh_depth = atoi(md);
354 if(max_flesh_depth < 0) max_flesh_depth = 1;
355 if(max_flesh_depth > 1000) max_flesh_depth = 1000;
364 if (dbi_conn_connect(writehandle) < 0) {
366 if (dbi_conn_connect(writehandle) < 0) {
367 dbi_conn_error(writehandle, &err);
368 osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
373 osrfLogInfo(OSRF_LOG_MARK, "%s successfully connected to the database", MODULENAME);
377 osrfStringArray* classes = osrfHashKeys( oilsIDL() );
379 while ( (classname = osrfStringArrayGetString(classes, i++)) ) {
380 osrfHash* class = osrfHashGet( oilsIDL(), classname );
381 osrfHash* fields = osrfHashGet( class, "fields" );
383 if( str_is_true( osrfHashGet(class, "virtual") ) ) {
384 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
388 char* tabledef = getSourceDefinition(class);
390 tabledef = strdup( "(null)" );
392 growing_buffer* sql_buf = buffer_init(32);
393 buffer_fadd( sql_buf, "SELECT * FROM %s AS x WHERE 1=0;", tabledef );
397 char* sql = buffer_release(sql_buf);
398 osrfLogDebug(OSRF_LOG_MARK, "%s Investigatory SQL = %s", MODULENAME, sql);
400 dbi_result result = dbi_conn_query(writehandle, sql);
406 const char* columnName;
408 while( (columnName = dbi_result_get_field_name(result, columnIndex)) ) {
410 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
412 /* fetch the fieldmapper index */
413 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
415 osrfLogDebug(OSRF_LOG_MARK, "Found [%s] in IDL hash...", (char*)columnName);
417 /* determine the field type and storage attributes */
419 switch( dbi_result_get_field_type_idx(result, columnIndex) ) {
421 case DBI_TYPE_INTEGER : {
423 if ( !osrfHashGet(_f, "primitive") )
424 osrfHashSet(_f,"number", "primitive");
426 int attr = dbi_result_get_field_attribs_idx(result, columnIndex);
427 if( attr & DBI_INTEGER_SIZE8 )
428 osrfHashSet(_f,"INT8", "datatype");
430 osrfHashSet(_f,"INT", "datatype");
433 case DBI_TYPE_DECIMAL :
434 if ( !osrfHashGet(_f, "primitive") )
435 osrfHashSet(_f,"number", "primitive");
437 osrfHashSet(_f,"NUMERIC", "datatype");
440 case DBI_TYPE_STRING :
441 if ( !osrfHashGet(_f, "primitive") )
442 osrfHashSet(_f,"string", "primitive");
443 osrfHashSet(_f,"TEXT", "datatype");
446 case DBI_TYPE_DATETIME :
447 if ( !osrfHashGet(_f, "primitive") )
448 osrfHashSet(_f,"string", "primitive");
450 osrfHashSet(_f,"TIMESTAMP", "datatype");
453 case DBI_TYPE_BINARY :
454 if ( !osrfHashGet(_f, "primitive") )
455 osrfHashSet(_f,"string", "primitive");
457 osrfHashSet(_f,"BYTEA", "datatype");
462 "Setting [%s] to primitive [%s] and datatype [%s]...",
464 osrfHashGet(_f, "primitive"),
465 osrfHashGet(_f, "datatype")
469 } // end while loop for traversing result
470 dbi_result_free(result);
472 osrfLogDebug(OSRF_LOG_MARK, "No data found for class [%s]...", (char*)classname);
476 osrfStringArrayFree(classes);
478 child_initialized = 1;
483 This function is a sleazy hack intended *only* for testing and
484 debugging. Any real server process should initialize the
485 database connection by calling osrfAppChildInit().
487 void set_cstore_dbi_conn( dbi_conn conn ) {
488 dbhandle = writehandle = conn;
491 void userDataFree( void* blob ) {
492 osrfHashFree( (osrfHash*)blob );
496 static void sessionDataFree( char* key, void* item ) {
497 if (!(strcmp(key,"xact_id"))) {
499 dbi_conn_query(writehandle, "ROLLBACK;");
506 int beginTransaction ( osrfMethodContext* ctx ) {
507 if(osrfMethodVerifyContext( ctx )) {
508 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
513 jsonObject* user = verifyUserPCRUD( ctx );
514 if (!user) return -1;
515 jsonObjectFree(user);
518 dbi_result result = dbi_conn_query(writehandle, "START TRANSACTION;");
520 osrfLogError(OSRF_LOG_MARK, "%s: Error starting transaction", MODULENAME );
521 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error starting transaction" );
524 jsonObject* ret = jsonNewObject(ctx->session->session_id);
525 osrfAppRespondComplete( ctx, ret );
528 if (!ctx->session->userData) {
529 ctx->session->userData = osrfNewHash();
530 osrfHashSetCallback((osrfHash*)ctx->session->userData, &sessionDataFree);
533 osrfHashSet( (osrfHash*)ctx->session->userData, strdup( ctx->session->session_id ), "xact_id" );
534 ctx->session->userDataFree = &userDataFree;
540 int setSavepoint ( osrfMethodContext* ctx ) {
541 if(osrfMethodVerifyContext( ctx )) {
542 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
549 jsonObject* user = verifyUserPCRUD( ctx );
550 if (!user) return -1;
551 jsonObjectFree(user);
554 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
555 osrfAppSessionStatus(
557 OSRF_STATUS_INTERNALSERVERERROR,
558 "osrfMethodException",
560 "No active transaction -- required for savepoints"
565 const char* spName = jsonObjectGetString(jsonObjectGetIndex(ctx->params, spNamePos));
567 dbi_result result = dbi_conn_queryf(writehandle, "SAVEPOINT \"%s\";", spName);
571 "%s: Error creating savepoint %s in transaction %s",
574 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
576 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR,
577 "osrfMethodException", ctx->request, "Error creating savepoint" );
580 jsonObject* ret = jsonNewObject(spName);
581 osrfAppRespondComplete( ctx, ret );
587 int releaseSavepoint ( osrfMethodContext* ctx ) {
588 if(osrfMethodVerifyContext( ctx )) {
589 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
596 jsonObject* user = verifyUserPCRUD( ctx );
597 if (!user) return -1;
598 jsonObjectFree(user);
601 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
602 osrfAppSessionStatus(
604 OSRF_STATUS_INTERNALSERVERERROR,
605 "osrfMethodException",
607 "No active transaction -- required for savepoints"
612 const char* spName = jsonObjectGetString( jsonObjectGetIndex(ctx->params, spNamePos) );
614 dbi_result result = dbi_conn_queryf(writehandle, "RELEASE SAVEPOINT \"%s\";", spName);
618 "%s: Error releasing savepoint %s in transaction %s",
621 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
623 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR,
624 "osrfMethodException", ctx->request, "Error releasing savepoint" );
627 jsonObject* ret = jsonNewObject(spName);
628 osrfAppRespondComplete( ctx, ret );
634 int rollbackSavepoint ( osrfMethodContext* ctx ) {
635 if(osrfMethodVerifyContext( ctx )) {
636 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
643 jsonObject* user = verifyUserPCRUD( ctx );
644 if (!user) return -1;
645 jsonObjectFree(user);
648 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
649 osrfAppSessionStatus(
651 OSRF_STATUS_INTERNALSERVERERROR,
652 "osrfMethodException",
654 "No active transaction -- required for savepoints"
659 const char* spName = jsonObjectGetString( jsonObjectGetIndex(ctx->params, spNamePos) );
661 dbi_result result = dbi_conn_queryf(writehandle, "ROLLBACK TO SAVEPOINT \"%s\";", spName);
665 "%s: Error rolling back savepoint %s in transaction %s",
668 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
670 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR,
671 "osrfMethodException", ctx->request, "Error rolling back savepoint" );
674 jsonObject* ret = jsonNewObject(spName);
675 osrfAppRespondComplete( ctx, ret );
681 int commitTransaction ( osrfMethodContext* ctx ) {
682 if(osrfMethodVerifyContext( ctx )) {
683 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
688 jsonObject* user = verifyUserPCRUD( ctx );
689 if (!user) return -1;
690 jsonObjectFree(user);
693 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
694 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to commit" );
698 dbi_result result = dbi_conn_query(writehandle, "COMMIT;");
700 osrfLogError(OSRF_LOG_MARK, "%s: Error committing transaction", MODULENAME );
701 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error committing transaction" );
704 osrfHashRemove(ctx->session->userData, "xact_id");
705 jsonObject* ret = jsonNewObject(ctx->session->session_id);
706 osrfAppRespondComplete( ctx, ret );
712 int rollbackTransaction ( osrfMethodContext* ctx ) {
713 if(osrfMethodVerifyContext( ctx )) {
714 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
719 jsonObject* user = verifyUserPCRUD( ctx );
720 if (!user) return -1;
721 jsonObjectFree(user);
724 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
725 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to roll back" );
729 dbi_result result = dbi_conn_query(writehandle, "ROLLBACK;");
731 osrfLogError(OSRF_LOG_MARK, "%s: Error rolling back transaction", MODULENAME );
732 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error rolling back transaction" );
735 osrfHashRemove(ctx->session->userData, "xact_id");
736 jsonObject* ret = jsonNewObject(ctx->session->session_id);
737 osrfAppRespondComplete( ctx, ret );
743 int dispatchCRUDMethod ( osrfMethodContext* ctx ) {
744 if(osrfMethodVerifyContext( ctx )) {
745 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
749 osrfHash* meta = (osrfHash*) ctx->method->userData;
750 osrfHash* class_obj = osrfHashGet( meta, "class" );
754 const char* methodtype = osrfHashGet(meta, "methodtype");
755 jsonObject * obj = NULL;
757 if (!strcmp(methodtype, "create")) {
758 obj = doCreate(ctx, &err);
759 osrfAppRespondComplete( ctx, obj );
761 else if (!strcmp(methodtype, "retrieve")) {
762 obj = doRetrieve(ctx, &err);
763 osrfAppRespondComplete( ctx, obj );
765 else if (!strcmp(methodtype, "update")) {
766 obj = doUpdate(ctx, &err);
767 osrfAppRespondComplete( ctx, obj );
769 else if (!strcmp(methodtype, "delete")) {
770 obj = doDelete(ctx, &err);
771 osrfAppRespondComplete( ctx, obj );
773 else if (!strcmp(methodtype, "search")) {
775 jsonObject* where_clause;
776 jsonObject* rest_of_query;
779 where_clause = jsonObjectGetIndex( ctx->params, 1 );
780 rest_of_query = jsonObjectGetIndex( ctx->params, 2 );
782 where_clause = jsonObjectGetIndex( ctx->params, 0 );
783 rest_of_query = jsonObjectGetIndex( ctx->params, 1 );
786 obj = doFieldmapperSearch( ctx, class_obj, where_clause, rest_of_query, &err );
791 unsigned long res_idx = 0;
792 while((cur = jsonObjectGetIndex( obj, res_idx++ ) )) {
794 if(!verifyObjectPCRUD(ctx, cur)) continue;
796 osrfAppRespond( ctx, cur );
798 osrfAppRespondComplete( ctx, NULL );
800 } else if (!strcmp(methodtype, "id_list")) {
802 jsonObject* where_clause;
803 jsonObject* rest_of_query;
805 // We use the where clause without change. But we need
806 // to massage the rest of the query, so we work with a copy
807 // of it instead of modifying the original.
809 where_clause = jsonObjectGetIndex( ctx->params, 1 );
810 rest_of_query = jsonObjectClone( jsonObjectGetIndex( ctx->params, 2 ) );
812 where_clause = jsonObjectGetIndex( ctx->params, 0 );
813 rest_of_query = jsonObjectClone( jsonObjectGetIndex( ctx->params, 1 ) );
816 if ( rest_of_query ) {
817 jsonObjectRemoveKey( rest_of_query, "select" );
818 jsonObjectRemoveKey( rest_of_query, "no_i18n" );
819 jsonObjectRemoveKey( rest_of_query, "flesh" );
820 jsonObjectRemoveKey( rest_of_query, "flesh_columns" );
822 rest_of_query = jsonNewObjectType( JSON_HASH );
825 jsonObjectSetKey( rest_of_query, "no_i18n", jsonNewBoolObject( 1 ) );
827 // Build a SELECT list containing just the primary key,
828 // i.e. like { "classname":["keyname"] }
829 jsonObject* col_list_obj = jsonNewObjectType( JSON_ARRAY );
830 jsonObjectPush( col_list_obj, // Load array with name of primary key
831 jsonNewObject( osrfHashGet( class_obj, "primarykey" ) ) );
832 jsonObject* select_clause = jsonNewObjectType( JSON_HASH );
833 jsonObjectSetKey( select_clause, osrfHashGet( class_obj, "classname" ), col_list_obj );
835 jsonObjectSetKey( rest_of_query, "select", select_clause );
837 obj = doFieldmapperSearch( ctx, class_obj, where_clause, rest_of_query, &err );
839 jsonObjectFree( rest_of_query );
843 unsigned long res_idx = 0;
844 while((cur = jsonObjectGetIndex( obj, res_idx++ ) )) {
846 if(!verifyObjectPCRUD(ctx, cur)) continue;
850 oilsFMGetObject( cur, osrfHashGet( class_obj, "primarykey" ) )
853 osrfAppRespondComplete( ctx, NULL );
856 osrfAppRespondComplete( ctx, obj );
864 static int verifyObjectClass ( osrfMethodContext* ctx, const jsonObject* param ) {
867 osrfHash* meta = (osrfHash*) ctx->method->userData;
868 osrfHash* class = osrfHashGet( meta, "class" );
870 if (!param->classname || (strcmp( osrfHashGet(class, "classname"), param->classname ))) {
872 const char* temp_classname = param->classname;
873 if( ! temp_classname )
874 temp_classname = "(null)";
876 growing_buffer* msg = buffer_init(128);
879 "%s: %s method for type %s was passed a %s",
881 osrfHashGet(meta, "methodtype"),
882 osrfHashGet(class, "classname"),
886 char* m = buffer_release(msg);
887 osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException", ctx->request, m );
895 ret = verifyObjectPCRUD( ctx, param );
903 static jsonObject* verifyUserPCRUD( osrfMethodContext* ctx ) {
904 const char* auth = jsonObjectGetString( jsonObjectGetIndex( ctx->params, 0 ) );
905 jsonObject* auth_object = jsonNewObject(auth);
906 jsonObject* user = oilsUtilsQuickReq("open-ils.auth","open-ils.auth.session.retrieve", auth_object);
907 jsonObjectFree(auth_object);
909 if (!user->classname || strcmp(user->classname, "au")) {
911 growing_buffer* msg = buffer_init(128);
914 "%s: permacrud received a bad auth token: %s",
919 char* m = buffer_release(msg);
920 osrfAppSessionStatus( ctx->session, OSRF_STATUS_UNAUTHORIZED, "osrfMethodException", ctx->request, m );
923 jsonObjectFree(user);
931 static int verifyObjectPCRUD ( osrfMethodContext* ctx, const jsonObject* obj ) {
933 dbhandle = writehandle;
935 osrfHash* method_metadata = (osrfHash*) ctx->method->userData;
936 osrfHash* class = osrfHashGet( method_metadata, "class" );
937 const char* method_type = osrfHashGet( method_metadata, "methodtype" );
940 if ( ( *method_type == 's' || *method_type == 'i' ) ) {
941 method_type = "retrieve"; // search and id_list are equivalant to retrieve for this
942 } else if ( *method_type == 'u' || *method_type == 'd' ) {
943 fetch = 1; // MUST go to the db for the object for update and delete
946 osrfHash* pcrud = osrfHashGet( osrfHashGet(class, "permacrud"), method_type );
949 // No permacrud for this method type on this class
951 growing_buffer* msg = buffer_init(128);
954 "%s: %s on class %s has no permacrud IDL entry",
956 osrfHashGet(method_metadata, "methodtype"),
957 osrfHashGet(class, "classname")
960 char* m = buffer_release(msg);
961 osrfAppSessionStatus( ctx->session, OSRF_STATUS_FORBIDDEN, "osrfMethodException", ctx->request, m );
968 jsonObject* user = verifyUserPCRUD( ctx );
971 int userid = atoi( oilsFMGetString( user, "id" ) );
972 jsonObjectFree(user);
974 osrfStringArray* permission = osrfHashGet(pcrud, "permission");
975 osrfStringArray* local_context = osrfHashGet(pcrud, "local_context");
976 osrfHash* foreign_context = osrfHashGet(pcrud, "foreign_context");
978 osrfStringArray* context_org_array = osrfNewStringArray(1);
981 char* pkey_value = NULL;
982 if ( str_is_true( osrfHashGet(pcrud, "global_required") ) ) {
983 osrfLogDebug( OSRF_LOG_MARK, "global-level permissions required, fetching top of the org tree" );
985 // check for perm at top of org tree
986 jsonObject* _tmp_params = single_hash( "parent_ou", NULL );
987 jsonObject* _list = doFieldmapperSearch(ctx, osrfHashGet( oilsIDL(), "aou" ),
988 _tmp_params, NULL, &err);
989 jsonObjectFree(_tmp_params);
991 jsonObject* _tree_top = jsonObjectGetIndex(_list, 0);
994 jsonObjectFree(_list);
996 growing_buffer* msg = buffer_init(128);
997 OSRF_BUFFER_ADD( msg, MODULENAME );
998 OSRF_BUFFER_ADD( msg,
999 ": Internal error, could not find the top of the org tree (parent_ou = NULL)" );
1001 char* m = buffer_release(msg);
1002 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, m );
1008 osrfStringArrayAdd( context_org_array, oilsFMGetString( _tree_top, "id" ) );
1009 osrfLogDebug( OSRF_LOG_MARK, "top of the org tree is %s", osrfStringArrayGetString(context_org_array, 0) );
1011 jsonObjectFree(_list);
1014 osrfLogDebug( OSRF_LOG_MARK, "global-level permissions not required, fetching context org ids" );
1015 const char* pkey = osrfHashGet(class, "primarykey");
1016 jsonObject *param = NULL;
1018 if (obj->classname) {
1019 pkey_value = oilsFMGetString( obj, pkey );
1020 if (!fetch) param = jsonObjectClone(obj);
1021 osrfLogDebug( OSRF_LOG_MARK, "Object supplied, using primary key value of %s", pkey_value );
1023 pkey_value = jsonObjectToSimpleString( obj );
1025 osrfLogDebug( OSRF_LOG_MARK, "Object not supplied, using primary key value of %s and retrieving from the database", pkey_value );
1029 jsonObject* _tmp_params = single_hash( pkey, pkey_value );
1030 jsonObject* _list = doFieldmapperSearch( ctx, class, _tmp_params, NULL, &err );
1031 jsonObjectFree(_tmp_params);
1033 param = jsonObjectExtractIndex(_list, 0);
1034 jsonObjectFree(_list);
1038 osrfLogDebug( OSRF_LOG_MARK, "Object not found in the database with primary key %s of %s", pkey, pkey_value );
1040 growing_buffer* msg = buffer_init(128);
1043 "%s: no object found with primary key %s of %s",
1049 char* m = buffer_release(msg);
1050 osrfAppSessionStatus(
1052 OSRF_STATUS_INTERNALSERVERERROR,
1053 "osrfMethodException",
1059 if (pkey_value) free(pkey_value);
1064 if (local_context->size > 0) {
1065 osrfLogDebug( OSRF_LOG_MARK, "%d class-local context field(s) specified", local_context->size);
1067 char* lcontext = NULL;
1068 while ( (lcontext = osrfStringArrayGetString(local_context, i++)) ) {
1069 osrfStringArrayAdd( context_org_array, oilsFMGetString( param, lcontext ) );
1072 "adding class-local field %s (value: %s) to the context org list",
1074 osrfStringArrayGetString(context_org_array, context_org_array->size - 1)
1079 osrfStringArray* class_list;
1081 if (foreign_context) {
1082 class_list = osrfHashKeys( foreign_context );
1083 osrfLogDebug( OSRF_LOG_MARK, "%d foreign context classes(s) specified", class_list->size);
1085 if (class_list->size > 0) {
1088 char* class_name = NULL;
1089 while ( (class_name = osrfStringArrayGetString(class_list, i++)) ) {
1090 osrfHash* fcontext = osrfHashGet(foreign_context, class_name);
1094 "%d foreign context fields(s) specified for class %s",
1095 ((osrfStringArray*)osrfHashGet(fcontext,"context"))->size,
1099 char* foreign_pkey = osrfHashGet(fcontext, "field");
1100 char* foreign_pkey_value = oilsFMGetString(param, osrfHashGet(fcontext, "fkey"));
1102 jsonObject* _tmp_params = single_hash( foreign_pkey, foreign_pkey_value );
1104 jsonObject* _list = doFieldmapperSearch(
1105 ctx, osrfHashGet( oilsIDL(), class_name ), _tmp_params, NULL, &err );
1107 jsonObject* _fparam = jsonObjectClone(jsonObjectGetIndex(_list, 0));
1108 jsonObjectFree(_tmp_params);
1109 jsonObjectFree(_list);
1111 osrfStringArray* jump_list = osrfHashGet(fcontext, "jump");
1113 if (_fparam && jump_list) {
1116 while ( (flink = osrfStringArrayGetString(jump_list, k++)) && _fparam ) {
1117 free(foreign_pkey_value);
1119 osrfHash* foreign_link_hash = oilsIDLFindPath( "/%s/links/%s", _fparam->classname, flink );
1121 foreign_pkey_value = oilsFMGetString(_fparam, flink);
1122 foreign_pkey = osrfHashGet( foreign_link_hash, "key" );
1124 _tmp_params = single_hash( foreign_pkey, foreign_pkey_value );
1126 _list = doFieldmapperSearch(
1128 osrfHashGet( oilsIDL(), osrfHashGet( foreign_link_hash, "class" ) ),
1134 _fparam = jsonObjectClone(jsonObjectGetIndex(_list, 0));
1135 jsonObjectFree(_tmp_params);
1136 jsonObjectFree(_list);
1143 growing_buffer* msg = buffer_init(128);
1146 "%s: no object found with primary key %s of %s",
1152 char* m = buffer_release(msg);
1153 osrfAppSessionStatus(
1155 OSRF_STATUS_INTERNALSERVERERROR,
1156 "osrfMethodException",
1162 osrfStringArrayFree(class_list);
1163 free(foreign_pkey_value);
1164 jsonObjectFree(param);
1169 free(foreign_pkey_value);
1172 char* foreign_field = NULL;
1173 while ( (foreign_field = osrfStringArrayGetString(osrfHashGet(fcontext,"context"), j++)) ) {
1174 osrfStringArrayAdd( context_org_array, oilsFMGetString( _fparam, foreign_field ) );
1177 "adding foreign class %s field %s (value: %s) to the context org list",
1180 osrfStringArrayGetString(context_org_array, context_org_array->size - 1)
1184 jsonObjectFree(_fparam);
1187 osrfStringArrayFree(class_list);
1191 jsonObjectFree(param);
1194 char* context_org = NULL;
1198 if (permission->size == 0) {
1199 osrfLogDebug( OSRF_LOG_MARK, "No permission specified for this action, passing through" );
1204 while ( (perm = osrfStringArrayGetString(permission, i++)) ) {
1206 while ( (context_org = osrfStringArrayGetString(context_org_array, j++)) ) {
1212 "Checking object permission [%s] for user %d on object %s (class %s) at org %d",
1216 osrfHashGet(class, "classname"),
1220 result = dbi_conn_queryf(
1222 "SELECT permission.usr_has_object_perm(%d, '%s', '%s', '%s', %d) AS has_perm;",
1225 osrfHashGet(class, "classname"),
1233 "Recieved a result for object permission [%s] for user %d on object %s (class %s) at org %d",
1237 osrfHashGet(class, "classname"),
1241 if (dbi_result_first_row(result)) {
1242 jsonObject* return_val = oilsMakeJSONFromResult( result );
1243 const char* has_perm = jsonObjectGetString( jsonObjectGetKeyConst(return_val, "has_perm") );
1247 "Status of object permission [%s] for user %d on object %s (class %s) at org %d is %s",
1251 osrfHashGet(class, "classname"),
1256 if ( *has_perm == 't' ) OK = 1;
1257 jsonObjectFree(return_val);
1260 dbi_result_free(result);
1265 osrfLogDebug( OSRF_LOG_MARK, "Checking non-object permission [%s] for user %d at org %d", perm, userid, atoi(context_org) );
1266 result = dbi_conn_queryf(
1268 "SELECT permission.usr_has_perm(%d, '%s', %d) AS has_perm;",
1275 osrfLogDebug( OSRF_LOG_MARK, "Received a result for permission [%s] for user %d at org %d",
1276 perm, userid, atoi(context_org) );
1277 if ( dbi_result_first_row(result) ) {
1278 jsonObject* return_val = oilsMakeJSONFromResult( result );
1279 const char* has_perm = jsonObjectGetString( jsonObjectGetKeyConst(return_val, "has_perm") );
1280 osrfLogDebug( OSRF_LOG_MARK, "Status of permission [%s] for user %d at org %d is [%s]",
1281 perm, userid, atoi(context_org), has_perm );
1282 if ( *has_perm == 't' ) OK = 1;
1283 jsonObjectFree(return_val);
1286 dbi_result_free(result);
1294 if (pkey_value) free(pkey_value);
1295 osrfStringArrayFree(context_org_array);
1301 Utility function: create a JSON_HASH with a single key/value pair.
1302 This function is equivalent to:
1304 jsonParseStringFmt( "{\"%s\":\"%s\"}", key, value )
1306 or, if value is NULL:
1308 jsonParseStringFmt( "{\"%s\":null}", key )
1310 ...but faster because it doesn't create and parse a JSON string.
1312 static jsonObject* single_hash( const char* key, const char* value ) {
1314 if( ! key ) key = "";
1316 jsonObject* hash = jsonNewObjectType( JSON_HASH );
1317 jsonObjectSetKey( hash, key, jsonNewObject( value ) );
1323 static jsonObject* doCreate(osrfMethodContext* ctx, int* err ) {
1325 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
1327 jsonObject* target = jsonObjectGetIndex( ctx->params, 1 );
1328 jsonObject* options = jsonObjectGetIndex( ctx->params, 2 );
1330 jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
1331 jsonObject* options = jsonObjectGetIndex( ctx->params, 1 );
1334 if (!verifyObjectClass(ctx, target)) {
1339 osrfLogDebug( OSRF_LOG_MARK, "Object seems to be of the correct type" );
1341 char* trans_id = NULL;
1342 if( ctx->session && ctx->session->userData )
1343 trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
1346 osrfLogError( OSRF_LOG_MARK, "No active transaction -- required for CREATE" );
1348 osrfAppSessionStatus(
1350 OSRF_STATUS_BADREQUEST,
1351 "osrfMethodException",
1353 "No active transaction -- required for CREATE"
1359 // The following test is harmless but redundant. If a class is
1360 // readonly, we don't register a create method for it.
1361 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
1362 osrfAppSessionStatus(
1364 OSRF_STATUS_BADREQUEST,
1365 "osrfMethodException",
1367 "Cannot INSERT readonly class"
1373 // Set the last_xact_id
1374 int index = oilsIDL_ntop( target->classname, "last_xact_id" );
1376 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
1377 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
1380 osrfLogDebug( OSRF_LOG_MARK, "There is a transaction running..." );
1382 dbhandle = writehandle;
1384 osrfHash* fields = osrfHashGet(meta, "fields");
1385 char* pkey = osrfHashGet(meta, "primarykey");
1386 char* seq = osrfHashGet(meta, "sequence");
1388 growing_buffer* table_buf = buffer_init(128);
1389 growing_buffer* col_buf = buffer_init(128);
1390 growing_buffer* val_buf = buffer_init(128);
1392 OSRF_BUFFER_ADD(table_buf, "INSERT INTO ");
1393 OSRF_BUFFER_ADD(table_buf, osrfHashGet(meta, "tablename"));
1394 OSRF_BUFFER_ADD_CHAR( col_buf, '(' );
1395 buffer_add(val_buf,"VALUES (");
1401 osrfStringArray* field_list = osrfHashKeys( fields );
1402 while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
1404 osrfHash* field = osrfHashGet( fields, field_name );
1406 if( str_is_true( osrfHashGet( field, "virtual" ) ) )
1409 const jsonObject* field_object = oilsFMGetObject( target, field_name );
1412 if (field_object && field_object->classname) {
1413 value = oilsFMGetString(
1415 (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
1418 value = jsonObjectToSimpleString( field_object );
1425 OSRF_BUFFER_ADD_CHAR( col_buf, ',' );
1426 OSRF_BUFFER_ADD_CHAR( val_buf, ',' );
1429 buffer_add(col_buf, field_name);
1431 if (!field_object || field_object->type == JSON_NULL) {
1432 buffer_add( val_buf, "DEFAULT" );
1434 } else if ( !strcmp(get_primitive( field ), "number") ) {
1435 const char* numtype = get_datatype( field );
1436 if ( !strcmp( numtype, "INT8") ) {
1437 buffer_fadd( val_buf, "%lld", atoll(value) );
1439 } else if ( !strcmp( numtype, "INT") ) {
1440 buffer_fadd( val_buf, "%d", atoi(value) );
1442 } else if ( !strcmp( numtype, "NUMERIC") ) {
1443 buffer_fadd( val_buf, "%f", atof(value) );
1446 if ( dbi_conn_quote_string(writehandle, &value) ) {
1447 OSRF_BUFFER_ADD( val_buf, value );
1450 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
1451 osrfAppSessionStatus(
1453 OSRF_STATUS_INTERNALSERVERERROR,
1454 "osrfMethodException",
1456 "Error quoting string -- please see the error log for more details"
1459 buffer_free(table_buf);
1460 buffer_free(col_buf);
1461 buffer_free(val_buf);
1472 OSRF_BUFFER_ADD_CHAR( col_buf, ')' );
1473 OSRF_BUFFER_ADD_CHAR( val_buf, ')' );
1475 char* table_str = buffer_release(table_buf);
1476 char* col_str = buffer_release(col_buf);
1477 char* val_str = buffer_release(val_buf);
1478 growing_buffer* sql = buffer_init(128);
1479 buffer_fadd( sql, "%s %s %s;", table_str, col_str, val_str );
1484 char* query = buffer_release(sql);
1486 osrfLogDebug(OSRF_LOG_MARK, "%s: Insert SQL [%s]", MODULENAME, query);
1489 dbi_result result = dbi_conn_query(writehandle, query);
1491 jsonObject* obj = NULL;
1494 obj = jsonNewObject(NULL);
1497 "%s ERROR inserting %s object using query [%s]",
1499 osrfHashGet(meta, "fieldmapper"),
1502 osrfAppSessionStatus(
1504 OSRF_STATUS_INTERNALSERVERERROR,
1505 "osrfMethodException",
1507 "INSERT error -- please see the error log for more details"
1512 char* id = oilsFMGetString(target, pkey);
1514 unsigned long long new_id = dbi_conn_sequence_last(writehandle, seq);
1515 growing_buffer* _id = buffer_init(10);
1516 buffer_fadd(_id, "%lld", new_id);
1517 id = buffer_release(_id);
1520 // Find quietness specification, if present
1521 const char* quiet_str = NULL;
1523 const jsonObject* quiet_obj = jsonObjectGetKeyConst( options, "quiet" );
1525 quiet_str = jsonObjectGetString( quiet_obj );
1528 if( str_is_true( quiet_str ) ) { // if quietness is specified
1529 obj = jsonNewObject(id);
1533 jsonObject* where_clause = jsonNewObjectType( JSON_HASH );
1534 jsonObjectSetKey( where_clause, pkey, jsonNewObject(id) );
1536 jsonObject* list = doFieldmapperSearch( ctx, meta, where_clause, NULL, err );
1538 jsonObjectFree( where_clause );
1543 obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
1546 jsonObjectFree( list );
1559 * Fetch one row from a specified table, using a specified value
1560 * for the primary key
1562 static jsonObject* doRetrieve(osrfMethodContext* ctx, int* err ) {
1572 osrfHash* class_def = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
1574 const jsonObject* id_obj = jsonObjectGetIndex(ctx->params, id_pos); // key value
1578 "%s retrieving %s object with primary key value of %s",
1580 osrfHashGet( class_def, "fieldmapper" ),
1581 jsonObjectGetString( id_obj )
1584 // Build a WHERE clause based on the key value
1585 jsonObject* where_clause = jsonNewObjectType( JSON_HASH );
1588 osrfHashGet( class_def, "primarykey" ),
1589 jsonObjectClone( id_obj )
1592 jsonObject* rest_of_query = jsonObjectGetIndex(ctx->params, order_pos);
1594 jsonObject* list = doFieldmapperSearch( ctx, class_def, where_clause, rest_of_query, err );
1596 jsonObjectFree( where_clause );
1600 jsonObject* obj = jsonObjectExtractIndex( list, 0 );
1601 jsonObjectFree( list );
1604 if(!verifyObjectPCRUD(ctx, obj)) {
1605 jsonObjectFree(obj);
1608 growing_buffer* msg = buffer_init(128);
1609 OSRF_BUFFER_ADD( msg, MODULENAME );
1610 OSRF_BUFFER_ADD( msg, ": Insufficient permissions to retrieve object" );
1612 char* m = buffer_release(msg);
1613 osrfAppSessionStatus( ctx->session, OSRF_STATUS_NOTALLOWED, "osrfMethodException", ctx->request, m );
1624 static char* jsonNumberToDBString ( osrfHash* field, const jsonObject* value ) {
1625 growing_buffer* val_buf = buffer_init(32);
1626 const char* numtype = get_datatype( field );
1628 if ( !strncmp( numtype, "INT", 3 ) ) {
1629 if (value->type == JSON_NUMBER)
1630 //buffer_fadd( val_buf, "%ld", (long)jsonObjectGetNumber(value) );
1631 buffer_fadd( val_buf, jsonObjectGetString( value ) );
1633 //const char* val_str = jsonObjectGetString( value );
1634 //buffer_fadd( val_buf, "%ld", atol(val_str) );
1635 buffer_fadd( val_buf, jsonObjectGetString( value ) );
1638 } else if ( !strcmp( numtype, "NUMERIC" ) ) {
1639 if (value->type == JSON_NUMBER)
1640 //buffer_fadd( val_buf, "%f", jsonObjectGetNumber(value) );
1641 buffer_fadd( val_buf, jsonObjectGetString( value ) );
1643 //const char* val_str = jsonObjectGetString( value );
1644 //buffer_fadd( val_buf, "%f", atof(val_str) );
1645 buffer_fadd( val_buf, jsonObjectGetString( value ) );
1649 // Presumably this was really intended ot be a string, so quote it
1650 char* str = jsonObjectToSimpleString( value );
1651 if ( dbi_conn_quote_string(dbhandle, &str) ) {
1652 OSRF_BUFFER_ADD( val_buf, str );
1655 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, str);
1657 buffer_free(val_buf);
1662 return buffer_release(val_buf);
1665 static char* searchINPredicate (const char* class, osrfHash* field,
1666 jsonObject* node, const char* op, osrfMethodContext* ctx ) {
1667 growing_buffer* sql_buf = buffer_init(32);
1673 osrfHashGet(field, "name")
1677 buffer_add(sql_buf, "IN (");
1678 } else if (!(strcasecmp(op,"not in"))) {
1679 buffer_add(sql_buf, "NOT IN (");
1681 buffer_add(sql_buf, "IN (");
1684 if (node->type == JSON_HASH) {
1685 // subquery predicate
1686 char* subpred = SELECT(
1688 jsonObjectGetKey( node, "select" ),
1689 jsonObjectGetKey( node, "from" ),
1690 jsonObjectGetKey( node, "where" ),
1691 jsonObjectGetKey( node, "having" ),
1692 jsonObjectGetKey( node, "order_by" ),
1693 jsonObjectGetKey( node, "limit" ),
1694 jsonObjectGetKey( node, "offset" ),
1699 buffer_add(sql_buf, subpred);
1702 buffer_free( sql_buf );
1706 } else if (node->type == JSON_ARRAY) {
1707 // literal value list
1708 int in_item_index = 0;
1709 int in_item_first = 1;
1710 const jsonObject* in_item;
1711 while ( (in_item = jsonObjectGetIndex(node, in_item_index++)) ) {
1716 buffer_add(sql_buf, ", ");
1719 if ( in_item->type != JSON_STRING && in_item->type != JSON_NUMBER ) {
1720 osrfLogError(OSRF_LOG_MARK, "%s: Expected string or number within IN list; found %s",
1721 MODULENAME, json_type( in_item->type ) );
1722 buffer_free(sql_buf);
1726 // Append the literal value -- quoted if not a number
1727 if ( JSON_NUMBER == in_item->type ) {
1728 char* val = jsonNumberToDBString( field, in_item );
1729 OSRF_BUFFER_ADD( sql_buf, val );
1732 } else if ( !strcmp( get_primitive( field ), "number") ) {
1733 char* val = jsonNumberToDBString( field, in_item );
1734 OSRF_BUFFER_ADD( sql_buf, val );
1738 char* key_string = jsonObjectToSimpleString(in_item);
1739 if ( dbi_conn_quote_string(dbhandle, &key_string) ) {
1740 OSRF_BUFFER_ADD( sql_buf, key_string );
1743 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string);
1745 buffer_free(sql_buf);
1751 if( in_item_first ) {
1752 osrfLogError(OSRF_LOG_MARK, "%s: Empty IN list", MODULENAME );
1753 buffer_free( sql_buf );
1757 osrfLogError(OSRF_LOG_MARK, "%s: Expected object or array for IN clause; found %s",
1758 MODULENAME, json_type( node->type ) );
1759 buffer_free(sql_buf);
1763 OSRF_BUFFER_ADD_CHAR( sql_buf, ')' );
1765 return buffer_release(sql_buf);
1768 // Receive a JSON_ARRAY representing a function call. The first
1769 // entry in the array is the function name. The rest are parameters.
1770 static char* searchValueTransform( const jsonObject* array ) {
1772 if( array->size < 1 ) {
1773 osrfLogError(OSRF_LOG_MARK, "%s: Empty array for value transform", MODULENAME);
1777 // Get the function name
1778 jsonObject* func_item = jsonObjectGetIndex( array, 0 );
1779 if( func_item->type != JSON_STRING ) {
1780 osrfLogError(OSRF_LOG_MARK, "%s: Error: expected function name, found %s",
1781 MODULENAME, json_type( func_item->type ) );
1785 growing_buffer* sql_buf = buffer_init(32);
1787 OSRF_BUFFER_ADD( sql_buf, jsonObjectGetString( func_item ) );
1788 OSRF_BUFFER_ADD( sql_buf, "( " );
1790 // Get the parameters
1791 int func_item_index = 1; // We already grabbed the zeroth entry
1792 while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1794 // Add a separator comma, if we need one
1795 if( func_item_index > 2 )
1796 buffer_add( sql_buf, ", " );
1798 // Add the current parameter
1799 if (func_item->type == JSON_NULL) {
1800 buffer_add( sql_buf, "NULL" );
1802 char* val = jsonObjectToSimpleString(func_item);
1803 if ( dbi_conn_quote_string(dbhandle, &val) ) {
1804 OSRF_BUFFER_ADD( sql_buf, val );
1807 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1808 buffer_free(sql_buf);
1815 buffer_add( sql_buf, " )" );
1817 return buffer_release(sql_buf);
1820 static char* searchFunctionPredicate (const char* class, osrfHash* field,
1821 const jsonObject* node, const char* op) {
1823 if( ! is_good_operator( op ) ) {
1824 osrfLogError( OSRF_LOG_MARK, "%s: Invalid operator [%s]", MODULENAME, op );
1828 char* val = searchValueTransform(node);
1832 growing_buffer* sql_buf = buffer_init(32);
1837 osrfHashGet(field, "name"),
1844 return buffer_release(sql_buf);
1847 // class is a class name
1848 // field is a field definition as stored in the IDL
1849 // node comes from the method parameter, and may represent an entry in the SELECT list
1850 static char* searchFieldTransform (const char* class, osrfHash* field, const jsonObject* node) {
1851 growing_buffer* sql_buf = buffer_init(32);
1853 const char* field_transform = jsonObjectGetString( jsonObjectGetKeyConst( node, "transform" ) );
1854 const char* transform_subcolumn = jsonObjectGetString( jsonObjectGetKeyConst( node, "result_field" ) );
1856 if(transform_subcolumn) {
1857 if( ! is_identifier( transform_subcolumn ) ) {
1858 osrfLogError( OSRF_LOG_MARK, "%s: Invalid subfield name: \"%s\"\n",
1859 MODULENAME, transform_subcolumn );
1860 buffer_free( sql_buf );
1863 OSRF_BUFFER_ADD_CHAR( sql_buf, '(' ); // enclose transform in parentheses
1866 if (field_transform) {
1868 if( ! is_identifier( field_transform ) ) {
1869 osrfLogError( OSRF_LOG_MARK, "%s: Expected function name, found \"%s\"\n",
1870 MODULENAME, field_transform );
1871 buffer_free( sql_buf );
1875 buffer_fadd( sql_buf, "%s(\"%s\".%s", field_transform, class, osrfHashGet(field, "name"));
1876 const jsonObject* array = jsonObjectGetKeyConst( node, "params" );
1879 if( array->type != JSON_ARRAY ) {
1880 osrfLogError( OSRF_LOG_MARK,
1881 "%s: Expected JSON_ARRAY for function params; found %s",
1882 MODULENAME, json_type( array->type ) );
1883 buffer_free( sql_buf );
1886 int func_item_index = 0;
1887 jsonObject* func_item;
1888 while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1890 char* val = jsonObjectToSimpleString(func_item);
1893 buffer_add( sql_buf, ",NULL" );
1894 } else if ( dbi_conn_quote_string(dbhandle, &val) ) {
1895 OSRF_BUFFER_ADD_CHAR( sql_buf, ',' );
1896 OSRF_BUFFER_ADD( sql_buf, val );
1898 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1900 buffer_free(sql_buf);
1907 buffer_add( sql_buf, " )" );
1910 buffer_fadd( sql_buf, "\"%s\".%s", class, osrfHashGet(field, "name"));
1913 if (transform_subcolumn)
1914 buffer_fadd( sql_buf, ").\"%s\"", transform_subcolumn );
1916 return buffer_release(sql_buf);
1919 static char* searchFieldTransformPredicate (const char* class, osrfHash* field,
1920 const jsonObject* node, const char* op ) {
1922 if( ! is_good_operator( op ) ) {
1923 osrfLogError(OSRF_LOG_MARK, "%s: Error: Invalid operator %s", MODULENAME, op);
1927 char* field_transform = searchFieldTransform( class, field, node );
1928 if( ! field_transform )
1931 int extra_parens = 0; // boolean
1933 const jsonObject* value_obj = jsonObjectGetKeyConst( node, "value" );
1934 if ( ! value_obj ) {
1935 value = searchWHERE( node, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN, NULL );
1937 osrfLogError(OSRF_LOG_MARK, "%s: Error building condition for field transform", MODULENAME);
1938 free(field_transform);
1942 } else if ( value_obj->type == JSON_ARRAY ) {
1943 value = searchValueTransform( value_obj );
1945 osrfLogError(OSRF_LOG_MARK, "%s: Error building value transform for field transform", MODULENAME);
1946 free( field_transform );
1949 } else if ( value_obj->type == JSON_HASH ) {
1950 value = searchWHERE( value_obj, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN, NULL );
1952 osrfLogError(OSRF_LOG_MARK, "%s: Error building predicate for field transform", MODULENAME);
1953 free(field_transform);
1957 } else if ( value_obj->type == JSON_NUMBER ) {
1958 value = jsonNumberToDBString( field, value_obj );
1959 } else if ( value_obj->type == JSON_NULL ) {
1960 osrfLogError(OSRF_LOG_MARK, "%s: Error building predicate for field transform: null value", MODULENAME);
1961 free(field_transform);
1963 } else if ( value_obj->type == JSON_BOOL ) {
1964 osrfLogError(OSRF_LOG_MARK, "%s: Error building predicate for field transform: boolean value", MODULENAME);
1965 free(field_transform);
1968 if ( !strcmp( get_primitive( field ), "number") ) {
1969 value = jsonNumberToDBString( field, value_obj );
1971 value = jsonObjectToSimpleString( value_obj );
1972 if ( !dbi_conn_quote_string(dbhandle, &value) ) {
1973 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, value);
1975 free(field_transform);
1981 const char* left_parens = "";
1982 const char* right_parens = "";
1984 if( extra_parens ) {
1989 growing_buffer* sql_buf = buffer_init(32);
1993 "%s%s %s %s %s %s%s",
2004 free(field_transform);
2006 return buffer_release(sql_buf);
2009 static char* searchSimplePredicate (const char* op, const char* class,
2010 osrfHash* field, const jsonObject* node) {
2012 if( ! is_good_operator( op ) ) {
2013 osrfLogError( OSRF_LOG_MARK, "%s: Invalid operator [%s]", MODULENAME, op );
2019 // Get the value to which we are comparing the specified column
2020 if (node->type != JSON_NULL) {
2021 if ( node->type == JSON_NUMBER ) {
2022 val = jsonNumberToDBString( field, node );
2023 } else if ( !strcmp( get_primitive( field ), "number" ) ) {
2024 val = jsonNumberToDBString( field, node );
2026 val = jsonObjectToSimpleString(node);
2031 if( JSON_NUMBER != node->type && strcmp( get_primitive( field ), "number") ) {
2032 // Value is not numeric; enclose it in quotes
2033 if ( !dbi_conn_quote_string( dbhandle, &val ) ) {
2034 osrfLogError( OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val );
2040 // Compare to a null value
2041 val = strdup( "NULL" );
2042 if (strcmp( op, "=" ))
2048 growing_buffer* sql_buf = buffer_init(32);
2049 buffer_fadd( sql_buf, "\"%s\".%s %s %s", class, osrfHashGet(field, "name"), op, val );
2050 char* pred = buffer_release( sql_buf );
2057 static char* searchBETWEENPredicate (const char* class, osrfHash* field, const jsonObject* node) {
2059 const jsonObject* x_node = jsonObjectGetIndex( node, 0 );
2060 const jsonObject* y_node = jsonObjectGetIndex( node, 1 );
2062 if( NULL == y_node ) {
2063 osrfLogError( OSRF_LOG_MARK, "%s: Not enough operands for BETWEEN operator", MODULENAME );
2066 else if( NULL != jsonObjectGetIndex( node, 2 ) ) {
2067 osrfLogError( OSRF_LOG_MARK, "%s: Too many operands for BETWEEN operator", MODULENAME );
2074 if ( !strcmp( get_primitive( field ), "number") ) {
2075 x_string = jsonNumberToDBString(field, x_node);
2076 y_string = jsonNumberToDBString(field, y_node);
2079 x_string = jsonObjectToSimpleString(x_node);
2080 y_string = jsonObjectToSimpleString(y_node);
2081 if ( !(dbi_conn_quote_string(dbhandle, &x_string) && dbi_conn_quote_string(dbhandle, &y_string)) ) {
2082 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key strings [%s] and [%s]",
2083 MODULENAME, x_string, y_string);
2090 growing_buffer* sql_buf = buffer_init(32);
2091 buffer_fadd( sql_buf, "%s BETWEEN %s AND %s", osrfHashGet(field, "name"), x_string, y_string );
2095 return buffer_release(sql_buf);
2098 static char* searchPredicate ( const char* class, osrfHash* field,
2099 jsonObject* node, osrfMethodContext* ctx ) {
2102 if (node->type == JSON_ARRAY) { // equality IN search
2103 pred = searchINPredicate( class, field, node, NULL, ctx );
2104 } else if (node->type == JSON_HASH) { // other search
2105 jsonIterator* pred_itr = jsonNewIterator( node );
2106 if( !jsonIteratorHasNext( pred_itr ) ) {
2107 osrfLogError( OSRF_LOG_MARK, "%s: Empty predicate for field \"%s\"",
2108 MODULENAME, osrfHashGet(field, "name") );
2110 jsonObject* pred_node = jsonIteratorNext( pred_itr );
2112 // Verify that there are no additional predicates
2113 if( jsonIteratorHasNext( pred_itr ) ) {
2114 osrfLogError( OSRF_LOG_MARK, "%s: Multiple predicates for field \"%s\"",
2115 MODULENAME, osrfHashGet(field, "name") );
2116 } else if ( !(strcasecmp( pred_itr->key,"between" )) )
2117 pred = searchBETWEENPredicate( class, field, pred_node );
2118 else if ( !(strcasecmp( pred_itr->key,"in" )) || !(strcasecmp( pred_itr->key,"not in" )) )
2119 pred = searchINPredicate( class, field, pred_node, pred_itr->key, ctx );
2120 else if ( pred_node->type == JSON_ARRAY )
2121 pred = searchFunctionPredicate( class, field, pred_node, pred_itr->key );
2122 else if ( pred_node->type == JSON_HASH )
2123 pred = searchFieldTransformPredicate( class, field, pred_node, pred_itr->key );
2125 pred = searchSimplePredicate( pred_itr->key, class, field, pred_node );
2127 jsonIteratorFree(pred_itr);
2129 } else if (node->type == JSON_NULL) { // IS NULL search
2130 growing_buffer* _p = buffer_init(64);
2133 "\"%s\".%s IS NULL",
2135 osrfHashGet(field, "name")
2137 pred = buffer_release(_p);
2138 } else { // equality search
2139 pred = searchSimplePredicate( "=", class, field, node );
2158 field : call_number,
2174 static char* searchJOIN ( const jsonObject* join_hash, osrfHash* leftmeta ) {
2176 const jsonObject* working_hash;
2177 jsonObject* freeable_hash = NULL;
2179 if (join_hash->type == JSON_STRING) {
2180 // create a wrapper around a copy of the original
2181 const char* _tmp = jsonObjectGetString( join_hash );
2182 freeable_hash = jsonNewObjectType(JSON_HASH);
2183 jsonObjectSetKey(freeable_hash, _tmp, NULL);
2184 working_hash = freeable_hash;
2187 if( join_hash->type != JSON_HASH ) {
2190 "%s: JOIN failed; expected JSON object type not found",
2195 working_hash = join_hash;
2198 growing_buffer* join_buf = buffer_init(128);
2199 const char* leftclass = osrfHashGet(leftmeta, "classname");
2201 jsonObject* snode = NULL;
2202 jsonIterator* search_itr = jsonNewIterator( working_hash );
2204 while ( (snode = jsonIteratorNext( search_itr )) ) {
2205 const char* class = search_itr->key;
2206 osrfHash* idlClass = osrfHashGet( oilsIDL(), class );
2210 "%s: JOIN failed. No class \"%s\" defined in IDL",
2214 jsonIteratorFree( search_itr );
2215 buffer_free( join_buf );
2217 jsonObjectFree( freeable_hash );
2221 const char* fkey = jsonObjectGetString( jsonObjectGetKeyConst( snode, "fkey" ) );
2222 const char* field = jsonObjectGetString( jsonObjectGetKeyConst( snode, "field" ) );
2224 if (field && !fkey) {
2225 // Look up the corresponding join column in the IDL.
2226 // The link must be defined in the child table,
2227 // and point to the right parent table.
2228 osrfHash* idl_link = (osrfHash*) oilsIDLFindPath( "/%s/links/%s", class, field );
2229 const char* reltype = NULL;
2230 const char* other_class = NULL;
2231 reltype = osrfHashGet( idl_link, "reltype" );
2232 if( reltype && strcmp( reltype, "has_many" ) )
2233 other_class = osrfHashGet( idl_link, "class" );
2234 if( other_class && !strcmp( other_class, leftclass ) )
2235 fkey = osrfHashGet( idl_link, "key" );
2239 "%s: JOIN failed. No link defined from %s.%s to %s",
2245 buffer_free(join_buf);
2247 jsonObjectFree(freeable_hash);
2248 jsonIteratorFree(search_itr);
2252 } else if (!field && fkey) {
2253 // Look up the corresponding join column in the IDL.
2254 // The link must be defined in the child table,
2255 // and point to the right parent table.
2256 osrfHash* idl_link = (osrfHash*) oilsIDLFindPath( "/%s/links/%s", leftclass, fkey );
2257 const char* reltype = NULL;
2258 const char* other_class = NULL;
2259 reltype = osrfHashGet( idl_link, "reltype" );
2260 if( reltype && strcmp( reltype, "has_many" ) )
2261 other_class = osrfHashGet( idl_link, "class" );
2262 if( other_class && !strcmp( other_class, class ) )
2263 field = osrfHashGet( idl_link, "key" );
2267 "%s: JOIN failed. No link defined from %s.%s to %s",
2273 buffer_free(join_buf);
2275 jsonObjectFree(freeable_hash);
2276 jsonIteratorFree(search_itr);
2280 } else if (!field && !fkey) {
2281 osrfHash* _links = oilsIDL_links( leftclass );
2283 // For each link defined for the left class:
2284 // see if the link references the joined class
2285 osrfHashIterator* itr = osrfNewHashIterator( _links );
2286 osrfHash* curr_link = NULL;
2287 while( (curr_link = osrfHashIteratorNext( itr ) ) ) {
2288 const char* other_class = osrfHashGet( curr_link, "class" );
2289 if( other_class && !strcmp( other_class, class ) ) {
2291 // In the IDL, the parent class doesn't know then names of the child
2292 // columns that are pointing to it, so don't use that end of the link
2293 const char* reltype = osrfHashGet( curr_link, "reltype" );
2294 if( reltype && strcmp( reltype, "has_many" ) ) {
2295 // Found a link between the classes
2296 fkey = osrfHashIteratorKey( itr );
2297 field = osrfHashGet( curr_link, "key" );
2302 osrfHashIteratorFree( itr );
2304 if (!field || !fkey) {
2305 // Do another such search, with the classes reversed
2306 _links = oilsIDL_links( class );
2308 // For each link defined for the joined class:
2309 // see if the link references the left class
2310 osrfHashIterator* itr = osrfNewHashIterator( _links );
2311 osrfHash* curr_link = NULL;
2312 while( (curr_link = osrfHashIteratorNext( itr ) ) ) {
2313 const char* other_class = osrfHashGet( curr_link, "class" );
2314 if( other_class && !strcmp( other_class, leftclass ) ) {
2316 // In the IDL, the parent class doesn't know then names of the child
2317 // columns that are pointing to it, so don't use that end of the link
2318 const char* reltype = osrfHashGet( curr_link, "reltype" );
2319 if( reltype && strcmp( reltype, "has_many" ) ) {
2320 // Found a link between the classes
2321 field = osrfHashIteratorKey( itr );
2322 fkey = osrfHashGet( curr_link, "key" );
2327 osrfHashIteratorFree( itr );
2330 if (!field || !fkey) {
2333 "%s: JOIN failed. No link defined between %s and %s",
2338 buffer_free(join_buf);
2340 jsonObjectFree(freeable_hash);
2341 jsonIteratorFree(search_itr);
2347 const char* type = jsonObjectGetString( jsonObjectGetKeyConst( snode, "type" ) );
2349 if ( !strcasecmp(type,"left") ) {
2350 buffer_add(join_buf, " LEFT JOIN");
2351 } else if ( !strcasecmp(type,"right") ) {
2352 buffer_add(join_buf, " RIGHT JOIN");
2353 } else if ( !strcasecmp(type,"full") ) {
2354 buffer_add(join_buf, " FULL JOIN");
2356 buffer_add(join_buf, " INNER JOIN");
2359 buffer_add(join_buf, " INNER JOIN");
2362 char* table = getSourceDefinition(idlClass);
2364 jsonIteratorFree( search_itr );
2365 buffer_free( join_buf );
2367 jsonObjectFree( freeable_hash );
2371 buffer_fadd(join_buf, " %s AS \"%s\" ON ( \"%s\".%s = \"%s\".%s",
2372 table, class, class, field, leftclass, fkey);
2375 const jsonObject* filter = jsonObjectGetKeyConst( snode, "filter" );
2377 const char* filter_op = jsonObjectGetString( jsonObjectGetKeyConst( snode, "filter_op" ) );
2378 if ( filter_op && !strcasecmp("or",filter_op) ) {
2379 buffer_add( join_buf, " OR " );
2381 buffer_add( join_buf, " AND " );
2384 char* jpred = searchWHERE( filter, idlClass, AND_OP_JOIN, NULL );
2386 OSRF_BUFFER_ADD_CHAR( join_buf, ' ' );
2387 OSRF_BUFFER_ADD( join_buf, jpred );
2392 "%s: JOIN failed. Invalid conditional expression.",
2395 jsonIteratorFree( search_itr );
2396 buffer_free( join_buf );
2398 jsonObjectFree( freeable_hash );
2403 buffer_add(join_buf, " ) ");
2405 const jsonObject* join_filter = jsonObjectGetKeyConst( snode, "join" );
2407 char* jpred = searchJOIN( join_filter, idlClass );
2409 OSRF_BUFFER_ADD_CHAR( join_buf, ' ' );
2410 OSRF_BUFFER_ADD( join_buf, jpred );
2413 osrfLogError( OSRF_LOG_MARK, "%s: Invalid nested join.", MODULENAME );
2414 jsonIteratorFree( search_itr );
2415 buffer_free( join_buf );
2417 jsonObjectFree( freeable_hash );
2424 jsonObjectFree(freeable_hash);
2425 jsonIteratorFree(search_itr);
2427 return buffer_release(join_buf);
2432 { +class : { -or|-and : { field : { op : value }, ... } ... }, ... }
2433 { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }
2434 [ { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }, ... ]
2436 Generate code to express a set of conditions, as for a WHERE clause. Parameters:
2438 search_hash is the JSON expression of the conditions.
2439 meta is the class definition from the IDL, for the relevant table.
2440 opjoin_type indicates whether multiple conditions, if present, should be
2441 connected by AND or OR.
2442 osrfMethodContext is loaded with all sorts of stuff, but all we do with it here is
2443 to pass it to other functions -- and all they do with it is to use the session
2444 and request members to send error messages back to the client.
2448 static char* searchWHERE ( const jsonObject* search_hash, osrfHash* meta, int opjoin_type, osrfMethodContext* ctx ) {
2452 "%s: Entering searchWHERE; search_hash addr = %p, meta addr = %p, opjoin_type = %d, ctx addr = %p",
2460 growing_buffer* sql_buf = buffer_init(128);
2462 jsonObject* node = NULL;
2465 if ( search_hash->type == JSON_ARRAY ) {
2466 osrfLogDebug(OSRF_LOG_MARK, "%s: In WHERE clause, condition type is JSON_ARRAY", MODULENAME);
2467 jsonIterator* search_itr = jsonNewIterator( search_hash );
2468 if( !jsonIteratorHasNext( search_itr ) ) {
2471 "%s: Invalid predicate structure: empty JSON array",
2474 jsonIteratorFree( search_itr );
2475 buffer_free( sql_buf );
2479 while ( (node = jsonIteratorNext( search_itr )) ) {
2483 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
2484 else buffer_add(sql_buf, " AND ");
2487 char* subpred = searchWHERE( node, meta, opjoin_type, ctx );
2489 buffer_fadd(sql_buf, "( %s )", subpred);
2492 jsonIteratorFree( search_itr );
2493 buffer_free( sql_buf );
2497 jsonIteratorFree(search_itr);
2499 } else if ( search_hash->type == JSON_HASH ) {
2500 osrfLogDebug(OSRF_LOG_MARK, "%s: In WHERE clause, condition type is JSON_HASH", MODULENAME);
2501 jsonIterator* search_itr = jsonNewIterator( search_hash );
2502 if( !jsonIteratorHasNext( search_itr ) ) {
2505 "%s: Invalid predicate structure: empty JSON object",
2508 jsonIteratorFree( search_itr );
2509 buffer_free( sql_buf );
2513 while ( (node = jsonIteratorNext( search_itr )) ) {
2518 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
2519 else buffer_add(sql_buf, " AND ");
2522 if ( '+' == search_itr->key[ 0 ] ) {
2523 if ( node->type == JSON_STRING ) {
2524 // Intended purpose; to allow reference to a Boolean column
2526 // Verify that the class alias is not empty
2527 if( '\0' == search_itr->key[ 1 ] ) {
2530 "%s: Table alias is empty",
2533 jsonIteratorFree( search_itr );
2534 buffer_free( sql_buf );
2538 // Verify that the string looks like an identifier.
2539 const char* subpred = jsonObjectGetString( node );
2540 if( ! is_identifier( subpred ) ) {
2543 "%s: Invalid boolean identifier in WHERE clause: \"%s\"",
2547 jsonIteratorFree( search_itr );
2548 buffer_free( sql_buf );
2552 buffer_fadd(sql_buf, " \"%s\".%s ", search_itr->key + 1, subpred);
2554 char* subpred = searchWHERE( node, osrfHashGet( oilsIDL(), search_itr->key + 1 ), AND_OP_JOIN, ctx );
2556 buffer_fadd(sql_buf, "( %s )", subpred);
2559 jsonIteratorFree( search_itr );
2560 buffer_free( sql_buf );
2564 } else if ( !strcasecmp("-or",search_itr->key) ) {
2565 char* subpred = searchWHERE( node, meta, OR_OP_JOIN, ctx );
2567 buffer_fadd(sql_buf, "( %s )", subpred);
2570 buffer_free( sql_buf );
2573 } else if ( !strcasecmp("-and",search_itr->key) ) {
2574 char* subpred = searchWHERE( node, meta, AND_OP_JOIN, ctx );
2576 buffer_fadd(sql_buf, "( %s )", subpred);
2579 buffer_free( sql_buf );
2582 } else if ( !strcasecmp("-not",search_itr->key) ) {
2583 char* subpred = searchWHERE( node, meta, AND_OP_JOIN, ctx );
2585 buffer_fadd(sql_buf, " NOT ( %s )", subpred);
2588 buffer_free( sql_buf );
2591 } else if ( !strcasecmp("-exists",search_itr->key) ) {
2592 char* subpred = SELECT(
2594 jsonObjectGetKey( node, "select" ),
2595 jsonObjectGetKey( node, "from" ),
2596 jsonObjectGetKey( node, "where" ),
2597 jsonObjectGetKey( node, "having" ),
2598 jsonObjectGetKey( node, "order_by" ),
2599 jsonObjectGetKey( node, "limit" ),
2600 jsonObjectGetKey( node, "offset" ),
2605 buffer_fadd(sql_buf, "EXISTS ( %s )", subpred);
2608 buffer_free( sql_buf );
2611 } else if ( !strcasecmp("-not-exists",search_itr->key) ) {
2612 char* subpred = SELECT(
2614 jsonObjectGetKey( node, "select" ),
2615 jsonObjectGetKey( node, "from" ),
2616 jsonObjectGetKey( node, "where" ),
2617 jsonObjectGetKey( node, "having" ),
2618 jsonObjectGetKey( node, "order_by" ),
2619 jsonObjectGetKey( node, "limit" ),
2620 jsonObjectGetKey( node, "offset" ),
2625 buffer_fadd(sql_buf, "NOT EXISTS ( %s )", subpred);
2628 buffer_free( sql_buf );
2634 char* class = osrfHashGet(meta, "classname");
2635 osrfHash* fields = osrfHashGet(meta, "fields");
2636 osrfHash* field = osrfHashGet( fields, search_itr->key );
2640 char* table = getSourceDefinition(meta);
2642 table = strdup( "(?)" );
2645 "%s: Attempt to reference non-existent column \"%s\" on %s (%s)",
2651 buffer_free(sql_buf);
2653 jsonIteratorFree(search_itr);
2657 char* subpred = searchPredicate( class, field, node, ctx );
2659 buffer_add( sql_buf, subpred );
2662 buffer_free(sql_buf);
2663 jsonIteratorFree(search_itr);
2668 jsonIteratorFree(search_itr);
2671 // ERROR ... only hash and array allowed at this level
2672 char* predicate_string = jsonObjectToJSON( search_hash );
2675 "%s: Invalid predicate structure: %s",
2679 buffer_free(sql_buf);
2680 free(predicate_string);
2684 return buffer_release(sql_buf);
2687 // Return 1 if the class is in the FROM clause, or 0 if not
2688 static int is_joined( jsonObject* from_clause, const char* class ) {
2692 else if( from_clause->type == JSON_STRING ) {
2693 if( strcmp( class, jsonObjectGetString( from_clause ) ) )
2697 } else if( from_clause->type != JSON_HASH ) {
2699 } else { // Look at any subjoins
2700 jsonIterator* class_itr = jsonNewIterator( from_clause );
2701 jsonObject* curr_class;
2702 int rc = 0; // return code
2703 while ( ( curr_class = jsonIteratorNext( class_itr ) ) ) {
2704 if( ! strcmp( class_itr->key, class ) ) {
2708 jsonObject* subjoin = jsonObjectGetKey( curr_class, "join" );
2709 if( subjoin && is_joined( subjoin, class ) ) {
2715 jsonIteratorFree( class_itr );
2722 /* method context */ osrfMethodContext* ctx,
2724 /* SELECT */ jsonObject* selhash,
2725 /* FROM */ jsonObject* join_hash,
2726 /* WHERE */ jsonObject* search_hash,
2727 /* HAVING */ jsonObject* having_hash,
2728 /* ORDER BY */ jsonObject* order_hash,
2729 /* LIMIT */ jsonObject* limit,
2730 /* OFFSET */ jsonObject* offset,
2731 /* flags */ int flags
2733 const char* locale = osrf_message_get_last_locale();
2735 // in case we don't get a select list
2736 jsonObject* defaultselhash = NULL;
2738 // general tmp objects
2739 const jsonObject* tmp_const;
2740 jsonObject* selclass = NULL;
2741 jsonObject* selfield = NULL;
2742 jsonObject* snode = NULL;
2743 jsonObject* onode = NULL;
2745 char* string = NULL;
2746 int from_function = 0;
2751 // the core search class
2752 char* core_class = NULL;
2754 // metadata about the core search class
2755 osrfHash* core_meta = NULL;
2757 osrfLogDebug(OSRF_LOG_MARK, "cstore SELECT locale: %s", locale);
2759 // punt if there's no core class
2760 if (!join_hash || ( join_hash->type == JSON_HASH && !join_hash->size )) {
2763 "%s: FROM clause is missing or empty",
2767 osrfAppSessionStatus(
2769 OSRF_STATUS_INTERNALSERVERERROR,
2770 "osrfMethodException",
2772 "FROM clause is missing or empty in JSON query"
2777 // get the core class -- the only key of the top level FROM clause, or a string
2778 if (join_hash->type == JSON_HASH) {
2779 jsonIterator* tmp_itr = jsonNewIterator( join_hash );
2780 snode = jsonIteratorNext( tmp_itr );
2782 core_class = strdup( tmp_itr->key );
2785 jsonObject* extra = jsonIteratorNext( tmp_itr );
2787 jsonIteratorFree( tmp_itr );
2790 // There shouldn't be more than one entry in join_hash
2794 "%s: Malformed FROM clause: extra entry in JSON_HASH",
2798 osrfAppSessionStatus(
2800 OSRF_STATUS_INTERNALSERVERERROR,
2801 "osrfMethodException",
2803 "Malformed FROM clause in JSON query"
2806 return NULL; // Malformed join_hash; extra entry
2808 } else if (join_hash->type == JSON_ARRAY) {
2810 core_class = jsonObjectToSimpleString( jsonObjectGetIndex(join_hash, 0) );
2813 } else if (join_hash->type == JSON_STRING) {
2814 core_class = jsonObjectToSimpleString( join_hash );
2820 "%s: FROM clause is unexpected JSON type: %s",
2822 json_type( join_hash->type )
2825 osrfAppSessionStatus(
2827 OSRF_STATUS_INTERNALSERVERERROR,
2828 "osrfMethodException",
2830 "Ill-formed FROM clause in JSON query"
2836 if (!from_function) {
2837 // Get the IDL class definition for the core class
2838 core_meta = osrfHashGet( oilsIDL(), core_class );
2839 if( !core_meta ) { // Didn't find it?
2842 "%s: SELECT clause references undefined class: \"%s\"",
2847 osrfAppSessionStatus(
2849 OSRF_STATUS_INTERNALSERVERERROR,
2850 "osrfMethodException",
2852 "SELECT clause references undefined class in JSON query"
2858 // Make sure the class isn't virtual
2859 if( str_is_true( osrfHashGet( core_meta, "virtual" ) ) ) {
2862 "%s: Core class is virtual: \"%s\"",
2867 osrfAppSessionStatus(
2869 OSRF_STATUS_INTERNALSERVERERROR,
2870 "osrfMethodException",
2872 "FROM clause references virtual class in JSON query"
2879 // if the select list is empty, or the core class field list is '*',
2880 // build the default select list ...
2882 selhash = defaultselhash = jsonNewObjectType(JSON_HASH);
2883 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2884 } else if( selhash->type != JSON_HASH ) {
2887 "%s: Expected JSON_HASH for SELECT clause; found %s",
2889 json_type( selhash->type )
2893 osrfAppSessionStatus(
2895 OSRF_STATUS_INTERNALSERVERERROR,
2896 "osrfMethodException",
2898 "Malformed SELECT clause in JSON query"
2902 } else if ( (tmp_const = jsonObjectGetKeyConst( selhash, core_class )) && tmp_const->type == JSON_STRING ) {
2903 const char* _x = jsonObjectGetString( tmp_const );
2904 if (!strncmp( "*", _x, 1 )) {
2905 jsonObjectRemoveKey( selhash, core_class );
2906 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2911 growing_buffer* sql_buf = buffer_init(128);
2913 // temp buffers for the SELECT list and GROUP BY clause
2914 growing_buffer* select_buf = buffer_init(128);
2915 growing_buffer* group_buf = buffer_init(128);
2917 int aggregate_found = 0; // boolean
2919 // Build a select list
2920 if(from_function) // From a function we select everything
2921 OSRF_BUFFER_ADD_CHAR( select_buf, '*' );
2924 // If we need to build a default list, prepare to do so
2925 jsonObject* _tmp = jsonObjectGetKey( selhash, core_class );
2926 if ( _tmp && !_tmp->size ) {
2928 osrfHash* core_fields = osrfHashGet( core_meta, "fields" );
2930 osrfHashIterator* field_itr = osrfNewHashIterator( core_fields );
2931 osrfHash* field_def;
2932 while( ( field_def = osrfHashIteratorNext( field_itr ) ) ) {
2933 if( ! str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2934 // This field is not virtual, so add it to the list
2935 jsonObjectPush( _tmp, jsonNewObject( osrfHashIteratorKey( field_itr ) ) );
2938 osrfHashIteratorFree( field_itr );
2941 // Now build the actual select list
2945 jsonIterator* selclass_itr = jsonNewIterator( selhash );
2946 while ( (selclass = jsonIteratorNext( selclass_itr )) ) { // For each class
2948 const char* cname = selclass_itr->key;
2950 // Make sure the target relation is in the FROM clause.
2952 // At this point join_hash is a step down from the join_hash we
2953 // received as a parameter. If the original was a JSON_STRING,
2954 // then json_hash is now NULL. If the original was a JSON_HASH,
2955 // then json_hash is now the first (and only) entry in it,
2956 // denoting the core class. We've already excluded the
2957 // possibility that the original was a JSON_ARRAY, because in
2958 // that case from_function would be non-NULL, and we wouldn't
2961 // If the current class isn't the core class
2962 // and it isn't in the join tree, bail out
2963 if ( strcmp( core_class, cname ) && ! is_joined( join_hash, cname ) ) {
2966 "%s: SELECT clause references class not in FROM clause: \"%s\"",
2971 osrfAppSessionStatus(
2973 OSRF_STATUS_INTERNALSERVERERROR,
2974 "osrfMethodException",
2976 "Selected class not in FROM clause in JSON query"
2978 jsonIteratorFree( selclass_itr );
2979 buffer_free( sql_buf );
2980 buffer_free( select_buf );
2981 buffer_free( group_buf );
2982 if( defaultselhash ) jsonObjectFree( defaultselhash );
2987 // Look up some attributes of the current class, so that we
2988 // don't have to look them up again for each field
2989 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
2990 osrfHash* class_field_set = osrfHashGet( idlClass, "fields" );
2991 const char* class_pkey = osrfHashGet( idlClass, "primarykey" );
2992 const char* class_tname = osrfHashGet( idlClass, "tablename" );
2994 // stitch together the column list ...
2995 jsonIterator* select_itr = jsonNewIterator( selclass );
2996 while ( (selfield = jsonIteratorNext( select_itr )) ) { // for each SELECT column
2998 // If we need a separator comma, add one
3002 OSRF_BUFFER_ADD_CHAR( select_buf, ',' );
3005 // ... if it's a string, just toss it on the pile
3006 if (selfield->type == JSON_STRING) {
3008 // Look up the field in the IDL
3009 const char* col_name = jsonObjectGetString( selfield );
3010 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
3012 // No such field in current class
3015 "%s: Selected column \"%s\" not defined in IDL for class \"%s\"",
3021 osrfAppSessionStatus(
3023 OSRF_STATUS_INTERNALSERVERERROR,
3024 "osrfMethodException",
3026 "Selected column not defined in JSON query"
3028 jsonIteratorFree( select_itr );
3029 jsonIteratorFree( selclass_itr );
3030 buffer_free( sql_buf );
3031 buffer_free( select_buf );
3032 buffer_free( group_buf );
3033 if( defaultselhash ) jsonObjectFree( defaultselhash );
3036 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
3037 // Virtual field not allowed
3040 "%s: Selected column \"%s\" for class \"%s\" is virtual",
3046 osrfAppSessionStatus(
3048 OSRF_STATUS_INTERNALSERVERERROR,
3049 "osrfMethodException",
3051 "Selected column may not be virtual in JSON query"
3053 jsonIteratorFree( select_itr );
3054 jsonIteratorFree( selclass_itr );
3055 buffer_free( sql_buf );
3056 buffer_free( select_buf );
3057 buffer_free( group_buf );
3058 if( defaultselhash ) jsonObjectFree( defaultselhash );
3065 if (flags & DISABLE_I18N)
3068 i18n = osrfHashGet(field_def, "i18n");
3070 if( str_is_true( i18n ) ) {
3071 buffer_fadd( select_buf,
3072 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
3073 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, col_name );
3075 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
3078 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
3081 // ... but it could be an object, in which case we check for a Field Transform
3082 } else if (selfield->type == JSON_HASH) {
3084 const char* col_name = jsonObjectGetString( jsonObjectGetKeyConst( selfield, "column" ) );
3086 // Get the field definition from the IDL
3087 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
3089 // No such field in current class
3092 "%s: Selected column \"%s\" is not defined in IDL for class \"%s\"",
3098 osrfAppSessionStatus(
3100 OSRF_STATUS_INTERNALSERVERERROR,
3101 "osrfMethodException",
3103 "Selected column is not defined in JSON query"
3105 jsonIteratorFree( select_itr );
3106 jsonIteratorFree( selclass_itr );
3107 buffer_free( sql_buf );
3108 buffer_free( select_buf );
3109 buffer_free( group_buf );
3110 if( defaultselhash ) jsonObjectFree( defaultselhash );
3113 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
3114 // No such field in current class
3117 "%s: Selected column \"%s\" is virtual for class \"%s\"",
3123 osrfAppSessionStatus(
3125 OSRF_STATUS_INTERNALSERVERERROR,
3126 "osrfMethodException",
3128 "Selected column is virtual in JSON query"
3130 jsonIteratorFree( select_itr );
3131 jsonIteratorFree( selclass_itr );
3132 buffer_free( sql_buf );
3133 buffer_free( select_buf );
3134 buffer_free( group_buf );
3135 if( defaultselhash ) jsonObjectFree( defaultselhash );
3140 // Decide what to use as a column alias
3142 if ((tmp_const = jsonObjectGetKeyConst( selfield, "alias" ))) {
3143 _alias = jsonObjectGetString( tmp_const );
3144 } else { // Use field name as the alias
3148 if (jsonObjectGetKeyConst( selfield, "transform" )) {
3149 char* transform_str = searchFieldTransform(cname, field_def, selfield);
3150 if( transform_str ) {
3151 buffer_fadd(select_buf, " %s AS \"%s\"", transform_str, _alias);
3152 free(transform_str);
3155 osrfAppSessionStatus(
3157 OSRF_STATUS_INTERNALSERVERERROR,
3158 "osrfMethodException",
3160 "Unable to generate transform function in JSON query"
3162 jsonIteratorFree( select_itr );
3163 jsonIteratorFree( selclass_itr );
3164 buffer_free( sql_buf );
3165 buffer_free( select_buf );
3166 buffer_free( group_buf );
3167 if( defaultselhash ) jsonObjectFree( defaultselhash );
3175 if (flags & DISABLE_I18N)
3178 i18n = osrfHashGet(field_def, "i18n");
3180 if( str_is_true( i18n ) ) {
3181 buffer_fadd( select_buf,
3182 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
3183 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, _alias);
3185 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
3188 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
3195 "%s: Selected item is unexpected JSON type: %s",
3197 json_type( selfield->type )
3200 osrfAppSessionStatus(
3202 OSRF_STATUS_INTERNALSERVERERROR,
3203 "osrfMethodException",
3205 "Ill-formed SELECT item in JSON query"
3207 jsonIteratorFree( select_itr );
3208 jsonIteratorFree( selclass_itr );
3209 buffer_free( sql_buf );
3210 buffer_free( select_buf );
3211 buffer_free( group_buf );
3212 if( defaultselhash ) jsonObjectFree( defaultselhash );
3217 const jsonObject* agg_obj = jsonObjectGetKey( selfield, "aggregate" );
3218 if( obj_is_true( agg_obj ) )
3219 aggregate_found = 1;
3221 // Append a comma (except for the first one)
3222 // and add the column to a GROUP BY clause
3226 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3228 buffer_fadd(group_buf, " %d", sel_pos);
3232 if (is_agg->size || (flags & SELECT_DISTINCT)) {
3234 const jsonObject* aggregate_obj = jsonObjectGetKey( selfield, "aggregate" );
3235 if ( ! obj_is_true( aggregate_obj ) ) {
3239 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3242 buffer_fadd(group_buf, " %d", sel_pos);
3245 } else if (is_agg = jsonObjectGetKey( selfield, "having" )) {
3249 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3252 _column = searchFieldTransform(cname, field, selfield);
3253 OSRF_BUFFER_ADD_CHAR(group_buf, ' ');
3254 OSRF_BUFFER_ADD(group_buf, _column);
3255 _column = searchFieldTransform(cname, field, selfield);
3262 } // end while -- iterating across SELECT columns
3264 jsonIteratorFree(select_itr);
3265 } // end while -- iterating across classes
3267 jsonIteratorFree(selclass_itr);
3271 char* col_list = buffer_release(select_buf);
3273 if (from_function) table = searchValueTransform(join_hash);
3274 else table = getSourceDefinition(core_meta);
3278 osrfAppSessionStatus(
3280 OSRF_STATUS_INTERNALSERVERERROR,
3281 "osrfMethodException",
3283 "Unable to identify table for core class"
3286 buffer_free( sql_buf );
3287 buffer_free( group_buf );
3288 if( defaultselhash ) jsonObjectFree( defaultselhash );
3293 // Put it all together
3294 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\" ", col_list, table, core_class );
3298 char* order_by_list = NULL;
3299 char* having_buf = NULL;
3301 if (!from_function) {
3303 // Now, walk the join tree and add that clause
3305 char* join_clause = searchJOIN( join_hash, core_meta );
3307 buffer_add(sql_buf, join_clause);
3311 osrfAppSessionStatus(
3313 OSRF_STATUS_INTERNALSERVERERROR,
3314 "osrfMethodException",
3316 "Unable to construct JOIN clause(s)"
3318 buffer_free( sql_buf );
3319 buffer_free( group_buf );
3320 if( defaultselhash ) jsonObjectFree( defaultselhash );
3326 // Build a WHERE clause, if there is one
3327 if ( search_hash ) {
3328 buffer_add(sql_buf, " WHERE ");
3330 // and it's on the WHERE clause
3331 char* pred = searchWHERE( search_hash, core_meta, AND_OP_JOIN, ctx );
3334 buffer_add(sql_buf, pred);
3338 osrfAppSessionStatus(
3340 OSRF_STATUS_INTERNALSERVERERROR,
3341 "osrfMethodException",
3343 "Severe query error in WHERE predicate -- see error log for more details"
3347 buffer_free(group_buf);
3348 buffer_free(sql_buf);
3349 if (defaultselhash) jsonObjectFree(defaultselhash);
3354 // Build a HAVING clause, if there is one
3355 if ( having_hash ) {
3357 // and it's on the the WHERE clause
3358 having_buf = searchWHERE( having_hash, core_meta, AND_OP_JOIN, ctx );
3360 if( ! having_buf ) {
3362 osrfAppSessionStatus(
3364 OSRF_STATUS_INTERNALSERVERERROR,
3365 "osrfMethodException",
3367 "Severe query error in HAVING predicate -- see error log for more details"
3371 buffer_free(group_buf);
3372 buffer_free(sql_buf);
3373 if (defaultselhash) jsonObjectFree(defaultselhash);
3378 growing_buffer* order_buf = NULL; // to collect ORDER BY list
3380 // Build an ORDER BY clause, if there is one
3381 if( NULL == order_hash )
3382 ; // No ORDER BY? do nothing
3383 else if( JSON_ARRAY == order_hash->type ) {
3384 // Array of field specifications, each specification being a
3385 // hash to define the class, field, and other details
3387 jsonObject* order_spec;
3388 while( (order_spec = jsonObjectGetIndex( order_hash, order_idx++ ) ) ) {
3390 if( JSON_HASH != order_spec->type ) {
3391 osrfLogError(OSRF_LOG_MARK,
3392 "%s: Malformed field specification in ORDER BY clause; expected JSON_HASH, found %s",
3393 MODULENAME, json_type( order_spec->type ) );
3395 osrfAppSessionStatus(
3397 OSRF_STATUS_INTERNALSERVERERROR,
3398 "osrfMethodException",
3400 "Malformed ORDER BY clause -- see error log for more details"
3402 buffer_free( order_buf );
3405 buffer_free(group_buf);
3406 buffer_free(sql_buf);
3407 if (defaultselhash) jsonObjectFree(defaultselhash);
3412 jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "class" ) );
3414 jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "field" ) );
3417 OSRF_BUFFER_ADD(order_buf, ", ");
3419 order_buf = buffer_init(128);
3421 if( !field || !class ) {
3422 osrfLogError(OSRF_LOG_MARK,
3423 "%s: Missing class or field name in field specification of ORDER BY clause",
3426 osrfAppSessionStatus(
3428 OSRF_STATUS_INTERNALSERVERERROR,
3429 "osrfMethodException",
3431 "Malformed ORDER BY clause -- see error log for more details"
3433 buffer_free( order_buf );
3436 buffer_free(group_buf);
3437 buffer_free(sql_buf);
3438 if (defaultselhash) jsonObjectFree(defaultselhash);
3442 if ( ! jsonObjectGetKeyConst( selhash, class )
3443 && strcmp( core_class, class )
3444 && ! is_joined( join_hash, class ) ) {
3445 osrfLogError(OSRF_LOG_MARK, "%s: ORDER BY clause references class \"%s\" "
3446 "not in FROM clause", MODULENAME, class );
3448 osrfAppSessionStatus(
3450 OSRF_STATUS_INTERNALSERVERERROR,
3451 "osrfMethodException",
3453 "Invalid class referenced in ORDER BY clause -- see error log for more details"
3457 buffer_free(group_buf);
3458 buffer_free(sql_buf);
3459 if (defaultselhash) jsonObjectFree(defaultselhash);
3463 osrfHash* field_def = oilsIDLFindPath( "/%s/fields/%s", class, field );
3465 osrfLogError(OSRF_LOG_MARK, "%s: Invalid field \"%s\".%s referenced in ORDER BY clause",
3466 MODULENAME, class, field );
3468 osrfAppSessionStatus(
3470 OSRF_STATUS_INTERNALSERVERERROR,
3471 "osrfMethodException",
3473 "Invalid field referenced in ORDER BY clause -- see error log for more details"
3477 buffer_free(group_buf);
3478 buffer_free(sql_buf);
3479 if (defaultselhash) jsonObjectFree(defaultselhash);
3481 } else if( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
3482 osrfLogError(OSRF_LOG_MARK, "%s: Virtual field \"%s\" in ORDER BY clause",
3483 MODULENAME, field );
3485 osrfAppSessionStatus(
3487 OSRF_STATUS_INTERNALSERVERERROR,
3488 "osrfMethodException",
3490 "Virtual field in ORDER BY clause -- see error log for more details"
3492 buffer_free( order_buf );
3495 buffer_free(group_buf);
3496 buffer_free(sql_buf);
3497 if (defaultselhash) jsonObjectFree(defaultselhash);
3501 if( jsonObjectGetKeyConst( order_spec, "transform" ) ) {
3502 char* transform_str = searchFieldTransform( class, field_def, order_spec );
3503 if( ! transform_str ) {
3505 osrfAppSessionStatus(
3507 OSRF_STATUS_INTERNALSERVERERROR,
3508 "osrfMethodException",
3510 "Severe query error in ORDER BY clause -- see error log for more details"
3512 buffer_free( order_buf );
3515 buffer_free(group_buf);
3516 buffer_free(sql_buf);
3517 if (defaultselhash) jsonObjectFree(defaultselhash);
3521 OSRF_BUFFER_ADD( order_buf, transform_str );
3522 free( transform_str );
3525 buffer_fadd( order_buf, "\"%s\".%s", class, field );
3527 const char* direction =
3528 jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "direction" ) );
3530 if( direction[ 0 ] || 'D' == direction[ 0 ] )
3531 OSRF_BUFFER_ADD( order_buf, " DESC" );
3533 OSRF_BUFFER_ADD( order_buf, " ASC" );
3536 } else if( JSON_HASH == order_hash->type ) {
3537 // This hash is keyed on class name. Each class has either
3538 // an array of field names or a hash keyed on field name.
3539 jsonIterator* class_itr = jsonNewIterator( order_hash );
3540 while ( (snode = jsonIteratorNext( class_itr )) ) {
3542 if ( ! jsonObjectGetKeyConst( selhash,class_itr->key )
3543 && strcmp( core_class, class_itr->key )
3544 && ! is_joined( join_hash, class_itr->key ) ) {
3545 osrfLogError(OSRF_LOG_MARK, "%s: Invalid class \"%s\" referenced in ORDER BY clause",
3546 MODULENAME, class_itr->key );
3548 osrfAppSessionStatus(
3550 OSRF_STATUS_INTERNALSERVERERROR,
3551 "osrfMethodException",
3553 "Invalid class referenced in ORDER BY clause -- see error log for more details"
3555 jsonIteratorFree( class_itr );
3556 buffer_free( order_buf );
3559 buffer_free(group_buf);
3560 buffer_free(sql_buf);
3561 if (defaultselhash) jsonObjectFree(defaultselhash);
3565 osrfHash* field_list_def = oilsIDLFindPath( "/%s/fields", class_itr->key );
3567 if ( snode->type == JSON_HASH ) {
3569 // Hash is keyed on field names from the current class. For each field
3570 // there is another layer of hash to define the sorting details, if any,
3571 // or a string to indicate direction of sorting.
3572 jsonIterator* order_itr = jsonNewIterator( snode );
3573 while ( (onode = jsonIteratorNext( order_itr )) ) {
3575 osrfHash* field_def = osrfHashGet( field_list_def, order_itr->key );
3577 osrfLogError(OSRF_LOG_MARK, "%s: Invalid field \"%s\" in ORDER BY clause",
3578 MODULENAME, order_itr->key );
3580 osrfAppSessionStatus(
3582 OSRF_STATUS_INTERNALSERVERERROR,
3583 "osrfMethodException",
3585 "Invalid field in ORDER BY clause -- see error log for more details"
3587 jsonIteratorFree( order_itr );
3588 jsonIteratorFree( class_itr );
3589 buffer_free( order_buf );
3592 buffer_free(group_buf);
3593 buffer_free(sql_buf);
3594 if (defaultselhash) jsonObjectFree(defaultselhash);
3596 } else if( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
3597 osrfLogError(OSRF_LOG_MARK, "%s: Virtual field \"%s\" in ORDER BY clause",
3598 MODULENAME, order_itr->key );
3600 osrfAppSessionStatus(
3602 OSRF_STATUS_INTERNALSERVERERROR,
3603 "osrfMethodException",
3605 "Virtual field in ORDER BY clause -- see error log for more details"
3607 jsonIteratorFree( order_itr );
3608 jsonIteratorFree( class_itr );
3609 buffer_free( order_buf );
3612 buffer_free(group_buf);
3613 buffer_free(sql_buf);
3614 if (defaultselhash) jsonObjectFree(defaultselhash);
3618 const char* direction = NULL;
3619 if ( onode->type == JSON_HASH ) {
3620 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3621 string = searchFieldTransform(
3623 osrfHashGet( field_list_def, order_itr->key ),
3627 if( ctx ) osrfAppSessionStatus(
3629 OSRF_STATUS_INTERNALSERVERERROR,
3630 "osrfMethodException",
3632 "Severe query error in ORDER BY clause -- see error log for more details"
3634 jsonIteratorFree( order_itr );
3635 jsonIteratorFree( class_itr );
3638 buffer_free(group_buf);
3639 buffer_free(order_buf);
3640 buffer_free(sql_buf);
3641 if (defaultselhash) jsonObjectFree(defaultselhash);
3645 growing_buffer* field_buf = buffer_init(16);
3646 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3647 string = buffer_release(field_buf);
3650 if ( (tmp_const = jsonObjectGetKeyConst( onode, "direction" )) ) {
3651 const char* dir = jsonObjectGetString(tmp_const);
3652 if (!strncasecmp(dir, "d", 1)) {
3653 direction = " DESC";
3659 } else if ( JSON_NULL == onode->type || JSON_ARRAY == onode->type ) {
3660 osrfLogError( OSRF_LOG_MARK,
3661 "%s: Expected JSON_STRING in ORDER BY clause; found %s",
3662 MODULENAME, json_type( onode->type ) );
3664 osrfAppSessionStatus(
3666 OSRF_STATUS_INTERNALSERVERERROR,
3667 "osrfMethodException",
3669 "Malformed ORDER BY clause -- see error log for more details"
3671 jsonIteratorFree( order_itr );
3672 jsonIteratorFree( class_itr );
3675 buffer_free(group_buf);
3676 buffer_free(order_buf);
3677 buffer_free(sql_buf);
3678 if (defaultselhash) jsonObjectFree(defaultselhash);
3682 string = strdup(order_itr->key);
3683 const char* dir = jsonObjectGetString(onode);
3684 if (!strncasecmp(dir, "d", 1)) {
3685 direction = " DESC";
3692 OSRF_BUFFER_ADD(order_buf, ", ");
3694 order_buf = buffer_init(128);
3696 OSRF_BUFFER_ADD(order_buf, string);
3700 OSRF_BUFFER_ADD(order_buf, direction);
3704 jsonIteratorFree(order_itr);
3706 } else if ( snode->type == JSON_ARRAY ) {
3708 // Array is a list of fields from the current class
3709 unsigned long order_idx = 0;
3710 while(( onode = jsonObjectGetIndex( snode, order_idx++ ) )) {
3712 const char* _f = jsonObjectGetString( onode );
3714 osrfHash* field_def = osrfHashGet( field_list_def, _f );
3716 osrfLogError(OSRF_LOG_MARK, "%s: Invalid field \"%s\" in ORDER BY clause",
3719 osrfAppSessionStatus(
3721 OSRF_STATUS_INTERNALSERVERERROR,
3722 "osrfMethodException",
3724 "Invalid field in ORDER BY clause -- see error log for more details"
3726 jsonIteratorFree( class_itr );
3727 buffer_free( order_buf );
3730 buffer_free(group_buf);
3731 buffer_free(sql_buf);
3732 if (defaultselhash) jsonObjectFree(defaultselhash);
3734 } else if( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
3735 osrfLogError(OSRF_LOG_MARK, "%s: Virtual field \"%s\" in ORDER BY clause",
3738 osrfAppSessionStatus(
3740 OSRF_STATUS_INTERNALSERVERERROR,
3741 "osrfMethodException",
3743 "Virtual field in ORDER BY clause -- see error log for more details"
3745 jsonIteratorFree( class_itr );
3746 buffer_free( order_buf );
3749 buffer_free(group_buf);
3750 buffer_free(sql_buf);
3751 if (defaultselhash) jsonObjectFree(defaultselhash);
3756 OSRF_BUFFER_ADD(order_buf, ", ");
3758 order_buf = buffer_init(128);
3760 buffer_fadd( order_buf, "\"%s\".%s", class_itr->key, _f);
3764 // IT'S THE OOOOOOOOOOOLD STYLE!
3766 osrfLogError(OSRF_LOG_MARK,
3767 "%s: Possible SQL injection attempt; direct order by is not allowed", MODULENAME);
3769 osrfAppSessionStatus(
3771 OSRF_STATUS_INTERNALSERVERERROR,
3772 "osrfMethodException",
3774 "Severe query error -- see error log for more details"
3780 buffer_free(group_buf);
3781 buffer_free(order_buf);
3782 buffer_free(sql_buf);
3783 if (defaultselhash) jsonObjectFree(defaultselhash);
3784 jsonIteratorFree(class_itr);
3788 jsonIteratorFree( class_itr );
3790 osrfLogError(OSRF_LOG_MARK,
3791 "%s: Malformed ORDER BY clause; expected JSON_HASH or JSON_ARRAY, found %s",
3792 MODULENAME, json_type( order_hash->type ) );
3794 osrfAppSessionStatus(
3796 OSRF_STATUS_INTERNALSERVERERROR,
3797 "osrfMethodException",
3799 "Malformed ORDER BY clause -- see error log for more details"
3801 buffer_free( order_buf );
3804 buffer_free(group_buf);
3805 buffer_free(sql_buf);
3806 if (defaultselhash) jsonObjectFree(defaultselhash);
3811 order_by_list = buffer_release( order_buf );
3815 string = buffer_release(group_buf);
3817 if ( *string && ( aggregate_found || (flags & SELECT_DISTINCT) ) ) {
3818 OSRF_BUFFER_ADD( sql_buf, " GROUP BY " );
3819 OSRF_BUFFER_ADD( sql_buf, string );
3824 if( having_buf && *having_buf ) {
3825 OSRF_BUFFER_ADD( sql_buf, " HAVING " );
3826 OSRF_BUFFER_ADD( sql_buf, having_buf );
3830 if( order_by_list ) {
3832 if ( *order_by_list ) {
3833 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3834 OSRF_BUFFER_ADD( sql_buf, order_by_list );
3837 free( order_by_list );
3841 const char* str = jsonObjectGetString(limit);
3842 buffer_fadd( sql_buf, " LIMIT %d", atoi(str) );
3846 const char* str = jsonObjectGetString(offset);
3847 buffer_fadd( sql_buf, " OFFSET %d", atoi(str) );
3850 if (!(flags & SUBSELECT)) OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3853 if (defaultselhash) jsonObjectFree(defaultselhash);
3855 return buffer_release(sql_buf);
3859 static char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
3861 const char* locale = osrf_message_get_last_locale();
3863 osrfHash* fields = osrfHashGet(meta, "fields");
3864 char* core_class = osrfHashGet(meta, "classname");
3866 const jsonObject* join_hash = jsonObjectGetKeyConst( order_hash, "join" );
3868 jsonObject* node = NULL;
3869 jsonObject* snode = NULL;
3870 jsonObject* onode = NULL;
3871 const jsonObject* _tmp = NULL;
3872 jsonObject* selhash = NULL;
3873 jsonObject* defaultselhash = NULL;
3875 growing_buffer* sql_buf = buffer_init(128);
3876 growing_buffer* select_buf = buffer_init(128);
3878 if ( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
3879 defaultselhash = jsonNewObjectType(JSON_HASH);
3880 selhash = defaultselhash;
3883 if ( !jsonObjectGetKeyConst(selhash,core_class) ) {
3884 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
3885 jsonObject* flist = jsonObjectGetKey( selhash, core_class );
3890 osrfStringArray* keys = osrfHashKeys( fields );
3891 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
3892 if( ! str_is_true( osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
3893 jsonObjectPush( flist, jsonNewObject( field ) );
3895 osrfStringArrayFree(keys);
3899 jsonIterator* class_itr = jsonNewIterator( selhash );
3900 while ( (snode = jsonIteratorNext( class_itr )) ) {
3902 char* cname = class_itr->key;
3903 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
3904 if (!idlClass) continue;
3906 if (strcmp(core_class,class_itr->key)) {
3907 if (!join_hash) continue;
3909 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", class_itr->key);
3911 jsonObjectFree(found);
3915 jsonObjectFree(found);
3918 jsonIterator* select_itr = jsonNewIterator( snode );
3919 while ( (node = jsonIteratorNext( select_itr )) ) {
3920 const char* item_str = jsonObjectGetString( node );
3921 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), item_str );
3922 char* fname = osrfHashGet(field, "name");
3924 if (!field) continue;
3929 OSRF_BUFFER_ADD_CHAR(select_buf, ',');
3934 const jsonObject* no_i18n_obj = jsonObjectGetKey( order_hash, "no_i18n" );
3935 if ( obj_is_true( no_i18n_obj ) ) // Suppress internationalization?
3938 i18n = osrfHashGet(field, "i18n");
3940 if( str_is_true( i18n ) ) {
3941 char* pkey = osrfHashGet(idlClass, "primarykey");
3942 char* tname = osrfHashGet(idlClass, "tablename");
3944 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);
3946 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3949 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3953 jsonIteratorFree(select_itr);
3956 jsonIteratorFree(class_itr);
3958 char* col_list = buffer_release(select_buf);
3959 char* table = getSourceDefinition(meta);
3961 table = strdup( "(null)" );
3963 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\"", col_list, table, core_class );
3968 char* join_clause = searchJOIN( join_hash, meta );
3969 OSRF_BUFFER_ADD_CHAR(sql_buf, ' ');
3970 OSRF_BUFFER_ADD(sql_buf, join_clause);
3974 osrfLogDebug(OSRF_LOG_MARK, "%s pre-predicate SQL = %s",
3975 MODULENAME, OSRF_BUFFER_C_STR(sql_buf));
3977 OSRF_BUFFER_ADD(sql_buf, " WHERE ");
3979 char* pred = searchWHERE( search_hash, meta, AND_OP_JOIN, ctx );
3981 osrfAppSessionStatus(
3983 OSRF_STATUS_INTERNALSERVERERROR,
3984 "osrfMethodException",
3986 "Severe query error -- see error log for more details"
3988 buffer_free(sql_buf);
3989 if(defaultselhash) jsonObjectFree(defaultselhash);
3992 buffer_add(sql_buf, pred);
3997 char* string = NULL;
3998 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "order_by" )) ){
4000 growing_buffer* order_buf = buffer_init(128);
4003 jsonIterator* class_itr = jsonNewIterator( _tmp );
4004 while ( (snode = jsonIteratorNext( class_itr )) ) {
4006 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
4009 if ( snode->type == JSON_HASH ) {
4011 jsonIterator* order_itr = jsonNewIterator( snode );
4012 while ( (onode = jsonIteratorNext( order_itr )) ) {
4014 osrfHash* field_def = oilsIDLFindPath( "/%s/fields/%s",
4015 class_itr->key, order_itr->key );
4019 char* direction = NULL;
4020 if ( onode->type == JSON_HASH ) {
4021 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
4022 string = searchFieldTransform( class_itr->key, field_def, onode );
4024 osrfAppSessionStatus(
4026 OSRF_STATUS_INTERNALSERVERERROR,
4027 "osrfMethodException",
4029 "Severe query error in ORDER BY clause -- see error log for more details"
4031 jsonIteratorFree( order_itr );
4032 jsonIteratorFree( class_itr );
4033 buffer_free( order_buf );
4034 buffer_free( sql_buf );
4035 if( defaultselhash ) jsonObjectFree( defaultselhash );
4039 growing_buffer* field_buf = buffer_init(16);
4040 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
4041 string = buffer_release(field_buf);
4044 if ( (_tmp = jsonObjectGetKeyConst( onode, "direction" )) ) {
4045 const char* dir = jsonObjectGetString(_tmp);
4046 if (!strncasecmp(dir, "d", 1)) {
4047 direction = " DESC";
4054 string = strdup(order_itr->key);
4055 const char* dir = jsonObjectGetString(onode);
4056 if (!strncasecmp(dir, "d", 1)) {
4057 direction = " DESC";
4066 buffer_add(order_buf, ", ");
4069 buffer_add(order_buf, string);
4073 buffer_add(order_buf, direction);
4078 jsonIteratorFree(order_itr);
4081 const char* str = jsonObjectGetString(snode);
4082 buffer_add(order_buf, str);
4088 jsonIteratorFree(class_itr);
4090 string = buffer_release(order_buf);
4093 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
4094 OSRF_BUFFER_ADD( sql_buf, string );
4100 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "limit" )) ){
4101 const char* str = jsonObjectGetString(_tmp);
4109 _tmp = jsonObjectGetKeyConst( order_hash, "offset" );
4111 const char* str = jsonObjectGetString(_tmp);
4120 if (defaultselhash) jsonObjectFree(defaultselhash);
4122 OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
4123 return buffer_release(sql_buf);
4126 int doJSONSearch ( osrfMethodContext* ctx ) {
4127 if(osrfMethodVerifyContext( ctx )) {
4128 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
4132 osrfLogDebug(OSRF_LOG_MARK, "Recieved query request");
4137 dbhandle = writehandle;
4139 jsonObject* hash = jsonObjectGetIndex(ctx->params, 0);
4143 if ( obj_is_true( jsonObjectGetKey( hash, "distinct" ) ) )
4144 flags |= SELECT_DISTINCT;
4146 if ( obj_is_true( jsonObjectGetKey( hash, "no_i18n" ) ) )
4147 flags |= DISABLE_I18N;
4149 osrfLogDebug(OSRF_LOG_MARK, "Building SQL ...");
4152 jsonObjectGetKey( hash, "select" ),
4153 jsonObjectGetKey( hash, "from" ),
4154 jsonObjectGetKey( hash, "where" ),
4155 jsonObjectGetKey( hash, "having" ),
4156 jsonObjectGetKey( hash, "order_by" ),
4157 jsonObjectGetKey( hash, "limit" ),
4158 jsonObjectGetKey( hash, "offset" ),
4167 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
4168 dbi_result result = dbi_conn_query(dbhandle, sql);
4171 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
4173 if (dbi_result_first_row(result)) {
4174 /* JSONify the result */
4175 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
4178 jsonObject* return_val = oilsMakeJSONFromResult( result );
4179 osrfAppRespond( ctx, return_val );
4180 jsonObjectFree( return_val );
4181 } while (dbi_result_next_row(result));
4184 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
4187 osrfAppRespondComplete( ctx, NULL );
4189 /* clean up the query */
4190 dbi_result_free(result);
4194 osrfLogError(OSRF_LOG_MARK, "%s: Error with query [%s]", MODULENAME, sql);
4195 osrfAppSessionStatus(
4197 OSRF_STATUS_INTERNALSERVERERROR,
4198 "osrfMethodException",
4200 "Severe query error -- see error log for more details"
4208 static jsonObject* doFieldmapperSearch ( osrfMethodContext* ctx, osrfHash* meta,
4209 jsonObject* where_hash, jsonObject* query_hash, int* err ) {
4212 dbhandle = writehandle;
4214 osrfHash* links = osrfHashGet(meta, "links");
4215 osrfHash* fields = osrfHashGet(meta, "fields");
4216 char* core_class = osrfHashGet(meta, "classname");
4217 char* pkey = osrfHashGet(meta, "primarykey");
4219 const jsonObject* _tmp;
4222 char* sql = buildSELECT( where_hash, query_hash, meta, ctx );
4224 osrfLogDebug(OSRF_LOG_MARK, "Problem building query, returning NULL");
4229 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
4231 dbi_result result = dbi_conn_query(dbhandle, sql);
4232 if( NULL == result ) {
4233 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]",
4234 MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
4235 osrfAppSessionStatus(
4237 OSRF_STATUS_INTERNALSERVERERROR,
4238 "osrfMethodException",
4240 "Severe query error -- see error log for more details"
4247 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
4250 jsonObject* res_list = jsonNewObjectType(JSON_ARRAY);
4251 osrfHash* dedup = osrfNewHash();
4253 if (dbi_result_first_row(result)) {
4254 /* JSONify the result */
4255 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
4257 obj = oilsMakeFieldmapperFromResult( result, meta );
4258 char* pkey_val = oilsFMGetString( obj, pkey );
4259 if ( osrfHashGet( dedup, pkey_val ) ) {
4260 jsonObjectFree(obj);
4263 osrfHashSet( dedup, pkey_val, pkey_val );
4264 jsonObjectPush(res_list, obj);
4266 } while (dbi_result_next_row(result));
4268 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s",
4272 osrfHashFree(dedup);
4273 /* clean up the query */
4274 dbi_result_free(result);
4277 if (res_list->size && query_hash) {
4278 _tmp = jsonObjectGetKeyConst( query_hash, "flesh" );
4280 int x = (int)jsonObjectGetNumber(_tmp);
4281 if (x == -1 || x > max_flesh_depth) x = max_flesh_depth;
4283 const jsonObject* temp_blob;
4284 if ((temp_blob = jsonObjectGetKeyConst( query_hash, "flesh_fields" )) && x > 0) {
4286 jsonObject* flesh_blob = jsonObjectClone( temp_blob );
4287 const jsonObject* flesh_fields = jsonObjectGetKeyConst( flesh_blob, core_class );
4289 osrfStringArray* link_fields = NULL;
4292 if (flesh_fields->size == 1) {
4293 const char* _t = jsonObjectGetString( jsonObjectGetIndex( flesh_fields, 0 ) );
4294 if (!strcmp(_t,"*")) link_fields = osrfHashKeys( links );
4299 link_fields = osrfNewStringArray(1);
4300 jsonIterator* _i = jsonNewIterator( flesh_fields );
4301 while ((_f = jsonIteratorNext( _i ))) {
4302 osrfStringArrayAdd( link_fields, jsonObjectGetString( _f ) );
4304 jsonIteratorFree(_i);
4309 unsigned long res_idx = 0;
4310 while ((cur = jsonObjectGetIndex( res_list, res_idx++ ) )) {
4315 while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
4317 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
4319 osrfHash* kid_link = osrfHashGet(links, link_field);
4320 if (!kid_link) continue;
4322 osrfHash* field = osrfHashGet(fields, link_field);
4323 if (!field) continue;
4325 osrfHash* value_field = field;
4327 osrfHash* kid_idl = osrfHashGet(oilsIDL(), osrfHashGet(kid_link, "class"));
4328 if (!kid_idl) continue;
4330 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
4331 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
4334 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
4335 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
4338 osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
4340 if (link_map->size > 0) {
4341 jsonObject* _kid_key = jsonNewObjectType(JSON_ARRAY);
4344 jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
4349 osrfHashGet(kid_link, "class"),
4356 "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
4357 osrfHashGet(kid_link, "field"),
4358 osrfHashGet(kid_link, "class"),
4359 osrfHashGet(kid_link, "key"),
4360 osrfHashGet(kid_link, "reltype")
4363 const char* search_key = jsonObjectGetString(
4366 atoi( osrfHashGet(value_field, "array_position") )
4371 osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
4375 osrfLogDebug(OSRF_LOG_MARK, "Creating param objects...");
4377 // construct WHERE clause
4378 jsonObject* where_clause = jsonNewObjectType(JSON_HASH);
4381 osrfHashGet(kid_link, "key"),
4382 jsonNewObject( search_key )
4385 // construct the rest of the query
4386 jsonObject* rest_of_query = jsonNewObjectType(JSON_HASH);
4387 jsonObjectSetKey( rest_of_query, "flesh",
4388 jsonNewNumberObject( (double)(x - 1 + link_map->size) )
4392 jsonObjectSetKey( rest_of_query, "flesh_fields", jsonObjectClone(flesh_blob) );
4394 if (jsonObjectGetKeyConst(query_hash, "order_by")) {
4395 jsonObjectSetKey( rest_of_query, "order_by",
4396 jsonObjectClone(jsonObjectGetKeyConst(query_hash, "order_by"))
4400 if (jsonObjectGetKeyConst(query_hash, "select")) {
4401 jsonObjectSetKey( rest_of_query, "select",
4402 jsonObjectClone(jsonObjectGetKeyConst(query_hash, "select"))
4406 jsonObject* kids = doFieldmapperSearch( ctx, kid_idl,
4407 where_clause, rest_of_query, err);
4409 jsonObjectFree( where_clause );
4410 jsonObjectFree( rest_of_query );
4413 osrfStringArrayFree(link_fields);
4414 jsonObjectFree(res_list);
4415 jsonObjectFree(flesh_blob);
4419 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
4421 jsonObject* X = NULL;
4422 if ( link_map->size > 0 && kids->size > 0 ) {
4424 kids = jsonNewObjectType(JSON_ARRAY);
4426 jsonObject* _k_node;
4427 unsigned long res_idx = 0;
4428 while ((_k_node = jsonObjectGetIndex( X, res_idx++ ) )) {
4434 (unsigned long)atoi(
4440 osrfHashGet(kid_link, "class")
4444 osrfStringArrayGetString( link_map, 0 )
4452 } // end while loop traversing X
4455 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" )) || !(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) {
4456 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
4459 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
4460 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
4464 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
4465 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
4468 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
4469 jsonObjectClone( kids )
4474 jsonObjectFree(kids);
4478 jsonObjectFree( kids );
4480 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
4481 osrfLogDebug(OSRF_LOG_MARK, "%s", jsonObjectToJSON(cur));
4484 } // end while loop traversing res_list
4485 jsonObjectFree( flesh_blob );
4486 osrfStringArrayFree(link_fields);
4495 static jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
4497 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4499 jsonObject* target = jsonObjectGetIndex( ctx->params, 1 );
4501 jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
4504 if (!verifyObjectClass(ctx, target)) {
4509 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4510 osrfAppSessionStatus(
4512 OSRF_STATUS_BADREQUEST,
4513 "osrfMethodException",
4515 "No active transaction -- required for UPDATE"
4521 // The following test is harmless but redundant. If a class is
4522 // readonly, we don't register an update method for it.
4523 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4524 osrfAppSessionStatus(
4526 OSRF_STATUS_BADREQUEST,
4527 "osrfMethodException",
4529 "Cannot UPDATE readonly class"
4535 dbhandle = writehandle;
4537 char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
4539 // Set the last_xact_id
4540 int index = oilsIDL_ntop( target->classname, "last_xact_id" );
4542 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
4543 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
4546 char* pkey = osrfHashGet(meta, "primarykey");
4547 osrfHash* fields = osrfHashGet(meta, "fields");
4549 char* id = oilsFMGetString( target, pkey );
4553 "%s updating %s object with %s = %s",
4555 osrfHashGet(meta, "fieldmapper"),
4560 growing_buffer* sql = buffer_init(128);
4561 buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
4566 osrfStringArray* field_list = osrfHashKeys( fields );
4567 while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
4569 osrfHash* field = osrfHashGet( fields, field_name );
4571 if(!( strcmp( field_name, pkey ) )) continue;
4572 if( str_is_true( osrfHashGet(osrfHashGet(fields,field_name), "virtual") ) )
4575 const jsonObject* field_object = oilsFMGetObject( target, field_name );
4577 int value_is_numeric = 0; // boolean
4579 if (field_object && field_object->classname) {
4580 value = oilsFMGetString(
4582 (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
4585 value = jsonObjectToSimpleString( field_object );
4586 if( field_object && JSON_NUMBER == field_object->type )
4587 value_is_numeric = 1;
4590 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
4592 if (!field_object || field_object->type == JSON_NULL) {
4593 if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
4594 if (first) first = 0;
4595 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4596 buffer_fadd( sql, " %s = NULL", field_name );
4599 } else if ( value_is_numeric || !strcmp( get_primitive( field ), "number") ) {
4600 if (first) first = 0;
4601 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4603 const char* numtype = get_datatype( field );
4604 if ( !strncmp( numtype, "INT", 3 ) ) {
4605 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
4606 } else if ( !strcmp( numtype, "NUMERIC" ) ) {
4607 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
4609 // Must really be intended as a string, so quote it
4610 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4611 buffer_fadd( sql, " %s = %s", field_name, value );
4613 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4614 osrfAppSessionStatus(
4616 OSRF_STATUS_INTERNALSERVERERROR,
4617 "osrfMethodException",
4619 "Error quoting string -- please see the error log for more details"
4629 osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, numtype );
4632 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4633 if (first) first = 0;
4634 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4635 buffer_fadd( sql, " %s = %s", field_name, value );
4638 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4639 osrfAppSessionStatus(
4641 OSRF_STATUS_INTERNALSERVERERROR,
4642 "osrfMethodException",
4644 "Error quoting string -- please see the error log for more details"
4658 jsonObject* obj = jsonNewObject(id);
4660 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4661 dbi_conn_quote_string(dbhandle, &id);
4663 buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
4665 char* query = buffer_release(sql);
4666 osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
4668 dbi_result result = dbi_conn_query(dbhandle, query);
4672 jsonObjectFree(obj);
4673 obj = jsonNewObject(NULL);
4676 "%s ERROR updating %s object with %s = %s",
4678 osrfHashGet(meta, "fieldmapper"),
4689 static jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
4691 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4693 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4694 osrfAppSessionStatus(
4696 OSRF_STATUS_BADREQUEST,
4697 "osrfMethodException",
4699 "No active transaction -- required for DELETE"
4705 // The following test is harmless but redundant. If a class is
4706 // readonly, we don't register a delete method for it.
4707 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4708 osrfAppSessionStatus(
4710 OSRF_STATUS_BADREQUEST,
4711 "osrfMethodException",
4713 "Cannot DELETE readonly class"
4719 dbhandle = writehandle;
4723 char* pkey = osrfHashGet(meta, "primarykey");
4731 if (jsonObjectGetIndex(ctx->params, _obj_pos)->classname) {
4732 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, _obj_pos ))) {
4737 id = oilsFMGetString( jsonObjectGetIndex(ctx->params, _obj_pos), pkey );
4740 if (!verifyObjectPCRUD( ctx, NULL )) {
4745 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, _obj_pos));
4750 "%s deleting %s object with %s = %s",
4752 osrfHashGet(meta, "fieldmapper"),
4757 obj = jsonNewObject(id);
4759 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4760 dbi_conn_quote_string(writehandle, &id);
4762 dbi_result result = dbi_conn_queryf(writehandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
4765 jsonObjectFree(obj);
4766 obj = jsonNewObject(NULL);
4769 "%s ERROR deleting %s object with %s = %s",
4771 osrfHashGet(meta, "fieldmapper"),
4784 static jsonObject* oilsMakeFieldmapperFromResult( dbi_result result, osrfHash* meta) {
4785 if(!(result && meta)) return jsonNULL;
4787 jsonObject* object = jsonNewObject(NULL);
4788 jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
4790 osrfHash* fields = osrfHashGet(meta, "fields");
4792 osrfLogInternal(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
4796 char dt_string[256];
4800 int columnIndex = 1;
4802 unsigned short type;
4803 const char* columnName;
4805 /* cycle through the column list */
4806 while( (columnName = dbi_result_get_field_name(result, columnIndex)) ) {
4808 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4810 fmIndex = -1; // reset the position
4812 /* determine the field type and storage attributes */
4813 type = dbi_result_get_field_type_idx(result, columnIndex);
4814 attr = dbi_result_get_field_attribs_idx(result, columnIndex);
4816 /* fetch the fieldmapper index */
4817 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
4819 if ( str_is_true( osrfHashGet(_f, "virtual") ) )
4822 const char* pos = (char*)osrfHashGet(_f, "array_position");
4823 if ( !pos ) continue;
4825 fmIndex = atoi( pos );
4826 osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
4831 if (dbi_result_field_is_null_idx(result, columnIndex)) {
4832 jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
4837 case DBI_TYPE_INTEGER :
4839 if( attr & DBI_INTEGER_SIZE8 )
4840 jsonObjectSetIndex( object, fmIndex,
4841 jsonNewNumberObject(dbi_result_get_longlong_idx(result, columnIndex)));
4843 jsonObjectSetIndex( object, fmIndex,
4844 jsonNewNumberObject(dbi_result_get_int_idx(result, columnIndex)));
4848 case DBI_TYPE_DECIMAL :
4849 jsonObjectSetIndex( object, fmIndex,
4850 jsonNewNumberObject(dbi_result_get_double_idx(result, columnIndex)));
4853 case DBI_TYPE_STRING :
4859 jsonNewObject( dbi_result_get_string_idx(result, columnIndex) )
4864 case DBI_TYPE_DATETIME :
4866 memset(dt_string, '\0', sizeof(dt_string));
4867 memset(&gmdt, '\0', sizeof(gmdt));
4869 _tmp_dt = dbi_result_get_datetime_idx(result, columnIndex);
4872 if (!(attr & DBI_DATETIME_DATE)) {
4873 gmtime_r( &_tmp_dt, &gmdt );
4874 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4875 } else if (!(attr & DBI_DATETIME_TIME)) {
4876 localtime_r( &_tmp_dt, &gmdt );
4877 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4879 localtime_r( &_tmp_dt, &gmdt );
4880 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4883 jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
4887 case DBI_TYPE_BINARY :
4888 osrfLogError( OSRF_LOG_MARK,
4889 "Can't do binary at column %s : index %d", columnName, columnIndex);
4898 static jsonObject* oilsMakeJSONFromResult( dbi_result result ) {
4899 if(!result) return jsonNULL;
4901 jsonObject* object = jsonNewObject(NULL);
4904 char dt_string[256];
4908 int columnIndex = 1;
4910 unsigned short type;
4911 const char* columnName;
4913 /* cycle through the column list */
4914 while( (columnName = dbi_result_get_field_name(result, columnIndex)) ) {
4916 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4918 fmIndex = -1; // reset the position
4920 /* determine the field type and storage attributes */
4921 type = dbi_result_get_field_type_idx(result, columnIndex);
4922 attr = dbi_result_get_field_attribs_idx(result, columnIndex);
4924 if (dbi_result_field_is_null_idx(result, columnIndex)) {
4925 jsonObjectSetKey( object, columnName, jsonNewObject(NULL) );
4930 case DBI_TYPE_INTEGER :
4932 if( attr & DBI_INTEGER_SIZE8 )
4933 jsonObjectSetKey( object, columnName,
4934 jsonNewNumberObject(dbi_result_get_longlong_idx(result, columnIndex)) );
4936 jsonObjectSetKey( object, columnName,
4937 jsonNewNumberObject(dbi_result_get_int_idx(result, columnIndex)) );
4940 case DBI_TYPE_DECIMAL :
4941 jsonObjectSetKey( object, columnName,
4942 jsonNewNumberObject(dbi_result_get_double_idx(result, columnIndex)) );
4945 case DBI_TYPE_STRING :
4946 jsonObjectSetKey( object, columnName,
4947 jsonNewObject(dbi_result_get_string_idx(result, columnIndex)) );
4950 case DBI_TYPE_DATETIME :
4952 memset(dt_string, '\0', sizeof(dt_string));
4953 memset(&gmdt, '\0', sizeof(gmdt));
4955 _tmp_dt = dbi_result_get_datetime_idx(result, columnIndex);
4958 if (!(attr & DBI_DATETIME_DATE)) {
4959 gmtime_r( &_tmp_dt, &gmdt );
4960 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4961 } else if (!(attr & DBI_DATETIME_TIME)) {
4962 localtime_r( &_tmp_dt, &gmdt );
4963 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4965 localtime_r( &_tmp_dt, &gmdt );
4966 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4969 jsonObjectSetKey( object, columnName, jsonNewObject(dt_string) );
4972 case DBI_TYPE_BINARY :
4973 osrfLogError( OSRF_LOG_MARK,
4974 "Can't do binary at column %s : index %d", columnName, columnIndex );
4978 } // end while loop traversing result
4983 // Interpret a string as true or false
4984 static int str_is_true( const char* str ) {
4985 if( NULL == str || strcasecmp( str, "true" ) )
4991 // Interpret a jsonObject as true or false
4992 static int obj_is_true( const jsonObject* obj ) {
4995 else switch( obj->type )
5003 if( strcasecmp( obj->value.s, "true" ) )
5007 case JSON_NUMBER : // Support 1/0 for perl's sake
5008 if( jsonObjectGetNumber( obj ) == 1.0 )
5017 // Translate a numeric code into a text string identifying a type of
5018 // jsonObject. To be used for building error messages.
5019 static const char* json_type( int code ) {
5025 return "JSON_ARRAY";
5027 return "JSON_STRING";
5029 return "JSON_NUMBER";
5035 return "(unrecognized)";
5039 // Extract the "primitive" attribute from an IDL field definition.
5040 // If we haven't initialized the app, then we must be running in
5041 // some kind of testbed. In that case, default to "string".
5042 static const char* get_primitive( osrfHash* field ) {
5043 const char* s = osrfHashGet( field, "primitive" );
5045 if( child_initialized )
5048 "%s ERROR No \"datatype\" attribute for field \"%s\"",
5050 osrfHashGet( field, "name" )
5058 // Extract the "datatype" attribute from an IDL field definition.
5059 // If we haven't initialized the app, then we must be running in
5060 // some kind of testbed. In that case, default to to NUMERIC,
5061 // since we look at the datatype only for numbers.
5062 static const char* get_datatype( osrfHash* field ) {
5063 const char* s = osrfHashGet( field, "datatype" );
5065 if( child_initialized )
5068 "%s ERROR No \"datatype\" attribute for field \"%s\"",
5070 osrfHashGet( field, "name" )
5079 If the input string is potentially a valid SQL identifier, return 1.
5082 Purpose: to prevent certain kinds of SQL injection. To that end we
5083 don't necessarily need to follow all the rules exactly, such as requiring
5084 that the first character not be a digit.
5086 We allow leading and trailing white space. In between, we do not allow
5087 punctuation (except for underscores and dollar signs), control
5088 characters, or embedded white space.
5090 More pedantically we should allow quoted identifiers containing arbitrary
5091 characters, but for the foreseeable future such quoted identifiers are not
5092 likely to be an issue.
5094 static int is_identifier( const char* s) {
5098 // Skip leading white space
5099 while( isspace( (unsigned char) *s ) )
5103 return 0; // Nothing but white space? Not okay.
5105 // Check each character until we reach white space or
5106 // end-of-string. Letters, digits, underscores, and
5107 // dollar signs are okay. With the exception of periods
5108 // (as in schema.identifier), control characters and other
5109 // punctuation characters are not okay. Anything else
5110 // is okay -- it could for example be part of a multibyte
5111 // UTF8 character such as a letter with diacritical marks,
5112 // and those are allowed.
5114 if( isalnum( (unsigned char) *s )
5118 ; // Fine; keep going
5119 else if( ispunct( (unsigned char) *s )
5120 || iscntrl( (unsigned char) *s ) )
5123 } while( *s && ! isspace( (unsigned char) *s ) );
5125 // If we found any white space in the above loop,
5126 // the rest had better be all white space.
5128 while( isspace( (unsigned char) *s ) )
5132 return 0; // White space was embedded within non-white space
5138 Determine whether to accept a character string as a comparison operator.
5139 Return 1 if it's good, or 0 if it's bad.
5141 We don't validate it for real. We just make sure that it doesn't contain
5142 any semicolons or white space (with a special exception for the
5143 "SIMILAR TO" operator). The idea is to block certain kinds of SQL
5144 injection. If it has no semicolons or white space but it's still not a
5145 valid operator, then the database will complain.
5147 Another approach would be to compare the string against a short list of
5148 approved operators. We don't do that because we want to allow custom
5149 operators like ">100*", which would be difficult or impossible to
5150 express otherwise in a JSON query.
5152 static int is_good_operator( const char* op ) {
5153 if( !op ) return 0; // Sanity check
5157 if( isspace( (unsigned char) *s ) ) {
5158 // Special exception for SIMILAR TO. Someday we might make
5159 // exceptions for IS DISTINCT FROM and IS NOT DISTINCT FROM.
5160 if( !strcasecmp( op, "similar to" ) )
5165 else if( ';' == *s )