Skip to content

Commit

Permalink
Initial concept of declarative partition support
Browse files Browse the repository at this point in the history
  • Loading branch information
sonya committed Mar 21, 2019
1 parent 667f099 commit bee8459
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 0 deletions.
15 changes: 15 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,21 @@ var PgDriver = Base.extend({
}
},

// the superclass doesn't really give us a chance to determine version
// before/within createTable, so this is just going to fail if the user
// tries to partition tables on older server versions :(
_applyTableOptions: function (options, tableName) {
var tableOptionClauses = [];
if (typeof options.partitionBy === 'string') {
var validExpression = /^(list|range|hash)\s*(\(.+\))?$/i;
if (options.partitionBy.match(validExpression) !== null) {
tableOptionClauses.push('PARTITION BY ' + options.partitionBy);
}
}

return tableOptionClauses.join(' ');
},

mapDataType: function (str) {
switch (str) {
case 'json':
Expand Down
174 changes: 174 additions & 0 deletions test/pg_tableoptions_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
var vows = require('vows');
var assert = require('assert');
var dataType = require('db-migrate-shared').dataType;
var driver = require('../');
var log = require('db-migrate-shared').log;
var semver = require('semver');

var config = require('./db.config.json').pg;
var db;
var version;

var internals = {};
internals.mod = {
log: log,
type: dataType
};
internals.interfaces = {
SeederInterface: {},
MigratorInterface: {}
};
internals.migrationTable = 'migrations';
log.silence(true);

function getPartitionInfo (db, tableName, callback) {
var sql = 'select pg_get_partkeydef(oid) ' +
'from pg_class ' +
"where relkind = 'p' " +
"and relname = '" + tableName + "'";

db.runSql(sql, function (err, metadata) {
var result = null;
if (!err && metadata.rows.length) {
result = metadata.rows[0].pg_get_partkeydef;
}
return callback(err, result);
});
}

vows
.describe('pg')
.addBatch({
connect: {
topic: function () {
driver.connect(config, internals, this.callback);
},

'is connected': function (err, _db) {
assert.isNull(err);
db = _db;
}
}
})
.addBatch({
determineVersion: {
topic: function () {
db.determineVersion(this.callback);
},

'has valid version': function (err, _version) {
assert.isNull(err);
version = _version;
}
}
})
.addBatch({
'partition by list': {
topic: function () {
db.createTable(
'partitioned_by_list', {
columns: {
key: dataType.STRING
},
partitionBy: 'LIST (key)'
},
this.callback.bind(this)
);
},

'has table metadata': {
topic: function () {
getPartitionInfo(db, 'partitioned_by_list', this.callback);
},

'is partitioned by LIST (key)': function (err, partitionDef) {
if (semver.gte(version, '10.0.0')) {
assert.isNull(err);
assert.strictEqual(partitionDef, 'LIST (key)');
} else {
assert.isNotNull(err);
}
}
},

teardown: function () {
db.dropTable('partitioned_by_list', this.callback);
}
},

'partition by range': {
topic: function () {
db.createTable(
'partitioned_by_range', {
columns: {
date: {
type: 'TIMESTAMP'
}
},
partitionBy: 'RANGE (date)'
},
this.callback.bind(this)
);
},

'has table metadata': {
topic: function () {
getPartitionInfo(db, 'partitioned_by_range', this.callback);
},

'is partitioned by RANGE (date)': function (err, partitionDef) {
if (semver.gte(version, '10.0.0')) {
assert.isNull(err);
assert.strictEqual(partitionDef, 'RANGE (date)');
} else {
assert.isNotNull(err);
}
}
},

teardown: function () {
db.dropTable('partitioned_by_range', this.callback);
}
}
})
.addBatch({
'partition by hash': {
topic: function () {
db.createTable(
'partitioned_by_hash', {
columns: {
txt: { type: dataType.TEXT, notNull: true, defaultValue: 'foo' }
},
partitionBy: 'HASH (txt)'
},
this.callback.bind(this)
);
},

'has table metadata': {
topic: function () {
getPartitionInfo(db, 'partitioned_by_hash', this.callback);
},

'is partitioned by HASH (txt)': function (err, partitionDef) {
if (semver.gte(version, '11.0.0')) {
assert.isNull(err);
assert.strictEqual(partitionDef, 'HASH (txt)');
} else if (semver.gte(version, '10.0.0')) {
// HASH is not recognized in pg10 and the table fails to create
// which causes the partition query to return nothing.
// (there's likely a better way to express these version tests)
assert.isNull(err);
assert.isNull(partitionDef);
} else {
assert.isNotNull(err);
}
}
},

teardown: function () {
db.dropTable('partitioned_by_hash', this.callback);
}
}
})
.export(module);

0 comments on commit bee8459

Please sign in to comment.