refs #731: add DB game scope metadata
This commit is contained in:
175
sql/adb/77_sgmp_game_scope_resolver.sql
Normal file
175
sql/adb/77_sgmp_game_scope_resolver.sql
Normal file
@@ -0,0 +1,175 @@
|
||||
-- DB-backed game query scope contract for MCP orchestration.
|
||||
--
|
||||
-- This script deliberately keeps game facts in database rows, not in application
|
||||
-- code or Select AI instructions. The view combines the active game alias source
|
||||
-- with an operator-maintained registry for known games that currently have no
|
||||
-- approved query object. A zero-row business result is still queryable; only the
|
||||
-- absence of an approved object makes a game scope unavailable.
|
||||
|
||||
DECLARE
|
||||
v_count PLS_INTEGER;
|
||||
BEGIN
|
||||
SELECT COUNT(*) INTO v_count
|
||||
FROM user_tables
|
||||
WHERE table_name = 'SG_GAME_SCOPE_REGISTRY';
|
||||
|
||||
IF v_count = 0 THEN
|
||||
EXECUTE IMMEDIATE q'[
|
||||
CREATE TABLE sg_game_scope_registry (
|
||||
game_key VARCHAR2(128) NOT NULL,
|
||||
display_name VARCHAR2(200) NOT NULL,
|
||||
game_alias VARCHAR2(200) NOT NULL,
|
||||
game_prefix VARCHAR2(30),
|
||||
active_yn VARCHAR2(1) DEFAULT 'Y' NOT NULL,
|
||||
alias_priority NUMBER(10) DEFAULT 100 NOT NULL,
|
||||
source_type VARCHAR2(30) DEFAULT 'OPERATOR' NOT NULL,
|
||||
work_dtm TIMESTAMP(6) DEFAULT SYSTIMESTAMP NOT NULL,
|
||||
CONSTRAINT sg_game_scope_registry_pk PRIMARY KEY (game_key, game_alias),
|
||||
CONSTRAINT sg_game_scope_registry_active_ck CHECK (active_yn IN ('Y', 'N'))
|
||||
)]';
|
||||
END IF;
|
||||
END;
|
||||
/
|
||||
|
||||
-- Seed only database facts needed to recognise currently unavailable games in
|
||||
-- the customer QA catalogue. Customer game-master synchronization can replace
|
||||
-- these rows without an application deployment.
|
||||
MERGE INTO sg_game_scope_registry t
|
||||
USING (
|
||||
SELECT 'LORDNINE' AS game_key,
|
||||
utl_i18n.raw_to_char(
|
||||
utl_encode.base64_decode(utl_raw.cast_to_raw('66Gc65Oc64KY7J24')),
|
||||
'AL32UTF8'
|
||||
) AS display_name,
|
||||
utl_i18n.raw_to_char(
|
||||
utl_encode.base64_decode(utl_raw.cast_to_raw('66Gc65Oc64KY7J24')),
|
||||
'AL32UTF8'
|
||||
) AS game_alias,
|
||||
CAST(NULL AS VARCHAR2(30)) AS game_prefix,
|
||||
100 AS alias_priority
|
||||
FROM dual
|
||||
UNION ALL
|
||||
SELECT 'BUBBLYZ', 'Bubblyz', 'Bubblyz', CAST(NULL AS VARCHAR2(30)), 100 FROM dual
|
||||
) s
|
||||
ON (t.game_key = s.game_key AND t.game_alias = s.game_alias)
|
||||
WHEN MATCHED THEN UPDATE SET
|
||||
t.display_name = s.display_name,
|
||||
t.game_prefix = s.game_prefix,
|
||||
t.active_yn = 'Y',
|
||||
t.alias_priority = s.alias_priority,
|
||||
t.source_type = 'OPERATOR',
|
||||
t.work_dtm = SYSTIMESTAMP
|
||||
WHEN NOT MATCHED THEN INSERT (
|
||||
game_key, display_name, game_alias, game_prefix, active_yn, alias_priority, source_type
|
||||
) VALUES (
|
||||
s.game_key, s.display_name, s.game_alias, s.game_prefix, 'Y', s.alias_priority, 'OPERATOR'
|
||||
);
|
||||
/
|
||||
|
||||
CREATE OR REPLACE VIEW sg_game_query_scope_v AS
|
||||
WITH existing_query_objects AS (
|
||||
-- USER_OBJECTS is the authoritative current-schema inventory. The profile
|
||||
-- object list alone is not enough because a stale entry must not make a game
|
||||
-- executable after its table or view has been removed or invalidated.
|
||||
SELECT object_name
|
||||
FROM user_objects
|
||||
WHERE object_type IN ('TABLE', 'VIEW')
|
||||
AND status = 'VALID'
|
||||
),
|
||||
profile_names AS (
|
||||
SELECT DISTINCT profile_name
|
||||
FROM user_cloud_ai_profile_attributes
|
||||
WHERE attribute_name = 'object_list'
|
||||
),
|
||||
profile_objects AS (
|
||||
SELECT DISTINCT p.profile_name, o.object_name
|
||||
FROM user_cloud_ai_profile_attributes p,
|
||||
JSON_TABLE(
|
||||
p.attribute_value,
|
||||
'$[*]' COLUMNS (object_name VARCHAR2(128) PATH '$.name')
|
||||
) o
|
||||
INNER JOIN existing_query_objects e
|
||||
ON e.object_name = o.object_name
|
||||
WHERE p.attribute_name = 'object_list'
|
||||
),
|
||||
source_alias AS (
|
||||
SELECT game_id AS game_key,
|
||||
game_nm AS display_name,
|
||||
game_alias_nm AS game_alias,
|
||||
game_prefix,
|
||||
use_yn AS active_yn,
|
||||
NVL(sort_order, 100) AS alias_priority,
|
||||
'GAME_ALIAS' AS source_type
|
||||
FROM comn_game_alias_bas
|
||||
),
|
||||
all_alias AS (
|
||||
SELECT game_key, display_name, game_alias, game_prefix, active_yn, alias_priority, source_type
|
||||
FROM source_alias
|
||||
UNION ALL
|
||||
SELECT r.game_key, r.display_name, r.game_alias, r.game_prefix,
|
||||
r.active_yn, r.alias_priority, r.source_type
|
||||
FROM sg_game_scope_registry r
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM source_alias a
|
||||
WHERE a.game_key = r.game_key
|
||||
AND a.game_alias = r.game_alias
|
||||
)
|
||||
),
|
||||
scope_object AS (
|
||||
SELECT n.profile_name,
|
||||
a.game_key,
|
||||
a.game_alias,
|
||||
COUNT(p.object_name) AS approved_object_count
|
||||
FROM profile_names n
|
||||
CROSS JOIN all_alias a
|
||||
LEFT JOIN profile_objects p
|
||||
ON p.profile_name = n.profile_name
|
||||
AND a.game_prefix IS NOT NULL
|
||||
AND SUBSTR(p.object_name, 1, LENGTH(a.game_prefix) + 1) = a.game_prefix || '_'
|
||||
GROUP BY n.profile_name, a.game_key, a.game_alias
|
||||
)
|
||||
SELECT o.profile_name,
|
||||
a.game_key,
|
||||
a.display_name,
|
||||
a.game_alias,
|
||||
a.game_prefix,
|
||||
a.active_yn,
|
||||
NVL(o.approved_object_count, 0) AS approved_object_count,
|
||||
CASE
|
||||
WHEN a.active_yn <> 'Y' THEN 'N'
|
||||
WHEN NVL(o.approved_object_count, 0) > 0 THEN 'Y'
|
||||
ELSE 'N'
|
||||
END AS query_allowed_yn,
|
||||
CASE
|
||||
WHEN a.active_yn <> 'Y' THEN 'GAME_INACTIVE'
|
||||
WHEN NVL(o.approved_object_count, 0) > 0 THEN 'APPROVED_OBJECT_AVAILABLE'
|
||||
ELSE 'OBJECT_LIST_NOT_AVAILABLE'
|
||||
END AS reason_code,
|
||||
a.alias_priority,
|
||||
a.source_type,
|
||||
TO_CHAR(MAX(a.alias_priority) OVER (PARTITION BY a.game_key), 'FM999999990') AS scope_version
|
||||
FROM all_alias a
|
||||
LEFT JOIN scope_object o
|
||||
ON o.game_key = a.game_key
|
||||
AND o.game_alias = a.game_alias;
|
||||
/
|
||||
|
||||
COMMENT ON TABLE sg_game_scope_registry IS
|
||||
'Operator-managed game aliases retained for scope resolution when a game has no approved query object.';
|
||||
COMMENT ON COLUMN sg_game_scope_registry.game_key IS
|
||||
'Stable game identifier used only by the DB-backed scope contract.';
|
||||
COMMENT ON COLUMN sg_game_scope_registry.game_alias IS
|
||||
'Question text alias matched by the resolver before any SQL worker is called.';
|
||||
COMMENT ON COLUMN sg_game_scope_registry.game_prefix IS
|
||||
'Optional data-object prefix. The scope view derives approved object availability from it.';
|
||||
COMMENT ON COLUMN sg_game_scope_registry.active_yn IS
|
||||
'Whether the game is eligible for scope resolution; inactive games are never executable.';
|
||||
COMMENT ON COLUMN sg_game_scope_registry.alias_priority IS
|
||||
'Database-defined ordering used to resolve overlapping aliases without application constants.';
|
||||
COMMENT ON COLUMN sg_game_query_scope_v.profile_name IS
|
||||
'Select AI profile whose current approved object list was used for this scope decision.';
|
||||
COMMENT ON COLUMN sg_game_query_scope_v.query_allowed_yn IS
|
||||
'Y only when the active game has at least one current Select AI approved and valid prefix-specific table or view.';
|
||||
COMMENT ON COLUMN sg_game_query_scope_v.reason_code IS
|
||||
'Database-derived explanation for scope availability returned to the MCP agent.';
|
||||
36
sql/adb/78_sgmp_game_alias_logical_joins.sql
Normal file
36
sql/adb/78_sgmp_game_alias_logical_joins.sql
Normal file
@@ -0,0 +1,36 @@
|
||||
-- Logical game-alias join guidance for Select AI.
|
||||
--
|
||||
-- COMN_GAME_ALIAS_BAS intentionally has multiple alias rows per GAME_ID, so
|
||||
-- GAME_ID cannot be modelled as a physical foreign key to that table. These
|
||||
-- annotations describe the safe semantic relationship without asserting a
|
||||
-- false database constraint or creating fan-out aggregation errors.
|
||||
|
||||
DECLARE v_result VARCHAR2(4000); BEGIN
|
||||
v_result := sgmp_set_annotation(
|
||||
'SGMP_POC', 'TABLE', 'COMN_SALES_TXN', NULL,
|
||||
'Logical game filter: COMN_SALES_TXN.GAME_ID is resolved through COMN_GAME_ALIAS_BAS. GAME_ID is not unique in the alias table because one game can have multiple aliases. For a game-name filter, use EXISTS against active aliases or join a DISTINCT GAME_ID alias subquery. Do not directly join all alias rows before SUM or COUNT because that can multiply fact rows.',
|
||||
'GAME_ALIAS_JOIN'
|
||||
);
|
||||
dbms_output.put_line(v_result);
|
||||
END;
|
||||
/
|
||||
|
||||
DECLARE v_result VARCHAR2(4000); BEGIN
|
||||
v_result := sgmp_set_annotation(
|
||||
'SGMP_POC', 'TABLE', 'COMN_REFUND_TXN', NULL,
|
||||
'Logical game filter: COMN_REFUND_TXN.GAME_ID is resolved through COMN_GAME_ALIAS_BAS. GAME_ID is not unique in the alias table because one game can have multiple aliases. For a game-name filter, use EXISTS against active aliases or join a DISTINCT GAME_ID alias subquery. Do not directly join all alias rows before SUM or COUNT because that can multiply fact rows.',
|
||||
'GAME_ALIAS_JOIN'
|
||||
);
|
||||
dbms_output.put_line(v_result);
|
||||
END;
|
||||
/
|
||||
|
||||
DECLARE v_result VARCHAR2(4000); BEGIN
|
||||
v_result := sgmp_set_annotation(
|
||||
'SGMP_POC', 'TABLE', 'COMN_SALES_PRODUCT_DISP_BAS', NULL,
|
||||
'Logical product scope: COMN_SALES_PRODUCT_DISP_BAS is keyed by GAME_ID and PRODUCT_ID. Resolve a natural-language game through COMN_GAME_ALIAS_BAS using EXISTS or a DISTINCT GAME_ID alias subquery before joining product data to transaction facts. The alias table has multiple aliases per GAME_ID and is not a physical foreign-key parent.',
|
||||
'GAME_ALIAS_JOIN'
|
||||
);
|
||||
dbms_output.put_line(v_result);
|
||||
END;
|
||||
/
|
||||
Reference in New Issue
Block a user