Skip to content

Commit 0085168

Browse files
authored
Fix 4 parser tests with hex literals, REPLACE transformer, and SAMPLE BY (#49)
1 parent b44a838 commit 0085168

File tree

8 files changed

+126
-11
lines changed

8 files changed

+126
-11
lines changed

internal/explain/explain.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func Node(sb *strings.Builder, node interface{}, depth int) {
7272
case *ast.WithElement:
7373
explainWithElement(sb, n, indent, depth)
7474
case *ast.Asterisk:
75-
explainAsterisk(sb, n, indent)
75+
explainAsterisk(sb, n, indent, depth)
7676
case *ast.ColumnsMatcher:
7777
fmt.Fprintf(sb, "%sColumnsRegexpMatcher\n", indent)
7878

internal/explain/expressions.go

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -471,12 +471,60 @@ func explainAliasedExpr(sb *strings.Builder, n *ast.AliasedExpr, depth int) {
471471
}
472472
}
473473

474-
func explainAsterisk(sb *strings.Builder, n *ast.Asterisk, indent string) {
474+
func explainAsterisk(sb *strings.Builder, n *ast.Asterisk, indent string, depth int) {
475+
// Check if there are any column transformers (EXCEPT, REPLACE)
476+
hasTransformers := len(n.Except) > 0 || len(n.Replace) > 0
477+
475478
if n.Table != "" {
476-
fmt.Fprintf(sb, "%sQualifiedAsterisk (children %d)\n", indent, 1)
477-
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Table)
479+
if hasTransformers {
480+
fmt.Fprintf(sb, "%sQualifiedAsterisk (children %d)\n", indent, 2)
481+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Table)
482+
explainColumnsTransformers(sb, n, indent+" ", depth+1)
483+
} else {
484+
fmt.Fprintf(sb, "%sQualifiedAsterisk (children %d)\n", indent, 1)
485+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Table)
486+
}
478487
} else {
479-
fmt.Fprintf(sb, "%sAsterisk\n", indent)
488+
if hasTransformers {
489+
fmt.Fprintf(sb, "%sAsterisk (children %d)\n", indent, 1)
490+
explainColumnsTransformers(sb, n, indent+" ", depth+1)
491+
} else {
492+
fmt.Fprintf(sb, "%sAsterisk\n", indent)
493+
}
494+
}
495+
}
496+
497+
func explainColumnsTransformers(sb *strings.Builder, n *ast.Asterisk, indent string, depth int) {
498+
transformerCount := 0
499+
if len(n.Except) > 0 {
500+
transformerCount++
501+
}
502+
if len(n.Replace) > 0 {
503+
transformerCount++
504+
}
505+
506+
fmt.Fprintf(sb, "%sColumnsTransformerList (children %d)\n", indent, transformerCount)
507+
508+
if len(n.Except) > 0 {
509+
fmt.Fprintf(sb, "%s ColumnsExceptTransformer (children %d)\n", indent, len(n.Except))
510+
for _, col := range n.Except {
511+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, col)
512+
}
513+
}
514+
515+
if len(n.Replace) > 0 {
516+
fmt.Fprintf(sb, "%s ColumnsReplaceTransformer (children %d)\n", indent, len(n.Replace))
517+
for _, replace := range n.Replace {
518+
children := 0
519+
if replace.Expr != nil {
520+
children = 1
521+
}
522+
fmt.Fprintf(sb, "%s ColumnsReplaceTransformer::Replacement (children %d)\n", indent, children)
523+
if replace.Expr != nil {
524+
// Output the expression without alias - the replacement name is implied
525+
Node(sb, replace.Expr, depth+3)
526+
}
527+
}
480528
}
481529
}
482530

internal/explain/statements.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ func explainCreateQuery(sb *strings.Builder, n *ast.CreateQuery, indent string,
147147
}
148148
}
149149
}
150-
if n.Engine != nil || len(n.OrderBy) > 0 || len(n.PrimaryKey) > 0 || n.PartitionBy != nil || len(n.Settings) > 0 {
150+
if n.Engine != nil || len(n.OrderBy) > 0 || len(n.PrimaryKey) > 0 || n.PartitionBy != nil || n.SampleBy != nil || len(n.Settings) > 0 {
151151
storageChildren := 0
152152
if n.Engine != nil {
153153
storageChildren++
@@ -161,6 +161,25 @@ func explainCreateQuery(sb *strings.Builder, n *ast.CreateQuery, indent string,
161161
if len(n.PrimaryKey) > 0 {
162162
storageChildren++
163163
}
164+
// SAMPLE BY is only shown in EXPLAIN AST when it's a function (not a simple identifier)
165+
// and when it's different from ORDER BY
166+
if n.SampleBy != nil {
167+
if _, isIdent := n.SampleBy.(*ast.Identifier); !isIdent {
168+
// Check if SAMPLE BY equals ORDER BY - if so, don't show it
169+
showSampleBy := true
170+
if len(n.OrderBy) == 1 {
171+
var orderBySb, sampleBySb strings.Builder
172+
Node(&orderBySb, n.OrderBy[0], 0)
173+
Node(&sampleBySb, n.SampleBy, 0)
174+
if orderBySb.String() == sampleBySb.String() {
175+
showSampleBy = false
176+
}
177+
}
178+
if showSampleBy {
179+
storageChildren++
180+
}
181+
}
182+
}
164183
if len(n.Settings) > 0 {
165184
storageChildren++
166185
}
@@ -241,6 +260,25 @@ func explainCreateQuery(sb *strings.Builder, n *ast.CreateQuery, indent string,
241260
}
242261
}
243262
}
263+
// SAMPLE BY is only shown in EXPLAIN AST when it's a function (not a simple identifier)
264+
// and when it's different from ORDER BY
265+
if n.SampleBy != nil {
266+
if _, isIdent := n.SampleBy.(*ast.Identifier); !isIdent {
267+
// Check if SAMPLE BY equals ORDER BY - if so, don't show it
268+
showSampleBy := true
269+
if len(n.OrderBy) == 1 {
270+
var orderBySb, sampleBySb strings.Builder
271+
Node(&orderBySb, n.OrderBy[0], 0)
272+
Node(&sampleBySb, n.SampleBy, 0)
273+
if orderBySb.String() == sampleBySb.String() {
274+
showSampleBy = false
275+
}
276+
}
277+
if showSampleBy {
278+
Node(sb, n.SampleBy, depth+2)
279+
}
280+
}
281+
}
244282
if len(n.Settings) > 0 {
245283
fmt.Fprintf(sb, "%s Set\n", indent)
246284
}

lexer/lexer.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,35 @@ func (l *Lexer) readString(quote rune) Item {
425425
return Item{Token: token.STRING, Value: sb.String(), Pos: pos}
426426
}
427427

428+
func (l *Lexer) readHexString() Item {
429+
pos := l.pos
430+
var sb strings.Builder
431+
l.readChar() // skip opening quote
432+
433+
for !l.eof {
434+
if l.ch == '\'' {
435+
l.readChar() // skip closing quote
436+
break
437+
}
438+
// Read two hex digits and convert to byte
439+
hex1 := l.ch
440+
l.readChar()
441+
if l.eof || l.ch == '\'' {
442+
// Odd number of hex digits - write single value
443+
sb.WriteByte(byte(hexValue(hex1)))
444+
if l.ch == '\'' {
445+
l.readChar() // skip closing quote
446+
}
447+
break
448+
}
449+
hex2 := l.ch
450+
val := hexValue(hex1)*16 + hexValue(hex2)
451+
sb.WriteByte(byte(val))
452+
l.readChar()
453+
}
454+
return Item{Token: token.STRING, Value: sb.String(), Pos: pos}
455+
}
456+
428457
func (l *Lexer) readQuotedIdentifier() Item {
429458
pos := l.pos
430459
var sb strings.Builder
@@ -841,7 +870,7 @@ func (l *Lexer) readIdentifier() Item {
841870
// Check for hex string literal: x'...' or X'...'
842871
if (l.ch == 'x' || l.ch == 'X') && l.peekChar() == '\'' {
843872
l.readChar() // skip x
844-
return l.readString('\'') // read as regular string
873+
return l.readHexString() // read as hex-decoded string
845874
}
846875

847876
// Check for binary string literal: b'...' or B'...'
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true}
1+
{}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true}
1+
{}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true}
1+
{}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true}
1+
{}

0 commit comments

Comments
 (0)