sedentary-pg 0.0.13 → 0.0.17

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 CHANGED
@@ -29,4 +29,49 @@
29
29
 
30
30
  # under development
31
31
 
32
- https://bucardo.org/postgres_all_versions.html
32
+ # Description
33
+
34
+ The **PostgreSQL** specilized package of [Sedentary](https://www.npmjs.com/package/sedentary).
35
+
36
+ # Installation
37
+
38
+ With [npm](https://www.npmjs.com/package/sedentary-pg):
39
+
40
+ ```sh
41
+ $ npm install --save sedentary-pg
42
+ ```
43
+
44
+ # Documentation
45
+
46
+ The full documentation is on [sedentary.readthedocs.io](https://sedentary.readthedocs.io/).
47
+
48
+ # Compatibility
49
+
50
+ Requires:
51
+
52
+ - Node.js: **v12**
53
+ - TypeScript: **v4.1** (or none if used in a JavaScript project).
54
+
55
+ The package is tested under [all version combinations](https://app.travis-ci.com/github/iccicci/sedentary-pg)
56
+ of **Node.js** currently supported accordingly to [Node.js Release](https://github.com/nodejs/Release#readme) and of
57
+ **PostgreSQL** currently supported accordingly to
58
+ [PostgreSQL Versioning](https://www.postgresql.org/support/versioning/).
59
+
60
+ To work with the package under Windows, be sure to configure `bash.exe` as your _script-shell_.
61
+
62
+ ```
63
+ > npm config set script-shell bash.exe
64
+ ```
65
+
66
+ # Licence
67
+
68
+ [MIT Licence](https://github.com/iccicci/sedentary-pg/blob/master/LICENSE)
69
+
70
+ # Bugs
71
+
72
+ Do not hesitate to report any bug or inconsistency [@github](https://github.com/iccicci/sedentary-pg/issues).
73
+
74
+ # Donating
75
+
76
+ If you find useful this package, please consider the opportunity to donate some satoshis to this bitcoin address:
77
+ **1Md9WFAHrXTb3yPBwQWmUfv2RmzrtbHioB**
package/index.d.ts CHANGED
@@ -1,6 +1,9 @@
1
- import { SchemaOptions, Sedentary } from "sedentary";
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?: SchemaOptions);
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, modelName, unique } = attribute;
19
+ if (!unique)
20
+ throw new Error(`Sedentary.FKEY: '${modelName}' model: '${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/adsrc.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function adsrc(version: number): "pg_get_expr(pg_attrdef.adbin, pg_attrdef.adrelid) AS adsrc" | "adsrc";
package/lib/adsrc.js ADDED
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.adsrc = void 0;
4
+ function adsrc(version) {
5
+ return version >= 12 ? "pg_get_expr(pg_attrdef.adbin, pg_attrdef.adrelid) AS adsrc" : "adsrc";
6
+ }
7
+ exports.adsrc = adsrc;
package/lib/adsrc.ts ADDED
@@ -0,0 +1,3 @@
1
+ export function adsrc(version: number) {
2
+ return version >= 12 ? "pg_get_expr(pg_attrdef.adbin, pg_attrdef.adrelid) AS adsrc" : "adsrc";
3
+ }
package/lib/pgdb.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { PoolConfig } from "pg";
2
- import { DB, Entry, Field, Natural, Table } from "sedentary/lib/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<void>;
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(field: Field<Natural, Entry>): string[];
16
- sync(): Promise<void>;
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
@@ -7,6 +7,7 @@ exports.PGDB = void 0;
7
7
  const pg_1 = require("pg");
8
8
  const pg_format_1 = __importDefault(require("pg-format"));
9
9
  const db_1 = require("sedentary/lib/db");
10
+ const adsrc_1 = require("./adsrc");
10
11
  const needDrop = [
11
12
  ["DATETIME", "int2"],
12
13
  ["DATETIME", "int4"],
@@ -31,76 +32,66 @@ class PGDB extends db_1.DB {
31
32
  this.version = parseInt(res.rows[0].version.split(" ")[1].split(".")[0], 10);
32
33
  }
33
34
  async dropConstraints(table) {
34
- let place = 1;
35
- const query = "SELECT conname FROM pg_attribute, pg_constraint WHERE attrelid = $1 AND conrelid = $1 AND attnum = conkey[1]";
36
- const values = [table.oid];
37
- const wheres = [];
38
- for (const i in table.constraints) {
39
- wheres.push(` AND (contype <> $${++place} or attname <> $${++place})`);
40
- values.push(table.constraints[i].type);
41
- values.push(table.constraints[i].field);
42
- }
43
- const res = await this.client.query(query + wheres.join(""), values);
44
- if (!res.rowCount)
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);
35
+ const indexes = [];
36
+ const res = await this.client.query("SELECT * FROM pg_constraint WHERE conrelid = $1 ORDER BY conname", [table.oid]);
37
+ for (const row of res.rows) {
38
+ const constraint = table.constraints.filter(_ => _.constraintName === row.conname);
39
+ if (constraint.length === 0) {
40
+ const statement = `ALTER TABLE ${table.tableName} DROP CONSTRAINT ${row.conname} CASCADE`;
41
+ this.syncLog(statement);
42
+ if (this.sync)
43
+ await this.client.query(statement);
44
+ }
45
+ else
46
+ indexes.push(row.conindid);
61
47
  }
48
+ return indexes;
62
49
  }
63
50
  async dropField(tableName, fieldName) {
64
51
  const statement = `ALTER TABLE ${tableName} DROP COLUMN ${fieldName}`;
65
- this.log(statement);
66
- await this.client.query(statement);
52
+ this.syncLog(statement);
53
+ if (this.sync)
54
+ await this.client.query(statement);
67
55
  }
68
56
  async dropFields(table) {
69
57
  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
58
  for (const i in res.rows)
71
- if (table.fields.filter(f => f.fieldName === res.rows[i].attname).length === 0)
59
+ if (!table.findField(res.rows[i].attname))
72
60
  await this.dropField(table.tableName, res.rows[i].attname);
73
61
  }
74
- async dropIndexes(table) {
62
+ async dropIndexes(table, constraintIndexes) {
75
63
  const { indexes, oid } = table;
76
64
  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]);
65
+ 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
66
  for (const row of res.rows) {
79
- const { amname, attname, indisunique, relname } = row;
80
- if (iobject[relname])
81
- iobject[relname].fields.push(attname);
82
- else
83
- iobject[relname] = { fields: [attname], name: relname, type: amname, unique: indisunique };
67
+ const { amname, attname, indexrelid, indisunique, relname } = row;
68
+ if (!constraintIndexes.includes(indexrelid)) {
69
+ if (iobject[relname])
70
+ iobject[relname].fields.push(attname);
71
+ else
72
+ iobject[relname] = { fields: [attname], indexName: relname, type: amname, unique: indisunique };
73
+ }
84
74
  }
85
75
  this.indexes = [];
86
76
  for (const index of indexes) {
87
- const { name } = index;
88
- if (iobject[name] && this.indexesEq(index, iobject[name])) {
89
- this.indexes.push(name);
90
- delete iobject[name];
77
+ const { indexName } = index;
78
+ if (iobject[indexName] && this.indexesEq(index, iobject[indexName])) {
79
+ this.indexes.push(indexName);
80
+ delete iobject[indexName];
91
81
  }
92
82
  }
93
83
  for (const index of Object.keys(iobject).sort()) {
94
84
  const statement = `DROP INDEX ${index}`;
95
- this.log(statement);
96
- await this.client.query(statement);
85
+ this.syncLog(statement);
86
+ if (this.sync)
87
+ await this.client.query(statement);
97
88
  }
98
89
  }
99
90
  async end() {
100
91
  await this.pool.end();
101
92
  }
102
- fieldType(field) {
103
- const { size, type } = field;
93
+ fieldType(attribute) {
94
+ const { size, type } = attribute;
104
95
  let ret;
105
96
  switch (type) {
106
97
  case "DATETIME":
@@ -115,10 +106,10 @@ class PGDB extends db_1.DB {
115
106
  }
116
107
  throw new Error(`Unknown type: '${type}', '${size}'`);
117
108
  }
118
- async sync() {
109
+ async syncDataBase() {
119
110
  let err;
120
111
  try {
121
- await super.sync();
112
+ await super.syncDataBase();
122
113
  }
123
114
  catch (e) {
124
115
  err = e;
@@ -128,73 +119,93 @@ class PGDB extends db_1.DB {
128
119
  throw err;
129
120
  }
130
121
  async syncConstraints(table) {
131
- for (const i in table.constraints) {
132
- const constraint = table.constraints[i];
122
+ for (const constraint of table.constraints) {
123
+ const { attribute, constraintName, type } = constraint;
133
124
  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
125
  table.oid,
135
- constraint.field
126
+ attribute.fieldName
136
127
  ]);
137
128
  if (!res.rowCount) {
138
- const statement = `ALTER TABLE ${table.tableName} ADD CONSTRAINT ${constraint.name} ${constraint.type === "u" ? `UNIQUE(${constraint.field})` : ``}`;
139
- this.log(statement);
140
- await this.client.query(statement);
129
+ let query;
130
+ switch (type) {
131
+ case "f":
132
+ query = `FOREIGN KEY (${attribute.fieldName}) REFERENCES ${attribute.foreignKey.tableName}(${attribute.foreignKey.fieldName})`;
133
+ break;
134
+ case "u":
135
+ query = `UNIQUE(${attribute.fieldName})`;
136
+ break;
137
+ }
138
+ const statement = `ALTER TABLE ${table.tableName} ADD CONSTRAINT ${constraintName} ${query}`;
139
+ this.syncLog(statement);
140
+ if (this.sync)
141
+ await this.client.query(statement);
141
142
  }
142
143
  }
143
144
  }
144
145
  async syncFields(table) {
145
- const { fields, oid, tableName } = table;
146
- for (const field of fields) {
147
- const { fieldName, notNull, size } = field;
148
- const defaultValue = field.defaultValue === undefined ? undefined : (0, pg_format_1.default)("%L", field.defaultValue);
149
- const [base, type] = this.fieldType(field);
150
- const res = await this.client.query(`SELECT *${this.version >= 12 ? ", pg_get_expr(pg_attrdef.adbin, pg_attrdef.adrelid) AS 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]);
146
+ const { attributes, oid, tableName } = table;
147
+ for (const attribute of attributes) {
148
+ const { fieldName, notNull, size } = attribute;
149
+ const defaultValue = attribute.defaultValue === undefined ? undefined : (0, pg_format_1.default)("%L", attribute.defaultValue);
150
+ const [base, type] = this.fieldType(attribute);
151
+ const where = "attrelid = $1 AND attnum > 0 AND atttypid = pg_type.oid AND attislocal = 't' AND attname = $2";
152
+ 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]);
151
153
  const addField = async () => {
152
154
  const statement = `ALTER TABLE ${tableName} ADD COLUMN ${fieldName} ${type}`;
153
- this.log(statement);
154
- await this.client.query(statement);
155
+ this.syncLog(statement);
156
+ if (this.sync)
157
+ await this.client.query(statement);
155
158
  };
156
159
  const dropDefault = async () => {
157
160
  const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} DROP DEFAULT`;
158
- this.log(statement);
159
- await this.client.query(statement);
160
- };
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);
161
+ this.syncLog(statement);
162
+ if (this.sync)
163
+ await this.client.query(statement);
167
164
  };
168
165
  const setNotNull = async (isNull) => {
169
166
  if (isNull === notNull)
170
167
  return;
171
168
  const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} ${notNull ? "SET" : "DROP"} NOT NULL`;
172
- this.log(statement);
173
- await this.client.query(statement);
169
+ this.syncLog(statement);
170
+ if (this.sync)
171
+ await this.client.query(statement);
172
+ };
173
+ const setDefault = async (isNull) => {
174
+ if (defaultValue !== undefined) {
175
+ let statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} SET DEFAULT ${defaultValue}`;
176
+ this.syncLog(statement);
177
+ if (this.sync)
178
+ await this.client.query(statement);
179
+ if (isNull) {
180
+ statement = `UPDATE ${tableName} SET ${fieldName} = ${defaultValue} WHERE ${fieldName} IS NULL`;
181
+ this.syncLog(statement);
182
+ if (this.sync)
183
+ this.client.query(statement);
184
+ }
185
+ }
186
+ await setNotNull(isNull);
174
187
  };
175
188
  if (!res.rowCount) {
176
189
  await addField();
177
- await setDefault();
178
- await setNotNull(false);
190
+ await setDefault(false);
179
191
  }
180
192
  else {
181
193
  const { adsrc, attnotnull, atttypmod, typname } = res.rows[0];
182
194
  if (types[typname] !== base || (base === "VARCHAR" && (size ? size + 4 !== atttypmod : atttypmod !== -1))) {
183
- if (needDrop.filter(([type, name]) => field.type === type && typname === name).length) {
195
+ if (needDrop.filter(([type, name]) => attribute.type === type && typname === name).length) {
184
196
  await this.dropField(tableName, fieldName);
185
197
  await addField();
186
- await setDefault();
187
- await setNotNull(false);
198
+ await setDefault(false);
188
199
  }
189
200
  else {
190
201
  if (adsrc)
191
202
  dropDefault();
192
- const using = needUsing.filter(([type, name]) => field.type === type && typname === name).length ? " USING " + fieldName + "::" + type : "";
203
+ const using = needUsing.filter(([type, name]) => attribute.type === type && typname === name).length ? " USING " + fieldName + "::" + type : "";
193
204
  const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} TYPE ${type}${using}`;
194
- this.log(statement);
195
- await this.client.query(statement);
196
- await setDefault();
197
- await setNotNull(attnotnull);
205
+ this.syncLog(statement);
206
+ if (this.sync)
207
+ await this.client.query(statement);
208
+ await setDefault(attnotnull);
198
209
  }
199
210
  }
200
211
  else if (defaultValue === undefined) {
@@ -202,21 +213,20 @@ class PGDB extends db_1.DB {
202
213
  dropDefault();
203
214
  await setNotNull(attnotnull);
204
215
  }
205
- else if (!adsrc || adsrc.split("::")[0] !== defaultValue) {
206
- await setDefault();
207
- await setNotNull(attnotnull);
208
- }
216
+ else if (!adsrc || adsrc.split("::")[0] !== defaultValue)
217
+ await setDefault(attnotnull);
209
218
  }
210
219
  }
211
220
  }
212
221
  async syncIndexes(table) {
213
222
  const { indexes, tableName } = table;
214
223
  for (const index of indexes) {
215
- const { fields, name, type, unique } = index;
216
- if (this.indexes.indexOf(name) === -1) {
217
- const statement = `CREATE${unique ? " UNIQUE" : ""} INDEX ${name} ON ${tableName} USING ${type} (${fields.join(", ")})`;
218
- this.log(statement);
219
- await this.client.query(statement);
224
+ const { fields, indexName, type, unique } = index;
225
+ if (!this.indexes.includes(indexName)) {
226
+ const statement = `CREATE${unique ? " UNIQUE" : ""} INDEX ${indexName} ON ${tableName} USING ${type} (${fields.join(", ")})`;
227
+ this.syncLog(statement);
228
+ if (this.sync)
229
+ await this.client.query(statement);
220
230
  }
221
231
  }
222
232
  }
@@ -224,10 +234,12 @@ class PGDB extends db_1.DB {
224
234
  if (!table.autoIncrementOwn)
225
235
  return;
226
236
  const statement = `ALTER SEQUENCE ${table.tableName}_id_seq OWNED BY ${table.tableName}.id`;
227
- this.log(statement);
228
- await this.client.query(statement);
237
+ this.syncLog(statement);
238
+ if (this.sync)
239
+ await this.client.query(statement);
229
240
  }
230
241
  async syncTable(table) {
242
+ var _a;
231
243
  if (table.autoIncrement) {
232
244
  await (async () => {
233
245
  try {
@@ -238,8 +250,9 @@ class PGDB extends db_1.DB {
238
250
  return;
239
251
  if (e.code === "42P01") {
240
252
  const statement = `CREATE SEQUENCE ${table.tableName}_id_seq`;
241
- this.log(statement);
242
- await this.client.query(statement);
253
+ this.syncLog(statement);
254
+ if (this.sync)
255
+ await this.client.query(statement);
243
256
  table.autoIncrementOwn = true;
244
257
  return;
245
258
  }
@@ -256,7 +269,7 @@ class PGDB extends db_1.DB {
256
269
  if (resParent.rowCount) {
257
270
  if (!table.parent)
258
271
  drop = true;
259
- else if (this.tables[table.parent.tableName].oid === resParent.rows[0].inhparent)
272
+ else if (this.findTable(table.parent.tableName).oid === resParent.rows[0].inhparent)
260
273
  return;
261
274
  drop = true;
262
275
  }
@@ -265,8 +278,9 @@ class PGDB extends db_1.DB {
265
278
  if (drop) {
266
279
  const statement = `DROP TABLE ${table.tableName} CASCADE`;
267
280
  create = true;
268
- this.log(statement);
269
- await this.client.query(statement);
281
+ this.syncLog(statement);
282
+ if (this.sync)
283
+ await this.client.query(statement);
270
284
  }
271
285
  }
272
286
  else
@@ -274,10 +288,11 @@ class PGDB extends db_1.DB {
274
288
  if (create) {
275
289
  const parent = table.parent ? ` INHERITS (${table.parent.tableName})` : "";
276
290
  const statement = `CREATE TABLE ${table.tableName} ()${parent}`;
277
- this.log(statement);
278
- await this.client.query(statement);
291
+ this.syncLog(statement);
292
+ if (this.sync)
293
+ await this.client.query(statement);
279
294
  const resTable = await this.client.query("SELECT oid FROM pg_class WHERE relname = $1", [table.tableName]);
280
- table.oid = resTable.rows[0].oid;
295
+ table.oid = (_a = resTable.rows[0]) === null || _a === void 0 ? void 0 : _a.oid;
281
296
  }
282
297
  }
283
298
  }
package/lib/pgdb.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Pool, PoolClient, PoolConfig } from "pg";
2
2
  import format from "pg-format";
3
- import { DB, Entry, Field, IndexDef, Natural, Table } from "sedentary/lib/db";
3
+ import { Attribute, DB, Index, Natural, Table } from "sedentary/lib/db";
4
+ import { adsrc } from "./adsrc";
4
5
 
5
6
  const needDrop = [
6
7
  ["DATETIME", "int2"],
@@ -36,84 +37,69 @@ export class PGDB extends DB {
36
37
  this.version = parseInt(res.rows[0].version.split(" ")[1].split(".")[0], 10);
37
38
  }
38
39
 
39
- async dropConstraints(table: Table): Promise<void> {
40
- let place = 1;
41
- const query = "SELECT conname FROM pg_attribute, pg_constraint WHERE attrelid = $1 AND conrelid = $1 AND attnum = conkey[1]";
42
- const values: (number | string)[] = [table.oid];
43
- const wheres = [];
40
+ async dropConstraints(table: Table): Promise<number[]> {
41
+ const indexes: number[] = [];
42
+ const res = await this.client.query("SELECT * FROM pg_constraint WHERE conrelid = $1 ORDER BY conname", [table.oid]);
44
43
 
45
- for(const i in table.constraints) {
46
- wheres.push(` AND (contype <> $${++place} or attname <> $${++place})`);
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
- */
44
+ for(const row of res.rows) {
45
+ const constraint = table.constraints.filter(_ => _.constraintName === row.conname);
65
46
 
66
- for(const i in res.rows) {
67
- const statement = `ALTER TABLE ${table.tableName} DROP CONSTRAINT ${res.rows[i].conname}`;
47
+ if(constraint.length === 0) {
48
+ const statement = `ALTER TABLE ${table.tableName} DROP CONSTRAINT ${row.conname} CASCADE`;
68
49
 
69
- this.log(statement);
70
- await this.client.query(statement);
50
+ this.syncLog(statement);
51
+ if(this.sync) await this.client.query(statement);
52
+ } else indexes.push(row.conindid);
71
53
  }
54
+
55
+ return indexes;
72
56
  }
73
57
 
74
58
  async dropField(tableName: string, fieldName: string): Promise<void> {
75
59
  const statement = `ALTER TABLE ${tableName} DROP COLUMN ${fieldName}`;
76
60
 
77
- this.log(statement);
78
- await this.client.query(statement);
61
+ this.syncLog(statement);
62
+ if(this.sync) await this.client.query(statement);
79
63
  }
80
64
 
81
65
  async dropFields(table: Table): Promise<void> {
82
66
  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
67
 
84
- for(const i in res.rows) if(table.fields.filter(f => f.fieldName === res.rows[i].attname).length === 0) await this.dropField(table.tableName, res.rows[i].attname);
68
+ for(const i in res.rows) if(! table.findField(res.rows[i].attname)) await this.dropField(table.tableName, res.rows[i].attname);
85
69
  }
86
70
 
87
- async dropIndexes(table: Table): Promise<void> {
71
+ async dropIndexes(table: Table, constraintIndexes: number[]): Promise<void> {
88
72
  const { indexes, oid } = table;
89
- const iobject: { [key: string]: IndexDef } = {};
73
+ const iobject: { [key: string]: Index } = {};
90
74
  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",
75
+ "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
76
  [oid]
93
77
  );
94
78
 
95
79
  for(const row of res.rows) {
96
- const { amname, attname, indisunique, relname } = row;
80
+ const { amname, attname, indexrelid, indisunique, relname } = row;
97
81
 
98
- if(iobject[relname]) iobject[relname].fields.push(attname);
99
- else iobject[relname] = { fields: [attname], name: relname, type: amname, unique: indisunique };
82
+ if(! constraintIndexes.includes(indexrelid)) {
83
+ if(iobject[relname]) iobject[relname].fields.push(attname);
84
+ else iobject[relname] = { fields: [attname], indexName: relname, type: amname, unique: indisunique };
85
+ }
100
86
  }
101
87
 
102
88
  this.indexes = [];
103
89
  for(const index of indexes) {
104
- const { name } = index;
90
+ const { indexName } = index;
105
91
 
106
- if(iobject[name] && this.indexesEq(index, iobject[name])) {
107
- this.indexes.push(name);
108
- delete iobject[name];
92
+ if(iobject[indexName] && this.indexesEq(index, iobject[indexName])) {
93
+ this.indexes.push(indexName);
94
+ delete iobject[indexName];
109
95
  }
110
96
  }
111
97
 
112
98
  for(const index of Object.keys(iobject).sort()) {
113
99
  const statement = `DROP INDEX ${index}`;
114
100
 
115
- this.log(statement);
116
- await this.client.query(statement);
101
+ this.syncLog(statement);
102
+ if(this.sync) await this.client.query(statement);
117
103
  }
118
104
  }
119
105
 
@@ -121,8 +107,8 @@ export class PGDB extends DB {
121
107
  await this.pool.end();
122
108
  }
123
109
 
124
- fieldType(field: Field<Natural, Entry>): string[] {
125
- const { size, type } = field;
110
+ fieldType(attribute: Attribute<Natural, unknown>): string[] {
111
+ const { size, type } = attribute;
126
112
  let ret;
127
113
 
128
114
  switch(type) {
@@ -141,11 +127,11 @@ export class PGDB extends DB {
141
127
  throw new Error(`Unknown type: '${type}', '${size}'`);
142
128
  }
143
129
 
144
- async sync(): Promise<void> {
130
+ async syncDataBase(): Promise<void> {
145
131
  let err: Error;
146
132
 
147
133
  try {
148
- await super.sync();
134
+ await super.syncDataBase();
149
135
  } catch(e) {
150
136
  err = e;
151
137
  }
@@ -156,101 +142,113 @@ export class PGDB extends DB {
156
142
  }
157
143
 
158
144
  async syncConstraints(table: Table): Promise<void> {
159
- for(const i in table.constraints) {
160
- const constraint = table.constraints[i];
161
-
145
+ for(const constraint of table.constraints) {
146
+ const { attribute, constraintName, type } = constraint;
162
147
  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
148
  table.oid,
164
- constraint.field
149
+ attribute.fieldName
165
150
  ]);
166
151
 
167
152
  if(! res.rowCount) {
168
- const statement = `ALTER TABLE ${table.tableName} ADD CONSTRAINT ${constraint.name} ${constraint.type === "u" ? `UNIQUE(${constraint.field})` : ``}`;
153
+ let query: string;
154
+
155
+ switch(type) {
156
+ case "f":
157
+ query = `FOREIGN KEY (${attribute.fieldName}) REFERENCES ${attribute.foreignKey.tableName}(${attribute.foreignKey.fieldName})`;
158
+ break;
159
+ case "u":
160
+ query = `UNIQUE(${attribute.fieldName})`;
161
+ break;
162
+ }
163
+
164
+ const statement = `ALTER TABLE ${table.tableName} ADD CONSTRAINT ${constraintName} ${query}`;
169
165
 
170
- this.log(statement);
171
- await this.client.query(statement);
166
+ this.syncLog(statement);
167
+ if(this.sync) await this.client.query(statement);
172
168
  }
173
169
  }
174
170
  }
175
171
 
176
172
  async syncFields(table: Table): Promise<void> {
177
- const { fields, oid, tableName } = table;
173
+ const { attributes, oid, tableName } = table;
178
174
 
179
- for(const field of fields) {
180
- const { fieldName, notNull, size } = field;
181
- const defaultValue = field.defaultValue === undefined ? undefined : format("%L", field.defaultValue);
182
- const [base, type] = this.fieldType(field);
175
+ for(const attribute of attributes) {
176
+ const { fieldName, notNull, size } = attribute;
177
+ const defaultValue = attribute.defaultValue === undefined ? undefined : format("%L", attribute.defaultValue);
178
+ const [base, type] = this.fieldType(attribute);
179
+ const where = "attrelid = $1 AND attnum > 0 AND atttypid = pg_type.oid AND attislocal = 't' AND attname = $2";
183
180
 
184
181
  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`,
182
+ `SELECT attnotnull, atttypmod, typname, ${adsrc(this.version)} FROM pg_type, pg_attribute LEFT JOIN pg_attrdef ON adrelid = attrelid AND adnum = attnum WHERE ${where}`,
188
183
  [oid, fieldName]
189
184
  );
190
185
 
191
186
  const addField = async () => {
192
187
  const statement = `ALTER TABLE ${tableName} ADD COLUMN ${fieldName} ${type}`;
193
188
 
194
- this.log(statement);
195
- await this.client.query(statement);
189
+ this.syncLog(statement);
190
+ if(this.sync) await this.client.query(statement);
196
191
  };
197
192
 
198
193
  const dropDefault = async () => {
199
194
  const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} DROP DEFAULT`;
200
195
 
201
- this.log(statement);
202
- await this.client.query(statement);
196
+ this.syncLog(statement);
197
+ if(this.sync) await this.client.query(statement);
203
198
  };
204
199
 
205
- const setDefault = async () => {
206
- if(defaultValue === undefined) return;
200
+ const setNotNull = async (isNull: boolean) => {
201
+ if(isNull === notNull) return;
207
202
 
208
- const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} SET DEFAULT ${defaultValue}`;
203
+ const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} ${notNull ? "SET" : "DROP"} NOT NULL`;
209
204
 
210
- this.log(statement);
211
- await this.client.query(statement);
205
+ this.syncLog(statement);
206
+ if(this.sync) await this.client.query(statement);
212
207
  };
213
208
 
214
- const setNotNull = async (isNull: boolean) => {
215
- if(isNull === notNull) return;
209
+ const setDefault = async (isNull: boolean) => {
210
+ if(defaultValue !== undefined) {
211
+ let statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} SET DEFAULT ${defaultValue}`;
216
212
 
217
- const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} ${notNull ? "SET" : "DROP"} NOT NULL`;
213
+ this.syncLog(statement);
214
+ if(this.sync) await this.client.query(statement);
215
+
216
+ if(isNull) {
217
+ statement = `UPDATE ${tableName} SET ${fieldName} = ${defaultValue} WHERE ${fieldName} IS NULL`;
218
218
 
219
- this.log(statement);
220
- await this.client.query(statement);
219
+ this.syncLog(statement);
220
+ if(this.sync) this.client.query(statement);
221
+ }
222
+ }
223
+
224
+ await setNotNull(isNull);
221
225
  };
222
226
 
223
227
  if(! res.rowCount) {
224
228
  await addField();
225
- await setDefault();
226
- await setNotNull(false);
229
+ await setDefault(false);
227
230
  } else {
228
231
  const { adsrc, attnotnull, atttypmod, typname } = res.rows[0];
229
232
 
230
233
  if(types[typname] !== base || (base === "VARCHAR" && (size ? size + 4 !== atttypmod : atttypmod !== -1))) {
231
- if(needDrop.filter(([type, name]) => field.type === type && typname === name).length) {
234
+ if(needDrop.filter(([type, name]) => attribute.type === type && typname === name).length) {
232
235
  await this.dropField(tableName, fieldName);
233
236
  await addField();
234
- await setDefault();
235
- await setNotNull(false);
237
+ await setDefault(false);
236
238
  } else {
237
239
  if(adsrc) dropDefault();
238
240
 
239
- const using = needUsing.filter(([type, name]) => field.type === type && typname === name).length ? " USING " + fieldName + "::" + type : "";
241
+ const using = needUsing.filter(([type, name]) => attribute.type === type && typname === name).length ? " USING " + fieldName + "::" + type : "";
240
242
  const statement = `ALTER TABLE ${tableName} ALTER COLUMN ${fieldName} TYPE ${type}${using}`;
241
243
 
242
- this.log(statement);
243
- await this.client.query(statement);
244
- await setDefault();
245
- await setNotNull(attnotnull);
244
+ this.syncLog(statement);
245
+ if(this.sync) await this.client.query(statement);
246
+ await setDefault(attnotnull);
246
247
  }
247
248
  } else if(defaultValue === undefined) {
248
249
  if(adsrc) dropDefault();
249
250
  await setNotNull(attnotnull);
250
- } else if(! adsrc || adsrc.split("::")[0] !== defaultValue) {
251
- await setDefault();
252
- await setNotNull(attnotnull);
253
- }
251
+ } else if(! adsrc || adsrc.split("::")[0] !== defaultValue) await setDefault(attnotnull);
254
252
  }
255
253
  }
256
254
  }
@@ -259,13 +257,13 @@ export class PGDB extends DB {
259
257
  const { indexes, tableName } = table;
260
258
 
261
259
  for(const index of indexes) {
262
- const { fields, name, type, unique } = index;
260
+ const { fields, indexName, type, unique } = index;
263
261
 
264
- if(this.indexes.indexOf(name) === -1) {
265
- const statement = `CREATE${unique ? " UNIQUE" : ""} INDEX ${name} ON ${tableName} USING ${type} (${fields.join(", ")})`;
262
+ if(! this.indexes.includes(indexName)) {
263
+ const statement = `CREATE${unique ? " UNIQUE" : ""} INDEX ${indexName} ON ${tableName} USING ${type} (${fields.join(", ")})`;
266
264
 
267
- this.log(statement);
268
- await this.client.query(statement);
265
+ this.syncLog(statement);
266
+ if(this.sync) await this.client.query(statement);
269
267
  }
270
268
  }
271
269
  }
@@ -275,8 +273,8 @@ export class PGDB extends DB {
275
273
 
276
274
  const statement = `ALTER SEQUENCE ${table.tableName}_id_seq OWNED BY ${table.tableName}.id`;
277
275
 
278
- this.log(statement);
279
- await this.client.query(statement);
276
+ this.syncLog(statement);
277
+ if(this.sync) await this.client.query(statement);
280
278
  }
281
279
 
282
280
  async syncTable(table: Table): Promise<void> {
@@ -289,8 +287,8 @@ export class PGDB extends DB {
289
287
  if(e.code === "42P01") {
290
288
  const statement = `CREATE SEQUENCE ${table.tableName}_id_seq`;
291
289
 
292
- this.log(statement);
293
- await this.client.query(statement);
290
+ this.syncLog(statement);
291
+ if(this.sync) await this.client.query(statement);
294
292
  table.autoIncrementOwn = true;
295
293
 
296
294
  return;
@@ -312,7 +310,7 @@ export class PGDB extends DB {
312
310
 
313
311
  if(resParent.rowCount) {
314
312
  if(! table.parent) drop = true;
315
- else if(this.tables[table.parent.tableName].oid === resParent.rows[0].inhparent) return;
313
+ else if(this.findTable(table.parent.tableName).oid === resParent.rows[0].inhparent) return;
316
314
 
317
315
  drop = true;
318
316
  } else if(table.parent) drop = true;
@@ -321,8 +319,8 @@ export class PGDB extends DB {
321
319
  const statement = `DROP TABLE ${table.tableName} CASCADE`;
322
320
 
323
321
  create = true;
324
- this.log(statement);
325
- await this.client.query(statement);
322
+ this.syncLog(statement);
323
+ if(this.sync) await this.client.query(statement);
326
324
  }
327
325
  } else create = true;
328
326
 
@@ -330,12 +328,12 @@ export class PGDB extends DB {
330
328
  const parent = table.parent ? ` INHERITS (${table.parent.tableName})` : "";
331
329
  const statement = `CREATE TABLE ${table.tableName} ()${parent}`;
332
330
 
333
- this.log(statement);
334
- await this.client.query(statement);
331
+ this.syncLog(statement);
332
+ if(this.sync) await this.client.query(statement);
335
333
 
336
334
  const resTable = await this.client.query("SELECT oid FROM pg_class WHERE relname = $1", [table.tableName]);
337
335
 
338
- table.oid = resTable.rows[0].oid;
336
+ table.oid = resTable.rows[0]?.oid;
339
337
  }
340
338
  }
341
339
  }
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.13"
9
+ "sedentary": "0.0.17"
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.7",
14
+ "@types/node": "16.11.10",
15
15
  "@types/yamljs": "0.2.31",
16
- "@typescript-eslint/eslint-plugin": "5.3.1",
17
- "@typescript-eslint/parser": "5.3.1",
18
- "eslint": "8.2.0",
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.4.1",
21
+ "prettier": "2.5.0",
22
22
  "ts-node": "10.4.0",
23
- "typescript": "4.4.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.13"
65
+ "version": "0.0.17"
63
66
  }