-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add redshift support without changing cli or package.json (#2233)
* Add a Redshift dialect that inherits from Postgres. * Turn .index() and .dropIndex() into no-ops with warnings in the Redshift dialect. * Update the Redshift dialect to be compatible with master. * Update package.json * Disable liftoff cli * Remove the CLI * Add lib to the repo * Allow the escaping of named bindings. * Update dist * Update the Redshift dialect’s instantiation of the query and column compilers. * Update the distribution * Fix a merge conflict * Take lib back out * Trying to bring back in line with tgreisser/knex * Add npm 5 package-lock * Bring cli.js back in line * Bring cli.js back in line * Progress commit on redshift integration tests * Revert "Progress commit on redshift integration tests" This reverts commit 207e316. * Progress commit * Working not null on primary columns in createTable * Working redshift unit tests * Working unit and integration tests, still need to fix migration tests * Brought datatypes more in line with what redshift actually supports * Added query compiler unit tests * Add a hacky returning clause for redshift ugh * Working migration integration tests * Working insert integration tests * Allow multiple insert returning values * Working select integration tests * Working join integration tests * Working aggregate integration tests * All integration suite tests working * Put docker index for reconnect tests back * Redshift does not support insert...returning, there does not seem to be a way around that, therefore accept it and test accordingly * Leave redshift integration tests in place, but do not run them by default * Fix mysql order by test * Fix more tests * Change test db name to knex_test for consistency * Address PR comments
- Loading branch information
Showing
26 changed files
with
2,784 additions
and
556 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ raw | |
.DS_Store | ||
.vagrant | ||
node_modules | ||
package-lock.json | ||
test.sqlite3 | ||
npm-debug.log | ||
tmp | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
|
||
// Redshift | ||
// ------- | ||
import inherits from 'inherits'; | ||
import Client_PG from '../postgres'; | ||
import { assign, map, } from 'lodash' | ||
|
||
import Transaction from './transaction'; | ||
import QueryCompiler from './query/compiler'; | ||
import ColumnBuilder from './schema/columnbuilder'; | ||
import ColumnCompiler from './schema/columncompiler'; | ||
import TableCompiler from './schema/tablecompiler'; | ||
import SchemaCompiler from './schema/compiler'; | ||
|
||
function Client_Redshift(config) { | ||
Client_PG.apply(this, arguments) | ||
} | ||
inherits(Client_Redshift, Client_PG) | ||
|
||
assign(Client_Redshift.prototype, { | ||
transaction() { | ||
return new Transaction(this, ...arguments) | ||
}, | ||
|
||
queryCompiler() { | ||
return new QueryCompiler(this, ...arguments) | ||
}, | ||
|
||
columnBuilder() { | ||
return new ColumnBuilder(this, ...arguments); | ||
}, | ||
|
||
columnCompiler() { | ||
return new ColumnCompiler(this, ...arguments) | ||
}, | ||
|
||
tableCompiler() { | ||
return new TableCompiler(this, ...arguments) | ||
}, | ||
|
||
schemaCompiler() { | ||
return new SchemaCompiler(this, ...arguments) | ||
}, | ||
|
||
dialect: 'redshift', | ||
|
||
driverName: 'pg-redshift', | ||
|
||
_driver() { | ||
return require('pg') | ||
}, | ||
|
||
// Ensures the response is returned in the same format as other clients. | ||
processResponse(obj, runner) { | ||
const resp = obj.response; | ||
if (obj.output) return obj.output.call(runner, resp); | ||
if (obj.method === 'raw') return resp; | ||
if (resp.command === 'SELECT') { | ||
if (obj.method === 'first') return resp.rows[0]; | ||
if (obj.method === 'pluck') return map(resp.rows, obj.pluck); | ||
return resp.rows; | ||
} | ||
if ( | ||
resp.command === 'INSERT' || | ||
resp.command === 'UPDATE' || | ||
resp.command === 'DELETE' | ||
) { | ||
return resp.rowCount; | ||
} | ||
return resp; | ||
} | ||
}) | ||
|
||
export default Client_Redshift; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
|
||
// Redshift Query Builder & Compiler | ||
// ------ | ||
import inherits from 'inherits'; | ||
|
||
import QueryCompiler from '../../../query/compiler'; | ||
import QueryCompiler_PG from '../../postgres/query/compiler'; | ||
import * as helpers from '../../../helpers'; | ||
|
||
import { assign, reduce } from 'lodash'; | ||
|
||
function QueryCompiler_Redshift(client, builder) { | ||
QueryCompiler_PG.call(this, client, builder); | ||
} | ||
inherits(QueryCompiler_Redshift, QueryCompiler_PG); | ||
|
||
assign(QueryCompiler_Redshift.prototype, { | ||
truncate() { | ||
return `truncate ${this.tableName.toLowerCase()}`; | ||
}, | ||
|
||
// Compiles an `insert` query, allowing for multiple | ||
// inserts using a single query statement. | ||
insert() { | ||
const sql = QueryCompiler.prototype.insert.apply(this, arguments); | ||
if (sql === '') return sql; | ||
this._slightReturn(); | ||
return { | ||
sql, | ||
}; | ||
}, | ||
|
||
// Compiles an `update` query, warning on unsupported returning | ||
update() { | ||
const sql = QueryCompiler.prototype.update.apply(this, arguments); | ||
this._slightReturn(); | ||
return { | ||
sql, | ||
}; | ||
}, | ||
|
||
// Compiles an `delete` query, warning on unsupported returning | ||
del() { | ||
const sql = QueryCompiler.prototype.del.apply(this, arguments); | ||
this._slightReturn(); | ||
return { | ||
sql, | ||
}; | ||
}, | ||
|
||
// simple: if trying to return, warn | ||
_slightReturn(){ | ||
if (this.single.isReturning) { | ||
helpers.warn('insert/update/delete returning is not supported by redshift dialect'); | ||
} | ||
}, | ||
|
||
forUpdate() { | ||
helpers.warn('table lock is not supported by redshift dialect'); | ||
return ''; | ||
}, | ||
|
||
forShare() { | ||
helpers.warn('lock for share is not supported by redshift dialect'); | ||
return ''; | ||
}, | ||
|
||
// Compiles a columnInfo query | ||
columnInfo() { | ||
const column = this.single.columnInfo; | ||
|
||
let sql = 'select * from information_schema.columns where table_name = ? and table_catalog = ?'; | ||
const bindings = [this.single.table.toLowerCase(), this.client.database().toLowerCase()]; | ||
|
||
if (this.single.schema) { | ||
sql += ' and table_schema = ?'; | ||
bindings.push(this.single.schema); | ||
} else { | ||
sql += ' and table_schema = current_schema()'; | ||
} | ||
|
||
return { | ||
sql, | ||
bindings, | ||
output(resp) { | ||
const out = reduce(resp.rows, function(columns, val) { | ||
columns[val.column_name] = { | ||
type: val.data_type, | ||
maxLength: val.character_maximum_length, | ||
nullable: (val.is_nullable === 'YES'), | ||
defaultValue: val.column_default | ||
}; | ||
return columns; | ||
}, {}); | ||
return column && out[column] || out; | ||
} | ||
}; | ||
} | ||
}) | ||
|
||
export default QueryCompiler_Redshift; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
"use strict"; | ||
|
||
import inherits from 'inherits'; | ||
import { warn } from '../../../helpers'; | ||
import ColumnBuilder from '../../../schema/columnbuilder'; | ||
|
||
function ColumnBuilder_Redshift() { | ||
ColumnBuilder.apply(this, arguments); | ||
} | ||
inherits(ColumnBuilder_Redshift, ColumnBuilder); | ||
|
||
// primary needs to set not null on non-preexisting columns, or fail | ||
ColumnBuilder_Redshift.prototype.primary = function () { | ||
this.notNullable(); | ||
return ColumnBuilder.prototype.primary.apply(this, arguments); | ||
}; | ||
|
||
ColumnBuilder_Redshift.prototype.index = function () { | ||
warn('Redshift does not support the creation of indexes.'); | ||
return this; | ||
} | ||
|
||
export default ColumnBuilder_Redshift; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
|
||
// Redshift Column Compiler | ||
// ------- | ||
|
||
import inherits from 'inherits'; | ||
import ColumnCompiler_PG from '../../postgres/schema/columncompiler'; | ||
|
||
import { assign } from 'lodash' | ||
|
||
function ColumnCompiler_Redshift() { | ||
ColumnCompiler_PG.apply(this, arguments); | ||
} | ||
inherits(ColumnCompiler_Redshift, ColumnCompiler_PG); | ||
|
||
assign(ColumnCompiler_Redshift.prototype, { | ||
// Types: | ||
// ------ | ||
bigincrements: 'bigint identity(1,1) primary key not null', | ||
binary: 'varchar(max)', | ||
bit(column) { | ||
return column.length !== false ? `char(${column.length})` : 'char(1)'; | ||
}, | ||
blob: 'varchar(max)', | ||
enu: 'varchar(255)', | ||
enum: 'varchar(255)', | ||
increments: 'integer identity(1,1) primary key not null', | ||
json: 'varchar(max)', | ||
jsonb: 'varchar(max)', | ||
longblob: 'varchar(max)', | ||
mediumblob: 'varchar(16777218)', | ||
set: 'text', | ||
text: 'varchar(max)', | ||
datetime(without) { | ||
return without ? 'timestamp' : 'timestamptz'; | ||
}, | ||
timestamp(without) { | ||
return without ? 'timestamp' : 'timestamptz'; | ||
}, | ||
tinyblob: 'varchar(256)', | ||
uuid: 'char(36)', | ||
varbinary: 'varchar(max)', | ||
bigint: 'bigint', | ||
bool: 'boolean', | ||
double: 'double precision', | ||
floating: 'real', | ||
smallint: 'smallint', | ||
tinyint: 'smallint', | ||
|
||
// Modifiers: | ||
// ------ | ||
comment(comment) { | ||
this.pushAdditional(function() { | ||
this.pushQuery(`comment on column ${this.tableCompiler.tableName()}.` + | ||
this.formatter.wrap(this.args[0]) + " is " + (comment ? `'${comment}'` : 'NULL')); | ||
}, comment); | ||
} | ||
}) | ||
|
||
export default ColumnCompiler_Redshift; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/* eslint max-len: 0 */ | ||
|
||
// Redshift Table Builder & Compiler | ||
// ------- | ||
|
||
import inherits from 'inherits'; | ||
import SchemaCompiler_PG from '../../postgres/schema/compiler'; | ||
|
||
function SchemaCompiler_Redshift() { | ||
SchemaCompiler_PG.apply(this, arguments); | ||
} | ||
inherits(SchemaCompiler_Redshift, SchemaCompiler_PG); | ||
|
||
export default SchemaCompiler_Redshift; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
/* eslint max-len: 0 */ | ||
|
||
// Redshift Table Builder & Compiler | ||
// ------- | ||
|
||
import { warn } from '../../../helpers'; | ||
import inherits from 'inherits'; | ||
import { has } from 'lodash'; | ||
import TableCompiler_PG from '../../postgres/schema/tablecompiler'; | ||
|
||
function TableCompiler_Redshift() { | ||
TableCompiler_PG.apply(this, arguments); | ||
} | ||
inherits(TableCompiler_Redshift, TableCompiler_PG); | ||
|
||
TableCompiler_Redshift.prototype.index = function(columns, indexName, indexType) { | ||
warn('Redshift does not support the creation of indexes.'); | ||
}; | ||
|
||
TableCompiler_Redshift.prototype.dropIndex = function(columns, indexName) { | ||
warn('Redshift does not support the deletion of indexes.'); | ||
}; | ||
|
||
// TODO: have to disable setting not null on columns that already exist... | ||
|
||
// Adds the "create" query to the query sequence. | ||
TableCompiler_Redshift.prototype.createQuery = function(columns, ifNot) { | ||
const createStatement = ifNot ? 'create table if not exists ' : 'create table '; | ||
let sql = createStatement + this.tableName() + ' (' + columns.sql.join(', ') + ')'; | ||
if (this.single.inherits) sql += ` like (${this.formatter.wrap(this.single.inherits)})`; | ||
this.pushQuery({ | ||
sql, | ||
bindings: columns.bindings | ||
}); | ||
const hasComment = has(this.single, 'comment'); | ||
if (hasComment) this.comment(this.single.comment); | ||
}; | ||
|
||
TableCompiler_Redshift.prototype.primary = function(columns, constraintName) { | ||
const self = this; | ||
constraintName = constraintName ? self.formatter.wrap(constraintName) : self.formatter.wrap(`${this.tableNameRaw}_pkey`); | ||
if (columns.constructor !== Array){ | ||
columns = [columns]; | ||
} | ||
const thiscolumns = self.grouped.columns; | ||
|
||
if (thiscolumns) { | ||
for (let i = 0; i < columns.length; i++){ | ||
let exists = thiscolumns.find(tcb => tcb.grouping === "columns" && | ||
tcb.builder && | ||
tcb.builder._method === "add" && | ||
tcb.builder._args && | ||
tcb.builder._args.indexOf(columns[i]) > -1); | ||
if (exists) { | ||
exists = exists.builder; | ||
} | ||
const nullable = !(exists && | ||
exists._modifiers && | ||
exists._modifiers["nullable"] && | ||
exists._modifiers["nullable"][0] === false); | ||
if (nullable){ | ||
if (exists){ | ||
return warn("Redshift does not allow primary keys to contain nullable columns."); | ||
} else { | ||
return warn("Redshift does not allow primary keys to contain nonexistent columns."); | ||
} | ||
} | ||
} | ||
} | ||
return self.pushQuery(`alter table ${self.tableName()} add constraint ${constraintName} primary key (${self.formatter.columnize(columns)})`); | ||
}; | ||
|
||
export default TableCompiler_Redshift; |
Oops, something went wrong.