sedentary-pg 0.0.12 → 0.0.16

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