Skip to content

Commit

Permalink
refactor(mViews): reorganize operations materialized views (#1086)
Browse files Browse the repository at this point in the history
  • Loading branch information
Shinigami92 committed Apr 11, 2024
1 parent c786932 commit 1aaff43
Show file tree
Hide file tree
Showing 19 changed files with 294 additions and 290 deletions.
27 changes: 16 additions & 11 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@ export type {
DropIndexOptions,
IndexColumn,
} from './operations/indexes';
export type {
AlterMaterializedView,
AlterMaterializedViewOptions,
CreateMaterializedView,
CreateMaterializedViewFn,
CreateMaterializedViewOptions,
DropMaterializedView,
RefreshMaterializedView,
RefreshMaterializedViewFn,
RefreshMaterializedViewOptions,
RenameMaterializedView,
RenameMaterializedViewColumn,
RenameMaterializedViewColumnFn,
RenameMaterializedViewFn,
StorageParameters,
} from './operations/materializedViews';
export type {
AddToOperatorFamily,
AddToOperatorFamilyFn,
Expand Down Expand Up @@ -177,17 +193,6 @@ export type {
RenameViewFn,
ViewOptions,
} from './operations/views';
export type {
AlterMaterializedView,
AlterMaterializedViewOptions,
CreateMaterializedView,
CreateMaterializedViewOptions,
DropMaterializedView,
RefreshMaterializedView,
RefreshMaterializedViewOptions,
RenameMaterializedView,
RenameMaterializedViewColumn,
} from './operations/viewsMaterializedTypes';
export { PgType } from './types';
export type { MigrationBuilder, RunnerOption } from './types';

Expand Down
2 changes: 1 addition & 1 deletion src/migration-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import * as domains from './operations/domains';
import * as extensions from './operations/extensions';
import * as functions from './operations/functions';
import * as indexes from './operations/indexes';
import * as mViews from './operations/materializedViews';
import * as operators from './operations/operators';
import PgLiteral from './operations/PgLiteral';
import * as policies from './operations/policies';
Expand All @@ -25,7 +26,6 @@ import * as tables from './operations/tables';
import * as triggers from './operations/triggers';
import * as types from './operations/types';
import * as views from './operations/views';
import * as mViews from './operations/viewsMaterialized';
import type { DB, Logger, MigrationBuilder, MigrationOptions } from './types';
import { createSchemalize } from './utils';

Expand Down
64 changes: 64 additions & 0 deletions src/operations/materializedViews/alterMaterializedView.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import type { MigrationOptions } from '../../types';
import { formatLines } from '../../utils';
import type { Name, Nullable } from '../generalTypes';
import type { StorageParameters } from './shared';
import { storageParameterStr } from './shared';

export interface AlterMaterializedViewOptions {
cluster?: null | false | string;

extension?: string;

storageParameters?: Nullable<StorageParameters>;
}

export type AlterMaterializedView = (
viewName: Name,
options: AlterMaterializedViewOptions
) => string | string[];

export function alterMaterializedView(
mOptions: MigrationOptions
): AlterMaterializedView {
const _alter: AlterMaterializedView = (viewName, options) => {
const { cluster, extension, storageParameters = {} } = options;

const clauses: string[] = [];

if (cluster !== undefined) {
if (cluster) {
clauses.push(`CLUSTER ON ${mOptions.literal(cluster)}`);
} else {
clauses.push('SET WITHOUT CLUSTER');
}
}

if (extension) {
clauses.push(`DEPENDS ON EXTENSION ${mOptions.literal(extension)}`);
}

const withOptions = Object.keys(storageParameters)
.filter((key) => storageParameters[key] !== null)
.map(storageParameterStr(storageParameters))
.join(', ');

if (withOptions) {
clauses.push(`SET (${withOptions})`);
}

const resetOptions = Object.keys(storageParameters)
.filter((key) => storageParameters[key] === null)
.join(', ');

if (resetOptions) {
clauses.push(`RESET (${resetOptions})`);
}

const clausesStr = formatLines(clauses);
const viewNameStr = mOptions.literal(viewName);

return `ALTER MATERIALIZED VIEW ${viewNameStr}\n${clausesStr};`;
};

return _alter;
}
61 changes: 61 additions & 0 deletions src/operations/materializedViews/createMaterializedView.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { MigrationOptions } from '../../types';
import type { DropOptions, IfNotExistsOption, Name } from '../generalTypes';
import { dropMaterializedView } from './dropMaterializedView';
import type { StorageParameters } from './shared';
import { dataClause, storageParameterStr } from './shared';

export interface CreateMaterializedViewOptions extends IfNotExistsOption {
columns?: string | string[];

tablespace?: string;

storageParameters?: StorageParameters;

data?: boolean;
}

export type CreateMaterializedViewFn = (
viewName: Name,
options: CreateMaterializedViewOptions & DropOptions,
definition: string
) => string | string[];

export type CreateMaterializedView = CreateMaterializedViewFn & {
reverse: CreateMaterializedViewFn;
};

export function createMaterializedView(
mOptions: MigrationOptions
): CreateMaterializedView {
const _create: CreateMaterializedView = (viewName, options, definition) => {
const {
ifNotExists,
columns = [],
tablespace,
storageParameters = {},
data,
} = options;

const columnNames = (Array.isArray(columns) ? columns : [columns])
.map(mOptions.literal)
.join(', ');
const withOptions = Object.keys(storageParameters)
.map(storageParameterStr(storageParameters))
.join(', ');

const ifNotExistsStr = ifNotExists ? ' IF NOT EXISTS' : '';
const columnsStr = columnNames ? `(${columnNames})` : '';
const withOptionsStr = withOptions ? ` WITH (${withOptions})` : '';
const tablespaceStr = tablespace
? ` TABLESPACE ${mOptions.literal(tablespace)}`
: '';
const dataStr = dataClause(data);
const viewNameStr = mOptions.literal(viewName);

return `CREATE MATERIALIZED VIEW${ifNotExistsStr} ${viewNameStr}${columnsStr}${withOptionsStr}${tablespaceStr} AS ${definition}${dataStr};`;
};

_create.reverse = dropMaterializedView(mOptions);

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

export type DropMaterializedView = (
viewName: Name,
options?: DropOptions
) => string | string[];

export function dropMaterializedView(
mOptions: MigrationOptions
): DropMaterializedView {
const _drop: DropMaterializedView = (viewName, options = {}) => {
const { ifExists, cascade } = options;

const ifExistsStr = ifExists ? ' IF EXISTS' : '';
const cascadeStr = cascade ? ' CASCADE' : '';
const viewNameStr = mOptions.literal(viewName);

return `DROP MATERIALIZED VIEW${ifExistsStr} ${viewNameStr}${cascadeStr};`;
};

return _drop;
}
7 changes: 7 additions & 0 deletions src/operations/materializedViews/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export * from './alterMaterializedView';
export * from './createMaterializedView';
export * from './dropMaterializedView';
export * from './refreshMaterializedView';
export * from './renameMaterializedView';
export * from './renameMaterializedViewColumn';
export * from './shared';
36 changes: 36 additions & 0 deletions src/operations/materializedViews/refreshMaterializedView.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { MigrationOptions } from '../../types';
import type { Name } from '../generalTypes';
import { dataClause } from './shared';

export interface RefreshMaterializedViewOptions {
concurrently?: boolean;

data?: boolean;
}

export type RefreshMaterializedViewFn = (
viewName: Name,
options?: RefreshMaterializedViewOptions
) => string | string[];

export type RefreshMaterializedView = RefreshMaterializedViewFn & {
reverse: RefreshMaterializedViewFn;
};

export function refreshMaterializedView(
mOptions: MigrationOptions
): RefreshMaterializedView {
const _refresh: RefreshMaterializedView = (viewName, options = {}) => {
const { concurrently, data } = options;

const concurrentlyStr = concurrently ? ' CONCURRENTLY' : '';
const dataStr = dataClause(data);
const viewNameStr = mOptions.literal(viewName);

return `REFRESH MATERIALIZED VIEW${concurrentlyStr} ${viewNameStr}${dataStr};`;
};

_refresh.reverse = _refresh;

return _refresh;
}
26 changes: 26 additions & 0 deletions src/operations/materializedViews/renameMaterializedView.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { MigrationOptions } from '../../types';
import type { Name } from '../generalTypes';

export type RenameMaterializedViewFn = (
viewName: Name,
newViewName: Name
) => string | string[];

export type RenameMaterializedView = RenameMaterializedViewFn & {
reverse: RenameMaterializedViewFn;
};

export function renameMaterializedView(
mOptions: MigrationOptions
): RenameMaterializedView {
const _rename: RenameMaterializedView = (viewName, newViewName) => {
const viewNameStr = mOptions.literal(viewName);
const newViewNameStr = mOptions.literal(newViewName);

return `ALTER MATERIALIZED VIEW ${viewNameStr} RENAME TO ${newViewNameStr};`;
};

_rename.reverse = (viewName, newViewName) => _rename(newViewName, viewName);

return _rename;
}
33 changes: 33 additions & 0 deletions src/operations/materializedViews/renameMaterializedViewColumn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { MigrationOptions } from '../../types';
import type { Name } from '../generalTypes';

export type RenameMaterializedViewColumnFn = (
viewName: Name,
columnName: string,
newColumnName: string
) => string | string[];

export type RenameMaterializedViewColumn = RenameMaterializedViewColumnFn & {
reverse: RenameMaterializedViewColumnFn;
};

export function renameMaterializedViewColumn(
mOptions: MigrationOptions
): RenameMaterializedViewColumn {
const _rename: RenameMaterializedViewColumn = (
viewName,
columnName,
newColumnName
) => {
const viewNameStr = mOptions.literal(viewName);
const columnNameStr = mOptions.literal(columnName);
const newColumnNameStr = mOptions.literal(newColumnName);

return `ALTER MATERIALIZED VIEW ${viewNameStr} RENAME COLUMN ${columnNameStr} TO ${newColumnNameStr};`;
};

_rename.reverse = (viewName, columnName, newColumnName) =>
_rename(viewName, newColumnName, columnName);

return _rename;
}
20 changes: 20 additions & 0 deletions src/operations/materializedViews/shared.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { Nullable } from '../generalTypes';

export type StorageParameters = { [key: string]: boolean | number };

export function dataClause(data?: boolean): string {
return data !== undefined ? ` WITH${data ? '' : ' NO'} DATA` : '';
}

export function storageParameterStr<
TStorageParameters extends Nullable<StorageParameters>,
TKey extends keyof TStorageParameters,
>(storageParameters: TStorageParameters): (key: TKey) => string {
return (key) => {
const value =
storageParameters[key] === true ? '' : ` = ${storageParameters[key]}`;

// @ts-expect-error: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'. ts(2731)
return `${key}${value}`;
};
}

0 comments on commit 1aaff43

Please sign in to comment.