Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(indexes): reorganize operations indexes #1075

Merged
merged 1 commit into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ export type {
} from './operations/generalTypes';
export type {
CreateIndex,
CreateIndexFn,
CreateIndexOptions,
DropIndex,
DropIndexOptions,
} from './operations/indexesTypes';
IndexColumn,
} from './operations/indexes';
export type {
AddToOperatorFamily,
CreateOperator,
Expand Down
154 changes: 0 additions & 154 deletions src/operations/indexes.ts

This file was deleted.

101 changes: 101 additions & 0 deletions src/operations/indexes/createIndex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import type { MigrationOptions } from '../../types';
import type { Name } from '../generalTypes';
import type { DropIndexOptions } from './dropIndex';
import { dropIndex } from './dropIndex';
import type { IndexColumn } from './shared';
import { generateColumnsString, generateIndexName } from './shared';

export interface CreateIndexOptions {
name?: string;

unique?: boolean;

where?: string;

concurrently?: boolean;

ifNotExists?: boolean;

/**
* @deprecated should be parameter of IndexColumn
*/
opclass?: Name;

method?: 'btree' | 'hash' | 'gist' | 'spgist' | 'gin';

include?: string | string[];
}

export type CreateIndexFn = (
tableName: Name,
columns: string | Array<string | IndexColumn>,
options?: CreateIndexOptions & DropIndexOptions
) => string | string[];

export type CreateIndex = CreateIndexFn & { reverse: CreateIndexFn };

export function createIndex(mOptions: MigrationOptions): CreateIndex {
const _create: CreateIndex = (tableName, rawColumns, options = {}) => {
/*
columns - the column, columns, or expression to create the index on

Options
name - explicitly specify the name for the index
unique - is this a unique index
where - where clause
concurrently -
ifNotExists - optionally create index
options.method - [ btree | hash | gist | spgist | gin ]
*/
const columns = Array.isArray(rawColumns)
? rawColumns.slice()
: [rawColumns];

if (options.opclass) {
mOptions.logger.warn(
"Using opclass is deprecated. You should use it as part of column definition e.g. pgm.createIndex('table', [['column', 'opclass', 'ASC']])"
);
const lastIndex = columns.length - 1;
const lastColumn = columns[lastIndex];

if (typeof lastColumn === 'string') {
columns[lastIndex] = { name: lastColumn, opclass: options.opclass };
} else if (lastColumn.opclass) {
throw new Error(
"There is already defined opclass on column, can't override it with global one"
);
} else {
columns[lastIndex] = { ...lastColumn, opclass: options.opclass };
}
}

const indexName = generateIndexName(
typeof tableName === 'object' ? tableName.name : tableName,
columns,
options,
mOptions.schemalize
);
const columnsString = generateColumnsString(columns, mOptions);
const unique = options.unique ? ' UNIQUE' : '';
const concurrently = options.concurrently ? ' CONCURRENTLY' : '';
const ifNotExistsStr = options.ifNotExists ? ' IF NOT EXISTS' : '';
const method = options.method ? ` USING ${options.method}` : '';
const where = options.where ? ` WHERE ${options.where}` : '';
const include = options.include
? ` INCLUDE (${(Array.isArray(options.include)
? options.include
: [options.include]
)
.map(mOptions.literal)
.join(', ')})`
: '';
const indexNameStr = mOptions.literal(indexName);
const tableNameStr = mOptions.literal(tableName);

return `CREATE${unique} INDEX${concurrently}${ifNotExistsStr} ${indexNameStr} ON ${tableNameStr}${method} (${columnsString})${include}${where};`;
};

_create.reverse = dropIndex(mOptions);

return _create;
}
42 changes: 42 additions & 0 deletions src/operations/indexes/dropIndex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { MigrationOptions } from '../../types';
import type { DropOptions, Name } from '../generalTypes';
import type { IndexColumn } from './shared';
import { generateIndexName } from './shared';

export interface DropIndexOptions extends DropOptions {
unique?: boolean;

name?: string;

concurrently?: boolean;
}

export type DropIndex = (
tableName: Name,
columns: string | Array<string | IndexColumn>,
options?: DropIndexOptions
) => string | string[];

export function dropIndex(mOptions: MigrationOptions): DropIndex {
const _drop: DropIndex = (tableName, rawColumns, options = {}) => {
const { concurrently, ifExists, cascade } = options;

const columns = Array.isArray(rawColumns)
? rawColumns.slice()
: [rawColumns];
const concurrentlyStr = concurrently ? ' CONCURRENTLY' : '';
const ifExistsStr = ifExists ? ' IF EXISTS' : '';
const indexName = generateIndexName(
tableName,
columns,
options,
mOptions.schemalize
);
const cascadeStr = cascade ? ' CASCADE' : '';
const indexNameStr = mOptions.literal(indexName);

return `DROP INDEX${concurrentlyStr}${ifExistsStr} ${indexNameStr}${cascadeStr};`;
};

return _drop;
}
3 changes: 3 additions & 0 deletions src/operations/indexes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './createIndex';
export * from './dropIndex';
export * from './shared';
68 changes: 68 additions & 0 deletions src/operations/indexes/shared.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import type { Literal, MigrationOptions } from '../../types';
import type { Name } from '../generalTypes';
import type { CreateIndexOptions } from './createIndex';
import type { DropIndexOptions } from './dropIndex';

export interface IndexColumn {
name: string;

opclass?: Name;

sort?: 'ASC' | 'DESC';
}

export function generateIndexName(
table: Name,
columns: Array<string | IndexColumn>,
options: CreateIndexOptions | DropIndexOptions,
schemalize: Literal
): Name {
if (options.name) {
return typeof table === 'object'
? { schema: table.schema, name: options.name }
: options.name;
}

const cols = columns
.map((col) => schemalize(typeof col === 'string' ? col : col.name))
.join('_');
const uniq = 'unique' in options && options.unique ? '_unique' : '';

return typeof table === 'object'
? {
schema: table.schema,
name: `${table.name}_${cols}${uniq}_index`,
}
: `${table}_${cols}${uniq}_index`;
}

export function generateColumnString(
column: Name,
mOptions: MigrationOptions
): string {
const name = mOptions.schemalize(column);
const isSpecial = /[. ()]/.test(name);

return isSpecial
? name // expression
: mOptions.literal(name); // single column
}

export function generateColumnsString(
columns: Array<string | IndexColumn>,
mOptions: MigrationOptions
): string {
return columns
.map((column) =>
typeof column === 'string'
? generateColumnString(column, mOptions)
: [
generateColumnString(column.name, mOptions),
column.opclass ? mOptions.literal(column.opclass) : undefined,
column.sort,
]
.filter((s) => typeof s === 'string' && s !== '')
.join(' ')
)
.join(', ');
}