2 #include "opensrf/osrf_application.h"
3 #include "opensrf/osrf_settings.h"
4 #include "opensrf/osrf_message.h"
5 #include "opensrf/utils.h"
6 #include "opensrf/osrf_json.h"
7 #include "opensrf/log.h"
8 #include "openils/oils_utils.h"
17 # define MODULENAME "open-ils.reporter-store"
20 # define MODULENAME "open-ils.pcrud"
22 # define MODULENAME "open-ils.cstore"
27 #define DISABLE_I18N 2
28 #define SELECT_DISTINCT 1
32 int osrfAppChildInit();
33 int osrfAppInitialize();
34 void osrfAppChildExit();
36 static int verifyObjectClass ( osrfMethodContext*, const jsonObject* );
38 int beginTransaction ( osrfMethodContext* );
39 int commitTransaction ( osrfMethodContext* );
40 int rollbackTransaction ( osrfMethodContext* );
42 int setSavepoint ( osrfMethodContext* );
43 int releaseSavepoint ( osrfMethodContext* );
44 int rollbackSavepoint ( osrfMethodContext* );
46 int doJSONSearch ( osrfMethodContext* );
48 int dispatchCRUDMethod ( osrfMethodContext* );
49 static jsonObject* doCreate ( osrfMethodContext*, int* );
50 static jsonObject* doRetrieve ( osrfMethodContext*, int* );
51 static jsonObject* doUpdate ( osrfMethodContext*, int* );
52 static jsonObject* doDelete ( osrfMethodContext*, int* );
53 static jsonObject* doFieldmapperSearch ( osrfMethodContext*, osrfHash*,
54 const jsonObject*, int* );
55 static jsonObject* oilsMakeFieldmapperFromResult( dbi_result, osrfHash* );
56 static jsonObject* oilsMakeJSONFromResult( dbi_result );
58 static char* searchSimplePredicate ( const char* op, const char* class,
59 osrfHash* field, const jsonObject* node );
60 static char* searchFunctionPredicate ( const char*, osrfHash*, const jsonObject*, const char* );
61 static char* searchFieldTransform ( const char*, osrfHash*, const jsonObject*);
62 static char* searchFieldTransformPredicate ( const char*, osrfHash*, const jsonObject*, const char* );
63 static char* searchBETWEENPredicate ( const char*, osrfHash*, const jsonObject* );
64 static char* searchINPredicate ( const char*, osrfHash*,
65 jsonObject*, const char*, osrfMethodContext* );
66 static char* searchPredicate ( const char*, osrfHash*, jsonObject*, osrfMethodContext* );
67 static char* searchJOIN ( const jsonObject*, osrfHash* );
68 static char* searchWHERE ( const jsonObject*, osrfHash*, int, osrfMethodContext* );
69 static char* buildSELECT ( jsonObject*, jsonObject*, osrfHash*, osrfMethodContext* );
71 char* SELECT ( osrfMethodContext*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, int );
73 void userDataFree( void* );
74 static void sessionDataFree( char*, void* );
75 static char* getSourceDefinition( osrfHash* );
76 static int str_is_true( const char* str );
77 static int obj_is_true( const jsonObject* obj );
78 static const char* json_type( int code );
79 static const char* get_primitive( osrfHash* field );
80 static const char* get_datatype( osrfHash* field );
81 static int is_identifier( const char* s);
84 static jsonObject* verifyUserPCRUD( osrfMethodContext* );
85 static int verifyObjectPCRUD( osrfMethodContext*, const jsonObject* );
88 static int child_initialized = 0; /* boolean */
90 static dbi_conn writehandle; /* our MASTER db connection */
91 static dbi_conn dbhandle; /* our CURRENT db connection */
92 //static osrfHash * readHandles;
93 static jsonObject* jsonNULL = NULL; //
94 static int max_flesh_depth = 100;
96 /* called when this process is about to exit */
97 void osrfAppChildExit() {
98 osrfLogDebug(OSRF_LOG_MARK, "Child is exiting, disconnecting from database...");
101 if (writehandle == dbhandle) same = 1;
103 dbi_conn_query(writehandle, "ROLLBACK;");
104 dbi_conn_close(writehandle);
107 if (dbhandle && !same)
108 dbi_conn_close(dbhandle);
110 // XXX add cleanup of readHandles whenever that gets used
115 int osrfAppInitialize() {
117 osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
118 osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
120 if (!oilsIDLInit( osrf_settings_host_value("/IDL") )) return 1; /* return non-zero to indicate error */
122 growing_buffer* method_name = buffer_init(64);
124 // Generic search thingy
125 buffer_add(method_name, MODULENAME);
126 buffer_add(method_name, ".json_query");
127 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
128 "doJSONSearch", "", 1, OSRF_METHOD_STREAMING );
131 // first we register all the transaction and savepoint methods
132 buffer_reset(method_name);
133 OSRF_BUFFER_ADD(method_name, MODULENAME);
134 OSRF_BUFFER_ADD(method_name, ".transaction.begin");
135 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
136 "beginTransaction", "", 0, 0 );
138 buffer_reset(method_name);
139 OSRF_BUFFER_ADD(method_name, MODULENAME);
140 OSRF_BUFFER_ADD(method_name, ".transaction.commit");
141 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
142 "commitTransaction", "", 0, 0 );
144 buffer_reset(method_name);
145 OSRF_BUFFER_ADD(method_name, MODULENAME);
146 OSRF_BUFFER_ADD(method_name, ".transaction.rollback");
147 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
148 "rollbackTransaction", "", 0, 0 );
150 buffer_reset(method_name);
151 OSRF_BUFFER_ADD(method_name, MODULENAME);
152 OSRF_BUFFER_ADD(method_name, ".savepoint.set");
153 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
154 "setSavepoint", "", 1, 0 );
156 buffer_reset(method_name);
157 OSRF_BUFFER_ADD(method_name, MODULENAME);
158 OSRF_BUFFER_ADD(method_name, ".savepoint.release");
159 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
160 "releaseSavepoint", "", 1, 0 );
162 buffer_reset(method_name);
163 OSRF_BUFFER_ADD(method_name, MODULENAME);
164 OSRF_BUFFER_ADD(method_name, ".savepoint.rollback");
165 osrfAppRegisterMethod( MODULENAME, OSRF_BUFFER_C_STR(method_name),
166 "rollbackSavepoint", "", 1, 0 );
168 buffer_free(method_name);
170 static const char* global_method[] = {
178 const int global_method_count
179 = sizeof( global_method ) / sizeof ( global_method[0] );
183 osrfStringArray* classes = osrfHashKeys( oilsIDL() );
184 osrfLogDebug(OSRF_LOG_MARK, "%d classes loaded", classes->size );
185 osrfLogDebug(OSRF_LOG_MARK,
186 "At least %d methods will be generated", classes->size * global_method_count);
188 while ( (classname = osrfStringArrayGetString(classes, c_index++)) ) {
189 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
191 osrfHash* idlClass = osrfHashGet(oilsIDL(), classname);
193 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), MODULENAME )) {
194 osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on", MODULENAME, classname);
198 if ( str_is_true( osrfHashGet(idlClass, "virtual") ) ) {
199 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
203 // Look up some other attributes of the current class
204 const char* idlClass_fieldmapper = osrfHashGet(idlClass, "fieldmapper");
205 const char* readonly = osrfHashGet(idlClass, "readonly");
207 osrfHash* idlClass_permacrud = osrfHashGet(idlClass, "permacrud");
211 for( i = 0; i < global_method_count; ++i ) {
212 const char* method_type = global_method[ i ];
213 osrfLogDebug(OSRF_LOG_MARK,
214 "Using files to build %s class methods for %s", method_type, classname);
216 if (!idlClass_fieldmapper) continue;
219 if (!idlClass_permacrud) continue;
221 const char* tmp_method = method_type;
222 if ( *tmp_method == 'i' || *tmp_method == 's') {
223 tmp_method = "retrieve";
225 if (!osrfHashGet( idlClass_permacrud, tmp_method )) continue;
228 if ( str_is_true( readonly ) &&
229 ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd')
232 osrfHash* method_meta = osrfNewHash();
233 osrfHashSet(method_meta, idlClass, "class");
235 method_name = buffer_init(64);
237 buffer_fadd(method_name, "%s.%s.%s", MODULENAME, method_type, classname);
241 char* _fm = strdup( idlClass_fieldmapper );
242 part = strtok_r(_fm, ":", &st_tmp);
244 buffer_fadd(method_name, "%s.direct.%s", MODULENAME, part);
246 while ((part = strtok_r(NULL, ":", &st_tmp))) {
247 OSRF_BUFFER_ADD_CHAR(method_name, '.');
248 OSRF_BUFFER_ADD(method_name, part);
250 OSRF_BUFFER_ADD_CHAR(method_name, '.');
251 OSRF_BUFFER_ADD(method_name, method_type);
255 char* method = buffer_release(method_name);
257 osrfHashSet( method_meta, method, "methodname" );
258 osrfHashSet( method_meta, strdup(method_type), "methodtype" );
261 if (*method_type == 'i' || *method_type == 's') {
262 flags = flags | OSRF_METHOD_STREAMING;
265 osrfAppRegisterExtendedMethod(
268 "dispatchCRUDMethod",
282 static char* getSourceDefinition( osrfHash* class ) {
284 char* tabledef = osrfHashGet(class, "tablename");
287 tabledef = strdup(tabledef);
289 tabledef = osrfHashGet(class, "source_definition");
291 growing_buffer* tablebuf = buffer_init(128);
292 buffer_fadd( tablebuf, "(%s)", tabledef );
293 tabledef = buffer_release(tablebuf);
295 const char* classname = osrfHashGet( class, "classname" );
300 "%s ERROR No tablename or source_definition for class \"%s\"",
311 * Connects to the database
313 int osrfAppChildInit() {
315 osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
316 dbi_initialize(NULL);
317 osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
319 char* driver = osrf_settings_host_value("/apps/%s/app_settings/driver", MODULENAME);
320 char* user = osrf_settings_host_value("/apps/%s/app_settings/database/user", MODULENAME);
321 char* host = osrf_settings_host_value("/apps/%s/app_settings/database/host", MODULENAME);
322 char* port = osrf_settings_host_value("/apps/%s/app_settings/database/port", MODULENAME);
323 char* db = osrf_settings_host_value("/apps/%s/app_settings/database/db", MODULENAME);
324 char* pw = osrf_settings_host_value("/apps/%s/app_settings/database/pw", MODULENAME);
325 char* md = osrf_settings_host_value("/apps/%s/app_settings/max_query_recursion", MODULENAME);
327 osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
328 writehandle = dbi_conn_new(driver);
331 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
334 osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
336 osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database. host=%s, "
337 "port=%s, user=%s, pw=%s, db=%s", MODULENAME, host, port, user, pw, db );
339 if(host) dbi_conn_set_option(writehandle, "host", host );
340 if(port) dbi_conn_set_option_numeric( writehandle, "port", atoi(port) );
341 if(user) dbi_conn_set_option(writehandle, "username", user);
342 if(pw) dbi_conn_set_option(writehandle, "password", pw );
343 if(db) dbi_conn_set_option(writehandle, "dbname", db );
345 if(md) max_flesh_depth = atoi(md);
346 if(max_flesh_depth < 0) max_flesh_depth = 1;
347 if(max_flesh_depth > 1000) max_flesh_depth = 1000;
356 if (dbi_conn_connect(writehandle) < 0) {
358 if (dbi_conn_connect(writehandle) < 0) {
359 dbi_conn_error(writehandle, &err);
360 osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
365 osrfLogInfo(OSRF_LOG_MARK, "%s successfully connected to the database", MODULENAME);
371 osrfStringArray* classes = osrfHashKeys( oilsIDL() );
373 while ( (classname = osrfStringArrayGetString(classes, i++)) ) {
374 osrfHash* class = osrfHashGet( oilsIDL(), classname );
375 osrfHash* fields = osrfHashGet( class, "fields" );
377 if( str_is_true( osrfHashGet(class, "virtual") ) ) {
378 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
382 char* tabledef = getSourceDefinition(class);
384 tabledef = strdup( "(null)" );
386 growing_buffer* sql_buf = buffer_init(32);
387 buffer_fadd( sql_buf, "SELECT * FROM %s AS x WHERE 1=0;", tabledef );
391 char* sql = buffer_release(sql_buf);
392 osrfLogDebug(OSRF_LOG_MARK, "%s Investigatory SQL = %s", MODULENAME, sql);
394 dbi_result result = dbi_conn_query(writehandle, sql);
400 const char* columnName;
402 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
404 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
406 /* fetch the fieldmapper index */
407 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
409 osrfLogDebug(OSRF_LOG_MARK, "Found [%s] in IDL hash...", (char*)columnName);
411 /* determine the field type and storage attributes */
412 type = dbi_result_get_field_type(result, columnName);
413 attr = dbi_result_get_field_attribs(result, columnName);
417 case DBI_TYPE_INTEGER :
419 if ( !osrfHashGet(_f, "primitive") )
420 osrfHashSet(_f,"number", "primitive");
422 if( attr & DBI_INTEGER_SIZE8 )
423 osrfHashSet(_f,"INT8", "datatype");
425 osrfHashSet(_f,"INT", "datatype");
428 case DBI_TYPE_DECIMAL :
429 if ( !osrfHashGet(_f, "primitive") )
430 osrfHashSet(_f,"number", "primitive");
432 osrfHashSet(_f,"NUMERIC", "datatype");
435 case DBI_TYPE_STRING :
436 if ( !osrfHashGet(_f, "primitive") )
437 osrfHashSet(_f,"string", "primitive");
438 osrfHashSet(_f,"TEXT", "datatype");
441 case DBI_TYPE_DATETIME :
442 if ( !osrfHashGet(_f, "primitive") )
443 osrfHashSet(_f,"string", "primitive");
445 osrfHashSet(_f,"TIMESTAMP", "datatype");
448 case DBI_TYPE_BINARY :
449 if ( !osrfHashGet(_f, "primitive") )
450 osrfHashSet(_f,"string", "primitive");
452 osrfHashSet(_f,"BYTEA", "datatype");
457 "Setting [%s] to primitive [%s] and datatype [%s]...",
459 osrfHashGet(_f, "primitive"),
460 osrfHashGet(_f, "datatype")
464 dbi_result_free(result);
466 osrfLogDebug(OSRF_LOG_MARK, "No data found for class [%s]...", (char*)classname);
470 osrfStringArrayFree(classes);
472 child_initialized = 1;
477 This function is a sleazy hack intended *only* for testing and
478 debugging. Any real server process should initialize the
479 database connection by calling osrfAppChildInit().
481 void set_cstore_dbi_conn( dbi_conn conn ) {
482 dbhandle = writehandle = conn;
485 void userDataFree( void* blob ) {
486 osrfHashFree( (osrfHash*)blob );
490 static void sessionDataFree( char* key, void* item ) {
491 if (!(strcmp(key,"xact_id"))) {
493 dbi_conn_query(writehandle, "ROLLBACK;");
500 int beginTransaction ( osrfMethodContext* ctx ) {
501 if(osrfMethodVerifyContext( ctx )) {
502 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
507 jsonObject* user = verifyUserPCRUD( ctx );
508 if (!user) return -1;
509 jsonObjectFree(user);
512 dbi_result result = dbi_conn_query(writehandle, "START TRANSACTION;");
514 osrfLogError(OSRF_LOG_MARK, "%s: Error starting transaction", MODULENAME );
515 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error starting transaction" );
518 jsonObject* ret = jsonNewObject(ctx->session->session_id);
519 osrfAppRespondComplete( ctx, ret );
522 if (!ctx->session->userData) {
523 ctx->session->userData = osrfNewHash();
524 osrfHashSetCallback((osrfHash*)ctx->session->userData, &sessionDataFree);
527 osrfHashSet( (osrfHash*)ctx->session->userData, strdup( ctx->session->session_id ), "xact_id" );
528 ctx->session->userDataFree = &userDataFree;
534 int setSavepoint ( osrfMethodContext* ctx ) {
535 if(osrfMethodVerifyContext( ctx )) {
536 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
543 jsonObject* user = verifyUserPCRUD( ctx );
544 if (!user) return -1;
545 jsonObjectFree(user);
548 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
549 osrfAppSessionStatus(
551 OSRF_STATUS_INTERNALSERVERERROR,
552 "osrfMethodException",
554 "No active transaction -- required for savepoints"
559 const char* spName = jsonObjectGetString(jsonObjectGetIndex(ctx->params, spNamePos));
561 dbi_result result = dbi_conn_queryf(writehandle, "SAVEPOINT \"%s\";", spName);
565 "%s: Error creating savepoint %s in transaction %s",
568 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
570 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR,
571 "osrfMethodException", ctx->request, "Error creating savepoint" );
574 jsonObject* ret = jsonNewObject(spName);
575 osrfAppRespondComplete( ctx, ret );
581 int releaseSavepoint ( osrfMethodContext* ctx ) {
582 if(osrfMethodVerifyContext( ctx )) {
583 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
590 jsonObject* user = verifyUserPCRUD( ctx );
591 if (!user) return -1;
592 jsonObjectFree(user);
595 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
596 osrfAppSessionStatus(
598 OSRF_STATUS_INTERNALSERVERERROR,
599 "osrfMethodException",
601 "No active transaction -- required for savepoints"
606 const char* spName = jsonObjectGetString( jsonObjectGetIndex(ctx->params, spNamePos) );
608 dbi_result result = dbi_conn_queryf(writehandle, "RELEASE SAVEPOINT \"%s\";", spName);
612 "%s: Error releasing savepoint %s in transaction %s",
615 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
617 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR,
618 "osrfMethodException", ctx->request, "Error releasing savepoint" );
621 jsonObject* ret = jsonNewObject(spName);
622 osrfAppRespondComplete( ctx, ret );
628 int rollbackSavepoint ( osrfMethodContext* ctx ) {
629 if(osrfMethodVerifyContext( ctx )) {
630 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
637 jsonObject* user = verifyUserPCRUD( ctx );
638 if (!user) return -1;
639 jsonObjectFree(user);
642 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
643 osrfAppSessionStatus(
645 OSRF_STATUS_INTERNALSERVERERROR,
646 "osrfMethodException",
648 "No active transaction -- required for savepoints"
653 const char* spName = jsonObjectGetString( jsonObjectGetIndex(ctx->params, spNamePos) );
655 dbi_result result = dbi_conn_queryf(writehandle, "ROLLBACK TO SAVEPOINT \"%s\";", spName);
659 "%s: Error rolling back savepoint %s in transaction %s",
662 osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )
664 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR,
665 "osrfMethodException", ctx->request, "Error rolling back savepoint" );
668 jsonObject* ret = jsonNewObject(spName);
669 osrfAppRespondComplete( ctx, ret );
675 int commitTransaction ( osrfMethodContext* ctx ) {
676 if(osrfMethodVerifyContext( ctx )) {
677 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
682 jsonObject* user = verifyUserPCRUD( ctx );
683 if (!user) return -1;
684 jsonObjectFree(user);
687 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
688 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to commit" );
692 dbi_result result = dbi_conn_query(writehandle, "COMMIT;");
694 osrfLogError(OSRF_LOG_MARK, "%s: Error committing transaction", MODULENAME );
695 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error committing transaction" );
698 osrfHashRemove(ctx->session->userData, "xact_id");
699 jsonObject* ret = jsonNewObject(ctx->session->session_id);
700 osrfAppRespondComplete( ctx, ret );
706 int rollbackTransaction ( osrfMethodContext* ctx ) {
707 if(osrfMethodVerifyContext( ctx )) {
708 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
713 jsonObject* user = verifyUserPCRUD( ctx );
714 if (!user) return -1;
715 jsonObjectFree(user);
718 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
719 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "No active transaction to roll back" );
723 dbi_result result = dbi_conn_query(writehandle, "ROLLBACK;");
725 osrfLogError(OSRF_LOG_MARK, "%s: Error rolling back transaction", MODULENAME );
726 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, "Error rolling back transaction" );
729 osrfHashRemove(ctx->session->userData, "xact_id");
730 jsonObject* ret = jsonNewObject(ctx->session->session_id);
731 osrfAppRespondComplete( ctx, ret );
737 int dispatchCRUDMethod ( osrfMethodContext* ctx ) {
738 if(osrfMethodVerifyContext( ctx )) {
739 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
743 osrfHash* meta = (osrfHash*) ctx->method->userData;
744 osrfHash* class_obj = osrfHashGet( meta, "class" );
748 const char* methodtype = osrfHashGet(meta, "methodtype");
749 jsonObject * obj = NULL;
751 if (!strcmp(methodtype, "create")) {
752 obj = doCreate(ctx, &err);
753 osrfAppRespondComplete( ctx, obj );
755 else if (!strcmp(methodtype, "retrieve")) {
756 obj = doRetrieve(ctx, &err);
757 osrfAppRespondComplete( ctx, obj );
759 else if (!strcmp(methodtype, "update")) {
760 obj = doUpdate(ctx, &err);
761 osrfAppRespondComplete( ctx, obj );
763 else if (!strcmp(methodtype, "delete")) {
764 obj = doDelete(ctx, &err);
765 osrfAppRespondComplete( ctx, obj );
767 else if (!strcmp(methodtype, "search")) {
769 jsonObject* _p = jsonObjectClone( ctx->params );
772 _p = jsonParseString("[]");
773 jsonObjectPush(_p, jsonObjectClone(jsonObjectGetIndex(ctx->params, 1)));
774 jsonObjectPush(_p, jsonObjectClone(jsonObjectGetIndex(ctx->params, 2)));
777 obj = doFieldmapperSearch(ctx, class_obj, _p, &err);
783 jsonIterator* itr = jsonNewIterator( obj );
784 while ((cur = jsonIteratorNext( itr ))) {
786 if(!verifyObjectPCRUD(ctx, cur)) continue;
788 osrfAppRespond( ctx, cur );
790 jsonIteratorFree(itr);
791 osrfAppRespondComplete( ctx, NULL );
793 } else if (!strcmp(methodtype, "id_list")) {
795 jsonObject* _p = jsonObjectClone( ctx->params );
798 _p = jsonParseString("[]");
799 jsonObjectPush(_p, jsonObjectClone(jsonObjectGetIndex(ctx->params, 1)));
800 jsonObjectPush(_p, jsonObjectClone(jsonObjectGetIndex(ctx->params, 2)));
803 if (jsonObjectGetIndex( _p, 1 )) {
804 jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "select" );
805 jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "no_i18n" );
806 jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "flesh" );
807 jsonObjectRemoveKey( jsonObjectGetIndex( _p, 1 ), "flesh_columns" );
809 jsonObjectSetIndex( _p, 1, jsonNewObjectType(JSON_HASH) );
812 jsonObjectSetKey( jsonObjectGetIndex( _p, 1 ), "no_i18n", jsonNewBoolObject( 1 ) );
815 jsonObjectGetIndex( _p, 1 ),
818 "{ \"%s\":[\"%s\"] }",
819 osrfHashGet( class_obj, "classname" ),
820 osrfHashGet( class_obj, "primarykey" )
824 obj = doFieldmapperSearch(ctx, class_obj, _p, &err);
830 jsonIterator* itr = jsonNewIterator( obj );
831 while ((cur = jsonIteratorNext( itr ))) {
833 if(!verifyObjectPCRUD(ctx, cur)) continue;
837 oilsFMGetObject( cur, osrfHashGet( class_obj, "primarykey" ) )
840 jsonIteratorFree(itr);
841 osrfAppRespondComplete( ctx, NULL );
844 osrfAppRespondComplete( ctx, obj );
852 static int verifyObjectClass ( osrfMethodContext* ctx, const jsonObject* param ) {
855 osrfHash* meta = (osrfHash*) ctx->method->userData;
856 osrfHash* class = osrfHashGet( meta, "class" );
858 if (!param->classname || (strcmp( osrfHashGet(class, "classname"), param->classname ))) {
860 growing_buffer* msg = buffer_init(128);
863 "%s: %s method for type %s was passed a %s",
865 osrfHashGet(meta, "methodtype"),
866 osrfHashGet(class, "classname"),
870 char* m = buffer_release(msg);
871 osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException", ctx->request, m );
879 ret = verifyObjectPCRUD( ctx, param );
887 static jsonObject* verifyUserPCRUD( osrfMethodContext* ctx ) {
888 const char* auth = jsonObjectGetString( jsonObjectGetIndex( ctx->params, 0 ) );
889 jsonObject* auth_object = jsonNewObject(auth);
890 jsonObject* user = oilsUtilsQuickReq("open-ils.auth","open-ils.auth.session.retrieve", auth_object);
891 jsonObjectFree(auth_object);
893 if (!user->classname || strcmp(user->classname, "au")) {
895 growing_buffer* msg = buffer_init(128);
898 "%s: permacrud received a bad auth token: %s",
903 char* m = buffer_release(msg);
904 osrfAppSessionStatus( ctx->session, OSRF_STATUS_UNAUTHORIZED, "osrfMethodException", ctx->request, m );
907 jsonObjectFree(user);
915 static int verifyObjectPCRUD ( osrfMethodContext* ctx, const jsonObject* obj ) {
917 dbhandle = writehandle;
919 osrfHash* meta = (osrfHash*) ctx->method->userData;
920 osrfHash* class = osrfHashGet( meta, "class" );
921 char* method_type = strdup( osrfHashGet(meta, "methodtype") );
924 if ( ( *method_type == 's' || *method_type == 'i' ) ) {
926 method_type = strdup("retrieve"); // search and id_list are equivelant to retrieve for this
927 } else if ( *method_type == 'u' || *method_type == 'd' ) {
928 fetch = 1; // MUST go to the db for the object for update and delete
931 osrfHash* pcrud = osrfHashGet( osrfHashGet(class, "permacrud"), method_type );
935 // No permacrud for this method type on this class
937 growing_buffer* msg = buffer_init(128);
940 "%s: %s on class %s has no permacrud IDL entry",
942 osrfHashGet(meta, "methodtype"),
943 osrfHashGet(class, "classname")
946 char* m = buffer_release(msg);
947 osrfAppSessionStatus( ctx->session, OSRF_STATUS_FORBIDDEN, "osrfMethodException", ctx->request, m );
954 jsonObject* user = verifyUserPCRUD( ctx );
957 int userid = atoi( oilsFMGetString( user, "id" ) );
958 jsonObjectFree(user);
960 osrfStringArray* permission = osrfHashGet(pcrud, "permission");
961 osrfStringArray* local_context = osrfHashGet(pcrud, "local_context");
962 osrfHash* foreign_context = osrfHashGet(pcrud, "foreign_context");
964 osrfStringArray* context_org_array = osrfNewStringArray(1);
967 char* pkey_value = NULL;
968 if ( str_is_true( osrfHashGet(pcrud, "global_required") ) ) {
969 osrfLogDebug( OSRF_LOG_MARK, "global-level permissions required, fetching top of the org tree" );
971 // check for perm at top of org tree
972 jsonObject* _tmp_params = jsonParseString("[{\"parent_ou\":null}]");
973 jsonObject* _list = doFieldmapperSearch(ctx, osrfHashGet( oilsIDL(), "aou" ), _tmp_params, &err);
975 jsonObject* _tree_top = jsonObjectGetIndex(_list, 0);
978 jsonObjectFree(_tmp_params);
979 jsonObjectFree(_list);
981 growing_buffer* msg = buffer_init(128);
982 OSRF_BUFFER_ADD( msg, MODULENAME );
983 OSRF_BUFFER_ADD( msg,
984 ": Internal error, could not find the top of the org tree (parent_ou = NULL)" );
986 char* m = buffer_release(msg);
987 osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, "osrfMethodException", ctx->request, m );
993 osrfStringArrayAdd( context_org_array, oilsFMGetString( _tree_top, "id" ) );
994 osrfLogDebug( OSRF_LOG_MARK, "top of the org tree is %s", osrfStringArrayGetString(context_org_array, 0) );
996 jsonObjectFree(_tmp_params);
997 jsonObjectFree(_list);
1000 osrfLogDebug( OSRF_LOG_MARK, "global-level permissions not required, fetching context org ids" );
1001 char* pkey = osrfHashGet(class, "primarykey");
1002 jsonObject *param = NULL;
1004 if (obj->classname) {
1005 pkey_value = oilsFMGetString( obj, pkey );
1006 if (!fetch) param = jsonObjectClone(obj);
1007 osrfLogDebug( OSRF_LOG_MARK, "Object supplied, using primary key value of %s", pkey_value );
1009 pkey_value = jsonObjectToSimpleString( obj );
1011 osrfLogDebug( OSRF_LOG_MARK, "Object not supplied, using primary key value of %s and retrieving from the database", pkey_value );
1015 jsonObject* _tmp_params = jsonParseStringFmt("[{\"%s\":\"%s\"}]", pkey, pkey_value);
1016 jsonObject* _list = doFieldmapperSearch(
1023 param = jsonObjectClone(jsonObjectGetIndex(_list, 0));
1025 jsonObjectFree(_tmp_params);
1026 jsonObjectFree(_list);
1030 osrfLogDebug( OSRF_LOG_MARK, "Object not found in the database with primary key %s of %s", pkey, pkey_value );
1032 growing_buffer* msg = buffer_init(128);
1035 "%s: no object found with primary key %s of %s",
1041 char* m = buffer_release(msg);
1042 osrfAppSessionStatus(
1044 OSRF_STATUS_INTERNALSERVERERROR,
1045 "osrfMethodException",
1051 if (pkey_value) free(pkey_value);
1056 if (local_context->size > 0) {
1057 osrfLogDebug( OSRF_LOG_MARK, "%d class-local context field(s) specified", local_context->size);
1059 char* lcontext = NULL;
1060 while ( (lcontext = osrfStringArrayGetString(local_context, i++)) ) {
1061 osrfStringArrayAdd( context_org_array, oilsFMGetString( param, lcontext ) );
1064 "adding class-local field %s (value: %s) to the context org list",
1066 osrfStringArrayGetString(context_org_array, context_org_array->size - 1)
1071 osrfStringArray* class_list;
1073 if (foreign_context) {
1074 class_list = osrfHashKeys( foreign_context );
1075 osrfLogDebug( OSRF_LOG_MARK, "%d foreign context classes(s) specified", class_list->size);
1077 if (class_list->size > 0) {
1080 char* class_name = NULL;
1081 while ( (class_name = osrfStringArrayGetString(class_list, i++)) ) {
1082 osrfHash* fcontext = osrfHashGet(foreign_context, class_name);
1086 "%d foreign context fields(s) specified for class %s",
1087 ((osrfStringArray*)osrfHashGet(fcontext,"context"))->size,
1091 char* foreign_pkey = osrfHashGet(fcontext, "field");
1092 char* foreign_pkey_value = oilsFMGetString(param, osrfHashGet(fcontext, "fkey"));
1094 jsonObject* _tmp_params = jsonParseStringFmt(
1095 "[{\"%s\":\"%s\"}]",
1100 jsonObject* _list = doFieldmapperSearch(
1102 osrfHashGet( oilsIDL(), class_name ),
1107 jsonObject* _fparam = jsonObjectClone(jsonObjectGetIndex(_list, 0));
1108 jsonObjectFree(_tmp_params);
1109 jsonObjectFree(_list);
1111 osrfStringArray* jump_list = osrfHashGet(fcontext, "jump");
1113 if (_fparam && jump_list) {
1116 while ( (flink = osrfStringArrayGetString(jump_list, k++)) && _fparam ) {
1117 free(foreign_pkey_value);
1119 osrfHash* foreign_link_hash = oilsIDLFindPath( "/%s/links/%s", _fparam->classname, flink );
1121 foreign_pkey_value = oilsFMGetString(_fparam, flink);
1122 foreign_pkey = osrfHashGet( foreign_link_hash, "key" );
1124 _tmp_params = jsonParseStringFmt(
1125 "[{\"%s\":\"%s\"}]",
1130 _list = doFieldmapperSearch(
1132 osrfHashGet( oilsIDL(), osrfHashGet( foreign_link_hash, "class" ) ),
1137 _fparam = jsonObjectClone(jsonObjectGetIndex(_list, 0));
1138 jsonObjectFree(_tmp_params);
1139 jsonObjectFree(_list);
1146 growing_buffer* msg = buffer_init(128);
1149 "%s: no object found with primary key %s of %s",
1155 char* m = buffer_release(msg);
1156 osrfAppSessionStatus(
1158 OSRF_STATUS_INTERNALSERVERERROR,
1159 "osrfMethodException",
1165 osrfStringArrayFree(class_list);
1166 free(foreign_pkey_value);
1167 jsonObjectFree(param);
1172 free(foreign_pkey_value);
1175 char* foreign_field = NULL;
1176 while ( (foreign_field = osrfStringArrayGetString(osrfHashGet(fcontext,"context"), j++)) ) {
1177 osrfStringArrayAdd( context_org_array, oilsFMGetString( _fparam, foreign_field ) );
1180 "adding foreign class %s field %s (value: %s) to the context org list",
1183 osrfStringArrayGetString(context_org_array, context_org_array->size - 1)
1187 jsonObjectFree(_fparam);
1190 osrfStringArrayFree(class_list);
1194 jsonObjectFree(param);
1197 char* context_org = NULL;
1201 if (permission->size == 0) {
1202 osrfLogDebug( OSRF_LOG_MARK, "No permission specified for this action, passing through" );
1207 while ( (perm = osrfStringArrayGetString(permission, i++)) ) {
1209 while ( (context_org = osrfStringArrayGetString(context_org_array, j++)) ) {
1215 "Checking object permission [%s] for user %d on object %s (class %s) at org %d",
1219 osrfHashGet(class, "classname"),
1223 result = dbi_conn_queryf(
1225 "SELECT permission.usr_has_object_perm(%d, '%s', '%s', '%s', %d) AS has_perm;",
1228 osrfHashGet(class, "classname"),
1236 "Recieved a result for object permission [%s] for user %d on object %s (class %s) at org %d",
1240 osrfHashGet(class, "classname"),
1244 if (dbi_result_first_row(result)) {
1245 jsonObject* return_val = oilsMakeJSONFromResult( result );
1246 const char* has_perm = jsonObjectGetString( jsonObjectGetKeyConst(return_val, "has_perm") );
1250 "Status of object permission [%s] for user %d on object %s (class %s) at org %d is %s",
1254 osrfHashGet(class, "classname"),
1259 if ( *has_perm == 't' ) OK = 1;
1260 jsonObjectFree(return_val);
1263 dbi_result_free(result);
1268 osrfLogDebug( OSRF_LOG_MARK, "Checking non-object permission [%s] for user %d at org %d", perm, userid, atoi(context_org) );
1269 result = dbi_conn_queryf(
1271 "SELECT permission.usr_has_perm(%d, '%s', %d) AS has_perm;",
1278 osrfLogDebug( OSRF_LOG_MARK, "Received a result for permission [%s] for user %d at org %d",
1279 perm, userid, atoi(context_org) );
1280 if ( dbi_result_first_row(result) ) {
1281 jsonObject* return_val = oilsMakeJSONFromResult( result );
1282 const char* has_perm = jsonObjectGetString( jsonObjectGetKeyConst(return_val, "has_perm") );
1283 osrfLogDebug( OSRF_LOG_MARK, "Status of permission [%s] for user %d at org %d is [%s]",
1284 perm, userid, atoi(context_org), has_perm );
1285 if ( *has_perm == 't' ) OK = 1;
1286 jsonObjectFree(return_val);
1289 dbi_result_free(result);
1297 if (pkey_value) free(pkey_value);
1298 osrfStringArrayFree(context_org_array);
1305 static jsonObject* doCreate(osrfMethodContext* ctx, int* err ) {
1307 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
1309 jsonObject* target = jsonObjectGetIndex( ctx->params, 1 );
1310 jsonObject* options = jsonObjectGetIndex( ctx->params, 2 );
1312 jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
1313 jsonObject* options = jsonObjectGetIndex( ctx->params, 1 );
1316 if (!verifyObjectClass(ctx, target)) {
1321 osrfLogDebug( OSRF_LOG_MARK, "Object seems to be of the correct type" );
1323 char* trans_id = NULL;
1324 if( ctx->session && ctx->session->userData )
1325 trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
1328 osrfLogError( OSRF_LOG_MARK, "No active transaction -- required for CREATE" );
1330 osrfAppSessionStatus(
1332 OSRF_STATUS_BADREQUEST,
1333 "osrfMethodException",
1335 "No active transaction -- required for CREATE"
1341 // The following test is harmless but redundant. If a class is
1342 // readonly, we don't register a create method for it.
1343 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
1344 osrfAppSessionStatus(
1346 OSRF_STATUS_BADREQUEST,
1347 "osrfMethodException",
1349 "Cannot INSERT readonly class"
1355 // Set the last_xact_id
1356 int index = oilsIDL_ntop( target->classname, "last_xact_id" );
1358 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
1359 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
1362 osrfLogDebug( OSRF_LOG_MARK, "There is a transaction running..." );
1364 dbhandle = writehandle;
1366 osrfHash* fields = osrfHashGet(meta, "fields");
1367 char* pkey = osrfHashGet(meta, "primarykey");
1368 char* seq = osrfHashGet(meta, "sequence");
1370 growing_buffer* table_buf = buffer_init(128);
1371 growing_buffer* col_buf = buffer_init(128);
1372 growing_buffer* val_buf = buffer_init(128);
1374 OSRF_BUFFER_ADD(table_buf, "INSERT INTO ");
1375 OSRF_BUFFER_ADD(table_buf, osrfHashGet(meta, "tablename"));
1376 OSRF_BUFFER_ADD_CHAR( col_buf, '(' );
1377 buffer_add(val_buf,"VALUES (");
1383 osrfStringArray* field_list = osrfHashKeys( fields );
1384 while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
1386 osrfHash* field = osrfHashGet( fields, field_name );
1388 if( str_is_true( osrfHashGet( field, "virtual" ) ) )
1391 const jsonObject* field_object = oilsFMGetObject( target, field_name );
1394 if (field_object && field_object->classname) {
1395 value = oilsFMGetString(
1397 (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
1400 value = jsonObjectToSimpleString( field_object );
1407 OSRF_BUFFER_ADD_CHAR( col_buf, ',' );
1408 OSRF_BUFFER_ADD_CHAR( val_buf, ',' );
1411 buffer_add(col_buf, field_name);
1413 if (!field_object || field_object->type == JSON_NULL) {
1414 buffer_add( val_buf, "DEFAULT" );
1416 } else if ( !strcmp(get_primitive( field ), "number") ) {
1417 const char* numtype = get_datatype( field );
1418 if ( !strcmp( numtype, "INT8") ) {
1419 buffer_fadd( val_buf, "%lld", atoll(value) );
1421 } else if ( !strcmp( numtype, "INT") ) {
1422 buffer_fadd( val_buf, "%d", atoi(value) );
1424 } else if ( !strcmp( numtype, "NUMERIC") ) {
1425 buffer_fadd( val_buf, "%f", atof(value) );
1428 if ( dbi_conn_quote_string(writehandle, &value) ) {
1429 OSRF_BUFFER_ADD( val_buf, value );
1432 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
1433 osrfAppSessionStatus(
1435 OSRF_STATUS_INTERNALSERVERERROR,
1436 "osrfMethodException",
1438 "Error quoting string -- please see the error log for more details"
1441 buffer_free(table_buf);
1442 buffer_free(col_buf);
1443 buffer_free(val_buf);
1454 OSRF_BUFFER_ADD_CHAR( col_buf, ')' );
1455 OSRF_BUFFER_ADD_CHAR( val_buf, ')' );
1457 char* table_str = buffer_release(table_buf);
1458 char* col_str = buffer_release(col_buf);
1459 char* val_str = buffer_release(val_buf);
1460 growing_buffer* sql = buffer_init(128);
1461 buffer_fadd( sql, "%s %s %s;", table_str, col_str, val_str );
1466 char* query = buffer_release(sql);
1468 osrfLogDebug(OSRF_LOG_MARK, "%s: Insert SQL [%s]", MODULENAME, query);
1471 dbi_result result = dbi_conn_query(writehandle, query);
1473 jsonObject* obj = NULL;
1476 obj = jsonNewObject(NULL);
1479 "%s ERROR inserting %s object using query [%s]",
1481 osrfHashGet(meta, "fieldmapper"),
1484 osrfAppSessionStatus(
1486 OSRF_STATUS_INTERNALSERVERERROR,
1487 "osrfMethodException",
1489 "INSERT error -- please see the error log for more details"
1494 char* id = oilsFMGetString(target, pkey);
1496 unsigned long long new_id = dbi_conn_sequence_last(writehandle, seq);
1497 growing_buffer* _id = buffer_init(10);
1498 buffer_fadd(_id, "%lld", new_id);
1499 id = buffer_release(_id);
1502 // Find quietness specification, if present
1503 const char* quiet_str = NULL;
1505 const jsonObject* quiet_obj = jsonObjectGetKeyConst( options, "quiet" );
1507 quiet_str = jsonObjectGetString( quiet_obj );
1510 if( str_is_true( quiet_str ) ) { // if quietness is specified
1511 obj = jsonNewObject(id);
1515 jsonObject* fake_params = jsonNewObjectType(JSON_ARRAY);
1516 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH));
1519 jsonObjectGetIndex(fake_params, 0),
1524 jsonObject* list = doFieldmapperSearch( ctx,meta, fake_params, err);
1527 jsonObjectFree( fake_params );
1530 obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
1533 jsonObjectFree( list );
1534 jsonObjectFree( fake_params );
1547 static jsonObject* doRetrieve(osrfMethodContext* ctx, int* err ) {
1557 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
1559 const char* id = jsonObjectGetString(jsonObjectGetIndex(ctx->params, id_pos));
1560 jsonObject* order_hash = jsonObjectGetIndex(ctx->params, order_pos);
1564 "%s retrieving %s object with primary key value of %s",
1566 osrfHashGet(meta, "fieldmapper"),
1570 jsonObject* fake_params = jsonNewObjectType(JSON_ARRAY);
1571 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH));
1574 jsonObjectGetIndex(fake_params, 0),
1575 osrfHashGet(meta, "primarykey"),
1576 jsonObjectClone(jsonObjectGetIndex(ctx->params, id_pos))
1580 if (order_hash) jsonObjectPush(fake_params, jsonObjectClone(order_hash) );
1582 jsonObject* list = doFieldmapperSearch( ctx,meta, fake_params, err);
1585 jsonObjectFree( fake_params );
1589 jsonObject* obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
1591 jsonObjectFree( list );
1592 jsonObjectFree( fake_params );
1595 if(!verifyObjectPCRUD(ctx, obj)) {
1596 jsonObjectFree(obj);
1599 growing_buffer* msg = buffer_init(128);
1600 OSRF_BUFFER_ADD( msg, MODULENAME );
1601 OSRF_BUFFER_ADD( msg, ": Insufficient permissions to retrieve object" );
1603 char* m = buffer_release(msg);
1604 osrfAppSessionStatus( ctx->session, OSRF_STATUS_NOTALLOWED, "osrfMethodException", ctx->request, m );
1615 static char* jsonNumberToDBString ( osrfHash* field, const jsonObject* value ) {
1616 growing_buffer* val_buf = buffer_init(32);
1617 const char* numtype = get_datatype( field );
1619 if ( !strncmp( numtype, "INT", 3 ) ) {
1620 if (value->type == JSON_NUMBER) buffer_fadd( val_buf, "%ld", (long)jsonObjectGetNumber(value) );
1622 const char* val_str = jsonObjectGetString( value );
1623 buffer_fadd( val_buf, "%ld", atol(val_str) );
1626 } else if ( !strcmp( numtype, "NUMERIC" ) ) {
1627 if (value->type == JSON_NUMBER) buffer_fadd( val_buf, "%f", jsonObjectGetNumber(value) );
1629 const char* val_str = jsonObjectGetString( value );
1630 buffer_fadd( val_buf, "%f", atof(val_str) );
1634 // Presumably this was really intended ot be a string, so quote it
1635 char* str = jsonObjectToSimpleString( value );
1636 if ( dbi_conn_quote_string(dbhandle, &str) ) {
1637 OSRF_BUFFER_ADD( val_buf, str );
1640 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, str);
1642 buffer_free(val_buf);
1647 return buffer_release(val_buf);
1650 static char* searchINPredicate (const char* class, osrfHash* field,
1651 jsonObject* node, const char* op, osrfMethodContext* ctx ) {
1652 growing_buffer* sql_buf = buffer_init(32);
1658 osrfHashGet(field, "name")
1662 buffer_add(sql_buf, "IN (");
1663 } else if (!(strcasecmp(op,"not in"))) {
1664 buffer_add(sql_buf, "NOT IN (");
1666 buffer_add(sql_buf, "IN (");
1669 if (node->type == JSON_HASH) {
1670 // subquery predicate
1671 char* subpred = SELECT(
1673 jsonObjectGetKey( node, "select" ),
1674 jsonObjectGetKey( node, "from" ),
1675 jsonObjectGetKey( node, "where" ),
1676 jsonObjectGetKey( node, "having" ),
1677 jsonObjectGetKey( node, "order_by" ),
1678 jsonObjectGetKey( node, "limit" ),
1679 jsonObjectGetKey( node, "offset" ),
1683 buffer_add(sql_buf, subpred);
1686 } else if (node->type == JSON_ARRAY) {
1687 // literal value list
1688 int in_item_index = 0;
1689 int in_item_first = 1;
1690 const jsonObject* in_item;
1691 while ( (in_item = jsonObjectGetIndex(node, in_item_index++)) ) {
1696 buffer_add(sql_buf, ", ");
1698 // Append the literal value -- quoted if not a number
1699 if ( JSON_NUMBER == in_item->type ) {
1700 char* val = jsonNumberToDBString( field, in_item );
1701 OSRF_BUFFER_ADD( sql_buf, val );
1704 } else if ( !strcmp( get_primitive( field ), "number") ) {
1705 char* val = jsonNumberToDBString( field, in_item );
1706 OSRF_BUFFER_ADD( sql_buf, val );
1710 char* key_string = jsonObjectToSimpleString(in_item);
1711 if ( dbi_conn_quote_string(dbhandle, &key_string) ) {
1712 OSRF_BUFFER_ADD( sql_buf, key_string );
1715 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string);
1717 buffer_free(sql_buf);
1723 if( in_item_first ) {
1724 osrfLogError(OSRF_LOG_MARK, "%s: Empty IN list", MODULENAME );
1725 buffer_free( sql_buf );
1730 OSRF_BUFFER_ADD_CHAR( sql_buf, ')' );
1732 return buffer_release(sql_buf);
1735 // Receive a JSON_ARRAY representing a function call. The first
1736 // entry in the array is the function name. The rest are parameters.
1737 static char* searchValueTransform( const jsonObject* array ) {
1738 growing_buffer* sql_buf = buffer_init(32);
1740 jsonObject* func_item;
1742 // Get the function name
1743 if( array->size > 0 ) {
1744 func_item = jsonObjectGetIndex( array, 0 );
1745 OSRF_BUFFER_ADD( sql_buf, jsonObjectGetString( func_item ) );
1746 OSRF_BUFFER_ADD( sql_buf, "( " );
1749 // Get the parameters
1750 int func_item_index = 1; // We already grabbed the zeroth entry
1751 while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1753 // Add a separator comma, if we need one
1754 if( func_item_index > 2 )
1755 buffer_add( sql_buf, ", " );
1757 // Add the current parameter
1758 if (func_item->type == JSON_NULL) {
1759 buffer_add( sql_buf, "NULL" );
1761 char* val = jsonObjectToSimpleString(func_item);
1762 if ( dbi_conn_quote_string(dbhandle, &val) ) {
1763 OSRF_BUFFER_ADD( sql_buf, val );
1766 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1767 buffer_free(sql_buf);
1774 buffer_add( sql_buf, " )" );
1776 return buffer_release(sql_buf);
1779 static char* searchFunctionPredicate (const char* class, osrfHash* field,
1780 const jsonObject* node, const char* node_key) {
1782 char* val = searchValueTransform(node);
1786 growing_buffer* sql_buf = buffer_init(32);
1791 osrfHashGet(field, "name"),
1798 return buffer_release(sql_buf);
1801 // class is a class name
1802 // field is a field definition as stored in the IDL
1803 // node comes from the method parameter, and represents an entry in the SELECT list
1804 static char* searchFieldTransform (const char* class, osrfHash* field, const jsonObject* node) {
1805 growing_buffer* sql_buf = buffer_init(32);
1807 const char* field_transform = jsonObjectGetString( jsonObjectGetKeyConst( node, "transform" ) );
1808 const char* transform_subcolumn = jsonObjectGetString( jsonObjectGetKeyConst( node, "result_field" ) );
1810 if(transform_subcolumn)
1811 OSRF_BUFFER_ADD_CHAR( sql_buf, '(' ); // enclose transform in parentheses
1813 if (field_transform) {
1814 buffer_fadd( sql_buf, "%s(\"%s\".%s", field_transform, class, osrfHashGet(field, "name"));
1815 const jsonObject* array = jsonObjectGetKeyConst( node, "params" );
1818 if( array->type != JSON_ARRAY ) {
1819 osrfLogError( OSRF_LOG_MARK,
1820 "%s: Expected JSON_ARRAY for function params; found %s",
1821 MODULENAME, json_type( array->type ) );
1822 buffer_free( sql_buf );
1825 int func_item_index = 0;
1826 jsonObject* func_item;
1827 while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
1829 char* val = jsonObjectToSimpleString(func_item);
1832 buffer_add( sql_buf, ",NULL" );
1833 } else if ( dbi_conn_quote_string(dbhandle, &val) ) {
1834 OSRF_BUFFER_ADD_CHAR( sql_buf, ',' );
1835 OSRF_BUFFER_ADD( sql_buf, val );
1837 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
1839 buffer_free(sql_buf);
1846 buffer_add( sql_buf, " )" );
1849 buffer_fadd( sql_buf, "\"%s\".%s", class, osrfHashGet(field, "name"));
1852 if (transform_subcolumn)
1853 buffer_fadd( sql_buf, ").\"%s\"", transform_subcolumn );
1855 return buffer_release(sql_buf);
1858 static char* searchFieldTransformPredicate (const char* class, osrfHash* field,
1859 const jsonObject* node, const char* node_key) {
1860 char* field_transform = searchFieldTransform( class, field, node );
1863 const jsonObject* value_obj = jsonObjectGetKeyConst( node, "value" );
1864 if ( ! value_obj ) {
1865 value = searchWHERE( node, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN, NULL );
1867 osrfLogError(OSRF_LOG_MARK, "%s: Error building condition for field transform", MODULENAME, value);
1868 free(field_transform);
1871 } else if ( value_obj->type == JSON_ARRAY ) {
1872 value = searchValueTransform( value_obj );
1874 osrfLogError(OSRF_LOG_MARK, "%s: Error building value transform for field transform",
1876 free( field_transform );
1879 } else if ( value_obj->type == JSON_HASH ) {
1880 value = searchWHERE( value_obj, osrfHashGet( oilsIDL(), class ), AND_OP_JOIN, NULL );
1882 osrfLogError(OSRF_LOG_MARK, "%s: Error building predicate for field transform", MODULENAME, value);
1883 free(field_transform);
1886 } else if ( value_obj->type == JSON_NUMBER ) {
1887 value = jsonNumberToDBString( field, value_obj );
1888 } else if ( value_obj->type != JSON_NULL ) {
1889 if ( !strcmp( get_primitive( field ), "number") ) {
1890 value = jsonNumberToDBString( field, value_obj );
1892 value = jsonObjectToSimpleString( value_obj );
1893 if ( !dbi_conn_quote_string(dbhandle, &value) ) {
1894 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, value);
1896 free(field_transform);
1902 growing_buffer* sql_buf = buffer_init(32);
1913 free(field_transform);
1915 return buffer_release(sql_buf);
1918 static char* searchSimplePredicate (const char* op, const char* class,
1919 osrfHash* field, const jsonObject* node) {
1923 // Get the value to which we are comparing the specified column
1924 if (node->type != JSON_NULL) {
1925 if ( node->type == JSON_NUMBER ) {
1926 val = jsonNumberToDBString( field, node );
1927 } else if ( !strcmp( get_primitive( field ), "number" ) ) {
1928 val = jsonNumberToDBString( field, node );
1930 val = jsonObjectToSimpleString(node);
1935 if( JSON_NUMBER != node->type && strcmp( get_primitive( field ), "number") ) {
1936 // Value is not numeric; enclose it in quotes
1937 if ( !dbi_conn_quote_string( dbhandle, &val ) ) {
1938 osrfLogError( OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val );
1944 // Compare to a null value
1945 val = strdup( "NULL" );
1946 if (strcmp( op, "=" ))
1952 growing_buffer* sql_buf = buffer_init(32);
1953 buffer_fadd( sql_buf, "\"%s\".%s %s %s", class, osrfHashGet(field, "name"), op, val );
1954 char* pred = buffer_release( sql_buf );
1961 static char* searchBETWEENPredicate (const char* class, osrfHash* field, const jsonObject* node) {
1966 if ( !strcmp( get_primitive( field ), "number") ) {
1967 x_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,0));
1968 y_string = jsonNumberToDBString(field, jsonObjectGetIndex(node,1));
1971 x_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,0));
1972 y_string = jsonObjectToSimpleString(jsonObjectGetIndex(node,1));
1973 if ( !(dbi_conn_quote_string(dbhandle, &x_string) && dbi_conn_quote_string(dbhandle, &y_string)) ) {
1974 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key strings [%s] and [%s]", MODULENAME, x_string, y_string);
1981 growing_buffer* sql_buf = buffer_init(32);
1982 buffer_fadd( sql_buf, "%s BETWEEN %s AND %s", osrfHashGet(field, "name"), x_string, y_string );
1986 return buffer_release(sql_buf);
1989 static char* searchPredicate ( const char* class, osrfHash* field,
1990 jsonObject* node, osrfMethodContext* ctx ) {
1993 if (node->type == JSON_ARRAY) { // equality IN search
1994 pred = searchINPredicate( class, field, node, NULL, ctx );
1995 } else if (node->type == JSON_HASH) { // non-equality search
1996 jsonIterator* pred_itr = jsonNewIterator( node );
1997 if( !jsonIteratorHasNext( pred_itr ) ) {
1998 osrfLogError( OSRF_LOG_MARK, "%s: Empty predicate for field \"%s\"",
1999 MODULENAME, osrfHashGet(field, "name") );
2001 jsonObject* pred_node = jsonIteratorNext( pred_itr );
2003 // Verify that there are no additional predicates
2004 if( jsonIteratorHasNext( pred_itr ) ) {
2005 osrfLogError( OSRF_LOG_MARK, "%s: Multiple predicates for field \"%s\"",
2006 MODULENAME, osrfHashGet(field, "name") );
2007 } else if ( !(strcasecmp( pred_itr->key,"between" )) )
2008 pred = searchBETWEENPredicate( class, field, pred_node );
2009 else if ( !(strcasecmp( pred_itr->key,"in" )) || !(strcasecmp( pred_itr->key,"not in" )) )
2010 pred = searchINPredicate( class, field, pred_node, pred_itr->key, ctx );
2011 else if ( pred_node->type == JSON_ARRAY )
2012 pred = searchFunctionPredicate( class, field, pred_node, pred_itr->key );
2013 else if ( pred_node->type == JSON_HASH )
2014 pred = searchFieldTransformPredicate( class, field, pred_node, pred_itr->key );
2016 pred = searchSimplePredicate( pred_itr->key, class, field, pred_node );
2018 jsonIteratorFree(pred_itr);
2020 } else if (node->type == JSON_NULL) { // IS NULL search
2021 growing_buffer* _p = buffer_init(64);
2024 "\"%s\".%s IS NULL",
2026 osrfHashGet(field, "name")
2028 pred = buffer_release(_p);
2029 } else { // equality search
2030 pred = searchSimplePredicate( "=", class, field, node );
2049 field : call_number,
2065 static char* searchJOIN ( const jsonObject* join_hash, osrfHash* leftmeta ) {
2067 const jsonObject* working_hash;
2068 jsonObject* freeable_hash = NULL;
2070 if (join_hash->type == JSON_STRING) {
2071 // create a wrapper around a copy of the original
2072 const char* _tmp = jsonObjectGetString( join_hash );
2073 freeable_hash = jsonNewObjectType(JSON_HASH);
2074 jsonObjectSetKey(freeable_hash, _tmp, NULL);
2075 working_hash = freeable_hash;
2078 if( join_hash->type != JSON_HASH ) {
2081 "%s: JOIN failed; expected JSON object type not found",
2086 working_hash = join_hash;
2089 growing_buffer* join_buf = buffer_init(128);
2090 const char* leftclass = osrfHashGet(leftmeta, "classname");
2092 jsonObject* snode = NULL;
2093 jsonIterator* search_itr = jsonNewIterator( working_hash );
2095 while ( (snode = jsonIteratorNext( search_itr )) ) {
2096 const char* class = search_itr->key;
2097 osrfHash* idlClass = osrfHashGet( oilsIDL(), class );
2101 "%s: JOIN failed. No class \"%s\" defined in IDL",
2105 jsonIteratorFree( search_itr );
2106 buffer_free( join_buf );
2108 jsonObjectFree( freeable_hash );
2112 const char* fkey = jsonObjectGetString( jsonObjectGetKeyConst( snode, "fkey" ) );
2113 const char* field = jsonObjectGetString( jsonObjectGetKeyConst( snode, "field" ) );
2115 if (field && !fkey) {
2116 fkey = (const char*)oilsIDLFindPath("/%s/links/%s/key", class, field);
2120 "%s: JOIN failed. No link defined from %s.%s to %s",
2126 buffer_free(join_buf);
2128 jsonObjectFree(freeable_hash);
2129 jsonIteratorFree(search_itr);
2133 } else if (!field && fkey) {
2134 field = (const char*)oilsIDLFindPath("/%s/links/%s/key", leftclass, fkey );
2138 "%s: JOIN failed. No link defined from %s.%s to %s",
2144 buffer_free(join_buf);
2146 jsonObjectFree(freeable_hash);
2147 jsonIteratorFree(search_itr);
2152 } else if (!field && !fkey) {
2153 osrfHash* _links = oilsIDL_links( leftclass );
2155 // For each link defined for the left class:
2156 // see if the link references the joined class
2157 osrfHashIterator* itr = osrfNewHashIterator( _links );
2158 osrfHash* curr_link = NULL;
2159 while( (curr_link = osrfHashIteratorNext( itr ) ) ) {
2160 const char* other_class = osrfHashGet( curr_link, "class" );
2161 if( other_class && !strcmp( other_class, class ) ) {
2163 // Found a link between the classes
2164 fkey = osrfHashIteratorKey( itr );
2165 field = osrfHashGet( curr_link, "key" );
2169 osrfHashIteratorFree( itr );
2171 if (!field || !fkey) {
2172 // Do another such search, with the classes reversed
2173 _links = oilsIDL_links( class );
2175 // For each link defined for the joined class:
2176 // see if the link references the left class
2177 osrfHashIterator* itr = osrfNewHashIterator( _links );
2178 osrfHash* curr_link = NULL;
2179 while( (curr_link = osrfHashIteratorNext( itr ) ) ) {
2180 const char* other_class = osrfHashGet( curr_link, "class" );
2181 if( other_class && !strcmp( other_class, leftclass ) ) {
2183 // Found a link between the classes
2184 fkey = osrfHashIteratorKey( itr );
2185 field = osrfHashGet( curr_link, "key" );
2189 osrfHashIteratorFree( itr );
2192 if (!field || !fkey) {
2195 "%s: JOIN failed. No link defined between %s and %s",
2200 buffer_free(join_buf);
2202 jsonObjectFree(freeable_hash);
2203 jsonIteratorFree(search_itr);
2209 const char* type = jsonObjectGetString( jsonObjectGetKeyConst( snode, "type" ) );
2211 if ( !strcasecmp(type,"left") ) {
2212 buffer_add(join_buf, " LEFT JOIN");
2213 } else if ( !strcasecmp(type,"right") ) {
2214 buffer_add(join_buf, " RIGHT JOIN");
2215 } else if ( !strcasecmp(type,"full") ) {
2216 buffer_add(join_buf, " FULL JOIN");
2218 buffer_add(join_buf, " INNER JOIN");
2221 buffer_add(join_buf, " INNER JOIN");
2224 char* table = getSourceDefinition(idlClass);
2226 jsonIteratorFree( search_itr );
2227 buffer_free( join_buf );
2229 jsonObjectFree( freeable_hash );
2233 buffer_fadd(join_buf, " %s AS \"%s\" ON ( \"%s\".%s = \"%s\".%s",
2234 table, class, class, field, leftclass, fkey);
2237 const jsonObject* filter = jsonObjectGetKeyConst( snode, "filter" );
2239 const char* filter_op = jsonObjectGetString( jsonObjectGetKeyConst( snode, "filter_op" ) );
2240 if ( filter_op && !strcasecmp("or",filter_op) ) {
2241 buffer_add( join_buf, " OR " );
2243 buffer_add( join_buf, " AND " );
2246 char* jpred = searchWHERE( filter, idlClass, AND_OP_JOIN, NULL );
2248 OSRF_BUFFER_ADD_CHAR( join_buf, ' ' );
2249 OSRF_BUFFER_ADD( join_buf, jpred );
2254 "%s: JOIN failed. Invalid conditional expression.",
2257 jsonIteratorFree( search_itr );
2258 buffer_free( join_buf );
2260 jsonObjectFree( freeable_hash );
2265 buffer_add(join_buf, " ) ");
2267 const jsonObject* join_filter = jsonObjectGetKeyConst( snode, "join" );
2269 char* jpred = searchJOIN( join_filter, idlClass );
2270 OSRF_BUFFER_ADD_CHAR( join_buf, ' ' );
2271 OSRF_BUFFER_ADD( join_buf, jpred );
2277 jsonObjectFree(freeable_hash);
2278 jsonIteratorFree(search_itr);
2280 return buffer_release(join_buf);
2285 { +class : { -or|-and : { field : { op : value }, ... } ... }, ... }
2286 { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }
2287 [ { +class : { -or|-and : [ { field : { op : value }, ... }, ...] ... }, ... }, ... ]
2289 Generate code to express a set of conditions, as for a WHERE clause. Parameters:
2291 search_hash is the JSON expression of the conditions.
2292 meta is the class definition from the IDL, for the relevant table.
2293 opjoin_type indicates whether multiple conditions, if present, should be
2294 connected by AND or OR.
2295 osrfMethodContext is loaded with all sorts of stuff, but all we do with it here is
2296 to pass it to other functions -- and all they do with it is to use the session
2297 and request members to send error messages back to the client.
2301 static char* searchWHERE ( const jsonObject* search_hash, osrfHash* meta, int opjoin_type, osrfMethodContext* ctx ) {
2305 "%s: Entering searchWHERE; search_hash addr = %p, meta addr = %p, opjoin_type = %d, ctx addr = %p",
2313 growing_buffer* sql_buf = buffer_init(128);
2315 jsonObject* node = NULL;
2318 if ( search_hash->type == JSON_ARRAY ) {
2319 osrfLogDebug(OSRF_LOG_MARK, "%s: In WHERE clause, condition type is JSON_ARRAY", MODULENAME);
2320 jsonIterator* search_itr = jsonNewIterator( search_hash );
2321 if( !jsonIteratorHasNext( search_itr ) ) {
2324 "%s: Invalid predicate structure: empty JSON array",
2327 jsonIteratorFree( search_itr );
2328 buffer_free( sql_buf );
2332 while ( (node = jsonIteratorNext( search_itr )) ) {
2336 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
2337 else buffer_add(sql_buf, " AND ");
2340 char* subpred = searchWHERE( node, meta, opjoin_type, ctx );
2342 buffer_fadd(sql_buf, "( %s )", subpred);
2345 jsonIteratorFree( search_itr );
2346 buffer_free( sql_buf );
2350 jsonIteratorFree(search_itr);
2352 } else if ( search_hash->type == JSON_HASH ) {
2353 osrfLogDebug(OSRF_LOG_MARK, "%s: In WHERE clause, condition type is JSON_HASH", MODULENAME);
2354 jsonIterator* search_itr = jsonNewIterator( search_hash );
2355 if( !jsonIteratorHasNext( search_itr ) ) {
2358 "%s: Invalid predicate structure: empty JSON object",
2361 jsonIteratorFree( search_itr );
2362 buffer_free( sql_buf );
2366 while ( (node = jsonIteratorNext( search_itr )) ) {
2371 if (opjoin_type == OR_OP_JOIN) buffer_add(sql_buf, " OR ");
2372 else buffer_add(sql_buf, " AND ");
2375 if ( '+' == search_itr->key[ 0 ] ) {
2376 if ( node->type == JSON_STRING ) {
2377 // Intended purpose; to allow reference to a Boolean column
2379 // Verify that the class alias is not empty
2380 if( '\0' == search_itr->key[ 1 ] ) {
2383 "%s: Table alias is empty",
2386 jsonIteratorFree( search_itr );
2387 buffer_free( sql_buf );
2391 // Verify that the string looks like an identifier.
2392 const char* subpred = jsonObjectGetString( node );
2393 if( ! is_identifier( subpred ) ) {
2396 "%s: Invalid boolean identifier in WHERE clause: \"%s\"",
2400 jsonIteratorFree( search_itr );
2401 buffer_free( sql_buf );
2405 buffer_fadd(sql_buf, " \"%s\".%s ", search_itr->key + 1, subpred);
2407 char* subpred = searchWHERE( node, osrfHashGet( oilsIDL(), search_itr->key + 1 ), AND_OP_JOIN, ctx );
2409 buffer_fadd(sql_buf, "( %s )", subpred);
2412 jsonIteratorFree( search_itr );
2413 buffer_free( sql_buf );
2417 } else if ( !strcasecmp("-or",search_itr->key) ) {
2418 char* subpred = searchWHERE( node, meta, OR_OP_JOIN, ctx );
2420 buffer_fadd(sql_buf, "( %s )", subpred);
2423 buffer_free( sql_buf );
2426 } else if ( !strcasecmp("-and",search_itr->key) ) {
2427 char* subpred = searchWHERE( node, meta, AND_OP_JOIN, ctx );
2429 buffer_fadd(sql_buf, "( %s )", subpred);
2432 buffer_free( sql_buf );
2435 } else if ( !strcasecmp("-not",search_itr->key) ) {
2436 char* subpred = searchWHERE( node, meta, AND_OP_JOIN, ctx );
2438 buffer_fadd(sql_buf, " NOT ( %s )", subpred);
2441 buffer_free( sql_buf );
2444 } else if ( !strcasecmp("-exists",search_itr->key) ) {
2445 char* subpred = SELECT(
2447 jsonObjectGetKey( node, "select" ),
2448 jsonObjectGetKey( node, "from" ),
2449 jsonObjectGetKey( node, "where" ),
2450 jsonObjectGetKey( node, "having" ),
2451 jsonObjectGetKey( node, "order_by" ),
2452 jsonObjectGetKey( node, "limit" ),
2453 jsonObjectGetKey( node, "offset" ),
2457 buffer_fadd(sql_buf, "EXISTS ( %s )", subpred);
2459 } else if ( !strcasecmp("-not-exists",search_itr->key) ) {
2460 char* subpred = SELECT(
2462 jsonObjectGetKey( node, "select" ),
2463 jsonObjectGetKey( node, "from" ),
2464 jsonObjectGetKey( node, "where" ),
2465 jsonObjectGetKey( node, "having" ),
2466 jsonObjectGetKey( node, "order_by" ),
2467 jsonObjectGetKey( node, "limit" ),
2468 jsonObjectGetKey( node, "offset" ),
2472 buffer_fadd(sql_buf, "NOT EXISTS ( %s )", subpred);
2476 char* class = osrfHashGet(meta, "classname");
2477 osrfHash* fields = osrfHashGet(meta, "fields");
2478 osrfHash* field = osrfHashGet( fields, search_itr->key );
2482 char* table = getSourceDefinition(meta);
2484 table = strdup( "(?)" );
2487 "%s: Attempt to reference non-existent column %s on %s (%s)",
2493 buffer_free(sql_buf);
2495 jsonIteratorFree(search_itr);
2499 char* subpred = searchPredicate( class, field, node, ctx );
2501 buffer_add( sql_buf, subpred );
2504 buffer_free(sql_buf);
2505 jsonIteratorFree(search_itr);
2510 jsonIteratorFree(search_itr);
2513 // ERROR ... only hash and array allowed at this level
2514 char* predicate_string = jsonObjectToJSON( search_hash );
2517 "%s: Invalid predicate structure: %s",
2521 buffer_free(sql_buf);
2522 free(predicate_string);
2526 return buffer_release(sql_buf);
2530 /* method context */ osrfMethodContext* ctx,
2532 /* SELECT */ jsonObject* selhash,
2533 /* FROM */ jsonObject* join_hash,
2534 /* WHERE */ jsonObject* search_hash,
2535 /* HAVING */ jsonObject* having_hash,
2536 /* ORDER BY */ jsonObject* order_hash,
2537 /* LIMIT */ jsonObject* limit,
2538 /* OFFSET */ jsonObject* offset,
2539 /* flags */ int flags
2541 const char* locale = osrf_message_get_last_locale();
2543 // in case we don't get a select list
2544 jsonObject* defaultselhash = NULL;
2546 // general tmp objects
2547 const jsonObject* tmp_const;
2548 jsonObject* selclass = NULL;
2549 jsonObject* selfield = NULL;
2550 jsonObject* snode = NULL;
2551 jsonObject* onode = NULL;
2553 char* string = NULL;
2554 int from_function = 0;
2559 // the core search class
2560 char* core_class = NULL;
2562 // metadata about the core search class
2563 osrfHash* core_meta = NULL;
2565 // punt if there's no core class
2566 if (!join_hash || ( join_hash->type == JSON_HASH && !join_hash->size )) {
2569 "%s: FROM clause is missing or empty",
2573 osrfAppSessionStatus(
2575 OSRF_STATUS_INTERNALSERVERERROR,
2576 "osrfMethodException",
2578 "FROM clause is missing or empty in JSON query"
2583 // get the core class -- the only key of the top level FROM clause, or a string
2584 if (join_hash->type == JSON_HASH) {
2585 jsonIterator* tmp_itr = jsonNewIterator( join_hash );
2586 snode = jsonIteratorNext( tmp_itr );
2588 core_class = strdup( tmp_itr->key );
2591 jsonObject* extra = jsonIteratorNext( tmp_itr );
2593 jsonIteratorFree( tmp_itr );
2596 // There shouldn't be more than one entry in join_hash
2600 "%s: Malformed FROM clause: extra entry in JSON_HASH",
2604 osrfAppSessionStatus(
2606 OSRF_STATUS_INTERNALSERVERERROR,
2607 "osrfMethodException",
2609 "Malformed FROM clause in JSON query"
2612 return NULL; // Malformed join_hash; extra entry
2614 } else if (join_hash->type == JSON_ARRAY) {
2616 core_class = jsonObjectToSimpleString( jsonObjectGetIndex(join_hash, 0) );
2619 } else if (join_hash->type == JSON_STRING) {
2620 core_class = jsonObjectToSimpleString( join_hash );
2626 "%s: FROM clause is unexpected JSON type: %s",
2628 json_type( join_hash->type )
2631 osrfAppSessionStatus(
2633 OSRF_STATUS_INTERNALSERVERERROR,
2634 "osrfMethodException",
2636 "Ill-formed FROM clause in JSON query"
2642 if (!from_function) {
2643 // Get the IDL class definition for the core class
2644 core_meta = osrfHashGet( oilsIDL(), core_class );
2645 if( !core_meta ) { // Didn't find it?
2648 "%s: SELECT clause references undefined class: \"%s\"",
2653 osrfAppSessionStatus(
2655 OSRF_STATUS_INTERNALSERVERERROR,
2656 "osrfMethodException",
2658 "SELECT clause references undefined class in JSON query"
2664 // Make sure the class isn't virtual
2665 if( str_is_true( osrfHashGet( core_meta, "virtual" ) ) ) {
2668 "%s: Core class is virtual: \"%s\"",
2673 osrfAppSessionStatus(
2675 OSRF_STATUS_INTERNALSERVERERROR,
2676 "osrfMethodException",
2678 "FROM clause references virtual class in JSON query"
2685 // if the select list is empty, or the core class field list is '*',
2686 // build the default select list ...
2688 selhash = defaultselhash = jsonNewObjectType(JSON_HASH);
2689 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2690 } else if( selhash->type != JSON_HASH ) {
2693 "%s: Expected JSON_HASH for SELECT clause; found %s",
2695 json_type( selhash->type )
2699 osrfAppSessionStatus(
2701 OSRF_STATUS_INTERNALSERVERERROR,
2702 "osrfMethodException",
2704 "Malformed SELECT clause in JSON query"
2708 } else if ( (tmp_const = jsonObjectGetKeyConst( selhash, core_class )) && tmp_const->type == JSON_STRING ) {
2709 const char* _x = jsonObjectGetString( tmp_const );
2710 if (!strncmp( "*", _x, 1 )) {
2711 jsonObjectRemoveKey( selhash, core_class );
2712 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
2717 growing_buffer* sql_buf = buffer_init(128);
2719 // temp buffer for the SELECT list
2720 growing_buffer* select_buf = buffer_init(128);
2721 growing_buffer* order_buf = buffer_init(128);
2722 growing_buffer* group_buf = buffer_init(128);
2723 growing_buffer* having_buf = buffer_init(128);
2725 int aggregate_found = 0; // boolean
2727 // Build a select list
2728 if(from_function) // From a function we select everything
2729 OSRF_BUFFER_ADD_CHAR( select_buf, '*' );
2732 // If we need to build a default list, prepare to do so
2733 jsonObject* _tmp = jsonObjectGetKey( selhash, core_class );
2734 if ( _tmp && !_tmp->size ) {
2736 osrfHash* core_fields = osrfHashGet( core_meta, "fields" );
2738 osrfHashIterator* field_itr = osrfNewHashIterator( core_fields );
2739 osrfHash* field_def;
2740 while( ( field_def = osrfHashIteratorNext( field_itr ) ) ) {
2741 if( ! str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2742 // This field is not virtual, so add it to the list
2743 jsonObjectPush( _tmp, jsonNewObject( osrfHashIteratorKey( field_itr ) ) );
2746 osrfHashIteratorFree( field_itr );
2749 // Now build the actual select list
2753 jsonIterator* selclass_itr = jsonNewIterator( selhash );
2754 while ( (selclass = jsonIteratorNext( selclass_itr )) ) { // For each class
2756 // Make sure the class is defined in the IDL
2757 const char* cname = selclass_itr->key;
2758 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
2762 "%s: Selected class \"%s\" not defined in IDL",
2768 osrfAppSessionStatus(
2770 OSRF_STATUS_INTERNALSERVERERROR,
2771 "osrfMethodException",
2773 "Selected class is not defined"
2775 jsonIteratorFree( selclass_itr );
2776 buffer_free( sql_buf );
2777 buffer_free( select_buf );
2778 buffer_free( order_buf );
2779 buffer_free( group_buf );
2780 buffer_free( having_buf );
2781 if( defaultselhash ) jsonObjectFree( defaultselhash );
2786 // Make sure the target relation is in the join tree.
2788 // At this point join_hash is a step down from the join_hash we
2789 // received as a parameter. If the original was a JSON_STRING,
2790 // then json_hash is now NULL. If the original was a JSON_HASH,
2791 // then json_hash is now the first (and only) entry in it,
2792 // denoting the core class. We've already excluded the
2793 // possibility that the original was a JSON_ARRAY, because in
2794 // that case from_function would be non-NULL, and we wouldn't
2797 int class_in_from_clause; // boolean
2799 if ( ! strcmp( core_class, cname ))
2800 // This is the core class -- no problem
2801 class_in_from_clause = 1;
2804 // There's only one class in the FROM clause, and this isn't it
2805 class_in_from_clause = 0;
2806 else if (join_hash->type == JSON_STRING) {
2807 // There's only one class in the FROM clause
2808 const char* str = jsonObjectGetString(join_hash);
2809 if ( strcmp( str, cname ) )
2810 class_in_from_clause = 0; // This isn't it
2812 class_in_from_clause = 1; // This is it
2814 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", cname);
2815 if ( 0 == found->size )
2816 class_in_from_clause = 0; // Nowhere in the join tree
2818 class_in_from_clause = 1; // Found it
2819 jsonObjectFree( found );
2823 // If the class isn't in the FROM clause, bail out
2824 if( ! class_in_from_clause ) {
2827 "%s: SELECT clause references class not in FROM clause: \"%s\"",
2832 osrfAppSessionStatus(
2834 OSRF_STATUS_INTERNALSERVERERROR,
2835 "osrfMethodException",
2837 "Selected class not in FROM clause in JSON query"
2839 jsonIteratorFree( selclass_itr );
2840 buffer_free( sql_buf );
2841 buffer_free( select_buf );
2842 buffer_free( order_buf );
2843 buffer_free( group_buf );
2844 buffer_free( having_buf );
2845 if( defaultselhash ) jsonObjectFree( defaultselhash );
2850 // Look up some attributes of the current class, so that we
2851 // don't have to look them up again for each field
2852 osrfHash* class_field_set = osrfHashGet( idlClass, "fields" );
2853 const char* class_pkey = osrfHashGet( idlClass, "primarykey" );
2854 const char* class_tname = osrfHashGet( idlClass, "tablename" );
2856 // stitch together the column list ...
2857 jsonIterator* select_itr = jsonNewIterator( selclass );
2858 while ( (selfield = jsonIteratorNext( select_itr )) ) { // for each SELECT column
2860 // If we need a separator comma, add one
2864 OSRF_BUFFER_ADD_CHAR( select_buf, ',' );
2867 // ... if it's a string, just toss it on the pile
2868 if (selfield->type == JSON_STRING) {
2870 // Look up the field in the IDL
2871 const char* col_name = jsonObjectGetString( selfield );
2872 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
2874 // No such field in current class
2877 "%s: Selected column \"%s\" not defined in IDL for class \"%s\"",
2883 osrfAppSessionStatus(
2885 OSRF_STATUS_INTERNALSERVERERROR,
2886 "osrfMethodException",
2888 "Selected column not defined in JSON query"
2890 jsonIteratorFree( select_itr );
2891 jsonIteratorFree( selclass_itr );
2892 buffer_free( sql_buf );
2893 buffer_free( select_buf );
2894 buffer_free( order_buf );
2895 buffer_free( group_buf );
2896 buffer_free( having_buf );
2897 if( defaultselhash ) jsonObjectFree( defaultselhash );
2900 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2901 // Virtual field not allowed
2904 "%s: Selected column \"%s\" for class \"%s\" is virtual",
2910 osrfAppSessionStatus(
2912 OSRF_STATUS_INTERNALSERVERERROR,
2913 "osrfMethodException",
2915 "Selected column may not be virtual in JSON query"
2917 jsonIteratorFree( select_itr );
2918 jsonIteratorFree( selclass_itr );
2919 buffer_free( sql_buf );
2920 buffer_free( select_buf );
2921 buffer_free( order_buf );
2922 buffer_free( group_buf );
2923 buffer_free( having_buf );
2924 if( defaultselhash ) jsonObjectFree( defaultselhash );
2931 if (flags & DISABLE_I18N)
2934 i18n = osrfHashGet(field_def, "i18n");
2936 if( str_is_true( i18n ) ) {
2937 buffer_fadd( select_buf,
2938 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
2939 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, col_name );
2941 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
2944 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
2947 // ... but it could be an object, in which case we check for a Field Transform
2948 } else if (selfield->type == JSON_HASH) {
2950 const char* col_name = jsonObjectGetString( jsonObjectGetKeyConst( selfield, "column" ) );
2952 // Get the field definition from the IDL
2953 osrfHash* field_def = osrfHashGet( class_field_set, col_name );
2955 // No such field in current class
2958 "%s: Selected column \"%s\" is not defined in IDL for class \"%s\"",
2964 osrfAppSessionStatus(
2966 OSRF_STATUS_INTERNALSERVERERROR,
2967 "osrfMethodException",
2969 "Selected column is not defined in JSON query"
2971 jsonIteratorFree( select_itr );
2972 jsonIteratorFree( selclass_itr );
2973 buffer_free( sql_buf );
2974 buffer_free( select_buf );
2975 buffer_free( order_buf );
2976 buffer_free( group_buf );
2977 buffer_free( having_buf );
2978 if( defaultselhash ) jsonObjectFree( defaultselhash );
2981 } else if ( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
2982 // No such field in current class
2985 "%s: Selected column \"%s\" is virtual for class \"%s\"",
2991 osrfAppSessionStatus(
2993 OSRF_STATUS_INTERNALSERVERERROR,
2994 "osrfMethodException",
2996 "Selected column is virtual in JSON query"
2998 jsonIteratorFree( select_itr );
2999 jsonIteratorFree( selclass_itr );
3000 buffer_free( sql_buf );
3001 buffer_free( select_buf );
3002 buffer_free( order_buf );
3003 buffer_free( group_buf );
3004 buffer_free( having_buf );
3005 if( defaultselhash ) jsonObjectFree( defaultselhash );
3010 // Decide what to use as a column alias
3012 if ((tmp_const = jsonObjectGetKeyConst( selfield, "alias" ))) {
3013 _alias = jsonObjectGetString( tmp_const );
3014 } else { // Use field name as the alias
3018 if (jsonObjectGetKeyConst( selfield, "transform" )) {
3019 char* transform_str = searchFieldTransform(cname, field_def, selfield);
3020 if( transform_str ) {
3021 buffer_fadd(select_buf, " %s AS \"%s\"", transform_str, _alias);
3022 free(transform_str);
3025 osrfAppSessionStatus(
3027 OSRF_STATUS_INTERNALSERVERERROR,
3028 "osrfMethodException",
3030 "Unable to generate transform function in JSON query"
3032 jsonIteratorFree( select_itr );
3033 jsonIteratorFree( selclass_itr );
3034 buffer_free( sql_buf );
3035 buffer_free( select_buf );
3036 buffer_free( order_buf );
3037 buffer_free( group_buf );
3038 buffer_free( having_buf );
3039 if( defaultselhash ) jsonObjectFree( defaultselhash );
3047 if (flags & DISABLE_I18N)
3050 i18n = osrfHashGet(field_def, "i18n");
3052 if( str_is_true( i18n ) ) {
3053 buffer_fadd( select_buf,
3054 " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
3055 class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, _alias);
3057 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
3060 buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, _alias);
3067 "%s: Selected item is unexpected JSON type: %s",
3069 json_type( selfield->type )
3072 osrfAppSessionStatus(
3074 OSRF_STATUS_INTERNALSERVERERROR,
3075 "osrfMethodException",
3077 "Ill-formed SELECT item in JSON query"
3079 jsonIteratorFree( select_itr );
3080 jsonIteratorFree( selclass_itr );
3081 buffer_free( sql_buf );
3082 buffer_free( select_buf );
3083 buffer_free( order_buf );
3084 buffer_free( group_buf );
3085 buffer_free( having_buf );
3086 if( defaultselhash ) jsonObjectFree( defaultselhash );
3091 const jsonObject* agg_obj = jsonObjectGetKey( selfield, "aggregate" );
3092 if( obj_is_true( agg_obj ) )
3093 aggregate_found = 1;
3095 // Append a comma (except for the first one)
3096 // and add the column to a GROUP BY clause
3100 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3102 buffer_fadd(group_buf, " %d", sel_pos);
3106 if (is_agg->size || (flags & SELECT_DISTINCT)) {
3108 const jsonObject* aggregate_obj = jsonObjectGetKey( selfield, "aggregate" );
3109 if ( ! obj_is_true( aggregate_obj ) ) {
3113 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3116 buffer_fadd(group_buf, " %d", sel_pos);
3119 } else if (is_agg = jsonObjectGetKey( selfield, "having" )) {
3123 OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
3126 _column = searchFieldTransform(cname, field, selfield);
3127 OSRF_BUFFER_ADD_CHAR(group_buf, ' ');
3128 OSRF_BUFFER_ADD(group_buf, _column);
3129 _column = searchFieldTransform(cname, field, selfield);
3136 } // end while -- iterating across SELECT columns
3138 jsonIteratorFree(select_itr);
3139 } // end while -- iterating across classes
3141 jsonIteratorFree(selclass_itr);
3145 char* col_list = buffer_release(select_buf);
3147 if (from_function) table = searchValueTransform(join_hash);
3148 else table = getSourceDefinition(core_meta);
3152 osrfAppSessionStatus(
3154 OSRF_STATUS_INTERNALSERVERERROR,
3155 "osrfMethodException",
3157 "Unable to identify table for core class"
3160 buffer_free( sql_buf );
3161 buffer_free( order_buf );
3162 buffer_free( group_buf );
3163 buffer_free( having_buf );
3164 if( defaultselhash ) jsonObjectFree( defaultselhash );
3169 // Put it all together
3170 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\" ", col_list, table, core_class );
3174 if (!from_function) {
3175 // Now, walk the join tree and add that clause
3177 char* join_clause = searchJOIN( join_hash, core_meta );
3179 buffer_add(sql_buf, join_clause);
3183 osrfAppSessionStatus(
3185 OSRF_STATUS_INTERNALSERVERERROR,
3186 "osrfMethodException",
3188 "Unable to construct JOIN clause(s)"
3190 buffer_free( sql_buf );
3191 buffer_free( order_buf );
3192 buffer_free( group_buf );
3193 buffer_free( having_buf );
3194 if( defaultselhash ) jsonObjectFree( defaultselhash );
3200 // Build a WHERE clause, if there is one
3201 if ( search_hash ) {
3202 buffer_add(sql_buf, " WHERE ");
3204 // and it's on the WHERE clause
3205 char* pred = searchWHERE( search_hash, core_meta, AND_OP_JOIN, ctx );
3208 buffer_add(sql_buf, pred);
3212 osrfAppSessionStatus(
3214 OSRF_STATUS_INTERNALSERVERERROR,
3215 "osrfMethodException",
3217 "Severe query error in WHERE predicate -- see error log for more details"
3221 buffer_free(having_buf);
3222 buffer_free(group_buf);
3223 buffer_free(order_buf);
3224 buffer_free(sql_buf);
3225 if (defaultselhash) jsonObjectFree(defaultselhash);
3230 // Build a HAVING clause, if there is one
3231 if ( having_hash ) {
3232 buffer_add(sql_buf, " HAVING ");
3234 // and it's on the the WHERE clause
3235 char* pred = searchWHERE( having_hash, core_meta, AND_OP_JOIN, ctx );
3238 buffer_add(sql_buf, pred);
3242 osrfAppSessionStatus(
3244 OSRF_STATUS_INTERNALSERVERERROR,
3245 "osrfMethodException",
3247 "Severe query error in HAVING predicate -- see error log for more details"
3251 buffer_free(having_buf);
3252 buffer_free(group_buf);
3253 buffer_free(order_buf);
3254 buffer_free(sql_buf);
3255 if (defaultselhash) jsonObjectFree(defaultselhash);
3260 // Build an ORDER BY clause, if there is one
3262 jsonIterator* class_itr = jsonNewIterator( order_hash );
3263 while ( (snode = jsonIteratorNext( class_itr )) ) {
3265 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
3268 if ( snode->type == JSON_HASH ) {
3270 jsonIterator* order_itr = jsonNewIterator( snode );
3271 while ( (onode = jsonIteratorNext( order_itr )) ) {
3273 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ))
3276 const char* direction = NULL;
3277 if ( onode->type == JSON_HASH ) {
3278 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3279 string = searchFieldTransform(
3281 oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ),
3285 growing_buffer* field_buf = buffer_init(16);
3286 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3287 string = buffer_release(field_buf);
3290 if ( (tmp_const = jsonObjectGetKeyConst( onode, "direction" )) ) {
3291 const char* dir = jsonObjectGetString(tmp_const);
3292 if (!strncasecmp(dir, "d", 1)) {
3293 direction = " DESC";
3300 string = strdup(order_itr->key);
3301 const char* dir = jsonObjectGetString(onode);
3302 if (!strncasecmp(dir, "d", 1)) {
3303 direction = " DESC";
3312 buffer_add(order_buf, ", ");
3315 buffer_add(order_buf, string);
3319 buffer_add(order_buf, direction);
3323 // jsonIteratorFree(order_itr);
3325 } else if ( snode->type == JSON_ARRAY ) {
3327 jsonIterator* order_itr = jsonNewIterator( snode );
3328 while ( (onode = jsonIteratorNext( order_itr )) ) {
3330 const char* _f = jsonObjectGetString( onode );
3332 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, _f))
3338 buffer_add(order_buf, ", ");
3341 buffer_add(order_buf, _f);
3344 // jsonIteratorFree(order_itr);
3347 // IT'S THE OOOOOOOOOOOLD STYLE!
3349 osrfLogError(OSRF_LOG_MARK, "%s: Possible SQL injection attempt; direct order by is not allowed", MODULENAME);
3351 osrfAppSessionStatus(
3353 OSRF_STATUS_INTERNALSERVERERROR,
3354 "osrfMethodException",
3356 "Severe query error -- see error log for more details"
3361 buffer_free(having_buf);
3362 buffer_free(group_buf);
3363 buffer_free(order_buf);
3364 buffer_free(sql_buf);
3365 if (defaultselhash) jsonObjectFree(defaultselhash);
3366 jsonIteratorFree(class_itr);
3371 // jsonIteratorFree(class_itr);
3375 string = buffer_release(group_buf);
3377 if ( *string && ( aggregate_found || (flags & SELECT_DISTINCT) ) ) {
3378 OSRF_BUFFER_ADD( sql_buf, " GROUP BY " );
3379 OSRF_BUFFER_ADD( sql_buf, string );
3384 string = buffer_release(having_buf);
3387 OSRF_BUFFER_ADD( sql_buf, " HAVING " );
3388 OSRF_BUFFER_ADD( sql_buf, string );
3393 string = buffer_release(order_buf);
3396 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3397 OSRF_BUFFER_ADD( sql_buf, string );
3403 const char* str = jsonObjectGetString(limit);
3404 buffer_fadd( sql_buf, " LIMIT %d", atoi(str) );
3408 const char* str = jsonObjectGetString(offset);
3409 buffer_fadd( sql_buf, " OFFSET %d", atoi(str) );
3412 if (!(flags & SUBSELECT)) OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3415 if (defaultselhash) jsonObjectFree(defaultselhash);
3417 return buffer_release(sql_buf);
3421 static char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
3423 const char* locale = osrf_message_get_last_locale();
3425 osrfHash* fields = osrfHashGet(meta, "fields");
3426 char* core_class = osrfHashGet(meta, "classname");
3428 const jsonObject* join_hash = jsonObjectGetKeyConst( order_hash, "join" );
3430 jsonObject* node = NULL;
3431 jsonObject* snode = NULL;
3432 jsonObject* onode = NULL;
3433 const jsonObject* _tmp = NULL;
3434 jsonObject* selhash = NULL;
3435 jsonObject* defaultselhash = NULL;
3437 growing_buffer* sql_buf = buffer_init(128);
3438 growing_buffer* select_buf = buffer_init(128);
3440 if ( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
3441 defaultselhash = jsonNewObjectType(JSON_HASH);
3442 selhash = defaultselhash;
3445 if ( !jsonObjectGetKeyConst(selhash,core_class) ) {
3446 jsonObjectSetKey( selhash, core_class, jsonNewObjectType(JSON_ARRAY) );
3447 jsonObject* flist = jsonObjectGetKey( selhash, core_class );
3452 osrfStringArray* keys = osrfHashKeys( fields );
3453 while ( (field = osrfStringArrayGetString(keys, i++)) ) {
3454 if( ! str_is_true( osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
3455 jsonObjectPush( flist, jsonNewObject( field ) );
3457 osrfStringArrayFree(keys);
3461 jsonIterator* class_itr = jsonNewIterator( selhash );
3462 while ( (snode = jsonIteratorNext( class_itr )) ) {
3464 char* cname = class_itr->key;
3465 osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
3466 if (!idlClass) continue;
3468 if (strcmp(core_class,class_itr->key)) {
3469 if (!join_hash) continue;
3471 jsonObject* found = jsonObjectFindPath(join_hash, "//%s", class_itr->key);
3473 jsonObjectFree(found);
3477 jsonObjectFree(found);
3480 jsonIterator* select_itr = jsonNewIterator( snode );
3481 while ( (node = jsonIteratorNext( select_itr )) ) {
3482 const char* item_str = jsonObjectGetString( node );
3483 osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), item_str );
3484 char* fname = osrfHashGet(field, "name");
3486 if (!field) continue;
3491 OSRF_BUFFER_ADD_CHAR(select_buf, ',');
3496 const jsonObject* no_i18n_obj = jsonObjectGetKey( order_hash, "no_i18n" );
3497 if ( obj_is_true( no_i18n_obj ) ) // Suppress internationalization?
3500 i18n = osrfHashGet(field, "i18n");
3502 if( str_is_true( i18n ) ) {
3503 char* pkey = osrfHashGet(idlClass, "primarykey");
3504 char* tname = osrfHashGet(idlClass, "tablename");
3506 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);
3508 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3511 buffer_fadd(select_buf, " \"%s\".%s", cname, fname);
3515 jsonIteratorFree(select_itr);
3518 jsonIteratorFree(class_itr);
3520 char* col_list = buffer_release(select_buf);
3521 char* table = getSourceDefinition(meta);
3523 table = strdup( "(null)" );
3525 buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\"", col_list, table, core_class );
3530 char* join_clause = searchJOIN( join_hash, meta );
3531 OSRF_BUFFER_ADD_CHAR(sql_buf, ' ');
3532 OSRF_BUFFER_ADD(sql_buf, join_clause);
3536 osrfLogDebug(OSRF_LOG_MARK, "%s pre-predicate SQL = %s",
3537 MODULENAME, OSRF_BUFFER_C_STR(sql_buf));
3539 buffer_add(sql_buf, " WHERE ");
3541 char* pred = searchWHERE( search_hash, meta, AND_OP_JOIN, ctx );
3543 osrfAppSessionStatus(
3545 OSRF_STATUS_INTERNALSERVERERROR,
3546 "osrfMethodException",
3548 "Severe query error -- see error log for more details"
3550 buffer_free(sql_buf);
3551 if(defaultselhash) jsonObjectFree(defaultselhash);
3554 buffer_add(sql_buf, pred);
3559 char* string = NULL;
3560 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "order_by" )) ){
3562 growing_buffer* order_buf = buffer_init(128);
3565 jsonIterator* class_itr = jsonNewIterator( _tmp );
3566 while ( (snode = jsonIteratorNext( class_itr )) ) {
3568 if (!jsonObjectGetKeyConst(selhash,class_itr->key))
3571 if ( snode->type == JSON_HASH ) {
3573 jsonIterator* order_itr = jsonNewIterator( snode );
3574 while ( (onode = jsonIteratorNext( order_itr )) ) {
3576 if (!oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ))
3579 char* direction = NULL;
3580 if ( onode->type == JSON_HASH ) {
3581 if ( jsonObjectGetKeyConst( onode, "transform" ) ) {
3582 string = searchFieldTransform(
3584 oilsIDLFindPath( "/%s/fields/%s", class_itr->key, order_itr->key ),
3588 growing_buffer* field_buf = buffer_init(16);
3589 buffer_fadd(field_buf, "\"%s\".%s", class_itr->key, order_itr->key);
3590 string = buffer_release(field_buf);
3593 if ( (_tmp = jsonObjectGetKeyConst( onode, "direction" )) ) {
3594 const char* dir = jsonObjectGetString(_tmp);
3595 if (!strncasecmp(dir, "d", 1)) {
3596 direction = " DESC";
3603 string = strdup(order_itr->key);
3604 const char* dir = jsonObjectGetString(onode);
3605 if (!strncasecmp(dir, "d", 1)) {
3606 direction = " DESC";
3615 buffer_add(order_buf, ", ");
3618 buffer_add(order_buf, string);
3622 buffer_add(order_buf, direction);
3627 jsonIteratorFree(order_itr);
3630 const char* str = jsonObjectGetString(snode);
3631 buffer_add(order_buf, str);
3637 jsonIteratorFree(class_itr);
3639 string = buffer_release(order_buf);
3642 OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
3643 OSRF_BUFFER_ADD( sql_buf, string );
3649 if ( (_tmp = jsonObjectGetKeyConst( order_hash, "limit" )) ){
3650 const char* str = jsonObjectGetString(_tmp);
3658 _tmp = jsonObjectGetKeyConst( order_hash, "offset" );
3660 const char* str = jsonObjectGetString(_tmp);
3669 if (defaultselhash) jsonObjectFree(defaultselhash);
3671 OSRF_BUFFER_ADD_CHAR(sql_buf, ';');
3672 return buffer_release(sql_buf);
3675 int doJSONSearch ( osrfMethodContext* ctx ) {
3676 if(osrfMethodVerifyContext( ctx )) {
3677 osrfLogError( OSRF_LOG_MARK, "Invalid method context" );
3681 osrfLogDebug(OSRF_LOG_MARK, "Recieved query request");
3686 dbhandle = writehandle;
3688 jsonObject* hash = jsonObjectGetIndex(ctx->params, 0);
3692 if ( obj_is_true( jsonObjectGetKey( hash, "distinct" ) ) )
3693 flags |= SELECT_DISTINCT;
3695 if ( obj_is_true( jsonObjectGetKey( hash, "no_i18n" ) ) )
3696 flags |= DISABLE_I18N;
3698 osrfLogDebug(OSRF_LOG_MARK, "Building SQL ...");
3701 jsonObjectGetKey( hash, "select" ),
3702 jsonObjectGetKey( hash, "from" ),
3703 jsonObjectGetKey( hash, "where" ),
3704 jsonObjectGetKey( hash, "having" ),
3705 jsonObjectGetKey( hash, "order_by" ),
3706 jsonObjectGetKey( hash, "limit" ),
3707 jsonObjectGetKey( hash, "offset" ),
3716 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
3717 dbi_result result = dbi_conn_query(dbhandle, sql);
3720 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
3722 if (dbi_result_first_row(result)) {
3723 /* JSONify the result */
3724 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
3727 jsonObject* return_val = oilsMakeJSONFromResult( result );
3728 osrfAppRespond( ctx, return_val );
3729 jsonObjectFree( return_val );
3730 } while (dbi_result_next_row(result));
3733 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
3736 osrfAppRespondComplete( ctx, NULL );
3738 /* clean up the query */
3739 dbi_result_free(result);
3743 osrfLogError(OSRF_LOG_MARK, "%s: Error with query [%s]", MODULENAME, sql);
3744 osrfAppSessionStatus(
3746 OSRF_STATUS_INTERNALSERVERERROR,
3747 "osrfMethodException",
3749 "Severe query error -- see error log for more details"
3757 static jsonObject* doFieldmapperSearch ( osrfMethodContext* ctx, osrfHash* meta,
3758 const jsonObject* params, int* err ) {
3761 dbhandle = writehandle;
3763 osrfHash* links = osrfHashGet(meta, "links");
3764 osrfHash* fields = osrfHashGet(meta, "fields");
3765 char* core_class = osrfHashGet(meta, "classname");
3766 char* pkey = osrfHashGet(meta, "primarykey");
3768 const jsonObject* _tmp;
3770 jsonObject* search_hash = jsonObjectGetIndex(params, 0);
3771 jsonObject* order_hash = jsonObjectGetIndex(params, 1);
3773 char* sql = buildSELECT( search_hash, order_hash, meta, ctx );
3775 osrfLogDebug(OSRF_LOG_MARK, "Problem building query, returning NULL");
3780 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
3782 dbi_result result = dbi_conn_query(dbhandle, sql);
3783 if( NULL == result ) {
3784 osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]",
3785 MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
3786 osrfAppSessionStatus(
3788 OSRF_STATUS_INTERNALSERVERERROR,
3789 "osrfMethodException",
3791 "Severe query error -- see error log for more details"
3798 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
3801 jsonObject* res_list = jsonNewObjectType(JSON_ARRAY);
3802 osrfHash* dedup = osrfNewHash();
3804 if (dbi_result_first_row(result)) {
3805 /* JSONify the result */
3806 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
3808 obj = oilsMakeFieldmapperFromResult( result, meta );
3809 char* pkey_val = oilsFMGetString( obj, pkey );
3810 if ( osrfHashGet( dedup, pkey_val ) ) {
3811 jsonObjectFree(obj);
3814 osrfHashSet( dedup, pkey_val, pkey_val );
3815 jsonObjectPush(res_list, obj);
3817 } while (dbi_result_next_row(result));
3819 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s",
3823 osrfHashFree(dedup);
3824 /* clean up the query */
3825 dbi_result_free(result);
3828 if (res_list->size && order_hash) {
3829 _tmp = jsonObjectGetKeyConst( order_hash, "flesh" );
3831 int x = (int)jsonObjectGetNumber(_tmp);
3832 if (x == -1 || x > max_flesh_depth) x = max_flesh_depth;
3834 const jsonObject* temp_blob;
3835 if ((temp_blob = jsonObjectGetKeyConst( order_hash, "flesh_fields" )) && x > 0) {
3837 jsonObject* flesh_blob = jsonObjectClone( temp_blob );
3838 const jsonObject* flesh_fields = jsonObjectGetKeyConst( flesh_blob, core_class );
3840 osrfStringArray* link_fields = NULL;
3843 if (flesh_fields->size == 1) {
3844 const char* _t = jsonObjectGetString( jsonObjectGetIndex( flesh_fields, 0 ) );
3845 if (!strcmp(_t,"*")) link_fields = osrfHashKeys( links );
3850 link_fields = osrfNewStringArray(1);
3851 jsonIterator* _i = jsonNewIterator( flesh_fields );
3852 while ((_f = jsonIteratorNext( _i ))) {
3853 osrfStringArrayAdd( link_fields, jsonObjectGetString( _f ) );
3855 jsonIteratorFree(_i);
3860 jsonIterator* itr = jsonNewIterator( res_list );
3861 while ((cur = jsonIteratorNext( itr ))) {
3866 while ( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
3868 osrfLogDebug(OSRF_LOG_MARK, "Starting to flesh %s", link_field);
3870 osrfHash* kid_link = osrfHashGet(links, link_field);
3871 if (!kid_link) continue;
3873 osrfHash* field = osrfHashGet(fields, link_field);
3874 if (!field) continue;
3876 osrfHash* value_field = field;
3878 osrfHash* kid_idl = osrfHashGet(oilsIDL(), osrfHashGet(kid_link, "class"));
3879 if (!kid_idl) continue;
3881 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
3882 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
3885 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) { // might_have
3886 value_field = osrfHashGet( fields, osrfHashGet(meta, "primarykey") );
3889 osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
3891 if (link_map->size > 0) {
3892 jsonObject* _kid_key = jsonNewObjectType(JSON_ARRAY);
3895 jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
3900 osrfHashGet(kid_link, "class"),
3907 "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
3908 osrfHashGet(kid_link, "field"),
3909 osrfHashGet(kid_link, "class"),
3910 osrfHashGet(kid_link, "key"),
3911 osrfHashGet(kid_link, "reltype")
3914 jsonObject* fake_params = jsonNewObjectType(JSON_ARRAY);
3915 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH)); // search hash
3916 jsonObjectPush(fake_params, jsonNewObjectType(JSON_HASH)); // order/flesh hash
3918 osrfLogDebug(OSRF_LOG_MARK, "Creating dummy params object...");
3920 const char* search_key = jsonObjectGetString(
3923 atoi( osrfHashGet(value_field, "array_position") )
3928 osrfLogDebug(OSRF_LOG_MARK, "Nothing to search for!");
3933 jsonObjectGetIndex(fake_params, 0),
3934 osrfHashGet(kid_link, "key"),
3935 jsonNewObject( search_key )
3939 jsonObjectGetIndex(fake_params, 1),
3941 jsonNewNumberObject( (double)(x - 1 + link_map->size) )
3945 jsonObjectSetKey( jsonObjectGetIndex(fake_params, 1), "flesh_fields", jsonObjectClone(flesh_blob) );
3947 if (jsonObjectGetKeyConst(order_hash, "order_by")) {
3949 jsonObjectGetIndex(fake_params, 1),
3951 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "order_by"))
3955 if (jsonObjectGetKeyConst(order_hash, "select")) {
3957 jsonObjectGetIndex(fake_params, 1),
3959 jsonObjectClone(jsonObjectGetKeyConst(order_hash, "select"))
3963 jsonObject* kids = doFieldmapperSearch(ctx, kid_idl, fake_params, err);
3966 jsonObjectFree( fake_params );
3967 osrfStringArrayFree(link_fields);
3968 jsonIteratorFree(itr);
3969 jsonObjectFree(res_list);
3970 jsonObjectFree(flesh_blob);
3974 osrfLogDebug(OSRF_LOG_MARK, "Search for %s return %d linked objects", osrfHashGet(kid_link, "class"), kids->size);
3976 jsonObject* X = NULL;
3977 if ( link_map->size > 0 && kids->size > 0 ) {
3979 kids = jsonNewObjectType(JSON_ARRAY);
3981 jsonObject* _k_node;
3982 jsonIterator* _k = jsonNewIterator( X );
3983 while ((_k_node = jsonIteratorNext( _k ))) {
3989 (unsigned long)atoi(
3995 osrfHashGet(kid_link, "class")
3999 osrfStringArrayGetString( link_map, 0 )
4008 jsonIteratorFree(_k);
4011 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_a" )) || !(strcmp( osrfHashGet(kid_link, "reltype"), "might_have" ))) {
4012 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
4015 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
4016 jsonObjectClone( jsonObjectGetIndex(kids, 0) )
4020 if (!(strcmp( osrfHashGet(kid_link, "reltype"), "has_many" ))) { // has_many
4021 osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s", osrfHashGet(kid_link, "field"));
4024 (unsigned long)atoi( osrfHashGet( field, "array_position" ) ),
4025 jsonObjectClone( kids )
4030 jsonObjectFree(kids);
4034 jsonObjectFree( kids );
4035 jsonObjectFree( fake_params );
4037 osrfLogDebug(OSRF_LOG_MARK, "Fleshing of %s complete", osrfHashGet(kid_link, "field"));
4038 osrfLogDebug(OSRF_LOG_MARK, "%s", jsonObjectToJSON(cur));
4042 jsonObjectFree( flesh_blob );
4043 osrfStringArrayFree(link_fields);
4044 jsonIteratorFree(itr);
4053 static jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) {
4055 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4057 jsonObject* target = jsonObjectGetIndex( ctx->params, 1 );
4059 jsonObject* target = jsonObjectGetIndex( ctx->params, 0 );
4062 if (!verifyObjectClass(ctx, target)) {
4067 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4068 osrfAppSessionStatus(
4070 OSRF_STATUS_BADREQUEST,
4071 "osrfMethodException",
4073 "No active transaction -- required for UPDATE"
4079 // The following test is harmless but redundant. If a class is
4080 // readonly, we don't register an update method for it.
4081 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4082 osrfAppSessionStatus(
4084 OSRF_STATUS_BADREQUEST,
4085 "osrfMethodException",
4087 "Cannot UPDATE readonly class"
4093 dbhandle = writehandle;
4095 char* trans_id = osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" );
4097 // Set the last_xact_id
4098 int index = oilsIDL_ntop( target->classname, "last_xact_id" );
4100 osrfLogDebug(OSRF_LOG_MARK, "Setting last_xact_id to %s on %s at position %d", trans_id, target->classname, index);
4101 jsonObjectSetIndex(target, index, jsonNewObject(trans_id));
4104 char* pkey = osrfHashGet(meta, "primarykey");
4105 osrfHash* fields = osrfHashGet(meta, "fields");
4107 char* id = oilsFMGetString( target, pkey );
4111 "%s updating %s object with %s = %s",
4113 osrfHashGet(meta, "fieldmapper"),
4118 growing_buffer* sql = buffer_init(128);
4119 buffer_fadd(sql,"UPDATE %s SET", osrfHashGet(meta, "tablename"));
4124 osrfStringArray* field_list = osrfHashKeys( fields );
4125 while ( (field_name = osrfStringArrayGetString(field_list, i++)) ) {
4127 osrfHash* field = osrfHashGet( fields, field_name );
4129 if(!( strcmp( field_name, pkey ) )) continue;
4130 if( str_is_true( osrfHashGet(osrfHashGet(fields,field_name), "virtual") ) )
4133 const jsonObject* field_object = oilsFMGetObject( target, field_name );
4135 int value_is_numeric = 0; // boolean
4137 if (field_object && field_object->classname) {
4138 value = oilsFMGetString(
4140 (char*)oilsIDLFindPath("/%s/primarykey", field_object->classname)
4143 value = jsonObjectToSimpleString( field_object );
4144 if( field_object && JSON_NUMBER == field_object->type )
4145 value_is_numeric = 1;
4148 osrfLogDebug( OSRF_LOG_MARK, "Updating %s object with %s = %s", osrfHashGet(meta, "fieldmapper"), field_name, value);
4150 if (!field_object || field_object->type == JSON_NULL) {
4151 if ( !(!( strcmp( osrfHashGet(meta, "classname"), "au" ) ) && !( strcmp( field_name, "passwd" ) )) ) { // arg at the special case!
4152 if (first) first = 0;
4153 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4154 buffer_fadd( sql, " %s = NULL", field_name );
4157 } else if ( value_is_numeric || !strcmp( get_primitive( field ), "number") ) {
4158 if (first) first = 0;
4159 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4161 const char* numtype = get_datatype( field );
4162 if ( !strncmp( numtype, "INT", 3 ) ) {
4163 buffer_fadd( sql, " %s = %ld", field_name, atol(value) );
4164 } else if ( !strcmp( numtype, "NUMERIC" ) ) {
4165 buffer_fadd( sql, " %s = %f", field_name, atof(value) );
4167 // Must really be intended as a string, so quote it
4168 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4169 buffer_fadd( sql, " %s = %s", field_name, value );
4171 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4172 osrfAppSessionStatus(
4174 OSRF_STATUS_INTERNALSERVERERROR,
4175 "osrfMethodException",
4177 "Error quoting string -- please see the error log for more details"
4187 osrfLogDebug( OSRF_LOG_MARK, "%s is of type %s", field_name, numtype );
4190 if ( dbi_conn_quote_string(dbhandle, &value) ) {
4191 if (first) first = 0;
4192 else OSRF_BUFFER_ADD_CHAR(sql, ',');
4193 buffer_fadd( sql, " %s = %s", field_name, value );
4196 osrfLogError(OSRF_LOG_MARK, "%s: Error quoting string [%s]", MODULENAME, value);
4197 osrfAppSessionStatus(
4199 OSRF_STATUS_INTERNALSERVERERROR,
4200 "osrfMethodException",
4202 "Error quoting string -- please see the error log for more details"
4216 jsonObject* obj = jsonNewObject(id);
4218 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4219 dbi_conn_quote_string(dbhandle, &id);
4221 buffer_fadd( sql, " WHERE %s = %s;", pkey, id );
4223 char* query = buffer_release(sql);
4224 osrfLogDebug(OSRF_LOG_MARK, "%s: Update SQL [%s]", MODULENAME, query);
4226 dbi_result result = dbi_conn_query(dbhandle, query);
4230 jsonObjectFree(obj);
4231 obj = jsonNewObject(NULL);
4234 "%s ERROR updating %s object with %s = %s",
4236 osrfHashGet(meta, "fieldmapper"),
4247 static jsonObject* doDelete(osrfMethodContext* ctx, int* err ) {
4249 osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
4251 if (!osrfHashGet( (osrfHash*)ctx->session->userData, "xact_id" )) {
4252 osrfAppSessionStatus(
4254 OSRF_STATUS_BADREQUEST,
4255 "osrfMethodException",
4257 "No active transaction -- required for DELETE"
4263 // The following test is harmless but redundant. If a class is
4264 // readonly, we don't register a delete method for it.
4265 if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
4266 osrfAppSessionStatus(
4268 OSRF_STATUS_BADREQUEST,
4269 "osrfMethodException",
4271 "Cannot DELETE readonly class"
4277 dbhandle = writehandle;
4281 char* pkey = osrfHashGet(meta, "primarykey");
4289 if (jsonObjectGetIndex(ctx->params, _obj_pos)->classname) {
4290 if (!verifyObjectClass(ctx, jsonObjectGetIndex( ctx->params, _obj_pos ))) {
4295 id = oilsFMGetString( jsonObjectGetIndex(ctx->params, _obj_pos), pkey );
4298 if (!verifyObjectPCRUD( ctx, NULL )) {
4303 id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, _obj_pos));
4308 "%s deleting %s object with %s = %s",
4310 osrfHashGet(meta, "fieldmapper"),
4315 obj = jsonNewObject(id);
4317 if ( strcmp( get_primitive( osrfHashGet( osrfHashGet(meta, "fields"), pkey ) ), "number" ) )
4318 dbi_conn_quote_string(writehandle, &id);
4320 dbi_result result = dbi_conn_queryf(writehandle, "DELETE FROM %s WHERE %s = %s;", osrfHashGet(meta, "tablename"), pkey, id);
4323 jsonObjectFree(obj);
4324 obj = jsonNewObject(NULL);
4327 "%s ERROR deleting %s object with %s = %s",
4329 osrfHashGet(meta, "fieldmapper"),
4342 static jsonObject* oilsMakeFieldmapperFromResult( dbi_result result, osrfHash* meta) {
4343 if(!(result && meta)) return jsonNULL;
4345 jsonObject* object = jsonNewObject(NULL);
4346 jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
4348 osrfHash* fields = osrfHashGet(meta, "fields");
4350 osrfLogInternal(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
4354 char dt_string[256];
4358 int columnIndex = 1;
4360 unsigned short type;
4361 const char* columnName;
4363 /* cycle through the column list */
4364 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4366 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4368 fmIndex = -1; // reset the position
4370 /* determine the field type and storage attributes */
4371 type = dbi_result_get_field_type(result, columnName);
4372 attr = dbi_result_get_field_attribs(result, columnName);
4374 /* fetch the fieldmapper index */
4375 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
4377 if ( str_is_true( osrfHashGet(_f, "virtual") ) )
4380 const char* pos = (char*)osrfHashGet(_f, "array_position");
4381 if ( !pos ) continue;
4383 fmIndex = atoi( pos );
4384 osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
4389 if (dbi_result_field_is_null(result, columnName)) {
4390 jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
4395 case DBI_TYPE_INTEGER :
4397 if( attr & DBI_INTEGER_SIZE8 )
4398 jsonObjectSetIndex( object, fmIndex,
4399 jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
4401 jsonObjectSetIndex( object, fmIndex,
4402 jsonNewNumberObject(dbi_result_get_int(result, columnName)));
4406 case DBI_TYPE_DECIMAL :
4407 jsonObjectSetIndex( object, fmIndex,
4408 jsonNewNumberObject(dbi_result_get_double(result, columnName)));
4411 case DBI_TYPE_STRING :
4417 jsonNewObject( dbi_result_get_string(result, columnName) )
4422 case DBI_TYPE_DATETIME :
4424 memset(dt_string, '\0', sizeof(dt_string));
4425 memset(&gmdt, '\0', sizeof(gmdt));
4427 _tmp_dt = dbi_result_get_datetime(result, columnName);
4430 if (!(attr & DBI_DATETIME_DATE)) {
4431 gmtime_r( &_tmp_dt, &gmdt );
4432 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4433 } else if (!(attr & DBI_DATETIME_TIME)) {
4434 localtime_r( &_tmp_dt, &gmdt );
4435 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4437 localtime_r( &_tmp_dt, &gmdt );
4438 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4441 jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
4445 case DBI_TYPE_BINARY :
4446 osrfLogError( OSRF_LOG_MARK,
4447 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4455 static jsonObject* oilsMakeJSONFromResult( dbi_result result ) {
4456 if(!result) return jsonNULL;
4458 jsonObject* object = jsonNewObject(NULL);
4461 char dt_string[256];
4465 int columnIndex = 1;
4467 unsigned short type;
4468 const char* columnName;
4470 /* cycle through the column list */
4471 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
4473 osrfLogInternal(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
4475 fmIndex = -1; // reset the position
4477 /* determine the field type and storage attributes */
4478 type = dbi_result_get_field_type(result, columnName);
4479 attr = dbi_result_get_field_attribs(result, columnName);
4481 if (dbi_result_field_is_null(result, columnName)) {
4482 jsonObjectSetKey( object, columnName, jsonNewObject(NULL) );
4487 case DBI_TYPE_INTEGER :
4489 if( attr & DBI_INTEGER_SIZE8 )
4490 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_longlong(result, columnName)) );
4492 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_int(result, columnName)) );
4495 case DBI_TYPE_DECIMAL :
4496 jsonObjectSetKey( object, columnName, jsonNewNumberObject(dbi_result_get_double(result, columnName)) );
4499 case DBI_TYPE_STRING :
4500 jsonObjectSetKey( object, columnName, jsonNewObject(dbi_result_get_string(result, columnName)) );
4503 case DBI_TYPE_DATETIME :
4505 memset(dt_string, '\0', sizeof(dt_string));
4506 memset(&gmdt, '\0', sizeof(gmdt));
4508 _tmp_dt = dbi_result_get_datetime(result, columnName);
4511 if (!(attr & DBI_DATETIME_DATE)) {
4512 gmtime_r( &_tmp_dt, &gmdt );
4513 strftime(dt_string, sizeof(dt_string), "%T", &gmdt);
4514 } else if (!(attr & DBI_DATETIME_TIME)) {
4515 localtime_r( &_tmp_dt, &gmdt );
4516 strftime(dt_string, sizeof(dt_string), "%F", &gmdt);
4518 localtime_r( &_tmp_dt, &gmdt );
4519 strftime(dt_string, sizeof(dt_string), "%FT%T%z", &gmdt);
4522 jsonObjectSetKey( object, columnName, jsonNewObject(dt_string) );
4525 case DBI_TYPE_BINARY :
4526 osrfLogError( OSRF_LOG_MARK,
4527 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);
4535 // Interpret a string as true or false
4536 static int str_is_true( const char* str ) {
4537 if( NULL == str || strcasecmp( str, "true" ) )
4543 // Interpret a jsonObject as true or false
4544 static int obj_is_true( const jsonObject* obj ) {
4547 else switch( obj->type )
4555 if( strcasecmp( obj->value.s, "true" ) )
4559 case JSON_NUMBER : // Support 1/0 for perl's sake
4560 if( jsonObjectGetNumber( obj ) == 1.0 )
4569 // Translate a numeric code into a text string identifying a type of
4570 // jsonObject. To be used for building error messages.
4571 static const char* json_type( int code ) {
4577 return "JSON_ARRAY";
4579 return "JSON_STRING";
4581 return "JSON_NUMBER";
4587 return "(unrecognized)";
4591 // Extract the "primitive" attribute from an IDL field definition.
4592 // If we haven't initialized the app, then we must be running in
4593 // some kind of testbed. In that case, default to "string".
4594 static const char* get_primitive( osrfHash* field ) {
4595 const char* s = osrfHashGet( field, "primitive" );
4597 if( child_initialized )
4600 "%s ERROR No \"datatype\" attribute for field \"%s\"",
4602 osrfHashGet( field, "name" )
4610 // Extract the "datatype" attribute from an IDL field definition.
4611 // If we haven't initialized the app, then we must be running in
4612 // some kind of testbed. In that case, default to to NUMERIC,
4613 // since we look at the datatype only for numbers.
4614 static const char* get_datatype( osrfHash* field ) {
4615 const char* s = osrfHashGet( field, "datatype" );
4617 if( child_initialized )
4620 "%s ERROR No \"datatype\" attribute for field \"%s\"",
4622 osrfHashGet( field, "name" )
4631 If the input string is potentially a valid SQL identifier, return 1.
4634 Purpose: to prevent certain kinds of SQL injection. To that end we
4635 don't necessarily need to follow all the rules exactly, such as requiring
4636 that the first character not be a digit.
4638 We allow leading and trailing white space. In between, we do not allow
4639 punctuation (except for underscores and dollar signs), control
4640 characters, or embedded white space.
4642 More pedantically we should allow quoted identifiers containing arbitrary
4643 characters, but for the foreseeable future such quoted identifiers are not
4644 likely to be an issue.
4646 static int is_identifier( const char* s) {
4650 // Skip leading white space
4651 while( isspace( (unsigned char) *s ) )
4655 return 0; // Nothing but white space? Not okay.
4657 // Check each character until we reach white space or
4658 // end-of-string. Letters, digits, underscores, and
4659 // dollar signs are okay. Control characters and other
4660 // punctuation characters are not okay. Anything else
4661 // is okay -- it could for example be part of a multibyte
4662 // UTF8 character such as a letter with diacritical marks,
4663 // and those are allowed.
4665 if( isalnum( (unsigned char) *s )
4668 ; // Fine; keep going
4669 else if( ispunct( (unsigned char) *s )
4670 || iscntrl( (unsigned char) *s ) )
4673 } while( *s && ! isspace( (unsigned char) *s ) );
4675 // If we found any white space in the above loop,
4676 // the rest had better be all white space.
4678 while( isspace( (unsigned char) *s ) )
4682 return 0; // White space was embedded within non-white space