@verdnatura/myt 1.5.1
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/.dockerignore +2 -0
- package/LICENSE +17 -0
- package/README.md +268 -0
- package/assets/db.ini +5 -0
- package/assets/myvc.default.yml +9 -0
- package/assets/structure.sql +24 -0
- package/cli.js +4 -0
- package/exporters/event.ejs +21 -0
- package/exporters/event.js +25 -0
- package/exporters/event.sql +19 -0
- package/exporters/function.ejs +20 -0
- package/exporters/function.js +23 -0
- package/exporters/function.sql +16 -0
- package/exporters/procedure.ejs +14 -0
- package/exporters/procedure.js +23 -0
- package/exporters/procedure.sql +14 -0
- package/exporters/trigger.ejs +7 -0
- package/exporters/trigger.js +5 -0
- package/exporters/trigger.sql +13 -0
- package/exporters/view.ejs +7 -0
- package/exporters/view.js +25 -0
- package/exporters/view.sql +12 -0
- package/lib/command.js +16 -0
- package/lib/docker.js +133 -0
- package/lib/exporter-engine.js +112 -0
- package/lib/exporter.js +115 -0
- package/lib/server.js +71 -0
- package/lib/util.js +6 -0
- package/myt-clean.js +52 -0
- package/myt-dump.js +71 -0
- package/myt-fixtures.js +28 -0
- package/myt-init.js +25 -0
- package/myt-pull.js +129 -0
- package/myt-push.js +475 -0
- package/myt-run.js +160 -0
- package/myt-start.js +51 -0
- package/myt-version.js +219 -0
- package/myt.js +646 -0
- package/package.json +43 -0
- package/server/Dockerfile.base +1 -0
- package/server/Dockerfile.client +14 -0
- package/server/Dockerfile.dump +16 -0
- package/server/Dockerfile.server +25 -0
- package/server/docker-dump.sh +4 -0
- package/server/docker-fixtures.sh +4 -0
- package/server/docker-import.sh +6 -0
- package/server/docker-init.sh +20 -0
- package/server/docker-start.sh +10 -0
- package/server/docker.cnf +9 -0
- package/template/dump/fixtures.sql +1 -0
- package/template/dump/structure.sql +1 -0
- package/template/myvc.config.yml +11 -0
- package/template/package.json +13 -0
- package/template/remotes/local.ini +5 -0
- package/template/routines/README.md +1 -0
- package/template/versions/00001-firstVersion/00-firstScript.sql +1 -0
- package/template/versions/README.md +1 -0
package/myt-pull.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
|
|
2
|
+
const Myt = require('./myt');
|
|
3
|
+
const Command = require('./lib/command');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const nodegit = require('nodegit');
|
|
6
|
+
const ExporterEngine = require('./lib/exporter-engine');
|
|
7
|
+
|
|
8
|
+
class Pull extends Command {
|
|
9
|
+
static usage = {
|
|
10
|
+
description: 'Incorporate database routine changes into workspace',
|
|
11
|
+
params: {
|
|
12
|
+
force: 'Do it even if there are local changes',
|
|
13
|
+
checkout: 'Move to same database commit before pull',
|
|
14
|
+
update: 'Update all routines',
|
|
15
|
+
sums: 'Save SHA sums of all objects'
|
|
16
|
+
},
|
|
17
|
+
operand: 'remote'
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
static localOpts = {
|
|
21
|
+
alias: {
|
|
22
|
+
force: 'f',
|
|
23
|
+
checkout: 'c',
|
|
24
|
+
update: 'u',
|
|
25
|
+
sums: 's'
|
|
26
|
+
},
|
|
27
|
+
boolean: [
|
|
28
|
+
'force',
|
|
29
|
+
'checkout',
|
|
30
|
+
'update',
|
|
31
|
+
'sums'
|
|
32
|
+
]
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
async run(myt, opts) {
|
|
36
|
+
const conn = await myt.dbConnect();
|
|
37
|
+
const repo = await myt.openRepo();
|
|
38
|
+
|
|
39
|
+
if (!opts.force) {
|
|
40
|
+
async function hasChanges(diff) {
|
|
41
|
+
if (diff)
|
|
42
|
+
for (const patch of await diff.patches()) {
|
|
43
|
+
const match = patch
|
|
44
|
+
.newFile()
|
|
45
|
+
.path()
|
|
46
|
+
.match(/^routines\/(.+)\.sql$/);
|
|
47
|
+
if (match) return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Check for unstaged changes
|
|
54
|
+
|
|
55
|
+
const unstagedDiff = await myt.getUnstaged(repo);
|
|
56
|
+
|
|
57
|
+
if (await hasChanges(unstagedDiff))
|
|
58
|
+
throw new Error('You have unstaged changes, save them before pull');
|
|
59
|
+
|
|
60
|
+
// Check for staged changes
|
|
61
|
+
|
|
62
|
+
const stagedDiff = await myt.getStaged(repo);
|
|
63
|
+
|
|
64
|
+
if (await hasChanges(stagedDiff))
|
|
65
|
+
throw new Error('You have staged changes, save them before pull');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Checkout to remote commit
|
|
69
|
+
|
|
70
|
+
if (opts.checkout) {
|
|
71
|
+
const version = await myt.fetchDbVersion();
|
|
72
|
+
|
|
73
|
+
if (version && version.gitCommit) {
|
|
74
|
+
const now = parseInt(new Date().toJSON());
|
|
75
|
+
const branchName = `myt-pull_${now}`;
|
|
76
|
+
console.log(`Creating branch '${branchName}' from database commit.`);
|
|
77
|
+
const commit = await repo.getCommit(version.gitCommit);
|
|
78
|
+
const branch = await nodegit.Branch.create(repo,
|
|
79
|
+
`myt-pull_${now}`, commit, () => {});
|
|
80
|
+
await repo.checkoutBranch(branch);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Export routines to SQL files
|
|
85
|
+
|
|
86
|
+
console.log(`Incorporating routine changes.`);
|
|
87
|
+
|
|
88
|
+
const engine = new ExporterEngine(conn, opts.mytDir);
|
|
89
|
+
await engine.init();
|
|
90
|
+
const shaSums = engine.shaSums;
|
|
91
|
+
|
|
92
|
+
const routinesDir = opts.routinesDir;
|
|
93
|
+
if (!await fs.pathExists(routinesDir))
|
|
94
|
+
await fs.mkdir(routinesDir);
|
|
95
|
+
|
|
96
|
+
// Delete old schemas
|
|
97
|
+
|
|
98
|
+
const schemas = await fs.readdir(routinesDir);
|
|
99
|
+
for (const schema of schemas) {
|
|
100
|
+
if (opts.schemas.indexOf(schema) == -1)
|
|
101
|
+
await fs.remove(`${routinesDir}/${schema}`, {recursive: true});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
for (const schema in shaSums) {
|
|
105
|
+
if (!await fs.pathExists(`${routinesDir}/${schema}`))
|
|
106
|
+
engine.deleteSchemaSums(schema);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Export objects to SQL files
|
|
110
|
+
|
|
111
|
+
for (const schema of opts.schemas) {
|
|
112
|
+
let schemaDir = `${routinesDir}/${schema}`;
|
|
113
|
+
if (!await fs.pathExists(schemaDir))
|
|
114
|
+
await fs.mkdir(schemaDir);
|
|
115
|
+
|
|
116
|
+
for (const exporter of engine.exporters)
|
|
117
|
+
await exporter.export(routinesDir,
|
|
118
|
+
schema, opts.update, opts.sums);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
await engine.refreshPullDate();
|
|
122
|
+
await engine.saveInfo();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
module.exports = Pull;
|
|
127
|
+
|
|
128
|
+
if (require.main === module)
|
|
129
|
+
new Myt().run(Pull);
|
package/myt-push.js
ADDED
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
|
|
2
|
+
const Myt = require('./myt');
|
|
3
|
+
const Command = require('./lib/command');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const nodegit = require('nodegit');
|
|
6
|
+
const ExporterEngine = require('./lib/exporter-engine');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Pushes changes to remote.
|
|
10
|
+
*/
|
|
11
|
+
class Push extends Command {
|
|
12
|
+
static usage = {
|
|
13
|
+
description: 'Apply changes into database',
|
|
14
|
+
params: {
|
|
15
|
+
force: 'Answer yes to all questions',
|
|
16
|
+
commit: 'Wether to save the commit SHA into database',
|
|
17
|
+
sums: 'Save SHA sums of pushed objects'
|
|
18
|
+
},
|
|
19
|
+
operand: 'remote'
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
static localOpts = {
|
|
23
|
+
alias: {
|
|
24
|
+
force: 'f',
|
|
25
|
+
commit: 'c',
|
|
26
|
+
sums: 's'
|
|
27
|
+
},
|
|
28
|
+
boolean: [
|
|
29
|
+
'force',
|
|
30
|
+
'commit',
|
|
31
|
+
'sums'
|
|
32
|
+
]
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
async run(myt, opts) {
|
|
36
|
+
const conn = await myt.dbConnect();
|
|
37
|
+
this.conn = conn;
|
|
38
|
+
|
|
39
|
+
if (opts.remote == 'local')
|
|
40
|
+
opts.commit = true;
|
|
41
|
+
|
|
42
|
+
// Obtain exclusive lock
|
|
43
|
+
|
|
44
|
+
const [[row]] = await conn.query(
|
|
45
|
+
`SELECT GET_LOCK('myt_push', 30) getLock`);
|
|
46
|
+
|
|
47
|
+
if (!row.getLock) {
|
|
48
|
+
let isUsed = 0;
|
|
49
|
+
|
|
50
|
+
if (row.getLock == 0) {
|
|
51
|
+
const [[row]] = await conn.query(
|
|
52
|
+
`SELECT IS_USED_LOCK('myt_push') isUsed`);
|
|
53
|
+
isUsed = row.isUsed;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
throw new Error(`Cannot obtain exclusive lock, used by connection ${isUsed}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function releaseLock() {
|
|
60
|
+
await conn.query(`DO RELEASE_LOCK('myt_push')`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
await this.push(myt, opts, conn);
|
|
65
|
+
} catch(err) {
|
|
66
|
+
try {
|
|
67
|
+
await releaseLock();
|
|
68
|
+
} catch (e) {
|
|
69
|
+
console.error(e);
|
|
70
|
+
}
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await releaseLock();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async push(myt, opts, conn) {
|
|
78
|
+
const pushConn = await myt.createConnection();
|
|
79
|
+
|
|
80
|
+
// Get database version
|
|
81
|
+
|
|
82
|
+
const version = await myt.fetchDbVersion() || {};
|
|
83
|
+
|
|
84
|
+
console.log(
|
|
85
|
+
`Database information:`
|
|
86
|
+
+ `\n -> Version: ${version.number}`
|
|
87
|
+
+ `\n -> Commit: ${version.gitCommit}`
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
if (!version.number)
|
|
91
|
+
version.number = String('0').padStart(opts.versionDigits, '0');
|
|
92
|
+
if (!/^[0-9]*$/.test(version.number))
|
|
93
|
+
throw new Error('Wrong database version');
|
|
94
|
+
|
|
95
|
+
// Prevent push to production by mistake
|
|
96
|
+
|
|
97
|
+
if (opts.remote == 'production') {
|
|
98
|
+
console.log(
|
|
99
|
+
'\n ( ( ) ( ( ) ) '
|
|
100
|
+
+ '\n )\\ ))\\ ) ( /( )\\ ) ( ))\\ ) ( /( ( /( '
|
|
101
|
+
+ '\n(()/(()/( )\\()|()/( ( )\\ ) /(()/( )\\()) )\\())'
|
|
102
|
+
+ '\n /(_))(_)|(_)\\ /(_)) )\\ (((_) ( )(_))(_)|(_)\\ ((_)\\ '
|
|
103
|
+
+ '\n(_))(_)) ((_|_))_ _ ((_))\\___(_(_()|__)) ((_) _((_)'
|
|
104
|
+
+ '\n| _ \\ _ \\ / _ \\| \\| | | ((/ __|_ _|_ _| / _ \\| \\| |'
|
|
105
|
+
+ '\n| _/ /| (_) | |) | |_| || (__ | | | | | (_) | . |'
|
|
106
|
+
+ '\n|_| |_|_\\ \\___/|___/ \\___/ \\___| |_| |___| \\___/|_|\\_|'
|
|
107
|
+
+ '\n'
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
if (!opts.force) {
|
|
111
|
+
const readline = require('readline');
|
|
112
|
+
const rl = readline.createInterface({
|
|
113
|
+
input: process.stdin,
|
|
114
|
+
output: process.stdout
|
|
115
|
+
});
|
|
116
|
+
const answer = await new Promise(resolve => {
|
|
117
|
+
rl.question('Are you sure? (Default: no) [yes|no] ', resolve);
|
|
118
|
+
});
|
|
119
|
+
rl.close();
|
|
120
|
+
|
|
121
|
+
if (answer !== 'yes')
|
|
122
|
+
throw new Error('Changes aborted');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Apply versions
|
|
127
|
+
|
|
128
|
+
console.log('Applying versions.');
|
|
129
|
+
|
|
130
|
+
let nChanges = 0;
|
|
131
|
+
let silent = true;
|
|
132
|
+
const versionsDir = opts.versionsDir;
|
|
133
|
+
|
|
134
|
+
function logVersion(version, name, error) {
|
|
135
|
+
console.log('', version.bold, name);
|
|
136
|
+
}
|
|
137
|
+
function logScript(type, message, error) {
|
|
138
|
+
console.log(' ', type.bold, message);
|
|
139
|
+
}
|
|
140
|
+
function isUndoScript(script) {
|
|
141
|
+
return /\.undo\.sql$/.test(script);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (await fs.pathExists(versionsDir)) {
|
|
145
|
+
const versionDirs = await fs.readdir(versionsDir);
|
|
146
|
+
|
|
147
|
+
for (const versionDir of versionDirs) {
|
|
148
|
+
if (versionDir == 'README.md')
|
|
149
|
+
continue;
|
|
150
|
+
|
|
151
|
+
const dirVersion = myt.parseVersionDir(versionDir);
|
|
152
|
+
if (!dirVersion) {
|
|
153
|
+
logVersion('[?????]'.yellow, versionDir,
|
|
154
|
+
`Wrong directory name.`
|
|
155
|
+
);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const versionNumber = dirVersion.number;
|
|
160
|
+
const versionName = dirVersion.name;
|
|
161
|
+
|
|
162
|
+
if (versionNumber.length != version.number.length) {
|
|
163
|
+
logVersion('[*****]'.gray, versionDir,
|
|
164
|
+
`Bad version length, should have ${version.number.length} characters.`
|
|
165
|
+
);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const scriptsDir = `${versionsDir}/${versionDir}`;
|
|
170
|
+
const scripts = await fs.readdir(scriptsDir);
|
|
171
|
+
|
|
172
|
+
const [versionLog] = await conn.query(
|
|
173
|
+
`SELECT file FROM versionLog
|
|
174
|
+
WHERE code = ?
|
|
175
|
+
AND number = ?
|
|
176
|
+
AND errorNumber IS NULL`,
|
|
177
|
+
[opts.code, versionNumber]
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
for (const script of scripts)
|
|
181
|
+
if (!isUndoScript(script)
|
|
182
|
+
&& versionLog.findIndex(x => x.file == script) === -1) {
|
|
183
|
+
silent = false;
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (silent) continue;
|
|
188
|
+
logVersion(`[${versionNumber}]`.cyan, versionName);
|
|
189
|
+
|
|
190
|
+
for (const script of scripts) {
|
|
191
|
+
if (!/^[0-9]{2}-[a-zA-Z0-9_]+(.undo)?\.sql$/.test(script)) {
|
|
192
|
+
logScript('[W]'.yellow, script, `Wrong file name.`);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (isUndoScript(script))
|
|
196
|
+
continue;
|
|
197
|
+
|
|
198
|
+
const [[row]] = await conn.query(
|
|
199
|
+
`SELECT errorNumber FROM versionLog
|
|
200
|
+
WHERE code = ?
|
|
201
|
+
AND number = ?
|
|
202
|
+
AND file = ?`,
|
|
203
|
+
[
|
|
204
|
+
opts.code,
|
|
205
|
+
versionNumber,
|
|
206
|
+
script
|
|
207
|
+
]
|
|
208
|
+
);
|
|
209
|
+
const apply = !row || row.errorNumber;
|
|
210
|
+
const actionMsg = apply ? '[+]'.green : '[I]'.blue;
|
|
211
|
+
|
|
212
|
+
logScript(actionMsg, script);
|
|
213
|
+
if (!apply) continue;
|
|
214
|
+
|
|
215
|
+
let err;
|
|
216
|
+
try {
|
|
217
|
+
await myt.queryFromFile(pushConn,
|
|
218
|
+
`${scriptsDir}/${script}`);
|
|
219
|
+
} catch (e) {
|
|
220
|
+
err = e;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
await conn.query(
|
|
224
|
+
`INSERT INTO versionLog
|
|
225
|
+
SET code = ?,
|
|
226
|
+
number = ?,
|
|
227
|
+
file = ?,
|
|
228
|
+
user = USER(),
|
|
229
|
+
updated = NOW(),
|
|
230
|
+
errorNumber = ?,
|
|
231
|
+
errorMessage = ?
|
|
232
|
+
ON DUPLICATE KEY UPDATE
|
|
233
|
+
updated = VALUES(updated),
|
|
234
|
+
user = VALUES(user),
|
|
235
|
+
errorNumber = VALUES(errorNumber),
|
|
236
|
+
errorMessage = VALUES(errorMessage)`,
|
|
237
|
+
[
|
|
238
|
+
opts.code,
|
|
239
|
+
versionNumber,
|
|
240
|
+
script,
|
|
241
|
+
err && err.errno,
|
|
242
|
+
err && err.message
|
|
243
|
+
]
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
if (err) throw err;
|
|
247
|
+
nChanges++;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
await this.updateVersion('number', versionNumber);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Apply routines
|
|
255
|
+
|
|
256
|
+
console.log('Applying changed routines.');
|
|
257
|
+
|
|
258
|
+
const gitExists = await fs.pathExists(`${opts.workspace}/.git`);
|
|
259
|
+
|
|
260
|
+
let nRoutines = 0;
|
|
261
|
+
let changes = await myt.changedRoutines(version.gitCommit);
|
|
262
|
+
changes = this.parseChanges(changes);
|
|
263
|
+
|
|
264
|
+
const routines = [];
|
|
265
|
+
for (const change of changes)
|
|
266
|
+
if (change.isRoutine)
|
|
267
|
+
routines.push([
|
|
268
|
+
change.schema,
|
|
269
|
+
change.name,
|
|
270
|
+
change.type.name
|
|
271
|
+
]);
|
|
272
|
+
|
|
273
|
+
if (routines.length) {
|
|
274
|
+
await conn.query(
|
|
275
|
+
`DROP TEMPORARY TABLE IF EXISTS tProcsPriv`
|
|
276
|
+
);
|
|
277
|
+
await conn.query(
|
|
278
|
+
`CREATE TEMPORARY TABLE tProcsPriv
|
|
279
|
+
ENGINE = MEMORY
|
|
280
|
+
SELECT * FROM mysql.procs_priv
|
|
281
|
+
WHERE (Db, Routine_name, Routine_type) IN (?)`,
|
|
282
|
+
[routines]
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const engine = new ExporterEngine(conn, opts.mytDir);
|
|
287
|
+
await engine.init();
|
|
288
|
+
|
|
289
|
+
async function finalize() {
|
|
290
|
+
await engine.saveInfo();
|
|
291
|
+
|
|
292
|
+
if (routines.length) {
|
|
293
|
+
await conn.query('FLUSH PRIVILEGES');
|
|
294
|
+
await conn.query(`DROP TEMPORARY TABLE tProcsPriv`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
for (const change of changes)
|
|
299
|
+
try {
|
|
300
|
+
const schema = change.schema;
|
|
301
|
+
const name = change.name;
|
|
302
|
+
const type = change.type.name.toLowerCase();
|
|
303
|
+
const fullPath = `${opts.routinesDir}/${change.path}.sql`;
|
|
304
|
+
const exists = await fs.pathExists(fullPath);
|
|
305
|
+
|
|
306
|
+
let newSql;
|
|
307
|
+
if (exists)
|
|
308
|
+
newSql = await fs.readFile(fullPath, 'utf8');
|
|
309
|
+
const oldSql = await engine.fetchRoutine(type, schema, name);
|
|
310
|
+
const oldSum = engine.getShaSum(type, schema, name);
|
|
311
|
+
const isEqual = newSql == oldSql;
|
|
312
|
+
|
|
313
|
+
let actionMsg;
|
|
314
|
+
if ((exists && isEqual) || (!exists && !oldSql))
|
|
315
|
+
actionMsg = '[I]'.blue;
|
|
316
|
+
else if (exists)
|
|
317
|
+
actionMsg = '[+]'.green;
|
|
318
|
+
else
|
|
319
|
+
actionMsg = '[-]'.red;
|
|
320
|
+
|
|
321
|
+
const typeMsg = `[${change.type.abbr}]`[change.type.color];
|
|
322
|
+
console.log('', actionMsg.bold, typeMsg.bold, change.fullName);
|
|
323
|
+
|
|
324
|
+
if (!isEqual) {
|
|
325
|
+
const scapedSchema = pushConn.escapeId(schema, true);
|
|
326
|
+
|
|
327
|
+
if (exists) {
|
|
328
|
+
if (change.type.name === 'VIEW')
|
|
329
|
+
await pushConn.query(`USE ${scapedSchema}`);
|
|
330
|
+
|
|
331
|
+
await myt.multiQuery(pushConn, newSql);
|
|
332
|
+
|
|
333
|
+
if (change.isRoutine) {
|
|
334
|
+
await conn.query(
|
|
335
|
+
`INSERT IGNORE INTO mysql.procs_priv
|
|
336
|
+
SELECT * FROM tProcsPriv
|
|
337
|
+
WHERE Db = ?
|
|
338
|
+
AND Routine_name = ?
|
|
339
|
+
AND Routine_type = ?`,
|
|
340
|
+
[schema, name, change.type.name]
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (opts.sums || oldSum)
|
|
345
|
+
await engine.fetchShaSum(type, schema, name);
|
|
346
|
+
} else {
|
|
347
|
+
const escapedName =
|
|
348
|
+
scapedSchema + '.' +
|
|
349
|
+
pushConn.escapeId(name, true);
|
|
350
|
+
|
|
351
|
+
const query = `DROP ${change.type.name} IF EXISTS ${escapedName}`;
|
|
352
|
+
await pushConn.query(query);
|
|
353
|
+
|
|
354
|
+
engine.deleteShaSum(type, schema, name);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
nRoutines++;
|
|
358
|
+
}
|
|
359
|
+
} catch (err) {
|
|
360
|
+
try {
|
|
361
|
+
await finalize();
|
|
362
|
+
} catch (e) {
|
|
363
|
+
console.error(e);
|
|
364
|
+
}
|
|
365
|
+
throw err;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
await finalize();
|
|
369
|
+
|
|
370
|
+
if (nRoutines > 0) {
|
|
371
|
+
console.log(` -> ${nRoutines} routines have changed.`);
|
|
372
|
+
} else
|
|
373
|
+
console.log(` -> No routines changed.`);
|
|
374
|
+
|
|
375
|
+
if (gitExists && opts.commit) {
|
|
376
|
+
const repo = await nodegit.Repository.open(this.opts.workspace);
|
|
377
|
+
const head = await repo.getHeadCommit();
|
|
378
|
+
|
|
379
|
+
if (head && version.gitCommit !== head.sha())
|
|
380
|
+
await this.updateVersion('gitCommit', head.sha());
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// End
|
|
384
|
+
|
|
385
|
+
await pushConn.end();
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
parseChanges(changes) {
|
|
389
|
+
const routines = [];
|
|
390
|
+
if (changes)
|
|
391
|
+
for (const change of changes)
|
|
392
|
+
routines.push(new Routine(change));
|
|
393
|
+
return routines;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
async updateVersion(column, value) {
|
|
397
|
+
column = this.conn.escapeId(column, true);
|
|
398
|
+
await this.conn.query(
|
|
399
|
+
`INSERT INTO version
|
|
400
|
+
SET code = ?,
|
|
401
|
+
${column} = ?,
|
|
402
|
+
updated = NOW()
|
|
403
|
+
ON DUPLICATE KEY UPDATE
|
|
404
|
+
${column} = VALUES(${column}),
|
|
405
|
+
updated = VALUES(updated)`,
|
|
406
|
+
[
|
|
407
|
+
this.opts.code,
|
|
408
|
+
value
|
|
409
|
+
]
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const typeMap = {
|
|
416
|
+
events: {
|
|
417
|
+
name: 'EVENT',
|
|
418
|
+
abbr: 'EVNT',
|
|
419
|
+
color: 'cyan'
|
|
420
|
+
},
|
|
421
|
+
functions: {
|
|
422
|
+
name: 'FUNCTION',
|
|
423
|
+
abbr: 'FUNC',
|
|
424
|
+
color: 'cyan'
|
|
425
|
+
},
|
|
426
|
+
procedures: {
|
|
427
|
+
name: 'PROCEDURE',
|
|
428
|
+
abbr: 'PROC',
|
|
429
|
+
color: 'yellow'
|
|
430
|
+
},
|
|
431
|
+
triggers: {
|
|
432
|
+
name: 'TRIGGER',
|
|
433
|
+
abbr: 'TRIG',
|
|
434
|
+
color: 'blue'
|
|
435
|
+
},
|
|
436
|
+
views: {
|
|
437
|
+
name: 'VIEW',
|
|
438
|
+
abbr: 'VIEW',
|
|
439
|
+
color: 'magenta'
|
|
440
|
+
},
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
const routineTypes = new Set([
|
|
444
|
+
'FUNCTION',
|
|
445
|
+
'PROCEDURE'
|
|
446
|
+
]);
|
|
447
|
+
|
|
448
|
+
class Routine {
|
|
449
|
+
constructor(change) {
|
|
450
|
+
const path = change.path;
|
|
451
|
+
const split = path.split('/');
|
|
452
|
+
|
|
453
|
+
const schema = split[0];
|
|
454
|
+
const type = typeMap[split[1]];
|
|
455
|
+
const name = split[2];
|
|
456
|
+
|
|
457
|
+
if (split.length !== 3 || !type)
|
|
458
|
+
throw new Error(`Wrong routine path for '${path}', check that the sql file is located in the correct directory`);
|
|
459
|
+
|
|
460
|
+
Object.assign(this, {
|
|
461
|
+
path,
|
|
462
|
+
mark: change.mark,
|
|
463
|
+
type,
|
|
464
|
+
schema,
|
|
465
|
+
name,
|
|
466
|
+
fullName: `${schema}.${name}`,
|
|
467
|
+
isRoutine: routineTypes.has(type.name)
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
module.exports = Push;
|
|
473
|
+
|
|
474
|
+
if (require.main === module)
|
|
475
|
+
new Myt().run(Push);
|