2 #include "opensrf/osrf_application.h"
3 #include "opensrf/osrf_settings.h"
4 #include "opensrf/osrf_message.h"
5 #include "opensrf/utils.h"
6 #include "opensrf/osrf_json.h"
7 #include "opensrf/log.h"
8 #include "openils/oils_utils.h"
17 # define MODULENAME "open-ils.reporter-store"
20 # define MODULENAME "open-ils.pcrud"
22 # define MODULENAME "open-ils.cstore"
27 #define DISABLE_I18N 2
28 #define SELECT_DISTINCT 1
32 int osrfAppChildInit();
33 int osrfAppInitialize();
34 void osrfAppChildExit();
36 static int verifyObjectClass ( osrfMethodContext*, const jsonObject* );
38 int beginTransaction ( osrfMethodContext* );
39 int commitTransaction ( osrfMethodContext* );
40 int rollbackTransaction ( osrfMethodContext* );
42 int setSavepoint ( osrfMethodContext* );
43 int releaseSavepoint ( osrfMethodContext* );
44 int rollbackSavepoint ( osrfMethodContext* );
46 int doJSONSearch ( osrfMethodContext* );
48 int dispatchCRUDMethod ( osrfMethodContext* );
49 static jsonObject* doCreate ( osrfMethodContext*, int* );
50 static jsonObject* doRetrieve ( osrfMethodContext*, int* );
51 static jsonObject* doUpdate ( osrfMethodContext*, int* );
52 static jsonObject* doDelete ( osrfMethodContext*, int* );
53 static jsonObject* doFieldmapperSearch ( osrfMethodContext*, osrfHash*,
54 const jsonObject*, int* );
55 static jsonObject* oilsMakeFieldmapperFromResult( dbi_result, osrfHash* );
56 static jsonObject* oilsMakeJSONFromResult( dbi_result );
58 static char* searchSimplePredicate ( const char* op, const char* class,
59 osrfHash* field, const jsonObject* node );
60 static char* searchFunctionPredicate ( const char*, osrfHash*, const jsonObject*, const char* );
61 static char* searchFieldTransform ( const char*, osrfHash*, const jsonObject*);
62 static char* searchFieldTransformPredicate ( const char*, osrfHash*, 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* );
89 static int child_initialized = 0; /* boolean */
91 static dbi_conn writehandle; /* our MASTER db connection */
92 static dbi_conn dbhandle; /* our CURRENT db connection */
93 //static osrfHash * readHandles;
94 static jsonObject* jsonNULL = NULL; //
95 static int max_flesh_depth = 100;
97 /* called when this process is about to exit */
98 void osrfAppChildExit() {
99 osrfLogDebug(OSRF_LOG_MARK, "Child is exiting, disconnecting from database...");
102 if (writehandle == dbhandle) same = 1;
104 dbi_conn_query(writehandle, "ROLLBACK;");
105 dbi_conn_close(writehandle);
108 if (dbhandle && !same)
109 dbi_conn_close(dbhandle);
111 // XXX add cleanup of readHandles whenever that gets used
116 int osrfAppInitialize() {
118 osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
119 osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
121 if (!oilsIDLInit( osrf_settings_host_value("/IDL") )) return 1; /* return non-zero to indicate error */
123 growing_buffer* method_name = buffer_init(64);
125 // Generic search thingy
126 buffer_add(method_name, MODULENAME);
127 buffer_add(method_name, ".json_query");
128 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
129 "doJSONSearch", "", 1, OSRF_METHOD_STREAMING );
132 // first we register all the transaction and savepoint methods
133 buffer_reset(method_name);
134 OSRF_BUFFER_ADD(method_name, MODULENAME);
135 OSRF_BUFFER_ADD(method_name, ".transaction.begin");
136 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
137 "beginTransaction", "", 0, 0 );
139 buffer_reset(method_name);
140 OSRF_BUFFER_ADD(method_name, MODULENAME);
141 OSRF_BUFFER_ADD(method_name, ".transaction.commit");
142 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
143 "commitTransaction", "", 0, 0 );
145 buffer_reset(method_name);
146 OSRF_BUFFER_ADD(method_name, MODULENAME);
147 OSRF_BUFFER_ADD(method_name, ".transaction.rollback");
148 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
149 "rollbackTransaction", "", 0, 0 );
151 buffer_reset(method_name);
152 OSRF_BUFFER_ADD(method_name, MODULENAME);
153 OSRF_BUFFER_ADD(method_name, ".savepoint.set");
154 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
155 "setSavepoint", "", 1, 0 );
157 buffer_reset(method_name);
158 OSRF_BUFFER_ADD(method_name, MODULENAME);
159 OSRF_BUFFER_ADD(method_name, ".savepoint.release");
160 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
161 "releaseSavepoint", "", 1, 0 );
163 buffer_reset(method_name);
164 OSRF_BUFFER_ADD(method_name, MODULENAME);
165 OSRF_BUFFER_ADD(method_name, ".savepoint.rollback");
166 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
167 "rollbackSavepoint", "", 1, 0 );
169 buffer_free(method_name);
171 static const char* global_method[] = {
179 const int global_method_count
180 = sizeof( global_method ) / sizeof ( global_method[0] );
184 osrfStringArray* classes = osrfHashKeys( oilsIDL() );
185 osrfLogDebug(OSRF_LOG_MARK, "%d classes loaded", classes->size );
186 osrfLogDebug(OSRF_LOG_MARK,
187 "At least %d methods will be generated", classes->size * global_method_count);
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 const char* readonly = osrfHashGet(idlClass, "readonly");
208 osrfHash* idlClass_permacrud = osrfHashGet(idlClass, "permacrud");
212 for( i = 0; i < global_method_count; ++i ) {
213 const char* method_type = global_method[ i ];
214 osrfLogDebug(OSRF_LOG_MARK,
215 "Using files to build %s class methods for %s", method_type, classname);
217 if (!idlClass_fieldmapper) continue;
220 if (!idlClass_permacrud) continue;
222 const char* tmp_method = method_type;
223 if ( *tmp_method == 'i' || *tmp_method == 's') {
224 tmp_method = "retrieve";
226 if (!osrfHashGet( idlClass_permacrud, tmp_method )) continue;
229 if ( str_is_true( readonly ) &&
230 ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd')
233 osrfHash* method_meta = osrfNewHash();
234 osrfHashSet(method_meta, idlClass, "class");
236 method_name = buffer_init(64);
238 buffer_fadd(method_name, "%s.%s.%s", MODULENAME, method_type, classname);
242 char* _fm = strdup( idlClass_fieldmapper );
243 part = strtok_r(_fm, ":", &st_tmp);
245 buffer_fadd(method_name, "%s.direct.%s", MODULENAME, part);
247 while ((part = strtok_r(NULL, ":", &st_tmp))) {
248 OSRF_BUFFER_ADD_CHAR(method_name, '.');
249 OSRF_BUFFER_ADD(method_name, part);
251 OSRF_BUFFER_ADD_CHAR(method_name, '.');
252 OSRF_BUFFER_ADD(method_name, method_type);
256 char* method = buffer_release(method_name);
258 osrfHashSet( method_meta, method, "methodname" );
259 osrfHashSet( method_meta, strdup(method_type), "methodtype" );
262 if (*method_type == 'i' || *method_type == 's') {
263 flags = flags | OSRF_METHOD_STREAMING;
266 osrfAppRegisterExtendedMethod(
269 "dispatchCRUDMethod",
283 static char* getSourceDefinition( osrfHash* class ) {
285 char* tabledef = osrfHashGet(class, "tablename");
288 tabledef = strdup(tabledef);
290 tabledef = osrfHashGet(class, "source_definition");
292 growing_buffer* tablebuf = buffer_init(128);
293 buffer_fadd( tablebuf, "(%s)", tabledef );
294 tabledef = buffer_release(tablebuf);
296 const char* classname = osrfHashGet( class, "classname" );
301 "%s ERROR No tablename or source_definition for class \"%s\"",
312 * Connects to the database
314 int osrfAppChildInit() {
316 osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
317 dbi_initialize(NULL);
318 osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
320 char* driver = osrf_settings_host_value("/apps/%s/app_settings/driver", MODULENAME);
321 char* user = osrf_settings_host_value("/apps/%s/app_settings/database/user", MODULENAME);
322 char* host = osrf_settings_host_value("/apps/%s/app_settings/database/host", MODULENAME);
323 char* port = osrf_settings_host_value("/apps/%s/app_settings/database/port", MODULENAME);
324 char* db = osrf_settings_host_value("/apps/%s/app_settings/database/db", MODULENAME);
325 char* pw = osrf_settings_host_value("/apps/%s/app_settings/database/pw", MODULENAME);
326 char* md = osrf_settings_host_value("/apps/%s/app_settings/max_query_recursion", MODULENAME);
328 osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
329 writehandle = dbi_conn_new(driver);
332 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
335 osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
337 osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database. host=%s, "
338 "port=%s, user=%s, pw=%s, db=%s", MODULENAME, host, port, user, pw, db );
340 if(host) dbi_conn_set_option(writehandle, "host", host );
341 if(port) dbi_conn_set_option_numeric( writehandle, "port", atoi(port) );
342 if(user) dbi_conn_set_option(writehandle, "username", user);
343 if(pw) dbi_conn_set_option(writehandle, "password", pw );
344 if(db) dbi_conn_set_option(writehandle, "dbname", db );
346 if(md) max_flesh_depth = atoi(md);
347 if(max_flesh_depth < 0) max_flesh_depth = 1;
348 if(max_flesh_depth > 1000) max_flesh_depth = 1000;
357 if (dbi_conn_connect(writehandle) < 0) {
359 if (dbi_conn_connect(writehandle) < 0) {
360 dbi_conn_error(writehandle, &err);
361 osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
366 osrfLogInfo(OSRF_LOG_MARK, "%s successfully connected to the database", MODULENAME);
371 osrfStringArray* classes = osrfHashKeys( oilsIDL() );
373 while ( (classname = osrfStringArrayGetString(classes, i++)) ) {
374 osrfHash* class = osrfHashGet( oilsIDL(), classname );
375 osrfHash* fields = osrfHashGet( class, "fields" );
377 if( str_is_true( osrfHashGet(class, "virtual") ) ) {
378 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
382 char* tabledef = getSourceDefinition(class);
384 tabledef = strdup( "(null)" );
386 growing_buffer* sql_buf = buffer_init(32);
387 buffer_fadd( sql_buf, "SELECT * FROM %s AS x WHERE 1=0;", tabledef );
391 char* sql = buffer_release(sql_buf);
392 osrfLogDebug(OSRF_LOG_MARK, "%s Investigatory SQL = %s", MODULENAME, sql);
394 dbi_result result = dbi_conn_query(writehandle, sql);
400 const char* columnName;
402 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
404 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
406 /* fetch the fieldmapper index */
407 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
409 osrfLogDebug(OSRF_LOG_MARK, "Found [%s] in IDL hash...", (char*)columnName);
411 /* determine the field type and storage attributes */
412 type = dbi_result_get_field_type(result, columnName);
416 case DBI_TYPE_INTEGER : {
418 if ( !osrfHashGet(_f, "primitive") )
419 osrfHashSet(_f,"number", "primitive");
421 int attr = dbi_result_get_field_attribs(result, columnName);
422 if( attr & DBI_INTEGER_SIZE8 )
423 osrfHashSet(_f,"INT8", "datatype");
425 osrfHashSet(_f,"INT", "datatype");
428 case DBI_TYPE_DECIMAL :
429 if ( !osrfHashGet(_f, "primitive") )
430 osrfHashSet(_f,"number", "primitive");
432 osrfHashSet(_f,"NUMERIC", "datatype");
435 case DBI_TYPE_STRING :
436 if ( !osrfHashGet(_f, "primitive") )
437 osrfHashSet(_f,"string", "primitive");
438 osrfHashSet(_f,"TEXT", "datatype");
441 case DBI_TYPE_DATETIME :
442 if ( !osrfHashGet(_f, "primitive") )
443 osrfHashSet(_f,"string", "primitive");
445 osrfHashSet(_f,"TIMESTAMP", "datatype");
448 case DBI_TYPE_BINARY :
449 if ( !osrfHashGet(_f, "primitive") )
450 osrfHashSet(_f,"string", "primitive");
452 osrfHashSet(_f,"BYTEA", "datatype");
457 "Setting [%s] to primitive [%s] and datatype [%s]...",
459 osrfHashGet(_f, "primitive"),
460 osrfHashGet(_f, "datatype")
464 dbi_result_free(result);
466 osrfLogDebug(OSRF_LOG_MARK, "No data found for class [%s]...", (char*)classname);
470 osrfStringArrayFree(classes);
472 child_initialized = 1;
477 This function is a sleazy hack intended *only* for testing and
478 debugging. Any real server process should initialize the
479 database connection by calling osrfAppChildInit().
481 void set_cstore_dbi_conn( dbi_conn conn ) {
482 dbhandle = writehandle = conn;
485 void userDataFree( void* blob ) {
486 osrfHashFree( (osrfHash*)blob );
490 static void sessionDataFree( char* key, void* item ) {
491 if (!(strcmp(key,"xact_id"))) {
493 dbi_conn_query(writehandle, "ROLLBACK;");
500 int beginTransaction ( osrfMethodContext* ctx ) {
501 if(osrfMethodVerifyContext( ctx )) {
502 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
507 jsonObject* user = verifyUserPCRUD( ctx );
508 if (!user) return -1;
509 jsonObjectFree(user);
512 dbi_result result = dbi_conn_query(writehandle, "START TRANSACTION;");
514 osrfLogError(OSRF_LOG_MARK, "%s: Error starting transaction", MODULENAME );
515 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error starting transaction" );
518 jsonObject* ret = jsonNewObject(ctx->session->session_id);
519 osrfAppRespondComplete( ctx, ret );
522 if (!ctx->session->userData) {
523 ctx->session->userData = osrfNewHash();
524 osrfHashSetCallback((osrfHash*)ctx->session->userData, &sessionDataFree);
527 osrfHashSet( (osrfHash*)ctx->session->userData, strdup( ctx->session->session_id ), "xact_id" );
528 ctx->session->userDataFree = &userDataFree;
534 int setSavepoint ( osrfMethodContext* ctx ) {
535 if(osrfMethodVerifyContext( ctx )) {
536 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
543 jsonObject* user = verifyUserPCRUD( ctx );
544 if (!user) return -1;
545 jsonObjectFree(user);
548 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
549 osrfAppSessionStatus(
551 OSRF_STATUS_INTERNALSERVERERROR,
552 "osrfMethodException",
554 "No active transaction -- required for savepoints"
559 const char* spName = jsonObjectGetString(jsonObjectGetIndex(ctx->params, spNamePos));
561 dbi_result result = dbi_conn_queryf(writehandle, "SAVEPOINT \"%s\";", spName);
565 "%s: Error creating savepoint %s in transaction %s",
568 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
570 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR,
571 "osrfMethodException", ctx->request, "Error creating savepoint" );
574 jsonObject* ret = jsonNewObject(spName);
575 osrfAppRespondComplete( ctx, ret );
581 int releaseSavepoint ( osrfMethodContext* ctx ) {
582 if(osrfMethodVerifyContext( ctx )) {
583 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
590 jsonObject* user = verifyUserPCRUD( ctx );
591 if (!user) return -1;
592 jsonObjectFree(user);
595 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
596 osrfAppSessionStatus(
598 OSRF_STATUS_INTERNALSERVERERROR,
599 "osrfMethodException",
601 "No active transaction -- required for savepoints"
606 const char* spName = jsonObjectGetString( jsonObjectGetIndex(ctx->params, spNamePos) );
608 dbi_result result = dbi_conn_queryf(writehandle, "RELEASE SAVEPOINT \"%s\";", spName);
612 "%s: Error releasing savepoint %s in transaction %s",
615 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
617 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR,
618 "osrfMethodException", ctx->request, "Error releasing savepoint" );
621 jsonObject* ret = jsonNewObject(spName);
622 osrfAppRespondComplete( ctx, ret );
628 int rollbackSavepoint ( osrfMethodContext* ctx ) {
629 if(osrfMethodVerifyContext( ctx )) {
630 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
637 jsonObject* user = verifyUserPCRUD( ctx );
638 if (!user) return -1;
639 jsonObjectFree(user);
642 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
643 osrfAppSessionStatus(
645 OSRF_STATUS_INTERNALSERVERERROR,
646 "osrfMethodException",
648 "No active transaction -- required for savepoints"
653 const char* spName = jsonObjectGetString( jsonObjectGetIndex(ctx->params, spNamePos) );
655 dbi_result result = dbi_conn_queryf(writehandle, "ROLLBACK TO SAVEPOINT \"%s\";", spName);
659 "%s: Error rolling back savepoint %s in transaction %s",
662 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
664 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR,
665 "osrfMethodException", ctx->request, "Error rolling back savepoint" );
668 jsonObject* ret = jsonNewObject(spName);
669 osrfAppRespondComplete( ctx, ret );
675 int commitTransaction ( osrfMethodContext* ctx ) {
676 if(osrfMethodVerifyContext( ctx )) {
677 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
682 jsonObject* user = verifyUserPCRUD( ctx );
683 if (!user) return -1;
684 jsonObjectFree(user);
687 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
688 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to commit" );
692 dbi_result result = dbi_conn_query(writehandle, "COMMIT;");
694 osrfLogError(OSRF_LOG_MARK, "%s: Error committing transaction", MODULENAME );
695 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error committing transaction" );
698 osrfHashRemove(ctx->session->userData, "xact_id");
699 jsonObject* ret = jsonNewObject(ctx->session->session_id);
700 osrfAppRespondComplete( ctx, ret );
706 int rollbackTransaction ( osrfMethodContext* ctx ) {
707 if(osrfMethodVerifyContext( ctx )) {
708 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
713 jsonObject* user = verifyUserPCRUD( ctx );
714 if (!user) return -1;
715 jsonObjectFree(user);
718 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
719 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to roll back" );
723 dbi_result result = dbi_conn_query(writehandle, "ROLLBACK;");
725 osrfLogError(OSRF_LOG_MARK, "%s: Error rolling back transaction", MODULENAME );
726 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error rolling back transaction" );
729 osrfHashRemove(ctx->session->userData, "xact_id");
730 jsonObject* ret = jsonNewObject(ctx->session->session_id);
731 osrfAppRespondComplete( ctx, ret );
737 int dispatchCRUDMethod ( osrfMethodContext* ctx ) {
738 if(osrfMethodVerifyContext( ctx )) {
739 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
743 osrfHash* meta = (osrfHash*) ctx->method->userData;
744 osrfHash* class_obj = osrfHashGet( meta, "class" );
748 const char* methodtype = osrfHashGet(meta, "methodtype");
749 jsonObject * obj = NULL;
751 if (!strcmp(methodtype, "create")) {
752 obj = doCreate(ctx, &err);
753 osrfAppRespondComplete( ctx, obj );
755 else if (!strcmp(methodtype, "retrieve")) {
756 obj = doRetrieve(ctx, &err);
757 osrfAppRespondComplete( ctx, obj );
759 else if (!strcmp(methodtype, "update")) {
760 obj = doUpdate(ctx, &err);
761 osrfAppRespondComplete( ctx, obj );
763 else if (!strcmp(methodtype, "delete")) {
764 obj = doDelete(ctx, &err);
765 osrfAppRespondComplete( ctx, obj );
767 else if (!strcmp(methodtype, "search")) {
769 jsonObject* _p = jsonObjectClone( ctx->params );
772 _p = jsonParseString("[]");
773 jsonObjectPush(_p, jsonObjectClone(jsonObjectGetIndex(ctx->params, 1)));
774 jsonObjectPush(_p, jsonObjectClone(jsonObjectGetIndex(ctx->params, 2)));
777 obj = doFieldmapperSearch(ctx, class_obj, _p, &err);
783 jsonIterator* itr = jsonNewIterator( obj );
784 while ((cur = jsonIteratorNext( itr ))) {
786 if(!verifyObjectPCRUD(ctx, cur)) continue;
788 osrfAppRespond( ctx, cur );
790 jsonIteratorFree(itr);
791 osrfAppRespondComplete( ctx, NULL );
793 } else if (!strcmp(methodtype, "id_list")) {
795 jsonObject* _p = jsonObjectClone( ctx->params );
798 _p = jsonParseString("[]");
799 jsonObjectPush(_p, jsonObjectClone(jsonObjectGetIndex(ctx->params, 1)));
800 jsonObjectPush(_p, jsonObjectClone(jsonObjectGetIndex(ctx->params, 2)));
803 if (jsonObjectGetIndex( _p, 1 )) {
804 jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "select" );
805 jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "no_i18n" );
806 jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "flesh" );
807 jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "flesh_columns" );
809 jsonObjectSetIndex( _p, 1, jsonNewObjectType(JSON_HASH) );
812 jsonObjectSetKey( jsonObjectGetIndex( _p, 1 ), "no_i18n", jsonNewBoolObject( 1 ) );
815 jsonObjectGetIndex( _p, 1 ),
818 "{ \"%s\":[\"%s\"] }",
819 osrfHashGet( class_obj, "classname" ),
820 osrfHashGet( class_obj, "primarykey" )
824 obj = doFieldmapperSearch(ctx, class_obj, _p, &err);
830 jsonIterator* itr = jsonNewIterator( obj );
831 while ((cur = jsonIteratorNext( itr ))) {
833 if(!verifyObjectPCRUD(ctx, cur)) continue;
837 oilsFMGetObject( cur, osrfHashGet( class_obj, "primarykey" ) )
840 jsonIteratorFree(itr);
841 osrfAppRespondComplete( ctx, NULL );
844 osrfAppRespondComplete( ctx, obj );
852 static int verifyObjectClass ( osrfMethodContext* ctx, const jsonObject* param ) {
855 osrfHash* meta = (osrfHash*) ctx->method->userData;
856 osrfHash* class = osrfHashGet( meta, "class" );
858 if (!param->classname || (strcmp( osrfHashGet(class, "classname"), param->classname ))) {
860 growing_buffer* msg = buffer_init(128);
863 "%s: %s method for type %s was passed a %s",
865 osrfHashGet(meta, "methodtype"),
866 osrfHashGet(class, "classname"),
870 char* m = buffer_release(msg);
871 osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException", ctx->request, m );
879 ret = verifyObjectPCRUD( ctx, param );
887 static jsonObject* verifyUserPCRUD( osrfMethodContext* ctx ) {
888 const char* auth = jsonObjectGetString( jsonObjectGetIndex( ctx->params, 0 ) );
889 jsonObject* auth_object = jsonNewObject(auth);
890 jsonObject* user = oilsUtilsQuickReq("open-ils.auth","open-ils.auth.session.retrieve", auth_object);
891 jsonObjectFree(auth_object);
893 if (!user->classname || strcmp(user->classname, "au")) {
895 growing_buffer* msg = buffer_init(128);
898 "%s: permacrud received a bad auth token: %s",
903 char* m = buffer_release(msg);
904 osrfAppSessionStatus( ctx->session, OSRF_STATUS_UNAUTHORIZED, "osrfMethodException", ctx->request, m );
907 jsonObjectFree(user);
915 static int verifyObjectPCRUD ( osrfMethodContext* ctx, const jsonObject* obj ) {
917 dbhandle = writehandle;
919 osrfHash* meta = (osrfHash*) ctx->method->userData;
920 osrfHash* class = osrfHashGet( meta, "class" );
921 char* method_type = strdup( osrfHashGet(meta, "methodtype") );
924 if ( ( *method_type == 's' || *method_type == 'i' ) ) {
926 method_type = strdup("retrieve"); // search and id_list are equivelant to retrieve for this
927 } else if ( *method_type == 'u' || *method_type == 'd' ) {
928 fetch = 1; // MUST go to the db for the object for update and delete
931 osrfHash* pcrud = osrfHashGet( osrfHashGet(class, "permacrud"), method_type );
935 // No permacrud for this method type on this class
937 growing_buffer* msg = buffer_init(128);
940 "%s: %s on class %s has no permacrud IDL entry",
942 osrfHashGet(meta, "methodtype"),
943 osrfHashGet(class, "classname")
946 char* m = buffer_release(msg);
947 osrfAppSessionStatus( ctx->session, OSRF_STATUS_FORBIDDEN, "osrfMethodException", ctx->request, m );
954 jsonObject* user = verifyUserPCRUD( ctx );
957 int userid = atoi( oilsFMGetString( user, "id" ) );
958 jsonObjectFree(user);
960 osrfStringArray* permission = osrfHashGet(pcrud, "permission");
961 osrfStringArray* local_context = osrfHashGet(pcrud, "local_context");
962 osrfHash* foreign_context = osrfHashGet(pcrud, "foreign_context");
964 osrfStringArray* context_org_array = osrfNewStringArray(1);
967 char* pkey_value = NULL;
968 if ( str_is_true( osrfHashGet(pcrud, "global_required") ) ) {
969 osrfLogDebug( OSRF_LOG_MARK, "global-level permissions required, fetching top of the org tree" );
971 // check for perm at top of org tree
972 jsonObject* _tmp_params = jsonParseString("[{\"parent_ou\":null}]");
973 jsonObject* _list = doFieldmapperSearch(ctx, osrfHashGet( oilsIDL(), "aou" ), _tmp_params, &err);
975 jsonObject* _tree_top = jsonObjectGetIndex(_list, 0);
978 jsonObjectFree(_tmp_params);
979 jsonObjectFree(_list);
981 growing_buffer* msg = buffer_init(128);
982 OSRF_BUFFER_ADD( msg, MODULENAME );
983 OSRF_BUFFER_ADD( msg,
984 ": Internal error, could not find the top of the org tree (parent_ou = NULL)" );
986 char* m = buffer_release(msg);
987 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, m );
993 osrfStringArrayAdd( context_org_array, oilsFMGetString( _tree_top, "id" ) );
994 osrfLogDebug( OSRF_LOG_MARK, "top of the org tree is %s", osrfStringArrayGetString(context_org_array, 0) );
996 jsonObjectFree(_tmp_params);
997 jsonObjectFree(_list);
1000 osrfLogDebug( OSRF_LOG_MARK, "global-level permissions not required, fetching context org ids" );
1001 char* pkey = osrfHashGet(class, "primarykey");
1002 jsonObject *param = NULL;
1004 if (obj->classname) {
1005 pkey_value = oilsFMGetString( obj, pkey );
1006 if (!fetch) param = jsonObjectClone(obj);
1007 osrfLogDebug( OSRF_LOG_MARK, "Object supplied, using primary key value of %s", pkey_value );
1009 pkey_value = jsonObjectToSimpleString( obj );
1011 osrfLogDebug( OSRF_LOG_MARK, "Object not supplied, using primary key value of %s and retrieving from the database", pkey_value );
1015 jsonObject* _tmp_params = jsonParseStringFmt("[{\"%s\":\"%s\"}]", pkey, pkey_value);
1016 jsonObject* _list = doFieldmapperSearch(
1023 param = jsonObjectClone(jsonObjectGetIndex(_list, 0));
1025 jsonObjectFree(_tmp_params);
1026 jsonObjectFree(_list);
1030 osrfLogDebug( OSRF_LOG_MARK, "Object not found in the database with primary key %s of %s", pkey, pkey_value );
1032 growing_buffer* msg = buffer_init(128);
1035 "%s: no object found with primary key %s of %s",
1041 char* m = buffer_release(msg);
1042 osrfAppSessionStatus(
1044 OSRF_STATUS_INTERNALSERVERERROR,
1045 "osrfMethodException",
1051 if (pkey_value) free(pkey_value);
1056 if (local_context->size > 0) {
1057 osrfLogDebug( OSRF_LOG_MARK, "%d class-local context field(s) specified", local_context->size);
1059 char* lcontext = NULL;
1060 while ( (lcontext = osrfStringArrayGetString(local_context, i++)) ) {
1061 osrfStringArrayAdd( context_org_array, oilsFMGetString( param, lcontext ) );
1064 "adding class-local field %s (value: %s) to the context org list",
1066 osrfStringArrayGetString(context_org_array, context_org_array->size - 1)
1071 osrfStringArray* class_list;
1073 if (foreign_context) {
1074 class_list = osrfHashKeys( foreign_context );
1075 osrfLogDebug( OSRF_LOG_MARK, "%d foreign context classes(s) specified", class_list->size);
1077 if (class_list->size > 0) {
1080 char* class_name = NULL;
1081 while ( (class_name = osrfStringArrayGetString(class_list, i++)) ) {
1082 osrfHash* fcontext = osrfHashGet(foreign_context, class_name);
1086 "%d foreign context fields(s) specified for class %s",
1087 ((osrfStringArray*)osrfHashGet(fcontext,"context"))->size,
1091 char* foreign_pkey = osrfHashGet(fcontext, "field");
1092 char* foreign_pkey_value = oilsFMGetString(param, osrfHashGet(fcontext, "fkey"));
1094 jsonObject* _tmp_params = jsonParseStringFmt(
1095 "[{\"%s\":\"%s\"}]",
1100 jsonObject* _list = doFieldmapperSearch(
1102 osrfHashGet( oilsIDL(), class_name ),
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 = jsonParseStringFmt(
1125 "[{\"%s\":\"%s\"}]",
1130 _list = doFieldmapperSearch(
1132 osrfHashGet( oilsIDL(), osrfHashGet( foreign_link_hash, "class" ) ),
1137 _fparam = jsonObjectClone(jsonObjectGetIndex(_list, 0));
1138 jsonObjectFree(_tmp_params);
1139 jsonObjectFree(_list);
1146 growing_buffer* msg = buffer_init(128);
1149 "%s: no object found with primary key %s of %s",
1155 char* m = buffer_release(msg);
1156 osrfAppSessionStatus(
1158 OSRF_STATUS_INTERNALSERVERERROR,
1159 "osrfMethodException",
1165 osrfStringArrayFree(class_list);
1166 free(foreign_pkey_value);
1167 jsonObjectFree(param);
1172 free(foreign_pkey_value);
1175 char* foreign_field = NULL;
1176 while ( (foreign_field = osrfStringArrayGetString(osrfHashGet(fcontext,"context"), j++)) ) {
1177 osrfStringArrayAdd( context_org_array, oilsFMGetString( _fparam, foreign_field ) );
1180 "adding foreign class %s field %s (value: %s) to the context org list",
1183 osrfStringArrayGetString(context_org_array, context_org_array->size - 1)
1187 jsonObjectFree(_fparam);
1190 osrfStringArrayFree(class_list);
1194 jsonObjectFree(param);
1197 char* context_org = NULL;
1201 if (permission->size == 0) {
1202 osrfLogDebug( OSRF_LOG_MARK, "No permission specified for this action, passing through" );
1207 while ( (perm = osrfStringArrayGetString(permission, i++)) ) {
1209 while ( (context_org = osrfStringArrayGetString(context_org_array, j++)) ) {
1215 "Checking object permission [%s] for user %d on object %s (class %s) at org %d",
1219 osrfHashGet(class, "classname"),
1223 result = dbi_conn_queryf(
1225 "SELECT permission.usr_has_object_perm(%d, '%s', '%s', '%s', %d) AS has_perm;",
1228 osrfHashGet(class, "classname"),
1236 "Recieved a result for object permission [%s] for user %d on object %s (class %s) at org %d",
1240 osrfHashGet(class, "classname"),
1244 if (dbi_result_first_row(result)) {
1245 jsonObject* return_val = oilsMakeJSONFromResult( result );
1246 const char* has_perm = jsonObjectGetString( jsonObjectGetKeyConst(return_val, "has_perm") );
1250 "Status of object permission [%s] for user %d on object %s (class %s) at org %d is %s",
1254 osrfHashGet(class, "classname"),
1259 if ( *has_perm == 't' ) OK = 1;
1260 jsonObjectFree(return_val);
1263 dbi_result_free(result);
1268 osrfLogDebug( OSRF_LOG_MARK, "Checking non-object permission [%s] for user %d at org %d", perm, userid, atoi(context_org) );
1269 result = dbi_conn_queryf(
1271 "SELECT permission.usr_has_perm(%d, '%s', %d) AS has_perm;",
1278 osrfLogDebug( OSRF_LOG_MARK, "Received a result for permission [%s] for user %d at org %d",
1279 perm, userid, atoi(context_org) );
1280 if ( dbi_result_first_row(result) ) {
1281 jsonObject* return_val = oilsMakeJSONFromResult( result );
1282 const char* has_perm = jsonObjectGetString( jsonObjectGetKeyConst(return_val, "has_perm") );
1283 osrfLogDebug( OSRF_LOG_MARK, "Status of permission [%s] for user %d at org %d is [%s]",
1284 perm, userid, atoi(context_org), has_perm );
1285 if ( *has_perm == 't' ) OK = 1;
1286 jsonObjectFree(return_val);
1289 dbi_result_free(result);
1297 if (pkey_value) free(pkey_value);
1298 osrfStringArrayFree(context_org_array);
1305 static jsonObject* doCreate(osrfMethodContext* ctx, int* err ) {
1307 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
1309 jsonObject* target = jsonObjectGetIndex( ctx->params, 1 );
1310 jsonObject* options = jsonObjectGetIndex( ctx->params, 2 );
1312 jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
1313 jsonObject* options = jsonObjectGetIndex( ctx->params, 1 );
1316 if (!verifyObjectClass(ctx, target)) {
1321 osrfLogDebug( OSRF_LOG_MARK, "Object seems to be of the correct type" );
1323 char* trans_id = NULL;
1324 if( ctx->session && ctx->session->userData )
1325 trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
1328 osrfLogError( OSRF_LOG_MARK, "No active transaction -- required for CREATE" );
1330 osrfAppSessionStatus(
1332 OSRF_STATUS_BADREQUEST,
1333 "osrfMethodException",
1335 "No active transaction -- required for CREATE"
1341 // The following test is harmless but redundant. If a class is
1342 // readonly, we don't register a create method for it.
1343 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
1344 osrfAppSessionStatus(
1346 OSRF_STATUS_BADREQUEST,
1347 "osrfMethodException",
1349 "Cannot INSERT readonly class"
1355 // Set the last_xact_id
1356 int index = oilsIDL_ntop( target->classname, "last_xact_id" );
1358 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
1359 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
1362 osrfLogDebug( OSRF_LOG_MARK, "There is a transaction running..." );
1364 dbhandle = writehandle;
1366 osrfHash* fields = osrfHashGet(meta, "fields");
1367 char* pkey = osrfHashGet(meta, "primarykey");
1368 char* seq = osrfHashGet(meta, "sequence");
1370 growing_buffer* table_buf = buffer_init(128);
1371 growing_buffer* col_buf = buffer_init(128);
1372 growing_buffer* val_buf = buffer_init(128);
1374 OSRF_BUFFER_ADD(table_buf, "INSERT INTO ");
1375 OSRF_BUFFER_ADD(table_buf, osrfHashGet(meta, "tablename"));
1376 OSRF_BUFFER_ADD_CHAR( col_buf, '(' );
1377 buffer_add(val_buf,"VALUES (");
1383 osrfStringArray* field_list = osrfHashKeys( fields );
1384 while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
1386 osrfHash* field = osrfHashGet( fields, field_name );
1388 if( str_is_true( osrfHashGet( field, "virtual" ) ) )
1391 const jsonObject* field_object = oilsFMGetObject( target, field_name );
1394 if (field_object && field_object->classname) {
1395 value = oilsFMGetString(
1397 (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
1400 value = jsonObjectToSimpleString( field_object );
1407 OSRF_BUFFER_ADD_CHAR( col_buf, ',' );
1408 OSRF_BUFFER_ADD_CHAR( val_buf, ',' );
1411 buffer_add(col_buf, field_name);
1413 if (!field_object || field_object->type == JSON_NULL) {
1414 buffer_add( val_buf, "DEFAULT" );
1416 } else if ( !strcmp(get_primitive( field ), "number") ) {
1417 const char* numtype = get_datatype( field );
1418 if ( !strcmp( numtype, "INT8") ) {
1419 buffer_fadd( val_buf, "%lld", atoll(value) );
1421 } else if ( !strcmp( numtype, "INT") ) {
1422 buffer_fadd( val_buf, "%d", atoi(value) );
1424 } else if ( !strcmp( numtype, "NUMERIC") ) {
1425 buffer_fadd( val_buf, "%f", atof(value) );
1428 if ( dbi_conn_quote_string(writehandle, &value) ) {
1429 OSRF_BUFFER_ADD( val_buf, value );
1432 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
1433 osrfAppSessionStatus(
1435 OSRF_STATUS_INTERNALSERVERERROR,
1436 "osrfMethodException",
1438 "Error quoting string -- please see the error log for more details"
1441 buffer_free(table_buf);
1442 buffer_free(col_buf);
1443 buffer_free(val_buf);
1454 OSRF_BUFFER_ADD_CHAR( col_buf, ')' );
1455 OSRF_BUFFER_ADD_CHAR( val_buf, ')' );
1457 char* table_str = buffer_release(table_buf);
1458 char* col_str = buffer_release(col_buf);
1459 char* val_str = buffer_release(val_buf);
1460 growing_buffer* sql = buffer_init(128);
1461 buffer_fadd( sql, "%s %s %s;", table_str, col_str, val_str );
1466 char* query = buffer_release(sql);
1468 osrfLogDebug(OSRF_LOG_MARK, "%s: Insert SQL [%s]", MODULENAME, query);
1471 dbi_result result = dbi_conn_query(writehandle, query);
1473 jsonObject* obj = NULL;
1476 obj = jsonNewObject(NULL);
1479 "%s ERROR inserting %s object using query [%s]",
1481 osrfHashGet(meta, "fieldmapper"),
1484 osrfAppSessionStatus(
1486 OSRF_STATUS_INTERNALSERVERERROR,
1487 "osrfMethodException",
1489 "INSERT error -- please see the error log for more details"
1494 char* id = oilsFMGetString(target, pkey);
1496 unsigned long long new_id = dbi_conn_sequence_last(writehandle, seq);
1497 growing_buffer* _id = buffer_init(10);
1498 buffer_fadd(_id, "%lld", new_id);
1499 id = buffer_release(_id);
1502 // Find quietness specification, if present
1503 const char* quiet_str = NULL;
1505 const jsonObject* quiet_obj = jsonObjectGetKeyConst( options, "quiet" );
1507 quiet_str = jsonObjectGetString( quiet_obj );
1510 if( str_is_true( quiet_str ) ) { // if quietness is specified
1511 obj = jsonNewObject(id);
1515 jsonObject* fake_params = jsonNewObjectType(JSON_ARRAY);
1516 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH));
1519 jsonObjectGetIndex(fake_params, 0),
1524 jsonObject* list = doFieldmapperSearch( ctx,meta, fake_params, err);
1527 jsonObjectFree( fake_params );
1530 obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
1533 jsonObjectFree( list );
1534 jsonObjectFree( fake_params );
1547 static jsonObject* doRetrieve(osrfMethodContext* ctx, int* err ) {
1557 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
1559 const char* id = jsonObjectGetString(jsonObjectGetIndex(ctx->params, id_pos));
1560 jsonObject* order_hash = jsonObjectGetIndex(ctx->params, order_pos);
1564 "%s retrieving %s object with primary key value of %s",
1566 osrfHashGet(meta, "fieldmapper"),
1570 jsonObject* fake_params = jsonNewObjectType(JSON_ARRAY);
1571 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH));
1574 jsonObjectGetIndex(fake_params, 0),
1575 osrfHashGet(meta, "primarykey"),
1576 jsonObjectClone(jsonObjectGetIndex(ctx->params, id_pos))
1580 if (order_hash) jsonObjectPush(fake_params, jsonObjectClone(order_hash) );
1582 jsonObject* list = doFieldmapperSearch( ctx,meta, fake_params, err);
1585 jsonObjectFree( fake_params );
1589 jsonObject* obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
1591 jsonObjectFree( list );
1592 jsonObjectFree( fake_params );
1595 if(!verifyObjectPCRUD(ctx, obj)) {
1596 jsonObjectFree(obj);
1599 growing_buffer* msg = buffer_init(128);
1600 OSRF_BUFFER_ADD( msg, MODULENAME );
1601 OSRF_BUFFER_ADD( msg, ": Insufficient permissions to retrieve object" );
1603 char* m = buffer_release(msg);
1604 osrfAppSessionStatus( ctx->session, OSRF_STATUS_NOTALLOWED, "osrfMethodException", ctx->request, m );
1615 static char* jsonNumberToDBString ( osrfHash* field, const jsonObject* value ) {
1616 growing_buffer* val_buf = buffer_init(32);
1617 const char* numtype = get_datatype( field );
1619 if ( !strncmp( numtype, "INT", 3 ) ) {
1620 if (value->type == JSON_NUMBER)
1621 //buffer_fadd( val_buf, "%ld", (long)jsonObjectGetNumber(value) );
1622 buffer_fadd( val_buf, jsonObjectGetString( value ) );
1624 //const char* val_str = jsonObjectGetString( value );
1625 //buffer_fadd( val_buf, "%ld", atol(val_str) );
1626 buffer_fadd( val_buf, jsonObjectGetString( value ) );
1629 } else if ( !strcmp( numtype, "NUMERIC" ) ) {
1630 if (value->type == JSON_NUMBER)
1631 //buffer_fadd( val_buf, "%f", jsonObjectGetNumber(value) );
1632 buffer_fadd( val_buf, jsonObjectGetString( value ) );
1634 //const char* val_str = jsonObjectGetString( value );
1635 //buffer_fadd( val_buf, "%f", atof(val_str) );
1636 buffer_fadd( val_buf, jsonObjectGetString( value ) );
1640 // Presumably this was really intended ot be a string, so quote it
1641 char* str = jsonObjectToSimpleString( value );
1642 if ( dbi_conn_quote_string(dbhandle, &str) ) {
1643 OSRF_BUFFER_ADD( val_buf, str );
1646 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, str);
1648 buffer_free(val_buf);
1653 return buffer_release(val_buf);
1656 static char* searchINPredicate (const char* class, osrfHash* field,
1657 jsonObject* node, const char* op, osrfMethodContext* ctx ) {
1658 growing_buffer* sql_buf = buffer_init(32);
1664 osrfHashGet(field, "name")
1668 buffer_add(sql_buf, "IN (");
1669 } else if (!(strcasecmp(op,"not in"))) {
1670 buffer_add(sql_buf, "NOT IN (");
1672 buffer_add(sql_buf, "IN (");
1675 if (node->type == JSON_HASH) {
1676 // subquery predicate
1677 char* subpred = SELECT(
1679 jsonObjectGetKey( node, "select" ),
1680 jsonObjectGetKey( node, "from" ),
1681 jsonObjectGetKey( node, "where" ),
1682 jsonObjectGetKey( node, "having" ),
1683 jsonObjectGetKey( node, "order_by" ),
1684 jsonObjectGetKey( node, "limit" ),
1685 jsonObjectGetKey( node, "offset" ),
1690 buffer_add(sql_buf, subpred);
1693 buffer_free( sql_buf );
1697 } else if (node->type == JSON_ARRAY) {
1698 // literal value list
1699 int in_item_index = 0;
1700 int in_item_first = 1;
1701 const jsonObject* in_item;
1702 while ( (in_item = jsonObjectGetIndex(node, in_item_index++)) ) {
1707 buffer_add(sql_buf, ", ");
1710 if ( in_item->type != JSON_STRING && in_item->type != JSON_NUMBER ) {
1711 osrfLogError(OSRF_LOG_MARK, "%s: Expected string or number within IN list; found %s",
1712 MODULENAME, json_type( in_item->type ) );
1713 buffer_free(sql_buf);
1717 // Append the literal value -- quoted if not a number
1718 if ( JSON_NUMBER == in_item->type ) {
1719 char* val = jsonNumberToDBString( field, in_item );
1720 OSRF_BUFFER_ADD( sql_buf, val );
1723 } else if ( !strcmp( get_primitive( field ), "number") ) {
1724 char* val = jsonNumberToDBString( field, in_item );
1725 OSRF_BUFFER_ADD( sql_buf, val );
1729 char* key_string = jsonObjectToSimpleString(in_item);
1730 if ( dbi_conn_quote_string(dbhandle, &key_string) ) {
1731 OSRF_BUFFER_ADD( sql_buf, key_string );
1734 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string);
1736 buffer_free(sql_buf);
1742 if( in_item_first ) {
1743 osrfLogError(OSRF_LOG_MARK, "%s: Empty IN list", MODULENAME );
1744 buffer_free( sql_buf );
1748 osrfLogError(OSRF_LOG_MARK, "%s: Expected object or array for IN clause; found %s",
1749 MODULENAME, json_type( node->type ) );
1750 buffer_free(sql_buf);
1754 OSRF_BUFFER_ADD_CHAR( sql_buf, ')' );
1756 return buffer_release(sql_buf);
1759 // Receive a JSON_ARRAY representing a function call. The first
1760 // entry in the array is the function name. The rest are parameters.
1761 static char* searchValueTransform( const jsonObject* array ) {
1763 if( array->size < 1 ) {
1764 osrfLogError(OSRF_LOG_MARK, "%s: Empty array for value transform", MODULENAME);
1768 // Get the function name
1769 jsonObject* func_item = jsonObjectGetIndex( array, 0 );
1770 if( func_item->type != JSON_STRING ) {
1771 osrfLogError(OSRF_LOG_MARK, "%s: Error: expected function name, found %s",
1772 MODULENAME, json_type( func_item->type ) );
1776 growing_buffer* sql_buf = buffer_init(32);
1778 OSRF_BUFFER_ADD( sql_buf, jsonObjectGetString( func_item ) );
1779 OSRF_BUFFER_ADD( sql_buf, "( " );
1781 // Get the parameters
1782 int func_item_index = 1; // We already grabbed the zeroth entry
1783 while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1785 // Add a separator comma, if we need one
1786 if( func_item_index > 2 )
1787 buffer_add( sql_buf, ", " );
1789 // Add the current parameter
1790 if (func_item->type == JSON_NULL) {
1791 buffer_add( sql_buf, "NULL" );
1793 char* val = jsonObjectToSimpleString(func_item);
1794 if ( dbi_conn_quote_string(dbhandle, &val) ) {
1795 OSRF_BUFFER_ADD( sql_buf, val );
1798 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1799 buffer_free(sql_buf);
1806 buffer_add( sql_buf, " )" );
1808 return buffer_release(sql_buf);
1811 static char* searchFunctionPredicate (const char* class, osrfHash* field,
1812 const jsonObject* node, const char* op) {
1814 if( ! is_good_operator( op ) ) {
1815 osrfLogError( OSRF_LOG_MARK, "%s: Invalid operator [%s]", MODULENAME, op );
1819 char* val = searchValueTransform(node);
1823 growing_buffer* sql_buf = buffer_init(32);
1828 osrfHashGet(field, "name"),
1835 return buffer_release(sql_buf);
1838 // class is a class name
1839 // field is a field definition as stored in the IDL
1840 // node comes from the method parameter, and may represent an entry in the SELECT list
1841 static char* searchFieldTransform (const char* class, osrfHash* field, const jsonObject* node) {
1842 growing_buffer* sql_buf = buffer_init(32);
1844 const char* field_transform = jsonObjectGetString( jsonObjectGetKeyConst( node, "transform" ) );
1845 const char* transform_subcolumn = jsonObjectGetString( jsonObjectGetKeyConst( node, "result_field" ) );
1847 if(transform_subcolumn) {
1848 if( ! is_identifier( transform_subcolumn ) ) {
1849 osrfLogError( OSRF_LOG_MARK, "%s: Invalid subfield name: \"%s\"\n",
1850 MODULENAME, transform_subcolumn );
1851 buffer_free( sql_buf );
1854 OSRF_BUFFER_ADD_CHAR( sql_buf, '(' ); // enclose transform in parentheses
1857 if (field_transform) {
1859 if( ! is_identifier( field_transform ) ) {
1860 osrfLogError( OSRF_LOG_MARK, "%s: Expected function name, found \"%s\"\n",
1861 MODULENAME, field_transform );
1862 buffer_free( sql_buf );
1866 buffer_fadd( sql_buf, "%s(\"%s\".%s", field_transform, class, osrfHashGet(field, "name"));
1867 const jsonObject* array = jsonObjectGetKeyConst( node, "params" );
1870 if( array->type != JSON_ARRAY ) {
1871 osrfLogError( OSRF_LOG_MARK,
1872 "%s: Expected JSON_ARRAY for function params; found %s",
1873 MODULENAME, json_type( array->type ) );
1874 buffer_free( sql_buf );
1877 int func_item_index = 0;
1878 jsonObject* func_item;
1879 while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1881 char* val = jsonObjectToSimpleString(func_item);
1884 buffer_add( sql_buf, ",NULL" );
1885 } else if ( dbi_conn_quote_string(dbhandle, &val) ) {
1886 OSRF_BUFFER_ADD_CHAR( sql_buf, ',' );
1887 OSRF_BUFFER_ADD( sql_buf, val );
1889 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1891 buffer_free(sql_buf);
1898 buffer_add( sql_buf, " )" );
1901 buffer_fadd( sql_buf, "\"%s\".%s", class, osrfHashGet(field, "name"));
1904 if (transform_subcolumn)
1905 buffer_fadd( sql_buf, ").\"%s\"", transform_subcolumn );
1907 return buffer_release(sql_buf);
1910 static char* searchFieldTransformPredicate (const char* class, osrfHash* field,
1911 const jsonObject* node, const char* op ) {
1913 if( ! is_good_operator( op ) ) {
1914 osrfLogError(OSRF_LOG_MARK, "%s: Error: Invalid operator %s", MODULENAME, op);
1918 char* field_transform = searchFieldTransform( class, field, node );
1919 if( ! field_transform )
1922 int extra_parens = 0; // boolean
1924 const jsonObject* value_obj = jsonObjectGetKeyConst( node, "value" );
1925 if ( ! value_obj ) {
1926 value = searchWHERE( node, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN, NULL );
1928 osrfLogError(OSRF_LOG_MARK, "%s: Error building condition for field transform", MODULENAME);
1929 free(field_transform);
1933 } else if ( value_obj->type == JSON_ARRAY ) {
1934 value = searchValueTransform( value_obj );
1936 osrfLogError(OSRF_LOG_MARK, "%s: Error building value transform for field transform", MODULENAME);
1937 free( field_transform );
1940 } else if ( value_obj->type == JSON_HASH ) {
1941 value = searchWHERE( value_obj, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN, NULL );
1943 osrfLogError(OSRF_LOG_MARK, "%s: Error building predicate for field transform", MODULENAME);
1944 free(field_transform);
1948 } else if ( value_obj->type == JSON_NUMBER ) {
1949 value = jsonNumberToDBString( field, value_obj );
1950 } else if ( value_obj->type == JSON_NULL ) {
1951 osrfLogError(OSRF_LOG_MARK, "%s: Error building predicate for field transform: null value", MODULENAME);
1952 free(field_transform);
1954 } else if ( value_obj->type == JSON_BOOL ) {
1955 osrfLogError(OSRF_LOG_MARK, "%s: Error building predicate for field transform: boolean value", MODULENAME);
1956 free(field_transform);
1959 if ( !strcmp( get_primitive( field ), "number") ) {
1960 value = jsonNumberToDBString( field, value_obj );
1962 value = jsonObjectToSimpleString( value_obj );
1963 if ( !dbi_conn_quote_string(dbhandle, &value) ) {
1964 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, value);
1966 free(field_transform);
1972 const char* left_parens = "";
1973 const char* right_parens = "";
1975 if( extra_parens ) {
1980 growing_buffer* sql_buf = buffer_init(32);
1984 "%s%s %s %s %s %s%s",
1995 free(field_transform);
1997 return buffer_release(sql_buf);
2000 static char* searchSimplePredicate (const char* op, const char* class,
2001 osrfHash* field, const jsonObject* node) {
2003 if( ! is_good_operator( op ) ) {
2004 osrfLogError( OSRF_LOG_MARK, "%s: Invalid operator [%s]", MODULENAME, op );
2010 // Get the value to which we are comparing the specified column
2011 if (node->type != JSON_NULL) {
2012 if ( node->type == JSON_NUMBER ) {
2013 val = jsonNumberToDBString( field, node );
2014 } else if ( !strcmp( get_primitive( field ), "number" ) ) {
2015 val = jsonNumberToDBString( field, node );
2017 val = jsonObjectToSimpleString(node);
2022 if( JSON_NUMBER != node->type && strcmp( get_primitive( field ), "number") ) {
2023 // Value is not numeric; enclose it in quotes
2024 if ( !dbi_conn_quote_string( dbhandle, &val ) ) {
2025 osrfLogError( OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val );
2031 // Compare to a null value
2032 val = strdup( "NULL" );
2033 if (strcmp( op, "=" ))
2039 growing_buffer* sql_buf = buffer_init(32);
2040 buffer_fadd( sql_buf, "\"%s\".%s %s %s", class, osrfHashGet(field, "name"), op, val );
2041 char* pred = buffer_release( sql_buf );
2048 static char* searchBETWEENPredicate (const char* class, osrfHash* field, const jsonObject* node) {
2050 const jsonObject* x_node = jsonObjectGetIndex( node, 0 );
2051 const jsonObject* y_node = jsonObjectGetIndex( node, 1 );
2053 if( NULL == y_node ) {
2054 osrfLogError( OSRF_LOG_MARK, "%s: Not enough operands for BETWEEN operator", MODULENAME );
2057 else if( NULL != jsonObjectGetIndex( node, 2 ) ) {
2058 osrfLogError( OSRF_LOG_MARK, "%s: Too many operands for BETWEEN operator", MODULENAME );
2065 if ( !strcmp( get_primitive( field ), "number") ) {
2066 x_string = jsonNumberToDBString(field, x_node);
2067 y_string = jsonNumberToDBString(field, y_node);
2070 x_string = jsonObjectToSimpleString(x_node);
2071 y_string = jsonObjectToSimpleString(y_node);
2072 if ( !(dbi_conn_quote_string(dbhandle, &x_string) && dbi_conn_quote_string(dbhandle, &y_string)) ) {
2073 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key strings [%s] and [%s]",
2074 MODULENAME, x_string, y_string);
2081 growing_buffer* sql_buf = buffer_init(32);
2082 buffer_fadd( sql_buf, "%s BETWEEN %s AND %s", osrfHashGet(field, "name"), x_string, y_string );
2086 return buffer_release(sql_buf);
2089 static char* searchPredicate ( const char* class, osrfHash* field,
2090 jsonObject* node, osrfMethodContext* ctx ) {
2093 if (node->type == JSON_ARRAY) { // equality IN search
2094 pred = searchINPredicate( class, field, node, NULL, ctx );
2095 } else if (node->type == JSON_HASH) { // other search
2096 jsonIterator* pred_itr = jsonNewIterator( node );
2097 if( !jsonIteratorHasNext( pred_itr ) ) {
2098 osrfLogError( OSRF_LOG_MARK, "%s: Empty predicate for field \"%s\"",
2099 MODULENAME, osrfHashGet(field, "name") );
2101 jsonObject* pred_node = jsonIteratorNext( pred_itr );
2103 // Verify that there are no additional predicates
2104 if( jsonIteratorHasNext( pred_itr ) ) {
2105 osrfLogError( OSRF_LOG_MARK, "%s: Multiple predicates for field \"%s\"",
2106 MODULENAME, osrfHashGet(field, "name") );
2107 } else if ( !(strcasecmp( pred_itr->key,"between" )) )
2108 pred = searchBETWEENPredicate( class, field, pred_node );
2109 else if ( !(strcasecmp( pred_itr->key,"in" )) || !(strcasecmp( pred_itr->key,"not in" )) )
2110 pred = searchINPredicate( class, field, pred_node, pred_itr->key, ctx );
2111 else if ( pred_node->type == JSON_ARRAY )
2112 pred = searchFunctionPredicate( class, field, pred_node, pred_itr->key );
2113 else if ( pred_node->type == JSON_HASH )
2114 pred = searchFieldTransformPredicate( class, field, pred_node, pred_itr->key );
2116 pred = searchSimplePredicate( pred_itr->key, class, field, pred_node );
2118 jsonIteratorFree(pred_itr);
2120 } else if (node->type == JSON_NULL) { // IS NULL search
2121 growing_buffer* _p = buffer_init(64);
2124 "\"%s\".%s IS NULL",
2126 osrfHashGet(field, "name")
2128 pred = buffer_release(_p);
2129 } else { // equality search
2130 pred = searchSimplePredicate( "=", class, field, node );
2149 field : call_number,
2165 static char* searchJOIN ( const jsonObject* join_hash, osrfHash* leftmeta ) {
2167 const jsonObject* working_hash;
2168 jsonObject* freeable_hash = NULL;
2170 if (join_hash->type == JSON_STRING) {
2171 // create a wrapper around a copy of the original
2172 const char* _tmp = jsonObjectGetString( join_hash );
2173 freeable_hash = jsonNewObjectType(JSON_HASH);
2174 jsonObjectSetKey(freeable_hash, _tmp, NULL);
2175 working_hash = freeable_hash;
2178 if( join_hash->type != JSON_HASH ) {
2181 "%s: JOIN failed; expected JSON object type not found",
2186 working_hash = join_hash;
2189 growing_buffer* join_buf = buffer_init(128);
2190 const char* leftclass = osrfHashGet(leftmeta, "classname");
2192 jsonObject* snode = NULL;
2193 jsonIterator* search_itr = jsonNewIterator( working_hash );
2195 while ( (snode = jsonIteratorNext( search_itr )) ) {
2196 const char* class = search_itr->key;
2197 osrfHash* idlClass = osrfHashGet( oilsIDL(), class );
2201 "%s: JOIN failed. No class \"%s\" defined in IDL",
2205 jsonIteratorFree( search_itr );
2206 buffer_free( join_buf );
2208 jsonObjectFree( freeable_hash );
2212 const char* fkey = jsonObjectGetString( jsonObjectGetKeyConst( snode, "fkey" ) );
2213 const char* field = jsonObjectGetString( jsonObjectGetKeyConst( snode, "field" ) );
2215 if (field && !fkey) {
2216 // Look up the corresponding join column in the IDL.
2217 // The link must be defined in the child table,
2218 // and point to the right parent table.
2219 osrfHash* idl_link = (osrfHash*) oilsIDLFindPath( "/%s/links/%s", class, field );
2220 const char* reltype = NULL;
2221 const char* other_class = NULL;
2222 reltype = osrfHashGet( idl_link, "reltype" );
2223 if( reltype && strcmp( reltype, "has_many" ) )
2224 other_class = osrfHashGet( idl_link, "class" );
2225 if( other_class && !strcmp( other_class, leftclass ) )
2226 fkey = osrfHashGet( idl_link, "key" );
2230 "%s: JOIN failed. No link defined from %s.%s to %s",
2236 buffer_free(join_buf);
2238 jsonObjectFree(freeable_hash);
2239 jsonIteratorFree(search_itr);
2243 } else if (!field && fkey) {
2244 // Look up the corresponding join column in the IDL.
2245 // The link must be defined in the child table,
2246 // and point to the right parent table.
2247 osrfHash* idl_link = (osrfHash*) oilsIDLFindPath( "/%s/links/%s", leftclass, fkey );
2248 const char* reltype = NULL;
2249 const char* other_class = NULL;
2250 reltype = osrfHashGet( idl_link, "reltype" );
2251 if( reltype && strcmp( reltype, "has_many" ) )
2252 other_class = osrfHashGet( idl_link, "class" );
2253 if( other_class && !strcmp( other_class, class ) )
2254 field = osrfHashGet( idl_link, "key" );
2258 "%s: JOIN failed. No link defined from %s.%s to %s",
2264 buffer_free(join_buf);
2266 jsonObjectFree(freeable_hash);
2267 jsonIteratorFree(search_itr);
2271 } else if (!field && !fkey) {
2272 osrfHash* _links = oilsIDL_links( leftclass );
2274 // For each link defined for the left class:
2275 // see if the link references the joined class
2276 osrfHashIterator* itr = osrfNewHashIterator( _links );
2277 osrfHash* curr_link = NULL;
2278 while( (curr_link = osrfHashIteratorNext( itr ) ) ) {
2279 const char* other_class = osrfHashGet( curr_link, "class" );
2280 if( other_class && !strcmp( other_class, class ) ) {
2282 // In the IDL, the parent class doesn't know then names of the child
2283 // columns that are pointing to it, so don't use that end of the link
2284 const char* reltype = osrfHashGet( curr_link, "reltype" );
2285 if( reltype && strcmp( reltype, "has_many" ) ) {
2286 // Found a link between the classes
2287 fkey = osrfHashIteratorKey( itr );
2288 field = osrfHashGet( curr_link, "key" );
2293 osrfHashIteratorFree( itr );
2295 if (!field || !fkey) {
2296 // Do another such search, with the classes reversed
2297 _links = oilsIDL_links( class );
2299 // For each link defined for the joined class:
2300 // see if the link references the left class
2301 osrfHashIterator* itr = osrfNewHashIterator( _links );
2302 osrfHash* curr_link = NULL;
2303 while( (curr_link = osrfHashIteratorNext( itr ) ) ) {
2304 const char* other_class = osrfHashGet( curr_link, "class" );
2305 if( other_class && !strcmp( other_class, leftclass ) ) {
2307 // In the IDL, the parent class doesn't know then names of the child
2308 // columns that are pointing to it, so don't use that end of the link
2309 const char* reltype = osrfHashGet( curr_link, "reltype" );
2310 if( reltype && strcmp( reltype, "has_many" ) ) {
2311 // Found a link between the classes
2312 field = osrfHashIteratorKey( itr );
2313 fkey = osrfHashGet( curr_link, "key" );
2318 osrfHashIteratorFree( itr );
2321 if (!field || !fkey) {
2324 "%s: JOIN failed. No link defined between %s and %s",
2329 buffer_free(join_buf);
2331 jsonObjectFree(freeable_hash);
2332 jsonIteratorFree(search_itr);
2338 const char* type = jsonObjectGetString( jsonObjectGetKeyConst( snode, "type" ) );
2340 if ( !strcasecmp(type,"left") ) {
2341 buffer_add(join_buf, " LEFT JOIN");
2342 } else if ( !strcasecmp(type,"right") ) {
2343 buffer_add(join_buf, " RIGHT JOIN");
2344 } else if ( !strcasecmp(type,"full") ) {
2345 buffer_add(join_buf, " FULL JOIN");
2347 buffer_add(join_buf, " INNER JOIN");
2350 buffer_add(join_buf, " INNER JOIN");
2353 char* table = getSourceDefinition(idlClass);
2355 jsonIteratorFree( search_itr );
2356 buffer_free( join_buf );
2358 jsonObjectFree( freeable_hash );
2362 buffer_fadd(join_buf, " %s AS \"%s\" ON ( \"%s\".%s = \"%s\".%s",
2363 table, class, class, field, leftclass, fkey);
2366 const jsonObject* filter = jsonObjectGetKeyConst( snode, "filter" );
2368 const char* filter_op = jsonObjectGetString( jsonObjectGetKeyConst( snode, "filter_op" ) );
2369 if ( filter_op && !strcasecmp("or",filter_op) ) {
2370 buffer_add( join_buf, " OR " );
2372 buffer_add( join_buf, " AND " );
2375 char* jpred = searchWHERE( filter, idlClass, AND_OP_JOIN, NULL );
2377 OSRF_BUFFER_ADD_CHAR( join_buf, ' ' );
2378 OSRF_BUFFER_ADD( join_buf, jpred );
2383 "%s: JOIN failed. Invalid conditional expression.",
2386 jsonIteratorFree( search_itr );
2387 buffer_free( join_buf );
2389 jsonObjectFree( freeable_hash );
2394 buffer_add(join_buf, " ) ");
2396 const jsonObject* join_filter = jsonObjectGetKeyConst( snode, "join" );
2398 char* jpred = searchJOIN( join_filter, idlClass );
2400 OSRF_BUFFER_ADD_CHAR( join_buf, ' ' );
2401 OSRF_BUFFER_ADD( join_buf, jpred );
2404 osrfLogError( OSRF_LOG_MARK, "%s: Invalid nested join.", MODULENAME );
2405 jsonIteratorFree( search_itr );
2406 buffer_free( join_buf );
2408 jsonObjectFree( freeable_hash );
2415 jsonObjectFree(freeable_hash);
2416 jsonIteratorFree(search_itr);
2418 return buffer_release(join_buf);
2423 { +class : { -or|-and : { field : { op : value }, ... } ... }, ... }
2424 { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }
2425 [ { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }, ... ]
2427 Generate code to express a set of conditions, as for a WHERE clause. Parameters:
2429 search_hash is the JSON expression of the conditions.
2430 meta is the class definition from the IDL, for the relevant table.
2431 opjoin_type indicates whether multiple conditions, if present, should be
2432 connected by AND or OR.
2433 osrfMethodContext is loaded with all sorts of stuff, but all we do with it here is
2434 to pass it to other functions -- and all they do with it is to use the session
2435 and request members to send error messages back to the client.
2439 static char* searchWHERE ( const jsonObject* search_hash, osrfHash* meta, int opjoin_type, osrfMethodContext* ctx ) {
2443 "%s: Entering searchWHERE; search_hash addr = %p, meta addr = %p, opjoin_type = %d, ctx addr = %p",
2451 growing_buffer* sql_buf = buffer_init(128);
2453 jsonObject* node = NULL;
2456 if ( search_hash->type == JSON_ARRAY ) {
2457 osrfLogDebug(OSRF_LOG_MARK, "%s: In WHERE clause, condition type is JSON_ARRAY", MODULENAME);
2458 jsonIterator* search_itr = jsonNewIterator( search_hash );
2459 if( !jsonIteratorHasNext( search_itr ) ) {
2462 "%s: Invalid predicate structure: empty JSON array",
2465 jsonIteratorFree( search_itr );
2466 buffer_free( sql_buf );
2470 while ( (node = jsonIteratorNext( search_itr )) ) {
2474 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
2475 else buffer_add(sql_buf, " AND ");
2478 char* subpred = searchWHERE( node, meta, opjoin_type, ctx );
2480 buffer_fadd(sql_buf, "( %s )", subpred);
2483 jsonIteratorFree( search_itr );
2484 buffer_free( sql_buf );
2488 jsonIteratorFree(search_itr);
2490 } else if ( search_hash->type == JSON_HASH ) {
2491 osrfLogDebug(OSRF_LOG_MARK, "%s: In WHERE clause, condition type is JSON_HASH", MODULENAME);
2492 jsonIterator* search_itr = jsonNewIterator( search_hash );
2493 if( !jsonIteratorHasNext( search_itr ) ) {
2496 "%s: Invalid predicate structure: empty JSON object",
2499 jsonIteratorFree( search_itr );
2500 buffer_free( sql_buf );
2504 while ( (node = jsonIteratorNext( search_itr )) ) {
2509 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
2510 else buffer_add(sql_buf, " AND ");
2513 if ( '+' == search_itr->key[ 0 ] ) {
2514 if ( node->type == JSON_STRING ) {
2515 // Intended purpose; to allow reference to a Boolean column
2517 // Verify that the class alias is not empty
2518 if( '\0' == search_itr->key[ 1 ] ) {
2521 "%s: Table alias is empty",
2524 jsonIteratorFree( search_itr );
2525 buffer_free( sql_buf );
2529 // Verify that the string looks like an identifier.
2530 const char* subpred = jsonObjectGetString( node );
2531 if( ! is_identifier( subpred ) ) {
2534 "%s: Invalid boolean identifier in WHERE clause: \"%s\"",
2538 jsonIteratorFree( search_itr );
2539 buffer_free( sql_buf );
2543 buffer_fadd(sql_buf, " \"%s\".%s ", search_itr->key + 1, subpred);
2545 char* subpred = searchWHERE( node, osrfHashGet( oilsIDL(), search_itr->key + 1 ), AND_OP_JOIN, ctx );
2547 buffer_fadd(sql_buf, "( %s )", subpred);
2550 jsonIteratorFree( search_itr );
2551 buffer_free( sql_buf );
2555 } else if ( !strcasecmp("-or",search_itr->key) ) {
2556 char* subpred = searchWHERE( node, meta, OR_OP_JOIN, ctx );
2558 buffer_fadd(sql_buf, "( %s )", subpred);
2561 buffer_free( sql_buf );
2564 } else if ( !strcasecmp("-and",search_itr->key) ) {
2565 char* subpred = searchWHERE( node, meta, AND_OP_JOIN, ctx );
2567 buffer_fadd(sql_buf, "( %s )", subpred);
2570 buffer_free( sql_buf );
2573 } else if ( !strcasecmp("-not",search_itr->key) ) {
2574 char* subpred = searchWHERE( node, meta, AND_OP_JOIN, ctx );
2576 buffer_fadd(sql_buf, " NOT ( %s )", subpred);
2579 buffer_free( sql_buf );
2582 } else if ( !strcasecmp("-exists",search_itr->key) ) {
2583 char* subpred = SELECT(
2585 jsonObjectGetKey( node, "select" ),
2586 jsonObjectGetKey( node, "from" ),
2587 jsonObjectGetKey( node, "where" ),
2588 jsonObjectGetKey( node, "having" ),
2589 jsonObjectGetKey( node, "order_by" ),
2590 jsonObjectGetKey( node, "limit" ),
2591 jsonObjectGetKey( node, "offset" ),
2596 buffer_fadd(sql_buf, "EXISTS ( %s )", subpred);
2599 buffer_free( sql_buf );
2602 } else if ( !strcasecmp("-not-exists",search_itr->key) ) {
2603 char* subpred = SELECT(
2605 jsonObjectGetKey( node, "select" ),
2606 jsonObjectGetKey( node, "from" ),
2607 jsonObjectGetKey( node, "where" ),
2608 jsonObjectGetKey( node, "having" ),
2609 jsonObjectGetKey( node, "order_by" ),
2610 jsonObjectGetKey( node, "limit" ),
2611 jsonObjectGetKey( node, "offset" ),
2616 buffer_fadd(sql_buf, "NOT EXISTS ( %s )", subpred);
2619 buffer_free( sql_buf );
2625 char* class = osrfHashGet(meta, "classname");
2626 osrfHash* fields = osrfHashGet(meta, "fields");
2627 osrfHash* field = osrfHashGet( fields, search_itr->key );
2631 char* table = getSourceDefinition(meta);
2633 table = strdup( "(?)" );
2636 "%s: Attempt to reference non-existent column \"%s\" on %s (%s)",
2642 buffer_free(sql_buf);
2644 jsonIteratorFree(search_itr);
2648 char* subpred = searchPredicate( class, field, node, ctx );
2650 buffer_add( sql_buf, subpred );
2653 buffer_free(sql_buf);
2654 jsonIteratorFree(search_itr);
2659 jsonIteratorFree(search_itr);
2662 // ERROR ... only hash and array allowed at this level
2663 char* predicate_string = jsonObjectToJSON( search_hash );
2666 "%s: Invalid predicate structure: %s",
2670 buffer_free(sql_buf);
2671 free(predicate_string);
2675 return buffer_release(sql_buf);
2678 // Return 1 if the class is in the FROM clause, or 0 if not
2679 static int is_joined( jsonObject* from_clause, const char* class ) {
2683 else if( from_clause->type == JSON_STRING ) {
2684 if( strcmp( class, jsonObjectGetString( from_clause ) ) )
2688 } else if( from_clause->type != JSON_HASH ) {
2690 } else { // Look at any subjoins
2691 jsonIterator* class_itr = jsonNewIterator( from_clause );
2692 jsonObject* curr_class;
2693 int rc = 0; // return code
2694 while ( ( curr_class = jsonIteratorNext( class_itr ) ) ) {
2695 if( ! strcmp( class_itr->key, class ) ) {
2699 jsonObject* subjoin = jsonObjectGetKey( curr_class, "join" );
2700 if( subjoin && is_joined( subjoin, class ) ) {
2706 jsonIteratorFree( class_itr );
2713 /* method context */ osrfMethodContext* ctx,
2715 /* SELECT */ jsonObject* selhash,
2716 /* FROM */ jsonObject* join_hash,
2717 /* WHERE */ jsonObject* search_hash,
2718 /* HAVING */ jsonObject* having_hash,
2719 /* ORDER BY */ jsonObject* order_hash,
2720 /* LIMIT */ jsonObject* limit,
2721 /* OFFSET */ jsonObject* offset,
2722 /* flags */ int flags
2724 const char* locale = osrf_message_get_last_locale();
2726 // in case we don't get a select list
2727 jsonObject* defaultselhash = NULL;
2729 // general tmp objects
2730 const jsonObject* tmp_const;
2731 jsonObject* selclass = NULL;
2732 jsonObject* selfield = NULL;
2733 jsonObject* snode = NULL;
2734 jsonObject* onode = NULL;
2736 char* string = NULL;
2737 int from_function = 0;
2742 // the core search class
2743 char* core_class = NULL;
2745 // metadata about the core search class
2746 osrfHash* core_meta = NULL;
2748 osrfLogDebug(OSRF_LOG_MARK, "cstore SELECT locale: %s", locale);
2750 // punt if there's no core class
2751 if (!join_hash || ( join_hash->type == JSON_HASH && !join_hash->size )) {
2754 "%s: FROM clause is missing or empty",
2758 osrfAppSessionStatus(
2760 OSRF_STATUS_INTERNALSERVERERROR,
2761 "osrfMethodException",
2763 "FROM clause is missing or empty in JSON query"
2768 // get the core class -- the only key of the top level FROM clause, or a string
2769 if (join_hash->type == JSON_HASH) {
2770 jsonIterator* tmp_itr = jsonNewIterator( join_hash );
2771 snode = jsonIteratorNext( tmp_itr );
2773 core_class = strdup( tmp_itr->key );
2776 jsonObject* extra = jsonIteratorNext( tmp_itr );
2778 jsonIteratorFree( tmp_itr );
2781 // There shouldn't be more than one entry in join_hash
2785 "%s: Malformed FROM clause: extra entry in JSON_HASH",
2789 osrfAppSessionStatus(
2791 OSRF_STATUS_INTERNALSERVERERROR,
2792 "osrfMethodException",
2794 "Malformed FROM clause in JSON query"
2797 return NULL; // Malformed join_hash; extra entry
2799 } else if (join_hash->type == JSON_ARRAY) {
2801 core_class = jsonObjectToSimpleString( jsonObjectGetIndex(join_hash, 0) );
2804 } else if (join_hash->type == JSON_STRING) {
2805 core_class = jsonObjectToSimpleString( join_hash );
2811 "%s: FROM clause is unexpected JSON type: %s",
2813 json_type( join_hash->type )
2816 osrfAppSessionStatus(
2818 OSRF_STATUS_INTERNALSERVERERROR,
2819 "osrfMethodException",
2821 "Ill-formed FROM clause in JSON query"
2827 if (!from_function) {
2828 // Get the IDL class definition for the core class
2829 core_meta = osrfHashGet( oilsIDL(), core_class );
2830 if( !core_meta ) { // Didn't find it?
2833 "%s: SELECT clause references undefined class: \"%s\"",
2838 osrfAppSessionStatus(
2840 OSRF_STATUS_INTERNALSERVERERROR,
2841 "osrfMethodException",
2843 "SELECT clause references undefined class in JSON query"
2849 // Make sure the class isn't virtual
2850 if( str_is_true( osrfHashGet( core_meta, "virtual" ) ) ) {
2853 "%s: Core class is virtual: \"%s\"",
2858 osrfAppSessionStatus(
2860 OSRF_STATUS_INTERNALSERVERERROR,
2861 "osrfMethodException",
2863 "FROM clause references virtual class in JSON query"
2870 // if the select list is empty, or the core class field list is '*',
2871 // build the default select list ...
2873 selhash = defaultselhash = jsonNewObjectType(JSON_HASH);
2874 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2875 } else if( selhash->type != JSON_HASH ) {
2878 "%s: Expected JSON_HASH for SELECT clause; found %s",
2880 json_type( selhash->type )
2884 osrfAppSessionStatus(
2886 OSRF_STATUS_INTERNALSERVERERROR,
2887 "osrfMethodException",
2889 "Malformed SELECT clause in JSON query"
2893 } else if ( (tmp_const = jsonObjectGetKeyConst( selhash, core_class )) && tmp_const->type == JSON_STRING ) {
2894 const char* _x = jsonObjectGetString( tmp_const );
2895 if (!strncmp( "*", _x, 1 )) {
2896 jsonObjectRemoveKey( selhash, core_class );
2897 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2902 growing_buffer* sql_buf = buffer_init(128);
2904 // temp buffers for the SELECT list and GROUP BY clause
2905 growing_buffer* select_buf = buffer_init(128);
2906 growing_buffer* group_buf = buffer_init(128);
2908 int aggregate_found = 0; // boolean
2910 // Build a select list
2911 if(from_function) // From a function we select everything
2912 OSRF_BUFFER_ADD_CHAR( select_buf, '*' );
2915 // If we need to build a default list, prepare to do so
2916 jsonObject* _tmp = jsonObjectGetKey( selhash, core_class );
2917 if ( _tmp && !_tmp->size ) {
2919 osrfHash* core_fields = osrfHashGet( core_meta, "fields" );
2921 osrfHashIterator* field_itr = osrfNewHashIterator( core_fields );
2922 osrfHash* field_def;
2923 while( ( field_def = osrfHashIteratorNext( field_itr ) ) ) {
2924 if( ! str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2925 // This field is not virtual, so add it to the list
2926 jsonObjectPush( _tmp, jsonNewObject( osrfHashIteratorKey( field_itr ) ) );
2929 osrfHashIteratorFree( field_itr );
2932 // Now build the actual select list
2936 jsonIterator* selclass_itr = jsonNewIterator( selhash );
2937 while ( (selclass = jsonIteratorNext( selclass_itr )) ) { // For each class
2939 // Make sure the class is defined in the IDL
2940 const char* cname = selclass_itr->key;
2941 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
2945 "%s: Selected class \"%s\" not defined in IDL",
2951 osrfAppSessionStatus(
2953 OSRF_STATUS_INTERNALSERVERERROR,
2954 "osrfMethodException",
2956 "Selected class is not defined"
2958 jsonIteratorFree( selclass_itr );
2959 buffer_free( sql_buf );
2960 buffer_free( select_buf );
2961 buffer_free( group_buf );
2962 if( defaultselhash ) jsonObjectFree( defaultselhash );
2967 // Make sure the target relation is in the FROM clause.
2969 // At this point join_hash is a step down from the join_hash we
2970 // received as a parameter. If the original was a JSON_STRING,
2971 // then json_hash is now NULL. If the original was a JSON_HASH,
2972 // then json_hash is now the first (and only) entry in it,
2973 // denoting the core class. We've already excluded the
2974 // possibility that the original was a JSON_ARRAY, because in
2975 // that case from_function would be non-NULL, and we wouldn't
2978 // If the current class isn't the core class
2979 // and it isn't in the join tree, bail out
2980 if ( strcmp( core_class, cname ) && ! is_joined( join_hash, cname ) ) {
2983 "%s: SELECT clause references class not in FROM clause: \"%s\"",
2988 osrfAppSessionStatus(
2990 OSRF_STATUS_INTERNALSERVERERROR,
2991 "osrfMethodException",
2993 "Selected class not in FROM clause in JSON query"
2995 jsonIteratorFree( selclass_itr );
2996 buffer_free( sql_buf );
2997 buffer_free( select_buf );
2998 buffer_free( group_buf );
2999 if( defaultselhash ) jsonObjectFree( defaultselhash );
3004 // Look up some attributes of the current class, so that we
3005 // don't have to look them up again for each field
3006 osrfHash* class_field_set = osrfHashGet( idlClass, "fields" );
3007 const char* class_pkey = osrfHashGet( idlClass, "primarykey" );
3008 const char* class_tname = osrfHashGet( idlClass, "tablename" );
3010 // stitch together the column list ...
3011 jsonIterator* select_itr = jsonNewIterator( selclass );
3012 while ( (selfield = jsonIteratorNext( select_itr )) ) { // for each SELECT column
3014 // If we need a separator comma, add one
3018 OSRF_BUFFER_ADD_CHAR( select_buf, ',' );
3021 // ... if it's a string, just toss it on the pile
3022 if (selfield->type == JSON_STRING) {
3024 // Look up the field in the IDL
3025 const char* col_name = jsonObjectGetString( selfield );
3026 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
3028 // No such field in current class
3031 "%s: Selected column \"%s\" not defined in IDL for class \"%s\"",
3037 osrfAppSessionStatus(
3039 OSRF_STATUS_INTERNALSERVERERROR,
3040 "osrfMethodException",
3042 "Selected column not defined in JSON query"
3044 jsonIteratorFree( select_itr );
3045 jsonIteratorFree( selclass_itr );
3046 buffer_free( sql_buf );
3047 buffer_free( select_buf );
3048 buffer_free( group_buf );
3049 if( defaultselhash ) jsonObjectFree( defaultselhash );
3052 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
3053 // Virtual field not allowed
3056 "%s: Selected column \"%s\" for class \"%s\" is virtual",
3062 osrfAppSessionStatus(
3064 OSRF_STATUS_INTERNALSERVERERROR,
3065 "osrfMethodException",
3067 "Selected column may not be virtual in JSON query"
3069 jsonIteratorFree( select_itr );
3070 jsonIteratorFree( selclass_itr );
3071 buffer_free( sql_buf );
3072 buffer_free( select_buf );
3073 buffer_free( group_buf );
3074 if( defaultselhash ) jsonObjectFree( defaultselhash );
3081 if (flags & DISABLE_I18N)
3084 i18n = osrfHashGet(field_def, "i18n");
3086 if( str_is_true( i18n ) ) {
3087 buffer_fadd( select_buf,
3088 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
3089 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, col_name );
3091 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
3094 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
3097 // ... but it could be an object, in which case we check for a Field Transform
3098 } else if (selfield->type == JSON_HASH) {
3100 const char* col_name = jsonObjectGetString( jsonObjectGetKeyConst( selfield, "column" ) );
3102 // Get the field definition from the IDL
3103 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
3105 // No such field in current class
3108 "%s: Selected column \"%s\" is not defined in IDL for class \"%s\"",
3114 osrfAppSessionStatus(
3116 OSRF_STATUS_INTERNALSERVERERROR,
3117 "osrfMethodException",
3119 "Selected column is not defined in JSON query"
3121 jsonIteratorFree( select_itr );
3122 jsonIteratorFree( selclass_itr );
3123 buffer_free( sql_buf );
3124 buffer_free( select_buf );
3125 buffer_free( group_buf );
3126 if( defaultselhash ) jsonObjectFree( defaultselhash );
3129 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
3130 // No such field in current class
3133 "%s: Selected column \"%s\" is virtual for class \"%s\"",
3139 osrfAppSessionStatus(
3141 OSRF_STATUS_INTERNALSERVERERROR,
3142 "osrfMethodException",
3144 "Selected column is virtual in JSON query"
3146 jsonIteratorFree( select_itr );
3147 jsonIteratorFree( selclass_itr );
3148 buffer_free( sql_buf );
3149 buffer_free( select_buf );
3150 buffer_free( group_buf );
3151 if( defaultselhash ) jsonObjectFree( defaultselhash );
3156 // Decide what to use as a column alias
3158 if ((tmp_const = jsonObjectGetKeyConst( selfield, "alias" ))) {
3159 _alias = jsonObjectGetString( tmp_const );
3160 } else { // Use field name as the alias
3164 if (jsonObjectGetKeyConst( selfield, "transform" )) {
3165 char* transform_str = searchFieldTransform(cname, field_def, selfield);
3166 if( transform_str ) {
3167 buffer_fadd(select_buf, " %s AS \"%s\"", transform_str, _alias);
3168 free(transform_str);
3171 osrfAppSessionStatus(
3173 OSRF_STATUS_INTERNALSERVERERROR,
3174 "osrfMethodException",
3176 "Unable to generate transform function in JSON query"
3178 jsonIteratorFree( select_itr );
3179 jsonIteratorFree( selclass_itr );
3180 buffer_free( sql_buf );
3181 buffer_free( select_buf );
3182 buffer_free( group_buf );
3183 if( defaultselhash ) jsonObjectFree( defaultselhash );
3191 if (flags & DISABLE_I18N)
3194 i18n = osrfHashGet(field_def, "i18n");
3196 if( str_is_true( i18n ) ) {
3197 buffer_fadd( select_buf,
3198 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
3199 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, _alias);
3201 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
3204 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
3211 "%s: Selected item is unexpected JSON type: %s",
3213 json_type( selfield->type )
3216 osrfAppSessionStatus(
3218 OSRF_STATUS_INTERNALSERVERERROR,
3219 "osrfMethodException",
3221 "Ill-formed SELECT item in JSON query"
3223 jsonIteratorFree( select_itr );
3224 jsonIteratorFree( selclass_itr );
3225 buffer_free( sql_buf );
3226 buffer_free( select_buf );
3227 buffer_free( group_buf );
3228 if( defaultselhash ) jsonObjectFree( defaultselhash );
3233 const jsonObject* agg_obj = jsonObjectGetKey( selfield, "aggregate" );
3234 if( obj_is_true( agg_obj ) )
3235 aggregate_found = 1;
3237 // Append a comma (except for the first one)
3238 // and add the column to a GROUP BY clause
3242 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3244 buffer_fadd(group_buf, " %d", sel_pos);
3248 if (is_agg->size || (flags & SELECT_DISTINCT)) {
3250 const jsonObject* aggregate_obj = jsonObjectGetKey( selfield, "aggregate" );
3251 if ( ! obj_is_true( aggregate_obj ) ) {
3255 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3258 buffer_fadd(group_buf, " %d", sel_pos);
3261 } else if (is_agg = jsonObjectGetKey( selfield, "having" )) {
3265 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3268 _column = searchFieldTransform(cname, field, selfield);
3269 OSRF_BUFFER_ADD_CHAR(group_buf, ' ');
3270 OSRF_BUFFER_ADD(group_buf, _column);
3271 _column = searchFieldTransform(cname, field, selfield);
3278 } // end while -- iterating across SELECT columns
3280 jsonIteratorFree(select_itr);
3281 } // end while -- iterating across classes
3283 jsonIteratorFree(selclass_itr);
3287 char* col_list = buffer_release(select_buf);
3289 if (from_function) table = searchValueTransform(join_hash);
3290 else table = getSourceDefinition(core_meta);
3294 osrfAppSessionStatus(
3296 OSRF_STATUS_INTERNALSERVERERROR,
3297 "osrfMethodException",
3299 "Unable to identify table for core class"
3302 buffer_free( sql_buf );
3303 buffer_free( group_buf );
3304 if( defaultselhash ) jsonObjectFree( defaultselhash );
3309 // Put it all together
3310 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\" ", col_list, table, core_class );
3314 char* order_by_list = NULL;
3315 growing_buffer* having_buf = buffer_init(128);
3317 if (!from_function) {
3319 // Now, walk the join tree and add that clause
3321 char* join_clause = searchJOIN( join_hash, core_meta );
3323 buffer_add(sql_buf, join_clause);
3327 osrfAppSessionStatus(
3329 OSRF_STATUS_INTERNALSERVERERROR,
3330 "osrfMethodException",
3332 "Unable to construct JOIN clause(s)"
3334 buffer_free( sql_buf );
3335 buffer_free( group_buf );
3336 buffer_free( having_buf );
3337 if( defaultselhash ) jsonObjectFree( defaultselhash );
3343 // Build a WHERE clause, if there is one
3344 if ( search_hash ) {
3345 buffer_add(sql_buf, " WHERE ");
3347 // and it's on the WHERE clause
3348 char* pred = searchWHERE( search_hash, core_meta, AND_OP_JOIN, ctx );
3351 buffer_add(sql_buf, pred);
3355 osrfAppSessionStatus(
3357 OSRF_STATUS_INTERNALSERVERERROR,
3358 "osrfMethodException",
3360 "Severe query error in WHERE predicate -- see error log for more details"
3364 buffer_free(having_buf);
3365 buffer_free(group_buf);
3366 buffer_free(sql_buf);
3367 if (defaultselhash) jsonObjectFree(defaultselhash);
3372 // Build a HAVING clause, if there is one
3373 if ( having_hash ) {
3375 // and it's on the the WHERE clause
3376 char* pred = searchWHERE( having_hash, core_meta, AND_OP_JOIN, ctx );
3379 buffer_add( having_buf, pred );
3383 osrfAppSessionStatus(
3385 OSRF_STATUS_INTERNALSERVERERROR,
3386 "osrfMethodException",
3388 "Severe query error in HAVING predicate -- see error log for more details"
3392 buffer_free(having_buf);
3393 buffer_free(group_buf);
3394 buffer_free(sql_buf);
3395 if (defaultselhash) jsonObjectFree(defaultselhash);
3400 growing_buffer* order_buf = NULL; // to collect ORDER BY list
3402 // Build an ORDER BY clause, if there is one
3403 if( NULL == order_hash )
3404 ; // No ORDER BY? do nothing
3405 else if( JSON_ARRAY == order_hash->type ) {
3406 // Array of field specifications, each specification being a
3407 // hash to define the class, field, and other details
3409 jsonObject* order_spec;
3410 while( (order_spec = jsonObjectGetIndex( order_hash, order_idx++ ) ) ) {
3412 if( JSON_HASH != order_spec->type ) {
3413 osrfLogError(OSRF_LOG_MARK,
3414 "%s: Malformed field specification in ORDER BY clause; expected JSON_HASH, found %s",
3415 MODULENAME, json_type( order_spec->type ) );
3417 osrfAppSessionStatus(
3419 OSRF_STATUS_INTERNALSERVERERROR,
3420 "osrfMethodException",
3422 "Malformed ORDER BY clause -- see error log for more details"
3424 buffer_free( order_buf );
3426 buffer_free(having_buf);
3427 buffer_free(group_buf);
3428 buffer_free(sql_buf);
3429 if (defaultselhash) jsonObjectFree(defaultselhash);
3434 jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "class" ) );
3436 jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "field" ) );
3439 OSRF_BUFFER_ADD(order_buf, ", ");
3441 order_buf = buffer_init(128);
3443 if( !field || !class ) {
3444 osrfLogError(OSRF_LOG_MARK,
3445 "%s: Missing class or field name in field specification of ORDER BY clause",
3448 osrfAppSessionStatus(
3450 OSRF_STATUS_INTERNALSERVERERROR,
3451 "osrfMethodException",
3453 "Malformed ORDER BY clause -- see error log for more details"
3455 buffer_free( order_buf );
3457 buffer_free(having_buf);
3458 buffer_free(group_buf);
3459 buffer_free(sql_buf);
3460 if (defaultselhash) jsonObjectFree(defaultselhash);
3464 if ( ! jsonObjectGetKeyConst( selhash, class )
3465 && strcmp( core_class, class )
3466 && ! is_joined( join_hash, class ) ) {
3467 osrfLogError(OSRF_LOG_MARK, "%s: ORDER BY clause references class \"%s\" "
3468 "not in FROM clause", MODULENAME, class );
3470 osrfAppSessionStatus(
3472 OSRF_STATUS_INTERNALSERVERERROR,
3473 "osrfMethodException",
3475 "Invalid class referenced in ORDER BY clause -- see error log for more details"
3478 buffer_free(having_buf);
3479 buffer_free(group_buf);
3480 buffer_free(sql_buf);
3481 if (defaultselhash) jsonObjectFree(defaultselhash);
3485 osrfHash* field_def = oilsIDLFindPath( "/%s/fields/%s", class, field );
3487 osrfLogError(OSRF_LOG_MARK, "%s: Invalid field \"%s\".%s referenced in ORDER BY clause",
3488 MODULENAME, class, field );
3490 osrfAppSessionStatus(
3492 OSRF_STATUS_INTERNALSERVERERROR,
3493 "osrfMethodException",
3495 "Invalid field referenced in ORDER BY clause -- see error log for more details"
3498 buffer_free(having_buf);
3499 buffer_free(group_buf);
3500 buffer_free(sql_buf);
3501 if (defaultselhash) jsonObjectFree(defaultselhash);
3503 } else if( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
3504 osrfLogError(OSRF_LOG_MARK, "%s: Virtual field \"%s\" in ORDER BY clause",
3505 MODULENAME, field );
3507 osrfAppSessionStatus(
3509 OSRF_STATUS_INTERNALSERVERERROR,
3510 "osrfMethodException",
3512 "Virtual field in ORDER BY clause -- see error log for more details"
3514 buffer_free( order_buf );
3516 buffer_free(having_buf);
3517 buffer_free(group_buf);
3518 buffer_free(sql_buf);
3519 if (defaultselhash) jsonObjectFree(defaultselhash);
3523 if( jsonObjectGetKeyConst( order_spec, "transform" ) ) {
3524 char* transform_str = searchFieldTransform( class, field_def, order_spec );
3525 if( ! transform_str ) {
3527 osrfAppSessionStatus(
3529 OSRF_STATUS_INTERNALSERVERERROR,
3530 "osrfMethodException",
3532 "Severe query error in ORDER BY clause -- see error log for more details"
3534 buffer_free( order_buf );
3536 buffer_free(having_buf);
3537 buffer_free(group_buf);
3538 buffer_free(sql_buf);
3539 if (defaultselhash) jsonObjectFree(defaultselhash);
3543 OSRF_BUFFER_ADD( order_buf, transform_str );
3544 free( transform_str );
3547 buffer_fadd( order_buf, "\"%s\".%s", class, field );
3549 const char* direction =
3550 jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "direction" ) );
3552 if( direction[ 0 ] || 'D' == direction[ 0 ] )
3553 OSRF_BUFFER_ADD( order_buf, " DESC" );
3555 OSRF_BUFFER_ADD( order_buf, " ASC" );
3558 } else if( JSON_HASH == order_hash->type ) {
3559 // This hash is keyed on class name. Each class has either
3560 // an array of field names or a hash keyed on field name.
3561 jsonIterator* class_itr = jsonNewIterator( order_hash );
3562 while ( (snode = jsonIteratorNext( class_itr )) ) {
3564 if ( ! jsonObjectGetKeyConst( selhash,class_itr->key )
3565 && strcmp( core_class, class_itr->key )
3566 && ! is_joined( join_hash, class_itr->key ) ) {
3567 osrfLogError(OSRF_LOG_MARK, "%s: Invalid class \"%s\" referenced in ORDER BY clause",
3568 MODULENAME, class_itr->key );
3570 osrfAppSessionStatus(
3572 OSRF_STATUS_INTERNALSERVERERROR,
3573 "osrfMethodException",
3575 "Invalid class referenced in ORDER BY clause -- see error log for more details"
3577 jsonIteratorFree( class_itr );
3578 buffer_free( order_buf );
3580 buffer_free(having_buf);
3581 buffer_free(group_buf);
3582 buffer_free(sql_buf);
3583 if (defaultselhash) jsonObjectFree(defaultselhash);
3587 osrfHash* field_list_def = oilsIDLFindPath( "/%s/fields", class_itr->key );
3589 if ( snode->type == JSON_HASH ) {
3591 // Hash is keyed on field names from the current class. For each field
3592 // there is another layer of hash to define the sorting details, if any,
3593 // or a string to indicate direction of sorting.
3594 jsonIterator* order_itr = jsonNewIterator( snode );
3595 while ( (onode = jsonIteratorNext( order_itr )) ) {
3597 osrfHash* field_def = osrfHashGet( field_list_def, order_itr->key );
3599 osrfLogError(OSRF_LOG_MARK, "%s: Invalid field \"%s\" in ORDER BY clause",
3600 MODULENAME, order_itr->key );
3602 osrfAppSessionStatus(
3604 OSRF_STATUS_INTERNALSERVERERROR,
3605 "osrfMethodException",
3607 "Invalid field in ORDER BY clause -- see error log for more details"
3609 jsonIteratorFree( order_itr );
3610 jsonIteratorFree( class_itr );
3611 buffer_free( order_buf );
3613 buffer_free(having_buf);
3614 buffer_free(group_buf);
3615 buffer_free(sql_buf);
3616 if (defaultselhash) jsonObjectFree(defaultselhash);
3618 } else if( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
3619 osrfLogError(OSRF_LOG_MARK, "%s: Virtual field \"%s\" in ORDER BY clause",
3620 MODULENAME, order_itr->key );
3622 osrfAppSessionStatus(
3624 OSRF_STATUS_INTERNALSERVERERROR,
3625 "osrfMethodException",
3627 "Virtual field in ORDER BY clause -- see error log for more details"
3629 jsonIteratorFree( order_itr );
3630 jsonIteratorFree( class_itr );
3631 buffer_free( order_buf );
3633 buffer_free(having_buf);
3634 buffer_free(group_buf);
3635 buffer_free(sql_buf);
3636 if (defaultselhash) jsonObjectFree(defaultselhash);
3640 const char* direction = NULL;
3641 if ( onode->type == JSON_HASH ) {
3642 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3643 string = searchFieldTransform(
3645 osrfHashGet( field_list_def, order_itr->key ),
3649 if( ctx ) osrfAppSessionStatus(
3651 OSRF_STATUS_INTERNALSERVERERROR,
3652 "osrfMethodException",
3654 "Severe query error in ORDER BY clause -- see error log for more details"
3656 jsonIteratorFree( order_itr );
3657 jsonIteratorFree( class_itr );
3659 buffer_free(having_buf);
3660 buffer_free(group_buf);
3661 buffer_free(order_buf);
3662 buffer_free(sql_buf);
3663 if (defaultselhash) jsonObjectFree(defaultselhash);
3667 growing_buffer* field_buf = buffer_init(16);
3668 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3669 string = buffer_release(field_buf);
3672 if ( (tmp_const = jsonObjectGetKeyConst( onode, "direction" )) ) {
3673 const char* dir = jsonObjectGetString(tmp_const);
3674 if (!strncasecmp(dir, "d", 1)) {
3675 direction = " DESC";
3681 } else if ( JSON_NULL == onode->type || JSON_ARRAY == onode->type ) {
3682 osrfLogError( OSRF_LOG_MARK,
3683 "%s: Expected JSON_STRING in ORDER BY clause; found %s",
3684 MODULENAME, json_type( onode->type ) );
3686 osrfAppSessionStatus(
3688 OSRF_STATUS_INTERNALSERVERERROR,
3689 "osrfMethodException",
3691 "Malformed ORDER BY clause -- see error log for more details"
3693 jsonIteratorFree( order_itr );
3694 jsonIteratorFree( class_itr );
3696 buffer_free(having_buf);
3697 buffer_free(group_buf);
3698 buffer_free(order_buf);
3699 buffer_free(sql_buf);
3700 if (defaultselhash) jsonObjectFree(defaultselhash);
3704 string = strdup(order_itr->key);
3705 const char* dir = jsonObjectGetString(onode);
3706 if (!strncasecmp(dir, "d", 1)) {
3707 direction = " DESC";
3714 OSRF_BUFFER_ADD(order_buf, ", ");
3716 order_buf = buffer_init(128);
3718 OSRF_BUFFER_ADD(order_buf, string);
3722 OSRF_BUFFER_ADD(order_buf, direction);
3726 jsonIteratorFree(order_itr);
3728 } else if ( snode->type == JSON_ARRAY ) {
3730 // Array is a list of fields from the current class
3731 jsonIterator* order_itr = jsonNewIterator( snode );
3732 while ( (onode = jsonIteratorNext( order_itr )) ) {
3734 const char* _f = jsonObjectGetString( onode );
3736 osrfHash* field_def = osrfHashGet( field_list_def, _f );
3738 osrfLogError(OSRF_LOG_MARK, "%s: Invalid field \"%s\" in ORDER BY clause",
3741 osrfAppSessionStatus(
3743 OSRF_STATUS_INTERNALSERVERERROR,
3744 "osrfMethodException",
3746 "Invalid field in ORDER BY clause -- see error log for more details"
3748 jsonIteratorFree( order_itr );
3749 jsonIteratorFree( class_itr );
3750 buffer_free( order_buf );
3752 buffer_free(having_buf);
3753 buffer_free(group_buf);
3754 buffer_free(sql_buf);
3755 if (defaultselhash) jsonObjectFree(defaultselhash);
3757 } else if( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
3758 osrfLogError(OSRF_LOG_MARK, "%s: Virtual field \"%s\" in ORDER BY clause",
3761 osrfAppSessionStatus(
3763 OSRF_STATUS_INTERNALSERVERERROR,
3764 "osrfMethodException",
3766 "Virtual field in ORDER BY clause -- see error log for more details"
3768 jsonIteratorFree( order_itr );
3769 jsonIteratorFree( class_itr );
3770 buffer_free( order_buf );
3772 buffer_free(having_buf);
3773 buffer_free(group_buf);
3774 buffer_free(sql_buf);
3775 if (defaultselhash) jsonObjectFree(defaultselhash);
3780 OSRF_BUFFER_ADD(order_buf, ", ");
3782 order_buf = buffer_init(128);
3784 buffer_fadd( order_buf, "\"%s\".%s", class_itr->key, _f);
3787 // jsonIteratorFree(order_itr);
3790 // IT'S THE OOOOOOOOOOOLD STYLE!
3792 osrfLogError(OSRF_LOG_MARK,
3793 "%s: Possible SQL injection attempt; direct order by is not allowed", MODULENAME);
3795 osrfAppSessionStatus(
3797 OSRF_STATUS_INTERNALSERVERERROR,
3798 "osrfMethodException",
3800 "Severe query error -- see error log for more details"
3805 buffer_free(having_buf);
3806 buffer_free(group_buf);
3807 buffer_free(order_buf);
3808 buffer_free(sql_buf);
3809 if (defaultselhash) jsonObjectFree(defaultselhash);
3810 jsonIteratorFree(class_itr);
3815 osrfLogError(OSRF_LOG_MARK,
3816 "%s: Malformed ORDER BY clause; expected JSON_HASH or JSON_ARRAY, found %s",
3817 MODULENAME, json_type( order_hash->type ) );
3819 osrfAppSessionStatus(
3821 OSRF_STATUS_INTERNALSERVERERROR,
3822 "osrfMethodException",
3824 "Malformed ORDER BY clause -- see error log for more details"
3826 buffer_free( order_buf );
3828 buffer_free(having_buf);
3829 buffer_free(group_buf);
3830 buffer_free(sql_buf);
3831 if (defaultselhash) jsonObjectFree(defaultselhash);
3834 // jsonIteratorFree(class_itr);
3837 order_by_list = buffer_release( order_buf );
3841 string = buffer_release(group_buf);
3843 if ( *string && ( aggregate_found || (flags & SELECT_DISTINCT) ) ) {
3844 OSRF_BUFFER_ADD( sql_buf, " GROUP BY " );
3845 OSRF_BUFFER_ADD( sql_buf, string );
3851 string = buffer_release(having_buf);
3854 OSRF_BUFFER_ADD( sql_buf, " HAVING " );
3855 OSRF_BUFFER_ADD( sql_buf, string );
3861 if( order_by_list ) {
3863 if ( *order_by_list ) {
3864 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3865 OSRF_BUFFER_ADD( sql_buf, order_by_list );
3868 free( order_by_list );
3872 const char* str = jsonObjectGetString(limit);
3873 buffer_fadd( sql_buf, " LIMIT %d", atoi(str) );
3877 const char* str = jsonObjectGetString(offset);
3878 buffer_fadd( sql_buf, " OFFSET %d", atoi(str) );
3881 if (!(flags & SUBSELECT)) OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3884 if (defaultselhash) jsonObjectFree(defaultselhash);
3886 return buffer_release(sql_buf);
3890 static char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
3892 const char* locale = osrf_message_get_last_locale();
3894 osrfHash* fields = osrfHashGet(meta, "fields");
3895 char* core_class = osrfHashGet(meta, "classname");
3897 const jsonObject* join_hash = jsonObjectGetKeyConst( order_hash, "join" );
3899 jsonObject* node = NULL;
3900 jsonObject* snode = NULL;
3901 jsonObject* onode = NULL;
3902 const jsonObject* _tmp = NULL;
3903 jsonObject* selhash = NULL;
3904 jsonObject* defaultselhash = NULL;
3906 growing_buffer* sql_buf = buffer_init(128);
3907 growing_buffer* select_buf = buffer_init(128);
3909 if ( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
3910 defaultselhash = jsonNewObjectType(JSON_HASH);
3911 selhash = defaultselhash;
3914 if ( !jsonObjectGetKeyConst(selhash,core_class) ) {
3915 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
3916 jsonObject* flist = jsonObjectGetKey( selhash, core_class );
3921 osrfStringArray* keys = osrfHashKeys( fields );
3922 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
3923 if( ! str_is_true( osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
3924 jsonObjectPush( flist, jsonNewObject( field ) );
3926 osrfStringArrayFree(keys);
3930 jsonIterator* class_itr = jsonNewIterator( selhash );
3931 while ( (snode = jsonIteratorNext( class_itr )) ) {
3933 char* cname = class_itr->key;
3934 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
3935 if (!idlClass) continue;
3937 if (strcmp(core_class,class_itr->key)) {
3938 if (!join_hash) continue;
3940 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", class_itr->key);
3942 jsonObjectFree(found);
3946 jsonObjectFree(found);
3949 jsonIterator* select_itr = jsonNewIterator( snode );
3950 while ( (node = jsonIteratorNext( select_itr )) ) {
3951 const char* item_str = jsonObjectGetString( node );
3952 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), item_str );
3953 char* fname = osrfHashGet(field, "name");
3955 if (!field) continue;
3960 OSRF_BUFFER_ADD_CHAR(select_buf, ',');
3965 const jsonObject* no_i18n_obj = jsonObjectGetKey( order_hash, "no_i18n" );
3966 if ( obj_is_true( no_i18n_obj ) ) // Suppress internationalization?
3969 i18n = osrfHashGet(field, "i18n");
3971 if( str_is_true( i18n ) ) {
3972 char* pkey = osrfHashGet(idlClass, "primarykey");
3973 char* tname = osrfHashGet(idlClass, "tablename");
3975 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);
3977 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3980 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3984 jsonIteratorFree(select_itr);
3987 jsonIteratorFree(class_itr);
3989 char* col_list = buffer_release(select_buf);
3990 char* table = getSourceDefinition(meta);
3992 table = strdup( "(null)" );
3994 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\"", col_list, table, core_class );
3999 char* join_clause = searchJOIN( join_hash, meta );
4000 OSRF_BUFFER_ADD_CHAR(sql_buf, ' ');
4001 OSRF_BUFFER_ADD(sql_buf, join_clause);
4005 osrfLogDebug(OSRF_LOG_MARK, "%s pre-predicate SQL = %s",
4006 MODULENAME, OSRF_BUFFER_C_STR(sql_buf));
4008 OSRF_BUFFER_ADD(sql_buf, " WHERE ");
4010 char* pred = searchWHERE( search_hash, meta, AND_OP_JOIN, ctx );
4012 osrfAppSessionStatus(
4014 OSRF_STATUS_INTERNALSERVERERROR,
4015 "osrfMethodException",
4017 "Severe query error -- see error log for more details"
4019 buffer_free(sql_buf);
4020 if(defaultselhash) jsonObjectFree(defaultselhash);
4023 buffer_add(sql_buf, pred);
4028 char* string = NULL;
4029 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "order_by" )) ){
4031 growing_buffer* order_buf = buffer_init(128);
4034 jsonIterator* class_itr = jsonNewIterator( _tmp );
4035 while ( (snode = jsonIteratorNext( class_itr )) ) {
4037 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
4040 if ( snode->type == JSON_HASH ) {
4042 jsonIterator* order_itr = jsonNewIterator( snode );
4043 while ( (onode = jsonIteratorNext( order_itr )) ) {
4045 osrfHash* field_def = oilsIDLFindPath( "/%s/fields/%s",
4046 class_itr->key, order_itr->key );
4050 char* direction = NULL;
4051 if ( onode->type == JSON_HASH ) {
4052 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
4053 string = searchFieldTransform( class_itr->key, field_def, onode );
4055 osrfAppSessionStatus(
4057 OSRF_STATUS_INTERNALSERVERERROR,
4058 "osrfMethodException",
4060 "Severe query error in ORDER BY clause -- see error log for more details"
4062 jsonIteratorFree( order_itr );
4063 jsonIteratorFree( class_itr );
4064 buffer_free( order_buf );
4065 buffer_free( sql_buf );
4066 if( defaultselhash ) jsonObjectFree( defaultselhash );
4070 growing_buffer* field_buf = buffer_init(16);
4071 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
4072 string = buffer_release(field_buf);
4075 if ( (_tmp = jsonObjectGetKeyConst( onode, "direction" )) ) {
4076 const char* dir = jsonObjectGetString(_tmp);
4077 if (!strncasecmp(dir, "d", 1)) {
4078 direction = " DESC";
4085 string = strdup(order_itr->key);
4086 const char* dir = jsonObjectGetString(onode);
4087 if (!strncasecmp(dir, "d", 1)) {
4088 direction = " DESC";
4097 buffer_add(order_buf, ", ");
4100 buffer_add(order_buf, string);
4104 buffer_add(order_buf, direction);
4109 jsonIteratorFree(order_itr);
4112 const char* str = jsonObjectGetString(snode);
4113 buffer_add(order_buf, str);
4119 jsonIteratorFree(class_itr);
4121 string = buffer_release(order_buf);
4124 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
4125 OSRF_BUFFER_ADD( sql_buf, string );
4131 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "limit" )) ){
4132 const char* str = jsonObjectGetString(_tmp);
4140 _tmp = jsonObjectGetKeyConst( order_hash, "offset" );
4142 const char* str = jsonObjectGetString(_tmp);
4151 if (defaultselhash) jsonObjectFree(defaultselhash);
4153 OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
4154 return buffer_release(sql_buf);
4157 int doJSONSearch ( osrfMethodContext* ctx ) {
4158 if(osrfMethodVerifyContext( ctx )) {
4159 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
4163 osrfLogDebug(OSRF_LOG_MARK, "Recieved query request");
4168 dbhandle = writehandle;
4170 jsonObject* hash = jsonObjectGetIndex(ctx->params, 0);
4174 if ( obj_is_true( jsonObjectGetKey( hash, "distinct" ) ) )
4175 flags |= SELECT_DISTINCT;
4177 if ( obj_is_true( jsonObjectGetKey( hash, "no_i18n" ) ) )
4178 flags |= DISABLE_I18N;
4180 osrfLogDebug(OSRF_LOG_MARK, "Building SQL ...");
4183 jsonObjectGetKey( hash, "select" ),
4184 jsonObjectGetKey( hash, "from" ),
4185 jsonObjectGetKey( hash, "where" ),
4186 jsonObjectGetKey( hash, "having" ),
4187 jsonObjectGetKey( hash, "order_by" ),
4188 jsonObjectGetKey( hash, "limit" ),
4189 jsonObjectGetKey( hash, "offset" ),
4198 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
4199 dbi_result result = dbi_conn_query(dbhandle, sql);
4202 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
4204 if (dbi_result_first_row(result)) {
4205 /* JSONify the result */
4206 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
4209 jsonObject* return_val = oilsMakeJSONFromResult( result );
4210 osrfAppRespond( ctx, return_val );
4211 jsonObjectFree( return_val );
4212 } while (dbi_result_next_row(result));
4215 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
4218 osrfAppRespondComplete( ctx, NULL );
4220 /* clean up the query */
4221 dbi_result_free(result);
4225 osrfLogError(OSRF_LOG_MARK, "%s: Error with query [%s]", MODULENAME, sql);
4226 osrfAppSessionStatus(
4228 OSRF_STATUS_INTERNALSERVERERROR,
4229 "osrfMethodException",
4231 "Severe query error -- see error log for more details"
4239 static jsonObject* doFieldmapperSearch ( osrfMethodContext* ctx, osrfHash* meta,
4240 const jsonObject* params, int* err ) {
4243 dbhandle = writehandle;
4245 osrfHash* links = osrfHashGet(meta, "links");
4246 osrfHash* fields = osrfHashGet(meta, "fields");
4247 char* core_class = osrfHashGet(meta, "classname");
4248 char* pkey = osrfHashGet(meta, "primarykey");
4250 const jsonObject* _tmp;
4252 jsonObject* search_hash = jsonObjectGetIndex(params, 0);
4253 jsonObject* order_hash = jsonObjectGetIndex(params, 1);
4255 char* sql = buildSELECT( search_hash, order_hash, meta, ctx );
4257 osrfLogDebug(OSRF_LOG_MARK, "Problem building query, returning NULL");
4262 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
4264 dbi_result result = dbi_conn_query(dbhandle, sql);
4265 if( NULL == result ) {
4266 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]",
4267 MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
4268 osrfAppSessionStatus(
4270 OSRF_STATUS_INTERNALSERVERERROR,
4271 "osrfMethodException",
4273 "Severe query error -- see error log for more details"
4280 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
4283 jsonObject* res_list = jsonNewObjectType(JSON_ARRAY);
4284 osrfHash* dedup = osrfNewHash();
4286 if (dbi_result_first_row(result)) {
4287 /* JSONify the result */
4288 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
4290 obj = oilsMakeFieldmapperFromResult( result, meta );
4291 char* pkey_val = oilsFMGetString( obj, pkey );
4292 if ( osrfHashGet( dedup, pkey_val ) ) {
4293 jsonObjectFree(obj);
4296 osrfHashSet( dedup, pkey_val, pkey_val );
4297 jsonObjectPush(res_list, obj);
4299 } while (dbi_result_next_row(result));
4301 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s",
4305 osrfHashFree(dedup);
4306 /* clean up the query */
4307 dbi_result_free(result);
4310 if (res_list->size && order_hash) {
4311 _tmp = jsonObjectGetKeyConst( order_hash, "flesh" );
4313 int x = (int)jsonObjectGetNumber(_tmp);
4314 if (x == -1 || x > max_flesh_depth) x = max_flesh_depth;
4316 const jsonObject* temp_blob;
4317 if ((temp_blob = jsonObjectGetKeyConst( order_hash, "flesh_fields" )) && x > 0) {
4319 jsonObject* flesh_blob = jsonObjectClone( temp_blob );
4320 const jsonObject* flesh_fields = jsonObjectGetKeyConst( flesh_blob, core_class );
4322 osrfStringArray* link_fields = NULL;
4325 if (flesh_fields->size == 1) {
4326 const char* _t = jsonObjectGetString( jsonObjectGetIndex( flesh_fields, 0 ) );
4327 if (!strcmp(_t,"*")) link_fields = osrfHashKeys( links );
4332 link_fields = osrfNewStringArray(1);
4333 jsonIterator* _i = jsonNewIterator( flesh_fields );
4334 while ((_f = jsonIteratorNext( _i ))) {
4335 osrfStringArrayAdd( link_fields, jsonObjectGetString( _f ) );
4337 jsonIteratorFree(_i);
4342 jsonIterator* itr = jsonNewIterator( res_list );
4343 while ((cur = jsonIteratorNext( itr ))) {
4348 while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
4350 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
4352 osrfHash* kid_link = osrfHashGet(links, link_field);
4353 if (!kid_link) continue;
4355 osrfHash* field = osrfHashGet(fields, link_field);
4356 if (!field) continue;
4358 osrfHash* value_field = field;
4360 osrfHash* kid_idl = osrfHashGet(oilsIDL(), osrfHashGet(kid_link, "class"));
4361 if (!kid_idl) continue;
4363 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
4364 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
4367 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
4368 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
4371 osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
4373 if (link_map->size > 0) {
4374 jsonObject* _kid_key = jsonNewObjectType(JSON_ARRAY);
4377 jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
4382 osrfHashGet(kid_link, "class"),
4389 "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
4390 osrfHashGet(kid_link, "field"),
4391 osrfHashGet(kid_link, "class"),
4392 osrfHashGet(kid_link, "key"),
4393 osrfHashGet(kid_link, "reltype")
4396 jsonObject* fake_params = jsonNewObjectType(JSON_ARRAY);
4397 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH)); // search hash
4398 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH)); // order/flesh hash
4400 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
4402 const char* search_key = jsonObjectGetString(
4405 atoi( osrfHashGet(value_field, "array_position") )
4410 osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
4415 jsonObjectGetIndex(fake_params, 0),
4416 osrfHashGet(kid_link, "key"),
4417 jsonNewObject( search_key )
4421 jsonObjectGetIndex(fake_params, 1),
4423 jsonNewNumberObject( (double)(x - 1 + link_map->size) )
4427 jsonObjectSetKey( jsonObjectGetIndex(fake_params, 1), "flesh_fields", jsonObjectClone(flesh_blob) );
4429 if (jsonObjectGetKeyConst(order_hash, "order_by")) {
4431 jsonObjectGetIndex(fake_params, 1),
4433 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "order_by"))
4437 if (jsonObjectGetKeyConst(order_hash, "select")) {
4439 jsonObjectGetIndex(fake_params, 1),
4441 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "select"))
4445 jsonObject* kids = doFieldmapperSearch(ctx, kid_idl, fake_params, err);
4448 jsonObjectFree( fake_params );
4449 osrfStringArrayFree(link_fields);
4450 jsonIteratorFree(itr);
4451 jsonObjectFree(res_list);
4452 jsonObjectFree(flesh_blob);
4456 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
4458 jsonObject* X = NULL;
4459 if ( link_map->size > 0 && kids->size > 0 ) {
4461 kids = jsonNewObjectType(JSON_ARRAY);
4463 jsonObject* _k_node;
4464 jsonIterator* _k = jsonNewIterator( X );
4465 while ((_k_node = jsonIteratorNext( _k ))) {
4471 (unsigned long)atoi(
4477 osrfHashGet(kid_link, "class")
4481 osrfStringArrayGetString( link_map, 0 )
4490 jsonIteratorFree(_k);
4493 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" )) || !(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) {
4494 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
4497 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
4498 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
4502 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
4503 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
4506 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
4507 jsonObjectClone( kids )
4512 jsonObjectFree(kids);
4516 jsonObjectFree( kids );
4517 jsonObjectFree( fake_params );
4519 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
4520 osrfLogDebug(OSRF_LOG_MARK, "%s", jsonObjectToJSON(cur));
4524 jsonObjectFree( flesh_blob );
4525 osrfStringArrayFree(link_fields);
4526 jsonIteratorFree(itr);
4535 static jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
4537 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4539 jsonObject* target = jsonObjectGetIndex( ctx->params, 1 );
4541 jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
4544 if (!verifyObjectClass(ctx, target)) {
4549 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4550 osrfAppSessionStatus(
4552 OSRF_STATUS_BADREQUEST,
4553 "osrfMethodException",
4555 "No active transaction -- required for UPDATE"
4561 // The following test is harmless but redundant. If a class is
4562 // readonly, we don't register an update method for it.
4563 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4564 osrfAppSessionStatus(
4566 OSRF_STATUS_BADREQUEST,
4567 "osrfMethodException",
4569 "Cannot UPDATE readonly class"
4575 dbhandle = writehandle;
4577 char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
4579 // Set the last_xact_id
4580 int index = oilsIDL_ntop( target->classname, "last_xact_id" );
4582 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
4583 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
4586 char* pkey = osrfHashGet(meta, "primarykey");
4587 osrfHash* fields = osrfHashGet(meta, "fields");
4589 char* id = oilsFMGetString( target, pkey );
4593 "%s updating %s object with %s = %s",
4595 osrfHashGet(meta, "fieldmapper"),
4600 growing_buffer* sql = buffer_init(128);
4601 buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
4606 osrfStringArray* field_list = osrfHashKeys( fields );
4607 while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
4609 osrfHash* field = osrfHashGet( fields, field_name );
4611 if(!( strcmp( field_name, pkey ) )) continue;
4612 if( str_is_true( osrfHashGet(osrfHashGet(fields,field_name), "virtual") ) )
4615 const jsonObject* field_object = oilsFMGetObject( target, field_name );
4617 int value_is_numeric = 0; // boolean
4619 if (field_object && field_object->classname) {
4620 value = oilsFMGetString(
4622 (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
4625 value = jsonObjectToSimpleString( field_object );
4626 if( field_object && JSON_NUMBER == field_object->type )
4627 value_is_numeric = 1;
4630 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
4632 if (!field_object || field_object->type == JSON_NULL) {
4633 if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
4634 if (first) first = 0;
4635 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4636 buffer_fadd( sql, " %s = NULL", field_name );
4639 } else if ( value_is_numeric || !strcmp( get_primitive( field ), "number") ) {
4640 if (first) first = 0;
4641 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4643 const char* numtype = get_datatype( field );
4644 if ( !strncmp( numtype, "INT", 3 ) ) {
4645 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
4646 } else if ( !strcmp( numtype, "NUMERIC" ) ) {
4647 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
4649 // Must really be intended as a string, so quote it
4650 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4651 buffer_fadd( sql, " %s = %s", field_name, value );
4653 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4654 osrfAppSessionStatus(
4656 OSRF_STATUS_INTERNALSERVERERROR,
4657 "osrfMethodException",
4659 "Error quoting string -- please see the error log for more details"
4669 osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, numtype );
4672 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4673 if (first) first = 0;
4674 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4675 buffer_fadd( sql, " %s = %s", field_name, value );
4678 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4679 osrfAppSessionStatus(
4681 OSRF_STATUS_INTERNALSERVERERROR,
4682 "osrfMethodException",
4684 "Error quoting string -- please see the error log for more details"
4698 jsonObject* obj = jsonNewObject(id);
4700 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4701 dbi_conn_quote_string(dbhandle, &id);
4703 buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
4705 char* query = buffer_release(sql);
4706 osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
4708 dbi_result result = dbi_conn_query(dbhandle, query);
4712 jsonObjectFree(obj);
4713 obj = jsonNewObject(NULL);
4716 "%s ERROR updating %s object with %s = %s",
4718 osrfHashGet(meta, "fieldmapper"),
4729 static jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
4731 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4733 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4734 osrfAppSessionStatus(
4736 OSRF_STATUS_BADREQUEST,
4737 "osrfMethodException",
4739 "No active transaction -- required for DELETE"
4745 // The following test is harmless but redundant. If a class is
4746 // readonly, we don't register a delete method for it.
4747 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4748 osrfAppSessionStatus(
4750 OSRF_STATUS_BADREQUEST,
4751 "osrfMethodException",
4753 "Cannot DELETE readonly class"
4759 dbhandle = writehandle;
4763 char* pkey = osrfHashGet(meta, "primarykey");
4771 if (jsonObjectGetIndex(ctx->params, _obj_pos)->classname) {
4772 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, _obj_pos ))) {
4777 id = oilsFMGetString( jsonObjectGetIndex(ctx->params, _obj_pos), pkey );
4780 if (!verifyObjectPCRUD( ctx, NULL )) {
4785 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, _obj_pos));
4790 "%s deleting %s object with %s = %s",
4792 osrfHashGet(meta, "fieldmapper"),
4797 obj = jsonNewObject(id);
4799 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4800 dbi_conn_quote_string(writehandle, &id);
4802 dbi_result result = dbi_conn_queryf(writehandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
4805 jsonObjectFree(obj);
4806 obj = jsonNewObject(NULL);
4809 "%s ERROR deleting %s object with %s = %s",
4811 osrfHashGet(meta, "fieldmapper"),
4824 static jsonObject* oilsMakeFieldmapperFromResult( dbi_result result, osrfHash* meta) {
4825 if(!(result && meta)) return jsonNULL;
4827 jsonObject* object = jsonNewObject(NULL);
4828 jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
4830 osrfHash* fields = osrfHashGet(meta, "fields");
4832 osrfLogInternal(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
4836 char dt_string[256];
4840 int columnIndex = 1;
4842 unsigned short type;
4843 const char* columnName;
4845 /* cycle through the column list */
4846 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4848 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4850 fmIndex = -1; // reset the position
4852 /* determine the field type and storage attributes */
4853 type = dbi_result_get_field_type(result, columnName);
4854 attr = dbi_result_get_field_attribs(result, columnName);
4856 /* fetch the fieldmapper index */
4857 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
4859 if ( str_is_true( osrfHashGet(_f, "virtual") ) )
4862 const char* pos = (char*)osrfHashGet(_f, "array_position");
4863 if ( !pos ) continue;
4865 fmIndex = atoi( pos );
4866 osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
4871 if (dbi_result_field_is_null(result, columnName)) {
4872 jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
4877 case DBI_TYPE_INTEGER :
4879 if( attr & DBI_INTEGER_SIZE8 )
4880 jsonObjectSetIndex( object, fmIndex,
4881 jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
4883 jsonObjectSetIndex( object, fmIndex,
4884 jsonNewNumberObject(dbi_result_get_int(result, columnName)));
4888 case DBI_TYPE_DECIMAL :
4889 jsonObjectSetIndex( object, fmIndex,
4890 jsonNewNumberObject(dbi_result_get_double(result, columnName)));
4893 case DBI_TYPE_STRING :
4899 jsonNewObject( dbi_result_get_string(result, columnName) )
4904 case DBI_TYPE_DATETIME :
4906 memset(dt_string, '\0', sizeof(dt_string));
4907 memset(&gmdt, '\0', sizeof(gmdt));
4909 _tmp_dt = dbi_result_get_datetime(result, columnName);
4912 if (!(attr & DBI_DATETIME_DATE)) {
4913 gmtime_r( &_tmp_dt, &gmdt );
4914 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4915 } else if (!(attr & DBI_DATETIME_TIME)) {
4916 localtime_r( &_tmp_dt, &gmdt );
4917 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4919 localtime_r( &_tmp_dt, &gmdt );
4920 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4923 jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
4927 case DBI_TYPE_BINARY :
4928 osrfLogError( OSRF_LOG_MARK,
4929 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4937 static jsonObject* oilsMakeJSONFromResult( dbi_result result ) {
4938 if(!result) return jsonNULL;
4940 jsonObject* object = jsonNewObject(NULL);
4943 char dt_string[256];
4947 int columnIndex = 1;
4949 unsigned short type;
4950 const char* columnName;
4952 /* cycle through the column list */
4953 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4955 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4957 fmIndex = -1; // reset the position
4959 /* determine the field type and storage attributes */
4960 type = dbi_result_get_field_type(result, columnName);
4961 attr = dbi_result_get_field_attribs(result, columnName);
4963 if (dbi_result_field_is_null(result, columnName)) {
4964 jsonObjectSetKey( object, columnName, jsonNewObject(NULL) );
4969 case DBI_TYPE_INTEGER :
4971 if( attr & DBI_INTEGER_SIZE8 )
4972 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_longlong(result, columnName)) );
4974 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_int(result, columnName)) );
4977 case DBI_TYPE_DECIMAL :
4978 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_double(result, columnName)) );
4981 case DBI_TYPE_STRING :
4982 jsonObjectSetKey( object, columnName, jsonNewObject(dbi_result_get_string(result, columnName)) );
4985 case DBI_TYPE_DATETIME :
4987 memset(dt_string, '\0', sizeof(dt_string));
4988 memset(&gmdt, '\0', sizeof(gmdt));
4990 _tmp_dt = dbi_result_get_datetime(result, columnName);
4993 if (!(attr & DBI_DATETIME_DATE)) {
4994 gmtime_r( &_tmp_dt, &gmdt );
4995 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4996 } else if (!(attr & DBI_DATETIME_TIME)) {
4997 localtime_r( &_tmp_dt, &gmdt );
4998 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
5000 localtime_r( &_tmp_dt, &gmdt );
5001 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
5004 jsonObjectSetKey( object, columnName, jsonNewObject(dt_string) );
5007 case DBI_TYPE_BINARY :
5008 osrfLogError( OSRF_LOG_MARK,
5009 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
5017 // Interpret a string as true or false
5018 static int str_is_true( const char* str ) {
5019 if( NULL == str || strcasecmp( str, "true" ) )
5025 // Interpret a jsonObject as true or false
5026 static int obj_is_true( const jsonObject* obj ) {
5029 else switch( obj->type )
5037 if( strcasecmp( obj->value.s, "true" ) )
5041 case JSON_NUMBER : // Support 1/0 for perl's sake
5042 if( jsonObjectGetNumber( obj ) == 1.0 )
5051 // Translate a numeric code into a text string identifying a type of
5052 // jsonObject. To be used for building error messages.
5053 static const char* json_type( int code ) {
5059 return "JSON_ARRAY";
5061 return "JSON_STRING";
5063 return "JSON_NUMBER";
5069 return "(unrecognized)";
5073 // Extract the "primitive" attribute from an IDL field definition.
5074 // If we haven't initialized the app, then we must be running in
5075 // some kind of testbed. In that case, default to "string".
5076 static const char* get_primitive( osrfHash* field ) {
5077 const char* s = osrfHashGet( field, "primitive" );
5079 if( child_initialized )
5082 "%s ERROR No \"datatype\" attribute for field \"%s\"",
5084 osrfHashGet( field, "name" )
5092 // Extract the "datatype" attribute from an IDL field definition.
5093 // If we haven't initialized the app, then we must be running in
5094 // some kind of testbed. In that case, default to to NUMERIC,
5095 // since we look at the datatype only for numbers.
5096 static const char* get_datatype( osrfHash* field ) {
5097 const char* s = osrfHashGet( field, "datatype" );
5099 if( child_initialized )
5102 "%s ERROR No \"datatype\" attribute for field \"%s\"",
5104 osrfHashGet( field, "name" )
5113 If the input string is potentially a valid SQL identifier, return 1.
5116 Purpose: to prevent certain kinds of SQL injection. To that end we
5117 don't necessarily need to follow all the rules exactly, such as requiring
5118 that the first character not be a digit.
5120 We allow leading and trailing white space. In between, we do not allow
5121 punctuation (except for underscores and dollar signs), control
5122 characters, or embedded white space.
5124 More pedantically we should allow quoted identifiers containing arbitrary
5125 characters, but for the foreseeable future such quoted identifiers are not
5126 likely to be an issue.
5128 static int is_identifier( const char* s) {
5132 // Skip leading white space
5133 while( isspace( (unsigned char) *s ) )
5137 return 0; // Nothing but white space? Not okay.
5139 // Check each character until we reach white space or
5140 // end-of-string. Letters, digits, underscores, and
5141 // dollar signs are okay. With the exception of periods
5142 // (as in schema.identifier), control characters and other
5143 // punctuation characters are not okay. Anything else
5144 // is okay -- it could for example be part of a multibyte
5145 // UTF8 character such as a letter with diacritical marks,
5146 // and those are allowed.
5148 if( isalnum( (unsigned char) *s )
5152 ; // Fine; keep going
5153 else if( ispunct( (unsigned char) *s )
5154 || iscntrl( (unsigned char) *s ) )
5157 } while( *s && ! isspace( (unsigned char) *s ) );
5159 // If we found any white space in the above loop,
5160 // the rest had better be all white space.
5162 while( isspace( (unsigned char) *s ) )
5166 return 0; // White space was embedded within non-white space
5172 Determine whether to accept a character string as a comparison operator.
5173 Return 1 if it's good, or 0 if it's bad.
5175 We don't validate it for real. We just make sure that it doesn't contain
5176 any semicolons or white space (with a special exception for the
5177 "SIMILAR TO" operator). The idea is to block certain kinds of SQL
5178 injection. If it has no semicolons or white space but it's still not a
5179 valid operator, then the database will complain.
5181 Another approach would be to compare the string against a short list of
5182 approved operators. We don't do that because we want to allow custom
5183 operators like ">100*", which would be difficult or impossible to
5184 express otherwise in a JSON query.
5186 static int is_good_operator( const char* op ) {
5187 if( !op ) return 0; // Sanity check
5191 if( isspace( (unsigned char) *s ) ) {
5192 // Special exception for SIMILAR TO. Someday we might make
5193 // exceptions for IS DISTINCT FROM and IS NOT DISTINCT FROM.
5194 if( !strcasecmp( op, "similar to" ) )
5199 else if( ';' == *s )