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);
84 static jsonObject* verifyUserPCRUD( osrfMethodContext* );
85 static int verifyObjectPCRUD( osrfMethodContext*, const jsonObject* );
88 static int child_initialized = 0; /* boolean */
90 static dbi_conn writehandle; /* our MASTER db connection */
91 static dbi_conn dbhandle; /* our CURRENT db connection */
92 //static osrfHash * readHandles;
93 static jsonObject* jsonNULL = NULL; //
94 static int max_flesh_depth = 100;
96 /* called when this process is about to exit */
97 void osrfAppChildExit() {
98 osrfLogDebug(OSRF_LOG_MARK, "Child is exiting, disconnecting from database...");
101 if (writehandle == dbhandle) same = 1;
103 dbi_conn_query(writehandle, "ROLLBACK;");
104 dbi_conn_close(writehandle);
107 if (dbhandle && !same)
108 dbi_conn_close(dbhandle);
110 // XXX add cleanup of readHandles whenever that gets used
115 int osrfAppInitialize() {
117 osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
118 osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
120 if (!oilsIDLInit( osrf_settings_host_value("/IDL") )) return 1; /* return non-zero to indicate error */
122 growing_buffer* method_name = buffer_init(64);
124 // Generic search thingy
125 buffer_add(method_name, MODULENAME);
126 buffer_add(method_name, ".json_query");
127 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
128 "doJSONSearch", "", 1, OSRF_METHOD_STREAMING );
131 // first we register all the transaction and savepoint methods
132 buffer_reset(method_name);
133 OSRF_BUFFER_ADD(method_name, MODULENAME);
134 OSRF_BUFFER_ADD(method_name, ".transaction.begin");
135 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
136 "beginTransaction", "", 0, 0 );
138 buffer_reset(method_name);
139 OSRF_BUFFER_ADD(method_name, MODULENAME);
140 OSRF_BUFFER_ADD(method_name, ".transaction.commit");
141 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
142 "commitTransaction", "", 0, 0 );
144 buffer_reset(method_name);
145 OSRF_BUFFER_ADD(method_name, MODULENAME);
146 OSRF_BUFFER_ADD(method_name, ".transaction.rollback");
147 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
148 "rollbackTransaction", "", 0, 0 );
150 buffer_reset(method_name);
151 OSRF_BUFFER_ADD(method_name, MODULENAME);
152 OSRF_BUFFER_ADD(method_name, ".savepoint.set");
153 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
154 "setSavepoint", "", 1, 0 );
156 buffer_reset(method_name);
157 OSRF_BUFFER_ADD(method_name, MODULENAME);
158 OSRF_BUFFER_ADD(method_name, ".savepoint.release");
159 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
160 "releaseSavepoint", "", 1, 0 );
162 buffer_reset(method_name);
163 OSRF_BUFFER_ADD(method_name, MODULENAME);
164 OSRF_BUFFER_ADD(method_name, ".savepoint.rollback");
165 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
166 "rollbackSavepoint", "", 1, 0 );
168 buffer_free(method_name);
170 static const char* global_method[] = {
178 const int global_method_count
179 = sizeof( global_method ) / sizeof ( global_method[0] );
183 osrfStringArray* classes = osrfHashKeys( oilsIDL() );
184 osrfLogDebug(OSRF_LOG_MARK, "%d classes loaded", classes->size );
185 osrfLogDebug(OSRF_LOG_MARK,
186 "At least %d methods will be generated", classes->size * global_method_count);
188 while ( (classname = osrfStringArrayGetString(classes, c_index++)) ) {
189 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
191 osrfHash* idlClass = osrfHashGet(oilsIDL(), classname);
193 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), MODULENAME )) {
194 osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on", MODULENAME, classname);
198 if ( str_is_true( osrfHashGet(idlClass, "virtual") ) ) {
199 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
203 // Look up some other attributes of the current class
204 const char* idlClass_fieldmapper = osrfHashGet(idlClass, "fieldmapper");
205 const char* readonly = osrfHashGet(idlClass, "readonly");
207 osrfHash* idlClass_permacrud = osrfHashGet(idlClass, "permacrud");
211 for( i = 0; i < global_method_count; ++i ) {
212 const char* method_type = global_method[ i ];
213 osrfLogDebug(OSRF_LOG_MARK,
214 "Using files to build %s class methods for %s", method_type, classname);
216 if (!idlClass_fieldmapper) continue;
219 if (!idlClass_permacrud) continue;
221 const char* tmp_method = method_type;
222 if ( *tmp_method == 'i' || *tmp_method == 's') {
223 tmp_method = "retrieve";
225 if (!osrfHashGet( idlClass_permacrud, tmp_method )) continue;
228 if ( str_is_true( readonly ) &&
229 ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd')
232 osrfHash* method_meta = osrfNewHash();
233 osrfHashSet(method_meta, idlClass, "class");
235 method_name = buffer_init(64);
237 buffer_fadd(method_name, "%s.%s.%s", MODULENAME, method_type, classname);
241 char* _fm = strdup( idlClass_fieldmapper );
242 part = strtok_r(_fm, ":", &st_tmp);
244 buffer_fadd(method_name, "%s.direct.%s", MODULENAME, part);
246 while ((part = strtok_r(NULL, ":", &st_tmp))) {
247 OSRF_BUFFER_ADD_CHAR(method_name, '.');
248 OSRF_BUFFER_ADD(method_name, part);
250 OSRF_BUFFER_ADD_CHAR(method_name, '.');
251 OSRF_BUFFER_ADD(method_name, method_type);
255 char* method = buffer_release(method_name);
257 osrfHashSet( method_meta, method, "methodname" );
258 osrfHashSet( method_meta, strdup(method_type), "methodtype" );
261 if (*method_type == 'i' || *method_type == 's') {
262 flags = flags | OSRF_METHOD_STREAMING;
265 osrfAppRegisterExtendedMethod(
268 "dispatchCRUDMethod",
282 static char* getSourceDefinition( osrfHash* class ) {
284 char* tabledef = osrfHashGet(class, "tablename");
287 tabledef = strdup(tabledef);
289 tabledef = osrfHashGet(class, "source_definition");
291 growing_buffer* tablebuf = buffer_init(128);
292 buffer_fadd( tablebuf, "(%s)", tabledef );
293 tabledef = buffer_release(tablebuf);
295 const char* classname = osrfHashGet( class, "classname" );
300 "%s ERROR No tablename or source_definition for class \"%s\"",
311 * Connects to the database
313 int osrfAppChildInit() {
315 osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
316 dbi_initialize(NULL);
317 osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
319 char* driver = osrf_settings_host_value("/apps/%s/app_settings/driver", MODULENAME);
320 char* user = osrf_settings_host_value("/apps/%s/app_settings/database/user", MODULENAME);
321 char* host = osrf_settings_host_value("/apps/%s/app_settings/database/host", MODULENAME);
322 char* port = osrf_settings_host_value("/apps/%s/app_settings/database/port", MODULENAME);
323 char* db = osrf_settings_host_value("/apps/%s/app_settings/database/db", MODULENAME);
324 char* pw = osrf_settings_host_value("/apps/%s/app_settings/database/pw", MODULENAME);
325 char* md = osrf_settings_host_value("/apps/%s/app_settings/max_query_recursion", MODULENAME);
327 osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
328 writehandle = dbi_conn_new(driver);
331 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
334 osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
336 osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database. host=%s, "
337 "port=%s, user=%s, pw=%s, db=%s", MODULENAME, host, port, user, pw, db );
339 if(host) dbi_conn_set_option(writehandle, "host", host );
340 if(port) dbi_conn_set_option_numeric( writehandle, "port", atoi(port) );
341 if(user) dbi_conn_set_option(writehandle, "username", user);
342 if(pw) dbi_conn_set_option(writehandle, "password", pw );
343 if(db) dbi_conn_set_option(writehandle, "dbname", db );
345 if(md) max_flesh_depth = atoi(md);
346 if(max_flesh_depth < 0) max_flesh_depth = 1;
347 if(max_flesh_depth > 1000) max_flesh_depth = 1000;
356 if (dbi_conn_connect(writehandle) < 0) {
358 if (dbi_conn_connect(writehandle) < 0) {
359 dbi_conn_error(writehandle, &err);
360 osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
365 osrfLogInfo(OSRF_LOG_MARK, "%s successfully connected to the database", MODULENAME);
371 osrfStringArray* classes = osrfHashKeys( oilsIDL() );
373 while ( (classname = osrfStringArrayGetString(classes, i++)) ) {
374 osrfHash* class = osrfHashGet( oilsIDL(), classname );
375 osrfHash* fields = osrfHashGet( class, "fields" );
377 if( str_is_true( osrfHashGet(class, "virtual") ) ) {
378 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
382 char* tabledef = getSourceDefinition(class);
384 tabledef = strdup( "(null)" );
386 growing_buffer* sql_buf = buffer_init(32);
387 buffer_fadd( sql_buf, "SELECT * FROM %s AS x WHERE 1=0;", tabledef );
391 char* sql = buffer_release(sql_buf);
392 osrfLogDebug(OSRF_LOG_MARK, "%s Investigatory SQL = %s", MODULENAME, sql);
394 dbi_result result = dbi_conn_query(writehandle, sql);
400 const char* columnName;
402 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
404 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
406 /* fetch the fieldmapper index */
407 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
409 osrfLogDebug(OSRF_LOG_MARK, "Found [%s] in IDL hash...", (char*)columnName);
411 /* determine the field type and storage attributes */
412 type = dbi_result_get_field_type(result, columnName);
413 attr = dbi_result_get_field_attribs(result, columnName);
417 case DBI_TYPE_INTEGER :
419 if ( !osrfHashGet(_f, "primitive") )
420 osrfHashSet(_f,"number", "primitive");
422 if( attr & DBI_INTEGER_SIZE8 )
423 osrfHashSet(_f,"INT8", "datatype");
425 osrfHashSet(_f,"INT", "datatype");
428 case DBI_TYPE_DECIMAL :
429 if ( !osrfHashGet(_f, "primitive") )
430 osrfHashSet(_f,"number", "primitive");
432 osrfHashSet(_f,"NUMERIC", "datatype");
435 case DBI_TYPE_STRING :
436 if ( !osrfHashGet(_f, "primitive") )
437 osrfHashSet(_f,"string", "primitive");
438 osrfHashSet(_f,"TEXT", "datatype");
441 case DBI_TYPE_DATETIME :
442 if ( !osrfHashGet(_f, "primitive") )
443 osrfHashSet(_f,"string", "primitive");
445 osrfHashSet(_f,"TIMESTAMP", "datatype");
448 case DBI_TYPE_BINARY :
449 if ( !osrfHashGet(_f, "primitive") )
450 osrfHashSet(_f,"string", "primitive");
452 osrfHashSet(_f,"BYTEA", "datatype");
457 "Setting [%s] to primitive [%s] and datatype [%s]...",
459 osrfHashGet(_f, "primitive"),
460 osrfHashGet(_f, "datatype")
464 dbi_result_free(result);
466 osrfLogDebug(OSRF_LOG_MARK, "No data found for class [%s]...", (char*)classname);
470 osrfStringArrayFree(classes);
472 child_initialized = 1;
477 This function is a sleazy hack intended *only* for testing and
478 debugging. Any real server process should initialize the
479 database connection by calling osrfAppChildInit().
481 void set_cstore_dbi_conn( dbi_conn conn ) {
482 dbhandle = writehandle = conn;
485 void userDataFree( void* blob ) {
486 osrfHashFree( (osrfHash*)blob );
490 static void sessionDataFree( char* key, void* item ) {
491 if (!(strcmp(key,"xact_id"))) {
493 dbi_conn_query(writehandle, "ROLLBACK;");
500 int beginTransaction ( osrfMethodContext* ctx ) {
501 if(osrfMethodVerifyContext( ctx )) {
502 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
507 jsonObject* user = verifyUserPCRUD( ctx );
508 if (!user) return -1;
509 jsonObjectFree(user);
512 dbi_result result = dbi_conn_query(writehandle, "START TRANSACTION;");
514 osrfLogError(OSRF_LOG_MARK, "%s: Error starting transaction", MODULENAME );
515 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error starting transaction" );
518 jsonObject* ret = jsonNewObject(ctx->session->session_id);
519 osrfAppRespondComplete( ctx, ret );
522 if (!ctx->session->userData) {
523 ctx->session->userData = osrfNewHash();
524 osrfHashSetCallback((osrfHash*)ctx->session->userData, &sessionDataFree);
527 osrfHashSet( (osrfHash*)ctx->session->userData, strdup( ctx->session->session_id ), "xact_id" );
528 ctx->session->userDataFree = &userDataFree;
534 int setSavepoint ( osrfMethodContext* ctx ) {
535 if(osrfMethodVerifyContext( ctx )) {
536 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
543 jsonObject* user = verifyUserPCRUD( ctx );
544 if (!user) return -1;
545 jsonObjectFree(user);
548 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
549 osrfAppSessionStatus(
551 OSRF_STATUS_INTERNALSERVERERROR,
552 "osrfMethodException",
554 "No active transaction -- required for savepoints"
559 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) buffer_fadd( val_buf, "%ld", (long)jsonObjectGetNumber(value) );
1622 const char* val_str = jsonObjectGetString( value );
1623 buffer_fadd( val_buf, "%ld", atol(val_str) );
1626 } else if ( !strcmp( numtype, "NUMERIC" ) ) {
1627 if (value->type == JSON_NUMBER) buffer_fadd( val_buf, "%f", jsonObjectGetNumber(value) );
1629 const char* val_str = jsonObjectGetString( value );
1630 buffer_fadd( val_buf, "%f", atof(val_str) );
1634 // Presumably this was really intended ot be a string, so quote it
1635 char* str = jsonObjectToSimpleString( value );
1636 if ( dbi_conn_quote_string(dbhandle, &str) ) {
1637 OSRF_BUFFER_ADD( val_buf, str );
1640 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, str);
1642 buffer_free(val_buf);
1647 return buffer_release(val_buf);
1650 static char* searchINPredicate (const char* class, osrfHash* field,
1651 jsonObject* node, const char* op, osrfMethodContext* ctx ) {
1652 growing_buffer* sql_buf = buffer_init(32);
1658 osrfHashGet(field, "name")
1662 buffer_add(sql_buf, "IN (");
1663 } else if (!(strcasecmp(op,"not in"))) {
1664 buffer_add(sql_buf, "NOT IN (");
1666 buffer_add(sql_buf, "IN (");
1669 if (node->type == JSON_HASH) {
1670 // subquery predicate
1671 char* subpred = SELECT(
1673 jsonObjectGetKey( node, "select" ),
1674 jsonObjectGetKey( node, "from" ),
1675 jsonObjectGetKey( node, "where" ),
1676 jsonObjectGetKey( node, "having" ),
1677 jsonObjectGetKey( node, "order_by" ),
1678 jsonObjectGetKey( node, "limit" ),
1679 jsonObjectGetKey( node, "offset" ),
1683 buffer_add(sql_buf, subpred);
1686 } else if (node->type == JSON_ARRAY) {
1687 // literal value list
1688 int in_item_index = 0;
1689 int in_item_first = 1;
1690 const jsonObject* in_item;
1691 while ( (in_item = jsonObjectGetIndex(node, in_item_index++)) ) {
1696 buffer_add(sql_buf, ", ");
1698 // Append the literal value -- quoted if not a number
1699 if ( JSON_NUMBER == in_item->type ) {
1700 char* val = jsonNumberToDBString( field, in_item );
1701 OSRF_BUFFER_ADD( sql_buf, val );
1704 } else if ( !strcmp( get_primitive( field ), "number") ) {
1705 char* val = jsonNumberToDBString( field, in_item );
1706 OSRF_BUFFER_ADD( sql_buf, val );
1710 char* key_string = jsonObjectToSimpleString(in_item);
1711 if ( dbi_conn_quote_string(dbhandle, &key_string) ) {
1712 OSRF_BUFFER_ADD( sql_buf, key_string );
1715 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string);
1717 buffer_free(sql_buf);
1723 if( in_item_first ) {
1724 osrfLogError(OSRF_LOG_MARK, "%s: Empty IN list", MODULENAME );
1725 buffer_free( sql_buf );
1730 OSRF_BUFFER_ADD_CHAR( sql_buf, ')' );
1732 return buffer_release(sql_buf);
1735 // Receive a JSON_ARRAY representing a function call. The first
1736 // entry in the array is the function name. The rest are parameters.
1737 static char* searchValueTransform( const jsonObject* array ) {
1738 growing_buffer* sql_buf = buffer_init(32);
1740 jsonObject* func_item;
1742 // Get the function name
1743 if( array->size > 0 ) {
1744 func_item = jsonObjectGetIndex( array, 0 );
1745 OSRF_BUFFER_ADD( sql_buf, jsonObjectGetString( func_item ) );
1746 OSRF_BUFFER_ADD( sql_buf, "( " );
1749 // Get the parameters
1750 int func_item_index = 1; // We already grabbed the zeroth entry
1751 while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1753 // Add a separator comma, if we need one
1754 if( func_item_index > 2 )
1755 buffer_add( sql_buf, ", " );
1757 // Add the current parameter
1758 if (func_item->type == JSON_NULL) {
1759 buffer_add( sql_buf, "NULL" );
1761 char* val = jsonObjectToSimpleString(func_item);
1762 if ( dbi_conn_quote_string(dbhandle, &val) ) {
1763 OSRF_BUFFER_ADD( sql_buf, val );
1766 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1767 buffer_free(sql_buf);
1774 buffer_add( sql_buf, " )" );
1776 return buffer_release(sql_buf);
1779 static char* searchFunctionPredicate (const char* class, osrfHash* field,
1780 const jsonObject* node, const char* node_key) {
1782 char* val = searchValueTransform(node);
1786 growing_buffer* sql_buf = buffer_init(32);
1791 osrfHashGet(field, "name"),
1798 return buffer_release(sql_buf);
1801 // class is a class name
1802 // field is a field definition as stored in the IDL
1803 // node comes from the method parameter, and represents an entry in the SELECT list
1804 static char* searchFieldTransform (const char* class, osrfHash* field, const jsonObject* node) {
1805 growing_buffer* sql_buf = buffer_init(32);
1807 const char* field_transform = jsonObjectGetString( jsonObjectGetKeyConst( node, "transform" ) );
1808 const char* transform_subcolumn = jsonObjectGetString( jsonObjectGetKeyConst( node, "result_field" ) );
1810 if(transform_subcolumn)
1811 OSRF_BUFFER_ADD_CHAR( sql_buf, '(' ); // enclose transform in parentheses
1813 if (field_transform) {
1814 buffer_fadd( sql_buf, "%s(\"%s\".%s", field_transform, class, osrfHashGet(field, "name"));
1815 const jsonObject* array = jsonObjectGetKeyConst( node, "params" );
1818 if( array->type != JSON_ARRAY ) {
1819 osrfLogError( OSRF_LOG_MARK,
1820 "%s: Expected JSON_ARRAY for function params; found %s",
1821 MODULENAME, json_type( array->type ) );
1822 buffer_free( sql_buf );
1825 int func_item_index = 0;
1826 jsonObject* func_item;
1827 while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1829 char* val = jsonObjectToSimpleString(func_item);
1832 buffer_add( sql_buf, ",NULL" );
1833 } else if ( dbi_conn_quote_string(dbhandle, &val) ) {
1834 OSRF_BUFFER_ADD_CHAR( sql_buf, ',' );
1835 OSRF_BUFFER_ADD( sql_buf, val );
1837 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1839 buffer_free(sql_buf);
1846 buffer_add( sql_buf, " )" );
1849 buffer_fadd( sql_buf, "\"%s\".%s", class, osrfHashGet(field, "name"));
1852 if (transform_subcolumn)
1853 buffer_fadd( sql_buf, ").\"%s\"", transform_subcolumn );
1855 return buffer_release(sql_buf);
1858 static char* searchFieldTransformPredicate (const char* class, osrfHash* field,
1859 const jsonObject* node, const char* node_key) {
1860 char* field_transform = searchFieldTransform( class, field, node );
1863 const jsonObject* value_obj = jsonObjectGetKeyConst( node, "value" );
1864 if ( ! value_obj ) {
1865 value = searchWHERE( node, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN, NULL );
1867 osrfLogError(OSRF_LOG_MARK, "%s: Error building condition for field transform", MODULENAME, value);
1868 free(field_transform);
1871 } else if ( value_obj->type == JSON_ARRAY ) {
1872 value = searchValueTransform( value_obj );
1874 osrfLogError(OSRF_LOG_MARK, "%s: Error building value transform for field transform",
1876 free( field_transform );
1879 } else if ( value_obj->type == JSON_HASH ) {
1880 value = searchWHERE( value_obj, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN, NULL );
1882 osrfLogError(OSRF_LOG_MARK, "%s: Error building predicate for field transform", MODULENAME, value);
1883 free(field_transform);
1886 } else if ( value_obj->type == JSON_NUMBER ) {
1887 value = jsonNumberToDBString( field, value_obj );
1888 } else if ( value_obj->type != JSON_NULL ) {
1889 if ( !strcmp( get_primitive( field ), "number") ) {
1890 value = jsonNumberToDBString( field, value_obj );
1892 value = jsonObjectToSimpleString( value_obj );
1893 if ( !dbi_conn_quote_string(dbhandle, &value) ) {
1894 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, value);
1896 free(field_transform);
1902 growing_buffer* sql_buf = buffer_init(32);
1913 free(field_transform);
1915 return buffer_release(sql_buf);
1918 static char* searchSimplePredicate (const char* op, const char* class,
1919 osrfHash* field, const jsonObject* node) {
1923 // Get the value to which we are comparing the specified column
1924 if (node->type != JSON_NULL) {
1925 if ( node->type == JSON_NUMBER ) {
1926 val = jsonNumberToDBString( field, node );
1927 } else if ( !strcmp( get_primitive( field ), "number" ) ) {
1928 val = jsonNumberToDBString( field, node );
1930 val = jsonObjectToSimpleString(node);
1935 if( JSON_NUMBER != node->type && strcmp( get_primitive( field ), "number") ) {
1936 // Value is not numeric; enclose it in quotes
1937 if ( !dbi_conn_quote_string( dbhandle, &val ) ) {
1938 osrfLogError( OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val );
1944 // Compare to a null value
1945 val = strdup( "NULL" );
1946 if (strcmp( op, "=" ))
1952 growing_buffer* sql_buf = buffer_init(32);
1953 buffer_fadd( sql_buf, "\"%s\".%s %s %s", class, osrfHashGet(field, "name"), op, val );
1954 char* pred = buffer_release( sql_buf );
1961 static char* searchBETWEENPredicate (const char* class, osrfHash* field, const jsonObject* node) {
1966 if ( !strcmp( get_primitive( field ), "number") ) {
1967 x_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,0));
1968 y_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,1));
1971 x_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,0));
1972 y_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,1));
1973 if ( !(dbi_conn_quote_string(dbhandle, &x_string) && dbi_conn_quote_string(dbhandle, &y_string)) ) {
1974 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key strings [%s] and [%s]", MODULENAME, x_string, y_string);
1981 growing_buffer* sql_buf = buffer_init(32);
1982 buffer_fadd( sql_buf, "%s BETWEEN %s AND %s", osrfHashGet(field, "name"), x_string, y_string );
1986 return buffer_release(sql_buf);
1989 static char* searchPredicate ( const char* class, osrfHash* field,
1990 jsonObject* node, osrfMethodContext* ctx ) {
1993 if (node->type == JSON_ARRAY) { // equality IN search
1994 pred = searchINPredicate( class, field, node, NULL, ctx );
1995 } else if (node->type == JSON_HASH) { // non-equality search
1996 jsonIterator* pred_itr = jsonNewIterator( node );
1997 if( !jsonIteratorHasNext( pred_itr ) ) {
1998 osrfLogError( OSRF_LOG_MARK, "%s: Empty predicate for field \"%s\"",
1999 MODULENAME, osrfHashGet(field, "name") );
2001 jsonObject* pred_node = jsonIteratorNext( pred_itr );
2002 if ( !(strcasecmp( pred_itr->key,"between" )) )
2003 pred = searchBETWEENPredicate( class, field, pred_node );
2004 else if ( !(strcasecmp( pred_itr->key,"in" )) || !(strcasecmp( pred_itr->key,"not in" )) )
2005 pred = searchINPredicate( class, field, pred_node, pred_itr->key, ctx );
2006 else if ( pred_node->type == JSON_ARRAY )
2007 pred = searchFunctionPredicate( class, field, pred_node, pred_itr->key );
2008 else if ( pred_node->type == JSON_HASH )
2009 pred = searchFieldTransformPredicate( class, field, pred_node, pred_itr->key );
2011 pred = searchSimplePredicate( pred_itr->key, class, field, pred_node );
2013 jsonIteratorFree(pred_itr);
2015 } else if (node->type == JSON_NULL) { // IS NULL search
2016 growing_buffer* _p = buffer_init(64);
2019 "\"%s\".%s IS NULL",
2021 osrfHashGet(field, "name")
2023 pred = buffer_release(_p);
2024 } else { // equality search
2025 pred = searchSimplePredicate( "=", class, field, node );
2044 field : call_number,
2060 static char* searchJOIN ( const jsonObject* join_hash, osrfHash* leftmeta ) {
2062 const jsonObject* working_hash;
2063 jsonObject* freeable_hash = NULL;
2065 if (join_hash->type == JSON_STRING) {
2066 // create a wrapper around a copy of the original
2067 const char* _tmp = jsonObjectGetString( join_hash );
2068 freeable_hash = jsonNewObjectType(JSON_HASH);
2069 jsonObjectSetKey(freeable_hash, _tmp, NULL);
2070 working_hash = freeable_hash;
2073 if( join_hash->type != JSON_HASH ) {
2076 "%s: JOIN failed; expected JSON object type not found",
2081 working_hash = join_hash;
2084 growing_buffer* join_buf = buffer_init(128);
2085 const char* leftclass = osrfHashGet(leftmeta, "classname");
2087 jsonObject* snode = NULL;
2088 jsonIterator* search_itr = jsonNewIterator( working_hash );
2090 while ( (snode = jsonIteratorNext( search_itr )) ) {
2091 const char* class = search_itr->key;
2092 osrfHash* idlClass = osrfHashGet( oilsIDL(), class );
2096 "%s: JOIN failed. No class \"%s\" defined in IDL",
2100 jsonIteratorFree( search_itr );
2101 buffer_free( join_buf );
2103 jsonObjectFree( freeable_hash );
2107 const char* fkey = jsonObjectGetString( jsonObjectGetKeyConst( snode, "fkey" ) );
2108 const char* field = jsonObjectGetString( jsonObjectGetKeyConst( snode, "field" ) );
2110 if (field && !fkey) {
2111 fkey = (const char*)oilsIDLFindPath("/%s/links/%s/key", class, field);
2115 "%s: JOIN failed. No link defined from %s.%s to %s",
2121 buffer_free(join_buf);
2123 jsonObjectFree(freeable_hash);
2124 jsonIteratorFree(search_itr);
2128 } else if (!field && fkey) {
2129 field = (const char*)oilsIDLFindPath("/%s/links/%s/key", leftclass, fkey );
2133 "%s: JOIN failed. No link defined from %s.%s to %s",
2139 buffer_free(join_buf);
2141 jsonObjectFree(freeable_hash);
2142 jsonIteratorFree(search_itr);
2147 } else if (!field && !fkey) {
2148 osrfHash* _links = oilsIDL_links( leftclass );
2150 // For each link defined for the left class:
2151 // see if the link references the joined class
2152 osrfHashIterator* itr = osrfNewHashIterator( _links );
2153 osrfHash* curr_link = NULL;
2154 while( (curr_link = osrfHashIteratorNext( itr ) ) ) {
2155 const char* other_class = osrfHashGet( curr_link, "class" );
2156 if( other_class && !strcmp( other_class, class ) ) {
2158 // Found a link between the classes
2159 fkey = osrfHashIteratorKey( itr );
2160 field = osrfHashGet( curr_link, "key" );
2164 osrfHashIteratorFree( itr );
2166 if (!field || !fkey) {
2167 // Do another such search, with the classes reversed
2168 _links = oilsIDL_links( class );
2170 // For each link defined for the joined class:
2171 // see if the link references the left class
2172 osrfHashIterator* itr = osrfNewHashIterator( _links );
2173 osrfHash* curr_link = NULL;
2174 while( (curr_link = osrfHashIteratorNext( itr ) ) ) {
2175 const char* other_class = osrfHashGet( curr_link, "class" );
2176 if( other_class && !strcmp( other_class, leftclass ) ) {
2178 // Found a link between the classes
2179 fkey = osrfHashIteratorKey( itr );
2180 field = osrfHashGet( curr_link, "key" );
2184 osrfHashIteratorFree( itr );
2187 if (!field || !fkey) {
2190 "%s: JOIN failed. No link defined between %s and %s",
2195 buffer_free(join_buf);
2197 jsonObjectFree(freeable_hash);
2198 jsonIteratorFree(search_itr);
2204 const char* type = jsonObjectGetString( jsonObjectGetKeyConst( snode, "type" ) );
2206 if ( !strcasecmp(type,"left") ) {
2207 buffer_add(join_buf, " LEFT JOIN");
2208 } else if ( !strcasecmp(type,"right") ) {
2209 buffer_add(join_buf, " RIGHT JOIN");
2210 } else if ( !strcasecmp(type,"full") ) {
2211 buffer_add(join_buf, " FULL JOIN");
2213 buffer_add(join_buf, " INNER JOIN");
2216 buffer_add(join_buf, " INNER JOIN");
2219 char* table = getSourceDefinition(idlClass);
2221 jsonIteratorFree( search_itr );
2222 buffer_free( join_buf );
2224 jsonObjectFree( freeable_hash );
2228 buffer_fadd(join_buf, " %s AS \"%s\" ON ( \"%s\".%s = \"%s\".%s",
2229 table, class, class, field, leftclass, fkey);
2232 const jsonObject* filter = jsonObjectGetKeyConst( snode, "filter" );
2234 const char* filter_op = jsonObjectGetString( jsonObjectGetKeyConst( snode, "filter_op" ) );
2235 if ( filter_op && !strcasecmp("or",filter_op) ) {
2236 buffer_add( join_buf, " OR " );
2238 buffer_add( join_buf, " AND " );
2241 char* jpred = searchWHERE( filter, idlClass, AND_OP_JOIN, NULL );
2243 OSRF_BUFFER_ADD_CHAR( join_buf, ' ' );
2244 OSRF_BUFFER_ADD( join_buf, jpred );
2249 "%s: JOIN failed. Invalid conditional expression.",
2252 jsonIteratorFree( search_itr );
2253 buffer_free( join_buf );
2255 jsonObjectFree( freeable_hash );
2260 buffer_add(join_buf, " ) ");
2262 const jsonObject* join_filter = jsonObjectGetKeyConst( snode, "join" );
2264 char* jpred = searchJOIN( join_filter, idlClass );
2265 OSRF_BUFFER_ADD_CHAR( join_buf, ' ' );
2266 OSRF_BUFFER_ADD( join_buf, jpred );
2272 jsonObjectFree(freeable_hash);
2273 jsonIteratorFree(search_itr);
2275 return buffer_release(join_buf);
2280 { +class : { -or|-and : { field : { op : value }, ... } ... }, ... }
2281 { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }
2282 [ { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }, ... ]
2284 Generate code to express a set of conditions, as for a WHERE clause. Parameters:
2286 search_hash is the JSON expression of the conditions.
2287 meta is the class definition from the IDL, for the relevant table.
2288 opjoin_type indicates whether multiple conditions, if present, should be
2289 connected by AND or OR.
2290 osrfMethodContext is loaded with all sorts of stuff, but all we do with it here is
2291 to pass it to other functions -- and all they do with it is to use the session
2292 and request members to send error messages back to the client.
2296 static char* searchWHERE ( const jsonObject* search_hash, osrfHash* meta, int opjoin_type, osrfMethodContext* ctx ) {
2300 "%s: Entering searchWHERE; search_hash addr = %p, meta addr = %p, opjoin_type = %d, ctx addr = %p",
2308 growing_buffer* sql_buf = buffer_init(128);
2310 jsonObject* node = NULL;
2313 if ( search_hash->type == JSON_ARRAY ) {
2314 osrfLogDebug(OSRF_LOG_MARK, "%s: In WHERE clause, condition type is JSON_ARRAY", MODULENAME);
2315 jsonIterator* search_itr = jsonNewIterator( search_hash );
2316 if( !jsonIteratorHasNext( search_itr ) ) {
2319 "%s: Invalid predicate structure: empty JSON array",
2322 jsonIteratorFree( search_itr );
2323 buffer_free( sql_buf );
2327 while ( (node = jsonIteratorNext( search_itr )) ) {
2331 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
2332 else buffer_add(sql_buf, " AND ");
2335 char* subpred = searchWHERE( node, meta, opjoin_type, ctx );
2337 buffer_fadd(sql_buf, "( %s )", subpred);
2340 jsonIteratorFree( search_itr );
2341 buffer_free( sql_buf );
2345 jsonIteratorFree(search_itr);
2347 } else if ( search_hash->type == JSON_HASH ) {
2348 osrfLogDebug(OSRF_LOG_MARK, "%s: In WHERE clause, condition type is JSON_HASH", MODULENAME);
2349 jsonIterator* search_itr = jsonNewIterator( search_hash );
2350 if( !jsonIteratorHasNext( search_itr ) ) {
2353 "%s: Invalid predicate structure: empty JSON object",
2356 jsonIteratorFree( search_itr );
2357 buffer_free( sql_buf );
2361 while ( (node = jsonIteratorNext( search_itr )) ) {
2366 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
2367 else buffer_add(sql_buf, " AND ");
2370 if ( '+' == search_itr->key[ 0 ] ) {
2371 if ( node->type == JSON_STRING ) {
2372 // Intended purpose; to allow reference to a Boolean column
2374 // Verify that the class alias is not empty
2375 if( '\0' == search_itr->key[ 1 ] ) {
2378 "%s: Table alias is empty",
2381 jsonIteratorFree( search_itr );
2382 buffer_free( sql_buf );
2386 // Verify that the string looks like an identifier.
2387 const char* subpred = jsonObjectGetString( node );
2388 if( ! is_identifier( subpred ) ) {
2391 "%s: Invalid boolean identifier in WHERE clause: \"%s\"",
2395 jsonIteratorFree( search_itr );
2396 buffer_free( sql_buf );
2400 buffer_fadd(sql_buf, " \"%s\".%s ", search_itr->key + 1, subpred);
2402 char* subpred = searchWHERE( node, osrfHashGet( oilsIDL(), search_itr->key + 1 ), AND_OP_JOIN, ctx );
2404 buffer_fadd(sql_buf, "( %s )", subpred);
2407 jsonIteratorFree( search_itr );
2408 buffer_free( sql_buf );
2412 } else if ( !strcasecmp("-or",search_itr->key) ) {
2413 char* subpred = searchWHERE( node, meta, OR_OP_JOIN, ctx );
2415 buffer_fadd(sql_buf, "( %s )", subpred);
2418 buffer_free( sql_buf );
2421 } else if ( !strcasecmp("-and",search_itr->key) ) {
2422 char* subpred = searchWHERE( node, meta, AND_OP_JOIN, ctx );
2424 buffer_fadd(sql_buf, "( %s )", subpred);
2427 buffer_free( sql_buf );
2430 } else if ( !strcasecmp("-not",search_itr->key) ) {
2431 char* subpred = searchWHERE( node, meta, AND_OP_JOIN, ctx );
2433 buffer_fadd(sql_buf, " NOT ( %s )", subpred);
2436 buffer_free( sql_buf );
2439 } else if ( !strcasecmp("-exists",search_itr->key) ) {
2440 char* subpred = SELECT(
2442 jsonObjectGetKey( node, "select" ),
2443 jsonObjectGetKey( node, "from" ),
2444 jsonObjectGetKey( node, "where" ),
2445 jsonObjectGetKey( node, "having" ),
2446 jsonObjectGetKey( node, "order_by" ),
2447 jsonObjectGetKey( node, "limit" ),
2448 jsonObjectGetKey( node, "offset" ),
2452 buffer_fadd(sql_buf, "EXISTS ( %s )", subpred);
2454 } else if ( !strcasecmp("-not-exists",search_itr->key) ) {
2455 char* subpred = SELECT(
2457 jsonObjectGetKey( node, "select" ),
2458 jsonObjectGetKey( node, "from" ),
2459 jsonObjectGetKey( node, "where" ),
2460 jsonObjectGetKey( node, "having" ),
2461 jsonObjectGetKey( node, "order_by" ),
2462 jsonObjectGetKey( node, "limit" ),
2463 jsonObjectGetKey( node, "offset" ),
2467 buffer_fadd(sql_buf, "NOT EXISTS ( %s )", subpred);
2471 char* class = osrfHashGet(meta, "classname");
2472 osrfHash* fields = osrfHashGet(meta, "fields");
2473 osrfHash* field = osrfHashGet( fields, search_itr->key );
2477 char* table = getSourceDefinition(meta);
2479 table = strdup( "(?)" );
2482 "%s: Attempt to reference non-existent column %s on %s (%s)",
2488 buffer_free(sql_buf);
2490 jsonIteratorFree(search_itr);
2494 char* subpred = searchPredicate( class, field, node, ctx );
2496 buffer_add( sql_buf, subpred );
2499 buffer_free(sql_buf);
2500 jsonIteratorFree(search_itr);
2505 jsonIteratorFree(search_itr);
2508 // ERROR ... only hash and array allowed at this level
2509 char* predicate_string = jsonObjectToJSON( search_hash );
2512 "%s: Invalid predicate structure: %s",
2516 buffer_free(sql_buf);
2517 free(predicate_string);
2521 return buffer_release(sql_buf);
2525 /* method context */ osrfMethodContext* ctx,
2527 /* SELECT */ jsonObject* selhash,
2528 /* FROM */ jsonObject* join_hash,
2529 /* WHERE */ jsonObject* search_hash,
2530 /* HAVING */ jsonObject* having_hash,
2531 /* ORDER BY */ jsonObject* order_hash,
2532 /* LIMIT */ jsonObject* limit,
2533 /* OFFSET */ jsonObject* offset,
2534 /* flags */ int flags
2536 const char* locale = osrf_message_get_last_locale();
2538 // in case we don't get a select list
2539 jsonObject* defaultselhash = NULL;
2541 // general tmp objects
2542 const jsonObject* tmp_const;
2543 jsonObject* selclass = NULL;
2544 jsonObject* selfield = NULL;
2545 jsonObject* snode = NULL;
2546 jsonObject* onode = NULL;
2548 char* string = NULL;
2549 int from_function = 0;
2554 // the core search class
2555 char* core_class = NULL;
2557 // metadata about the core search class
2558 osrfHash* core_meta = NULL;
2560 // punt if there's no core class
2561 if (!join_hash || ( join_hash->type == JSON_HASH && !join_hash->size )) {
2564 "%s: FROM clause is missing or empty",
2568 osrfAppSessionStatus(
2570 OSRF_STATUS_INTERNALSERVERERROR,
2571 "osrfMethodException",
2573 "FROM clause is missing or empty in JSON query"
2578 // get the core class -- the only key of the top level FROM clause, or a string
2579 if (join_hash->type == JSON_HASH) {
2580 jsonIterator* tmp_itr = jsonNewIterator( join_hash );
2581 snode = jsonIteratorNext( tmp_itr );
2583 core_class = strdup( tmp_itr->key );
2586 jsonObject* extra = jsonIteratorNext( tmp_itr );
2588 jsonIteratorFree( tmp_itr );
2591 // There shouldn't be more than one entry in join_hash
2595 "%s: Malformed FROM clause: extra entry in JSON_HASH",
2599 osrfAppSessionStatus(
2601 OSRF_STATUS_INTERNALSERVERERROR,
2602 "osrfMethodException",
2604 "Malformed FROM clause in JSON query"
2607 return NULL; // Malformed join_hash; extra entry
2609 } else if (join_hash->type == JSON_ARRAY) {
2611 core_class = jsonObjectToSimpleString( jsonObjectGetIndex(join_hash, 0) );
2614 } else if (join_hash->type == JSON_STRING) {
2615 core_class = jsonObjectToSimpleString( join_hash );
2621 "%s: FROM clause is unexpected JSON type: %s",
2623 json_type( join_hash->type )
2626 osrfAppSessionStatus(
2628 OSRF_STATUS_INTERNALSERVERERROR,
2629 "osrfMethodException",
2631 "Ill-formed FROM clause in JSON query"
2637 if (!from_function) {
2638 // Get the IDL class definition for the core class
2639 core_meta = osrfHashGet( oilsIDL(), core_class );
2640 if( !core_meta ) { // Didn't find it?
2643 "%s: SELECT clause references undefined class: \"%s\"",
2648 osrfAppSessionStatus(
2650 OSRF_STATUS_INTERNALSERVERERROR,
2651 "osrfMethodException",
2653 "SELECT clause references undefined class in JSON query"
2659 // Make sure the class isn't virtual
2660 if( str_is_true( osrfHashGet( core_meta, "virtual" ) ) ) {
2663 "%s: Core class is virtual: \"%s\"",
2668 osrfAppSessionStatus(
2670 OSRF_STATUS_INTERNALSERVERERROR,
2671 "osrfMethodException",
2673 "FROM clause references virtual class in JSON query"
2680 // if the select list is empty, or the core class field list is '*',
2681 // build the default select list ...
2683 selhash = defaultselhash = jsonNewObjectType(JSON_HASH);
2684 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2685 } else if( selhash->type != JSON_HASH ) {
2688 "%s: Expected JSON_HASH for SELECT clause; found %s",
2690 json_type( selhash->type )
2694 osrfAppSessionStatus(
2696 OSRF_STATUS_INTERNALSERVERERROR,
2697 "osrfMethodException",
2699 "Malformed SELECT clause in JSON query"
2703 } else if ( (tmp_const = jsonObjectGetKeyConst( selhash, core_class )) && tmp_const->type == JSON_STRING ) {
2704 const char* _x = jsonObjectGetString( tmp_const );
2705 if (!strncmp( "*", _x, 1 )) {
2706 jsonObjectRemoveKey( selhash, core_class );
2707 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2712 growing_buffer* sql_buf = buffer_init(128);
2714 // temp buffer for the SELECT list
2715 growing_buffer* select_buf = buffer_init(128);
2716 growing_buffer* order_buf = buffer_init(128);
2717 growing_buffer* group_buf = buffer_init(128);
2718 growing_buffer* having_buf = buffer_init(128);
2720 int aggregate_found = 0; // boolean
2722 // Build a select list
2723 if(from_function) // From a function we select everything
2724 OSRF_BUFFER_ADD_CHAR( select_buf, '*' );
2727 // If we need to build a default list, prepare to do so
2728 jsonObject* _tmp = jsonObjectGetKey( selhash, core_class );
2729 if ( _tmp && !_tmp->size ) {
2731 osrfHash* core_fields = osrfHashGet( core_meta, "fields" );
2733 osrfHashIterator* field_itr = osrfNewHashIterator( core_fields );
2734 osrfHash* field_def;
2735 while( ( field_def = osrfHashIteratorNext( field_itr ) ) ) {
2736 if( ! str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2737 // This field is not virtual, so add it to the list
2738 jsonObjectPush( _tmp, jsonNewObject( osrfHashIteratorKey( field_itr ) ) );
2741 osrfHashIteratorFree( field_itr );
2744 // Now build the actual select list
2748 jsonIterator* selclass_itr = jsonNewIterator( selhash );
2749 while ( (selclass = jsonIteratorNext( selclass_itr )) ) { // For each class
2751 // Make sure the class is defined in the IDL
2752 const char* cname = selclass_itr->key;
2753 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
2757 "%s: Selected class \"%s\" not defined in IDL",
2763 osrfAppSessionStatus(
2765 OSRF_STATUS_INTERNALSERVERERROR,
2766 "osrfMethodException",
2768 "Selected class is not defined"
2770 jsonIteratorFree( selclass_itr );
2771 buffer_free( sql_buf );
2772 buffer_free( select_buf );
2773 buffer_free( order_buf );
2774 buffer_free( group_buf );
2775 buffer_free( having_buf );
2776 if( defaultselhash ) jsonObjectFree( defaultselhash );
2781 // Make sure the target relation is in the join tree.
2783 // At this point join_hash is a step down from the join_hash we
2784 // received as a parameter. If the original was a JSON_STRING,
2785 // then json_hash is now NULL. If the original was a JSON_HASH,
2786 // then json_hash is now the first (and only) entry in it,
2787 // denoting the core class. We've already excluded the
2788 // possibility that the original was a JSON_ARRAY, because in
2789 // that case from_function would be non-NULL, and we wouldn't
2792 int class_in_from_clause; // boolean
2794 if ( ! strcmp( core_class, cname ))
2795 // This is the core class -- no problem
2796 class_in_from_clause = 1;
2799 // There's only one class in the FROM clause, and this isn't it
2800 class_in_from_clause = 0;
2801 else if (join_hash->type == JSON_STRING) {
2802 // There's only one class in the FROM clause
2803 const char* str = jsonObjectGetString(join_hash);
2804 if ( strcmp( str, cname ) )
2805 class_in_from_clause = 0; // This isn't it
2807 class_in_from_clause = 1; // This is it
2809 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", cname);
2810 if ( 0 == found->size )
2811 class_in_from_clause = 0; // Nowhere in the join tree
2813 class_in_from_clause = 1; // Found it
2814 jsonObjectFree( found );
2818 // If the class isn't in the FROM clause, bail out
2819 if( ! class_in_from_clause ) {
2822 "%s: SELECT clause references class not in FROM clause: \"%s\"",
2827 osrfAppSessionStatus(
2829 OSRF_STATUS_INTERNALSERVERERROR,
2830 "osrfMethodException",
2832 "Selected class not in FROM clause in JSON query"
2834 jsonIteratorFree( selclass_itr );
2835 buffer_free( sql_buf );
2836 buffer_free( select_buf );
2837 buffer_free( order_buf );
2838 buffer_free( group_buf );
2839 buffer_free( having_buf );
2840 if( defaultselhash ) jsonObjectFree( defaultselhash );
2845 // Look up some attributes of the current class, so that we
2846 // don't have to look them up again for each field
2847 osrfHash* class_field_set = osrfHashGet( idlClass, "fields" );
2848 const char* class_pkey = osrfHashGet( idlClass, "primarykey" );
2849 const char* class_tname = osrfHashGet( idlClass, "tablename" );
2851 // stitch together the column list ...
2852 jsonIterator* select_itr = jsonNewIterator( selclass );
2853 while ( (selfield = jsonIteratorNext( select_itr )) ) { // for each SELECT column
2855 // If we need a separator comma, add one
2859 OSRF_BUFFER_ADD_CHAR( select_buf, ',' );
2862 // ... if it's a string, just toss it on the pile
2863 if (selfield->type == JSON_STRING) {
2865 // Look up the field in the IDL
2866 const char* col_name = jsonObjectGetString( selfield );
2867 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
2869 // No such field in current class
2872 "%s: Selected column \"%s\" not defined in IDL for class \"%s\"",
2878 osrfAppSessionStatus(
2880 OSRF_STATUS_INTERNALSERVERERROR,
2881 "osrfMethodException",
2883 "Selected column not defined in JSON query"
2885 jsonIteratorFree( select_itr );
2886 jsonIteratorFree( selclass_itr );
2887 buffer_free( sql_buf );
2888 buffer_free( select_buf );
2889 buffer_free( order_buf );
2890 buffer_free( group_buf );
2891 buffer_free( having_buf );
2892 if( defaultselhash ) jsonObjectFree( defaultselhash );
2895 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2896 // Virtual field not allowed
2899 "%s: Selected column \"%s\" for class \"%s\" is virtual",
2905 osrfAppSessionStatus(
2907 OSRF_STATUS_INTERNALSERVERERROR,
2908 "osrfMethodException",
2910 "Selected column may not be virtual in JSON query"
2912 jsonIteratorFree( select_itr );
2913 jsonIteratorFree( selclass_itr );
2914 buffer_free( sql_buf );
2915 buffer_free( select_buf );
2916 buffer_free( order_buf );
2917 buffer_free( group_buf );
2918 buffer_free( having_buf );
2919 if( defaultselhash ) jsonObjectFree( defaultselhash );
2926 if (flags & DISABLE_I18N)
2929 i18n = osrfHashGet(field_def, "i18n");
2931 if( str_is_true( i18n ) ) {
2932 buffer_fadd( select_buf,
2933 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
2934 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, col_name );
2936 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
2939 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
2942 // ... but it could be an object, in which case we check for a Field Transform
2943 } else if (selfield->type == JSON_HASH) {
2945 const char* col_name = jsonObjectGetString( jsonObjectGetKeyConst( selfield, "column" ) );
2947 // Get the field definition from the IDL
2948 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
2950 // No such field in current class
2953 "%s: Selected column \"%s\" is not defined in IDL for class \"%s\"",
2959 osrfAppSessionStatus(
2961 OSRF_STATUS_INTERNALSERVERERROR,
2962 "osrfMethodException",
2964 "Selected column is not defined in JSON query"
2966 jsonIteratorFree( select_itr );
2967 jsonIteratorFree( selclass_itr );
2968 buffer_free( sql_buf );
2969 buffer_free( select_buf );
2970 buffer_free( order_buf );
2971 buffer_free( group_buf );
2972 buffer_free( having_buf );
2973 if( defaultselhash ) jsonObjectFree( defaultselhash );
2976 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2977 // No such field in current class
2980 "%s: Selected column \"%s\" is virtual for class \"%s\"",
2986 osrfAppSessionStatus(
2988 OSRF_STATUS_INTERNALSERVERERROR,
2989 "osrfMethodException",
2991 "Selected column is virtual in JSON query"
2993 jsonIteratorFree( select_itr );
2994 jsonIteratorFree( selclass_itr );
2995 buffer_free( sql_buf );
2996 buffer_free( select_buf );
2997 buffer_free( order_buf );
2998 buffer_free( group_buf );
2999 buffer_free( having_buf );
3000 if( defaultselhash ) jsonObjectFree( defaultselhash );
3005 // Decide what to use as a column alias
3007 if ((tmp_const = jsonObjectGetKeyConst( selfield, "alias" ))) {
3008 _alias = jsonObjectGetString( tmp_const );
3009 } else { // Use field name as the alias
3013 if (jsonObjectGetKeyConst( selfield, "transform" )) {
3014 char* transform_str = searchFieldTransform(cname, field_def, selfield);
3015 if( transform_str ) {
3016 buffer_fadd(select_buf, " %s AS \"%s\"", transform_str, _alias);
3017 free(transform_str);
3020 osrfAppSessionStatus(
3022 OSRF_STATUS_INTERNALSERVERERROR,
3023 "osrfMethodException",
3025 "Unable to generate transform function in JSON query"
3027 jsonIteratorFree( select_itr );
3028 jsonIteratorFree( selclass_itr );
3029 buffer_free( sql_buf );
3030 buffer_free( select_buf );
3031 buffer_free( order_buf );
3032 buffer_free( group_buf );
3033 buffer_free( having_buf );
3034 if( defaultselhash ) jsonObjectFree( defaultselhash );
3042 if (flags & DISABLE_I18N)
3045 i18n = osrfHashGet(field_def, "i18n");
3047 if( str_is_true( i18n ) ) {
3048 buffer_fadd( select_buf,
3049 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
3050 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, _alias);
3052 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
3055 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
3062 "%s: Selected item is unexpected JSON type: %s",
3064 json_type( selfield->type )
3067 osrfAppSessionStatus(
3069 OSRF_STATUS_INTERNALSERVERERROR,
3070 "osrfMethodException",
3072 "Ill-formed SELECT item in JSON query"
3074 jsonIteratorFree( select_itr );
3075 jsonIteratorFree( selclass_itr );
3076 buffer_free( sql_buf );
3077 buffer_free( select_buf );
3078 buffer_free( order_buf );
3079 buffer_free( group_buf );
3080 buffer_free( having_buf );
3081 if( defaultselhash ) jsonObjectFree( defaultselhash );
3086 const jsonObject* agg_obj = jsonObjectGetKey( selfield, "aggregate" );
3087 if( obj_is_true( agg_obj ) )
3088 aggregate_found = 1;
3090 // Append a comma (except for the first one)
3091 // and add the column to a GROUP BY clause
3095 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3097 buffer_fadd(group_buf, " %d", sel_pos);
3101 if (is_agg->size || (flags & SELECT_DISTINCT)) {
3103 const jsonObject* aggregate_obj = jsonObjectGetKey( selfield, "aggregate" );
3104 if ( ! obj_is_true( aggregate_obj ) ) {
3108 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3111 buffer_fadd(group_buf, " %d", sel_pos);
3114 } else if (is_agg = jsonObjectGetKey( selfield, "having" )) {
3118 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3121 _column = searchFieldTransform(cname, field, selfield);
3122 OSRF_BUFFER_ADD_CHAR(group_buf, ' ');
3123 OSRF_BUFFER_ADD(group_buf, _column);
3124 _column = searchFieldTransform(cname, field, selfield);
3131 } // end while -- iterating across SELECT columns
3133 jsonIteratorFree(select_itr);
3134 } // end while -- iterating across classes
3136 jsonIteratorFree(selclass_itr);
3140 char* col_list = buffer_release(select_buf);
3142 if (from_function) table = searchValueTransform(join_hash);
3143 else table = getSourceDefinition(core_meta);
3147 osrfAppSessionStatus(
3149 OSRF_STATUS_INTERNALSERVERERROR,
3150 "osrfMethodException",
3152 "Unable to identify table for core class"
3155 buffer_free( sql_buf );
3156 buffer_free( order_buf );
3157 buffer_free( group_buf );
3158 buffer_free( having_buf );
3159 if( defaultselhash ) jsonObjectFree( defaultselhash );
3164 // Put it all together
3165 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\" ", col_list, table, core_class );
3169 if (!from_function) {
3170 // Now, walk the join tree and add that clause
3172 char* join_clause = searchJOIN( join_hash, core_meta );
3174 buffer_add(sql_buf, join_clause);
3178 osrfAppSessionStatus(
3180 OSRF_STATUS_INTERNALSERVERERROR,
3181 "osrfMethodException",
3183 "Unable to construct JOIN clause(s)"
3185 buffer_free( sql_buf );
3186 buffer_free( order_buf );
3187 buffer_free( group_buf );
3188 buffer_free( having_buf );
3189 if( defaultselhash ) jsonObjectFree( defaultselhash );
3195 // Build a WHERE clause, if there is one
3196 if ( search_hash ) {
3197 buffer_add(sql_buf, " WHERE ");
3199 // and it's on the WHERE clause
3200 char* pred = searchWHERE( search_hash, core_meta, AND_OP_JOIN, ctx );
3203 buffer_add(sql_buf, pred);
3207 osrfAppSessionStatus(
3209 OSRF_STATUS_INTERNALSERVERERROR,
3210 "osrfMethodException",
3212 "Severe query error in WHERE predicate -- see error log for more details"
3216 buffer_free(having_buf);
3217 buffer_free(group_buf);
3218 buffer_free(order_buf);
3219 buffer_free(sql_buf);
3220 if (defaultselhash) jsonObjectFree(defaultselhash);
3225 // Build a HAVING clause, if there is one
3226 if ( having_hash ) {
3227 buffer_add(sql_buf, " HAVING ");
3229 // and it's on the the WHERE clause
3230 char* pred = searchWHERE( having_hash, core_meta, AND_OP_JOIN, ctx );
3233 buffer_add(sql_buf, pred);
3237 osrfAppSessionStatus(
3239 OSRF_STATUS_INTERNALSERVERERROR,
3240 "osrfMethodException",
3242 "Severe query error in HAVING predicate -- see error log for more details"
3246 buffer_free(having_buf);
3247 buffer_free(group_buf);
3248 buffer_free(order_buf);
3249 buffer_free(sql_buf);
3250 if (defaultselhash) jsonObjectFree(defaultselhash);
3255 // Build an ORDER BY clause, if there is one
3257 jsonIterator* class_itr = jsonNewIterator( order_hash );
3258 while ( (snode = jsonIteratorNext( class_itr )) ) {
3260 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
3263 if ( snode->type == JSON_HASH ) {
3265 jsonIterator* order_itr = jsonNewIterator( snode );
3266 while ( (onode = jsonIteratorNext( order_itr )) ) {
3268 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ))
3271 const char* direction = NULL;
3272 if ( onode->type == JSON_HASH ) {
3273 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3274 string = searchFieldTransform(
3276 oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ),
3280 growing_buffer* field_buf = buffer_init(16);
3281 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3282 string = buffer_release(field_buf);
3285 if ( (tmp_const = jsonObjectGetKeyConst( onode, "direction" )) ) {
3286 const char* dir = jsonObjectGetString(tmp_const);
3287 if (!strncasecmp(dir, "d", 1)) {
3288 direction = " DESC";
3295 string = strdup(order_itr->key);
3296 const char* dir = jsonObjectGetString(onode);
3297 if (!strncasecmp(dir, "d", 1)) {
3298 direction = " DESC";
3307 buffer_add(order_buf, ", ");
3310 buffer_add(order_buf, string);
3314 buffer_add(order_buf, direction);
3318 // jsonIteratorFree(order_itr);
3320 } else if ( snode->type == JSON_ARRAY ) {
3322 jsonIterator* order_itr = jsonNewIterator( snode );
3323 while ( (onode = jsonIteratorNext( order_itr )) ) {
3325 const char* _f = jsonObjectGetString( onode );
3327 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, _f))
3333 buffer_add(order_buf, ", ");
3336 buffer_add(order_buf, _f);
3339 // jsonIteratorFree(order_itr);
3342 // IT'S THE OOOOOOOOOOOLD STYLE!
3344 osrfLogError(OSRF_LOG_MARK, "%s: Possible SQL injection attempt; direct order by is not allowed", MODULENAME);
3346 osrfAppSessionStatus(
3348 OSRF_STATUS_INTERNALSERVERERROR,
3349 "osrfMethodException",
3351 "Severe query error -- see error log for more details"
3356 buffer_free(having_buf);
3357 buffer_free(group_buf);
3358 buffer_free(order_buf);
3359 buffer_free(sql_buf);
3360 if (defaultselhash) jsonObjectFree(defaultselhash);
3361 jsonIteratorFree(class_itr);
3366 // jsonIteratorFree(class_itr);
3370 string = buffer_release(group_buf);
3372 if ( *string && ( aggregate_found || (flags & SELECT_DISTINCT) ) ) {
3373 OSRF_BUFFER_ADD( sql_buf, " GROUP BY " );
3374 OSRF_BUFFER_ADD( sql_buf, string );
3379 string = buffer_release(having_buf);
3382 OSRF_BUFFER_ADD( sql_buf, " HAVING " );
3383 OSRF_BUFFER_ADD( sql_buf, string );
3388 string = buffer_release(order_buf);
3391 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3392 OSRF_BUFFER_ADD( sql_buf, string );
3398 const char* str = jsonObjectGetString(limit);
3399 buffer_fadd( sql_buf, " LIMIT %d", atoi(str) );
3403 const char* str = jsonObjectGetString(offset);
3404 buffer_fadd( sql_buf, " OFFSET %d", atoi(str) );
3407 if (!(flags & SUBSELECT)) OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3410 if (defaultselhash) jsonObjectFree(defaultselhash);
3412 return buffer_release(sql_buf);
3416 static char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
3418 const char* locale = osrf_message_get_last_locale();
3420 osrfHash* fields = osrfHashGet(meta, "fields");
3421 char* core_class = osrfHashGet(meta, "classname");
3423 const jsonObject* join_hash = jsonObjectGetKeyConst( order_hash, "join" );
3425 jsonObject* node = NULL;
3426 jsonObject* snode = NULL;
3427 jsonObject* onode = NULL;
3428 const jsonObject* _tmp = NULL;
3429 jsonObject* selhash = NULL;
3430 jsonObject* defaultselhash = NULL;
3432 growing_buffer* sql_buf = buffer_init(128);
3433 growing_buffer* select_buf = buffer_init(128);
3435 if ( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
3436 defaultselhash = jsonNewObjectType(JSON_HASH);
3437 selhash = defaultselhash;
3440 if ( !jsonObjectGetKeyConst(selhash,core_class) ) {
3441 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
3442 jsonObject* flist = jsonObjectGetKey( selhash, core_class );
3447 osrfStringArray* keys = osrfHashKeys( fields );
3448 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
3449 if( ! str_is_true( osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
3450 jsonObjectPush( flist, jsonNewObject( field ) );
3452 osrfStringArrayFree(keys);
3456 jsonIterator* class_itr = jsonNewIterator( selhash );
3457 while ( (snode = jsonIteratorNext( class_itr )) ) {
3459 char* cname = class_itr->key;
3460 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
3461 if (!idlClass) continue;
3463 if (strcmp(core_class,class_itr->key)) {
3464 if (!join_hash) continue;
3466 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", class_itr->key);
3468 jsonObjectFree(found);
3472 jsonObjectFree(found);
3475 jsonIterator* select_itr = jsonNewIterator( snode );
3476 while ( (node = jsonIteratorNext( select_itr )) ) {
3477 const char* item_str = jsonObjectGetString( node );
3478 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), item_str );
3479 char* fname = osrfHashGet(field, "name");
3481 if (!field) continue;
3486 OSRF_BUFFER_ADD_CHAR(select_buf, ',');
3491 const jsonObject* no_i18n_obj = jsonObjectGetKey( order_hash, "no_i18n" );
3492 if ( obj_is_true( no_i18n_obj ) ) // Suppress internationalization?
3495 i18n = osrfHashGet(field, "i18n");
3497 if( str_is_true( i18n ) ) {
3498 char* pkey = osrfHashGet(idlClass, "primarykey");
3499 char* tname = osrfHashGet(idlClass, "tablename");
3501 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);
3503 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3506 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3510 jsonIteratorFree(select_itr);
3513 jsonIteratorFree(class_itr);
3515 char* col_list = buffer_release(select_buf);
3516 char* table = getSourceDefinition(meta);
3518 table = strdup( "(null)" );
3520 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\"", col_list, table, core_class );
3525 char* join_clause = searchJOIN( join_hash, meta );
3526 OSRF_BUFFER_ADD_CHAR(sql_buf, ' ');
3527 OSRF_BUFFER_ADD(sql_buf, join_clause);
3531 osrfLogDebug(OSRF_LOG_MARK, "%s pre-predicate SQL = %s",
3532 MODULENAME, OSRF_BUFFER_C_STR(sql_buf));
3534 buffer_add(sql_buf, " WHERE ");
3536 char* pred = searchWHERE( search_hash, meta, AND_OP_JOIN, ctx );
3538 osrfAppSessionStatus(
3540 OSRF_STATUS_INTERNALSERVERERROR,
3541 "osrfMethodException",
3543 "Severe query error -- see error log for more details"
3545 buffer_free(sql_buf);
3546 if(defaultselhash) jsonObjectFree(defaultselhash);
3549 buffer_add(sql_buf, pred);
3554 char* string = NULL;
3555 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "order_by" )) ){
3557 growing_buffer* order_buf = buffer_init(128);
3560 jsonIterator* class_itr = jsonNewIterator( _tmp );
3561 while ( (snode = jsonIteratorNext( class_itr )) ) {
3563 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
3566 if ( snode->type == JSON_HASH ) {
3568 jsonIterator* order_itr = jsonNewIterator( snode );
3569 while ( (onode = jsonIteratorNext( order_itr )) ) {
3571 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ))
3574 char* direction = NULL;
3575 if ( onode->type == JSON_HASH ) {
3576 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3577 string = searchFieldTransform(
3579 oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ),
3583 growing_buffer* field_buf = buffer_init(16);
3584 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3585 string = buffer_release(field_buf);
3588 if ( (_tmp = jsonObjectGetKeyConst( onode, "direction" )) ) {
3589 const char* dir = jsonObjectGetString(_tmp);
3590 if (!strncasecmp(dir, "d", 1)) {
3591 direction = " DESC";
3598 string = strdup(order_itr->key);
3599 const char* dir = jsonObjectGetString(onode);
3600 if (!strncasecmp(dir, "d", 1)) {
3601 direction = " DESC";
3610 buffer_add(order_buf, ", ");
3613 buffer_add(order_buf, string);
3617 buffer_add(order_buf, direction);
3622 jsonIteratorFree(order_itr);
3625 const char* str = jsonObjectGetString(snode);
3626 buffer_add(order_buf, str);
3632 jsonIteratorFree(class_itr);
3634 string = buffer_release(order_buf);
3637 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3638 OSRF_BUFFER_ADD( sql_buf, string );
3644 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "limit" )) ){
3645 const char* str = jsonObjectGetString(_tmp);
3653 _tmp = jsonObjectGetKeyConst( order_hash, "offset" );
3655 const char* str = jsonObjectGetString(_tmp);
3664 if (defaultselhash) jsonObjectFree(defaultselhash);
3666 OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3667 return buffer_release(sql_buf);
3670 int doJSONSearch ( osrfMethodContext* ctx ) {
3671 if(osrfMethodVerifyContext( ctx )) {
3672 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
3676 osrfLogDebug(OSRF_LOG_MARK, "Recieved query request");
3681 dbhandle = writehandle;
3683 jsonObject* hash = jsonObjectGetIndex(ctx->params, 0);
3687 if ( obj_is_true( jsonObjectGetKey( hash, "distinct" ) ) )
3688 flags |= SELECT_DISTINCT;
3690 if ( obj_is_true( jsonObjectGetKey( hash, "no_i18n" ) ) )
3691 flags |= DISABLE_I18N;
3693 osrfLogDebug(OSRF_LOG_MARK, "Building SQL ...");
3696 jsonObjectGetKey( hash, "select" ),
3697 jsonObjectGetKey( hash, "from" ),
3698 jsonObjectGetKey( hash, "where" ),
3699 jsonObjectGetKey( hash, "having" ),
3700 jsonObjectGetKey( hash, "order_by" ),
3701 jsonObjectGetKey( hash, "limit" ),
3702 jsonObjectGetKey( hash, "offset" ),
3711 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
3712 dbi_result result = dbi_conn_query(dbhandle, sql);
3715 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
3717 if (dbi_result_first_row(result)) {
3718 /* JSONify the result */
3719 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
3722 jsonObject* return_val = oilsMakeJSONFromResult( result );
3723 osrfAppRespond( ctx, return_val );
3724 jsonObjectFree( return_val );
3725 } while (dbi_result_next_row(result));
3728 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
3731 osrfAppRespondComplete( ctx, NULL );
3733 /* clean up the query */
3734 dbi_result_free(result);
3738 osrfLogError(OSRF_LOG_MARK, "%s: Error with query [%s]", MODULENAME, sql);
3739 osrfAppSessionStatus(
3741 OSRF_STATUS_INTERNALSERVERERROR,
3742 "osrfMethodException",
3744 "Severe query error -- see error log for more details"
3752 static jsonObject* doFieldmapperSearch ( osrfMethodContext* ctx, osrfHash* meta,
3753 const jsonObject* params, int* err ) {
3756 dbhandle = writehandle;
3758 osrfHash* links = osrfHashGet(meta, "links");
3759 osrfHash* fields = osrfHashGet(meta, "fields");
3760 char* core_class = osrfHashGet(meta, "classname");
3761 char* pkey = osrfHashGet(meta, "primarykey");
3763 const jsonObject* _tmp;
3765 jsonObject* search_hash = jsonObjectGetIndex(params, 0);
3766 jsonObject* order_hash = jsonObjectGetIndex(params, 1);
3768 char* sql = buildSELECT( search_hash, order_hash, meta, ctx );
3770 osrfLogDebug(OSRF_LOG_MARK, "Problem building query, returning NULL");
3775 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
3777 dbi_result result = dbi_conn_query(dbhandle, sql);
3778 if( NULL == result ) {
3779 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]",
3780 MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
3781 osrfAppSessionStatus(
3783 OSRF_STATUS_INTERNALSERVERERROR,
3784 "osrfMethodException",
3786 "Severe query error -- see error log for more details"
3793 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
3796 jsonObject* res_list = jsonNewObjectType(JSON_ARRAY);
3797 osrfHash* dedup = osrfNewHash();
3799 if (dbi_result_first_row(result)) {
3800 /* JSONify the result */
3801 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
3803 obj = oilsMakeFieldmapperFromResult( result, meta );
3804 char* pkey_val = oilsFMGetString( obj, pkey );
3805 if ( osrfHashGet( dedup, pkey_val ) ) {
3806 jsonObjectFree(obj);
3809 osrfHashSet( dedup, pkey_val, pkey_val );
3810 jsonObjectPush(res_list, obj);
3812 } while (dbi_result_next_row(result));
3814 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s",
3818 osrfHashFree(dedup);
3819 /* clean up the query */
3820 dbi_result_free(result);
3823 if (res_list->size && order_hash) {
3824 _tmp = jsonObjectGetKeyConst( order_hash, "flesh" );
3826 int x = (int)jsonObjectGetNumber(_tmp);
3827 if (x == -1 || x > max_flesh_depth) x = max_flesh_depth;
3829 const jsonObject* temp_blob;
3830 if ((temp_blob = jsonObjectGetKeyConst( order_hash, "flesh_fields" )) && x > 0) {
3832 jsonObject* flesh_blob = jsonObjectClone( temp_blob );
3833 const jsonObject* flesh_fields = jsonObjectGetKeyConst( flesh_blob, core_class );
3835 osrfStringArray* link_fields = NULL;
3838 if (flesh_fields->size == 1) {
3839 const char* _t = jsonObjectGetString( jsonObjectGetIndex( flesh_fields, 0 ) );
3840 if (!strcmp(_t,"*")) link_fields = osrfHashKeys( links );
3845 link_fields = osrfNewStringArray(1);
3846 jsonIterator* _i = jsonNewIterator( flesh_fields );
3847 while ((_f = jsonIteratorNext( _i ))) {
3848 osrfStringArrayAdd( link_fields, jsonObjectGetString( _f ) );
3850 jsonIteratorFree(_i);
3855 jsonIterator* itr = jsonNewIterator( res_list );
3856 while ((cur = jsonIteratorNext( itr ))) {
3861 while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
3863 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
3865 osrfHash* kid_link = osrfHashGet(links, link_field);
3866 if (!kid_link) continue;
3868 osrfHash* field = osrfHashGet(fields, link_field);
3869 if (!field) continue;
3871 osrfHash* value_field = field;
3873 osrfHash* kid_idl = osrfHashGet(oilsIDL(), osrfHashGet(kid_link, "class"));
3874 if (!kid_idl) continue;
3876 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
3877 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
3880 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
3881 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
3884 osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
3886 if (link_map->size > 0) {
3887 jsonObject* _kid_key = jsonNewObjectType(JSON_ARRAY);
3890 jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
3895 osrfHashGet(kid_link, "class"),
3902 "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
3903 osrfHashGet(kid_link, "field"),
3904 osrfHashGet(kid_link, "class"),
3905 osrfHashGet(kid_link, "key"),
3906 osrfHashGet(kid_link, "reltype")
3909 jsonObject* fake_params = jsonNewObjectType(JSON_ARRAY);
3910 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH)); // search hash
3911 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH)); // order/flesh hash
3913 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
3915 const char* search_key = jsonObjectGetString(
3918 atoi( osrfHashGet(value_field, "array_position") )
3923 osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
3928 jsonObjectGetIndex(fake_params, 0),
3929 osrfHashGet(kid_link, "key"),
3930 jsonNewObject( search_key )
3934 jsonObjectGetIndex(fake_params, 1),
3936 jsonNewNumberObject( (double)(x - 1 + link_map->size) )
3940 jsonObjectSetKey( jsonObjectGetIndex(fake_params, 1), "flesh_fields", jsonObjectClone(flesh_blob) );
3942 if (jsonObjectGetKeyConst(order_hash, "order_by")) {
3944 jsonObjectGetIndex(fake_params, 1),
3946 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "order_by"))
3950 if (jsonObjectGetKeyConst(order_hash, "select")) {
3952 jsonObjectGetIndex(fake_params, 1),
3954 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "select"))
3958 jsonObject* kids = doFieldmapperSearch(ctx, kid_idl, fake_params, err);
3961 jsonObjectFree( fake_params );
3962 osrfStringArrayFree(link_fields);
3963 jsonIteratorFree(itr);
3964 jsonObjectFree(res_list);
3965 jsonObjectFree(flesh_blob);
3969 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
3971 jsonObject* X = NULL;
3972 if ( link_map->size > 0 && kids->size > 0 ) {
3974 kids = jsonNewObjectType(JSON_ARRAY);
3976 jsonObject* _k_node;
3977 jsonIterator* _k = jsonNewIterator( X );
3978 while ((_k_node = jsonIteratorNext( _k ))) {
3984 (unsigned long)atoi(
3990 osrfHashGet(kid_link, "class")
3994 osrfStringArrayGetString( link_map, 0 )
4003 jsonIteratorFree(_k);
4006 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" )) || !(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) {
4007 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
4010 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
4011 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
4015 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
4016 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
4019 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
4020 jsonObjectClone( kids )
4025 jsonObjectFree(kids);
4029 jsonObjectFree( kids );
4030 jsonObjectFree( fake_params );
4032 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
4033 osrfLogDebug(OSRF_LOG_MARK, "%s", jsonObjectToJSON(cur));
4037 jsonObjectFree( flesh_blob );
4038 osrfStringArrayFree(link_fields);
4039 jsonIteratorFree(itr);
4048 static jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
4050 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4052 jsonObject* target = jsonObjectGetIndex( ctx->params, 1 );
4054 jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
4057 if (!verifyObjectClass(ctx, target)) {
4062 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4063 osrfAppSessionStatus(
4065 OSRF_STATUS_BADREQUEST,
4066 "osrfMethodException",
4068 "No active transaction -- required for UPDATE"
4074 // The following test is harmless but redundant. If a class is
4075 // readonly, we don't register an update method for it.
4076 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4077 osrfAppSessionStatus(
4079 OSRF_STATUS_BADREQUEST,
4080 "osrfMethodException",
4082 "Cannot UPDATE readonly class"
4088 dbhandle = writehandle;
4090 char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
4092 // Set the last_xact_id
4093 int index = oilsIDL_ntop( target->classname, "last_xact_id" );
4095 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
4096 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
4099 char* pkey = osrfHashGet(meta, "primarykey");
4100 osrfHash* fields = osrfHashGet(meta, "fields");
4102 char* id = oilsFMGetString( target, pkey );
4106 "%s updating %s object with %s = %s",
4108 osrfHashGet(meta, "fieldmapper"),
4113 growing_buffer* sql = buffer_init(128);
4114 buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
4119 osrfStringArray* field_list = osrfHashKeys( fields );
4120 while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
4122 osrfHash* field = osrfHashGet( fields, field_name );
4124 if(!( strcmp( field_name, pkey ) )) continue;
4125 if( str_is_true( osrfHashGet(osrfHashGet(fields,field_name), "virtual") ) )
4128 const jsonObject* field_object = oilsFMGetObject( target, field_name );
4130 int value_is_numeric = 0; // boolean
4132 if (field_object && field_object->classname) {
4133 value = oilsFMGetString(
4135 (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
4138 value = jsonObjectToSimpleString( field_object );
4139 if( field_object && JSON_NUMBER == field_object->type )
4140 value_is_numeric = 1;
4143 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
4145 if (!field_object || field_object->type == JSON_NULL) {
4146 if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
4147 if (first) first = 0;
4148 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4149 buffer_fadd( sql, " %s = NULL", field_name );
4152 } else if ( value_is_numeric || !strcmp( get_primitive( field ), "number") ) {
4153 if (first) first = 0;
4154 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4156 const char* numtype = get_datatype( field );
4157 if ( !strncmp( numtype, "INT", 3 ) ) {
4158 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
4159 } else if ( !strcmp( numtype, "NUMERIC" ) ) {
4160 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
4162 // Must really be intended as a string, so quote it
4163 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4164 buffer_fadd( sql, " %s = %s", field_name, value );
4166 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4167 osrfAppSessionStatus(
4169 OSRF_STATUS_INTERNALSERVERERROR,
4170 "osrfMethodException",
4172 "Error quoting string -- please see the error log for more details"
4182 osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, numtype );
4185 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4186 if (first) first = 0;
4187 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4188 buffer_fadd( sql, " %s = %s", field_name, value );
4191 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4192 osrfAppSessionStatus(
4194 OSRF_STATUS_INTERNALSERVERERROR,
4195 "osrfMethodException",
4197 "Error quoting string -- please see the error log for more details"
4211 jsonObject* obj = jsonNewObject(id);
4213 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4214 dbi_conn_quote_string(dbhandle, &id);
4216 buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
4218 char* query = buffer_release(sql);
4219 osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
4221 dbi_result result = dbi_conn_query(dbhandle, query);
4225 jsonObjectFree(obj);
4226 obj = jsonNewObject(NULL);
4229 "%s ERROR updating %s object with %s = %s",
4231 osrfHashGet(meta, "fieldmapper"),
4242 static jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
4244 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4246 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4247 osrfAppSessionStatus(
4249 OSRF_STATUS_BADREQUEST,
4250 "osrfMethodException",
4252 "No active transaction -- required for DELETE"
4258 // The following test is harmless but redundant. If a class is
4259 // readonly, we don't register a delete method for it.
4260 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4261 osrfAppSessionStatus(
4263 OSRF_STATUS_BADREQUEST,
4264 "osrfMethodException",
4266 "Cannot DELETE readonly class"
4272 dbhandle = writehandle;
4276 char* pkey = osrfHashGet(meta, "primarykey");
4284 if (jsonObjectGetIndex(ctx->params, _obj_pos)->classname) {
4285 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, _obj_pos ))) {
4290 id = oilsFMGetString( jsonObjectGetIndex(ctx->params, _obj_pos), pkey );
4293 if (!verifyObjectPCRUD( ctx, NULL )) {
4298 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, _obj_pos));
4303 "%s deleting %s object with %s = %s",
4305 osrfHashGet(meta, "fieldmapper"),
4310 obj = jsonNewObject(id);
4312 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4313 dbi_conn_quote_string(writehandle, &id);
4315 dbi_result result = dbi_conn_queryf(writehandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
4318 jsonObjectFree(obj);
4319 obj = jsonNewObject(NULL);
4322 "%s ERROR deleting %s object with %s = %s",
4324 osrfHashGet(meta, "fieldmapper"),
4337 static jsonObject* oilsMakeFieldmapperFromResult( dbi_result result, osrfHash* meta) {
4338 if(!(result && meta)) return jsonNULL;
4340 jsonObject* object = jsonNewObject(NULL);
4341 jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
4343 osrfHash* fields = osrfHashGet(meta, "fields");
4345 osrfLogInternal(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
4349 char dt_string[256];
4353 int columnIndex = 1;
4355 unsigned short type;
4356 const char* columnName;
4358 /* cycle through the column list */
4359 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4361 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4363 fmIndex = -1; // reset the position
4365 /* determine the field type and storage attributes */
4366 type = dbi_result_get_field_type(result, columnName);
4367 attr = dbi_result_get_field_attribs(result, columnName);
4369 /* fetch the fieldmapper index */
4370 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
4372 if ( str_is_true( osrfHashGet(_f, "virtual") ) )
4375 const char* pos = (char*)osrfHashGet(_f, "array_position");
4376 if ( !pos ) continue;
4378 fmIndex = atoi( pos );
4379 osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
4384 if (dbi_result_field_is_null(result, columnName)) {
4385 jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
4390 case DBI_TYPE_INTEGER :
4392 if( attr & DBI_INTEGER_SIZE8 )
4393 jsonObjectSetIndex( object, fmIndex,
4394 jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
4396 jsonObjectSetIndex( object, fmIndex,
4397 jsonNewNumberObject(dbi_result_get_int(result, columnName)));
4401 case DBI_TYPE_DECIMAL :
4402 jsonObjectSetIndex( object, fmIndex,
4403 jsonNewNumberObject(dbi_result_get_double(result, columnName)));
4406 case DBI_TYPE_STRING :
4412 jsonNewObject( dbi_result_get_string(result, columnName) )
4417 case DBI_TYPE_DATETIME :
4419 memset(dt_string, '\0', sizeof(dt_string));
4420 memset(&gmdt, '\0', sizeof(gmdt));
4422 _tmp_dt = dbi_result_get_datetime(result, columnName);
4425 if (!(attr & DBI_DATETIME_DATE)) {
4426 gmtime_r( &_tmp_dt, &gmdt );
4427 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4428 } else if (!(attr & DBI_DATETIME_TIME)) {
4429 localtime_r( &_tmp_dt, &gmdt );
4430 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4432 localtime_r( &_tmp_dt, &gmdt );
4433 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4436 jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
4440 case DBI_TYPE_BINARY :
4441 osrfLogError( OSRF_LOG_MARK,
4442 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4450 static jsonObject* oilsMakeJSONFromResult( dbi_result result ) {
4451 if(!result) return jsonNULL;
4453 jsonObject* object = jsonNewObject(NULL);
4456 char dt_string[256];
4460 int columnIndex = 1;
4462 unsigned short type;
4463 const char* columnName;
4465 /* cycle through the column list */
4466 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4468 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4470 fmIndex = -1; // reset the position
4472 /* determine the field type and storage attributes */
4473 type = dbi_result_get_field_type(result, columnName);
4474 attr = dbi_result_get_field_attribs(result, columnName);
4476 if (dbi_result_field_is_null(result, columnName)) {
4477 jsonObjectSetKey( object, columnName, jsonNewObject(NULL) );
4482 case DBI_TYPE_INTEGER :
4484 if( attr & DBI_INTEGER_SIZE8 )
4485 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_longlong(result, columnName)) );
4487 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_int(result, columnName)) );
4490 case DBI_TYPE_DECIMAL :
4491 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_double(result, columnName)) );
4494 case DBI_TYPE_STRING :
4495 jsonObjectSetKey( object, columnName, jsonNewObject(dbi_result_get_string(result, columnName)) );
4498 case DBI_TYPE_DATETIME :
4500 memset(dt_string, '\0', sizeof(dt_string));
4501 memset(&gmdt, '\0', sizeof(gmdt));
4503 _tmp_dt = dbi_result_get_datetime(result, columnName);
4506 if (!(attr & DBI_DATETIME_DATE)) {
4507 gmtime_r( &_tmp_dt, &gmdt );
4508 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4509 } else if (!(attr & DBI_DATETIME_TIME)) {
4510 localtime_r( &_tmp_dt, &gmdt );
4511 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4513 localtime_r( &_tmp_dt, &gmdt );
4514 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4517 jsonObjectSetKey( object, columnName, jsonNewObject(dt_string) );
4520 case DBI_TYPE_BINARY :
4521 osrfLogError( OSRF_LOG_MARK,
4522 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4530 // Interpret a string as true or false
4531 static int str_is_true( const char* str ) {
4532 if( NULL == str || strcasecmp( str, "true" ) )
4538 // Interpret a jsonObject as true or false
4539 static int obj_is_true( const jsonObject* obj ) {
4542 else switch( obj->type )
4550 if( strcasecmp( obj->value.s, "true" ) )
4554 case JSON_NUMBER : // Support 1/0 for perl's sake
4555 if( jsonObjectGetNumber( obj ) == 1.0 )
4564 // Translate a numeric code into a text string identifying a type of
4565 // jsonObject. To be used for building error messages.
4566 static const char* json_type( int code ) {
4572 return "JSON_ARRAY";
4574 return "JSON_STRING";
4576 return "JSON_NUMBER";
4582 return "(unrecognized)";
4586 // Extract the "primitive" attribute from an IDL field definition.
4587 // If we haven't initialized the app, then we must be running in
4588 // some kind of testbed. In that case, default to "string".
4589 static const char* get_primitive( osrfHash* field ) {
4590 const char* s = osrfHashGet( field, "primitive" );
4592 if( child_initialized )
4595 "%s ERROR No \"datatype\" attribute for field \"%s\"",
4597 osrfHashGet( field, "name" )
4605 // Extract the "datatype" attribute from an IDL field definition.
4606 // If we haven't initialized the app, then we must be running in
4607 // some kind of testbed. In that case, default to to NUMERIC,
4608 // since we look at the datatype only for numbers.
4609 static const char* get_datatype( osrfHash* field ) {
4610 const char* s = osrfHashGet( field, "datatype" );
4612 if( child_initialized )
4615 "%s ERROR No \"datatype\" attribute for field \"%s\"",
4617 osrfHashGet( field, "name" )
4626 If the input string is potentially a valid SQL identifier, return 1.
4629 Purpose: to prevent certain kinds of SQL injection. To that end we
4630 don't necessarily need to follow all the rules exactly, such as requiring
4631 that the first character not be a digit.
4633 We allow leading and trailing white space. In between, we do not allow
4634 punctuation (except for underscores and dollar signs), control
4635 characters, or embedded white space.
4637 More pedantically we should allow quoted identifiers containing arbitrary
4638 characters, but for the foreseeable future such quoted identifiers are not
4639 likely to be an issue.
4641 static int is_identifier( const char* s) {
4645 // Skip leading white space
4646 while( isspace( (unsigned char) *s ) )
4650 return 0; // Nothing but white space? Not okay.
4652 // Check each character until we reach white space or
4653 // end-of-string. Letters, digits, underscores, and
4654 // dollar signs are okay. Control characters and other
4655 // punctuation characters are not okay. Anything else
4656 // is okay -- it could for example be part of a multibyte
4657 // UTF8 character such as a letter with diacritical marks,
4658 // and those are allowed.
4660 if( isalnum( (unsigned char) *s )
4663 ; // Fine; keep going
4664 else if( ispunct( (unsigned char) *s )
4665 || iscntrl( (unsigned char) *s ) )
4668 } while( *s && ! isspace( (unsigned char) *s ) );
4670 // If we found any white space in the above loop,
4671 // the rest had better be all white space.
4673 while( isspace( (unsigned char) *s ) )
4677 return 0; // White space was embedded within non-white space