ddl-manager
Version:
store postgres procedures and triggers in files
183 lines • 7.45 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileParser = void 0;
const psql_lang_1 = require("psql-lang");
const ast_1 = require("../ast");
const CacheParser_1 = require("./CacheParser");
const Comment_1 = require("../database/schema/Comment");
const DatabaseFunction_1 = require("../database/schema/DatabaseFunction");
const DatabaseTrigger_1 = require("../database/schema/DatabaseTrigger");
const TableID_1 = require("../database/schema/TableID");
const defaults_1 = require("./defaults");
const FileSyntax_1 = require("./FileSyntax");
const assert_1 = __importDefault(require("assert"));
class FileParser {
static parse(sql) {
const parser = new FileParser();
return parser.parseSql(sql);
}
static parseFile(filePath) {
const parser = new FileParser();
return parser.parseFile(filePath);
}
static parseFunction(sql) {
const fileContent = FileParser.parse(sql);
assert_1.default.ok(fileContent, "should be not empty sql");
const func = (fileContent.functions || [])[0];
assert_1.default.ok(func instanceof DatabaseFunction_1.DatabaseFunction, "sql should contain function");
return func;
}
static parseCache(sql) {
const fileContent = FileParser.parse(sql);
assert_1.default.ok(fileContent, "should be not empty sql");
const cache = (fileContent.cache || [])[0];
assert_1.default.ok(cache instanceof ast_1.Cache, "sql should contain cache");
return cache;
}
parseFile(filePath) {
const { cursor } = psql_lang_1.Sql.file(filePath);
return this.parse(cursor);
}
parseSql(sql) {
const { cursor } = psql_lang_1.Sql.code(sql);
return this.parse(cursor);
}
parse(cursor) {
const content = cursor.parse(FileSyntax_1.FileSyntax);
const state = {
functions: [],
triggers: [],
cache: content.row.caches.map(buildCache)
};
for (const funcNode of content.row.functions) {
addFunction(cursor, state, funcNode);
}
for (const triggerNode of content.row.triggers) {
addTrigger(cursor, state, triggerNode);
}
return state;
}
}
exports.FileParser = FileParser;
function addFunction(cursor, state, funcNode) {
const func = buildFunction(funcNode);
// check duplicate
const isDuplicate = state.functions.some((prevFunc) => prevFunc.getSignature()
===
func.getSignature());
if (isDuplicate) {
cursor.throwError("duplicated function: " + func.getSignature(), funcNode);
}
// two function inside file, can be with only same name and schema
const isWrongName = state.functions.some((prevFunc) => prevFunc.name !== func.name ||
prevFunc.schema !== func.schema);
if (isWrongName) {
cursor.throwError("two function inside file, can be with only same name and schema", funcNode);
}
state.functions.push(func);
}
function addTrigger(cursor, state, triggerNode) {
const trigger = buildTrigger(triggerNode);
// validate function name and trigger procedure
const firstFunc = state.functions[0];
if (firstFunc) {
if (firstFunc.schema !== trigger.procedure.schema ||
firstFunc.name !== trigger.procedure.name) {
const wrongName = `${trigger.procedure.schema}.${trigger.procedure.name}`;
cursor.throwError(`wrong procedure name ${wrongName}`, triggerNode);
}
// validate function returns type
const hasTriggerFunc = state.functions.some((func) => func.returns.type === "trigger");
if (!hasTriggerFunc) {
cursor.throwError("file must contain function with returns type trigger", triggerNode);
}
}
// check duplicate
const isDuplicate = state.triggers.some((prevTrigger) => prevTrigger.getSignature()
===
trigger.getSignature());
if (isDuplicate) {
cursor.throwError("duplicated trigger: " + trigger.getSignature(), triggerNode);
}
state.triggers.push(trigger);
}
function buildFunction(funcNode) {
var _a, _b;
return new DatabaseFunction_1.DatabaseFunction({
schema: ((_a = funcNode.row.schema) === null || _a === void 0 ? void 0 : _a.toString()) || defaults_1.DEFAULT_SCHEMA,
name: funcNode.row.name.toString(),
args: funcNode.row.args.map(parseArg),
returns: parseReturns(funcNode),
body: funcNode.row.body.row.string,
language: funcNode.row.language,
immutable: funcNode.row.immutability === "immutable",
returnsNullOnNull: funcNode.row.inputNullsRule === "returns null on null input",
stable: funcNode.row.immutability === "stable",
strict: funcNode.row.inputNullsRule === "strict",
parallel: funcNode.row.parallel ? [funcNode.row.parallel] : undefined,
cost: Number((_b = funcNode.row.cost) === null || _b === void 0 ? void 0 : _b.toString()) || undefined,
comment: Comment_1.Comment.fromFs({
objectType: "function"
})
});
}
function buildTrigger(triggerNode) {
var _a, _b, _c;
return new DatabaseTrigger_1.DatabaseTrigger({
name: triggerNode.row.name.toString(),
table: TableID_1.TableID.fromString(triggerNode.row.table.toString()),
procedure: {
schema: ((_a = triggerNode.row.procedure.row.schema) === null || _a === void 0 ? void 0 : _a.toString()) || defaults_1.DEFAULT_SCHEMA,
name: (_b = triggerNode.row.procedure.row.name) === null || _b === void 0 ? void 0 : _b.toString(),
args: []
},
before: triggerNode.row.events.before,
after: !triggerNode.row.events.before,
insert: triggerNode.row.events.insert,
update: !!triggerNode.row.events.update,
updateOf: Array.isArray(triggerNode.row.events.update) ?
triggerNode.row.events.update
.map(name => name.toValue())
.sort() :
undefined,
delete: triggerNode.row.events.delete,
when: (_c = triggerNode.row.when) === null || _c === void 0 ? void 0 : _c.toString(),
constraint: triggerNode.row.constraint,
deferrable: triggerNode.row.deferrable,
statement: triggerNode.row.statement,
initially: triggerNode.row.initially,
comment: Comment_1.Comment.fromFs({
objectType: "trigger"
})
});
}
function buildCache(cacheSyntax) {
return CacheParser_1.CacheParser.parse(cacheSyntax);
}
function parseReturns(funcNode) {
const returns = funcNode.row.returns.row;
if ("table" in returns) {
return {
setof: returns.setOf,
table: returns.table.map(parseArg)
};
}
return {
setof: returns.setOf,
type: returns.type.toString()
};
}
function parseArg(argNode) {
var _a, _b;
return {
out: argNode.row.out,
in: argNode.row.in,
name: (_a = argNode.row.name) === null || _a === void 0 ? void 0 : _a.toString(),
type: argNode.row.type.toString(),
default: (_b = argNode.row.default) === null || _b === void 0 ? void 0 : _b.toString()
};
}
//# sourceMappingURL=FileParser.js.map