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("-not",search_itr->key) ) {
2414 char* subpred = searchWHERE( node, meta, AND_OP_JOIN, ctx );
2416 buffer_fadd(sql_buf, " NOT ( %s )", subpred);
2419 buffer_free( sql_buf );
2422 } else if ( !strcasecmp("-exists",search_itr->key) ) {
2423 char* subpred = SELECT(
2425 jsonObjectGetKey( node, "select" ),
2426 jsonObjectGetKey( node, "from" ),
2427 jsonObjectGetKey( node, "where" ),
2428 jsonObjectGetKey( node, "having" ),
2429 jsonObjectGetKey( node, "order_by" ),
2430 jsonObjectGetKey( node, "limit" ),
2431 jsonObjectGetKey( node, "offset" ),
2435 buffer_fadd(sql_buf, "EXISTS ( %s )", subpred);
2437 } else if ( !strcasecmp("-not-exists",search_itr->key) ) {
2438 char* subpred = SELECT(
2440 jsonObjectGetKey( node, "select" ),
2441 jsonObjectGetKey( node, "from" ),
2442 jsonObjectGetKey( node, "where" ),
2443 jsonObjectGetKey( node, "having" ),
2444 jsonObjectGetKey( node, "order_by" ),
2445 jsonObjectGetKey( node, "limit" ),
2446 jsonObjectGetKey( node, "offset" ),
2450 buffer_fadd(sql_buf, "NOT EXISTS ( %s )", subpred);
2454 char* class = osrfHashGet(meta, "classname");
2455 osrfHash* fields = osrfHashGet(meta, "fields");
2456 osrfHash* field = osrfHashGet( fields, search_itr->key );
2460 char* table = getSourceDefinition(meta);
2462 table = strdup( "(?)" );
2465 "%s: Attempt to reference non-existent column %s on %s (%s)",
2471 buffer_free(sql_buf);
2473 jsonIteratorFree(search_itr);
2477 char* subpred = searchPredicate( class, field, node, ctx );
2478 buffer_add( sql_buf, subpred );
2482 jsonIteratorFree(search_itr);
2485 // ERROR ... only hash and array allowed at this level
2486 char* predicate_string = jsonObjectToJSON( search_hash );
2489 "%s: Invalid predicate structure: %s",
2493 buffer_free(sql_buf);
2494 free(predicate_string);
2498 return buffer_release(sql_buf);
2502 /* method context */ osrfMethodContext* ctx,
2504 /* SELECT */ jsonObject* selhash,
2505 /* FROM */ jsonObject* join_hash,
2506 /* WHERE */ jsonObject* search_hash,
2507 /* HAVING */ jsonObject* having_hash,
2508 /* ORDER BY */ jsonObject* order_hash,
2509 /* LIMIT */ jsonObject* limit,
2510 /* OFFSET */ jsonObject* offset,
2511 /* flags */ int flags
2513 const char* locale = osrf_message_get_last_locale();
2515 // in case we don't get a select list
2516 jsonObject* defaultselhash = NULL;
2518 // general tmp objects
2519 const jsonObject* tmp_const;
2520 jsonObject* selclass = NULL;
2521 jsonObject* selfield = NULL;
2522 jsonObject* snode = NULL;
2523 jsonObject* onode = NULL;
2525 char* string = NULL;
2526 int from_function = 0;
2531 // the core search class
2532 char* core_class = NULL;
2534 // metadata about the core search class
2535 osrfHash* core_meta = NULL;
2537 // punt if there's no core class
2538 if (!join_hash || ( join_hash->type == JSON_HASH && !join_hash->size )) {
2541 "%s: FROM clause is missing or empty",
2545 osrfAppSessionStatus(
2547 OSRF_STATUS_INTERNALSERVERERROR,
2548 "osrfMethodException",
2550 "FROM clause is missing or empty in JSON query"
2555 // get the core class -- the only key of the top level FROM clause, or a string
2556 if (join_hash->type == JSON_HASH) {
2557 jsonIterator* tmp_itr = jsonNewIterator( join_hash );
2558 snode = jsonIteratorNext( tmp_itr );
2560 core_class = strdup( tmp_itr->key );
2563 jsonObject* extra = jsonIteratorNext( tmp_itr );
2565 jsonIteratorFree( tmp_itr );
2568 // There shouldn't be more than one entry in join_hash
2572 "%s: Malformed FROM clause: extra entry in JSON_HASH",
2576 osrfAppSessionStatus(
2578 OSRF_STATUS_INTERNALSERVERERROR,
2579 "osrfMethodException",
2581 "Malformed FROM clause in JSON query"
2584 return NULL; // Malformed join_hash; extra entry
2586 } else if (join_hash->type == JSON_ARRAY) {
2588 core_class = jsonObjectToSimpleString( jsonObjectGetIndex(join_hash, 0) );
2591 } else if (join_hash->type == JSON_STRING) {
2592 core_class = jsonObjectToSimpleString( join_hash );
2598 "%s: FROM clause is unexpected JSON type: %s",
2600 json_type( join_hash->type )
2603 osrfAppSessionStatus(
2605 OSRF_STATUS_INTERNALSERVERERROR,
2606 "osrfMethodException",
2608 "Ill-formed FROM clause in JSON query"
2614 if (!from_function) {
2615 // Get the IDL class definition for the core class
2616 core_meta = osrfHashGet( oilsIDL(), core_class );
2617 if( !core_meta ) { // Didn't find it?
2620 "%s: SELECT clause references undefined class: \"%s\"",
2625 osrfAppSessionStatus(
2627 OSRF_STATUS_INTERNALSERVERERROR,
2628 "osrfMethodException",
2630 "SELECT clause references undefined class in JSON query"
2636 // Make sure the class isn't virtual
2637 if( str_is_true( osrfHashGet( core_meta, "virtual" ) ) ) {
2640 "%s: Core class is virtual: \"%s\"",
2645 osrfAppSessionStatus(
2647 OSRF_STATUS_INTERNALSERVERERROR,
2648 "osrfMethodException",
2650 "FROM clause references virtual class in JSON query"
2657 // if the select list is empty, or the core class field list is '*',
2658 // build the default select list ...
2660 selhash = defaultselhash = jsonNewObjectType(JSON_HASH);
2661 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2662 } else if( selhash->type != JSON_HASH ) {
2665 "%s: Expected JSON_HASH for SELECT clause; found %s",
2667 json_type( selhash->type )
2671 osrfAppSessionStatus(
2673 OSRF_STATUS_INTERNALSERVERERROR,
2674 "osrfMethodException",
2676 "Malformed SELECT clause in JSON query"
2680 } else if ( (tmp_const = jsonObjectGetKeyConst( selhash, core_class )) && tmp_const->type == JSON_STRING ) {
2681 const char* _x = jsonObjectGetString( tmp_const );
2682 if (!strncmp( "*", _x, 1 )) {
2683 jsonObjectRemoveKey( selhash, core_class );
2684 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2689 growing_buffer* sql_buf = buffer_init(128);
2691 // temp buffer for the SELECT list
2692 growing_buffer* select_buf = buffer_init(128);
2693 growing_buffer* order_buf = buffer_init(128);
2694 growing_buffer* group_buf = buffer_init(128);
2695 growing_buffer* having_buf = buffer_init(128);
2697 int aggregate_found = 0; // boolean
2699 // Build a select list
2700 if(from_function) // From a function we select everything
2701 OSRF_BUFFER_ADD_CHAR( select_buf, '*' );
2704 // If we need to build a default list, prepare to do so
2705 jsonObject* _tmp = jsonObjectGetKey( selhash, core_class );
2706 if ( _tmp && !_tmp->size ) {
2708 osrfHash* core_fields = osrfHashGet( core_meta, "fields" );
2710 osrfHashIterator* field_itr = osrfNewHashIterator( core_fields );
2711 osrfHash* field_def;
2712 while( ( field_def = osrfHashIteratorNext( field_itr ) ) ) {
2713 if( ! str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2714 // This field is not virtual, so add it to the list
2715 jsonObjectPush( _tmp, jsonNewObject( osrfHashIteratorKey( field_itr ) ) );
2718 osrfHashIteratorFree( field_itr );
2721 // Now build the actual select list
2725 jsonIterator* selclass_itr = jsonNewIterator( selhash );
2726 while ( (selclass = jsonIteratorNext( selclass_itr )) ) { // For each class
2728 // Make sure the class is defined in the IDL
2729 const char* cname = selclass_itr->key;
2730 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
2734 "%s: Selected class \"%s\" not defined in IDL",
2740 osrfAppSessionStatus(
2742 OSRF_STATUS_INTERNALSERVERERROR,
2743 "osrfMethodException",
2745 "Selected class is not defined"
2747 jsonIteratorFree( selclass_itr );
2748 buffer_free( sql_buf );
2749 buffer_free( select_buf );
2750 buffer_free( order_buf );
2751 buffer_free( group_buf );
2752 buffer_free( having_buf );
2753 if( defaultselhash ) jsonObjectFree( defaultselhash );
2758 // Make sure the target relation is in the join tree.
2760 // At this point join_hash is a step down from the join_hash we
2761 // received as a parameter. If the original was a JSON_STRING,
2762 // then json_hash is now NULL. If the original was a JSON_HASH,
2763 // then json_hash is now the first (and only) entry in it,
2764 // denoting the core class. We've already excluded the
2765 // possibility that the original was a JSON_ARRAY, because in
2766 // that case from_function would be non-NULL, and we wouldn't
2769 int class_in_from_clause; // boolean
2771 if ( ! strcmp( core_class, cname ))
2772 // This is the core class -- no problem
2773 class_in_from_clause = 1;
2776 // There's only one class in the FROM clause, and this isn't it
2777 class_in_from_clause = 0;
2778 else if (join_hash->type == JSON_STRING) {
2779 // There's only one class in the FROM clause
2780 const char* str = jsonObjectGetString(join_hash);
2781 if ( strcmp( str, cname ) )
2782 class_in_from_clause = 0; // This isn't it
2784 class_in_from_clause = 1; // This is it
2786 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", cname);
2787 if ( 0 == found->size )
2788 class_in_from_clause = 0; // Nowhere in the join tree
2790 class_in_from_clause = 1; // Found it
2791 jsonObjectFree( found );
2795 // If the class isn't in the FROM clause, bail out
2796 if( ! class_in_from_clause ) {
2799 "%s: SELECT clause references class not in FROM clause: \"%s\"",
2804 osrfAppSessionStatus(
2806 OSRF_STATUS_INTERNALSERVERERROR,
2807 "osrfMethodException",
2809 "Selected class not in FROM clause in JSON query"
2811 jsonIteratorFree( selclass_itr );
2812 buffer_free( sql_buf );
2813 buffer_free( select_buf );
2814 buffer_free( order_buf );
2815 buffer_free( group_buf );
2816 buffer_free( having_buf );
2817 if( defaultselhash ) jsonObjectFree( defaultselhash );
2822 // Look up some attributes of the current class, so that we
2823 // don't have to look them up again for each field
2824 osrfHash* class_field_set = osrfHashGet( idlClass, "fields" );
2825 const char* class_pkey = osrfHashGet( idlClass, "primarykey" );
2826 const char* class_tname = osrfHashGet( idlClass, "tablename" );
2828 // stitch together the column list ...
2829 jsonIterator* select_itr = jsonNewIterator( selclass );
2830 while ( (selfield = jsonIteratorNext( select_itr )) ) { // for each SELECT column
2832 // If we need a separator comma, add one
2836 OSRF_BUFFER_ADD_CHAR( select_buf, ',' );
2839 // ... if it's a string, just toss it on the pile
2840 if (selfield->type == JSON_STRING) {
2842 // Look up the field in the IDL
2843 const char* col_name = jsonObjectGetString( selfield );
2844 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
2846 // No such field in current class
2849 "%s: Selected column \"%s\" not defined in IDL for class \"%s\"",
2855 osrfAppSessionStatus(
2857 OSRF_STATUS_INTERNALSERVERERROR,
2858 "osrfMethodException",
2860 "Selected column not defined in JSON query"
2862 jsonIteratorFree( select_itr );
2863 jsonIteratorFree( selclass_itr );
2864 buffer_free( sql_buf );
2865 buffer_free( select_buf );
2866 buffer_free( order_buf );
2867 buffer_free( group_buf );
2868 buffer_free( having_buf );
2869 if( defaultselhash ) jsonObjectFree( defaultselhash );
2872 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2873 // Virtual field not allowed
2876 "%s: Selected column \"%s\" for class \"%s\" is virtual",
2882 osrfAppSessionStatus(
2884 OSRF_STATUS_INTERNALSERVERERROR,
2885 "osrfMethodException",
2887 "Selected column may not be virtual in JSON query"
2889 jsonIteratorFree( select_itr );
2890 jsonIteratorFree( selclass_itr );
2891 buffer_free( sql_buf );
2892 buffer_free( select_buf );
2893 buffer_free( order_buf );
2894 buffer_free( group_buf );
2895 buffer_free( having_buf );
2896 if( defaultselhash ) jsonObjectFree( defaultselhash );
2903 if (flags & DISABLE_I18N)
2906 i18n = osrfHashGet(field_def, "i18n");
2908 if( str_is_true( i18n ) ) {
2909 buffer_fadd( select_buf,
2910 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
2911 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, col_name );
2913 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
2916 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
2919 // ... but it could be an object, in which case we check for a Field Transform
2920 } else if (selfield->type == JSON_HASH) {
2922 const char* col_name = jsonObjectGetString( jsonObjectGetKeyConst( selfield, "column" ) );
2924 // Get the field definition from the IDL
2925 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
2927 // No such field in current class
2930 "%s: Selected column \"%s\" is not defined in IDL for class \"%s\"",
2936 osrfAppSessionStatus(
2938 OSRF_STATUS_INTERNALSERVERERROR,
2939 "osrfMethodException",
2941 "Selected column is not defined in JSON query"
2943 jsonIteratorFree( select_itr );
2944 jsonIteratorFree( selclass_itr );
2945 buffer_free( sql_buf );
2946 buffer_free( select_buf );
2947 buffer_free( order_buf );
2948 buffer_free( group_buf );
2949 buffer_free( having_buf );
2950 if( defaultselhash ) jsonObjectFree( defaultselhash );
2953 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2954 // No such field in current class
2957 "%s: Selected column \"%s\" is virtual for class \"%s\"",
2963 osrfAppSessionStatus(
2965 OSRF_STATUS_INTERNALSERVERERROR,
2966 "osrfMethodException",
2968 "Selected column is virtual in JSON query"
2970 jsonIteratorFree( select_itr );
2971 jsonIteratorFree( selclass_itr );
2972 buffer_free( sql_buf );
2973 buffer_free( select_buf );
2974 buffer_free( order_buf );
2975 buffer_free( group_buf );
2976 buffer_free( having_buf );
2977 if( defaultselhash ) jsonObjectFree( defaultselhash );
2982 // Decide what to use as a column alias
2984 if ((tmp_const = jsonObjectGetKeyConst( selfield, "alias" ))) {
2985 _alias = jsonObjectGetString( tmp_const );
2986 } else { // Use field name as the alias
2990 if (jsonObjectGetKeyConst( selfield, "transform" )) {
2991 char* transform_str = searchFieldTransform(cname, field_def, selfield);
2992 if( transform_str ) {
2993 buffer_fadd(select_buf, " %s AS \"%s\"", transform_str, _alias);
2994 free(transform_str);
2997 osrfAppSessionStatus(
2999 OSRF_STATUS_INTERNALSERVERERROR,
3000 "osrfMethodException",
3002 "Unable to generate transform function in JSON query"
3004 jsonIteratorFree( select_itr );
3005 jsonIteratorFree( selclass_itr );
3006 buffer_free( sql_buf );
3007 buffer_free( select_buf );
3008 buffer_free( order_buf );
3009 buffer_free( group_buf );
3010 buffer_free( having_buf );
3011 if( defaultselhash ) jsonObjectFree( defaultselhash );
3019 if (flags & DISABLE_I18N)
3022 i18n = osrfHashGet(field_def, "i18n");
3024 if( str_is_true( i18n ) ) {
3025 buffer_fadd( select_buf,
3026 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
3027 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, _alias);
3029 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
3032 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
3039 "%s: Selected item is unexpected JSON type: %s",
3041 json_type( selfield->type )
3044 osrfAppSessionStatus(
3046 OSRF_STATUS_INTERNALSERVERERROR,
3047 "osrfMethodException",
3049 "Ill-formed SELECT item in JSON query"
3051 jsonIteratorFree( select_itr );
3052 jsonIteratorFree( selclass_itr );
3053 buffer_free( sql_buf );
3054 buffer_free( select_buf );
3055 buffer_free( order_buf );
3056 buffer_free( group_buf );
3057 buffer_free( having_buf );
3058 if( defaultselhash ) jsonObjectFree( defaultselhash );
3063 const jsonObject* agg_obj = jsonObjectGetKey( selfield, "aggregate" );
3064 if( obj_is_true( agg_obj ) )
3065 aggregate_found = 1;
3067 // Append a comma (except for the first one)
3068 // and add the column to a GROUP BY clause
3072 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3074 buffer_fadd(group_buf, " %d", sel_pos);
3078 if (is_agg->size || (flags & SELECT_DISTINCT)) {
3080 const jsonObject* aggregate_obj = jsonObjectGetKey( selfield, "aggregate" );
3081 if ( ! obj_is_true( aggregate_obj ) ) {
3085 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3088 buffer_fadd(group_buf, " %d", sel_pos);
3091 } else if (is_agg = jsonObjectGetKey( selfield, "having" )) {
3095 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3098 _column = searchFieldTransform(cname, field, selfield);
3099 OSRF_BUFFER_ADD_CHAR(group_buf, ' ');
3100 OSRF_BUFFER_ADD(group_buf, _column);
3101 _column = searchFieldTransform(cname, field, selfield);
3108 } // end while -- iterating across SELECT columns
3110 jsonIteratorFree(select_itr);
3111 } // end while -- iterating across classes
3113 jsonIteratorFree(selclass_itr);
3117 char* col_list = buffer_release(select_buf);
3119 if (from_function) table = searchValueTransform(join_hash);
3120 else table = getSourceDefinition(core_meta);
3124 osrfAppSessionStatus(
3126 OSRF_STATUS_INTERNALSERVERERROR,
3127 "osrfMethodException",
3129 "Unable to identify table for core class"
3132 buffer_free( sql_buf );
3133 buffer_free( order_buf );
3134 buffer_free( group_buf );
3135 buffer_free( having_buf );
3136 if( defaultselhash ) jsonObjectFree( defaultselhash );
3141 // Put it all together
3142 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\" ", col_list, table, core_class );
3146 if (!from_function) {
3147 // Now, walk the join tree and add that clause
3149 char* join_clause = searchJOIN( join_hash, core_meta );
3151 buffer_add(sql_buf, join_clause);
3155 osrfAppSessionStatus(
3157 OSRF_STATUS_INTERNALSERVERERROR,
3158 "osrfMethodException",
3160 "Unable to construct JOIN clause(s)"
3162 buffer_free( sql_buf );
3163 buffer_free( order_buf );
3164 buffer_free( group_buf );
3165 buffer_free( having_buf );
3166 if( defaultselhash ) jsonObjectFree( defaultselhash );
3172 // Build a WHERE clause, if there is one
3173 if ( search_hash ) {
3174 buffer_add(sql_buf, " WHERE ");
3176 // and it's on the WHERE clause
3177 char* pred = searchWHERE( search_hash, core_meta, AND_OP_JOIN, ctx );
3180 buffer_add(sql_buf, pred);
3184 osrfAppSessionStatus(
3186 OSRF_STATUS_INTERNALSERVERERROR,
3187 "osrfMethodException",
3189 "Severe query error in WHERE predicate -- see error log for more details"
3193 buffer_free(having_buf);
3194 buffer_free(group_buf);
3195 buffer_free(order_buf);
3196 buffer_free(sql_buf);
3197 if (defaultselhash) jsonObjectFree(defaultselhash);
3202 // Build a HAVING clause, if there is one
3203 if ( having_hash ) {
3204 buffer_add(sql_buf, " HAVING ");
3206 // and it's on the the WHERE clause
3207 char* pred = searchWHERE( having_hash, core_meta, AND_OP_JOIN, ctx );
3210 buffer_add(sql_buf, pred);
3214 osrfAppSessionStatus(
3216 OSRF_STATUS_INTERNALSERVERERROR,
3217 "osrfMethodException",
3219 "Severe query error in HAVING predicate -- see error log for more details"
3223 buffer_free(having_buf);
3224 buffer_free(group_buf);
3225 buffer_free(order_buf);
3226 buffer_free(sql_buf);
3227 if (defaultselhash) jsonObjectFree(defaultselhash);
3232 // Build an ORDER BY clause, if there is one
3234 jsonIterator* class_itr = jsonNewIterator( order_hash );
3235 while ( (snode = jsonIteratorNext( class_itr )) ) {
3237 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
3240 if ( snode->type == JSON_HASH ) {
3242 jsonIterator* order_itr = jsonNewIterator( snode );
3243 while ( (onode = jsonIteratorNext( order_itr )) ) {
3245 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ))
3248 const char* direction = NULL;
3249 if ( onode->type == JSON_HASH ) {
3250 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3251 string = searchFieldTransform(
3253 oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ),
3257 growing_buffer* field_buf = buffer_init(16);
3258 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3259 string = buffer_release(field_buf);
3262 if ( (tmp_const = jsonObjectGetKeyConst( onode, "direction" )) ) {
3263 const char* dir = jsonObjectGetString(tmp_const);
3264 if (!strncasecmp(dir, "d", 1)) {
3265 direction = " DESC";
3272 string = strdup(order_itr->key);
3273 const char* dir = jsonObjectGetString(onode);
3274 if (!strncasecmp(dir, "d", 1)) {
3275 direction = " DESC";
3284 buffer_add(order_buf, ", ");
3287 buffer_add(order_buf, string);
3291 buffer_add(order_buf, direction);
3295 // jsonIteratorFree(order_itr);
3297 } else if ( snode->type == JSON_ARRAY ) {
3299 jsonIterator* order_itr = jsonNewIterator( snode );
3300 while ( (onode = jsonIteratorNext( order_itr )) ) {
3302 const char* _f = jsonObjectGetString( onode );
3304 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, _f))
3310 buffer_add(order_buf, ", ");
3313 buffer_add(order_buf, _f);
3316 // jsonIteratorFree(order_itr);
3319 // IT'S THE OOOOOOOOOOOLD STYLE!
3321 osrfLogError(OSRF_LOG_MARK, "%s: Possible SQL injection attempt; direct order by is not allowed", MODULENAME);
3323 osrfAppSessionStatus(
3325 OSRF_STATUS_INTERNALSERVERERROR,
3326 "osrfMethodException",
3328 "Severe query error -- see error log for more details"
3333 buffer_free(having_buf);
3334 buffer_free(group_buf);
3335 buffer_free(order_buf);
3336 buffer_free(sql_buf);
3337 if (defaultselhash) jsonObjectFree(defaultselhash);
3338 jsonIteratorFree(class_itr);
3343 // jsonIteratorFree(class_itr);
3347 string = buffer_release(group_buf);
3349 if ( *string && ( aggregate_found || (flags & SELECT_DISTINCT) ) ) {
3350 OSRF_BUFFER_ADD( sql_buf, " GROUP BY " );
3351 OSRF_BUFFER_ADD( sql_buf, string );
3356 string = buffer_release(having_buf);
3359 OSRF_BUFFER_ADD( sql_buf, " HAVING " );
3360 OSRF_BUFFER_ADD( sql_buf, string );
3365 string = buffer_release(order_buf);
3368 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3369 OSRF_BUFFER_ADD( sql_buf, string );
3375 const char* str = jsonObjectGetString(limit);
3376 buffer_fadd( sql_buf, " LIMIT %d", atoi(str) );
3380 const char* str = jsonObjectGetString(offset);
3381 buffer_fadd( sql_buf, " OFFSET %d", atoi(str) );
3384 if (!(flags & SUBSELECT)) OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3387 if (defaultselhash) jsonObjectFree(defaultselhash);
3389 return buffer_release(sql_buf);
3393 static char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
3395 const char* locale = osrf_message_get_last_locale();
3397 osrfHash* fields = osrfHashGet(meta, "fields");
3398 char* core_class = osrfHashGet(meta, "classname");
3400 const jsonObject* join_hash = jsonObjectGetKeyConst( order_hash, "join" );
3402 jsonObject* node = NULL;
3403 jsonObject* snode = NULL;
3404 jsonObject* onode = NULL;
3405 const jsonObject* _tmp = NULL;
3406 jsonObject* selhash = NULL;
3407 jsonObject* defaultselhash = NULL;
3409 growing_buffer* sql_buf = buffer_init(128);
3410 growing_buffer* select_buf = buffer_init(128);
3412 if ( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
3413 defaultselhash = jsonNewObjectType(JSON_HASH);
3414 selhash = defaultselhash;
3417 if ( !jsonObjectGetKeyConst(selhash,core_class) ) {
3418 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
3419 jsonObject* flist = jsonObjectGetKey( selhash, core_class );
3424 osrfStringArray* keys = osrfHashKeys( fields );
3425 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
3426 if( ! str_is_true( osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
3427 jsonObjectPush( flist, jsonNewObject( field ) );
3429 osrfStringArrayFree(keys);
3433 jsonIterator* class_itr = jsonNewIterator( selhash );
3434 while ( (snode = jsonIteratorNext( class_itr )) ) {
3436 char* cname = class_itr->key;
3437 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
3438 if (!idlClass) continue;
3440 if (strcmp(core_class,class_itr->key)) {
3441 if (!join_hash) continue;
3443 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", class_itr->key);
3445 jsonObjectFree(found);
3449 jsonObjectFree(found);
3452 jsonIterator* select_itr = jsonNewIterator( snode );
3453 while ( (node = jsonIteratorNext( select_itr )) ) {
3454 const char* item_str = jsonObjectGetString( node );
3455 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), item_str );
3456 char* fname = osrfHashGet(field, "name");
3458 if (!field) continue;
3463 OSRF_BUFFER_ADD_CHAR(select_buf, ',');
3468 const jsonObject* no_i18n_obj = jsonObjectGetKey( order_hash, "no_i18n" );
3469 if ( obj_is_true( no_i18n_obj ) ) // Suppress internationalization?
3472 i18n = osrfHashGet(field, "i18n");
3474 if( str_is_true( i18n ) ) {
3475 char* pkey = osrfHashGet(idlClass, "primarykey");
3476 char* tname = osrfHashGet(idlClass, "tablename");
3478 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);
3480 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3483 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3487 jsonIteratorFree(select_itr);
3490 jsonIteratorFree(class_itr);
3492 char* col_list = buffer_release(select_buf);
3493 char* table = getSourceDefinition(meta);
3495 table = strdup( "(null)" );
3497 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\"", col_list, table, core_class );
3502 char* join_clause = searchJOIN( join_hash, meta );
3503 OSRF_BUFFER_ADD_CHAR(sql_buf, ' ');
3504 OSRF_BUFFER_ADD(sql_buf, join_clause);
3508 osrfLogDebug(OSRF_LOG_MARK, "%s pre-predicate SQL = %s",
3509 MODULENAME, OSRF_BUFFER_C_STR(sql_buf));
3511 buffer_add(sql_buf, " WHERE ");
3513 char* pred = searchWHERE( search_hash, meta, AND_OP_JOIN, ctx );
3515 osrfAppSessionStatus(
3517 OSRF_STATUS_INTERNALSERVERERROR,
3518 "osrfMethodException",
3520 "Severe query error -- see error log for more details"
3522 buffer_free(sql_buf);
3523 if(defaultselhash) jsonObjectFree(defaultselhash);
3526 buffer_add(sql_buf, pred);
3531 char* string = NULL;
3532 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "order_by" )) ){
3534 growing_buffer* order_buf = buffer_init(128);
3537 jsonIterator* class_itr = jsonNewIterator( _tmp );
3538 while ( (snode = jsonIteratorNext( class_itr )) ) {
3540 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
3543 if ( snode->type == JSON_HASH ) {
3545 jsonIterator* order_itr = jsonNewIterator( snode );
3546 while ( (onode = jsonIteratorNext( order_itr )) ) {
3548 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ))
3551 char* direction = NULL;
3552 if ( onode->type == JSON_HASH ) {
3553 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3554 string = searchFieldTransform(
3556 oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ),
3560 growing_buffer* field_buf = buffer_init(16);
3561 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3562 string = buffer_release(field_buf);
3565 if ( (_tmp = jsonObjectGetKeyConst( onode, "direction" )) ) {
3566 const char* dir = jsonObjectGetString(_tmp);
3567 if (!strncasecmp(dir, "d", 1)) {
3568 direction = " DESC";
3575 string = strdup(order_itr->key);
3576 const char* dir = jsonObjectGetString(onode);
3577 if (!strncasecmp(dir, "d", 1)) {
3578 direction = " DESC";
3587 buffer_add(order_buf, ", ");
3590 buffer_add(order_buf, string);
3594 buffer_add(order_buf, direction);
3599 jsonIteratorFree(order_itr);
3602 const char* str = jsonObjectGetString(snode);
3603 buffer_add(order_buf, str);
3609 jsonIteratorFree(class_itr);
3611 string = buffer_release(order_buf);
3614 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3615 OSRF_BUFFER_ADD( sql_buf, string );
3621 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "limit" )) ){
3622 const char* str = jsonObjectGetString(_tmp);
3630 _tmp = jsonObjectGetKeyConst( order_hash, "offset" );
3632 const char* str = jsonObjectGetString(_tmp);
3641 if (defaultselhash) jsonObjectFree(defaultselhash);
3643 OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3644 return buffer_release(sql_buf);
3647 int doJSONSearch ( osrfMethodContext* ctx ) {
3648 if(osrfMethodVerifyContext( ctx )) {
3649 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
3653 osrfLogDebug(OSRF_LOG_MARK, "Recieved query request");
3658 dbhandle = writehandle;
3660 jsonObject* hash = jsonObjectGetIndex(ctx->params, 0);
3664 if ( obj_is_true( jsonObjectGetKey( hash, "distinct" ) ) )
3665 flags |= SELECT_DISTINCT;
3667 if ( obj_is_true( jsonObjectGetKey( hash, "no_i18n" ) ) )
3668 flags |= DISABLE_I18N;
3670 osrfLogDebug(OSRF_LOG_MARK, "Building SQL ...");
3673 jsonObjectGetKey( hash, "select" ),
3674 jsonObjectGetKey( hash, "from" ),
3675 jsonObjectGetKey( hash, "where" ),
3676 jsonObjectGetKey( hash, "having" ),
3677 jsonObjectGetKey( hash, "order_by" ),
3678 jsonObjectGetKey( hash, "limit" ),
3679 jsonObjectGetKey( hash, "offset" ),
3688 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
3689 dbi_result result = dbi_conn_query(dbhandle, sql);
3692 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
3694 if (dbi_result_first_row(result)) {
3695 /* JSONify the result */
3696 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
3699 jsonObject* return_val = oilsMakeJSONFromResult( result );
3700 osrfAppRespond( ctx, return_val );
3701 jsonObjectFree( return_val );
3702 } while (dbi_result_next_row(result));
3705 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
3708 osrfAppRespondComplete( ctx, NULL );
3710 /* clean up the query */
3711 dbi_result_free(result);
3715 osrfLogError(OSRF_LOG_MARK, "%s: Error with query [%s]", MODULENAME, sql);
3716 osrfAppSessionStatus(
3718 OSRF_STATUS_INTERNALSERVERERROR,
3719 "osrfMethodException",
3721 "Severe query error -- see error log for more details"
3729 static jsonObject* doFieldmapperSearch ( osrfMethodContext* ctx, osrfHash* meta,
3730 const jsonObject* params, int* err ) {
3733 dbhandle = writehandle;
3735 osrfHash* links = osrfHashGet(meta, "links");
3736 osrfHash* fields = osrfHashGet(meta, "fields");
3737 char* core_class = osrfHashGet(meta, "classname");
3738 char* pkey = osrfHashGet(meta, "primarykey");
3740 const jsonObject* _tmp;
3742 jsonObject* search_hash = jsonObjectGetIndex(params, 0);
3743 jsonObject* order_hash = jsonObjectGetIndex(params, 1);
3745 char* sql = buildSELECT( search_hash, order_hash, meta, ctx );
3747 osrfLogDebug(OSRF_LOG_MARK, "Problem building query, returning NULL");
3752 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
3754 dbi_result result = dbi_conn_query(dbhandle, sql);
3755 if( NULL == result ) {
3756 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]",
3757 MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
3758 osrfAppSessionStatus(
3760 OSRF_STATUS_INTERNALSERVERERROR,
3761 "osrfMethodException",
3763 "Severe query error -- see error log for more details"
3770 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
3773 jsonObject* res_list = jsonNewObjectType(JSON_ARRAY);
3774 osrfHash* dedup = osrfNewHash();
3776 if (dbi_result_first_row(result)) {
3777 /* JSONify the result */
3778 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
3780 obj = oilsMakeFieldmapperFromResult( result, meta );
3781 char* pkey_val = oilsFMGetString( obj, pkey );
3782 if ( osrfHashGet( dedup, pkey_val ) ) {
3783 jsonObjectFree(obj);
3786 osrfHashSet( dedup, pkey_val, pkey_val );
3787 jsonObjectPush(res_list, obj);
3789 } while (dbi_result_next_row(result));
3791 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s",
3795 osrfHashFree(dedup);
3796 /* clean up the query */
3797 dbi_result_free(result);
3800 if (res_list->size && order_hash) {
3801 _tmp = jsonObjectGetKeyConst( order_hash, "flesh" );
3803 int x = (int)jsonObjectGetNumber(_tmp);
3804 if (x == -1 || x > max_flesh_depth) x = max_flesh_depth;
3806 const jsonObject* temp_blob;
3807 if ((temp_blob = jsonObjectGetKeyConst( order_hash, "flesh_fields" )) && x > 0) {
3809 jsonObject* flesh_blob = jsonObjectClone( temp_blob );
3810 const jsonObject* flesh_fields = jsonObjectGetKeyConst( flesh_blob, core_class );
3812 osrfStringArray* link_fields = NULL;
3815 if (flesh_fields->size == 1) {
3816 const char* _t = jsonObjectGetString( jsonObjectGetIndex( flesh_fields, 0 ) );
3817 if (!strcmp(_t,"*")) link_fields = osrfHashKeys( links );
3822 link_fields = osrfNewStringArray(1);
3823 jsonIterator* _i = jsonNewIterator( flesh_fields );
3824 while ((_f = jsonIteratorNext( _i ))) {
3825 osrfStringArrayAdd( link_fields, jsonObjectGetString( _f ) );
3827 jsonIteratorFree(_i);
3832 jsonIterator* itr = jsonNewIterator( res_list );
3833 while ((cur = jsonIteratorNext( itr ))) {
3838 while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
3840 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
3842 osrfHash* kid_link = osrfHashGet(links, link_field);
3843 if (!kid_link) continue;
3845 osrfHash* field = osrfHashGet(fields, link_field);
3846 if (!field) continue;
3848 osrfHash* value_field = field;
3850 osrfHash* kid_idl = osrfHashGet(oilsIDL(), osrfHashGet(kid_link, "class"));
3851 if (!kid_idl) continue;
3853 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
3854 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
3857 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
3858 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
3861 osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
3863 if (link_map->size > 0) {
3864 jsonObject* _kid_key = jsonNewObjectType(JSON_ARRAY);
3867 jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
3872 osrfHashGet(kid_link, "class"),
3879 "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
3880 osrfHashGet(kid_link, "field"),
3881 osrfHashGet(kid_link, "class"),
3882 osrfHashGet(kid_link, "key"),
3883 osrfHashGet(kid_link, "reltype")
3886 jsonObject* fake_params = jsonNewObjectType(JSON_ARRAY);
3887 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH)); // search hash
3888 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH)); // order/flesh hash
3890 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
3892 const char* search_key = jsonObjectGetString(
3895 atoi( osrfHashGet(value_field, "array_position") )
3900 osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
3905 jsonObjectGetIndex(fake_params, 0),
3906 osrfHashGet(kid_link, "key"),
3907 jsonNewObject( search_key )
3911 jsonObjectGetIndex(fake_params, 1),
3913 jsonNewNumberObject( (double)(x - 1 + link_map->size) )
3917 jsonObjectSetKey( jsonObjectGetIndex(fake_params, 1), "flesh_fields", jsonObjectClone(flesh_blob) );
3919 if (jsonObjectGetKeyConst(order_hash, "order_by")) {
3921 jsonObjectGetIndex(fake_params, 1),
3923 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "order_by"))
3927 if (jsonObjectGetKeyConst(order_hash, "select")) {
3929 jsonObjectGetIndex(fake_params, 1),
3931 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "select"))
3935 jsonObject* kids = doFieldmapperSearch(ctx, kid_idl, fake_params, err);
3938 jsonObjectFree( fake_params );
3939 osrfStringArrayFree(link_fields);
3940 jsonIteratorFree(itr);
3941 jsonObjectFree(res_list);
3942 jsonObjectFree(flesh_blob);
3946 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
3948 jsonObject* X = NULL;
3949 if ( link_map->size > 0 && kids->size > 0 ) {
3951 kids = jsonNewObjectType(JSON_ARRAY);
3953 jsonObject* _k_node;
3954 jsonIterator* _k = jsonNewIterator( X );
3955 while ((_k_node = jsonIteratorNext( _k ))) {
3961 (unsigned long)atoi(
3967 osrfHashGet(kid_link, "class")
3971 osrfStringArrayGetString( link_map, 0 )
3980 jsonIteratorFree(_k);
3983 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" )) || !(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) {
3984 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
3987 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
3988 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
3992 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
3993 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
3996 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
3997 jsonObjectClone( kids )
4002 jsonObjectFree(kids);
4006 jsonObjectFree( kids );
4007 jsonObjectFree( fake_params );
4009 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
4010 osrfLogDebug(OSRF_LOG_MARK, "%s", jsonObjectToJSON(cur));
4014 jsonObjectFree( flesh_blob );
4015 osrfStringArrayFree(link_fields);
4016 jsonIteratorFree(itr);
4025 static jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
4027 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4029 jsonObject* target = jsonObjectGetIndex( ctx->params, 1 );
4031 jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
4034 if (!verifyObjectClass(ctx, target)) {
4039 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4040 osrfAppSessionStatus(
4042 OSRF_STATUS_BADREQUEST,
4043 "osrfMethodException",
4045 "No active transaction -- required for UPDATE"
4051 // The following test is harmless but redundant. If a class is
4052 // readonly, we don't register an update method for it.
4053 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4054 osrfAppSessionStatus(
4056 OSRF_STATUS_BADREQUEST,
4057 "osrfMethodException",
4059 "Cannot UPDATE readonly class"
4065 dbhandle = writehandle;
4067 char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
4069 // Set the last_xact_id
4070 int index = oilsIDL_ntop( target->classname, "last_xact_id" );
4072 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
4073 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
4076 char* pkey = osrfHashGet(meta, "primarykey");
4077 osrfHash* fields = osrfHashGet(meta, "fields");
4079 char* id = oilsFMGetString( target, pkey );
4083 "%s updating %s object with %s = %s",
4085 osrfHashGet(meta, "fieldmapper"),
4090 growing_buffer* sql = buffer_init(128);
4091 buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
4096 osrfStringArray* field_list = osrfHashKeys( fields );
4097 while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
4099 osrfHash* field = osrfHashGet( fields, field_name );
4101 if(!( strcmp( field_name, pkey ) )) continue;
4102 if( str_is_true( osrfHashGet(osrfHashGet(fields,field_name), "virtual") ) )
4105 const jsonObject* field_object = oilsFMGetObject( target, field_name );
4107 int value_is_numeric = 0; // boolean
4109 if (field_object && field_object->classname) {
4110 value = oilsFMGetString(
4112 (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
4115 value = jsonObjectToSimpleString( field_object );
4116 if( field_object && JSON_NUMBER == field_object->type )
4117 value_is_numeric = 1;
4120 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
4122 if (!field_object || field_object->type == JSON_NULL) {
4123 if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
4124 if (first) first = 0;
4125 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4126 buffer_fadd( sql, " %s = NULL", field_name );
4129 } else if ( value_is_numeric || !strcmp( get_primitive( field ), "number") ) {
4130 if (first) first = 0;
4131 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4133 const char* numtype = get_datatype( field );
4134 if ( !strncmp( numtype, "INT", 3 ) ) {
4135 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
4136 } else if ( !strcmp( numtype, "NUMERIC" ) ) {
4137 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
4139 // Must really be intended as a string, so quote it
4140 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4141 buffer_fadd( sql, " %s = %s", field_name, value );
4143 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4144 osrfAppSessionStatus(
4146 OSRF_STATUS_INTERNALSERVERERROR,
4147 "osrfMethodException",
4149 "Error quoting string -- please see the error log for more details"
4159 osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, numtype );
4162 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4163 if (first) first = 0;
4164 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4165 buffer_fadd( sql, " %s = %s", field_name, value );
4168 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4169 osrfAppSessionStatus(
4171 OSRF_STATUS_INTERNALSERVERERROR,
4172 "osrfMethodException",
4174 "Error quoting string -- please see the error log for more details"
4188 jsonObject* obj = jsonNewObject(id);
4190 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4191 dbi_conn_quote_string(dbhandle, &id);
4193 buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
4195 char* query = buffer_release(sql);
4196 osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
4198 dbi_result result = dbi_conn_query(dbhandle, query);
4202 jsonObjectFree(obj);
4203 obj = jsonNewObject(NULL);
4206 "%s ERROR updating %s object with %s = %s",
4208 osrfHashGet(meta, "fieldmapper"),
4219 static jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
4221 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4223 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4224 osrfAppSessionStatus(
4226 OSRF_STATUS_BADREQUEST,
4227 "osrfMethodException",
4229 "No active transaction -- required for DELETE"
4235 // The following test is harmless but redundant. If a class is
4236 // readonly, we don't register a delete method for it.
4237 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4238 osrfAppSessionStatus(
4240 OSRF_STATUS_BADREQUEST,
4241 "osrfMethodException",
4243 "Cannot DELETE readonly class"
4249 dbhandle = writehandle;
4253 char* pkey = osrfHashGet(meta, "primarykey");
4261 if (jsonObjectGetIndex(ctx->params, _obj_pos)->classname) {
4262 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, _obj_pos ))) {
4267 id = oilsFMGetString( jsonObjectGetIndex(ctx->params, _obj_pos), pkey );
4270 if (!verifyObjectPCRUD( ctx, NULL )) {
4275 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, _obj_pos));
4280 "%s deleting %s object with %s = %s",
4282 osrfHashGet(meta, "fieldmapper"),
4287 obj = jsonNewObject(id);
4289 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4290 dbi_conn_quote_string(writehandle, &id);
4292 dbi_result result = dbi_conn_queryf(writehandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
4295 jsonObjectFree(obj);
4296 obj = jsonNewObject(NULL);
4299 "%s ERROR deleting %s object with %s = %s",
4301 osrfHashGet(meta, "fieldmapper"),
4314 static jsonObject* oilsMakeFieldmapperFromResult( dbi_result result, osrfHash* meta) {
4315 if(!(result && meta)) return jsonNULL;
4317 jsonObject* object = jsonNewObject(NULL);
4318 jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
4320 osrfHash* fields = osrfHashGet(meta, "fields");
4322 osrfLogInternal(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
4326 char dt_string[256];
4330 int columnIndex = 1;
4332 unsigned short type;
4333 const char* columnName;
4335 /* cycle through the column list */
4336 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4338 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4340 fmIndex = -1; // reset the position
4342 /* determine the field type and storage attributes */
4343 type = dbi_result_get_field_type(result, columnName);
4344 attr = dbi_result_get_field_attribs(result, columnName);
4346 /* fetch the fieldmapper index */
4347 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
4349 if ( str_is_true( osrfHashGet(_f, "virtual") ) )
4352 const char* pos = (char*)osrfHashGet(_f, "array_position");
4353 if ( !pos ) continue;
4355 fmIndex = atoi( pos );
4356 osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
4361 if (dbi_result_field_is_null(result, columnName)) {
4362 jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
4367 case DBI_TYPE_INTEGER :
4369 if( attr & DBI_INTEGER_SIZE8 )
4370 jsonObjectSetIndex( object, fmIndex,
4371 jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
4373 jsonObjectSetIndex( object, fmIndex,
4374 jsonNewNumberObject(dbi_result_get_int(result, columnName)));
4378 case DBI_TYPE_DECIMAL :
4379 jsonObjectSetIndex( object, fmIndex,
4380 jsonNewNumberObject(dbi_result_get_double(result, columnName)));
4383 case DBI_TYPE_STRING :
4389 jsonNewObject( dbi_result_get_string(result, columnName) )
4394 case DBI_TYPE_DATETIME :
4396 memset(dt_string, '\0', sizeof(dt_string));
4397 memset(&gmdt, '\0', sizeof(gmdt));
4399 _tmp_dt = dbi_result_get_datetime(result, columnName);
4402 if (!(attr & DBI_DATETIME_DATE)) {
4403 gmtime_r( &_tmp_dt, &gmdt );
4404 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4405 } else if (!(attr & DBI_DATETIME_TIME)) {
4406 localtime_r( &_tmp_dt, &gmdt );
4407 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4409 localtime_r( &_tmp_dt, &gmdt );
4410 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4413 jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
4417 case DBI_TYPE_BINARY :
4418 osrfLogError( OSRF_LOG_MARK,
4419 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4427 static jsonObject* oilsMakeJSONFromResult( dbi_result result ) {
4428 if(!result) return jsonNULL;
4430 jsonObject* object = jsonNewObject(NULL);
4433 char dt_string[256];
4437 int columnIndex = 1;
4439 unsigned short type;
4440 const char* columnName;
4442 /* cycle through the column list */
4443 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4445 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4447 fmIndex = -1; // reset the position
4449 /* determine the field type and storage attributes */
4450 type = dbi_result_get_field_type(result, columnName);
4451 attr = dbi_result_get_field_attribs(result, columnName);
4453 if (dbi_result_field_is_null(result, columnName)) {
4454 jsonObjectSetKey( object, columnName, jsonNewObject(NULL) );
4459 case DBI_TYPE_INTEGER :
4461 if( attr & DBI_INTEGER_SIZE8 )
4462 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_longlong(result, columnName)) );
4464 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_int(result, columnName)) );
4467 case DBI_TYPE_DECIMAL :
4468 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_double(result, columnName)) );
4471 case DBI_TYPE_STRING :
4472 jsonObjectSetKey( object, columnName, jsonNewObject(dbi_result_get_string(result, columnName)) );
4475 case DBI_TYPE_DATETIME :
4477 memset(dt_string, '\0', sizeof(dt_string));
4478 memset(&gmdt, '\0', sizeof(gmdt));
4480 _tmp_dt = dbi_result_get_datetime(result, columnName);
4483 if (!(attr & DBI_DATETIME_DATE)) {
4484 gmtime_r( &_tmp_dt, &gmdt );
4485 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4486 } else if (!(attr & DBI_DATETIME_TIME)) {
4487 localtime_r( &_tmp_dt, &gmdt );
4488 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4490 localtime_r( &_tmp_dt, &gmdt );
4491 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4494 jsonObjectSetKey( object, columnName, jsonNewObject(dt_string) );
4497 case DBI_TYPE_BINARY :
4498 osrfLogError( OSRF_LOG_MARK,
4499 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4507 // Interpret a string as true or false
4508 static int str_is_true( const char* str ) {
4509 if( NULL == str || strcasecmp( str, "true" ) )
4515 // Interpret a jsonObject as true or false
4516 static int obj_is_true( const jsonObject* obj ) {
4519 else switch( obj->type )
4527 if( strcasecmp( obj->value.s, "true" ) )
4531 case JSON_NUMBER : // Support 1/0 for perl's sake
4532 if( jsonObjectGetNumber( obj ) == 1.0 )
4541 // Translate a numeric code into a text string identifying a type of
4542 // jsonObject. To be used for building error messages.
4543 static const char* json_type( int code ) {
4549 return "JSON_ARRAY";
4551 return "JSON_STRING";
4553 return "JSON_NUMBER";
4559 return "(unrecognized)";
4563 // Extract the "primitive" attribute from an IDL field definition.
4564 // If we haven't initialized the app, then we must be running in
4565 // some kind of testbed. In that case, default to "string".
4566 static const char* get_primitive( osrfHash* field ) {
4567 const char* s = osrfHashGet( field, "primitive" );
4569 if( child_initialized )
4572 "%s ERROR No \"datatype\" attribute for field \"%s\"",
4574 osrfHashGet( field, "name" )
4582 // Extract the "datatype" attribute from an IDL field definition.
4583 // If we haven't initialized the app, then we must be running in
4584 // some kind of testbed. In that case, default to to NUMERIC,
4585 // since we look at the datatype only for numbers.
4586 static const char* get_datatype( osrfHash* field ) {
4587 const char* s = osrfHashGet( field, "datatype" );
4589 if( child_initialized )
4592 "%s ERROR No \"datatype\" attribute for field \"%s\"",
4594 osrfHashGet( field, "name" )
4603 If the input string is potentially a valid SQL identifier, return 1.
4606 Purpose: to prevent certain kinds of SQL injection. To that end we
4607 don't necessarily need to follow all the rules exactly, such as requiring
4608 that the first character not be a digit.
4610 We allow leading and trailing white space. In between, we do not allow
4611 punctuation (except for underscores and dollar signs), control
4612 characters, or embedded white space.
4614 More pedantically we should allow quoted identifiers containing arbitrary
4615 characters, but for the foreseeable future such quoted identifiers are not
4616 likely to be an issue.
4618 static int is_identifier( const char* s) {
4622 // Skip leading white space
4623 while( isspace( (unsigned char) *s ) )
4627 return 0; // Nothing but white space? Not okay.
4629 // Check each character until we reach white space or
4630 // end-of-string. Letters, digits, underscores, and
4631 // dollar signs are okay. Control characters and other
4632 // punctuation characters are not okay. Anything else
4633 // is okay -- it could for example be part of a multibyte
4634 // UTF8 character such as a letter with diacritical marks,
4635 // and those are allowed.
4637 if( isalnum( (unsigned char) *s )
4640 ; // Fine; keep going
4641 else if( ispunct( (unsigned char) *s )
4642 || iscntrl( (unsigned char) *s ) )
4645 } while( *s && ! isspace( (unsigned char) *s ) );
4647 // If we found any white space in the above loop,
4648 // the rest had better be all white space.
4650 while( isspace( (unsigned char) *s ) )
4654 return 0; // White space was embedded within non-white space