Parse SQL into an instance exposing dialect-neutral AST inspection, mutation, and extraction.
On a direct-parse failure, applies preprocessing fallbacks — splitting a
trailing OPTION (...) clause and aliasing bracket-quoted identifiers
whose interior contains parser-defeating characters ([Active People],
[my-cte]) — so a wider class of SQL becomes AST-addressable. ToSQL
transparently restores both transforms.
Never throws on unparseable SQL — check IsValid.
The dialect supplied at construction.
Whether ANY top-level statement is a write — a DML mutation (INSERT/UPDATE/DELETE/MERGE/REPLACE), DDL (DROP/CREATE/ALTER/TRUNCATE/ RENAME), or EXEC/CALL/GRANT/REVOKE/USE.
Unlike StatementKind (which classifies only the first statement),
this inspects EVERY top-level statement, so a stacked payload such as
SELECT 1; DROP TABLE x is detected. Benign session prefixes (SET,
DECLARE) and SELECTs are not writes. Matching is on the AST statement
type, so the REPLACE() string function inside a SELECT is never
mistaken for a REPLACE statement.
Whether the SQL parsed into an AST (directly or via preprocessing).
The outermost row cap on the parsed SELECT, dialect-neutral, or null
when none is present. Backed by the dialect's ASTDialectAdapter, so the
caller never needs to know whether the cap was a TOP or LIMIT.
High-level classification of the parsed statement:
'select' | 'select-into' | 'set-op' | 'mutation' | 'other'.
Removes the top-level ORDER BY from the parsed statement, following the
set-op (UNION / INTERSECT / EXCEPT) chain so an ORDER BY on any
branch is cleared. No-op when there is none. Dialect-universal — the
orderby field has the same shape across dialects.
Used by the count-SQL builder: ORDER BY is irrelevant for a COUNT and is illegal inside a SQL Server CTE without TOP.
Removes the outermost row cap (TOP on SQL Server, LIMIT on PostgreSQL) from the parsed SELECT. No-op when the root isn't a SELECT.
Used by the count-SQL builder (queryPagingEngine.stripCountBody) so a
paged query's count reflects the full set rather than the capped subset.
It clears both forms, so a PostgreSQL count drops an explicit LIMIT —
consistent with how SQL Server's TOP is dropped.
Extract all column references from this instance's SQL. Convenience delegate to the static overload.
Extract CTE definitions from this instance's SQL. Convenience delegate to the static overload.
Extract SELECT clause columns from this instance's SQL. Convenience delegate to the static overload.
Extract all table/view references from this instance's SQL. Convenience delegate to the static overload.
Sets the outermost row cap. The adapter writes the dialect's form
(TOP N on SQL Server, LIMIT N elsewhere), preserving any existing
OFFSET. No-op when the root isn't a SELECT.
Serialize the (possibly mutated) AST back to SQL, restoring any
preprocessing transforms applied at construction (bracket-identifier
aliases, then the trailing OPTION (...) clause).
Throws if the SQL was not parseable (check IsValid first).
StaticAnalyzeTokenize and return a summary with boolean flags. Useful for quick checks like "does this SQL have MJ extensions?"
StaticAstifyParse MJ SQL into an extended AST.
Pipeline:
The MJ SQL string to parse
SQL dialect ('TransactSQL' | 'PostgresQL', default: 'TransactSQL')
StaticExprConvert a single AST expression node to a SQL string. Useful for extracting ORDER BY terms, column expressions, etc.
node-sql-parser's exprToSQL always produces backtick-quoted identifiers
regardless of dialect. This method converts backticks to the dialect's
native identifier quoting via dialect.QuoteIdentifier().
StaticExtractExtract all column references from SQL. Handles Nunjucks templates via placeholder substitution before AST parsing.
StaticExtractExtract all {{query:"..."}} composition references from SQL. Returns structured info including category path, query name, and parsed parameters.
StaticExtractExtract conditional blocks, pairing if/elif/else/endif into structured trees.
StaticExtractExtract CTE definitions from SQL starting with a WITH clause.
Given: WITH A AS (SELECT 1), B AS (SELECT 2) SELECT A.x, B.y FROM A, B
Returns: CTEDefinitions: ["A AS (...)", "B AS (...)"], MainStatement: "SELECT ..."
Uses AST parsing first (produces bracket-quoted identifiers for SQL Server), falls back to paren-depth scanning if AST fails (e.g., Nunjucks-templated SQL).
StaticExtractExtract parameter metadata from template expressions.
Walks tokens with a lexical-scope stack so that loop-local variables
introduced by {% for X in Y %} blocks are NOT registered as query
parameters (they're rebound on each iteration; callers can't supply
them). The iterable side of {% for %} (Y) IS registered as a
parameter — typically array and required, unless wrapped in
{% if Y %} which makes it optional.
Skip-Brain Bug B: previously this function treated {{ kw }} inside
{% for kw in OrgKeywords %} as a required parameter and failed to
register OrgKeywords at all. See __tests__/extract-parameter-info-loops.test.ts
and SKIP-QUERY-RENDERING-BUGS.md (Bug B) at the repo root.
Infers type from filters, isRequired from conditional block context.
StaticExtractExtracts the SELECT clause columns with their output names, source columns, and table qualifiers.
Handles:
u.Name → { OutputName: "Name", SourceColumn: "Name", TableQualifier: "u" }e.Name AS EntityName → { OutputName: "EntityName", SourceColumn: "Name", TableQualifier: "e" }COUNT(*) → { OutputName: "COUNT()", SourceColumn: "COUNT()", IsExpression: true }StaticExtractExtract all table/view references from SQL. Handles Nunjucks templates via placeholder substitution before AST parsing.
StaticExtractExtract all {{ variable | filter }} expressions from SQL. Returns structured info for each expression including variable name and filter chain.
StaticHasToken-aware detection of stacked statements: a statement-separating semicolon that has real content after it (outside string literals, quoted identifiers, and comments). A single trailing semicolon — or a run of them followed only by whitespace/comments — is allowed.
Unlike an AST check, this fires even when the trailing payload makes the
SQL unparseable (SELECT 1; EXEC xp_cmdshell '…', SELECT 1; WAITFOR DELAY '…'), which is exactly the stacked-injection class an AST scan
misses (the whole string fails to parse, so the AST is null). A rendered
read query must be a single statement, so any internal ; is rejected.
StaticHasToken-aware scan for SQL clauses that cannot legally appear inside a
derived table — wrapping a query that contains one of these in
SELECT ... FROM (<sql>) AS t would produce invalid SQL.
Detects (case-insensitive, outside string literals and quoted identifiers):
FOR JSON …FOR XML …OPTION (…)The dialect determines which identifier quoting styles are recognized
([…] for SQL Server, `…` for MySQL, "…" always).
StaticParseParse SQL and extract table + column references. This is the original SQLParser.Parse() method signature. Automatically handles MJ Nunjucks templates via placeholder substitution.
Parse options, or just a SQL string for backward compatibility
Optionaldialect: SQLParserDialectStaticParseParse plain SQL into a node-sql-parser AST. Handles FOR XML multi-directive workaround automatically. Returns null if parsing fails.
StaticParseParse SQL with MJ Nunjucks template preprocessing. This is the original SQLParser.ParseWithTemplatePreprocessing() signature. Now equivalent to Parse() since all methods handle templates automatically.
Parse options, or just a SQL string for backward compatibility
Optionaldialect: SQLParserDialectStaticRenameRenames a template variable in all {{ variable | filters }} expressions throughout the SQL.
Preserves the filter chain and whitespace formatting.
Example: RenameTemplateVariable("WHERE x = {{ region | sqlString }}", "region", "userRegion")
→ "WHERE x = {{ userRegion | sqlString }}"
Uses MJLexer for deterministic token identification — no regex guessing.
StaticSqlifyReconstruct MJ SQL from an Astify result.
For plain SQL: uses node-sql-parser's sqlify for normalized output. For MJ SQL: reconstructs from tokens, preserving all Nunjucks and composition syntax verbatim.
StaticSqlifyConvert a node-sql-parser AST (or array of ASTs) back to a SQL string. This is a thin wrapper around node-sql-parser's sqlify.
StaticStripStrip line and block comments from SQL, preserving content inside string literals and quoted identifiers. Block comments support nesting.
The dialect determines which identifier quoting styles are recognized:
SQL Server uses […], PostgreSQL uses "…", MySQL uses `…`.
Double-quoted identifiers are honored on every dialect.
StaticSubstituteReplace MJ tokens with SQL-safe placeholders.
Context-aware: sqlString → string literal, sqlNumber → numeric literal,
sqlIdentifier → bare identifier. Block tags and comments are stripped.
Returns a position map for reversing the substitution.
StaticSubstituteSubstitutes a template variable with a literal value in all {{ variable | filters }} expressions.
The entire expression (including filters) is replaced with the literal value, since filters
are irrelevant when injecting a concrete value.
Example: SubstituteTemplateVariable("WHERE x = {{ region | sqlString }}", "region", "'West'")
→ "WHERE x = 'West'"
Uses MJLexer for deterministic token identification — no regex guessing.
StaticTokenizeTokenize MJ SQL into an ordered list of tokens. Returns MJ tokens (template expressions, composition refs, block tags, comments) interleaved with SQL_TEXT tokens for the plain SQL segments.
StaticWalkWalk the AST from an Astify result and annotate where MJ placeholders appear.
Returns an MJASTWalkResult with annotations mapping each placeholder to its SQL clause context (SELECT, WHERE, ORDER BY, etc.) and the resolved MJ node.
This enables queries like:
The MJAstifyResult from Astify()
Walk result with annotations, or empty result if AST parsing failed
Unified parser for MemberJunction's SQL superset.
Handles standard SQL, Nunjucks template expressions (
{{ var | filter }}), Nunjucks block tags ({% if %}...{% endif %}), composition tokens ({{query:"Path/Name(params)"}}), and template comments ({# ... #}).Two-tier API:
new SQLParser(sql, dialect)) — parses once (with a preprocessing fallback) and exposes typed, dialect-neutral AST inspection/mutation (StatementKind,OuterCap,SetOuterCap,ToSQL) plus extraction helpers (ExtractCTEs,ExtractTableRefs,ExtractColumnRefs,ExtractSelectColumns).StripComments,Tokenize,Analyze,ParseSQL,SqlifyAST,HasUnwrappableTrailingClause, the MJ-template helpers, etc.).