Skip to content

Commit 059657b

Browse files
authored
Add dictionary-related AST node types and EXPLAIN output (#87)
1 parent 2a57a5f commit 059657b

File tree

79 files changed

+731
-295
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+731
-295
lines changed

ast/ast.go

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,10 @@ type CreateQuery struct {
268268
CreateDatabase bool `json:"create_database,omitempty"`
269269
CreateFunction bool `json:"create_function,omitempty"`
270270
CreateUser bool `json:"create_user,omitempty"`
271-
CreateDictionary bool `json:"create_dictionary,omitempty"`
272-
FunctionName string `json:"function_name,omitempty"`
271+
CreateDictionary bool `json:"create_dictionary,omitempty"`
272+
DictionaryAttrs []*DictionaryAttributeDeclaration `json:"dictionary_attrs,omitempty"`
273+
DictionaryDef *DictionaryDefinition `json:"dictionary_def,omitempty"`
274+
FunctionName string `json:"function_name,omitempty"`
273275
FunctionBody Expression `json:"function_body,omitempty"`
274276
UserName string `json:"user_name,omitempty"`
275277
}
@@ -295,6 +297,85 @@ type ColumnDeclaration struct {
295297
func (c *ColumnDeclaration) Pos() token.Position { return c.Position }
296298
func (c *ColumnDeclaration) End() token.Position { return c.Position }
297299

300+
// DictionaryAttributeDeclaration represents a dictionary attribute definition.
301+
type DictionaryAttributeDeclaration struct {
302+
Position token.Position `json:"-"`
303+
Name string `json:"name"`
304+
Type *DataType `json:"type"`
305+
Default Expression `json:"default,omitempty"`
306+
Expression Expression `json:"expression,omitempty"` // EXPRESSION clause
307+
Hierarchical bool `json:"hierarchical,omitempty"` // HIERARCHICAL flag
308+
Injective bool `json:"injective,omitempty"` // INJECTIVE flag
309+
IsObjectID bool `json:"is_object_id,omitempty"` // IS_OBJECT_ID flag
310+
}
311+
312+
func (d *DictionaryAttributeDeclaration) Pos() token.Position { return d.Position }
313+
func (d *DictionaryAttributeDeclaration) End() token.Position { return d.Position }
314+
315+
// DictionaryDefinition represents the definition part of a dictionary (PRIMARY KEY, SOURCE, LIFETIME, LAYOUT).
316+
type DictionaryDefinition struct {
317+
Position token.Position `json:"-"`
318+
PrimaryKey []Expression `json:"primary_key,omitempty"`
319+
Source *DictionarySource `json:"source,omitempty"`
320+
Lifetime *DictionaryLifetime `json:"lifetime,omitempty"`
321+
Layout *DictionaryLayout `json:"layout,omitempty"`
322+
Range *DictionaryRange `json:"range,omitempty"`
323+
Settings []*SettingExpr `json:"settings,omitempty"`
324+
}
325+
326+
func (d *DictionaryDefinition) Pos() token.Position { return d.Position }
327+
func (d *DictionaryDefinition) End() token.Position { return d.Position }
328+
329+
// DictionarySource represents the SOURCE clause of a dictionary.
330+
type DictionarySource struct {
331+
Position token.Position `json:"-"`
332+
Type string `json:"type"` // e.g., "CLICKHOUSE", "MYSQL", "FILE"
333+
Args []*KeyValuePair `json:"args,omitempty"`
334+
}
335+
336+
func (d *DictionarySource) Pos() token.Position { return d.Position }
337+
func (d *DictionarySource) End() token.Position { return d.Position }
338+
339+
// KeyValuePair represents a key-value pair in dictionary source or other contexts.
340+
type KeyValuePair struct {
341+
Position token.Position `json:"-"`
342+
Key string `json:"key"`
343+
Value Expression `json:"value"`
344+
}
345+
346+
func (k *KeyValuePair) Pos() token.Position { return k.Position }
347+
func (k *KeyValuePair) End() token.Position { return k.Position }
348+
349+
// DictionaryLifetime represents the LIFETIME clause of a dictionary.
350+
type DictionaryLifetime struct {
351+
Position token.Position `json:"-"`
352+
Min Expression `json:"min,omitempty"`
353+
Max Expression `json:"max,omitempty"`
354+
}
355+
356+
func (d *DictionaryLifetime) Pos() token.Position { return d.Position }
357+
func (d *DictionaryLifetime) End() token.Position { return d.Position }
358+
359+
// DictionaryLayout represents the LAYOUT clause of a dictionary.
360+
type DictionaryLayout struct {
361+
Position token.Position `json:"-"`
362+
Type string `json:"type"` // e.g., "FLAT", "HASHED", "COMPLEX_KEY_HASHED"
363+
Args []*KeyValuePair `json:"args,omitempty"`
364+
}
365+
366+
func (d *DictionaryLayout) Pos() token.Position { return d.Position }
367+
func (d *DictionaryLayout) End() token.Position { return d.Position }
368+
369+
// DictionaryRange represents the RANGE clause of a dictionary.
370+
type DictionaryRange struct {
371+
Position token.Position `json:"-"`
372+
Min Expression `json:"min,omitempty"`
373+
Max Expression `json:"max,omitempty"`
374+
}
375+
376+
func (d *DictionaryRange) Pos() token.Position { return d.Position }
377+
func (d *DictionaryRange) End() token.Position { return d.Position }
378+
298379
// DataType represents a data type.
299380
type DataType struct {
300381
Position token.Position `json:"-"`

internal/explain/dictionary.go

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
package explain
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/sqlc-dev/doubleclick/ast"
8+
)
9+
10+
// explainDictionaryAttributeDeclaration outputs a dictionary attribute declaration.
11+
func explainDictionaryAttributeDeclaration(sb *strings.Builder, n *ast.DictionaryAttributeDeclaration, indent string, depth int) {
12+
children := 0
13+
if n.Type != nil {
14+
children++
15+
}
16+
if n.Default != nil {
17+
children++
18+
}
19+
if n.Expression != nil {
20+
children++
21+
}
22+
if children > 0 {
23+
fmt.Fprintf(sb, "%sDictionaryAttributeDeclaration %s (children %d)\n", indent, n.Name, children)
24+
} else {
25+
fmt.Fprintf(sb, "%sDictionaryAttributeDeclaration %s\n", indent, n.Name)
26+
}
27+
if n.Type != nil {
28+
Node(sb, n.Type, depth+1)
29+
}
30+
if n.Default != nil {
31+
Node(sb, n.Default, depth+1)
32+
}
33+
if n.Expression != nil {
34+
Node(sb, n.Expression, depth+1)
35+
}
36+
}
37+
38+
// explainDictionaryDefinition outputs a dictionary definition section.
39+
func explainDictionaryDefinition(sb *strings.Builder, n *ast.DictionaryDefinition, indent string, depth int) {
40+
children := 0
41+
if len(n.PrimaryKey) > 0 {
42+
children++
43+
}
44+
if n.Source != nil {
45+
children++
46+
}
47+
if n.Lifetime != nil {
48+
children++
49+
}
50+
if n.Layout != nil {
51+
children++
52+
}
53+
if n.Range != nil {
54+
children++
55+
}
56+
if len(n.Settings) > 0 {
57+
children++
58+
}
59+
if children > 0 {
60+
fmt.Fprintf(sb, "%sDictionary definition (children %d)\n", indent, children)
61+
} else {
62+
fmt.Fprintf(sb, "%sDictionary definition\n", indent)
63+
}
64+
65+
// PRIMARY KEY
66+
if len(n.PrimaryKey) > 0 {
67+
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(n.PrimaryKey))
68+
for _, pk := range n.PrimaryKey {
69+
Node(sb, pk, depth+2)
70+
}
71+
}
72+
73+
// SOURCE
74+
if n.Source != nil {
75+
explainDictionarySource(sb, n.Source, indent+" ", depth+1)
76+
}
77+
78+
// LIFETIME
79+
if n.Lifetime != nil {
80+
explainDictionaryLifetime(sb, n.Lifetime, indent+" ", depth+1)
81+
}
82+
83+
// RANGE (if present, comes before LAYOUT)
84+
if n.Range != nil {
85+
explainDictionaryRange(sb, n.Range, indent+" ", depth+1)
86+
}
87+
88+
// LAYOUT
89+
if n.Layout != nil {
90+
explainDictionaryLayout(sb, n.Layout, indent+" ", depth+1)
91+
}
92+
93+
// SETTINGS
94+
if len(n.Settings) > 0 {
95+
fmt.Fprintf(sb, "%s Set\n", indent)
96+
}
97+
}
98+
99+
// explainDictionarySource outputs a dictionary SOURCE clause.
100+
func explainDictionarySource(sb *strings.Builder, n *ast.DictionarySource, indent string, depth int) {
101+
// FunctionWithKeyValueArguments has extra space before name
102+
children := 0
103+
if len(n.Args) > 0 {
104+
children = 1
105+
}
106+
if children > 0 {
107+
fmt.Fprintf(sb, "%sFunctionWithKeyValueArguments %s (children %d)\n", indent, strings.ToLower(n.Type), children)
108+
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(n.Args))
109+
for _, arg := range n.Args {
110+
explainKeyValuePair(sb, arg, indent+" ", depth+2)
111+
}
112+
} else {
113+
fmt.Fprintf(sb, "%sFunctionWithKeyValueArguments %s\n", indent, strings.ToLower(n.Type))
114+
}
115+
}
116+
117+
// explainKeyValuePair outputs a key-value pair (lowercase "pair").
118+
func explainKeyValuePair(sb *strings.Builder, n *ast.KeyValuePair, indent string, depth int) {
119+
children := 0
120+
if n.Value != nil {
121+
children = 1
122+
}
123+
if children > 0 {
124+
fmt.Fprintf(sb, "%spair (children %d)\n", indent, children)
125+
Node(sb, n.Value, depth+1)
126+
} else {
127+
fmt.Fprintf(sb, "%spair\n", indent)
128+
}
129+
}
130+
131+
// explainDictionaryLifetime outputs a dictionary LIFETIME clause.
132+
func explainDictionaryLifetime(sb *strings.Builder, n *ast.DictionaryLifetime, indent string, depth int) {
133+
// LIFETIME is output as "Dictionary lifetime" without children count typically
134+
fmt.Fprintf(sb, "%sDictionary lifetime\n", indent)
135+
}
136+
137+
// explainDictionaryLayout outputs a dictionary LAYOUT clause.
138+
func explainDictionaryLayout(sb *strings.Builder, n *ast.DictionaryLayout, indent string, depth int) {
139+
children := 0
140+
if len(n.Args) > 0 {
141+
children = 1
142+
}
143+
if children > 0 {
144+
fmt.Fprintf(sb, "%sDictionary layout (children %d)\n", indent, children)
145+
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(n.Args))
146+
for _, arg := range n.Args {
147+
explainKeyValuePair(sb, arg, indent+" ", depth+2)
148+
}
149+
} else {
150+
fmt.Fprintf(sb, "%sDictionary layout (children 1)\n", indent)
151+
fmt.Fprintf(sb, "%s ExpressionList\n", indent)
152+
}
153+
}
154+
155+
// explainDictionaryRange outputs a dictionary RANGE clause.
156+
func explainDictionaryRange(sb *strings.Builder, n *ast.DictionaryRange, indent string, depth int) {
157+
children := 0
158+
if n.Min != nil {
159+
children++
160+
}
161+
if n.Max != nil {
162+
children++
163+
}
164+
if children > 0 {
165+
fmt.Fprintf(sb, "%sDictionary range (children %d)\n", indent, children)
166+
if n.Min != nil {
167+
Node(sb, n.Min, depth+1)
168+
}
169+
if n.Max != nil {
170+
Node(sb, n.Max, depth+1)
171+
}
172+
} else {
173+
fmt.Fprintf(sb, "%sDictionary range\n", indent)
174+
}
175+
}

internal/explain/explain.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,22 @@ func Node(sb *strings.Builder, node interface{}, depth int) {
152152
case *ast.Parameter:
153153
explainParameter(sb, n, indent)
154154

155+
// Dictionary types
156+
case *ast.DictionaryAttributeDeclaration:
157+
explainDictionaryAttributeDeclaration(sb, n, indent, depth)
158+
case *ast.DictionaryDefinition:
159+
explainDictionaryDefinition(sb, n, indent, depth)
160+
case *ast.DictionarySource:
161+
explainDictionarySource(sb, n, indent, depth)
162+
case *ast.KeyValuePair:
163+
explainKeyValuePair(sb, n, indent, depth)
164+
case *ast.DictionaryLifetime:
165+
explainDictionaryLifetime(sb, n, indent, depth)
166+
case *ast.DictionaryLayout:
167+
explainDictionaryLayout(sb, n, indent, depth)
168+
case *ast.DictionaryRange:
169+
explainDictionaryRange(sb, n, indent, depth)
170+
155171
default:
156172
// For unhandled types, just print the type name
157173
fmt.Fprintf(sb, "%s%T\n", indent, node)

internal/explain/statements.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,27 @@ func explainCreateQuery(sb *strings.Builder, n *ast.CreateQuery, indent string,
8989
return
9090
}
9191
if n.CreateDictionary {
92-
fmt.Fprintf(sb, "%sCreateDictionaryQuery %s (children 1)\n", indent, n.Table)
92+
// Dictionary: count children = identifier + attributes (if any) + definition (if any)
93+
children := 1 // identifier
94+
if len(n.DictionaryAttrs) > 0 {
95+
children++
96+
}
97+
if n.DictionaryDef != nil {
98+
children++
99+
}
100+
fmt.Fprintf(sb, "%sCreateQuery %s (children %d)\n", indent, n.Table, children)
93101
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Table)
102+
// Dictionary attributes
103+
if len(n.DictionaryAttrs) > 0 {
104+
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(n.DictionaryAttrs))
105+
for _, attr := range n.DictionaryAttrs {
106+
explainDictionaryAttributeDeclaration(sb, attr, indent+" ", depth+2)
107+
}
108+
}
109+
// Dictionary definition
110+
if n.DictionaryDef != nil {
111+
explainDictionaryDefinition(sb, n.DictionaryDef, indent+" ", depth+1)
112+
}
94113
return
95114
}
96115

0 commit comments

Comments
 (0)