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 );
2404 } else if ( !strcasecmp("-and",search_itr->key) ) {
2405 char* subpred = searchWHERE( node, meta, AND_OP_JOIN, ctx );
2407 buffer_fadd(sql_buf, "( %s )", subpred);
2410 buffer_free( sql_buf );
2413 } else if ( !strcasecmp("-exists",search_itr->key) ) {
2414 char* subpred = SELECT(
2416 jsonObjectGetKey( node, "select" ),
2417 jsonObjectGetKey( node, "from" ),
2418 jsonObjectGetKey( node, "where" ),
2419 jsonObjectGetKey( node, "having" ),
2420 jsonObjectGetKey( node, "order_by" ),
2421 jsonObjectGetKey( node, "limit" ),
2422 jsonObjectGetKey( node, "offset" ),
2426 buffer_fadd(sql_buf, "EXISTS ( %s )", subpred);
2428 } else if ( !strcasecmp("-not-exists",search_itr->key) ) {
2429 char* subpred = SELECT(
2431 jsonObjectGetKey( node, "select" ),
2432 jsonObjectGetKey( node, "from" ),
2433 jsonObjectGetKey( node, "where" ),
2434 jsonObjectGetKey( node, "having" ),
2435 jsonObjectGetKey( node, "order_by" ),
2436 jsonObjectGetKey( node, "limit" ),
2437 jsonObjectGetKey( node, "offset" ),
2441 buffer_fadd(sql_buf, "NOT EXISTS ( %s )", subpred);
2445 char* class = osrfHashGet(meta, "classname");
2446 osrfHash* fields = osrfHashGet(meta, "fields");
2447 osrfHash* field = osrfHashGet( fields, search_itr->key );
2451 char* table = getSourceDefinition(meta);
2453 table = strdup( "(?)" );
2456 "%s: Attempt to reference non-existent column %s on %s (%s)",
2462 buffer_free(sql_buf);
2464 jsonIteratorFree(search_itr);
2468 char* subpred = searchPredicate( class, field, node, ctx );
2469 buffer_add( sql_buf, subpred );
2473 jsonIteratorFree(search_itr);
2476 // ERROR ... only hash and array allowed at this level
2477 char* predicate_string = jsonObjectToJSON( search_hash );
2480 "%s: Invalid predicate structure: %s",
2484 buffer_free(sql_buf);
2485 free(predicate_string);
2489 return buffer_release(sql_buf);
2493 /* method context */ osrfMethodContext* ctx,
2495 /* SELECT */ jsonObject* selhash,
2496 /* FROM */ jsonObject* join_hash,
2497 /* WHERE */ jsonObject* search_hash,
2498 /* HAVING */ jsonObject* having_hash,
2499 /* ORDER BY */ jsonObject* order_hash,
2500 /* LIMIT */ jsonObject* limit,
2501 /* OFFSET */ jsonObject* offset,
2502 /* flags */ int flags
2504 const char* locale = osrf_message_get_last_locale();
2506 // in case we don't get a select list
2507 jsonObject* defaultselhash = NULL;
2509 // general tmp objects
2510 const jsonObject* tmp_const;
2511 jsonObject* selclass = NULL;
2512 jsonObject* selfield = NULL;
2513 jsonObject* snode = NULL;
2514 jsonObject* onode = NULL;
2516 char* string = NULL;
2517 int from_function = 0;
2522 // the core search class
2523 char* core_class = NULL;
2525 // metadata about the core search class
2526 osrfHash* core_meta = NULL;
2528 // punt if there's no core class
2529 if (!join_hash || ( join_hash->type == JSON_HASH && !join_hash->size )) {
2532 "%s: FROM clause is missing or empty",
2536 osrfAppSessionStatus(
2538 OSRF_STATUS_INTERNALSERVERERROR,
2539 "osrfMethodException",
2541 "FROM clause is missing or empty in JSON query"
2546 // get the core class -- the only key of the top level FROM clause, or a string
2547 if (join_hash->type == JSON_HASH) {
2548 jsonIterator* tmp_itr = jsonNewIterator( join_hash );
2549 snode = jsonIteratorNext( tmp_itr );
2551 core_class = strdup( tmp_itr->key );
2554 jsonObject* extra = jsonIteratorNext( tmp_itr );
2556 jsonIteratorFree( tmp_itr );
2559 // There shouldn't be more than one entry in join_hash
2563 "%s: Malformed FROM clause: extra entry in JSON_HASH",
2567 osrfAppSessionStatus(
2569 OSRF_STATUS_INTERNALSERVERERROR,
2570 "osrfMethodException",
2572 "Malformed FROM clause in JSON query"
2575 return NULL; // Malformed join_hash; extra entry
2577 } else if (join_hash->type == JSON_ARRAY) {
2579 core_class = jsonObjectToSimpleString( jsonObjectGetIndex(join_hash, 0) );
2582 } else if (join_hash->type == JSON_STRING) {
2583 core_class = jsonObjectToSimpleString( join_hash );
2589 "%s: FROM clause is unexpected JSON type: %s",
2591 json_type( join_hash->type )
2594 osrfAppSessionStatus(
2596 OSRF_STATUS_INTERNALSERVERERROR,
2597 "osrfMethodException",
2599 "Ill-formed FROM clause in JSON query"
2605 if (!from_function) {
2606 // Get the IDL class definition for the core class
2607 core_meta = osrfHashGet( oilsIDL(), core_class );
2608 if( !core_meta ) { // Didn't find it?
2611 "%s: SELECT clause references undefined class: \"%s\"",
2616 osrfAppSessionStatus(
2618 OSRF_STATUS_INTERNALSERVERERROR,
2619 "osrfMethodException",
2621 "SELECT clause references undefined class in JSON query"
2627 // Make sure the class isn't virtual
2628 if( str_is_true( osrfHashGet( core_meta, "virtual" ) ) ) {
2631 "%s: Core class is virtual: \"%s\"",
2636 osrfAppSessionStatus(
2638 OSRF_STATUS_INTERNALSERVERERROR,
2639 "osrfMethodException",
2641 "FROM clause references virtual class in JSON query"
2648 // if the select list is empty, or the core class field list is '*',
2649 // build the default select list ...
2651 selhash = defaultselhash = jsonNewObjectType(JSON_HASH);
2652 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2653 } else if( selhash->type != JSON_HASH ) {
2656 "%s: Expected JSON_HASH for SELECT clause; found %s",
2658 json_type( selhash->type )
2662 osrfAppSessionStatus(
2664 OSRF_STATUS_INTERNALSERVERERROR,
2665 "osrfMethodException",
2667 "Malformed SELECT clause in JSON query"
2671 } else if ( (tmp_const = jsonObjectGetKeyConst( selhash, core_class )) && tmp_const->type == JSON_STRING ) {
2672 const char* _x = jsonObjectGetString( tmp_const );
2673 if (!strncmp( "*", _x, 1 )) {
2674 jsonObjectRemoveKey( selhash, core_class );
2675 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2680 growing_buffer* sql_buf = buffer_init(128);
2682 // temp buffer for the SELECT list
2683 growing_buffer* select_buf = buffer_init(128);
2684 growing_buffer* order_buf = buffer_init(128);
2685 growing_buffer* group_buf = buffer_init(128);
2686 growing_buffer* having_buf = buffer_init(128);
2688 int aggregate_found = 0; // boolean
2690 // Build a select list
2691 if(from_function) // From a function we select everything
2692 OSRF_BUFFER_ADD_CHAR( select_buf, '*' );
2695 // If we need to build a default list, prepare to do so
2696 jsonObject* _tmp = jsonObjectGetKey( selhash, core_class );
2697 if ( _tmp && !_tmp->size ) {
2699 osrfHash* core_fields = osrfHashGet( core_meta, "fields" );
2701 osrfHashIterator* field_itr = osrfNewHashIterator( core_fields );
2702 osrfHash* field_def;
2703 while( ( field_def = osrfHashIteratorNext( field_itr ) ) ) {
2704 if( ! str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2705 // This field is not virtual, so add it to the list
2706 jsonObjectPush( _tmp, jsonNewObject( osrfHashIteratorKey( field_itr ) ) );
2709 osrfHashIteratorFree( field_itr );
2712 // Now build the actual select list
2716 jsonIterator* selclass_itr = jsonNewIterator( selhash );
2717 while ( (selclass = jsonIteratorNext( selclass_itr )) ) { // For each class
2719 // Make sure the class is defined in the IDL
2720 const char* cname = selclass_itr->key;
2721 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
2725 "%s: Selected class \"%s\" not defined in IDL",
2731 osrfAppSessionStatus(
2733 OSRF_STATUS_INTERNALSERVERERROR,
2734 "osrfMethodException",
2736 "Selected class is not defined"
2738 jsonIteratorFree( selclass_itr );
2739 buffer_free( sql_buf );
2740 buffer_free( select_buf );
2741 buffer_free( order_buf );
2742 buffer_free( group_buf );
2743 buffer_free( having_buf );
2744 if( defaultselhash ) jsonObjectFree( defaultselhash );
2749 // Make sure the target relation is in the join tree.
2751 // At this point join_hash is a step down from the join_hash we
2752 // received as a parameter. If the original was a JSON_STRING,
2753 // then json_hash is now NULL. If the original was a JSON_HASH,
2754 // then json_hash is now the first (and only) entry in it,
2755 // denoting the core class. We've already excluded the
2756 // possibility that the original was a JSON_ARRAY, because in
2757 // that case from_function would be non-NULL, and we wouldn't
2760 int class_in_from_clause; // boolean
2762 if ( ! strcmp( core_class, cname ))
2763 // This is the core class -- no problem
2764 class_in_from_clause = 1;
2767 // There's only one class in the FROM clause, and this isn't it
2768 class_in_from_clause = 0;
2769 else if (join_hash->type == JSON_STRING) {
2770 // There's only one class in the FROM clause
2771 const char* str = jsonObjectGetString(join_hash);
2772 if ( strcmp( str, cname ) )
2773 class_in_from_clause = 0; // This isn't it
2775 class_in_from_clause = 1; // This is it
2777 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", cname);
2778 if ( 0 == found->size )
2779 class_in_from_clause = 0; // Nowhere in the join tree
2781 class_in_from_clause = 1; // Found it
2782 jsonObjectFree( found );
2786 // If the class isn't in the FROM clause, bail out
2787 if( ! class_in_from_clause ) {
2790 "%s: SELECT clause references class not in FROM clause: \"%s\"",
2795 osrfAppSessionStatus(
2797 OSRF_STATUS_INTERNALSERVERERROR,
2798 "osrfMethodException",
2800 "Selected class not in FROM clause in JSON query"
2802 jsonIteratorFree( selclass_itr );
2803 buffer_free( sql_buf );
2804 buffer_free( select_buf );
2805 buffer_free( order_buf );
2806 buffer_free( group_buf );
2807 buffer_free( having_buf );
2808 if( defaultselhash ) jsonObjectFree( defaultselhash );
2813 // Look up some attributes of the current class, so that we
2814 // don't have to look them up again for each field
2815 osrfHash* class_field_set = osrfHashGet( idlClass, "fields" );
2816 const char* class_pkey = osrfHashGet( idlClass, "primarykey" );
2817 const char* class_tname = osrfHashGet( idlClass, "tablename" );
2819 // stitch together the column list ...
2820 jsonIterator* select_itr = jsonNewIterator( selclass );
2821 while ( (selfield = jsonIteratorNext( select_itr )) ) { // for each SELECT column
2823 // If we need a separator comma, add one
2827 OSRF_BUFFER_ADD_CHAR( select_buf, ',' );
2830 // ... if it's a string, just toss it on the pile
2831 if (selfield->type == JSON_STRING) {
2833 // Look up the field in the IDL
2834 const char* col_name = jsonObjectGetString( selfield );
2835 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
2837 // No such field in current class
2840 "%s: Selected column \"%s\" not defined in IDL for class \"%s\"",
2846 osrfAppSessionStatus(
2848 OSRF_STATUS_INTERNALSERVERERROR,
2849 "osrfMethodException",
2851 "Selected column not defined in JSON query"
2853 jsonIteratorFree( select_itr );
2854 jsonIteratorFree( selclass_itr );
2855 buffer_free( sql_buf );
2856 buffer_free( select_buf );
2857 buffer_free( order_buf );
2858 buffer_free( group_buf );
2859 buffer_free( having_buf );
2860 if( defaultselhash ) jsonObjectFree( defaultselhash );
2863 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2864 // Virtual field not allowed
2867 "%s: Selected column \"%s\" for class \"%s\" is virtual",
2873 osrfAppSessionStatus(
2875 OSRF_STATUS_INTERNALSERVERERROR,
2876 "osrfMethodException",
2878 "Selected column may not be virtual in JSON query"
2880 jsonIteratorFree( select_itr );
2881 jsonIteratorFree( selclass_itr );
2882 buffer_free( sql_buf );
2883 buffer_free( select_buf );
2884 buffer_free( order_buf );
2885 buffer_free( group_buf );
2886 buffer_free( having_buf );
2887 if( defaultselhash ) jsonObjectFree( defaultselhash );
2894 if (flags & DISABLE_I18N)
2897 i18n = osrfHashGet(field_def, "i18n");
2899 if( str_is_true( i18n ) ) {
2900 buffer_fadd( select_buf,
2901 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
2902 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, col_name );
2904 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
2907 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
2910 // ... but it could be an object, in which case we check for a Field Transform
2911 } else if (selfield->type == JSON_HASH) {
2913 const char* col_name = jsonObjectGetString( jsonObjectGetKeyConst( selfield, "column" ) );
2915 // Get the field definition from the IDL
2916 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
2918 // No such field in current class
2921 "%s: Selected column \"%s\" is not defined in IDL for class \"%s\"",
2927 osrfAppSessionStatus(
2929 OSRF_STATUS_INTERNALSERVERERROR,
2930 "osrfMethodException",
2932 "Selected column is not defined in JSON query"
2934 jsonIteratorFree( select_itr );
2935 jsonIteratorFree( selclass_itr );
2936 buffer_free( sql_buf );
2937 buffer_free( select_buf );
2938 buffer_free( order_buf );
2939 buffer_free( group_buf );
2940 buffer_free( having_buf );
2941 if( defaultselhash ) jsonObjectFree( defaultselhash );
2944 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2945 // No such field in current class
2948 "%s: Selected column \"%s\" is virtual for class \"%s\"",
2954 osrfAppSessionStatus(
2956 OSRF_STATUS_INTERNALSERVERERROR,
2957 "osrfMethodException",
2959 "Selected column is virtual in JSON query"
2961 jsonIteratorFree( select_itr );
2962 jsonIteratorFree( selclass_itr );
2963 buffer_free( sql_buf );
2964 buffer_free( select_buf );
2965 buffer_free( order_buf );
2966 buffer_free( group_buf );
2967 buffer_free( having_buf );
2968 if( defaultselhash ) jsonObjectFree( defaultselhash );
2973 // Decide what to use as a column alias
2975 if ((tmp_const = jsonObjectGetKeyConst( selfield, "alias" ))) {
2976 _alias = jsonObjectGetString( tmp_const );
2977 } else { // Use field name as the alias
2981 if (jsonObjectGetKeyConst( selfield, "transform" )) {
2982 char* transform_str = searchFieldTransform(cname, field_def, selfield);
2983 if( transform_str ) {
2984 buffer_fadd(select_buf, " %s AS \"%s\"", transform_str, _alias);
2985 free(transform_str);
2988 osrfAppSessionStatus(
2990 OSRF_STATUS_INTERNALSERVERERROR,
2991 "osrfMethodException",
2993 "Unable to generate transform function in JSON query"
2995 jsonIteratorFree( select_itr );
2996 jsonIteratorFree( selclass_itr );
2997 buffer_free( sql_buf );
2998 buffer_free( select_buf );
2999 buffer_free( order_buf );
3000 buffer_free( group_buf );
3001 buffer_free( having_buf );
3002 if( defaultselhash ) jsonObjectFree( defaultselhash );
3010 if (flags & DISABLE_I18N)
3013 i18n = osrfHashGet(field_def, "i18n");
3015 if( str_is_true( i18n ) ) {
3016 buffer_fadd( select_buf,
3017 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
3018 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, _alias);
3020 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
3023 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
3030 "%s: Selected item is unexpected JSON type: %s",
3032 json_type( selfield->type )
3035 osrfAppSessionStatus(
3037 OSRF_STATUS_INTERNALSERVERERROR,
3038 "osrfMethodException",
3040 "Ill-formed SELECT item in JSON query"
3042 jsonIteratorFree( select_itr );
3043 jsonIteratorFree( selclass_itr );
3044 buffer_free( sql_buf );
3045 buffer_free( select_buf );
3046 buffer_free( order_buf );
3047 buffer_free( group_buf );
3048 buffer_free( having_buf );
3049 if( defaultselhash ) jsonObjectFree( defaultselhash );
3054 const jsonObject* agg_obj = jsonObjectGetKey( selfield, "aggregate" );
3055 if( obj_is_true( agg_obj ) )
3056 aggregate_found = 1;
3058 // Append a comma (except for the first one)
3059 // and add the column to a GROUP BY clause
3063 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3065 buffer_fadd(group_buf, " %d", sel_pos);
3069 if (is_agg->size || (flags & SELECT_DISTINCT)) {
3071 const jsonObject* aggregate_obj = jsonObjectGetKey( selfield, "aggregate" );
3072 if ( ! obj_is_true( aggregate_obj ) ) {
3076 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3079 buffer_fadd(group_buf, " %d", sel_pos);
3082 } else if (is_agg = jsonObjectGetKey( selfield, "having" )) {
3086 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3089 _column = searchFieldTransform(cname, field, selfield);
3090 OSRF_BUFFER_ADD_CHAR(group_buf, ' ');
3091 OSRF_BUFFER_ADD(group_buf, _column);
3092 _column = searchFieldTransform(cname, field, selfield);
3099 } // end while -- iterating across SELECT columns
3101 jsonIteratorFree(select_itr);
3102 } // end while -- iterating across classes
3104 jsonIteratorFree(selclass_itr);
3108 char* col_list = buffer_release(select_buf);
3110 if (from_function) table = searchValueTransform(join_hash);
3111 else table = getSourceDefinition(core_meta);
3115 osrfAppSessionStatus(
3117 OSRF_STATUS_INTERNALSERVERERROR,
3118 "osrfMethodException",
3120 "Unable to identify table for core class"
3123 buffer_free( sql_buf );
3124 buffer_free( order_buf );
3125 buffer_free( group_buf );
3126 buffer_free( having_buf );
3127 if( defaultselhash ) jsonObjectFree( defaultselhash );
3132 // Put it all together
3133 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\" ", col_list, table, core_class );
3137 if (!from_function) {
3138 // Now, walk the join tree and add that clause
3140 char* join_clause = searchJOIN( join_hash, core_meta );
3142 buffer_add(sql_buf, join_clause);
3146 osrfAppSessionStatus(
3148 OSRF_STATUS_INTERNALSERVERERROR,
3149 "osrfMethodException",
3151 "Unable to construct JOIN clause(s)"
3153 buffer_free( sql_buf );
3154 buffer_free( order_buf );
3155 buffer_free( group_buf );
3156 buffer_free( having_buf );
3157 if( defaultselhash ) jsonObjectFree( defaultselhash );
3163 // Build a WHERE clause, if there is one
3164 if ( search_hash ) {
3165 buffer_add(sql_buf, " WHERE ");
3167 // and it's on the WHERE clause
3168 char* pred = searchWHERE( search_hash, core_meta, AND_OP_JOIN, ctx );
3171 buffer_add(sql_buf, pred);
3175 osrfAppSessionStatus(
3177 OSRF_STATUS_INTERNALSERVERERROR,
3178 "osrfMethodException",
3180 "Severe query error in WHERE predicate -- see error log for more details"
3184 buffer_free(having_buf);
3185 buffer_free(group_buf);
3186 buffer_free(order_buf);
3187 buffer_free(sql_buf);
3188 if (defaultselhash) jsonObjectFree(defaultselhash);
3193 // Build a HAVING clause, if there is one
3194 if ( having_hash ) {
3195 buffer_add(sql_buf, " HAVING ");
3197 // and it's on the the WHERE clause
3198 char* pred = searchWHERE( having_hash, core_meta, AND_OP_JOIN, ctx );
3201 buffer_add(sql_buf, pred);
3205 osrfAppSessionStatus(
3207 OSRF_STATUS_INTERNALSERVERERROR,
3208 "osrfMethodException",
3210 "Severe query error in HAVING predicate -- see error log for more details"
3214 buffer_free(having_buf);
3215 buffer_free(group_buf);
3216 buffer_free(order_buf);
3217 buffer_free(sql_buf);
3218 if (defaultselhash) jsonObjectFree(defaultselhash);
3223 // Build an ORDER BY clause, if there is one
3225 jsonIterator* class_itr = jsonNewIterator( order_hash );
3226 while ( (snode = jsonIteratorNext( class_itr )) ) {
3228 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
3231 if ( snode->type == JSON_HASH ) {
3233 jsonIterator* order_itr = jsonNewIterator( snode );
3234 while ( (onode = jsonIteratorNext( order_itr )) ) {
3236 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ))
3239 const char* direction = NULL;
3240 if ( onode->type == JSON_HASH ) {
3241 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3242 string = searchFieldTransform(
3244 oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ),
3248 growing_buffer* field_buf = buffer_init(16);
3249 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3250 string = buffer_release(field_buf);
3253 if ( (tmp_const = jsonObjectGetKeyConst( onode, "direction" )) ) {
3254 const char* dir = jsonObjectGetString(tmp_const);
3255 if (!strncasecmp(dir, "d", 1)) {
3256 direction = " DESC";
3263 string = strdup(order_itr->key);
3264 const char* dir = jsonObjectGetString(onode);
3265 if (!strncasecmp(dir, "d", 1)) {
3266 direction = " DESC";
3275 buffer_add(order_buf, ", ");
3278 buffer_add(order_buf, string);
3282 buffer_add(order_buf, direction);
3286 // jsonIteratorFree(order_itr);
3288 } else if ( snode->type == JSON_ARRAY ) {
3290 jsonIterator* order_itr = jsonNewIterator( snode );
3291 while ( (onode = jsonIteratorNext( order_itr )) ) {
3293 const char* _f = jsonObjectGetString( onode );
3295 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, _f))
3301 buffer_add(order_buf, ", ");
3304 buffer_add(order_buf, _f);
3307 // jsonIteratorFree(order_itr);
3310 // IT'S THE OOOOOOOOOOOLD STYLE!
3312 osrfLogError(OSRF_LOG_MARK, "%s: Possible SQL injection attempt; direct order by is not allowed", MODULENAME);
3314 osrfAppSessionStatus(
3316 OSRF_STATUS_INTERNALSERVERERROR,
3317 "osrfMethodException",
3319 "Severe query error -- see error log for more details"
3324 buffer_free(having_buf);
3325 buffer_free(group_buf);
3326 buffer_free(order_buf);
3327 buffer_free(sql_buf);
3328 if (defaultselhash) jsonObjectFree(defaultselhash);
3329 jsonIteratorFree(class_itr);
3334 // jsonIteratorFree(class_itr);
3338 string = buffer_release(group_buf);
3340 if ( *string && ( aggregate_found || (flags & SELECT_DISTINCT) ) ) {
3341 OSRF_BUFFER_ADD( sql_buf, " GROUP BY " );
3342 OSRF_BUFFER_ADD( sql_buf, string );
3347 string = buffer_release(having_buf);
3350 OSRF_BUFFER_ADD( sql_buf, " HAVING " );
3351 OSRF_BUFFER_ADD( sql_buf, string );
3356 string = buffer_release(order_buf);
3359 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3360 OSRF_BUFFER_ADD( sql_buf, string );
3366 const char* str = jsonObjectGetString(limit);
3367 buffer_fadd( sql_buf, " LIMIT %d", atoi(str) );
3371 const char* str = jsonObjectGetString(offset);
3372 buffer_fadd( sql_buf, " OFFSET %d", atoi(str) );
3375 if (!(flags & SUBSELECT)) OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3378 if (defaultselhash) jsonObjectFree(defaultselhash);
3380 return buffer_release(sql_buf);
3384 static char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
3386 const char* locale = osrf_message_get_last_locale();
3388 osrfHash* fields = osrfHashGet(meta, "fields");
3389 char* core_class = osrfHashGet(meta, "classname");
3391 const jsonObject* join_hash = jsonObjectGetKeyConst( order_hash, "join" );
3393 jsonObject* node = NULL;
3394 jsonObject* snode = NULL;
3395 jsonObject* onode = NULL;
3396 const jsonObject* _tmp = NULL;
3397 jsonObject* selhash = NULL;
3398 jsonObject* defaultselhash = NULL;
3400 growing_buffer* sql_buf = buffer_init(128);
3401 growing_buffer* select_buf = buffer_init(128);
3403 if ( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
3404 defaultselhash = jsonNewObjectType(JSON_HASH);
3405 selhash = defaultselhash;
3408 if ( !jsonObjectGetKeyConst(selhash,core_class) ) {
3409 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
3410 jsonObject* flist = jsonObjectGetKey( selhash, core_class );
3415 osrfStringArray* keys = osrfHashKeys( fields );
3416 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
3417 if( ! str_is_true( osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
3418 jsonObjectPush( flist, jsonNewObject( field ) );
3420 osrfStringArrayFree(keys);
3424 jsonIterator* class_itr = jsonNewIterator( selhash );
3425 while ( (snode = jsonIteratorNext( class_itr )) ) {
3427 char* cname = class_itr->key;
3428 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
3429 if (!idlClass) continue;
3431 if (strcmp(core_class,class_itr->key)) {
3432 if (!join_hash) continue;
3434 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", class_itr->key);
3436 jsonObjectFree(found);
3440 jsonObjectFree(found);
3443 jsonIterator* select_itr = jsonNewIterator( snode );
3444 while ( (node = jsonIteratorNext( select_itr )) ) {
3445 const char* item_str = jsonObjectGetString( node );
3446 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), item_str );
3447 char* fname = osrfHashGet(field, "name");
3449 if (!field) continue;
3454 OSRF_BUFFER_ADD_CHAR(select_buf, ',');
3459 const jsonObject* no_i18n_obj = jsonObjectGetKey( order_hash, "no_i18n" );
3460 if ( obj_is_true( no_i18n_obj ) ) // Suppress internationalization?
3463 i18n = osrfHashGet(field, "i18n");
3465 if( str_is_true( i18n ) ) {
3466 char* pkey = osrfHashGet(idlClass, "primarykey");
3467 char* tname = osrfHashGet(idlClass, "tablename");
3469 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);
3471 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3474 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3478 jsonIteratorFree(select_itr);
3481 jsonIteratorFree(class_itr);
3483 char* col_list = buffer_release(select_buf);
3484 char* table = getSourceDefinition(meta);
3486 table = strdup( "(null)" );
3488 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\"", col_list, table, core_class );
3493 char* join_clause = searchJOIN( join_hash, meta );
3494 OSRF_BUFFER_ADD_CHAR(sql_buf, ' ');
3495 OSRF_BUFFER_ADD(sql_buf, join_clause);
3499 osrfLogDebug(OSRF_LOG_MARK, "%s pre-predicate SQL = %s",
3500 MODULENAME, OSRF_BUFFER_C_STR(sql_buf));
3502 buffer_add(sql_buf, " WHERE ");
3504 char* pred = searchWHERE( search_hash, meta, AND_OP_JOIN, ctx );
3506 osrfAppSessionStatus(
3508 OSRF_STATUS_INTERNALSERVERERROR,
3509 "osrfMethodException",
3511 "Severe query error -- see error log for more details"
3513 buffer_free(sql_buf);
3514 if(defaultselhash) jsonObjectFree(defaultselhash);
3517 buffer_add(sql_buf, pred);
3522 char* string = NULL;
3523 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "order_by" )) ){
3525 growing_buffer* order_buf = buffer_init(128);
3528 jsonIterator* class_itr = jsonNewIterator( _tmp );
3529 while ( (snode = jsonIteratorNext( class_itr )) ) {
3531 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
3534 if ( snode->type == JSON_HASH ) {
3536 jsonIterator* order_itr = jsonNewIterator( snode );
3537 while ( (onode = jsonIteratorNext( order_itr )) ) {
3539 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ))
3542 char* direction = NULL;
3543 if ( onode->type == JSON_HASH ) {
3544 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3545 string = searchFieldTransform(
3547 oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ),
3551 growing_buffer* field_buf = buffer_init(16);
3552 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3553 string = buffer_release(field_buf);
3556 if ( (_tmp = jsonObjectGetKeyConst( onode, "direction" )) ) {
3557 const char* dir = jsonObjectGetString(_tmp);
3558 if (!strncasecmp(dir, "d", 1)) {
3559 direction = " DESC";
3566 string = strdup(order_itr->key);
3567 const char* dir = jsonObjectGetString(onode);
3568 if (!strncasecmp(dir, "d", 1)) {
3569 direction = " DESC";
3578 buffer_add(order_buf, ", ");
3581 buffer_add(order_buf, string);
3585 buffer_add(order_buf, direction);
3590 jsonIteratorFree(order_itr);
3593 const char* str = jsonObjectGetString(snode);
3594 buffer_add(order_buf, str);
3600 jsonIteratorFree(class_itr);
3602 string = buffer_release(order_buf);
3605 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3606 OSRF_BUFFER_ADD( sql_buf, string );
3612 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "limit" )) ){
3613 const char* str = jsonObjectGetString(_tmp);
3621 _tmp = jsonObjectGetKeyConst( order_hash, "offset" );
3623 const char* str = jsonObjectGetString(_tmp);
3632 if (defaultselhash) jsonObjectFree(defaultselhash);
3634 OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3635 return buffer_release(sql_buf);
3638 int doJSONSearch ( osrfMethodContext* ctx ) {
3639 if(osrfMethodVerifyContext( ctx )) {
3640 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
3644 osrfLogDebug(OSRF_LOG_MARK, "Recieved query request");
3649 dbhandle = writehandle;
3651 jsonObject* hash = jsonObjectGetIndex(ctx->params, 0);
3655 if ( obj_is_true( jsonObjectGetKey( hash, "distinct" ) ) )
3656 flags |= SELECT_DISTINCT;
3658 if ( obj_is_true( jsonObjectGetKey( hash, "no_i18n" ) ) )
3659 flags |= DISABLE_I18N;
3661 osrfLogDebug(OSRF_LOG_MARK, "Building SQL ...");
3664 jsonObjectGetKey( hash, "select" ),
3665 jsonObjectGetKey( hash, "from" ),
3666 jsonObjectGetKey( hash, "where" ),
3667 jsonObjectGetKey( hash, "having" ),
3668 jsonObjectGetKey( hash, "order_by" ),
3669 jsonObjectGetKey( hash, "limit" ),
3670 jsonObjectGetKey( hash, "offset" ),
3679 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
3680 dbi_result result = dbi_conn_query(dbhandle, sql);
3683 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
3685 if (dbi_result_first_row(result)) {
3686 /* JSONify the result */
3687 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
3690 jsonObject* return_val = oilsMakeJSONFromResult( result );
3691 osrfAppRespond( ctx, return_val );
3692 jsonObjectFree( return_val );
3693 } while (dbi_result_next_row(result));
3696 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
3699 osrfAppRespondComplete( ctx, NULL );
3701 /* clean up the query */
3702 dbi_result_free(result);
3706 osrfLogError(OSRF_LOG_MARK, "%s: Error with query [%s]", MODULENAME, sql);
3707 osrfAppSessionStatus(
3709 OSRF_STATUS_INTERNALSERVERERROR,
3710 "osrfMethodException",
3712 "Severe query error -- see error log for more details"
3720 static jsonObject* doFieldmapperSearch ( osrfMethodContext* ctx, osrfHash* meta,
3721 const jsonObject* params, int* err ) {
3724 dbhandle = writehandle;
3726 osrfHash* links = osrfHashGet(meta, "links");
3727 osrfHash* fields = osrfHashGet(meta, "fields");
3728 char* core_class = osrfHashGet(meta, "classname");
3729 char* pkey = osrfHashGet(meta, "primarykey");
3731 const jsonObject* _tmp;
3733 jsonObject* search_hash = jsonObjectGetIndex(params, 0);
3734 jsonObject* order_hash = jsonObjectGetIndex(params, 1);
3736 char* sql = buildSELECT( search_hash, order_hash, meta, ctx );
3738 osrfLogDebug(OSRF_LOG_MARK, "Problem building query, returning NULL");
3743 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
3745 dbi_result result = dbi_conn_query(dbhandle, sql);
3746 if( NULL == result ) {
3747 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]",
3748 MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
3749 osrfAppSessionStatus(
3751 OSRF_STATUS_INTERNALSERVERERROR,
3752 "osrfMethodException",
3754 "Severe query error -- see error log for more details"
3761 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
3764 jsonObject* res_list = jsonNewObjectType(JSON_ARRAY);
3765 osrfHash* dedup = osrfNewHash();
3767 if (dbi_result_first_row(result)) {
3768 /* JSONify the result */
3769 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
3771 obj = oilsMakeFieldmapperFromResult( result, meta );
3772 char* pkey_val = oilsFMGetString( obj, pkey );
3773 if ( osrfHashGet( dedup, pkey_val ) ) {
3774 jsonObjectFree(obj);
3777 osrfHashSet( dedup, pkey_val, pkey_val );
3778 jsonObjectPush(res_list, obj);
3780 } while (dbi_result_next_row(result));
3782 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s",
3786 osrfHashFree(dedup);
3787 /* clean up the query */
3788 dbi_result_free(result);
3791 if (res_list->size && order_hash) {
3792 _tmp = jsonObjectGetKeyConst( order_hash, "flesh" );
3794 int x = (int)jsonObjectGetNumber(_tmp);
3795 if (x == -1 || x > max_flesh_depth) x = max_flesh_depth;
3797 const jsonObject* temp_blob;
3798 if ((temp_blob = jsonObjectGetKeyConst( order_hash, "flesh_fields" )) && x > 0) {
3800 jsonObject* flesh_blob = jsonObjectClone( temp_blob );
3801 const jsonObject* flesh_fields = jsonObjectGetKeyConst( flesh_blob, core_class );
3803 osrfStringArray* link_fields = NULL;
3806 if (flesh_fields->size == 1) {
3807 const char* _t = jsonObjectGetString( jsonObjectGetIndex( flesh_fields, 0 ) );
3808 if (!strcmp(_t,"*")) link_fields = osrfHashKeys( links );
3813 link_fields = osrfNewStringArray(1);
3814 jsonIterator* _i = jsonNewIterator( flesh_fields );
3815 while ((_f = jsonIteratorNext( _i ))) {
3816 osrfStringArrayAdd( link_fields, jsonObjectGetString( _f ) );
3818 jsonIteratorFree(_i);
3823 jsonIterator* itr = jsonNewIterator( res_list );
3824 while ((cur = jsonIteratorNext( itr ))) {
3829 while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
3831 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
3833 osrfHash* kid_link = osrfHashGet(links, link_field);
3834 if (!kid_link) continue;
3836 osrfHash* field = osrfHashGet(fields, link_field);
3837 if (!field) continue;
3839 osrfHash* value_field = field;
3841 osrfHash* kid_idl = osrfHashGet(oilsIDL(), osrfHashGet(kid_link, "class"));
3842 if (!kid_idl) continue;
3844 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
3845 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
3848 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
3849 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
3852 osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
3854 if (link_map->size > 0) {
3855 jsonObject* _kid_key = jsonNewObjectType(JSON_ARRAY);
3858 jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
3863 osrfHashGet(kid_link, "class"),
3870 "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
3871 osrfHashGet(kid_link, "field"),
3872 osrfHashGet(kid_link, "class"),
3873 osrfHashGet(kid_link, "key"),
3874 osrfHashGet(kid_link, "reltype")
3877 jsonObject* fake_params = jsonNewObjectType(JSON_ARRAY);
3878 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH)); // search hash
3879 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH)); // order/flesh hash
3881 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
3883 const char* search_key = jsonObjectGetString(
3886 atoi( osrfHashGet(value_field, "array_position") )
3891 osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
3896 jsonObjectGetIndex(fake_params, 0),
3897 osrfHashGet(kid_link, "key"),
3898 jsonNewObject( search_key )
3902 jsonObjectGetIndex(fake_params, 1),
3904 jsonNewNumberObject( (double)(x - 1 + link_map->size) )
3908 jsonObjectSetKey( jsonObjectGetIndex(fake_params, 1), "flesh_fields", jsonObjectClone(flesh_blob) );
3910 if (jsonObjectGetKeyConst(order_hash, "order_by")) {
3912 jsonObjectGetIndex(fake_params, 1),
3914 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "order_by"))
3918 if (jsonObjectGetKeyConst(order_hash, "select")) {
3920 jsonObjectGetIndex(fake_params, 1),
3922 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "select"))
3926 jsonObject* kids = doFieldmapperSearch(ctx, kid_idl, fake_params, err);
3929 jsonObjectFree( fake_params );
3930 osrfStringArrayFree(link_fields);
3931 jsonIteratorFree(itr);
3932 jsonObjectFree(res_list);
3933 jsonObjectFree(flesh_blob);
3937 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
3939 jsonObject* X = NULL;
3940 if ( link_map->size > 0 && kids->size > 0 ) {
3942 kids = jsonNewObjectType(JSON_ARRAY);
3944 jsonObject* _k_node;
3945 jsonIterator* _k = jsonNewIterator( X );
3946 while ((_k_node = jsonIteratorNext( _k ))) {
3952 (unsigned long)atoi(
3958 osrfHashGet(kid_link, "class")
3962 osrfStringArrayGetString( link_map, 0 )
3971 jsonIteratorFree(_k);
3974 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" )) || !(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) {
3975 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
3978 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
3979 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
3983 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
3984 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
3987 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
3988 jsonObjectClone( kids )
3993 jsonObjectFree(kids);
3997 jsonObjectFree( kids );
3998 jsonObjectFree( fake_params );
4000 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
4001 osrfLogDebug(OSRF_LOG_MARK, "%s", jsonObjectToJSON(cur));
4005 jsonObjectFree( flesh_blob );
4006 osrfStringArrayFree(link_fields);
4007 jsonIteratorFree(itr);
4016 static jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
4018 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4020 jsonObject* target = jsonObjectGetIndex( ctx->params, 1 );
4022 jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
4025 if (!verifyObjectClass(ctx, target)) {
4030 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4031 osrfAppSessionStatus(
4033 OSRF_STATUS_BADREQUEST,
4034 "osrfMethodException",
4036 "No active transaction -- required for UPDATE"
4042 // The following test is harmless but redundant. If a class is
4043 // readonly, we don't register an update method for it.
4044 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4045 osrfAppSessionStatus(
4047 OSRF_STATUS_BADREQUEST,
4048 "osrfMethodException",
4050 "Cannot UPDATE readonly class"
4056 dbhandle = writehandle;
4058 char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
4060 // Set the last_xact_id
4061 int index = oilsIDL_ntop( target->classname, "last_xact_id" );
4063 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
4064 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
4067 char* pkey = osrfHashGet(meta, "primarykey");
4068 osrfHash* fields = osrfHashGet(meta, "fields");
4070 char* id = oilsFMGetString( target, pkey );
4074 "%s updating %s object with %s = %s",
4076 osrfHashGet(meta, "fieldmapper"),
4081 growing_buffer* sql = buffer_init(128);
4082 buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
4087 osrfStringArray* field_list = osrfHashKeys( fields );
4088 while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
4090 osrfHash* field = osrfHashGet( fields, field_name );
4092 if(!( strcmp( field_name, pkey ) )) continue;
4093 if( str_is_true( osrfHashGet(osrfHashGet(fields,field_name), "virtual") ) )
4096 const jsonObject* field_object = oilsFMGetObject( target, field_name );
4098 int value_is_numeric = 0; // boolean
4100 if (field_object && field_object->classname) {
4101 value = oilsFMGetString(
4103 (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
4106 value = jsonObjectToSimpleString( field_object );
4107 if( field_object && JSON_NUMBER == field_object->type )
4108 value_is_numeric = 1;
4111 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
4113 if (!field_object || field_object->type == JSON_NULL) {
4114 if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
4115 if (first) first = 0;
4116 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4117 buffer_fadd( sql, " %s = NULL", field_name );
4120 } else if ( value_is_numeric || !strcmp( get_primitive( field ), "number") ) {
4121 if (first) first = 0;
4122 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4124 const char* numtype = get_datatype( field );
4125 if ( !strncmp( numtype, "INT", 3 ) ) {
4126 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
4127 } else if ( !strcmp( numtype, "NUMERIC" ) ) {
4128 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
4130 // Must really be intended as a string, so quote it
4131 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4132 buffer_fadd( sql, " %s = %s", field_name, value );
4134 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4135 osrfAppSessionStatus(
4137 OSRF_STATUS_INTERNALSERVERERROR,
4138 "osrfMethodException",
4140 "Error quoting string -- please see the error log for more details"
4150 osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, numtype );
4153 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4154 if (first) first = 0;
4155 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4156 buffer_fadd( sql, " %s = %s", field_name, value );
4159 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4160 osrfAppSessionStatus(
4162 OSRF_STATUS_INTERNALSERVERERROR,
4163 "osrfMethodException",
4165 "Error quoting string -- please see the error log for more details"
4179 jsonObject* obj = jsonNewObject(id);
4181 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4182 dbi_conn_quote_string(dbhandle, &id);
4184 buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
4186 char* query = buffer_release(sql);
4187 osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
4189 dbi_result result = dbi_conn_query(dbhandle, query);
4193 jsonObjectFree(obj);
4194 obj = jsonNewObject(NULL);
4197 "%s ERROR updating %s object with %s = %s",
4199 osrfHashGet(meta, "fieldmapper"),
4210 static jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
4212 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4214 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4215 osrfAppSessionStatus(
4217 OSRF_STATUS_BADREQUEST,
4218 "osrfMethodException",
4220 "No active transaction -- required for DELETE"
4226 // The following test is harmless but redundant. If a class is
4227 // readonly, we don't register a delete method for it.
4228 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4229 osrfAppSessionStatus(
4231 OSRF_STATUS_BADREQUEST,
4232 "osrfMethodException",
4234 "Cannot DELETE readonly class"
4240 dbhandle = writehandle;
4244 char* pkey = osrfHashGet(meta, "primarykey");
4252 if (jsonObjectGetIndex(ctx->params, _obj_pos)->classname) {
4253 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, _obj_pos ))) {
4258 id = oilsFMGetString( jsonObjectGetIndex(ctx->params, _obj_pos), pkey );
4261 if (!verifyObjectPCRUD( ctx, NULL )) {
4266 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, _obj_pos));
4271 "%s deleting %s object with %s = %s",
4273 osrfHashGet(meta, "fieldmapper"),
4278 obj = jsonNewObject(id);
4280 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4281 dbi_conn_quote_string(writehandle, &id);
4283 dbi_result result = dbi_conn_queryf(writehandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
4286 jsonObjectFree(obj);
4287 obj = jsonNewObject(NULL);
4290 "%s ERROR deleting %s object with %s = %s",
4292 osrfHashGet(meta, "fieldmapper"),
4305 static jsonObject* oilsMakeFieldmapperFromResult( dbi_result result, osrfHash* meta) {
4306 if(!(result && meta)) return jsonNULL;
4308 jsonObject* object = jsonNewObject(NULL);
4309 jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
4311 osrfHash* fields = osrfHashGet(meta, "fields");
4313 osrfLogInternal(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
4317 char dt_string[256];
4321 int columnIndex = 1;
4323 unsigned short type;
4324 const char* columnName;
4326 /* cycle through the column list */
4327 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4329 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4331 fmIndex = -1; // reset the position
4333 /* determine the field type and storage attributes */
4334 type = dbi_result_get_field_type(result, columnName);
4335 attr = dbi_result_get_field_attribs(result, columnName);
4337 /* fetch the fieldmapper index */
4338 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
4340 if ( str_is_true( osrfHashGet(_f, "virtual") ) )
4343 const char* pos = (char*)osrfHashGet(_f, "array_position");
4344 if ( !pos ) continue;
4346 fmIndex = atoi( pos );
4347 osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
4352 if (dbi_result_field_is_null(result, columnName)) {
4353 jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
4358 case DBI_TYPE_INTEGER :
4360 if( attr & DBI_INTEGER_SIZE8 )
4361 jsonObjectSetIndex( object, fmIndex,
4362 jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
4364 jsonObjectSetIndex( object, fmIndex,
4365 jsonNewNumberObject(dbi_result_get_int(result, columnName)));
4369 case DBI_TYPE_DECIMAL :
4370 jsonObjectSetIndex( object, fmIndex,
4371 jsonNewNumberObject(dbi_result_get_double(result, columnName)));
4374 case DBI_TYPE_STRING :
4380 jsonNewObject( dbi_result_get_string(result, columnName) )
4385 case DBI_TYPE_DATETIME :
4387 memset(dt_string, '\0', sizeof(dt_string));
4388 memset(&gmdt, '\0', sizeof(gmdt));
4390 _tmp_dt = dbi_result_get_datetime(result, columnName);
4393 if (!(attr & DBI_DATETIME_DATE)) {
4394 gmtime_r( &_tmp_dt, &gmdt );
4395 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4396 } else if (!(attr & DBI_DATETIME_TIME)) {
4397 localtime_r( &_tmp_dt, &gmdt );
4398 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4400 localtime_r( &_tmp_dt, &gmdt );
4401 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4404 jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
4408 case DBI_TYPE_BINARY :
4409 osrfLogError( OSRF_LOG_MARK,
4410 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4418 static jsonObject* oilsMakeJSONFromResult( dbi_result result ) {
4419 if(!result) return jsonNULL;
4421 jsonObject* object = jsonNewObject(NULL);
4424 char dt_string[256];
4428 int columnIndex = 1;
4430 unsigned short type;
4431 const char* columnName;
4433 /* cycle through the column list */
4434 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4436 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4438 fmIndex = -1; // reset the position
4440 /* determine the field type and storage attributes */
4441 type = dbi_result_get_field_type(result, columnName);
4442 attr = dbi_result_get_field_attribs(result, columnName);
4444 if (dbi_result_field_is_null(result, columnName)) {
4445 jsonObjectSetKey( object, columnName, jsonNewObject(NULL) );
4450 case DBI_TYPE_INTEGER :
4452 if( attr & DBI_INTEGER_SIZE8 )
4453 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_longlong(result, columnName)) );
4455 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_int(result, columnName)) );
4458 case DBI_TYPE_DECIMAL :
4459 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_double(result, columnName)) );
4462 case DBI_TYPE_STRING :
4463 jsonObjectSetKey( object, columnName, jsonNewObject(dbi_result_get_string(result, columnName)) );
4466 case DBI_TYPE_DATETIME :
4468 memset(dt_string, '\0', sizeof(dt_string));
4469 memset(&gmdt, '\0', sizeof(gmdt));
4471 _tmp_dt = dbi_result_get_datetime(result, columnName);
4474 if (!(attr & DBI_DATETIME_DATE)) {
4475 gmtime_r( &_tmp_dt, &gmdt );
4476 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4477 } else if (!(attr & DBI_DATETIME_TIME)) {
4478 localtime_r( &_tmp_dt, &gmdt );
4479 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4481 localtime_r( &_tmp_dt, &gmdt );
4482 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4485 jsonObjectSetKey( object, columnName, jsonNewObject(dt_string) );
4488 case DBI_TYPE_BINARY :
4489 osrfLogError( OSRF_LOG_MARK,
4490 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4498 // Interpret a string as true or false
4499 static int str_is_true( const char* str ) {
4500 if( NULL == str || strcasecmp( str, "true" ) )
4506 // Interpret a jsonObject as true or false
4507 static int obj_is_true( const jsonObject* obj ) {
4510 else switch( obj->type )
4518 if( strcasecmp( obj->value.s, "true" ) )
4522 case JSON_NUMBER : // Support 1/0 for perl's sake
4523 if( jsonObjectGetNumber( obj ) == 1.0 )
4532 // Translate a numeric code into a text string identifying a type of
4533 // jsonObject. To be used for building error messages.
4534 static const char* json_type( int code ) {
4540 return "JSON_ARRAY";
4542 return "JSON_STRING";
4544 return "JSON_NUMBER";
4550 return "(unrecognized)";
4554 // Extract the "primitive" attribute from an IDL field definition.
4555 // If we haven't initialized the app, then we must be running in
4556 // some kind of testbed. In that case, default to "string".
4557 static const char* get_primitive( osrfHash* field ) {
4558 const char* s = osrfHashGet( field, "primitive" );
4560 if( child_initialized )
4563 "%s ERROR No \"datatype\" attribute for field \"%s\"",
4565 osrfHashGet( field, "name" )
4573 // Extract the "datatype" attribute from an IDL field definition.
4574 // If we haven't initialized the app, then we must be running in
4575 // some kind of testbed. In that case, default to to NUMERIC,
4576 // since we look at the datatype only for numbers.
4577 static const char* get_datatype( osrfHash* field ) {
4578 const char* s = osrfHashGet( field, "datatype" );
4580 if( child_initialized )
4583 "%s ERROR No \"datatype\" attribute for field \"%s\"",
4585 osrfHashGet( field, "name" )
4594 If the input string is potentially a valid SQL identifier, return 1.
4597 Purpose: to prevent certain kinds of SQL injection. To that end we
4598 don't necessarily need to follow all the rules exactly, such as requiring
4599 that the first character not be a digit.
4601 We allow leading and trailing white space. In between, we do not allow
4602 punctuation (except for underscores and dollar signs), control
4603 characters, or embedded white space.
4605 More pedantically we should allow quoted identifiers containing arbitrary
4606 characters, but for the foreseeable future such quoted identifiers are not
4607 likely to be an issue.
4609 static int is_identifier( const char* s) {
4613 // Skip leading white space
4614 while( isspace( (unsigned char) *s ) )
4618 return 0; // Nothing but white space? Not okay.
4620 // Check each character until we reach white space or
4621 // end-of-string. Letters, digits, underscores, and
4622 // dollar signs are okay. Control characters and other
4623 // punctuation characters are not okay. Anything else
4624 // is okay -- it could for example be part of a multibyte
4625 // UTF8 character such as a letter with diacritical marks,
4626 // and those are allowed.
4628 if( isalnum( (unsigned char) *s )
4631 ; // Fine; keep going
4632 else if( ispunct( (unsigned char) *s )
4633 || iscntrl( (unsigned char) *s ) )
4636 } while( *s && ! isspace( (unsigned char) *s ) );
4638 // If we found any white space in the above loop,
4639 // the rest had better be all white space.
4641 while( isspace( (unsigned char) *s ) )
4645 return 0; // White space was embedded within non-white space