sedentary-pg 0.0.12 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/index.d.ts +5 -2
- package/index.js +11 -1
- package/lib/pgdb.d.ts +5 -5
- package/lib/pgdb.js +74 -76
- package/lib/pgdb.ts +79 -82
- package/package.json +11 -8
package/README.md
CHANGED
package/index.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Entry, Natural, SedentaryOptions, Sedentary, Type } from "sedentary";
|
|
2
2
|
import { PoolConfig } from "pg";
|
|
3
|
+
export { AttributeDefinition, AttributeOptions, AttributesDefinition, Entry, IndexAttributes, IndexDefinition, IndexOptions, IndexesDefinition } from "sedentary";
|
|
4
|
+
export { ModelOptions, Natural, SedentaryOptions, Type, TypeDefinition } from "sedentary";
|
|
3
5
|
export declare class SedentaryPG extends Sedentary {
|
|
4
|
-
constructor(connection: PoolConfig, options?:
|
|
6
|
+
constructor(connection: PoolConfig, options?: SedentaryOptions);
|
|
7
|
+
FKEY<N extends Natural, E extends Entry>(attribute: Type<N, E>): Type<N, E>;
|
|
5
8
|
}
|
|
6
9
|
export declare const Package: typeof SedentaryPG;
|
package/index.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Package = exports.SedentaryPG = void 0;
|
|
3
|
+
exports.Package = exports.SedentaryPG = exports.Type = exports.Entry = void 0;
|
|
4
4
|
const sedentary_1 = require("sedentary");
|
|
5
5
|
const pgdb_1 = require("./lib/pgdb");
|
|
6
|
+
var sedentary_2 = require("sedentary");
|
|
7
|
+
Object.defineProperty(exports, "Entry", { enumerable: true, get: function () { return sedentary_2.Entry; } });
|
|
8
|
+
var sedentary_3 = require("sedentary");
|
|
9
|
+
Object.defineProperty(exports, "Type", { enumerable: true, get: function () { return sedentary_3.Type; } });
|
|
6
10
|
class SedentaryPG extends sedentary_1.Sedentary {
|
|
7
11
|
constructor(connection, options) {
|
|
8
12
|
super("", options);
|
|
@@ -10,6 +14,12 @@ class SedentaryPG extends sedentary_1.Sedentary {
|
|
|
10
14
|
throw new Error("SedentaryPG.constructor: 'connection' argument: Wrong type, expected 'Object'");
|
|
11
15
|
this.db = new pgdb_1.PGDB(connection, this.log);
|
|
12
16
|
}
|
|
17
|
+
FKEY(attribute) {
|
|
18
|
+
const { attributeName, tableName, unique } = attribute;
|
|
19
|
+
if (!unique)
|
|
20
|
+
throw new Error(`Sedentary.FKEY: '${tableName}' table: '${attributeName}' attribute: is not unique: can't be used as FKEY target`);
|
|
21
|
+
return super.FKEY(attribute);
|
|
22
|
+
}
|
|
13
23
|
}
|
|
14
24
|
exports.SedentaryPG = SedentaryPG;
|
|
15
25
|
exports.Package = SedentaryPG;
|
package/lib/pgdb.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PoolConfig } from "pg";
|
|
2
|
-
import { DB,
|
|
2
|
+
import { Attribute, DB, Natural, Table } from "sedentary/lib/db";
|
|
3
3
|
export declare class PGDB extends DB {
|
|
4
4
|
private client;
|
|
5
5
|
private indexes;
|
|
@@ -7,13 +7,13 @@ export declare class PGDB extends DB {
|
|
|
7
7
|
private version;
|
|
8
8
|
constructor(connection: PoolConfig, log: (message: string) => void);
|
|
9
9
|
connect(): Promise<void>;
|
|
10
|
-
dropConstraints(table: Table): Promise<
|
|
10
|
+
dropConstraints(table: Table): Promise<number[]>;
|
|
11
11
|
dropField(tableName: string, fieldName: string): Promise<void>;
|
|
12
12
|
dropFields(table: Table): Promise<void>;
|
|
13
|
-
dropIndexes(table: Table): Promise<void>;
|
|
13
|
+
dropIndexes(table: Table, constraintIndexes: number[]): Promise<void>;
|
|
14
14
|
end(): Promise<void>;
|
|
15
|
-
fieldType(
|
|
16
|
-
|
|
15
|
+
fieldType(attribute: Attribute<Natural, unknown>): string[];
|
|
16
|
+
syncDataBase(): Promise<void>;
|
|
17
17
|
syncConstraints(table: Table): Promise<void>;
|
|
18
18
|
syncFields(table: Table): Promise<void>;
|
|
19
19
|
syncIndexes(table: Table): Promise<void>;
|
package/lib/pgdb.js
CHANGED
|
@@ -31,34 +31,19 @@ class PGDB extends db_1.DB {
|
|
|
31
31
|
this.version = parseInt(res.rows[0].version.split(" ")[1].split(".")[0], 10);
|
|
32
32
|
}
|
|
33
33
|
async dropConstraints(table) {
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return;
|
|
46
|
-
/*
|
|
47
|
-
return pgo.client.query("SELECT conindid FROM pg_attribute, pg_constraint WHERE attrelid = $1 AND conrelid = $1 AND attnum = conkey[1]", [table.oid], function(err, res) {
|
|
48
|
-
const arr = [];
|
|
49
|
-
|
|
50
|
-
if(pgo.error(err, 1011, table.name)) return;
|
|
51
|
-
|
|
52
|
-
for(const i in res.rows) arr.push(res.rows[i].conindid);
|
|
53
|
-
|
|
54
|
-
dropIndex(pgo, arr);
|
|
55
|
-
});
|
|
56
|
-
*/
|
|
57
|
-
for (const i in res.rows) {
|
|
58
|
-
const statement = `ALTER TABLE ${table.tableName} DROP CONSTRAINT ${res.rows[i].conname}`;
|
|
59
|
-
this.log(statement);
|
|
60
|
-
await this.client.query(statement);
|
|
34
|
+
const indexes = [];
|
|
35
|
+
const res = await this.client.query("SELECT * FROM pg_constraint WHERE conrelid = $1 ORDER BY conname", [table.oid]);
|
|
36
|
+
for (const row of res.rows) {
|
|
37
|
+
const constraint = table.constraints.filter(_ => _.constraintName === row.conname);
|
|
38
|
+
if (constraint.length === 0) {
|
|
39
|
+
const statement = `ALTER TABLE ${table.tableName} DROP CONSTRAINT ${row.conname} CASCADE`;
|
|
40
|
+
this.log(statement);
|
|
41
|
+
await this.client.query(statement);
|
|
42
|
+
}
|
|
43
|
+
else
|
|
44
|
+
indexes.push(row.conindid);
|
|
61
45
|
}
|
|
46
|
+
return indexes;
|
|
62
47
|
}
|
|
63
48
|
async dropField(tableName, fieldName) {
|
|
64
49
|
const statement = `ALTER TABLE ${tableName} DROP COLUMN ${fieldName}`;
|
|
@@ -68,26 +53,28 @@ class PGDB extends db_1.DB {
|
|
|
68
53
|
async dropFields(table) {
|
|
69
54
|
const res = await this.client.query("SELECT attname FROM pg_attribute WHERE attrelid = $1 AND attnum > 0 AND attisdropped = false AND attinhcount = 0", [table.oid]);
|
|
70
55
|
for (const i in res.rows)
|
|
71
|
-
if (table.
|
|
56
|
+
if (!table.findField(res.rows[i].attname))
|
|
72
57
|
await this.dropField(table.tableName, res.rows[i].attname);
|
|
73
58
|
}
|
|
74
|
-
async dropIndexes(table) {
|
|
59
|
+
async dropIndexes(table, constraintIndexes) {
|
|
75
60
|
const { indexes, oid } = table;
|
|
76
61
|
const iobject = {};
|
|
77
|
-
const res = await this.client.query("SELECT amname, attname, indisunique, relname FROM pg_class, pg_index, pg_attribute, pg_am WHERE indrelid = $1 AND indexrelid = pg_class.oid AND attrelid = pg_class.oid AND relam = pg_am.oid ORDER BY attnum", [oid]);
|
|
62
|
+
const res = await this.client.query("SELECT amname, attname, indexrelid, indisunique, relname FROM pg_class, pg_index, pg_attribute, pg_am WHERE indrelid = $1 AND indexrelid = pg_class.oid AND attrelid = pg_class.oid AND relam = pg_am.oid ORDER BY attnum", [oid]);
|
|
78
63
|
for (const row of res.rows) {
|
|
79
|
-
const { amname, attname, indisunique, relname } = row;
|
|
80
|
-
if (
|
|
81
|
-
iobject[relname]
|
|
82
|
-
|
|
83
|
-
|
|
64
|
+
const { amname, attname, indexrelid, indisunique, relname } = row;
|
|
65
|
+
if (!constraintIndexes.includes(indexrelid)) {
|
|
66
|
+
if (iobject[relname])
|
|
67
|
+
iobject[relname].fields.push(attname);
|
|
68
|
+
else
|
|
69
|
+
iobject[relname] = { fields: [attname], indexName: relname, type: amname, unique: indisunique };
|
|
70
|
+
}
|
|
84
71
|
}
|
|
85
72
|
this.indexes = [];
|
|
86
73
|
for (const index of indexes) {
|
|
87
|
-
const {
|
|
88
|
-
if (iobject[
|
|
89
|
-
this.indexes.push(
|
|
90
|
-
delete iobject[
|
|
74
|
+
const { indexName } = index;
|
|
75
|
+
if (iobject[indexName] && this.indexesEq(index, iobject[indexName])) {
|
|
76
|
+
this.indexes.push(indexName);
|
|
77
|
+
delete iobject[indexName];
|
|
91
78
|
}
|
|
92
79
|
}
|
|
93
80
|
for (const index of Object.keys(iobject).sort()) {
|
|
@@ -99,8 +86,8 @@ class PGDB extends db_1.DB {
|
|
|
99
86
|
async end() {
|
|
100
87
|
await this.pool.end();
|
|
101
88
|
}
|
|
102
|
-
fieldType(
|
|
103
|
-
const { size, type } =
|
|
89
|
+
fieldType(attribute) {
|
|
90
|
+
const { size, type } = attribute;
|
|
104
91
|
let ret;
|
|
105
92
|
switch (type) {
|
|
106
93
|
case "DATETIME":
|
|
@@ -115,10 +102,10 @@ class PGDB extends db_1.DB {
|
|
|
115
102
|
}
|
|
116
103
|
throw new Error(`Unknown type: '${type}', '${size}'`);
|
|
117
104
|
}
|
|
118
|
-
async
|
|
105
|
+
async syncDataBase() {
|
|
119
106
|
let err;
|
|
120
107
|
try {
|
|
121
|
-
await super.
|
|
108
|
+
await super.syncDataBase();
|
|
122
109
|
}
|
|
123
110
|
catch (e) {
|
|
124
111
|
err = e;
|
|
@@ -128,26 +115,36 @@ class PGDB extends db_1.DB {
|
|
|
128
115
|
throw err;
|
|
129
116
|
}
|
|
130
117
|
async syncConstraints(table) {
|
|
131
|
-
for (const
|
|
132
|
-
const
|
|
118
|
+
for (const constraint of table.constraints) {
|
|
119
|
+
const { attribute, constraintName, type } = constraint;
|
|
133
120
|
const res = await this.client.query("SELECT attname FROM pg_attribute, pg_constraint WHERE attrelid = $1 AND conrelid = $1 AND attnum = conkey[1] AND attname = $2", [
|
|
134
121
|
table.oid,
|
|
135
|
-
|
|
122
|
+
attribute.fieldName
|
|
136
123
|
]);
|
|
137
124
|
if (!res.rowCount) {
|
|
138
|
-
|
|
125
|
+
let query;
|
|
126
|
+
switch (type) {
|
|
127
|
+
case "f":
|
|
128
|
+
query = `FOREIGN KEY (${attribute.fieldName}) REFERENCES ${attribute.foreignKey.tableName}(${attribute.foreignKey.fieldName})`;
|
|
129
|
+
break;
|
|
130
|
+
case "u":
|
|
131
|
+
query = `UNIQUE(${attribute.fieldName})`;
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
const statement = `ALTER TABLE ${table.tableName} ADD CONSTRAINT ${constraintName} ${query}`;
|
|
139
135
|
this.log(statement);
|
|
140
136
|
await this.client.query(statement);
|
|
141
137
|
}
|
|
142
138
|
}
|
|
143
139
|
}
|
|
144
140
|
async syncFields(table) {
|
|
145
|
-
const {
|
|
146
|
-
for (const
|
|
147
|
-
const { fieldName, notNull, size } =
|
|
148
|
-
const defaultValue =
|
|
149
|
-
const [base, type] = this.fieldType(
|
|
150
|
-
const
|
|
141
|
+
const { attributes, oid, tableName } = table;
|
|
142
|
+
for (const attribute of attributes) {
|
|
143
|
+
const { fieldName, notNull, size } = attribute;
|
|
144
|
+
const defaultValue = attribute.defaultValue === undefined ? undefined : (0, pg_format_1.default)("%L", attribute.defaultValue);
|
|
145
|
+
const [base, type] = this.fieldType(attribute);
|
|
146
|
+
const adsrc = this.version >= 12 ? "pg_get_expr(pg_attrdef.adbin, pg_attrdef.adrelid) AS adsrc" : "adsrc";
|
|
147
|
+
const res = await this.client.query(`SELECT attnotnull, atttypmod, typname, ${adsrc} FROM pg_type, pg_attribute LEFT JOIN pg_attrdef ON adrelid = attrelid AND adnum = attnum WHERE attrelid = $1 AND attnum > 0 AND atttypid = pg_type.oid AND attislocal = 't' AND attname = $2`, [oid, fieldName]);
|
|
151
148
|
const addField = async () => {
|
|
152
149
|
const statement = `ALTER TABLE ${tableName} ADD COLUMN ${fieldName} ${type}`;
|
|
153
150
|
this.log(statement);
|
|
@@ -158,13 +155,6 @@ class PGDB extends db_1.DB {
|
|
|
158
155
|
this.log(statement);
|
|
159
156
|
await this.client.query(statement);
|
|
160
157
|
};
|
|
161
|
-
const setDefault = async () => {
|
|
162
|
-
if (defaultValue === undefined)
|
|
163
|
-
return;
|
|
164
|
-
const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} SET DEFAULT ${defaultValue}`;
|
|
165
|
-
this.log(statement);
|
|
166
|
-
await this.client.query(statement);
|
|
167
|
-
};
|
|
168
158
|
const setNotNull = async (isNull) => {
|
|
169
159
|
if (isNull === notNull)
|
|
170
160
|
return;
|
|
@@ -172,29 +162,39 @@ class PGDB extends db_1.DB {
|
|
|
172
162
|
this.log(statement);
|
|
173
163
|
await this.client.query(statement);
|
|
174
164
|
};
|
|
165
|
+
const setDefault = async (isNull) => {
|
|
166
|
+
if (defaultValue !== undefined) {
|
|
167
|
+
let statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} SET DEFAULT ${defaultValue}`;
|
|
168
|
+
this.log(statement);
|
|
169
|
+
await this.client.query(statement);
|
|
170
|
+
if (isNull) {
|
|
171
|
+
statement = `UPDATE ${tableName} SET ${fieldName} = ${defaultValue} WHERE ${fieldName} IS NULL`;
|
|
172
|
+
this.log(statement);
|
|
173
|
+
this.client.query(statement);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
await setNotNull(isNull);
|
|
177
|
+
};
|
|
175
178
|
if (!res.rowCount) {
|
|
176
179
|
await addField();
|
|
177
|
-
await setDefault();
|
|
178
|
-
await setNotNull(false);
|
|
180
|
+
await setDefault(false);
|
|
179
181
|
}
|
|
180
182
|
else {
|
|
181
183
|
const { adsrc, attnotnull, atttypmod, typname } = res.rows[0];
|
|
182
184
|
if (types[typname] !== base || (base === "VARCHAR" && (size ? size + 4 !== atttypmod : atttypmod !== -1))) {
|
|
183
|
-
if (needDrop.filter(([type, name]) =>
|
|
185
|
+
if (needDrop.filter(([type, name]) => attribute.type === type && typname === name).length) {
|
|
184
186
|
await this.dropField(tableName, fieldName);
|
|
185
187
|
await addField();
|
|
186
|
-
await setDefault();
|
|
187
|
-
await setNotNull(false);
|
|
188
|
+
await setDefault(false);
|
|
188
189
|
}
|
|
189
190
|
else {
|
|
190
191
|
if (adsrc)
|
|
191
192
|
dropDefault();
|
|
192
|
-
const using = needUsing.filter(([type, name]) =>
|
|
193
|
+
const using = needUsing.filter(([type, name]) => attribute.type === type && typname === name).length ? " USING " + fieldName + "::" + type : "";
|
|
193
194
|
const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} TYPE ${type}${using}`;
|
|
194
195
|
this.log(statement);
|
|
195
196
|
await this.client.query(statement);
|
|
196
|
-
await setDefault();
|
|
197
|
-
await setNotNull(attnotnull);
|
|
197
|
+
await setDefault(attnotnull);
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
else if (defaultValue === undefined) {
|
|
@@ -202,19 +202,17 @@ class PGDB extends db_1.DB {
|
|
|
202
202
|
dropDefault();
|
|
203
203
|
await setNotNull(attnotnull);
|
|
204
204
|
}
|
|
205
|
-
else if (!adsrc || adsrc.split("::")[0] !== defaultValue)
|
|
206
|
-
await setDefault();
|
|
207
|
-
await setNotNull(attnotnull);
|
|
208
|
-
}
|
|
205
|
+
else if (!adsrc || adsrc.split("::")[0] !== defaultValue)
|
|
206
|
+
await setDefault(attnotnull);
|
|
209
207
|
}
|
|
210
208
|
}
|
|
211
209
|
}
|
|
212
210
|
async syncIndexes(table) {
|
|
213
211
|
const { indexes, tableName } = table;
|
|
214
212
|
for (const index of indexes) {
|
|
215
|
-
const { fields,
|
|
216
|
-
if (this.indexes.
|
|
217
|
-
const statement = `CREATE${unique ? " UNIQUE" : ""} INDEX ${
|
|
213
|
+
const { fields, indexName, type, unique } = index;
|
|
214
|
+
if (!this.indexes.includes(indexName)) {
|
|
215
|
+
const statement = `CREATE${unique ? " UNIQUE" : ""} INDEX ${indexName} ON ${tableName} USING ${type} (${fields.join(", ")})`;
|
|
218
216
|
this.log(statement);
|
|
219
217
|
await this.client.query(statement);
|
|
220
218
|
}
|
|
@@ -256,7 +254,7 @@ class PGDB extends db_1.DB {
|
|
|
256
254
|
if (resParent.rowCount) {
|
|
257
255
|
if (!table.parent)
|
|
258
256
|
drop = true;
|
|
259
|
-
else if (this.
|
|
257
|
+
else if (this.findTable(table.parent.tableName).oid === resParent.rows[0].inhparent)
|
|
260
258
|
return;
|
|
261
259
|
drop = true;
|
|
262
260
|
}
|
package/lib/pgdb.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Pool, PoolClient, PoolConfig } from "pg";
|
|
2
2
|
import format from "pg-format";
|
|
3
|
-
import { DB,
|
|
3
|
+
import { Attribute, DB, Index, Natural, Table } from "sedentary/lib/db";
|
|
4
4
|
|
|
5
5
|
const needDrop = [
|
|
6
6
|
["DATETIME", "int2"],
|
|
@@ -36,39 +36,22 @@ export class PGDB extends DB {
|
|
|
36
36
|
this.version = parseInt(res.rows[0].version.split(" ")[1].split(".")[0], 10);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
async dropConstraints(table: Table): Promise<
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
const values: (number | string)[] = [table.oid];
|
|
43
|
-
const wheres = [];
|
|
39
|
+
async dropConstraints(table: Table): Promise<number[]> {
|
|
40
|
+
const indexes: number[] = [];
|
|
41
|
+
const res = await this.client.query("SELECT * FROM pg_constraint WHERE conrelid = $1 ORDER BY conname", [table.oid]);
|
|
44
42
|
|
|
45
|
-
for(const
|
|
46
|
-
|
|
47
|
-
values.push(table.constraints[i].type);
|
|
48
|
-
values.push(table.constraints[i].field);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const res = await this.client.query(query + wheres.join(""), values);
|
|
52
|
-
|
|
53
|
-
if(! res.rowCount) return;
|
|
54
|
-
/*
|
|
55
|
-
return pgo.client.query("SELECT conindid FROM pg_attribute, pg_constraint WHERE attrelid = $1 AND conrelid = $1 AND attnum = conkey[1]", [table.oid], function(err, res) {
|
|
56
|
-
const arr = [];
|
|
57
|
-
|
|
58
|
-
if(pgo.error(err, 1011, table.name)) return;
|
|
59
|
-
|
|
60
|
-
for(const i in res.rows) arr.push(res.rows[i].conindid);
|
|
61
|
-
|
|
62
|
-
dropIndex(pgo, arr);
|
|
63
|
-
});
|
|
64
|
-
*/
|
|
43
|
+
for(const row of res.rows) {
|
|
44
|
+
const constraint = table.constraints.filter(_ => _.constraintName === row.conname);
|
|
65
45
|
|
|
66
|
-
|
|
67
|
-
|
|
46
|
+
if(constraint.length === 0) {
|
|
47
|
+
const statement = `ALTER TABLE ${table.tableName} DROP CONSTRAINT ${row.conname} CASCADE`;
|
|
68
48
|
|
|
69
|
-
|
|
70
|
-
|
|
49
|
+
this.log(statement);
|
|
50
|
+
await this.client.query(statement);
|
|
51
|
+
} else indexes.push(row.conindid);
|
|
71
52
|
}
|
|
53
|
+
|
|
54
|
+
return indexes;
|
|
72
55
|
}
|
|
73
56
|
|
|
74
57
|
async dropField(tableName: string, fieldName: string): Promise<void> {
|
|
@@ -81,31 +64,33 @@ export class PGDB extends DB {
|
|
|
81
64
|
async dropFields(table: Table): Promise<void> {
|
|
82
65
|
const res = await this.client.query("SELECT attname FROM pg_attribute WHERE attrelid = $1 AND attnum > 0 AND attisdropped = false AND attinhcount = 0", [table.oid]);
|
|
83
66
|
|
|
84
|
-
for(const i in res.rows) if(table.
|
|
67
|
+
for(const i in res.rows) if(! table.findField(res.rows[i].attname)) await this.dropField(table.tableName, res.rows[i].attname);
|
|
85
68
|
}
|
|
86
69
|
|
|
87
|
-
async dropIndexes(table: Table): Promise<void> {
|
|
70
|
+
async dropIndexes(table: Table, constraintIndexes: number[]): Promise<void> {
|
|
88
71
|
const { indexes, oid } = table;
|
|
89
|
-
const iobject: { [key: string]:
|
|
72
|
+
const iobject: { [key: string]: Index } = {};
|
|
90
73
|
const res = await this.client.query(
|
|
91
|
-
"SELECT amname, attname, indisunique, relname FROM pg_class, pg_index, pg_attribute, pg_am WHERE indrelid = $1 AND indexrelid = pg_class.oid AND attrelid = pg_class.oid AND relam = pg_am.oid ORDER BY attnum",
|
|
74
|
+
"SELECT amname, attname, indexrelid, indisunique, relname FROM pg_class, pg_index, pg_attribute, pg_am WHERE indrelid = $1 AND indexrelid = pg_class.oid AND attrelid = pg_class.oid AND relam = pg_am.oid ORDER BY attnum",
|
|
92
75
|
[oid]
|
|
93
76
|
);
|
|
94
77
|
|
|
95
78
|
for(const row of res.rows) {
|
|
96
|
-
const { amname, attname, indisunique, relname } = row;
|
|
79
|
+
const { amname, attname, indexrelid, indisunique, relname } = row;
|
|
97
80
|
|
|
98
|
-
if(
|
|
99
|
-
|
|
81
|
+
if(! constraintIndexes.includes(indexrelid)) {
|
|
82
|
+
if(iobject[relname]) iobject[relname].fields.push(attname);
|
|
83
|
+
else iobject[relname] = { fields: [attname], indexName: relname, type: amname, unique: indisunique };
|
|
84
|
+
}
|
|
100
85
|
}
|
|
101
86
|
|
|
102
87
|
this.indexes = [];
|
|
103
88
|
for(const index of indexes) {
|
|
104
|
-
const {
|
|
89
|
+
const { indexName } = index;
|
|
105
90
|
|
|
106
|
-
if(iobject[
|
|
107
|
-
this.indexes.push(
|
|
108
|
-
delete iobject[
|
|
91
|
+
if(iobject[indexName] && this.indexesEq(index, iobject[indexName])) {
|
|
92
|
+
this.indexes.push(indexName);
|
|
93
|
+
delete iobject[indexName];
|
|
109
94
|
}
|
|
110
95
|
}
|
|
111
96
|
|
|
@@ -121,8 +106,8 @@ export class PGDB extends DB {
|
|
|
121
106
|
await this.pool.end();
|
|
122
107
|
}
|
|
123
108
|
|
|
124
|
-
fieldType(
|
|
125
|
-
const { size, type } =
|
|
109
|
+
fieldType(attribute: Attribute<Natural, unknown>): string[] {
|
|
110
|
+
const { size, type } = attribute;
|
|
126
111
|
let ret;
|
|
127
112
|
|
|
128
113
|
switch(type) {
|
|
@@ -141,11 +126,11 @@ export class PGDB extends DB {
|
|
|
141
126
|
throw new Error(`Unknown type: '${type}', '${size}'`);
|
|
142
127
|
}
|
|
143
128
|
|
|
144
|
-
async
|
|
129
|
+
async syncDataBase(): Promise<void> {
|
|
145
130
|
let err: Error;
|
|
146
131
|
|
|
147
132
|
try {
|
|
148
|
-
await super.
|
|
133
|
+
await super.syncDataBase();
|
|
149
134
|
} catch(e) {
|
|
150
135
|
err = e;
|
|
151
136
|
}
|
|
@@ -156,16 +141,26 @@ export class PGDB extends DB {
|
|
|
156
141
|
}
|
|
157
142
|
|
|
158
143
|
async syncConstraints(table: Table): Promise<void> {
|
|
159
|
-
for(const
|
|
160
|
-
const
|
|
161
|
-
|
|
144
|
+
for(const constraint of table.constraints) {
|
|
145
|
+
const { attribute, constraintName, type } = constraint;
|
|
162
146
|
const res = await this.client.query("SELECT attname FROM pg_attribute, pg_constraint WHERE attrelid = $1 AND conrelid = $1 AND attnum = conkey[1] AND attname = $2", [
|
|
163
147
|
table.oid,
|
|
164
|
-
|
|
148
|
+
attribute.fieldName
|
|
165
149
|
]);
|
|
166
150
|
|
|
167
151
|
if(! res.rowCount) {
|
|
168
|
-
|
|
152
|
+
let query: string;
|
|
153
|
+
|
|
154
|
+
switch(type) {
|
|
155
|
+
case "f":
|
|
156
|
+
query = `FOREIGN KEY (${attribute.fieldName}) REFERENCES ${attribute.foreignKey.tableName}(${attribute.foreignKey.fieldName})`;
|
|
157
|
+
break;
|
|
158
|
+
case "u":
|
|
159
|
+
query = `UNIQUE(${attribute.fieldName})`;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const statement = `ALTER TABLE ${table.tableName} ADD CONSTRAINT ${constraintName} ${query}`;
|
|
169
164
|
|
|
170
165
|
this.log(statement);
|
|
171
166
|
await this.client.query(statement);
|
|
@@ -174,17 +169,16 @@ export class PGDB extends DB {
|
|
|
174
169
|
}
|
|
175
170
|
|
|
176
171
|
async syncFields(table: Table): Promise<void> {
|
|
177
|
-
const {
|
|
172
|
+
const { attributes, oid, tableName } = table;
|
|
178
173
|
|
|
179
|
-
for(const
|
|
180
|
-
const { fieldName, notNull, size } =
|
|
181
|
-
const defaultValue =
|
|
182
|
-
const [base, type] = this.fieldType(
|
|
174
|
+
for(const attribute of attributes) {
|
|
175
|
+
const { fieldName, notNull, size } = attribute;
|
|
176
|
+
const defaultValue = attribute.defaultValue === undefined ? undefined : format("%L", attribute.defaultValue);
|
|
177
|
+
const [base, type] = this.fieldType(attribute);
|
|
178
|
+
const adsrc = this.version >= 12 ? "pg_get_expr(pg_attrdef.adbin, pg_attrdef.adrelid) AS adsrc" : "adsrc";
|
|
183
179
|
|
|
184
180
|
const res = await this.client.query(
|
|
185
|
-
`SELECT
|
|
186
|
-
this.version >= 12 ? ", pg_get_expr(pg_attrdef.adbin, pg_attrdef.adrelid) AS adsrc" : ""
|
|
187
|
-
} FROM pg_type, pg_attribute LEFT JOIN pg_attrdef ON adrelid = attrelid AND adnum = attnum WHERE attrelid = $1 AND attnum > 0 AND atttypid = pg_type.oid AND attislocal = 't' AND attname = $2`,
|
|
181
|
+
`SELECT attnotnull, atttypmod, typname, ${adsrc} FROM pg_type, pg_attribute LEFT JOIN pg_attrdef ON adrelid = attrelid AND adnum = attnum WHERE attrelid = $1 AND attnum > 0 AND atttypid = pg_type.oid AND attislocal = 't' AND attname = $2`,
|
|
188
182
|
[oid, fieldName]
|
|
189
183
|
);
|
|
190
184
|
|
|
@@ -202,55 +196,58 @@ export class PGDB extends DB {
|
|
|
202
196
|
await this.client.query(statement);
|
|
203
197
|
};
|
|
204
198
|
|
|
205
|
-
const
|
|
206
|
-
if(
|
|
199
|
+
const setNotNull = async (isNull: boolean) => {
|
|
200
|
+
if(isNull === notNull) return;
|
|
207
201
|
|
|
208
|
-
const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} SET
|
|
202
|
+
const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} ${notNull ? "SET" : "DROP"} NOT NULL`;
|
|
209
203
|
|
|
210
204
|
this.log(statement);
|
|
211
205
|
await this.client.query(statement);
|
|
212
206
|
};
|
|
213
207
|
|
|
214
|
-
const
|
|
215
|
-
if(
|
|
208
|
+
const setDefault = async (isNull: boolean) => {
|
|
209
|
+
if(defaultValue !== undefined) {
|
|
210
|
+
let statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} SET DEFAULT ${defaultValue}`;
|
|
216
211
|
|
|
217
|
-
|
|
212
|
+
this.log(statement);
|
|
213
|
+
await this.client.query(statement);
|
|
218
214
|
|
|
219
|
-
|
|
220
|
-
|
|
215
|
+
if(isNull) {
|
|
216
|
+
statement = `UPDATE ${tableName} SET ${fieldName} = ${defaultValue} WHERE ${fieldName} IS NULL`;
|
|
217
|
+
|
|
218
|
+
this.log(statement);
|
|
219
|
+
this.client.query(statement);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
await setNotNull(isNull);
|
|
221
224
|
};
|
|
222
225
|
|
|
223
226
|
if(! res.rowCount) {
|
|
224
227
|
await addField();
|
|
225
|
-
await setDefault();
|
|
226
|
-
await setNotNull(false);
|
|
228
|
+
await setDefault(false);
|
|
227
229
|
} else {
|
|
228
230
|
const { adsrc, attnotnull, atttypmod, typname } = res.rows[0];
|
|
229
231
|
|
|
230
232
|
if(types[typname] !== base || (base === "VARCHAR" && (size ? size + 4 !== atttypmod : atttypmod !== -1))) {
|
|
231
|
-
if(needDrop.filter(([type, name]) =>
|
|
233
|
+
if(needDrop.filter(([type, name]) => attribute.type === type && typname === name).length) {
|
|
232
234
|
await this.dropField(tableName, fieldName);
|
|
233
235
|
await addField();
|
|
234
|
-
await setDefault();
|
|
235
|
-
await setNotNull(false);
|
|
236
|
+
await setDefault(false);
|
|
236
237
|
} else {
|
|
237
238
|
if(adsrc) dropDefault();
|
|
238
239
|
|
|
239
|
-
const using = needUsing.filter(([type, name]) =>
|
|
240
|
+
const using = needUsing.filter(([type, name]) => attribute.type === type && typname === name).length ? " USING " + fieldName + "::" + type : "";
|
|
240
241
|
const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} TYPE ${type}${using}`;
|
|
241
242
|
|
|
242
243
|
this.log(statement);
|
|
243
244
|
await this.client.query(statement);
|
|
244
|
-
await setDefault();
|
|
245
|
-
await setNotNull(attnotnull);
|
|
245
|
+
await setDefault(attnotnull);
|
|
246
246
|
}
|
|
247
247
|
} else if(defaultValue === undefined) {
|
|
248
248
|
if(adsrc) dropDefault();
|
|
249
249
|
await setNotNull(attnotnull);
|
|
250
|
-
} else if(! adsrc || adsrc.split("::")[0] !== defaultValue)
|
|
251
|
-
await setDefault();
|
|
252
|
-
await setNotNull(attnotnull);
|
|
253
|
-
}
|
|
250
|
+
} else if(! adsrc || adsrc.split("::")[0] !== defaultValue) await setDefault(attnotnull);
|
|
254
251
|
}
|
|
255
252
|
}
|
|
256
253
|
}
|
|
@@ -259,10 +256,10 @@ export class PGDB extends DB {
|
|
|
259
256
|
const { indexes, tableName } = table;
|
|
260
257
|
|
|
261
258
|
for(const index of indexes) {
|
|
262
|
-
const { fields,
|
|
259
|
+
const { fields, indexName, type, unique } = index;
|
|
263
260
|
|
|
264
|
-
if(this.indexes.
|
|
265
|
-
const statement = `CREATE${unique ? " UNIQUE" : ""} INDEX ${
|
|
261
|
+
if(! this.indexes.includes(indexName)) {
|
|
262
|
+
const statement = `CREATE${unique ? " UNIQUE" : ""} INDEX ${indexName} ON ${tableName} USING ${type} (${fields.join(", ")})`;
|
|
266
263
|
|
|
267
264
|
this.log(statement);
|
|
268
265
|
await this.client.query(statement);
|
|
@@ -312,7 +309,7 @@ export class PGDB extends DB {
|
|
|
312
309
|
|
|
313
310
|
if(resParent.rowCount) {
|
|
314
311
|
if(! table.parent) drop = true;
|
|
315
|
-
else if(this.
|
|
312
|
+
else if(this.findTable(table.parent.tableName).oid === resParent.rows[0].inhparent) return;
|
|
316
313
|
|
|
317
314
|
drop = true;
|
|
318
315
|
} else if(table.parent) drop = true;
|
package/package.json
CHANGED
|
@@ -6,26 +6,29 @@
|
|
|
6
6
|
"@types/pg-format": "1.0.2",
|
|
7
7
|
"pg": "8.7.1",
|
|
8
8
|
"pg-format": "1.0.4",
|
|
9
|
-
"sedentary": "0.0.
|
|
9
|
+
"sedentary": "0.0.16"
|
|
10
10
|
},
|
|
11
11
|
"description": "The ORM which never needs to migrate - PostgreSQL",
|
|
12
12
|
"devDependencies": {
|
|
13
13
|
"@types/mocha": "9.0.0",
|
|
14
|
-
"@types/node": "16.11.
|
|
14
|
+
"@types/node": "16.11.10",
|
|
15
15
|
"@types/yamljs": "0.2.31",
|
|
16
|
-
"@typescript-eslint/eslint-plugin": "5.
|
|
17
|
-
"@typescript-eslint/parser": "5.
|
|
18
|
-
"eslint": "8.
|
|
16
|
+
"@typescript-eslint/eslint-plugin": "5.4.0",
|
|
17
|
+
"@typescript-eslint/parser": "5.4.0",
|
|
18
|
+
"eslint": "8.3.0",
|
|
19
19
|
"mocha": "9.1.3",
|
|
20
20
|
"nyc": "15.1.0",
|
|
21
|
-
"prettier": "2.
|
|
21
|
+
"prettier": "2.5.0",
|
|
22
22
|
"ts-node": "10.4.0",
|
|
23
|
-
"typescript": "4.
|
|
23
|
+
"typescript": "4.5.2",
|
|
24
24
|
"yamljs": "0.3.0"
|
|
25
25
|
},
|
|
26
26
|
"engines": {
|
|
27
27
|
"node": ">=12.0"
|
|
28
28
|
},
|
|
29
|
+
"funding": {
|
|
30
|
+
"url": "https://blockchain.info/address/1Md9WFAHrXTb3yPBwQWmUfv2RmzrtbHioB"
|
|
31
|
+
},
|
|
29
32
|
"homepage": "https://github.com/iccicci/sedentary-pg#readme",
|
|
30
33
|
"keywords": [
|
|
31
34
|
"DB",
|
|
@@ -59,5 +62,5 @@
|
|
|
59
62
|
"version": "node -r ts-node/register utils.ts version"
|
|
60
63
|
},
|
|
61
64
|
"types": "index.d.ts",
|
|
62
|
-
"version": "0.0.
|
|
65
|
+
"version": "0.0.16"
|
|
63
66
|
}
|