mirror of
https://github.com/knex/knex.git
synced 2025-12-01 01:16:18 +00:00
Implement partial index support (#4768)
This commit is contained in:
parent
821e8494fd
commit
ace439d5c7
@ -219,14 +219,22 @@ class TableCompiler_MSSQL extends TableCompiler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
index(columns, indexName) {
|
index(columns, indexName, options) {
|
||||||
indexName = indexName
|
indexName = indexName
|
||||||
? this.formatter.wrap(indexName)
|
? this.formatter.wrap(indexName)
|
||||||
: this._indexCommand('index', this.tableNameRaw, columns);
|
: this._indexCommand('index', this.tableNameRaw, columns);
|
||||||
|
|
||||||
|
let predicate;
|
||||||
|
if (isObject(options)) {
|
||||||
|
({ predicate } = options);
|
||||||
|
}
|
||||||
|
const predicateQuery = predicate
|
||||||
|
? ' ' + this.client.queryCompiler(predicate).where()
|
||||||
|
: '';
|
||||||
this.pushQuery(
|
this.pushQuery(
|
||||||
`CREATE INDEX ${indexName} ON ${this.tableName()} (${this.formatter.columnize(
|
`CREATE INDEX ${indexName} ON ${this.tableName()} (${this.formatter.columnize(
|
||||||
columns
|
columns
|
||||||
)})`
|
)})${predicateQuery}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
// MySQL Table Builder & Compiler
|
// MySQL Table Builder & Compiler
|
||||||
// -------
|
// -------
|
||||||
const TableCompiler = require('../../../schema/tablecompiler');
|
const TableCompiler = require('../../../schema/tablecompiler');
|
||||||
const { isObject } = require('../../../util/is');
|
const { isObject, isString } = require('../../../util/is');
|
||||||
|
|
||||||
// Table Compiler
|
// Table Compiler
|
||||||
// ------
|
// ------
|
||||||
@ -211,10 +211,14 @@ class TableCompiler_MySQL extends TableCompiler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
index(columns, indexName, indexType) {
|
index(columns, indexName, options) {
|
||||||
let storageEngineIndexType;
|
let storageEngineIndexType;
|
||||||
if (isObject(indexName)) {
|
let indexType;
|
||||||
({ indexName, storageEngineIndexType } = indexName);
|
|
||||||
|
if (isString(options)) {
|
||||||
|
indexType = options;
|
||||||
|
} else if (isObject(options)) {
|
||||||
|
({ indexType, storageEngineIndexType } = options);
|
||||||
}
|
}
|
||||||
|
|
||||||
indexName = indexName
|
indexName = indexName
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
const has = require('lodash/has');
|
const has = require('lodash/has');
|
||||||
const TableCompiler = require('../../../schema/tablecompiler');
|
const TableCompiler = require('../../../schema/tablecompiler');
|
||||||
const { isObject } = require('../../../util/is');
|
const { isObject, isString } = require('../../../util/is');
|
||||||
|
|
||||||
class TableCompiler_PG extends TableCompiler {
|
class TableCompiler_PG extends TableCompiler {
|
||||||
constructor(client, tableBuilder) {
|
constructor(client, tableBuilder) {
|
||||||
@ -161,17 +161,32 @@ class TableCompiler_PG extends TableCompiler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
index(columns, indexName, indexType) {
|
index(columns, indexName, options) {
|
||||||
indexName = indexName
|
indexName = indexName
|
||||||
? this.formatter.wrap(indexName)
|
? this.formatter.wrap(indexName)
|
||||||
: this._indexCommand('index', this.tableNameRaw, columns);
|
: this._indexCommand('index', this.tableNameRaw, columns);
|
||||||
|
|
||||||
|
let predicate;
|
||||||
|
let indexType;
|
||||||
|
|
||||||
|
if (isString(options)) {
|
||||||
|
indexType = options;
|
||||||
|
} else if (isObject(options)) {
|
||||||
|
({ indexType, predicate } = options);
|
||||||
|
}
|
||||||
|
|
||||||
|
const predicateQuery = predicate
|
||||||
|
? ' ' + this.client.queryCompiler(predicate).where()
|
||||||
|
: '';
|
||||||
|
|
||||||
this.pushQuery(
|
this.pushQuery(
|
||||||
`create index ${indexName} on ${this.tableName()}${
|
`create index ${indexName} on ${this.tableName()}${
|
||||||
(indexType && ` using ${indexType}`) || ''
|
(indexType && ` using ${indexType}`) || ''
|
||||||
}` +
|
}` +
|
||||||
' (' +
|
' (' +
|
||||||
this.formatter.columnize(columns) +
|
this.formatter.columnize(columns) +
|
||||||
')'
|
')' +
|
||||||
|
`${predicateQuery}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ class TableCompiler_Redshift extends TableCompiler_PG {
|
|||||||
super(...arguments);
|
super(...arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
index(columns, indexName, indexType) {
|
index(columns, indexName, options) {
|
||||||
this.client.logger.warn(
|
this.client.logger.warn(
|
||||||
'Redshift does not support the creation of indexes.'
|
'Redshift does not support the creation of indexes.'
|
||||||
);
|
);
|
||||||
|
|||||||
@ -145,13 +145,21 @@ class TableCompiler_SQLite3 extends TableCompiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compile a plain index key command.
|
// Compile a plain index key command.
|
||||||
index(columns, indexName) {
|
index(columns, indexName, options) {
|
||||||
indexName = indexName
|
indexName = indexName
|
||||||
? this.formatter.wrap(indexName)
|
? this.formatter.wrap(indexName)
|
||||||
: this._indexCommand('index', this.tableNameRaw, columns);
|
: this._indexCommand('index', this.tableNameRaw, columns);
|
||||||
columns = this.formatter.columnize(columns);
|
columns = this.formatter.columnize(columns);
|
||||||
|
|
||||||
|
let predicate;
|
||||||
|
if (isObject(options)) {
|
||||||
|
({ predicate } = options);
|
||||||
|
}
|
||||||
|
const predicateQuery = predicate
|
||||||
|
? ' ' + this.client.queryCompiler(predicate).where()
|
||||||
|
: '';
|
||||||
this.pushQuery(
|
this.pushQuery(
|
||||||
`create index ${indexName} on ${this.tableName()} (${columns})`
|
`create index ${indexName} on ${this.tableName()} (${columns})${predicateQuery}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -63,11 +63,10 @@ describe('Schema', () => {
|
|||||||
}
|
}
|
||||||
await knex.schema
|
await knex.schema
|
||||||
.alterTable('alter_table', (table) => {
|
.alterTable('alter_table', (table) => {
|
||||||
table.index(
|
table.index(['column_string', 'column_datetime'], 'idx_1', {
|
||||||
['column_string', 'column_datetime'],
|
indexType: 'FULLTEXT',
|
||||||
{ indexName: 'idx_1', storageEngineIndexType: 'BTREE' },
|
storageEngineIndexType: 'BTREE',
|
||||||
'FULLTEXT'
|
});
|
||||||
);
|
|
||||||
table.unique('column_notNullable', {
|
table.unique('column_notNullable', {
|
||||||
indexName: 'idx_2',
|
indexName: 'idx_2',
|
||||||
storageEngineIndexType: 'HASH',
|
storageEngineIndexType: 'HASH',
|
||||||
|
|||||||
@ -1432,6 +1432,43 @@ describe('Schema (misc)', () => {
|
|||||||
knex.schema.table('test_table_one', (t) => {
|
knex.schema.table('test_table_one', (t) => {
|
||||||
t.dropIndex('first_name');
|
t.dropIndex('first_name');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
describe('supports partial indexes - postgres, sqlite, and mssql', function () {
|
||||||
|
it('allows creating indexes with predicate', async function () {
|
||||||
|
if (!(isPostgreSQL(knex) || isMssql(knex) || isSQLite(knex))) {
|
||||||
|
return this.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
await knex.schema.table('test_table_one', function (t) {
|
||||||
|
t.index('first_name', 'first_name_idx', {
|
||||||
|
predicate: knex.whereRaw("first_name = 'brandon'"),
|
||||||
|
});
|
||||||
|
t.index('phone', 'phone_idx', {
|
||||||
|
predicate: knex.whereNotNull('phone'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('actually stores the predicate in the Postgres server', async function () {
|
||||||
|
if (!isPostgreSQL(knex)) {
|
||||||
|
return this.skip();
|
||||||
|
}
|
||||||
|
await knex.schema.table('test_table_one', function (t) {
|
||||||
|
t.index('phone', 'phone_idx_2', {
|
||||||
|
predicate: knex.whereNotNull('phone'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const results = await knex
|
||||||
|
.from('pg_class')
|
||||||
|
.innerJoin('pg_index', 'pg_index.indexrelid', 'pg_class.oid')
|
||||||
|
.where({
|
||||||
|
relname: 'phone_idx_2',
|
||||||
|
indisvalid: true,
|
||||||
|
})
|
||||||
|
.whereNotNull('indpred');
|
||||||
|
expect(results).to.not.be.empty;
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('hasTable', () => {
|
describe('hasTable', () => {
|
||||||
|
|||||||
@ -554,6 +554,36 @@ describe('MSSQL SchemaBuilder', function () {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('test adding index with a predicate', function () {
|
||||||
|
tableSql = client
|
||||||
|
.schemaBuilder()
|
||||||
|
.table('users', function (table) {
|
||||||
|
table.index(['foo', 'bar'], 'baz', {
|
||||||
|
predicate: client.queryBuilder().whereRaw('email = "foo@bar"'),
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.toSQL();
|
||||||
|
equal(1, tableSql.length);
|
||||||
|
expect(tableSql[0].sql).to.equal(
|
||||||
|
'CREATE INDEX [baz] ON [users] ([foo], [bar]) where email = "foo@bar"'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test adding index with a where not null predicate', function () {
|
||||||
|
tableSql = client
|
||||||
|
.schemaBuilder()
|
||||||
|
.table('users', function (table) {
|
||||||
|
table.index(['foo', 'bar'], 'baz', {
|
||||||
|
predicate: client.queryBuilder().whereNotNull('email'),
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.toSQL();
|
||||||
|
equal(1, tableSql.length);
|
||||||
|
expect(tableSql[0].sql).to.equal(
|
||||||
|
'CREATE INDEX [baz] ON [users] ([foo], [bar]) where [email] is not null'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('test adding foreign key', function () {
|
it('test adding foreign key', function () {
|
||||||
tableSql = client
|
tableSql = client
|
||||||
.schemaBuilder()
|
.schemaBuilder()
|
||||||
|
|||||||
@ -489,11 +489,10 @@ module.exports = function (dialect) {
|
|||||||
tableSql = client
|
tableSql = client
|
||||||
.schemaBuilder()
|
.schemaBuilder()
|
||||||
.table('users', function () {
|
.table('users', function () {
|
||||||
this.index(
|
this.index(['foo', 'bar'], 'baz', {
|
||||||
['foo', 'bar'],
|
indexType: 'UNIQUE',
|
||||||
{ indexName: 'baz', storageEngineIndexType: 'BTREE' },
|
storageEngineIndexType: 'BTREE',
|
||||||
'UNIQUE'
|
});
|
||||||
);
|
|
||||||
})
|
})
|
||||||
.toSQL();
|
.toSQL();
|
||||||
|
|
||||||
|
|||||||
@ -791,7 +791,7 @@ describe('PostgreSQL SchemaBuilder', function () {
|
|||||||
tableSql = client
|
tableSql = client
|
||||||
.schemaBuilder()
|
.schemaBuilder()
|
||||||
.table('users', function (table) {
|
.table('users', function (table) {
|
||||||
table.string('name').index(null, 'gist');
|
table.string('name').index(null, { indexType: 'gist' });
|
||||||
})
|
})
|
||||||
.toSQL();
|
.toSQL();
|
||||||
equal(2, tableSql.length);
|
equal(2, tableSql.length);
|
||||||
@ -803,6 +803,52 @@ describe('PostgreSQL SchemaBuilder', function () {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('adding index with a predicate', function () {
|
||||||
|
tableSql = client
|
||||||
|
.schemaBuilder()
|
||||||
|
.table('users', function (table) {
|
||||||
|
table.index(['foo', 'bar'], 'baz', {
|
||||||
|
predicate: client.queryBuilder().whereRaw('email = "foo@bar"'),
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.toSQL();
|
||||||
|
equal(1, tableSql.length);
|
||||||
|
expect(tableSql[0].sql).to.equal(
|
||||||
|
'create index "baz" on "users" ("foo", "bar") where email = "foo@bar"'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adding index with an index type and a predicate', function () {
|
||||||
|
tableSql = client
|
||||||
|
.schemaBuilder()
|
||||||
|
.table('users', function (table) {
|
||||||
|
table.index(['foo', 'bar'], 'baz', {
|
||||||
|
indexType: 'gist',
|
||||||
|
predicate: client.queryBuilder().whereRaw('email = "foo@bar"'),
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.toSQL();
|
||||||
|
equal(1, tableSql.length);
|
||||||
|
expect(tableSql[0].sql).to.equal(
|
||||||
|
'create index "baz" on "users" using gist ("foo", "bar") where email = "foo@bar"'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adding index with a where not null predicate', function () {
|
||||||
|
tableSql = client
|
||||||
|
.schemaBuilder()
|
||||||
|
.table('users', function (table) {
|
||||||
|
table.index(['foo', 'bar'], 'baz', {
|
||||||
|
predicate: client.queryBuilder().whereNotNull('email'),
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.toSQL();
|
||||||
|
equal(1, tableSql.length);
|
||||||
|
expect(tableSql[0].sql).to.equal(
|
||||||
|
'create index "baz" on "users" ("foo", "bar") where "email" is not null'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('adding incrementing id', function () {
|
it('adding incrementing id', function () {
|
||||||
tableSql = client
|
tableSql = client
|
||||||
.schemaBuilder()
|
.schemaBuilder()
|
||||||
|
|||||||
@ -466,6 +466,36 @@ describe('SQLite SchemaBuilder', function () {
|
|||||||
equal(tableSql[0].sql, 'create index `baz` on `users` (`foo`, `bar`)');
|
equal(tableSql[0].sql, 'create index `baz` on `users` (`foo`, `bar`)');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('adding index with a predicate', function () {
|
||||||
|
tableSql = client
|
||||||
|
.schemaBuilder()
|
||||||
|
.table('users', function (table) {
|
||||||
|
table.index(['foo', 'bar'], 'baz', {
|
||||||
|
predicate: client.queryBuilder().whereRaw('email = "foo@bar"'),
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.toSQL();
|
||||||
|
equal(1, tableSql.length);
|
||||||
|
expect(tableSql[0].sql).to.equal(
|
||||||
|
'create index `baz` on `users` (`foo`, `bar`) where email = "foo@bar"'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adding index with a where not null predicate', function () {
|
||||||
|
tableSql = client
|
||||||
|
.schemaBuilder()
|
||||||
|
.table('users', function (table) {
|
||||||
|
table.index(['foo', 'bar'], 'baz', {
|
||||||
|
predicate: client.queryBuilder().whereNotNull('email'),
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.toSQL();
|
||||||
|
equal(1, tableSql.length);
|
||||||
|
expect(tableSql[0].sql).to.equal(
|
||||||
|
'create index `baz` on `users` (`foo`, `bar`) where `email` is not null'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('adding incrementing id', function () {
|
it('adding incrementing id', function () {
|
||||||
tableSql = client
|
tableSql = client
|
||||||
.schemaBuilder()
|
.schemaBuilder()
|
||||||
|
|||||||
32
types/index.d.ts
vendored
32
types/index.d.ts
vendored
@ -2045,11 +2045,11 @@ export declare namespace Knex {
|
|||||||
index(
|
index(
|
||||||
columnNames: string | readonly (string | Raw)[],
|
columnNames: string | readonly (string | Raw)[],
|
||||||
indexName?: string,
|
indexName?: string,
|
||||||
indexType?: string
|
options?: Readonly<{indexType?: string, storageEngineIndexType?: storageEngineIndexType, predicate?: QueryBuilder}>
|
||||||
): TableBuilder;
|
): TableBuilder;
|
||||||
setNullable(column: string): TableBuilder;
|
setNullable(column: string): TableBuilder;
|
||||||
dropNullable(column: string): TableBuilder;
|
dropNullable(column: string): TableBuilder;
|
||||||
unique(columnNames: readonly (string | Raw)[], options?: Readonly<{indexName?: string, deferrable?: deferrableType}>): TableBuilder;
|
unique(columnNames: readonly (string | Raw)[], options?: Readonly<{indexName?: string, storageEngineIndexType?: string, deferrable?: deferrableType}>): TableBuilder;
|
||||||
/** @deprecated */
|
/** @deprecated */
|
||||||
unique(columnNames: readonly (string | Raw)[], indexName?: string): TableBuilder;
|
unique(columnNames: readonly (string | Raw)[], indexName?: string): TableBuilder;
|
||||||
foreign(column: string, foreignKeyName?: string): ForeignConstraintBuilder;
|
foreign(column: string, foreignKeyName?: string): ForeignConstraintBuilder;
|
||||||
@ -2093,6 +2093,8 @@ export declare namespace Knex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type deferrableType = 'not deferrable' | 'immediate' | 'deferred';
|
type deferrableType = 'not deferrable' | 'immediate' | 'deferred';
|
||||||
|
type storageEngineIndexType = 'hash' | 'btree';
|
||||||
|
|
||||||
interface ColumnBuilder {
|
interface ColumnBuilder {
|
||||||
index(indexName?: string): ColumnBuilder;
|
index(indexName?: string): ColumnBuilder;
|
||||||
primary(options?: Readonly<{constraintName?: string, deferrable?: deferrableType}>): ColumnBuilder;
|
primary(options?: Readonly<{constraintName?: string, deferrable?: deferrableType}>): ColumnBuilder;
|
||||||
@ -2122,7 +2124,31 @@ export declare namespace Knex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface PostgreSqlColumnBuilder extends ColumnBuilder {
|
interface PostgreSqlColumnBuilder extends ColumnBuilder {
|
||||||
index(indexName?: string, indexType?: string): ColumnBuilder;
|
index(
|
||||||
|
indexName?: string,
|
||||||
|
options?: Readonly<{indexType?: string, predicate?: QueryBuilder}>
|
||||||
|
): ColumnBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SqlLiteColumnBuilder extends ColumnBuilder {
|
||||||
|
index(
|
||||||
|
indexName?: string,
|
||||||
|
options?: Readonly<{predicate?: QueryBuilder}>
|
||||||
|
): ColumnBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MsSqlColumnBuilder extends ColumnBuilder {
|
||||||
|
index(
|
||||||
|
indexName?: string,
|
||||||
|
options?: Readonly<{predicate?: QueryBuilder}>
|
||||||
|
): ColumnBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MySqlColumnBuilder extends ColumnBuilder {
|
||||||
|
index(
|
||||||
|
indexName?: string,
|
||||||
|
options?: Readonly<{indexType?: string, storageEngineIndexType?: storageEngineIndexType}>
|
||||||
|
): ColumnBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
// patched ColumnBuilder methods to return ReferencingColumnBuilder with new methods
|
// patched ColumnBuilder methods to return ReferencingColumnBuilder with new methods
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user