Skip to content

Commit 58ce604

Browse files
author
Sakib Hasan
authored
Merge pull request #38 from strongloop/refactor-migration-ibmdb
Refactor migration ibmdb
2 parents 285f72f + 072ad63 commit 58ce604

2 files changed

Lines changed: 265 additions & 0 deletions

File tree

lib/ibmdb.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -930,3 +930,5 @@ IBMDB.prototype.applyPagination = function(model, stmt, filter) {
930930
var limitClause = buildLimit(filter.limit, filter.offset || filter.skip);
931931
return stmt.merge(limitClause);
932932
};
933+
934+
require('./migration')(IBMDB);

lib/migration.js

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
// Copyright IBM Corp. 2016. All Rights Reserved.
2+
// Node module: loopback-ibmdb
3+
// This file is licensed under the Artistic License 2.0.
4+
// License text available at https://opensource.org/licenses/Artistic-2.0
5+
6+
'use strict';
7+
8+
var async = require('async');
9+
10+
module.exports = function(IBMDB) {
11+
IBMDB.prototype.getAddModifyColumns = function(model, fields) {};
12+
13+
IBMDB.prototype.getDropColumns = function(model, fields) {};
14+
15+
IBMDB.prototype.showFields = function(model, cb) {
16+
var self = this;
17+
var sql = 'SELECT COLNAME AS NAME, TYPENAME AS DATATYPE, ' +
18+
'COLNO, LENGTH AS DATALENGTH, NULLS FROM SYSCAT.COLUMNS ' +
19+
'WHERE TABNAME LIKE \'' +
20+
self.table(model) + '\' ' +
21+
'AND TABSCHEMA LIKE \'' +
22+
self.schema + '\'' +
23+
' ORDER BY COLNO';
24+
this.execute(sql, function(err, fields) {
25+
if (err) {
26+
return cb(err);
27+
} else {
28+
cb(err, fields);
29+
}
30+
});
31+
};
32+
33+
IBMDB.prototype.showIndexes = function(model, cb) {
34+
var self = this;
35+
var sql = 'SELECT TABNAME, TABSCHEMA, INDNAME, ' +
36+
'COLNAMES, UNIQUERULE FROM SYSCAT.INDEXES ' +
37+
'WHERE TRIM(TABNAME) LIKE \'' +
38+
self.table(model) + '\' ' +
39+
'AND TRIM(TABSCHEMA) LIKE \'' +
40+
self.schema + '\'';
41+
this.execute(sql, function(err, indexes) {
42+
if (err) {
43+
return cb(err);
44+
} else {
45+
cb(err, indexes);
46+
}
47+
});
48+
};
49+
50+
IBMDB.prototype.alterTable = function(model, actualFields, actualIndexes,
51+
callback, checkOnly) {
52+
var self = this;
53+
var m = this.getModelDefinition(model);
54+
var propNames = Object.keys(m.properties).filter(function(name) {
55+
return !!m.properties[name];
56+
});
57+
var indexes = m.settings.indexes || {};
58+
var indexNames = Object.keys(indexes).filter(function(name) {
59+
return !!m.settings.indexes[name];
60+
});
61+
var sql = [];
62+
var tasks = [];
63+
var operations = [];
64+
var ai = {};
65+
var type = '';
66+
67+
if (actualIndexes) {
68+
actualIndexes.forEach(function(i) {
69+
var name = i.INDNAME;
70+
if (!ai[name]) {
71+
ai[name] = {
72+
info: i,
73+
columns: [],
74+
};
75+
}
76+
77+
i.COLNAMES.split(/\+\s*/).forEach(function(columnName, j) {
78+
// This is a bit of a dirty way to get around this but DB2 returns
79+
// column names as a string started with and separated by a '+'.
80+
// The code below will strip out the initial '+' then store the
81+
// actual column names.
82+
if (j > 0)
83+
ai[name].columns[j - 1] = columnName;
84+
});
85+
});
86+
}
87+
var aiNames = Object.keys(ai);
88+
89+
// change/add new fields
90+
propNames.forEach(function(propName) {
91+
if (m.properties[propName] && self.id(model, propName)) return;
92+
var found;
93+
if (actualFields) {
94+
actualFields.forEach(function(f) {
95+
if (f.NAME === propName) {
96+
found = f;
97+
}
98+
});
99+
}
100+
101+
if (found) {
102+
actualize(propName, found);
103+
} else {
104+
operations.push('ADD COLUMN ' + propName + ' ' +
105+
self.buildColumnDefinition(model, propName));
106+
}
107+
});
108+
109+
// drop columns
110+
if (actualFields) {
111+
actualFields.forEach(function(f) {
112+
var notFound = !~propNames.indexOf(f.NAME);
113+
if (m.properties[f.NAME] && self.id(model, f.NAME)) return;
114+
if (notFound || !m.properties[f.NAME]) {
115+
operations.push('DROP COLUMN ' + f.NAME);
116+
}
117+
});
118+
}
119+
120+
if (operations.length) {
121+
// Add the ALTER TABLE statement to the list of tasks to perform later.
122+
sql.push('ALTER TABLE ' + self.schema + '.' +
123+
self.tableEscaped(model) + ' ' + operations.join(' ') + ';');
124+
}
125+
126+
operations = [];
127+
128+
// remove indexes
129+
aiNames.forEach(function(indexName) {
130+
if (ai[indexName].info.UNIQUERULE === 'P'
131+
|| // indexName === 'PRIMARY' ||
132+
(m.properties[indexName] && self.id(model, indexName))) return;
133+
134+
if (indexNames.indexOf(indexName) === -1 && !m.properties[indexName] ||
135+
m.properties[indexName] && !m.properties[indexName].index) {
136+
if (ai[indexName].info.UNIQUERULE === 'P') {
137+
operations.push('DROP PRIMARY KEY');
138+
} else if (ai[indexName].info.UNIQUERULE === 'U') {
139+
operations.push('DROP UNIQUE ' + indexName);
140+
}
141+
} else {
142+
// first: check single (only type and kind)
143+
if (m.properties[indexName] && !m.properties[indexName].index) {
144+
// TODO
145+
return;
146+
}
147+
// second: check multiple indexes
148+
var orderMatched = true;
149+
if (indexNames.indexOf(indexName) !== -1) {
150+
m.settings.indexes[indexName].columns.split(/,\s*/).forEach(
151+
function(columnName, i) {
152+
if (ai[indexName].columns[i] !== columnName)
153+
orderMatched = false;
154+
});
155+
}
156+
157+
if (!orderMatched) {
158+
if (ai[indexName].info.UNIQUERULE === 'P') {
159+
operations.push('DROP PRIMARY KEY');
160+
} else if (ai[indexName].info.UNIQUERULE === 'U') {
161+
operations.push('DROP UNIQUE ' + indexName);
162+
}
163+
164+
delete ai[indexName];
165+
}
166+
}
167+
});
168+
169+
if (operations.length) {
170+
// Add the ALTER TABLE statement to the list of tasks to perform later.
171+
sql.push('ALTER TABLE ' + self.schema + '.' +
172+
self.tableEscaped(model) + ' ' + operations.join(' ') + ';');
173+
}
174+
175+
// add single-column indexes
176+
propNames.forEach(function(propName) {
177+
var i = m.properties[propName].index;
178+
if (!i) {
179+
return;
180+
}
181+
var found = ai[propName] && ai[propName].info;
182+
if (!found) {
183+
var pName = propName;
184+
type = '';
185+
if (i.type) {
186+
type = i.type;
187+
}
188+
sql.push('CREATE ' + type + ' INDEX ' + pName + ' ON ' +
189+
self.schema + '.' + self.tableEscaped(model) +
190+
'(\"' + pName + '\") ');
191+
}
192+
});
193+
194+
// add multi-column indexes
195+
indexNames.forEach(function(indexName) {
196+
var i = m.settings.indexes[indexName];
197+
var found = ai[indexName] && ai[indexName].info;
198+
if (!found) {
199+
var iName = indexName;
200+
var type = '';
201+
if (i.type) {
202+
type = i.type;
203+
}
204+
var stmt = 'CREATE ' + type + 'INDEX ' + iName + ' ON ' +
205+
self.schema + '.' + self.tableEscaped(model) + '(';
206+
207+
var splitNames = i.columns.split(/,\s*/);
208+
var colNames = splitNames.join('\",\"');
209+
210+
stmt += '\"' + colNames + '\")';
211+
212+
sql.push(stmt);
213+
}
214+
});
215+
216+
sql.forEach(function(i) {
217+
tasks.push(function(cb) {
218+
self.execute(i, function(err, results) {
219+
cb(err);
220+
});
221+
});
222+
});
223+
224+
if (tasks.length) {
225+
if (checkOnly) {
226+
return (callback(null, true, {statements: sql}));
227+
} else {
228+
async.series(tasks, function() {
229+
return (callback());
230+
});
231+
}
232+
} else {
233+
return (callback());
234+
}
235+
236+
function actualize(propName, oldSettings) {
237+
var newSettings = m.properties[propName];
238+
if (newSettings && changed(newSettings, oldSettings)) {
239+
// TODO: NO TESTS EXECUTE THIS CODE PATH
240+
var pName = '\'' + propName + '\'';
241+
operations.push('CHANGE COLUMN ' + pName + ' ' + pName + ' ' +
242+
self.buildColumnDefinition(model, propName));
243+
}
244+
}
245+
246+
function changed(newSettings, oldSettings) {
247+
if (oldSettings.Null === 'YES') {
248+
// Used to allow null and does not now.
249+
if (!self.isNullable(newSettings)) {
250+
return true;
251+
}
252+
}
253+
if (oldSettings.Null === 'NO') {
254+
// Did not allow null and now does.
255+
if (self.isNullable(newSettings)) {
256+
return true;
257+
}
258+
}
259+
260+
return false;
261+
}
262+
};
263+
};

0 commit comments

Comments
 (0)