sedentary-pg 0.0.18 → 0.0.23
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 +28 -0
- package/{lib/adsrc.d.ts → adsrc.d.ts} +0 -0
- package/{lib/adsrc.js → adsrc.js} +0 -0
- package/index.d.ts +4 -4
- package/index.js +5 -6
- package/package.json +32 -10
- package/{lib/pgdb.d.ts → pgdb.d.ts} +15 -5
- package/{lib/pgdb.js → pgdb.js} +107 -50
- package/lib/adsrc.ts +0 -3
- package/lib/pgdb.ts +0 -351
package/README.md
CHANGED
|
@@ -33,6 +33,34 @@
|
|
|
33
33
|
|
|
34
34
|
The **PostgreSQL** specilized package of [Sedentary](https://www.npmjs.com/package/sedentary).
|
|
35
35
|
|
|
36
|
+
# Usage
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
import { SedentaryPG } from "sedentary-pg";
|
|
40
|
+
|
|
41
|
+
const db = new SedentaryPG(/* PG connection */);
|
|
42
|
+
|
|
43
|
+
class Items extends db.model("Item", {
|
|
44
|
+
num: db.INT,
|
|
45
|
+
str: db.VARCHAR(30)
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
(async function () {
|
|
49
|
+
await db.connect();
|
|
50
|
+
|
|
51
|
+
const item = Items.create();
|
|
52
|
+
|
|
53
|
+
item.num = 0;
|
|
54
|
+
item.str = "0";
|
|
55
|
+
|
|
56
|
+
await item.save();
|
|
57
|
+
|
|
58
|
+
const records = await Items.load({});
|
|
59
|
+
|
|
60
|
+
console.log(records); // [{ id: 1, num: 0, str: "0" }]
|
|
61
|
+
})();
|
|
62
|
+
```
|
|
63
|
+
|
|
36
64
|
# Installation
|
|
37
65
|
|
|
38
66
|
With [npm](https://www.npmjs.com/package/sedentary-pg):
|
|
File without changes
|
|
File without changes
|
package/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EntryBase, ForeignKeyOptions, Natural, Sedentary, SedentaryOptions, Type } from "sedentary";
|
|
2
|
+
import { Attribute } from "sedentary/db";
|
|
2
3
|
import { PoolConfig } from "pg";
|
|
3
|
-
export {
|
|
4
|
-
export { IndexAttributes, IndexDefinition, IndexOptions, IndexesDefinition, ModelOptions, Natural, SedentaryOptions, Type, TypeDefinition } from "sedentary";
|
|
4
|
+
export { EntryBase, SedentaryOptions, Type } from "sedentary";
|
|
5
5
|
export declare class SedentaryPG extends Sedentary {
|
|
6
6
|
constructor(connection: PoolConfig, options?: SedentaryOptions);
|
|
7
|
-
FKEY<N extends Natural, E extends
|
|
7
|
+
FKEY<N extends Natural, E extends EntryBase>(attribute: Attribute<N, E>, options?: ForeignKeyOptions): Type<N, E>;
|
|
8
8
|
}
|
|
9
9
|
export declare const Package: typeof SedentaryPG;
|
package/index.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Package = exports.SedentaryPG = exports.Type = exports.
|
|
3
|
+
exports.Package = exports.SedentaryPG = exports.Type = exports.EntryBase = void 0;
|
|
4
4
|
const sedentary_1 = require("sedentary");
|
|
5
|
-
const pgdb_1 = require("./
|
|
5
|
+
const pgdb_1 = require("./pgdb");
|
|
6
6
|
var sedentary_2 = require("sedentary");
|
|
7
|
-
Object.defineProperty(exports, "
|
|
8
|
-
|
|
9
|
-
Object.defineProperty(exports, "Type", { enumerable: true, get: function () { return sedentary_3.Type; } });
|
|
7
|
+
Object.defineProperty(exports, "EntryBase", { enumerable: true, get: function () { return sedentary_2.EntryBase; } });
|
|
8
|
+
Object.defineProperty(exports, "Type", { enumerable: true, get: function () { return sedentary_2.Type; } });
|
|
10
9
|
class SedentaryPG extends sedentary_1.Sedentary {
|
|
11
10
|
constructor(connection, options) {
|
|
12
|
-
super(
|
|
11
|
+
super(options);
|
|
13
12
|
if (!(connection instanceof Object))
|
|
14
13
|
throw new Error("SedentaryPG.constructor: 'connection' argument: Wrong type, expected 'Object'");
|
|
15
14
|
this.db = new pgdb_1.PGDB(connection, this.log);
|
package/package.json
CHANGED
|
@@ -1,26 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"author": "Daniele Ricci <daniele.icc@gmail.com> (https://github.com/iccicci)",
|
|
3
3
|
"bugs": "https://github.com/iccicci/sedentary-pg/issues",
|
|
4
|
+
"contributors": [
|
|
5
|
+
"Daniele Ricci <daniele.icc@gmail.com> (https://github.com/iccicci)",
|
|
6
|
+
"yossarian <sergiybiluk@gmail.com> (https://github.com/captain-yossarian)"
|
|
7
|
+
],
|
|
4
8
|
"dependencies": {
|
|
5
|
-
"@types/pg": "8.6.1",
|
|
6
|
-
"@types/pg-format": "1.0.2",
|
|
7
9
|
"pg": "8.7.1",
|
|
8
10
|
"pg-format": "1.0.4",
|
|
9
|
-
"sedentary": "0.0.
|
|
11
|
+
"sedentary": "0.0.23"
|
|
10
12
|
},
|
|
11
13
|
"description": "The ORM which never needs to migrate - PostgreSQL",
|
|
12
14
|
"devDependencies": {
|
|
13
15
|
"@types/mocha": "9.0.0",
|
|
14
|
-
"@types/node": "
|
|
16
|
+
"@types/node": "17.0.5",
|
|
17
|
+
"@types/pg": "8.6.3",
|
|
18
|
+
"@types/pg-format": "1.0.2",
|
|
15
19
|
"@types/yamljs": "0.2.31",
|
|
16
|
-
"@typescript-eslint/eslint-plugin": "5.
|
|
17
|
-
"@typescript-eslint/parser": "5.
|
|
18
|
-
"eslint": "8.
|
|
20
|
+
"@typescript-eslint/eslint-plugin": "5.8.1",
|
|
21
|
+
"@typescript-eslint/parser": "5.8.1",
|
|
22
|
+
"eslint": "8.5.0",
|
|
19
23
|
"mocha": "9.1.3",
|
|
20
24
|
"nyc": "15.1.0",
|
|
21
|
-
"prettier": "2.5.
|
|
25
|
+
"prettier": "2.5.1",
|
|
22
26
|
"ts-node": "10.4.0",
|
|
23
|
-
"
|
|
27
|
+
"tsd": "0.19.0",
|
|
28
|
+
"typescript": "4.5.4",
|
|
24
29
|
"yamljs": "0.3.0"
|
|
25
30
|
},
|
|
26
31
|
"engines": {
|
|
@@ -61,6 +66,23 @@
|
|
|
61
66
|
"tsc": "tsc --declaration",
|
|
62
67
|
"version": "node -r ts-node/register utils.ts version"
|
|
63
68
|
},
|
|
69
|
+
"tsd": {
|
|
70
|
+
"compilerOptions": {
|
|
71
|
+
"alwaysStrict": true,
|
|
72
|
+
"declaration": true,
|
|
73
|
+
"esModuleInterop": true,
|
|
74
|
+
"module": "commonjs",
|
|
75
|
+
"noImplicitAny": true,
|
|
76
|
+
"noImplicitReturns": true,
|
|
77
|
+
"noImplicitThis": true,
|
|
78
|
+
"strict": true,
|
|
79
|
+
"strictBindCallApply": true,
|
|
80
|
+
"strictFunctionTypes": true,
|
|
81
|
+
"strictNullChecks": true,
|
|
82
|
+
"strictPropertyInitialization": true,
|
|
83
|
+
"target": "es2017"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
64
86
|
"types": "index.d.ts",
|
|
65
|
-
"version": "0.0.
|
|
87
|
+
"version": "0.0.23"
|
|
66
88
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PoolConfig } from "pg";
|
|
2
|
-
import { Attribute, DB, Natural, Table } from "sedentary/
|
|
1
|
+
import { PoolClient, PoolConfig } from "pg";
|
|
2
|
+
import { Attribute, DB, Natural, Table, Transaction } from "sedentary/db";
|
|
3
3
|
export declare class PGDB extends DB {
|
|
4
4
|
private client;
|
|
5
5
|
private indexes;
|
|
@@ -7,16 +7,26 @@ 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
|
+
end(): Promise<void>;
|
|
11
|
+
defaultNeq(src: string, value: Natural): boolean;
|
|
12
|
+
escape(value: Natural): string;
|
|
13
|
+
fill(attributes: Attribute<Natural, unknown>[], row: Record<string, Natural>, entity: Record<string, Natural>): void;
|
|
14
|
+
save(tableName: string, attributes: Attribute<Natural, unknown>[]): (this: Record<string, Natural> & {
|
|
15
|
+
loaded?: Record<string, unknown>;
|
|
16
|
+
tx?: TransactionPG;
|
|
17
|
+
}) => Promise<boolean>;
|
|
10
18
|
dropConstraints(table: Table): Promise<number[]>;
|
|
11
19
|
dropField(tableName: string, fieldName: string): Promise<void>;
|
|
12
20
|
dropFields(table: Table): Promise<void>;
|
|
13
21
|
dropIndexes(table: Table, constraintIndexes: number[]): Promise<void>;
|
|
14
|
-
end(): Promise<void>;
|
|
15
|
-
fieldType(attribute: Attribute<Natural, unknown>): string[];
|
|
16
|
-
syncDataBase(): Promise<void>;
|
|
17
22
|
syncConstraints(table: Table): Promise<void>;
|
|
23
|
+
fieldType(attribute: Attribute<Natural, unknown>): string[];
|
|
18
24
|
syncFields(table: Table): Promise<void>;
|
|
19
25
|
syncIndexes(table: Table): Promise<void>;
|
|
20
26
|
syncSequence(table: Table): Promise<void>;
|
|
21
27
|
syncTable(table: Table): Promise<void>;
|
|
22
28
|
}
|
|
29
|
+
declare class TransactionPG extends Transaction {
|
|
30
|
+
client: PoolClient;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
package/{lib/pgdb.js → pgdb.js}
RENAMED
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.PGDB = void 0;
|
|
7
7
|
const pg_1 = require("pg");
|
|
8
8
|
const pg_format_1 = __importDefault(require("pg-format"));
|
|
9
|
-
const db_1 = require("sedentary/
|
|
9
|
+
const db_1 = require("sedentary/db");
|
|
10
10
|
const adsrc_1 = require("./adsrc");
|
|
11
11
|
const needDrop = [
|
|
12
12
|
["DATETIME", "int2"],
|
|
@@ -20,18 +20,85 @@ const needUsing = [
|
|
|
20
20
|
["INT", "varchar"],
|
|
21
21
|
["INT8", "varchar"]
|
|
22
22
|
];
|
|
23
|
-
const types = { int2: "SMALLINT", int4: "INTEGER", int8: "BIGINT", varchar: "VARCHAR" };
|
|
23
|
+
const types = { int2: "SMALLINT", int4: "INTEGER", int8: "BIGINT", timestamptz: "DATETIME", varchar: "VARCHAR" };
|
|
24
24
|
const actions = { cascade: "c", "no action": "a", restrict: "r", "set default": "d", "set null": "n" };
|
|
25
25
|
class PGDB extends db_1.DB {
|
|
26
26
|
constructor(connection, log) {
|
|
27
27
|
super(log);
|
|
28
|
+
this.client = {};
|
|
29
|
+
this.indexes = [];
|
|
28
30
|
this.pool = new pg_1.Pool(connection);
|
|
31
|
+
this.version = 0;
|
|
29
32
|
}
|
|
30
33
|
async connect() {
|
|
31
34
|
this.client = await this.pool.connect();
|
|
32
35
|
const res = await this.client.query("SELECT version()");
|
|
33
36
|
this.version = parseInt(res.rows[0].version.split(" ")[1].split(".")[0], 10);
|
|
34
37
|
}
|
|
38
|
+
async end() {
|
|
39
|
+
this.client.release();
|
|
40
|
+
await this.pool.end();
|
|
41
|
+
}
|
|
42
|
+
defaultNeq(src, value) {
|
|
43
|
+
if (src === value)
|
|
44
|
+
return false;
|
|
45
|
+
return src.split("::")[0] !== value;
|
|
46
|
+
}
|
|
47
|
+
escape(value) {
|
|
48
|
+
if (value === null || value === undefined)
|
|
49
|
+
throw new Error("SedentaryPG: Can't escape null nor undefined values; use the 'IS NULL' operator instead");
|
|
50
|
+
const type = typeof value;
|
|
51
|
+
if (type === "number" || type === "boolean")
|
|
52
|
+
return value.toString();
|
|
53
|
+
if (type === "string")
|
|
54
|
+
return (0, pg_format_1.default)("%L", value);
|
|
55
|
+
if (value instanceof Date)
|
|
56
|
+
return (0, pg_format_1.default)("%L", value).replace(/\.\d\d\d\+/, "+");
|
|
57
|
+
return (0, pg_format_1.default)("%L", JSON.stringify(value));
|
|
58
|
+
}
|
|
59
|
+
fill(attributes, row, entity) {
|
|
60
|
+
for (const attribute of attributes)
|
|
61
|
+
entity[attribute.attributeName] = row[attribute.fieldName];
|
|
62
|
+
}
|
|
63
|
+
save(tableName, attributes) {
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
65
|
+
const self = this;
|
|
66
|
+
return async function () {
|
|
67
|
+
const client = this.tx ? this.tx.client : await self.pool.connect();
|
|
68
|
+
if (!this.loaded) {
|
|
69
|
+
const fields = [];
|
|
70
|
+
const values = [];
|
|
71
|
+
for (const attribute of attributes) {
|
|
72
|
+
const value = this[attribute.attributeName];
|
|
73
|
+
if (value !== null && value !== undefined) {
|
|
74
|
+
fields.push(attribute.fieldName);
|
|
75
|
+
values.push(self.escape(this[attribute.attributeName]));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const query = fields.length ? `INSERT INTO ${tableName} (${fields.join(",")}) VALUES (${values.join(",")})` : `INSERT INTO ${tableName} DEFAULT VALUES`;
|
|
79
|
+
try {
|
|
80
|
+
self.log(query);
|
|
81
|
+
self.fill(attributes, (await client.query(query + " RETURNING *")).rows[0], this);
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
if (!this.tx)
|
|
85
|
+
client.release();
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
let changed = false;
|
|
90
|
+
const { loaded } = this;
|
|
91
|
+
for (const key in this) {
|
|
92
|
+
if (typeof this[key] === "object") {
|
|
93
|
+
if (JSON.stringify(this[key]) !== JSON.stringify(loaded[key]))
|
|
94
|
+
changed = true;
|
|
95
|
+
}
|
|
96
|
+
else if (this[key] !== loaded[key])
|
|
97
|
+
changed = true;
|
|
98
|
+
}
|
|
99
|
+
return Promise.resolve(changed);
|
|
100
|
+
};
|
|
101
|
+
}
|
|
35
102
|
async dropConstraints(table) {
|
|
36
103
|
const indexes = [];
|
|
37
104
|
const res = await this.client.query("SELECT confdeltype, confupdtype, conindid, conname, contype FROM pg_constraint WHERE conrelid = $1 ORDER BY conname", [table.oid]);
|
|
@@ -96,37 +163,6 @@ class PGDB extends db_1.DB {
|
|
|
96
163
|
await this.client.query(statement);
|
|
97
164
|
}
|
|
98
165
|
}
|
|
99
|
-
async end() {
|
|
100
|
-
await this.pool.end();
|
|
101
|
-
}
|
|
102
|
-
fieldType(attribute) {
|
|
103
|
-
const { size, type } = attribute;
|
|
104
|
-
let ret;
|
|
105
|
-
switch (type) {
|
|
106
|
-
case "DATETIME":
|
|
107
|
-
return ["DATETIME", "TIMESTAMP (3) WITH TIME ZONE"];
|
|
108
|
-
case "INT":
|
|
109
|
-
ret = size === 2 ? "SMALLINT" : "INTEGER";
|
|
110
|
-
return [ret, ret];
|
|
111
|
-
case "INT8":
|
|
112
|
-
return ["BIGINT", "BIGINT"];
|
|
113
|
-
case "VARCHAR":
|
|
114
|
-
return ["VARCHAR", "VARCHAR" + (size ? `(${size})` : "")];
|
|
115
|
-
}
|
|
116
|
-
throw new Error(`Unknown type: '${type}', '${size}'`);
|
|
117
|
-
}
|
|
118
|
-
async syncDataBase() {
|
|
119
|
-
let err;
|
|
120
|
-
try {
|
|
121
|
-
await super.syncDataBase();
|
|
122
|
-
}
|
|
123
|
-
catch (e) {
|
|
124
|
-
err = e;
|
|
125
|
-
}
|
|
126
|
-
this.client.release();
|
|
127
|
-
if (err)
|
|
128
|
-
throw err;
|
|
129
|
-
}
|
|
130
166
|
async syncConstraints(table) {
|
|
131
167
|
for (const constraint of table.constraints) {
|
|
132
168
|
const { attribute, constraintName, type } = constraint;
|
|
@@ -151,11 +187,27 @@ class PGDB extends db_1.DB {
|
|
|
151
187
|
}
|
|
152
188
|
}
|
|
153
189
|
}
|
|
190
|
+
fieldType(attribute) {
|
|
191
|
+
const { size, type } = attribute;
|
|
192
|
+
let ret;
|
|
193
|
+
switch (type) {
|
|
194
|
+
case "DATETIME":
|
|
195
|
+
return ["DATETIME", "TIMESTAMP (3) WITH TIME ZONE"];
|
|
196
|
+
case "INT":
|
|
197
|
+
ret = size === 2 ? "SMALLINT" : "INTEGER";
|
|
198
|
+
return [ret, ret];
|
|
199
|
+
case "INT8":
|
|
200
|
+
return ["BIGINT", "BIGINT"];
|
|
201
|
+
case "VARCHAR":
|
|
202
|
+
return ["VARCHAR", "VARCHAR" + (size ? `(${size})` : "")];
|
|
203
|
+
}
|
|
204
|
+
throw new Error(`Unknown type: '${type}', '${size}'`);
|
|
205
|
+
}
|
|
154
206
|
async syncFields(table) {
|
|
155
|
-
const { attributes, oid, tableName } = table;
|
|
207
|
+
const { attributes, autoIncrement, oid, tableName } = table;
|
|
156
208
|
for (const attribute of attributes) {
|
|
157
209
|
const { fieldName, notNull, size } = attribute;
|
|
158
|
-
const defaultValue = attribute.defaultValue === undefined ? undefined :
|
|
210
|
+
const defaultValue = attribute.defaultValue === undefined ? (autoIncrement && fieldName === "id" ? `nextval('${tableName}_id_seq'::regclass)` : undefined) : this.escape(attribute.defaultValue);
|
|
159
211
|
const [base, type] = this.fieldType(attribute);
|
|
160
212
|
const where = "attrelid = $1 AND attnum > 0 AND atttypid = pg_type.oid AND attislocal = 't' AND attname = $2";
|
|
161
213
|
const res = await this.client.query(`SELECT attnotnull, atttypmod, typname, ${(0, adsrc_1.adsrc)(this.version)} FROM pg_type, pg_attribute LEFT JOIN pg_attrdef ON adrelid = attrelid AND adnum = attnum WHERE ${where}`, [oid, fieldName]);
|
|
@@ -171,32 +223,32 @@ class PGDB extends db_1.DB {
|
|
|
171
223
|
if (this.sync)
|
|
172
224
|
await this.client.query(statement);
|
|
173
225
|
};
|
|
174
|
-
const setNotNull = async (
|
|
175
|
-
if (
|
|
226
|
+
const setNotNull = async (isNotNull) => {
|
|
227
|
+
if (isNotNull === notNull)
|
|
176
228
|
return;
|
|
177
229
|
const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} ${notNull ? "SET" : "DROP"} NOT NULL`;
|
|
178
230
|
this.syncLog(statement);
|
|
179
231
|
if (this.sync)
|
|
180
232
|
await this.client.query(statement);
|
|
181
233
|
};
|
|
182
|
-
const setDefault = async (
|
|
234
|
+
const setDefault = async (isNotNull, create) => {
|
|
183
235
|
if (defaultValue !== undefined) {
|
|
184
236
|
let statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} SET DEFAULT ${defaultValue}`;
|
|
185
237
|
this.syncLog(statement);
|
|
186
238
|
if (this.sync)
|
|
187
239
|
await this.client.query(statement);
|
|
188
|
-
if (
|
|
240
|
+
if (!isNotNull && !create) {
|
|
189
241
|
statement = `UPDATE ${tableName} SET ${fieldName} = ${defaultValue} WHERE ${fieldName} IS NULL`;
|
|
190
242
|
this.syncLog(statement);
|
|
191
243
|
if (this.sync)
|
|
192
244
|
this.client.query(statement);
|
|
193
245
|
}
|
|
194
246
|
}
|
|
195
|
-
await setNotNull(
|
|
247
|
+
await setNotNull(isNotNull);
|
|
196
248
|
};
|
|
197
249
|
if (!res.rowCount) {
|
|
198
250
|
await addField();
|
|
199
|
-
await setDefault(false);
|
|
251
|
+
await setDefault(false, true);
|
|
200
252
|
}
|
|
201
253
|
else {
|
|
202
254
|
const { adsrc, attnotnull, atttypmod, typname } = res.rows[0];
|
|
@@ -204,7 +256,7 @@ class PGDB extends db_1.DB {
|
|
|
204
256
|
if (needDrop.filter(([type, name]) => attribute.type === type && typname === name).length) {
|
|
205
257
|
await this.dropField(tableName, fieldName);
|
|
206
258
|
await addField();
|
|
207
|
-
await setDefault(false);
|
|
259
|
+
await setDefault(false, true);
|
|
208
260
|
}
|
|
209
261
|
else {
|
|
210
262
|
if (adsrc)
|
|
@@ -214,7 +266,7 @@ class PGDB extends db_1.DB {
|
|
|
214
266
|
this.syncLog(statement);
|
|
215
267
|
if (this.sync)
|
|
216
268
|
await this.client.query(statement);
|
|
217
|
-
await setDefault(attnotnull);
|
|
269
|
+
await setDefault(attnotnull, false);
|
|
218
270
|
}
|
|
219
271
|
}
|
|
220
272
|
else if (defaultValue === undefined) {
|
|
@@ -222,8 +274,8 @@ class PGDB extends db_1.DB {
|
|
|
222
274
|
dropDefault();
|
|
223
275
|
await setNotNull(attnotnull);
|
|
224
276
|
}
|
|
225
|
-
else if (!adsrc ||
|
|
226
|
-
await setDefault(attnotnull);
|
|
277
|
+
else if (!adsrc || this.defaultNeq(adsrc, defaultValue))
|
|
278
|
+
await setDefault(attnotnull, false);
|
|
227
279
|
}
|
|
228
280
|
}
|
|
229
281
|
}
|
|
@@ -255,9 +307,9 @@ class PGDB extends db_1.DB {
|
|
|
255
307
|
await this.client.query(`SELECT currval('${table.tableName}_id_seq')`);
|
|
256
308
|
}
|
|
257
309
|
catch (e) {
|
|
258
|
-
if (e.code === "55000")
|
|
310
|
+
if (e instanceof pg_1.DatabaseError && e.code === "55000")
|
|
259
311
|
return;
|
|
260
|
-
if (e.code === "42P01") {
|
|
312
|
+
if (e instanceof pg_1.DatabaseError && e.code === "42P01") {
|
|
261
313
|
const statement = `CREATE SEQUENCE ${table.tableName}_id_seq`;
|
|
262
314
|
this.syncLog(statement);
|
|
263
315
|
if (this.sync)
|
|
@@ -269,11 +321,11 @@ class PGDB extends db_1.DB {
|
|
|
269
321
|
}
|
|
270
322
|
})();
|
|
271
323
|
}
|
|
272
|
-
let create;
|
|
324
|
+
let create = false;
|
|
273
325
|
const resTable = await this.client.query("SELECT oid FROM pg_class WHERE relname = $1", [table.tableName]);
|
|
274
326
|
if (resTable.rowCount) {
|
|
275
327
|
table.oid = resTable.rows[0].oid;
|
|
276
|
-
let drop;
|
|
328
|
+
let drop = false;
|
|
277
329
|
const resParent = await this.client.query("SELECT inhparent FROM pg_inherits WHERE inhrelid = $1", [table.oid]);
|
|
278
330
|
if (resParent.rowCount) {
|
|
279
331
|
if (!table.parent)
|
|
@@ -306,4 +358,9 @@ class PGDB extends db_1.DB {
|
|
|
306
358
|
}
|
|
307
359
|
}
|
|
308
360
|
exports.PGDB = PGDB;
|
|
309
|
-
|
|
361
|
+
class TransactionPG extends db_1.Transaction {
|
|
362
|
+
constructor() {
|
|
363
|
+
super(...arguments);
|
|
364
|
+
this.client = {};
|
|
365
|
+
}
|
|
366
|
+
}
|
package/lib/adsrc.ts
DELETED
package/lib/pgdb.ts
DELETED
|
@@ -1,351 +0,0 @@
|
|
|
1
|
-
import { Pool, PoolClient, PoolConfig } from "pg";
|
|
2
|
-
import format from "pg-format";
|
|
3
|
-
import { Attribute, DB, Index, Natural, ForeignKeyActions, Table } from "sedentary/lib/db";
|
|
4
|
-
import { adsrc } from "./adsrc";
|
|
5
|
-
|
|
6
|
-
const needDrop = [
|
|
7
|
-
["DATETIME", "int2"],
|
|
8
|
-
["DATETIME", "int4"],
|
|
9
|
-
["DATETIME", "int8"],
|
|
10
|
-
["INT", "timestamptz"],
|
|
11
|
-
["INT8", "timestamptz"]
|
|
12
|
-
];
|
|
13
|
-
const needUsing = [
|
|
14
|
-
["DATETIME", "varchar"],
|
|
15
|
-
["INT", "varchar"],
|
|
16
|
-
["INT8", "varchar"]
|
|
17
|
-
];
|
|
18
|
-
const types = { int2: "SMALLINT", int4: "INTEGER", int8: "BIGINT", varchar: "VARCHAR" };
|
|
19
|
-
|
|
20
|
-
const actions: { [k in ForeignKeyActions]: string } = { cascade: "c", "no action": "a", restrict: "r", "set default": "d", "set null": "n" };
|
|
21
|
-
|
|
22
|
-
export class PGDB extends DB {
|
|
23
|
-
private client: PoolClient;
|
|
24
|
-
private indexes: string[];
|
|
25
|
-
private pool: Pool;
|
|
26
|
-
private version: number;
|
|
27
|
-
|
|
28
|
-
constructor(connection: PoolConfig, log: (message: string) => void) {
|
|
29
|
-
super(log);
|
|
30
|
-
|
|
31
|
-
this.pool = new Pool(connection);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async connect(): Promise<void> {
|
|
35
|
-
this.client = await this.pool.connect();
|
|
36
|
-
|
|
37
|
-
const res = await this.client.query("SELECT version()");
|
|
38
|
-
|
|
39
|
-
this.version = parseInt(res.rows[0].version.split(" ")[1].split(".")[0], 10);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async dropConstraints(table: Table): Promise<number[]> {
|
|
43
|
-
const indexes: number[] = [];
|
|
44
|
-
const res = await this.client.query("SELECT confdeltype, confupdtype, conindid, conname, contype FROM pg_constraint WHERE conrelid = $1 ORDER BY conname", [table.oid]);
|
|
45
|
-
|
|
46
|
-
for(const row of res.rows) {
|
|
47
|
-
const arr = table.constraints.filter(_ => _.constraintName === row.conname && _.type === row.contype);
|
|
48
|
-
let drop = false;
|
|
49
|
-
|
|
50
|
-
if(arr.length === 0) drop = true;
|
|
51
|
-
else if(row.contype === "u") indexes.push(row.conindid);
|
|
52
|
-
else {
|
|
53
|
-
const { options } = arr[0].attribute.foreignKey;
|
|
54
|
-
|
|
55
|
-
if(actions[options.onDelete] !== row.confdeltype || actions[options.onUpdate] !== row.confupdtype) drop = true;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if(drop) {
|
|
59
|
-
const statement = `ALTER TABLE ${table.tableName} DROP CONSTRAINT ${row.conname} CASCADE`;
|
|
60
|
-
|
|
61
|
-
this.syncLog(statement);
|
|
62
|
-
if(this.sync) await this.client.query(statement);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return indexes;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async dropField(tableName: string, fieldName: string): Promise<void> {
|
|
70
|
-
const statement = `ALTER TABLE ${tableName} DROP COLUMN ${fieldName}`;
|
|
71
|
-
|
|
72
|
-
this.syncLog(statement);
|
|
73
|
-
if(this.sync) await this.client.query(statement);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
async dropFields(table: Table): Promise<void> {
|
|
77
|
-
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]);
|
|
78
|
-
|
|
79
|
-
for(const i in res.rows) if(! table.findField(res.rows[i].attname)) await this.dropField(table.tableName, res.rows[i].attname);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async dropIndexes(table: Table, constraintIndexes: number[]): Promise<void> {
|
|
83
|
-
const { indexes, oid } = table;
|
|
84
|
-
const iobject: { [key: string]: Index } = {};
|
|
85
|
-
const res = await this.client.query(
|
|
86
|
-
"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",
|
|
87
|
-
[oid]
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
for(const row of res.rows) {
|
|
91
|
-
const { amname, attname, indexrelid, indisunique, relname } = row;
|
|
92
|
-
|
|
93
|
-
if(! constraintIndexes.includes(indexrelid)) {
|
|
94
|
-
if(iobject[relname]) iobject[relname].fields.push(attname);
|
|
95
|
-
else iobject[relname] = { fields: [attname], indexName: relname, type: amname, unique: indisunique };
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
this.indexes = [];
|
|
100
|
-
for(const index of indexes) {
|
|
101
|
-
const { indexName } = index;
|
|
102
|
-
|
|
103
|
-
if(iobject[indexName] && this.indexesEq(index, iobject[indexName])) {
|
|
104
|
-
this.indexes.push(indexName);
|
|
105
|
-
delete iobject[indexName];
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
for(const index of Object.keys(iobject).sort()) {
|
|
110
|
-
const statement = `DROP INDEX ${index}`;
|
|
111
|
-
|
|
112
|
-
this.syncLog(statement);
|
|
113
|
-
if(this.sync) await this.client.query(statement);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
async end(): Promise<void> {
|
|
118
|
-
await this.pool.end();
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
fieldType(attribute: Attribute<Natural, unknown>): string[] {
|
|
122
|
-
const { size, type } = attribute;
|
|
123
|
-
let ret;
|
|
124
|
-
|
|
125
|
-
switch(type) {
|
|
126
|
-
case "DATETIME":
|
|
127
|
-
return ["DATETIME", "TIMESTAMP (3) WITH TIME ZONE"];
|
|
128
|
-
case "INT":
|
|
129
|
-
ret = size === 2 ? "SMALLINT" : "INTEGER";
|
|
130
|
-
|
|
131
|
-
return [ret, ret];
|
|
132
|
-
case "INT8":
|
|
133
|
-
return ["BIGINT", "BIGINT"];
|
|
134
|
-
case "VARCHAR":
|
|
135
|
-
return ["VARCHAR", "VARCHAR" + (size ? `(${size})` : "")];
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
throw new Error(`Unknown type: '${type}', '${size}'`);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
async syncDataBase(): Promise<void> {
|
|
142
|
-
let err: Error;
|
|
143
|
-
|
|
144
|
-
try {
|
|
145
|
-
await super.syncDataBase();
|
|
146
|
-
} catch(e) {
|
|
147
|
-
err = e;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
this.client.release();
|
|
151
|
-
|
|
152
|
-
if(err) throw err;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
async syncConstraints(table: Table): Promise<void> {
|
|
156
|
-
for(const constraint of table.constraints) {
|
|
157
|
-
const { attribute, constraintName, type } = constraint;
|
|
158
|
-
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", [
|
|
159
|
-
table.oid,
|
|
160
|
-
attribute.fieldName
|
|
161
|
-
]);
|
|
162
|
-
|
|
163
|
-
if(! res.rowCount) {
|
|
164
|
-
let query: string;
|
|
165
|
-
|
|
166
|
-
if(type === "f") {
|
|
167
|
-
const { fieldName, options, tableName } = attribute.foreignKey;
|
|
168
|
-
const onDelete = options.onDelete !== "no action" ? ` ON DELETE ${options.onDelete.toUpperCase()}` : "";
|
|
169
|
-
const onUpdate = options.onUpdate !== "no action" ? ` ON UPDATE ${options.onUpdate.toUpperCase()}` : "";
|
|
170
|
-
|
|
171
|
-
query = `FOREIGN KEY (${attribute.fieldName}) REFERENCES ${tableName}(${fieldName})${onDelete}${onUpdate}`;
|
|
172
|
-
} else query = `UNIQUE(${attribute.fieldName})`;
|
|
173
|
-
|
|
174
|
-
const statement = `ALTER TABLE ${table.tableName} ADD CONSTRAINT ${constraintName} ${query}`;
|
|
175
|
-
|
|
176
|
-
this.syncLog(statement);
|
|
177
|
-
if(this.sync) await this.client.query(statement);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
async syncFields(table: Table): Promise<void> {
|
|
183
|
-
const { attributes, oid, tableName } = table;
|
|
184
|
-
|
|
185
|
-
for(const attribute of attributes) {
|
|
186
|
-
const { fieldName, notNull, size } = attribute;
|
|
187
|
-
const defaultValue = attribute.defaultValue === undefined ? undefined : format("%L", attribute.defaultValue);
|
|
188
|
-
const [base, type] = this.fieldType(attribute);
|
|
189
|
-
const where = "attrelid = $1 AND attnum > 0 AND atttypid = pg_type.oid AND attislocal = 't' AND attname = $2";
|
|
190
|
-
|
|
191
|
-
const res = await this.client.query(
|
|
192
|
-
`SELECT attnotnull, atttypmod, typname, ${adsrc(this.version)} FROM pg_type, pg_attribute LEFT JOIN pg_attrdef ON adrelid = attrelid AND adnum = attnum WHERE ${where}`,
|
|
193
|
-
[oid, fieldName]
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
const addField = async () => {
|
|
197
|
-
const statement = `ALTER TABLE ${tableName} ADD COLUMN ${fieldName} ${type}`;
|
|
198
|
-
|
|
199
|
-
this.syncLog(statement);
|
|
200
|
-
if(this.sync) await this.client.query(statement);
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
const dropDefault = async () => {
|
|
204
|
-
const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} DROP DEFAULT`;
|
|
205
|
-
|
|
206
|
-
this.syncLog(statement);
|
|
207
|
-
if(this.sync) await this.client.query(statement);
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
const setNotNull = async (isNull: boolean) => {
|
|
211
|
-
if(isNull === notNull) return;
|
|
212
|
-
|
|
213
|
-
const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} ${notNull ? "SET" : "DROP"} NOT NULL`;
|
|
214
|
-
|
|
215
|
-
this.syncLog(statement);
|
|
216
|
-
if(this.sync) await this.client.query(statement);
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
const setDefault = async (isNull: boolean) => {
|
|
220
|
-
if(defaultValue !== undefined) {
|
|
221
|
-
let statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} SET DEFAULT ${defaultValue}`;
|
|
222
|
-
|
|
223
|
-
this.syncLog(statement);
|
|
224
|
-
if(this.sync) await this.client.query(statement);
|
|
225
|
-
|
|
226
|
-
if(isNull) {
|
|
227
|
-
statement = `UPDATE ${tableName} SET ${fieldName} = ${defaultValue} WHERE ${fieldName} IS NULL`;
|
|
228
|
-
|
|
229
|
-
this.syncLog(statement);
|
|
230
|
-
if(this.sync) this.client.query(statement);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
await setNotNull(isNull);
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
if(! res.rowCount) {
|
|
238
|
-
await addField();
|
|
239
|
-
await setDefault(false);
|
|
240
|
-
} else {
|
|
241
|
-
const { adsrc, attnotnull, atttypmod, typname } = res.rows[0];
|
|
242
|
-
|
|
243
|
-
if(types[typname] !== base || (base === "VARCHAR" && (size ? size + 4 !== atttypmod : atttypmod !== -1))) {
|
|
244
|
-
if(needDrop.filter(([type, name]) => attribute.type === type && typname === name).length) {
|
|
245
|
-
await this.dropField(tableName, fieldName);
|
|
246
|
-
await addField();
|
|
247
|
-
await setDefault(false);
|
|
248
|
-
} else {
|
|
249
|
-
if(adsrc) dropDefault();
|
|
250
|
-
|
|
251
|
-
const using = needUsing.filter(([type, name]) => attribute.type === type && typname === name).length ? " USING " + fieldName + "::" + type : "";
|
|
252
|
-
const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} TYPE ${type}${using}`;
|
|
253
|
-
|
|
254
|
-
this.syncLog(statement);
|
|
255
|
-
if(this.sync) await this.client.query(statement);
|
|
256
|
-
await setDefault(attnotnull);
|
|
257
|
-
}
|
|
258
|
-
} else if(defaultValue === undefined) {
|
|
259
|
-
if(adsrc) dropDefault();
|
|
260
|
-
await setNotNull(attnotnull);
|
|
261
|
-
} else if(! adsrc || adsrc.split("::")[0] !== defaultValue) await setDefault(attnotnull);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
async syncIndexes(table: Table): Promise<void> {
|
|
267
|
-
const { indexes, tableName } = table;
|
|
268
|
-
|
|
269
|
-
for(const index of indexes) {
|
|
270
|
-
const { fields, indexName, type, unique } = index;
|
|
271
|
-
|
|
272
|
-
if(! this.indexes.includes(indexName)) {
|
|
273
|
-
const statement = `CREATE${unique ? " UNIQUE" : ""} INDEX ${indexName} ON ${tableName} USING ${type} (${fields.join(", ")})`;
|
|
274
|
-
|
|
275
|
-
this.syncLog(statement);
|
|
276
|
-
if(this.sync) await this.client.query(statement);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
async syncSequence(table: Table): Promise<void> {
|
|
282
|
-
if(! table.autoIncrementOwn) return;
|
|
283
|
-
|
|
284
|
-
const statement = `ALTER SEQUENCE ${table.tableName}_id_seq OWNED BY ${table.tableName}.id`;
|
|
285
|
-
|
|
286
|
-
this.syncLog(statement);
|
|
287
|
-
if(this.sync) await this.client.query(statement);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
async syncTable(table: Table): Promise<void> {
|
|
291
|
-
if(table.autoIncrement) {
|
|
292
|
-
await (async () => {
|
|
293
|
-
try {
|
|
294
|
-
await this.client.query(`SELECT currval('${table.tableName}_id_seq')`);
|
|
295
|
-
} catch(e) {
|
|
296
|
-
if(e.code === "55000") return;
|
|
297
|
-
if(e.code === "42P01") {
|
|
298
|
-
const statement = `CREATE SEQUENCE ${table.tableName}_id_seq`;
|
|
299
|
-
|
|
300
|
-
this.syncLog(statement);
|
|
301
|
-
if(this.sync) await this.client.query(statement);
|
|
302
|
-
table.autoIncrementOwn = true;
|
|
303
|
-
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
throw e;
|
|
308
|
-
}
|
|
309
|
-
})();
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
let create: boolean;
|
|
313
|
-
const resTable = await this.client.query("SELECT oid FROM pg_class WHERE relname = $1", [table.tableName]);
|
|
314
|
-
|
|
315
|
-
if(resTable.rowCount) {
|
|
316
|
-
table.oid = resTable.rows[0].oid;
|
|
317
|
-
|
|
318
|
-
let drop: boolean;
|
|
319
|
-
const resParent = await this.client.query("SELECT inhparent FROM pg_inherits WHERE inhrelid = $1", [table.oid]);
|
|
320
|
-
|
|
321
|
-
if(resParent.rowCount) {
|
|
322
|
-
if(! table.parent) drop = true;
|
|
323
|
-
else if(this.findTable(table.parent.tableName).oid === resParent.rows[0].inhparent) return;
|
|
324
|
-
|
|
325
|
-
drop = true;
|
|
326
|
-
} else if(table.parent) drop = true;
|
|
327
|
-
|
|
328
|
-
if(drop) {
|
|
329
|
-
const statement = `DROP TABLE ${table.tableName} CASCADE`;
|
|
330
|
-
|
|
331
|
-
create = true;
|
|
332
|
-
this.syncLog(statement);
|
|
333
|
-
if(this.sync) await this.client.query(statement);
|
|
334
|
-
}
|
|
335
|
-
} else create = true;
|
|
336
|
-
|
|
337
|
-
if(create) {
|
|
338
|
-
const parent = table.parent ? ` INHERITS (${table.parent.tableName})` : "";
|
|
339
|
-
const statement = `CREATE TABLE ${table.tableName} ()${parent}`;
|
|
340
|
-
|
|
341
|
-
this.syncLog(statement);
|
|
342
|
-
if(this.sync) await this.client.query(statement);
|
|
343
|
-
|
|
344
|
-
const resTable = await this.client.query("SELECT oid FROM pg_class WHERE relname = $1", [table.tableName]);
|
|
345
|
-
|
|
346
|
-
table.oid = resTable.rows[0]?.oid;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// farray[0].defaultValue = "nextval('" + tname + "_id_seq'::regclass)";
|