-- LegacyWorlds Beta 6 -- PostgreSQL database scripts -- -- Functions that read event data and update event -- states on behalf of event viewing / processing -- interfaces. -- -- Copyright(C) 2004-2012, DeepClone Development -- -------------------------------------------------------- /* * SOME GENERAL DOCUMENTATION * -------------------------- * * This file provides a few functions the server can use to access the events: * * The events.ep_read() function variants provide read-only access to the * list. They may be used with or without a target (whether that target is an * user or an empire), and they can be given an offset and a limit if partial * selection is desired. ep_read() accepts the following parameters ("+" * means that the parameter is required). * * + access interface identifier * target identifier * target is empire? * + access processed events? * + access preprocessed events? * + access new events? * offset of first event * maximal amount of events * * The events.ep_access() function variants provide the same service, but will * additionally update the events' state after reading them. ep_access() * uses the same parameters as ep_read(), but adds two new mandatory parameters * at the end: * * + mark preprocessed events as processed? * + state to set for new events */ /* * Insert new events in unprocessed list * ------------------------------------- * * This trigger function is used to insert new events into the list of * unprocessed events for all interface types. */ DROP FUNCTION IF EXISTS events.tgf_insert_unprocessed( ) CASCADE; CREATE FUNCTION events.tgf_insert_unprocessed( ) RETURNS TRIGGER LANGUAGE PLPGSQL STRICT VOLATILE SECURITY DEFINER AS $tgf_insert_unprocessed$ BEGIN INSERT INTO events.unprocessed_events( event_id , evi_id ) SELECT NEW.event_id , evi_id FROM events.interfaces; RETURN NEW; END; $tgf_insert_unprocessed$; REVOKE EXECUTE ON FUNCTION events.tgf_insert_unprocessed( ) FROM PUBLIC; CREATE TRIGGER tg_insert_unprocessed AFTER INSERT ON events.events_v2 FOR EACH ROW EXECUTE PROCEDURE events.tgf_insert_unprocessed( ); /* * Event processing states * ----------------------- * * This type serves as an indication of event processing state. * * Note: this type is not used for storage, only for access. */ DROP TYPE IF EXISTS events.processing_state CASCADE; CREATE TYPE events.processing_state AS ENUM( /* The event is new and hasn't been processed yet */ 'NEW' , /* The event has been preprocessed */ 'PRE' , /* The event has been fully processed */ 'DONE' ); /* * Event list entry * ---------------- * * This type corresponds to entries of the event lists returned by * events.ep_list_events(). */ DROP TYPE IF EXISTS events.ep_list_entry CASCADE; CREATE TYPE events.ep_list_entry AS ( /* The event's identifier */ event_id BIGINT , /* The event type */ evdef_id TEXT , /* The user's identifier */ empire_id INT , /* The event's processing state */ event_state events.processing_state ); /* * Build the events listing query * ------------------------------ * * /!\ INTERNAL FUNCTION /!\ * * This procedure generates the query string used to extract events for a given * set of parameters. * * Parameters: * _check_emp TRUE if the query ought to check the events' target * empire, FALSE if all empires should be returned * _accept_old Whether processed events should be selected * _accept_pre Whether preprocessed events should be selected * _accept_new Whether unprocessed events should be selected * * Returns: * ??? The query string */ DROP FUNCTION IF EXISTS events.ep_make_list_query( BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN ); CREATE FUNCTION events.ep_make_list_query( _check_emp BOOLEAN , _accept_old BOOLEAN , _accept_pre BOOLEAN , _accept_new BOOLEAN ) RETURNS TEXT LANGUAGE PLPGSQL STRICT IMMUTABLE SECURITY INVOKER AS $ep_make_list_query$ DECLARE _qstr TEXT; _wstr TEXT; _need_or BOOLEAN; BEGIN IF NOT ( _accept_old OR _accept_pre OR _accept_new ) THEN RAISE EXCEPTION 'No events accepted'; END IF; _qstr := $sel_query$ SELECT _evt.event_id ,_evt.evdef_id , _evt.empire_id , ( CASE WHEN _upe.upe_preprocessed IS NULL THEN 'DONE' WHEN _upe.upe_preprocessed THEN 'PRE' ELSE 'NEW' END )::events.processing_state FROM events.events_v2 _evt LEFT OUTER JOIN events.unprocessed_events _upe ON _upe.event_id = _evt.event_id AND _upe.evi_id = $1 $sel_query$; IF _check_emp THEN _wstr := '_evt.empire_id = $2'; ELSE _wstr := ''; END IF; IF NOT ( _accept_old AND _accept_pre AND _accept_new ) THEN IF _wstr <> '' THEN _wstr := _wstr || ' AND '; END IF; _wstr := _wstr || '('; _need_or := FALSE; IF _accept_old THEN _wstr := _wstr || '_upe.event_id IS NULL'; _need_or := TRUE; END IF; IF _accept_pre THEN IF _need_or THEN _wstr := _wstr || ' OR '; ELSE _need_or := TRUE; END IF; _wstr := _wstr || '_upe.upe_preprocessed'; END IF; IF _accept_new THEN IF _need_or THEN _wstr := _wstr || ' OR '; END IF; _wstr := _wstr || 'NOT _upe.upe_preprocessed'; END IF; _wstr := _wstr || ')'; END IF; IF _wstr <> '' THEN _qstr := _qstr || ' WHERE ' || _wstr; END IF; _qstr := _qstr || ' ORDER BY _evt.event_id DESC'; RETURN _qstr; END; $ep_make_list_query$; REVOKE EXECUTE ON FUNCTION events.ep_make_list_query( BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN ) FROM PUBLIC; /* * List events * ----------- * * /!\ INTERNAL FUNCTION /!\ * * This procedure generates a list of events for some type of interface. * * Parameters: * _evi_id Identifier of the interface accessing events * _empire_id The identifier of the empire to get events for (may be NULL) * _accept_old Whether processed events should be selected * _accept_pre Whether preprocessed events should be selected * _accept_new Whether unprocessed events should be selected * * Returns: * the set of events */ DROP FUNCTION IF EXISTS events.ep_list_events( TEXT , INT , BOOLEAN , BOOLEAN , BOOLEAN ); CREATE FUNCTION events.ep_list_events( _evi_id TEXT , _empire_id INT , _accept_old BOOLEAN , _accept_pre BOOLEAN , _accept_new BOOLEAN ) RETURNS SETOF events.ep_list_entry LANGUAGE PLPGSQL CALLED ON NULL INPUT STABLE SECURITY INVOKER AS $ep_list_events$ DECLARE _record events.ep_list_entry; BEGIN FOR _record IN EXECUTE events.ep_make_list_query( _empire_id IS NOT NULL , _accept_old , _accept_pre , _accept_new ) USING _evi_id , _empire_id LOOP RETURN NEXT _record; END LOOP; END; $ep_list_events$; REVOKE EXECUTE ON FUNCTION events.ep_list_events( TEXT , INT , BOOLEAN , BOOLEAN , BOOLEAN ) FROM PUBLIC; DROP TYPE IF EXISTS events.event_list_type CASCADE; CREATE TYPE events.event_list_type AS ( /* Identifier of the empire to which the event applies */ empire_id INT , /* Identifier of the user who controls the empire */ user_id INT , /* Selected language for the empire */ lang_id TEXT , /* Numeric identifier of the event */ event_id BIGINT , /* The event's processing state */ event_state events.processing_state , /* Real time at which the event occurred */ event_rtime TIMESTAMP WITHOUT TIME ZONE , /* Game time (tick identifier) at which the event occurred */ event_gtime BIGINT , /* Event priority from either the definition or the custom priorities */ event_priority INT , /* Type of the event */ evdef_id TEXT , /* Internationalised string that contains the name of the event type; * used when displaying priority settings. */ evdef_name TEXT , /* Internationalised string that contains the template to use when * generating the output for a single event. */ evdef_template TEXT , /* Field name */ efdef_id TEXT , /* Field value */ field_value TEXT ); /* * Main events listing function * ---------------------------- * * /!\ INTERNAL FUNCTION /!\ * * This function contains the main code for event access. It is capable of * selecting events based on their target and/or state, can limit the amount * of events fetched or fetch them starting from some offset, and is capable * of updating event states for selected events. * * Parameters: * _evi_id The identifier of the interface accessing the events list * _target_id An empire or user identifier (may be NULL) * _target_is_emp Whether the specified identifier is an empire (TRUE) or an * user. May be NULL. * _accept_old Whether processed events should be selected * _accept_pre Whether preprocessed events should be selected * _accept_new Whether unprocessed events should be selected * _offset Offset of the first event to obtain in the list. May be * NULL. * _limit Maximal amount of events to list. May be NULL. * _clear_pre If TRUE, selected events that had been preprocessed will * be marked as fully processed. May be NULL. * _change_new The state to which new events should be set. May be NULL * or 'NEW' if no changes are to be made. * * Returns: * A set of events.event_list_type records */ DROP FUNCTION IF EXISTS events.ep_get_events_internal( TEXT , INT , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN , INT , INT , BOOLEAN , events.processing_state ) CASCADE; CREATE FUNCTION events.ep_get_events_internal( _evi_id TEXT , _target_id INT , _target_is_emp BOOLEAN , _accept_old BOOLEAN , _accept_pre BOOLEAN , _accept_new BOOLEAN , _offset INT , _limit INT , _clear_pre BOOLEAN , _change_new events.processing_state ) RETURNS SETOF events.event_list_type LANGUAGE PLPGSQL CALLED ON NULL INPUT VOLATILE SECURITY INVOKER AS $ep_get_events_internal$ DECLARE _sel_query TEXT; _empire_id INT; _record events.event_list_type; BEGIN -- Get empire identifier if necessary IF NOT _target_is_emp THEN SELECT INTO _empire_id _emp.name_id FROM emp.empires _emp INNER JOIN naming.empire_names _emp_name ON _emp.name_id = _emp_name.id WHERE _emp_name.owner_id = _target_id; IF NOT FOUND THEN RETURN; END IF; ELSE _empire_id := _target_id; END IF; -- Generate event selection query _sel_query := $sel_query$ CREATE TEMPORARY TABLE ep_selected ON COMMIT DROP AS SELECT * FROM events.ep_list_events( $1 , $2 , $3 , $4 , $5 ) $sel_query$; IF _offset >= 0 THEN _sel_query := _sel_query || ' OFFSET ' || _offset; END IF; IF _limit > 0 THEN _sel_query := _sel_query || ' LIMIT ' || _limit; END IF; -- Select events EXECUTE _sel_query USING _evi_id , _empire_id , _accept_old , _accept_pre , _accept_new; -- Update event states if necessary IF _clear_pre THEN DELETE FROM events.unprocessed_events WHERE event_id IN ( SELECT event_id FROM ep_selected ) AND upe_preprocessed AND evi_id = _evi_id; END IF; IF _change_new = 'DONE' THEN DELETE FROM events.unprocessed_events WHERE event_id IN ( SELECT event_id FROM ep_selected ) AND NOT upe_preprocessed AND evi_id = _evi_id; ELSIF _change_new = 'PRE' THEN UPDATE events.unprocessed_events _upe SET upe_preprocessed = TRUE FROM ep_selected _sel WHERE _sel.event_id = _upe.event_id AND NOT _upe.upe_preprocessed AND _upe.evi_id = _evi_id; END IF; -- Select and return event data FOR _record IN SELECT _sel.empire_id , _emp_name.owner_id AS user_id , _lg.language AS lang_id , _sel.event_id , _sel.event_state , _ev.event_rtime , _ev.event_gtime , ( CASE WHEN _cp.evcp_priority IS NULL THEN _evdef.evdef_priority ELSE _cp.evcp_priority END ) AS event_priority , _evdef.evdef_id , _edns.name AS evdef_name , _edts.name AS evdef_template , _fval.efdef_id , _fval.efval_litteral AS field_value FROM ep_selected _sel INNER JOIN events.events_v2 _ev USING ( event_id , evdef_id ) INNER JOIN events.event_definitions _evdef USING ( evdef_id ) INNER JOIN naming.empire_names _emp_name ON _emp_name.id = _sel.empire_id INNER JOIN users.credentials _user ON _user.address_id = _emp_name.owner_id INNER JOIN defs.languages _lg ON _lg.id = _user.language_id INNER JOIN defs.strings _edns ON _edns.id = _evdef.evdef_name_id INNER JOIN defs.strings _edts ON _edts.id = _evdef.evdef_template_id LEFT OUTER JOIN events.field_values _fval USING ( event_id , evdef_id ) LEFT OUTER JOIN events.custom_priorities _cp USING ( evdef_id , evdef_adjustable , address_id ) LOOP RETURN NEXT _record; END LOOP; DROP TABLE ep_selected; END; $ep_get_events_internal$; REVOKE EXECUTE ON FUNCTION events.ep_get_events_internal( TEXT , INT , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN , INT , INT , BOOLEAN , events.processing_state ) FROM PUBLIC; /* * READ-ONLY EVENT ACCESS WRAPPERS */ DROP FUNCTION IF EXISTS events.ep_read( TEXT, BOOLEAN , BOOLEAN , BOOLEAN ); CREATE FUNCTION events.ep_read( _evi_id TEXT , _accept_old BOOLEAN , _accept_pre BOOLEAN , _accept_new BOOLEAN ) RETURNS SETOF events.event_list_type LANGUAGE SQL STRICT VOLATILE SECURITY DEFINER AS $ep_read$ SELECT * FROM events.ep_get_events_internal( $1 , NULL , NULL , $2 , $3 , $4 , NULL , NULL , NULL , NULL ); $ep_read$; REVOKE EXECUTE ON FUNCTION events.ep_read( TEXT , BOOLEAN , BOOLEAN , BOOLEAN ) FROM PUBLIC; GRANT EXECUTE ON FUNCTION events.ep_read( TEXT , BOOLEAN , BOOLEAN , BOOLEAN ) TO :dbuser; DROP FUNCTION IF EXISTS events.ep_read( TEXT , INT , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN ); CREATE FUNCTION events.ep_read( _evi_id TEXT , _target_id INT , _target_is_emp BOOLEAN , _accept_old BOOLEAN , _accept_pre BOOLEAN , _accept_new BOOLEAN ) RETURNS SETOF events.event_list_type LANGUAGE SQL STRICT VOLATILE SECURITY DEFINER AS $ep_read$ SELECT * FROM events.ep_get_events_internal( $1 , $2 , $3 , $4 , $5 , $6 , NULL , NULL , NULL , NULL ); $ep_read$; REVOKE EXECUTE ON FUNCTION events.ep_read( TEXT , INT , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN ) FROM PUBLIC; GRANT EXECUTE ON FUNCTION events.ep_read( TEXT , INT , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN ) TO :dbuser; DROP FUNCTION IF EXISTS events.ep_read( TEXT, BOOLEAN , BOOLEAN , BOOLEAN , INT , INT ); CREATE FUNCTION events.ep_read( _evi_id TEXT , _accept_old BOOLEAN , _accept_pre BOOLEAN , _accept_new BOOLEAN , _offset INT , _limit INT ) RETURNS SETOF events.event_list_type LANGUAGE SQL STRICT VOLATILE SECURITY DEFINER AS $ep_read$ SELECT * FROM events.ep_get_events_internal( $1 , NULL , NULL , $2 , $3 , $4 , $5 , $6 , NULL , NULL ); $ep_read$; REVOKE EXECUTE ON FUNCTION events.ep_read( TEXT , BOOLEAN , BOOLEAN , BOOLEAN , INT , INT ) FROM PUBLIC; GRANT EXECUTE ON FUNCTION events.ep_read( TEXT , BOOLEAN , BOOLEAN , BOOLEAN , INT , INT ) TO :dbuser; DROP FUNCTION IF EXISTS events.ep_read( TEXT , INT , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN , INT , INT ); CREATE FUNCTION events.ep_read( _evi_id TEXT , _target_id INT , _target_is_emp BOOLEAN , _accept_old BOOLEAN , _accept_pre BOOLEAN , _accept_new BOOLEAN , _offset INT , _limit INT ) RETURNS SETOF events.event_list_type LANGUAGE SQL STRICT VOLATILE SECURITY DEFINER AS $ep_read$ SELECT * FROM events.ep_get_events_internal( $1 , $2 , $3 , $4 , $5 , $6 , $7 , $8 , NULL , NULL ); $ep_read$; REVOKE EXECUTE ON FUNCTION events.ep_read( TEXT , INT , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN , INT , INT ) FROM PUBLIC; GRANT EXECUTE ON FUNCTION events.ep_read( TEXT , INT , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN , INT , INT ) TO :dbuser; /* * EVENT ACCESS WRAPPERS WITH UPDATE CAPABILITIES */ DROP FUNCTION IF EXISTS events.ep_access( TEXT, BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN , events.processing_state ); CREATE FUNCTION events.ep_access( _evi_id TEXT , _accept_old BOOLEAN , _accept_pre BOOLEAN , _accept_new BOOLEAN , _clear_pre BOOLEAN , _change_new events.processing_state ) RETURNS SETOF events.event_list_type LANGUAGE SQL STRICT VOLATILE SECURITY DEFINER AS $ep_access$ SELECT * FROM events.ep_get_events_internal( $1 , NULL , NULL , $2 , $3 , $4 , NULL , NULL , $5 , $6 ); $ep_access$; REVOKE EXECUTE ON FUNCTION events.ep_access( TEXT , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN , events.processing_state ) FROM PUBLIC; GRANT EXECUTE ON FUNCTION events.ep_access( TEXT , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN , events.processing_state ) TO :dbuser; DROP FUNCTION IF EXISTS events.ep_access( TEXT , INT , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN , events.processing_state ); CREATE FUNCTION events.ep_access( _evi_id TEXT , _target_id INT , _target_is_emp BOOLEAN , _accept_old BOOLEAN , _accept_pre BOOLEAN , _accept_new BOOLEAN , _clear_pre BOOLEAN , _change_new events.processing_state ) RETURNS SETOF events.event_list_type LANGUAGE SQL STRICT VOLATILE SECURITY DEFINER AS $ep_access$ SELECT * FROM events.ep_get_events_internal( $1 , $2 , $3 , $4 , $5 , $6 , NULL , NULL , $7 , $8 ); $ep_access$; REVOKE EXECUTE ON FUNCTION events.ep_access( TEXT , INT , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN , events.processing_state ) FROM PUBLIC; GRANT EXECUTE ON FUNCTION events.ep_access( TEXT , INT , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN , events.processing_state ) TO :dbuser; DROP FUNCTION IF EXISTS events.ep_access( TEXT, BOOLEAN , BOOLEAN , BOOLEAN , INT , INT , BOOLEAN , events.processing_state ); CREATE FUNCTION events.ep_access( _evi_id TEXT , _accept_old BOOLEAN , _accept_pre BOOLEAN , _accept_new BOOLEAN , _offset INT , _limit INT , _clear_pre BOOLEAN , _change_new events.processing_state ) RETURNS SETOF events.event_list_type LANGUAGE SQL STRICT VOLATILE SECURITY DEFINER AS $ep_access$ SELECT * FROM events.ep_get_events_internal( $1 , NULL , NULL , $2 , $3 , $4 , $5 , $6 , $7 , $8 ); $ep_access$; REVOKE EXECUTE ON FUNCTION events.ep_access( TEXT , BOOLEAN , BOOLEAN , BOOLEAN , INT , INT , BOOLEAN , events.processing_state ) FROM PUBLIC; GRANT EXECUTE ON FUNCTION events.ep_access( TEXT , BOOLEAN , BOOLEAN , BOOLEAN , INT , INT , BOOLEAN , events.processing_state ) TO :dbuser; DROP FUNCTION IF EXISTS events.ep_access( TEXT , INT , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN , INT , INT , BOOLEAN , events.processing_state ); CREATE FUNCTION events.ep_access( _evi_id TEXT , _target_id INT , _target_is_emp BOOLEAN , _accept_old BOOLEAN , _accept_pre BOOLEAN , _accept_new BOOLEAN , _offset INT , _limit INT , _clear_pre BOOLEAN , _change_new events.processing_state ) RETURNS SETOF events.event_list_type LANGUAGE SQL STRICT VOLATILE SECURITY DEFINER AS $ep_access$ SELECT * FROM events.ep_get_events_internal( $1 , $2 , $3 , $4 , $5 , $6 , $7 , $8 , $9 , $10 ); $ep_access$; REVOKE EXECUTE ON FUNCTION events.ep_access( TEXT , INT , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN , INT , INT, BOOLEAN , events.processing_state ) FROM PUBLIC; GRANT EXECUTE ON FUNCTION events.ep_access( TEXT , INT , BOOLEAN , BOOLEAN , BOOLEAN , BOOLEAN , INT , INT , BOOLEAN , events.processing_state ) TO :dbuser;