2 #include "opensrf/osrf_application.h"
3 #include "opensrf/osrf_settings.h"
4 #include "opensrf/osrf_message.h"
5 #include "opensrf/utils.h"
6 #include "opensrf/osrf_json.h"
7 #include "opensrf/log.h"
8 #include "openils/oils_utils.h"
17 # define MODULENAME "open-ils.reporter-store"
20 # define MODULENAME "open-ils.pcrud"
22 # define MODULENAME "open-ils.cstore"
27 #define DISABLE_I18N 2
28 #define SELECT_DISTINCT 1
32 int osrfAppChildInit();
33 int osrfAppInitialize();
34 void osrfAppChildExit();
36 static int verifyObjectClass ( osrfMethodContext*, const jsonObject* );
38 int beginTransaction ( osrfMethodContext* );
39 int commitTransaction ( osrfMethodContext* );
40 int rollbackTransaction ( osrfMethodContext* );
42 int setSavepoint ( osrfMethodContext* );
43 int releaseSavepoint ( osrfMethodContext* );
44 int rollbackSavepoint ( osrfMethodContext* );
46 int doJSONSearch ( osrfMethodContext* );
48 int dispatchCRUDMethod ( osrfMethodContext* );
49 static jsonObject* doCreate ( osrfMethodContext*, int* );
50 static jsonObject* doRetrieve ( osrfMethodContext*, int* );
51 static jsonObject* doUpdate ( osrfMethodContext*, int* );
52 static jsonObject* doDelete ( osrfMethodContext*, int* );
53 static jsonObject* doFieldmapperSearch ( osrfMethodContext*, osrfHash*,
54 const jsonObject*, int* );
55 static jsonObject* oilsMakeFieldmapperFromResult( dbi_result, osrfHash* );
56 static jsonObject* oilsMakeJSONFromResult( dbi_result );
58 static char* searchSimplePredicate ( const char* op, const char* class,
59 osrfHash* field, const jsonObject* node );
60 static char* searchFunctionPredicate ( const char*, osrfHash*, const jsonObject*, const char* );
61 static char* searchFieldTransform ( const char*, osrfHash*, const jsonObject*);
62 static char* searchFieldTransformPredicate ( const char*, osrfHash*, jsonObject*, const char* );
63 static char* searchBETWEENPredicate ( const char*, osrfHash*, jsonObject* );
64 static char* searchINPredicate ( const char*, osrfHash*,
65 jsonObject*, const char*, osrfMethodContext* );
66 static char* searchPredicate ( const char*, osrfHash*, jsonObject*, osrfMethodContext* );
67 static char* searchJOIN ( const jsonObject*, osrfHash* );
68 static char* searchWHERE ( const jsonObject*, osrfHash*, int, osrfMethodContext* );
69 static char* buildSELECT ( jsonObject*, jsonObject*, osrfHash*, osrfMethodContext* );
71 char* SELECT ( osrfMethodContext*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, int );
73 void userDataFree( void* );
74 static void sessionDataFree( char*, void* );
75 static char* getSourceDefinition( osrfHash* );
76 static int str_is_true( const char* str );
77 static int obj_is_true( const jsonObject* obj );
78 static const char* json_type( int code );
79 static const char* get_primitive( osrfHash* field );
80 static const char* get_datatype( osrfHash* field );
81 static int is_identifier( const char* s);
84 static jsonObject* verifyUserPCRUD( osrfMethodContext* );
85 static int verifyObjectPCRUD( osrfMethodContext*, const jsonObject* );
88 static int child_initialized = 0; /* boolean */
90 static dbi_conn writehandle; /* our MASTER db connection */
91 static dbi_conn dbhandle; /* our CURRENT db connection */
92 //static osrfHash * readHandles;
93 static jsonObject* jsonNULL = NULL; //
94 static int max_flesh_depth = 100;
96 /* called when this process is about to exit */
97 void osrfAppChildExit() {
98 osrfLogDebug(OSRF_LOG_MARK, "Child is exiting, disconnecting from database...");
101 if (writehandle == dbhandle) same = 1;
103 dbi_conn_query(writehandle, "ROLLBACK;");
104 dbi_conn_close(writehandle);
107 if (dbhandle && !same)
108 dbi_conn_close(dbhandle);
110 // XXX add cleanup of readHandles whenever that gets used
115 int osrfAppInitialize() {
117 osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
118 osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
120 if (!oilsIDLInit( osrf_settings_host_value("/IDL") )) return 1; /* return non-zero to indicate error */
122 growing_buffer* method_name = buffer_init(64);
124 // Generic search thingy
125 buffer_add(method_name, MODULENAME);
126 buffer_add(method_name, ".json_query");
127 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
128 "doJSONSearch", "", 1, OSRF_METHOD_STREAMING );
131 // first we register all the transaction and savepoint methods
132 buffer_reset(method_name);
133 OSRF_BUFFER_ADD(method_name, MODULENAME);
134 OSRF_BUFFER_ADD(method_name, ".transaction.begin");
135 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
136 "beginTransaction", "", 0, 0 );
138 buffer_reset(method_name);
139 OSRF_BUFFER_ADD(method_name, MODULENAME);
140 OSRF_BUFFER_ADD(method_name, ".transaction.commit");
141 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
142 "commitTransaction", "", 0, 0 );
144 buffer_reset(method_name);
145 OSRF_BUFFER_ADD(method_name, MODULENAME);
146 OSRF_BUFFER_ADD(method_name, ".transaction.rollback");
147 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
148 "rollbackTransaction", "", 0, 0 );
150 buffer_reset(method_name);
151 OSRF_BUFFER_ADD(method_name, MODULENAME);
152 OSRF_BUFFER_ADD(method_name, ".savepoint.set");
153 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
154 "setSavepoint", "", 1, 0 );
156 buffer_reset(method_name);
157 OSRF_BUFFER_ADD(method_name, MODULENAME);
158 OSRF_BUFFER_ADD(method_name, ".savepoint.release");
159 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
160 "releaseSavepoint", "", 1, 0 );
162 buffer_reset(method_name);
163 OSRF_BUFFER_ADD(method_name, MODULENAME);
164 OSRF_BUFFER_ADD(method_name, ".savepoint.rollback");
165 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
166 "rollbackSavepoint", "", 1, 0 );
168 buffer_free(method_name);
170 static const char* global_method[] = {
178 const int global_method_count
179 = sizeof( global_method ) / sizeof ( global_method[0] );
183 osrfStringArray* classes = osrfHashKeys( oilsIDL() );
184 osrfLogDebug(OSRF_LOG_MARK, "%d classes loaded", classes->size );
185 osrfLogDebug(OSRF_LOG_MARK,
186 "At least %d methods will be generated", classes->size * global_method_count);
188 while ( (classname = osrfStringArrayGetString(classes, c_index++)) ) {
189 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
191 osrfHash* idlClass = osrfHashGet(oilsIDL(), classname);
193 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), MODULENAME )) {
194 osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on", MODULENAME, classname);
198 if ( str_is_true( osrfHashGet(idlClass, "virtual") ) ) {
199 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
203 // Look up some other attributes of the current class
204 const char* idlClass_fieldmapper = osrfHashGet(idlClass, "fieldmapper");
205 const char* readonly = osrfHashGet(idlClass, "readonly");
207 osrfHash* idlClass_permacrud = osrfHashGet(idlClass, "permacrud");
211 for( i = 0; i < global_method_count; ++i ) {
212 const char* method_type = global_method[ i ];
213 osrfLogDebug(OSRF_LOG_MARK,
214 "Using files to build %s class methods for %s", method_type, classname);
216 if (!idlClass_fieldmapper) continue;
219 if (!idlClass_permacrud) continue;
221 const char* tmp_method = method_type;
222 if ( *tmp_method == 'i' || *tmp_method == 's') {
223 tmp_method = "retrieve";
225 if (!osrfHashGet( idlClass_permacrud, tmp_method )) continue;
228 if ( str_is_true( readonly ) &&
229 ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd')
232 osrfHash* method_meta = osrfNewHash();
233 osrfHashSet(method_meta, idlClass, "class");
235 method_name = buffer_init(64);
237 buffer_fadd(method_name, "%s.%s.%s", MODULENAME, method_type, classname);
241 char* _fm = strdup( idlClass_fieldmapper );
242 part = strtok_r(_fm, ":", &st_tmp);
244 buffer_fadd(method_name, "%s.direct.%s", MODULENAME, part);
246 while ((part = strtok_r(NULL, ":", &st_tmp))) {
247 OSRF_BUFFER_ADD_CHAR(method_name, '.');
248 OSRF_BUFFER_ADD(method_name, part);
250 OSRF_BUFFER_ADD_CHAR(method_name, '.');
251 OSRF_BUFFER_ADD(method_name, method_type);
255 char* method = buffer_release(method_name);
257 osrfHashSet( method_meta, method, "methodname" );
258 osrfHashSet( method_meta, strdup(method_type), "methodtype" );
261 if (*method_type == 'i' || *method_type == 's') {
262 flags = flags | OSRF_METHOD_STREAMING;
265 osrfAppRegisterExtendedMethod(
268 "dispatchCRUDMethod",
282 static char* getSourceDefinition( osrfHash* class ) {
284 char* tabledef = osrfHashGet(class, "tablename");
287 tabledef = strdup(tabledef);
289 tabledef = osrfHashGet(class, "source_definition");
291 growing_buffer* tablebuf = buffer_init(128);
292 buffer_fadd( tablebuf, "(%s)", tabledef );
293 tabledef = buffer_release(tablebuf);
295 const char* classname = osrfHashGet( class, "classname" );
300 "%s ERROR No tablename or source_definition for class \"%s\"",
311 * Connects to the database
313 int osrfAppChildInit() {
315 osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
316 dbi_initialize(NULL);
317 osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
319 char* driver = osrf_settings_host_value("/apps/%s/app_settings/driver", MODULENAME);
320 char* user = osrf_settings_host_value("/apps/%s/app_settings/database/user", MODULENAME);
321 char* host = osrf_settings_host_value("/apps/%s/app_settings/database/host", MODULENAME);
322 char* port = osrf_settings_host_value("/apps/%s/app_settings/database/port", MODULENAME);
323 char* db = osrf_settings_host_value("/apps/%s/app_settings/database/db", MODULENAME);
324 char* pw = osrf_settings_host_value("/apps/%s/app_settings/database/pw", MODULENAME);
325 char* md = osrf_settings_host_value("/apps/%s/app_settings/max_query_recursion", MODULENAME);
327 osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
328 writehandle = dbi_conn_new(driver);
331 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
334 osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
336 osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database. host=%s, "
337 "port=%s, user=%s, pw=%s, db=%s", MODULENAME, host, port, user, pw, db );
339 if(host) dbi_conn_set_option(writehandle, "host", host );
340 if(port) dbi_conn_set_option_numeric( writehandle, "port", atoi(port) );
341 if(user) dbi_conn_set_option(writehandle, "username", user);
342 if(pw) dbi_conn_set_option(writehandle, "password", pw );
343 if(db) dbi_conn_set_option(writehandle, "dbname", db );
345 if(md) max_flesh_depth = atoi(md);
346 if(max_flesh_depth < 0) max_flesh_depth = 1;
347 if(max_flesh_depth > 1000) max_flesh_depth = 1000;
356 if (dbi_conn_connect(writehandle) < 0) {
358 if (dbi_conn_connect(writehandle) < 0) {
359 dbi_conn_error(writehandle, &err);
360 osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
365 osrfLogInfo(OSRF_LOG_MARK, "%s successfully connected to the database", MODULENAME);
371 osrfStringArray* classes = osrfHashKeys( oilsIDL() );
373 while ( (classname = osrfStringArrayGetString(classes, i++)) ) {
374 osrfHash* class = osrfHashGet( oilsIDL(), classname );
375 osrfHash* fields = osrfHashGet( class, "fields" );
377 if( str_is_true( osrfHashGet(class, "virtual") ) ) {
378 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
382 char* tabledef = getSourceDefinition(class);
384 tabledef = strdup( "(null)" );
386 growing_buffer* sql_buf = buffer_init(32);
387 buffer_fadd( sql_buf, "SELECT * FROM %s AS x WHERE 1=0;", tabledef );
391 char* sql = buffer_release(sql_buf);
392 osrfLogDebug(OSRF_LOG_MARK, "%s Investigatory SQL = %s", MODULENAME, sql);
394 dbi_result result = dbi_conn_query(writehandle, sql);
400 const char* columnName;
402 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
404 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
406 /* fetch the fieldmapper index */
407 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
409 osrfLogDebug(OSRF_LOG_MARK, "Found [%s] in IDL hash...", (char*)columnName);
411 /* determine the field type and storage attributes */
412 type = dbi_result_get_field_type(result, columnName);
413 attr = dbi_result_get_field_attribs(result, columnName);
417 case DBI_TYPE_INTEGER :
419 if ( !osrfHashGet(_f, "primitive") )
420 osrfHashSet(_f,"number", "primitive");
422 if( attr & DBI_INTEGER_SIZE8 )
423 osrfHashSet(_f,"INT8", "datatype");
425 osrfHashSet(_f,"INT", "datatype");
428 case DBI_TYPE_DECIMAL :
429 if ( !osrfHashGet(_f, "primitive") )
430 osrfHashSet(_f,"number", "primitive");
432 osrfHashSet(_f,"NUMERIC", "datatype");
435 case DBI_TYPE_STRING :
436 if ( !osrfHashGet(_f, "primitive") )
437 osrfHashSet(_f,"string", "primitive");
438 osrfHashSet(_f,"TEXT", "datatype");
441 case DBI_TYPE_DATETIME :
442 if ( !osrfHashGet(_f, "primitive") )
443 osrfHashSet(_f,"string", "primitive");
445 osrfHashSet(_f,"TIMESTAMP", "datatype");
448 case DBI_TYPE_BINARY :
449 if ( !osrfHashGet(_f, "primitive") )
450 osrfHashSet(_f,"string", "primitive");
452 osrfHashSet(_f,"BYTEA", "datatype");
457 "Setting [%s] to primitive [%s] and datatype [%s]...",
459 osrfHashGet(_f, "primitive"),
460 osrfHashGet(_f, "datatype")
464 dbi_result_free(result);
466 osrfLogDebug(OSRF_LOG_MARK, "No data found for class [%s]...", (char*)classname);
470 osrfStringArrayFree(classes);
472 child_initialized = 1;
477 This function is a sleazy hack intended *only* for testing and
478 debugging. Any real server process should initialize the
479 database connection by calling osrfAppChildInit().
481 void set_cstore_dbi_conn( dbi_conn conn ) {
482 dbhandle = writehandle = conn;
485 void userDataFree( void* blob ) {
486 osrfHashFree( (osrfHash*)blob );
490 static void sessionDataFree( char* key, void* item ) {
491 if (!(strcmp(key,"xact_id"))) {
493 dbi_conn_query(writehandle, "ROLLBACK;");
500 int beginTransaction ( osrfMethodContext* ctx ) {
501 if(osrfMethodVerifyContext( ctx )) {
502 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
507 jsonObject* user = verifyUserPCRUD( ctx );
508 if (!user) return -1;
509 jsonObjectFree(user);
512 dbi_result result = dbi_conn_query(writehandle, "START TRANSACTION;");
514 osrfLogError(OSRF_LOG_MARK, "%s: Error starting transaction", MODULENAME );
515 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error starting transaction" );
518 jsonObject* ret = jsonNewObject(ctx->session->session_id);
519 osrfAppRespondComplete( ctx, ret );
522 if (!ctx->session->userData) {
523 ctx->session->userData = osrfNewHash();
524 osrfHashSetCallback((osrfHash*)ctx->session->userData, &sessionDataFree);
527 osrfHashSet( (osrfHash*)ctx->session->userData, strdup( ctx->session->session_id ), "xact_id" );
528 ctx->session->userDataFree = &userDataFree;
534 int setSavepoint ( osrfMethodContext* ctx ) {
535 if(osrfMethodVerifyContext( ctx )) {
536 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
543 jsonObject* user = verifyUserPCRUD( ctx );
544 if (!user) return -1;
545 jsonObjectFree(user);
548 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
549 osrfAppSessionStatus(
551 OSRF_STATUS_INTERNALSERVERERROR,
552 "osrfMethodException",
554 "No active transaction -- required for savepoints"
559 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);
1724 OSRF_BUFFER_ADD_CHAR( sql_buf, ')' );
1726 return buffer_release(sql_buf);
1729 // Receive a JSON_ARRAY representing a function call. The first
1730 // entry in the array is the function name. The rest are parameters.
1731 static char* searchValueTransform( const jsonObject* array ) {
1732 growing_buffer* sql_buf = buffer_init(32);
1734 jsonObject* func_item;
1736 // Get the function name
1737 if( array->size > 0 ) {
1738 func_item = jsonObjectGetIndex( array, 0 );
1739 OSRF_BUFFER_ADD( sql_buf, jsonObjectGetString( func_item ) );
1740 OSRF_BUFFER_ADD( sql_buf, "( " );
1743 // Get the parameters
1744 int func_item_index = 1; // We already grabbed the zeroth entry
1745 while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1747 // Add a separator comma, if we need one
1748 if( func_item_index > 2 )
1749 buffer_add( sql_buf, ", " );
1751 // Add the current parameter
1752 if (func_item->type == JSON_NULL) {
1753 buffer_add( sql_buf, "NULL" );
1755 char* val = jsonObjectToSimpleString(func_item);
1756 if ( dbi_conn_quote_string(dbhandle, &val) ) {
1757 OSRF_BUFFER_ADD( sql_buf, val );
1760 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1761 buffer_free(sql_buf);
1768 buffer_add( sql_buf, " )" );
1770 return buffer_release(sql_buf);
1773 static char* searchFunctionPredicate (const char* class, osrfHash* field,
1774 const jsonObject* node, const char* node_key) {
1775 growing_buffer* sql_buf = buffer_init(32);
1777 char* val = searchValueTransform(node);
1783 osrfHashGet(field, "name"),
1790 return buffer_release(sql_buf);
1793 // class is a class name
1794 // field is a field definition as stored in the IDL
1795 // node comes from the method parameter, and represents an entry in the SELECT list
1796 static char* searchFieldTransform (const char* class, osrfHash* field, const jsonObject* node) {
1797 growing_buffer* sql_buf = buffer_init(32);
1799 const char* field_transform = jsonObjectGetString( jsonObjectGetKeyConst( node, "transform" ) );
1800 const char* transform_subcolumn = jsonObjectGetString( jsonObjectGetKeyConst( node, "result_field" ) );
1802 if(transform_subcolumn)
1803 OSRF_BUFFER_ADD_CHAR( sql_buf, '(' ); // enclose transform in parentheses
1805 if (field_transform) {
1806 buffer_fadd( sql_buf, "%s(\"%s\".%s", field_transform, class, osrfHashGet(field, "name"));
1807 const jsonObject* array = jsonObjectGetKeyConst( node, "params" );
1810 if( array->type != JSON_ARRAY ) {
1811 osrfLogError( OSRF_LOG_MARK,
1812 "%s: Expected JSON_ARRAY for function params; found %s",
1813 MODULENAME, json_type( array->type ) );
1814 buffer_free( sql_buf );
1817 int func_item_index = 0;
1818 jsonObject* func_item;
1819 while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1821 char* val = jsonObjectToSimpleString(func_item);
1824 buffer_add( sql_buf, ",NULL" );
1825 } else if ( dbi_conn_quote_string(dbhandle, &val) ) {
1826 OSRF_BUFFER_ADD_CHAR( sql_buf, ',' );
1827 OSRF_BUFFER_ADD( sql_buf, val );
1829 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1831 buffer_free(sql_buf);
1838 buffer_add( sql_buf, " )" );
1841 buffer_fadd( sql_buf, "\"%s\".%s", class, osrfHashGet(field, "name"));
1844 if (transform_subcolumn)
1845 buffer_fadd( sql_buf, ").\"%s\"", transform_subcolumn );
1847 return buffer_release(sql_buf);
1850 static char* searchFieldTransformPredicate (const char* class, osrfHash* field, jsonObject* node, const char* node_key) {
1851 char* field_transform = searchFieldTransform( class, field, node );
1854 const jsonObject* value_obj = jsonObjectGetKeyConst( node, "value" );
1855 if ( ! value_obj ) {
1856 value = searchWHERE( node, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN, NULL );
1858 osrfLogError(OSRF_LOG_MARK, "%s: Error building condition for field transform", MODULENAME, value);
1859 free(field_transform);
1862 } else if ( value_obj->type == JSON_ARRAY ) {
1863 value = searchValueTransform( value_obj );
1864 } else if ( value_obj->type == JSON_HASH ) {
1865 value = searchWHERE( value_obj, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN, NULL );
1867 osrfLogError(OSRF_LOG_MARK, "%s: Error building predicate for field transform", MODULENAME, value);
1868 free(field_transform);
1871 } else if ( value_obj->type == JSON_NUMBER ) {
1872 value = jsonNumberToDBString( field, value_obj );
1873 } else if ( value_obj->type != JSON_NULL ) {
1874 if ( !strcmp( get_primitive( field ), "number") ) {
1875 value = jsonNumberToDBString( field, value_obj );
1877 value = jsonObjectToSimpleString( value_obj );
1878 if ( !dbi_conn_quote_string(dbhandle, &value) ) {
1879 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, value);
1881 free(field_transform);
1887 growing_buffer* sql_buf = buffer_init(32);
1898 free(field_transform);
1900 return buffer_release(sql_buf);
1903 static char* searchSimplePredicate (const char* op, const char* class,
1904 osrfHash* field, const jsonObject* node) {
1908 // Get the value to which we are comparing the specified column
1909 if (node->type != JSON_NULL) {
1910 if ( node->type == JSON_NUMBER ) {
1911 val = jsonNumberToDBString( field, node );
1912 } else if ( !strcmp( get_primitive( field ), "number" ) ) {
1913 val = jsonNumberToDBString( field, node );
1915 val = jsonObjectToSimpleString(node);
1920 if( JSON_NUMBER != node->type && strcmp( get_primitive( field ), "number") ) {
1921 // Value is not numeric; enclose it in quotes
1922 if ( !dbi_conn_quote_string( dbhandle, &val ) ) {
1923 osrfLogError( OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val );
1929 // Compare to a null value
1930 val = strdup( "NULL" );
1931 if (strcmp( op, "=" ))
1937 growing_buffer* sql_buf = buffer_init(32);
1938 buffer_fadd( sql_buf, "\"%s\".%s %s %s", class, osrfHashGet(field, "name"), op, val );
1939 char* pred = buffer_release( sql_buf );
1946 static char* searchBETWEENPredicate (const char* class, osrfHash* field, jsonObject* node) {
1951 if ( !strcmp( get_primitive( field ), "number") ) {
1952 x_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,0));
1953 y_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,1));
1956 x_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,0));
1957 y_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,1));
1958 if ( !(dbi_conn_quote_string(dbhandle, &x_string) && dbi_conn_quote_string(dbhandle, &y_string)) ) {
1959 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key strings [%s] and [%s]", MODULENAME, x_string, y_string);
1966 growing_buffer* sql_buf = buffer_init(32);
1967 buffer_fadd( sql_buf, "%s BETWEEN %s AND %s", osrfHashGet(field, "name"), x_string, y_string );
1971 return buffer_release(sql_buf);
1974 static char* searchPredicate ( const char* class, osrfHash* field,
1975 jsonObject* node, osrfMethodContext* ctx ) {
1978 if (node->type == JSON_ARRAY) { // equality IN search
1979 pred = searchINPredicate( class, field, node, NULL, ctx );
1980 } else if (node->type == JSON_HASH) { // non-equality search
1981 jsonObject* pred_node;
1982 jsonIterator* pred_itr = jsonNewIterator( node );
1983 while ( (pred_node = jsonIteratorNext( pred_itr )) ) {
1984 if ( !(strcasecmp( pred_itr->key,"between" )) )
1985 pred = searchBETWEENPredicate( class, field, pred_node );
1986 else if ( !(strcasecmp( pred_itr->key,"in" )) || !(strcasecmp( pred_itr->key,"not in" )) )
1987 pred = searchINPredicate( class, field, pred_node, pred_itr->key, ctx );
1988 else if ( pred_node->type == JSON_ARRAY )
1989 pred = searchFunctionPredicate( class, field, pred_node, pred_itr->key );
1990 else if ( pred_node->type == JSON_HASH )
1991 pred = searchFieldTransformPredicate( class, field, pred_node, pred_itr->key );
1993 pred = searchSimplePredicate( pred_itr->key, class, field, pred_node );
1997 jsonIteratorFree(pred_itr);
1998 } else if (node->type == JSON_NULL) { // IS NULL search
1999 growing_buffer* _p = buffer_init(64);
2002 "\"%s\".%s IS NULL",
2004 osrfHashGet(field, "name")
2006 pred = buffer_release(_p);
2007 } else { // equality search
2008 pred = searchSimplePredicate( "=", class, field, node );
2027 field : call_number,
2043 static char* searchJOIN ( const jsonObject* join_hash, osrfHash* leftmeta ) {
2045 const jsonObject* working_hash;
2046 jsonObject* freeable_hash = NULL;
2048 if (join_hash->type == JSON_STRING) {
2049 // create a wrapper around a copy of the original
2050 const char* _tmp = jsonObjectGetString( join_hash );
2051 freeable_hash = jsonNewObjectType(JSON_HASH);
2052 jsonObjectSetKey(freeable_hash, _tmp, NULL);
2053 working_hash = freeable_hash;
2056 if( join_hash->type != JSON_HASH ) {
2059 "%s: JOIN failed; expected JSON object type not found",
2064 working_hash = join_hash;
2067 growing_buffer* join_buf = buffer_init(128);
2068 const char* leftclass = osrfHashGet(leftmeta, "classname");
2070 jsonObject* snode = NULL;
2071 jsonIterator* search_itr = jsonNewIterator( working_hash );
2073 while ( (snode = jsonIteratorNext( search_itr )) ) {
2074 const char* class = search_itr->key;
2075 osrfHash* idlClass = osrfHashGet( oilsIDL(), class );
2079 "%s: JOIN failed. No class \"%s\" defined in IDL",
2083 jsonIteratorFree( search_itr );
2084 buffer_free( join_buf );
2086 jsonObjectFree( freeable_hash );
2090 const char* fkey = jsonObjectGetString( jsonObjectGetKeyConst( snode, "fkey" ) );
2091 const char* field = jsonObjectGetString( jsonObjectGetKeyConst( snode, "field" ) );
2093 if (field && !fkey) {
2094 fkey = (const char*)oilsIDLFindPath("/%s/links/%s/key", class, field);
2098 "%s: JOIN failed. No link defined from %s.%s to %s",
2104 buffer_free(join_buf);
2106 jsonObjectFree(freeable_hash);
2107 jsonIteratorFree(search_itr);
2111 } else if (!field && fkey) {
2112 field = (const char*)oilsIDLFindPath("/%s/links/%s/key", leftclass, fkey );
2116 "%s: JOIN failed. No link defined from %s.%s to %s",
2122 buffer_free(join_buf);
2124 jsonObjectFree(freeable_hash);
2125 jsonIteratorFree(search_itr);
2130 } else if (!field && !fkey) {
2131 osrfHash* _links = oilsIDL_links( leftclass );
2133 // For each link defined for the left class:
2134 // see if the link references the joined class
2135 osrfHashIterator* itr = osrfNewHashIterator( _links );
2136 osrfHash* curr_link = NULL;
2137 while( (curr_link = osrfHashIteratorNext( itr ) ) ) {
2138 const char* other_class = osrfHashGet( curr_link, "class" );
2139 if( other_class && !strcmp( other_class, class ) ) {
2141 // Found a link between the classes
2142 fkey = osrfHashIteratorKey( itr );
2143 field = osrfHashGet( curr_link, "key" );
2147 osrfHashIteratorFree( itr );
2149 if (!field || !fkey) {
2150 // Do another such search, with the classes reversed
2151 _links = oilsIDL_links( class );
2153 // For each link defined for the joined class:
2154 // see if the link references the left class
2155 osrfHashIterator* itr = osrfNewHashIterator( _links );
2156 osrfHash* curr_link = NULL;
2157 while( (curr_link = osrfHashIteratorNext( itr ) ) ) {
2158 const char* other_class = osrfHashGet( curr_link, "class" );
2159 if( other_class && !strcmp( other_class, leftclass ) ) {
2161 // Found a link between the classes
2162 fkey = osrfHashIteratorKey( itr );
2163 field = osrfHashGet( curr_link, "key" );
2167 osrfHashIteratorFree( itr );
2170 if (!field || !fkey) {
2173 "%s: JOIN failed. No link defined between %s and %s",
2178 buffer_free(join_buf);
2180 jsonObjectFree(freeable_hash);
2181 jsonIteratorFree(search_itr);
2187 const char* type = jsonObjectGetString( jsonObjectGetKeyConst( snode, "type" ) );
2189 if ( !strcasecmp(type,"left") ) {
2190 buffer_add(join_buf, " LEFT JOIN");
2191 } else if ( !strcasecmp(type,"right") ) {
2192 buffer_add(join_buf, " RIGHT JOIN");
2193 } else if ( !strcasecmp(type,"full") ) {
2194 buffer_add(join_buf, " FULL JOIN");
2196 buffer_add(join_buf, " INNER JOIN");
2199 buffer_add(join_buf, " INNER JOIN");
2202 char* table = getSourceDefinition(idlClass);
2204 jsonIteratorFree( search_itr );
2205 buffer_free( join_buf );
2207 jsonObjectFree( freeable_hash );
2211 buffer_fadd(join_buf, " %s AS \"%s\" ON ( \"%s\".%s = \"%s\".%s",
2212 table, class, class, field, leftclass, fkey);
2215 const jsonObject* filter = jsonObjectGetKeyConst( snode, "filter" );
2217 const char* filter_op = jsonObjectGetString( jsonObjectGetKeyConst( snode, "filter_op" ) );
2218 if ( filter_op && !strcasecmp("or",filter_op) ) {
2219 buffer_add( join_buf, " OR " );
2221 buffer_add( join_buf, " AND " );
2224 char* jpred = searchWHERE( filter, idlClass, AND_OP_JOIN, NULL );
2226 OSRF_BUFFER_ADD_CHAR( join_buf, ' ' );
2227 OSRF_BUFFER_ADD( join_buf, jpred );
2232 "%s: JOIN failed. Invalid conditional expression.",
2235 jsonIteratorFree( search_itr );
2236 buffer_free( join_buf );
2238 jsonObjectFree( freeable_hash );
2243 buffer_add(join_buf, " ) ");
2245 const jsonObject* join_filter = jsonObjectGetKeyConst( snode, "join" );
2247 char* jpred = searchJOIN( join_filter, idlClass );
2248 OSRF_BUFFER_ADD_CHAR( join_buf, ' ' );
2249 OSRF_BUFFER_ADD( join_buf, jpred );
2255 jsonObjectFree(freeable_hash);
2256 jsonIteratorFree(search_itr);
2258 return buffer_release(join_buf);
2263 { +class : { -or|-and : { field : { op : value }, ... } ... }, ... }
2264 { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }
2265 [ { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }, ... ]
2267 Generate code to express a set of conditions, as for a WHERE clause. Parameters:
2269 search_hash is the JSON expression of the conditions.
2270 meta is the class definition from the IDL, for the relevant table.
2271 opjoin_type indicates whether multiple conditions, if present, should be
2272 connected by AND or OR.
2273 osrfMethodContext is loaded with all sorts of stuff, but all we do with it here is
2274 to pass it to other functions -- and all they do with it is to use the session
2275 and request members to send error messages back to the client.
2279 static char* searchWHERE ( const jsonObject* search_hash, osrfHash* meta, int opjoin_type, osrfMethodContext* ctx ) {
2283 "%s: Entering searchWHERE; search_hash addr = %p, meta addr = %p, opjoin_type = %d, ctx addr = %p",
2291 growing_buffer* sql_buf = buffer_init(128);
2293 jsonObject* node = NULL;
2296 if ( search_hash->type == JSON_ARRAY ) {
2297 osrfLogDebug(OSRF_LOG_MARK, "%s: In WHERE clause, condition type is JSON_ARRAY", MODULENAME);
2298 jsonIterator* search_itr = jsonNewIterator( search_hash );
2299 if( !jsonIteratorHasNext( search_itr ) ) {
2302 "%s: Invalid predicate structure: empty JSON array",
2305 jsonIteratorFree( search_itr );
2306 buffer_free( sql_buf );
2310 while ( (node = jsonIteratorNext( search_itr )) ) {
2314 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
2315 else buffer_add(sql_buf, " AND ");
2318 char* subpred = searchWHERE( node, meta, opjoin_type, ctx );
2320 buffer_fadd(sql_buf, "( %s )", subpred);
2323 jsonIteratorFree( search_itr );
2324 buffer_free( sql_buf );
2328 jsonIteratorFree(search_itr);
2330 } else if ( search_hash->type == JSON_HASH ) {
2331 osrfLogDebug(OSRF_LOG_MARK, "%s: In WHERE clause, condition type is JSON_HASH", MODULENAME);
2332 jsonIterator* search_itr = jsonNewIterator( search_hash );
2333 if( !jsonIteratorHasNext( search_itr ) ) {
2336 "%s: Invalid predicate structure: empty JSON object",
2339 jsonIteratorFree( search_itr );
2340 buffer_free( sql_buf );
2344 while ( (node = jsonIteratorNext( search_itr )) ) {
2349 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
2350 else buffer_add(sql_buf, " AND ");
2353 if ( '+' == search_itr->key[ 0 ] ) {
2354 if ( node->type == JSON_STRING ) {
2355 // Intended purpose; to allow reference to a Boolean column
2357 // Verify that the class alias is not empty
2358 if( '\0' == search_itr->key[ 1 ] ) {
2361 "%s: Table alias is empty",
2364 jsonIteratorFree( search_itr );
2365 buffer_free( sql_buf );
2369 // Verify that the string looks like an identifier.
2370 const char* subpred = jsonObjectGetString( node );
2371 if( ! is_identifier( subpred ) ) {
2374 "%s: Invalid boolean identifier in WHERE clause: \"%s\"",
2378 jsonIteratorFree( search_itr );
2379 buffer_free( sql_buf );
2383 buffer_fadd(sql_buf, " \"%s\".%s ", search_itr->key + 1, subpred);
2385 char* subpred = searchWHERE( node, osrfHashGet( oilsIDL(), search_itr->key + 1 ), AND_OP_JOIN, ctx );
2387 buffer_fadd(sql_buf, "( %s )", subpred);
2390 jsonIteratorFree( search_itr );
2391 buffer_free( sql_buf );
2395 } else if ( !strcasecmp("-or",search_itr->key) ) {
2396 char* subpred = searchWHERE( node, meta, OR_OP_JOIN, ctx );
2398 buffer_fadd(sql_buf, "( %s )", subpred);
2401 buffer_free( sql_buf );
2405 } else if ( !strcasecmp("-and",search_itr->key) ) {
2406 char* subpred = searchWHERE( node, meta, AND_OP_JOIN, ctx );
2408 buffer_fadd(sql_buf, "( %s )", subpred);
2411 buffer_free( sql_buf );
2414 } else if ( !strcasecmp("-exists",search_itr->key) ) {
2415 char* subpred = SELECT(
2417 jsonObjectGetKey( node, "select" ),
2418 jsonObjectGetKey( node, "from" ),
2419 jsonObjectGetKey( node, "where" ),
2420 jsonObjectGetKey( node, "having" ),
2421 jsonObjectGetKey( node, "order_by" ),
2422 jsonObjectGetKey( node, "limit" ),
2423 jsonObjectGetKey( node, "offset" ),
2427 buffer_fadd(sql_buf, "EXISTS ( %s )", subpred);
2429 } else if ( !strcasecmp("-not-exists",search_itr->key) ) {
2430 char* subpred = SELECT(
2432 jsonObjectGetKey( node, "select" ),
2433 jsonObjectGetKey( node, "from" ),
2434 jsonObjectGetKey( node, "where" ),
2435 jsonObjectGetKey( node, "having" ),
2436 jsonObjectGetKey( node, "order_by" ),
2437 jsonObjectGetKey( node, "limit" ),
2438 jsonObjectGetKey( node, "offset" ),
2442 buffer_fadd(sql_buf, "NOT EXISTS ( %s )", subpred);
2446 char* class = osrfHashGet(meta, "classname");
2447 osrfHash* fields = osrfHashGet(meta, "fields");
2448 osrfHash* field = osrfHashGet( fields, search_itr->key );
2452 char* table = getSourceDefinition(meta);
2454 table = strdup( "(?)" );
2457 "%s: Attempt to reference non-existent column %s on %s (%s)",
2463 buffer_free(sql_buf);
2465 jsonIteratorFree(search_itr);
2469 char* subpred = searchPredicate( class, field, node, ctx );
2470 buffer_add( sql_buf, subpred );
2474 jsonIteratorFree(search_itr);
2477 // ERROR ... only hash and array allowed at this level
2478 char* predicate_string = jsonObjectToJSON( search_hash );
2481 "%s: Invalid predicate structure: %s",
2485 buffer_free(sql_buf);
2486 free(predicate_string);
2491 return buffer_release(sql_buf);
2495 /* method context */ osrfMethodContext* ctx,
2497 /* SELECT */ jsonObject* selhash,
2498 /* FROM */ jsonObject* join_hash,
2499 /* WHERE */ jsonObject* search_hash,
2500 /* HAVING */ jsonObject* having_hash,
2501 /* ORDER BY */ jsonObject* order_hash,
2502 /* LIMIT */ jsonObject* limit,
2503 /* OFFSET */ jsonObject* offset,
2504 /* flags */ int flags
2506 const char* locale = osrf_message_get_last_locale();
2508 // in case we don't get a select list
2509 jsonObject* defaultselhash = NULL;
2511 // general tmp objects
2512 const jsonObject* tmp_const;
2513 jsonObject* selclass = NULL;
2514 jsonObject* selfield = NULL;
2515 jsonObject* snode = NULL;
2516 jsonObject* onode = NULL;
2518 char* string = NULL;
2519 int from_function = 0;
2524 // the core search class
2525 char* core_class = NULL;
2527 // metadata about the core search class
2528 osrfHash* core_meta = NULL;
2530 // punt if there's no core class
2531 if (!join_hash || ( join_hash->type == JSON_HASH && !join_hash->size )) {
2534 "%s: FROM clause is missing or empty",
2538 osrfAppSessionStatus(
2540 OSRF_STATUS_INTERNALSERVERERROR,
2541 "osrfMethodException",
2543 "FROM clause is missing or empty in JSON query"
2548 // get the core class -- the only key of the top level FROM clause, or a string
2549 if (join_hash->type == JSON_HASH) {
2550 jsonIterator* tmp_itr = jsonNewIterator( join_hash );
2551 snode = jsonIteratorNext( tmp_itr );
2553 core_class = strdup( tmp_itr->key );
2556 jsonObject* extra = jsonIteratorNext( tmp_itr );
2558 jsonIteratorFree( tmp_itr );
2561 // There shouldn't be more than one entry in join_hash
2565 "%s: Malformed FROM clause: extra entry in JSON_HASH",
2569 osrfAppSessionStatus(
2571 OSRF_STATUS_INTERNALSERVERERROR,
2572 "osrfMethodException",
2574 "Malformed FROM clause in JSON query"
2577 return NULL; // Malformed join_hash; extra entry
2579 } else if (join_hash->type == JSON_ARRAY) {
2581 core_class = jsonObjectToSimpleString( jsonObjectGetIndex(join_hash, 0) );
2584 } else if (join_hash->type == JSON_STRING) {
2585 core_class = jsonObjectToSimpleString( join_hash );
2591 "%s: FROM clause is unexpected JSON type: %s",
2593 json_type( join_hash->type )
2596 osrfAppSessionStatus(
2598 OSRF_STATUS_INTERNALSERVERERROR,
2599 "osrfMethodException",
2601 "Ill-formed FROM clause in JSON query"
2607 if (!from_function) {
2608 // Get the IDL class definition for the core class
2609 core_meta = osrfHashGet( oilsIDL(), core_class );
2610 if( !core_meta ) { // Didn't find it?
2613 "%s: SELECT clause references undefined class: \"%s\"",
2618 osrfAppSessionStatus(
2620 OSRF_STATUS_INTERNALSERVERERROR,
2621 "osrfMethodException",
2623 "SELECT clause references undefined class in JSON query"
2629 // Make sure the class isn't virtual
2630 if( str_is_true( osrfHashGet( core_meta, "virtual" ) ) ) {
2633 "%s: Core class is virtual: \"%s\"",
2638 osrfAppSessionStatus(
2640 OSRF_STATUS_INTERNALSERVERERROR,
2641 "osrfMethodException",
2643 "FROM clause references virtual class in JSON query"
2650 // if the select list is empty, or the core class field list is '*',
2651 // build the default select list ...
2653 selhash = defaultselhash = jsonNewObjectType(JSON_HASH);
2654 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2655 } else if( selhash->type != JSON_HASH ) {
2658 "%s: Expected JSON_HASH for SELECT clause; found %s",
2660 json_type( selhash->type )
2664 osrfAppSessionStatus(
2666 OSRF_STATUS_INTERNALSERVERERROR,
2667 "osrfMethodException",
2669 "Malformed SELECT clause in JSON query"
2673 } else if ( (tmp_const = jsonObjectGetKeyConst( selhash, core_class )) && tmp_const->type == JSON_STRING ) {
2674 const char* _x = jsonObjectGetString( tmp_const );
2675 if (!strncmp( "*", _x, 1 )) {
2676 jsonObjectRemoveKey( selhash, core_class );
2677 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2682 growing_buffer* sql_buf = buffer_init(128);
2684 // temp buffer for the SELECT list
2685 growing_buffer* select_buf = buffer_init(128);
2686 growing_buffer* order_buf = buffer_init(128);
2687 growing_buffer* group_buf = buffer_init(128);
2688 growing_buffer* having_buf = buffer_init(128);
2690 int aggregate_found = 0; // boolean
2692 // Build a select list
2693 if(from_function) // From a function we select everything
2694 OSRF_BUFFER_ADD_CHAR( select_buf, '*' );
2697 // If we need to build a default list, prepare to do so
2698 jsonObject* _tmp = jsonObjectGetKey( selhash, core_class );
2699 if ( _tmp && !_tmp->size ) {
2701 osrfHash* core_fields = osrfHashGet( core_meta, "fields" );
2703 osrfHashIterator* field_itr = osrfNewHashIterator( core_fields );
2704 osrfHash* field_def;
2705 while( ( field_def = osrfHashIteratorNext( field_itr ) ) ) {
2706 if( ! str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2707 // This field is not virtual, so add it to the list
2708 jsonObjectPush( _tmp, jsonNewObject( osrfHashIteratorKey( field_itr ) ) );
2711 osrfHashIteratorFree( field_itr );
2714 // Now build the actual select list
2718 jsonIterator* selclass_itr = jsonNewIterator( selhash );
2719 while ( (selclass = jsonIteratorNext( selclass_itr )) ) { // For each class
2721 // Make sure the class is defined in the IDL
2722 const char* cname = selclass_itr->key;
2723 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
2727 "%s: Selected class \"%s\" not defined in IDL",
2733 osrfAppSessionStatus(
2735 OSRF_STATUS_INTERNALSERVERERROR,
2736 "osrfMethodException",
2738 "Selected class is not defined"
2740 jsonIteratorFree( selclass_itr );
2741 buffer_free( sql_buf );
2742 buffer_free( select_buf );
2743 buffer_free( order_buf );
2744 buffer_free( group_buf );
2745 buffer_free( having_buf );
2746 if( defaultselhash ) jsonObjectFree( defaultselhash );
2751 // Make sure the target relation is in the join tree.
2753 // At this point join_hash is a step down from the join_hash we
2754 // received as a parameter. If the original was a JSON_STRING,
2755 // then json_hash is now NULL. If the original was a JSON_HASH,
2756 // then json_hash is now the first (and only) entry in it,
2757 // denoting the core class. We've already excluded the
2758 // possibility that the original was a JSON_ARRAY, because in
2759 // that case from_function would be non-NULL, and we wouldn't
2762 int class_in_from_clause; // boolean
2764 if ( ! strcmp( core_class, cname ))
2765 // This is the core class -- no problem
2766 class_in_from_clause = 1;
2769 // There's only one class in the FROM clause, and this isn't it
2770 class_in_from_clause = 0;
2771 else if (join_hash->type == JSON_STRING) {
2772 // There's only one class in the FROM clause
2773 const char* str = jsonObjectGetString(join_hash);
2774 if ( strcmp( str, cname ) )
2775 class_in_from_clause = 0; // This isn't it
2777 class_in_from_clause = 1; // This is it
2779 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", cname);
2780 if ( 0 == found->size )
2781 class_in_from_clause = 0; // Nowhere in the join tree
2783 class_in_from_clause = 1; // Found it
2784 jsonObjectFree( found );
2788 // If the class isn't in the FROM clause, bail out
2789 if( ! class_in_from_clause ) {
2792 "%s: SELECT clause references class not in FROM clause: \"%s\"",
2797 osrfAppSessionStatus(
2799 OSRF_STATUS_INTERNALSERVERERROR,
2800 "osrfMethodException",
2802 "Selected class not in FROM clause in JSON query"
2804 jsonIteratorFree( selclass_itr );
2805 buffer_free( sql_buf );
2806 buffer_free( select_buf );
2807 buffer_free( order_buf );
2808 buffer_free( group_buf );
2809 buffer_free( having_buf );
2810 if( defaultselhash ) jsonObjectFree( defaultselhash );
2815 // Look up some attributes of the current class, so that we
2816 // don't have to look them up again for each field
2817 osrfHash* class_field_set = osrfHashGet( idlClass, "fields" );
2818 const char* class_pkey = osrfHashGet( idlClass, "primarykey" );
2819 const char* class_tname = osrfHashGet( idlClass, "tablename" );
2821 // stitch together the column list ...
2822 jsonIterator* select_itr = jsonNewIterator( selclass );
2823 while ( (selfield = jsonIteratorNext( select_itr )) ) { // for each SELECT column
2825 // If we need a separator comma, add one
2829 OSRF_BUFFER_ADD_CHAR( select_buf, ',' );
2832 // ... if it's a string, just toss it on the pile
2833 if (selfield->type == JSON_STRING) {
2835 // Look up the field in the IDL
2836 const char* col_name = jsonObjectGetString( selfield );
2837 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
2839 // No such field in current class
2842 "%s: Selected column \"%s\" not defined in IDL for class \"%s\"",
2848 osrfAppSessionStatus(
2850 OSRF_STATUS_INTERNALSERVERERROR,
2851 "osrfMethodException",
2853 "Selected column not defined in JSON query"
2855 jsonIteratorFree( select_itr );
2856 jsonIteratorFree( selclass_itr );
2857 buffer_free( sql_buf );
2858 buffer_free( select_buf );
2859 buffer_free( order_buf );
2860 buffer_free( group_buf );
2861 buffer_free( having_buf );
2862 if( defaultselhash ) jsonObjectFree( defaultselhash );
2865 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2866 // Virtual field not allowed
2869 "%s: Selected column \"%s\" for class \"%s\" is virtual",
2875 osrfAppSessionStatus(
2877 OSRF_STATUS_INTERNALSERVERERROR,
2878 "osrfMethodException",
2880 "Selected column may not be virtual in JSON query"
2882 jsonIteratorFree( select_itr );
2883 jsonIteratorFree( selclass_itr );
2884 buffer_free( sql_buf );
2885 buffer_free( select_buf );
2886 buffer_free( order_buf );
2887 buffer_free( group_buf );
2888 buffer_free( having_buf );
2889 if( defaultselhash ) jsonObjectFree( defaultselhash );
2896 if (flags & DISABLE_I18N)
2899 i18n = osrfHashGet(field_def, "i18n");
2901 if( str_is_true( i18n ) ) {
2902 buffer_fadd( select_buf,
2903 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
2904 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, col_name );
2906 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
2909 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
2912 // ... but it could be an object, in which case we check for a Field Transform
2913 } else if (selfield->type == JSON_HASH) {
2915 const char* col_name = jsonObjectGetString( jsonObjectGetKeyConst( selfield, "column" ) );
2917 // Get the field definition from the IDL
2918 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
2920 // No such field in current class
2923 "%s: Selected column \"%s\" is not defined in IDL for class \"%s\"",
2929 osrfAppSessionStatus(
2931 OSRF_STATUS_INTERNALSERVERERROR,
2932 "osrfMethodException",
2934 "Selected column is not defined in JSON query"
2936 jsonIteratorFree( select_itr );
2937 jsonIteratorFree( selclass_itr );
2938 buffer_free( sql_buf );
2939 buffer_free( select_buf );
2940 buffer_free( order_buf );
2941 buffer_free( group_buf );
2942 buffer_free( having_buf );
2943 if( defaultselhash ) jsonObjectFree( defaultselhash );
2946 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2947 // No such field in current class
2950 "%s: Selected column \"%s\" is virtual for class \"%s\"",
2956 osrfAppSessionStatus(
2958 OSRF_STATUS_INTERNALSERVERERROR,
2959 "osrfMethodException",
2961 "Selected column is virtual in JSON query"
2963 jsonIteratorFree( select_itr );
2964 jsonIteratorFree( selclass_itr );
2965 buffer_free( sql_buf );
2966 buffer_free( select_buf );
2967 buffer_free( order_buf );
2968 buffer_free( group_buf );
2969 buffer_free( having_buf );
2970 if( defaultselhash ) jsonObjectFree( defaultselhash );
2975 // Decide what to use as a column alias
2977 if ((tmp_const = jsonObjectGetKeyConst( selfield, "alias" ))) {
2978 _alias = jsonObjectGetString( tmp_const );
2979 } else { // Use field name as the alias
2983 if (jsonObjectGetKeyConst( selfield, "transform" )) {
2984 char* transform_str = searchFieldTransform(cname, field_def, selfield);
2985 if( transform_str ) {
2986 buffer_fadd(select_buf, " %s AS \"%s\"", transform_str, _alias);
2987 free(transform_str);
2990 osrfAppSessionStatus(
2992 OSRF_STATUS_INTERNALSERVERERROR,
2993 "osrfMethodException",
2995 "Unable to generate transform function in JSON query"
2997 jsonIteratorFree( select_itr );
2998 jsonIteratorFree( selclass_itr );
2999 buffer_free( sql_buf );
3000 buffer_free( select_buf );
3001 buffer_free( order_buf );
3002 buffer_free( group_buf );
3003 buffer_free( having_buf );
3004 if( defaultselhash ) jsonObjectFree( defaultselhash );
3012 if (flags & DISABLE_I18N)
3015 i18n = osrfHashGet(field_def, "i18n");
3017 if( str_is_true( i18n ) ) {
3018 buffer_fadd( select_buf,
3019 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
3020 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, _alias);
3022 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
3025 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
3032 "%s: Selected item is unexpected JSON type: %s",
3034 json_type( selfield->type )
3037 osrfAppSessionStatus(
3039 OSRF_STATUS_INTERNALSERVERERROR,
3040 "osrfMethodException",
3042 "Ill-formed SELECT item in JSON query"
3044 jsonIteratorFree( select_itr );
3045 jsonIteratorFree( selclass_itr );
3046 buffer_free( sql_buf );
3047 buffer_free( select_buf );
3048 buffer_free( order_buf );
3049 buffer_free( group_buf );
3050 buffer_free( having_buf );
3051 if( defaultselhash ) jsonObjectFree( defaultselhash );
3056 const jsonObject* agg_obj = jsonObjectGetKey( selfield, "aggregate" );
3057 if( obj_is_true( agg_obj ) )
3058 aggregate_found = 1;
3060 // Append a comma (except for the first one)
3061 // and add the column to a GROUP BY clause
3065 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3067 buffer_fadd(group_buf, " %d", sel_pos);
3071 if (is_agg->size || (flags & SELECT_DISTINCT)) {
3073 const jsonObject* aggregate_obj = jsonObjectGetKey( selfield, "aggregate" );
3074 if ( ! obj_is_true( aggregate_obj ) ) {
3078 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3081 buffer_fadd(group_buf, " %d", sel_pos);
3084 } else if (is_agg = jsonObjectGetKey( selfield, "having" )) {
3088 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3091 _column = searchFieldTransform(cname, field, selfield);
3092 OSRF_BUFFER_ADD_CHAR(group_buf, ' ');
3093 OSRF_BUFFER_ADD(group_buf, _column);
3094 _column = searchFieldTransform(cname, field, selfield);
3101 } // end while -- iterating across SELECT columns
3103 jsonIteratorFree(select_itr);
3104 } // end while -- iterating across classes
3106 jsonIteratorFree(selclass_itr);
3110 char* col_list = buffer_release(select_buf);
3112 if (from_function) table = searchValueTransform(join_hash);
3113 else table = getSourceDefinition(core_meta);
3117 osrfAppSessionStatus(
3119 OSRF_STATUS_INTERNALSERVERERROR,
3120 "osrfMethodException",
3122 "Unable to identify table for core class"
3125 buffer_free( sql_buf );
3126 buffer_free( order_buf );
3127 buffer_free( group_buf );
3128 buffer_free( having_buf );
3129 if( defaultselhash ) jsonObjectFree( defaultselhash );
3134 // Put it all together
3135 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\" ", col_list, table, core_class );
3139 if (!from_function) {
3140 // Now, walk the join tree and add that clause
3142 char* join_clause = searchJOIN( join_hash, core_meta );
3144 buffer_add(sql_buf, join_clause);
3148 osrfAppSessionStatus(
3150 OSRF_STATUS_INTERNALSERVERERROR,
3151 "osrfMethodException",
3153 "Unable to construct JOIN clause(s)"
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 );
3165 // Build a WHERE clause, if there is one
3166 if ( search_hash ) {
3167 buffer_add(sql_buf, " WHERE ");
3169 // and it's on the WHERE clause
3170 char* pred = searchWHERE( search_hash, core_meta, AND_OP_JOIN, ctx );
3173 buffer_add(sql_buf, pred);
3177 osrfAppSessionStatus(
3179 OSRF_STATUS_INTERNALSERVERERROR,
3180 "osrfMethodException",
3182 "Severe query error in WHERE predicate -- see error log for more details"
3186 buffer_free(having_buf);
3187 buffer_free(group_buf);
3188 buffer_free(order_buf);
3189 buffer_free(sql_buf);
3190 if (defaultselhash) jsonObjectFree(defaultselhash);
3195 // Build a HAVING clause, if there is one
3196 if ( having_hash ) {
3197 buffer_add(sql_buf, " HAVING ");
3199 // and it's on the the WHERE clause
3200 char* pred = searchWHERE( having_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 HAVING 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 an ORDER BY clause, if there is one
3227 jsonIterator* class_itr = jsonNewIterator( order_hash );
3228 while ( (snode = jsonIteratorNext( class_itr )) ) {
3230 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
3233 if ( snode->type == JSON_HASH ) {
3235 jsonIterator* order_itr = jsonNewIterator( snode );
3236 while ( (onode = jsonIteratorNext( order_itr )) ) {
3238 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ))
3241 const char* direction = NULL;
3242 if ( onode->type == JSON_HASH ) {
3243 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3244 string = searchFieldTransform(
3246 oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ),
3250 growing_buffer* field_buf = buffer_init(16);
3251 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3252 string = buffer_release(field_buf);
3255 if ( (tmp_const = jsonObjectGetKeyConst( onode, "direction" )) ) {
3256 const char* dir = jsonObjectGetString(tmp_const);
3257 if (!strncasecmp(dir, "d", 1)) {
3258 direction = " DESC";
3265 string = strdup(order_itr->key);
3266 const char* dir = jsonObjectGetString(onode);
3267 if (!strncasecmp(dir, "d", 1)) {
3268 direction = " DESC";
3277 buffer_add(order_buf, ", ");
3280 buffer_add(order_buf, string);
3284 buffer_add(order_buf, direction);
3288 // jsonIteratorFree(order_itr);
3290 } else if ( snode->type == JSON_ARRAY ) {
3292 jsonIterator* order_itr = jsonNewIterator( snode );
3293 while ( (onode = jsonIteratorNext( order_itr )) ) {
3295 const char* _f = jsonObjectGetString( onode );
3297 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, _f))
3303 buffer_add(order_buf, ", ");
3306 buffer_add(order_buf, _f);
3309 // jsonIteratorFree(order_itr);
3312 // IT'S THE OOOOOOOOOOOLD STYLE!
3314 osrfLogError(OSRF_LOG_MARK, "%s: Possible SQL injection attempt; direct order by is not allowed", MODULENAME);
3316 osrfAppSessionStatus(
3318 OSRF_STATUS_INTERNALSERVERERROR,
3319 "osrfMethodException",
3321 "Severe query error -- see error log for more details"
3326 buffer_free(having_buf);
3327 buffer_free(group_buf);
3328 buffer_free(order_buf);
3329 buffer_free(sql_buf);
3330 if (defaultselhash) jsonObjectFree(defaultselhash);
3331 jsonIteratorFree(class_itr);
3336 // jsonIteratorFree(class_itr);
3340 string = buffer_release(group_buf);
3342 if ( *string && ( aggregate_found || (flags & SELECT_DISTINCT) ) ) {
3343 OSRF_BUFFER_ADD( sql_buf, " GROUP BY " );
3344 OSRF_BUFFER_ADD( sql_buf, string );
3349 string = buffer_release(having_buf);
3352 OSRF_BUFFER_ADD( sql_buf, " HAVING " );
3353 OSRF_BUFFER_ADD( sql_buf, string );
3358 string = buffer_release(order_buf);
3361 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3362 OSRF_BUFFER_ADD( sql_buf, string );
3368 const char* str = jsonObjectGetString(limit);
3369 buffer_fadd( sql_buf, " LIMIT %d", atoi(str) );
3373 const char* str = jsonObjectGetString(offset);
3374 buffer_fadd( sql_buf, " OFFSET %d", atoi(str) );
3377 if (!(flags & SUBSELECT)) OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3380 if (defaultselhash) jsonObjectFree(defaultselhash);
3382 return buffer_release(sql_buf);
3386 static char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
3388 const char* locale = osrf_message_get_last_locale();
3390 osrfHash* fields = osrfHashGet(meta, "fields");
3391 char* core_class = osrfHashGet(meta, "classname");
3393 const jsonObject* join_hash = jsonObjectGetKeyConst( order_hash, "join" );
3395 jsonObject* node = NULL;
3396 jsonObject* snode = NULL;
3397 jsonObject* onode = NULL;
3398 const jsonObject* _tmp = NULL;
3399 jsonObject* selhash = NULL;
3400 jsonObject* defaultselhash = NULL;
3402 growing_buffer* sql_buf = buffer_init(128);
3403 growing_buffer* select_buf = buffer_init(128);
3405 if ( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
3406 defaultselhash = jsonNewObjectType(JSON_HASH);
3407 selhash = defaultselhash;
3410 if ( !jsonObjectGetKeyConst(selhash,core_class) ) {
3411 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
3412 jsonObject* flist = jsonObjectGetKey( selhash, core_class );
3417 osrfStringArray* keys = osrfHashKeys( fields );
3418 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
3419 if( ! str_is_true( osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
3420 jsonObjectPush( flist, jsonNewObject( field ) );
3422 osrfStringArrayFree(keys);
3426 jsonIterator* class_itr = jsonNewIterator( selhash );
3427 while ( (snode = jsonIteratorNext( class_itr )) ) {
3429 char* cname = class_itr->key;
3430 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
3431 if (!idlClass) continue;
3433 if (strcmp(core_class,class_itr->key)) {
3434 if (!join_hash) continue;
3436 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", class_itr->key);
3438 jsonObjectFree(found);
3442 jsonObjectFree(found);
3445 jsonIterator* select_itr = jsonNewIterator( snode );
3446 while ( (node = jsonIteratorNext( select_itr )) ) {
3447 const char* item_str = jsonObjectGetString( node );
3448 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), item_str );
3449 char* fname = osrfHashGet(field, "name");
3451 if (!field) continue;
3456 OSRF_BUFFER_ADD_CHAR(select_buf, ',');
3461 const jsonObject* no_i18n_obj = jsonObjectGetKey( order_hash, "no_i18n" );
3462 if ( obj_is_true( no_i18n_obj ) ) // Suppress internationalization?
3465 i18n = osrfHashGet(field, "i18n");
3467 if( str_is_true( i18n ) ) {
3468 char* pkey = osrfHashGet(idlClass, "primarykey");
3469 char* tname = osrfHashGet(idlClass, "tablename");
3471 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);
3473 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3476 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3480 jsonIteratorFree(select_itr);
3483 jsonIteratorFree(class_itr);
3485 char* col_list = buffer_release(select_buf);
3486 char* table = getSourceDefinition(meta);
3488 table = strdup( "(null)" );
3490 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\"", col_list, table, core_class );
3495 char* join_clause = searchJOIN( join_hash, meta );
3496 OSRF_BUFFER_ADD_CHAR(sql_buf, ' ');
3497 OSRF_BUFFER_ADD(sql_buf, join_clause);
3501 osrfLogDebug(OSRF_LOG_MARK, "%s pre-predicate SQL = %s",
3502 MODULENAME, OSRF_BUFFER_C_STR(sql_buf));
3504 buffer_add(sql_buf, " WHERE ");
3506 char* pred = searchWHERE( search_hash, meta, AND_OP_JOIN, ctx );
3508 osrfAppSessionStatus(
3510 OSRF_STATUS_INTERNALSERVERERROR,
3511 "osrfMethodException",
3513 "Severe query error -- see error log for more details"
3515 buffer_free(sql_buf);
3516 if(defaultselhash) jsonObjectFree(defaultselhash);
3519 buffer_add(sql_buf, pred);
3524 char* string = NULL;
3525 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "order_by" )) ){
3527 growing_buffer* order_buf = buffer_init(128);
3530 jsonIterator* class_itr = jsonNewIterator( _tmp );
3531 while ( (snode = jsonIteratorNext( class_itr )) ) {
3533 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
3536 if ( snode->type == JSON_HASH ) {
3538 jsonIterator* order_itr = jsonNewIterator( snode );
3539 while ( (onode = jsonIteratorNext( order_itr )) ) {
3541 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ))
3544 char* direction = NULL;
3545 if ( onode->type == JSON_HASH ) {
3546 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3547 string = searchFieldTransform(
3549 oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ),
3553 growing_buffer* field_buf = buffer_init(16);
3554 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3555 string = buffer_release(field_buf);
3558 if ( (_tmp = jsonObjectGetKeyConst( onode, "direction" )) ) {
3559 const char* dir = jsonObjectGetString(_tmp);
3560 if (!strncasecmp(dir, "d", 1)) {
3561 direction = " DESC";
3568 string = strdup(order_itr->key);
3569 const char* dir = jsonObjectGetString(onode);
3570 if (!strncasecmp(dir, "d", 1)) {
3571 direction = " DESC";
3580 buffer_add(order_buf, ", ");
3583 buffer_add(order_buf, string);
3587 buffer_add(order_buf, direction);
3592 jsonIteratorFree(order_itr);
3595 const char* str = jsonObjectGetString(snode);
3596 buffer_add(order_buf, str);
3602 jsonIteratorFree(class_itr);
3604 string = buffer_release(order_buf);
3607 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3608 OSRF_BUFFER_ADD( sql_buf, string );
3614 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "limit" )) ){
3615 const char* str = jsonObjectGetString(_tmp);
3623 _tmp = jsonObjectGetKeyConst( order_hash, "offset" );
3625 const char* str = jsonObjectGetString(_tmp);
3634 if (defaultselhash) jsonObjectFree(defaultselhash);
3636 OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3637 return buffer_release(sql_buf);
3640 int doJSONSearch ( osrfMethodContext* ctx ) {
3641 if(osrfMethodVerifyContext( ctx )) {
3642 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
3646 osrfLogDebug(OSRF_LOG_MARK, "Recieved query request");
3651 dbhandle = writehandle;
3653 jsonObject* hash = jsonObjectGetIndex(ctx->params, 0);
3657 if ( obj_is_true( jsonObjectGetKey( hash, "distinct" ) ) )
3658 flags |= SELECT_DISTINCT;
3660 if ( obj_is_true( jsonObjectGetKey( hash, "no_i18n" ) ) )
3661 flags |= DISABLE_I18N;
3663 osrfLogDebug(OSRF_LOG_MARK, "Building SQL ...");
3666 jsonObjectGetKey( hash, "select" ),
3667 jsonObjectGetKey( hash, "from" ),
3668 jsonObjectGetKey( hash, "where" ),
3669 jsonObjectGetKey( hash, "having" ),
3670 jsonObjectGetKey( hash, "order_by" ),
3671 jsonObjectGetKey( hash, "limit" ),
3672 jsonObjectGetKey( hash, "offset" ),
3681 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
3682 dbi_result result = dbi_conn_query(dbhandle, sql);
3685 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
3687 if (dbi_result_first_row(result)) {
3688 /* JSONify the result */
3689 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
3692 jsonObject* return_val = oilsMakeJSONFromResult( result );
3693 osrfAppRespond( ctx, return_val );
3694 jsonObjectFree( return_val );
3695 } while (dbi_result_next_row(result));
3698 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
3701 osrfAppRespondComplete( ctx, NULL );
3703 /* clean up the query */
3704 dbi_result_free(result);
3708 osrfLogError(OSRF_LOG_MARK, "%s: Error with query [%s]", MODULENAME, sql);
3709 osrfAppSessionStatus(
3711 OSRF_STATUS_INTERNALSERVERERROR,
3712 "osrfMethodException",
3714 "Severe query error -- see error log for more details"
3722 static jsonObject* doFieldmapperSearch ( osrfMethodContext* ctx, osrfHash* meta,
3723 const jsonObject* params, int* err ) {
3726 dbhandle = writehandle;
3728 osrfHash* links = osrfHashGet(meta, "links");
3729 osrfHash* fields = osrfHashGet(meta, "fields");
3730 char* core_class = osrfHashGet(meta, "classname");
3731 char* pkey = osrfHashGet(meta, "primarykey");
3733 const jsonObject* _tmp;
3735 jsonObject* search_hash = jsonObjectGetIndex(params, 0);
3736 jsonObject* order_hash = jsonObjectGetIndex(params, 1);
3738 char* sql = buildSELECT( search_hash, order_hash, meta, ctx );
3740 osrfLogDebug(OSRF_LOG_MARK, "Problem building query, returning NULL");
3745 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
3747 dbi_result result = dbi_conn_query(dbhandle, sql);
3748 if( NULL == result ) {
3749 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]",
3750 MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
3751 osrfAppSessionStatus(
3753 OSRF_STATUS_INTERNALSERVERERROR,
3754 "osrfMethodException",
3756 "Severe query error -- see error log for more details"
3763 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
3766 jsonObject* res_list = jsonNewObjectType(JSON_ARRAY);
3767 osrfHash* dedup = osrfNewHash();
3769 if (dbi_result_first_row(result)) {
3770 /* JSONify the result */
3771 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
3773 obj = oilsMakeFieldmapperFromResult( result, meta );
3774 char* pkey_val = oilsFMGetString( obj, pkey );
3775 if ( osrfHashGet( dedup, pkey_val ) ) {
3776 jsonObjectFree(obj);
3779 osrfHashSet( dedup, pkey_val, pkey_val );
3780 jsonObjectPush(res_list, obj);
3782 } while (dbi_result_next_row(result));
3784 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s",
3788 osrfHashFree(dedup);
3789 /* clean up the query */
3790 dbi_result_free(result);
3793 if (res_list->size && order_hash) {
3794 _tmp = jsonObjectGetKeyConst( order_hash, "flesh" );
3796 int x = (int)jsonObjectGetNumber(_tmp);
3797 if (x == -1 || x > max_flesh_depth) x = max_flesh_depth;
3799 const jsonObject* temp_blob;
3800 if ((temp_blob = jsonObjectGetKeyConst( order_hash, "flesh_fields" )) && x > 0) {
3802 jsonObject* flesh_blob = jsonObjectClone( temp_blob );
3803 const jsonObject* flesh_fields = jsonObjectGetKeyConst( flesh_blob, core_class );
3805 osrfStringArray* link_fields = NULL;
3808 if (flesh_fields->size == 1) {
3809 const char* _t = jsonObjectGetString( jsonObjectGetIndex( flesh_fields, 0 ) );
3810 if (!strcmp(_t,"*")) link_fields = osrfHashKeys( links );
3815 link_fields = osrfNewStringArray(1);
3816 jsonIterator* _i = jsonNewIterator( flesh_fields );
3817 while ((_f = jsonIteratorNext( _i ))) {
3818 osrfStringArrayAdd( link_fields, jsonObjectGetString( _f ) );
3820 jsonIteratorFree(_i);
3825 jsonIterator* itr = jsonNewIterator( res_list );
3826 while ((cur = jsonIteratorNext( itr ))) {
3831 while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
3833 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
3835 osrfHash* kid_link = osrfHashGet(links, link_field);
3836 if (!kid_link) continue;
3838 osrfHash* field = osrfHashGet(fields, link_field);
3839 if (!field) continue;
3841 osrfHash* value_field = field;
3843 osrfHash* kid_idl = osrfHashGet(oilsIDL(), osrfHashGet(kid_link, "class"));
3844 if (!kid_idl) continue;
3846 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
3847 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
3850 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
3851 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
3854 osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
3856 if (link_map->size > 0) {
3857 jsonObject* _kid_key = jsonNewObjectType(JSON_ARRAY);
3860 jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
3865 osrfHashGet(kid_link, "class"),
3872 "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
3873 osrfHashGet(kid_link, "field"),
3874 osrfHashGet(kid_link, "class"),
3875 osrfHashGet(kid_link, "key"),
3876 osrfHashGet(kid_link, "reltype")
3879 jsonObject* fake_params = jsonNewObjectType(JSON_ARRAY);
3880 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH)); // search hash
3881 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH)); // order/flesh hash
3883 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
3885 const char* search_key = jsonObjectGetString(
3888 atoi( osrfHashGet(value_field, "array_position") )
3893 osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
3898 jsonObjectGetIndex(fake_params, 0),
3899 osrfHashGet(kid_link, "key"),
3900 jsonNewObject( search_key )
3904 jsonObjectGetIndex(fake_params, 1),
3906 jsonNewNumberObject( (double)(x - 1 + link_map->size) )
3910 jsonObjectSetKey( jsonObjectGetIndex(fake_params, 1), "flesh_fields", jsonObjectClone(flesh_blob) );
3912 if (jsonObjectGetKeyConst(order_hash, "order_by")) {
3914 jsonObjectGetIndex(fake_params, 1),
3916 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "order_by"))
3920 if (jsonObjectGetKeyConst(order_hash, "select")) {
3922 jsonObjectGetIndex(fake_params, 1),
3924 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "select"))
3928 jsonObject* kids = doFieldmapperSearch(ctx, kid_idl, fake_params, err);
3931 jsonObjectFree( fake_params );
3932 osrfStringArrayFree(link_fields);
3933 jsonIteratorFree(itr);
3934 jsonObjectFree(res_list);
3935 jsonObjectFree(flesh_blob);
3939 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
3941 jsonObject* X = NULL;
3942 if ( link_map->size > 0 && kids->size > 0 ) {
3944 kids = jsonNewObjectType(JSON_ARRAY);
3946 jsonObject* _k_node;
3947 jsonIterator* _k = jsonNewIterator( X );
3948 while ((_k_node = jsonIteratorNext( _k ))) {
3954 (unsigned long)atoi(
3960 osrfHashGet(kid_link, "class")
3964 osrfStringArrayGetString( link_map, 0 )
3973 jsonIteratorFree(_k);
3976 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" )) || !(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) {
3977 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
3980 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
3981 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
3985 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
3986 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
3989 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
3990 jsonObjectClone( kids )
3995 jsonObjectFree(kids);
3999 jsonObjectFree( kids );
4000 jsonObjectFree( fake_params );
4002 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
4003 osrfLogDebug(OSRF_LOG_MARK, "%s", jsonObjectToJSON(cur));
4007 jsonObjectFree( flesh_blob );
4008 osrfStringArrayFree(link_fields);
4009 jsonIteratorFree(itr);
4018 static jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
4020 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4022 jsonObject* target = jsonObjectGetIndex( ctx->params, 1 );
4024 jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
4027 if (!verifyObjectClass(ctx, target)) {
4032 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4033 osrfAppSessionStatus(
4035 OSRF_STATUS_BADREQUEST,
4036 "osrfMethodException",
4038 "No active transaction -- required for UPDATE"
4044 // The following test is harmless but redundant. If a class is
4045 // readonly, we don't register an update method for it.
4046 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4047 osrfAppSessionStatus(
4049 OSRF_STATUS_BADREQUEST,
4050 "osrfMethodException",
4052 "Cannot UPDATE readonly class"
4058 dbhandle = writehandle;
4060 char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
4062 // Set the last_xact_id
4063 int index = oilsIDL_ntop( target->classname, "last_xact_id" );
4065 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
4066 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
4069 char* pkey = osrfHashGet(meta, "primarykey");
4070 osrfHash* fields = osrfHashGet(meta, "fields");
4072 char* id = oilsFMGetString( target, pkey );
4076 "%s updating %s object with %s = %s",
4078 osrfHashGet(meta, "fieldmapper"),
4083 growing_buffer* sql = buffer_init(128);
4084 buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
4089 osrfStringArray* field_list = osrfHashKeys( fields );
4090 while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
4092 osrfHash* field = osrfHashGet( fields, field_name );
4094 if(!( strcmp( field_name, pkey ) )) continue;
4095 if( str_is_true( osrfHashGet(osrfHashGet(fields,field_name), "virtual") ) )
4098 const jsonObject* field_object = oilsFMGetObject( target, field_name );
4100 int value_is_numeric = 0; // boolean
4102 if (field_object && field_object->classname) {
4103 value = oilsFMGetString(
4105 (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
4108 value = jsonObjectToSimpleString( field_object );
4109 if( field_object && JSON_NUMBER == field_object->type )
4110 value_is_numeric = 1;
4113 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
4115 if (!field_object || field_object->type == JSON_NULL) {
4116 if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
4117 if (first) first = 0;
4118 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4119 buffer_fadd( sql, " %s = NULL", field_name );
4122 } else if ( value_is_numeric || !strcmp( get_primitive( field ), "number") ) {
4123 if (first) first = 0;
4124 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4126 const char* numtype = get_datatype( field );
4127 if ( !strncmp( numtype, "INT", 3 ) ) {
4128 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
4129 } else if ( !strcmp( numtype, "NUMERIC" ) ) {
4130 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
4132 // Must really be intended as a string, so quote it
4133 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4134 buffer_fadd( sql, " %s = %s", field_name, value );
4136 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4137 osrfAppSessionStatus(
4139 OSRF_STATUS_INTERNALSERVERERROR,
4140 "osrfMethodException",
4142 "Error quoting string -- please see the error log for more details"
4152 osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, numtype );
4155 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4156 if (first) first = 0;
4157 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4158 buffer_fadd( sql, " %s = %s", field_name, value );
4161 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4162 osrfAppSessionStatus(
4164 OSRF_STATUS_INTERNALSERVERERROR,
4165 "osrfMethodException",
4167 "Error quoting string -- please see the error log for more details"
4181 jsonObject* obj = jsonNewObject(id);
4183 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4184 dbi_conn_quote_string(dbhandle, &id);
4186 buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
4188 char* query = buffer_release(sql);
4189 osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
4191 dbi_result result = dbi_conn_query(dbhandle, query);
4195 jsonObjectFree(obj);
4196 obj = jsonNewObject(NULL);
4199 "%s ERROR updating %s object with %s = %s",
4201 osrfHashGet(meta, "fieldmapper"),
4212 static jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
4214 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4216 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4217 osrfAppSessionStatus(
4219 OSRF_STATUS_BADREQUEST,
4220 "osrfMethodException",
4222 "No active transaction -- required for DELETE"
4228 // The following test is harmless but redundant. If a class is
4229 // readonly, we don't register a delete method for it.
4230 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4231 osrfAppSessionStatus(
4233 OSRF_STATUS_BADREQUEST,
4234 "osrfMethodException",
4236 "Cannot DELETE readonly class"
4242 dbhandle = writehandle;
4246 char* pkey = osrfHashGet(meta, "primarykey");
4254 if (jsonObjectGetIndex(ctx->params, _obj_pos)->classname) {
4255 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, _obj_pos ))) {
4260 id = oilsFMGetString( jsonObjectGetIndex(ctx->params, _obj_pos), pkey );
4263 if (!verifyObjectPCRUD( ctx, NULL )) {
4268 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, _obj_pos));
4273 "%s deleting %s object with %s = %s",
4275 osrfHashGet(meta, "fieldmapper"),
4280 obj = jsonNewObject(id);
4282 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4283 dbi_conn_quote_string(writehandle, &id);
4285 dbi_result result = dbi_conn_queryf(writehandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
4288 jsonObjectFree(obj);
4289 obj = jsonNewObject(NULL);
4292 "%s ERROR deleting %s object with %s = %s",
4294 osrfHashGet(meta, "fieldmapper"),
4307 static jsonObject* oilsMakeFieldmapperFromResult( dbi_result result, osrfHash* meta) {
4308 if(!(result && meta)) return jsonNULL;
4310 jsonObject* object = jsonNewObject(NULL);
4311 jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
4313 osrfHash* fields = osrfHashGet(meta, "fields");
4315 osrfLogInternal(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
4319 char dt_string[256];
4323 int columnIndex = 1;
4325 unsigned short type;
4326 const char* columnName;
4328 /* cycle through the column list */
4329 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4331 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4333 fmIndex = -1; // reset the position
4335 /* determine the field type and storage attributes */
4336 type = dbi_result_get_field_type(result, columnName);
4337 attr = dbi_result_get_field_attribs(result, columnName);
4339 /* fetch the fieldmapper index */
4340 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
4342 if ( str_is_true( osrfHashGet(_f, "virtual") ) )
4345 const char* pos = (char*)osrfHashGet(_f, "array_position");
4346 if ( !pos ) continue;
4348 fmIndex = atoi( pos );
4349 osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
4354 if (dbi_result_field_is_null(result, columnName)) {
4355 jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
4360 case DBI_TYPE_INTEGER :
4362 if( attr & DBI_INTEGER_SIZE8 )
4363 jsonObjectSetIndex( object, fmIndex,
4364 jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
4366 jsonObjectSetIndex( object, fmIndex,
4367 jsonNewNumberObject(dbi_result_get_int(result, columnName)));
4371 case DBI_TYPE_DECIMAL :
4372 jsonObjectSetIndex( object, fmIndex,
4373 jsonNewNumberObject(dbi_result_get_double(result, columnName)));
4376 case DBI_TYPE_STRING :
4382 jsonNewObject( dbi_result_get_string(result, columnName) )
4387 case DBI_TYPE_DATETIME :
4389 memset(dt_string, '\0', sizeof(dt_string));
4390 memset(&gmdt, '\0', sizeof(gmdt));
4392 _tmp_dt = dbi_result_get_datetime(result, columnName);
4395 if (!(attr & DBI_DATETIME_DATE)) {
4396 gmtime_r( &_tmp_dt, &gmdt );
4397 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4398 } else if (!(attr & DBI_DATETIME_TIME)) {
4399 localtime_r( &_tmp_dt, &gmdt );
4400 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4402 localtime_r( &_tmp_dt, &gmdt );
4403 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4406 jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
4410 case DBI_TYPE_BINARY :
4411 osrfLogError( OSRF_LOG_MARK,
4412 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4420 static jsonObject* oilsMakeJSONFromResult( dbi_result result ) {
4421 if(!result) return jsonNULL;
4423 jsonObject* object = jsonNewObject(NULL);
4426 char dt_string[256];
4430 int columnIndex = 1;
4432 unsigned short type;
4433 const char* columnName;
4435 /* cycle through the column list */
4436 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4438 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4440 fmIndex = -1; // reset the position
4442 /* determine the field type and storage attributes */
4443 type = dbi_result_get_field_type(result, columnName);
4444 attr = dbi_result_get_field_attribs(result, columnName);
4446 if (dbi_result_field_is_null(result, columnName)) {
4447 jsonObjectSetKey( object, columnName, jsonNewObject(NULL) );
4452 case DBI_TYPE_INTEGER :
4454 if( attr & DBI_INTEGER_SIZE8 )
4455 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_longlong(result, columnName)) );
4457 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_int(result, columnName)) );
4460 case DBI_TYPE_DECIMAL :
4461 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_double(result, columnName)) );
4464 case DBI_TYPE_STRING :
4465 jsonObjectSetKey( object, columnName, jsonNewObject(dbi_result_get_string(result, columnName)) );
4468 case DBI_TYPE_DATETIME :
4470 memset(dt_string, '\0', sizeof(dt_string));
4471 memset(&gmdt, '\0', sizeof(gmdt));
4473 _tmp_dt = dbi_result_get_datetime(result, columnName);
4476 if (!(attr & DBI_DATETIME_DATE)) {
4477 gmtime_r( &_tmp_dt, &gmdt );
4478 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4479 } else if (!(attr & DBI_DATETIME_TIME)) {
4480 localtime_r( &_tmp_dt, &gmdt );
4481 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4483 localtime_r( &_tmp_dt, &gmdt );
4484 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4487 jsonObjectSetKey( object, columnName, jsonNewObject(dt_string) );
4490 case DBI_TYPE_BINARY :
4491 osrfLogError( OSRF_LOG_MARK,
4492 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4500 // Interpret a string as true or false
4501 static int str_is_true( const char* str ) {
4502 if( NULL == str || strcasecmp( str, "true" ) )
4508 // Interpret a jsonObject as true or false
4509 static int obj_is_true( const jsonObject* obj ) {
4512 else switch( obj->type )
4520 if( strcasecmp( obj->value.s, "true" ) )
4524 case JSON_NUMBER : // Support 1/0 for perl's sake
4525 if( jsonObjectGetNumber( obj ) == 1.0 )
4534 // Translate a numeric code into a text string identifying a type of
4535 // jsonObject. To be used for building error messages.
4536 static const char* json_type( int code ) {
4542 return "JSON_ARRAY";
4544 return "JSON_STRING";
4546 return "JSON_NUMBER";
4552 return "(unrecognized)";
4556 // Extract the "primitive" attribute from an IDL field definition.
4557 // If we haven't initialized the app, then we must be running in
4558 // some kind of testbed. In that case, default to "string".
4559 static const char* get_primitive( osrfHash* field ) {
4560 const char* s = osrfHashGet( field, "primitive" );
4562 if( child_initialized )
4565 "%s ERROR No \"datatype\" attribute for field \"%s\"",
4567 osrfHashGet( field, "name" )
4575 // Extract the "datatype" attribute from an IDL field definition.
4576 // If we haven't initialized the app, then we must be running in
4577 // some kind of testbed. In that case, default to to NUMERIC,
4578 // since we look at the datatype only for numbers.
4579 static const char* get_datatype( osrfHash* field ) {
4580 const char* s = osrfHashGet( field, "datatype" );
4582 if( child_initialized )
4585 "%s ERROR No \"datatype\" attribute for field \"%s\"",
4587 osrfHashGet( field, "name" )
4596 If the input string is potentially a valid SQL identifier, return 1.
4599 Purpose: to prevent certain kinds of SQL injection. To that end we
4600 don't necessarily need to follow all the rules exactly, such as requiring
4601 that the first character not be a digit.
4603 We allow leading and trailing white space. In between, we do not allow
4604 punctuation (except for underscores and dollar signs), control
4605 characters, or embedded white space.
4607 More pedantically we should allow quoted identifiers containing arbitrary
4608 characters, but for the foreseeable future such quoted identifiers are not
4609 likely to be an issue.
4611 static int is_identifier( const char* s) {
4615 // Skip leading white space
4616 while( isspace( (unsigned char) *s ) )
4620 return 0; // Nothing but white space? Not okay.
4622 // Check each character until we reach white space or
4623 // end-of-string. Letters, digits, underscores, and
4624 // dollar signs are okay. Control characters and other
4625 // punctuation characters are not okay. Anything else
4626 // is okay -- it could for example be part of a multibyte
4627 // UTF8 character such as a letter with diacritical marks,
4628 // and those are allowed.
4630 if( isalnum( (unsigned char) *s )
4633 ; // Fine; keep going
4634 else if( ispunct( (unsigned char) *s )
4635 || iscntrl( (unsigned char) *s ) )
4638 } while( *s && ! isspace( (unsigned char) *s ) );
4640 // If we found any white space in the above loop,
4641 // the rest had better be all white space.
4643 while( isspace( (unsigned char) *s ) )
4647 return 0; // White space was embedded within non-white space