pgsql-ast-parser
Advanced tools
Comparing version 8.0.0 to 8.1.0
@@ -44,2 +44,3 @@ import * as a from './syntax/ast'; | ||
with?: (val: a.WithStatement) => a.SelectStatement | nil; | ||
withRecursive?: (val: a.WithRecursiveStatement) => a.SelectStatement | nil; | ||
union?: (val: a.SelectFromUnion) => a.SelectStatement | nil; | ||
@@ -188,2 +189,3 @@ select?: (val: a.SelectStatement) => a.SelectStatement | nil; | ||
with(val: a.WithStatement): a.SelectStatement | nil; | ||
withRecursive(val: a.WithRecursiveStatement): a.SelectStatement | nil; | ||
from(from: a.From): a.From | nil; | ||
@@ -190,0 +192,0 @@ fromCall(from: a.FromCall): a.From | nil; |
{ | ||
"name": "pgsql-ast-parser", | ||
"version": "8.0.0", | ||
"version": "8.1.0", | ||
"description": "Yet another simple Postgres SQL parser/modifier", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -47,2 +47,3 @@ import * as a from './syntax/ast'; | ||
with?: (val: a.WithStatement) => a.SelectStatement | nil | ||
withRecursive?: (val: a.WithRecursiveStatement) => a.SelectStatement | nil; | ||
union?: (val: a.SelectFromUnion) => a.SelectStatement | nil | ||
@@ -230,2 +231,4 @@ select?: (val: a.SelectStatement) => a.SelectStatement | nil | ||
return this.with(val); | ||
case 'with recursive': | ||
return this.withRecursive(val); | ||
case 'select': | ||
@@ -812,2 +815,4 @@ return this.selection(val); | ||
return this.values(val); | ||
case 'with recursive': | ||
return this.withRecursive(val); | ||
default: | ||
@@ -889,3 +894,22 @@ throw NotSupported.never(val); | ||
withRecursive(val: a.WithRecursiveStatement): a.SelectStatement | nil { | ||
const statement = this.union(val.bind); | ||
if (!statement) { | ||
return null; | ||
} | ||
// 'with recursive' only accepts unions | ||
if (statement.type !== 'union' && statement.type !== 'union all') { | ||
return null; | ||
} | ||
const _in = this.statement(val.in); | ||
if (!withAccepts(_in)) { | ||
return null; | ||
} | ||
return assignChanged(val, { | ||
bind: statement, | ||
in: _in, | ||
}); | ||
} | ||
from(from: a.From): a.From | nil { | ||
@@ -1011,2 +1035,3 @@ switch (from.type) { | ||
case 'with': | ||
case 'with recursive': | ||
return this.select(val); | ||
@@ -1013,0 +1038,0 @@ case 'keyword': |
@@ -39,2 +39,3 @@ // import { IType } from '../../interfaces'; | ||
| CreateSchemaStatement | ||
| WithRecursiveStatement | ||
| RaiseStatement | ||
@@ -503,3 +504,9 @@ | ValuesStatement | ||
export type WithStatementBinding = SelectStatement | WithStatement | InsertStatement | UpdateStatement | DeleteStatement; | ||
export type WithStatementBinding = SelectStatement | ||
| WithStatement | ||
| WithRecursiveStatement | ||
| InsertStatement | ||
| UpdateStatement | ||
| DeleteStatement; | ||
export interface WithStatement extends PGNode { | ||
@@ -514,6 +521,15 @@ type: 'with'; | ||
export interface WithRecursiveStatement extends PGNode { | ||
type: 'with recursive'; | ||
alias: Name; | ||
columnNames: Name[]; | ||
bind: SelectFromUnion; | ||
in: WithStatementBinding; | ||
} | ||
export type SelectStatement = SelectFromStatement | ||
| SelectFromUnion | ||
| ValuesStatement | ||
| WithStatement; | ||
| WithStatement | ||
| WithRecursiveStatement; | ||
@@ -520,0 +536,0 @@ export interface SelectFromStatement extends PGNode { |
@@ -82,2 +82,70 @@ import { Parser, Grammar } from 'nearley'; | ||
function deepEqual<T>(a: T, b: T, strict?: boolean, depth = 10, numberDelta = 0.0001) { | ||
if (depth < 0) { | ||
throw new Error('Comparing too deep entities'); | ||
} | ||
if (a === b) { | ||
return true; | ||
} | ||
if (!strict) { | ||
// should not use '==' because it could call .toString() on objects when compared to strings. | ||
// ... which is not ok. Especially when working with translatable objects, which .toString() returns a transaltion (a string, thus) | ||
if (!a && !b) { | ||
return true; | ||
} | ||
} | ||
if (Array.isArray(a)) { | ||
if (!Array.isArray(b) || a.length !== b.length) | ||
return false; | ||
for (let i = 0; i < a.length; i++) { | ||
if (!deepEqual(a[i], b[i], strict, depth - 1, numberDelta)) | ||
return false; | ||
} | ||
return true; | ||
} | ||
// handle dates | ||
if (a instanceof Date || b instanceof Date) { | ||
return a === b; | ||
} | ||
const fa = Number.isFinite(<any>a); | ||
const fb = Number.isFinite(<any>b); | ||
if (fa || fb) { | ||
return fa && fb && Math.abs(<any>a - <any>b) <= numberDelta; | ||
} | ||
// handle plain objects | ||
if (typeof a !== 'object' || typeof a !== typeof b) | ||
return false; | ||
if (!a || !b) { | ||
return false; | ||
} | ||
const ak = Object.keys(a); | ||
const bk = Object.keys(b); | ||
if (strict && ak.length !== bk.length) | ||
return false; | ||
const set: Iterable<string> = strict | ||
? Object.keys(a) | ||
: new Set([...Object.keys(a), ...Object.keys(b)]); | ||
for (const k of set) { | ||
if (!deepEqual((a as any)[k], (b as any)[k], strict, depth - 1, numberDelta)) | ||
return false; | ||
} | ||
return true; | ||
} | ||
declare var __non_webpack_require__: any; | ||
function inspect(elt: any) { | ||
return __non_webpack_require__('util').inspect(elt); | ||
} | ||
function checkTree<T>(value: string | string[], expected: T, mapper: (parsed: T, m: IAstMapper | IAstToSql) => any, start?: string, checkLocations?: boolean) { | ||
@@ -100,3 +168,12 @@ if (typeof value === 'string') { | ||
} | ||
expect(ret.length).to.equal(1, 'Ambiguous matches'); | ||
if (ret.length !== 1) { | ||
const noLocs = ret.map(hideLocs); | ||
if (noLocs.slice(1).every(p => deepEqual(p, noLocs[0]))) { | ||
assert.fail(`${noLocs.length} ambiguous syntaxes, but they yielded the same ASTs : ` + inspect(noLocs[0])); | ||
} else { | ||
assert.fail(`${noLocs.length} ambiguous syntaxes, AND THEY HAVE YIELDED DIFFERENT ASTs : \n` + noLocs | ||
.map(inspect) | ||
.join('\n\n ====================== \n\n')); | ||
} | ||
} | ||
return trimNullish(ret[0]); | ||
@@ -103,0 +180,0 @@ } |
import 'mocha'; | ||
import 'chai'; | ||
import { checkSelect, columns, tbl } from './spec-utils'; | ||
import { checkSelect, columns, int, tbl } from './spec-utils'; | ||
import { SelectedColumn } from './ast'; | ||
@@ -119,2 +119,8 @@ | ||
}) | ||
checkSelect(`values (1) union values (2)`, { | ||
type: 'union', | ||
left: {type: 'values', values: [[int(1)]]}, | ||
right: {type: 'values', values: [[int(2)]]}, | ||
}); | ||
}); |
import 'mocha'; | ||
import 'chai'; | ||
import { checkStatement, checkInvalidExpr, tbl } from './spec-utils'; | ||
import { checkStatement, checkInvalidExpr, tbl, name, int, binary, ref } from './spec-utils'; | ||
import { expect } from 'chai'; | ||
@@ -117,2 +117,40 @@ import { SelectedColumn } from './ast'; | ||
}) | ||
checkStatement(`WITH RECURSIVE t(n) AS ( | ||
VALUES (1) | ||
UNION ALL | ||
SELECT n+1 FROM t WHERE n < 100 | ||
) | ||
SELECT sum(n) FROM t`, { | ||
type: 'with recursive', | ||
alias: name('t'), | ||
columnNames: [name('n')], | ||
bind: { | ||
type: 'union all', | ||
left: { | ||
type: 'values', | ||
values: [[int(1)]], | ||
}, | ||
right: { | ||
type: 'select', | ||
from: [tbl('t')], | ||
columns: [{ | ||
expr: binary(ref('n'), '+', int(1)) | ||
}], | ||
where: binary(ref('n'), '<', int(100)), | ||
} | ||
}, | ||
in: { | ||
type: 'select', | ||
from: [tbl('t')], | ||
columns: [{ | ||
expr: { | ||
type: 'call', | ||
function: name('sum'), | ||
args: [ref('n')], | ||
} | ||
}] | ||
} | ||
}) | ||
}); |
@@ -710,2 +710,14 @@ import { IAstPartialMapper, AstDefaultMapper } from './ast-mapper'; | ||
withRecursive: val => { | ||
ret.push('WITH RECURSIVE ' | ||
, name(val.alias) | ||
, '(' | ||
, ...val.columnNames.map(name).join(', ') | ||
, ') AS ('); | ||
m.union(val.bind); | ||
ret.push(') '); | ||
m.statement(val.in); | ||
}, | ||
setGlobal: g => { | ||
@@ -941,3 +953,3 @@ ret.push('SET ', name(g.variable), ' = '); | ||
if (s.name.columnNames) { | ||
if(!s.name.alias) { | ||
if (!s.name.alias) { | ||
throw new Error('Cannot specify aliased column names without an alias'); | ||
@@ -944,0 +956,0 @@ } |
import { nil } from '../utils'; | ||
export declare function locationOf(node: PGNode): NodeLocation; | ||
export declare type Statement = SelectStatement | CreateTableStatement | CreateSequenceStatement | CreateIndexStatement | CreateExtensionStatement | CommitStatement | InsertStatement | UpdateStatement | ShowStatement | PrepareStatement | DeleteStatement | WithStatement | RollbackStatement | TablespaceStatement | CreateViewStatement | CreateMaterializedViewStatement | AlterTableStatement | AlterSequenceStatement | SetGlobalStatement | SetTimezone | CreateEnumType | TruncateTableStatement | DropTableStatement | DropSequenceStatement | DropIndexStatement | CommentStatement | CreateSchemaStatement | RaiseStatement | ValuesStatement | CreateFunctionStatement | DoStatement | BeginStatement | StartTransactionStatement; | ||
export declare type Statement = SelectStatement | CreateTableStatement | CreateSequenceStatement | CreateIndexStatement | CreateExtensionStatement | CommitStatement | InsertStatement | UpdateStatement | ShowStatement | PrepareStatement | DeleteStatement | WithStatement | RollbackStatement | TablespaceStatement | CreateViewStatement | CreateMaterializedViewStatement | AlterTableStatement | AlterSequenceStatement | SetGlobalStatement | SetTimezone | CreateEnumType | TruncateTableStatement | DropTableStatement | DropSequenceStatement | DropIndexStatement | CommentStatement | CreateSchemaStatement | WithRecursiveStatement | RaiseStatement | ValuesStatement | CreateFunctionStatement | DoStatement | BeginStatement | StartTransactionStatement; | ||
export interface PGNode { | ||
@@ -362,3 +362,3 @@ _location?: NodeLocation; | ||
} | ||
export declare type WithStatementBinding = SelectStatement | WithStatement | InsertStatement | UpdateStatement | DeleteStatement; | ||
export declare type WithStatementBinding = SelectStatement | WithStatement | WithRecursiveStatement | InsertStatement | UpdateStatement | DeleteStatement; | ||
export interface WithStatement extends PGNode { | ||
@@ -372,3 +372,10 @@ type: 'with'; | ||
} | ||
export declare type SelectStatement = SelectFromStatement | SelectFromUnion | ValuesStatement | WithStatement; | ||
export interface WithRecursiveStatement extends PGNode { | ||
type: 'with recursive'; | ||
alias: Name; | ||
columnNames: Name[]; | ||
bind: SelectFromUnion; | ||
in: WithStatementBinding; | ||
} | ||
export declare type SelectStatement = SelectFromStatement | SelectFromUnion | ValuesStatement | WithStatement | WithRecursiveStatement; | ||
export interface SelectFromStatement extends PGNode { | ||
@@ -375,0 +382,0 @@ type: 'select'; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
1432348
15831