-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpg-patcher.js
169 lines (144 loc) · 4.88 KB
/
pg-patcher.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// ----------------------------------------------------------------------------
//
// pg-patcher - A Postgres patch helper for node-postgres.
//
// Copyright (c) 2013, Andrew Chilton. All Rights Reserved.
//
// License: MIT - http://chilts.mit-license.org/2013/
//
// ----------------------------------------------------------------------------
// core
var fs = require('fs');
// npm
var xtend = require('xtend');
var async = require('async');
// ----------------------------------------------------------------------------
var defaults = {
dir : 'db',
prefix : 'patch',
logger : function() {}, // noop
};
module.exports = function pgpatcher(client, level, opts, callback) {
// optional 'opts'
if ( typeof opts === 'function' ) {
callback = opts;
opts = {};
}
opts = xtend({}, defaults, opts);
var logger = opts.logger;
var forwardPatch = {};
var reversePatch = {};
var currentPatchLevel;
async.series(
[
readPatchDir,
getCurrentPatch,
// checkAllPatchFilesExist,
begin,
nextPatch,
writeCurrentLevel,
commit,
],
function(err) {
if (err) {
return callback(err);
}
callback(null, currentPatchLevel);
}
);
function readPatchDir(done) {
fs.readdir(opts.dir, function(err, files) {
files.forEach(function(filename) {
if ( filename.match(/~$/) ) {
return;
}
var parts = filename.split(/[-_\.]/);
var from = +parts[1];
var to = +parts[2];
var patch = {
from : from,
to : to,
filename : filename,
};
if ( to > from ) {
forwardPatch[to] = patch;
}
else {
reversePatch[to] = patch;
}
});
done();
});
}
function begin(done) {
logger('Beginning transaction ...');
client.query("BEGIN", done);
logger('Beginning transaction ... done!');
}
function commit(done) {
logger('Commiting transaction ...');
client.query("COMMIT", done);
logger('Commiting transaction ... done!');
}
function getCurrentPatch(done) {
logger('Getting current patch ...');
client.query("SELECT value FROM property WHERE key = 'patch'", function(err, res) {
if (err) {
if ( '' + err === 'error: relation "property" does not exist' ) {
logger('Property table does not exist, patch level zero!');
currentPatchLevel = 0;
return done();
}
return done(err);
}
currentPatchLevel = +res.rows[0].value;
logger('Current patch is ' + currentPatchLevel);
logger('Getting current patch ... done!');
done();
});
}
function nextPatch(done) {
var tryLevel;
var patch;
if ( level > currentPatchLevel ) {
tryLevel = currentPatchLevel + 1;
patch = forwardPatch[tryLevel];
logger('Trying patch to %s ...', tryLevel);
}
else if ( level < currentPatchLevel ) {
tryLevel = currentPatchLevel - 1;
patch = reversePatch[tryLevel];
logger('Trying patch to %s ...', tryLevel);
}
else {
// same, nothing to do
logger('No (more) patching needed');
return process.nextTick(done);
}
// make the filename
filename = opts.dir + '/' + patch.filename;
// read the patch file
fs.readFile(filename, { encoding : 'utf8' }, function(err, sql) {
client.query(sql, function(err, res) {
if (err) return done(err);
// update the current patch level state
currentPatchLevel = tryLevel;
logger('Trying patch to %s ...', tryLevel, 'done!');
nextPatch(done);
});
});
}
function writeCurrentLevel(done) {
// only write the if the patch level is greater than zero
if ( currentPatchLevel > 0 ) {
logger('Writing current patch level %s to database ...', currentPatchLevel);
client.query("UPDATE property SET value = $1 WHERE key = 'patch'", [ currentPatchLevel ], done);
logger('Writing current patch level %s to database ...', currentPatchLevel, 'done!');
}
else {
logger("Patch level 0 - no need to write this to the database");
process.nextTick(done);
}
}
};
// ----------------------------------------------------------------------------