diff --git a/src/ast-mapper.ts b/src/ast-mapper.ts index 96eaf67..de32912 100644 --- a/src/ast-mapper.ts +++ b/src/ast-mapper.ts @@ -87,6 +87,9 @@ export interface IAstPartialMapper { createSequence?(seq: a.CreateSequenceStatement): a.Statement | nil alterSequence?(seq: a.AlterSequenceStatement): a.Statement | nil begin?(begin: a.BeginStatement): a.Statement | nil + setTransactionSnapshot?(snapshot: a.SetTransactionSnapshot): a.Statement | nil + setTransaction?(transaction: a.SetTransactionMode): a.Statement | nil + setSession?(session: a.SetTransactionMode): a.Statement | nil } export type IAstFullMapper = { @@ -247,6 +250,12 @@ export class AstDefaultMapper implements IAstMapper { return this.setGlobal(val); case 'set timezone': return this.setTimezone(val); + case 'set transaction snapshot': + return this.setTransactionSnapshot(val); + case 'set transaction': + return this.setTransaction(val); + case 'set session characteristics as transaction': + return this.setSession(val); case 'create sequence': return this.createSequence(val); case 'alter sequence': @@ -437,6 +446,17 @@ export class AstDefaultMapper implements IAstMapper { return val; } + setTransactionSnapshot?(snapshot: a.SetTransactionSnapshot): a.Statement | nil { + return snapshot; + } + + setTransaction?(transaction: a.SetTransactionMode): a.Statement | nil { + return transaction; + } + + setSession?(session: a.SetTransactionMode): a.Statement | nil { + return session; + } update(val: a.UpdateStatement): a.Statement | nil { if (!val) { diff --git a/src/syntax/ast.ts b/src/syntax/ast.ts index 6ca7786..2966853 100644 --- a/src/syntax/ast.ts +++ b/src/syntax/ast.ts @@ -62,6 +62,18 @@ export interface BeginStatement extends PGNode { deferrable?: boolean; } +export interface SetTransactionSnapshot extends PGNode { + type: 'set transaction snapshot'; + snapshotId: Name; +} + +export interface SetTransactionMode extends PGNode { + type: 'set transaction' | 'set session characteristics as transaction'; + isolationLevel: 'serializable' | 'repeatable read' | 'read committed' | 'read uncommitted'; + writeable?: 'read write' | 'read only'; + deferrable?: boolean; +} + export interface DoStatement extends PGNode { type: 'do'; language?: Name; diff --git a/src/syntax/base.ne b/src/syntax/base.ne index 5bd3381..781e3fd 100644 --- a/src/syntax/base.ne +++ b/src/syntax/base.ne @@ -144,6 +144,9 @@ kw_filter -> %word {% notReservedKw('filter') %} kw_commit -> %word {% notReservedKw('commit') %} kw_tablespace -> %word {% notReservedKw('tablespace') %} kw_transaction -> %word {% notReservedKw('transaction') %} +kw_snapshot -> %word {% notReservedKw('snapshot') %} +kw_session -> %word {% notReservedKw('session') %} +kw_characteristics -> %word {% notReservedKw('characteristics') %} kw_work -> %word {% notReservedKw('work') %} kw_read -> %word {% notReservedKw('read') %} kw_write -> %word {% notReservedKw('write') %} diff --git a/src/syntax/simple-statements.ne b/src/syntax/simple-statements.ne index df5b1b1..99395b7 100644 --- a/src/syntax/simple-statements.ne +++ b/src/syntax/simple-statements.ne @@ -34,7 +34,11 @@ simplestatements_tablespace -> kw_tablespace word {% x => track(x, { }) %} -simplestatements_set -> kw_set (simplestatements_set_simple | simplestatements_set_timezone) {% last %} +simplestatements_set -> kw_set (simplestatements_set_simple + | simplestatements_set_timezone + | simplestatements_set_session + | simplestatements_set_transaction_snapshot + | simplestatements_set_transaction) {% last %} simplestatements_set_timezone -> kw_time kw_zone simplestatements_set_timezone_val {% x => track(x, { type: 'set timezone', to: x[2] }) %} @@ -65,6 +69,27 @@ simplestatements_set_val_raw simplestatements_show -> kw_show ident {% x => track(x, { type: 'show', variable: asName(x[1]) }) %} +# https://www.postgresql.org/docs/current/sql-set-transaction.html +simplestatements_set_transaction_snapshot + -> kw_transaction kw_snapshot ident {% x => track(x, { type: 'set transaction snapshot', snapshotId: asName(x[2]) }) %} + +simplestatements_set_session + -> kw_session kw_characteristics %kw_as kw_transaction + (simplestatements_begin_isol | simplestatements_begin_writ | simplestatements_begin_def):* {% + x => track(x, { + type: 'set session characteristics as transaction', + ...x[4].reduce((a: any, b: any) => ({...unwrap(a), ...unwrap(b)}), {}), + }) + %} + +simplestatements_set_transaction + -> kw_transaction (simplestatements_begin_isol | simplestatements_begin_writ | simplestatements_begin_def):* {% + x => track(x, { + type: 'set transaction', + ...x[1].reduce((a: any, b: any) => ({...unwrap(a), ...unwrap(b)}), {}), + }) + %} + # https://www.postgresql.org/docs/current/sql-createschema.html create_schema -> (%kw_create kw_schema) kw_ifnotexists:? ident {% x => track(x, { diff --git a/src/syntax/simple-statements.spec.ts b/src/syntax/simple-statements.spec.ts index 145a03f..11fa298 100644 --- a/src/syntax/simple-statements.spec.ts +++ b/src/syntax/simple-statements.spec.ts @@ -247,6 +247,26 @@ describe('Simple statements', () => { }) + checkStatement(`set transaction SNAPSHOT mysnapshot`, { + type: 'set transaction snapshot', + snapshotId: { name: 'mysnapshot' }, + }); + + checkStatement(`set transaction isolation level repeatable read read only not deferrable`, { + type: 'set transaction', + isolationLevel: 'repeatable read', + writeable: 'read only', + deferrable: false, + }); + + checkStatement(`set session characteristics as transaction isolation level read uncommitted deferrable read write`, { + type: 'set session characteristics as transaction', + isolationLevel: 'read uncommitted', + writeable: 'read write', + deferrable: true, + }); + + checkStatement(`select * from (select a from mytable) myalias(col_renamed)`, { type: 'select', columns: [{ expr: { type: 'ref', name: '*' } }], diff --git a/src/to-sql.ts b/src/to-sql.ts index ca076f3..3937344 100644 --- a/src/to-sql.ts +++ b/src/to-sql.ts @@ -564,6 +564,43 @@ const visitor = astVisitor(m => ({ } }, + setTransactionSnapshot: s => { + ret.push('SET TRANSACTION SNAPSHOT '); + ret.push(name(s.snapshotId)); + }, + + setTransaction: tx => { + ret.push('SET TRANSACTION '); + if (tx.isolationLevel) { + ret.push('ISOLATION LEVEL ', tx.isolationLevel.toUpperCase(), ' '); + } + if (tx.writeable) { + ret.push(tx.writeable.toUpperCase(), ' '); + } + if (typeof tx.deferrable === 'boolean') { + if (!tx.deferrable) { + ret.push('NOT '); + } + ret.push('DEFERRABLE '); + } + }, + + setSession: s => { + ret.push('SET SESSION CHARACTERISTICS AS TRANSACTION '); + if (s.isolationLevel) { + ret.push('ISOLATION LEVEL ', s.isolationLevel.toUpperCase(), ' '); + } + if (s.writeable) { + ret.push(s.writeable.toUpperCase(), ' '); + } + if (typeof s.deferrable === 'boolean') { + if (!s.deferrable) { + ret.push('NOT '); + } + ret.push('DEFERRABLE '); + } + }, + begin: beg => { ret.push('BEGIN '); if (beg.isolationLevel) {