rake-db 2.2.4 → 2.2.6
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/CHANGELOG.md +14 -0
- package/db.ts +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.esm.js +92 -60
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +92 -60
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/ast.ts +4 -0
- package/src/commands/migrateOrRollback.ts +8 -6
- package/src/common.test.ts +9 -6
- package/src/common.ts +21 -11
- package/src/migration/changeTable.test.ts +17 -0
- package/src/migration/changeTable.ts +13 -10
- package/src/migration/createJoinTable.test.ts +96 -0
- package/src/migration/createJoinTable.ts +17 -6
- package/src/migration/createTable.test.ts +16 -0
- package/src/migration/createTable.ts +11 -8
- package/src/migration/migration.test.ts +18 -86
- package/src/migration/migration.ts +18 -4
- package/src/migration/migrationUtils.ts +33 -21
- package/src/pull/dbStructure.test.ts +158 -0
- package/src/pull/dbStructure.ts +272 -0
- package/src/pull/getColumnByType.ts +15 -0
- package/src/pull/structureToAst.test.ts +308 -0
- package/src/pull/structureToAst.ts +145 -0
|
@@ -10,7 +10,12 @@ import {
|
|
|
10
10
|
toArray,
|
|
11
11
|
} from 'pqb';
|
|
12
12
|
import { ColumnComment, Migration } from './migration';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
getSchemaAndTableFromName,
|
|
15
|
+
joinColumns,
|
|
16
|
+
joinWords,
|
|
17
|
+
quoteWithSchema,
|
|
18
|
+
} from '../common';
|
|
14
19
|
|
|
15
20
|
export const columnToSql = (
|
|
16
21
|
key: string,
|
|
@@ -30,7 +35,7 @@ export const columnToSql = (
|
|
|
30
35
|
|
|
31
36
|
if (item.isPrimaryKey && !hasMultiplePrimaryKeys) {
|
|
32
37
|
line.push('PRIMARY KEY');
|
|
33
|
-
} else if (!item.isNullable) {
|
|
38
|
+
} else if (!item.data.isNullable) {
|
|
34
39
|
line.push('NOT NULL');
|
|
35
40
|
}
|
|
36
41
|
|
|
@@ -48,7 +53,7 @@ export const columnToSql = (
|
|
|
48
53
|
|
|
49
54
|
const { foreignKey } = item.data;
|
|
50
55
|
if (foreignKey) {
|
|
51
|
-
const table = getForeignKeyTable(
|
|
56
|
+
const [schema, table] = getForeignKeyTable(
|
|
52
57
|
'fn' in foreignKey ? foreignKey.fn : foreignKey.table,
|
|
53
58
|
);
|
|
54
59
|
|
|
@@ -56,7 +61,7 @@ export const columnToSql = (
|
|
|
56
61
|
line.push(`CONSTRAINT "${foreignKey.name}"`);
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
line.push(referencesToSql(table, foreignKey.columns, foreignKey));
|
|
64
|
+
line.push(referencesToSql(schema, table, foreignKey.columns, foreignKey));
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
return line.join(' ');
|
|
@@ -89,42 +94,49 @@ export const addColumnComment = (
|
|
|
89
94
|
|
|
90
95
|
export const getForeignKeyTable = (
|
|
91
96
|
fnOrTable: (() => ForeignKeyTable) | string,
|
|
92
|
-
) => {
|
|
97
|
+
): [string | undefined, string] => {
|
|
93
98
|
if (typeof fnOrTable === 'string') {
|
|
94
|
-
return fnOrTable;
|
|
99
|
+
return getSchemaAndTableFromName(fnOrTable);
|
|
95
100
|
}
|
|
96
101
|
|
|
97
|
-
const
|
|
98
|
-
return
|
|
102
|
+
const item = new (fnOrTable())();
|
|
103
|
+
return [item.schema, item.table];
|
|
99
104
|
};
|
|
100
105
|
|
|
101
106
|
export const constraintToSql = (
|
|
102
|
-
|
|
107
|
+
{ name }: { schema?: string; name: string },
|
|
103
108
|
up: boolean,
|
|
104
109
|
foreignKey: TableData['foreignKeys'][number],
|
|
105
110
|
) => {
|
|
106
111
|
const constraintName =
|
|
107
|
-
foreignKey.options.name ||
|
|
108
|
-
`${tableName}_${foreignKey.columns.join('_')}_fkey`;
|
|
112
|
+
foreignKey.options.name || `${name}_${foreignKey.columns.join('_')}_fkey`;
|
|
109
113
|
|
|
110
114
|
if (!up) {
|
|
111
115
|
const { dropMode } = foreignKey.options;
|
|
112
116
|
return `CONSTRAINT "${constraintName}"${dropMode ? ` ${dropMode}` : ''}`;
|
|
113
117
|
}
|
|
114
118
|
|
|
115
|
-
const table = getForeignKeyTable(foreignKey.fnOrTable);
|
|
119
|
+
const [schema, table] = getForeignKeyTable(foreignKey.fnOrTable);
|
|
116
120
|
return `CONSTRAINT "${constraintName}" FOREIGN KEY (${joinColumns(
|
|
117
121
|
foreignKey.columns,
|
|
118
|
-
)}) ${referencesToSql(
|
|
122
|
+
)}) ${referencesToSql(
|
|
123
|
+
schema,
|
|
124
|
+
table,
|
|
125
|
+
foreignKey.foreignColumns,
|
|
126
|
+
foreignKey.options,
|
|
127
|
+
)}`;
|
|
119
128
|
};
|
|
120
129
|
|
|
121
130
|
export const referencesToSql = (
|
|
131
|
+
schema: string | undefined,
|
|
122
132
|
table: string,
|
|
123
133
|
columns: string[],
|
|
124
134
|
foreignKey: Pick<ForeignKeyOptions, 'match' | 'onDelete' | 'onUpdate'>,
|
|
125
135
|
) => {
|
|
126
136
|
const sql: string[] = [
|
|
127
|
-
`REFERENCES ${
|
|
137
|
+
`REFERENCES ${quoteWithSchema({ schema, name: table })}(${joinColumns(
|
|
138
|
+
columns,
|
|
139
|
+
)})`,
|
|
128
140
|
];
|
|
129
141
|
|
|
130
142
|
if (foreignKey.match) {
|
|
@@ -144,13 +156,13 @@ export const referencesToSql = (
|
|
|
144
156
|
|
|
145
157
|
export const indexesToQuery = (
|
|
146
158
|
up: boolean,
|
|
147
|
-
|
|
159
|
+
{ schema, name }: { schema?: string; name: string },
|
|
148
160
|
indexes: TableData.Index[],
|
|
149
161
|
): Sql[] => {
|
|
150
162
|
return indexes.map(({ columns, options }) => {
|
|
151
163
|
const indexName =
|
|
152
164
|
options.name ||
|
|
153
|
-
joinWords(
|
|
165
|
+
joinWords(name, ...columns.map(({ column }) => column), 'index');
|
|
154
166
|
|
|
155
167
|
if (!up) {
|
|
156
168
|
return {
|
|
@@ -169,7 +181,7 @@ export const indexesToQuery = (
|
|
|
169
181
|
sql.push('UNIQUE');
|
|
170
182
|
}
|
|
171
183
|
|
|
172
|
-
sql.push(`INDEX "${indexName}" ON ${
|
|
184
|
+
sql.push(`INDEX "${indexName}" ON ${quoteWithSchema({ schema, name })}`);
|
|
173
185
|
|
|
174
186
|
if (options.using) {
|
|
175
187
|
sql.push(`USING ${options.using}`);
|
|
@@ -234,13 +246,13 @@ export const indexesToQuery = (
|
|
|
234
246
|
};
|
|
235
247
|
|
|
236
248
|
export const commentsToQuery = (
|
|
237
|
-
|
|
249
|
+
schemaTable: { schema?: string; name: string },
|
|
238
250
|
comments: ColumnComment[],
|
|
239
251
|
): Sql[] => {
|
|
240
252
|
return comments.map(({ column, comment }) => ({
|
|
241
|
-
text: `COMMENT ON COLUMN ${
|
|
242
|
-
|
|
243
|
-
)}`,
|
|
253
|
+
text: `COMMENT ON COLUMN ${quoteWithSchema(
|
|
254
|
+
schemaTable,
|
|
255
|
+
)}."${column}" IS ${quote(comment)}`,
|
|
244
256
|
values: [],
|
|
245
257
|
}));
|
|
246
258
|
};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { DbStructure } from './dbStructure';
|
|
2
|
+
import { Adapter } from 'pqb';
|
|
3
|
+
|
|
4
|
+
const adapter = new Adapter({
|
|
5
|
+
databaseURL: 'file:path',
|
|
6
|
+
});
|
|
7
|
+
let rows: unknown[][] | Record<string, unknown>[] = [];
|
|
8
|
+
adapter.query = jest.fn().mockImplementation(() => ({ rows }));
|
|
9
|
+
adapter.arrays = jest.fn().mockImplementation(() => ({ rows }));
|
|
10
|
+
const db = new DbStructure(adapter);
|
|
11
|
+
|
|
12
|
+
describe('dbStructure', () => {
|
|
13
|
+
describe('getSchemas', () => {
|
|
14
|
+
it('should return schemas', async () => {
|
|
15
|
+
rows = [['a'], ['b']];
|
|
16
|
+
const result = await db.getSchemas();
|
|
17
|
+
expect(result).toEqual(['a', 'b']);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('getTables', () => {
|
|
22
|
+
it('should return tables', async () => {
|
|
23
|
+
rows = [{ schemaName: 'schema', name: 'table' }];
|
|
24
|
+
const result = await db.getTables();
|
|
25
|
+
expect(result).toEqual(rows);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('getViews', () => {
|
|
30
|
+
it('should return views', async () => {
|
|
31
|
+
rows = [{ schemaName: 'schema', name: 'view' }];
|
|
32
|
+
const result = await db.getViews();
|
|
33
|
+
expect(result).toEqual(rows);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('getProcedures', () => {
|
|
38
|
+
it('should return procedures', async () => {
|
|
39
|
+
rows = [
|
|
40
|
+
{
|
|
41
|
+
schemaName: 'public',
|
|
42
|
+
name: 'name',
|
|
43
|
+
returnSet: true,
|
|
44
|
+
returnType: 'int4',
|
|
45
|
+
kind: 'f',
|
|
46
|
+
isTrigger: false,
|
|
47
|
+
types: ['int4', 'int4', 'int4'],
|
|
48
|
+
argTypes: [23, 23, 23],
|
|
49
|
+
argModes: ['i', 'i', 'o'],
|
|
50
|
+
argNames: ['a', 'b', 'c'],
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
const result = await db.getProcedures();
|
|
54
|
+
expect(result).toEqual(rows);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('getColumns', () => {
|
|
59
|
+
it('should return columns', async () => {
|
|
60
|
+
rows = [
|
|
61
|
+
{
|
|
62
|
+
schemaName: 'public',
|
|
63
|
+
tableName: 'table',
|
|
64
|
+
name: 'name',
|
|
65
|
+
type: 'int4',
|
|
66
|
+
default: '123',
|
|
67
|
+
isNullable: false,
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
const result = await db.getColumns();
|
|
71
|
+
expect(result).toEqual(rows);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('getIndexes', () => {
|
|
76
|
+
it('should return indexes', async () => {
|
|
77
|
+
rows = [
|
|
78
|
+
{
|
|
79
|
+
schemaName: 'public',
|
|
80
|
+
tableName: 'table',
|
|
81
|
+
columnNames: ['column'],
|
|
82
|
+
name: 'indexName',
|
|
83
|
+
isUnique: true,
|
|
84
|
+
isPrimary: true,
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
const result = await db.getIndexes();
|
|
88
|
+
expect(result).toEqual(rows);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('getForeignKeys', () => {
|
|
93
|
+
it('should return foreignKeys', async () => {
|
|
94
|
+
rows = [
|
|
95
|
+
{
|
|
96
|
+
schemaName: 'public',
|
|
97
|
+
tableName: 'table',
|
|
98
|
+
foreignTableSchemaName: 'public',
|
|
99
|
+
foreignTableName: 'foreignTable',
|
|
100
|
+
name: 'name',
|
|
101
|
+
columnNames: ['column'],
|
|
102
|
+
foreignColumnNames: ['foreignColumn'],
|
|
103
|
+
},
|
|
104
|
+
];
|
|
105
|
+
const result = await db.getForeignKeys();
|
|
106
|
+
expect(result).toEqual(rows);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('getConstraints', () => {
|
|
111
|
+
it('should return constraints', async () => {
|
|
112
|
+
rows = [
|
|
113
|
+
{
|
|
114
|
+
schemaName: 'public',
|
|
115
|
+
tableName: 'table',
|
|
116
|
+
name: 'name',
|
|
117
|
+
type: 'PRIMARY KEY',
|
|
118
|
+
columnNames: ['id'],
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
const result = await db.getConstraints();
|
|
122
|
+
expect(result).toEqual(rows);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('getTriggers', () => {
|
|
127
|
+
it('should return triggers', async () => {
|
|
128
|
+
rows = [
|
|
129
|
+
{
|
|
130
|
+
schemaName: 'public',
|
|
131
|
+
tableName: 'table',
|
|
132
|
+
triggerSchema: 'public',
|
|
133
|
+
name: 'name',
|
|
134
|
+
events: ['UPDATE'],
|
|
135
|
+
activation: 'BEFORE',
|
|
136
|
+
condition: null,
|
|
137
|
+
definition: 'EXECUTE FUNCTION name()',
|
|
138
|
+
},
|
|
139
|
+
];
|
|
140
|
+
const result = await db.getTriggers();
|
|
141
|
+
expect(result).toEqual(rows);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe('getExtensions', () => {
|
|
146
|
+
it('should return extensions', async () => {
|
|
147
|
+
rows = [
|
|
148
|
+
{
|
|
149
|
+
schemaName: 'public',
|
|
150
|
+
name: 'pg_trgm',
|
|
151
|
+
version: '1.6',
|
|
152
|
+
},
|
|
153
|
+
];
|
|
154
|
+
const result = await db.getExtensions();
|
|
155
|
+
expect(result).toEqual(rows);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
});
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { Adapter } from 'pqb';
|
|
2
|
+
|
|
3
|
+
export namespace DbStructure {
|
|
4
|
+
export type Table = {
|
|
5
|
+
schemaName: string;
|
|
6
|
+
name: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type View = {
|
|
10
|
+
schemaName: string;
|
|
11
|
+
name: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type Procedure = {
|
|
15
|
+
schemaName: string;
|
|
16
|
+
name: string;
|
|
17
|
+
returnSet: boolean;
|
|
18
|
+
returnType: string;
|
|
19
|
+
kind: string;
|
|
20
|
+
isTrigger: boolean;
|
|
21
|
+
types: string[];
|
|
22
|
+
argTypes: string[];
|
|
23
|
+
argModes: ('i' | 'o')[];
|
|
24
|
+
argNames?: string[];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type Column = {
|
|
28
|
+
schemaName: string;
|
|
29
|
+
tableName: string;
|
|
30
|
+
name: string;
|
|
31
|
+
type: string;
|
|
32
|
+
maxChars?: number;
|
|
33
|
+
numericPrecision?: number;
|
|
34
|
+
numericScale?: number;
|
|
35
|
+
dateTimePrecision?: number;
|
|
36
|
+
default?: string;
|
|
37
|
+
isNullable: boolean;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type Index = {
|
|
41
|
+
schemaName: string;
|
|
42
|
+
tableName: string;
|
|
43
|
+
columnNames: string[];
|
|
44
|
+
name: string;
|
|
45
|
+
isUnique: boolean;
|
|
46
|
+
isPrimary: boolean;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type ForeignKey = {
|
|
50
|
+
schemaName: string;
|
|
51
|
+
tableName: string;
|
|
52
|
+
foreignTableSchemaName: string;
|
|
53
|
+
foreignTableName: string;
|
|
54
|
+
name: string;
|
|
55
|
+
columnNames: string[];
|
|
56
|
+
foreignColumnNames: string[];
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type Constraint = {
|
|
60
|
+
schemaName: string;
|
|
61
|
+
tableName: string;
|
|
62
|
+
name: string;
|
|
63
|
+
type: 'CHECK' | 'FOREIGN KEY' | 'PRIMARY KEY' | 'UNIQUE';
|
|
64
|
+
columnNames: string[];
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export type Trigger = {
|
|
68
|
+
schemaName: string;
|
|
69
|
+
tableName: string;
|
|
70
|
+
triggerSchema: string;
|
|
71
|
+
name: string;
|
|
72
|
+
events: string[];
|
|
73
|
+
activation: string;
|
|
74
|
+
condition?: string;
|
|
75
|
+
definition: string;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export type Extension = {
|
|
79
|
+
schemaName: string;
|
|
80
|
+
name: string;
|
|
81
|
+
version?: string;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const filterSchema = (table: string) =>
|
|
86
|
+
`${table} !~ '^pg_' AND ${table} != 'information_schema'`;
|
|
87
|
+
|
|
88
|
+
export class DbStructure {
|
|
89
|
+
constructor(private db: Adapter) {}
|
|
90
|
+
|
|
91
|
+
async getSchemas(): Promise<string[]> {
|
|
92
|
+
const { rows } = await this.db.arrays<[string]>(
|
|
93
|
+
`SELECT n.nspname "name"
|
|
94
|
+
FROM pg_catalog.pg_namespace n
|
|
95
|
+
WHERE ${filterSchema('n.nspname')}
|
|
96
|
+
ORDER BY "name"`,
|
|
97
|
+
);
|
|
98
|
+
return rows.flat();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async getTables() {
|
|
102
|
+
const { rows } = await this.db.query<DbStructure.Table>(
|
|
103
|
+
`SELECT
|
|
104
|
+
table_schema "schemaName",
|
|
105
|
+
table_name "name"
|
|
106
|
+
FROM information_schema.tables
|
|
107
|
+
WHERE table_type = 'BASE TABLE'
|
|
108
|
+
AND ${filterSchema('table_schema')}
|
|
109
|
+
ORDER BY table_name`,
|
|
110
|
+
);
|
|
111
|
+
return rows;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async getViews() {
|
|
115
|
+
const { rows } = await this.db.query<DbStructure.View[]>(
|
|
116
|
+
`SELECT
|
|
117
|
+
table_schema "schemaName",
|
|
118
|
+
table_name "name"
|
|
119
|
+
FROM information_schema.tables
|
|
120
|
+
WHERE table_type = 'VIEW'
|
|
121
|
+
AND ${filterSchema('table_schema')}
|
|
122
|
+
ORDER BY table_name`,
|
|
123
|
+
);
|
|
124
|
+
return rows;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async getProcedures() {
|
|
128
|
+
const { rows } = await this.db.query<DbStructure.Procedure[]>(
|
|
129
|
+
`SELECT
|
|
130
|
+
n.nspname AS "schemaName",
|
|
131
|
+
proname AS name,
|
|
132
|
+
proretset AS "returnSet",
|
|
133
|
+
(
|
|
134
|
+
SELECT typname FROM pg_type WHERE oid = prorettype
|
|
135
|
+
) AS "returnType",
|
|
136
|
+
prokind AS "kind",
|
|
137
|
+
coalesce((
|
|
138
|
+
SELECT true FROM information_schema.triggers
|
|
139
|
+
WHERE n.nspname = trigger_schema AND trigger_name = proname
|
|
140
|
+
LIMIT 1
|
|
141
|
+
), false) AS "isTrigger",
|
|
142
|
+
coalesce((
|
|
143
|
+
SELECT json_agg(pg_type.typname)
|
|
144
|
+
FROM unnest(coalesce(proallargtypes, proargtypes)) typeId
|
|
145
|
+
JOIN pg_type ON pg_type.oid = typeId
|
|
146
|
+
), '[]') AS "types",
|
|
147
|
+
coalesce(to_json(proallargtypes::int[]), to_json(proargtypes::int[])) AS "argTypes",
|
|
148
|
+
coalesce(to_json(proargmodes), '[]') AS "argModes",
|
|
149
|
+
to_json(proargnames) AS "argNames"
|
|
150
|
+
FROM pg_proc p
|
|
151
|
+
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
152
|
+
WHERE ${filterSchema('n.nspname')}`,
|
|
153
|
+
);
|
|
154
|
+
return rows;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async getColumns() {
|
|
158
|
+
const { rows } = await this.db.query<DbStructure.Column>(
|
|
159
|
+
`SELECT table_schema "schemaName",
|
|
160
|
+
table_name "tableName",
|
|
161
|
+
column_name "name",
|
|
162
|
+
udt_name "type",
|
|
163
|
+
character_maximum_length AS "maxChars",
|
|
164
|
+
numeric_precision AS "numericPrecision",
|
|
165
|
+
numeric_scale AS "numericScale",
|
|
166
|
+
datetime_precision AS "dateTimePrecision",
|
|
167
|
+
column_default "default",
|
|
168
|
+
is_nullable::boolean "isNullable"
|
|
169
|
+
FROM information_schema.columns
|
|
170
|
+
WHERE ${filterSchema('table_schema')}
|
|
171
|
+
ORDER BY ordinal_position`,
|
|
172
|
+
);
|
|
173
|
+
return rows;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async getIndexes() {
|
|
177
|
+
const { rows } = await this.db.query<DbStructure.Index>(
|
|
178
|
+
`SELECT
|
|
179
|
+
nspname "schemaName",
|
|
180
|
+
t.relname "tableName",
|
|
181
|
+
json_agg(attname) "columnNames",
|
|
182
|
+
ic.relname "name",
|
|
183
|
+
indisunique "isUnique",
|
|
184
|
+
indisprimary "isPrimary"
|
|
185
|
+
FROM pg_index
|
|
186
|
+
JOIN pg_class t ON t.oid = indrelid
|
|
187
|
+
JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
188
|
+
JOIN pg_attribute ON attrelid = t.oid AND attnum = any(indkey)
|
|
189
|
+
JOIN pg_class ic ON ic.oid = indexrelid
|
|
190
|
+
WHERE ${filterSchema('n.nspname')}
|
|
191
|
+
GROUP BY "schemaName", "tableName", "name", "isUnique", "isPrimary"
|
|
192
|
+
ORDER BY "name"`,
|
|
193
|
+
);
|
|
194
|
+
return rows;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async getForeignKeys() {
|
|
198
|
+
const { rows } = await this.db.query<DbStructure.ForeignKey>(
|
|
199
|
+
`SELECT tc.table_schema AS "schemaName",
|
|
200
|
+
tc.table_name AS "tableName",
|
|
201
|
+
ccu.table_schema AS "foreignTableSchemaName",
|
|
202
|
+
ccu.table_name AS "foreignTableName",
|
|
203
|
+
tc.constraint_name AS "name",
|
|
204
|
+
(
|
|
205
|
+
SELECT json_agg(kcu.column_name)
|
|
206
|
+
FROM information_schema.key_column_usage kcu
|
|
207
|
+
WHERE kcu.constraint_name = tc.constraint_name
|
|
208
|
+
AND kcu.table_schema = tc.table_schema
|
|
209
|
+
) AS "columnNames",
|
|
210
|
+
json_agg(ccu.column_name) AS "foreignColumnNames"
|
|
211
|
+
FROM information_schema.table_constraints tc
|
|
212
|
+
JOIN information_schema.constraint_column_usage ccu
|
|
213
|
+
ON ccu.constraint_name = tc.constraint_name
|
|
214
|
+
AND ccu.table_schema = tc.table_schema
|
|
215
|
+
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
216
|
+
AND ${filterSchema('tc.table_schema')}
|
|
217
|
+
GROUP BY "schemaName", "tableName", "name", "foreignTableSchemaName", "foreignTableName"
|
|
218
|
+
ORDER BY "name"`,
|
|
219
|
+
);
|
|
220
|
+
return rows;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async getConstraints() {
|
|
224
|
+
const { rows } = await this.db.query<DbStructure.Constraint>(
|
|
225
|
+
`SELECT tc.table_schema AS "schemaName",
|
|
226
|
+
tc.table_name AS "tableName",
|
|
227
|
+
tc.constraint_name AS "name",
|
|
228
|
+
tc.constraint_type AS "type",
|
|
229
|
+
json_agg(ccu.column_name) "columnNames"
|
|
230
|
+
FROM information_schema.table_constraints tc
|
|
231
|
+
JOIN information_schema.constraint_column_usage ccu
|
|
232
|
+
ON ccu.constraint_name = tc.constraint_name
|
|
233
|
+
AND ccu.table_schema = tc.table_schema
|
|
234
|
+
WHERE tc.constraint_type != 'FOREIGN KEY'
|
|
235
|
+
AND ${filterSchema('tc.table_schema')}
|
|
236
|
+
GROUP BY "schemaName", "tableName", "name", "type"
|
|
237
|
+
ORDER BY "name"`,
|
|
238
|
+
);
|
|
239
|
+
return rows;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async getTriggers() {
|
|
243
|
+
const { rows } = await this.db.query<DbStructure.Trigger>(
|
|
244
|
+
`SELECT event_object_schema AS "schemaName",
|
|
245
|
+
event_object_table AS "tableName",
|
|
246
|
+
trigger_schema AS "triggerSchema",
|
|
247
|
+
trigger_name AS name,
|
|
248
|
+
json_agg(event_manipulation) AS events,
|
|
249
|
+
action_timing AS activation,
|
|
250
|
+
action_condition AS condition,
|
|
251
|
+
action_statement AS definition
|
|
252
|
+
FROM information_schema.triggers
|
|
253
|
+
WHERE ${filterSchema('event_object_schema')}
|
|
254
|
+
GROUP BY event_object_schema, event_object_table, trigger_schema, trigger_name, action_timing, action_condition, action_statement
|
|
255
|
+
ORDER BY trigger_name`,
|
|
256
|
+
);
|
|
257
|
+
return rows;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async getExtensions() {
|
|
261
|
+
const { rows } = await this.db.query<DbStructure.Extension>(
|
|
262
|
+
`SELECT
|
|
263
|
+
nspname AS "schemaName",
|
|
264
|
+
extname AS "name",
|
|
265
|
+
extversion AS version
|
|
266
|
+
FROM pg_extension
|
|
267
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = extnamespace
|
|
268
|
+
AND ${filterSchema('n.nspname')}`,
|
|
269
|
+
);
|
|
270
|
+
return rows;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ColumnType, ColumnTypesBase } from 'pqb';
|
|
2
|
+
|
|
3
|
+
export const getColumnsByTypesMap = (types: ColumnTypesBase) => {
|
|
4
|
+
const map: Record<string, new () => ColumnType> = {};
|
|
5
|
+
for (const key in types) {
|
|
6
|
+
const type = types[key] as unknown as new () => ColumnType;
|
|
7
|
+
if (type instanceof ColumnType) {
|
|
8
|
+
map[type.dataType] = type;
|
|
9
|
+
if (type.typeAlias) {
|
|
10
|
+
map[type.typeAlias] = type;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return map;
|
|
15
|
+
};
|