@@ -275,27 +275,47 @@ except(q1, q2).toSql();
275275
276276## AST Visitor
277277
278- Walk and transform parsed AST nodes.
278+ Walk, search, and transform parsed AST nodes.
279279
280280``` typescript
281- import { parse , Dialect , walk , transform , findAll , getColumns , renameColumns } from ' @polyglot-sql/sdk' ;
281+ import {
282+ parse , Dialect , walk , transform , findAll , findFirst , findByType ,
283+ getColumns , getColumnNames , getTableNames , renameColumns , renameTables ,
284+ addWhere , removeWhere , setLimit , setDistinct , qualifyColumns ,
285+ getAggregateFunctions , hasSubqueries , nodeCount ,
286+ } from ' @polyglot-sql/sdk' ;
282287
283288const { ast } = parse (' SELECT a, b FROM t WHERE x > 1' , Dialect .Generic );
284289
285- // Walk all nodes
286- walk (ast , (node ) => console .log (node ));
290+ // Walk all nodes with visitor callbacks
291+ walk (ast , {
292+ enter : (node ) => console .log (' Entering:' , node ),
293+ column : (node ) => console .log (' Found column:' , node ),
294+ });
287295
288- // Find all nodes matching a predicate
296+ // Search for nodes
289297const columns = getColumns (ast );
298+ const first = findFirst (ast , (node ) => getExprType (node ) === ' column' );
299+ const selects = findByType (ast , ' select' );
290300
291- // Transform AST nodes
292- const transformed = transform (ast , (node ) => {
293- // Return modified node or undefined to keep original
294- return node ;
295- });
301+ // Get names as strings
302+ const colNames = getColumnNames (ast ); // ['a', 'b']
303+ const tableNames = getTableNames (ast ); // ['t']
304+
305+ // Check for specific constructs
306+ const hasAggs = hasAggregates (ast );
307+ const hasSubs = hasSubqueries (ast );
308+ const count = nodeCount (ast );
296309
297- // Rename columns
310+ // Transform AST nodes
298311const renamed = renameColumns (ast , { a: ' alpha' , b: ' beta' });
312+ const renamedTables = renameTables (ast , { t: ' users' });
313+ const qualified = qualifyColumns (ast , ' users' );
314+
315+ // Modify query structure
316+ const withLimit = setLimit (ast , 100 );
317+ const distinct = setDistinct (ast , true );
318+ const noWhere = removeWhere (ast );
299319```
300320
301321## Validation
@@ -344,6 +364,66 @@ const result = validateWithSchema(
344364// result.errors will contain an error for unknown_col
345365```
346366
367+ ## Column Lineage
368+
369+ Trace how columns flow through SQL queries, from source tables to the result set.
370+
371+ ``` typescript
372+ import { lineage , getSourceTables } from ' @polyglot-sql/sdk' ;
373+
374+ // Trace a column through joins, CTEs, and subqueries
375+ const result = lineage (' total' , ' SELECT o.total FROM orders o JOIN users u ON o.user_id = u.id' );
376+ if (result .success ) {
377+ console .log (result .lineage .name ); // 'total'
378+ console .log (result .lineage .downstream ); // source nodes
379+ }
380+
381+ // Get all source tables that contribute to a column
382+ const tables = getSourceTables (' total' , ' SELECT o.total FROM orders o JOIN users u ON o.user_id = u.id' );
383+ if (tables .success ) {
384+ console .log (tables .tables ); // ['orders']
385+ }
386+ ```
387+
388+ ## SQL Diff
389+
390+ Compare two SQL statements and get a list of edit operations using the ChangeDistiller algorithm.
391+
392+ ``` typescript
393+ import { diff , hasChanges , changesOnly } from ' @polyglot-sql/sdk' ;
394+
395+ const result = diff (
396+ ' SELECT a, b FROM t WHERE x > 1' ,
397+ ' SELECT a, c FROM t WHERE x > 2' ,
398+ );
399+
400+ if (result .success ) {
401+ console .log (hasChanges (result .edits )); // true
402+ console .log (changesOnly (result .edits )); // only insert/remove/move/update edits
403+
404+ for (const edit of result .edits ) {
405+ // edit.type: 'insert' | 'remove' | 'move' | 'update' | 'keep'
406+ console .log (edit .type , edit .source , edit .target );
407+ }
408+ }
409+ ```
410+
411+ ## Query Planner
412+
413+ Convert a SQL query into an execution plan represented as a DAG of steps.
414+
415+ ``` typescript
416+ import { plan } from ' @polyglot-sql/sdk' ;
417+
418+ const result = plan (' SELECT dept, SUM(salary) FROM employees GROUP BY dept' );
419+ if (result .success ) {
420+ const { root, leaves } = result .plan ;
421+ console .log (root .kind ); // 'aggregate'
422+ console .log (leaves [0 ].kind ); // 'scan'
423+ console .log (leaves [0 ].name ); // 'employees'
424+ }
425+ ```
426+
347427## Class-Based API
348428
349429For an object-oriented style, use the singleton ` Polyglot ` class:
@@ -371,6 +451,17 @@ const formatted = pg.format('SELECT a,b FROM t');
371451| ` getDialects() ` | List supported dialect names |
372452| ` getVersion() ` | Get library version |
373453
454+ ### Analysis Functions
455+
456+ | Function | Description |
457+ | ----------| -------------|
458+ | ` lineage(column, sql, dialect?, trimSelects?) ` | Trace column lineage through a query |
459+ | ` getSourceTables(column, sql, dialect?) ` | Get source tables for a column |
460+ | ` diff(source, target, dialect?, options?) ` | Diff two SQL statements |
461+ | ` hasChanges(edits) ` | Check if diff has non-keep edits |
462+ | ` changesOnly(edits) ` | Filter to only change edits |
463+ | ` plan(sql, dialect?) ` | Build a query execution plan DAG |
464+
374465### Expression Helpers
375466
376467| Function | Description |
@@ -402,20 +493,57 @@ const formatted = pg.format('SELECT a,b FROM t');
402493| ` CaseBuilder ` | ` caseWhen() ` / ` caseOf(expr) ` | CASE expressions |
403494| ` SetOpBuilder ` | ` union() ` / ` unionAll() ` / ` intersect() ` / ` except() ` | Set operations |
404495
405- ### AST Visitor
496+ ### AST Walker
406497
407498| Function | Description |
408499| ----------| -------------|
409- | ` walk(node, callback ) ` | Walk all AST nodes |
500+ | ` walk(node, visitor ) ` | Walk all AST nodes with visitor callbacks |
410501| ` findAll(node, predicate) ` | Find nodes matching a predicate |
411- | ` transform(node, callback) ` | Transform AST nodes |
502+ | ` findByType(node, type) ` | Find all nodes of a specific type |
503+ | ` findFirst(node, predicate) ` | Find the first matching node |
504+ | ` some(node, predicate) ` | Check if any node matches |
505+ | ` every(node, predicate) ` | Check if all nodes match |
506+ | ` countNodes(node, predicate) ` | Count nodes matching a predicate |
507+ | ` getChildren(node) ` | Get direct children of a node |
508+ | ` getColumns(node) ` | Get all column expression nodes |
509+ | ` getTables(node) ` | Get all table expression nodes |
510+ | ` getIdentifiers(node) ` | Get all identifier nodes |
511+ | ` getFunctions(node) ` | Get all function call nodes |
512+ | ` getAggregateFunctions(node) ` | Get all aggregate function nodes |
513+ | ` getWindowFunctions(node) ` | Get all window function nodes |
514+ | ` getSubqueries(node) ` | Get all subquery nodes |
515+ | ` getLiterals(node) ` | Get all literal nodes |
516+ | ` getColumnNames(node) ` | Get column names as strings |
517+ | ` getTableNames(node) ` | Get table names as strings |
518+ | ` hasAggregates(node) ` | Check for aggregate functions |
519+ | ` hasWindowFunctions(node) ` | Check for window functions |
520+ | ` hasSubqueries(node) ` | Check for subqueries |
521+ | ` nodeCount(node) ` | Total number of AST nodes |
522+ | ` getDepth(node) ` | Max depth of the AST tree |
523+ | ` getParent(root, target) ` | Find parent of a node |
524+ | ` findAncestor(root, target, predicate) ` | Find matching ancestor |
525+ | ` getNodeDepth(root, target) ` | Depth of a specific node |
526+
527+ ### AST Transformer
528+
529+ | Function | Description |
530+ | ----------| -------------|
531+ | ` transform(node, config) ` | Immutable tree transformation with callbacks |
532+ | ` replaceNodes(node, predicate, replacement) ` | Replace nodes matching a predicate |
533+ | ` replaceByType(node, type, replacement) ` | Replace nodes of a specific type |
412534| ` renameColumns(node, mapping) ` | Rename columns in AST |
413535| ` renameTables(node, mapping) ` | Rename tables in AST |
414- | ` getColumns(node) ` | Get all column references |
415- | ` getTables(node) ` | Get all table references |
416536| ` qualifyColumns(node, table) ` | Add table qualifier to columns |
417- | ` addWhere(node, condition) ` | Add WHERE clause |
537+ | ` addWhere(node, condition, operator?) ` | Add/extend WHERE clause (AND/OR) |
538+ | ` removeWhere(node) ` | Remove WHERE clause |
539+ | ` addSelectColumns(node, ...columns) ` | Add columns to SELECT |
540+ | ` removeSelectColumns(node, predicate) ` | Remove columns from SELECT |
541+ | ` setLimit(node, limit) ` | Set LIMIT clause |
542+ | ` setOffset(node, offset) ` | Set OFFSET clause |
543+ | ` removeLimitOffset(node) ` | Remove LIMIT and OFFSET |
544+ | ` setDistinct(node, distinct?) ` | Set SELECT DISTINCT |
418545| ` clone(node) ` | Deep clone AST |
546+ | ` remove(node, predicate) ` | Remove nodes matching a predicate |
419547
420548## Supported Dialects
421549
0 commit comments