chore: import upstream snapshot with attribution
Integration Tests - MySQL + Elasticsearch / Detect Changes (push) Has been cancelled
Integration Tests - MySQL + Elasticsearch / integration-tests-mysql-elasticsearch (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / Detect Changes (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / integration-tests-postgres-elasticsearch-redis (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / Detect Changes (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / integration-tests-postgres-opensearch (push) Has been cancelled
Java Checkstyle / java-checkstyle (push) Has been cancelled
Maven Collate Tests / maven-collate-ci (push) Has been cancelled
OpenMetadata Service Unit Tests / openmetadata-service-unit-tests-status (push) Has been cancelled
Publish Package to Maven Central Repository / publish-maven-packages (push) Has been cancelled
OpenMetadata Service Unit Tests / Detect Changes (push) Has been cancelled
OpenMetadata Service Unit Tests / openmetadata-service-unit-tests (push) Has been cancelled
OpenMetadata Service Unit Tests / k8s_operator-unit-tests (push) Has been cancelled
Integration Tests - MySQL + Elasticsearch / Detect Changes (push) Has been cancelled
Integration Tests - MySQL + Elasticsearch / integration-tests-mysql-elasticsearch (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / Detect Changes (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / integration-tests-postgres-elasticsearch-redis (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / Detect Changes (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / integration-tests-postgres-opensearch (push) Has been cancelled
Java Checkstyle / java-checkstyle (push) Has been cancelled
Maven Collate Tests / maven-collate-ci (push) Has been cancelled
OpenMetadata Service Unit Tests / openmetadata-service-unit-tests-status (push) Has been cancelled
Publish Package to Maven Central Repository / publish-maven-packages (push) Has been cancelled
OpenMetadata Service Unit Tests / Detect Changes (push) Has been cancelled
OpenMetadata Service Unit Tests / openmetadata-service-unit-tests (push) Has been cancelled
OpenMetadata Service Unit Tests / k8s_operator-unit-tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
-- Post data migration script for Task System Redesign - OpenMetadata 2.0.0
|
||||
-- This script runs after the data migration completes
|
||||
|
||||
-- =====================================================
|
||||
-- NOTE: Suggestion migration (suggestions → task_entity),
|
||||
-- thread-based task migration (thread_entity → task_entity),
|
||||
-- and legacy system activity migration
|
||||
-- (thread_entity generated feed rows → activity_stream)
|
||||
-- are handled in Java MigrationUtil because they require
|
||||
-- entity-link aware transformation logic.
|
||||
-- =====================================================
|
||||
|
||||
-- =====================================================
|
||||
-- PHASE 2D: Migrate announcements from thread_entity → announcement_entity
|
||||
-- =====================================================
|
||||
INSERT INTO announcement_entity (id, json, fqnHash)
|
||||
SELECT
|
||||
a_id AS id,
|
||||
a_json AS json,
|
||||
a_fqnHash AS fqnHash
|
||||
FROM (
|
||||
SELECT
|
||||
JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.id')) AS a_id,
|
||||
JSON_OBJECT(
|
||||
'id', JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.id')),
|
||||
'name', CONCAT('announcement-', JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.id'))),
|
||||
'fullyQualifiedName', CONCAT('announcement-', JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.id'))),
|
||||
'displayName', NULLIF(JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.message')), ''),
|
||||
'description', COALESCE(
|
||||
JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.announcement.description')),
|
||||
JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.message')),
|
||||
''
|
||||
),
|
||||
'entityLink', JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.about')),
|
||||
'startTime', CAST(JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.announcement.startTime')) AS UNSIGNED),
|
||||
'endTime', CAST(JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.announcement.endTime')) AS UNSIGNED),
|
||||
'status', CASE
|
||||
WHEN CAST(JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.announcement.endTime')) AS UNSIGNED) < UNIX_TIMESTAMP() * 1000
|
||||
THEN 'Expired'
|
||||
WHEN CAST(JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.announcement.startTime')) AS UNSIGNED) > UNIX_TIMESTAMP() * 1000
|
||||
THEN 'Scheduled'
|
||||
ELSE 'Active'
|
||||
END,
|
||||
'createdBy', JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.createdBy')),
|
||||
'updatedBy', COALESCE(JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.updatedBy')), JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.createdBy'))),
|
||||
'createdAt', CAST(JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.threadTs')) AS UNSIGNED),
|
||||
'updatedAt', CAST(
|
||||
COALESCE(
|
||||
JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.updatedAt')),
|
||||
JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.threadTs'))
|
||||
) AS UNSIGNED
|
||||
),
|
||||
'deleted', false,
|
||||
'version', 0.1,
|
||||
'reactions', COALESCE(JSON_EXTRACT(t.json, '$.reactions'), JSON_ARRAY())
|
||||
) AS a_json,
|
||||
MD5(CONCAT('announcement-', JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.id')))) AS a_fqnHash
|
||||
FROM thread_entity t
|
||||
WHERE JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.type')) = 'Announcement'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM announcement_entity a WHERE a.id = JSON_UNQUOTE(JSON_EXTRACT(t.json, '$.id'))
|
||||
)
|
||||
) migrated;
|
||||
|
||||
-- =====================================================
|
||||
-- PHASE 2E: Rename legacy thread storage to fail stale references
|
||||
-- =====================================================
|
||||
SET @thread_entity_exists = (
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'thread_entity'
|
||||
);
|
||||
|
||||
SET @thread_entity_legacy_exists = (
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'thread_entity_legacy'
|
||||
);
|
||||
|
||||
SET @rename_thread_entity_sql = IF(
|
||||
@thread_entity_exists = 1 AND @thread_entity_legacy_exists = 0,
|
||||
'RENAME TABLE thread_entity TO thread_entity_legacy',
|
||||
'SELECT 1'
|
||||
);
|
||||
|
||||
PREPARE rename_thread_entity_stmt FROM @rename_thread_entity_sql;
|
||||
EXECUTE rename_thread_entity_stmt;
|
||||
DEALLOCATE PREPARE rename_thread_entity_stmt;
|
||||
|
||||
-- =====================================================
|
||||
-- PHASE 2F: Lower workflow trigger polling intervals
|
||||
-- =====================================================
|
||||
-- Reduce WorkflowEventConsumer poll interval from 10s to 1s.
|
||||
-- The legacy 10s default added up to a 10s wait between an entity change and the
|
||||
-- workflow-triggered approval task being created. On CI under resource pressure this
|
||||
-- often drifted to >2 minutes when combined with Flowable's 60s async job poll. The
|
||||
-- new value keeps the trigger pipeline near-real-time.
|
||||
UPDATE event_subscription_entity
|
||||
SET json = JSON_SET(json, '$.pollInterval', 1)
|
||||
WHERE name = 'WorkflowEventConsumer'
|
||||
AND CAST(JSON_EXTRACT(json, '$.pollInterval') AS UNSIGNED) > 1;
|
||||
|
||||
-- Lower Flowable async/timer job acquisition intervals to keep workflow-driven
|
||||
-- task creation responsive. The previous 60s default was a Flowable production setting
|
||||
-- carried over verbatim; for OpenMetadata's interactive task UX we want sub-second pickup.
|
||||
UPDATE openmetadata_settings
|
||||
SET json = JSON_SET(
|
||||
JSON_SET(json, '$.executorConfiguration.asyncJobAcquisitionInterval', 1000),
|
||||
'$.executorConfiguration.timerJobAcquisitionInterval', 5000)
|
||||
WHERE configType = 'workflowSettings'
|
||||
AND JSON_EXTRACT(json, '$.executorConfiguration') IS NOT NULL
|
||||
AND (CAST(JSON_EXTRACT(json, '$.executorConfiguration.asyncJobAcquisitionInterval') AS UNSIGNED) > 1000
|
||||
OR CAST(JSON_EXTRACT(json, '$.executorConfiguration.timerJobAcquisitionInterval') AS UNSIGNED) > 5000);
|
||||
|
||||
-- pipelineStatuses is a derived field, read on demand from entity_extension_time_series, and is
|
||||
-- now an array instead of a single object. Strip any stale single-object value that a GET->PUT
|
||||
-- round-trip may have persisted into the stored entity JSON so it cannot break deserialization.
|
||||
UPDATE ingestion_pipeline_entity
|
||||
SET json = JSON_REMOVE(json, '$.pipelineStatuses')
|
||||
WHERE JSON_CONTAINS_PATH(json, 'one', '$.pipelineStatuses');
|
||||
|
||||
-- MCP Server and MCP Chat are no longer internal Applications; their enablement now lives in
|
||||
-- platform settings (mcpConfiguration). The MCP Chat app was never shipped to customers, so
|
||||
-- aiSettings.mcpChat keeps its seeded default shape (no config carry-over).
|
||||
|
||||
-- MCP Server: keep it disabled if the server app was not installed (mcpConfiguration defaults
|
||||
-- to enabled=true, which would otherwise turn it on).
|
||||
UPDATE openmetadata_settings
|
||||
SET json = JSON_SET(json, '$.enabled', false)
|
||||
WHERE configType = 'mcpConfiguration'
|
||||
AND NOT EXISTS (SELECT 1 FROM installed_apps ia WHERE ia.name = 'McpApplication');
|
||||
|
||||
-- Retire the MCP apps (their Java classes and marketplace seeds are removed). Keep the bot users
|
||||
-- so the MCP server keeps its principal.
|
||||
DELETE er FROM entity_relationship er
|
||||
JOIN installed_apps ia ON er.fromId = ia.id OR er.toId = ia.id
|
||||
WHERE ia.name IN ('McpApplication', 'McpChatApplication');
|
||||
DELETE er FROM entity_relationship er
|
||||
JOIN apps_marketplace ia ON er.fromId = ia.id OR er.toId = ia.id
|
||||
WHERE ia.name IN ('McpApplication', 'McpChatApplication');
|
||||
DELETE FROM installed_apps WHERE name IN ('McpApplication', 'McpChatApplication');
|
||||
DELETE FROM apps_marketplace WHERE name IN ('McpApplication', 'McpChatApplication');
|
||||
|
||||
-- Post data migration script for Task workflow cutover - OpenMetadata 2.0.0 (moved from 2.0.1)
|
||||
|
||||
-- RdfIndexApp: switch to weekly Saturday cron and full-rebuild every run.
|
||||
-- Previous defaults (daily, incremental) were producing unbounded triple growth
|
||||
-- because relationship-removal paths weren't fully reconciled. With per-run
|
||||
-- CLEAR ALL the dataset always converges to MySQL state; weekly cadence keeps
|
||||
-- per-run cost from saturating Fuseki.
|
||||
--
|
||||
-- Also rewrite `entities` to `["all"]`. Pre-upgrade, an operator could have
|
||||
-- narrowed RDF indexing to a subset of entity types; the new recreateIndex=true
|
||||
-- semantics issues a CLEAR ALL before indexing, which would otherwise wipe
|
||||
-- triples for entity types still in MySQL but missing from the subset list.
|
||||
-- Forcing the subset list back to `["all"]` ensures the post-CLEAR-ALL run
|
||||
-- repopulates the graph fully; operators can re-narrow after the migration if
|
||||
-- they need partial indexing.
|
||||
UPDATE installed_apps
|
||||
SET json = JSON_SET(
|
||||
JSON_SET(
|
||||
json,
|
||||
'$.appConfiguration.recreateIndex', CAST('true' AS JSON),
|
||||
'$.appSchedule.cronExpression', '0 0 * * 6'
|
||||
),
|
||||
'$.appConfiguration.entities', JSON_ARRAY('all')
|
||||
)
|
||||
WHERE name = 'RdfIndexApp';
|
||||
|
||||
UPDATE apps_marketplace
|
||||
SET json = JSON_SET(json, '$.appConfiguration.recreateIndex', CAST('true' AS JSON))
|
||||
WHERE name = 'RdfIndexApp';
|
||||
|
||||
-- Backfill policyAgentConfig defaults on existing Snowflake services. The schema-level
|
||||
-- defaults in snowflakeConnection.json only apply at create-time deserialization; rows
|
||||
-- already persisted carry the previous all-false shape and won't pick up the new defaults
|
||||
-- without this rewrite. Only fields that are currently false are flipped — any operator-set
|
||||
-- true value is preserved.
|
||||
UPDATE dbservice_entity
|
||||
SET json = JSON_SET(json, '$.connection.config.policyAgentConfig.enabled', true)
|
||||
WHERE serviceType = 'Snowflake'
|
||||
AND JSON_EXTRACT(json, '$.connection.config.policyAgentConfig.enabled') = false;
|
||||
|
||||
UPDATE dbservice_entity
|
||||
SET json = JSON_SET(json, '$.connection.config.policyAgentConfig.supportsFullAccess', true)
|
||||
WHERE serviceType = 'Snowflake'
|
||||
AND JSON_EXTRACT(json, '$.connection.config.policyAgentConfig.supportsFullAccess') = false;
|
||||
|
||||
UPDATE dbservice_entity
|
||||
SET json = JSON_SET(json, '$.connection.config.policyAgentConfig.supportsMaskedAccess', true)
|
||||
WHERE serviceType = 'Snowflake'
|
||||
AND JSON_EXTRACT(json, '$.connection.config.policyAgentConfig.supportsMaskedAccess') = false;
|
||||
|
||||
-- Services that pre-date the policyAgentConfig field entirely (older rows where the whole
|
||||
-- object is missing) — write the full block in one shot. JSON_SET creates the key path.
|
||||
UPDATE dbservice_entity
|
||||
SET json = JSON_SET(
|
||||
json,
|
||||
'$.connection.config.policyAgentConfig',
|
||||
JSON_OBJECT(
|
||||
'enabled', true,
|
||||
'supportsColumnAccess', false,
|
||||
'supportsFullAccess', true,
|
||||
'supportsMaskedAccess', true
|
||||
)
|
||||
)
|
||||
WHERE serviceType = 'Snowflake'
|
||||
AND JSON_EXTRACT(json, '$.connection.config.policyAgentConfig') IS NULL;
|
||||
|
||||
-- Databricks: enabled/Full default to true, Column and Masked stay false.
|
||||
-- Two guarded flips + one full-object write for legacy rows.
|
||||
UPDATE dbservice_entity
|
||||
SET json = JSON_SET(json, '$.connection.config.policyAgentConfig.enabled', true)
|
||||
WHERE serviceType = 'Databricks'
|
||||
AND JSON_EXTRACT(json, '$.connection.config.policyAgentConfig.enabled') = false;
|
||||
|
||||
UPDATE dbservice_entity
|
||||
SET json = JSON_SET(json, '$.connection.config.policyAgentConfig.supportsFullAccess', true)
|
||||
WHERE serviceType = 'Databricks'
|
||||
AND JSON_EXTRACT(json, '$.connection.config.policyAgentConfig.supportsFullAccess') = false;
|
||||
|
||||
UPDATE dbservice_entity
|
||||
SET json = JSON_SET(
|
||||
json,
|
||||
'$.connection.config.policyAgentConfig',
|
||||
JSON_OBJECT(
|
||||
'enabled', true,
|
||||
'supportsColumnAccess', false,
|
||||
'supportsFullAccess', true,
|
||||
'supportsMaskedAccess', false
|
||||
)
|
||||
)
|
||||
WHERE serviceType = 'Databricks'
|
||||
AND JSON_EXTRACT(json, '$.connection.config.policyAgentConfig') IS NULL;
|
||||
|
||||
-- Corrective heal for instances that already ran the earlier version of this script.
|
||||
-- Earlier the Databricks block forced supportsMaskedAccess to true; the intended
|
||||
-- default is false. Reset it on every Databricks row that currently has it true.
|
||||
UPDATE dbservice_entity
|
||||
SET json = JSON_SET(json, '$.connection.config.policyAgentConfig.supportsMaskedAccess', false)
|
||||
WHERE serviceType = 'Databricks'
|
||||
AND JSON_EXTRACT(json, '$.connection.config.policyAgentConfig.supportsMaskedAccess') = true;
|
||||
|
||||
-- Postgres no longer declares policyAgentConfig. Earlier this script backfilled the
|
||||
-- object onto Postgres rows; remove it so the stored shape matches the schema.
|
||||
UPDATE dbservice_entity
|
||||
SET json = JSON_REMOVE(json, '$.connection.config.policyAgentConfig')
|
||||
WHERE serviceType = 'Postgres'
|
||||
AND JSON_EXTRACT(json, '$.connection.config.policyAgentConfig') IS NOT NULL;
|
||||
|
||||
-- Remove runtime-only fields (openMetadataServerConnection, privateConfiguration) from stored application data.
|
||||
UPDATE installed_apps
|
||||
SET json = JSON_REMOVE(json, '$.openMetadataServerConnection', '$.privateConfiguration')
|
||||
WHERE JSON_EXTRACT(json, '$.openMetadataServerConnection') IS NOT NULL
|
||||
OR JSON_EXTRACT(json, '$.privateConfiguration') IS NOT NULL;
|
||||
|
||||
UPDATE entity_extension
|
||||
SET json = JSON_REMOVE(json, '$.openMetadataServerConnection', '$.privateConfiguration')
|
||||
WHERE extension LIKE 'app.version.%'
|
||||
AND (JSON_EXTRACT(json, '$.openMetadataServerConnection') IS NOT NULL
|
||||
OR JSON_EXTRACT(json, '$.privateConfiguration') IS NOT NULL);
|
||||
@@ -0,0 +1,512 @@
|
||||
-- Task System Redesign - OpenMetadata 2.0.0
|
||||
-- This migration creates the new Task entity tables and related infrastructure
|
||||
|
||||
-- CSV import/export and bulk-edit background job metadata.
|
||||
ALTER TABLE background_jobs
|
||||
ADD COLUMN progress int DEFAULT 0,
|
||||
ADD COLUMN total int DEFAULT 0,
|
||||
ADD COLUMN result longtext,
|
||||
ADD COLUMN error longtext,
|
||||
ADD COLUMN message varchar(2048),
|
||||
ADD COLUMN cancelRequested boolean DEFAULT false,
|
||||
ADD COLUMN completedAt bigint;
|
||||
|
||||
CREATE INDEX idx_background_jobs_job_type_created_by
|
||||
ON background_jobs (jobType, createdBy, createdAt);
|
||||
|
||||
CREATE INDEX idx_background_jobs_status_updated_at
|
||||
ON background_jobs (status, updatedAt);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS background_job_logs (
|
||||
logId varchar(36) NOT NULL,
|
||||
jobId bigint unsigned NOT NULL,
|
||||
createdAt bigint NOT NULL,
|
||||
level varchar(16) NOT NULL,
|
||||
message varchar(4096) NOT NULL,
|
||||
PRIMARY KEY (logId),
|
||||
KEY idx_background_job_logs_job_id_created_at (jobId, createdAt),
|
||||
CONSTRAINT fk_background_job_logs_job_id
|
||||
FOREIGN KEY (jobId) REFERENCES background_jobs(id)
|
||||
ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS task_entity (
|
||||
id varchar(36) NOT NULL,
|
||||
json json NOT NULL,
|
||||
fqnHash varchar(768) NOT NULL,
|
||||
taskId varchar(20) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.taskId'))) STORED NOT NULL,
|
||||
name varchar(256) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.name'))) STORED NOT NULL,
|
||||
category varchar(32) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.category'))) STORED NOT NULL,
|
||||
type varchar(64) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.type'))) STORED NOT NULL,
|
||||
status varchar(32) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.status'))) STORED NOT NULL,
|
||||
priority varchar(16) GENERATED ALWAYS AS (COALESCE(json_unquote(json_extract(`json`,_utf8mb4'$.priority')), 'Medium')) STORED,
|
||||
createdAt bigint GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.createdAt'))) STORED NOT NULL,
|
||||
updatedAt bigint GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.updatedAt'))) STORED NOT NULL,
|
||||
deleted tinyint(1) GENERATED ALWAYS AS (json_extract(`json`,_utf8mb4'$.deleted')) STORED,
|
||||
aboutFqnHash varchar(256) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.aboutFqnHash'))) STORED,
|
||||
createdById varchar(36) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.createdById'))) STORED,
|
||||
approvedById varchar(36) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.approvedById'))) STORED,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_fqn_hash (fqnHash),
|
||||
KEY idx_task_id (taskId),
|
||||
KEY idx_status (status),
|
||||
KEY idx_category (category),
|
||||
KEY idx_type (type),
|
||||
KEY idx_priority (priority),
|
||||
KEY idx_created_at (createdAt),
|
||||
KEY idx_updated_at (updatedAt),
|
||||
KEY idx_deleted (deleted),
|
||||
KEY idx_status_category (status, category),
|
||||
KEY idx_about_fqn_hash (aboutFqnHash),
|
||||
KEY idx_status_about (status, aboutFqnHash),
|
||||
KEY idx_created_by_id (createdById),
|
||||
KEY idx_created_by_category (createdById, category),
|
||||
KEY idx_approved_by_id (approvedById)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
-- For 2.0.0 environments that ran the CREATE TABLE above before the
|
||||
-- approvedById generated column was added inline, attach it now. CREATE TABLE
|
||||
-- IF NOT EXISTS is a no-op on those environments so the column would never
|
||||
-- appear otherwise. MySQL doesn't reliably support `ADD COLUMN IF NOT EXISTS`
|
||||
-- across 8.0 versions and has no `ADD KEY IF NOT EXISTS`, so guard both via
|
||||
-- information_schema.
|
||||
SET @ddl = (
|
||||
SELECT IF(
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'task_entity'
|
||||
AND column_name = 'approvedById'
|
||||
),
|
||||
'SELECT 1',
|
||||
'ALTER TABLE task_entity ADD COLUMN approvedById varchar(36) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4''$.approvedById''))) STORED'
|
||||
)
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @ddl = (
|
||||
SELECT IF(
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'task_entity'
|
||||
AND index_name = 'idx_approved_by_id'
|
||||
),
|
||||
'SELECT 1',
|
||||
'ALTER TABLE task_entity ADD KEY idx_approved_by_id (approvedById)'
|
||||
)
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS new_task_sequence (
|
||||
id bigint NOT NULL DEFAULT 0
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
INSERT INTO new_task_sequence (id) SELECT 0 WHERE NOT EXISTS (SELECT 1 FROM new_task_sequence);
|
||||
|
||||
-- =====================================================
|
||||
-- ACTIVITY STREAM TABLE (Partitioned by time)
|
||||
-- Lightweight, ephemeral activity notifications
|
||||
-- NOT for audit/compliance - use entity version history
|
||||
-- Partitions are managed dynamically by ActivityStreamPartitionManager
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS activity_stream (
|
||||
id varchar(36) NOT NULL,
|
||||
eventType varchar(64) NOT NULL,
|
||||
entityType varchar(64) NOT NULL,
|
||||
entityId varchar(36) NOT NULL,
|
||||
entityFqnHash varchar(768) CHARACTER SET ascii COLLATE ascii_bin,
|
||||
about varchar(2048),
|
||||
aboutFqnHash varchar(768) CHARACTER SET ascii COLLATE ascii_bin,
|
||||
-- Nullable for system events and hard-deleted users; actorName is the display fallback.
|
||||
actorId varchar(36),
|
||||
actorName varchar(256),
|
||||
timestamp bigint NOT NULL,
|
||||
summary varchar(500),
|
||||
fieldName varchar(256),
|
||||
oldValue text,
|
||||
newValue text,
|
||||
domains json,
|
||||
json json NOT NULL,
|
||||
PRIMARY KEY (id, timestamp),
|
||||
KEY idx_activity_timestamp (timestamp),
|
||||
KEY idx_activity_entity (entityType, entityId, timestamp),
|
||||
KEY idx_activity_actor (actorId, timestamp),
|
||||
KEY idx_activity_event_type (eventType, timestamp),
|
||||
KEY idx_activity_entity_fqn (entityFqnHash, timestamp),
|
||||
KEY idx_activity_about (aboutFqnHash, timestamp)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
|
||||
PARTITION BY RANGE (timestamp) (
|
||||
-- Catch-all partition - ActivityStreamPartitionManager will reorganize this
|
||||
-- by splitting it into monthly partitions as needed
|
||||
PARTITION p_max VALUES LESS THAN MAXVALUE
|
||||
);
|
||||
|
||||
-- Activity stream configuration per domain
|
||||
CREATE TABLE IF NOT EXISTS activity_stream_config (
|
||||
id varchar(36) NOT NULL,
|
||||
json json NOT NULL,
|
||||
scope varchar(32) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.scope'))) STORED NOT NULL,
|
||||
domainId varchar(36) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.scopeReference.id'))) STORED,
|
||||
enabled tinyint(1) GENERATED ALWAYS AS (json_extract(`json`,_utf8mb4'$.enabled')) STORED,
|
||||
retentionDays int GENERATED ALWAYS AS (json_extract(`json`,_utf8mb4'$.retentionDays')) STORED,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_domain_config (domainId),
|
||||
KEY idx_scope (scope),
|
||||
KEY idx_enabled (enabled)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
-- =====================================================
|
||||
-- ANNOUNCEMENT ENTITY TABLE
|
||||
-- Standalone entity for asset announcements (migrated from thread_entity)
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS announcement_entity (
|
||||
id varchar(36) NOT NULL,
|
||||
json json NOT NULL,
|
||||
fqnHash varchar(768) NOT NULL,
|
||||
name varchar(256) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.name'))) STORED NOT NULL,
|
||||
entityLink varchar(512) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.entityLink'))) STORED,
|
||||
status varchar(32) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.status'))) STORED,
|
||||
startTime bigint GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.startTime'))) STORED,
|
||||
endTime bigint GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.endTime'))) STORED,
|
||||
createdBy varchar(256) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.createdBy'))) STORED,
|
||||
createdAt bigint GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.createdAt'))) STORED,
|
||||
updatedAt bigint GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.updatedAt'))) STORED,
|
||||
deleted tinyint(1) GENERATED ALWAYS AS (json_extract(`json`,_utf8mb4'$.deleted')) STORED,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_announcement_fqn_hash (fqnHash),
|
||||
KEY idx_announcement_status (status),
|
||||
KEY idx_announcement_entity_link (entityLink),
|
||||
KEY idx_announcement_start_time (startTime),
|
||||
KEY idx_announcement_end_time (endTime),
|
||||
KEY idx_announcement_deleted (deleted)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
-- =====================================================
|
||||
-- TASK FORM SCHEMA ENTITY TABLE
|
||||
-- Stores form schemas for different task types
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS task_form_schema_entity (
|
||||
id varchar(36) NOT NULL,
|
||||
json json NOT NULL,
|
||||
fqnHash varchar(768) NOT NULL,
|
||||
name varchar(256) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.name'))) STORED NOT NULL,
|
||||
taskType varchar(64) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.taskType'))) STORED,
|
||||
taskCategory varchar(32) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.taskCategory'))) STORED,
|
||||
updatedAt bigint GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.updatedAt'))) STORED,
|
||||
deleted tinyint(1) GENERATED ALWAYS AS (json_extract(`json`,_utf8mb4'$.deleted')) STORED,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_task_form_schema_fqn_hash (fqnHash),
|
||||
KEY idx_task_form_schema_name (name),
|
||||
KEY idx_task_form_schema_task_type (taskType),
|
||||
KEY idx_task_form_schema_deleted (deleted)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
-- =====================================================
|
||||
-- KNOWLEDGE CENTER + CONTEXT CENTER DRIVE (Collate → OM port)
|
||||
-- Appended below the Task Redesign tables to preserve main's
|
||||
-- migration order when merging.
|
||||
-- =====================================================
|
||||
|
||||
-- MCP tables are created in 1.13.0 migration.
|
||||
|
||||
-- Knowledge Center: page entity table (Article, QuickLink).
|
||||
-- Existing Collate customers already have this table from 1.2.0-collate with
|
||||
-- subsequent shape changes through 1.6.0-collate (nameHash -> fqnHash VARCHAR(756),
|
||||
-- pageType generated column, composite deleted index). CREATE TABLE IF NOT EXISTS
|
||||
-- is a no-op for them and creates the final shape for fresh OpenMetadata installs.
|
||||
CREATE TABLE IF NOT EXISTS knowledge_center (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> '$.id') STORED NOT NULL,
|
||||
fqnHash VARCHAR(756) NOT NULL COLLATE ascii_bin,
|
||||
name VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.name') STORED NOT NULL,
|
||||
json JSON NOT NULL,
|
||||
updatedAt BIGINT UNSIGNED GENERATED ALWAYS AS (json ->> '$.updatedAt') STORED NOT NULL,
|
||||
updatedBy VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.updatedBy') STORED NOT NULL,
|
||||
deleted BOOLEAN GENERATED ALWAYS AS (json -> '$.deleted') STORED,
|
||||
pageType VARCHAR(16) GENERATED ALWAYS AS (json ->> '$.pageType') STORED NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE (fqnHash),
|
||||
INDEX knowledge_center_name_index (name),
|
||||
INDEX index_knowledge_center_deleted (fqnHash, deleted)
|
||||
);
|
||||
|
||||
-- Context Center Drive: Folder entity table.
|
||||
CREATE TABLE IF NOT EXISTS drive_folder (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> '$.id') STORED NOT NULL,
|
||||
name VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.name') STORED NOT NULL,
|
||||
nameHash VARCHAR(256) NOT NULL COLLATE ascii_bin,
|
||||
json JSON NOT NULL,
|
||||
updatedAt BIGINT UNSIGNED GENERATED ALWAYS AS (json ->> '$.updatedAt') STORED NOT NULL,
|
||||
updatedBy VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.updatedBy') STORED NOT NULL,
|
||||
deleted BOOLEAN GENERATED ALWAYS AS (json -> '$.deleted') STORED,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY unique_drive_folder_name (nameHash),
|
||||
INDEX idx_drive_folder_updated_at (updatedAt)
|
||||
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
-- Context Center Drive: File entity table (uploaded PDF/image/spreadsheet/office docs).
|
||||
CREATE TABLE IF NOT EXISTS context_file (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> '$.id') STORED NOT NULL,
|
||||
name VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.name') STORED NOT NULL,
|
||||
nameHash VARCHAR(256) NOT NULL COLLATE ascii_bin,
|
||||
json JSON NOT NULL,
|
||||
updatedAt BIGINT UNSIGNED GENERATED ALWAYS AS (json ->> '$.updatedAt') STORED NOT NULL,
|
||||
updatedBy VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.updatedBy') STORED NOT NULL,
|
||||
deleted BOOLEAN GENERATED ALWAYS AS (json -> '$.deleted') STORED,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY unique_context_file_name (nameHash),
|
||||
INDEX idx_context_file_updated_at (updatedAt)
|
||||
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
-- Attachments: Asset entity table for uploaded file blobs referenced by ContextFiles, Pages, etc.
|
||||
-- Existing Collate customers have this from 1.7.0-collate. CREATE TABLE IF NOT EXISTS is a no-op for them.
|
||||
CREATE TABLE IF NOT EXISTS asset_entity (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> '$.id') STORED NOT NULL,
|
||||
name VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.fileName') STORED NOT NULL,
|
||||
url VARCHAR(1024) GENERATED ALWAYS AS (json ->> '$.url') STORED NOT NULL,
|
||||
fullyQualifiedName VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.fullyQualifiedName') STORED NOT NULL,
|
||||
assetType VARCHAR(100) GENERATED ALWAYS AS (json ->> '$.assetType') STORED NOT NULL,
|
||||
json JSON NOT NULL,
|
||||
updatedAt BIGINT UNSIGNED GENERATED ALWAYS AS (json ->> '$.updatedAt') STORED NOT NULL,
|
||||
updatedBy VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.updatedBy') STORED NOT NULL,
|
||||
fqnHash VARCHAR(768) CHARACTER SET ascii COLLATE ascii_bin DEFAULT NULL,
|
||||
deleted BOOLEAN GENERATED ALWAYS AS (json -> '$.deleted') STORED,
|
||||
PRIMARY KEY (id),
|
||||
INDEX fqnhash_index (fqnHash),
|
||||
INDEX asset_type_index (assetType),
|
||||
INDEX idx_asset_deleted (deleted)
|
||||
);
|
||||
|
||||
-- Context Center Drive: File content snapshot table (revisions, extracted text).
|
||||
CREATE TABLE IF NOT EXISTS context_file_content (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> '$.id') STORED NOT NULL,
|
||||
name VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.name') STORED NOT NULL,
|
||||
nameHash VARCHAR(256) NOT NULL COLLATE ascii_bin,
|
||||
json JSON NOT NULL,
|
||||
updatedAt BIGINT UNSIGNED GENERATED ALWAYS AS (json ->> '$.updatedAt') STORED NOT NULL,
|
||||
updatedBy VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.updatedBy') STORED NOT NULL,
|
||||
deleted BOOLEAN GENERATED ALWAYS AS (json -> '$.deleted') STORED,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY unique_context_file_content_name (nameHash),
|
||||
INDEX idx_context_file_content_updated_at (updatedAt)
|
||||
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
-- Add tag_usage.metadata column if missing (newer tag usage payloads carry metadata).
|
||||
SET @ddl = (
|
||||
SELECT IF(
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'tag_usage'
|
||||
AND column_name = 'metadata'
|
||||
),
|
||||
'SELECT 1',
|
||||
'ALTER TABLE tag_usage ADD COLUMN metadata JSON NULL'
|
||||
)
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- Add audit_log_event.search_text column if missing (searchable audit log text).
|
||||
SET @ddl = (
|
||||
SELECT IF(
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'audit_log_event'
|
||||
AND column_name = 'search_text'
|
||||
),
|
||||
'SELECT 1',
|
||||
'ALTER TABLE audit_log_event ADD COLUMN search_text LONGTEXT NULL'
|
||||
)
|
||||
);
|
||||
PREPARE stmt FROM @ddl;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- Distributed reindex job tracking.
|
||||
CREATE TABLE IF NOT EXISTS search_index_job (
|
||||
id VARCHAR(64) NOT NULL,
|
||||
status VARCHAR(64) NOT NULL,
|
||||
jobConfiguration JSON NOT NULL,
|
||||
targetIndexPrefix VARCHAR(256) NOT NULL,
|
||||
stagedIndexMapping JSON DEFAULT NULL,
|
||||
totalRecords BIGINT NOT NULL DEFAULT 0,
|
||||
processedRecords BIGINT NOT NULL DEFAULT 0,
|
||||
successRecords BIGINT NOT NULL DEFAULT 0,
|
||||
failedRecords BIGINT NOT NULL DEFAULT 0,
|
||||
stats JSON NOT NULL,
|
||||
createdBy VARCHAR(256) NOT NULL,
|
||||
createdAt BIGINT NOT NULL,
|
||||
startedAt BIGINT DEFAULT NULL,
|
||||
completedAt BIGINT DEFAULT NULL,
|
||||
updatedAt BIGINT NOT NULL,
|
||||
errorMessage LONGTEXT DEFAULT NULL,
|
||||
registrationDeadline BIGINT DEFAULT NULL,
|
||||
registeredServerCount INT DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_search_index_job_status_created_at (status, createdAt DESC)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
-- Retry queue for failed search-index writes.
|
||||
CREATE TABLE IF NOT EXISTS search_index_retry_queue (
|
||||
entityId VARCHAR(64) NOT NULL,
|
||||
entityFqn VARCHAR(700) NOT NULL,
|
||||
failureReason LONGTEXT DEFAULT NULL,
|
||||
status VARCHAR(64) NOT NULL,
|
||||
entityType VARCHAR(128) NOT NULL,
|
||||
retryCount INT NOT NULL DEFAULT 0,
|
||||
claimedAt TIMESTAMP NULL DEFAULT NULL,
|
||||
PRIMARY KEY (entityId, entityFqn),
|
||||
KEY idx_search_index_retry_queue_status (status),
|
||||
KEY idx_search_index_retry_queue_claimed_at (claimedAt)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
-- ContextMemory entity - reusable Context Center memory.
|
||||
CREATE TABLE IF NOT EXISTS context_memory (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> '$.id') STORED NOT NULL,
|
||||
name VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.name') STORED NOT NULL,
|
||||
nameHash VARCHAR(256) NOT NULL COLLATE ascii_bin,
|
||||
json JSON NOT NULL,
|
||||
updatedAt BIGINT UNSIGNED GENERATED ALWAYS AS (json ->> '$.updatedAt') STORED NOT NULL,
|
||||
updatedBy VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.updatedBy') STORED NOT NULL,
|
||||
deleted BOOLEAN GENERATED ALWAYS AS (json -> '$.deleted') STORED,
|
||||
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY unique_context_memory_name (nameHash),
|
||||
INDEX idx_context_memory_updated_at (updatedAt)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
-- AI Governance Studio (Phase 4): framework + control entity tables.
|
||||
CREATE TABLE IF NOT EXISTS ai_governance_framework_entity (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> '$.id') STORED NOT NULL,
|
||||
name VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.name') NOT NULL,
|
||||
fqnHash VARCHAR(768) NOT NULL,
|
||||
json JSON NOT NULL,
|
||||
updatedAt BIGINT UNSIGNED GENERATED ALWAYS AS (json ->> '$.updatedAt') NOT NULL,
|
||||
updatedBy VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.updatedBy') NOT NULL,
|
||||
impersonatedBy VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.impersonatedBy') VIRTUAL,
|
||||
deleted BOOLEAN GENERATED ALWAYS AS (JSON_EXTRACT(json, '$.deleted')),
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY unique_name (fqnHash),
|
||||
INDEX name_index (name),
|
||||
INDEX deleted_index (deleted)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='AI Governance Framework entities';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ai_framework_control_entity (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> '$.id') STORED NOT NULL,
|
||||
name VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.name') NOT NULL,
|
||||
fqnHash VARCHAR(768) NOT NULL,
|
||||
json JSON NOT NULL,
|
||||
updatedAt BIGINT UNSIGNED GENERATED ALWAYS AS (json ->> '$.updatedAt') NOT NULL,
|
||||
updatedBy VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.updatedBy') NOT NULL,
|
||||
impersonatedBy VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.impersonatedBy') VIRTUAL,
|
||||
deleted BOOLEAN GENERATED ALWAYS AS (JSON_EXTRACT(json, '$.deleted')),
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY unique_name (fqnHash),
|
||||
INDEX name_index (name),
|
||||
INDEX deleted_index (deleted)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='AI Framework Control entities';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS audit_report_entity (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> '$.id') STORED NOT NULL,
|
||||
name VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.name') NOT NULL,
|
||||
fqnHash VARCHAR(768) NOT NULL,
|
||||
json JSON NOT NULL,
|
||||
updatedAt BIGINT UNSIGNED GENERATED ALWAYS AS (json ->> '$.updatedAt') NOT NULL,
|
||||
updatedBy VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.updatedBy') NOT NULL,
|
||||
impersonatedBy VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.impersonatedBy') VIRTUAL,
|
||||
deleted BOOLEAN GENERATED ALWAYS AS (JSON_EXTRACT(json, '$.deleted')),
|
||||
status VARCHAR(32) GENERATED ALWAYS AS (json ->> '$.status') VIRTUAL,
|
||||
requestSignature VARCHAR(512) GENERATED ALWAYS AS (json ->> '$.requestSignature') STORED,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY unique_name (fqnHash),
|
||||
INDEX name_index (name),
|
||||
INDEX status_index (status),
|
||||
INDEX deleted_index (deleted),
|
||||
INDEX request_signature_index (requestSignature)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='AI Audit Report entities';
|
||||
-- Database-backed user session store for multi-pod session management (issue #21971).
|
||||
CREATE TABLE IF NOT EXISTS `user_session` (
|
||||
`id` varchar(64) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.id'))) STORED NOT NULL,
|
||||
`userId` varchar(36) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.userId'))) STORED,
|
||||
`status` varchar(32) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.status'))) STORED NOT NULL,
|
||||
`expiresAt` bigint unsigned GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.expiresAt'))) STORED NOT NULL,
|
||||
`idleExpiresAt` bigint unsigned GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.idleExpiresAt'))) STORED NOT NULL,
|
||||
`updatedAt` bigint unsigned GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.updatedAt'))) STORED NOT NULL,
|
||||
`sessionType` varchar(32) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.type'))) VIRTUAL,
|
||||
`provider` varchar(64) GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.provider'))) VIRTUAL,
|
||||
`version` bigint unsigned GENERATED ALWAYS AS (json_unquote(json_extract(`json`,_utf8mb4'$.version'))) VIRTUAL,
|
||||
`lastAccessedAt` bigint unsigned GENERATED ALWAYS AS (nullif(json_unquote(json_extract(`json`,_utf8mb4'$.lastAccessedAt')),'null')) VIRTUAL,
|
||||
`refreshLeaseUntil` bigint unsigned GENERATED ALWAYS AS (nullif(json_unquote(json_extract(`json`,_utf8mb4'$.refreshLeaseUntil')),'null')) VIRTUAL,
|
||||
`json` json NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `user_session_user_status` (`userId`,`status`),
|
||||
KEY `user_session_expiry` (`status`,`expiresAt`),
|
||||
KEY `user_session_idle_expiry` (`status`,`idleExpiresAt`),
|
||||
KEY `user_session_prune` (`status`,`updatedAt`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
-- Per-entity `name` index for entity tables first created in 2.0.0, so the
|
||||
-- distributed reindex's `... ORDER BY name, id LIMIT 1 OFFSET :n` cursor query
|
||||
-- (EntityRepository.getCursorAtOffset) runs index-only instead of a filesort that
|
||||
-- can exhaust sort memory (ER_OUT_OF_SORTMEMORY) on large tables. Added here rather
|
||||
-- than in 1.13.1 because these tables are created above, in this same 2.0.0 migration.
|
||||
CREATE INDEX task_entity_name_index ON task_entity (name);
|
||||
CREATE INDEX announcement_entity_name_index ON announcement_entity (name);
|
||||
CREATE INDEX drive_folder_name_index ON drive_folder (name);
|
||||
CREATE INDEX asset_entity_name_index ON asset_entity (name);
|
||||
-- context_file / context_memory are also created above in this migration and are reindexed;
|
||||
-- they only had a `nameHash` unique key, so add the leading-`name` index the cursor query needs.
|
||||
CREATE INDEX context_file_name_index ON context_file (name);
|
||||
CREATE INDEX context_memory_name_index ON context_memory (name);
|
||||
|
||||
-- MCP conversation table for MCP Client message tracking
|
||||
CREATE TABLE IF NOT EXISTS mcp_conversation (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> '$.id') STORED NOT NULL,
|
||||
json JSON NOT NULL,
|
||||
userId VARCHAR(256) GENERATED ALWAYS AS (json ->> '$.user.id') NOT NULL,
|
||||
createdAt BIGINT UNSIGNED GENERATED ALWAYS AS (json ->> '$.createdAt') NOT NULL,
|
||||
updatedAt BIGINT UNSIGNED GENERATED ALWAYS AS (json ->> '$.updatedAt') NOT NULL,
|
||||
createdBy VARCHAR(50) GENERATED ALWAYS AS (json ->> '$.createdBy') NOT NULL,
|
||||
updatedBy VARCHAR(50) GENERATED ALWAYS AS (json ->> '$.updatedBy') NOT NULL,
|
||||
messageCount INT GENERATED ALWAYS AS (json ->> '$.messageCount') STORED,
|
||||
|
||||
PRIMARY KEY (id),
|
||||
INDEX idx_mcp_conversation_user_updated (userId, updatedAt DESC)
|
||||
);
|
||||
|
||||
-- MCP message table for MCP Client message tracking
|
||||
CREATE TABLE IF NOT EXISTS mcp_message (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> '$.id') STORED NOT NULL,
|
||||
json JSON NOT NULL,
|
||||
conversationId VARCHAR(36) GENERATED ALWAYS AS (json ->> '$.conversationId') STORED NOT NULL,
|
||||
sender VARCHAR(10) GENERATED ALWAYS AS (json ->> '$.sender') STORED NOT NULL,
|
||||
messageIndex INT GENERATED ALWAYS AS (json ->> '$.index') STORED,
|
||||
timestamp BIGINT UNSIGNED GENERATED ALWAYS AS (json ->> '$.timestamp') NOT NULL,
|
||||
|
||||
PRIMARY KEY (id),
|
||||
CONSTRAINT fk_mcp_message_conversation FOREIGN KEY (conversationId) REFERENCES mcp_conversation(id) ON DELETE CASCADE,
|
||||
INDEX idx_mcp_message_conversation_index (conversationId, messageIndex),
|
||||
INDEX idx_mcp_message_conversation_created (conversationId, timestamp)
|
||||
);
|
||||
-- Task workflow cutover support - OpenMetadata 2.0.0 (moved from 2.0.1)
|
||||
-- Maps legacy thread task IDs to new task entity IDs for migration traceability and redirects.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS task_migration_mapping (
|
||||
old_thread_id varchar(36) NOT NULL,
|
||||
new_task_id varchar(36) NOT NULL,
|
||||
migrated_at bigint NOT NULL,
|
||||
source varchar(64) DEFAULT 'thread_task_migration',
|
||||
PRIMARY KEY (old_thread_id),
|
||||
KEY idx_task_migration_mapping_new_task_id (new_task_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
@@ -0,0 +1,248 @@
|
||||
-- Post data migration script for Task System Redesign - OpenMetadata 2.0.0
|
||||
-- This script runs after the data migration completes
|
||||
|
||||
-- =====================================================
|
||||
-- NOTE: Suggestion migration (suggestions → task_entity),
|
||||
-- thread-based task migration (thread_entity → task_entity),
|
||||
-- and legacy system activity migration
|
||||
-- (thread_entity generated feed rows → activity_stream)
|
||||
-- are handled in Java MigrationUtil because they require
|
||||
-- entity-link aware transformation logic.
|
||||
-- =====================================================
|
||||
|
||||
-- =====================================================
|
||||
-- PHASE 2D: Migrate announcements from thread_entity → announcement_entity
|
||||
-- =====================================================
|
||||
|
||||
INSERT INTO announcement_entity (id, json, fqnhash)
|
||||
SELECT
|
||||
json->>'id' AS id,
|
||||
jsonb_build_object(
|
||||
'id', json->>'id',
|
||||
'name', 'announcement-' || (json->>'id'),
|
||||
'fullyQualifiedName', 'announcement-' || (json->>'id'),
|
||||
'displayName', NULLIF(json->>'message', ''),
|
||||
'description', COALESCE(
|
||||
json->'announcement'->>'description',
|
||||
json->>'message',
|
||||
''
|
||||
),
|
||||
'entityLink', json->>'about',
|
||||
'startTime', (json->'announcement'->>'startTime')::bigint,
|
||||
'endTime', (json->'announcement'->>'endTime')::bigint,
|
||||
'status', CASE
|
||||
WHEN (json->'announcement'->>'endTime')::bigint < (extract(epoch from now()) * 1000)::bigint
|
||||
THEN 'Expired'
|
||||
WHEN (json->'announcement'->>'startTime')::bigint > (extract(epoch from now()) * 1000)::bigint
|
||||
THEN 'Scheduled'
|
||||
ELSE 'Active'
|
||||
END,
|
||||
'createdBy', json->>'createdBy',
|
||||
'updatedBy', COALESCE(json->>'updatedBy', json->>'createdBy'),
|
||||
'createdAt', (json->>'threadTs')::bigint,
|
||||
'updatedAt', COALESCE((json->>'updatedAt')::bigint, (json->>'threadTs')::bigint),
|
||||
'deleted', false,
|
||||
'version', 0.1,
|
||||
'reactions', COALESCE(json->'reactions', '[]'::jsonb)
|
||||
) AS json,
|
||||
md5('announcement-' || (json->>'id')) AS fqnhash
|
||||
FROM thread_entity t
|
||||
WHERE json->>'type' = 'Announcement'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM announcement_entity a WHERE a.id = t.json->>'id'
|
||||
)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- =====================================================
|
||||
-- PHASE 2E: Rename legacy thread storage to fail stale references
|
||||
-- =====================================================
|
||||
ALTER TABLE IF EXISTS thread_entity RENAME TO thread_entity_legacy;
|
||||
|
||||
-- =====================================================
|
||||
-- PHASE 2F: Lower workflow trigger polling intervals
|
||||
-- =====================================================
|
||||
-- Reduce WorkflowEventConsumer poll interval from 10s to 1s.
|
||||
-- The legacy 10s default added up to a 10s wait between an entity change and the
|
||||
-- workflow-triggered approval task being created. On CI under resource pressure this
|
||||
-- often drifted to >2 minutes when combined with Flowable's 60s async job poll. The
|
||||
-- new value keeps the trigger pipeline near-real-time.
|
||||
UPDATE event_subscription_entity
|
||||
SET json = jsonb_set(json, '{pollInterval}', '1'::jsonb)
|
||||
WHERE name = 'WorkflowEventConsumer'
|
||||
AND (json->>'pollInterval')::int > 1;
|
||||
|
||||
-- Lower Flowable async/timer job acquisition intervals to keep workflow-driven
|
||||
-- task creation responsive. The previous 60s default was a Flowable production setting
|
||||
-- carried over verbatim; for OpenMetadata's interactive task UX we want sub-second pickup.
|
||||
UPDATE openmetadata_settings
|
||||
SET json = jsonb_set(
|
||||
jsonb_set(json, '{executorConfiguration,asyncJobAcquisitionInterval}', '1000'::jsonb),
|
||||
'{executorConfiguration,timerJobAcquisitionInterval}', '5000'::jsonb)
|
||||
WHERE configtype = 'workflowSettings'
|
||||
AND json->'executorConfiguration' IS NOT NULL
|
||||
AND ((json->'executorConfiguration'->>'asyncJobAcquisitionInterval')::int > 1000
|
||||
OR (json->'executorConfiguration'->>'timerJobAcquisitionInterval')::int > 5000);
|
||||
|
||||
-- pipelineStatuses is a derived field, read on demand from entity_extension_time_series, and is
|
||||
-- now an array instead of a single object. Strip any stale single-object value that a GET->PUT
|
||||
-- round-trip may have persisted into the stored entity JSON so it cannot break deserialization.
|
||||
UPDATE ingestion_pipeline_entity
|
||||
SET json = (json::jsonb #- '{pipelineStatuses}')::json
|
||||
WHERE json::jsonb #> '{pipelineStatuses}' IS NOT NULL;
|
||||
|
||||
-- MCP Server and MCP Chat are no longer internal Applications; their enablement now lives in
|
||||
-- platform settings (mcpConfiguration). The MCP Chat app was never shipped to customers, so
|
||||
-- aiSettings.mcpChat keeps its seeded default shape (no config carry-over).
|
||||
|
||||
-- MCP Server: keep it disabled if the server app was not installed (mcpConfiguration defaults
|
||||
-- to enabled=true, which would otherwise turn it on).
|
||||
UPDATE openmetadata_settings
|
||||
SET json = jsonb_set(json, '{enabled}', 'false'::jsonb)
|
||||
WHERE configtype = 'mcpConfiguration'
|
||||
AND NOT EXISTS (SELECT 1 FROM installed_apps ia WHERE ia.name = 'McpApplication');
|
||||
|
||||
-- Retire the MCP apps (their Java classes and marketplace seeds are removed). Keep the bot users
|
||||
-- so the MCP server keeps its principal.
|
||||
DELETE FROM entity_relationship er USING installed_apps ia
|
||||
WHERE (er.fromId = ia.id OR er.toId = ia.id) AND ia.name IN ('McpApplication', 'McpChatApplication');
|
||||
DELETE FROM entity_relationship er USING apps_marketplace ia
|
||||
WHERE (er.fromId = ia.id OR er.toId = ia.id) AND ia.name IN ('McpApplication', 'McpChatApplication');
|
||||
DELETE FROM installed_apps WHERE name IN ('McpApplication', 'McpChatApplication');
|
||||
DELETE FROM apps_marketplace WHERE name IN ('McpApplication', 'McpChatApplication');
|
||||
|
||||
-- Post data migration script for Task workflow cutover - OpenMetadata 2.0.0 (moved from 2.0.1)
|
||||
|
||||
-- RdfIndexApp: switch to weekly Saturday cron and full-rebuild every run.
|
||||
-- Previous defaults (daily, incremental) were producing unbounded triple growth
|
||||
-- because relationship-removal paths weren't fully reconciled. With per-run
|
||||
-- CLEAR ALL the dataset always converges to MySQL state; weekly cadence keeps
|
||||
-- per-run cost from saturating Fuseki.
|
||||
--
|
||||
-- Also rewrite `entities` to `["all"]`. Pre-upgrade, an operator could have
|
||||
-- narrowed RDF indexing to a subset of entity types; the new recreateIndex=true
|
||||
-- semantics issues a CLEAR ALL before indexing, which would otherwise wipe
|
||||
-- triples for entity types still in MySQL but missing from the subset list.
|
||||
-- Forcing the subset list back to `["all"]` ensures the post-CLEAR-ALL run
|
||||
-- repopulates the graph fully; operators can re-narrow after the migration if
|
||||
-- they need partial indexing.
|
||||
UPDATE installed_apps
|
||||
SET json = jsonb_set(
|
||||
jsonb_set(
|
||||
jsonb_set(json::jsonb, '{appConfiguration,recreateIndex}', 'true'),
|
||||
'{appSchedule,cronExpression}',
|
||||
'"0 0 * * 6"'
|
||||
),
|
||||
'{appConfiguration,entities}',
|
||||
'["all"]'::jsonb
|
||||
)
|
||||
WHERE name = 'RdfIndexApp';
|
||||
|
||||
UPDATE apps_marketplace
|
||||
SET json = jsonb_set(json::jsonb, '{appConfiguration,recreateIndex}', 'true')
|
||||
WHERE name = 'RdfIndexApp';
|
||||
|
||||
-- Backfill policyAgentConfig defaults on existing Snowflake services. The schema-level
|
||||
-- defaults in snowflakeConnection.json only apply at create-time deserialization; rows
|
||||
-- already persisted carry the previous all-false shape and won't pick up the new defaults
|
||||
-- without this rewrite. Only fields that are currently false are flipped — any operator-set
|
||||
-- true value is preserved.
|
||||
UPDATE dbservice_entity
|
||||
SET json = jsonb_set(
|
||||
json::jsonb,
|
||||
'{connection,config,policyAgentConfig,enabled}',
|
||||
to_jsonb(true)
|
||||
)
|
||||
WHERE serviceType = 'Snowflake'
|
||||
AND json::jsonb #> '{connection,config,policyAgentConfig,enabled}' = 'false'::jsonb;
|
||||
|
||||
UPDATE dbservice_entity
|
||||
SET json = jsonb_set(
|
||||
json::jsonb,
|
||||
'{connection,config,policyAgentConfig,supportsFullAccess}',
|
||||
to_jsonb(true)
|
||||
)
|
||||
WHERE serviceType = 'Snowflake'
|
||||
AND json::jsonb #> '{connection,config,policyAgentConfig,supportsFullAccess}' = 'false'::jsonb;
|
||||
|
||||
UPDATE dbservice_entity
|
||||
SET json = jsonb_set(
|
||||
json::jsonb,
|
||||
'{connection,config,policyAgentConfig,supportsMaskedAccess}',
|
||||
to_jsonb(true)
|
||||
)
|
||||
WHERE serviceType = 'Snowflake'
|
||||
AND json::jsonb #> '{connection,config,policyAgentConfig,supportsMaskedAccess}' = 'false'::jsonb;
|
||||
|
||||
-- Services that pre-date the policyAgentConfig field entirely (older rows where the whole
|
||||
-- object is missing) — write the full block in one shot. `jsonb_set(..., true)` creates the
|
||||
-- key if absent.
|
||||
UPDATE dbservice_entity
|
||||
SET json = jsonb_set(
|
||||
json::jsonb,
|
||||
'{connection,config,policyAgentConfig}',
|
||||
'{"enabled":true,"supportsColumnAccess":false,"supportsFullAccess":true,"supportsMaskedAccess":true}'::jsonb,
|
||||
true
|
||||
)
|
||||
WHERE serviceType = 'Snowflake'
|
||||
AND json::jsonb #> '{connection,config,policyAgentConfig}' IS NULL;
|
||||
|
||||
-- Databricks: enabled/Full default to true, Column and Masked stay false.
|
||||
-- Two guarded flips + one full-object write for legacy rows.
|
||||
UPDATE dbservice_entity
|
||||
SET json = jsonb_set(
|
||||
json::jsonb,
|
||||
'{connection,config,policyAgentConfig,enabled}',
|
||||
to_jsonb(true)
|
||||
)
|
||||
WHERE serviceType = 'Databricks'
|
||||
AND json::jsonb #> '{connection,config,policyAgentConfig,enabled}' = 'false'::jsonb;
|
||||
|
||||
UPDATE dbservice_entity
|
||||
SET json = jsonb_set(
|
||||
json::jsonb,
|
||||
'{connection,config,policyAgentConfig,supportsFullAccess}',
|
||||
to_jsonb(true)
|
||||
)
|
||||
WHERE serviceType = 'Databricks'
|
||||
AND json::jsonb #> '{connection,config,policyAgentConfig,supportsFullAccess}' = 'false'::jsonb;
|
||||
|
||||
UPDATE dbservice_entity
|
||||
SET json = jsonb_set(
|
||||
json::jsonb,
|
||||
'{connection,config,policyAgentConfig}',
|
||||
'{"enabled":true,"supportsColumnAccess":false,"supportsFullAccess":true,"supportsMaskedAccess":false}'::jsonb,
|
||||
true
|
||||
)
|
||||
WHERE serviceType = 'Databricks'
|
||||
AND json::jsonb #> '{connection,config,policyAgentConfig}' IS NULL;
|
||||
|
||||
-- Corrective heal for instances that already ran the earlier version of this script.
|
||||
-- Earlier the Databricks block forced supportsMaskedAccess to true; the intended
|
||||
-- default is false. Reset it on every Databricks row that currently has it true.
|
||||
UPDATE dbservice_entity
|
||||
SET json = jsonb_set(
|
||||
json::jsonb,
|
||||
'{connection,config,policyAgentConfig,supportsMaskedAccess}',
|
||||
to_jsonb(false)
|
||||
)
|
||||
WHERE serviceType = 'Databricks'
|
||||
AND json::jsonb #> '{connection,config,policyAgentConfig,supportsMaskedAccess}' = 'true'::jsonb;
|
||||
|
||||
-- Postgres no longer declares policyAgentConfig. Earlier this script backfilled the
|
||||
-- object onto Postgres rows; remove it so the stored shape matches the schema.
|
||||
UPDATE dbservice_entity
|
||||
SET json = (json::jsonb #- '{connection,config,policyAgentConfig}')
|
||||
WHERE serviceType = 'Postgres'
|
||||
AND json::jsonb #> '{connection,config,policyAgentConfig}' IS NOT NULL;
|
||||
|
||||
-- Remove runtime-only fields (openMetadataServerConnection, privateConfiguration) from stored application data.
|
||||
UPDATE installed_apps
|
||||
SET json = (json::jsonb - 'openMetadataServerConnection' - 'privateConfiguration')
|
||||
WHERE jsonb_exists(json::jsonb, 'openMetadataServerConnection')
|
||||
OR jsonb_exists(json::jsonb, 'privateConfiguration');
|
||||
|
||||
UPDATE entity_extension
|
||||
SET json = (json::jsonb - 'openMetadataServerConnection' - 'privateConfiguration')
|
||||
WHERE extension LIKE 'app.version.%'
|
||||
AND (jsonb_exists(json::jsonb, 'openMetadataServerConnection')
|
||||
OR jsonb_exists(json::jsonb, 'privateConfiguration'));
|
||||
@@ -0,0 +1,471 @@
|
||||
-- Task System Redesign - OpenMetadata 2.0.0
|
||||
-- This migration creates the new Task entity tables and related infrastructure
|
||||
|
||||
-- CSV import/export and bulk-edit background job metadata.
|
||||
ALTER TABLE background_jobs
|
||||
ADD COLUMN IF NOT EXISTS progress integer DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS total integer DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS result text,
|
||||
ADD COLUMN IF NOT EXISTS error text,
|
||||
ADD COLUMN IF NOT EXISTS message character varying(2048),
|
||||
ADD COLUMN IF NOT EXISTS cancelRequested boolean DEFAULT false,
|
||||
ADD COLUMN IF NOT EXISTS completedAt bigint;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_background_jobs_job_type_created_by
|
||||
ON background_jobs (jobType, createdBy, createdAt);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_background_jobs_status_updated_at
|
||||
ON background_jobs (status, updatedAt);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS background_job_logs (
|
||||
logId character varying(36) NOT NULL,
|
||||
jobId bigint NOT NULL,
|
||||
createdAt bigint NOT NULL,
|
||||
level character varying(16) NOT NULL,
|
||||
message character varying(4096) NOT NULL,
|
||||
PRIMARY KEY (logId),
|
||||
CONSTRAINT fk_background_job_logs_job_id
|
||||
FOREIGN KEY (jobId) REFERENCES background_jobs(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_background_job_logs_job_id_created_at
|
||||
ON background_job_logs (jobId, createdAt);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS task_entity (
|
||||
id character varying(36) NOT NULL,
|
||||
json jsonb NOT NULL,
|
||||
fqnhash character varying(768) NOT NULL,
|
||||
taskid character varying(20) GENERATED ALWAYS AS ((json ->> 'taskId'::text)) STORED NOT NULL,
|
||||
name character varying(256) GENERATED ALWAYS AS ((json ->> 'name'::text)) STORED NOT NULL,
|
||||
category character varying(32) GENERATED ALWAYS AS ((json ->> 'category'::text)) STORED NOT NULL,
|
||||
type character varying(64) GENERATED ALWAYS AS ((json ->> 'type'::text)) STORED NOT NULL,
|
||||
status character varying(32) GENERATED ALWAYS AS ((json ->> 'status'::text)) STORED NOT NULL,
|
||||
priority character varying(16) GENERATED ALWAYS AS (COALESCE((json ->> 'priority'::text), 'Medium'::text)) STORED,
|
||||
createdat bigint GENERATED ALWAYS AS (((json ->> 'createdAt'::text))::bigint) STORED NOT NULL,
|
||||
updatedat bigint GENERATED ALWAYS AS (((json ->> 'updatedAt'::text))::bigint) STORED NOT NULL,
|
||||
deleted boolean GENERATED ALWAYS AS (((json ->> 'deleted'::text))::boolean) STORED,
|
||||
aboutfqnhash character varying(256) GENERATED ALWAYS AS ((json ->> 'aboutFqnHash'::text)) STORED,
|
||||
createdbyid character varying(36) GENERATED ALWAYS AS ((json ->> 'createdById'::text)) STORED,
|
||||
approvedbyid character varying(36) GENERATED ALWAYS AS ((json ->> 'approvedById'::text)) STORED,
|
||||
PRIMARY KEY (id),
|
||||
CONSTRAINT uk_task_fqn_hash UNIQUE (fqnhash)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_task_taskid ON task_entity (taskid);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_status ON task_entity (status);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_category ON task_entity (category);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_type ON task_entity (type);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_priority ON task_entity (priority);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_createdat ON task_entity (createdat);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_updatedat ON task_entity (updatedat);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_deleted ON task_entity (deleted);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_status_category ON task_entity (status, category);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_about_fqn_hash ON task_entity (aboutfqnhash);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_status_about ON task_entity (status, aboutfqnhash);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_created_by_id ON task_entity (createdbyid);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_created_by_category ON task_entity (createdbyid, category);
|
||||
|
||||
-- For 2.0.0 environments that ran the CREATE TABLE above before the
|
||||
-- approvedbyid generated column was added inline, attach it now. CREATE TABLE
|
||||
-- IF NOT EXISTS is a no-op on those environments so the column would never
|
||||
-- appear otherwise. Postgres supports `ADD COLUMN IF NOT EXISTS` natively.
|
||||
-- The ALTER must run before idx_task_approved_by_id is created — otherwise
|
||||
-- existing-2.0.0 deployments would fail the CREATE INDEX with "column does
|
||||
-- not exist" before the ADD COLUMN ever runs.
|
||||
ALTER TABLE task_entity
|
||||
ADD COLUMN IF NOT EXISTS approvedbyid character varying(36)
|
||||
GENERATED ALWAYS AS ((json ->> 'approvedById'::text)) STORED;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_task_approved_by_id ON task_entity (approvedbyid);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS new_task_sequence (
|
||||
id bigint NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
INSERT INTO new_task_sequence (id) SELECT 0 WHERE NOT EXISTS (SELECT 1 FROM new_task_sequence);
|
||||
|
||||
-- =====================================================
|
||||
-- ACTIVITY STREAM TABLE (Partitioned by time)
|
||||
-- Lightweight, ephemeral activity notifications
|
||||
-- NOT for audit/compliance - use entity version history
|
||||
-- Partitions are managed dynamically by ActivityStreamPartitionManager
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS activity_stream (
|
||||
id character varying(36) NOT NULL,
|
||||
eventtype character varying(64) NOT NULL,
|
||||
entitytype character varying(64) NOT NULL,
|
||||
entityid character varying(36) NOT NULL,
|
||||
entityfqnhash character varying(768),
|
||||
about character varying(2048),
|
||||
aboutfqnhash character varying(768),
|
||||
-- Nullable for system events and hard-deleted users; actorname is the display fallback.
|
||||
actorid character varying(36),
|
||||
actorname character varying(256),
|
||||
timestamp bigint NOT NULL,
|
||||
summary character varying(500),
|
||||
fieldname character varying(256),
|
||||
oldvalue text,
|
||||
newvalue text,
|
||||
domains jsonb,
|
||||
json jsonb NOT NULL,
|
||||
PRIMARY KEY (id, timestamp)
|
||||
) PARTITION BY RANGE (timestamp);
|
||||
|
||||
-- Default partition catches all data until monthly partitions are created
|
||||
-- ActivityStreamPartitionManager will create monthly partitions and detach old ones
|
||||
CREATE TABLE IF NOT EXISTS activity_stream_default PARTITION OF activity_stream DEFAULT;
|
||||
|
||||
-- Indexes for activity stream (created on parent, inherited by partitions)
|
||||
CREATE INDEX IF NOT EXISTS idx_activity_timestamp ON activity_stream (timestamp);
|
||||
CREATE INDEX IF NOT EXISTS idx_activity_entity ON activity_stream (entitytype, entityid, timestamp);
|
||||
CREATE INDEX IF NOT EXISTS idx_activity_actor ON activity_stream (actorid, timestamp);
|
||||
CREATE INDEX IF NOT EXISTS idx_activity_event_type ON activity_stream (eventtype, timestamp);
|
||||
CREATE INDEX IF NOT EXISTS idx_activity_entity_fqn ON activity_stream (entityfqnhash, timestamp);
|
||||
CREATE INDEX IF NOT EXISTS idx_activity_about ON activity_stream (aboutfqnhash, timestamp);
|
||||
|
||||
-- Activity stream configuration per domain
|
||||
CREATE TABLE IF NOT EXISTS activity_stream_config (
|
||||
id character varying(36) NOT NULL,
|
||||
json jsonb NOT NULL,
|
||||
scope character varying(32) GENERATED ALWAYS AS ((json ->> 'scope'::text)) STORED NOT NULL,
|
||||
domainid character varying(36) GENERATED ALWAYS AS ((json -> 'scopeReference' ->> 'id'::text)) STORED,
|
||||
enabled boolean GENERATED ALWAYS AS (((json ->> 'enabled'::text))::boolean) STORED,
|
||||
retentiondays integer GENERATED ALWAYS AS (((json ->> 'retentionDays'::text))::integer) STORED,
|
||||
PRIMARY KEY (id),
|
||||
CONSTRAINT uk_activity_domain_config UNIQUE (domainid)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_activity_config_scope ON activity_stream_config (scope);
|
||||
CREATE INDEX IF NOT EXISTS idx_activity_config_enabled ON activity_stream_config (enabled);
|
||||
|
||||
-- =====================================================
|
||||
-- ANNOUNCEMENT ENTITY TABLE
|
||||
-- Standalone entity for asset announcements (migrated from thread_entity)
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS announcement_entity (
|
||||
id character varying(36) NOT NULL,
|
||||
json jsonb NOT NULL,
|
||||
fqnhash character varying(768) NOT NULL,
|
||||
name character varying(256) GENERATED ALWAYS AS ((json ->> 'name'::text)) STORED NOT NULL,
|
||||
entitylink character varying(512) GENERATED ALWAYS AS ((json ->> 'entityLink'::text)) STORED,
|
||||
status character varying(32) GENERATED ALWAYS AS ((json ->> 'status'::text)) STORED,
|
||||
starttime bigint GENERATED ALWAYS AS (((json ->> 'startTime'::text))::bigint) STORED,
|
||||
endtime bigint GENERATED ALWAYS AS (((json ->> 'endTime'::text))::bigint) STORED,
|
||||
createdby character varying(256) GENERATED ALWAYS AS ((json ->> 'createdBy'::text)) STORED,
|
||||
createdat bigint GENERATED ALWAYS AS (((json ->> 'createdAt'::text))::bigint) STORED,
|
||||
updatedat bigint GENERATED ALWAYS AS (((json ->> 'updatedAt'::text))::bigint) STORED,
|
||||
deleted boolean GENERATED ALWAYS AS (((json ->> 'deleted'::text))::boolean) STORED,
|
||||
PRIMARY KEY (id),
|
||||
CONSTRAINT uk_announcement_fqn_hash UNIQUE (fqnhash)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_announcement_status ON announcement_entity (status);
|
||||
CREATE INDEX IF NOT EXISTS idx_announcement_entitylink ON announcement_entity (entitylink);
|
||||
CREATE INDEX IF NOT EXISTS idx_announcement_starttime ON announcement_entity (starttime);
|
||||
CREATE INDEX IF NOT EXISTS idx_announcement_endtime ON announcement_entity (endtime);
|
||||
CREATE INDEX IF NOT EXISTS idx_announcement_deleted ON announcement_entity (deleted);
|
||||
|
||||
-- =====================================================
|
||||
-- TASK FORM SCHEMA ENTITY TABLE
|
||||
-- Stores form schemas for different task types
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS task_form_schema_entity (
|
||||
id character varying(36) NOT NULL,
|
||||
json jsonb NOT NULL,
|
||||
fqnhash character varying(768) NOT NULL,
|
||||
name character varying(256) GENERATED ALWAYS AS ((json ->> 'name'::text)) STORED NOT NULL,
|
||||
tasktype character varying(64) GENERATED ALWAYS AS ((json ->> 'taskType'::text)) STORED,
|
||||
taskcategory character varying(32) GENERATED ALWAYS AS ((json ->> 'taskCategory'::text)) STORED,
|
||||
updatedat bigint GENERATED ALWAYS AS (((json ->> 'updatedAt'::text))::bigint) STORED,
|
||||
deleted boolean GENERATED ALWAYS AS (((json ->> 'deleted'::text))::boolean) STORED,
|
||||
PRIMARY KEY (id),
|
||||
CONSTRAINT uk_task_form_schema_fqn_hash UNIQUE (fqnhash)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_task_form_schema_name ON task_form_schema_entity (name);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_form_schema_tasktype ON task_form_schema_entity (tasktype);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_form_schema_deleted ON task_form_schema_entity (deleted);
|
||||
|
||||
-- =====================================================
|
||||
-- KNOWLEDGE CENTER + CONTEXT CENTER DRIVE (Collate → OM port)
|
||||
-- Appended below the Task Redesign tables to preserve main's
|
||||
-- migration order when merging.
|
||||
-- =====================================================
|
||||
|
||||
-- MCP tables are created in 1.13.0 migration.
|
||||
|
||||
-- Knowledge Center: page entity table (Article, QuickLink).
|
||||
-- Existing Collate customers already have this table from 1.2.0-collate with
|
||||
-- subsequent shape changes through 1.6.0-collate (nameHash -> fqnHash VARCHAR(756),
|
||||
-- pageType generated column, composite deleted index). CREATE TABLE IF NOT EXISTS
|
||||
-- is a no-op for them and creates the final shape for fresh OpenMetadata installs.
|
||||
CREATE TABLE IF NOT EXISTS knowledge_center (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> 'id') STORED NOT NULL,
|
||||
fqnHash VARCHAR(756) NOT NULL,
|
||||
name VARCHAR(256) GENERATED ALWAYS AS (json ->> 'name') STORED NOT NULL,
|
||||
json JSONB NOT NULL,
|
||||
updatedAt BIGINT GENERATED ALWAYS AS ((json ->> 'updatedAt')::bigint) STORED NOT NULL,
|
||||
updatedBy VARCHAR(256) GENERATED ALWAYS AS (json ->> 'updatedBy') STORED NOT NULL,
|
||||
deleted BOOLEAN GENERATED ALWAYS AS (COALESCE((json ->> 'deleted')::boolean, false)) STORED,
|
||||
pageType VARCHAR(16) GENERATED ALWAYS AS (json ->> 'pageType') STORED NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE (fqnHash)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS knowledge_center_name_index ON knowledge_center (name);
|
||||
CREATE INDEX IF NOT EXISTS index_knowledge_center_deleted ON knowledge_center (fqnHash, deleted);
|
||||
|
||||
-- Context Center Drive: Folder entity table.
|
||||
CREATE TABLE IF NOT EXISTS drive_folder (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> 'id') STORED NOT NULL,
|
||||
name VARCHAR(256) GENERATED ALWAYS AS (json ->> 'name') STORED NOT NULL,
|
||||
nameHash VARCHAR(256) NOT NULL,
|
||||
json JSONB NOT NULL,
|
||||
updatedAt BIGINT GENERATED ALWAYS AS ((json ->> 'updatedAt')::bigint) STORED NOT NULL,
|
||||
updatedBy VARCHAR(256) GENERATED ALWAYS AS (json ->> 'updatedBy') STORED NOT NULL,
|
||||
deleted BOOLEAN GENERATED ALWAYS AS (COALESCE((json ->> 'deleted')::boolean, false)) STORED,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE (nameHash)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_drive_folder_updated_at ON drive_folder (updatedAt);
|
||||
|
||||
-- Context Center Drive: File entity table (uploaded PDF/image/spreadsheet/office docs).
|
||||
CREATE TABLE IF NOT EXISTS context_file (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> 'id') STORED NOT NULL,
|
||||
name VARCHAR(256) GENERATED ALWAYS AS (json ->> 'name') STORED NOT NULL,
|
||||
nameHash VARCHAR(256) NOT NULL,
|
||||
json JSONB NOT NULL,
|
||||
updatedAt BIGINT GENERATED ALWAYS AS ((json ->> 'updatedAt')::bigint) STORED NOT NULL,
|
||||
updatedBy VARCHAR(256) GENERATED ALWAYS AS (json ->> 'updatedBy') STORED NOT NULL,
|
||||
deleted BOOLEAN GENERATED ALWAYS AS (COALESCE((json ->> 'deleted')::boolean, false)) STORED,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE (nameHash)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_context_file_updated_at ON context_file (updatedAt);
|
||||
|
||||
-- Attachments: Asset entity table for uploaded file blobs referenced by ContextFiles, Pages, etc.
|
||||
-- Existing Collate customers have this from 1.7.0-collate. CREATE TABLE IF NOT EXISTS is a no-op for them.
|
||||
CREATE TABLE IF NOT EXISTS asset_entity (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> 'id') STORED NOT NULL,
|
||||
name VARCHAR(256) GENERATED ALWAYS AS (json ->> 'fileName') STORED NOT NULL,
|
||||
url VARCHAR(1024) GENERATED ALWAYS AS (json ->> 'url') STORED NOT NULL,
|
||||
fullyQualifiedName VARCHAR(256) GENERATED ALWAYS AS (json ->> 'fullyQualifiedName') STORED NOT NULL,
|
||||
assetType VARCHAR(100) GENERATED ALWAYS AS (json ->> 'assetType') STORED NOT NULL,
|
||||
json JSONB NOT NULL,
|
||||
updatedAt BIGINT GENERATED ALWAYS AS ((json ->> 'updatedAt')::bigint) STORED NOT NULL,
|
||||
updatedBy VARCHAR(256) GENERATED ALWAYS AS (json ->> 'updatedBy') STORED NOT NULL,
|
||||
fqnHash VARCHAR(768) NOT NULL,
|
||||
deleted BOOLEAN GENERATED ALWAYS AS (COALESCE(CAST(json ->> 'deleted' AS BOOLEAN), false)) STORED,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS fqnhash_index ON asset_entity (fqnHash);
|
||||
CREATE INDEX IF NOT EXISTS asset_type_index ON asset_entity (assetType);
|
||||
CREATE INDEX IF NOT EXISTS idx_asset_deleted ON asset_entity (deleted);
|
||||
|
||||
-- Context Center Drive: File content snapshot table (revisions, extracted text).
|
||||
CREATE TABLE IF NOT EXISTS context_file_content (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> 'id') STORED NOT NULL,
|
||||
name VARCHAR(256) GENERATED ALWAYS AS (json ->> 'name') STORED NOT NULL,
|
||||
nameHash VARCHAR(256) NOT NULL,
|
||||
json JSONB NOT NULL,
|
||||
updatedAt BIGINT GENERATED ALWAYS AS ((json ->> 'updatedAt')::bigint) STORED NOT NULL,
|
||||
updatedBy VARCHAR(256) GENERATED ALWAYS AS (json ->> 'updatedBy') STORED NOT NULL,
|
||||
deleted BOOLEAN GENERATED ALWAYS AS (COALESCE((json ->> 'deleted')::boolean, false)) STORED,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE (nameHash)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_context_file_content_updated_at ON context_file_content (updatedAt);
|
||||
|
||||
-- Add tag_usage.metadata column if missing (newer tag usage payloads carry metadata).
|
||||
ALTER TABLE IF EXISTS tag_usage
|
||||
ADD COLUMN IF NOT EXISTS metadata JSONB;
|
||||
|
||||
-- Add audit_log_event.search_text column if missing (searchable audit log text).
|
||||
ALTER TABLE IF EXISTS audit_log_event
|
||||
ADD COLUMN IF NOT EXISTS search_text TEXT;
|
||||
|
||||
-- Distributed reindex job tracking.
|
||||
CREATE TABLE IF NOT EXISTS search_index_job (
|
||||
id VARCHAR(64) PRIMARY KEY,
|
||||
status VARCHAR(64) NOT NULL,
|
||||
jobConfiguration JSONB NOT NULL,
|
||||
targetIndexPrefix VARCHAR(256) NOT NULL,
|
||||
stagedIndexMapping JSONB NULL,
|
||||
totalRecords BIGINT NOT NULL DEFAULT 0,
|
||||
processedRecords BIGINT NOT NULL DEFAULT 0,
|
||||
successRecords BIGINT NOT NULL DEFAULT 0,
|
||||
failedRecords BIGINT NOT NULL DEFAULT 0,
|
||||
stats JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
createdBy VARCHAR(256) NOT NULL,
|
||||
createdAt BIGINT NOT NULL,
|
||||
startedAt BIGINT NULL,
|
||||
completedAt BIGINT NULL,
|
||||
updatedAt BIGINT NOT NULL,
|
||||
errorMessage TEXT NULL,
|
||||
registrationDeadline BIGINT NULL,
|
||||
registeredServerCount INTEGER NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_search_index_job_status_created_at
|
||||
ON search_index_job (status, createdAt DESC);
|
||||
|
||||
-- Retry queue for failed search-index writes.
|
||||
CREATE TABLE IF NOT EXISTS search_index_retry_queue (
|
||||
entityId VARCHAR(64) NOT NULL,
|
||||
entityFqn VARCHAR(768) NOT NULL,
|
||||
failureReason TEXT NULL,
|
||||
status VARCHAR(64) NOT NULL,
|
||||
entityType VARCHAR(128) NOT NULL,
|
||||
retryCount INTEGER NOT NULL DEFAULT 0,
|
||||
claimedAt TIMESTAMP NULL,
|
||||
PRIMARY KEY (entityId, entityFqn)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_search_index_retry_queue_status
|
||||
ON search_index_retry_queue (status);
|
||||
CREATE INDEX IF NOT EXISTS idx_search_index_retry_queue_claimed_at
|
||||
ON search_index_retry_queue (claimedAt);
|
||||
|
||||
-- ContextMemory entity - reusable Context Center memory.
|
||||
CREATE TABLE IF NOT EXISTS context_memory (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> 'id') STORED NOT NULL,
|
||||
name VARCHAR(256) GENERATED ALWAYS AS (json ->> 'name') STORED NOT NULL,
|
||||
nameHash VARCHAR(256) NOT NULL,
|
||||
json JSONB NOT NULL,
|
||||
updatedAt BIGINT GENERATED ALWAYS AS ((json ->> 'updatedAt')::bigint) STORED NOT NULL,
|
||||
updatedBy VARCHAR(256) GENERATED ALWAYS AS (json ->> 'updatedBy') STORED NOT NULL,
|
||||
deleted BOOLEAN GENERATED ALWAYS AS ((json ->> 'deleted')::boolean) STORED,
|
||||
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE (nameHash)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_context_memory_updated_at ON context_memory (updatedAt);
|
||||
|
||||
-- AI Governance Studio (Phase 4): framework + control entity tables.
|
||||
CREATE TABLE IF NOT EXISTS ai_governance_framework_entity (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json->>'id') STORED NOT NULL,
|
||||
name VARCHAR(256) GENERATED ALWAYS AS (json->>'name') STORED NOT NULL,
|
||||
fqnHash VARCHAR(768) NOT NULL,
|
||||
json JSONB NOT NULL,
|
||||
updatedAt BIGINT GENERATED ALWAYS AS ((json->>'updatedAt')::bigint) STORED NOT NULL,
|
||||
updatedBy VARCHAR(256) GENERATED ALWAYS AS (json->>'updatedBy') STORED NOT NULL,
|
||||
impersonatedBy VARCHAR(256) GENERATED ALWAYS AS (json->>'impersonatedBy') STORED,
|
||||
deleted BOOLEAN GENERATED ALWAYS AS ((json->>'deleted')::boolean) STORED,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE (fqnHash)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS ai_governance_framework_name_index ON ai_governance_framework_entity(name);
|
||||
CREATE INDEX IF NOT EXISTS ai_governance_framework_deleted_index ON ai_governance_framework_entity(deleted);
|
||||
COMMENT ON TABLE ai_governance_framework_entity IS 'AI Governance Framework entities';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ai_framework_control_entity (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json->>'id') STORED NOT NULL,
|
||||
name VARCHAR(256) GENERATED ALWAYS AS (json->>'name') STORED NOT NULL,
|
||||
fqnHash VARCHAR(768) NOT NULL,
|
||||
json JSONB NOT NULL,
|
||||
updatedAt BIGINT GENERATED ALWAYS AS ((json->>'updatedAt')::bigint) STORED NOT NULL,
|
||||
updatedBy VARCHAR(256) GENERATED ALWAYS AS (json->>'updatedBy') STORED NOT NULL,
|
||||
impersonatedBy VARCHAR(256) GENERATED ALWAYS AS (json->>'impersonatedBy') STORED,
|
||||
deleted BOOLEAN GENERATED ALWAYS AS ((json->>'deleted')::boolean) STORED,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE (fqnHash)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS ai_framework_control_name_index ON ai_framework_control_entity(name);
|
||||
CREATE INDEX IF NOT EXISTS ai_framework_control_deleted_index ON ai_framework_control_entity(deleted);
|
||||
COMMENT ON TABLE ai_framework_control_entity IS 'AI Framework Control entities';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS audit_report_entity (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json->>'id') STORED NOT NULL,
|
||||
name VARCHAR(256) GENERATED ALWAYS AS (json->>'name') STORED NOT NULL,
|
||||
fqnHash VARCHAR(768) NOT NULL,
|
||||
json JSONB NOT NULL,
|
||||
updatedAt BIGINT GENERATED ALWAYS AS ((json->>'updatedAt')::bigint) STORED NOT NULL,
|
||||
updatedBy VARCHAR(256) GENERATED ALWAYS AS (json->>'updatedBy') STORED NOT NULL,
|
||||
impersonatedBy VARCHAR(256) GENERATED ALWAYS AS (json->>'impersonatedBy') STORED,
|
||||
deleted BOOLEAN GENERATED ALWAYS AS ((json->>'deleted')::boolean) STORED,
|
||||
status VARCHAR(32) GENERATED ALWAYS AS (json->>'status') STORED,
|
||||
requestSignature VARCHAR(512) GENERATED ALWAYS AS (json->>'requestSignature') STORED,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE (fqnHash)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS audit_report_name_index ON audit_report_entity(name);
|
||||
CREATE INDEX IF NOT EXISTS audit_report_status_index ON audit_report_entity(status);
|
||||
CREATE INDEX IF NOT EXISTS audit_report_deleted_index ON audit_report_entity(deleted);
|
||||
CREATE INDEX IF NOT EXISTS audit_report_request_signature_index ON audit_report_entity(requestSignature);
|
||||
COMMENT ON TABLE audit_report_entity IS 'AI Audit Report entities';
|
||||
-- Database-backed user session store for multi-pod session management (issue #21971).
|
||||
CREATE TABLE IF NOT EXISTS user_session (
|
||||
id character varying(64) GENERATED ALWAYS AS ((json ->> 'id'::text)) STORED NOT NULL,
|
||||
userid character varying(36) GENERATED ALWAYS AS ((json ->> 'userId'::text)) STORED,
|
||||
status character varying(32) GENERATED ALWAYS AS ((json ->> 'status'::text)) STORED NOT NULL,
|
||||
expiresat bigint GENERATED ALWAYS AS (((json ->> 'expiresAt'::text))::bigint) STORED NOT NULL,
|
||||
idleexpiresat bigint GENERATED ALWAYS AS (((json ->> 'idleExpiresAt'::text))::bigint) STORED NOT NULL,
|
||||
updatedat bigint GENERATED ALWAYS AS (((json ->> 'updatedAt'::text))::bigint) STORED NOT NULL,
|
||||
sessiontype character varying(32) GENERATED ALWAYS AS ((json ->> 'type'::text)) STORED,
|
||||
provider character varying(64) GENERATED ALWAYS AS ((json ->> 'provider'::text)) STORED,
|
||||
version bigint GENERATED ALWAYS AS (((json ->> 'version'::text))::bigint) STORED,
|
||||
lastaccessedat bigint GENERATED ALWAYS AS (((json ->> 'lastAccessedAt'::text))::bigint) STORED,
|
||||
refreshleaseuntil bigint GENERATED ALWAYS AS (((json ->> 'refreshLeaseUntil'::text))::bigint) STORED,
|
||||
json jsonb NOT NULL,
|
||||
CONSTRAINT user_session_pkey PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS user_session_user_status_idx ON user_session USING btree (userid, status);
|
||||
CREATE INDEX IF NOT EXISTS user_session_expiry_idx ON user_session USING btree (status, expiresat);
|
||||
CREATE INDEX IF NOT EXISTS user_session_idle_expiry_idx ON user_session USING btree (status, idleexpiresat);
|
||||
CREATE INDEX IF NOT EXISTS user_session_prune_idx ON user_session USING btree (status, updatedat);
|
||||
|
||||
-- Per-entity `name` index for entity tables first created in 2.0.0, so the distributed
|
||||
-- reindex's `... ORDER BY name, id LIMIT 1 OFFSET :n` cursor query
|
||||
-- (EntityRepository.getCursorAtOffset) runs index-only instead of a sort that can exhaust
|
||||
-- work_mem on large tables. Added here (not 1.13.1) because these tables are created above
|
||||
-- in this same 2.0.0 migration. Idempotent via IF NOT EXISTS.
|
||||
CREATE INDEX IF NOT EXISTS task_entity_name_index ON task_entity (name);
|
||||
CREATE INDEX IF NOT EXISTS announcement_entity_name_index ON announcement_entity (name);
|
||||
CREATE INDEX IF NOT EXISTS drive_folder_name_index ON drive_folder (name);
|
||||
CREATE INDEX IF NOT EXISTS asset_entity_name_index ON asset_entity (name);
|
||||
-- context_file / context_memory are also created above in this migration and are reindexed;
|
||||
-- they only had a `nameHash` unique key, so add the leading-`name` index the cursor query needs.
|
||||
CREATE INDEX IF NOT EXISTS context_file_name_index ON context_file (name);
|
||||
CREATE INDEX IF NOT EXISTS context_memory_name_index ON context_memory (name);
|
||||
|
||||
-- MCP conversation table for MCP Client message tracking
|
||||
CREATE TABLE IF NOT EXISTS mcp_conversation (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> 'id') STORED NOT NULL,
|
||||
json JSONB NOT NULL,
|
||||
userId VARCHAR(256) GENERATED ALWAYS AS ((json -> 'user') ->> 'id') STORED NOT NULL,
|
||||
createdAt BIGINT GENERATED ALWAYS AS ((json ->> 'createdAt')::bigint) STORED NOT NULL,
|
||||
updatedAt BIGINT GENERATED ALWAYS AS ((json ->> 'updatedAt')::bigint) STORED NOT NULL,
|
||||
createdBy VARCHAR(50) GENERATED ALWAYS AS (json ->> 'createdBy') STORED NOT NULL,
|
||||
updatedBy VARCHAR(50) GENERATED ALWAYS AS (json ->> 'updatedBy') STORED NOT NULL,
|
||||
messageCount INT GENERATED ALWAYS AS ((json ->> 'messageCount')::int) STORED,
|
||||
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_mcp_conversation_user_updated ON mcp_conversation (userId, updatedAt DESC);
|
||||
|
||||
-- MCP message table for MCP Client message tracking
|
||||
CREATE TABLE IF NOT EXISTS mcp_message (
|
||||
id VARCHAR(36) GENERATED ALWAYS AS (json ->> 'id') STORED NOT NULL,
|
||||
json JSONB NOT NULL,
|
||||
conversationId VARCHAR(36) GENERATED ALWAYS AS (json ->> 'conversationId') STORED NOT NULL,
|
||||
sender VARCHAR(10) GENERATED ALWAYS AS (json ->> 'sender') STORED NOT NULL,
|
||||
messageIndex INT GENERATED ALWAYS AS ((json ->> 'index')::int) STORED,
|
||||
timestamp BIGINT GENERATED ALWAYS AS ((json ->> 'timestamp')::bigint) STORED NOT NULL,
|
||||
|
||||
PRIMARY KEY (id),
|
||||
CONSTRAINT fk_mcp_message_conversation FOREIGN KEY (conversationId) REFERENCES mcp_conversation(id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_mcp_message_conversation_index ON mcp_message (conversationId, messageIndex);
|
||||
CREATE INDEX IF NOT EXISTS idx_mcp_message_conversation_created ON mcp_message (conversationId, timestamp);
|
||||
-- Task workflow cutover support - OpenMetadata 2.0.0 (moved from 2.0.1)
|
||||
-- Maps legacy thread task IDs to new task entity IDs for migration traceability and redirects.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS task_migration_mapping (
|
||||
old_thread_id character varying(36) NOT NULL,
|
||||
new_task_id character varying(36) NOT NULL,
|
||||
migrated_at bigint NOT NULL,
|
||||
source character varying(64) DEFAULT 'thread_task_migration',
|
||||
PRIMARY KEY (old_thread_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_task_migration_mapping_new_task_id
|
||||
ON task_migration_mapping (new_task_id);
|
||||
Reference in New Issue
Block a user