dbmodel 0.1.2 → 5.2.2-alpha.11
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/LICENSE +21 -21
- package/README.md +88 -116
- package/bin/dbmodel.js +118 -81
- package/package.json +45 -47
- package/lib/build.js +0 -24
- package/lib/clients/mssql/build.js +0 -191
- package/lib/clients/mssql/connect.js +0 -9
- package/lib/clients/mssql/deploy.js +0 -343
- package/lib/clients/mssql/getsql.js +0 -140
- package/lib/clients/mssql/load.js +0 -210
- package/lib/clients/mssql/query.js +0 -6
- package/lib/connect.js +0 -8
- package/lib/deploy.js +0 -22
- package/lib/dump.js +0 -38
- package/lib/efmodel.js +0 -132
- package/lib/hooks/autoIndexForeignKeys.js +0 -23
- package/lib/hooks/index.js +0 -3
- package/lib/hooks/runHooks.js +0 -10
- package/lib/index.js +0 -20
- package/lib/load.js +0 -22
- package/lib/query.js +0 -7
- package/lib/read.js +0 -61
- package/lib/runAndExit.js +0 -13
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
const _ = require('lodash');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const endOfLine = require('os').EOL;
|
|
4
|
-
const {
|
|
5
|
-
getCreateTableSql,
|
|
6
|
-
getCreateColumnSql,
|
|
7
|
-
getCreateFkSql,
|
|
8
|
-
getCreateIndexSql,
|
|
9
|
-
getProjectIndexesAsDatabase,
|
|
10
|
-
sortViewsByDependency,
|
|
11
|
-
getFillTableSql,
|
|
12
|
-
} = require('./getsql');
|
|
13
|
-
|
|
14
|
-
function writeLine(options, data) {
|
|
15
|
-
fs.writeSync(options.outputDescriptor, data);
|
|
16
|
-
fs.writeSync(options.outputDescriptor, endOfLine);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function buildTables(options) {
|
|
20
|
-
const tables = options.projectStructure.tables;
|
|
21
|
-
|
|
22
|
-
for (const tableName of _.keys(tables)) {
|
|
23
|
-
const table = tables[tableName];
|
|
24
|
-
writeLine(options, `IF (NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='${tableName}'))`);
|
|
25
|
-
writeLine(options, 'BEGIN');
|
|
26
|
-
writeLine(options, `PRINT 'Creating table ${tableName}'`);
|
|
27
|
-
writeLine(options, getCreateTableSql(table));
|
|
28
|
-
writeLine(options, 'END');
|
|
29
|
-
writeLine(options, 'ELSE');
|
|
30
|
-
writeLine(options, 'BEGIN');
|
|
31
|
-
writeLine(options, `PRINT 'Table ${tableName} already exists'`);
|
|
32
|
-
writeLine(options, 'END');
|
|
33
|
-
writeLine(options, 'GO');
|
|
34
|
-
|
|
35
|
-
for (const column of table.columns) {
|
|
36
|
-
writeLine(
|
|
37
|
-
options,
|
|
38
|
-
`IF (NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='${tableName}' AND COLUMN_NAME='${column.name}'))`
|
|
39
|
-
);
|
|
40
|
-
writeLine(options, 'BEGIN');
|
|
41
|
-
writeLine(options, `PRINT 'Creating column ${tableName}.${column.name}'`);
|
|
42
|
-
writeLine(options, getCreateColumnSql(table, column));
|
|
43
|
-
writeLine(options, 'END');
|
|
44
|
-
writeLine(options, 'GO');
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
for (const tableName of _.keys(tables)) {
|
|
49
|
-
const table = tables[tableName];
|
|
50
|
-
for (const column of table.columns) {
|
|
51
|
-
if (column.references) {
|
|
52
|
-
writeLine(
|
|
53
|
-
options,
|
|
54
|
-
`IF (NOT EXISTS(
|
|
55
|
-
SELECT *
|
|
56
|
-
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C
|
|
57
|
-
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK
|
|
58
|
-
ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME AND c.CONSTRAINT_SCHEMA = FK.CONSTRAINT_SCHEMA
|
|
59
|
-
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK
|
|
60
|
-
ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME AND c.UNIQUE_CONSTRAINT_SCHEMA = PK.CONSTRAINT_SCHEMA
|
|
61
|
-
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU
|
|
62
|
-
ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME AND C.CONSTRAINT_SCHEMA = CU.CONSTRAINT_SCHEMA
|
|
63
|
-
WHERE c.CONSTRAINT_SCHEMA = 'dbo'
|
|
64
|
-
AND FK.TABLE_NAME='${tableName}'
|
|
65
|
-
AND PK.TABLE_NAME='${column.references}'
|
|
66
|
-
AND CU.COLUMN_NAME = '${column.name}'
|
|
67
|
-
))
|
|
68
|
-
`
|
|
69
|
-
);
|
|
70
|
-
writeLine(options, 'BEGIN');
|
|
71
|
-
writeLine(options, `PRINT 'Creating foreign key on ${tableName}.${column.name}'`);
|
|
72
|
-
writeLine(options, getCreateFkSql(table, column, tables));
|
|
73
|
-
writeLine(options, 'END');
|
|
74
|
-
writeLine(options, 'GO');
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const projectIndexArray = getProjectIndexesAsDatabase(tables);
|
|
80
|
-
|
|
81
|
-
for (const index of projectIndexArray) {
|
|
82
|
-
writeLine(
|
|
83
|
-
options,
|
|
84
|
-
`IF (NOT EXISTS(
|
|
85
|
-
SELECT *
|
|
86
|
-
FROM sys.indexes i
|
|
87
|
-
inner join sys.objects o ON o.object_id = i.object_id
|
|
88
|
-
WHERE i.name='${index.indexName}' and o.name='${index.tableName}'
|
|
89
|
-
and i.is_primary_key=0
|
|
90
|
-
and i.is_hypothetical=0 and indexproperty(i.object_id, i.name, 'IsStatistics') = 0
|
|
91
|
-
and objectproperty(i.object_id, 'IsUserTable') = 1
|
|
92
|
-
and i.index_id between 1 and 254
|
|
93
|
-
))`
|
|
94
|
-
);
|
|
95
|
-
writeLine(options, 'BEGIN');
|
|
96
|
-
writeLine(options, `PRINT 'Creating index ${index.indexName} on ${index.tableName}'`);
|
|
97
|
-
writeLine(options, getCreateIndexSql(index));
|
|
98
|
-
writeLine(options, 'END');
|
|
99
|
-
writeLine(options, 'GO');
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function buildObjects(
|
|
104
|
-
options,
|
|
105
|
-
projectObjects,
|
|
106
|
-
objectTypeName,
|
|
107
|
-
createRegex,
|
|
108
|
-
createCommand,
|
|
109
|
-
alterCommand,
|
|
110
|
-
info_schema,
|
|
111
|
-
info_schema_column,
|
|
112
|
-
sortObjects
|
|
113
|
-
) {
|
|
114
|
-
const sortedList = sortObjects ? sortObjects(_.keys(projectObjects), projectObjects) : _.keys(projectObjects);
|
|
115
|
-
|
|
116
|
-
for (const objectName of sortedList) {
|
|
117
|
-
const objectSql = projectObjects[objectName];
|
|
118
|
-
writeLine(options, `DECLARE @sql NVARCHAR(MAX) = '';`);
|
|
119
|
-
|
|
120
|
-
writeLine(
|
|
121
|
-
options,
|
|
122
|
-
`IF (NOT EXISTS(
|
|
123
|
-
SELECT * FROM INFORMATION_SCHEMA.${info_schema} WHERE ${info_schema_column}='${objectName}'
|
|
124
|
-
))`
|
|
125
|
-
);
|
|
126
|
-
writeLine(options, 'BEGIN');
|
|
127
|
-
writeLine(options, `PRINT 'Creating ${objectTypeName} ${objectName}'`);
|
|
128
|
-
writeLine(options, `SET @sql = '${createCommand}';`);
|
|
129
|
-
writeLine(options, 'END');
|
|
130
|
-
writeLine(options, `ELSE`);
|
|
131
|
-
writeLine(options, 'BEGIN');
|
|
132
|
-
writeLine(options, `PRINT 'Altering ${objectTypeName} ${objectName}'`);
|
|
133
|
-
writeLine(options, `SET @sql = '${alterCommand}';`);
|
|
134
|
-
writeLine(options, 'END');
|
|
135
|
-
|
|
136
|
-
writeLine(options, `SET @sql = @sql + ' ' + '${objectSql.replace(createRegex, '').replace(/'/g, "''")}';`);
|
|
137
|
-
writeLine(options, `EXEC sp_executesql @sql;`);
|
|
138
|
-
writeLine(options, 'GO');
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function buildFillTables(options) {
|
|
143
|
-
const tables = options.projectStructure.tables;
|
|
144
|
-
for (const tableName of _.keys(tables)) {
|
|
145
|
-
const table = tables[tableName];
|
|
146
|
-
const sql = getFillTableSql(table);
|
|
147
|
-
if (!sql) continue;
|
|
148
|
-
writeLine(options, `PRINT 'Filling table ${tableName}'`);
|
|
149
|
-
writeLine(options, sql);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
async function build(options) {
|
|
154
|
-
buildTables(options);
|
|
155
|
-
|
|
156
|
-
buildObjects(
|
|
157
|
-
options,
|
|
158
|
-
options.projectStructure.views,
|
|
159
|
-
'view',
|
|
160
|
-
/create\s+view/i,
|
|
161
|
-
'CREATE VIEW',
|
|
162
|
-
'ALTER VIEW',
|
|
163
|
-
'VIEWS',
|
|
164
|
-
'TABLE_NAME',
|
|
165
|
-
sortViewsByDependency
|
|
166
|
-
);
|
|
167
|
-
buildObjects(
|
|
168
|
-
options,
|
|
169
|
-
options.projectStructure.procedures,
|
|
170
|
-
'procedure',
|
|
171
|
-
/create\s+procedure/i,
|
|
172
|
-
'CREATE PROCEDURE',
|
|
173
|
-
'ALTER PROCEDURE',
|
|
174
|
-
'ROUTINES',
|
|
175
|
-
'ROUTINE_NAME'
|
|
176
|
-
);
|
|
177
|
-
buildObjects(
|
|
178
|
-
options,
|
|
179
|
-
options.projectStructure.functions,
|
|
180
|
-
'function',
|
|
181
|
-
/create\s+function/i,
|
|
182
|
-
'CREATE FUNCTION',
|
|
183
|
-
'ALTER FUNCTION',
|
|
184
|
-
'ROUTINES',
|
|
185
|
-
'ROUTINE_NAME'
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
buildFillTables(options);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
module.exports = build;
|
|
@@ -1,343 +0,0 @@
|
|
|
1
|
-
const _ = require('lodash');
|
|
2
|
-
const JSONNormalize = require('json-normalize');
|
|
3
|
-
const {
|
|
4
|
-
getColumnDefinitionSql,
|
|
5
|
-
getCreateTableSql,
|
|
6
|
-
getCreateColumnSql,
|
|
7
|
-
getCreateFkSql,
|
|
8
|
-
getCreateIndexSql,
|
|
9
|
-
getProjectIndexesAsDatabase,
|
|
10
|
-
getFillTableSql,
|
|
11
|
-
sortViewsByDependency,
|
|
12
|
-
} = require('./getsql');
|
|
13
|
-
|
|
14
|
-
async function executeAndLog(options, log, command, logSql = true) {
|
|
15
|
-
if (_.isArray(command)) {
|
|
16
|
-
if (log) {
|
|
17
|
-
console.log(log);
|
|
18
|
-
}
|
|
19
|
-
for (const cmd of command) {
|
|
20
|
-
await executeAndLog(options, null, cmd, logSql);
|
|
21
|
-
}
|
|
22
|
-
} else {
|
|
23
|
-
if (log) {
|
|
24
|
-
console.log(log);
|
|
25
|
-
}
|
|
26
|
-
if (logSql) {
|
|
27
|
-
console.log(command);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const { mssqlPool } = options;
|
|
31
|
-
await mssqlPool.request().query(command);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function processAddedTables(options) {
|
|
36
|
-
const dbTables = options.databaseStructure.mssqlModel.tables;
|
|
37
|
-
const projectTables = options.projectStructure.tables;
|
|
38
|
-
const adding = _.difference(Object.keys(projectTables), Object.keys(dbTables));
|
|
39
|
-
await Promise.all(
|
|
40
|
-
adding.map(async table => {
|
|
41
|
-
const createSql = getCreateTableSql(projectTables[table]);
|
|
42
|
-
await executeAndLog(options, `Adding table ${table}`, createSql);
|
|
43
|
-
})
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function processTableData(options) {
|
|
48
|
-
const projectTables = options.projectStructure.tables;
|
|
49
|
-
|
|
50
|
-
for (const table of _.values(projectTables)) {
|
|
51
|
-
const sql = getFillTableSql(table);
|
|
52
|
-
if (!sql) continue;
|
|
53
|
-
await executeAndLog(options, `Filling table ${table.name}`, sql, false);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async function processAddedColumns(options) {
|
|
58
|
-
const dbTables = options.databaseStructure.mssqlModel.tables;
|
|
59
|
-
const projectTables = options.projectStructure.tables;
|
|
60
|
-
|
|
61
|
-
const both = _.intersection(Object.keys(projectTables), Object.keys(dbTables));
|
|
62
|
-
await Promise.all(
|
|
63
|
-
both.map(async table => {
|
|
64
|
-
const dbTable = dbTables[table];
|
|
65
|
-
const projectTable = projectTables[table];
|
|
66
|
-
const adding = _.difference(
|
|
67
|
-
projectTable.columns.map(x => x.name),
|
|
68
|
-
dbTable.map(x => x.columnName)
|
|
69
|
-
);
|
|
70
|
-
await Promise.all(
|
|
71
|
-
adding.map(async column => {
|
|
72
|
-
const createSql = getCreateColumnSql(
|
|
73
|
-
projectTable,
|
|
74
|
-
projectTable.columns.find(x => x.name == column)
|
|
75
|
-
);
|
|
76
|
-
await executeAndLog(options, `Adding column ${column}`, createSql);
|
|
77
|
-
})
|
|
78
|
-
);
|
|
79
|
-
})
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function normalizeDefaultValue(value) {
|
|
84
|
-
if (value == null) return null;
|
|
85
|
-
let svalue = value.toString();
|
|
86
|
-
while (svalue.startsWith('(') && svalue.endsWith(')')) {
|
|
87
|
-
svalue = svalue.substring(1, svalue.length - 1);
|
|
88
|
-
}
|
|
89
|
-
return svalue;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async function processColumnTypeUpdate(options) {
|
|
93
|
-
const dbTables = options.databaseStructure.mssqlModel.tables;
|
|
94
|
-
const projectTables = options.projectStructure.tables;
|
|
95
|
-
const { suggestions } = options;
|
|
96
|
-
|
|
97
|
-
const bothTables = _.intersection(Object.keys(projectTables), Object.keys(dbTables));
|
|
98
|
-
await Promise.all(
|
|
99
|
-
bothTables.map(async table => {
|
|
100
|
-
const dbTable = dbTables[table];
|
|
101
|
-
const projectTable = projectTables[table];
|
|
102
|
-
const bothColumns = _.intersection(
|
|
103
|
-
projectTable.columns.map(x => x.name),
|
|
104
|
-
dbTable.map(x => x.columnName)
|
|
105
|
-
);
|
|
106
|
-
for (const column of bothColumns) {
|
|
107
|
-
const projectCol = projectTable.columns.filter(x => x.name == column)[0];
|
|
108
|
-
const dbCol = dbTable.filter(x => x.columnName == column)[0];
|
|
109
|
-
|
|
110
|
-
if (!dbCol.isNullable != (projectCol.notNull || false)) {
|
|
111
|
-
const sql = `ALTER TABLE [${table}] ALTER COLUMN ${getColumnDefinitionSql(projectTable, projectCol)};`;
|
|
112
|
-
const message = `Changing column notNull flag ${table}.${column} from ${!dbCol.isNullable} to ${
|
|
113
|
-
projectCol.notNull
|
|
114
|
-
}`;
|
|
115
|
-
suggestions.push({ sql, message });
|
|
116
|
-
// await executeAndLog(options, message, sql);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (dbCol.defaultValue == null && projectCol.default != null) {
|
|
120
|
-
const sql = `ALTER TABLE [${table}] ADD CONSTRAINT [DF_${table}_${column}] DEFAULT ${projectCol.default} FOR [${column}]`;
|
|
121
|
-
const message = `Creating default value for column ${column} from table ${table}, ${projectCol.default}`;
|
|
122
|
-
suggestions.push({ sql, message });
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (dbCol.defaultValue != null && projectCol.default == null) {
|
|
126
|
-
const sql = `ALTER TABLE [${table}] DROP CONSTRAINT [DF_${table}_${column}]`;
|
|
127
|
-
const message = `Drop default value for column ${column} from table ${table}`;
|
|
128
|
-
suggestions.push({ sql, message });
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (
|
|
132
|
-
dbCol.defaultValue != null &&
|
|
133
|
-
projectCol.default != null &&
|
|
134
|
-
normalizeDefaultValue(projectCol.default) != normalizeDefaultValue(dbCol.defaultValue)
|
|
135
|
-
) {
|
|
136
|
-
const sql = `ALTER TABLE [${table}] DROP CONSTRAINT [DF_${table}_${column}];ALTER TABLE [${table}] ADD CONSTRAINT [DF_${table}_${column}] DEFAULT ${projectCol.default} FOR [${column}]`;
|
|
137
|
-
const message = `Changing default value for column ${column} from table ${table}, ${projectCol.default}`;
|
|
138
|
-
suggestions.push({ sql, message });
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
})
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
async function addMissingReferences(options) {
|
|
146
|
-
const projectTables = options.projectStructure.tables;
|
|
147
|
-
const dbReferences = options.databaseStructure.mssqlModel.references;
|
|
148
|
-
|
|
149
|
-
await Promise.all(
|
|
150
|
-
Object.keys(projectTables).map(async table => {
|
|
151
|
-
await Promise.all(
|
|
152
|
-
projectTables[table].columns
|
|
153
|
-
.filter(x => x.references)
|
|
154
|
-
.filter(x => !dbReferences.find(y => table == y.fkTable && x.name == y.fkColumn))
|
|
155
|
-
.map(async column => {
|
|
156
|
-
const createSql = getCreateFkSql(projectTables[table], column, projectTables);
|
|
157
|
-
await executeAndLog(options, `Adding foreign key ${table}.${column.name}`, createSql);
|
|
158
|
-
})
|
|
159
|
-
);
|
|
160
|
-
})
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function jsonNormalizeIndex(index) {
|
|
165
|
-
const indexCopy = { ...index };
|
|
166
|
-
if (!indexCopy.filterDefinition) delete indexCopy.filterDefinition;
|
|
167
|
-
delete indexCopy.continueOnError;
|
|
168
|
-
if (indexCopy.filterDefinition) {
|
|
169
|
-
indexCopy.filterDefinition = indexCopy.filterDefinition
|
|
170
|
-
.replace(/\s/g, '')
|
|
171
|
-
.replace(/\(/g, '')
|
|
172
|
-
.replace(/\)/g, '')
|
|
173
|
-
.replace(/\[/g, '')
|
|
174
|
-
.replace(/\]/g, '');
|
|
175
|
-
}
|
|
176
|
-
return JSONNormalize.stringifySync(indexCopy);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
async function processIndexes(options) {
|
|
180
|
-
const dbIndexes = options.databaseStructure.mssqlModel.indexes;
|
|
181
|
-
const projectTables = options.projectStructure.tables;
|
|
182
|
-
const { suggestions } = options;
|
|
183
|
-
|
|
184
|
-
const dbIndexArray = _.flatten(_.values(dbIndexes).map(it => _.values(it)));
|
|
185
|
-
const projectIndexArray = getProjectIndexesAsDatabase(projectTables);
|
|
186
|
-
|
|
187
|
-
for (const dbIndex of dbIndexArray) {
|
|
188
|
-
const prjIndex = projectIndexArray.find(
|
|
189
|
-
x => x.tableName === dbIndex.tableName && x.indexName === dbIndex.indexName
|
|
190
|
-
);
|
|
191
|
-
if (prjIndex != null && (prjIndex == null || jsonNormalizeIndex(prjIndex) != jsonNormalizeIndex(dbIndex))) {
|
|
192
|
-
await executeAndLog(
|
|
193
|
-
options,
|
|
194
|
-
`Removing index ${dbIndex.indexName} on ${dbIndex.tableName}`,
|
|
195
|
-
`DROP INDEX [${dbIndex.indexName}] ON [${dbIndex.tableName}]`
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (prjIndex == null) {
|
|
200
|
-
suggestions.push({
|
|
201
|
-
message: `Index ${dbIndex.indexName} on ${dbIndex.tableName} not found in project`,
|
|
202
|
-
sql: `DROP INDEX [${dbIndex.indexName}] ON [${dbIndex.tableName}];`,
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// eslint-disable-next-line require-atomic-updates
|
|
208
|
-
for (const prjIndex of projectIndexArray) {
|
|
209
|
-
const dbIndex = dbIndexArray.find(x => x.tableName === prjIndex.tableName && x.indexName === prjIndex.indexName);
|
|
210
|
-
if (dbIndex == null || jsonNormalizeIndex(prjIndex) != jsonNormalizeIndex(dbIndex)) {
|
|
211
|
-
try {
|
|
212
|
-
await executeAndLog(
|
|
213
|
-
options,
|
|
214
|
-
`Creating index ${prjIndex.indexName} on ${prjIndex.tableName}`,
|
|
215
|
-
getCreateIndexSql(prjIndex)
|
|
216
|
-
);
|
|
217
|
-
} catch (e) {
|
|
218
|
-
if (prjIndex.continueOnError) {
|
|
219
|
-
console.log(`FAILED creating index ${prjIndex.indexName}: ${e.message}`);
|
|
220
|
-
console.log('Continue because of continueOnError');
|
|
221
|
-
} else {
|
|
222
|
-
throw e;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
async function detectTablesOnlyInDb(options) {
|
|
230
|
-
const projectTables = options.projectStructure.tables;
|
|
231
|
-
const dbTables = options.databaseStructure.mssqlModel.tables;
|
|
232
|
-
const { suggestions } = options;
|
|
233
|
-
|
|
234
|
-
const removed = _.difference(Object.keys(dbTables), Object.keys(projectTables));
|
|
235
|
-
removed.forEach(table => {
|
|
236
|
-
suggestions.push({
|
|
237
|
-
message: `Table ${table} is only in DB (missing in project)`,
|
|
238
|
-
sql: `DROP TABLE [${table}];`,
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
async function detectColumnsOnlyInDb(options) {
|
|
244
|
-
const projectTables = options.projectStructure.tables;
|
|
245
|
-
const dbTables = options.databaseStructure.mssqlModel.tables;
|
|
246
|
-
const { suggestions } = options;
|
|
247
|
-
|
|
248
|
-
const bothTables = _.intersection(Object.keys(projectTables), Object.keys(dbTables));
|
|
249
|
-
for (const tableName of bothTables) {
|
|
250
|
-
const dbTable = dbTables[tableName];
|
|
251
|
-
const projectTable = projectTables[tableName];
|
|
252
|
-
|
|
253
|
-
const removed = _.difference(
|
|
254
|
-
dbTable.map(x => x.columnName),
|
|
255
|
-
projectTable.columns.map(x => x.name)
|
|
256
|
-
);
|
|
257
|
-
suggestions.push(
|
|
258
|
-
...removed.map(col => ({
|
|
259
|
-
message: `Column ${col} from table ${tableName} is only in DB (missing in project)`,
|
|
260
|
-
sql: `ALTER TABLE [${tableName}] DROP COLUMN [${col}];`,
|
|
261
|
-
}))
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
async function processObjects(
|
|
267
|
-
options,
|
|
268
|
-
dbObjects,
|
|
269
|
-
projectObjects,
|
|
270
|
-
objectTypeName,
|
|
271
|
-
createRegex,
|
|
272
|
-
alterCommand,
|
|
273
|
-
sortObjects
|
|
274
|
-
) {
|
|
275
|
-
const addedObjects = _.difference(Object.keys(projectObjects), Object.keys(dbObjects));
|
|
276
|
-
const bothObjects = _.intersection(Object.keys(projectObjects), Object.keys(dbObjects));
|
|
277
|
-
|
|
278
|
-
const addedSorted = sortObjects ? sortObjects(addedObjects, projectObjects) : addedObjects;
|
|
279
|
-
|
|
280
|
-
for (const addedObject of addedSorted) {
|
|
281
|
-
await executeAndLog(options, `Creating ${objectTypeName} ${addedObject}`, projectObjects[addedObject]);
|
|
282
|
-
}
|
|
283
|
-
for (const objectName of bothObjects) {
|
|
284
|
-
if (dbObjects[objectName].replace(createRegex, '') == projectObjects[objectName].replace(createRegex, '')) continue;
|
|
285
|
-
const sql = projectObjects[objectName].replace(createRegex, alterCommand);
|
|
286
|
-
await executeAndLog(options, `Altering ${objectTypeName} ${objectName}`, sql);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
async function deploy(options) {
|
|
291
|
-
options.suggestions = [];
|
|
292
|
-
|
|
293
|
-
await processAddedTables(options);
|
|
294
|
-
await processAddedColumns(options);
|
|
295
|
-
await processTableData(options);
|
|
296
|
-
await processColumnTypeUpdate(options);
|
|
297
|
-
await addMissingReferences(options);
|
|
298
|
-
await processIndexes(options);
|
|
299
|
-
await detectTablesOnlyInDb(options);
|
|
300
|
-
await detectColumnsOnlyInDb(options);
|
|
301
|
-
await processObjects(
|
|
302
|
-
options,
|
|
303
|
-
options.databaseStructure.views,
|
|
304
|
-
options.projectStructure.views,
|
|
305
|
-
'view',
|
|
306
|
-
/create\s+view/i,
|
|
307
|
-
'ALTER VIEW',
|
|
308
|
-
sortViewsByDependency
|
|
309
|
-
);
|
|
310
|
-
await processObjects(
|
|
311
|
-
options,
|
|
312
|
-
options.databaseStructure.procedures,
|
|
313
|
-
options.projectStructure.procedures,
|
|
314
|
-
'procedure',
|
|
315
|
-
/create\s+procedure/i,
|
|
316
|
-
'ALTER PROCEDURE'
|
|
317
|
-
);
|
|
318
|
-
await processObjects(
|
|
319
|
-
options,
|
|
320
|
-
options.databaseStructure.functions,
|
|
321
|
-
options.projectStructure.functions,
|
|
322
|
-
'function',
|
|
323
|
-
/create\s+function/i,
|
|
324
|
-
'ALTER FUNCTION'
|
|
325
|
-
);
|
|
326
|
-
|
|
327
|
-
if (options.suggestions.length > 0) {
|
|
328
|
-
console.log(`Suggestions: ${options.suggestions.length}`);
|
|
329
|
-
for (const { message } of options.suggestions) {
|
|
330
|
-
console.log(message);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
console.log('-------------------------------------------------------------');
|
|
334
|
-
for (const { sql } of options.suggestions) {
|
|
335
|
-
console.log(sql);
|
|
336
|
-
console.log('GO');
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
return options;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
module.exports = deploy;
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
const _ = require('lodash');
|
|
2
|
-
const toposort = require('toposort');
|
|
3
|
-
|
|
4
|
-
function getColumnDefinitionSql(tabledef, column, allowDefault = false) {
|
|
5
|
-
const dataType = column.type && column.type.toLowerCase() == 'json' ? 'nvarchar(max)' : column.type;
|
|
6
|
-
return (
|
|
7
|
-
`[${column.name}] ${dataType}${column.length ? '(' + (column.length < 0 ? 'MAX' : column.length) + ')' : ''} ` +
|
|
8
|
-
`${column.autoIncrement ? 'IDENTITY' : ''} ${column.notNull ? 'NOT NULL' : 'NULL'}` +
|
|
9
|
-
(allowDefault && column.default !== undefined
|
|
10
|
-
? ` CONSTRAINT [DF_${tabledef.name}_${column.name}] DEFAULT ${column.default} `
|
|
11
|
-
: '')
|
|
12
|
-
);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function getCreateTableSql(tabledef) {
|
|
16
|
-
if (!tabledef.primaryKey) {
|
|
17
|
-
throw new Error(`Missing primary key in table ${tabledef.name}`);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const deflines = [
|
|
21
|
-
...tabledef.columns.map(column => getColumnDefinitionSql(tabledef, column, true)),
|
|
22
|
-
`CONSTRAINT PK_${tabledef.name} PRIMARY KEY (${tabledef.primaryKey.map(x => `[${x}]`).join(',')})`,
|
|
23
|
-
];
|
|
24
|
-
return `CREATE TABLE [${tabledef.name}] (\n${deflines.map(x => ' ' + x).join(',\n')}\n);\n`;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function getCreateColumnSql(tabledef, column) {
|
|
28
|
-
if (column.notNull && column.default !== undefined) {
|
|
29
|
-
const nullColumn = { ...column, notNull: false };
|
|
30
|
-
return [
|
|
31
|
-
`ALTER TABLE [${tabledef.name}] ADD ${getColumnDefinitionSql(tabledef, nullColumn)}`,
|
|
32
|
-
`ALTER TABLE [${tabledef.name}] ADD CONSTRAINT [DF_${tabledef.name}_${column.name}] DEFAULT` +
|
|
33
|
-
` ${column.default} FOR [${column.name}]`,
|
|
34
|
-
`UPDATE [${tabledef.name}] SET [${column.name}]=${column.default}`,
|
|
35
|
-
`ALTER TABLE [${tabledef.name}] ALTER COLUMN ${getColumnDefinitionSql(tabledef, column)}`,
|
|
36
|
-
];
|
|
37
|
-
}
|
|
38
|
-
return `ALTER TABLE [${tabledef.name}] ADD ${getColumnDefinitionSql(tabledef, column)}`;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function getCreateFkSql(tabledef, column, projectTables) {
|
|
42
|
-
const references = column.references;
|
|
43
|
-
const idcol = (projectTables[references] && projectTables[references].primaryKey[0]) || 'id';
|
|
44
|
-
return `ALTER TABLE [${tabledef.name}] ADD CONSTRAINT [FK_${tabledef.name}_${column.name}] FOREIGN KEY ([${column.name}]) REFERENCES [${references}]([${idcol}])`;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function getCreateIndexSql(prjIndex) {
|
|
48
|
-
return `CREATE ${prjIndex.isUnique ? 'UNIQUE' : ''} INDEX [${prjIndex.indexName}] ON [${
|
|
49
|
-
prjIndex.tableName
|
|
50
|
-
}] (${prjIndex.columns.map(x => `[${x.columnName}]`).join(',')}) ${
|
|
51
|
-
prjIndex.filterDefinition ? `WHERE ${prjIndex.filterDefinition}` : ''
|
|
52
|
-
}`;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function getProjectIndexesAsDatabase(projectTables) {
|
|
56
|
-
const projectIndexArray = _.flatten(
|
|
57
|
-
_.values(
|
|
58
|
-
_.mapValues(projectTables, table =>
|
|
59
|
-
(table.indexes || []).map(index => ({
|
|
60
|
-
indexName: index.name,
|
|
61
|
-
isUnique: index.unique || false,
|
|
62
|
-
filterDefinition: index.filter,
|
|
63
|
-
continueOnError: index.continueOnError,
|
|
64
|
-
tableName: table.name,
|
|
65
|
-
columns: index.columns.map(col => ({
|
|
66
|
-
columnName: col,
|
|
67
|
-
isIncludedColumn: false,
|
|
68
|
-
})),
|
|
69
|
-
}))
|
|
70
|
-
)
|
|
71
|
-
)
|
|
72
|
-
);
|
|
73
|
-
return projectIndexArray;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function sortViewsByDependency(viewNames, viewDict) {
|
|
77
|
-
const edges = [];
|
|
78
|
-
for (const viewName of viewNames) {
|
|
79
|
-
edges.push([viewName, null]);
|
|
80
|
-
const viewText = viewDict[viewName];
|
|
81
|
-
for (const otherView of viewNames) {
|
|
82
|
-
if (otherView === viewName) continue;
|
|
83
|
-
if ((' ' + viewText + ' ').match('[\\W]' + otherView + '[\\W]')) {
|
|
84
|
-
edges.push([otherView, viewName]);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return _.compact(toposort(edges));
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function getSqlExpression(data) {
|
|
92
|
-
if (data == null) return 'NULL';
|
|
93
|
-
if (data === false) return '0';
|
|
94
|
-
if (data === true) return '1';
|
|
95
|
-
if (_.isNumber(data)) return data.toString();
|
|
96
|
-
return `'${data.replace(/'/g, "''")}'`;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function getFillTableSql(table) {
|
|
100
|
-
const keyCols = table.insertKey || table.primaryKey;
|
|
101
|
-
if (!keyCols) return null;
|
|
102
|
-
if (!table.data) return null;
|
|
103
|
-
const sql = table.data
|
|
104
|
-
.map(row => {
|
|
105
|
-
const keyCond = keyCols.map(col => `[${table.name}].[${col}]=${getSqlExpression(row[col])}`).join(' AND ');
|
|
106
|
-
const rowCols = Object.keys(row);
|
|
107
|
-
const notKeyCols = _.difference(rowCols, keyCols);
|
|
108
|
-
const updateCmd = notKeyCols.map(col => `[${col}]=${getSqlExpression(row[col])}`).join(',');
|
|
109
|
-
const rowColsCmd = rowCols.map(col => `[${col}]`).join(',');
|
|
110
|
-
const rowValuesCmd = rowCols.map(col => `${getSqlExpression(row[col])}`).join(',');
|
|
111
|
-
const cmd =
|
|
112
|
-
`IF (EXISTS(SELECT * FROM [${table.name}]` +
|
|
113
|
-
` WHERE ${keyCond})) UPDATE [${table.name}]` +
|
|
114
|
-
` SET ${updateCmd} WHERE ${keyCond}` +
|
|
115
|
-
` ELSE INSERT INTO [${table.name}]` +
|
|
116
|
-
` (${rowColsCmd}) VALUES (${rowValuesCmd});\n`;
|
|
117
|
-
return cmd;
|
|
118
|
-
})
|
|
119
|
-
.join('');
|
|
120
|
-
if (!sql) return null;
|
|
121
|
-
|
|
122
|
-
const identityColumn = table.columns.find(col => col.autoIncrement);
|
|
123
|
-
const sqlWithSetup =
|
|
124
|
-
identityColumn && keyCols.includes(identityColumn.name)
|
|
125
|
-
? `SET IDENTITY_INSERT [${table.name}] ON;\n${sql}SET IDENTITY_INSERT [${table.name}] OFF;`
|
|
126
|
-
: sql;
|
|
127
|
-
|
|
128
|
-
return sqlWithSetup;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
module.exports = {
|
|
132
|
-
getColumnDefinitionSql,
|
|
133
|
-
getCreateTableSql,
|
|
134
|
-
getCreateColumnSql,
|
|
135
|
-
getCreateFkSql,
|
|
136
|
-
getCreateIndexSql,
|
|
137
|
-
getProjectIndexesAsDatabase,
|
|
138
|
-
getFillTableSql,
|
|
139
|
-
sortViewsByDependency,
|
|
140
|
-
};
|