sedentary-pg 0.0.25 → 0.0.30
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 -2
- package/{adsrc.js → dist/cjs/adsrc.js} +0 -0
- package/{index.js → dist/cjs/index.js} +0 -0
- package/{pgdb.js → dist/cjs/pgdb.js} +21 -15
- package/dist/es/adsrc.js +3 -0
- package/dist/es/index.js +22 -0
- package/dist/es/pgdb.js +454 -0
- package/{adsrc.d.ts → dist/types/adsrc.d.ts} +0 -0
- package/{index.d.ts → dist/types/index.d.ts} +1 -2
- package/{pgdb.d.ts → dist/types/pgdb.d.ts} +2 -2
- package/package.json +19 -18
package/README.md
CHANGED
|
@@ -77,8 +77,8 @@ The full documentation is on [sedentary.readthedocs.io](https://sedentary.readth
|
|
|
77
77
|
|
|
78
78
|
Requires:
|
|
79
79
|
|
|
80
|
-
- Node.js: **
|
|
81
|
-
- TypeScript: **v4.
|
|
80
|
+
- Node.js: **v14**
|
|
81
|
+
- TypeScript: **v4.6** (or none if used in a JavaScript project).
|
|
82
82
|
|
|
83
83
|
The package is tested under [all version combinations](https://app.travis-ci.com/github/iccicci/sedentary-pg)
|
|
84
84
|
of **Node.js** currently supported accordingly to [Node.js Release](https://github.com/nodejs/Release#readme) and of
|
|
File without changes
|
|
File without changes
|
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.TransactionPG = exports.PGDB = void 0;
|
|
7
7
|
const pg_1 = require("pg");
|
|
8
8
|
const pg_format_1 = __importDefault(require("pg-format"));
|
|
9
|
-
const
|
|
9
|
+
const sedentary_1 = require("sedentary");
|
|
10
10
|
const adsrc_1 = require("./adsrc");
|
|
11
11
|
const needDrop = [
|
|
12
12
|
["DATETIME", "int2"],
|
|
@@ -22,7 +22,7 @@ const needUsing = [
|
|
|
22
22
|
];
|
|
23
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
|
-
class PGDB extends
|
|
25
|
+
class PGDB extends sedentary_1.DB {
|
|
26
26
|
constructor(connection, log) {
|
|
27
27
|
super(log);
|
|
28
28
|
this.oidLoad = {};
|
|
@@ -37,8 +37,7 @@ class PGDB extends db_1.DB {
|
|
|
37
37
|
this.version = parseInt(res.rows[0].version.split(" ")[1].split(".")[0], 10);
|
|
38
38
|
}
|
|
39
39
|
async end() {
|
|
40
|
-
|
|
41
|
-
this.client.release();
|
|
40
|
+
this.client.release();
|
|
42
41
|
await this.pool.end();
|
|
43
42
|
}
|
|
44
43
|
defaultNeq(src, value) {
|
|
@@ -47,7 +46,7 @@ class PGDB extends db_1.DB {
|
|
|
47
46
|
return src.split("::")[0] !== value;
|
|
48
47
|
}
|
|
49
48
|
async begin() {
|
|
50
|
-
const ret = new TransactionPG(await this.pool.connect());
|
|
49
|
+
const ret = new TransactionPG(this.log, await this.pool.connect());
|
|
51
50
|
await ret.client.query("BEGIN");
|
|
52
51
|
return ret;
|
|
53
52
|
}
|
|
@@ -59,9 +58,9 @@ class PGDB extends db_1.DB {
|
|
|
59
58
|
return value.toString();
|
|
60
59
|
if (type === "string")
|
|
61
60
|
return (0, pg_format_1.default)("%L", value);
|
|
62
|
-
if
|
|
63
|
-
|
|
64
|
-
return (
|
|
61
|
+
//if(value instanceof Date)
|
|
62
|
+
return (0, pg_format_1.default)("%L", value).replace(/\.\d\d\d\+/, "+");
|
|
63
|
+
//return format("%L", JSON.stringify(value));
|
|
65
64
|
}
|
|
66
65
|
fill(attributes, row, entry) {
|
|
67
66
|
const loaded = {};
|
|
@@ -71,19 +70,23 @@ class PGDB extends db_1.DB {
|
|
|
71
70
|
}
|
|
72
71
|
load(tableName, attributes, pk, model, table) {
|
|
73
72
|
const pkFldName = pk.fieldName;
|
|
74
|
-
return async (where, order, tx) => {
|
|
73
|
+
return async (where, order, tx, lock) => {
|
|
75
74
|
const { oid } = table;
|
|
76
75
|
const ret = [];
|
|
77
76
|
const client = tx ? tx.client : await this.pool.connect();
|
|
78
77
|
const oidPK = {};
|
|
79
78
|
try {
|
|
80
|
-
const
|
|
79
|
+
const forUpdate = lock ? " FOR UPDATE" : "";
|
|
80
|
+
const orderBy = order && order.length ? ` ORDER BY ${order.map(_ => (_.startsWith("-") ? `${_.substring(1)} DESC` : _)).join(",")}` : "";
|
|
81
|
+
const query = `SELECT *, tableoid FROM ${tableName}${where ? ` WHERE ${where}` : ""}${orderBy}${forUpdate}`;
|
|
81
82
|
this.log(query);
|
|
82
83
|
const res = await client.query(query);
|
|
83
84
|
for (const row of res.rows) {
|
|
84
85
|
if (row.tableoid === oid) {
|
|
85
86
|
const entry = new model("load");
|
|
86
87
|
this.fill(attributes, row, entry);
|
|
88
|
+
if (tx)
|
|
89
|
+
tx.addEntry(entry);
|
|
87
90
|
ret.push(entry);
|
|
88
91
|
entry.postLoad();
|
|
89
92
|
}
|
|
@@ -97,8 +100,9 @@ class PGDB extends db_1.DB {
|
|
|
97
100
|
for (const oid in oidPK) {
|
|
98
101
|
const res = await this.oidLoad[oid](oidPK[oid].map(_ => _[1]));
|
|
99
102
|
for (const entry of res)
|
|
100
|
-
for (const [id] of oidPK[oid])
|
|
101
|
-
|
|
103
|
+
for (const [id, pk] of oidPK[oid])
|
|
104
|
+
if (pk === entry[pkFldName])
|
|
105
|
+
ret[id] = entry;
|
|
102
106
|
}
|
|
103
107
|
}
|
|
104
108
|
finally {
|
|
@@ -419,9 +423,9 @@ class PGDB extends db_1.DB {
|
|
|
419
423
|
}
|
|
420
424
|
}
|
|
421
425
|
exports.PGDB = PGDB;
|
|
422
|
-
class TransactionPG extends
|
|
423
|
-
constructor(client) {
|
|
424
|
-
super();
|
|
426
|
+
class TransactionPG extends sedentary_1.Transaction {
|
|
427
|
+
constructor(log, client) {
|
|
428
|
+
super(log);
|
|
425
429
|
this.released = false;
|
|
426
430
|
this.client = client;
|
|
427
431
|
}
|
|
@@ -431,6 +435,7 @@ class TransactionPG extends db_1.Transaction {
|
|
|
431
435
|
}
|
|
432
436
|
async commit() {
|
|
433
437
|
if (!this.released) {
|
|
438
|
+
this.log("COMMIT");
|
|
434
439
|
await this.client.query("COMMIT");
|
|
435
440
|
this.release();
|
|
436
441
|
super.commit();
|
|
@@ -440,6 +445,7 @@ class TransactionPG extends db_1.Transaction {
|
|
|
440
445
|
try {
|
|
441
446
|
if (!this.released) {
|
|
442
447
|
super.rollback();
|
|
448
|
+
this.log("ROLLBACK");
|
|
443
449
|
await this.client.query("ROLLBACK");
|
|
444
450
|
}
|
|
445
451
|
}
|
package/dist/es/adsrc.js
ADDED
package/dist/es/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Sedentary } from "sedentary";
|
|
2
|
+
import { PGDB } from "./pgdb";
|
|
3
|
+
export { EntryBase, Type } from "sedentary";
|
|
4
|
+
export { TransactionPG } from "./pgdb";
|
|
5
|
+
export class SedentaryPG extends Sedentary {
|
|
6
|
+
constructor(connection, options) {
|
|
7
|
+
super(options);
|
|
8
|
+
if (!(connection instanceof Object))
|
|
9
|
+
throw new Error("SedentaryPG.constructor: 'connection' argument: Wrong type, expected 'Object'");
|
|
10
|
+
this.db = new PGDB(connection, this.log);
|
|
11
|
+
}
|
|
12
|
+
FKEY(attribute, options) {
|
|
13
|
+
const { attributeName, modelName, unique } = attribute;
|
|
14
|
+
if (!unique)
|
|
15
|
+
throw new Error(`Sedentary.FKEY: '${modelName}' model: '${attributeName}' attribute: is not unique: can't be used as FKEY target`);
|
|
16
|
+
return super.FKEY(attribute, options);
|
|
17
|
+
}
|
|
18
|
+
async begin() {
|
|
19
|
+
return this.db.begin();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export const Package = SedentaryPG;
|
package/dist/es/pgdb.js
ADDED
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
import { DatabaseError, Pool } from "pg";
|
|
2
|
+
import format from "pg-format";
|
|
3
|
+
import { DB, Transaction } from "sedentary";
|
|
4
|
+
import { adsrc } from "./adsrc";
|
|
5
|
+
const needDrop = [
|
|
6
|
+
["DATETIME", "int2"],
|
|
7
|
+
["DATETIME", "int4"],
|
|
8
|
+
["DATETIME", "int8"],
|
|
9
|
+
["INT", "timestamptz"],
|
|
10
|
+
["INT8", "timestamptz"]
|
|
11
|
+
];
|
|
12
|
+
const needUsing = [
|
|
13
|
+
["DATETIME", "varchar"],
|
|
14
|
+
["INT", "varchar"],
|
|
15
|
+
["INT8", "varchar"]
|
|
16
|
+
];
|
|
17
|
+
const types = { int2: "SMALLINT", int4: "INTEGER", int8: "BIGINT", timestamptz: "DATETIME", varchar: "VARCHAR" };
|
|
18
|
+
const actions = { cascade: "c", "no action": "a", restrict: "r", "set default": "d", "set null": "n" };
|
|
19
|
+
export class PGDB extends DB {
|
|
20
|
+
client;
|
|
21
|
+
indexes;
|
|
22
|
+
oidLoad = {};
|
|
23
|
+
pool;
|
|
24
|
+
version;
|
|
25
|
+
constructor(connection, log) {
|
|
26
|
+
super(log);
|
|
27
|
+
this.client = {};
|
|
28
|
+
this.indexes = [];
|
|
29
|
+
this.pool = new Pool(connection);
|
|
30
|
+
this.version = 0;
|
|
31
|
+
}
|
|
32
|
+
async connect() {
|
|
33
|
+
this.client = await this.pool.connect();
|
|
34
|
+
const res = await this.client.query("SELECT version()");
|
|
35
|
+
this.version = parseInt(res.rows[0].version.split(" ")[1].split(".")[0], 10);
|
|
36
|
+
}
|
|
37
|
+
async end() {
|
|
38
|
+
this.client.release();
|
|
39
|
+
await this.pool.end();
|
|
40
|
+
}
|
|
41
|
+
defaultNeq(src, value) {
|
|
42
|
+
if (src === value)
|
|
43
|
+
return false;
|
|
44
|
+
return src.split("::")[0] !== value;
|
|
45
|
+
}
|
|
46
|
+
async begin() {
|
|
47
|
+
const ret = new TransactionPG(this.log, await this.pool.connect());
|
|
48
|
+
await ret.client.query("BEGIN");
|
|
49
|
+
return ret;
|
|
50
|
+
}
|
|
51
|
+
escape(value) {
|
|
52
|
+
if (value === null || value === undefined)
|
|
53
|
+
throw new Error("SedentaryPG: Can't escape null nor undefined values; use the 'IS NULL' operator instead");
|
|
54
|
+
const type = typeof value;
|
|
55
|
+
if (type === "number" || type === "boolean")
|
|
56
|
+
return value.toString();
|
|
57
|
+
if (type === "string")
|
|
58
|
+
return format("%L", value);
|
|
59
|
+
//if(value instanceof Date)
|
|
60
|
+
return format("%L", value).replace(/\.\d\d\d\+/, "+");
|
|
61
|
+
//return format("%L", JSON.stringify(value));
|
|
62
|
+
}
|
|
63
|
+
fill(attributes, row, entry) {
|
|
64
|
+
const loaded = {};
|
|
65
|
+
for (const attribute in attributes)
|
|
66
|
+
entry[attribute] = loaded[attribute] = row[attributes[attribute]];
|
|
67
|
+
Object.defineProperty(entry, "loaded", { configurable: true, value: loaded });
|
|
68
|
+
}
|
|
69
|
+
load(tableName, attributes, pk, model, table) {
|
|
70
|
+
const pkFldName = pk.fieldName;
|
|
71
|
+
return async (where, order, tx, lock) => {
|
|
72
|
+
const { oid } = table;
|
|
73
|
+
const ret = [];
|
|
74
|
+
const client = tx ? tx.client : await this.pool.connect();
|
|
75
|
+
const oidPK = {};
|
|
76
|
+
try {
|
|
77
|
+
const forUpdate = lock ? " FOR UPDATE" : "";
|
|
78
|
+
const orderBy = order && order.length ? ` ORDER BY ${order.map(_ => (_.startsWith("-") ? `${_.substring(1)} DESC` : _)).join(",")}` : "";
|
|
79
|
+
const query = `SELECT *, tableoid FROM ${tableName}${where ? ` WHERE ${where}` : ""}${orderBy}${forUpdate}`;
|
|
80
|
+
this.log(query);
|
|
81
|
+
const res = await client.query(query);
|
|
82
|
+
for (const row of res.rows) {
|
|
83
|
+
if (row.tableoid === oid) {
|
|
84
|
+
const entry = new model("load");
|
|
85
|
+
this.fill(attributes, row, entry);
|
|
86
|
+
if (tx)
|
|
87
|
+
tx.addEntry(entry);
|
|
88
|
+
ret.push(entry);
|
|
89
|
+
entry.postLoad();
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
if (!oidPK[row.tableoid])
|
|
93
|
+
oidPK[row.tableoid] = [];
|
|
94
|
+
oidPK[row.tableoid].push([ret.length, row[pkFldName]]);
|
|
95
|
+
ret.push(null);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
for (const oid in oidPK) {
|
|
99
|
+
const res = await this.oidLoad[oid](oidPK[oid].map(_ => _[1]));
|
|
100
|
+
for (const entry of res)
|
|
101
|
+
for (const [id, pk] of oidPK[oid])
|
|
102
|
+
if (pk === entry[pkFldName])
|
|
103
|
+
ret[id] = entry;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
if (!tx)
|
|
108
|
+
client.release();
|
|
109
|
+
}
|
|
110
|
+
return ret;
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
save(tableName, attributes, pk) {
|
|
114
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
115
|
+
const self = this;
|
|
116
|
+
const pkAttrnName = pk.attributeName;
|
|
117
|
+
const pkFldName = pk.fieldName;
|
|
118
|
+
return async function () {
|
|
119
|
+
let changed = false;
|
|
120
|
+
const client = this.tx ? this.tx.client : await self.pool.connect();
|
|
121
|
+
try {
|
|
122
|
+
const { loaded } = this;
|
|
123
|
+
if (loaded) {
|
|
124
|
+
const actions = [];
|
|
125
|
+
for (const attribute in attributes) {
|
|
126
|
+
const value = this[attribute];
|
|
127
|
+
if (value !== loaded[attribute])
|
|
128
|
+
actions.push(`${attributes[attribute]} = ${self.escape(value)}`);
|
|
129
|
+
}
|
|
130
|
+
if (actions.length) {
|
|
131
|
+
const query = `UPDATE ${tableName} SET ${actions.join(", ")} WHERE ${pkFldName} = ${self.escape(this[pkAttrnName])}`;
|
|
132
|
+
self.log(query);
|
|
133
|
+
self.fill(attributes, (await client.query(query + " RETURNING *")).rows[0], this);
|
|
134
|
+
changed = true;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
const fields = [];
|
|
139
|
+
const values = [];
|
|
140
|
+
for (const attribute in attributes) {
|
|
141
|
+
const value = this[attribute];
|
|
142
|
+
if (value !== null && value !== undefined) {
|
|
143
|
+
fields.push(attributes[attribute]);
|
|
144
|
+
values.push(self.escape(value));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const query = fields.length ? `INSERT INTO ${tableName} (${fields.join(", ")}) VALUES (${values.join(", ")})` : `INSERT INTO ${tableName} DEFAULT VALUES`;
|
|
148
|
+
self.log(query);
|
|
149
|
+
self.fill(attributes, (await client.query(query + " RETURNING *")).rows[0], this);
|
|
150
|
+
changed = true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
if (!this.tx)
|
|
155
|
+
client.release();
|
|
156
|
+
}
|
|
157
|
+
return changed;
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
async dropConstraints(table) {
|
|
161
|
+
const indexes = [];
|
|
162
|
+
const res = await this.client.query("SELECT confdeltype, confupdtype, conindid, conname, contype FROM pg_constraint WHERE conrelid = $1 ORDER BY conname", [table.oid]);
|
|
163
|
+
for (const row of res.rows) {
|
|
164
|
+
const arr = table.constraints.filter(_ => _.constraintName === row.conname && _.type === row.contype);
|
|
165
|
+
let drop = false;
|
|
166
|
+
if (arr.length === 0)
|
|
167
|
+
drop = true;
|
|
168
|
+
else if (row.contype === "u")
|
|
169
|
+
indexes.push(row.conindid);
|
|
170
|
+
else {
|
|
171
|
+
const { options } = arr[0].attribute.foreignKey;
|
|
172
|
+
if (actions[options.onDelete] !== row.confdeltype || actions[options.onUpdate] !== row.confupdtype)
|
|
173
|
+
drop = true;
|
|
174
|
+
}
|
|
175
|
+
if (drop) {
|
|
176
|
+
const statement = `ALTER TABLE ${table.tableName} DROP CONSTRAINT ${row.conname} CASCADE`;
|
|
177
|
+
this.syncLog(statement);
|
|
178
|
+
if (this.sync)
|
|
179
|
+
await this.client.query(statement);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return indexes;
|
|
183
|
+
}
|
|
184
|
+
async dropField(tableName, fieldName) {
|
|
185
|
+
const statement = `ALTER TABLE ${tableName} DROP COLUMN ${fieldName}`;
|
|
186
|
+
this.syncLog(statement);
|
|
187
|
+
if (this.sync)
|
|
188
|
+
await this.client.query(statement);
|
|
189
|
+
}
|
|
190
|
+
async dropFields(table) {
|
|
191
|
+
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]);
|
|
192
|
+
for (const i in res.rows)
|
|
193
|
+
if (!table.findField(res.rows[i].attname))
|
|
194
|
+
await this.dropField(table.tableName, res.rows[i].attname);
|
|
195
|
+
}
|
|
196
|
+
async dropIndexes(table, constraintIndexes) {
|
|
197
|
+
const { indexes, oid } = table;
|
|
198
|
+
const iobject = {};
|
|
199
|
+
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]);
|
|
200
|
+
for (const row of res.rows) {
|
|
201
|
+
const { amname, attname, indexrelid, indisunique, relname } = row;
|
|
202
|
+
if (!constraintIndexes.includes(indexrelid)) {
|
|
203
|
+
if (iobject[relname])
|
|
204
|
+
iobject[relname].fields.push(attname);
|
|
205
|
+
else
|
|
206
|
+
iobject[relname] = { fields: [attname], indexName: relname, type: amname, unique: indisunique };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
this.indexes = [];
|
|
210
|
+
for (const index of indexes) {
|
|
211
|
+
const { indexName } = index;
|
|
212
|
+
if (iobject[indexName] && this.indexesEq(index, iobject[indexName])) {
|
|
213
|
+
this.indexes.push(indexName);
|
|
214
|
+
delete iobject[indexName];
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
for (const index of Object.keys(iobject).sort()) {
|
|
218
|
+
const statement = `DROP INDEX ${index}`;
|
|
219
|
+
this.syncLog(statement);
|
|
220
|
+
if (this.sync)
|
|
221
|
+
await this.client.query(statement);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
async syncConstraints(table) {
|
|
225
|
+
for (const constraint of table.constraints) {
|
|
226
|
+
const { attribute, constraintName, type } = constraint;
|
|
227
|
+
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", [
|
|
228
|
+
table.oid,
|
|
229
|
+
attribute.fieldName
|
|
230
|
+
]);
|
|
231
|
+
if (!res.rowCount) {
|
|
232
|
+
let query;
|
|
233
|
+
if (type === "f") {
|
|
234
|
+
const { fieldName, options, tableName } = attribute.foreignKey;
|
|
235
|
+
const onDelete = options.onDelete !== "no action" ? ` ON DELETE ${options.onDelete.toUpperCase()}` : "";
|
|
236
|
+
const onUpdate = options.onUpdate !== "no action" ? ` ON UPDATE ${options.onUpdate.toUpperCase()}` : "";
|
|
237
|
+
query = `FOREIGN KEY (${attribute.fieldName}) REFERENCES ${tableName}(${fieldName})${onDelete}${onUpdate}`;
|
|
238
|
+
}
|
|
239
|
+
else
|
|
240
|
+
query = `UNIQUE(${attribute.fieldName})`;
|
|
241
|
+
const statement = `ALTER TABLE ${table.tableName} ADD CONSTRAINT ${constraintName} ${query}`;
|
|
242
|
+
this.syncLog(statement);
|
|
243
|
+
if (this.sync)
|
|
244
|
+
await this.client.query(statement);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
async syncDataBase() {
|
|
249
|
+
await super.syncDataBase();
|
|
250
|
+
for (const table of this.tables)
|
|
251
|
+
this.oidLoad[table.oid || 0] = (ids) => table.model.load({ [table.pk.attributeName]: ["IN", ids] });
|
|
252
|
+
}
|
|
253
|
+
fieldType(attribute) {
|
|
254
|
+
const { size, type } = attribute;
|
|
255
|
+
let ret;
|
|
256
|
+
switch (type) {
|
|
257
|
+
case "DATETIME":
|
|
258
|
+
return ["DATETIME", "TIMESTAMP (3) WITH TIME ZONE"];
|
|
259
|
+
case "INT":
|
|
260
|
+
ret = size === 2 ? "SMALLINT" : "INTEGER";
|
|
261
|
+
return [ret, ret];
|
|
262
|
+
case "INT8":
|
|
263
|
+
return ["BIGINT", "BIGINT"];
|
|
264
|
+
case "VARCHAR":
|
|
265
|
+
return ["VARCHAR", "VARCHAR" + (size ? `(${size})` : "")];
|
|
266
|
+
}
|
|
267
|
+
throw new Error(`Unknown type: '${type}', '${size}'`);
|
|
268
|
+
}
|
|
269
|
+
async syncFields(table) {
|
|
270
|
+
const { attributes, autoIncrement, oid, tableName } = table;
|
|
271
|
+
for (const attribute of attributes) {
|
|
272
|
+
const { fieldName, notNull, size } = attribute;
|
|
273
|
+
const defaultValue = attribute.defaultValue === undefined ? (autoIncrement && fieldName === "id" ? `nextval('${tableName}_id_seq'::regclass)` : undefined) : this.escape(attribute.defaultValue);
|
|
274
|
+
const [base, type] = this.fieldType(attribute);
|
|
275
|
+
const where = "attrelid = $1 AND attnum > 0 AND atttypid = pg_type.oid AND attislocal = 't' AND attname = $2";
|
|
276
|
+
const res = await this.client.query(`SELECT attnotnull, atttypmod, typname, ${adsrc(this.version)} FROM pg_type, pg_attribute LEFT JOIN pg_attrdef ON adrelid = attrelid AND adnum = attnum WHERE ${where}`, [oid, fieldName]);
|
|
277
|
+
const addField = async () => {
|
|
278
|
+
const statement = `ALTER TABLE ${tableName} ADD COLUMN ${fieldName} ${type}`;
|
|
279
|
+
this.syncLog(statement);
|
|
280
|
+
if (this.sync)
|
|
281
|
+
await this.client.query(statement);
|
|
282
|
+
};
|
|
283
|
+
const dropDefault = async () => {
|
|
284
|
+
const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} DROP DEFAULT`;
|
|
285
|
+
this.syncLog(statement);
|
|
286
|
+
if (this.sync)
|
|
287
|
+
await this.client.query(statement);
|
|
288
|
+
};
|
|
289
|
+
const setNotNull = async (isNotNull) => {
|
|
290
|
+
if (isNotNull === notNull)
|
|
291
|
+
return;
|
|
292
|
+
const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} ${notNull ? "SET" : "DROP"} NOT NULL`;
|
|
293
|
+
this.syncLog(statement);
|
|
294
|
+
if (this.sync)
|
|
295
|
+
await this.client.query(statement);
|
|
296
|
+
};
|
|
297
|
+
const setDefault = async (isNotNull, create) => {
|
|
298
|
+
if (defaultValue !== undefined) {
|
|
299
|
+
let statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} SET DEFAULT ${defaultValue}`;
|
|
300
|
+
this.syncLog(statement);
|
|
301
|
+
if (this.sync)
|
|
302
|
+
await this.client.query(statement);
|
|
303
|
+
if (!isNotNull && !create) {
|
|
304
|
+
statement = `UPDATE ${tableName} SET ${fieldName} = ${defaultValue} WHERE ${fieldName} IS NULL`;
|
|
305
|
+
this.syncLog(statement);
|
|
306
|
+
if (this.sync)
|
|
307
|
+
this.client.query(statement);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
await setNotNull(isNotNull);
|
|
311
|
+
};
|
|
312
|
+
if (!res.rowCount) {
|
|
313
|
+
await addField();
|
|
314
|
+
await setDefault(false, true);
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
const { adsrc, attnotnull, atttypmod, typname } = res.rows[0];
|
|
318
|
+
if (types[typname] !== base || (base === "VARCHAR" && (size ? size + 4 !== atttypmod : atttypmod !== -1))) {
|
|
319
|
+
if (needDrop.filter(([type, name]) => attribute.type === type && typname === name).length) {
|
|
320
|
+
await this.dropField(tableName, fieldName);
|
|
321
|
+
await addField();
|
|
322
|
+
await setDefault(false, true);
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
if (adsrc)
|
|
326
|
+
dropDefault();
|
|
327
|
+
const using = needUsing.filter(([type, name]) => attribute.type === type && typname === name).length ? " USING " + fieldName + "::" + type : "";
|
|
328
|
+
const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} TYPE ${type}${using}`;
|
|
329
|
+
this.syncLog(statement);
|
|
330
|
+
if (this.sync)
|
|
331
|
+
await this.client.query(statement);
|
|
332
|
+
await setDefault(attnotnull, false);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
else if (defaultValue === undefined) {
|
|
336
|
+
if (adsrc)
|
|
337
|
+
dropDefault();
|
|
338
|
+
await setNotNull(attnotnull);
|
|
339
|
+
}
|
|
340
|
+
else if (!adsrc || this.defaultNeq(adsrc, defaultValue))
|
|
341
|
+
await setDefault(attnotnull, false);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
async syncIndexes(table) {
|
|
346
|
+
const { indexes, tableName } = table;
|
|
347
|
+
for (const index of indexes) {
|
|
348
|
+
const { fields, indexName, type, unique } = index;
|
|
349
|
+
if (!this.indexes.includes(indexName)) {
|
|
350
|
+
const statement = `CREATE${unique ? " UNIQUE" : ""} INDEX ${indexName} ON ${tableName} USING ${type} (${fields.join(", ")})`;
|
|
351
|
+
this.syncLog(statement);
|
|
352
|
+
if (this.sync)
|
|
353
|
+
await this.client.query(statement);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
async syncSequence(table) {
|
|
358
|
+
if (!table.autoIncrementOwn)
|
|
359
|
+
return;
|
|
360
|
+
const statement = `ALTER SEQUENCE ${table.tableName}_id_seq OWNED BY ${table.tableName}.id`;
|
|
361
|
+
this.syncLog(statement);
|
|
362
|
+
if (this.sync)
|
|
363
|
+
await this.client.query(statement);
|
|
364
|
+
}
|
|
365
|
+
async syncTable(table) {
|
|
366
|
+
if (table.autoIncrement) {
|
|
367
|
+
await (async () => {
|
|
368
|
+
try {
|
|
369
|
+
await this.client.query(`SELECT currval('${table.tableName}_id_seq')`);
|
|
370
|
+
}
|
|
371
|
+
catch (e) {
|
|
372
|
+
if (e instanceof DatabaseError && e.code === "55000")
|
|
373
|
+
return;
|
|
374
|
+
if (e instanceof DatabaseError && e.code === "42P01") {
|
|
375
|
+
const statement = `CREATE SEQUENCE ${table.tableName}_id_seq`;
|
|
376
|
+
this.syncLog(statement);
|
|
377
|
+
if (this.sync)
|
|
378
|
+
await this.client.query(statement);
|
|
379
|
+
table.autoIncrementOwn = true;
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
throw e;
|
|
383
|
+
}
|
|
384
|
+
})();
|
|
385
|
+
}
|
|
386
|
+
let create = false;
|
|
387
|
+
const resTable = await this.client.query("SELECT oid FROM pg_class WHERE relname = $1", [table.tableName]);
|
|
388
|
+
if (resTable.rowCount) {
|
|
389
|
+
table.oid = resTable.rows[0].oid;
|
|
390
|
+
let drop = false;
|
|
391
|
+
const resParent = await this.client.query("SELECT inhparent FROM pg_inherits WHERE inhrelid = $1", [table.oid]);
|
|
392
|
+
if (resParent.rowCount) {
|
|
393
|
+
if (!table.parent)
|
|
394
|
+
drop = true;
|
|
395
|
+
else if (this.findTable(table.parent.tableName).oid === resParent.rows[0].inhparent)
|
|
396
|
+
return;
|
|
397
|
+
drop = true;
|
|
398
|
+
}
|
|
399
|
+
else if (table.parent)
|
|
400
|
+
drop = true;
|
|
401
|
+
if (drop) {
|
|
402
|
+
const statement = `DROP TABLE ${table.tableName} CASCADE`;
|
|
403
|
+
create = true;
|
|
404
|
+
this.syncLog(statement);
|
|
405
|
+
if (this.sync)
|
|
406
|
+
await this.client.query(statement);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
else
|
|
410
|
+
create = true;
|
|
411
|
+
if (create) {
|
|
412
|
+
const parent = table.parent ? ` INHERITS (${table.parent.tableName})` : "";
|
|
413
|
+
const statement = `CREATE TABLE ${table.tableName} ()${parent}`;
|
|
414
|
+
this.syncLog(statement);
|
|
415
|
+
if (this.sync)
|
|
416
|
+
await this.client.query(statement);
|
|
417
|
+
const resTable = await this.client.query("SELECT oid FROM pg_class WHERE relname = $1", [table.tableName]);
|
|
418
|
+
table.oid = resTable.rows[0]?.oid;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
export class TransactionPG extends Transaction {
|
|
423
|
+
client;
|
|
424
|
+
released = false;
|
|
425
|
+
constructor(log, client) {
|
|
426
|
+
super(log);
|
|
427
|
+
this.client = client;
|
|
428
|
+
}
|
|
429
|
+
release() {
|
|
430
|
+
this.released = true;
|
|
431
|
+
this.client.release();
|
|
432
|
+
}
|
|
433
|
+
async commit() {
|
|
434
|
+
if (!this.released) {
|
|
435
|
+
this.log("COMMIT");
|
|
436
|
+
await this.client.query("COMMIT");
|
|
437
|
+
this.release();
|
|
438
|
+
super.commit();
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
async rollback() {
|
|
442
|
+
try {
|
|
443
|
+
if (!this.released) {
|
|
444
|
+
super.rollback();
|
|
445
|
+
this.log("ROLLBACK");
|
|
446
|
+
await this.client.query("ROLLBACK");
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
finally {
|
|
450
|
+
if (!this.released)
|
|
451
|
+
this.release();
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
File without changes
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { EntryBase, ForeignKeyOptions, Natural, Sedentary, SedentaryOptions, Type } from "sedentary";
|
|
2
|
-
import { Attribute } from "sedentary/db";
|
|
1
|
+
import { Attribute, EntryBase, ForeignKeyOptions, Natural, Sedentary, SedentaryOptions, Type } from "sedentary";
|
|
3
2
|
import { PoolConfig } from "pg";
|
|
4
3
|
import { PGDB, TransactionPG } from "./pgdb";
|
|
5
4
|
export { EntryBase, SedentaryOptions, Type } from "sedentary";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PoolClient, PoolConfig } from "pg";
|
|
2
|
-
import { Attribute, DB, EntryBase, Natural, Table, Transaction } from "sedentary
|
|
2
|
+
import { Attribute, DB, EntryBase, Natural, Table, Transaction } from "sedentary";
|
|
3
3
|
export declare class PGDB extends DB<TransactionPG> {
|
|
4
4
|
private client;
|
|
5
5
|
private indexes;
|
|
@@ -33,7 +33,7 @@ export declare class PGDB extends DB<TransactionPG> {
|
|
|
33
33
|
export declare class TransactionPG extends Transaction {
|
|
34
34
|
client: PoolClient;
|
|
35
35
|
released: boolean;
|
|
36
|
-
constructor(client: PoolClient);
|
|
36
|
+
constructor(log: (message: string) => void, client: PoolClient);
|
|
37
37
|
private release;
|
|
38
38
|
commit(): Promise<void>;
|
|
39
39
|
rollback(): Promise<void>;
|
package/package.json
CHANGED
|
@@ -6,29 +6,29 @@
|
|
|
6
6
|
"yossarian <sergiybiluk@gmail.com> (https://github.com/captain-yossarian)"
|
|
7
7
|
],
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@types/pg": "8.6.
|
|
9
|
+
"@types/pg": "8.6.5",
|
|
10
10
|
"pg": "8.7.3",
|
|
11
11
|
"pg-format": "1.0.4",
|
|
12
|
-
"sedentary": "0.0.
|
|
12
|
+
"sedentary": "0.0.30"
|
|
13
13
|
},
|
|
14
14
|
"description": "The ORM which never needs to migrate - PostgreSQL",
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@types/mocha": "9.1.0",
|
|
17
|
-
"@types/node": "17.0.
|
|
17
|
+
"@types/node": "17.0.23",
|
|
18
18
|
"@types/pg-format": "1.0.2",
|
|
19
19
|
"@types/yamljs": "0.2.31",
|
|
20
|
-
"@typescript-eslint/eslint-plugin": "5.
|
|
21
|
-
"@typescript-eslint/parser": "5.
|
|
22
|
-
"eslint": "8.
|
|
23
|
-
"mocha": "9.2.
|
|
20
|
+
"@typescript-eslint/eslint-plugin": "5.17.0",
|
|
21
|
+
"@typescript-eslint/parser": "5.17.0",
|
|
22
|
+
"eslint": "8.12.0",
|
|
23
|
+
"mocha": "9.2.2",
|
|
24
24
|
"nyc": "15.1.0",
|
|
25
|
-
"prettier": "2.
|
|
26
|
-
"ts-node": "10.
|
|
27
|
-
"typescript": "4.
|
|
25
|
+
"prettier": "2.6.1",
|
|
26
|
+
"ts-node": "10.7.0",
|
|
27
|
+
"typescript": "4.6.3",
|
|
28
28
|
"yamljs": "0.3.0"
|
|
29
29
|
},
|
|
30
30
|
"engines": {
|
|
31
|
-
"node": ">=
|
|
31
|
+
"node": ">=14.0"
|
|
32
32
|
},
|
|
33
33
|
"funding": {
|
|
34
34
|
"url": "https://blockchain.info/address/1Md9WFAHrXTb3yPBwQWmUfv2RmzrtbHioB"
|
|
@@ -44,6 +44,8 @@
|
|
|
44
44
|
"sqlite"
|
|
45
45
|
],
|
|
46
46
|
"license": "MIT",
|
|
47
|
+
"main": "./dist/cjs/index.js",
|
|
48
|
+
"module": "./dist/es/index.js",
|
|
47
49
|
"name": "sedentary-pg",
|
|
48
50
|
"prettier": {
|
|
49
51
|
"arrowParens": "avoid",
|
|
@@ -62,15 +64,14 @@
|
|
|
62
64
|
"packagejson": "node -r ts-node/register utils.ts packagejson",
|
|
63
65
|
"test": "mocha -r ts-node/register test/*ts",
|
|
64
66
|
"travis": "node -r ts-node/register utils.ts travis",
|
|
65
|
-
"tsc": "tsc
|
|
67
|
+
"tsc": "tsc -p tsconfig.cjs.json && tsc -p tsconfig.es.json && tsc -p tsconfig.types.json",
|
|
66
68
|
"version": "node -r ts-node/register utils.ts version"
|
|
67
69
|
},
|
|
68
70
|
"tsd": {
|
|
69
71
|
"compilerOptions": {
|
|
70
72
|
"alwaysStrict": true,
|
|
71
|
-
"declaration": true,
|
|
72
73
|
"esModuleInterop": true,
|
|
73
|
-
"
|
|
74
|
+
"moduleResolution": "node",
|
|
74
75
|
"noImplicitAny": true,
|
|
75
76
|
"noImplicitReturns": true,
|
|
76
77
|
"noImplicitThis": true,
|
|
@@ -79,9 +80,9 @@
|
|
|
79
80
|
"strictFunctionTypes": true,
|
|
80
81
|
"strictNullChecks": true,
|
|
81
82
|
"strictPropertyInitialization": true,
|
|
82
|
-
"target": "
|
|
83
|
+
"target": "esnext"
|
|
83
84
|
}
|
|
84
85
|
},
|
|
85
|
-
"types": "index.d.ts",
|
|
86
|
-
"version": "0.0.
|
|
87
|
-
}
|
|
86
|
+
"types": "./dist/types/index.d.ts",
|
|
87
|
+
"version": "0.0.30"
|
|
88
|
+
}
|