From 254d06ea91300d6db09919f8e12e9d0a9d181356 Mon Sep 17 00:00:00 2001 From: Jan Dolezel Date: Mon, 22 Jan 2018 11:51:20 +0100 Subject: [PATCH] Sequences operations --- README.md | 65 ++++++++++++++++++++++++++ index.d.ts | 69 ++++++++++++++++++++++------ lib/migration-builder.js | 6 +++ lib/operations/sequences.js | 91 +++++++++++++++++++++++++++++++++++++ 4 files changed, 216 insertions(+), 15 deletions(-) create mode 100644 lib/operations/sequences.js diff --git a/README.md b/README.md index dad94bb5..7ef028c9 100644 --- a/README.md +++ b/README.md @@ -748,6 +748,71 @@ This is required for some SQL operations that cannot be run within a transaction ----------------------------------------------------- +### Sequence Operations + +#### `pgm.createSequence( sequence_name, type, options )` + +> Create a new sequence - [postgres docs](https://www.postgresql.org/docs/current/static/sql-createsequence.html) + +**Arguments:** +- `sequence_name` _[string]_ - name of the new sequence +- `options` _[object]_ - options: + - `temporary` _[boolean]_ - adds `TEMPORARY` clause + - `ifNotExists` _[boolean]_ - adds `IF NOT EXISTS` clause + - `type` _[string]_ - type of the sequence + - `increment` _[number]_ - sets first value of sequence + - `minvalue` _[number or boolean]_ - sets minimum value of sequence or `NO MINVALUE` (on false or null value) + - `maxvalue` _[number or boolean]_ - sets maximum value of sequencee or `NO MAXVALUE` (on false or null value) + - `start` _[number]_ - sets first value of sequence + - `cache` _[number]_ - sets how many sequence numbers should be preallocated + - `cycle` _[boolean]_ - adds `CYCLE` or `NO CYCLE` clause if option is present + - `owner` _[string or boolean]_ - sets owner of sequence or no owner (on false or null value) + +**Reverse Operation:** `dropSequence` + +----------------------------------------------------- + +#### `pgm.dropSequence( sequence_name, drop_options )` + +> Drop a sequence - [postgres docs](http://www.postgresql.org/docs/current/static/sql-dropsequence.html) + +**Arguments:** +- `sequence_name` _[string]_ - name of the the sequence to drop +- `drop_options` _[object]_ - options: + - `ifExists` _[boolean]_ - drops sequence only if it exists + - `cascade` _[boolean]_ - drops also dependent objects + +----------------------------------------------------- + +#### `pgm.alterSequence( sequence_name, options )` + +> Alter a sequence - [postgres docs](https://www.postgresql.org/docs/current/static/sql-altersequence.html) + +**Arguments:** +- `sequence_name` _[string]_ - name of the new sequence +- `options` _[object]_ - options: + - `type` _[string]_ - type of the sequence + - `increment` _[number]_ - sets first value of sequence + - `minvalue` _[number or boolean]_ - sets minimum value of sequence or `NO MINVALUE` (on false or null value) + - `maxvalue` _[number or boolean]_ - sets maximum value of sequencee or `NO MAXVALUE` (on false or null value) + - `start` _[number]_ - sets first value of sequence (no effect until restart) + - `restart` _[number or boolean]_ - sets first value of sequence or using `start` value (on true value) + - `cache` _[number]_ - sets how many sequence numbers should be preallocated + - `cycle` _[boolean]_ - adds `CYCLE` or `NO CYCLE` clause if option is present + - `owner` _[string or boolean]_ - sets owner of sequence or no owner (on false or null value) + +----------------------------------------------------- + +#### `pgm.renameSequence( old_sequence_name, new_sequence_name )` + +> Rename a sequence - [postgres docs](http://www.postgresql.org/docs/current/static/sql-altersequence.html) + +**Arguments:** +- `old_sequence_name` _[string]_ - old name of the sequence +- `new_sequence_name` _[string]_ - new name of the sequence + +----------------------------------------------------- + ### Miscellaneous Operations #### `pgm.sql( sql )` diff --git a/index.d.ts b/index.d.ts index 661bf172..751895c2 100644 --- a/index.d.ts +++ b/index.d.ts @@ -5,6 +5,7 @@ // Definitions by: Jan Doležel interface ValueArray extends Array {} + export type Value = null | boolean | string | number | PgLiteral | ValueArray export type Name = string | { schema?: string, name: string } @@ -153,18 +154,23 @@ export interface RoleOptions { admin?: string | string[] } -export interface DropOptions { +export interface IfExistsOption { ifExists?: boolean +} + +export interface CascadeOption { cascade?: boolean } -export interface DropIndexOptions { +export type DropOptions = IfExistsOption & CascadeOption + +interface DropIndexOptionsEn { name?: string concurrently?: boolean - ifExists?: boolean - cascade?: boolean } +export type DropIndexOptions = DropIndexOptionsEn & DropOptions + export interface FunctionParamType { mode: 'IN' | 'OUT' | 'INOUT' | 'VARIADIC' name: string @@ -184,7 +190,7 @@ export interface FunctionOptions { parallel?: 'UNSAFE' | 'RESTRICTED' | 'SAFE' } -export interface TriggerOptionsEn { +interface TriggerOptionsEn { when: 'BEFORE' | 'AFTER' | 'INSTEAD OF' operation: string | string[] constraint: boolean @@ -195,23 +201,50 @@ export interface TriggerOptionsEn { deferred: boolean } -export interface DomainOptionsCreate { +export type TriggerOptions = TriggerOptionsEn & FunctionOptions + +interface DomainOptions { default?: Value - collation?: string notNull?: boolean check?: string constraintName?: string } -export interface DomainOptionsAlter { - default?: Value - notNull?: boolean +interface DomainOptionsCreateEn { + collation?: string +} + +interface DomainOptionsAlterEn { allowNull?: boolean - check?: string - constraintName?: string } -export type TriggerOptions = TriggerOptionsEn & FunctionOptions +export type DomainOptionsCreate = DomainOptionsCreateEn & DomainOptions + +export type DomainOptionsAlter = DomainOptionsAlterEn & DomainOptions + +interface SequenceOptions { + type?: string + increment?: number + minvalue?: number | null | false + maxvalue?: number | null | false + start?: number + cache?: number + cycle?: boolean + owner?: string | null | false +} + +interface SequenceOptionsCreateEn { + temporary?: boolean + ifNotExists?: boolean +} + +interface SequenceOptionsAlterEn { + restart?: number | true +} + +export type SequenceOptionsCreate = SequenceOptionsCreateEn & SequenceOptions + +export type SequenceOptionsAlter = SequenceOptionsAlterEn & SequenceOptions export interface MigrationBuilder { // Tables @@ -249,14 +282,14 @@ export interface MigrationBuilder { dropType(type_name: Name, drop_options: DropOptions): void renameType(type_name: Name, new_type_name: Name): void addTypeAttribute(type_name: Name, attribute_name: string, attribute_type: Type): void - dropTypeAttribute(type_name: Name, attribute_name: string, options: { ifExists?: boolean }): void + dropTypeAttribute(type_name: Name, attribute_name: string, options: IfExistsOption): void setTypeAttribute(type_name: Name, attribute_name: string, attribute_type: Type): void addTypeValue(type_name: Name, value: Value, options: { ifNotExists?: boolean, before?: string, after?: string }): void renameTypeAttribute(type_name: Name, attribute_name: string, new_attribute_name: string): void // Roles createRole(role_name: Name, role_options: RoleOptions): void - dropRole(role_name: Name, options: { ifExists?: boolean }): void + dropRole(role_name: Name, options: IfExistsOption): void alterRole(role_name: Name, role_options: RoleOptions): void renameRole(old_role_name: Name, new_role_name: Name): void @@ -281,6 +314,12 @@ export interface MigrationBuilder { alterDomain(domain_name: Name, domain_options: DomainOptionsAlter): void renameDomain(old_domain_name: Name, new_domain_name: Name): void + // Domains + createSequence(sequence_name: Name, sequence_options: SequenceOptionsCreate): void + dropSequence(sequence_name: Name, drop_options: DropOptions): void + alterSequence(sequence_name: Name, sequence_options: SequenceOptionsAlter): void + renameSequence(old_sequence_name: Name, new_sequence_name: Name): void + sql(sql: string, args?: object): void func(sql: string): PgLiteral noTransaction(): void diff --git a/lib/migration-builder.js b/lib/migration-builder.js index 0edd8c2e..96fe82a2 100644 --- a/lib/migration-builder.js +++ b/lib/migration-builder.js @@ -20,6 +20,7 @@ import * as functions from './operations/functions'; import * as triggers from './operations/triggers'; import * as schemas from './operations/schemas'; import * as domains from './operations/domains'; +import * as sequences from './operations/sequences'; import * as other from './operations/other'; export default class MigrationBuilder { @@ -101,6 +102,11 @@ export default class MigrationBuilder { this.alterDomain = wrap(domains.alter); this.renameDomain = wrap(domains.rename); + this.createSequence = wrap(sequences.create(options.typeShorthands)); + this.dropSequence = wrap(sequences.drop); + this.alterSequence = wrap(sequences.alter(options.typeShorthands)); + this.renameSequence = wrap(sequences.rename); + this.sql = wrap(other.sql); // Other utilities which may be useful diff --git a/lib/operations/sequences.js b/lib/operations/sequences.js new file mode 100644 index 00000000..b760c33a --- /dev/null +++ b/lib/operations/sequences.js @@ -0,0 +1,91 @@ +import { template, applyType } from '../utils'; + +const parseOptions = (type_shorthands, options) => { + const { + type, + increment, + minvalue, + maxvalue, + start, + cache, + cycle, + owner, + } = options; + const clauses = []; + if (type) { + clauses.push(`AS ${applyType(type, type_shorthands).type}`); + } + if (increment) { + clauses.push(`INCREMENT BY ${increment}`); + } + if (minvalue) { + clauses.push(`MINVALUE ${minvalue}`); + } else if (minvalue === null || minvalue === false) { + clauses.push('NO MINVALUE'); + } + if (maxvalue) { + clauses.push(`MAXVALUE ${maxvalue}`); + } else if (maxvalue === null || maxvalue === false) { + clauses.push('NO MAXVALUE'); + } + if (start) { + clauses.push(`START WITH ${start}`); + } + if (cache) { + clauses.push(`CACHE ${cache}`); + } + if (cycle) { + clauses.push('CYCLE'); + } else if (cycle === false) { + clauses.push('NO CYCLE'); + } + if (owner) { + clauses.push(`OWNED BY ${owner}`); + } else if (owner === null || owner === false) { + clauses.push('OWNED BY NONE'); + } + return clauses; +}; + +export const drop = (sequence_name, { ifExists, cascade } = {}) => + template`DROP SEQUENCE${ifExists ? ' IF EXISTS' : ''} "${sequence_name}"${cascade ? ' CASCADE' : ''};`; + +export const create = (type_shorthands) => { + const _create = (sequence_name, options) => { + const { + temporary, + ifNotExists, + } = options; + const clauses = parseOptions(type_shorthands, options); + return template`CREATE${temporary ? ' TEMPORARY' : ''} SEQUENCE${ifNotExists ? ' IF NOT EXISTS' : ''} "${sequence_name}" + ${clauses.join('\n ')};`; + }; + _create.reverse = drop; + return _create; +}; + +export const alter = type_shorthands => + (sequence_name, options) => { + const { + restart, + } = options; + const clauses = parseOptions(type_shorthands, options); + if (restart) { + if (restart === true) { + clauses.push('RESTART'); + } else { + clauses.push(`RESTART WITH ${restart}`); + } + } + return template`ALTER SEQUENCE "${sequence_name}" + ${clauses.join('\n ')};`; + }; + +// RENAME +export const rename = (sequence_name, new_sequence_name) => + template`ALTER SEQUENCE "${sequence_name}" RENAME TO "${new_sequence_name}";`; + +export const undoRename = (sequence_name, new_sequence_name) => + rename(new_sequence_name, sequence_name); + +rename.reverse = undoRename;