Skip to content

Commit

Permalink
View options (#656)
Browse files Browse the repository at this point in the history
* View options

* Fix migration

* Fix creating SQL
  • Loading branch information
dolezel authored Jun 26, 2020
1 parent 4d3e84d commit 9a6380c
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 24 deletions.
2 changes: 1 addition & 1 deletion docs/mViews.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
- `options` _[object]_ - options:
- `cluster` _[string]_ - optional index name for clustering
- `extension` _[string]_ - optional name of extension view is dependent on
- `storageParameters` _[object]_ - optional key value pairs of [Storage Parameters](https://www.postgresql.org/docs/current/static/sql-createtable.html#SQL-CREATETABLE-STORAGE-PARAMETERS)
- `storageParameters` _[object]_ - optional key value (`null` to reset) pairs of [Storage Parameters](https://www.postgresql.org/docs/current/static/sql-createtable.html#SQL-CREATETABLE-STORAGE-PARAMETERS)

---

Expand Down
4 changes: 3 additions & 1 deletion docs/views.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- `replace` _[boolean]_ - default false
- `recursive` _[boolean]_ - default false
- `columns` _[string or array]_ - use if you want to name columns differently then inferred from definition
- `options` _[object]_ - key value pairs of [View Options](https://www.postgresql.org/docs/current/sql-createview.html)
- `checkOption` _[string]_ - `CASCADED` or `LOCAL`
- `definition` _[string]_ - SQL of SELECT statement

Expand Down Expand Up @@ -40,7 +41,8 @@

- `viewName` _[[Name](migrations.md#type)]_ - name of the view to alter
- `options` _[object]_ - options:
- `checkOption` _[string]_ - `CASCADED`, `LOCAL` or `null` to drop
- `checkOption` _[string]_ - `CASCADED`, `LOCAL` or `null` to reset
- `options` _[object]_ - key value (`null` to reset) pairs of [View Options](https://www.postgresql.org/docs/current/sql-alterview.html)

---

Expand Down
2 changes: 2 additions & 0 deletions src/operations/generalTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import PgLiteral from './PgLiteral'
// eslint-disable-next-line camelcase
export type LiteralUnion<T extends U, U = string> = T | (U & { zz_IGNORE_ME?: never })

export type Nullable<T> = { [P in keyof T]: T[P] | null }

export type Value = null | boolean | string | number | PgLiteral | Value[]

export type Type = string | { type: string }
Expand Down
48 changes: 35 additions & 13 deletions src/operations/views.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { MigrationOptions } from '../types'
import { escapeValue } from '../utils'
import { CreateView, DropView, AlterView, AlterViewColumn, RenameView } from './viewsTypes'
import { CreateView, DropView, AlterView, AlterViewColumn, RenameView, ViewOptions } from './viewsTypes'
import { Nullable } from './generalTypes'

export { CreateView, DropView, AlterView, AlterViewColumn, RenameView }
export { CreateView, DropView, AlterView, AlterViewColumn, RenameView, ViewOptions }

const viewOptionStr = <T extends Nullable<ViewOptions>, K extends keyof T>(options: T) => (key: K) => {
const value = options[key] === true ? '' : ` = ${options[key]}`
return `${key}${value}`
}

export function dropView(mOptions: MigrationOptions) {
const _drop: DropView = (viewName, options = {}) => {
Expand All @@ -16,34 +22,50 @@ export function dropView(mOptions: MigrationOptions) {
}

export function createView(mOptions: MigrationOptions) {
const _create: CreateView = (viewName, options, definition) => {
const { temporary, replace, recursive, columns = [], checkOption } = options
// prettier-ignore
const columnNames = (Array.isArray(columns) ? columns : [columns]).map(mOptions.literal).join(", ");
const _create: CreateView = (viewName, viewOptions, definition) => {
const { temporary, replace, recursive, columns = [], options = {}, checkOption } = viewOptions
const columnNames = (Array.isArray(columns) ? columns : [columns]).map(mOptions.literal).join(', ')
const withOptions = Object.keys(options).map(viewOptionStr(options)).join(', ')

const replaceStr = replace ? ' OR REPLACE' : ''
const temporaryStr = temporary ? ' TEMPORARY' : ''
const recursiveStr = recursive ? ' RECURSIVE' : ''
const columnStr = columnNames ? `(${columnNames})` : ''
const withOptionsStr = withOptions ? ` WITH (${withOptions})` : ''
const checkOptionStr = checkOption ? ` WITH ${checkOption} CHECK OPTION` : ''
const viewNameStr = mOptions.literal(viewName)

return `CREATE${replaceStr}${temporaryStr}${recursiveStr} VIEW ${viewNameStr}${columnStr} AS ${definition}${checkOptionStr};`
return `CREATE${replaceStr}${temporaryStr}${recursiveStr} VIEW ${viewNameStr}${columnStr}${withOptionsStr} AS ${definition}${checkOptionStr};`
}
_create.reverse = dropView(mOptions)
return _create
}

export function alterView(mOptions: MigrationOptions) {
const _alter: AlterView = (viewName, options) => {
const { checkOption } = options
const clauses = []
const _alter: AlterView = (viewName, viewOptions) => {
const { checkOption, options = {} } = viewOptions
if (checkOption !== undefined) {
if (checkOption) {
clauses.push(`SET check_option = ${checkOption}`)
if (options.check_option === undefined) {
options.check_option = checkOption
} else {
clauses.push(`RESET check_option`)
throw new Error('"options.check_option" and "checkOption" can\'t be specified together')
}
}
const clauses = []
const withOptions = Object.keys(options)
.filter((key) => options[key] !== null)
.map(viewOptionStr(options))
.join(', ')
if (withOptions) {
clauses.push(`SET (${withOptions})`)
}
const resetOptions = Object.keys(options)
.filter((key) => options[key] === null)
.join(', ')
if (resetOptions) {
clauses.push(`RESET (${resetOptions})`)
}

return clauses.map((clause) => `ALTER VIEW ${mOptions.literal(viewName)} ${clause};`).join('\n')
}
return _alter
Expand Down
12 changes: 7 additions & 5 deletions src/operations/viewsMaterialized.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
RenameMaterializedViewColumn,
RefreshMaterializedView,
} from './viewsMaterializedTypes'
import { Nullable } from './generalTypes'

export {
CreateMaterializedView,
Expand All @@ -20,7 +21,9 @@ export {
}

const dataClause = (data?: boolean) => (data !== undefined ? ` WITH${data ? '' : ' NO'} DATA` : '')
const storageParameterStr = <T extends StorageParameters, K extends keyof T>(storageParameters: T) => (key: K) => {
const storageParameterStr = <T extends Nullable<StorageParameters>, K extends keyof T>(storageParameters: T) => (
key: K,
) => {
const value = storageParameters[key] === true ? '' : ` = ${storageParameters[key]}`
return `${key}${value}`
}
Expand All @@ -39,8 +42,7 @@ export function dropMaterializedView(mOptions: MigrationOptions) {
export function createMaterializedView(mOptions: MigrationOptions) {
const _create: CreateMaterializedView = (viewName, options, definition) => {
const { ifNotExists, columns = [], tablespace, storageParameters = {}, data } = options
// prettier-ignore
const columnNames = (Array.isArray(columns) ? columns : [columns]).map(mOptions.literal).join(", ");
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' : ''
Expand Down Expand Up @@ -71,14 +73,14 @@ export function alterMaterializedView(mOptions: MigrationOptions) {
clauses.push(`DEPENDS ON EXTENSION ${mOptions.literal(extension)}`)
}
const withOptions = Object.keys(storageParameters)
.filter((key) => storageParameters[key])
.filter((key) => storageParameters[key] !== null)
.map(storageParameterStr(storageParameters))
.join(', ')
if (withOptions) {
clauses.push(`SET (${withOptions})`)
}
const resetOptions = Object.keys(storageParameters)
.filter((key) => !storageParameters[key])
.filter((key) => storageParameters[key] === null)
.join(', ')
if (resetOptions) {
clauses.push(`RESET (${resetOptions})`)
Expand Down
4 changes: 2 additions & 2 deletions src/operations/viewsMaterializedTypes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Name, DropOptions, IfNotExistsOption } from './generalTypes'
import { Name, DropOptions, IfNotExistsOption, Nullable } from './generalTypes'

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

Expand All @@ -12,7 +12,7 @@ export interface CreateMaterializedViewOptions extends IfNotExistsOption {
export interface AlterMaterializedViewOptions {
cluster?: null | false | string
extension?: string
storageParameters?: StorageParameters
storageParameters?: Nullable<StorageParameters>
}

export interface RefreshMaterializedViewOptions {
Expand Down
8 changes: 6 additions & 2 deletions src/operations/viewsTypes.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { Name, Value, DropOptions } from './generalTypes'
import { Name, Value, DropOptions, Nullable } from './generalTypes'

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

export interface CreateViewOptions {
temporary?: boolean
replace?: boolean
recursive?: boolean
columns?: string | string[]
checkOption?: 'CASCADED' | 'LOCAL'
options?: ViewOptions
}

export interface AlterViewOptions {
checkOption?: null | false | 'CASCADED' | 'LOCAL'
checkOption?: null | 'CASCADED' | 'LOCAL'
options?: Nullable<ViewOptions>
}

export interface AlterViewColumnOptions {
Expand Down
36 changes: 36 additions & 0 deletions test/migrations/082_view_options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
exports.up = (pgm) => {
pgm.createTable('tvo', {
id: 'id',
string: { type: 'text', notNull: true },
created: {
type: 'timestamp',
notNull: true,
default: pgm.func('current_timestamp'),
},
})
pgm.createView(
'vo',
{
columns: ['id', 'str'],
options: {
check_option: 'LOCAL',
},
},
'SELECT id, string FROM tvo',
)
pgm.alterView('vo', {
options: {
check_option: 'CASCADED',
},
})
pgm.alterView('vo', {
options: {
check_option: null,
},
})
}

exports.down = (pgm) => {
pgm.dropView('vo')
pgm.dropTable('tvo')
}

0 comments on commit 9a6380c

Please sign in to comment.