--- title: "Parsing, validation and lineage" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Parsing, validation and lineage} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ``` ```{r setup} library(polyglotSQL) ``` ## The AST `sql_parse()` returns the full abstract syntax tree as nested R lists, following the upstream JSON AST format: ```{r} ast <- sql_parse("SELECT a, SUM(b) AS total FROM t GROUP BY a") ast str(ast$statements[[1]], max.level = 3, list.len = 4) ``` The AST round-trips: `sql_generate()` renders it back to SQL in any dialect. ## Tokens For lower-level tooling (syntax highlighting, linters), `sql_tokenize()` exposes the token stream with exact positions: ```{r} sql_tokenize("SELECT a FROM t WHERE x = 'hé'") ``` ## Validation Three layers of checking are available: ```{r} # 1. Syntax only (default) sql_validate("SELECT FROM WHERE") # 2. Strict syntax + semantic lint warnings sql_validate("SELECT name, FROM employees", strict_syntax = TRUE) sql_validate("SELECT *, category FROM products LIMIT 10", semantic = TRUE) ``` The third layer is **schema-aware** validation. Describe your tables as a named list — names are tables, values are (optionally named) column vectors: ```{r} schema <- list( orders = c(o_id = "INT", o_user = "INT", o_total = "DECIMAL(10,2)"), users = c(id = "INT", name = "TEXT") ) sql_validate("SELECT o_missing FROM orders", schema = schema) ``` Use `error = TRUE` to turn an invalid result into a `polyglot_validation_error` condition — convenient in pipelines. ## Source tables ```{r} sql_source_tables( "WITH cte AS (SELECT id FROM base) SELECT * FROM cte JOIN other USING (id)" ) ``` Note the CTE itself is not listed — only physical sources are. ## Column-level lineage `sql_lineage()` traces every output column through CTEs, subqueries and expressions down to source tables: ```{r} lin <- sql_lineage( "WITH base AS (SELECT id, amount FROM payments) SELECT id, amount * 2 AS doubled FROM base" ) lin ``` Each entry carries the full lineage tree: ```{r} str(lin$columns[[2]]$tree, max.level = 2) ``` A schema improves resolution of unqualified or ambiguous columns, and `column =` restricts lineage to one output column. ## Structural analysis `sql_analyze()` condenses a query into facts — shape, projections, relations, CTEs, set operations: ```{r} a <- sql_analyze( "WITH x AS (SELECT id FROM t) SELECT x.id, UPPER(name) AS shout FROM x JOIN u ON x.id = u.id" ) a vapply(a$projections, function(p) p$transformKind, character(1)) ``` ## OpenLineage export For data catalogs that speak [OpenLineage](https://openlineage.io/), `sql_openlineage()` emits a `columnLineage` facet with inferred input/output datasets: ```{r} ol <- sql_openlineage( "INSERT INTO reports SELECT id, total FROM sales", namespace = "warehouse" ) names(ol) ```