UNPKG

ddl-manager

Version:

store postgres procedures and triggers in files

233 lines 8.09 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DatabaseFunction = void 0; const wrapText_1 = require("../postgres/wrapText"); const constants_1 = require("../postgres/constants"); const Comment_1 = require("./Comment"); const lodash_1 = require("lodash"); const Type_1 = require("./Type"); class DatabaseFunction { constructor(json) { Object.assign(this, json); this.language = json.language || "plpgsql"; this.name = this.name.slice(0, constants_1.MAX_NAME_LENGTH); this.comment = json.comment || Comment_1.Comment.fromFs({ objectType: "function" }); this.cacheSignature = this.comment.cacheSignature; this.frozen = this.comment.frozen; } equalName(schemaName) { if (!schemaName.includes(".")) { schemaName = "public." + schemaName; } return this.schema + "." + this.name === schemaName; } equal(otherFunc) { return (this.schema === otherFunc.schema && this.name === otherFunc.name && this.body === otherFunc.body && this.args.length === otherFunc.args.length && this.args.every((myArg, i) => equalArgument(myArg, otherFunc.args[i])) && equalReturns(this.returns, otherFunc.returns) && this.language === otherFunc.language && !!this.immutable === !!otherFunc.immutable && !!this.returnsNullOnNull === !!otherFunc.returnsNullOnNull && !!this.stable === !!otherFunc.stable && !!this.strict === !!otherFunc.strict && // null == undefined // tslint:disable-next-line: triple-equals String(this.parallel) == String(otherFunc.parallel) && this.comment.equal(otherFunc.comment)); } getSignature() { const argsTypes = this.args .filter((arg) => !arg.out) .map((arg) => arg.type); return `${this.schema}.${this.name}(${argsTypes.join(", ")})`; } findAssignColumns() { if (this.assignedColumns) { return this.assignedColumns; } const body = this.body .replace(/--[^\n\r]+/g, "") .replace(/\/\*.+?\*\//g, ""); const matches = body.match(/(begin|then|loop|;)\s*;?\s*new\.(\w+)\s*=/g) || []; const assignedColumns = matches.map(str => str .replace(/^((begin|then|loop)\s*;?|;)\s*new\./, "") .replace(/\s*=$/, "") .toLowerCase()); this.assignedColumns = lodash_1.uniq(assignedColumns).sort(); return this.assignedColumns; } toSQL(params = {}) { let additionalParams = ""; if (coalesce(params.immutable, this.immutable)) { additionalParams += " immutable"; } else if (coalesce(params.stable, this.stable)) { additionalParams += " stable"; } if (this.returnsNullOnNull) { additionalParams += " returns null on null input"; } else if (this.strict) { additionalParams += " strict"; } if (this.parallel) { additionalParams += " parallel "; additionalParams += this.parallel; } if (this.cost != null) { additionalParams += " cost " + this.cost; } if (additionalParams) { additionalParams = "\n" + additionalParams + "\n"; } const returnsSql = returns2sql(this.returns); let argsSql = this.args.map((arg) => " " + arg2sql(arg)).join(",\n"); if (this.args.length) { argsSql = "\n" + argsSql + "\n"; } // отступов не должно быть! // иначе DDLManager.dump будет писать некрасивый код return ` create or replace function ${this.schema === "public" ? "" : this.schema + "."}${this.name}(${argsSql}) returns ${returnsSql}${additionalParams} as ${wrapText_1.wrapText(coalesce(params.body, this.body), "body")} language ${this.language} `.trim(); } toSQLWithLog() { const DONT_LOG_FUNCS = [ "log", "cm_build_array_for", "get_options" ]; if (DONT_LOG_FUNCS.includes(this.name) || this.language === "sql") { return this.toSQL(); } // вставляем в самый первый begin let body = (" " + this.body).replace(/(([^\w_])begin[^\w_])/i, `$2 declare ___call_id integer; begin insert into system_calls ( tid, func_name, call_time ) values ( txid_current(), '${this.schema}.${this.name}', extract(epoch from timeofday()::timestamp without time zone) ) returning id into ___call_id; `); // перед каждым return body = body.replace(/([^\w_]return[^\w_]|[^\w_]end\s*;?\s*(\$\w+\$)?\s*$)/ig, ` insert into system_calls ( tid, end_time, end_id ) values ( txid_current(), extract(epoch from timeofday()::timestamp without time zone), ___call_id ) on conflict (end_id) do update set end_time = excluded.end_time, id = excluded.id; $1 `); const sql = this.toSQL({ body, // INSERT is not allowed in a non-volatile function immutable: false, stable: false }); return sql; } } exports.DatabaseFunction = DatabaseFunction; function returns2sql(returns) { let out = ""; if (returns.setof) { out += "setof "; } if (returns.table) { out += `table(${returns.table.map((arg) => arg2sql(arg)).join(", ")})`; } else { out += returns.type; } return out; } function arg2sql(arg) { let out = ""; if (arg.out) { out += "out "; } else if (arg.in) { out += "in "; } if (arg.name) { out += arg.name; out += " "; } out += arg.type; if (arg.default) { out += " default "; out += arg.default; } return out; } function equalArgument(argA, argB) { return ( // null == undefined // tslint:disable-next-line: triple-equals argA.name == argB.name && Type_1.equalType(argA.type, argB.type) && equalArgumentDefault(argA, argB) && !!argA.in === !!argB.in && !!argA.out === !!argB.out); } function equalArgumentDefault(argA, argB) { if (!argA.default && !argB.default) { return true; } // null == undefined // tslint:disable-next-line: triple-equals if (argA.default == argB.default) { return true; } const defaultA = formatDefault(argA); const defaultB = formatDefault(argB); return defaultA === defaultB; } function formatDefault(someArg) { let someDefault = ("" + someArg.default).trim().toLowerCase(); someDefault = someDefault.replace(/\s*::\s*([\w\s]+|numeric\([\d\s,]+\))(\[])?$/, ""); const type = Type_1.formatType(someArg.type); someDefault += "::" + type; if (someDefault.startsWith("{}::")) { someDefault = someDefault.replace("{}::", "'{}'::"); } return someDefault; } function equalReturns(returnsA, returnsB) { return (Type_1.equalType(returnsA.type, returnsB.type) && !!returnsA.setof === !!returnsB.setof && (returnsA.table && returnsB.table && returnsA.table.every((argA, i) => equalArgument(argA, returnsB.table[i])) || // null == undefined // tslint:disable-next-line: triple-equals returnsA.table == returnsB.table)); } function coalesce(...values) { return values.find(value => value != null); } //# sourceMappingURL=DatabaseFunction.js.map