ddl-manager
Version:
store postgres procedures and triggers in files
233 lines • 8.09 kB
JavaScript
"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