mirror of
https://github.com/knex/knex.git
synced 2025-11-29 00:16:20 +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
|
||||
? this.formatter.wrap(indexName)
|
||||
: this._indexCommand('index', this.tableNameRaw, columns);
|
||||
|
||||
let predicate;
|
||||
if (isObject(options)) {
|
||||
({ predicate } = options);
|
||||
}
|
||||
const predicateQuery = predicate
|
||||
? ' ' + this.client.queryCompiler(predicate).where()
|
||||
: '';
|
||||
this.pushQuery(
|
||||
`CREATE INDEX ${indexName} ON ${this.tableName()} (${this.formatter.columnize(
|
||||
columns
|
||||
)})`
|
||||
)})${predicateQuery}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
// MySQL Table Builder & Compiler
|
||||
// -------
|
||||
const TableCompiler = require('../../../schema/tablecompiler');
|
||||
const { isObject } = require('../../../util/is');
|
||||
const { isObject, isString } = require('../../../util/is');
|
||||
|
||||
// Table Compiler
|
||||
// ------
|
||||
@ -211,10 +211,14 @@ class TableCompiler_MySQL extends TableCompiler {
|
||||
);
|
||||
}
|
||||
|
||||
index(columns, indexName, indexType) {
|
||||
index(columns, indexName, options) {
|
||||
let storageEngineIndexType;
|
||||
if (isObject(indexName)) {
|
||||
({ indexName, storageEngineIndexType } = indexName);
|
||||
let indexType;
|
||||
|
||||
if (isString(options)) {
|
||||
indexType = options;
|
||||
} else if (isObject(options)) {
|
||||
({ indexType, storageEngineIndexType } = options);
|
||||
}
|
||||
|
||||
indexName = indexName
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
const has = require('lodash/has');
|
||||
const TableCompiler = require('../../../schema/tablecompiler');
|
||||
const { isObject } = require('../../../util/is');
|
||||
const { isObject, isString } = require('../../../util/is');
|
||||
|
||||
class TableCompiler_PG extends TableCompiler {
|
||||
constructor(client, tableBuilder) {
|
||||
@ -161,17 +161,32 @@ class TableCompiler_PG extends TableCompiler {
|
||||
);
|
||||
}
|
||||
|
||||
index(columns, indexName, indexType) {
|
||||
index(columns, indexName, options) {
|
||||
indexName = indexName
|
||||
? this.formatter.wrap(indexName)
|
||||
: 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(
|
||||
`create index ${indexName} on ${this.tableName()}${
|
||||
(indexType && ` using ${indexType}`) || ''
|
||||
}` +
|
||||
' (' +
|
||||
this.formatter.columnize(columns) +
|
||||
')'
|
||||
')' +
|
||||
`${predicateQuery}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ class TableCompiler_Redshift extends TableCompiler_PG {
|
||||
super(...arguments);
|
||||
}
|
||||
|
||||
index(columns, indexName, indexType) {
|
||||
index(columns, indexName, options) {
|
||||
this.client.logger.warn(
|
||||
'Redshift does not support the creation of indexes.'
|
||||
);
|
||||
|
||||
@ -145,13 +145,21 @@ class TableCompiler_SQLite3 extends TableCompiler {
|
||||
}
|
||||
|
||||
// Compile a plain index key command.
|
||||
index(columns, indexName) {
|
||||
index(columns, indexName, options) {
|
||||
indexName = indexName
|
||||
? this.formatter.wrap(indexName)
|
||||
: this._indexCommand('index', this.tableNameRaw, columns);
|
||||
columns = this.formatter.columnize(columns);
|
||||
|
||||
let predicate;
|
||||
if (isObject(options)) {
|
||||
({ predicate } = options);
|
||||
}
|
||||
const predicateQuery = predicate
|
||||
? ' ' + this.client.queryCompiler(predicate).where()
|
||||
: '';
|
||||
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
|
||||
.alterTable('alter_table', (table) => {
|
||||
table.index(
|
||||
['column_string', 'column_datetime'],
|
||||
{ indexName: 'idx_1', storageEngineIndexType: 'BTREE' },
|
||||
'FULLTEXT'
|
||||
);
|
||||
table.index(['column_string', 'column_datetime'], 'idx_1', {
|
||||
indexType: 'FULLTEXT',
|
||||
storageEngineIndexType: 'BTREE',
|
||||
});
|
||||
table.unique('column_notNullable', {
|
||||
indexName: 'idx_2',
|
||||
storageEngineIndexType: 'HASH',
|
||||
|
||||
@ -1432,6 +1432,43 @@ describe('Schema (misc)', () => {
|
||||
knex.schema.table('test_table_one', (t) => {
|
||||
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', () => {
|
||||
|
||||
@ -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 () {
|
||||
tableSql = client
|
||||
.schemaBuilder()
|
||||
|
||||
@ -489,11 +489,10 @@ module.exports = function (dialect) {
|
||||
tableSql = client
|
||||
.schemaBuilder()
|
||||
.table('users', function () {
|
||||
this.index(
|
||||
['foo', 'bar'],
|
||||
{ indexName: 'baz', storageEngineIndexType: 'BTREE' },
|
||||
'UNIQUE'
|
||||
);
|
||||
this.index(['foo', 'bar'], 'baz', {
|
||||
indexType: 'UNIQUE',
|
||||
storageEngineIndexType: 'BTREE',
|
||||
});
|
||||
})
|
||||
.toSQL();
|
||||
|
||||
|
||||
@ -791,7 +791,7 @@ describe('PostgreSQL SchemaBuilder', function () {
|
||||
tableSql = client
|
||||
.schemaBuilder()
|
||||
.table('users', function (table) {
|
||||
table.string('name').index(null, 'gist');
|
||||
table.string('name').index(null, { indexType: 'gist' });
|
||||
})
|
||||
.toSQL();
|
||||
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 () {
|
||||
tableSql = client
|
||||
.schemaBuilder()
|
||||
|
||||
@ -466,6 +466,36 @@ describe('SQLite SchemaBuilder', function () {
|
||||
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 () {
|
||||
tableSql = client
|
||||
.schemaBuilder()
|
||||
|
||||
32
types/index.d.ts
vendored
32
types/index.d.ts
vendored
@ -2045,11 +2045,11 @@ export declare namespace Knex {
|
||||
index(
|
||||
columnNames: string | readonly (string | Raw)[],
|
||||
indexName?: string,
|
||||
indexType?: string
|
||||
options?: Readonly<{indexType?: string, storageEngineIndexType?: storageEngineIndexType, predicate?: QueryBuilder}>
|
||||
): TableBuilder;
|
||||
setNullable(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 */
|
||||
unique(columnNames: readonly (string | Raw)[], indexName?: string): TableBuilder;
|
||||
foreign(column: string, foreignKeyName?: string): ForeignConstraintBuilder;
|
||||
@ -2093,6 +2093,8 @@ export declare namespace Knex {
|
||||
}
|
||||
|
||||
type deferrableType = 'not deferrable' | 'immediate' | 'deferred';
|
||||
type storageEngineIndexType = 'hash' | 'btree';
|
||||
|
||||
interface ColumnBuilder {
|
||||
index(indexName?: string): ColumnBuilder;
|
||||
primary(options?: Readonly<{constraintName?: string, deferrable?: deferrableType}>): ColumnBuilder;
|
||||
@ -2122,7 +2124,31 @@ export declare namespace Knex {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user