UNPKG

ts-sql

Version:

An SQL builder in Typescript. This project is heavily inspired by [XQL](/extjs/xql). A big shout out to @exjs and @kobalicek for this amazing project.

191 lines 7.84 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const jsymbol_1 = require("jsymbol"); const ast = require("./astsql"); const queryVisitor_1 = require("./queryVisitor"); var SymbolState; (function (SymbolState) { SymbolState[SymbolState["Resolved"] = 0] = "Resolved"; SymbolState[SymbolState["Unresolved"] = 1] = "Unresolved"; })(SymbolState = exports.SymbolState || (exports.SymbolState = {})); class SqlAstSymbol { constructor(identifier, type) { this.identifier = identifier; this.type = type; this.state = SymbolState.Unresolved; } toString() { return this.identifier; } } exports.SqlAstSymbol = SqlAstSymbol; class ValidationContext { constructor(currentNode) { this.currentNode = currentNode; this.symbolTable = new jsymbol_1.SymbolTable(); this.collectedSymbols = []; } } exports.ValidationContext = ValidationContext; class QueryValidator extends queryVisitor_1.QueryVisitor { constructor(dialect, query, contextBuilder = ValidationContext) { super(dialect, query, contextBuilder); this.visitSelectStatement = (context, node) => { context.symbolTable.enterScope(); this.visitGenericNode(context, node.query); for (let sym of context.symbolTable) { context.collectedSymbols.push(sym); } context.symbolTable.exitScope(); return context; }; this.visitQueryExpression = (context, node) => { if (node.from) { this.visitNode(context, node.from); } if (node.elements && node.elements.length) { for (let se of node.elements) { this.visitNode(context, se); } } if (node.orderBy) { this.visitNode(context, node.orderBy); } if (node.limit) { this.visitNode(context, node.limit); } return context; }; this.visitTableSpec = (context, node) => { node.tableName = this.addResolvedSymbol(context, node.tableName.toString(), ast.SqlSymbolType.Table); if (node.partitions && node.partitions.length) { for (let index = 0; index < node.partitions.length; index++) { let symbol = this.addResolvedSymbol(context, node.partitions[index].toString(), ast.SqlSymbolType.Field); symbol.parent = node.tableName; node.partitions[index] = symbol; } } return context; }; this.visitAllColumns = (context, node) => { node.table = this.lookupOrAddTable(context, node.table.toString()); return context; }; this.visitColumnName = (context, node) => { let parent = node.table ? this.lookupOrAddTable(context, node.table.toString()) : undefined; node.table = parent; let fieldSymbol = this.lookupOrAddSymbol(context, node.name.toString(), ast.SqlSymbolType.Field); fieldSymbol.parent = parent; fieldSymbol.state = parent ? parent.state : SymbolState.Unresolved; node.name = fieldSymbol; return context; }; this.visitSimpleFunctionCall = (context, node) => { node.name = this.addResolvedSymbol(context, node.name.toString(), ast.SqlSymbolType.Function); if (node.args) { for (let fe of node.args) { if (this.shouldVisitNode(fe)) { context = this.visitNode(context, fe); } } } return context; }; this.visitAssignedTerm = (context, node) => { context = this.visitNode(context, node.value); if (node.variable) { node.variable = this.addResolvedSymbol(context, node.variable.toString(), ast.SqlSymbolType.Variable); } return context; }; this.visitAssignedExpressionAtom = (context, node) => { context = this.visitNode(context, node.expression); if (node.variable) { node.variable = this.addResolvedSymbol(context, node.variable.toString(), ast.SqlSymbolType.Variable); } return context; }; this.visitVariable = (context, node) => { node.name = this.addResolvedSymbol(context, node.name.toString(), ast.SqlSymbolType.Variable); return context; }; this.visitSelectIntoFieldsExpression = (context, node) => { for (let index = 0; index < node.fields.length; index++) { node.fields[index] = this.addResolvedSymbol(context, node.fields[index].toString(), ast.SqlSymbolType.Variable); } return context; }; this.visitAliasedTerm = (context, node) => { if (!node.alias) { return this.visitGenericNode(context, node); } context = this.visitNode(context, node.term); let aliasSymbol = this.addResolvedSymbol(context, node.alias.toString(), ast.SqlSymbolType.Alias); let nodeType = ast.SqlAstNode.getNodeType(node.term); switch (nodeType) { case "ColumnName": aliasSymbol.parent = (node.term.name); break; case "TableSpec": aliasSymbol.parent = (node.term.tableName); break; default: break; } if (aliasSymbol.parent) { aliasSymbol.state = aliasSymbol.parent.state; } node.alias = aliasSymbol; return context; }; } validate() { let context = this.visit(); for (let sym of context.symbolTable) { context.collectedSymbols.push(sym); } return context.collectedSymbols; } addResolvedSymbol(context, name, type) { let symbol = new SqlAstSymbol(name, type); symbol.state = SymbolState.Resolved; context.symbolTable.add(symbol); return symbol; } lookupOrAddTable(context, name) { let symbols = context.symbolTable.lookup(name); if (symbols) { for (let index = 0; index < symbols.length; index++) { if (symbols[index].type === ast.SqlSymbolType.Alias || symbols[index].type === ast.SqlSymbolType.Table) { return symbols[index]; } } } let symbol = new SqlAstSymbol(name, ast.SqlSymbolType.Table); symbol.state = SymbolState.Unresolved; context.symbolTable.add(symbol); return symbol; } lookupOrAddSymbol(context, name, type) { let symbols = context.symbolTable.lookup(name, type); if (symbols) { if (symbols.length > 1) { throw Error("Found more than one symbol of the same type in the current scope"); } return symbols[0]; } let symbol = new SqlAstSymbol(name, type); symbol.state = SymbolState.Unresolved; context.symbolTable.add(symbol); return symbol; } } exports.QueryValidator = QueryValidator; class MySQLQueryValidator extends QueryValidator { constructor(query) { super("MySQL", query, ValidationContext); } } exports.MySQLQueryValidator = MySQLQueryValidator; //# sourceMappingURL=queryValidator.js.map