Deep dive into entity resolution, cross-database lineage, stored procedures, parallel processing, and the full AddLineageRequest lifecycle.
Lineage Ingestion — Advanced Topics
This page covers entity resolution, cross-database lineage, stored procedure and view lineage, parallel processing, publishing, dialect mapping, and configuration. For the pipeline overview, SQL parsing, and graph analysis, see Lineage Ingestion.
Entity Resolution
Before publishing, table names from SQL must be resolved to OpenMetadata entities:
search_table_entities(service_name, database, schema, table) 1. Search Elasticsearch (fast) for table FQN 2. If not found, try API search (reliable) 3. If schema not found, retry without schema qualifier 4. Cache results (1,000-entry LRU cache)
This resolution step is critical — lineage can only be created between entities that exist in the catalog.
Cross-Database Lineage
When processCrossDatabaseLineage is enabled:
# Query references tables from multiple services:INSERT INTO warehouse.analytics.summarySELECT * FROM production.sales.orders# Resolution searches across multiple services:search_table_entities( service_names=["warehouse_service", "production_service"], ...)
Configured via crossDatabaseServiceNames in the workflow config.
Stored Procedure Lineage
lineage_processors.py handles lineage from stored procedures:
Fetch stored procedure definitions
Extract individual SQL statements from the procedure body
Filter for lineage-relevant statements (is_lineage_query())
Build a directed graph of the procedure’s data flow
For each lineage edge, link to the stored procedure entity as the pipeline reference
The resulting lineage includes pipeline=EntityReference(type="storedProcedure") to show which procedure creates the data flow.
View Lineage
Views are processed separately:
view_lineage_processor(view_entity) 1. Get view definition SQL 2. Parse with LineageParser 3. Extract source tables from the SELECT 4. Create lineage: source tables → view 5. Override existing view lineage (views can be redefined)
add_lineage(request, check_patch=False) 1. Check if edge already exists 2. If exists: merge column lineage (union of old + new columns) 3. If new: create edge 4. Cache the result
For views, write_override_lineage() replaces existing lineage entirely (since view definitions can change).
Dialect Mapping
lineage/models.py maps each database connection type to a SQL dialect for correct parsing:
Connection Type
Dialect
Postgres, Redshift
POSTGRES
MySQL, MariaDB
MYSQL
Snowflake
SNOWFLAKE
BigQuery
BIGQUERY
SQL Server, Azure SQL
TSQL
Hive, Spark, Databricks
SPARKSQL
Oracle
ORACLE
Trino, Presto
TRINO
+ 20 more
…
Configuration
source: type: postgres-lineage # Database-specific lineage source serviceName: my_postgres sourceConfig: config: type: DatabaseLineage queryLogDuration: 1 # Days to look back resultLimit: 100000 # Max queries to fetch parsingTimeoutLimit: 30 # Seconds per query processCrossDatabaseLineage: false crossDatabaseServiceNames: [] threads: 4 # Parallel processing threads queryLogFilePath: "" # Optional: CSV file path
All paths above are relative to ingestion/src/metadata/ingestion/. For example, lineage/parser.py means ingestion/src/metadata/ingestion/lineage/parser.py.