Skip to content

Commit 5bc3959

Browse files
committed
Refinements to index handling
Improved handling of indexes in isActual and autoupdate.
1 parent a93dd62 commit 5bc3959

File tree

2 files changed

+109
-71
lines changed

2 files changed

+109
-71
lines changed

lib/migration.js

Lines changed: 107 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,52 @@ function mixinMigration(PostgreSQL) {
209209
});
210210
};
211211

212+
PostgreSQL.prototype.isActualIndexes = function (modelName, callback) {
213+
debug('modelName %j', modelName);
214+
const self = this;
215+
216+
const model = self._models[modelName];
217+
218+
self.showIndexes(modelName, (err, asIsModelIndexes) => {
219+
if (err) return callback(err);
220+
221+
debug('asIsModelIndexes %j', asIsModelIndexes);
222+
223+
const asIsIndexes = generateAsIsIndexCompare(asIsModelIndexes);
224+
225+
const toBeIndexes = generateToBeIndexCompare(model.settings.indexes);
226+
debug('toBeIndexes', toBeIndexes);
227+
228+
// Look for Differences
229+
let differenceFound = false;
230+
for (const name in toBeIndexes) {
231+
debug('name %j', name);
232+
if (asIsIndexes[name] !== toBeIndexes[name]) {
233+
debug('asIsIndexes[name] %j', asIsIndexes[name]);
234+
debug('toBeIndexes[name] %j', toBeIndexes[name]);
235+
differenceFound = true;
236+
break;
237+
}
238+
}
239+
240+
if (!differenceFound) {
241+
for (const name in asIsIndexes) {
242+
debug('name %j', name);
243+
if (!toBeIndexes[name]) {
244+
debug('name not found in toBe %j', name);
245+
differenceFound = true;
246+
break;
247+
}
248+
}
249+
}
250+
if (differenceFound) {
251+
debug('differenceFound for %j', modelName);
252+
}
253+
254+
callback(null, !differenceFound);
255+
});
256+
};
257+
212258
PostgreSQL.prototype.getAddModifyColumns = function (model, actualFields) {
213259
let sql = [];
214260
const self = this;
@@ -757,13 +803,16 @@ function mixinMigration(PostgreSQL) {
757803
function isCaseInsensitiveEqual(val1, val2) {
758804
return val1.toLowerCase() === val2.toLowerCase();
759805
}
806+
760807
/*!
761808
* Case insensitive comparison of object properties
762809
* @param {Object} properties
763810
* @param {String} name
764811
*/
765812
function hasColumnProperty(properties, name) {
766-
if (!name) { return false; }
813+
if (!name) {
814+
return false;
815+
}
767816

768817
return (Object.keys(properties)
769818
.map(function (k) {
@@ -870,17 +919,16 @@ function mixinMigration(PostgreSQL) {
870919
}
871920

872921
PostgreSQL.prototype.addIndexes = function (model, actualIndexes, options, cb) {
922+
873923
const self = this;
924+
874925
const m = self._models[model];
875-
const propNames = Object.keys(m.properties).filter(function (name) {
876-
return !!m.properties[name];
877-
});
878-
const indexNames = m.settings.indexes && Object.keys(m.settings.indexes).filter(function (name) {
879-
return !!m.settings.indexes[name];
880-
}) || [];
926+
927+
const schema = self.escapeName(self.schema(model) || 'public');
928+
881929
const sql = [];
930+
882931
const ai = {};
883-
const propNameRegEx = new RegExp('^' + self.table(model) + '_([^_]+)_idx');
884932

885933
if (actualIndexes) {
886934
actualIndexes.forEach(function (i) {
@@ -890,75 +938,52 @@ function mixinMigration(PostgreSQL) {
890938
}
891939
});
892940
}
893-
const aiNames = Object.keys(ai);
894-
895-
// remove indexes
896-
aiNames.forEach(function (indexName) {
897-
const schema = self.escapeName(self.schema(model) || 'public');
898-
const i = ai[indexName];
899-
let propName = propNameRegEx.exec(indexName);
900-
let si; // index definition from model schema
941+
debug('ai %j', ai);
901942

902-
if (i.primary || (m.properties[indexName] && self.id(model, indexName))) return;
943+
const asIsIndexes = generateAsIsIndexCompare(actualIndexes);
944+
debug('asIsIndexes %j', asIsIndexes);
903945

904-
propName = propName && self.propertyName(model, propName[1]) || null;
905-
if (!(indexNames.indexOf(indexName) > -1) && !(propName && m.properties[propName] &&
906-
m.properties[propName].index)) {
907-
sql.push('DROP INDEX ' + schema + '.' + self.escapeName(indexName));
908-
} else {
909-
// The index was found, verify that database matches what we're expecting.
910-
// first: check single column indexes.
911-
if (propName) {
912-
// If this property has an index definition, verify that it matches
913-
if (m.properties[propName] && (si = m.properties[propName].index)) {
914-
if (
915-
(typeof si === 'object') &&
916-
!((!si.type || si.type === ai[indexName].type) && (!si.unique || si.unique === ai[indexName].unique))
917-
) {
918-
// Drop the index if the type or unique differs from the actual table
919-
sql.push('DROP INDEX ' + schema + '.' + self.escapeName(indexName));
920-
delete ai[indexName];
921-
}
922-
}
923-
} else {
924-
// second: check other indexes
925-
si = normalizeIndexDefinition(m.settings.indexes[indexName]);
926-
927-
let identical =
928-
(!si.type || si.type === i.type) && // compare type
929-
((si.options && !!si.options.unique) === i.unique); // compare unique
930-
931-
// if this is a multi-column query, verify that the order matches
932-
const siKeys = Object.keys(si.keys);
933-
if (identical && siKeys.length > 1) {
934-
if (siKeys.length !== i.keys.length) {
935-
// lengths differ, obviously non-matching
936-
identical = false;
937-
} else {
938-
siKeys.forEach(function (propName, iter) {
939-
identical = identical && self.column(model, propName) === i.keys[iter];
940-
});
941-
}
942-
}
946+
const toBeIndexes = generateToBeIndexCompare(m.settings.indexes);
947+
debug('toBeIndexes %j', toBeIndexes);
943948

944-
if (!identical) {
945-
sql.push('DROP INDEX ' + schema + '.' + self.escapeName(indexName));
946-
delete ai[indexName];
947-
}
948-
}
949+
for (const name in asIsIndexes) {
950+
debug('name %j', name);
951+
if (asIsIndexes[name] !== toBeIndexes[name]) {
952+
debug('asIsIndexes[name] %j', asIsIndexes[name]);
953+
debug('toBeIndexes[name] %j', toBeIndexes[name]);
954+
sql.push('DROP INDEX ' + schema + '.' + self.escapeName(name));
955+
delete ai[name];
949956
}
957+
}
958+
debug('sql after drop indexes %j', sql);
959+
debug('ai after drop indexes %j', ai);
960+
961+
const propNames = Object.keys(m.properties).filter(function (name) {
962+
return !!m.properties[name];
950963
});
964+
debug('propNames %j', propNames);
965+
966+
const indexNames =
967+
(m.settings.indexes &&
968+
Object.keys(m.settings.indexes).filter(function (name) {
969+
return !!m.settings.indexes[name];
970+
})) ||
971+
[];
972+
debug('indexNames %j', indexNames);
951973

952974
// add single-column indexes
953975
propNames.forEach(function (propName) {
976+
debug('propName %j', propName);
954977
const i = m.properties[propName].index;
955978
if (!i) {
956979
return;
957980
}
981+
debug('i %j', i);
958982

959983
// The index name used should match the default naming scheme
960984
// by postgres: <column>_<table>_idx
961985
const iName = [self.table(model), self.column(model, propName), 'idx'].join('_');
986+
debug('iName %j', iName);
962987

963988
const found = ai[iName]; // && ai[iName].info;
964989
if (!found) {
@@ -972,12 +997,22 @@ function mixinMigration(PostgreSQL) {
972997
kind = i.kind;
973998
}
974999

975-
if (!kind && !type && typeof i === 'object' || i.unique && i.unique === true) {
1000+
if ((!kind && !type && typeof i === 'object') || (i.unique && i.unique === true)) {
9761001
kind = ' UNIQUE ';
9771002
}
9781003

979-
sql.push('CREATE ' + kind + ' INDEX ' + self.escapeName(iName) + ' ON ' + self.tableEscaped(model) +
980-
type + ' ( ' + pName + ' )');
1004+
sql.push(
1005+
'CREATE ' +
1006+
kind +
1007+
' INDEX ' +
1008+
self.escapeName(iName) +
1009+
' ON ' +
1010+
self.tableEscaped(model) +
1011+
type +
1012+
' ( ' +
1013+
pName +
1014+
' )'
1015+
);
9811016
}
9821017
});
9831018

@@ -988,9 +1023,11 @@ function mixinMigration(PostgreSQL) {
9881023
if (!found) {
9891024
i = normalizeIndexDefinition(i);
9901025
const iName = self.escapeName(indexName);
991-
const columns = i.keys.map(function (key) {
992-
return self.escapeName(self.column(model, key[0])) + (key[1] ? ' ' + key[1] : '');
993-
}).join(', ');
1026+
const columns = i.keys
1027+
.map(function (key) {
1028+
return self.escapeName(self.column(model, key[0])) + (key[1] ? ' ' + key[1] : '');
1029+
})
1030+
.join(', ');
9941031

9951032
let type = '';
9961033
let kind = '';
@@ -1005,8 +1042,9 @@ function mixinMigration(PostgreSQL) {
10051042
kind = ' UNIQUE ';
10061043
}
10071044

1008-
sql.push('CREATE ' + kind + ' INDEX ' + iName + ' ON ' + self.tableEscaped(model) +
1009-
type + ' ( ' + columns + ')');
1045+
sql.push(
1046+
'CREATE ' + kind + ' INDEX ' + iName + ' ON ' + self.tableEscaped(model) + type + ' ( ' + columns + ')'
1047+
);
10101048
}
10111049
});
10121050

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@apexdesigner/loopback-connector-postgresql",
3-
"version": "7.0.8",
3+
"version": "7.0.9",
44
"description": "Loopback PostgreSQL Connector",
55
"keywords": [
66
"LoopBack",
@@ -33,7 +33,7 @@
3333
"bluebird": "^3.4.6",
3434
"chalk": "^4.0.0",
3535
"debug": "^4.1.1",
36-
"loopback-connector": "npm:@apexdesigner/loopback-connector@^6.0.0",
36+
"loopback-connector": "npm:@apexdesigner/loopback-connector@^6.1.4",
3737
"pg": "^8.0.2",
3838
"strong-globalize": "^6.0.0",
3939
"uuid": "^9.0.0"

0 commit comments

Comments
 (0)