Skip to content

Commit e52fc2d

Browse files
committed
Support PostgreSQL CYCLE clause in WITH
1 parent f5d331b commit e52fc2d

File tree

4 files changed

+88
-2
lines changed

4 files changed

+88
-2
lines changed

src/cst/Select.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
} from "./Expr";
1212
import { Alias } from "./Alias";
1313
import { FrameClause } from "./WindowFrame";
14-
import { StringLiteral } from "./Literal";
14+
import { Literal, StringLiteral } from "./Literal";
1515
import { MysqlModifier } from "./dialects/Mysql";
1616
import { ColumnDefinition } from "./CreateTable";
1717
import { PostgresqlOperator, PostgresqlOperatorExpr } from "./Node";
@@ -22,6 +22,8 @@ export type AllSelectNodes =
2222
| WithClause
2323
| CommonTableExpr
2424
| CteSearchClause
25+
| CteCycleClause
26+
| CteCycleClauseValues
2527
| SelectClause
2628
| SelectAll
2729
| SelectDistinct
@@ -147,8 +149,10 @@ export interface CommonTableExpr extends BaseNode {
147149
| [Keyword<"NOT">, Keyword<"MATERIALIZED">];
148150
expr: ParenExpr<SubSelect>;
149151
search?: CteSearchClause;
152+
cycle?: CteCycleClause;
150153
}
151154

155+
// PostgreSQL
152156
export interface CteSearchClause extends BaseNode {
153157
type: "cte_search_clause";
154158
searchKw: [
@@ -162,6 +166,27 @@ export interface CteSearchClause extends BaseNode {
162166
resultColumn: Identifier;
163167
}
164168

169+
// PostgreSQL
170+
export interface CteCycleClause extends BaseNode {
171+
type: "cte_cycle_clause";
172+
cycleKw: Keyword<"CYCLE">;
173+
columns: ListExpr<Identifier>;
174+
setKw: Keyword<"SET">;
175+
resultColumn: Identifier;
176+
values?: CteCycleClauseValues;
177+
usingKw: Keyword<"USING">;
178+
pathColumn: Identifier;
179+
}
180+
181+
// PostgreSQL
182+
export interface CteCycleClauseValues extends BaseNode {
183+
type: "cte_cycle_clause_values";
184+
toKw: Keyword<"TO">;
185+
markValue: Literal;
186+
defaultKw?: Keyword<"DEFAULT">;
187+
defaultValue: Literal;
188+
}
189+
165190
export interface SelectClause extends BaseNode {
166191
type: "select_clause";
167192
selectKw: Keyword<"SELECT">;

src/parser.pegjs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,8 @@ common_table_expr
192192
asKw:(__ AS)
193193
materialized:(__ cte_materialized)?
194194
select:(__ paren$compound_select_stmt)
195-
search:(__ cte_search_clause)? {
195+
search:(__ cte_search_clause)?
196+
cycle:(__ cte_cycle_clause)? {
196197
return loc({
197198
type: "common_table_expr",
198199
table: table,
@@ -201,6 +202,7 @@ common_table_expr
201202
materializedKw: read(materialized),
202203
expr: read(select),
203204
search: read(search),
205+
cycle: read(cycle),
204206
});
205207
}
206208

@@ -219,6 +221,33 @@ cte_search_clause
219221
});
220222
}
221223

224+
cte_cycle_clause
225+
= kw:(CYCLE __) columns:list$ident setKw:(__ SET __) resultColumn:ident
226+
values:(__ cte_cycle_clause_values)?
227+
usingKw:(__ USING __) pathColumn:ident &postgres {
228+
return loc({
229+
type: "cte_cycle_clause",
230+
cycleKw: read(kw),
231+
columns: columns,
232+
setKw: read(setKw),
233+
resultColumn: resultColumn,
234+
values: read(values),
235+
usingKw: read(usingKw),
236+
pathColumn: pathColumn,
237+
});
238+
}
239+
240+
cte_cycle_clause_values
241+
= toKw:(TO __) markValue:literal defaultKw:(__ DEFAULT __) defaultValue:literal {
242+
return loc({
243+
type: "cte_cycle_clause_values",
244+
toKw: read(toKw),
245+
markValue: markValue,
246+
defaultKw: read(defaultKw),
247+
defaultValue: defaultValue,
248+
});
249+
}
250+
222251
// Other clauses of SELECT statement (besides WITH & SELECT)
223252
other_clause
224253
= from_clause
@@ -5890,6 +5919,7 @@ CURRENT_SCHEMA = kw:"CURRENT_SCHEMA"i !ident_part { return loc(createK
58905919
CURRENT_TIME = kw:"CURRENT_TIME"i !ident_part { return loc(createKeyword(kw)); }
58915920
CURRENT_TIMESTAMP = kw:"CURRENT_TIMESTAMP"i !ident_part { return loc(createKeyword(kw)); }
58925921
CURRENT_USER = kw:"CURRENT_USER"i !ident_part { return loc(createKeyword(kw)); }
5922+
CYCLE = kw:"CYCLE"i !ident_part { return loc(createKeyword(kw)); }
58935923
DATA = kw:"DATA"i !ident_part { return loc(createKeyword(kw)); }
58945924
DATABASE = kw:"DATABASE"i !ident_part { return loc(createKeyword(kw)); }
58955925
DATE = kw:"DATE"i !ident_part { return loc(createKeyword(kw)); }

src/showNode/select.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,22 @@ export const selectMap: FullTransformMap<string, AllSelectNodes> = {
1515
node.materializedKw,
1616
node.expr,
1717
node.search,
18+
node.cycle,
1819
]),
1920
cte_search_clause: (node) =>
2021
show([node.searchKw, node.columns, node.setKw, node.resultColumn]),
22+
cte_cycle_clause: (node) =>
23+
show([
24+
node.cycleKw,
25+
node.columns,
26+
node.setKw,
27+
node.resultColumn,
28+
node.values,
29+
node.usingKw,
30+
node.pathColumn,
31+
]),
32+
cte_cycle_clause_values: (node) =>
33+
show([node.toKw, node.markValue, node.defaultKw, node.defaultValue]),
2134
// SELECT
2235
select_clause: (node) => show([node.selectKw, node.modifiers, node.columns]),
2336
select_all: (node) => show(node.allKw),

test/select/with.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,23 @@ describe("select WITH", () => {
4242
SELECT * FROM tree ORDER BY ordercol
4343
`);
4444
});
45+
46+
it("supports CYCLE clause", () => {
47+
testWc(`
48+
WITH RECURSIVE search_graph AS (
49+
SELECT 1
50+
) CYCLE col1, col2 SET is_cycle USING path_col
51+
SELECT * FROM search_graph
52+
`);
53+
});
54+
55+
it("supports CYCLE clause with TO..DEFAULT", () => {
56+
testWc(`
57+
WITH RECURSIVE search_graph AS (
58+
SELECT 1
59+
) CYCLE col1 SET is_cycle TO true DEFAULT false USING path_col
60+
SELECT * FROM search_graph
61+
`);
62+
});
4563
});
4664
});

0 commit comments

Comments
 (0)