486 lines
18 KiB
MySQL
486 lines
18 KiB
MySQL
-- Vector-score candidate selection. The closest catalog vector is accepted
|
|
-- only when it is within the customer-managed policy threshold.
|
|
CREATE OR REPLACE FUNCTION sg_game_match_candidate(
|
|
p_mention IN CLOB,
|
|
p_candidates_json IN CLOB
|
|
) RETURN CLOB AUTHID DEFINER
|
|
IS
|
|
v_candidates JSON_ARRAY_T;
|
|
v_candidate JSON_OBJECT_T;
|
|
v_result JSON_OBJECT_T := JSON_OBJECT_T();
|
|
v_threshold NUMBER;
|
|
v_distance NUMBER;
|
|
v_game_key VARCHAR2(128);
|
|
BEGIN
|
|
SELECT number_value
|
|
INTO v_threshold
|
|
FROM sg_game_scope_policy
|
|
WHERE policy_key = 'GAME_ALIAS_MAX_COSINE_DISTANCE'
|
|
AND active_yn = 'Y';
|
|
|
|
v_candidates := JSON_ARRAY_T.parse(p_candidates_json);
|
|
IF v_candidates.get_size = 0 THEN
|
|
v_result.put('status', 'UNMATCHED');
|
|
v_result.put_null('game_key');
|
|
v_result.put('reason', 'No active game catalog vector candidate was returned.');
|
|
RETURN v_result.to_clob;
|
|
END IF;
|
|
|
|
v_candidate := TREAT(v_candidates.get(0) AS JSON_OBJECT_T);
|
|
v_distance := v_candidate.get_number('cosineDistance');
|
|
v_game_key := v_candidate.get_string('gameKey');
|
|
|
|
IF v_distance <= v_threshold THEN
|
|
v_result.put('status', 'MATCHED');
|
|
v_result.put('game_key', v_game_key);
|
|
v_result.put(
|
|
'reason',
|
|
'Closest catalog vector distance '
|
|
|| TO_CHAR(v_distance, 'FM0D000000')
|
|
|| ' is within configured maximum '
|
|
|| TO_CHAR(v_threshold, 'FM0D000000') || '.'
|
|
);
|
|
ELSE
|
|
v_result.put('status', 'UNMATCHED');
|
|
v_result.put_null('game_key');
|
|
v_result.put(
|
|
'reason',
|
|
'Closest catalog vector distance '
|
|
|| TO_CHAR(v_distance, 'FM0D000000')
|
|
|| ' exceeds configured maximum '
|
|
|| TO_CHAR(v_threshold, 'FM0D000000') || '.'
|
|
);
|
|
END IF;
|
|
RETURN v_result.to_clob;
|
|
END;
|
|
/
|
|
|
|
-- A game can be known to the scope registry while not being an active alias
|
|
-- source for a common fact query. Keep that availability separate from a
|
|
-- role-specific physical-object availability such as a user-master table.
|
|
CREATE OR REPLACE FUNCTION sg_game_fact_scope_status(
|
|
p_game_id IN VARCHAR2
|
|
) RETURN VARCHAR2 AUTHID DEFINER IS
|
|
v_exists NUMBER;
|
|
BEGIN
|
|
SELECT COUNT(*)
|
|
INTO v_exists
|
|
FROM SGMP_POC.comn_game_alias_bas
|
|
WHERE use_yn = 'Y'
|
|
AND game_id = p_game_id;
|
|
RETURN CASE WHEN v_exists > 0 THEN 'ACTIVE_ALIAS' ELSE 'REGISTRY_ONLY' END;
|
|
END;
|
|
/
|
|
|
|
-- Complete game query planning inside ADB. The MCP server only invokes this
|
|
-- function and returns its structured result.
|
|
CREATE OR REPLACE FUNCTION sg_game_query_plan(
|
|
p_question IN CLOB,
|
|
p_top_k IN PLS_INTEGER DEFAULT 5
|
|
) RETURN CLOB AUTHID DEFINER
|
|
IS
|
|
TYPE t_seen_game_keys IS TABLE OF BOOLEAN INDEX BY VARCHAR2(128);
|
|
|
|
v_extract_raw CLOB;
|
|
v_extract JSON_OBJECT_T;
|
|
v_mentions JSON_ARRAY_T;
|
|
v_scope_type VARCHAR2(30);
|
|
v_target_type VARCHAR2(20);
|
|
v_targets JSON_ARRAY_T := JSON_ARRAY_T();
|
|
v_supported JSON_ARRAY_T := JSON_ARRAY_T();
|
|
v_data_eligible JSON_ARRAY_T := JSON_ARRAY_T();
|
|
v_data_ineligible JSON_ARRAY_T := JSON_ARRAY_T();
|
|
v_unresolved JSON_ARRAY_T := JSON_ARRAY_T();
|
|
v_unmatched JSON_ARRAY_T := JSON_ARRAY_T();
|
|
v_mention_results JSON_ARRAY_T := JSON_ARRAY_T();
|
|
v_execution_tasks JSON_ARRAY_T := JSON_ARRAY_T();
|
|
v_seen_game_keys t_seen_game_keys;
|
|
v_result JSON_OBJECT_T := JSON_OBJECT_T();
|
|
v_reference_summary JSON_OBJECT_T := JSON_OBJECT_T();
|
|
v_select_ai_reference CLOB;
|
|
v_plan_status VARCHAR2(30);
|
|
v_matched_count PLS_INTEGER := 0;
|
|
v_data_eligible_count PLS_INTEGER := 0;
|
|
v_unmatched_count PLS_INTEGER := 0;
|
|
|
|
v_mention VARCHAR2(1000);
|
|
v_candidates JSON_ARRAY_T;
|
|
v_candidate JSON_OBJECT_T;
|
|
v_decision_raw CLOB;
|
|
v_decision JSON_OBJECT_T;
|
|
v_decision_status VARCHAR2(30);
|
|
v_selected_key VARCHAR2(128);
|
|
v_reason VARCHAR2(4000);
|
|
v_matched_candidate JSON_OBJECT_T;
|
|
v_mention_result JSON_OBJECT_T;
|
|
v_target JSON_OBJECT_T;
|
|
v_unmatched_item JSON_OBJECT_T;
|
|
v_execution_task JSON_OBJECT_T;
|
|
v_worker_arguments JSON_OBJECT_T;
|
|
v_fewshot_arguments JSON_OBJECT_T;
|
|
v_task_plan JSON_OBJECT_T;
|
|
v_task_reference JSON_OBJECT_T;
|
|
v_task_targets JSON_ARRAY_T;
|
|
v_excluded_mentions JSON_ARRAY_T;
|
|
v_task_question CLOB;
|
|
|
|
v_cursor SYS_REFCURSOR;
|
|
v_game_key VARCHAR2(128);
|
|
v_game_id VARCHAR2(128);
|
|
v_game_prefix VARCHAR2(128);
|
|
v_game_nm VARCHAR2(512);
|
|
v_game_alias_nm VARCHAR2(512);
|
|
v_user_master_object_name VARCHAR2(128);
|
|
v_fact_scope_status VARCHAR2(30);
|
|
v_cosine_distance NUMBER;
|
|
BEGIN
|
|
v_extract_raw := sg_game_extract_mentions(p_question);
|
|
v_extract := JSON_OBJECT_T.parse(v_extract_raw);
|
|
v_mentions := v_extract.get_array('game_mentions');
|
|
v_scope_type := NVL(v_extract.get_string('scope_hint'), 'UNKNOWN');
|
|
|
|
IF v_scope_type = 'ALL_GAMES' THEN
|
|
v_target_type := 'ALL';
|
|
ELSIF v_mentions.get_size = 0 THEN
|
|
v_target_type := 'NONE';
|
|
ELSIF v_mentions.get_size = 1 THEN
|
|
v_target_type := 'SINGLE';
|
|
ELSE
|
|
v_target_type := 'MULTI';
|
|
END IF;
|
|
|
|
IF v_target_type = 'ALL' THEN
|
|
FOR game_row IN (
|
|
SELECT game_key, game_id, game_prefix, game_nm, game_alias_nm,
|
|
user_master_object_name
|
|
FROM SGMP_POC.sg_game_catalog
|
|
WHERE active_yn = 'Y'
|
|
ORDER BY priority, game_key
|
|
) LOOP
|
|
v_target := JSON_OBJECT_T();
|
|
v_target.put_null('mention');
|
|
v_target.put('status', 'CATALOG');
|
|
v_target.put('matchStatus', 'MATCHED');
|
|
v_target.put('gameKey', game_row.game_key);
|
|
v_target.put('gameId', game_row.game_id);
|
|
v_target.put('gamePrefix', game_row.game_prefix);
|
|
v_target.put('gameName', game_row.game_nm);
|
|
v_target.put('aliases', game_row.game_alias_nm);
|
|
v_target.put(
|
|
'userMasterObjectName',
|
|
game_row.user_master_object_name
|
|
);
|
|
v_target.put(
|
|
'objectStatus',
|
|
CASE WHEN game_row.user_master_object_name IS NULL
|
|
THEN 'UNAVAILABLE' ELSE 'AVAILABLE' END
|
|
);
|
|
v_fact_scope_status := sg_game_fact_scope_status(game_row.game_id);
|
|
v_target.put('factScopeStatus', v_fact_scope_status);
|
|
v_target.put(
|
|
'dataStatus',
|
|
CASE WHEN v_fact_scope_status = 'ACTIVE_ALIAS'
|
|
THEN 'AVAILABLE' ELSE 'UNAVAILABLE' END
|
|
);
|
|
v_target.put_null('cosineDistance');
|
|
v_target.put('reasonCode', 'ALL_GAMES_CATALOG');
|
|
v_target.put('reason', 'Active game returned from the database catalog.');
|
|
v_targets.append(v_target);
|
|
v_matched_count := v_matched_count + 1;
|
|
IF game_row.user_master_object_name IS NOT NULL THEN
|
|
v_supported.append(v_target);
|
|
END IF;
|
|
IF v_fact_scope_status = 'ACTIVE_ALIAS' THEN
|
|
v_data_eligible.append(v_target);
|
|
v_data_eligible_count := v_data_eligible_count + 1;
|
|
ELSE
|
|
v_data_ineligible.append(v_target);
|
|
v_unresolved.append(v_target);
|
|
END IF;
|
|
END LOOP;
|
|
ELSIF v_target_type = 'NONE' THEN
|
|
v_target := JSON_OBJECT_T();
|
|
v_target.put_null('mention');
|
|
v_target.put('status', 'NONE');
|
|
v_target.put('matchStatus', 'NOT_APPLICABLE');
|
|
v_target.put_null('gameKey');
|
|
v_target.put_null('gameId');
|
|
v_target.put_null('gamePrefix');
|
|
v_target.put_null('gameName');
|
|
v_target.put_null('aliases');
|
|
v_target.put_null('userMasterObjectName');
|
|
v_target.put('objectStatus', 'NOT_APPLICABLE');
|
|
v_target.put('factScopeStatus', 'NOT_APPLICABLE');
|
|
v_target.put('dataStatus', 'NOT_APPLICABLE');
|
|
v_target.put_null('cosineDistance');
|
|
v_target.put('reasonCode', 'NO_GAME_TARGET');
|
|
v_target.put(
|
|
'reason',
|
|
'The question does not select a particular game.'
|
|
);
|
|
v_targets.append(v_target);
|
|
ELSE
|
|
FOR i IN 0 .. v_mentions.get_size - 1 LOOP
|
|
v_mention := v_mentions.get_string(i);
|
|
v_candidates := JSON_ARRAY_T();
|
|
v_cursor := SGMP_POC.sg_game_catalog_search(
|
|
v_mention,
|
|
LEAST(GREATEST(NVL(p_top_k, 5), 1), 20)
|
|
);
|
|
|
|
LOOP
|
|
FETCH v_cursor INTO
|
|
v_game_key,
|
|
v_game_id,
|
|
v_game_prefix,
|
|
v_game_nm,
|
|
v_game_alias_nm,
|
|
v_user_master_object_name,
|
|
v_cosine_distance;
|
|
EXIT WHEN v_cursor%NOTFOUND;
|
|
|
|
v_candidate := JSON_OBJECT_T();
|
|
v_candidate.put('gameKey', v_game_key);
|
|
v_candidate.put('gameId', v_game_id);
|
|
v_candidate.put('gamePrefix', v_game_prefix);
|
|
v_candidate.put('gameName', v_game_nm);
|
|
v_candidate.put('aliases', v_game_alias_nm);
|
|
v_candidate.put(
|
|
'userMasterObjectName',
|
|
v_user_master_object_name
|
|
);
|
|
v_candidate.put(
|
|
'objectStatus',
|
|
CASE WHEN v_user_master_object_name IS NULL
|
|
THEN 'UNAVAILABLE' ELSE 'AVAILABLE' END
|
|
);
|
|
v_candidate.put(
|
|
'factScopeStatus',
|
|
sg_game_fact_scope_status(v_game_id)
|
|
);
|
|
v_candidate.put('cosineDistance', v_cosine_distance);
|
|
v_candidates.append(v_candidate);
|
|
END LOOP;
|
|
CLOSE v_cursor;
|
|
|
|
v_decision_raw := sg_game_match_candidate(
|
|
v_mention,
|
|
v_candidates.to_clob
|
|
);
|
|
v_decision := JSON_OBJECT_T.parse(v_decision_raw);
|
|
v_decision_status := UPPER(
|
|
NVL(v_decision.get_string('status'), 'UNMATCHED')
|
|
);
|
|
v_selected_key := v_decision.get_string('game_key');
|
|
v_reason := v_decision.get_string('reason');
|
|
v_matched_candidate := NULL;
|
|
|
|
IF v_decision_status = 'MATCHED' AND v_selected_key IS NOT NULL THEN
|
|
FOR j IN 0 .. v_candidates.get_size - 1 LOOP
|
|
v_candidate := TREAT(v_candidates.get(j) AS JSON_OBJECT_T);
|
|
IF v_candidate.get_string('gameKey') = v_selected_key THEN
|
|
v_matched_candidate := v_candidate;
|
|
EXIT;
|
|
END IF;
|
|
END LOOP;
|
|
END IF;
|
|
|
|
v_mention_result := JSON_OBJECT_T();
|
|
v_mention_result.put('mention', v_mention);
|
|
v_mention_result.put('candidateGames', v_candidates);
|
|
v_mention_result.put('reason', v_reason);
|
|
|
|
IF v_matched_candidate IS NOT NULL THEN
|
|
v_mention_result.put('status', 'MATCHED');
|
|
v_mention_result.put('reasonCode', 'VECTOR_SCORE_MATCH');
|
|
v_mention_result.put('matchedGameKey', v_selected_key);
|
|
|
|
v_target := JSON_OBJECT_T.parse(v_matched_candidate.to_clob);
|
|
v_target.put('mention', v_mention);
|
|
v_target.put('status', 'MATCHED');
|
|
v_target.put('matchStatus', 'MATCHED');
|
|
v_target.put('reasonCode', 'VECTOR_SCORE_MATCH');
|
|
v_target.put('reason', v_reason);
|
|
v_matched_count := v_matched_count + 1;
|
|
|
|
IF v_matched_candidate.get_string('factScopeStatus') = 'ACTIVE_ALIAS' THEN
|
|
v_target.put('dataStatus', 'AVAILABLE');
|
|
v_data_eligible.append(v_target);
|
|
v_data_eligible_count := v_data_eligible_count + 1;
|
|
ELSE
|
|
v_target.put('dataStatus', 'UNAVAILABLE');
|
|
v_data_ineligible.append(v_target);
|
|
v_unresolved.append(v_target);
|
|
END IF;
|
|
v_targets.append(v_target);
|
|
IF NOT v_seen_game_keys.EXISTS(v_selected_key) THEN
|
|
v_supported.append(v_target);
|
|
v_seen_game_keys(v_selected_key) := TRUE;
|
|
END IF;
|
|
ELSE
|
|
v_mention_result.put('status', 'UNMATCHED');
|
|
v_mention_result.put('reasonCode', 'VECTOR_SCORE_OVER_THRESHOLD');
|
|
|
|
v_target := JSON_OBJECT_T();
|
|
v_target.put('mention', v_mention);
|
|
v_target.put('status', 'UNMATCHED');
|
|
v_target.put('matchStatus', 'UNMATCHED');
|
|
v_target.put_null('gameKey');
|
|
v_target.put_null('gameId');
|
|
v_target.put_null('gamePrefix');
|
|
v_target.put_null('gameName');
|
|
v_target.put_null('aliases');
|
|
v_target.put_null('userMasterObjectName');
|
|
v_target.put('objectStatus', 'UNAVAILABLE');
|
|
v_target.put('factScopeStatus', 'UNAVAILABLE');
|
|
v_target.put('dataStatus', 'UNAVAILABLE');
|
|
v_target.put_null('cosineDistance');
|
|
v_target.put(
|
|
'reasonCode',
|
|
v_mention_result.get_string('reasonCode')
|
|
);
|
|
v_target.put('reason', v_reason);
|
|
v_targets.append(v_target);
|
|
|
|
v_unmatched_item := JSON_OBJECT_T();
|
|
v_unmatched_item.put('mention', v_mention);
|
|
v_unmatched_item.put(
|
|
'reasonCode',
|
|
v_mention_result.get_string('reasonCode')
|
|
);
|
|
v_unmatched_item.put('reason', v_reason);
|
|
v_unmatched.append(v_unmatched_item);
|
|
v_unresolved.append(v_target);
|
|
v_unmatched_count := v_unmatched_count + 1;
|
|
END IF;
|
|
v_mention_results.append(v_mention_result);
|
|
END LOOP;
|
|
END IF;
|
|
|
|
IF v_target_type = 'NONE' THEN
|
|
v_plan_status := 'NO_TARGET';
|
|
ELSIF v_matched_count = 0 THEN
|
|
v_plan_status := 'UNMATCHED';
|
|
ELSIF v_data_eligible_count = 0 THEN
|
|
v_plan_status := 'UNAVAILABLE';
|
|
ELSIF v_unmatched_count > 0 THEN
|
|
v_plan_status := 'PARTIAL';
|
|
ELSE
|
|
v_plan_status := 'SUPPORTED';
|
|
END IF;
|
|
|
|
-- This is the sole Select AI handoff contract. It is deliberately generic:
|
|
-- game values and physical objects come only from the database lookup above.
|
|
v_reference_summary.put('targetType', v_target_type);
|
|
v_reference_summary.put('status', v_plan_status);
|
|
v_reference_summary.put('gameTargets', v_supported);
|
|
v_select_ai_reference := '[GAME QUERY REFERENCE]' || CHR(10) || CHR(10)
|
|
|| v_reference_summary.to_clob()
|
|
|| CHR(10) || CHR(10)
|
|
|| 'Field meanings:' || CHR(10) || CHR(10)
|
|
|| '* targetType:' || CHR(10)
|
|
|| ' * NONE: No game was resolved.' || CHR(10)
|
|
|| ' * SINGLE: Exactly one game was resolved.' || CHR(10)
|
|
|| ' * MULTI: Multiple specific games were resolved.' || CHR(10)
|
|
|| ' * ALL: The query applies to all supported games.' || CHR(10)
|
|
|| '* status: The result of game-target resolution.' || CHR(10)
|
|
|| '* gameTargets: The exact games resolved by the DB lookup. Each item may include:' || CHR(10)
|
|
|| ' * gameKey: Canonical game identifier.' || CHR(10)
|
|
|| ' * gamePrefix: Prefix used for game-scoped objects.' || CHR(10)
|
|
|| ' * userMasterObjectName: Resolved user-master object for that game.' || CHR(10) || CHR(10)
|
|
|| 'Object-selection guidance:' || CHR(10) || CHR(10)
|
|
|| '* Use only the targets listed in gameTargets.' || CHR(10)
|
|
|| '* For NONE, use a game-neutral common object when it directly answers the question.' || CHR(10)
|
|
|| '* If no suitable common object exists, state that a game name is required.' || CHR(10)
|
|
|| '* Do not infer an unlisted game or game-scoped object.';
|
|
|
|
v_result.put('contractVersion', '2.0');
|
|
v_result.put('targetType', v_target_type);
|
|
v_result.put('scopeType', v_target_type);
|
|
v_result.put('selectAiReference', v_select_ai_reference);
|
|
v_result.put('extractScopeHint', v_scope_type);
|
|
v_result.put('status', v_plan_status);
|
|
-- `gameTargets` is the public downstream contract. The detailed `targets`
|
|
-- collection remains diagnostic evidence only for the MCP response.
|
|
v_result.put('gameTargets', v_supported);
|
|
v_result.put('targets', v_targets);
|
|
v_result.put('matchedGames', v_supported);
|
|
v_result.put('mentionResults', v_mention_results);
|
|
v_result.put('supportedGames', v_supported);
|
|
v_result.put('dataEligibleTargets', v_data_eligible);
|
|
v_result.put('unresolvedTargets', v_unresolved);
|
|
v_result.put('dataIneligibleTargets', v_data_ineligible);
|
|
v_result.put('unmatchedGames', v_unmatched);
|
|
-- Dynamic ReAct work items. Every game identity and availability state comes
|
|
-- from the catalog lookup above; clients must not infer their own targets.
|
|
FOR i IN 0 .. v_targets.get_size - 1 LOOP
|
|
v_target := TREAT(v_targets.get(i) AS JSON_OBJECT_T);
|
|
IF v_target.get_string('gameKey') IS NOT NULL THEN
|
|
v_execution_task := JSON_OBJECT_T();
|
|
v_execution_task.put(
|
|
'action',
|
|
CASE
|
|
WHEN v_target.get_string('dataStatus') = 'AVAILABLE'
|
|
AND v_target.get_string('userMasterObjectName') IS NOT NULL
|
|
THEN 'QUERY'
|
|
ELSE 'REPORT_UNAVAILABLE'
|
|
END
|
|
);
|
|
v_execution_task.put('scopeGameKey', v_target.get_string('gameKey'));
|
|
v_execution_task.put('target', v_target);
|
|
IF v_execution_task.get_string('action') = 'QUERY' THEN
|
|
v_excluded_mentions := JSON_ARRAY_T();
|
|
FOR j IN 0 .. v_targets.get_size - 1 LOOP
|
|
v_candidate := TREAT(v_targets.get(j) AS JSON_OBJECT_T);
|
|
IF v_candidate.get_string('gameKey') <> v_target.get_string('gameKey')
|
|
AND v_candidate.get_string('mention') IS NOT NULL THEN
|
|
v_excluded_mentions.append(v_candidate.get_string('mention'));
|
|
END IF;
|
|
END LOOP;
|
|
|
|
-- Preserve the original question verbatim. The target-specific
|
|
-- queryPlan below is the separate, authoritative scope contract.
|
|
v_task_question := p_question;
|
|
v_task_reference := JSON_OBJECT_T();
|
|
v_task_targets := JSON_ARRAY_T();
|
|
v_task_targets.append(v_target);
|
|
v_task_reference.put('targetType', 'SINGLE');
|
|
v_task_reference.put('status', 'SUPPORTED');
|
|
v_task_reference.put('gameTargets', v_task_targets);
|
|
|
|
v_task_plan := JSON_OBJECT_T();
|
|
v_task_plan.put('contractVersion', '2.0');
|
|
v_task_plan.put('targetType', 'SINGLE');
|
|
v_task_plan.put('status', 'SUPPORTED');
|
|
v_task_plan.put('gameTargets', v_task_targets);
|
|
v_task_plan.put('selectAiReference',
|
|
'[GAME QUERY REFERENCE]' || CHR(10) || CHR(10) || v_task_reference.to_clob());
|
|
|
|
v_worker_arguments := JSON_OBJECT_T();
|
|
v_worker_arguments.put('prompt', v_task_question);
|
|
v_worker_arguments.put('scopeGameKey', v_target.get_string('gameKey'));
|
|
v_worker_arguments.put('queryPlan', v_task_plan);
|
|
v_fewshot_arguments := JSON_OBJECT_T();
|
|
v_fewshot_arguments.put('question', v_task_question);
|
|
v_fewshot_arguments.put('topK', 3);
|
|
v_execution_task.put('workerTool', 'oracle.select_ai.smilegate_fewshot_nl2sql');
|
|
v_execution_task.put('workerArguments', v_worker_arguments);
|
|
v_execution_task.put('fewShotArguments', v_fewshot_arguments);
|
|
END IF;
|
|
v_execution_tasks.append(v_execution_task);
|
|
END IF;
|
|
END LOOP;
|
|
v_result.put('executionTasks', v_execution_tasks);
|
|
v_result.put('nextAction', 'CALL_FEWSHOT');
|
|
|
|
CASE v_target_type
|
|
WHEN 'NONE' THEN v_result.put('executionMode', 'UNSCOPED');
|
|
WHEN 'SINGLE' THEN v_result.put('executionMode', 'SINGLE');
|
|
WHEN 'MULTI' THEN v_result.put('executionMode', 'COMBINED');
|
|
WHEN 'ALL' THEN v_result.put('executionMode', 'ALL');
|
|
END CASE;
|
|
|
|
RETURN v_result.to_clob;
|
|
END;
|
|
/
|