ddl-manager
Version:
store postgres procedures and triggers in files
213 lines • 8.29 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExpressionParser = void 0;
const psql_lang_1 = require("psql-lang");
const ast_1 = require("../ast");
const UnknownExpressionElementParser_1 = require("./UnknownExpressionElementParser");
const ColumnReferenceParser_1 = require("./ColumnReferenceParser");
const ArrayElement_1 = require("../ast/expression/ArrayElement");
const assert_1 = require("assert");
const funcsWithoutOrderBy = [
// 1 + 1 = 1 + 1
"count",
// a + b = b + a
"sum",
// Math.max(a, b) = Math.max(b, a)
"max",
// Math.min(a, b) = Math.max(b, a)
"min",
// x || y = y || x
"bool_or",
// x && y = y && x
"bool_and"
];
class ExpressionParser {
constructor() {
this.columnReferenceParser = new ColumnReferenceParser_1.ColumnReferenceParser();
this.unknownExpressionElementParser = new UnknownExpressionElementParser_1.UnknownExpressionElementParser();
}
parse(input) {
const sql = toOperand(input);
const elements = this.parseElements(sql);
if (elements.length === 1 && elements[0] instanceof ast_1.Expression) {
return elements[0];
}
return new ast_1.Expression(elements);
}
parseElements(elemSyntax) {
if (elemSyntax instanceof psql_lang_1.SubExpression) {
return [new ast_1.Expression(this.parseElements(elemSyntax.row.subExpression), true)];
}
return this.tryParseBinaryOperator(elemSyntax) || [
this.tryParseFunctionCall(elemSyntax) ||
this.tryParsePgArray(elemSyntax) ||
this.tryParseExtract(elemSyntax) ||
this.tryParseCaseWhen(elemSyntax) ||
this.tryParseColumnRef(elemSyntax) ||
this.parseUnknown(elemSyntax)
];
}
tryParseBinaryOperator(elemSyntax) {
if (elemSyntax instanceof psql_lang_1.BinaryOperator) {
return [
...this.parseElements(elemSyntax.row.left),
new ast_1.Operator(elemSyntax.row.operator),
...this.parseElements(elemSyntax.row.right)
];
}
if (elemSyntax instanceof psql_lang_1.PreUnaryOperator) {
return [
new ast_1.Operator(elemSyntax.row.preOperator),
...this.parseElements(elemSyntax.row.operand)
];
}
if (elemSyntax instanceof psql_lang_1.PostUnaryOperator) {
return [
...this.parseElements(elemSyntax.row.operand),
new ast_1.Operator(elemSyntax.row.postOperator)
];
}
if (elemSyntax instanceof psql_lang_1.CastTo) {
return [
...this.parseElements(elemSyntax.row.cast),
new ast_1.Operator("::"),
ast_1.UnknownExpressionElement.fromSql(elemSyntax.row.to.toString())
];
}
if (elemSyntax instanceof psql_lang_1.SquareBrackets) {
return [
...this.parseElements(elemSyntax.row.operand),
ast_1.UnknownExpressionElement.fromSql("[" + elemSyntax.row.index.toString() + "]")
];
}
if (elemSyntax instanceof psql_lang_1.EqualAny) {
return [
...this.parseElements(elemSyntax.row.operand),
new ast_1.Operator("="),
ast_1.UnknownExpressionElement.fromSql("any(" + elemSyntax.row.equalAny.toString() + ")", this.unknownExpressionElementParser.buildColumnsMap(elemSyntax.row.equalAny))
];
}
if (elemSyntax instanceof psql_lang_1.In) {
const unknownSql = Array.isArray(elemSyntax.row.in) ?
`in (${elemSyntax.row.in.join(", ")})` :
`in (${elemSyntax.row.in})`;
return [
...this.parseElements(elemSyntax.row.operand),
ast_1.UnknownExpressionElement.fromSql(unknownSql, this.unknownExpressionElementParser.buildColumnsMap(elemSyntax.row.in))
];
}
}
tryParseFunctionCall(funcCallSyntax) {
if (!(funcCallSyntax instanceof psql_lang_1.FunctionCall)) {
return;
}
const funcNameSyntax = funcCallSyntax.row.call;
const funcName = funcNameSyntax.toString();
let args = funcCallSyntax.row.arguments.map(argSql => this.parseFunctionCallArgument(funcName, argSql));
let where;
if (funcCallSyntax.row.filter) {
where = this.parse(funcCallSyntax.row.filter);
}
let orderByItems = [];
if (funcCallSyntax.row.orderBy) {
funcCallSyntax.row.orderBy.forEach(itemSyntax => {
const nulls = itemSyntax.row.nulls;
const vector = itemSyntax.row.vector;
const expression = this.parse(itemSyntax.row.expression);
const item = new ast_1.OrderByItem({
type: vector,
expression,
nulls
});
orderByItems.push(item);
});
}
let distinct = funcCallSyntax.row.form == "distinct";
if (funcsWithoutOrderBy.includes(funcName)) {
orderByItems = [];
}
if (funcName === "sum") {
orderByItems = [];
}
// min(distinct x) = min(x)
// max(distinct x) = max(x)
// bool_or(distinct x) = bool_or(x)
// bool_and(distinct x) = bool_and(x)
const funcsWithoutDistinct = [
"max",
"min",
"bool_or",
"bool_and"
];
if (funcsWithoutDistinct.includes(funcName)) {
distinct = false;
}
// count(id_client) = count(id_partner) = count(*)
// count(distinct id_client) != count(id_partner)
if (funcName === "count" && !distinct) {
args = [ast_1.Expression.unknown("*")];
}
const funcCall = new ast_1.FuncCall(funcName, args, where, distinct, orderByItems.length ?
new ast_1.OrderBy(orderByItems) :
undefined);
return funcCall;
}
tryParsePgArray(elemSyntax) {
if (!(elemSyntax instanceof psql_lang_1.ArrayLiteral)) {
return;
}
const content = elemSyntax.row.array.map(expression => this.parse(expression));
return new ArrayElement_1.ArrayElement(content);
}
tryParseExtract(elemSyntax) {
if (!(elemSyntax instanceof psql_lang_1.Extract)) {
return;
}
const extract = elemSyntax.row.extract;
const from = this.parse(elemSyntax.row.from);
return new ast_1.Extract(extract, from);
}
tryParseCaseWhen(elemSyntax) {
if (!(elemSyntax instanceof psql_lang_1.CaseWhen)) {
return;
}
return new ast_1.CaseWhen({
cases: elemSyntax.row.case.map(caseSyntax => ({
when: this.parse(caseSyntax.row.when),
then: this.parse(caseSyntax.row.then)
})),
else: elemSyntax.row.else ? this.parse(elemSyntax.row.else) : undefined
});
}
tryParseColumnRef(elemSyntax) {
if (!(elemSyntax instanceof psql_lang_1.ColumnReference)) {
return;
}
assert_1.strict.ok(!elemSyntax.row.allColumns);
return this.columnReferenceParser.parse(elemSyntax);
}
parseUnknown(elemSyntax) {
return this.unknownExpressionElementParser.parse(elemSyntax);
}
parseFunctionCallArgument(funcName, argSql) {
if (funcName === "count" && (argSql.toString()).trim() === "*") {
return new ast_1.Expression([
new ast_1.UnknownExpressionElement(new psql_lang_1.ColumnReference({
row: { column: [] }
}))
]);
}
return this.parse(argSql);
}
}
exports.ExpressionParser = ExpressionParser;
function toOperand(input) {
if (typeof input === "string") {
return psql_lang_1.Sql.code(input).parse(psql_lang_1.Expression).operand();
}
if (input instanceof psql_lang_1.Expression) {
return input.operand();
}
return input;
}
//# sourceMappingURL=ExpressionParser.js.map