{"id":6756,"date":"2026-07-12T02:06:52","date_gmt":"2026-07-12T10:06:52","guid":{"rendered":"https:\/\/www.gudusoft.com\/postgresql-data-lineage\/"},"modified":"2026-07-12T02:06:52","modified_gmt":"2026-07-12T10:06:52","slug":"postgresql-data-lineage","status":"publish","type":"page","link":"https:\/\/www.gudusoft.com\/pt\/postgresql-data-lineage\/","title":{"rendered":"PostgreSQL Data Lineage: Column-Level Lineage from Views and SQL"},"content":{"rendered":"<p><strong>PostgreSQL data lineage<\/strong> is the column-level map of how data moves through your PostgreSQL views, CTEs, and DML: which base-table columns feed each view or report column, and which joins, filters, casts, and aggregates transform the data along the way. PostgreSQL&#8217;s own catalog tells you that objects depend on each other \u2014 <code>pg_depend<\/code> records that a view references a table \u2014 but it says nothing about how columns transform. Building real lineage means parsing the SQL itself, and that is exactly what <strong>Gudu SQLFlow<\/strong> does, with a dedicated PostgreSQL dialect parser rather than a generic ANSI grammar.<\/p>\n\n\n\n<div class=\"wp-block-group alignfull has-background is-layout-constrained\" style=\"background-color:#eef7fb;padding-top:24px;padding-bottom:24px\"><div class=\"wp-block-group__inner-container\">\n\n<p><strong>Try it in 30 seconds:<\/strong> paste any PostgreSQL query or view definition into the <a href=\"https:\/\/sqlflow.gudusoft.com\/?utm_source=gudusoft&amp;utm_medium=website&amp;utm_campaign=postgresql-data-lineage\" target=\"_blank\" rel=\"noreferrer noopener\">free SQLFlow lineage visualizer<\/a> and get an interactive column-level lineage diagram. The cloud edition has a free tier.<\/p>\n\n<\/div><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Why pg_depend can&#8217;t answer lineage questions<\/h2>\n\n\n\n<p>PostgreSQL keeps meticulous dependency records. <code>pg_depend<\/code> e <code>pg_rewrite<\/code> know that <code>v_region_revenue<\/code> cannot be dropped while it references <code>v_orders_enriched<\/code>, e <code>information_schema<\/code> views expose which tables a view touches. That is object-level dependency tracking, built for <code>DROP ... CASCADE<\/code> safety \u2014 not lineage.<\/p>\n\n\n\n<p>What the catalog cannot tell you: which specific columns feed <code>revenue<\/code>, whether <code>amount<\/code> was cast or aggregated on the way, whether <code>status<\/code> filters the result without ever appearing in it, and what the full path looks like when views stack three or four levels deep. Answering &#8220;where does this number come from?&#8221; or &#8220;what breaks if I drop this column?&#8221; requires reconstructing the transformation logic from the SQL text stored in the view definitions \u2014 a parsing problem, not a catalog query.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How SQLFlow builds PostgreSQL data lineage<\/h2>\n\n\n\n<ol><li><strong>Ingest SQL.<\/strong> Paste queries, upload script files, connect to PostgreSQL over JDBC to pull DDL and view definitions directly, or import a dbt manifest for dbt-on-Postgres projects.<\/li>\n<li><strong>Parse with a PostgreSQL-specific grammar.<\/strong> SQLFlow is built on the <a href=\"https:\/\/www.sqlparser.com\/\">General SQL Parser (GSP)<\/a>, a commercial SQL compiler front-end developed since the mid-2000s and validated against roughly 13,600 per-dialect test fixtures. Its PostgreSQL parser handles the dialect&#8217;s own constructs \u2014 writable CTEs, <code>INSERT ... ON CONFLICT<\/code>, the <code>::<\/code> cast operator, inheritance and partitioning DDL \u2014 and its semantic resolver expands <code>SELECT *<\/code>, resolves column references through CTEs, subqueries, and view definitions, then extracts source-to-target relationships at column granularity.<\/li>\n<li><strong>Visualize and export.<\/strong> The output is an interactive diagram you can trace upstream and downstream from any column, exportable as JSON, CSV, or PNG, or queryable through a REST API.<\/li><\/ol>\n\n\n\n<p>PostgreSQL is one of 39 dialects SQLFlow ships parsers for \u2014 including the Postgres-family engines Greenplum, Amazon Redshift, and EDB Postgres, each with its own parser because each has diverged from core PostgreSQL in ways that matter to a lineage engine. The full methodology is covered in the <a href=\"https:\/\/www.gudusoft.com\/pt\/ferramenta-de-linhagem-de-dados-sql\/\">SQL data lineage tool overview<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Tracing a nested view chain back to base tables<\/h2>\n\n\n\n<p>View-on-view stacks are the default way logic accumulates in PostgreSQL, and they are where manual lineage tracing gives out. Take a two-level chain:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE VIEW v_orders_enriched AS\nSELECT o.order_id,\n       o.customer_id,\n       o.amount::numeric(12,2) AS amount,\n       c.region\nFROM   orders o\nJOIN   customers c ON c.customer_id = o.customer_id\nWHERE  o.status = 'shipped';\n\nCREATE VIEW v_region_revenue AS\nSELECT region,\n       SUM(amount) AS revenue\nFROM   v_orders_enriched\nGROUP  BY region;<\/code><\/pre>\n\n\n\n<p>Ask SQLFlow where <code>v_region_revenue.revenue<\/code> comes from and it resolves the full chain: <code>revenue<\/code> is <code>SUM()<\/code> over <code>v_orders_enriched.amount<\/code>, which is a <code>numeric(12,2)<\/code> cast of <code>orders.amount<\/code>. The diagram shows each hop \u2014 base table, intermediate view, aggregate \u2014 as a drillable path, not a flattened guess. It also surfaces what a naive reading misses: <code>orders.status<\/code> never appears in the output, yet every revenue figure depends on the <code>WHERE o.status = 'shipped'<\/code> filter. SQLFlow records that as indirect lineage (more below).<\/p>\n\n\n\n<p>The same resolution works at any depth. In a real schema where views stack five levels deep and half of them use <code>SELECT *<\/code>, SQLFlow expands the stars against the actual table definitions pulled over JDBC, so the lineage stays column-accurate even when the SQL text never names the columns.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL constructs that break generic parsers<\/h2>\n\n\n\n<p>Tools that treat PostgreSQL as &#8220;roughly ANSI SQL&#8221; fail precisely on the statements that carry the most dataflow. These are all handled by SQLFlow&#8217;s PostgreSQL dialect parser:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Construct<\/th><th>Why it matters for lineage<\/th><\/tr><\/thead><tbody>\n<tr><td>Writable CTEs (<code>WITH ... INSERT\/UPDATE\/DELETE ... RETURNING<\/code>)<\/td><td>A single statement both reads and writes: the CTE&#8217;s DML output feeds the outer query. Lineage must connect the modified table, the <code>RETURNING<\/code> columns, and the final target in one graph.<\/td><\/tr>\n<tr><td><code>INSERT ... ON CONFLICT DO UPDATE<\/code><\/td><td>Upserts route data down two paths \u2014 the insert path and the update path via the <code>EXCLUDED<\/code> pseudo-table. Both are real column flows into the target table.<\/td><\/tr>\n<tr><td>Table inheritance and declarative partitioning<\/td><td>Queries against a parent table implicitly read its children. SQLFlow parses the inheritance and partitioning DDL so lineage attaches correctly at the parent level instead of fragmenting per partition.<\/td><\/tr>\n<tr><td>Chained CTEs and nested views<\/td><td>Column references must be resolved through every intermediate layer, including CTEs that reference earlier CTEs, back to physical base tables.<\/td><\/tr>\n<tr><td><code>::<\/code> casts and expression columns<\/td><td>Every output column is mapped to its sources through the functions and casts applied, so you can see not just <em>where<\/em> a value came from but <em>what happened to it<\/em>.<\/td><\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Direct vs indirect lineage: the filter columns you&#8217;d otherwise miss<\/h2>\n\n\n\n<p>SQLFlow distinguishes <strong>direct lineage<\/strong> \u2014 column values that physically flow into an output \u2014 from <strong>indirect (impact) lineage<\/strong> \u2014 columns used in <code>WHERE<\/code> clauses, <code>JOIN<\/code> conditions, and <code>GROUP BY<\/code> keys that shape the result without landing in it. In the example above, <code>orders.amount<\/code> is direct lineage for <code>revenue<\/code>; <code>orders.status<\/code> and both <code>customer_id<\/code> join keys are indirect.<\/p>\n\n\n\n<p>The distinction is toggleable in the diagram, and it changes real decisions. If you plan to change the domain of <code>orders.status<\/code>, direct-only lineage says nothing depends on it; indirect lineage shows every revenue report it silently filters. Most lineage tools do not model this relationship type at all.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">From one query to a whole PostgreSQL estate<\/h2>\n\n\n\n<p>For a single gnarly view chain, the SQLFlow Cloud free tier is enough: paste, analyze, drill. Beyond that:<\/p>\n\n\n\n<ul><li><strong>Whole-database scans:<\/strong> connect over JDBC (or use the Grabit metadata extractor) to pull every view and object definition and build a persistent lineage repository. Enterprise deployments batch-scan estates of 100+ databases and over a million columns, with incremental re-scans.<\/li>\n<li><strong>Catalog integration:<\/strong> export adapters push column-level lineage into DataHub, Microsoft Purview, and OpenMetadata, so SQLFlow can serve as the lineage engine behind the catalog you already run.<\/li>\n<li><strong>Air-gapped environments:<\/strong> <a href=\"https:\/\/www.gudusoft.com\/pt\/sqlflow-on-premise-versao\/\">SQLFlow no local<\/a> runs on Docker or Kubernetes inside your network at $500\/month or $4,800 one-time per database type, installable on two servers. SQL text never leaves your infrastructure.<\/li>\n<li><strong>Privacy by design:<\/strong> SQLFlow performs static analysis of SQL code and schema metadata only. It never reads the rows in your tables.<\/li><\/ul>\n\n\n\n<p>Since version 8.2.3 you can also query the resulting graph in plain English \u2014 &#8220;which views depend on <code>orders.amount<\/code>?&#8221; \u2014 with every table and column in the answer validated against the analyzed lineage before it is shown.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What about open-source PostgreSQL lineage tools?<\/h2>\n\n\n\n<p>Open-source parsers like <code>sqllineage<\/code> e <code>sqlglot<\/code> are genuinely good at extracting table-level and basic column-level lineage from individual queries, and for a handful of clean SELECT statements they may be all you need. The gaps show up on production PostgreSQL: resolving <code>SELECT *<\/code> through view chains requires live schema metadata; writable CTEs and <code>ON CONFLICT<\/code> upserts need dialect-exact grammars; indirect lineage through filters and join keys is usually not modeled; and extracting lineage is not the same as visualizing, storing, and diffing it across thousands of objects. If you are evaluating, run your deepest view stack and your ugliest upsert through both and compare the graphs.<\/p>\n\n\n\n<p>If your estate mixes engines, the same parser family covers them with the same output format \u2014 see the companion pages for <a href=\"https:\/\/www.gudusoft.com\/pt\/mysql-data-lineage\/\">MySQL data lineage<\/a>, <a href=\"https:\/\/www.gudusoft.com\/pt\/redshift-data-lineage\/\">Redshift data lineage<\/a>, e <a href=\"https:\/\/www.gudusoft.com\/pt\/greenplum-data-lineage\/\">Greenplum data lineage<\/a>, or the <a href=\"https:\/\/www.gudusoft.com\/pt\/base-de-conhecimento-de-linhagem-de-dados\/\">data lineage knowledge base<\/a> for concepts.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Frequently asked questions<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Does PostgreSQL have built-in data lineage?<\/h3>\n\n\n<p>No. PostgreSQL tracks object-level dependencies in <code>pg_depend<\/code> \u2014 which views reference which tables \u2014 for integrity purposes like <code>DROP ... CASCADE<\/code>. It does not record how columns are transformed. Column-level lineage has to be derived by parsing the SQL in view definitions and scripts, which is what SQLFlow does.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Does SQLFlow need access to my table data?<\/h3>\n\n\n<p>No. SQLFlow performs static analysis of SQL code and optionally reads schema metadata (table and column definitions) to resolve references. It never reads rows. With the On-Premise edition, even the SQL text stays inside your network.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Can SQLFlow trace lineage through writable CTEs and ON CONFLICT upserts?<\/h3>\n\n\n<p>Yes. The PostgreSQL dialect parser handles <code>WITH ... INSERT\/UPDATE\/DELETE ... RETURNING<\/code> statements and <code>INSERT ... ON CONFLICT DO UPDATE<\/code>, mapping both the insert and update column flows into the target table.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How are partitioned and inherited tables handled?<\/h3>\n\n\n<p>SQLFlow parses PostgreSQL inheritance and declarative partitioning DDL, so lineage for queries against a parent table is attached at the parent level rather than splintering into per-partition edges.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How do I get lineage for an entire PostgreSQL database, not just one query?<\/h3>\n\n\n<p>Connect SQLFlow to the database over JDBC or extract metadata with the Grabit ingester. SQLFlow pulls all DDL and view definitions, analyzes them together, and maintains a persistent, incrementally updated lineage repository \u2014 scaling to estates of 100+ databases.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How much does SQLFlow cost?<\/h3>\n\n\n<p>SQLFlow Cloud starts free; premium accounts are $49.99\/month. SQLFlow On-Premise is $500\/month or $4,800 one-time per selected database type, plus $100\/month or $1,000 one-time per additional database type.<\/p>\n\n\n\n<div class=\"wp-block-group alignfull has-background is-layout-constrained\" style=\"background-color:#60d5f6;padding-top:32px;padding-bottom:32px\"><div class=\"wp-block-group__inner-container\">\n\n<h2 class=\"wp-block-heading\">Trace your PostgreSQL lineage now<\/h2>\n\n\n<p>Paste a view definition into the free visualizer and see the column-level graph, or talk to us about scanning your whole PostgreSQL estate.<\/p>\n\n\n<div class=\"wp-block-buttons is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/sqlflow.gudusoft.com\/?utm_source=gudusoft&amp;utm_medium=website&amp;utm_campaign=postgresql-data-lineage\" target=\"_blank\" rel=\"noreferrer noopener\">Try SQLFlow free<\/a><\/div>\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/www.gudusoft.com\/pt\/contato\/\">Request an enterprise demo<\/a><\/div>\n<\/div>\n\n<\/div><\/div>\n\n\n\n<script type=\"application\/ld+json\">{\n    \"@context\": \"https:\\\/\\\/schema.org\",\n    \"@graph\": [\n        {\n            \"@type\": \"SoftwareApplication\",\n            \"name\": \"Gudu SQLFlow\",\n            \"applicationCategory\": \"DeveloperApplication\",\n            \"applicationSubCategory\": \"SQL Data Lineage Tool\",\n            \"operatingSystem\": \"Web, Linux, Windows, macOS\",\n            \"url\": \"https:\\\/\\\/www.gudusoft.com\\\/postgresql-data-lineage\\\/\",\n            \"description\": \"Automated PostgreSQL data lineage: SQLFlow parses views, writable CTEs, ON CONFLICT upserts, and partitioned-table DDL to produce interactive column-level lineage diagrams.\",\n            \"featureList\": \"PostgreSQL dialect parser, column-level lineage, nested view chain resolution, writable CTE analysis, INSERT ON CONFLICT lineage, inheritance and partitioning support, indirect\\\/impact lineage, JDBC ingestion, DataHub\\\/Purview\\\/OpenMetadata export, AI lineage query\",\n            \"softwareVersion\": \"8.2.3\",\n            \"offers\": [\n                {\n                    \"@type\": \"Offer\",\n                    \"name\": \"SQLFlow Cloud Free\",\n                    \"price\": \"0\",\n                    \"priceCurrency\": \"USD\"\n                },\n                {\n                    \"@type\": \"Offer\",\n                    \"name\": \"SQLFlow Cloud Premium\",\n                    \"price\": \"49.99\",\n                    \"priceCurrency\": \"USD\",\n                    \"priceSpecification\": {\n                        \"@type\": \"UnitPriceSpecification\",\n                        \"price\": \"49.99\",\n                        \"priceCurrency\": \"USD\",\n                        \"billingIncrement\": 1,\n                        \"unitText\": \"MONTH\"\n                    }\n                },\n                {\n                    \"@type\": \"Offer\",\n                    \"name\": \"SQLFlow On-Premise\",\n                    \"price\": \"4800\",\n                    \"priceCurrency\": \"USD\"\n                }\n            ],\n            \"publisher\": {\n                \"@type\": \"Organization\",\n                \"name\": \"Gudu Software\",\n                \"url\": \"https:\\\/\\\/www.gudusoft.com\\\/\"\n            }\n        },\n        {\n            \"@type\": \"FAQPage\",\n            \"mainEntity\": [\n                {\n                    \"@type\": \"Question\",\n                    \"name\": \"Does PostgreSQL have built-in data lineage?\",\n                    \"acceptedAnswer\": {\n                        \"@type\": \"Answer\",\n                        \"text\": \"No. PostgreSQL tracks object-level dependencies in pg_depend for integrity purposes like DROP CASCADE, but it does not record how columns are transformed. Column-level lineage has to be derived by parsing the SQL in view definitions and scripts, which is what SQLFlow does.\"\n                    }\n                },\n                {\n                    \"@type\": \"Question\",\n                    \"name\": \"Does SQLFlow need access to my table data?\",\n                    \"acceptedAnswer\": {\n                        \"@type\": \"Answer\",\n                        \"text\": \"No. SQLFlow performs static analysis of SQL code and optionally reads schema metadata to resolve references. It never reads rows. With the On-Premise edition, even the SQL text stays inside your network.\"\n                    }\n                },\n                {\n                    \"@type\": \"Question\",\n                    \"name\": \"Can SQLFlow trace lineage through writable CTEs and ON CONFLICT upserts?\",\n                    \"acceptedAnswer\": {\n                        \"@type\": \"Answer\",\n                        \"text\": \"Yes. The PostgreSQL dialect parser handles WITH ... INSERT\\\/UPDATE\\\/DELETE ... RETURNING statements and INSERT ... ON CONFLICT DO UPDATE, mapping both the insert and update column flows into the target table.\"\n                    }\n                },\n                {\n                    \"@type\": \"Question\",\n                    \"name\": \"How are partitioned and inherited tables handled?\",\n                    \"acceptedAnswer\": {\n                        \"@type\": \"Answer\",\n                        \"text\": \"SQLFlow parses PostgreSQL inheritance and declarative partitioning DDL, so lineage for queries against a parent table is attached at the parent level rather than splintering into per-partition edges.\"\n                    }\n                },\n                {\n                    \"@type\": \"Question\",\n                    \"name\": \"How do I get lineage for an entire PostgreSQL database, not just one query?\",\n                    \"acceptedAnswer\": {\n                        \"@type\": \"Answer\",\n                        \"text\": \"Connect SQLFlow to the database over JDBC or extract metadata with the Grabit ingester. SQLFlow pulls all DDL and view definitions, analyzes them together, and maintains a persistent, incrementally updated lineage repository scaling to estates of 100+ databases.\"\n                    }\n                },\n                {\n                    \"@type\": \"Question\",\n                    \"name\": \"How much does SQLFlow cost?\",\n                    \"acceptedAnswer\": {\n                        \"@type\": \"Answer\",\n                        \"text\": \"SQLFlow Cloud starts free; premium accounts are $49.99\\\/month. SQLFlow On-Premise is $500\\\/month or $4,800 one-time per selected database type, plus $100\\\/month or $1,000 one-time per additional database type.\"\n                    }\n                }\n            ]\n        }\n    ]\n}<\/script>","protected":false},"excerpt":{"rendered":"<p>PostgreSQL data lineage is the column-level map of how data moves through your PostgreSQL views, CTEs, and DML: which base-table columns feed each view or report column, and which joins, filters, casts, and aggregates transform the data along the way. PostgreSQL&#8217;s own catalog tells you that objects depend on each other \u2014 pg_depend records that a view references a table \u2014 but it says nothing about how columns transform. Building real lineage means parsing the SQL itself, and that is exactly what Gudu SQLFlow does, with a dedicated PostgreSQL dialect parser rather than a generic ANSI grammar. Try it in\u2026<\/p>","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":[],"blocksy_meta":{"styles_descriptor":{"styles":{"desktop":"","tablet":"","mobile":""},"google_fonts":[],"version":5}},"_links":{"self":[{"href":"https:\/\/www.gudusoft.com\/pt\/wp-json\/wp\/v2\/pages\/6756"}],"collection":[{"href":"https:\/\/www.gudusoft.com\/pt\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.gudusoft.com\/pt\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.gudusoft.com\/pt\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.gudusoft.com\/pt\/wp-json\/wp\/v2\/comments?post=6756"}],"version-history":[{"count":0,"href":"https:\/\/www.gudusoft.com\/pt\/wp-json\/wp\/v2\/pages\/6756\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.gudusoft.com\/pt\/wp-json\/wp\/v2\/media?parent=6756"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}