Skip to content

Release Notes

GSP Java v3.x to v4 Migration Guide

Version history of general sql parser

GSP Java version 4.2.16(2026-10-12)

  • 2026-10-11 Oracle, PostgreSQL and MySQL names follow the rules of the real servers (checked on Oracle 23.26, PostgreSQL 16.15 and MySQL 8.4.10). Oracle keeps ß when it upper-cases an unquoted name: CREATE TABLE straße creates STRAßE, so SELECT x FROM STRASSE reads another table, as Oracle's ORA-00942 shows. With CREATE TABLE straße(x int); CREATE TABLE t(x int); INSERT INTO t SELECT x FROM STRASSE, lineage read straße.x and now reads STRASSE.x, a table the script does not create. PostgreSQL lower-cases only A to Z in an unquoted name and cuts names to 63 bytes: after CREATE TABLE "Äbc"(x int), INSERT INTO t SELECT x FROM ÄBC now reads "Äbc".x, where it read a separate table ÄBC, and Greek or Georgian capitals stay as written. MySQL compares table names by the simple lower-case mapping, column and index names the same way, and function, procedure and parameter names by the simple upper-case mapping: with lower_case_table_names=1, `İ` and i are one table, ς and σ are two columns, and İ and i are one column; index and parameter names ignore case whatever lower_case_table_names is. IdentifierRules.CaseFold has three new values, UPPER_SIMPLE, LOWER_SIMPLE and LOWER_ASCII, and IdentifierRules two new fields, collapse (IdentifierRules.CaseCollapse) and maxUtf8Bytes, set by a new six-argument constructor; the rules are IdentifierRules.forOracleNative(), forPostgreSQLNative(), forMySQLNative(int), forMySQLNativeColumn() and forMySQLNativeRoutine(). Greenplum, GaussDB, EDB, OceanBase, Doris, StarRocks, Dameng and the other dialects derived from these three keep their earlier rules.
  • 2026-10-11 SQL Server accent-insensitive collations make café and cafe one name. GSP compares with a table of accent equivalences measured on SQL Server 2022 for Latin1_General_CI_AI, Latin1_General_CS_AI, SQL_Latin1_General_CP1_CI_AI, Latin1_General_100_CI_AI and Latin1_General_100_CS_AI, with their _SC and _SC_UTF8 variants; it treated _CS_AI as exact and did not equate ø with o, ł with l, đ with d or ı with i under _CI_AI. Other _AI collations are compared as before. IdentifierService.isCaseSensitive(ESQLDataObjectType) is new, and it, NameService.isCaseSensitive and VendorNameMatcher.isCaseSensitive now return true for _CS, _BIN and _BIN2 collations.
  • 2026-10-11 TSQLResolver2 looks names up with the IdentifierProfile given to TGSqlParser.setIdentifierProfile, where some lookups used the vendor's default rules. With Latin1_General_CI_AI, SELECT d.cafe FROM (SELECT 1 AS [café]) d resolves to [café] (it was NOT_FOUND); with MySQL lower_case_table_names=0, WITH c AS (SELECT 1 AS y), C AS (SELECT 2 AS y) SELECT y FROM C reads C.
  • 2026-10-11 IdentifierService.keyForMap returns keys that are equal exactly when areEqual is true, for every vendor. The key is not a name: for SQL Server it is an encoded collation key, and a MySQL column key is lower case. Before, one SQL Server or MySQL column name could give two different keys.
  • 2026-10-11 IdentifierService.persistentKeyV1 gives new policy IDs for Oracle, PostgreSQL and MySQL, because their rules changed (Oracle tables pk1-ry85qY4x... became pk1-Yo5P1xXR...). A key saved by an earlier release for these three databases does not match a key of this release; make the keys again from the names. Keys of the other databases are unchanged.
  • 2026-10-10 SQL Server dynamic SQL whose table names come from a configuration table in the procedure gives lineage (Mantis 4962). A #temp table or table variable that the procedure fills with literal INSERT ... VALUES rows is queried as SQL Server would query it: SELECT @target = raw_table FROM #registry WHERE pipeline_type = @pipeline_type reads the matching rows, with =, <>, <, >, <=, >=, IN, LIKE, IS [NOT] NULL, AND, OR, NOT, TOP and ORDER BY. After INSERT INTO #registry VALUES ('Pipeline_TypeX', 'dbo.BIZ_RAW_X'), ('Pipeline_TypeY', 'dbo.BIZ_RAW_Y'), a procedure that runs SET @sql = 'BULK INSERT ' + @target + ' FROM ''' + @file + '''' and EXEC sp_executesql @sql, called as EXEC dbo.ingest 'E:\in\orders_x.csv', 'Pipeline_TypeX', now gives dbo.BIZ_RAW_X.* <- 'E:\in\orders_x.csv' and nothing for dbo.BIZ_RAW_Y; its dynamic SQL site was UNRESOLVED ("argument is a runtime variable") and gave no lineage. When every call of the procedure in the analysis is bound, only the bound statements are published; otherwise the statements for every row are published as well. Parameter values come, in this order, from DataFlowAnalyzer.resolveDynamicSqlLineage(...), presets in TSQLEnv, a literal EXEC in the same script and a literal EXEC in another file of the analysis; a caller's variable or parameter, and calls nested up to four deep, are followed. Dynamic SQL inside BEGIN TRY ... END CATCH is evaluated, where the whole body was skipped and resolveDynamicSqlLineage returned no sites; DynamicSqlSite.isExecutionCertain() is false for a statement in a CATCH block or behind a condition or loop the analysis cannot decide. A query the evaluator does not model (a JOIN, an aggregate, a subquery, an unknown function), more than 8 values for one variable (DynamicLineageOptions.maxValueSetSize) or more than 1024 rows (maxConstRelationRows, which was 64) leaves the site UNRESOLVED with the reason instead of guessing. String comparisons ignore case and trailing blanks, as SQL Server's default collation does; Option.setDynamicSqlCaseSensitiveCompare(true) makes them case-sensitive. The query evaluator is public in gudusoft.gsqlparser.compiler: TLiteralRelationQuery.evaluate(...) runs a SELECT over the rows of an ISqlRowSource, with values as TSqlValue. Not there yet: in a procedure without callers, a BULK INSERT built with SET @sql = ... gives no path node, while the SELECT @sql = ... form gives one.
  • 2026-10-10 SQL Server BULK INSERT gives lineage from its file to its table (Mantis 4962): BULK INSERT dbo.BIZ_RAW_X FROM 'E:\in\orders_x.csv' WITH (FIRSTROW = 2) gives the path node 'E:\in\orders_x.csv' and dbo.BIZ_RAW_X.* <- 'E:\in\orders_x.csv', as Redshift COPY does, where it gave nothing. The edge goes to the whole table, never to columns by position. A file name that comes from a procedure parameter with no known value gives a template path node, '@file', whose table.getEndpointKind() is DYNAMIC_TEMPLATE. For WITH (DATA_SOURCE = 'src') the XML output gives the path node a dataSourceName, and its uri starts with the LOCATION of a CREATE EXTERNAL DATA SOURCE src in the script. The parser keeps the WITH options: TMssqlBulkInsert.getOptions(), getOption(String) and getOptionValue(String) return them as TMssqlBulkInsertOption nodes, with getName() and getValue().
  • 2026-10-10 A procedure's parameters can be preset for dynamic SQL lineage (Mantis 4962). TSQLEnv.setProcedureParameterValue("dbo.ingest", "@pipeline_type", "Pipeline_TypeX"), or procedures[].parameterBindings in a metadata JSON file (for each entry a parameter name and a string, a number or null), gives a procedure analyzed without its callers the values its dynamic SQL is built from; getProcedureParameterValue(procedure, parameter) and getProcedureParameterPresets(procedure) read them back as TSQLRoutine.PresetValue. A procedure name without a database or schema is in the default catalog and schema, so preset a procedure defined under USE X with X in its name. An explicit binding of resolveDynamicSqlLineage replaces the presets, and the presets come before the calls found in the analysis.
  • 2026-10-10 The evaluation of SQL Server dynamic SQL strings follows T-SQL in three more places (Mantis 4962). SET @sql += ' WHERE x = 1' appends to @sql; it was read as SET @sql = ' WHERE x = 1', so the statement lost its start. A SELECT @v = col FROM t WHERE ... that finds no row leaves @v unchanged, and SET @v = (SELECT col FROM t WHERE ...) sets it to NULL, so a site whose value comes only from a query that finds no row is UNRESOLVED and its reason says the query returned zero rows, where every row of a table variable was published because WHERE was ignored. A value read into a variable is converted to the variable's declared type.

GSP Java version 4.2.15(2026-10-10)

  • 2026-10-09 Teradata parses eleven forms that Vantage accepts and that returned 2 from parse() with a syntax error (Mantis 4945-4947, 4952-4958, 4961): LEAD, LAG, SCRIPT and COLUMNS as names (LEFT JOIN (SELECT k FROM t2) LEAD ON t1.k = LEAD.k, SUM(lag) OVER (...), FROM Script SCR, SELECT Columns AS col_b), while LEAD(x) OVER (...), LAG(x) OVER (...) and the SCRIPT table operator keep their meaning; trunc(date); the type attribute 100.0(DOUBLE PRECISION); a column named period followed by (NAMED a, CHAR(17)); more than one name after an array index in a JSON path, col."result"[0]."x"."y"; a derived table's column list, (SELECT DISTINCT k FROM t2) c (k); an alias that starts with $ after AS, AS $col_b (every other $name is still read as a placeholder variable, as before); and a CREATE TABLE AS column list that mixes attributed and bare names, (col_a CASESPECIFIC, col_b NOT NULL, col_c, col_d)
  • 2026-10-09 SQL Server parses the two-argument LTRIM(string, characters) and RTRIM(string, characters) (Mantis 4948), whose two arguments TFunctionCall.getArgs() returns; a statement that ends in a -- comment starting with a digit and has no newline after it, ... ON a.k = b.k--1 note (Mantis 4951), which gave no statement and an empty error message (a line that holds only blanks and -- followed by a digit is now always a comment, as SQL Server reads it, whatever the length of the lines before it); and rename as a table or derived-table alias, FROM (SELECT col_a FROM tab1) rename (Mantis 4959). RENAME stays a keyword before OBJECT, DATABASE, COLUMN and CONSTRAINT, so RENAME OBJECT t TO t2 parses as before
  • 2026-10-09 Oracle parses WITH FUNCTION in front of the query of a CREATE VIEW (Mantis 4949), which failed with "Error when tokenize" at the first ; of the function, and a function declaration section with variables and constants; an expression as the third argument of TRANSLATE, TRANSLATE(col_a, 'x', (SELECT max(c1) FROM tab2) || ' ') (Mantis 4950), whose columns are now lineage sources and whose literal is no longer listed as a column (the third argument is now an ordinary expression, so a literal there is simple_constant_t, where it was simple_source_token_t); and an expression as the XQuery string of XMLTABLE, xmltable(('"' || replace(str, '//', '","') || '"')) (Mantis 4960). TXmlTableParameter.getXQueryExpression() returns the string in every form; getXQueryString() still returns the literal or bind variable and returns null for any other expression
  • 2026-10-08 Teradata parses the forms it writes into stored view text (Mantis 4899-4905, 4907-4909); each returned 2 from parse() with a syntax error: a temporal qualifier after a table name, FROM t CURRENT TRANSACTIONTIME and FROM t TRANSACTIONTIME AS OF (SELECT ...) s (and the VALIDTIME forms), kept as TTable.getFlashback(); TD_SYSFNLIB.XMLAGG, TD_SYSFNLIB.XMLSERIALIZE and TD_SYSFNLIB.XMLTABLE; XMLTABLE(... PASSING BY VALUE ...); LAG(x RESPECT NULLS, 1, NULL) and LEAD(x IGNORE NULLS, 1, NULL); the interval qualifier SECOND(4,6); a table operator without USING, SYSLIB.GetColumns ( ON DBC.COLUMNSV ) AS G; the RETURNS clause of a function call, (f(x) RETURNS INTEGER), (f(x) RETURNS STYLE t.c) and TRYCAST(x) RETURNS INTEGER, whose type TFunctionCall.getTypename() returns; and a method call on a parenthesized value, (NEW JSON(x, LATIN)).JSONEXTRACTVALUE('$.a'). A function call followed by RETURNS without the enclosing parentheses is still a syntax error, as Teradata requires them. Not there yet: the source columns of an interval-qualified expression and of NEW JSON(...), the table read in an AS OF subquery, and the aggregate edge of TD_SYSFNLIB.XMLAGG (Mantis 4918-4921).
  • 2026-10-08 Teradata parses PERIOD(a, b) right after an opening bracket, SELECT (PERIOD(T.B, T.E)) AS P and SELECT (PERIOD(DATE(T.B), DATE(T.E))) AS P (Mantis 4906); the bracket was read as the start of a cast shorthand such as x (PERIOD(DATE)), which still works, and lineage gives P <- T.B, T.E.
  • 2026-10-08 TCreateTableSqlStatement.isIfNotExists() returns true for CREATE TABLE IF NOT EXISTS in every dialect whose grammar accepts it. It was true only in Hive, Impala, Oracle and Dameng, and false in BigQuery, Spark SQL, Databricks, Athena, Presto, Trino, MySQL, PostgreSQL, Snowflake, Redshift and the other dialects that parse the clause. Lineage reads the flag, so in these dialects too a CREATE TABLE IF NOT EXISTS of a table declared earlier in the script declares no fields of its own. toScript() and the formatter output are unchanged.
  • 2026-10-08 Databricks reads every column alias of a LATERAL VIEW, as Spark SQL and Hive do. In LATERAL VIEW posexplode(items) lv AS pos, item, TLateralView.getColumnAliasList() returned [pos] and item became a second table of the FROM clause, so CREATE VIEW v AS SELECT pos, item.name AS name FROM events LATERAL VIEW posexplode(items) lv AS pos, item stopped with a NullPointerException; the aliases are now [pos, item], and the view gives lv.item <- events.items and v.name <- lv.item. lv AS x, other_table o, which parsed as a comma join, is now a syntax error, as in Spark SQL and Hive.
  • 2026-10-08 A field of a constructed STRUCT that reads a field under another name gets its edge in the simple output. Trino CAST(ROW(payload.address.city, payload.phone) AS ROW(town VARCHAR, tel VARCHAR)) AS contact gave no edge for its fields; it now gives v.contact.town <- events.payload.address.city and v.contact.tel <- events.payload.phone, and a renamed field of a Hive, Spark SQL or Databricks named_struct gets its edge in the same way. The simple output keeps every source that an fdd path of the full output reaches and no longer drops a STRUCT source whose last name part differs from the target's; DataflowRemoveHelper and RemoveDataflowFunction fold the same way.
  • 2026-10-08 A STRUCT select item behind a star of a table without DDL counts as one item, whatever its number of fields. With CREATE TABLE contacts (order_id INT64, note STRING, contact STRUCT<name STRING, city STRING>), BigQuery INSERT INTO contacts (order_id, note, contact) SELECT o.*, STRUCT(c.name AS name, c.city AS city) FROM recent_orders o, customers c gave contacts.note <- customers.name and contacts.contact <- customers.city; it now gives contacts.note <- recent_orders.*, contacts.contact.name <- customers.name and contacts.contact.city <- customers.city, and so do the column list of a view, a CREATE TABLE and a CTE, and an INSERT without a column list. A CTE column list over one star with an item after it, CREATE VIEW v AS WITH c (x, y, z) AS (SELECT s.*, t.p FROM src s, other t) SELECT x, y, z FROM c, stopped the analysis with an IndexOutOfBoundsException; it now gives v.x <- src.*, v.y <- src.* and v.z <- other.p.
  • 2026-10-07 TABLE t, the SQL-standard shorthand for SELECT * FROM t, is analyzed as SELECT * FROM t in PostgreSQL, DuckDB, GaussDB, EDB, Greenplum, SQLite, MySQL, OceanBase, Spark SQL, Databricks, Athena, Presto, Trino and Snowflake. The parser built an empty select for it, so the table was missing from getTables(), TSQLResolver2 and lineage: CREATE TABLE t2 AS TABLE t1 gave no lineage in PostgreSQL and Snowflake and stopped the analysis of the statement with a NullPointerException in Athena, Presto and Trino; it now gives t2.a <- t1.a and t2.b <- t1.b, as CREATE TABLE t2 AS SELECT * FROM t1 does. The same holds for INSERT INTO t2 TABLE t1, a UNION, EXCEPT or IN branch TABLE t3, WITH c AS (TABLE t1) and a statement TABLE t1 [ORDER BY ...] [LIMIT ...], which Athena, Presto, Trino, Databricks, Snowflake, Greenplum and SQLite now accept. toString() still prints TABLE t, and toScript() writes it back as TABLE [ONLY] t; a relation written ONLY t now keeps ONLY in toScript() (SELECT * FROM ONLY src gave select * from src). API changes: for CREATE TABLE dst AS TABLE src, getTables() of the CREATE TABLE statement is [dst] and src is a table of getSubQuery(), as for the AS SELECT form, and getAsTable() returns that table (null before on Athena, Presto, Databricks, Snowflake, MySQL, OceanBase and Spark SQL); TPostgresqlTableStmt keeps its statement type sstPostgresqlTable and now extends TSelectSqlStatement; TFromTable.isOnlyKeyword() and TTable.isOnlyKeyword() are true for ONLY t in the PostgreSQL family and Databricks.
  • 2026-10-07 Databricks parses a CREATE TABLE AS SELECT written without AS or USING (Mantis 4894): CREATE TABLE my_schema.t2 SELECT * FROM my_schema.t1 and CREATE OR REPLACE TABLE ... returned 2 from parse() with a syntax error at SELECT and gave no lineage; they now parse as the AS form does, TCreateTableSqlStatement.getSubQuery() returns the query, and lineage gives the same edges, my_schema.t2.* <- my_schema.t1.*. CREATE TABLE t2 (values INT) and other first columns named like a keyword stay column definitions.
  • 2026-10-07 Databricks parses DESCRIBE TABLE [EXTENDED] t AS JSON (Mantis 4895), which dbt-databricks issues; it returned 2 from parse() with a syntax error at AS. The statement is a TDescribeStmt and TDescribeStmt.isAsJson() returns true.
  • 2026-10-07 Databricks splits DECLARE [OR REPLACE] [VARIABLE] name [type] [DEFAULT expr] into a statement of its own, typed ESqlStatementType.sstmysqldeclare, as Spark SQL does (Mantis 4896). DECLARE VARIABLE my_var STRING DEFAULT 'x'; SELECT my_var; returned 1 from parse() with a tokenizer error, and the lineage of every other statement of such a script was lost: DECLARE ...; INSERT INTO s.t2 SELECT a FROM s.t1; now gives s.t2.a <- s.t1.a.
  • 2026-10-07 SQL Server parses an XML method called on the result of another, T.c.query('.').value('.', 'varchar(10)') (Mantis 4897), in SELECT, INSERT, SELECT INTO, UPDATE, DELETE, MERGE, WHERE and function arguments; it returned 1 from parse() with a syntax error at the second dot. INSERT INTO dbo.target (v) SELECT T.c.query('.').value('.', 'varchar(10)') FROM dbo.src s CROSS APPLY s.xml_col.nodes('/Root/item') T(c) now gives dbo.target.v <- dbo.src.xml_col, the edge the single method T.c.value(...) gives, and TSQLResolver2 sees the column under the chain.
  • 2026-10-07 MySQL parses a CREATE TABLE whose query is in parentheses right after the table name (Mantis 4898): CREATE TABLE t2 ( SELECT * FROM t1 ORDER BY c ASC ) returned 2 from parse() with a syntax error at *; it now parses as CREATE TABLE t2 AS SELECT ... does, with the ORDER BY kept in getSubQuery(), and gives the same lineage. IF NOT EXISTS, TEMPORARY, a parenthesized UNION, WITH, VALUES and TABLE t1 queries parse too; column lists, constraints and columns named like a keyword are unchanged.
  • 2026-10-07 Spark SQL parses a field of an array element, items[0].name, and a subscript that is any expression, items[i].name or items[i + 1].name, as Hive and Databricks do (Mantis 4885). SELECT items[0].name AS n FROM events returned 2 from parse() with a syntax error at the dot; it now parses, the expression is a field access (EExpressionType.field_access_t) over the subscript, and lineage gives RS-1.n <- events.items[*].name, the edges Hive gives for the same SQL.
  • 2026-10-07 A STRUCT that a select item builds keeps its fields wherever the item goes (Mantis 4878, 4891, 4892, 4893). With CREATE TABLE out (payload STRUCT<a STRING, b STRING>, z STRING), BigQuery INSERT INTO out (payload, z) SELECT STRUCT(s.x AS a, s.y AS b) AS payload, s.w FROM src s gave out.payload <- src.x and out.z <- src.y, the fields counted as two positions; it now gives out.payload.a <- src.x, out.payload.b <- src.y and out.z <- src.w, and so do a view's column list (Trino CREATE VIEW w (contact, z) AS SELECT CAST(ROW(s.x, s.y) AS ROW(a VARCHAR, b VARCHAR)), s.w ...), a CREATE TABLE column list and a CTE column list; an item without an alias feeds the fields too, beside its whole column. Hive CREATE TABLE out AS WITH q AS (SELECT named_struct('a', s.x, 'b', s.y) AS payload FROM src s) SELECT payload FROM q gave out.payload <- src.x, src.y; it now gives out.payload.a <- src.x and out.payload.b <- src.y, through a CTE, a derived table (Spark SQL FROM (SELECT named_struct(...) AS payload FROM src s) q) or a UNION branch, while a branch that builds no STRUCT feeds the whole column. A field read through a derived table, q.payload.a, reads only its field. A STRUCT inside a STRUCT publishes its leaves, named_struct('a', named_struct('b', s.x)) gives out.payload.a.b <- src.x, and a typed BigQuery STRUCT<a STRING>(s.x) names its field a, by its type, where it took the name of the value column x. Behind a star of a table without DDL, INSERT INTO out (x, y, payload) SELECT s.*, STRUCT(s.z AS a) FROM src s gives out.payload.a <- src.z: the last item fills the last column whatever the width of the star. Hive struct(s.x, s.y) AS st names its fields col1 and col2; Spark SQL and Databricks name a field that reads a column after it, st.x and st.y. A BigQuery view of STRUCT(customer.name AS name, order_id AS id) AS buyer now lists the column buyer above buyer.name and buyer.id, as Trino does. The elements of an array constructor feed the column: PostgreSQL SELECT ARRAY[t.a, t.b] AS arr FROM t gives RS-1.arr <- t.a, t.b, where it gave nothing. A declared STRUCT column projected whole, SELECT customer AS c FROM orders_raw, stays one edge.
  • 2026-10-07 A Hive, Spark SQL or Databricks LATERAL VIEW is one relation of its SELECT (Mantis 4887, 4888, 4889, 4890). The WHERE clause of a query with a LATERAL VIEW now gives its row lineage, RS-1.RelationRows <- lv.item, and a CASE over a field of the element reads that field, v.n <- events.items[*].name. The function's argument is read from the relation TSQLResolver2 resolves among those to the view's left: in FROM src, host LATERAL VIEW explode(src.items) lv AS item it reads src.items and never host.items, an input of a CTE or derived table to the left is read, and one that names a relation to the right reads nothing. A LATERAL VIEW over a field of an earlier one, explode(item.subs), reads events.items[*].subs. A view without a table alias (Spark SQL, Databricks) and a view after a JOIN resolve their column aliases, every INSERT of a Hive multi-insert reads the view of the shared FROM clause, and the relation of a view never publishes the name of a table: one aliased like a table of the script is numbered, tmp1-1.
  • 2026-10-07 The fields of a nested column follow the CREATE TABLE in force at each statement (Mantis 4861, 4862, 4884). After an Athena DROP TABLE events and a second CREATE EXTERNAL TABLE events (payload STRUCT<new_field:STRING>) LOCATION 's3://b/new/', a read of payload.new_field gives RS-1.new_value <- events.payload.new_field, and a field only the dropped table declared is no field any more; a CREATE TABLE IF NOT EXISTS of an existing table declares nothing. An Athena array's element node is fed by its LOCATION: events.tags[*] <- 's3://b/e/'.tags[*]. Impala builds the same tree as Hive, table events: items, items[*], items[*].name, where it published the fields as columns. A subscript reads the array's element in every dialect that models nested columns: BigQuery tags[OFFSET(0)], Hive, Spark SQL, Databricks, Athena, Presto and Trino tags[1] give v.t <- events.tags[*], where several gave v.t <- events.tags; a subscript of a declared MAP reads its value node, and one of a declared STRUCT or JSON column reads the column.
  • 2026-10-07 DlineageUtil.modelsStructTree(EDbVendor), DlineageUtil.modelsStructTree(), NameResolver.isTrinoFamily(EDbVendor) and NameResolver.readsFieldChains(EDbVendor) are deprecated. They keep returning what they returned; the vendor rules for nested columns now live in one public class, gudusoft.gsqlparser.nodes.structured.NestedAccessProfile, and NestedAccessProfile.forVendor(vendor) with buildsDeclaredTree(), isTrinoFamily() and readsFieldChains() replaces them.
  • 2026-10-06 dlineage reports the positions of what it analyzes inside SQL Server dynamic SQL (EXEC (@sql), EXEC ('...'), sp_executesql) in the procedure, not in the string (Mantis 4851). For a procedure whose line 4 is SET @SQL1 = ' and whose lines 5-7 continue the string with INSERT INTO dbo.tgt (a) SELECT COALESCE(s.x, s.y) FROM dbo.src s, the source column s.x had the coordinate [3,17], line 3 of the string, and now has [6,17]; so did tables, columns and the statement itself. A string built in several SETs keeps the literal each part was read from, '' counts as two characters of the source, and a part that comes from a variable takes the place where it is spliced in, after the closing quote of the literal before it. A string assembled out of source order, and dynamic SQL inside dynamic SQL, take the position of their EXEC. EXECUTE IMMEDIATE and the EXECUTE of other dialects keep the positions of the string, and so do the syntax errors reported inside a dynamic string.
  • 2026-10-05 Hive, Spark SQL and Databricks named_struct('a', x, 'b', y) AS payload gives each field its own lineage, as BigQuery STRUCT(x AS a, y AS b) does (Mantis 4847). CREATE TABLE out AS SELECT named_struct('a', s.x, 'b', s.y) AS payload FROM src s gave out.payload <- src.x, src.y, and a later SELECT payload.a FROM out read a field node that nothing fed; it now gives out.payload.a <- src.x and out.payload.b <- src.y, and the read gives v.a <- out.payload.a; a view gets the same fields. A CREATE TABLE AS SELECT of a Trino, Athena or Presto CAST(ROW(s.x, s.y) AS ROW(a VARCHAR, b VARCHAR)) AS contact made the columns a and b of the table; it now makes the field nodes out.contact.a and out.contact.b, as a view does (Mantis 4834). A named_struct with a name that is not a non-empty string literal, qualified by a database, inside another function or in a filter is analyzed as before; one without an alias, or paired by position with a column list (INSERT INTO t (a, b) SELECT ..., CREATE VIEW v (a, b) AS ...), is covered by the 2026-10-07 entry on STRUCT select items.
  • 2026-10-05 A field of the element that a Hive, Spark SQL or Databricks LATERAL VIEW explodes is read from the array's element (Mantis 4845). After CREATE TABLE events (id INT, items ARRAY<STRUCT<name:STRING>>), CREATE VIEW v AS SELECT lv.item.name AS n FROM events LATERAL VIEW explode(items) lv AS item gave v.n <- lv.item, the whole element, and TSQLResolver2 reported lv.item.name as NOT_FOUND; it now gives v.n <- events.items[*].name, with and without DDL, and lv.item.name resolves with TObjectName.getFieldPathRead() item.name. TSQLResolver2 keeps a relation for a LATERAL VIEW, whose columns are its aliases, so an unqualified item resolves to it (TObjectName.getSourceTable() is the view's table, of type ETableSource.lateralView) instead of to the first table of the FROM clause, which gave lineage columns such as ods_adp.lofta_tinstitutionantimoneyinfo.contract_split that the table does not have; a name that another table also has is ambiguous. An unqualified item.name over a declared table, or a LATERAL VIEW without column aliases, no longer stops the analysis of the view with a NullPointerException, and for posexplode and other LATERAL VIEWs a field read reads the whole column, lv.item. The LATERAL VIEWs of two queries that share an alias were one table, so the lineage of each query reached the column the other exploded; a later view now gets a table of its own, numbered tmp1-1, tmp1-2, and a script without a repeated alias gives the same output as before.
  • 2026-10-05 DynamicLineageEdge names each call of a function as a node of its own (Mantis 4848). DynamicSqlLineageResolver named an intermediate node by its display name only, so the three COALESCE(...) calls of INSERT INTO dbo.tgt (k1, k2, d) SELECT COALESCE(A.k1, B.k1), COALESCE(A.k2, B.k2), COALESCE(A.d, B.d) ... run through EXEC (@sql) were one node COALESCE.COALESCE, and a consumer that followed getSourceColumn() to getTargetColumn() through it linked every input to every output, 18 base edges where 6 are real. A display name that several nodes share is now numbered, COALESCE#1.COALESCE, COALESCE#2.COALESCE and COALESCE#3.COALESCE, across all the dynamic statements and value variants of one DynamicSqlLineageResolver.resolve call, whose getProjectedEdges() is one list, so the RS-1 of a second EXEC is RS-1#2; a number that a table's name already has is skipped, and the first node with a name no other node has keeps it, such as a function called once, RS-16 or INSERT-SELECT-1.
  • 2026-10-05 A column that ALTER TABLE ... ADD COLUMN (ADD COLUMNS, ADD COLUMN IF NOT EXISTS) adds to a table declared earlier in the script is a column of that table, with its type, for the statements after the ALTER, in lineage and in TSQLResolver2 (Mantis 4846). The catalog kept the table as CREATE TABLE declared it, and DataFlowAnalyzer analyzed every view before every ALTER: after CREATE TABLE events (id BIGINT); ALTER TABLE events ADD COLUMNS (payload STRUCT<name:STRING>);, Hive, Spark SQL and Databricks CREATE VIEW v AS SELECT payload.name AS n FROM events had no lineage and payload.name was NOT_FOUND; it now gives v.n <- events.payload.name, and TObjectName.getFieldPathRead() returns payload.name. In every dialect CREATE VIEW v AS SELECT * FROM events after ALTER TABLE events ADD COLUMN c INT now also gives v.c <- events.c. A statement before the ALTER still sees the table without the column: there c in SELECT c FROM events, other is still other.c, and SELECT * FROM events reads no c. The ALTER gives the TSQLEnv a new version of the table, TSQLTable.newVersion(), as CREATE OR REPLACE TABLE replaces one, and a statement before it keeps the version it found; an ALTER inside a routine, which runs when the routine is called, and a sealed table are left alone. events and public.events are one table here too. A stored procedure after the ALTER, and a view that such a procedure reads, still read the table as declared (Mantis 4883), and so does TSQLResolver2 for a SQL Server #temp table (Mantis 4886).
  • 2026-10-05 Hive items[0].name reads the field name of an element of the array column items (Mantis 4844). The grammar builds a field access over the subscript, and nothing below it was parsed or visited, so CREATE VIEW va AS SELECT items[0].name AS n FROM events had no lineage and TSQLResolver2 had no reference to items; it now gives va.n <- events.items[*].name, with and without DDL, also for e.items[0][1].name (events.items[*][*].name), items[0].a[1].b (events.items[*].a[*].b) and (items[0]).name, and TObjectName.getFieldPathRead() returns items[*].name. The column records the subscripts and the field after them in TObjectName.getIndirection(), as BigQuery's grammar does (Mantis 4833). Impala, whose grammar builds the same expression, now resolves the column inside it too: items[0].name gives va.n <- events.items[*].name where it gave nothing (the element node since the 2026-10-07 entry on nested fields).
  • 2026-10-05 An input that ends inside a double-quoted identifier is a syntax error at the opening quote, in every dialect (Mantis 4842). The lexer reached the end inside the identifier and returned no token for it, so the quote and everything after it were dropped: SELECT 1 id"; returned 0 from parse() with no error in Athena, PostgreSQL, Oracle and almost every other dialect, SELECT 1 id"; SELECT 2; lost its second statement, and Oracle accepted SELECT 1 AS "id;. parse() now fails, and getSyntaxErrors() gives the position of the quote, line 1 column 12 for SELECT 1 id";. In MySQL, where double quotes delimit a string, the same holds for that string. Closed identifiers parse as before; an input ending inside a single-quoted string or a block comment is unchanged (Mantis 4882).
  • 2026-10-05 The columns of a VALUES table without a column list take the vendor's names, in lineage and in TSQLResolver2 (Mantis 4823): PostgreSQL, Greenplum, Redshift and Snowflake call them column1, column2, ..., Spark SQL and Databricks col1, col2, .... SELECT r.column1 FROM (VALUES ((SELECT x FROM t), 2)) AS r in PostgreSQL gave no lineage and r.column1 was NOT_FOUND; now RS-1.column1 <- result_of_r-1.column1 <- t.x and r.column1 resolves. TValueClause.defaultColumnName(vendor, position) is the rule.
  • 2026-10-05 A function in a VALUES row gives the column its value in lineage, as in a SELECT list (Mantis 4824): in SELECT r.a FROM (VALUES (COALESCE((SELECT x FROM t), 0))) AS r(a), r.a now has t.x upstream through COALESCE; the same for ABS, CASE and a column inside a function, such as CROSS APPLY (VALUES (UPPER(t.name))) AS r(a) in SQL Server.
  • 2026-10-05 A derived table's alias column list renames the columns a SELECT * expands to, by position, in TSQLResolver2 (Mantis 4843). Over t3(c1, c2, c3), in SELECT d.a, d.b, d.c1, d.c3 FROM (SELECT * FROM t3) d(a, b), d.a and d.b were NOT_FOUND and d.c1 resolved; now d.a resolves to t3.c1 and d.b to t3.c2 (ColumnSource.getFinalColumnName() returns c1 and c2), d.c1 and d.c2 are NOT_FOUND, and d.c3 still resolves. A column after the star takes the position after the star's columns. The star's columns are known from DDL, or from a catalog table that lists every one of them; otherwise (no metadata, a catalog table without columns or with an unexpanded star, * EXCEPT, * EXCLUDE or * REPLACE, a USING, NATURAL, semi or anti join) d.a is a column of d with no column of t3 behind it (ColumnSource.getFinalTable() returns null and getAllFinalTables() is empty), instead of an invented t3.a, and a name renamed away stays visible when another column of the star may pass it on. In lineage every name past such a star reads the star: INSERT INTO o (x, y) SELECT d.a, d.b FROM (SELECT * FROM t) d(a, b) gives o.x <- t.* and o.y <- t.*, where it gave o.x <- t.a and nothing for o.y. A derived table over a set operation, (SELECT * FROM t UNION ALL SELECT * FROM u) d(a), is Mantis 4870.
  • 2026-10-05 A PostgreSQL SELECT ... INTO name statement creates the table name, in lineage and in the parse (Mantis 4857), as PostgreSQL documents and as INTO TEMP name, Greenplum and Redshift already did. After CREATE TABLE TABLE_1 (COL_1 int, COL_2 int); select COL_1, COL_2 into TMP2 from TABLE_1; insert into TABLE_2 (COL_1, COL_2_RENAMED) select * from TMP2; the target was a record variable, so the simple output dropped it and gave only TABLE_2.COL_1 <- TABLE_1.COL_1; it now gives the table TMP2 with TMP2.COL_1 <- TABLE_1.COL_1, TMP2.COL_2 <- TABLE_1.COL_2, TABLE_2.COL_1 <- TMP2.COL_1 and TABLE_2.COL_2_RENAMED <- TMP2.COL_2. The target's TObjectName.getDbObjectType() is EDbObjectType.table and the SELECT lists it among its tables with ETableEffectType.tetSelectInto, as for the other forms. The body of a LANGUAGE SQL function holds SQL and creates the table too, while in a PL/pgSQL body, of a function, a procedure or a DO block, INTO still assigns variables, and a SELECT ... INTO run through EXECUTE, which PL/pgSQL rejects, creates none.
  • 2026-10-05 OceanBase in a MySQL tenant ends a stored routine written without a DELIMITER command at the END; that closes its outermost BEGIN, as MySQL does (Mantis 4841). CREATE PROCEDURE p() BEGIN SELECT 1; END; followed by SELECT 2; gave one statement and a syntax error at SELECT 2, so every statement after a routine was lost; TGSqlParser(EDbVendor.dbvoceanbase) now splits such a script into the statements EDbVendor.dbvmysql gives, also for routines in a row, a nested BEGIN ... END; or BEGIN ... END label;, and inside a body END IF;, END WHILE;, CASE ... END CASE; and a CASE ... END expression, which do not end the routine. A name ending in $, such as col$, ends a routine only after a DELIMITER command set $.
  • 2026-10-05 Doris INSERT, INSERT OVERWRITE, UPDATE, DELETE and CREATE VIEW are parsed into their statement classes and have lineage (Mantis 4855). They returned 0 from parse() as TUnknownSqlStatements with no AST, so DataFlowAnalyzer found no relationship in them and nothing reported it: INSERT INTO t2 SELECT a FROM t1 now gives t2.a <- t1.a, as in MySQL. The Doris forms parse too: INSERT ... WITH LABEL name, INSERT OVERWRITE TABLE t PARTITION(*), DELETE FROM t PARTITION p1 and PARTITIONS (p1, p2), UPDATE t1 SET ... FROM t2 JOIN t3 ON ..., whose FROM tables are read, the COMMENT of a view and of its columns, and CREATE VIEW IF NOT EXISTS. CREATE MATERIALIZED VIEW is now its own type, ESqlStatementType.sstcreatematerializedview, and like MERGE it stays a statement without an AST.
  • 2026-10-05 In the simple output with fdr edges shown, a source that reaches a column through a value path is an fdd source of that column even when it also reaches it through a filter, whatever relationship ids the statements before it used (Mantis 4867). The fold walked each intermediate node once, meeting a node's relationships in the string order of their ids (10 before 7), so the first path to a shared node decided the type: INSERT INTO tgt (v) SELECT SUM(e.k) FROM (SELECT d.k FROM (SELECT k FROM s) d WHERE ABS(d.k) > 0) e gave fdr s.k -> tgt.v with clause type where when analyzed alone, and fdd s.k -> tgt.v after two other statements. A node is now walked again when a better path reaches it, so a value path always wins, and the source shown, with its clause type and transforms, comes from a path of the type it gets: with Option.setTransform(true) the fdd edge into tgt.v lists SUM(e.k), no longer the filter's ABS(d.k). Only such paths can mark the folded relationship as an assumed external-script passthrough. Option.setIgnoreRecordSet(true) folds the same way. The default simple output, which shows fdd edges only, is unchanged.
  • 2026-10-05 In the simple output, a source read in a WHERE, ON or HAVING filter through a function, aggregate, CASE, derived table, CTE or variable carries the filter's clause (Mantis 4866). The simple output (Option.setSimpleOutput(true), with fdr edges shown through Option.setSimpleShowRelationTypes("fdd", "fdr")) folds such intermediate nodes away, and the folded source kept the clause of the edge that reads the table, which has none: UPDATE t1 SET v = 1 WHERE ABS(t1.x) > 5 AND t1.y = 2 gave t1.y the clause type where and t1.x none, and so did HAVING SUM(o.amount) > 100000, ON UPPER(c.code) = o.code, a CASE, and ... FROM (SELECT id, x FROM s) d WHERE d.x > 5 for s.x. sourceColumn.getClauseType() of such a folded fdr source now returns where, joinCondition or having, the clause the full output gives the filter's own operand, in the XML and the JSON model alike; a folded source that has a clause of its own keeps it, and one that also reaches the target as a value stays an fdd source without a clause. A GROUP BY key that an aggregate in HAVING reads gets having too: with GROUP BY o.customer_id HAVING SUM(o.amount) > 100000, both Orders.amount and Orders.customer_id feed the result's RelationRows with having. The same holds with Option.setIgnoreRecordSet(true) and in ParallelDataFlowAnalyzer. Only the attribute moves: no edge is added or removed, the default simple output, which shows fdd edges only, is unchanged, and a source that reaches the filter only through a condition analyzed without a clause, such as a MERGE WHEN ... AND condition, still gets none.
  • 2026-10-04 A function, aggregate or CASE operand of a WHERE, ON or HAVING filter carries the filter's clause on its lineage source (Mantis 4865). Such an operand is its own node: for HAVING SUM(o.amount) > 100000 the source of the fdr edge into the result's RelationRows is SUM.SUM, and sourceColumn.getClauseType() returned unknown for it, or nothing for a CASE, where a column operand returns where, joinCondition or having. A consumer that tells filters apart by the source's clause type, as the dlineage guide says to, missed HAVING SUM(...), WHERE ABS(...) and ON UPPER(c.code) = o.code. In the full output (Option.setSimpleOutput(false)) the source now carries having, where or joinCondition, in the XML and the JSON model alike; the relationship's own clause was already right, and no edge is added or removed. The same holds for a constant function such as BigQuery CURRENT_TIMESTAMP(), read from the constants table. A condition analyzed without a clause, such as an IF condition, a MERGE WHEN ... AND condition or a window PARTITION BY, is unchanged.

GSP Java version 4.2.14.1(2026-10-07)

  • 2026-10-07 [Formatter] Multi-row INSERT VALUES keeps a stable indentation for sibling tuples. Stacked rows previously inherited the preceding row's final column, and normalizing every row repeatedly padded the first tuple; large batches could allocate quadratic amounts of whitespace and exhaust the Java heap. Each tuple is now normalized once and stacked rows share the first tuple's indentation. SQL tokens and comments are preserved.

GSP Java version 4.2.14(2026-10-04)

  • 2026-10-04 A table reference that names its database finds the table that a CREATE TABLE or the metadata declares without one (Mantis 4751). After Sybase CREATE TABLE DW_RE0.BusinessOwners (CustomerName varchar(300), ...), create view dw_dina.DW_RE0.aa_BusinessOwners as select * from dw_dina.DW_RE0.BusinessOwners gave only dw_dina.DW_RE0.aa_BusinessOwners.* <- dw_dina.DW_RE0.BusinessOwners.*; it now gives one edge per declared column, such as dw_dina.DW_RE0.aa_BusinessOwners.customername <- dw_dina.DW_RE0.BusinessOwners.customername, and TTable.getResolvedTable() and TTable.getExpandedStarColumns() return the declared table and its columns. A column the declaration does not have is no longer guessed to belong to the reference: with CREATE TABLE DW_RE0.T1 (a int), c in SELECT c FROM dw_dina.DW_RE0.T1, DW_RE0.T2 is an orphan column instead of dw_dina.DW_RE0.T1.c, as it already was when the DDL named dw_dina.DW_RE0.T1. A declaration without a database is in the session's current database, so the match is made only while that database is unknown: after USE other_db the table is in other_db, and after TSQLEnv.setDefaultCatalogName("prod") it is in prod, so a reference to dw_dina still finds nothing. Only the database may be missing: CREATE TABLE T1 is not dw_dina.DW_RE0.T1. A table or synonym that the named database holds still wins, and a reference through a linked server (serverB.dw_dina.DW_RE0.T1) or a database link is never matched. The match is a best guess, not a proof: two references naming different databases, dw_dina.DW_RE0.T1 and other_db.DW_RE0.T1, both read the one declaration, and each keeps its own name in the lineage (in BigQuery too, where db1.s1.t1 and db2.s1.t1 stay two tables beside the declared s1.t1). This holds in every dialect with a database above the schema.
  • 2026-10-04 [Formatter] Formatting on several threads at once no longer throws a NullPointerException. GFmtOptFactory.newInstance(), which SQLUtil.formatSql also uses, took System.nanoTime() as the option's sessionId, so two threads could get the same id, and the first FormatterFactory.pp call to finish removed the formatters the other call was still using. Each option now gets an id of its own.
  • 2026-10-04 The files of an external table's LOCATION now feed each STRUCT field the table declares, including the elements and fields of an ARRAY of STRUCTs, not only its columns (Mantis 4858), in Athena, Spark SQL, Databricks, Hive, Presto and Trino. After CREATE EXTERNAL TABLE events (payload STRUCT<address:STRUCT<city:STRING>>) STORED AS PARQUET LOCATION 's3://b/e/', SELECT payload.address.city AS c FROM events gave RS-1.c <- events.payload.address.city, a field node with no upstream, so c could not be traced to the files; the field now has events.payload.address.city <- 's3://b/e/'.payload.address.city, next to the unchanged events.payload <- 's3://b/e/'.payload. Each field is fed from its own field of the files, so a sibling such as payload.address.zip is never a source of c.
  • 2026-10-04 An INSERT fed by SELECT * from a permanent table made by SELECT ... INTO pairs the columns by position in SQL Server, Azure SQL, SAP ASE, SAP IQ, SQL Anywhere, Redshift and Greenplum (Mantis 4856). After select COL_1, COL_2 into TMP2 from TABLE_1, insert into TABLE_2 (COL_2_RENAMED, COL_1) select * from TMP2 gave only the wrong TABLE_2.COL_1 <- TMP2.COL_1; it now gives TABLE_2.COL_2_RENAMED <- TMP2.COL_1 and TABLE_2.COL_1 <- TMP2.COL_2, as does an INSERT without the column list into a TABLE_2 declared as (COL_2_RENAMED int, COL_1 int). An ALTER TABLE of the table, or a second SELECT ... INTO making it, leaves its * unexpanded. When the * reads a table whose column order is not known (no DDL, its columns known only from other statements), an INSERT column-list target now reads that table's * instead of the column named like it: insert into TABLE_2 (COL_2_RENAMED, COL_1) select * from T9; select COL_1, COL_2 from T9; gives TABLE_2.COL_1 <- T9.* (was <- T9.COL_1).
  • 2026-10-03 An INSERT column list fed by SELECT * from a temporary table made by SELECT ... INTO pairs the columns by position in SAP ASE, SAP IQ, SQL Anywhere and Redshift, and for SQL Server's dbo.#t and [#t] (Mantis 4753). After select COL_1, COL_2 into #TMP from TABLE_1, insert into TABLE_2 (COL_1, COL_2_RENAMED) select * from #TMP linked only the same-named TABLE_2.COL_1 <- #TMP.COL_1, and the reordered list (COL_2_RENAMED, COL_1) gave the wrong TABLE_2.COL_1 <- #TMP.COL_1. They now give TABLE_2.COL_2_RENAMED <- #TMP.COL_2, and TABLE_2.COL_2_RENAMED <- #TMP.COL_1 with TABLE_2.COL_1 <- #TMP.COL_2, as SQL Server did for an unqualified #TMP; Redshift's select ... into temp table t too. A read of a column the SELECT ... INTO did not create, b in select a into #T2 from S1; insert into X (xa, xb) select a, b from #T2, no longer gets an edge. A temporary table that two SELECT ... INTO statements make, as in the two branches of an IF, takes its columns from the branch that ran, so its * is not paired by position.
  • 2026-10-03 HAVING clause lineage. Each column and aggregate a HAVING clause reads now influences the result's rows, as WHERE does: SELECT o.customer_id FROM sales.dbo.Orders o GROUP BY o.customer_id HAVING SUM(o.amount) > 100000 gives fdr SUM -> RelationRows with clause="having" (JoinRelationship.JoinClauseType.having), and fdd Orders.amount -> SUM. An aggregate in HAVING reads its GROUP BY keys and its rows as the same aggregate in the select list does, so HAVING count(1) > 1 is traced back to its table (Mantis 4854). In MySQL (and OceanBase in a MySQL tenant), BigQuery, Hive, Spark SQL, Databricks, Impala and Doris a SELECT-list alias named in HAVING (SELECT o.amount AS amt ... HAVING amt > 1) is now read as the alias, in both resolvers and in lineage, instead of an invented column orders.amt. A column of that name that the FROM clause provably has still wins, except in MySQL, where it wins only when GROUP BY names it, as MySQL documents.
  • 2026-10-03 Hive, Spark SQL and Databricks model a STRUCT column as a tree, as BigQuery and the Trino family do (Mantis 4821, 4837). CREATE TABLE events (payload STRUCT<name:STRING>) kept only a column name, so the declared column disappeared and every read of payload.name was dropped; without DDL a field read invented a column named after the field. TSQLResolver2 now reads payload.name and customer.address.city as the column with a field path when the first part names no relation in scope (a LATERAL VIEW alias is a relation), and lineage gives v.city <- orders_raw.customer.address.city with and without DDL. A Hive listType_t or Databricks ARRAY<STRUCT<...>> column gets its element nodes, items[*].name.
  • 2026-10-03 A STRUCT field read through a CTE or derived table reads the field, not the whole column (Mantis 4836): WITH c AS (SELECT customer FROM orders_raw) SELECT customer.address.city AS city FROM c gives v.city <- orders_raw.customer.address.city (was orders_raw.customer). When the CTE's column passes the table column on unchanged, each result column on the way gets a field node customer.address.city; a set operation, a function, a STRUCT the query builds or a wildcard keep reading the whole column.
  • 2026-10-02 A UNION derived table's column in a JOIN or WHERE condition influences the rows (Mantis 4819). In LEFT JOIN (SELECT 0 AS rank, 'G' AS type UNION ALL SELECT 1, 'I') AS r ON r.type = c.cos_type the fdr on the result's rows listed only calls.cos_type; result_of_r.type is there now, as it is for the VALUES form.
  • 2026-10-03 BigQuery items[OFFSET(0)].item_name reads the field of the array element, orders_raw.items[*].item_name, not the whole array (Mantis 4833), with and without DDL, qualified or not (o.items[OFFSET(0)].item_name), and for SAFE_OFFSET, ORDINAL, SAFE_ORDINAL and a plain index; items[OFFSET(0)] with no field after it still reads the array column items. The same item without an alias no longer stops the analysis with a NullPointerException; its column is named after the field, item_name. The grammar now records the subscript and the field after it on the array column (TObjectName.getIndirection()), which TObjectName.getFieldPathRead() reads.
  • 2026-10-03 A ROW(...) constructor has lineage (Mantis 4834). Its values were never sources; now ROW(a, b) AS contact gives contact <- a, b, and a Trino, Athena or Presto CAST(ROW(payload.address.city, payload.phone) AS ROW(city VARCHAR, phone VARCHAR)) AS contact gives one column per field, v.contact.city <- events.payload.address.city and v.contact.phone <- events.payload.phone, as BigQuery STRUCT(... AS city) AS contact does. In a view these are field nodes below the view's column contact, so a later SELECT v.contact.city FROM v reads v.contact.city. A row compared in a predicate, ROW(a, b) = ROW(1, 2), now makes a and b filter sources.
  • 2026-10-03 BigQuery PIVOT(COUNT(customer.name) FOR order_id IN (1)) reads the counted field, v.1 <- orders_raw.customer.name, and customer is no longer published as a grouping column (Mantis 4829). Through a CTE the read is of the CTE's column customer.
  • 2026-10-03 A STRUCT field read through a CTE or derived table reads the column it starts at (Mantis 4830, 4831, 4818). BigQuery WITH c AS (SELECT * FROM orders_raw) SELECT customer.address.city AS city FROM c gave v.city <- orders_raw.address and invented the column address, and SELECT x.customer.address.city FROM (SELECT customer FROM orders_raw) x invented orders_raw.city; both now give v.city <- orders_raw.customer. In Athena, Presto and BigQuery SELECT payload.name AS n FROM (SELECT payload FROM events) sub now has the edge from the derived column payload to n. The read is of the whole column; field paths through result sets are Mantis 4836.
  • 2026-10-03 BigQuery and Athena CREATE VIEW v AS SELECT customer.* FROM orders_raw without DDL gives the view one column * read from orders_raw.customer (Mantis 4827), not the columns * and CUSTOMER with no edge. With DDL the declared fields expand as before.
  • 2026-10-03 An attribute read two levels deep after a table alias, o.customer.address.city, reads the column customer (Mantis 4839, 4840). Redshift SUPER reads were NOT_FOUND because the four-part name put the alias in the database slot; they now resolve to orders_raw with the field path address.city, and lineage gives v.city <- orders_raw.customer with and without DDL instead of nothing or the invented columns city and o.customer.name. Oracle object-type attributes were resolved but lost in lineage; they now give the same edge.
  • 2026-10-02 BigQuery UNNEST of a CTE or derived-table column has lineage (Mantis 4835): WITH c AS (SELECT items FROM orders_raw) SELECT i.item_name AS item FROM c, UNNEST(c.items) AS i gives v.item <- orders_raw.items. The unnest element i comes from a result set, which carries no field nodes (Mantis 4836), so a field read through the element's name reads the whole element; a derived table analyzed later in the FROM clause is analyzed first.
  • 2026-10-02 BigQuery SELECT item_name FROM orders_raw, UNNEST(items) without DDL no longer binds item_name to orders_raw (Mantis 4828). An unaliased UNNEST is a value table that exposes its element's fields under their own names, so without the element type nothing proves whether item_name is a column of the table or a field of the element: ResolutionResult.getStatus() is now AMBIGUOUS with both as candidates, and lineage lists the column as an orphan instead of inventing orders_raw.item_name. With DDL the field is read as before, orders_raw.items[*].item_name; an aliased UNNEST and the UNNEST argument itself are not affected. Presto, Trino and Athena, which expand an unaliased UNNEST of an ARRAY(ROW) into one column per field, get the same reading; with several unaliased UNNESTs every value table is a candidate, and an UNNEST argument considers only the value tables to its left.
  • 2026-10-02 Trino and Athena UNNEST output columns are linked to the unnested array, as Presto's were (Mantis 4826): CREATE VIEW v AS SELECT t.x AS n FROM events CROSS JOIN UNNEST(arr) AS t(x) gives v.n <- events.arr[*]. An alias reads the argument at its position when the counts agree, every alias reads the one argument's element when there is a single argument (Trino expands an ARRAY(ROW) into one alias per field), and the WITH ORDINALITY alias, which TUnnestClause.isWithOrdinality() now records, reads every array whole; with several arguments and a different number of aliases no edge is published, because the mapping needs the element types. For Trino and Athena the link is made through the resolver's binding of each argument, so joins, filters, other tables, several UNNESTs and an argument from a CTE or derived table keep their lineage; Presto's derived-table argument is now matched by name, not by its position in the UNNEST, and a CTE before an UNNEST is no longer modelled as a table.
  • 2026-10-02 Lineage reads the columns inside an array subscript, as in Athena, Presto and Trino SELECT arr[n] AS part FROM logs and SELECT split(principal_id, ':')[n] AS part FROM logs (Mantis 4817). The index column selects the element, so logs.n is now a source of part (and of the split function's result), as it already was for element_at(split(principal_id, ':'), n); TSQLResolver2 resolves n to logs. TObjectName.getSubscriptExpressions(), TFunctionCall.getSubscriptExpressions() and TIndices.getSubscriptExpressions() expose the expressions inside [...], and TIndices.acceptChildren visits them.
  • 2026-10-02 COUNT(*) over a table function has a row-filter source, as it has over a table (Mantis 4815): SQL Server SELECT COUNT(*) AS n FROM DB..FN(@D) adds fdr COUNT.COUNT <- DB..FN.RelationRows. With Option.setShowJoin(true) a join on a table function's column, ON t.id = f.c, records the function's output column as the join side.
  • 2026-10-02 resolver2 reads a derived table's alias column list, as in FROM (SELECT z FROM t3) d(y) (Mantis 4814, 4832, 4819). The columns were named after the SELECT list, so y was NOT_FOUND against d; in SELECT (SELECT max(y) FROM (SELECT z FROM t3) d(y)) AS m FROM o the enclosing table captured it and y.getSourceTable() was o. It is d now, with ResolutionResult.getStatus() EXACT_MATCH, in the default resolver, resolver2 and parse-time linking alike (the deprecated legacy EResolverType.RESOLVER is unchanged). The list renames the columns by position, also for a set operation (... UNION ALL ...) AS r(rank, type), and names the cells of a VALUES derived table (VALUES (0, 'G'), (1, 'I')) AS r(rank, type), whose r.rank and r.type were NOT_FOUND. Trino SELECT x.p.address.city FROM (SELECT payload FROM events) AS x(p) resolves to x with the field path p.address.city, and its lineage v.city <- events.payload is back.
  • 2026-10-02 Spark SQL parses a nested type whose closers are written together, as in CREATE TABLE orders_raw (order_id BIGINT, customer STRUCT<name: STRING, address: STRUCT<city: STRING>>), ARRAY<ARRAY<ARRAY<INT>>> and CAST(x AS STRUCT<a: ARRAY<INT>>) (Mantis 4838). The lexer read >> as one operator token that no grammar rule accepted; it now gives a run of > or < one character at a time, as the Hive and Databricks lexers do. The same token made the shift operators unparseable: SELECT a << 2, a >> 2, a >>> 2 FROM t now parses, with EExpressionType.left_shift_t and right_shift_t (>>> is right_shift_t too, its three tokens tell it apart), and the shifts bind below + and - as in Spark's operator table. Because the lexer gives one bracket at a time, a spaced a > > b is read as a shift as well, as Hive, Databricks and BigQuery already read it.
  • 2026-10-02 Athena keeps the argument of an aliased UNNEST, as in FROM events CROSS JOIN UNNEST(arr) AS t(x) (Mantis 4825). TUnnestClause.getColumns() (or getArrayExpr() for UNNEST(split(csv, ','))) was null for the aliased forms and set only for the unaliased ones, so arr was neither resolved to events nor available to lineage.
  • 2026-10-02 OceanBase in a MySQL or system tenant reads a routine's DECLAREd variable as that variable, as MySQL does (Mantis 4813). In CREATE PROCEDURE p() BEGIN DECLARE c INT; SELECT c FROM t1; END; with CREATE TABLE t1 (c INT, d INT), c in the SELECT had getDbObjectType() column and getSourceTable() t1 for dbvoceanbase; it is now variable, and d stays the column of t1. The routine body also keeps every statement MySQL keeps: DECLARE, OPEN, FETCH, CLOSE, IF, CASE, SET, RETURN and the ITERATE stub were dropped for OceanBase, SET inside a routine became a DB2 statement, and a LOOP threw a NullPointerException out of parse(). The Oracle tenant is unchanged.
  • 2026-10-02 IScope.getVisibleNamespaces() of resolver2 terminates when called on a SELECT, UPDATE, DELETE or MERGE scope or on its FROM scope (Mantis 4820). The SELECT scope asked its FROM scope for the visible namespaces and the FROM scope asked its parent, the same SELECT scope, so the call overflowed the stack; the owner now reads the FROM scope's own relations, and every namespace is listed once.
  • 2026-10-02 The lineage of SQL in several files, new DataFlowAnalyzer(File[], Option), is the lineage of the same SQL in one file. The metadata that the analyzer reads from the CREATE statements of the files kept the columns of a table without their types when the table came from a later file, and it took the files in the order their threads finished. A STRUCT column keeps its fields in its type, so with CREATE TABLE orders_raw (..., items ARRAY<STRUCT<item_name STRING, qty INT64>>) in one file and CREATE VIEW v AS SELECT item_name FROM orders_raw, UNNEST(items) in another, the edge from orders_raw.items[*].item_name to v.item_name was missing in about one run out of three. The files are now merged in their order, and a column keeps its type; a column that an earlier file gives without a type, as a CREATE TABLE ... AS SELECT does, takes the type of a later definition.
  • 2026-10-02 Athena, Spark SQL, Databricks, Presto and Trino lineage shows the files of a table, as it already did for Hive, Impala and Redshift: CREATE EXTERNAL TABLE events (city STRING) STORED AS PARQUET LOCATION 's3://b/e/' gives the path 's3://b/e/' in dataflow.getPaths() and an edge from 's3://b/e/'.city to events.city, so that a table can be traced back to its files. TCreateTableSqlStatement.getTableLocation() returns the location given by the LOCATION clause of Athena, Spark SQL and Databricks, and by the table property external_location of Presto, Trino and Athena, or location of the Iceberg and Delta Lake connectors, in WITH (...); it was null for all of them. A table without a column list, as in CREATE TABLE events USING DELTA LOCATION 's3://b/e/', gets an edge from 's3://b/e/'.* to events.*. In Hive, CREATE EXTERNAL TABLE events LIKE src LOCATION 's3://b/e/' gets the same edge; it gave a path with no column and no edge. A CREATE TABLE ... AS SELECT writes its result to the location, so it gives the location but no path, as in Hive. The Spark SQL and Databricks LOCATION option is the new ECreateTableOption.etoLocation, with its path in TCreateTableOption.getTableLocation(); in Databricks it was etoUnknown.
  • 2026-10-02 BigQuery, Athena, Presto and Trino keep the lineage of a STRUCT field read through a CTE or a subquery, as in WITH c AS (SELECT customer FROM orders_raw) SELECT customer.address.city FROM c: the read comes from the CTE column customer, so from orders_raw.customer, because a CTE has no field nodes of its own. In 4.2.13 it came from every leaf field of customer when the script declared orders_raw, and it had no lineage without DDL, through an aliased subquery (x.customer.name) or a CTE column list, or in Athena, Presto and Trino. The same read in a WHERE or ON condition, a GROUP BY or a BigQuery SELECT * REPLACE (customer.name AS customer) comes from the CTE column too; a WHERE condition added a CTE column named after the field (address), and TSQLResolver2 now resolves the columns of a REPLACE expression. The read has no edge when the DDL of the column that the CTE passes on has no such field, or when its first part also names the table it is read from (c.name FROM q AS c). A BigQuery customer.address.city with an alias on the table that declares customer, as in FROM orders_raw o, z, comes from orders_raw.customer.address.city; it came from a column z.customer.address.city, or had no lineage when the script declared z. The new page STRUCT columns in lineage, explained shows the lineage of STRUCT columns with examples.
  • 2026-10-02 BigQuery, Athena, Presto and Trino lineage models a STRUCT or ROW column as a tree: the declared column, and below it a node for every field, intermediate fields included, with [*] for the element of an array (orders_raw.customer.address.city, orders_raw.items[*].item_name). A read of the whole column comes from the declared column, and a read of a field from exactly that field, with and without DDL in the script. Before, BigQuery with DDL had only the leaf fields, so SELECT customer came from every leaf of customer and SELECT customer.address also from its sibling customer.phone, and Athena, Presto and Trino read every field from the whole column (events.payload). The tables in the output list the field nodes after their column, and a field that the DDL does not declare gets no node, as a column the table does not declare gets no column.
  • 2026-10-02 The STRUCT tree covers the forms around a field read. UNNEST(items) AS i reads the array element orders_raw.items[*], and BigQuery i.item_name the field orders_raw.items[*].item_name, also for a nested array as in UNNEST(customer.orders); WITH OFFSET AS off stays a column of the UNNEST. A Presto UNNEST(payload.items) AS t(item) reads events.payload.items[*] instead of a column events.items the table does not have. SELECT customer.* reads the fields of customer: one result column per field when the DDL declares them, otherwise one column from customer; Athena, Presto and Trino read payload.* that way too instead of as every column of events. BigQuery UPDATE ... SET customer.name = ... and MERGE ... UPDATE SET customer.name = ... write orders_raw.customer.name, where the write went to a column orders_raw.name without DDL and was dropped with DDL. INSERT INTO t SELECT STRUCT(...) ... writes the declared fields of a STRUCT column by position, and ARRAY[customer.customer_id] reads the field. When two tables in scope could own the column, as in SELECT customer.address.city FROM orders, items, the read is an orphan instead of a column of an invented table customer.address; when only one of them declares customer, the read comes from that one.
  • 2026-10-02 Presto, Trino and Athena keep the fields of a ROW(...) type and the element type of ARRAY(...) in the parse tree: TTypeName.getColumnDefList() and getTypeOfList() were empty for them, so CREATE TABLE events (payload ROW(address ROW(city VARCHAR))) declared no fields. BigQuery resolves the columns inside an INTERVAL expression, as in DATE_ADD(d, INTERVAL SAFE_CAST(n AS INT64) DAY), which TSQLResolver2 never visited. In Athena, Presto and Trino a column declared by DDL wins over a table of the same name for the first part of a dotted name, so payload.events.id FROM source, events reads the field events.id of source.payload.
  • 2026-10-02 Athena, Presto and Trino keep the lineage of a read two or more fields deep into a STRUCT or ROW column, as in SELECT payload.address.city FROM events (Mantis 4822). The read comes from the field events.payload.address.city: with CREATE EXTERNAL TABLE events (payload STRUCT<address:STRUCT<city:STRING>>) in the script it was dropped without an error, and without DDL it invented a column events.city. The same holds behind a table alias (e.payload.address.city) or a schema-qualified table (sch.events.payload.address), with more fields or a subscript (payload.address.city[1]), in WHERE, GROUP BY, DELETE and correlated subqueries, and for a ROW(...) column; Trino now also reads the one-field forms of Mantis 4811. A dotted name is read the way Trino reads it, longest qualifier first, so sch.events.id is the column of the table sch.events in scope even when an enclosing query aliases another table sch. A column that receives such a read is named after its last field (INSERT INTO t SELECT payload.address.city ... writes t.city; SELECT payload.address wrote t.payload.address and now writes t.address). When no DDL decides which of two joined tables owns payload, lineage reports an orphan column instead of inventing a table payload.address.
  • 2026-10-01 DataFlowGraphGenerator.genDataflowGraph(Dataflow, boolean) and genDlineageGraph(EDbVendor, boolean, dataflow, boolean) can keep the tables and resultsets that take part in no relationship (Mantis 4812). With true, a script that only declares a table, or SELECT 1 AS n, gives a graph with that node and no edge, and a table nothing reads joins the graph next to the connected ones; the methods without the option still drop them, as before.
  • 2026-10-01 Athena and Presto keep the lineage of a read of a STRUCT or ARRAY field, as in SELECT payload.items[1].name FROM events (Mantis 4811). With CREATE EXTERNAL TABLE events (payload STRUCT<...>) in the script the read was dropped without an error, because the table's lineage columns were the struct's field names (items, name); they are now the declared columns with a node for every field, and the read comes from the field events.payload.items[*].name, also through a table alias. Without DDL the read no longer invents a column events.items: a qualifier that names no table in scope is read as a column with a field path, as Trino does.
  • 2026-10-01 Lineage of a join with a VALUES derived table, as in LEFT JOIN (VALUES (0, 'G'), (1, 'I')) AS r(rank, type) ON r.type = c.cos_type, no longer fails with NullPointerException: ... "joinResultSet" is null in PostgreSQL, Athena and Databricks (Mantis 4802). The statement is analyzed to its end, so the join condition reports r.type and c.cos_type as row influence, the lineage SQL Server and Snowflake give for the same SQL, and no literal becomes a column of a table. A scalar subquery that is itself a VALUES cell now feeds its column, in every dialect.
  • 2026-10-01 Athena parses GROUPS window frames, inline and in a WINDOW clause, such as first_value(value) OVER (ORDER BY ts GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) (Mantis 4803). The frame keeps its unit, TWindowFrame.getLimitRowType() returns ELimitRowType.Groups, and both bounds; it is not read as ROWS. A window named groups is still a window name, as in OVER (groups).
  • 2026-10-01 Athena accepts a VALUES query as the body of a CTE, as in WITH v(id, label) AS (VALUES (1, 'a'), (2, 'b')) SELECT id, label FROM v (Mantis 4804). The CTE columns take the rows by position, and lineage reports no physical source for the literals.
  • 2026-10-01 Athena accepts a lambda whose single parameter is in parentheses, as in filter(ARRAY['a', ''], (x) -> COALESCE(x, '') <> '') (Mantis 4805). Lineage no longer reports a lambda parameter as an orphan column of pseudo_table_include_orphan_column when the query has a FROM clause, in every dialect with lambdas: SELECT transform(arr, x -> x + t.off) FROM t reads only t.arr and t.off. TSQLResolver2 marks lambda parameters with TObjectName.ttobjLambdaParameter, and matches them by the dialect's identifier rules on a name's first segment: in Snowflake the quoted "X" in "x" -> "X" stays a column of the table, and x[1] or x.name read the parameter x, where they were reported as a column x or an orphan.
  • 2026-10-01 Athena accepts a field or a subscript directly after a call, as in element_at(queues, 1).queue_name and split(principal_id, ':')[2] (Mantis 4806, 4807). The postfix is in TFunctionCall.getIndirection(), and lineage reads the call's arguments. The subscript of MAP(...)[key], which was parsed and then dropped, is kept the same way.
  • 2026-10-01 Athena accepts an aggregate FILTER (WHERE ...), as in COUNT(*) FILTER (WHERE "date"(creation_date) <> partition_date), also with DISTINCT and OVER (Mantis 4808). The predicate is in TFunctionCall.getFilterClause(), and lineage matches PostgreSQL's. filter is still an identifier everywhere else: the filter(array, lambda) function and a column or alias named filter parse as before.
  • 2026-10-01 Athena accepts MAP as a table alias, a qualifier or a column name in a query, as in SELECT map.field0 FROM events map (Mantis 4809). MAP(...) and the MAP<k, v> type keep their meaning.
  • 2026-10-01 Athena accepts DATE(expression), the alias of CAST(expression AS DATE), as in SELECT DATE(c.year || '-' || c.month || '-' || c.day) FROM dates c (Mantis 4810). It is a call shaped like "date"(expression), so lineage links year, month and day as it does for the cast; DATE('2020-01-01') stays the typed literal it was.
  • 2026-10-01 Lineage reads a table function's column in a WHERE, ON or CASE condition and in GROUP BY or HAVING through the function, as it already did in the select list (Mantis 4797). In SELECT ... FROM [dbo].[fn_StrToTable](@strId, ',') tbl_Id JOIN dim_LagerSapClassifier LSC ON (tbl_Id.id = LSC.classifierId) the join condition had no source from the function, and the derived table (SELECT DATARIFERIMENTO FROM HERMIONE_AX443_FILIALI_ESTERE_ULT(@D_RIF) WHERE DATARIFERIMENTO = @D_RIF) filtered on nothing of its own; both now read the function's output. A table function the catalog does not know is no longer taken to have none of the columns read from it (TFunctionCall.isColumnInThisTableFunction answers not sure instead of no): its columns were reported as orphans in pseudo_table_include_orphan_column, or bound to a table next to it, and now flow from the function.
  • 2026-10-01 The statements of a script see the tables its CREATE TABLE statements declare before them while they are parsed, whatever the resolver (Mantis 4797). Column linking at parse time (TCustomSqlStatement.linkColumnToTable) reads the parser's catalog, which got the script's CREATE TABLE only from the resolver run after the parse, and with EResolverType.NONE never: in CREATE TABLE t1 (a int); CREATE TABLE t2 (b int); SELECT a, b FROM t1, t2, parser.getSqlEnv().searchTable("..t1") returned null and a and b had no getSourceTable(), and in the SAP IQ query of Mantis 4752, RESOLVER and NONE read the subquery name K as a column of Collaterals instead of the enclosing alias. Each declaration is now taken when it is parsed, as resolver2 places it: in the database and schema of the USE statements before it, also inside a block, and a procedure's temporary table only inside that procedure. A CREATE TABLE ... AS SELECT has the columns of its query (a SELECT * leaves them open), a #t is the same table in every database, so SELECT a FROM #t after USE db2 and SELECT a FROM tempdb..#t read it, and a SELECT ... INTO #t inside a procedure is read by the procedure's later statements in every resolver type. The resolvers still register the declarations themselves, at their positions; with NONE the parser's catalog gets what resolver2 would put in it when the parse succeeds, so getSqlEnv() holds them in every resolver type, and a procedure's SELECT ... INTO #t no longer reaches it, where another procedure's #t was read as that table.
  • 2026-10-01 Knowing the script's tables while it is parsed exposed four rules of column linking, which now follow SQL (Mantis 4797). An unqualified name that two tables of a statement both have is ambiguous: with t1 and t2 both declaring a, SELECT a FROM t1, t2 and SELECT a FROM t1 x, t1 y no longer read a as a column of the first table, with the caller's catalog too, unless USING (a) or a NATURAL join merges the column; and a guess between several tables never takes one the catalog proves has no such column. An unqualified name in a derived table belongs to that derived table while one of its tables may have the column, as a derived table sees neither the tables next to it nor the enclosing query: in SELECT x.a FROM t, (SELECT a FROM u WHERE a > 0) x the inner a was taken for a column of t whenever the catalog knew t, and lineage reported a derived table over a table function, such as (SELECT PORTFOLIO_ID FROM TABLE(pkg.fn(...))) PF, as reading a table next to it; ClickHouse still prefers an enclosing WITH ... AS alias to a column. A subquery in an expression reads a name from its own tables while they may have it, and reads the enclosing query's column only when the name is compared with a column of the subquery's own tables, its own tables do not prove it and the enclosing query does, by one rule at parse time and in resolver2 alike: in SELECT (SELECT max(v) FROM customfieldvalues WHERE resourceid = resumeid) FROM (SELECT id AS resumeid FROM resumes) r, resumeid is r's, where resolver2 took it for a column of customfieldvalues, while in (select Jahr from m_version_table2 where Jahr234 = year(now())) both names stay m_version_table2's though a derived table around the subquery names them too. In MySQL, and OceanBase in a MySQL or system tenant, a name declared as a local variable or a parameter of the routine is that variable, also when a table of the statement has a column of that name, as MySQL documents: after DECLARE BirthDate DATE, SELECT BirthDate INTO BirthDate FROM Employees reads the variable, and lineage no longer reports Employees.BirthDate flowing into it, while VALUES(c) and DEFAULT(c) in ON DUPLICATE KEY UPDATE read the column c. Oracle reads the column there, as Oracle documents, and now does so with a script's own declarations too.
  • 2026-10-01 CountSql reads each file by its byte order mark and writes its CSV and sql_statements.json as UTF-8 (Mantis 4801). It read every file in the platform charset, so a UTF-16 file came out as one statement full of U+0000, and a UTF-8 file with a byte order mark gave no statements at all: in the test corpus, a 552-statement Oracle script counted 0. A file without a mark is still read in the platform charset, which -Dfile.encoding sets. On a windows-1252 JVM sql_statements.json no longer holds windows-1252 bytes, and the CSV no longer writes a file name it cannot encode as ?. The vendor taken from a directory name no longer depends on the default locale: under a Turkish locale the directory INFORMIX gave dbvgeneric.
  • 2026-10-01 The JSON that CountSql writes to sql_statements.json and the analyzer v2 export (AnalysisResult.exportJson()) stay valid when a value holds a control character (Mantis 4800). Their escapers passed U+0000-U+001F through raw, apart from the few each one named, so a statement, or a quoted routine or table name, holding one made the JSON invalid: run over the stored-procedure test corpus, 275 of the 2624 lines that CountSql wrote held a raw U+0000. Such characters are now written as \u00XX, and the analyzer export also escapes \b and \f.
  • 2026-10-01 JSON.toJSONString writes every value inside a JSON array by its type (Mantis 4799). A number, a boolean or a nested array inside a list or array went through the bean writer and came out as {} or as its getters, so re-serializing parsed metadata JSON, as the analysis of a SQLFlow or Grabit export, FileSplitter and LargeFileDetector do, turned [1,true,[2],2.5] into [{},{},{"empty":false,"first":2,"last":2},{"infinite":false,"naN":false}]. A top-level list of strings came out as each string's getters, a bean whose list held a null threw JSONException, and NaN or Infinity was written as is, which is not JSON; it is now written as null, as JSON.stringify does. The lineage and graph JSON that GSP builds itself hold no such values and are unchanged byte for byte.
  • 2026-10-01 The lineage graph JSON stays valid when a node label contains a double quote, a backslash or a control character (Mantis 4798). DataFlowGraphGenerator.genDataflowGraph, genDlineageGraph and genERGraph key their tooltip map by the node label, shortened when it is longer than 25 characters, and JSON.toJSONString escaped values but wrote map keys verbatim, so SELECT o.amount AS "customer_lifetime_value_usd" FROM orders o produced "tooltip":{""customer_...e_value_usd"":...}, which no JSON reader accepts (the output first reported in Mantis 4385). A backslash, as in the label 'C:\temp\new_folder' || o.name, kept the JSON valid but made the key read back as a different string, so a viewer that looks a tooltip up by its label found nothing. JSON.toJSONString, with or without a Writer, now escapes every map key like a value, writes a control character as \u00XX instead of raw, no longer cuts a string short at a U+FFFF character, and no longer leaves a trailing comma after a null at the end of a list or array.

GSP Java version 4.2.13(2026-10-01)

  • 2026-09-30 [Sybase, Sybase IQ, SQL Anywhere] A SELECT-list alias used again in its SELECT block is the alias (Mantis 4752). In select COL_1 as ALIAS_1, ALIAS_1 as ALIAS_2 into TABLE_2 from TABLE_1, ALIAS_1 was read as a column of TABLE_1, so TABLE_2.ALIAS_2 had no lineage, and with no catalog lineage reported a column TABLE_1.ALIAS_1 that does not exist. As SAP IQ and SQL Anywhere document, an unqualified name that matches an alias defined earlier in the same SELECT block now means that alias, in the select list, WHERE, GROUP BY and HAVING, for the older resolver and for resolver2: TObjectName.getSourceColumn() is the alias's TResultColumn and getSourceTable() is null. Lineage reports TABLE_2.ALIAS_2 <- TABLE_1.COL_1. The alias wins over a column of the same name, as SQL Anywhere documents, and a table-qualified name still reaches the column. This applies to dbvsybase, dbvsybaseiq and dbvsqlanywhere; Sybase ASE (dbvsybasease) is unchanged. A name in a subquery stands for an alias of the SELECT block around it too, as SQL Anywhere allows: in SELECT a AS x, (SELECT x) AS y FROM outer_t, y reads a, no longer the column outer_t.x. That holds when the subquery provably has no such column: it reads no table, or the catalog knows all the columns of each table it reads, as bound in that statement's own database, and none has that name. A table with no metadata, or one whose columns are not all known (as after SELECT * INTO #t), may have it, so there the name stays a column of that table. Lineage follows the alias wherever the resolvers bound one, also over a column of the same name: in SELECT DepartmentID AS DepartmentName FROM Departments WHERE DepartmentName = 'Marketing' the filter reads DepartmentID, not Departments.DepartmentName.
  • 2026-09-30 [Sybase, Sybase IQ, SQL Anywhere] An ORDER BY in the argument list of LIST parses (Mantis 4721). LIST([ALL | DISTINCT] expr [, 'delimiter'] ORDER BY expr [ASC | DESC], ...), as SAP IQ and SQL Anywhere document it, was a syntax error at ORDER, so the statement, or the whole procedure, got no lineage. It now parses in dbvsybase, dbvsybaseiq and dbvsqlanywhere, and the clause is the TFunctionCall.getSortClause() of the call; Sybase ASE, which has no LIST, still rejects it. Wherever the parser keeps such an ORDER BY as the TFunctionCall.getSortClause() of the call (PostgreSQL string_agg and array_agg, Oracle and Teradata XMLAGG, among others), its keys now resolve to their tables like the arguments, in resolver2 and in the older resolver, and lineage reports each key as an indirect (fdr) source of the function, as it already did for Oracle XMLAGG: in PostgreSQL string_agg(s.a, ',' ORDER BY u.b), TObjectName.getSourceTable() of u.b returned null and lineage had no edge from usr.b. An XMLAGG key named like the alias of the call, as c in XMLAGG(... ORDER BY c) AS c, is now the column instead of an unlinked alias. A key of BigQuery ARRAY_AGG stays a direct source, now of the table it names: in ARRAY_AGG(s.a ORDER BY u.b) over ds.src s JOIN ds.usr u, lineage gave u.b to the first table of the FROM clause and reported ds.src.b; it now reports ds.usr.b. The ORDER BY of BigQuery STRING_AGG is still not kept in the syntax tree.
  • 2026-09-30 [Oracle, Snowflake] The WHEN conditions of a conditional multi-table insert (INSERT ALL, INSERT FIRST) are row filters (fdr) of exactly the INTO clauses they route rows to. An INTO clause without a column list or VALUES, such as WHEN ottl > 290000 THEN INTO special_orders, got no filter at all; a qualified condition such as WHEN X.K > 0, or a name inside a function, reached no column; and every condition was applied to every target, so in INSERT ALL WHEN K > 0 THEN INTO S.Y (C1) VALUES (V) WHEN W > 0 THEN INTO S.Z (D1) VALUES (V) SELECT ... W was reported as a filter of S.Y.C1. An INSERT ALL clause is now filtered by its own condition, an INSERT FIRST clause by its own and every earlier one, and an ELSE clause by all of them, and each condition name is bound to the select item it reads (TObjectName.getSourceColumn()), including the names in the conditions of a CASE inside a WHEN. A name read through a wildcard (SELECT X.* FROM S.X X) reaches its column when the table is declared or the wildcard reads a CTE; over an undeclared table it gets no filter, because inside a routine it may be a parameter rather than a column.
  • 2026-09-29 [Oracle, Snowflake] A multi-table insert (INSERT ALL, INSERT FIRST) has lineage for every INTO clause, by that clause's own column list and VALUES. The INTO clauses were mapped by the subquery's column order, so INSERT ALL INTO S.Y (C2, C1) VALUES (V, W) SELECT X.V, X.W FROM S.X X sent X.V into C1 when the script declared S.Y, and had no lineage at all when it did not. A second INTO clause into the same table replaced the first, the ELSE branch had no lineage, a VALUES expression such as V + W or a select-list alias was not followed, and a VALUES name of INSERT FIRST was bound to the insert target, so TTable.getLinkedColumns() of the target listed the value names as its columns. Each VALUES item now reads the select item its names are bound to (TObjectName.getSourceColumn()), every branch of a UNION subquery reaches the target, and a name that reads no select item, such as a PL/SQL variable, is not credited to a column of the subquery's tables.
  • 2026-09-29 A DML target column whose name starts with its table's alias no longer reports the alias as a schema (Mantis 4747). In Oracle MERGE INTO S.T TGT USING (...) SRC ON (...) WHEN MATCHED THEN UPDATE SET TGT.OBJCOL.ATTR = SRC.V, S.T.W = SRC.V, the target TGT.OBJCOL.ATTR, an attribute of the object column OBJCOL, reported TObjectName.getSchemaString() TGT, getTableString() OBJCOL and getColumnNameOnly() ATTR, the same shape as the schema-qualified S.T.W, while getSourceTable() was S.T; lineage then wrote a column S.T.ATTR that the table does not have. The same name in a WHERE clause or a SELECT list was already read as the alias TGT, the column OBJCOL and the attribute ATTR (getPropertyToken()). A target is now read the same way, under every EResolverType, in the SET of UPDATE and MERGE and in the column lists of INSERT, MERGE ... INSERT and Oracle INSERT ALL, for every vendor: BigQuery UPDATE ds.t tgt SET tgt.info.name = 'x' now names the field name of the STRUCT column info. Lineage writes S.T.OBJCOL.ATTR, or the declared column S.T.OBJCOL when the script declares the table, the column a read of the attribute lands in; an UPDATE or MERGE into such a declared table no longer loses the edge, and an INSERT column list no longer writes the attribute into the declared column at its list position. Two attributes of one object column set by one statement keep their own values. A schema-qualified target such as S.T.W, MySQL test.t1.c or SQL Server dbo.t.c is unchanged.
  • 2026-09-29 [Sybase, Sybase IQ, SQL Anywhere] The Watcom CASE statement of a procedure body parses (Mantis 4717). CASE [expr] WHEN ... THEN statements ... [ELSE statements] END [CASE] inside CREATE PROCEDURE ... BEGIN ... END was a syntax error from its first WHEN, so the procedure lost its lineage, while the same logic written with IF ... THEN ... END IF worked. It now parses to a TCaseStmt, whose getCaseExpr() holds the WHEN branches and the ELSE statements, with END CASE and with a bare END, for dbvsybase, dbvsybaseiq and dbvsqlanywhere, and lineage reports the statements of every branch. The statement splitter also no longer takes the CASE of END CASE for the start of another CASE, which merged every statement after such a procedure into it. Sybase ASE has no CASE statement and still rejects it.
  • 2026-09-29 [SQL Server, Sybase] An EXEC of a procedure that a later unit defines binds its arguments (Mantis 4730). Each query of a SQLFlow ingester export, and each file of a multi-file analysis, is analyzed in turn, so when an export listed OD_CustomTransferOfficers before CopyOrganizationProd, EXEC PARAMETER_CONTEXT_TEST.CopyOrganizationProd @SrcOrgID, @AnalystID bound no argument: lineage had neither @OrgID <- @SrcOrgID nor @SenderID <- @AnalystID, which it reported when the export listed the two procedures the other way round. Such a call's arguments, OUTPUT ones included, are now bound in either order, and the procedure the call names is described by its definition: in dataflow.getProcedures() and the JSON dbobjs, CopyOrganizationProd has the name and position of its own CREATE PROCEDURE, where it had those of the EXEC. The column lineage of an INSERT ... EXEC, and calls of functions, into a routine that a later unit defines still depend on the order.
  • 2026-09-29 [Redshift, Sybase] The date part of a date function is no longer taken for a column (Mantis 4760). After DELETE FROM hist WHERE d < DATEADD(YEAR, -1, CURRENT_DATE), INSERT INTO tgt SELECT * FROM hist was reported with a column YEAR of hist feeding tgt.YEAR, and TObjectName.getSourceTable() of YEAR returned hist. The first argument of Redshift's DATEADD, DATEDIFF, DATE_PART and PGDATE_PART, of DATEADD, DATEDIFF, DATENAME and DATEPART in SAP ASE, SAP IQ and SQL Anywhere, and of SAP IQ's DATEFLOOR, DATECEILING and DATEROUND is now a date part for the parser, both resolvers and lineage, as it already was for SQL Server, Snowflake and Vertica. A column in another argument, such as year in DATEADD(day, year, d), is still a column, and so is the first argument of a function named DATENAME or DATEPART in Redshift, which has no such built-in: the parser no longer marks it as a date part.
  • 2026-09-29 A procedure's temporary table is its own: lineage no longer merges the temporary tables that unrelated procedures create under one name (Mantis 4715). Next to a procedure that runs create table #T(COL_2 varchar(10)), the table in create procedure P1() begin select COL_1 into #T from S.TABLE_1; insert into S.TABLE_2(COL_1) select T.COL_1 from #T as T end took the other declaration's columns, so #T.COL_1 -> S.TABLE_2.COL_1 was lost and S.TABLE_2 had no source; after CREATE TEMP TABLE, CREATE TEMPORARY TABLE or CREATE VOLATILE TABLE in another procedure, an INSERT without a column list was mapped onto that procedure's columns and reported a column feeding a table it never reaches. A temporary table is now shared where the SQL proves it: a procedure sees the tables of the procedures that call it, and a session table (PostgreSQL, Greenplum, Redshift, MySQL, Snowflake, BigQuery, Teradata VOLATILE, SAP HANA local, SAP IQ and SQL Anywhere # tables, Oracle PRIVATE TEMPORARY) created by a procedure is also seen by its caller after the call, while a SQL Server or SAP ASE # table ends with the procedure that creates it. A reference before the declaration, or in a procedure that neither declares the table nor is called with it, no longer reads it, and a temporary table named like a permanent table hides it only inside its procedure. Global temporary tables (Oracle, Db2, Teradata, SAP HANA and Vertica GLOBAL TEMPORARY, SQL Server ##) stay one table for every procedure; PostgreSQL's and Snowflake's GLOBAL TEMPORARY are session tables, as those databases define them. Scripts without procedures are analyzed as before.
  • 2026-09-29 [SQL Server] A procedure, function or trigger whose body holds a statement GSP cannot parse can keep the rest of its body: TGSqlParser.setRecoverRoutineBodyErrors(true), or Option.setRecoverRoutineBodyErrors(true) for dlineage, where it applies to the analyzed SQL and not to SQL parsed on the way (dynamic SQL); off by default. Without it such a routine fails as a whole and keeps no body statement, so one statement GSP cannot parse loses the lineage of every other statement in the routine. With it the statement holding each syntax error is skipped -- the innermost statement, in the innermost BEGIN ... END block around the error, that the raw statement splitter finds -- and the routine is parsed again over the same tokens, so every position stays the original one and temporary tables and variables keep their context across the skipped statement. The skipped statements' errors are reported by getRecoveredSyntaxErrors() (dlineage adds them as syntax errors noted "statement at lines a-b skipped, rest of the routine analyzed") and do not count in getErrorCount(). A statement that holds a BEGIN ... END block (an IF or WHILE whose condition is broken), or that runs on past a semicolon into the next, is not skipped: it would take statements that parse with it. A routine it cannot recover -- an error in the header, one no statement holds, blocks that do not balance, such a statement -- fails exactly as without the option: every parse of the routine starts from its tokens' state before the first, since a parse changes the tokens it reads.
  • 2026-09-29 [SQL Server] ALTER TABLE ... SWITCH has lineage. The parser read ALTER TABLE source SWITCH [PARTITION p] TO target [PARTITION q], but dlineage built no relation for it, so a load that switches a staging table into a partition of a fact table, or a partition out to an archive table, had no lineage at all. The rows move one way (effectType="switch_partition", a new EffectType constant). With every column of both tables known (a CREATE TABLE, or a SELECT ... INTO that names them all, in the script, or metadata) each column flows into its namesake under the identifier rules, a real column named * ([*]) included, and a column only one of the tables has gets no relation; with one table's columns known the other takes them, as SWITCH requires the same columns on both; with neither, one relation joins the source's * to the target's *. EXCHANGE PARTITION swaps, so it keeps a relation in each direction. The table-level relation's partition is the source partition when one is written (SWITCH PARTITION 1 TO archive), which is what exchange_partition records too: the partition of the altered table. The SLPC adapter lists the new effect with the other storage operations it does not map yet.
  • 2026-09-29 A CTE exposes only the columns its SELECT list projects, and the body of a CTE or of a derived table no longer sees the FROM clause that contains it. A CTE without a star claimed any other name when its body read a table, so in WITH s AS (SELECT id FROM t1) SELECT t2.id FROM t2 WHERE EXISTS (SELECT 1 FROM s WHERE s.id = t2.id AND region = 'EU') the correlated region bound to s, and lineage published a column s does not have instead of t2.region; a column of another joined table without metadata became ambiguous and lost its lineage. A name a body's own FROM clause lacks bound to a sibling table or to the CTE or derived table itself, which in Snowflake replaced a select-list alias referenced in WHERE. LATERAL and APPLY bodies, and every Teradata or DuckDB derived table, still see the tables to their left, and a wildcard over a CTE still binds to it.
  • 2026-09-28 A MERGE's row-level lineage reaches its target whatever the USING source is. With a table, #temp, VALUES or CTE source, or a subquery without a filter of its own, the ON and WHEN conditions stopped at the source's result set (RESULT_OF_<alias>), and a source table filled under a filter or a join (SELECT ... INTO #t ... WHERE) passed none of that on, so those columns reached no table. The rows the conditions pick, and such a source table's rows, now reach the target's RelationRows (effectType="merge") and the rows of the MERGE's UPDATE and INSERT actions, as a filtered subquery source's rows already did. Oracle's UPDATE ... WHERE, DELETE WHERE and INSERT ... WHERE in a MERGE are analyzed as conditions too.
  • 2026-09-28 [ClickHouse] A column, table or database named format in an INSERT is no longer taken for its FORMAT clause. The parser took the first format word for the clause and the text after it for data rows, so INSERT INTO t (format, b) VALUES (1, 2), INSERT INTO t (a, format) VALUES (1, 'csv'), INSERT INTO format (a) SELECT x FROM s and INSERT INTO db.format FORMAT CSV ... failed to parse. The clause is now the FORMAT after the table, outside parentheses, that is followed by a format name, so a setting named format (SETTINGS format = 1) is passed over too.
  • 2026-09-28 [Formatter] Each FormatterFactory.pp call prints in the output format its own option asks for. pp kept the output config of the first GFmtOpt.outputFmt = GOutputFmt.ofhtml call in a static field and printed every later call in the JVM with it, so after one HTML call pp and SqlFormatter.format returned HTML for default options, and removeComment parsed its own HTML as SQL and returned a second layer of HTML (17,715 characters for a 36-character query). A config set with FormatterFactory.setOutputConfig still applies to every call until it is set back to null, and the comment-removal pass now always reads SQL back, also under such a config. Threads can share such a config: the calls that first printed with it at the same time could throw a NullPointerException, because HtmlOutputConfig marked itself ready before it had filled its renders.
  • 2026-09-28 [ClickHouse, Formatter] The rows after INSERT ... FORMAT <name> keep their text. They were read as SQL tokens, so FormatterFactory.pp and pp2 printed INSERT INTO t FORMAT CSV\n1,a\n2,b; as the single row 1,a 2,b, turned the tabs of FORMAT TSV rows into spaces, merged runs of spaces and re-cased values such as select and null, and pp2 reported OK. The rows are now one token holding the text as written, from where ClickHouse starts reading them: after the format name, the spaces and tabs after it, and one line break. pp2 compares that token byte for byte, so output that changes a row is no longer reported OK. Rows a SQL reader would take for something else are data too: a first row reading select (the statement failed to parse), and rows holding only a comment or a blank line (removed by removeComment and the empty-line options). The HTML output escapes the rows and shows their line breaks.
  • 2026-09-28 [Informix] An EXECUTE or EXEC of a prepared statement starts at its keyword. The splitter began the statement after EXECUTE without adding it, so getStartToken() was the space after the keyword, toString() returned statement_1 USING :order_date;, and the formatter printed the keyword outside the statement (EXECUTE s1;EXECUTE on one line). Parse results and corpus coverage are unchanged
  • 2026-09-28 [ClickHouse] Any -- starts a line comment, as in ClickHouse, where the space after it may be omitted. GSP kept MySQL's rule, where -- followed by anything but a space is two minus signs, so SELECT a --original value FROM s was read as a - -original AS value: lineage reported a source column original and a target column value instead of a, and --comment lines stopped a script from parsing. SELECT 1--1 is now 1 followed by a comment, as ClickHouse reads it, and an operator stops before a -- (a !=--note is != and a comment). In the rows of INSERT ... FORMAT, which are data, a ; after -- or # still ends the rows: 1,--none--; or 1,-- x; followed by another statement no longer takes that statement into the rows
  • 2026-09-28 [ClickHouse] stmt1 PARALLEL WITH stmt2 keeps its words. The parser rewrote their text to ; to split the two statements, so FormatterFactory.pp and pp2 printed DROP TABLE a PARALLEL WITH DROP TABLE b; as DROP TABLE a ; ; and reported OK: the combinator was silently lost, and pp2's content check could not see it. The words now keep their text, stay keywords (so caseKeywords applies to them) and belong to neither statement, so the first statement's toString() is DROP TABLE a (it was DROP TABLE a ;) and each statement's text still parses on its own; the parse and the lineage of both statements are unchanged. FormatterFactory.pp gives the keyword case to any keyword between two statements, which no statement's case step reached before: Informix execute in front of an EXECUTE statement now takes it too
  • 2026-09-28 [Formatter, SQL Server, Sybase] pp2 keeps every GO batch separator. A script that took pp2's per-statement path, because a statement did not parse or Pp2FormatOptions.wholeDocumentFastPath was off, came back without its GO lines and with status OK: each statement was formatted without its GO, and nothing put it back. After CREATE PROCEDURE p AS SELECT 1 the lost GO put the next batch in the procedure's body. GO 5 keeps its count on its line, and a direct Pp2Engine.format call for Azure SQL splits batches at GO too. If the output of the per-statement path changes the input's tokens, pp2 now returns the input unchanged with status FAILED instead
  • 2026-09-28 [Formatter] The delimited-name case (caseQuotedIdentifier) no longer changes a double-quoted text that the script or the dialect makes a string, in FormatterFactory.pp or pp2. After SET QUOTED_IDENTIFIER OFF, SET ANSI_NULLS, QUOTED_IDENTIFIER OFF or SET ANSI_DEFAULTS OFF (SQL Server, Azure SQL, SQL Anywhere), after SQL Anywhere's SET TEMPORARY OPTION quoted_identifier = 'Off' and Firebird's SET SQL DIALECT 1, and anywhere in Access, MDX, MySQL and the other dialects whose double quotes can delimit strings, SELECT "abc" keeps "abc", where 4.2.12 upper-cased it and pp2 reported the whole document as formatted. A later SET QUOTED_IDENTIFIER ON is not followed, so a double-quoted name after it keeps its case. Without such a statement SQL Server, Azure SQL, SQL Anywhere and Firebird double quotes are names, as GSP's parser reads them: a script for a connection that has the option off must turn it off itself
  • 2026-09-28 [Formatter] The name cases of 4.2.12 (caseTableName, caseColumnName, caseTableAlias, caseColumnAlias) no longer change a name in backticks where the dialect delimits names with them, as ClickHouse, Databricks and Spark do: SELECT `Id` FROM `Orders` keeps both names
  • 2026-09-28 [Formatter] Under those name cases a name written alike comes out spelled alike. Where a column alias and its use (SELECT a AS Foo FROM t ORDER BY Foo), or a column and a declaration name resolution does not report (WITH c(Foo) AS (SELECT 1) SELECT c.Foo FROM c, ClickHouse ALTER TABLE t MODIFY COLUMN Foo Int32), would come out spelled two ways, the four settings are off for the whole input and Pp2FormatResult.isNameCaseApplied() is false; 4.2.12 wrote AS FOO ... ORDER BY foo
  • 2026-09-27 [Snowflake] A statement in the block of CREATE TASK ... AS BEGIN ... END has lineage. Looking up the procedure such a statement belongs to went back and forth between the task and the unnamed block until the stack overflowed, so the analysis reported only an error; the task's statements now belong to no procedure
  • 2026-09-27 With SELECT * over a join, a column that the join's USING list names is published once, matched by the vendor's identifier rules. A quoted name such as "a(b" made the statement lose its lineage, USING (a, b) published A1 and B1, USING ("A") did not merge, and a USING in a comment or in a subquery, or one that does not join the table, merged the column anyway
  • 2026-09-27 [Formatter] Formatting no longer slows down on long scripts. With logging off, the formatter still built a log message for every token edit, each walking a stack trace, and it stripped the blanks before a line break with a regex that went quadratic on a long run of blanks: a customer script that took 1.7 s now formats in 25 ms, with the same output
  • 2026-09-27 [Formatter] Removing the tokens before a join's table (SourceTokenOperator.removeAllBeforeTokenVT) no longer loops forever when one of them was already deleted or token operations are off
  • 2026-09-27 [OceanBase] Splitting a script no longer slows down on an unclosed comment or quoted text. The regexes that hid comments and strings before looking for system-tenant DDL retried from every position, so a 130 KB script took 6.9 s; it now takes 3.5 ms
  • 2026-09-27 Lineage's procedure exclusion patterns (*, ?) and the folding of LTRIM and RTRIM in dynamic SQL match in one pass, where regexes could retry every split of a long name or text; the results are unchanged
  • 2026-09-27 The parallel analyzer's merges join a call of a routine, analyzed by one worker, to the routine another worker defined by the vendor's identifier rules, taken from the names as written. In PostgreSQL SELECT * FROM "MyFn"() now reaches the lineage of the routine "MyFn", which it missed, and myfn() or "MyFn"() no longer reach the lineage of a different routine ("MyFn", myfn), also with normalized output (Option.setNormalizeOutput(true)), which renames both before the merge
  • 2026-09-27 [Oracle] In a PIVOT query a bare select-list name no longer resolves to a quoted pivot column of another name. In SELECT Store FROM orders PIVOT (SUM(order_total) FOR order_mode IN ('direct' AS "Store")), Store is Oracle's STORE, a column of orders, where lineage reported the pivot column "Store"
  • 2026-09-27 [BigQuery] A column of UNNEST((SELECT ...)) is published under its name as written (MixedCol), like the columns around it, where its folded name (MIXEDCOL) was
  • 2026-09-26 DlineageUtil.getIdentifierNormalColumnName, normalizeColumnName, getIdentifierNormalTableName, getIdentifierNormalFunctionName, getIdentifierNormalName and getColumnName are deprecated. Lineage no longer calls them; they keep forwarding for callers outside the library, and each deprecation names its replacement for identity and for display
  • 2026-09-26 A relationship endpoint carries the name of the column its ids point to. When lineage makes one column of several references to it, such as a column reached both through SELECT * and by name, the endpoints that named it by another spelling now carry the column's published name, in the full and the simple output, the JSON model, the CSV exports and after the parallel analyzer's merges. In CREATE TABLE tgt AS (SELECT p.*, a AS b FROM src p) both edges into tgt.b are labelled b, where one said B. In a PostgreSQL function that reads a RECORD in IF EXISTS (...) before INSERT INTO rolepermissions (role_psk, permission_psk) VALUES (query_record.r_psk, query_record.p_psk), permission_psk is fed by query_record.P_PSK, where the label said R_PSK. A label can therefore change case or delimiters with the name its column is published under (EMPLOYEES_DUMP.employee_id is now EMPLOYEES_DUMP.EMPLOYEE_ID). A relationship that then repeats another in every attribute is reported once. The public merges of published output, DataflowUtility.mergeDataflows and DataflowUtility.iterativeMergeDataflowsFromFiles, keep apart two relationships one merged dataflow reported apart: the same edge reached from two places in the SQL keeps both coordinates, where a merge kept only the first when their labels agreed. The parallel analyzer with ignoreCoordinate no longer drops an edge from one of two stars of one select list: in select s1.*, s2.* into tgt from s1 join s2 on s1.id = s2.id, tgt.* is fed from both stars, where its merge kept only the edge from s1.*
  • 2026-09-26 Columns that lineage reaches through SELECT * are told apart by the vendor's identifier rules instead of by their folded names. With metadata that lists a PostgreSQL or Oracle table t with the columns "Amount" and amount, CREATE VIEW v AS SELECT * FROM t reports two columns of t and of v, "Amount" and AMOUNT, each fed by its own column, where one column (AMOUNT in PostgreSQL, Amount in Oracle) fed by both was reported. [Snowflake] A string used as a column alias, as in CREATE TABLE t2 AS (SELECT p.*, a COMMENT 'Remark' FROM t p), is no longer also reported as a column 'REMARK' of the select list and of t2. The parallel analyzer's spilled merge no longer reports a column twice when one worker reached it through a star and another named it (SQL Server CAPARTENZA beside CAPartenza); it publishes the columns its in-memory merge publishes. The in-memory merge no longer merges a column that one worker only inferred without an identity, such as a name taken from a routine parameter in INSERT INTO tbl VALUES (a), into a same-named column another worker published with one; the spilled merge already kept them apart. The system property gsp.dlineage.starLinks=legacy restores the grouping by folded name
  • 2026-09-26 [Oracle] A record declared as a row of the table it is inserted into (m g%ROWTYPE, then INSERT INTO g VALUES m) fills each column of g from the field of the column's name, also for a field written m.lid by SELECT ... INTO m.lid. After UPDATE g SET v = NULL WHERE lid = m.lid; SELECT s.NEXTVAL INTO m.lid FROM dual; INSERT INTO g VALUES m; the insert reaches the column g.lid that the UPDATE reads, where a second column lid of g was reported; m."Lid" fills g."Lid" and m.Lid fills g.LID the same way
  • 2026-09-26 The parallel lineage analyzer (ParallelDataFlowAnalyzer) keeps column identity where it lost it. Each worker's copy of the catalog keeps every object's name as it was created: PostgreSQL "Amount" no longer becomes a second amount, a Netezza view created as S."$v_q" is found by the worker that reads it instead of being reported as a new table "$v_q", and functions, synonyms and Oracle packages keep their return columns, priority and members. Merging the workers' results keeps one column per identity: a result set keeps its own columns (a.id and b.id are no longer merged into one), a column named * is not merged into the star, a merged column keeps every attribute a worker stated for it, and SQL Server Name and NAME are one column also when results are spilled to disk
  • 2026-09-25 Two columns of one result set are qualified by their tables (qualifiedTable) when they are one name under the vendor's rules, not when their folded texts match: HANA sp.Position, sp.POSITION are still qualified, and so are a.NAME RSK, b.NAME RSK (one alias twice, shown as RSK and RSK(1)) and BigQuery struct leaves a.left.Name, b.right.name; a literal 'Name' beside the column Name no longer is. A table function's RETURN TABLE columns are matched to its query's columns by identity
  • 2026-09-25 [SQL Server, BigQuery] Lineage output tells a column named * from a star. Such a column is published with star="false" (column.standsForStar() reads the marker), so DataflowUtility.mergeDataflows no longer adds an expand_star edge from it to the table's other columns, the ER graph keeps it, and a whole-row pseudo star of a table that also has such a column is reported under its own endpoint instead of the column's id. A star written into the catalog for a table whose width is not known, by CREATE VIEW ... SELECT * or by lineage writing back a star it could not expand, is recorded as such (TSQLTable.addUnresolvedStar(), TSQLColumn.isUnresolvedStar()) and is not a column of the table; a catalog column named * is a column, so a synonym over it keeps its synonym relationship
  • 2026-09-25 [SQL Server, BigQuery] A column named * ([*] in SQL Server, `*` in BigQuery) is a column, not a star, although it is shown as *. INSERT INTO t2 (x, y) SELECT [*], a FROM t reports t.* -> INSERT-SELECT-1.* -> t2.x; previously t2.x had no source, and a column A that t does not have was reported
  • 2026-09-25 A select-list expression that contains *, such as a * b or COUNT(*), is no longer taken for a star. A CREATE TABLE ... AS SELECT whose list has one is defined by its select list, so a later INSERT INTO that table without a column list maps by position to its columns instead of adding columns named after the inserted ones, and a script variable such as $list in NOT IN ($list) is no longer reported as a column of the table
  • 2026-09-25 [Oracle] A synonym in the metadata catalog is matched to the analyzed SQL by identity under Oracle's rules. FROM ZY."address_synonym" a reads the stored lower-case synonym address_synonym and reaches its base table; the unquoted ZY.address_synonym, which Oracle reads as ADDRESS_SYNONYM, no longer does. A profile's own rules for synonyms (IdentifierProfile.builder(vendor).withObjectRules(ESQLDataObjectType.dotSynonyms, ...)) apply to them. A synonym relationship stated both by the metadata and by the catalog is reported once, and a base table named only by the catalog reports no source position
  • 2026-09-25 [Snowflake, ClickHouse, BigQuery, Oracle] The SQLDep and sqlflow metadata readers key names by the vendor's rules instead of a folded name: Snowflake orders and "orders", ClickHouse Orders and orders, and BigQuery p.d.Orders and p.d.orders are two tables, and Oracle procedures load_p and LOAD_P in SQLDep metadata are one. Procedures, functions, triggers and synonyms are keyed by their own object rules, and a metadata server of another vendor than the analysis keeps that vendor's rules for its tables and columns
  • 2026-09-25 [Oracle] A pipelined table function's cursor may name a schema-qualified table by its bare name: in FOR item IN (SELECT src_table.id FROM hr.src_table) the function's column traces to hr.src_table.id, no longer to a table src_table the query does not read. Object and collection types are found by their names as written, so "demo_row" and DEMO_ROW are two types. The ModelBindingManager pipelined registry methods that take text keys (indexObjectType, getObjectTypeFields, indexCollectionType, getElementTypeName, findPipelinedSignature, findPipelinedSignatureAny, getPipelinedSignatures) and the PendingPipelinedCallSite constructor are deprecated in favour of indexObjectTypeFields, getObjectTypeFieldsByKey, indexCollectionElementType, getCollectionElementType, findPipelinedSignatureByKeys, findPipelinedSignatureAnyByKeys, getPipelinedSignaturesByKey and PendingPipelinedCallSite.forFunctionKeys
  • 2026-09-25 [Oracle] Lineage reads a package body's variables, constants and cursors from every member of the package. v_count := 0 in reset_count and INSERT INTO log_t(n) VALUES (v_count) in log_it now read one package variable v_count; previously each member got its own unrelated copy, and only a member named like its package saw the package's. FOR r IN c LOOP and FETCH c INTO ... over a package-level cursor c in a member now follow that cursor, so src.a -> dst.a through it is reported
  • 2026-09-25 [Oracle] A routine declared inside another routine or inside a trigger reads the enclosing routine's variables: in PROCEDURE outer_p AS v NUMBER; PROCEDURE inner_p IS BEGIN INSERT INTO dst(a) VALUES (v); END; BEGIN SELECT a INTO v FROM src; inner_p; END; the v in inner_p is outer_p's, so src.a -> dst.a is reported. A FETCH ... INTO those variables binds each cursor column to its own target instead of fanning every column onto every target. A declaration in the nested routine still shadows the outer one: a nested CURSOR c is its own cursor
  • 2026-09-25 [Oracle] %ROWTYPE and %TYPE anchors name the cursor, record or type declared in scope before a table of the same name, as PL/SQL does: with a table c and a local CURSOR c, r c%ROWTYPE takes its fields from the cursor, and nothing flows out of the table c. A record-field anchor such as s_col g_gof.sColumn%TYPE is a type, no longer a data flow from a local variable that happens to be called sColumn
  • 2026-09-25 [SQL Server, Sybase, MySQL] After USE db, a reference inside a routine to one of its parameters is that parameter. A second, differently scoped copy is no longer created: CREATE PROCEDURE find_jobs @nm VARCHAR(50) AS ... WHERE JobTitle = @nm reports one @nm. An argument passed to a routine binds to the callee's parameter, not to a same-named local of the caller
  • 2026-09-25 [SQL Server] Inside a routine body, the routine's own parameter wins over a same-named variable of a later batch. In CREATE PROCEDURE copy_it (@OrgID INT, @SenderID INT, @AnalystId INT = NULL) AS EXEC push_it @OrgID, @SenderID, @AnalystId, followed after GO by DECLARE @AnalystID INT ... EXEC copy_it @SrcOrgID, @AnalystID, the @AnalystId passed to push_it is copy_it's parameter. The batch's @AnalystID fills @SenderID (the second positional argument) and no longer appears to reach the column fed by @AnalystId
  • 2026-09-25 [Oracle] A trigger's correlation names (:NEW, :OLD, REFERENCING aliases) and its local variables are separate names: with REFERENCING NEW AS n and DECLARE n NUMBER, :n.a := n writes the local n into the new row's column a. Two triggers that declare the same variable name keep two variables
  • 2026-09-25 Variables, cursors, declared types, trigger correlation names and table variables in the lineage model are keyed by the scope that declares them and their names as written, under the analysis's identifier rules. ModelBindingManager.getTableByName(String) finds one by its name as written, such as @x; a name composed with a routine's scope, such as PROC.@X, no longer finds a variable, and ModelBindingManager.getModel(Object) no longer returns variables or cursors for such composed strings. New ModelFactory.createReferencedVariable(...) resolves a reference to the declaration visible from it; ModelFactory.createVariable(...) keeps declaring in the current scope. ModelBindingManager.pushIdentityScope(String), ModelBindingManager.peekIdentityScope() and ModelFactory.pushIdentityScope(String) are deprecated
  • 2026-09-25 [Oracle] A call to a package member written with its package, such as emp_admin.hire_employee(...), binds to that member, so its arguments flow into the member's parameters instead of into an opaque function result. A call never binds to a trigger of the same name
  • 2026-09-25 [SQL Server] EXEC schema.[innerSp] binds to a procedure defined as [schema].[innerSp], and a call written sync2_CanUpdate binds to sync2_canUpdate: calls match definitions by the vendor's identifier rules, not by raw text
  • 2026-09-25 [DB2, Oracle, SQL Server] A delimited routine name is matched by the vendor's rules. DB2 F_GetRFECustomerName(...), which DB2 reads as F_GETRFECUSTOMERNAME, no longer links to CREATE FUNCTION "F_GetRFECustomerName", a different routine; an Oracle call "mixedCase"(...) now links to CREATE FUNCTION "mixedCase". A delimited name that contains a parenthesis, such as SQL Server [P(], is read as one name
  • 2026-09-24 [Teradata] Parsing Teradata on several threads at once no longer loses data type names. Two threads could fill the type maps together on their first lookup and lose entries for the rest of the JVM, so x(DATE) parsed as a function call; the maps are now filled once, before any thread reads them
  • 2026-09-24 A call is published as made from the routine it is written in, not from the first routine of that name: in PostgreSQL, a call of h() inside the overload p(a int, b int) was published as made from p(a int)
  • 2026-09-24 Lineage tells tables apart by the vendor's identifier rules also when the analysis configures no identifier profile. Under the vendor defaults it keyed a table by a folded name, so it merged tables the rules keep apart, such as Snowflake orders and "Orders", and split tables the rules call one; a table found in the SQL, in the catalog or in a metadata file now carries the identity of its name as written

GSP Java version 4.2.12(2026-09-28)

  • 2026-09-27 [Formatter] New GFmtOpt settings give table names, column names, table aliases and column aliases a case of their own: caseTableName, caseColumnName, caseTableAlias and caseColumnAlias. Unset, they follow caseIdentifier, so existing output does not change. Which name is which comes from name resolution (TSQLResolver2), so they apply only to SQL that parses. They never change delimited names ("x", [x], `x`), and where resolution does not report every use of a table name or alias (for example SQL Server's UPDATE o ... FROM orders o) and one name would get two cases, they are off for the whole input
  • 2026-09-27 [Formatter] Pp2FormatResult.isWholeDocument() tells whether pp2 returned pp's output for the whole input, and isNameCaseApplied() whether the per-kind name cases above applied. FormatterFactory.pp clears its per-session objects even when formatting throws
  • 2026-09-27 [Formatter] Requested transformations are accepted in their exact expected form: Pp2FormatOptions.transformPolicy (TransformPolicy) lets TokenEquivalence and pp2's guards accept variable case, delimited-name case, compact mode's rewrite of -- comments as /* -- ... */, and comment removal, each only as asked, or unchanged. Optimizer hints and MySQL /*! ... */ comments never change, and name case applies only to real names in each dialect (not to double-quoted strings in MySQL and related dialects, never to {...} ODBC escapes). With the policy, pp2 keeps compact or comment-removing output instead of falling back to recovery layout
  • 2026-09-27 [Formatter] GFmtOptFactory.newRegisteredCopy registers a full copy of a GFmtOpt under a fresh session id, and GFmtOptFactory.remove releases a registration
  • 2026-09-27 GSP format worker service (gsp_format_worker): the Instant SQL Formatter's compact mode is applied, with its line width in compact mode; table, column, alias and other identifier case each apply to their own names; column aliases keep their spelling; the list style no longer stacks function arguments (a call such as COALESCE(a, b, 'none') stays on one line, as in the classic formatter); /check accepts a policy for requested transformations; and each candidate reports the options its run did not apply (options_not_applied)

GSP Java version 4.2.11(2026-09-25)

  • 2026-09-25 No change to the parser library: 4.2.11 is 4.2.10 plus three endpoints in the GSP format worker service (gsp_format_worker): /check reports whether formatted SQL kept every token and comment of the input, /parse-probe reports which of several dialects parse a script, within a time budget, and /highlight colors SQL by the GSP lexer without changing its text

GSP Java version 4.2.10(2026-09-24)

  • 2026-09-24 [Doris, StarRocks] More column definitions parse in CREATE TABLE: the aggregate types HLL_UNION, BITMAP_UNION, REPLACE_IF_NOT_NULL, PERCENTILE_UNION (StarRocks) and QUANTILE_UNION (Doris); ARRAY<...>, MAP<...> and STRUCT<...> column types, nested ones such as ARRAY<ARRAY<INT>> included; CREATE EXTERNAL TABLE; StarRocks generated columns written without parentheses (newcol DOUBLE AS array_avg(arr)); DECIMALV3(p, s) and inverted indexes with PROPERTIES (...) on Doris; VARBINARY without a length on StarRocks. Doris reads PROPERTIES values written in double quotes, and the >> operator parses on both
  • 2026-09-24 [Doris, StarRocks] More CREATE TABLE ... AS SELECT forms parse with their query: a list of column names, as in CREATE TABLE t (a, b, c) AS SELECT k1, k2, k3 FROM src, which names the columns of the query by position; a PROPERTIES (...) table option, also before the query (on Doris PROPERTIES stays usable as a column name); and table clauses the grammar does not read, such as DISTRIBUTED BY or PRIMARY KEY on StarRocks or PROPERTIES with double-quoted strings, which are skipped while the table name, the column list and the query are kept. A syntax error inside the query is reported instead of the query being cut short
  • 2026-09-24 [Sybase] After SET QUOTED_IDENTIFIER OFF, a double-quoted text such as "psychology" is read as a character string, as ASE reads it, instead of a column, so lineage no longer reports a column the script never named. SET QUOTED_IDENTIFIER ON restores the identifier reading. A script that never sets the option keeps reading double-quoted text as an identifier
  • 2026-09-24 Lineage keeps two different names apart when the legacy name fold gives them one string. Examples: PostgreSQL and Snowflake "Amount" and amount, ClickHouse userId and userid, Oracle and Dameng "Amount" and amount, and SQL Server Amount and AMOUNT under a case-sensitive collation. Without metadata, a reference through SELECT * used to report both columns as its source; it now reports its own. Flink CREATE TABLE Orders and CREATE TABLE orders are now two tables. A column list over SELECT * (CTAS, view, CTE) still gets the column in its position. Analyses without such a pair produce the same output as before
  • 2026-09-23 [ANSI, Generic, ODBC] Regular identifiers fold to upper case and delimited ones stay exact, as the SQL standard specifies: "ORDERS" now matches orders, and "orders" no longer does. Metadata stored in upper case is found by a bare reference
  • 2026-09-23 [Access, DAX, MDX, Power Query] Identifier rules follow each product. Access, DAX and MDX compare delimited names case-insensitively and keep their case, and Power Query M compares names case-sensitively. Access reads [Orders] as a delimited name. In SQL_RENDER listings a DAX column keeps its brackets, and a simple DAX table name is shown without quotes (Orders, while 'Sales Territory' keeps them)
  • 2026-09-23 [Sybase, SQL Anywhere] A quoted name matches its other spellings case-insensitively ("Orders" and orders are one table on ASE, IQ and SQL Anywhere), so metadata registered under a quoted name is found and SELECT * expands to its columns
  • 2026-09-23 [DuckDB, SQLite, Firebird] DuckDB and SQLite compare quoted names case-insensitively and keep their case; Firebird folds unquoted names to upper case like Oracle. Metadata stored as "Orders" ("ORDERS" on Firebird) is found by orders, and SELECT * expands to its columns
  • 2026-09-23 [Flink] Identifiers keep their case and match case-sensitively, quoted or not, as Flink specifies: a backticked `userId` is the column userId, and src no longer resolves to a table Src
  • 2026-09-23 [Teradata] Quoted names compare case-insensitively, as Teradata specifies, and keep their stored case, so metadata that quotes every name (as the SQLFlow exporter writes it) matches unquoted references
  • 2026-09-23 [Hive, Spark SQL, Impala, Databricks] Backticked names compare case-insensitively, as each product documents, and keep their stored case: `Orders` resolves to metadata stored as orders, and SELECT * through it expands to the real columns. Backticked column names are shown without backticks in lineage output
  • 2026-09-23 [Redshift] Delimited identifiers fold to lower case and compare case-insensitively, as AWS documents when enable_case_sensitive_identifier is off: "Orders" and orders are one table. Delimited column names are shown without delimiters in lineage output
  • 2026-09-23 Lineage follows a configured IdentifierProfile. Under a stricter configuration (for example SQL Server with a case-sensitive collation), tables such as dbo.Orders and dbo.ORDERS, and columns such as Amount and AMOUNT, stay two objects in the lineage model, the output merges and resolver2's SELECT * push-down. A configured profile with neither metadata nor DDL no longer produces an empty analysis. Output under the default rules is unchanged
  • 2026-09-23 The catalog's schema index is keyed by the name service for every vendor, so TSQLEnv.searchTable gives the same answer whether it is passed a TObjectName or a qualified String. Previously the two could disagree under a configured rule for all but Snowflake and BigQuery
  • 2026-09-22 IdentifierProfile rules can be configured per object category with IdentifierProfile.builder(vendor).withObjectRules(ESQLDataObjectType, IdentifierRules), for example case-insensitive table names alongside case-sensitive column names. TSQLEnv(vendor, profile), TGSqlParser.setIdentifierProfile, Option.setIdentifierProfile (lineage) and TSQLResolverConfig.setIdentifierProfile apply one configuration to the catalog, the parser, resolver2 and lineage of an analysis, and a conflicting configuration throws
  • 2026-09-22 [Spark SQL, DuckDB, ClickHouse] Columns removed by a star exclusion (* EXCEPT (...), * EXCLUDE (...)) are no longer projected into lineage. A ClickHouse EXCEPT (x) REPLACE (...) keeps its EXCEPT, and an exclusion by regular expression keeps the full expansion, since its excluded set cannot be computed. Two stars of unknown width in one select list are no longer split between the targets by position
  • 2026-09-22 [BigQuery] Tables whose names differ only in case stay two tables in the schema index and in lineage
  • 2026-09-21 Lineage keeps a column's spelling and catalog identity through lookup, star expansion, model copies and merging. [Oracle] A %TYPE anchor keeps its written name, and an ordinary qualified type name no longer creates a dependency
  • 2026-09-21 [Formatter] PrettyScore penalizes a script formatted into one over-wide line: the new TOKEN_WALL, INDENT_NOT_SHOWN and WIDTH_DOMINANT findings fire only when most of the script sits on one line, so such output no longer outranks a real wrap
  • 2026-09-20 [Snowflake] Quoted names that differ only in case ("Orders", ORDERS) stay separate catalog objects, and environment merges keep stored names
  • 2026-09-20 Lineage looks up a delimited table by its spelling as written, so a table missing from the catalog is not retried as a different, unquoted object
  • 2026-09-20 The catalog provider is now the only index in TSQLEnv. Oracle "Orders" and ORDERS no longer share an entry, so SELECT * FROM "Orders" no longer expands to the columns of ORDERS. SQL Server [my.db].dbo.t2 is filed under the catalog my.db, and a name with a dot inside it is no longer found by its last part
  • 2026-09-20 TTable.getCatalogStatus() (ETableCatalogStatus: NOT_CHECKED, FOUND, NOT_FOUND) reports whether a base table was found in the catalog, separately from how its columns resolved. Oracle "Orders" and ORDERS no longer collide in the catalog store
  • 2026-09-20 Metadata files declare how their names are written: "nameForm": "SQL_LEXICAL" or "CATALOG_STORED". A name read back from a database catalog (Oracle stores a column created as "Amount" under Amount) is now matched as that stored name. The sqlflow servers format (plain and sharded) defaults to SQL_LEXICAL and the JSON manifest to CATALOG_STORED. Eager and lazy catalog loading and the model validator all honour the declaration, and a misspelled nameForm fails loudly
  • 2026-09-20 [MySQL, Doris, OceanBase, StarRocks] A catalog column, function, procedure or trigger is normalized by its own object type rather than the table rule, so a column registered as Amount reports getName() = Amount
  • 2026-09-19 Keyword tables were audited against each vendor's official reserved words. 108 missing reserved words were added across 11 dialects, among them Snowflake WINDOW, MySQL QUALIFY and TABLESAMPLE, and Hive MERGE and EXCEPT. The SAP IQ and HANA lists were extended, SAP IQ no longer shares ASE's table, and thirteen dialects get a table of their own. Generated SQL names delimit these words
  • 2026-09-19 SQL_RENDER listings print one deterministic spelling per object instead of echoing each spelling the script used, so dbo.SalesOrder and [dbo].[SalesOrder] are one table. SqlNameGenerator.QuotePolicy (WHEN_NEEDED by default, or ALWAYS) controls the delimiters
  • 2026-09-19 The Tables/Fields listings (TGetTableColumn, TSQLResolver2ResultFormatter) de-duplicate by object identity rather than by rendered text: BigQuery Sales and SALES are listed as two tables, and Oracle Sales, SALES and "SALES" as one
  • 2026-09-19 New SqlNameGenerator generates a SQL name from stored name segments with correct delimiting and escaping. For example, a SQL Server stored name [db name], whose brackets are part of the name, becomes [[db name]]]. A name it cannot prove correct fails with a reason instead of returning a plausible-looking spelling
  • 2026-09-19 Display names come from the name service. DisplayNameMode.CANONICAL, which used to behave like DISPLAY, now folds unquoted names as the vendor does (upper case on Oracle and Snowflake, lower case on PostgreSQL), while quoted names keep their case
  • 2026-09-18 Opt-in SQL comment object bindings: with Option.setCollectCommentBindings(true) lineage collects each comment together with the AST object it belongs to, keeps the bindings through merging, filtering and parallel analysis, and reports coverage and degradation diagnostics. Off by default
  • 2026-09-18 [Oracle] Bare and qualified PL/SQL calls (proc_name;, pkg.member()) bind to the declared package members, including forward calls, and keep their argument lineage, call-site comments and anonymous callers; a fullwidth comma (U+FF0C) is accepted as a separator without rewriting the SQL
  • 2026-09-17 [SQL Server] Temporary-table metadata stays local to each routine, while proven caller and session visibility is kept. Function-name output is compatible again, and expanding declared columns keeps their source coordinates
  • 2026-09-17 DDL metadata in a script is resolved at each statement's position, and catalog names are kept distinct from their SQL spellings
  • 2026-09-17 [Doris, StarRocks] CREATE TABLE is built as a TCreateTableSqlStatement by the statement splitter and parsed by the grammar, where it used to pass through unparsed
  • 2026-09-17 [Oracle] A bind variable is accepted as the XMLTABLE query string, and PASSING expressions are traversed so the resolver and lineage see their SQL inputs (MantisBT #4740)
  • 2026-09-16 [Oracle] A script's own comments reach the output: the xml procedure model gains comment and displayName (GSP never fills displayName), oraclePackage gains comment, and getCommentAfterNode() returns the comment after a node instead of the leading one (it used to throw on a node without one); the new getCommentBeforeNode(boolean) returns the comment block that belongs to a node
  • 2026-09-16 [Databricks] Liquid clustering (CLUSTER BY) in CREATE TABLE and CTAS is parsed and its columns are registered against the target for the resolver and lineage (MantisBT #4732)
  • 2026-09-16 [Databricks] Parenthesized table sources in FROM are parsed, keeping the underlying table name for resolution and column lineage (MantisBT #4733)
  • 2026-09-16 [Teradata] Generic table operators accept AS input aliases, PARTITION BY and ORDER BY; the input clauses are exposed apart from the output alias and resolve against the ON source only (MantisBT #4734)
  • 2026-09-16 [Snowflake] COPY INTO with an explicit target column list and a stage source binds the destination columns to the COPY target and keeps the selected subset in stage/file lineage (MantisBT #4735)
  • 2026-09-16 [Snowflake] Function names with an omitted schema (db..func()) are parsed and resolved, applying Snowflake's PUBLIC schema rule independently of the session's current schema (MantisBT #4736)
  • 2026-09-16 [Oracle] The physical table inside a Data Pump RELATIONAL(...) wrapper is parsed with its flashback clause and alias, so resolver and lineage keep the actual source table (MantisBT #4737)
  • 2026-09-16 [Oracle] Support SHARING before table definitions and CTAS
  • 2026-09-16 [Oracle] Support AS OF SNAPSHOT flashback expressions
  • 2026-09-16 [Oracle] Preserve bind parameters in character type lengths
  • 2026-09-16 [Oracle] Recognize U-prefixed Unicode string literals
  • 2026-09-16 [Oracle] Support asterisk precision for DECIMAL and DEC
  • 2026-09-16 [Oracle] Support index maintenance clauses after a partition exchange
  • 2026-09-16 [Oracle] Support parenthesized value tuples in row comparisons
  • 2026-09-15 The Tables/Fields listing drops a t.* line once every column of t is printed on its own line (listStarColumn=false still hides it outright), and resolver2 sees CREATE TABLE statements earlier in the same batch
  • 2026-09-14 [BigQuery, Snowflake] A struct field whose base column more than one relation in FROM could provide is left unresolved, instead of being bound silently to the first relation. resolver2 now models JOIN ... USING merged columns, so SELECT c FROM a JOIN b USING (c) resolves c instead of reporting it as ambiguous
  • 2026-09-11 [SQL Server] A signed literal in an EXEC argument (EXEC p @NumberRecords = -1) keeps its digits and is typed as unary minus (MantisBT #4724)
  • 2026-09-11 [Snowflake] A CREATE FUNCTION body delimited by single quotes is returned by getBlockBody(), stamped as a Snowflake block node, like the $$ form; lineage was not affected (MantisBT #4723)

GSP Java version 4.2.9(2026-09-09)

  • 2026-09-05 [Snowflake] IDENTIFIER(...) table names are recognised from the SQL tokens, so whitespace or a comment before the opening parenthesis no longer leaks into the source table name; qualified-name parts and quoted spelling are preserved (MantisBT #4714)
  • 2026-09-05 [Formatter] PrettyScore no longer rewards an alias split off its expression under the ALIAS_COLUMN_VAR rule
  • 2026-09-04 [SQL Server] MERGE and UPDATE targets that are writable CTEs are analyzed: the modified projections map to the one physical base table behind the CTE. An ambiguous or non-updatable CTE stays a result set and raises a syntax hint instead of inventing a base table
  • 2026-09-04 Projected DML targets in lineage are hardened against the same shape
  • 2026-09-04 [Formatter] Statements inside PL/SQL IF branches are laid out on their own lines, and a standalone comment is aligned with the code it precedes
  • 2026-09-04 Fix the source offsets reported for the end-of-file token
  • 2026-09-02 [Snowflake] Parse dynamic table refresh DML
  • 2026-09-01 [Snowflake] Parse the dynamic table EXECUTE AS user and column nullability
  • 2026-08-31 [Oracle] A trigger's SELECT INTO keeps each pseudo-record target by ordinal, so a multi-column SELECT INTO no longer collapses every column onto the final field. Custom REFERENCING bindings and positional handling through set operations are preserved (MantisBT #4707)
  • 2026-08-31 [SQL Server] Optional support for :name, :1 and :@name bind variables, off by default and scoped to the parser instance (Option.setEnableMssqlColonBindVariables, also honoured by DataFlowAnalyzer); ITokenListHandle callbacks are restored for delegated parsers (MantisBT #4706)
  • 2026-08-30 [SQL Server] SEND ON CONVERSATION keeps its parsed conversation_handle when the optional clauses are absent, instead of failing a token-to-object-name cast (MantisBT #4690)
  • 2026-09-03 [SQL Server] DynamicSqlSite.getUnresolvedFragments() names the holes of a dynamic-SQL site: for every EXEC / sp_executesql site the abstract evaluator saw, one UnresolvedFragment per value it could not fold, with the variable or parameter (@Where, @entity), the hole kind (SqlFragment.HoleOrigin, now also QUERY_ASSIGNED_VARIABLE and FLOW_DEPENDENT_VARIABLE), the reason, the data source the value would come from (dbo.SSF_Entity.Entity), and the 1-based origin span to highlight (the assignment, the parameter declaration, or the reference). getMaterializedText() is the materialized argument when it reduced. INSERT INTO t EXEC(@sql) (the string form, no module name) is now analyzed like its top-level twin: it records a site and its inner statement's lineage. getUnresolvedHoleCount() now equals the list size whenever the fragments are known: a variable assigned from a query, appended to a literal, reported 0 exact; a query-assigned statement executed through INSERT INTO #t EXEC sp_executesql @sql reported -1 (the evaluator did not descend into INSERT ... EXEC). A site whose argument is nothing but one unknown value is reported as argument is a runtime variable instead of a parse failure of text that was never SQL. In LEGACY trust mode a RESOLVED site whose argument carries a real hole no longer lists proven relationship ids. The evaluator now treats a variable written by an OUTPUT argument or return status of a static procedure call, or by FETCH ... INTO, as an unknown value: previously it was believed NULL, so CONCAT(...) / ISNULL(...) erased it and a fabricated literal string could be analyzed as resolved lineage; those sites are now UNRESOLVED with the variable listed. Lineage output is otherwise unchanged
  • 2026-09-02 [Formatter] TokenEquivalence exposes its two halves, solidTokensEquivalent and commentsPreserved, and the format worker (gsp_format_worker) now applies them instead of a second token policy of its own: a candidate GSP core accepts is usable in the worker too (the Oracle SQL*Plus-vs-keyword case), a pp candidate core rejects is reported as INVARIANT_FAILED while the token-safe pp2 result stays usable (the DB2 =? files), and the comment check compares text and order rather than counts
  • 2026-09-02 [Formatter] Fixes for the ISF prettyscore corpus handoff: FormatterFactory.pp no longer throws NullPointerException on a BigQuery subquery expression without its own parenthesis tokens; TokenEquivalence accepts the Oracle lexer's SQL*Plus-command classification of a word at column 0 (exit) against the same word as a keyword once indented; and FormatterFactory.pp2 gains a whole-document fast path (Pp2FormatOptions.wholeDocumentFastPath, default on): when the input parses as a whole and pp formats it without changing a token, pp2 returns that text as OK, giving byte parity with pp on parseable input and fixing the Snowflake END$$ end-of-input token loss; the regional pipeline still handles everything else
  • 2026-09-02 [Formatter] Compound statements without a dedicated formatter (T-SQL BEGIN TRY ... END CATCH, PL/pgSQL and DB2 loops, MySQL and DB2 IF / WHILE, REPEAT) had their bodies printed as one glued line by FormatterFactory.pp. Their body statements now get their own lines one indent deeper, the opening (ELSE, BEGIN CATCH) and closing (END ..., EXCEPTION, ELSEIF) keywords start their own line at the statement column, and the body statements are formatted recursively. A SQL Server procedure whose body starts after comment lines is laid out too (the search for AS skipped no comments and left the whole body on one line)
  • 2026-09-02 [Snowflake, BigQuery, Teradata, Databricks] FormatterFactory.pp puts a QUALIFY clause on its own line, aligned with the other clause keywords, and breaks its predicate at AND / OR like WHERE; GROUP BY ALL gets its own line although it has no items
  • 2026-09-02 [Formatter] CREATE MATERIALIZED VIEW ... AS <query> is laid out like CREATE VIEW (AS on its own line, the query one block indent deeper) instead of hanging off the column after AS. PrettyScore no longer treats BigQuery's SELECT * EXCEPT (...) / * REPLACE (...) as a set operator that must start a line
  • 2026-09-02 [Formatter] A function argument that fits on its line keeps its AND, OR and || inline in FormatterFactory.pp (If(a = 1 OR b = 'X', x, NULL) used to break before OR like a WHERE clause); a packed select list (longer than maxSelectListColumn) is filled by line width only, without the extra break every 20 items
  • 2026-09-02 [Formatter] PrettyScore consistency rule: a width-filled list (several items per line up to the line width, pp's layout for lists longer than maxSelectListColumn) no longer counts as LIST_STYLE_MIXED when every line break is forced by the width or by a multi-line item, and the select list of a parenthesized subquery is judged once instead of again as a paren list
  • 2026-09-02 [Formatter] Inside a routine body the clause keywords of DELETE, UPDATE, INSERT and MERGE (SET, WHERE, VALUES) landed at column 0 while the statement was indented; they now follow the statement column like SELECT's clauses. Alias alignment no longer follows one over-long select item past the line width (the outlier keeps a single blank). A wrapped argument list (functionCallParametersStyle=AsWrapped, the default) breaks at lineWidth instead of staying on one line. After ( -- comment the next token starts right after the parenthesis
  • 2026-09-02 [Formatter] PrettyScore spine rule: a JOIN under the first table of FROM and an INSERT ... SELECT whose query sits one unit under INSERT are pp's own layouts and no longer count as CLAUSE_COLUMN_DRIFT
  • 2026-09-02 [Formatter] Lay out CREATE TABLE ... AS <query> like CREATE VIEW in FormatterFactory.pp: AS on its own line at the statement column and the query one block indent deeper. The query used to hang off the column after AS, so a long table name pushed every line of the query to the right. SQL Server alias = expr select items are no longer padded by alias alignment
  • 2026-09-02 [Formatter] Break the line before a statement that starts on the line of the previous statement's terminator (SELECT x FROM t; SELECT y ...) in FormatterFactory.pp; every column of the second statement was measured from the terminator, so its select items landed at column 24 and its FROM at column 10
  • 2026-09-02 [Formatter] PrettyScore indent rule: SELECT * before FROM no longer demotes FROM from a clause head (a * is not a multiplication there), select-list modifiers (DISTINCT, TOP n, DISTINCT ON) no longer count as the first item, and inside CASE an AND hanging under the WHEN predicate or a || one unit past the THEN expression is accepted. These were scorer false positives that made INDENT_JUMP the top backlog rule
  • 2026-09-02 [Formatter] Keep packed select lists readable in FormatterFactory.pp. A select list longer than maxSelectListColumn (default 20) is packed several items per line; a packed line now respects lineWidth, ends at a comment (the item after the comment used to be re-indented to the column it would have had on the unbroken line, up to hundreds of blanks), gives a CASE expression or subquery item a line of its own, and is no longer padded by alias alignment (MantisBT #4299). The re-indent after a line comment elsewhere is capped at half the line width and otherwise falls back to the indent of the preceding code, so successive comment lines no longer push statements further right
  • 2026-09-02 [Formatter] Stop emitting blanks before a line break in FormatterFactory.pp output. A blank left behind a -- comment became part of the comment when the output was lexed again, and most formatted lines ended in trailing whitespace; no-format zones stay verbatim and the blank after a MySQL $ delimiter is kept. Existing golden outputs were regenerated (whitespace only)
  • 2026-09-02 [BigQuery] Keep the user's spelling of a backtick-quoted path such as `project.dataset.table` in formatted output (pp and pp2). The BigQuery tokenizer splits it into parts for the grammar; the parts now carry TSourceToken.splitFrom and the formatter prints the original token once
  • 2026-09-02 [Formatter] Fix NullPointerException in pp when a comment sits between WITH and the first CTE, between a CTE's AS and its query, or between an INSERT column list's ( and its first column (SourceTokenSearcher.backforwardSearchSolid skips trivia), and when a function-call argument list has no source token
  • 2026-09-02 [Formatter] Add PrettyScore (gudusoft.gsqlparser.pp.score.PrettyScore), a readability scorer for formatted SQL: given a formatted text and its dialect (plus optionally the original text and the formatter options used) it returns a 0-100 score, the attainable score with unfixable deductions added back, per-dimension scores (spine, indent, width, density, alignment, consistency, grouping, comments), every finding with a position-independent fingerprint, and a ranked improvement list mapped to GFmtOpt / pp2 renderer targets. Three style profiles ship (isf-default, river, compact); syntax-broken input is scored through the pp2 lexical facts. JSON contract version prettyscore/0.1, calibration heuristic-v0 (relative comparison within one profile and corpus only). CLI gudusoft.gsqlparser.demos.prettyscore.PrettyScoreMain adds gold anchoring, corpus baselines with regression checks, a per-rule backlog, finding-level run diffs and blind acceptance samples

GSP Java version 4.2.8(2026-08-31)

  • 2026-08-31 Refactor the build process for the SQLFlow integration

GSP Java version 4.2.7(2026-08-30)

  • 2026-08-31 [SQL Server] Accept statements the grammar models as a bare dummy node — DROP PARTITION FUNCTION, DROP PARTITION SCHEME, DROP SYNONYM, CREATE MASTER KEY, CREATE CREDENTIAL, CREATE DATABASE SCOPED CREDENTIAL, CREATE DATABASE ENCRYPTION KEY, CREATE EVENT SESSION, CREATE EXTERNAL DATA SOURCE, CREATE EXTERNAL LANGUAGE, CREATE SELECTIVE XML INDEX and ALTER SECURITY POLICY — as the body of an IF/WHILE. They parsed standalone but produced no statement object when embedded
  • 2026-08-30 [Teradata] Reject empty INSERT column lists instead of failing later
  • 2026-08-30 [Snowflake] Reject malformed SET statement roots
  • 2026-08-30 [SQL Server] Reject an IF/WHILE with no statement body
  • 2026-08-30 [SQL Server] Guard prefixed literal decoding against malformed input
  • 2026-08-30 [SQL Server, Teradata] Guard against cycles in CTE resolution
  • 2026-08-30 [SQL Server, Oracle] Narrow the SELECT recovery guards so valid statements are not affected
  • 2026-08-30 [SQL Server, Greenplum] Build constraint attributes on recovered statements
  • 2026-08-29 [SQL Server] Prevent data lineage model crashes on recovered ASTs
  • 2026-08-29 [Databricks] Support $var notebook variable substitution
  • 2026-08-28 [SQL Server, Sybase] Fix NullPointerException on a malformed select-list alias form
  • 2026-08-28 [MySQL, ClickHouse] Fix NullPointerException on RENAME without the COLUMN keyword
  • 2026-08-27 [Semantic IR] Publish Join Analysis Consumption Profile v1. New integrations should use SqlSemanticAnalyzer and consume immutable join facts, structured diagnostics, and Catalog DTO input; low-level builder and binding APIs remain advanced/preview, while validation and parity packages remain internal
  • 2026-08-27 [Semantic IR] Make AnalysisResult.getSchemaVersion() match the payload-sensitive version written to its JSON (3 for join-analysis facts); SqlSemanticAnalyzer.schemaVersion() remains the historical baseline version 1
  • 2026-08-26 [SQL Server] Fix statement splitting in CTE-bodied views
  • 2026-08-25 Report the ON/WHERE operand's own coordinate on data lineage join and filter sources: a clauseType="joinCondition"/"where" source now points at the operand reference in the clause instead of the definition site of a shared #temp/CTE/derived-table column, and star-expanded SELECT * INTO #t columns carry the * token's span instead of a degenerate [1,1] coordinate
  • 2026-08-25 [Snowflake] Drop the stale session schema after USE <database>
  • 2026-08-24 [Snowflake] Resolve a bare USE <name> as USE DATABASE
  • 2026-08-24 Keep dynamic-SQL lineage when an enrichment walk fails instead of dropping the dynamic site
  • 2026-08-24 Guard null children in acceptChildren to prevent NullPointerException on recovered ASTs
  • 2026-08-20 [Sybase, Sybase IQ, SQL Anywhere, SQL Server] Support DROP ... IF EXISTS
  • 2026-08-20 [SQL Server] Resolve finite dynamic SQL values
  • 2026-08-19 [Oracle] Classify pseudocolumns instead of binding them, and fix the edge cases found in review

GSP Java version 4.2.6(2026-08-18)

  • 2026-08-18 [MySQL] Reach 100% measured syntax coverage: 135 of 135 documented constructs, 242 of 242 statements
  • 2026-08-18 [MySQL] Recognize the admin DDL statement families in the statement splitter: ALTER USER, ALTER INSTANCE, CREATE/ALTER/DROP SERVER, TABLESPACE (including UNDO TABLESPACE), LOGFILE GROUP, CREATE/DROP ROLE, GET DIAGNOSTICS, and IMPORT TABLE FROM
  • 2026-08-18 [MySQL] End a BEGIN-bodied stored routine at a nesting-balanced END; when no DELIMITER command was issued, instead of merging every following statement into the routine; a routine terminator fused with the delimiter character (END$) works again
  • 2026-08-18 [MySQL] Parse the ODBC escape sequence { OJ ... } as a table reference

GSP Java version 4.2.5(2026-08-18)

  • 2026-08-18 [Oracle] Reach 100% measured syntax coverage: 182 of 182 documented constructs, 319 of 319 statements
  • 2026-08-18 [Oracle] Recognize the admin DDL statement families in the statement splitter: ANALYTIC VIEW, ATTRIBUTE DIMENSION, AUDIT POLICY, HIERARCHY, INMEMORY JOIN GROUP, LOCKDOWN PROFILE, MATERIALIZED ZONEMAP, PLUGGABLE DATABASE, PMEM FILESTORE, FLASHBACK ARCHIVE, DATABASE LINK and SYNONYM alter forms, EDITION, ADMINISTER KEY MANAGEMENT, and private/or-replace OUTLINE variants; fix a typo that broke CREATE RESTORE POINT
  • 2026-08-18 [Oracle] Keep multi-line SET CONSTRAINT[S]/ROLE/TRANSACTION as SQL statements instead of truncating them as line-terminated SQL*Plus SET commands, and keep semicolons inside a CREATE JAVA SOURCE class body from ending the statement
  • 2026-08-18 [Oracle] Parse TRUNCATE CLUSTER, ALTER MATERIALIZED VIEW option heads (CONSIDER FRESH, ENABLE/DISABLE QUERY REWRITE, COMPILE), DROP MATERIALIZED VIEW LOG ON, and CREATE DATABASE with its full clause list

GSP Java version 4.2.4(2026-08-18)

  • 2026-08-18 [SQL Server] Reach 100% measured syntax coverage: 290 of 290 documented constructs, 1082 of 1082 statements
  • 2026-08-18 [SQL Server] Recognize 48 admin DDL statement kinds in the statement splitter (DROP/ALTER/CREATE for broker priorities, encryption keys, external objects, resource pools, audits, availability groups and more) and stop mid-statement splits at SET, IF EXISTS and MERGE
  • 2026-08-18 [SQL Server] Parse GRANT/DENY/REVOKE securable classes (ENDPOINT::, AVAILABILITY GROUP::, SYMMETRIC KEY::, XML SCHEMA COLLECTION:: and more), CASCADE tails, REVOKE GRANT OPTION FOR, and CREATE ENDPOINT as a grantable permission
  • 2026-08-18 [SQL Server] Parse CREATE EXTERNAL TABLE without WITH, selective XML index ALTERs, SEND ON CONVERSATION handle lists, DROP DEFAULT IF EXISTS, and GET CONVERSATION GROUP

GSP Java version 4.2.3(2026-08-18)

  • 2026-08-18 [Snowflake] Reach 100% measured syntax coverage: 455 of 455 documented constructs, 757 of 757 statements
  • 2026-08-18 [Snowflake] Recognize integration, managed account, stream, procedure, DESC, EXPLAIN USING and SHOW statement families in the statement splitter
  • 2026-08-18 [Snowflake] Fix a mid-statement split that turned ALTER ... SET into two statements, and stop re-parsing CREATE EXTERNAL FUNCTION request URLs as SQL bodies
  • 2026-08-18 [Snowflake] Parse ALTER DATABASE/SCHEMA/PIPE/TASK/EXTERNAL TABLE variants, CREATE DATABASE AS REPLICA, REVOKE ... FROM SHARE, open-ended privilege names, SHOW STREAMS/TASKS filters, and Windows file paths in PUT

GSP Java version 4.2.2(2026-08-18)

  • 2026-08-18 [ClickHouse] Reach 100% measured syntax coverage: 156 of 156 documented constructs, 717 of 717 statements
  • 2026-08-18 [ClickHouse] Recognize access-entity and admin DDL in the statement splitter: CREATE/ALTER USER, ROLE, ROW POLICY, QUOTA, SETTINGS PROFILE, MASKING POLICY, NAMED COLLECTION, DICTIONARY, plus CHECK, EXISTS, EXECUTE AS, MOVE, EXCHANGE TABLES, UNDROP, REPLACE TABLE and the SHOW family
  • 2026-08-18 [ClickHouse] Parse dollar-quoted strings ($tag$...$tag$), query parameters {name: Type}, TRUNCATE/USE/SET/DESCRIBE variants, CREATE DATABASE engine clauses, more ALTER TABLE forms, ASSUME constraints, compound and unit-less INTERVAL, and AT TIME ZONE
  • 2026-08-18 [ClickHouse] Fix a tokenizer crash that silently produced zero tokens for query-parameter syntax followed by whitespace

GSP Java version 4.2.1(2026-08-18)

  • 2026-08-18 [ClickHouse] Raise measured syntax coverage from 78/156 to 100/156 constructs (504 to 548 of 706 statements)
  • 2026-08-18 [ClickHouse] Parse Map literals, two-argument CAST, dotted nested column names and tuple element access (a.2)
  • 2026-08-18 [ClickHouse] Fix statement mistyping after token-level rewrites: a statement following a :: typecast could be reported as invalid
  • 2026-08-18 [ClickHouse] Parse ALTER TABLE UPDATE/DELETE IN PARTITION, column names from/to, INSERT ... FORMAT with inline data, and PARALLEL WITH
  • 2026-08-18 [ClickHouse] Parse named Tuple fields in type and expression positions, ILIKE column matchers, COLUMNS arithmetic
  • 2026-08-17 [ClickHouse] Parse == comparison, ASC/DESC COLLATE, INTERSECT DISTINCT, OFFSET/FETCH, GROUPING SETS, ASOF/ANY join order, WITH FILL STALENESS/INTERPOLATE, VALUES table literals, trailing commas

GSP Java version 4.2.0(2026-08-17)

  • 2026-08-17 [Sybase ASE, SAP SQL Anywhere, Sybase IQ] Add three per-product vendors, each with its own grammar, splitter and syntax coverage page; sybase remains the permissive union of the family and is unchanged
  • 2026-08-17 [Sybase ASE] Reject Watcom-SQL procedure syntax that belongs to SQL Anywhere: leading parameter modes, DEFAULT parameter defaults, RESULT clauses, ON EXCEPTION RESUME, a body without AS, and parameter names without @
  • 2026-08-17 [Sybase ASE, SAP SQL Anywhere] Reject Sybase IQ specialized index types and IQ-only DDL
  • 2026-08-17 [Sybase IQ, SAP SQL Anywhere] Reject ASE-only statements such as DUMP TRANSACTION, READTEXT, UPDATE INDEX STATISTICS and DELETE STATISTICS, with a message naming the statement that belongs to a different product
  • 2026-08-17 Report the full capability matrix for all 42 dialects
  • 2026-08-16 [Sybase] Keep the trailing parameter mode OUTPUT/OUT
  • 2026-08-15 [Sybase] Support DEFAULT in procedure parameters
  • 2026-08-14 [Snowflake, Vertica] Bind WHERE clause references to a SELECT list alias
  • 2026-08-14 [Snowflake] Pin COPY INTO option order
  • 2026-08-14 [Snowflake] Keep quotes in PIVOT IN-list column names, and fix Oracle PIVOT quote naming
  • 2026-08-13 [Snowflake] Resolve FLATTEN implicit output columns
  • 2026-08-13 [SQL Server] Decode quoted SELECT aliases in data lineage output
  • 2026-08-02 Fix a multi-threading defect where repeated analysis of the same input returned different results
  • 2026-08-02 Add a column lookup cache to speed up data lineage analysis
  • 2026-08-01 Detect large SQL files automatically and split them for parallel analysis
  • 2026-08-01 Merge and deduplicate data lineage incrementally, and speed up relation deduplication by replacing MD5 with a 64-bit hash
  • 2026-08-01 Fix null pointer and concurrency defects in data lineage analysis, and improve sorting and temporary file cleanup
  • 2026-08-01 Scan the whole SQL environment before dataflow and procedure dataflow analysis
  • 2026-07-31 Avoid out-of-memory and an infinite loop when dataflow analysis falls back to procedure dataflow analysis
  • 2026-07-30 Fix missing database and schema when source code is read from JSONL without a group

GSP Java version 4.1.11(2026-08-10)

  • 2026-08-10 [ClickHouse] Model remote()/remoteSecure() table functions as lineage sources, with ClickHouse-exact string-literal decoding
  • 2026-08-10 [ClickHouse] Parse ALTER TABLE sub-clause families: DETACH/ATTACH/MOVE PART, FREEZE WITH NAME, MATERIALIZE/CLEAR/APPLY, projections, expression indexes, MODIFY/RESET SETTING, TTL and COMMENT
  • 2026-08-09 [ClickHouse] Resolve expression-CTE aliases (WITH expr AS name) as scalar symbols in the resolver and data lineage
  • 2026-08-09 [ClickHouse] Parse expression CTEs, materialized CTEs and lambda-bodied CREATE FUNCTION
  • 2026-08-09 [ClickHouse] Parse the VIEW family: ON CLUSTER, refreshable materialized views, MODIFY QUERY
  • 2026-08-09 [ClickHouse] Parse OPTIMIZE TABLE tails and table functions in FROM
  • 2026-08-09 [ClickHouse] Add a documented syntax coverage corpus; measured coverage rises from 51/156 to 78/156 constructs (504/704 statements)
  • 2026-08-09 Document fault-tolerant SQL formatting and all SQL formatter options
  • 2026-07-29 [PostgreSQL] Map && to overlaps_t and parse unknown operators
  • 2026-07-29 [Snowflake] Support alias after parenthesized PIVOT table
  • 2026-07-29 Render aliased nodes from the token chain, not list index

GSP Java version 4.1.10(2026-08-08)

  • 2026-08-08 Stop duplicating star-traced columns in linkedColumns
  • 2026-08-08 Reorganize documentation by user goal
  • 2026-08-07 Keep undefined MERGE VALUES names unresolved
  • 2026-08-07 [SQL Server] Fix CLR routines with AS EXTERNAL NAME
  • 2026-08-07 [BigQuery] Fix CTE with parenthesized main query
  • 2026-08-07 Add AST rewriting how-to and demo guide
  • 2026-08-07 Add offline SQL syntax validation guide
  • 2026-08-06 [Vertica] Raise syntax coverage from 77% to 100% of corpus
  • 2026-08-06 [OpenEdge] Raise corpus parse rate from 52% to 100%
  • 2026-08-06 [Teradata] Raise corpus parse rate from 83% to 98%
  • 2026-08-06 [HANA] Raise syntax coverage from 77% to 98% of corpus
  • 2026-08-06 [Informix] Raise corpus parse rate from 86% to 94%
  • 2026-08-06 [Greenplum] Raise corpus parse rate from 75% to 96%
  • 2026-08-05 [DB2] Reach 100% corpus parse coverage
  • 2026-08-05 [Impala] Raise corpus parse rate from 70% to 99%
  • 2026-08-05 [Exasol] Raise syntax coverage from 42% to 98% of corpus
  • 2026-08-05 [Flink] Raise corpus parse rate from 67% to 98%
  • 2026-08-05 [Sybase] Raise IQ syntax coverage to 88% of corpus
  • 2026-08-05 [SQL Server] Fix token shift from Exasol token placement
  • 2026-08-05 [Teradata] Fix whitespace-sensitive cast shorthand
  • 2026-08-04 [Databricks] Fix Mantis 4638-4646 syntax gaps
  • 2026-08-04 Add trusted playground lineage
  • 2026-08-03 Harden stored procedure lineage trust
  • 2026-08-03 Add doris, starrocks, flink, soql and odbc to the sqlcoverage corpus
  • 2026-08-01 Add SELECT/DML/proc parse rates to coverage docs
  • 2026-07-31 [Databricks] Pin ODBC date escape shapes from Mantis 4575
  • 2026-07-31 [Docs] Fix quick-start Java sample and trial-limit claims
  • 2026-07-31 Unify public dialect metadata
  • 2026-07-31 Publish an SBOM and smoke-test the install daily
  • 2026-07-30 [MySQL] Only treat line-leading DELIMITER as a directive
  • 2026-07-30 [SQL Server] Allow empty OVER() in WITHIN GROUP
  • 2026-07-29 [Teradata] Support table operators and CTAS COMPRESS
  • 2026-07-29 [Presto] Add MERGE, view security, UNNEST and ident fixes
  • 2026-07-29 [Oracle] Support OFFSET expr and per-INTO LOG ERRORS
  • 2026-07-29 Add generator for docs-site parse-coverage tables
  • 2026-07-28 [Oracle] Restore record-fill lineage with exact bindings

GSP Java version 4.1.9(2026-07-28)

  • 2026-07-28 Apply the trial size cap once, where SQL is read
  • 2026-07-26 Fixed stackover flow bug.
  • 2026-07-26 Fix several CREATE FUNCTION bugs
  • 2026-07-26 Fix getLastSelectStmt returning the wrong SELECT statement
  • 2026-07-26 fix(dlineage): Fix intermediate result retention in stored procedure/function scenarios
  • 2026-07-26 fix(dataflow): Fix parallel analysis losing stored procedure/function lineage
  • 2026-07-26 fix(DataflowUtility): Fix loss of stored procedure short-name mappings
  • 2026-07-26 feat(dataflow): Add procedure variable association and target table marking
  • 2026-07-26 refactor(dataflow): Refactor stored procedure/function name retrieval and improve result set handling
  • 2026-07-25 feat: Add stored procedure ID association logic and improve data flow analysis
  • 2026-07-25 Stop column-level CSV export dropping all lineage on NPE
  • 2026-07-22 Fix schema-topology sqlflow-sharded lineage loading
  • 2026-07-21 [SQL Server] Add TEXTPTR statement lineage
  • 2026-07-21 [SQL Server] Add TEXTPTR statement AST support
  • 2026-07-21 [MSSQL] Enable external-script passthrough by default
  • 2026-07-21 [MSSQL] Add opt-in external-script passthrough lineage
  • 2026-07-21 [MSSQL] Recover external-script input lineage
  • 2026-07-21 [MSSQL] Harden sharded version parse, baseDir, block source (round 3)
  • 2026-07-21 [MSSQL] Close remaining sharded consumer gaps (round 2)
  • 2026-07-21 [MSSQL] Skip sourceUnavailable records instead of parsing their text
  • 2026-07-20 [MSSQL] Let the LR automaton report what the prefix committed to
  • 2026-07-20 [MSSQL] Prove span ownership by token identity, not by text
  • 2026-07-20 [MSSQL] Scan tokens, direct the roles, bind the parse to the site
  • 2026-07-20 [MSSQL] Do not let delimited names trip the structural scan
  • 2026-07-20 [MSSQL] Require structural containment and edge-owned bindings
  • 2026-07-20 [MSSQL] Fix caller-trusted evidence and uncommitted binder regions
  • 2026-07-20 [MSSQL] Close direct-binding trace gaps before gating
  • 2026-07-20 [MSSQL] Require the alias wrapper in binding-trace completeness
  • 2026-07-20 [MSSQL] Add binding-trace capture to resolver2
  • 2026-07-19 [MSSQL] Count CREATE TABLE reparse only when it actually runs
  • 2026-07-19 [MSSQL] Fix CASE re-evaluation blowup and typing gaps in fragment IR
  • 2026-07-19 [MSSQL] Close provenance-laundering paths in fragment IR
  • 2026-07-19 [MSSQL] Add fragment provenance to dynamic-SQL evaluator
  • 2026-07-19 [Oracle] Guard null trigger body in acceptChildren
  • 2026-07-18 [Oracle, MSSQL] Add routine identity + catalog B0
  • 2026-07-18 Guard recovered argument bindings against wildcards
  • 2026-07-18 [MSSQL] Bind nested EXEC arguments under USE context
  • 2026-07-18 [Oracle] Bind PL/SQL package-member call arguments
  • 2026-07-18 [Oracle] Resolve DBMS_SQL.PARSE dynamic SQL
  • 2026-07-18 [MSSQL, Oracle] Index cross-file routines in a shared catalog
  • 2026-07-18 [Oracle] Bind EXECUTE IMMEDIATE via call-site literals
  • 2026-07-18 [MSSQL] Resolve dynamic SQL via cross-file call bindings
  • 2026-07-18 [MSSQL] Resolve dynamic SQL via literal call-site bindings
  • 2026-07-17 [MSSQL] Discover dynamic-SQL sites inside WHILE loops
  • 2026-07-17 [MSSQL] Evaluate template-built dynamic SQL in default lineage path
  • 2026-07-17 [Oracle] Bind trigger correlation names for column lineage
  • 2026-07-17 [Snowflake] Restore extractSnowflakeSQL script callback
  • 2026-07-17 [Snowflake] Skip non-SQL procedure bodies in lineage
  • 2026-07-17 [Redshift, PostgreSQL] Accept ANALYSE spelling of ANALYZE
  • 2026-07-17 [Redshift] Allow PRIOR as a table alias in expressions
  • 2026-07-16 [DuckDB, EDB] Eliminate parser conflicts
  • 2026-07-16 Eliminate parser conflicts in 10 more dialects
  • 2026-07-16 [Oracle, MSSQL, MySQL, PostgreSQL] Eliminate conflicts
  • 2026-07-16 [MSSQL, MySQL, PostgreSQL] Fix parser conflicts
  • 2026-07-16 [BigQuery] Fix all 5 reduce/reduce grammar conflicts
  • 2026-07-15 [Redshift] Support INTERVAL field-qualified types
  • 2026-07-15 [Redshift] Support order-independent COPY QUOTE option
  • 2026-07-15 [Redshift] Support schema-qualified PERCENTILE_DISC/CONT
  • 2026-07-14 [Build] Fix Maven trial artifact metadata (QS-001/002/009)
  • 2026-07-13 [Teradata] Split same-line statement after SET QUERY_BAND
  • 2026-07-13 [Databricks] Support standalone REPLACE TABLE
  • 2026-07-13 [Databricks] Support CTE with the AS keyword omitted
  • 2026-07-13 [Databricks] Support {d } and {t } ODBC date/time escapes
  • 2026-07-13 [Databricks] Support SEMI JOIN and ANTI JOIN
  • 2026-07-13 [Databricks] Support POSITION(substr IN expr) form
  • 2026-07-12 Add identifier normalization guide and guard
  • 2026-07-12 Skip no-op segment parse on identifier hot path
  • 2026-07-12 Reconcile NameService onto the canonical engine
  • 2026-07-12 Refine dlineage identifier comparison scope (P0d.3c2)
  • 2026-07-12 Vendor-aware PL/SQL scope name matching (P0d.3d)
  • 2026-07-12 Canonicalize dlineage wrapper compares (P0d.3c)
  • 2026-07-12 Canonicalize TObjectName table matching (P0d.3b)
  • 2026-07-12 Drop hand-rolled strippers in resolver2 (P0d.3a)
  • 2026-07-12 Unify all identifier equality on the canon (P0d.2)
  • 2026-07-12 Make areEqual canonical-key equality (P0d.1)
  • 2026-07-12 Fold identifiers with Locale.ROOT (P0d.0)
  • 2026-07-12 Convert AST node identifier compares (P0b w4)
  • 2026-07-12 Convert dlineage identifier compares (P0b w3)
  • 2026-07-12 Convert resolver2 qualified-name compares (P0b w2)
  • 2026-07-12 Route safe identifier compares to sameName (P0b w1)
  • 2026-07-12 Add sameName identifier equality facade (P0a)
  • 2026-07-11 Index column-name matching in analyzeDataFlowRelation
  • 2026-07-11 Memoize CTENamespace final-table tracing
  • 2026-07-11 Speed up column lineage with provable memoization
  • 2026-07-10 [Databricks] Support named bind variable markers (:param)
  • 2026-07-05 perf(cloneTable): Optimize table cloning performance and add a parallel-clone threshold
  • 2026-07-04 refactor(sqlutil): Optimize parseNames performance and caching logic
  • 2026-07-02 Fix an array index out-of-bounds bug
  • 2026-06-28 feat(FileSplitter): add structured logging and improve error handling
  • 2026-06-12 Apply GF-029 ruling: set-op row influence is FILTER

GSP Java version 4.1.5.14(2026-07-08)

  • 2026-07-08 [Redshift] Support LIKE/ILIKE ... ESCAPE clause
  • 2026-07-08 [Redshift] Support SUPER object UNPIVOT in FROM clause
  • 2026-07-08 [Redshift] Support BETWEEN in the SELECT list
  • 2026-07-08 [Redshift] Support $$-quoted SQL UDF body with semicolons
  • 2026-07-08 [Redshift] Support IS [NOT] DISTINCT FROM operator
  • 2026-07-08 [Redshift] Support named inline column constraints
  • 2026-07-08 [Redshift] Support UNLOAD EXTENSION AS clause
  • 2026-07-08 [Redshift] Support array unnesting with AT index alias
  • 2026-07-06 Fix constructor escape and routine metadata races
  • 2026-07-06 Fix shared-SQLEnv DDL registration races and NPE

GSP Java version 4.1.5.13(2026-07-06)

  • 2026-07-06 [Sybase] Support empty statements and ON COMMIT ROWS
  • 2026-07-02 [Snowflake] Support GRANT OWNERSHIP COPY CURRENT GRANTS
  • 2026-06-30 [SQL Server] Don't fail unit on partial-fold dynamic-SQL re-parse

GSP Java version 4.1.5.12(2026-06-30)

  • 2026-06-30 [SQL Server] Degrade undecidable-gated dynamic-SQL sites to CONDITIONAL
  • 2026-06-29 [Sybase] Guard create-procedure cast against stale rootNode
  • 2026-06-29 [SQL Server] Fold CAST/CONVERT, degrade unfoldable dynamic SQL to PARTIAL
  • 2026-06-28 [SQL Server] Resolve dynamic-SQL lineage with parameter bindings
  • 2026-06-28 [SQL Server] Expose endpoint classification on dlineage model
  • 2026-06-28 Add sqlflow-sharded JSON file splitting (FileSplitter)
  • 2026-06-28 Add sqlflow-sharded metadata parsing and SQL environment support
  • 2026-06-27 Add owner-name fields to catalog model objects
  • 2026-06-27 Add catalog metadata helpers for sharded scenarios

GSP Java version 4.1.5.10(2026-06-27)

  • 2026-06-26 Expose candidate set for unqualified tables ambiguous across schemas
  • 2026-06-26 [Hive] Resolve unqualified column ambiguous across derived tables
  • 2026-06-26 Honor Option default database over attached TSQLEnv

GSP Java version 4.1.5.9(2026-06-25)

  • 2026-06-25 Resolve ambiguous columns to physical base tables in candidateTables (UNION/EXCEPT/INTERSECT/CTE aware)
  • 2026-06-24 [SQL Server] Fold literal EXEC('') so it yields lineage
  • 2026-06-24 [SQL Server] Emit per-site dynamic-SQL diagnostics and produced-lineage signal
  • 2026-06-23 [Oracle] Improve synonym and dblink data lineage analysis
  • 2026-06-23 Improve synonym support; fix synonym resolution and lineage propagation
  • 2026-06-22 Add catalog metadata fields to dlineage model
  • 2026-06-21 Fix NPE in dataflow Table model

GSP Java version 4.1.5.8(2026-06-21)

  • 2026-06-21 [SQL Server] Add synonym base target, resolution, and export

GSP Java version 4.1.5.7(2026-06-19)

  • 2026-06-19 [Oracle] Add MantisBT 4516 regression for ${} regex literal
  • 2026-06-19 [SQL Server] Accept NULL/NOT NULL on scalar DECLARE
  • 2026-06-19 [SQL Server] Set itSpatial index type on CREATE SPATIAL INDEX
  • 2026-06-18 [Snowflake] Wire scoped temporary into temp-kind consumers
  • 2026-06-18 [Snowflake] Support SCOPED TEMPORARY table
  • 2026-06-18 [Snowflake] Allow colon path access on constants
  • 2026-06-18 [Snowflake] Support CASE cast in SQL UDF body
  • 2026-06-18 [Oracle] Fix CTE alias resolution for repeated references
  • 2026-06-18 [dlineage] Add /csv-simple option to DataFlowAnalyzer
  • 2026-06-18 [Teradata] Parse MEETS/PRECEDES/SUCCEEDS period predicates
  • 2026-06-18 [Snowflake] Add regression tests for TIMESTAMP-typed proc params
  • 2026-06-18 [Snowflake] Flag temporary view in dlineage output
  • 2026-06-18 [Core] Add null checks to several methods to avoid NPE
  • 2026-06-17 [Snowflake] Allow TIMESTAMP as procedure parameter name
  • 2026-06-17 [Teradata] Allow trailing loop label on END WHILE / END REPEAT
  • 2026-06-17 [Snowflake] Add regression test for 4540 PIVOT aliased IN values
  • 2026-06-17 [SQL Server] Add regression test for 4530 ALTER DATABASE in block
  • 2026-06-17 [SQL Server] Add regression test for 4531 GRANT ON TYPE::
  • 2026-06-17 [SQL Server] Allow chained methods on parenthesized expr
  • 2026-06-17 [Snowflake] Gate procedure exclusion on statement type
  • 2026-06-17 [Snowflake] Apply procedure exclusion to SQL procedures

GSP Java version 4.1.5.6(2026-06-15)

  • 2026-06-15 [Dameng] Support CREATE [OR REPLACE] PROCEDURE
  • 2026-06-13 [SQL Server] Add single-quote regression tests for 4496
  • 2026-06-13 [Oracle] Add regression tests for ${} in string literals
  • 2026-06-12 [PostgreSQL] Fix NOT DEFERRABLE token corruption
  • 2026-06-12 [PostgreSQL] Support EXCLUDE WHERE and constraint attributes
  • 2026-06-12 [PostgreSQL] Extend Mantis 4512 regression coverage
  • 2026-06-12 [PostgreSQL] Fix constraint toString truncating clauses
  • 2026-06-12 [PostgreSQL] Fix formatter NPE on ADD CONSTRAINT CHECK

GSP Java version 4.1.5.5(2026-06-11)

  • 2026-06-11 [Snowflake] Allow commas in ALTER DYNAMIC TABLE SET
  • 2026-06-11 [Snowflake] Add dynamic table SCHEDULER attribute
  • 2026-06-11 [OceanBase] Add Q-quote regression test for Oracle mode
  • 2026-06-11 [Dameng] Fix Q-quote lexing and stale keyword values
  • 2026-06-11 [Oracle, OceanBase] Fix Q' inside Q-quote literals
  • 2026-06-11 [Oracle] Add regression test for MantisBT 4508
  • 2026-06-11 [Oracle] Match quoted parameter names in resolution
  • 2026-06-11 [Oracle] Don't report errors for partial dynamic SQL
  • 2026-06-11 [Oracle] Skip unknowable dynamic SQL in lineage
  • 2026-06-10 Link UPDATE target alias to FROM clause table
  • 2026-06-10 Fix lineage crash on expression with quoted dot
  • 2026-06-10 [Snowflake] Support ODBC fn escape syntax
  • 2026-06-10 [Redshift] Add TRIM comma-form regression test
  • 2026-06-10 [Redshift] Allow ANSI TRIM position keyword without char
  • 2026-06-09 [PostgreSQL, Greenplum, Redshift] '/' on a line is division
  • 2026-06-09 [Redshift] Treat '/' alone on a line as division

GSP Java version 4.1.5.4(2026-06-08)

  • 2026-06-08 Mark LATERAL joins lateral in the join graph
  • 2026-06-07 Degrade non-exact ON-predicate binding over join
  • 2026-06-07 Degrade star expansion over nested join
  • 2026-06-07 [Oracle] Fix synonym filtering and complete star-column bridge relations
  • 2026-06-07 Degrade catalog-less star expansion over join
  • 2026-06-06 Recurse into parenthesized joined-table operand
  • 2026-06-06 Degrade non-exact column binding over join graph
  • 2026-06-06 [Oracle, PostgreSQL] Degrade USING-join unqualified column miss
  • 2026-06-06 [Oracle] Support (+) outer-join syntax in implicit joins
  • 2026-06-06 [Oracle] Promote implicit comma-join predicates to INNER joins
  • 2026-06-06 [Oracle, PostgreSQL] Resolve unqualified NATURAL JOIN merged key
  • 2026-06-06 [Formatter] pp2: break statements onto separate lines
  • 2026-06-05 [SQL Server] Fix MERGE USING CTE dropping lineage
  • 2026-06-05 [Teradata] Support PIVOT DEFAULT ON NULL clause
  • 2026-06-05 [BigQuery] Fix bitwise shift vs AND precedence
  • 2026-06-03 [Semantic IR] Expose semi-joins and fix USING clause span
  • 2026-06-03 [Semantic IR] Degrade catalog-less NATURAL JOIN to a warning
  • 2026-06-02 [Impala] Parse PRIMARY KEY / FOREIGN KEY in CREATE TABLE
  • 2026-06-02 [Vertica] Parse AUTO_INCREMENT column attribute
  • 2026-06-02 [DB2] Surface stored-procedure CALL in dlineage call graph
  • 2026-06-02 [Couchbase] Parse ANSI JOIN ... ON
  • 2026-06-02 [SparkSQL] Parse PRIMARY KEY / FOREIGN KEY in CREATE TABLE
  • 2026-06-02 [Informix] Carry column list on table-level PRIMARY KEY
  • 2026-06-02 [DB2] Populate FETCH FIRST n count on TFetchFirstClause
  • 2026-06-02 [Impala] Attach ON condition to LEFT [OUTER] JOIN
  • 2026-06-02 Surface inline column PRIMARY KEY / FOREIGN KEY in dlineage
  • 2026-06-02 [SQL Server] Support CROSS/OUTER APPLY in semantic IR
  • 2026-06-02 [MySQL] Degrade STRAIGHT_JOIN to INNER in semantic IR

GSP Java version 4.1.5.3(2026-06-02)

  • 2026-06-02 [Oracle] Filter invalid coordinates in coordinate handling
  • 2026-06-02 [Oracle] Fix uninitialized coordinateItems update; add synonym + dataflow merge tests
  • 2026-06-01 [Oracle] Support synonym statement parsing and analysis
  • 2026-06-01 Refactor dataflow merge logic and ID handling
  • 2026-05-30 [Semantic IR] Admit PIVOT row-shaping clauses
  • 2026-05-29 [SQL Server] Lock in permanent SELECT INTO target tableKind=etkBase
  • 2026-05-29 [Sybase] Support Watcom IF ... THEN ... END IF in procedure bodies
  • 2026-05-29 [Hive, SparkSQL] Structured-dataflow lineage for explode(from_json(...))
  • 2026-05-29 [Hive] Harden CTE chain walkers
  • 2026-05-29 [Hive] Stop calculated/aliased CTE columns leaking into base linkedColumns
  • 2026-05-28 [Hive] Treat comments as trivia in ALL.col lookahead
  • 2026-05-28 [Hive] Allow ALL as table alias in qualified name
  • 2026-05-28 Admit nested PIVOT/UNPIVOT in a predicate-subquery body
  • 2026-05-28 Admit nested PIVOT/UNPIVOT in a set-op branch
  • 2026-05-28 Admit nested PIVOT/UNPIVOT as a CTE body
  • 2026-05-27 Admit nested PIVOT/UNPIVOT in FROM-subquery body
  • 2026-05-27 Expand bare SELECT * over subquery-source PIVOT/UNPIVOT
  • 2026-05-27 Admit PIVOT/UNPIVOT over a FROM-subquery source
  • 2026-05-27 Expand multi-value UNPIVOT SELECT * via catalog
  • 2026-05-27 Expand bare SELECT * over UNPIVOT via catalog
  • 2026-05-27 Expand bare SELECT * over PIVOT via catalog
  • 2026-05-27 Add multi-value-column UNPIVOT lineage
  • 2026-05-27 Add UNPIVOT output-column lineage
  • 2026-05-27 Add PIVOT output-column lineage
  • 2026-05-27 Bind PIVOT source + capture consumed refs
  • 2026-05-27 Add structured GROUP BY element model
  • 2026-05-27 Capture GROUP BY ROLLUP/CUBE/GROUPING SETS refs
  • 2026-05-27 Validate QUALIFY inline window-fn names
  • 2026-05-26 Lift QUALIFY, emit qualifyColumnRefs
  • 2026-05-26 Unify MERGE OUTPUT USING-source lineage
  • 2026-05-26 Unify RETURNING/OUTPUT subquery lineage
  • 2026-05-26 [PG, MSSQL] MERGE USING-subquery sees outer CTE
  • 2026-05-26 [PG, MSSQL, Snowflake, Oracle] Admit scalar-body WHERE predicate subqueries

GSP Java version 4.1.5.2(2026-05-26)

  • 2026-05-26 [BigQuery] Harden star-column lineage guard
  • 2026-05-25 [BigQuery] Fix UNNEST CTE star lineage crash
  • 2026-05-25 [Teradata] Allow optional alias in MERGE USING VALUES
  • 2026-05-25 [Teradata] Support NUMBER(*) unbounded precision
  • 2026-05-25 [Snowflake] Harden COPY INTO lineage
  • 2026-05-25 [Snowflake] Support COPY INTO inside procedure block
  • 2026-05-25 [Snowflake] Model USING TEMPLATE distinct from CTAS
  • 2026-05-25 [Snowflake] Support CREATE TABLE USING TEMPLATE
  • 2026-05-25 [Hana] Support WITH RANGE_RESTRICTION clause
  • 2026-05-25 [Oracle] Resolve PL/SQL params in dynamic SQL, not orphan columns
  • 2026-05-25 Centralize re-parse coordinate shifting
  • 2026-05-24 Harden EXECUTE IMMEDIATE coordinate shift for nested statements
  • 2026-05-24 Fix wrong line numbers for function/procedure body coordinates
  • 2026-05-24 [Oracle] Fix wrong line numbers for dynamic SQL coordinates
  • 2026-05-24 [PG, MSSQL, Snowflake] Admit FROM-subquery body WHERE predicate subqueries
  • 2026-05-24 [PG, MSSQL, Snowflake, Oracle] Admit UPDATE SET-RHS mixed-expression scalar subqueries
  • 2026-05-23 [PG, Oracle] Admit MERGE per-WHEN action WHERE correlated predicate subqueries
  • 2026-05-23 [PG, MSSQL, Snowflake, Oracle, MySQL] Admit UPDATE SET-RHS correlated scalar subqueries
  • 2026-05-23 Handle null DataFlow gracefully in parallel analysis
  • 2026-05-23 Thread-safe CatalogStore
  • 2026-05-23 [PG, Oracle] Admit uncorrelated MERGE per-WHEN action WHERE predicate subqueries
  • 2026-05-23 [PG, MSSQL, Snowflake, Oracle, MySQL] Admit uncorrelated UPDATE SET-RHS scalar subqueries
  • 2026-05-23 [PG, MSSQL, Snowflake] Admit uncorrelated WHERE-side predicate subqueries on CTE bodies
  • 2026-05-22 [PG, MSSQL, Snowflake] Admit uncorrelated WHERE-side predicate subqueries on set-op branches
  • 2026-05-22 [PG, MSSQL, Snowflake] Admit uncorrelated WHERE-side predicate subqueries on SELECT
  • 2026-05-22 [PG, MSSQL, Snowflake] Admit uncorrelated WHERE-side predicate subqueries on DELETE
  • 2026-05-22 [PG, MSSQL, Snowflake] Admit uncorrelated WHERE-side predicate subqueries on UPDATE
  • 2026-05-22 [Hive] Admit outer-WITH on Hive multi-insert
  • 2026-05-22 [PG, Oracle, Snowflake] Admit mixed outer+inner WITH on INSERT (shadowing case)
  • 2026-05-22 [PG, Oracle, Snowflake] Admit mixed outer+inner WITH on INSERT (disjoint names)
  • 2026-05-21 [SQL Server] Split plain BEGIN block from preceding statement
  • 2026-05-19 [Snowflake] Preserve closing quote after even backslash run
  • 2026-05-19 [Snowflake] Fix MantisBT 4435 sequential backslash-escaped quotes
  • 2026-05-19 [Teradata, Snowflake] Pin MantisBT 4440 join-with-comments behavior
  • 2026-05-19 [Sybase] Hook EXCEPTION clause into TBlockSqlNode + TMssqlBlock
  • 2026-05-19 [Sybase] Support Watcom EXCEPTION handler + SIGNAL/RESIGNAL
  • 2026-05-18 [Snowflake] Support LAG=DOWNSTREAM in CREATE DYNAMIC TABLE

GSP Java version 4.1.5.1(2026-05-18)

  • 2026-05-18 [PG, MSSQL, Snowflake, Redshift] Admit outer WITH on DELETE
  • 2026-05-18 [PG, MSSQL, Snowflake, Redshift] Admit outer WITH on UPDATE
  • 2026-05-18 [PG, MSSQL, Oracle, Snowflake, Redshift, Hive] Admit outer WITH on INSERT
  • 2026-05-18 [Catalog] Stabilize concurrentSnapshotReadsDoNotThrow test
  • 2026-05-18 [SqlGuard] Add bin/probe.sh inner-loop wrapper for the worker
  • 2026-05-17 [PG, MSSQL] Admit WITH cte(a,b) SELECT rename
  • 2026-05-17 [PG, MSSQL] Admit WITH cte(a,b) MERGE rename
  • 2026-05-17 [PG, MSSQL] Admit WITH-clause / CTE on MERGE
  • 2026-05-17 [MSSQL] OUTPUT pseudo-table star expansion (MERGE and non-MERGE)
  • 2026-05-17 [MSSQL] MERGE OUTPUT projection
  • 2026-05-17 [MSSQL, BigQuery] MERGE WHEN NOT MATCHED BY SOURCE
  • 2026-05-17 [PostgreSQL, DuckDB] MERGE WHEN MATCHED THEN DO NOTHING (PG 15+)
  • 2026-05-17 MERGE per-WHEN action WHERE predicates
  • 2026-05-17 [Teradata] Allow STORAGE FORMAT after JSON INLINE LENGTH (MantisBT 4438)
  • 2026-05-17 [SqlGuard] Worker startup + smoke scripts; default port 18083
  • 2026-05-16 MERGE skeleton + Semantic IR semantics for MERGE
  • 2026-05-16 [Teradata] Support INLINE LENGTH on JSON(n) datatypes (MantisBT 4438)
  • 2026-05-16 [Hive] Fix CROSS JOIN + other JOIN associativity (MantisBT 4436)
  • 2026-05-16 [Teradata] Add ALTER TABLE ... ALTER (column action[, ...]) support
  • 2026-05-16 [MySQL] Self-reference DELETE admission
  • 2026-05-16 [Hive] Fix LATERAL VIEW with 3+ column aliases (MantisBT 4455)
  • 2026-05-16 [Oracle] MODIFY SUBPARTITION exposes partition name (MantisBT 4459)
  • 2026-05-16 [Snowflake] DELETE USING full-contract tests
  • 2026-05-16 RETURNING /t. catalog-backed expansion
  • 2026-05-16 [Oracle] Fix MODIFY col DECRYPT/VISIBLE/INVISIBLE wrongly sets datatype
  • 2026-05-15 Fix alias incorrectly filtered bug (Gitee IJNQ17)
  • 2026-05-15 [BigQuery] Fix NPE in DataFlowAnalyzer for EXCEPT/REPLACE with aggregates
  • 2026-05-14 [Oracle] Admit RETURNING INTO; Resolver2 sees RETURNING refs
  • 2026-05-14 Case-insensitive alias lookup in lineage emitter
  • 2026-05-14 [MySQL] USING/NATURAL joined UPDATE
  • 2026-05-14 RETURNING / OUTPUT projections
  • 2026-05-14 [MySQL] Joined DELETE
  • 2026-05-13 UPDATE FROM subquery (Semantic IR)
  • 2026-05-13 Joined UPDATE / single-target DELETE / UPDATE shapes (Semantic IR)
  • 2026-05-13 CTAS / CREATE VIEW Semantic IR skeleton
  • 2026-05-13 INSERT INTO target SELECT (Semantic IR)
  • 2026-05-13 Catalog-miss WARN + corpus parity; minimal catalog DTO; SqlSemanticAnalyzer API
  • 2026-05-13 Anonymous FROM-subquery admit (Semantic IR)
  • 2026-05-13 [PostgreSQL] DISTINCT ON column refs (Semantic IR)
  • 2026-05-13 Set-op outer row-limit + vendor row-limit metadata
  • 2026-05-12 [Sybase] Allow SQL SECURITY in CREATE FUNCTION
  • 2026-05-12 Single-SELECT LIMIT / FETCH FIRST (Semantic IR)
  • 2026-05-12 Single-SELECT ORDER BY projection alias / ordinal (Semantic IR)
  • 2026-05-12 Structured diagnostics in Semantic IR
  • 2026-05-12 NATURAL JOIN / JOIN USING / CROSS JOIN (Semantic IR)
  • 2026-05-11 Constant-only projection lift; CTE/FROM-subquery star
  • 2026-05-10 GSP binding name resolution rewrite (case-insensitive, catalog-verified)
  • 2026-05-10 [BigQuery] Regen dataflow fixtures for case-insensitive resolution
  • 2026-05-08 [Vertica] Support AND filter and WHERE in MERGE clauses

GSP Java version 4.1.1.0(2026-05-08)

  • 2026-05-07 Fix DataFlowAnalyzer ClassCastException (TObjectName cannot be cast to TResultColumn)
  • 2026-05-06 Add dlineage tests for Oracle PL/SQL procedures (Gitee IJLBB7, IJLB9E)
  • 2026-05-02 Add procedure include/exclude filter for data flow analysis (Gitee IJJX1M)
  • 2026-05-02 [BigQuery] Strengthen tests for func(args).* STRUCT star expansion (Mantis #4426)
  • 2026-05-01 [BigQuery] Support func(args).* star expansion on STRUCT return
  • 2026-05-01 [Teradata] Fix NPE on USING ... ABORT BTEQ statements

GSP Java version 4.1.0.15(2026-05-01)

  • 2026-05-01 [BigQuery] Fix CTE self-reference resolution in ScopeBuilder (Mantis #4412)
  • 2026-05-01 [Snowflake] Populate stageLocation for quoted COPY INTO source
  • 2026-05-01 [Redshift] Support UNION DISTINCT syntax
  • 2026-05-01 [Redshift] Support bare postfix NOTNULL operator
  • 2026-04-28 Harden MERGE formatter (codex review follow-ups)
  • 2026-04-27 Fix MERGE formatter: USING indent and WHEN action layout
  • 2026-04-21 [Hive] Add regression tests for ALTER TABLE CHANGE ... ENABLE
  • 2026-04-21 [Teradata] Fix SET with subquery RHS spanning multiple lines
  • 2026-04-21 Fix TFunctionCall isBuiltIn array index out of bounds
  • 2026-04-20 [DAX, PowerQuery] Fix DAX label and promote PowerQuery tier
  • 2026-04-20 [PowerQuery] Wire lineage into DataFlowAnalyzer
  • 2026-04-20 [PowerQuery] Infer vendor from inline connector calls
  • 2026-04-20 [Snowflake] Support GROUP BY ALL syntax and lineage
  • 2026-04-19 [PowerQuery] Add parser, AST and lineage analyzer
  • 2026-04-19 [BigQuery] Add column-level lineage for array_agg(table_alias)
  • 2026-04-18 [Oracle] Add XML column-level data lineage for XML functions and XMLTABLE
  • 2026-04-18 [MSSQL] Support WRITETEXT inside stored procedures
  • 2026-04-18 [Oracle] Support ARRAY as synonym for VARRAY in CREATE TYPE
  • 2026-04-18 [Teradata] Support IF/WHILE/LOOP inside CASE stmt
  • 2026-04-18 [PostgreSQL] Correct toScript() for LIMIT/OFFSET clauses
  • 2026-04-18 [DB2] Support IMPLICITLY HIDDEN column attribute
  • 2026-04-18 [Oracle] Support PARALLEL/NOPARALLEL in CTAS
  • 2026-04-18 [Oracle] Trace pipelined function lineage through CTEs
  • 2026-04-18 [Teradata] Support USING source-table MERGE prefix
  • 2026-04-18 [Teradata] Support parenthesized type in USING var defs
  • 2026-04-18 [Teradata] Support temporal qualifier DML
  • 2026-04-18 [MSSQL] Support 3/4-part column names in INSERT column list

GSP Java version 4.1.0.12(2026-04-17)

  • 2026-04-16 [Databricks] Accept bare time units in TIMESTAMPDIFF
  • 2026-04-14 [OceanBase] Support SWITCH REPLICA LEADER SERVER/TENANT options
  • 2026-04-14 [OceanBase] Support ALTER SYSTEM ADD ZONE IDC/ZONE_TYPE options
  • 2026-04-14 [OceanBase] Add XA transaction support in MySQL mode
  • 2026-04-14 [OceanBase] Add CREATE/DROP RESTORE POINT support
  • 2026-04-14 [OceanBase] Add CREATE/DROP DBLINK support
  • 2026-04-14 [OceanBase] Add CREATE TABLEGROUP PARTITION BY KEY N and LIST COLUMNS N
  • 2026-04-14 [OceanBase] Add ALTER TABLEGROUP partition operations
  • 2026-04-14 [OceanBase] Add ALTER SYSTEM CANCEL BACKUP operations
  • 2026-04-14 [OceanBase] Add ALTER RESOURCE POOL SPLIT and MERGE operations
  • 2026-04-14 [Dameng] Support CREATE [OR REPLACE] PACKAGE BODY
  • 2026-04-14 [Dameng] Support CREATE [OR REPLACE] PACKAGE specification
  • 2026-04-14 [Dameng] Add TIMER (scheduled) triggers on DATABASE
  • 2026-04-14 [Dameng] Add system-event and DDL-event triggers ON DATABASE
  • 2026-04-14 [Dameng] Add INSTEAD OF trigger on view
  • 2026-04-14 [Dameng] Add combined-event triggers with IF/ELSIF/ELSE and EXCEPTION decl
  • 2026-04-14 [Dameng] Add basic DML CREATE TRIGGER support
  • 2026-04-13 [OceanBase] Fix statement splitter for RELEASE SAVEPOINT, PURGE INDEX/TENANT, CREATE/DROP TABLESPACE
  • 2026-04-13 [OceanBase] Fix statement splitter for ALTER USER, CREATE/DROP ROLE in MySQL mode
  • 2026-04-13 [OceanBase] Fix CREATE OR REPLACE OUTLINE splitter in both MySQL and Oracle modes
  • 2026-04-13 [OceanBase] Add Oracle mode TABLEGROUP PARTITION, ALTER TABLEGROUP ADD TABLE, ALTER TABLE SET TABLEGROUP
  • 2026-04-13 [OceanBase] Add MySQL mode TABLEGROUP RANGE COLUMNS N and SUBPARTITION support
  • 2026-04-13 [OceanBase] Add FLASHBACK TENANT and FLASHBACK RENAME TO support
  • 2026-04-13 [OceanBase] Add CREATE/ALTER RESOURCE UNIT space-separated parameter syntax
  • 2026-04-13 [OceanBase] Add CREATE INDEX IF NOT EXISTS support in MySQL mode
  • 2026-04-13 [OceanBase] Add ALTER TENANT extended options in system tenant mode
  • 2026-04-13 [OceanBase] Add ALTER TABLE SET OceanBase-specific options in MySQL mode
  • 2026-04-13 [OceanBase] Add ALTER SYSTEM server/zone management ZONE= and REGION= clauses
  • 2026-04-13 [OceanBase] Add ALTER SYSTEM advanced operations (SWITCH REPLICA, BOOTSTRAP, RECOVERY, RESTORE)
  • 2026-04-13 [OceanBase] Add ALTER SYSTEM SET COMMENT clause and FLUSH PLAN CACHE extensions
  • 2026-04-13 [Dameng] Support PRINT statement in PL/SQL blocks
  • 2026-04-13 [Dameng] Support PL/SQL variable assignment (:=) in block body
  • 2026-04-13 [Dameng] Support DROP TYPE and DROP TYPE BODY with IF EXISTS
  • 2026-04-13 [Dameng] Support DECLARE anonymous PL/SQL blocks
  • 2026-04-13 [Dameng] Support CREATE/ALTER/DROP CLASS (Dameng proprietary)
  • 2026-04-13 [Dameng] Support CREATE TYPE BODY with method implementations
  • 2026-04-13 [Dameng] Support CREATE Type AS VARRAY and TABLE OF (collection types)
  • 2026-04-13 [Dameng] Support CREATE TYPE AS OBJECT with member functions
  • 2026-04-13 [Dameng] Support CREATE FUNCTION with PL/SQL body (BEGIN/END)
  • 2026-04-13 [Dameng] Support CREATE FUNCTION ... AGGREGATE USING
  • 2026-04-13 [Dameng] Support // single-line comments in Dameng lexer
  • 2026-04-12 [OceanBase] Add SHOW extensions (TENANTS, RECYCLEBIN, TABLEGROUPS, PARAMETERS, TENANT STATUS)
  • 2026-04-12 [OceanBase] Add SHOW TENANT / SHOW RESOURCE POOL / SHOW CREATE TENANT
  • 2026-04-12 [OceanBase] Add SELECT FOR UPDATE WAIT N support
  • 2026-04-12 [OceanBase] Add MERGE INTO statement support
  • 2026-04-12 [OceanBase] Add FLASHBACK and PURGE statement support
  • 2026-04-12 [OceanBase] Add EXPLAIN statement support
  • 2026-04-12 [OceanBase] Add ALTER TABLEGROUP ADD TABLE and ALTER TABLE SET TABLEGROUP
  • 2026-04-12 [OceanBase] Add ALTER SYSTEM subcommands (FLUSH, MINOR FREEZE, FREEZE TENANT)
  • 2026-04-12 [OceanBase] Add ALTER SYSTEM low-priority subcommands
  • 2026-04-12 [OceanBase] Add ALTER SYSTEM SET extended clauses (SCOPE, TENANT, COMMENT)
  • 2026-04-12 [OceanBase] Add ALTER SEQUENCE support
  • 2026-04-12 [OceanBase] Add checksyntax OceanBase tenant-mode vendor aliases
  • 2026-04-12 [OceanBase] CREATE TENANT with space-separated SET clause
  • 2026-04-12 [OceanBase] CREATE TABLEGROUP with trailing partition_clause
  • 2026-04-12 [Dameng] Support flashback query clause + trailing alias
  • 2026-04-12 [Dameng] Support bare parameterless package call (PKG.PROC;)
  • 2026-04-12 [Dameng] Support WHEN TIMESTAMP flashback query clause
  • 2026-04-12 [Dameng] Support DROP TABLE ... CASCADE (PostgreSQL-style)
  • 2026-04-12 [Dameng] Support CREATE HUGE TABLE syntax
  • 2026-04-12 [Dameng] Support AS OF LSN and VERSIONS BETWEEN LSN flashback
  • 2026-04-12 [Dameng] Support ALTER TABLE WITH/WITHOUT ADVANCED LOG
  • 2026-04-12 [Dameng] Support ALTER TABLE TRUNCATE ADVANCED LOG
  • 2026-04-11 [Dameng] Support zero-argument pseudo-function calls
  • 2026-04-11 [Dameng] Support uppercase hex literals (0xFFFEE)
  • 2026-04-11 [Dameng] Support multi-row VALUES clause in INSERT
  • 2026-04-11 [Dameng] Support TIME and DATETIME typed datetime literals
  • 2026-04-11 [Dameng] Support SQL-standard SUBSTRING and OVERLAY
  • 2026-04-11 [Dameng] Support REFRESH MATERIALIZED VIEW statement
  • 2026-04-11 [Dameng] Support IDENTITY + CLUSTER PRIMARY KEY and STORAGE clause
  • 2026-04-11 [Dameng] Support DROP VIEW with RESTRICT/CASCADE
  • 2026-04-11 [Dameng] Support DROP SCHEMA CASCADE
  • 2026-04-11 [Dameng] Support DROP MATERIALIZED VIEW [IF EXISTS]
  • 2026-04-11 [Dameng] Support DROP MATERIALIZED VIEW LOG [IF EXISTS] ON
  • 2026-04-11 [Dameng] Support DATE() as a conversion function
  • 2026-04-11 [Dameng] Support CREATE VIEW WITH LOCAL/CASCADED CHECK OPTION
  • 2026-04-11 [Dameng] Support CREATE MV LOG PURGE options
  • 2026-04-11 [Dameng] Support CREATE MV FOR ON PREBUILT TABLE
  • 2026-04-11 [Dameng] Support CONNECT BY LEVEL without FROM in SELECT
  • 2026-04-11 [Dameng] Support ALTER MV ENABLE/DISABLE QUERY REWRITE and COMPILE
  • 2026-04-11 [Dameng] Parse CREATE LIBRARY libname AS 'path'
  • 2026-04-11 [Dameng] Parse CREATE FUNCTION IF NOT EXISTS external bodies
  • 2026-04-11 [Dameng] Parse CREATE FUNCTION ... EXTERNAL body
  • 2026-04-11 [Dameng] Parse AS LANGUAGE C|CS LIBRARY function body
  • 2026-04-11 [Dameng] Parse two-argument XMLQUERY form
  • 2026-04-11 [Dameng] Parse bare top-level function-call statement
  • 2026-04-11 [Dameng] Parse SELECT ... FOR XML PATH clause
  • 2026-04-11 [Dameng] Add Dameng database dialect support
  • 2026-04-09 [Teradata] Fix SHOW TABLE statement recognition
  • 2026-04-09 [Teradata] Store first argument of QUANTILE function
  • 2026-04-09 [Teradata] Recognize BEGIN REQUEST / END REQUEST block
  • 2026-04-09 [PostgreSQL] Fix COMMENT ON PROCEDURE with empty parens
  • 2026-04-09 [MSSQL] Fix ALTER DATABASE inside procedure body
  • 2026-04-09 [BigQuery] Support FORMAT('literal') in CAST clause
  • 2026-04-09 [Oracle] Add pipelined table function lineage stitching
  • 2026-03-12 [PostgreSQL] Fix ClassCastException in TSequenceOption
  • 2026-03-12 [Oracle] Fix OOM in TSqlplusCmdStatement.extractCommandText
  • 2026-03-12 [MySQL] Fix tokenize error for backslash delimiter
  • 2026-03-10 [Snowflake] Fix escaped quotes unescaping in proc body
  • 2026-03-10 [Oracle] Fix &variable. substitution with trailing dot
  • 2026-03-10 [MySQL] Fix NPE when FK REFERENCES has no column list
  • 2026-03-10 [MSSQL] Fix SELECT not split from CREATE VIEW body
  • 2026-03-10 [MSSQL] Fix NPE in EXECUTE sp_executesql without args
  • 2026-03-09 [PostgreSQL] Fix ClassCastException for SEQUENCE NAME option
  • 2026-03-08 [Snowflake] Fix procedure with $$ body blocking subsequent statements
  • 2026-03-08 [Snowflake] Fix GRANT/REVOKE after procedure body not splitting
  • 2026-03-08 [MSSQL] Fix splitter incorrectly splitting proc on END; CREATE TABLE
  • 2026-03-07 [MySQL] Fix trailing tokens after conditional comments
  • 2026-03-07 [MySQL] Fix tokenizer error on multi-dash comments
  • 2026-03-07 [MySQL] Fix tokenizer error on dash-separator comments
  • 2026-03-07 [Hive] Fix statement splitter for SET after semicolon-less statements
  • 2026-03-07 [Hive] Fix statement splitter for SELECT after semicolon-less SELECT

GSP Java version 4.1.0.11(2026-04-08)

  • 2026-04-08 [Snowflake] Support LIKE ANY/ALL/SOME with pattern list or expr
  • 2026-04-08 [Hive] Accept backtick alias with dot after subquery
  • 2026-04-08 [DB2] Support CREATE PROCEDURE with single inline DML body
  • 2026-04-07 [PostgreSQL, Snowflake] Fix regressions from #3672 and #3491
  • 2026-04-07 [BigQuery] Allow digit-leading identifiers in qualified names
  • 2026-04-07 [Snowflake] Support * REPLACE, # comments and nameless masking policy
  • 2026-04-07 [Redshift] Allow LIKE/ILIKE/SIMILAR TO in SELECT list
  • 2026-04-07 [PostgreSQL] Support TRIM(LEADING|TRAILING|BOTH FROM expr, expr)
  • 2026-04-07 [PostgreSQL] Fix toScript emitting MySQL LIMIT for non-MySQL dialects
  • 2026-04-07 [Oracle] Allow CREATE TYPE AS (attrs) without OBJECT keyword
  • 2026-04-07 [Snowflake] Support DESC SCHEMA, SHOW TERSE ROLES, SHOW DATABASES IN ACCOUNT
  • 2026-04-07 [Teradata] Allow DIAGNOSTIC ... ON FOR SESSION inside macro body
  • 2026-04-07 [Oracle] Do not inherit implicit schema for db_link tables
  • 2026-04-07 [MSSQL] Populate cursorName for OPEN/CLOSE cursor stmts
  • 2026-04-07 [Teradata] Support RESPECT NULLS on window functions
  • 2026-04-07 [Teradata] Allow alias on multi-column PIVOT IN tuple
  • 2026-04-07 Set dbObjectType on cloneSourceTable to populate parts
  • 2026-04-07 [Oracle] Allow SUBPARTITIONS n in composite partition clause
  • 2026-04-07 [Teradata] Allow LAST_VALUE/FIRST_VALUE as column names
  • 2026-04-07 [Oracle] Add composite LIST+subpartition rule
  • 2026-04-07 [Snowflake] Treat \' as escape + close quote in string literals
  • 2026-04-07 [MSSQL] Add DROP PARTITION FUNCTION, ALTER SEQUENCE, fix ALTER INDEX SET splitter
  • 2026-04-07 [Oracle] Dispatch CREATE CLUSTER to TOracleCreateClusterStmt
  • 2026-04-07 [PostgreSQL] Map uuid type to EDataType.uuid_t
  • 2026-04-07 [MSSQL] UPDATE alias resolution + trigger splitter follow-up
  • 2026-04-07 [MSSQL] Distinguish WHILE from IF in sqlstatementtype
  • 2026-04-07 [MSSQL] Add TRY_CONVERT with typename exposed via getTypename()
  • 2026-04-07 [MSSQL] Preserve ALTER statement type for procedure/function/trigger
  • 2026-04-06 [Redshift] Register ELSIF keyword in lexer for PL/pgSQL
  • 2026-04-06 [GaussDB] Support EXECUTE DIRECT ON statement
  • 2026-04-06 [GaussDB] Support SET LOCAL TRANSACTION and CREATE DIRECTORY
  • 2026-04-06 [MSSQL] Expose CHECK/NOCHECK CONSTRAINT in AST
  • 2026-04-06 [Teradata] Support angle-bracket identifiers in BTEQ
  • 2026-04-05 [Snowflake] Expose new stage name in ALTER STAGE RENAME TO
  • 2026-04-05 [Snowflake] Expose stage properties in ALTER STAGE
  • 2026-04-05 [Teradata] Support CREATE OR REPLACE syntax
  • 2026-04-05 [Teradata] Support @Parameter in macro body
  • 2026-04-05 [Vertica] Parse AS SELECT subquery in CREATE PROJECTION
  • 2026-04-05 [BigQuery] Fix incorrect target column name for ARRAY[scalar_function()]
  • 2026-04-05 [Teradata] Support qualified column names in COLLECT STATISTICS COLUMN()
  • 2026-04-05 Fix thread-safety issues in multi-threaded parsing
  • 2026-04-05 [MDX] Add data lineage support for MDX queries
  • 2026-04-04 [Teradata] Fix formatter for REPLACE VIEW with LOCKING
  • 2026-04-04 [Redshift] Wire EXCLUDE clause to TExceptReplaceClause
  • 2026-04-04 Add getTableKind() to TIntoClause for SELECT INTO

GSP Java version 4.1.0.10(2026-04-04)

  • 2026-04-03 [Databricks] Add ODBC escape syntax support
  • 2026-04-02 [Oracle] Fix CASE WHEN direct/indirect dataflow handling in DataFlowAnalyzer
  • 2026-04-01 [Teradata] Fix dataflow analysis for complex queries
  • 2026-03-31 [Teradata, Hive] Set schemaToken in parseTablename
  • 2026-03-31 [DuckDB] Comprehensive DuckDB dialect support: PIVOT/UNPIVOT, MACRO, SECRET, lambda expressions, list comprehension, struct/map/union types, QUALIFY, EXCLUDE/REPLACE on star, POSITIONAL/ASOF JOIN, FROM-first syntax, USING SAMPLE, and 60+ additional syntax features

GSP Java version 4.1.0.9(2026-03-23)

  • 2026-03-23 [MSSQL] Fix $action column order in MERGE OUTPUT clause
  • 2026-03-23 [MSSQL] Resolve MERGE OUTPUT inserted/deleted to MERGE target
  • 2026-03-22 [EDB] Comprehensive EDB Postgres SPL support (triggers, types, collections, cursors, exceptions, loops, bulk operations)
  • 2026-03-20 [BigQuery] Fix star column dedup in resolver
  • 2026-03-20 [MSSQL] Preserve star column relationships with ignoreCoordinate
  • 2026-03-19 [Teradata] Unify GRANT privilege types
  • 2026-03-19 [Redshift] Add interval unit keywords and AT TIME ZONE support
  • 2026-03-19 [Redshift] Add GROUPING SETS, ROLLUP, CUBE support
  • 2026-03-18 [Snowflake] Add TEMP/TEMPORARY FUNCTION with $$ body
  • 2026-03-17 [MSSQL, Snowflake] Respect vendor flags in identifier rules
  • 2026-03-17 [Oracle, Snowflake, BigQuery] Prevent AIOOB in statement splitter
  • 2026-03-17 Add builder pattern for custom IdentifierRules injection
  • 2026-03-16 Optimize core parse path performance
  • 2026-03-15 [Netezza] Fix AexprConst precedence and trim whitespace
  • 2026-03-15 Eliminate parser S/R and R/R conflicts across all vendors
  • 2026-03-14 Allow unreserved keywords as column names for all vendors
  • 2026-03-14 [Redshift] Allow AWS-unreserved keywords as column names

GSP Java version 4.1.0.8(2026-03-13)

  • 2026-03-14 [Redshift] Allow LISTAGG, CONVERT, APPROXIMATE, CONNECT_BY_ROOT, CHARACTER, DOUBLE as unquoted column names
  • 2026-03-13 [MySQL] Fix tokenize error for backslash delimiter
  • 2026-03-13 [Hive] Add CREATE PROCEDURE support
  • 2026-03-13 [Greenplum, Snowflake] Fix syntax parsing issues
  • 2026-03-13 [Oracle] Fix OOM in TSqlplusCmdStatement.extractCommandText

GSP Java version 4.1.0.7(2026-03-12)

  • 2026-03-12 [BigQuery, PostgreSQL, MSSQL, MySQL, Greenplum] Fix syntax issues across 5 SQL dialects
  • 2026-03-12 [BigQuery] Support PIVOT/UNPIVOT in JOIN context
  • 2026-03-11 [PostgreSQL] Support RETURN as top-level statement
  • 2026-03-11 [PostgreSQL] Fix ClassCastException in TSequenceOption
  • 2026-03-11 [Snowflake] Support {var} template variables in identifiers
  • 2026-03-11 [Oracle] Split multi-statement files with CTE after SELECT
  • 2026-03-11 [Oracle] Support $P{} and $X{} template variables
  • 2026-03-11 [Oracle] Support MATCH_RECOGNIZE after aliased table
  • 2026-03-11 [MSSQL] Fix NPE in EXECUTE sp_executesql without args
  • 2026-03-11 [MySQL] Fix NPE when FK REFERENCES has no column list
  • 2026-03-10 [Snowflake] Support {{ variable }} template syntax
  • 2026-03-10 [Oracle] Fix &variable. substitution with trailing dot
  • 2026-03-10 [Redshift] Support CREATE PROCEDURE without args
  • 2026-03-10 [MSSQL] Fix SELECT not split from CREATE VIEW body
  • 2026-03-10 [DB2] Support {{ variable }} template syntax
  • 2026-03-10 [Oracle] Support ${variable} template syntax
  • 2026-03-10 [MySQL] Accept -- comments without trailing space
  • 2026-03-10 [BigQuery] Support ${variable} template syntax
  • 2026-03-10 [Snowflake] Handle \' escaped quotes in SQL input
  • 2026-03-10 [Snowflake] Fix \'' in direct expression context
  • 2026-03-10 [Snowflake] Fix \'' unescaping in single-quoted proc body
  • 2026-03-10 [MSSQL] Split CTE without semicolon after DML
  • 2026-03-09 [MySQL] Support ON as value in SET variable assignment
  • 2026-03-09 [PostgreSQL] Support PL/pgSQL PERFORM FROM, ELSEIF, RETURN NEXT
  • 2026-03-09 [PostgreSQL] Fix ClassCastException for SEQUENCE NAME option
  • 2026-03-09 [Snowflake] Support ${variable} and backtick-quoted identifiers
  • 2026-03-09 [Redshift] Split DDL statements without semicolons
  • 2026-03-09 [Oracle] Handle forward slash delimiter on same line as semicolon
  • 2026-03-09 [Snowflake] Split SET statements without semicolons
  • 2026-03-09 [MySQL] Support DEFAULT before both CHARSET and COLLATE
  • 2026-03-09 [PostgreSQL] Support TYPE as name in column alias lists
  • 2026-03-09 [Snowflake] Split DDL statements without semicolons

GSP Java version 4.1.0.6(2026-03-09)

  • 2026-03-09 [MySQL] Support CREATE/DROP SEQUENCE statements
  • 2026-03-08 [Hive] Support ${variable} template substitution syntax
  • 2026-03-08 [PostgreSQL] Support COMMENT ON FUNCTION/PROCEDURE without args
  • 2026-03-08 [MSSQL] Fix splitter incorrectly splitting proc on END; CREATE TABLE
  • 2026-03-08 [MSSQL] Support bracket-quoted XML method names
  • 2026-03-08 [MSSQL] Support WITH XMLNAMESPACES combined with CTEs for all DML
  • 2026-03-07 [Snowflake] Fix GRANT/REVOKE after procedure body not splitting
  • 2026-03-07 [MySQL] Support function calls in CHECK constraints
  • 2026-03-07 [Snowflake] Fix procedure with $$ body blocking subsequent statements
  • 2026-03-07 [Snowflake] Add CREATE OR ALTER DYNAMIC TABLE to splitter
  • 2026-03-07 [Snowflake] Support standalone scripting statements
  • 2026-03-07 [Snowflake] Handle standalone scripting blocks (WHILE/FOR/LOOP/IF)
  • 2026-03-07 [PostgreSQL] Handle psql meta-commands and RETURNS TABLE LANGUAGE
  • 2026-03-07 [PostgreSQL] Add CASCADE and VERSION options to CREATE EXTENSION
  • 2026-03-07 [Hive] Fix statement splitter for SELECT after semicolon-less SELECT
  • 2026-03-07 [Hive] Fix statement splitter for SET after semicolon-less statements
  • 2026-03-07 [MySQL] Fix tokenizer error on dash-separator comments
  • 2026-03-07 [PostgreSQL] Allow CREATE TYPE inside DO blocks
  • 2026-03-06 [MySQL] Fix tokenizer error on multi-dash comments
  • 2026-03-06 [Snowflake] Support CREATE SEARCH OPTIMIZATION statement
  • 2026-03-06 [MySQL] Fix trailing tokens after conditional comments
  • 2026-03-06 Support TAlterViewStatement
  • 2026-03-06 [Teradata] Support GRANT LOGON syntax

GSP Java version 4.1.0.5(2026-03-06)

  • 2026-03-06 [Teradata] Support abbreviated GRANT privileges
  • 2026-03-06 [Vertica] Support CREATE FLEX TABLE syntax
  • 2026-03-06 [Teradata] Support USER, DATABASE, CREATE USER as GRANT privileges
  • 2026-03-06 [Teradata] Support TABLE, VIEW, MACRO as GRANT privileges
  • 2026-03-06 [Snowflake] Populate stage location from COPY INTO subquery
  • 2026-03-06 [Redshift] Support IGNORE/RESPECT NULLS in both positions
  • 2026-03-06 Add comprehensive TAttributeNode deprecated API inventory
  • 2026-03-06 Support SQL query statistics
  • 2026-03-06 Add large SQL file splitter tool
  • 2026-03-06 Fixed NPE in logger error handling
  • 2026-03-05 [Oracle] Improve call graph for parameterless calls and trigger tables
  • 2026-03-05 [Oracle] Fix forward declaration causing AMBIGUOUS resolution
  • 2026-03-05 [Oracle] Model package body initializer as synthetic routine
  • 2026-03-05 [Oracle] Classify built-in functions as external deps
  • 2026-03-05 [Oracle] Detect calls from enclosing routine to nested functions
  • 2026-03-05 [Oracle] Detect function calls in TABLE(func) without arguments
  • 2026-03-05 [Oracle] Detect parameterless function calls in all contexts
  • 2026-03-05 [Oracle] Detect calls inside EXCEPTION handler blocks
  • 2026-03-05 [Oracle] Detect constructor calls inside PIPE ROW statements
  • 2026-03-05 [Oracle] Filter boolean literals from call graph edges
  • 2026-03-05 [Oracle] Detect function calls in PL/SQL declarative sections
  • 2026-03-05 [Oracle] Traverse compound trigger timing section bodies
  • 2026-03-05 [Oracle] Detect double-parenthesized function calls
  • 2026-03-05 [Oracle] Detect standalone dotted procedure calls
  • 2026-03-05 [Oracle] Detect procedure calls in CASE statement branches
  • 2026-03-05 [Oracle] Filter collection parameter access from call graph
  • 2026-03-05 [Oracle] Filter SYSDATE and pseudo-columns from call graph
  • 2026-03-05 [Oracle] Filter out collection variable/method calls from call graph
  • 2026-03-05 [Oracle] Merge forward declaration and implementation
  • 2026-03-05 [Oracle] Resolve calls to procs with default parameters
  • 2026-03-05 [Oracle] Detect built-in SQL function calls in PL/SQL expressions
  • 2026-03-05 [Oracle] Deduplicate cursor table accesses on OPEN CURSOR
  • 2026-03-05 [Oracle] Fix trigger ON table access kind classification
  • 2026-03-05 [MSSQL] Add T-SQL call graph support (Phase A IR)
  • 2026-03-05 [MSSQL] Filter XML methods and .WRITE() from call graph
  • 2026-03-05 [MSSQL] Classify JSON built-in functions as external deps
  • 2026-03-05 [MSSQL] Detect function calls in SET assignments
  • 2026-03-04 [Vertica] Support PLvSQL truncating assignment (<-)
  • 2026-03-03 [PostgreSQL] Fix parser errors on various SELECT commands
  • 2026-03-03 [PostgreSQL] Expose ON CONFLICT clause in GSP API
  • 2026-03-03 [PostgreSQL] INCLUDE clause in constraints
  • 2026-03-03 [PostgreSQL] Add PGvector distance operators
  • 2026-03-03 [PostgreSQL] Process operands for binary operator expressions
  • 2026-03-03 [PostgreSQL] Fix NPE in TElsifStmt.acceptChildren
  • 2026-03-03 Fix null guards in TStarColumnPushDownResolver
  • 2026-03-03 [SparkSQL] Support FOR loop statement
  • 2026-03-03 [SparkSQL] Support LOOP/END LOOP statement
  • 2026-03-03 [SparkSQL] Support labeled WHILE and REPEAT loops
  • 2026-03-03 [SparkSQL] Support LEAVE and ITERATE without labels
  • 2026-03-02 [Redshift] Database@Catalog cross-database notation
  • 2026-03-02 [Redshift] VACUUM TO PERCENT BOOST and ANALYZE ALL COLUMNS
  • 2026-03-02 [Redshift] Transaction isolation level extensions
  • 2026-03-02 [Redshift] SHOW TEMPLATES LIKE/LIMIT and SHOW DATABASES extensions
  • 2026-03-02 [Redshift] SHOW COLUMN GRANTS/CONSTRAINTS and FOR ROLE
  • 2026-03-02 [Redshift] SHOW GRANTS extended types and FOR/LIMIT clauses
  • 2026-03-02 [Redshift] COPY SIMPLIFY AUTO clause for Shapefile format
  • 2026-03-02 [Redshift] CREATE/ALTER USER EXTERNALID double-quoted value
  • 2026-03-02 [Redshift] ALTER USER CONNECTION LIMIT, VALID UNTIL, SET SEARCH_PATH
  • 2026-03-02 [Redshift] ALTER TABLE MASKING and RLS FOR DATASHARES
  • 2026-03-02 [Redshift] CREATE DATABASE extensions
  • 2026-03-02 [Redshift] ALTER DATABASE CONNECTION LIMIT and SET INTEGRATION REFRESH
  • 2026-03-02 [Redshift] Allow POSITION keyword as column name

GSP Java version 4.1.0.3(2026-03-02)

  • 2026-03-02 [SparkSQL] Support standalone TABLE expression
  • 2026-03-02 [SparkSQL] Support MSCK REPAIR TABLE with ADD/DROP/SYNC PARTITIONS
  • 2026-03-02 [SparkSQL] Support FROM-first query syntax
  • 2026-03-02 [SparkSQL] Support ILIKE operator for case-insensitive pattern matching
  • 2026-03-02 [SparkSQL] Support VALUES with table alias and column definitions
  • 2026-03-02 [SparkSQL] Support default keyword as schema/database identifier
  • 2026-03-02 [SparkSQL] Support compound statement (BEGIN...END)
  • 2026-03-02 [SparkSQL] Support Pipe Syntax operators
  • 2026-03-02 [SparkSQL] Support TRANSFORM clause, UNPIVOT, CREATE SQL FUNCTION
  • 2026-03-02 [SparkSQL] Support ALTER DATABASE, ALTER TABLE extended operations
  • 2026-03-02 [SparkSQL] Support WITHIN GROUP, GROUP BY WITH ROLLUP/CUBE
  • 2026-03-02 [SparkSQL] Support IGNORE/RESPECT NULLS, EXCEPT ALL, MINUS ALL
  • 2026-03-02 [SparkSQL] Support OFFSET, SET VAR, DROP TEMPORARY VARIABLE
  • 2026-03-02 [SparkSQL] Support EXECUTE IMMEDIATE, DECLARE VARIABLE
  • 2026-03-02 [SparkSQL] Support star EXCEPT with struct field references
  • 2026-03-02 [SparkSQL] Support WINDOW clause, INSERT BY NAME, REPLACE WHERE
  • 2026-03-02 [SparkSQL] Support ADD/LIST resource management, CREATE TABLE/VIEW AS VALUES
  • 2026-03-02 [SparkSQL] Support CREATE/ALTER VIEW WITH SCHEMA EVOLUTION
  • 2026-03-02 [SparkSQL] Support SHOW SCHEMAS, SHOW SYSTEM FUNCTIONS, ANALYZE TABLES
  • 2026-03-02 [Redshift] Support SHOW TEMPLATE command
  • 2026-03-02 [Redshift] Support NONATOMIC stored procedures
  • 2026-03-02 [Redshift] Support CREATE EXTERNAL MODEL with MODEL_TYPE BEDROCK
  • 2026-03-02 [Redshift] Support EXTERNALID and namespaced usernames
  • 2026-03-02 [Redshift] Support CREATE EXTERNAL SCHEMA FROM MSK with AUTHENTICATION
  • 2026-03-02 [Redshift] Support nested complex types in CREATE EXTERNAL TABLE
  • 2026-03-02 [Redshift] Support BZIP2 and SERIALIZETOJSON in COPY command
  • 2026-03-02 [Redshift] Support DISTSTYLE AUTO and SORTKEY AUTO
  • 2026-03-02 [Redshift] Support VACUUM BOOST, LATERAL, ARRAY()
  • 2026-03-02 [Redshift] Support CREATE VIEW WITH NO SCHEMA BINDING
  • 2026-03-02 [Redshift] Support GENERATED ALWAYS AS IDENTITY
  • 2026-03-02 [Redshift] Support UNPIVOT INCLUDE/EXCLUDE NULLS
  • 2026-03-02 [Redshift] Support CREATE/ALTER/DROP RLS POLICY and MASKING POLICY
  • 2026-03-02 [Redshift] Support CONNECT BY hierarchical queries
  • 2026-03-02 [Redshift] Support COPY/UNLOAD extensions (ORC, SHAPEFILE, KMS_KEY_ID)
  • 2026-03-02 [Redshift] Support CREATE/ALTER DATABASE extensions
  • 2026-03-02 [Redshift] Support CREATE/ALTER USER extensions
  • 2026-03-02 [Redshift] Support SHOW extensions (COLUMNS, TABLES, SCHEMAS, GRANTS, DATASHARES)
  • 2026-03-02 [Redshift] Support CREATE/ALTER/DROP TEMPLATE, ROLE, DATASHARE
  • 2026-03-02 [Redshift] Support CREATE EXTERNAL FUNCTION for Lambda UDFs
  • 2026-03-02 [Redshift] Support GRANT/REVOKE extensions (column-level, scoped, ROLE)
  • 2026-03-02 [Redshift] Support SELECT * EXCLUDE (columns) syntax
  • 2026-03-02 [Redshift] Support USE statement
  • 2026-03-02 [Snowflake] Add CHANGES clause for time travel queries
  • 2026-03-02 [Snowflake] Support object wildcard {} and {table.} in SELECT
  • 2026-03-02 [Snowflake] Support @%table and @~ stage references in SELECT
  • 2026-03-02 [Snowflake] Support parenthesized COPY in CREATE PIPE
  • 2026-03-02 [Snowflake] Support FILE_FORMAT shorthand and quoted MATCH_BY_COLUMN_NAME
  • 2026-03-02 [Snowflake] Support unquoted S3 URLs in COPY INTO FROM
  • 2026-03-02 [Snowflake] Support PARTITION BY with external locations in COPY INTO
  • 2026-03-02 [Snowflake] Support standalone HEADER keyword in COPY INTO
  • 2026-03-02 [Snowflake] Support @stage/ trailing slash in COPY INTO
  • 2026-03-02 [Snowflake] Allow varchar2 as unquoted identifier
  • 2026-03-02 [Snowflake] Support ALTER TABLE extensions (search optimization, policies, tags, metrics)
  • 2026-03-02 [Snowflake] Support multi-column ALTER/MODIFY, constraint management
  • 2026-03-02 [BigQuery] Support CREATE MODEL ARIMA custom holidays, INPUT/OUTPUT clauses
  • 2026-03-02 [BigQuery] Support Apache Spark procedures and WITH CONNECTION for Python UDFs
  • 2026-03-02 [BigQuery] Support OPTIONS clause after function body
  • 2026-03-02 [BigQuery] Support hyphenated identifiers in DROP CAPACITY/RESERVATION/ASSIGNMENT
  • 2026-03-02 [BigQuery] Support RANGE type in column definitions
  • 2026-03-02 fix: getFinalTable() traces through column aliases to physical tables
  • 2026-03-02 feat: Add getTracedFinalTable() for alias-aware lineage
  • 2026-03-02 fix: [Oracle] ALTER INDEX RENAME TO sets newIndexName, not tableName

GSP Java version 4.1.0.2(2026-02-25)

  • 2026-02-25 Fixed NPE
  • 2026-02-25 [BigQuery] Regenerate yacc table to fix parser regression
  • 2026-02-25 [BigQuery] Fix NPE crash on malformed input
  • 2026-02-25 [BigQuery] Support CREATE FUNCTION OPTIONS after RETURNS
  • 2026-02-25 [BigQuery] Support labeled loop and block statements
  • 2026-02-25 [BigQuery] Support bare RAISE statement in exception handlers
  • 2026-02-25 [BigQuery] Support ELSEIF in IF statements
  • 2026-02-25 [BigQuery] Add tests for REPEAT...UNTIL loop syntax
  • 2026-02-25 [BigQuery] Support WHILE...DO loop syntax
  • 2026-02-25 [Snowflake] Support CREATE PROCEDURE without LANGUAGE clause
  • 2026-02-25 [Snowflake] Support EXECUTE IMMEDIATE FROM with DRY_RUN option
  • 2026-02-25 [Snowflake] Support EXECUTE IMMEDIATE FROM with USING clause
  • 2026-02-25 [Snowflake] Support EXECUTE IMMEDIATE FROM syntax
  • 2026-02-25 [Netezza] Regenerate yacc table to restore || precedence
  • 2026-02-25 [Snowflake] Support CREATE ICEBERG TABLE syntax
  • 2026-02-25 [Snowflake] Support backslash-escaped strings in function body
  • 2026-02-25 [Snowflake] Support GRANT USAGE ON FUNCTION with parameter signature

GSP Java version 4.1.0.1(2026-02-23)

  • 2026-02-23 [Teradata] Fix objectToken when alias matches schema name
  • 2026-02-23 [Snowflake] Fix phantom star column expansion in JOIN lineage
  • 2026-02-23 Add PL/SQL call graph sample files with JSON output
  • 2026-02-23 Add JSON schema v1.1 for call graph and impact analysis
  • 2026-02-23 [Oracle] Upgrade Phase A IR to industrial-grade
  • 2026-02-22 [Oracle] Add OracleBoundIRBuilder and CallGraph
  • 2026-02-22 Add TSQLResolver2 adapter strategy for IBoundIRBuilder
  • 2026-02-22 [BigQuery] Fix nested struct column lineage naming
  • 2026-02-22 [BigQuery] Add StructFieldHint for 3-part no-alias struct access
  • 2026-02-22 [BigQuery] Add FieldPath infrastructure for nested column lineage
  • 2026-02-21 [Snowflake] Support nested stored procedure declarations
  • 2026-02-21 [Snowflake] Support EXIT keyword in exception handlers
  • 2026-02-21 [Snowflake] Fix NOT NULL constraint on variable declarations
  • 2026-02-21 [BigQuery] Support MATCH_RECOGNIZE pipe operator
  • 2026-02-21 [BigQuery] Support WINDOW pipe operator
  • 2026-02-21 [BigQuery] Support CALL pipe operator for table-valued functions
  • 2026-02-21 [BigQuery] Support TABLESAMPLE pipe operator
  • 2026-02-21 [BigQuery] Support OFFSET in pipe LIMIT operator
  • 2026-02-21 [Vertica] Fix CREATE EXTERNAL TABLE AS COPY FROM support
  • 2026-02-21 [Vertica] Add SECURITY INVOKER/DEFINER support for CREATE PROCEDURE
  • 2026-02-21 [Redshift] Fix comparison operator access in group_comparison_t
  • 2026-02-21 [Redshift] Fix quantified comparison (ALL/ANY/SOME) AST
  • 2026-02-20 [Snowflake] Support ASYNC keyword in parenthesized resultset assignments
  • 2026-02-20 [Snowflake] Support cursor row dot notation in FOR loops
  • 2026-02-20 [BigQuery] Allow expression in RAISE USING MESSAGE
  • 2026-02-20 [Snowflake] Support DECLARE EXCEPTION without parameters
  • 2026-02-20 [BigQuery] Add support for additional ML table functions
  • 2026-02-20 [Snowflake] Fix NULL; statement NullPointerException in $$ bodies
  • 2026-02-20 [BigQuery] Support FOR SYSTEM_TIME AS OF with alias in JOIN
  • 2026-02-20 [Impala] Add PURGE keyword support for DROP TABLE
  • 2026-02-20 [Redshift] Fix TOP clause returning null on AST
  • 2026-02-20 Resolve GROUP BY alias references to source tables

GSP Java version 3.3.1.9(2025-11-23)

  • 2025-11-23 Convert ENABLE_RESOLVER to ThreadLocal for thread safety
  • 2025-11-22 Add support for PL/SQL NEW constructor with empty params
  • 2025-11-20 Fix resolver error in parseExpression method

3.3.1.7 (2025-11-16)

  • 2025-11-16: Improve UNION column propagation and prevent duplicate links
  • 2025-11-16: Fix star column push down for UNION in CTE subqueries
  • 2025-11-16: Fix Snowflake CREATE TABLE to support multiple table options
  • 2025-11-15: Fix parse bug for lock row for access used in cursor declaration
  • 2025-11-15: Fix CASE expression matching in Teradata procedures
  • 2025-11-15: Fix NullPointerException in Teradata implicit table column linking
  • 2025-11-15: Fix UNION column name resolution to use leftmost query
  • 2025-11-15: Add SET TIME ZONE statement parsing for Teradata
  • 2025-11-15: Add END_FASTEXPORT command type for Teradata

3.2.1.5 (2025-10-26)

  • 2025-10-26: Fix DROP SECURITY POLICY parsing in IF statements
  • 2025-10-26: Fix PARTITION BY expression parsing in Snowflake
  • 2025-10-26: Integrate file format properties parsing and XML output
  • 2025-10-26: Add parseFileFormatProperties method to TCreateTableOption
  • 2025-10-26: Add file format properties list to TCreateTableOption

3.2.1.4 (2025-10-26)

  • 2025-10-26: Add areEqualStatic method to IdentifierService
  • 2025-10-26: Add static cache to IdentifierService for performance
  • 2025-10-26: Refactor identifier normalization to use service pattern
  • 2025-10-26: Fix multithreading bug in SqlCmdsFactory
  • 2025-10-26: Improve SQL Server identifier handling and tests
  • 2025-10-22: Enable catalog hierarchical and bucketed indexes
  • 2025-10-21: Use SQLUtil.getIdentifierNormalName in keyForMap (temporary)
  • 2025-10-21: Enhance IdentifierRules Javadoc with compatibility info
  • 2025-10-20: Enhance Javadoc for IdentifierService and SQLUtil
  • 2025-10-20: Remove redundant catalogStore field and USE_CALCITE_CATALOG_VIEW flag
  • 2025-10-20: Refactor CatalogStore to use IdentifierService
  • 2025-10-19: Add catalog documentation README index
  • 2025-10-19: Add comprehensive catalog documentation for 12 major databases
  • 2025-10-18: Add Oracle catalog documentation for beginners
  • 2025-10-18: Fix collation case sensitivity configuration bugs

3.2.0.2 (2025-10-15)

  • 2025-10-15: Fix parsing conversion functions as column names
  • 2025-10-15: Add array indexing support for GaussDB
  • 2025-10-15: Add table kind support for GaussDB CREATE TABLE
  • 2025-10-10: Add GitHub Actions workflow for PR test automation
  • 2025-10-10: Add INSERTED/DELETED as SQL Server reserved words

3.2.0.1 (2025-10-08)

  • 2025-10-08: Fix json_table rewrite bug in script generator
  • 2025-10-08: Remove unused findXXXCmd methods from TSqlCmds
  • 2025-10-08: Refactor TSqlCmds.java into per-vendor TSqlCmds subclasses
  • 2025-10-08: Document command ordering bug with real example
  • 2025-10-08: Fix command ordering for DATABASE SCOPED CREDENTIAL
  • 2025-10-08: Migrate MDX to modular architecture
  • 2025-10-08: Unify vendor implementations for Trino, SOQL, ODBC
  • 2025-10-07: Migrate Vertica to modular architecture
  • 2025-10-07: Migrate Sybase to modular architecture
  • 2025-10-07: Migrate SparkSQL to modular architecture
  • 2025-10-07: Migrate Snowflake to modular architecture
  • 2025-10-07: Migrate Redshift to modular architecture
  • 2025-10-07: Migrate Presto to modular architecture
  • 2025-10-07: Migrate OpenEdge to modular architecture
  • 2025-10-07: Migrate Netezza to modular architecture
  • 2025-10-07: Migrate Informix to modular architecture
  • 2025-10-07: Migrate HANA to modular architecture
  • 2025-10-07: Migrate Greenplum to modular architecture
  • 2025-10-07: Migrate GaussDB to modular architecture
  • 2025-10-07: Migrate DB2 to modular architecture
  • 2025-10-07: Migrate Databricks to modular architecture
  • 2025-10-07: Migrate Couchbase to modular architecture
  • 2025-10-07: Migrate BigQuery to modular architecture
  • 2025-10-07: Migrate Teradata to modular architecture
  • 2025-10-07: Remove FindMssqlCmdUsedByYacc
  • 2025-10-07: Migrate MySQL to modular architecture
  • 2025-10-07: Migrate PostgreSQL to modular architecture
  • 2025-10-07: Migrate MSSQL to modular vendor-specific architecture
  • 2025-10-07: Support vendor-specific token strings in modular architecture
  • 2025-10-06: Migrate MSSQL command initialization to TSqlCmdsMssql
  • 2025-10-06: Refactor TSqlCmds to vendor-specific lazy-loading architecture
  • 2025-10-05: Refactor: Move TSqlCmds to sqlcmds package
  • 2025-10-05: Add high-impact factory methods based on profiler analysis
  • 2025-10-05: Optimize Oracle package function lookup with thread-safe lazy init
  • 2025-10-05: Expand factory methods for common node types
  • 2025-10-04: Avoid skipping table.* when only one source exists
  • 2025-10-04: Fix Teradata lexer to treat $_ identifiers correctly
  • 2025-10-04: Merge pull request #550 from sqlparser/dev
  • 2025-10-04: Fixed MySQL Metadata Bug.

3.1.1.3 (2025-10-02)

  • 2025-10-02: Add getLastLineNoOfLastStatementBeenValidated() for huge file splitting
  • 2025-10-02: Optimize token code comparisons in domssqlgetrawsqlstatements

3.1.1.2 (2025-09-27)

  • 2025-09-27: Optimize isopchar and isselfchar methods in TCustomLexer
  • 2025-09-27: Remove sourceBuffer of lexer
  • 2025-09-27: Add test units
  • 2025-09-27: Fix CTE column mapping when subquery uses SELECT *
  • 2025-09-27: Fix Teradata table resolution when original name used despite alias
  • 2025-09-27: Document Teradata implicit derived tables processing

3.1.1.1 (2025-09-21)

  • 2025-09-20: Optimize showERG graph demo
  • 2025-09-20: Update DataFlowAnalyzer demo
  • 2025-09-19: Build pom for dlineage
  • 2025-09-19: Set genDataflowGraph showLinkOnly default value to true

3.1.1.0 (2025-09-14)

  • 2025-09-14: Feat: Add stable sqlHash for lineage statement identity
  • 2025-09-14: Feat: Add stable queryId to SQL statements
  • 2025-09-14: Feat: Introduce new source token management system
  • 2025-09-14: feat(lexer): Implement lazy loading for token text
  • 2025-09-13: tmp for lazyloading token
  • 2025-09-13: Refactor: Implement lazy loading lexer architecture
  • 2025-09-13: Docs: Document lexer architecture and lazy loading design
  • 2025-09-13: Optimize memory usage with lazy ArrayList initialization

3.1.0.9 (2025-09-13)

  • 2025-09-13: Fix column linking for inserted/deleted in triggers
  • 2025-09-13: claude helpfile
  • 2025-09-13: Add parser pool implementation without performance gains
  • 2025-09-13: Add thread-safe parser pool for 10x performance gain

3.1.0.8 (2025-09-13)

  • 2025-09-13: Refactor and harden parser build scripts
  • 2025-09-13: Refactor: Move SQL grammar files to core module

3.1.0.7 (2025-09-11)

  • 2025-09-11: Test: Verify pseudo-table resolution in triggers
  • 2025-09-10: Refactor: Support chained transformations for columns
  • 2025-09-08: Docs: Explain issues with varied relationship types
  • 2025-09-07: Update lineage design doc
  • 2025-09-07: Fix(Snowflake): Correct name of wildcard with EXCLUDE clause
  • 2025-09-07: Fix duplicate output for field selection expressions
  • 2025-09-07: Fix SQL generation for array access and slices
  • 2025-09-07: Add type discrimination to TIndices for array vs qualified name syntax
  • 2025-09-07: Fix column resolution for Oracle dblink tables
  • 2025-09-06: Restructure dlineage demo
  • 2025-09-06: Merge pull request #537 from sqlparser/dev
  • 2025-09-06: Doc: Define modeling practices for JOIN conditions
  • 2025-09-03: Clarify graphPartition sharding note in v2 doc
  • 2025-09-03: Rename WEAK_COPY to MODIFIED_COPY and refine categories
  • 2025-08-31: Docs: Extend v2 design with effectType rules and table/column combos

3.1.0.5 (2025-08-30)

  • 2025-08-30: Add GROUP BY ALL modifier support to TGroupBy class
  • 2025-08-30: Fix: Restrict INSERTED/DELETED column logic to SQL Server
  • 2025-08-30: Docs: Warn of SQL formatter side effects on parser state
  • 2025-08-30: Refactor toString method for better readability
  • 2025-08-30: Fix token iteration in visitNodeByToken
  • 2025-08-28: Docs: Unify and enhance data lineage specification
  • 2025-08-28: Refactor: Restructure demos into Maven multi-module project
  • 2025-08-28: Refactor: Move database connection logic to provider

3.1.0.2 (2025-08-15)

  • 2025-08-15: [Dlineage] Fix a bug the data lineage of parameters in function call is not mapping correctly.

3.1.0.1 (2025-08-01)

  • 2025-08-01: [GaussDB] Fixed a bug where the COUNT aggregate function was not properly recognized as a built-in function in the parser.
  • 2025-08-01: [GaussDB] Fixed a bug where the LIKE operator incorrectly had lower precedence than the AND operator in pattern matching conditions. Updated operator precedence to ensure LIKE has higher precedence.

3.1.0.0 (2025-07-27)

  • 2025-07-27: [Postgres] Fix a bug not materialized is not supported in WITH clause.
  • 2025-07-27: [Oracle] Support default collation clause in create view/create function/create procedure/create trigger.
  • 2025-07-27: [SQL Server] Fix a bug ALTER TABLE REBUILD WITH(DATA_COMPRESSION = PAGE) is not parsed correctly.

3.0.9.9 (2025-07-26)

  • 2025-07-26: [Redshift] Fixed a performance issue with large INSERT statements; efficiently handles thousands of tuples (e.g. $1, $2, ...).

3.0.9.8 (2025-07-20)

  • 2025-07-20: [SparkSQL] Bugfix, fix a bug position keyword can't be used as column name.

3.0.9.7 (2025-07-19)

  • 2025-07-19: [scriptWriter] Fixed a bug where field selection syntax with parentheses (e.g. "(d).grantee") was not being generated correctly.
  • 2025-07-19: [scriptWriter] Fixed a bug where column lists in DISTINCT ON clauses were not being properly generated.

3.0.9.6 (2025-07-10)

  • 2025-07-10: [data lineage] Fixed bug in DataflowAnalyzer using SQL environment settings.

3.0.9.5 (2025-07-06)

  • 2025-07-06: [Vertica] Support USING PARAMETERS clause in LISTAGG function.
  • 2025-07-06: [Vertica] Support NULLSEQUAL operator.
  • 2025-07-06: [Snowflake] Fixed bug with procedure bodies using only two consecutive tokens before semicolon.
  • 2025-07-06: [SQL Server] Fix INSERTED pseudo table in CREATE TRIGGER column resolution.
  • 2025-07-06: [SQL Server] Fix column in IF UPDATE() OR UPDATE() of trigger not picked up.

3.0.9.4 (2025-06-15)

  • 2025-06-15: [PostgreSQL] Support create recursive view.
  • 2025-06-15: [SQL Server] Fix can't parse partitions clause in TRUNCATE TABLE.
  • 2025-06-15: [BigQuery] Add support for star column expressions in array access.
  • 2025-06-15: [BigQuery] Add support for various DROP statements.
  • 2025-06-15: [BigQuery] Fix length specification for BYTES datatype.
  • 2025-06-15: [BigQuery] Fix IF NOT EXISTS clause in CREATE MATERIALIZED VIEW.
  • 2025-06-15: [Athena] Fix NPE in ALTER TABLE RENAME PARTITION.
  • 2025-06-15: [General] Fix thread-safety in TNodeFactory.

3.0.9.3 (2025-06-08)

  • 2025-06-08: [scriptWriter] Fix bug with column.property rewrite in SQL script generation.

3.0.9.3 (2025-06-08)

  • 2025-06-08: [PostgreSQL] Fix bug with AS datatype clause in CREATE SEQUENCE.
  • 2025-06-08: [PostgerSQL] Support ALTER INDEX.
  • 2025-06-08: [Teradata] Preserve both NAMED clause and AS alias in SELECT list.
  • 2025-06-08: [Teradata] Support EXECUTE ... USING ... in procedures.
  • 2025-06-08: [Teradata] Add FOREIGN TABLE syntax in FROM clause.
  • 2025-06-08: [Teradata] Add IGNORE NULLS clause before OVER in agg functions.
  • 2025-06-08: [Teradata] Fix parsing SELECT substring(..., position(...) + 5 FOR 5)
  • 2025-06-08: [PostgerSQL] Support CREATE/ALTER ROLE.
  • 2025-06-08: [Snowflake] Fix COMMENT keyword as column alias in SELECT.

3.0.9.3 (2025-06-07)

  • 2025-06-07: [SparkSQL] Support LEFT/RIGHT JOIN without explicit ON.
  • 2025-06-07: [SparkSQL] Support grouping sets after group by items without comma.
  • 2025-06-07: [SparkSQL] Fix AS keyword wrongly required in CTAS.
  • 2025-06-07: [SparkSQL] Fix parsing FILTER clauses after aggregates.

3.0.9.2 (2025-06-02)

  • 2025-06-02: [scriptWriter] Fix bug: array types like text[] rewritten as text.
  • 2025-06-02: [scriptWriter] Fix bug: IS TRUE / IS FALSE not rewritten correctly.
  • 2025-06-02: [scriptWriter] Fix bug: RECURSIVE keyword missing in CTE rewrites.
  • 2025-06-02: [scriptWriter] Fix bug: ORDER BY in function call arguments.
  • 2025-06-02: [scriptWriter] Fix bug: parens around CTE subqueries.

3.0.9.1 (2025-05-29)

  • 2025-05-29: [Teradata] Fix can't process .EXPORT FILE BTEQ command.

3.0.9.0 (2025-05-27)

  • 2025-05-27: [Oracle] Support sequence option in CREATE TABLE with IDENTITY.

3.0.8.8 (2025-05-18)

  • 2025-05-18: [API] Enhance EDbVendor: getAllVendorAliases(), getDescription(), getStatus()
  • 2025-05-18: [PostgreSQL] Support JSON operators (#-, @?, @@)
  • 2025-05-18: [Vertica] Fix CREATE OR REPLACE procedure parsing.
  • 2025-05-18: [Netezza] Fix/Support interval datatypes (all variations)

3.0.8.8 (2025-05-18)

  • 2025-05-18: [General] Support sqlimpact.

3.0.8.7 (2025-05-18)

  • 2025-05-18: [GaussDB] Support LANGUAGE clause in do statement.

3.0.8.6 (2025-05-17)

  • 2025-05-17: [General] Parser handles missing semicolons robustly (adds virtual semicolon when missing).
  • 2025-05-17: [Teradata] Fix NPE for ADD_MONTHS() with subquery param.
  • 2025-05-17: [API] Add getOperatorTokens() to TExpression for multi-token ops (e.g. SIMILAR TO, IS DISTINCT FROM).
  • 2025-05-17: [scriptWriter] Fix group comparisons in SELECT list SQL generation.
  • 2025-05-17: [GaussDB] Support distribute BY in CREATE TABLE.
  • 2025-05-17: [GaussDB] Support TRY_CAST function.
  • 2025-05-17: [GaussDB] Fix LOCALTIMESTAMP(n) precision param parsed as column.
  • 2025-05-17: [SparkSQL] Fix can't retrieve distribute by / sort by clause.
  • 2025-05-17: [Snowflake] Fix parsing of LEFT JOIN in single-quote/dollar-proc.
  • 2025-05-17: [GaussDB] Fix cast(1, int) not supported.
  • 2025-05-17: [scriptWriter] toScript() appends semicolon if original ended with semicolon.

3.0.8.5 (2025-05-12)

  • 2025-05-12: [SQL Server] Improve distinguishing -- comment vs. minus.
  • 2025-05-12: [SQL Server] Fix TRUNCATE TABLE with table aliases.

3.0.8.4 (2025-05-11)

  • 2025-05-11: [Oracle] Detect package constant/variable used in INSERT for lineage.

3.0.8.3 (2025-05-11)

  • 2025-05-11: [Snowflake] Support table index constraint.
  • 2025-05-11: [scriptWriter] In PostgreSQL, special SQL functions handling parens.

3.0.8.2 (2025-05-11)

  • 2025-05-11: [General] INSERT processed during the SQL Resolver.

3.0.8.1 (2025-05-08)

  • 2025-05-08: [General] Fix critical OOM in TStarColumnPushDownResolver (nested UNION ALL).

3.0.8.1 (2025-05-07)

  • 2025-05-07: [SQL Server] Fix can't parse CREATE OR ALTER FUNCTION.

3.0.8.0 (2025-05-06)

  • 2025-05-06: [General] Merge some source code.

3.0.7.9 (2025-05-04)

  • 2025-05-04: [PostgreSQL] Fix parsing of UNION DISTINCT clause.

3.0.7.8 (2025-05-04)

  • 2025-05-04: [Databricks] Fix normalization of backtick-quoted identifiers.
  • 2025-05-04: [SparkSQL] Fix parsing column alias lists in parentheses with EXPLODE.

3.0.7.7 (2025-05-03)

  • 2025-05-03: [Hive] Fix LATERAL VIEW EXPLODE alias analysis.
  • 2025-05-03: [PostgreSQL/toScript] Fix type cast (::) handling in script generation.

3.0.7.6 (2025-05-01)

  • 2025-05-01: [Performance] Cached/specialized factory methods in TNodeFactory.createNode()

3.0.7.5 (2025-04-29)

  • 2025-04-29: [Teradata] Support array element access with quoted identifiers.
  • 2025-04-29: [Teradata] Fix same-name tables in different DBs treated as the same.

3.0.7.4 (2025-04-28)

  • 2025-04-28: [SparkSQL] Fix == equality operator handling.
  • 2025-04-28: [SparkSQL] Fix & bitwise AND operator handling.

3.0.7.3 (2025-04-27)

  • 2025-04-27: [General] Performance improved.

3.0.7.2 (2025-04-20)

  • 2025-04-20: [PostgreSQL] Support array element access on array-returning functions.
  • 2025-04-20: [BigQuery] Support CREATE OR REPLACE TEMPORARY TABLE.
  • 2025-04-20: [SQL Server] Fix comments with leading whitespace.
  • 2025-04-20: [API] Add TRedshiftTableAttributes.
  • 2025-04-20: [Redshift] Add DISTSTYLE for materialized views.
  • 2025-04-20: [Teradata] Fix DAYNUMBER_OF_MONTH not recognized.
  • 2025-04-20: [Teradata] Fix || precedence.
  • 2025-04-20: [API] Add implicit_datatype_cast_as_t for '... AS ...' expressions.
  • 2025-04-20: [API] PARSE/TRY_PARSE use getArgs() for SQL Server.

3.0.7.2 (2025-04-19)

  • 2025-04-19: [Snowflake] Properly detect INSERT OVERWRITE clause.
  • 2025-04-19: [Vertica] Fix '||' concatenate op.
  • 2025-04-19: [MySQL] Support JSON_TABLE.
  • 2025-04-19: [MySQL] Support column-level check constraint.
  • 2025-04-19: [MySQL] Support ALTER TABLE ADD CONSTRAINT CHECK.
  • 2025-04-19: [DB2] Fix TYPE keyword as valid column name in SELECT.

3.0.7.1 (2025-04-17)

  • 2025-04-17: [General] Fix various test case failures.

3.0.7.0 (2025-04-15)

  • 2025-04-15: [Snowflake] Fix LEFT/RIGHT join keywords treated as column aliases.

3.0.6.9 (2025-04-13)

  • 2025-04-13: [gettablecolumn] Match table.column.property to table.* in FROM.

3.0.6.8 (2025-04-12)

  • 2025-04-12: [BigQuery] Link array element accesses with [offset] to table columns.
  • 2025-04-12: [API] Add EExpressionType.teradata_at_t AT time zone support.
  • 2025-04-12: [Teradata] Extract time_zone_specification from AT clause.
  • 2025-04-12: [Teradata] Add support for many named builtins.
  • 2025-04-12: [Snowflake] Support ASOF JOIN.
  • 2025-04-12: [SparkSQL] Support grouping set in group by.

3.0.6.7 (2025-04-12)

  • 2025-04-12: [Hive,SparkSQL] Clarify alias rules for SELECT list.
  • 2025-04-12: [Presto] Fix select trailing comma.

3.0.6.6 (2025-04-06)

  • 2025-04-06: [Teradata] Fix "WITH DATA" clause treats "data" as column name.
  • 2025-04-06: [resolver] New column-to-table algorithm takes precedence over old.
  • 2025-04-06: [Sybase] Fix alias in SELECT: 'id_col=identity(5)'.

3.0.6.5 (2025-04-05)

  • 2025-04-05: [Oracle] Fix can't parse EXECUTE PROC_A3; correctly.
  • 2025-04-05: [Oracle] Fetch proc name from sqlplus EXEC.

3.0.6.4 (2025-04-05)

  • 2025-04-05: [Snowflake] REAL keyword can be table alias.
  • 2025-04-05: [Oracle] Support parens expr with property access.
  • 2025-04-05: [Snowflake] Fix SWAP WITH table direction.
  • 2025-04-05: [MySQL] Support EXCEPT clause.

3.0.6.3 (2025-04-04)

  • 2025-04-04: [Databricks] Fix parens around split() in array access.
  • 2025-04-04: [BigQuery] Add TArrayConcatAggFunction.
  • 2025-04-04: [GaussDB] Fix 'day' param in TIMESTAMPDIFF.
  • 2025-04-04: [General] Remove fake orphan column warning.
  • 2025-04-04: [GaussDB] DOUBLE alias for DOUBLE PRECISION.
  • 2025-04-04: [GaussDB] Fix can't parse LIMIT 1,1.

3.0.6.2 (2025-04-01)

  • 2025-04-01: [SparkSQL] Support array access expression.
  • 2025-04-01: [SparkSQL] Parsing fix: split negative numbers tokens.
  • 2025-04-01: [BigQuery] Show dot after numeric in TObjectName.toString().

3.0.6.0 (2025-03-24)

  • 2025-03-24: [Teradata] Fix duplicate derived tables in WHERE clause.

3.0.5.9 (2025-03-23)

  • 2025-03-23: [Databricks/Hive] Support Hash (#) comments.

3.0.5.8 (2025-03-11)

  • 2025-03-11: [column resolver] Fix bug: column inside UNNEST mislinked.

3.0.5.7 (2025-03-09)

  • 2025-03-09: [API] ETableEffectType.tetTeradataReference renamed to tetImplicitLateralDerivedTable.
  • 2025-03-09: [column resolver] Subquery columns link to subquery FROM by preference.

3.0.5.6 (2025-03-04)

  • 2025-03-04: [demo] Remove dbConnect from gsp_demo_java.

3.0.5.5 (2025-03-02)

  • 2025-03-02: [SQL formatter] Fix can't process Teradata Implicit Lateral Derived Table.

3.0.5.4 (2025-03-02)

  • 2025-03-02: [Vertica] Fix localtime() number param as column.
  • 2025-03-02: [Vertica] Support RENAME COLUMN in ALTER TABLE.

3.0.5.3 (2025-03-01)

  • 2025-03-01: [column resolver] Fix SET in UPDATE to wrong table.
  • 2025-03-01: [GaussDB] Support OVERWRITE in INSERT (DWS).
  • 2025-03-01: [General] Remove fake orphan column warning.
  • 2025-03-01: [Oracle] Resolve package/function/proc name in procedure.

3.0.5.2 (2025-03-01)

  • 2025-03-01: [DB2] Support ARRAY[1,2,3] (DB2 10.5+).
  • 2025-03-01: [DB2] Fix null exception with MERGE/USING/VALUES.

3.0.5.1 (2025-02-27)

  • 2025-02-27: [getTableColumn] Refactor name resolution logic.

3.0.4.7 (2025-02-08)

  • 2025-02-08: [General] Fix bugs in resolver, refactor needed.

3.0.4.5 (2025-02-06)

  • 2025-02-06: [dlineage] Incorrect mapping for SQL Server alias.

3.0.4.5 (2025-02-06)

  • 2025-02-06: [dlineage] Missing column link for BigQuery.
  • 2025-02-06: [semantic] How GSP links unqualified columns to tables.

  • Consider this query:

SELECT append_business_key, f.worklist_field_name AS field_name, f.field_data_type FROM FIN_ICOLLECTION.ico_worklist_append_t a INNER JOIN FIN_ICOLLECTION.ico_worklist_field_info_t f ON a.append_business_key = f.worklist_field_id

  • When GSP analyzes this query without extra metadata, it deduces which table an unqualified column belongs to.
  • The JOIN condition (a.append_business_key) signals that append_business_key belongs to FIN_ICOLLECTION.ico_worklist_append_t.
  • GSP 3.0.4.4 or later links the unqualified append_business_key in SELECT to FIN_ICOLLECTION.ico_worklist_append_t.
  • But if both tables have append_business_key, the reference is ambiguous and needs explicit qualification.

  • Changed behavior:

  • Before 3.0.4.4: append_business_key was orphan column.
  • Since 3.0.4.4: linked to FIN_ICOLLECTION.ico_worklist_append_t if found in JOIN.

3.0.4.3 (2025-02-02)

  • 2025-02-02: [Vertica] Support PARTITION BY clause in CREATE TABLE.
  • 2025-02-02: [Vertica] Support CREATE TABLE LIKE.
  • 2025-02-02: [Vertica] Support ARRAY datatype.
  • 2025-02-02: [Vertica] Window support for partitioned result in LIMIT.
  • 2025-02-02: [Vertica] Fix on MERGE condition could omit parenthesis.
  • 2025-02-02: [Vertica] Support ENABLED/DISABLED in PK col constraint.
  • 2025-02-02: [Vertica] TEMP table fix for TableKinds.

3.0.4.2 (2025-01-25)

  • 2025-01-25: [Vertica] Support DISABLED/ENABLED keyword in constraints.
  • 2025-01-25: [Teradata] Fix TRUNC function in IN clause.
  • 2025-01-25: [Vertica] Support CALL.
  • 2025-01-25: [Vertica] Fix can't parse UNION DISTINCT.
  • 2025-01-25: [Vertica] Support ISNULL function.
  • 2025-01-25: [Vertica] Support MINUS operator.
  • 2025-01-25: [Teradata] Fix CHECKSUM=ALL in CREATE TABLE.

3.0.4.1 (2025-01-20)

  • 2025-01-20: [Evaluator] Remove outer quotes/handle escaped quotes in string.

3.0.4.0 (2025-01-19)

  • 2025-01-19: [Evaluator] Support plsql block with loop.
  • 2025-01-19: [BigQuery] Fix unable to expand star in SELECT *.

3.0.3.9 (2025-01-12)

  • 2025-01-12: [Snowflake] Support WITH TAG in view columns.
  • 2025-01-12: [SQL Server] Fix can't parse CTAS with CTE used in select.
  • 2025-01-12: [Oracle] Recognize CALL after procedure.

3.0.3.8 (2025-01-11)

  • 2025-01-11: [Snowflake] Fix double dollar delimiter for non-addition binary ops.
  • 2025-01-11: [Teradata] Support partition by column in CREATE TABLE.
  • 2025-01-11: [Teradata] Support partition by all but.
  • 2025-01-11: [Teradata] Parse multiple params in SIGNAL SET.

3.0.3.7 (2025-01-05)

  • 2025-01-05: [Teradata] Support sequenced/nonsequenced VALIDTIME.
  • 2025-01-05: [Teradata] Fix INTERVAL ... HOUR TO MINUTE in CAST.
  • 2025-01-05: [Teradata] Insert in WHEN NOT MATCHED should be assignment_t type.
  • 2025-01-05: [Teradata] Recognize TTeradataBTEQCmd.bteqCmdType (BTEQ CMD).

3.0.3.7 (2025-01-04)

  • 2025-01-04: [Snowflake] Support BEGIN; in single-quoted stored procs.
  • 2025-01-04: [Snowflake] Support BEGIN; in stored procs.
  • 2025-01-04: [dlineage] Add showCandidateTable option for candidate table column links.