• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

codenotary / immudb / 6850968753

13 Nov 2023 01:58PM UTC coverage: 89.235% (-0.3%) from 89.556%
6850968753

push

gh-ci

jeroiraz
feat(embedded/sql): show table stmt

Signed-off-by: Jeronimo Irazabal <jeronimo.irazabal@gmail.com>

82 of 169 new or added lines in 2 files covered. (48.52%)

643 existing lines in 10 files now uncovered.

33838 of 37920 relevant lines covered (89.24%)

144254.33 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.47
/embedded/sql/parser.go
1
/*
2
Copyright 2022 Codenotary Inc. All rights reserved.
3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
        http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package sql
18

19
import (
20
        "bytes"
21
        "encoding/hex"
22
        "errors"
23
        "fmt"
24
        "io"
25
        "strconv"
26
        "strings"
27
)
28

29
//go:generate go run golang.org/x/tools/cmd/goyacc -l -o sql_parser.go sql_grammar.y
30

31
var reservedWords = map[string]int{
32
        "CREATE":         CREATE,
33
        "DROP":           DROP,
34
        "USE":            USE,
35
        "DATABASE":       DATABASE,
36
        "SNAPSHOT":       SNAPSHOT,
37
        "HISTORY":        HISTORY,
38
        "OF":             OF,
39
        "SINCE":          SINCE,
40
        "AFTER":          AFTER,
41
        "BEFORE":         BEFORE,
42
        "UNTIL":          UNTIL,
43
        "TABLE":          TABLE,
44
        "PRIMARY":        PRIMARY,
45
        "KEY":            KEY,
46
        "UNIQUE":         UNIQUE,
47
        "INDEX":          INDEX,
48
        "ON":             ON,
49
        "ALTER":          ALTER,
50
        "ADD":            ADD,
51
        "RENAME":         RENAME,
52
        "TO":             TO,
53
        "COLUMN":         COLUMN,
54
        "INSERT":         INSERT,
55
        "CONFLICT":       CONFLICT,
56
        "DO":             DO,
57
        "NOTHING":        NOTHING,
58
        "UPSERT":         UPSERT,
59
        "INTO":           INTO,
60
        "VALUES":         VALUES,
61
        "UPDATE":         UPDATE,
62
        "SET":            SET,
63
        "DELETE":         DELETE,
64
        "BEGIN":          BEGIN,
65
        "TRANSACTION":    TRANSACTION,
66
        "COMMIT":         COMMIT,
67
        "ROLLBACK":       ROLLBACK,
68
        "SELECT":         SELECT,
69
        "DISTINCT":       DISTINCT,
70
        "FROM":           FROM,
71
        "UNION":          UNION,
72
        "ALL":            ALL,
73
        "TX":             TX,
74
        "JOIN":           JOIN,
75
        "HAVING":         HAVING,
76
        "WHERE":          WHERE,
77
        "GROUP":          GROUP,
78
        "BY":             BY,
79
        "LIMIT":          LIMIT,
80
        "OFFSET":         OFFSET,
81
        "ORDER":          ORDER,
82
        "AS":             AS,
83
        "ASC":            ASC,
84
        "DESC":           DESC,
85
        "NOT":            NOT,
86
        "LIKE":           LIKE,
87
        "EXISTS":         EXISTS,
88
        "IN":             IN,
89
        "AUTO_INCREMENT": AUTO_INCREMENT,
90
        "NULL":           NULL,
91
        "IF":             IF,
92
        "IS":             IS,
93
        "CAST":           CAST,
94
        "::":             SCAST,
95
        "SHOW":           SHOW,
96
        "DATABASES":      DATABASES,
97
        "TABLES":         TABLES,
98
        "USERS":          USERS,
99
        "USER":           USER,
100
        "WITH":           WITH,
101
        "PASSWORD":       PASSWORD,
102
        "READ":           READ,
103
        "READWRITE":      READWRITE,
104
        "ADMIN":          ADMIN,
105
}
106

107
var joinTypes = map[string]JoinType{
108
        "INNER": InnerJoin,
109
        "LEFT":  LeftJoin,
110
        "RIGHT": RightJoin,
111
}
112

113
var types = map[string]SQLValueType{
114
        "INTEGER":   IntegerType,
115
        "BOOLEAN":   BooleanType,
116
        "VARCHAR":   VarcharType,
117
        "UUID":      UUIDType,
118
        "BLOB":      BLOBType,
119
        "TIMESTAMP": TimestampType,
120
        "FLOAT":     Float64Type,
121
}
122

123
var aggregateFns = map[string]AggregateFn{
124
        "COUNT": COUNT,
125
        "SUM":   SUM,
126
        "MAX":   MAX,
127
        "MIN":   MIN,
128
        "AVG":   AVG,
129
}
130

131
var boolValues = map[string]bool{
132
        "TRUE":  true,
133
        "FALSE": false,
134
}
135

136
var cmpOps = map[string]CmpOperator{
137
        "=":  EQ,
138
        "!=": NE,
139
        "<>": NE,
140
        "<":  LT,
141
        "<=": LE,
142
        ">":  GT,
143
        ">=": GE,
144
}
145

146
var logicOps = map[string]LogicOperator{
147
        "AND": AND,
148
        "OR":  OR,
149
}
150

151
var ErrEitherNamedOrUnnamedParams = errors.New("either named or unnamed params")
152
var ErrEitherPosOrNonPosParams = errors.New("either positional or non-positional named params")
153
var ErrInvalidPositionalParameter = errors.New("invalid positional parameter")
154

155
type positionalParamType int
156

157
const (
158
        NamedNonPositionalParamType positionalParamType = iota + 1
159
        NamedPositionalParamType
160
        UnnamedParamType
161
)
162

163
type lexer struct {
164
        r               *aheadByteReader
165
        err             error
166
        namedParamsType positionalParamType
167
        paramsCount     int
168
        result          []SQLStmt
169
}
170

171
type aheadByteReader struct {
172
        nextChar  byte
173
        nextErr   error
174
        r         io.ByteReader
175
        readCount int
176
}
177

178
func newAheadByteReader(r io.ByteReader) *aheadByteReader {
1,770✔
179
        ar := &aheadByteReader{r: r}
1,770✔
180
        ar.nextChar, ar.nextErr = r.ReadByte()
1,770✔
181
        return ar
1,770✔
182
}
1,770✔
183

184
func (ar *aheadByteReader) ReadByte() (byte, error) {
107,845✔
185
        defer func() {
215,690✔
186
                if ar.nextErr == nil {
213,957✔
187
                        ar.nextChar, ar.nextErr = ar.r.ReadByte()
106,112✔
188
                }
106,112✔
189
        }()
190

191
        ar.readCount++
107,845✔
192

107,845✔
193
        return ar.nextChar, ar.nextErr
107,845✔
194
}
195

196
func (ar *aheadByteReader) ReadCount() int {
39✔
197
        return ar.readCount
39✔
198
}
39✔
199

200
func (ar *aheadByteReader) NextByte() (byte, error) {
82,806✔
201
        return ar.nextChar, ar.nextErr
82,806✔
202
}
82,806✔
203

204
func ParseString(sql string) ([]SQLStmt, error) {
98✔
205
        return Parse(strings.NewReader(sql))
98✔
206
}
98✔
207

208
func Parse(r io.ByteReader) ([]SQLStmt, error) {
1,770✔
209
        lexer := newLexer(r)
1,770✔
210

1,770✔
211
        yyParse(lexer)
1,770✔
212

1,770✔
213
        return lexer.result, lexer.err
1,770✔
214
}
1,770✔
215

216
func newLexer(r io.ByteReader) *lexer {
1,770✔
217
        return &lexer{
1,770✔
218
                r:   newAheadByteReader(r),
1,770✔
219
                err: nil,
1,770✔
220
        }
1,770✔
221
}
1,770✔
222

223
func (l *lexer) Lex(lval *yySymType) int {
24,690✔
224
        var ch byte
24,690✔
225
        var err error
24,690✔
226

24,690✔
227
        for {
64,429✔
228
                ch, err = l.r.ReadByte()
39,739✔
229
                if err == io.EOF {
41,472✔
230
                        return 0
1,733✔
231
                }
1,733✔
232
                if err != nil {
38,006✔
UNCOV
233
                        lval.err = err
×
UNCOV
234
                        return ERROR
×
UNCOV
235
                }
×
236

237
                if ch == '\t' {
40,672✔
238
                        continue
2,666✔
239
                }
240

241
                if ch == '/' && l.r.nextChar == '*' {
35,343✔
242
                        l.r.ReadByte()
3✔
243

3✔
244
                        for {
102✔
245
                                ch, err := l.r.ReadByte()
99✔
246
                                if err == io.EOF {
99✔
UNCOV
247
                                        break
×
248
                                }
249
                                if err != nil {
99✔
UNCOV
250
                                        lval.err = err
×
UNCOV
251
                                        return ERROR
×
UNCOV
252
                                }
×
253

254
                                if ch == '*' && l.r.nextChar == '/' {
102✔
255
                                        l.r.ReadByte() // consume closing slash
3✔
256
                                        break
3✔
257
                                }
258
                        }
259

260
                        continue
3✔
261
                }
262

263
                if isLineBreak(ch) {
36,134✔
264
                        if ch == '\r' && l.r.nextChar == '\n' {
798✔
265
                                l.r.ReadByte()
1✔
266
                        }
1✔
267
                        continue
797✔
268
                }
269

270
                if !isSpace(ch) {
57,497✔
271
                        break
22,957✔
272
                }
273
        }
274

275
        if isSeparator(ch) {
23,334✔
276
                return STMT_SEPARATOR
377✔
277
        }
377✔
278

279
        if isBLOBPrefix(ch) && isQuote(l.r.nextChar) {
22,673✔
280
                l.r.ReadByte() // consume starting quote
93✔
281

93✔
282
                tail, err := l.readString()
93✔
283
                if err != nil {
93✔
284
                        lval.err = err
×
285
                        return ERROR
×
UNCOV
286
                }
×
287

288
                val, err := hex.DecodeString(tail)
93✔
289
                if err != nil {
93✔
UNCOV
290
                        lval.err = err
×
UNCOV
291
                        return ERROR
×
UNCOV
292
                }
×
293

294
                lval.blob = val
93✔
295
                return BLOB
93✔
296
        }
297

298
        if isLetter(ch) {
35,171✔
299
                tail, err := l.readWord()
12,684✔
300
                if err != nil {
12,684✔
UNCOV
301
                        lval.err = err
×
UNCOV
302
                        return ERROR
×
UNCOV
303
                }
×
304

305
                w := fmt.Sprintf("%c%s", ch, tail)
12,684✔
306
                tid := strings.ToUpper(w)
12,684✔
307

12,684✔
308
                sqlType, ok := types[tid]
12,684✔
309
                if ok {
13,270✔
310
                        lval.sqlType = sqlType
586✔
311
                        return TYPE
586✔
312
                }
586✔
313

314
                val, ok := boolValues[tid]
12,098✔
315
                if ok {
12,282✔
316
                        lval.boolean = val
184✔
317
                        return BOOLEAN
184✔
318
                }
184✔
319

320
                lop, ok := logicOps[tid]
11,914✔
321
                if ok {
12,030✔
322
                        lval.logicOp = lop
116✔
323
                        return LOP
116✔
324
                }
116✔
325

326
                afn, ok := aggregateFns[tid]
11,798✔
327
                if ok {
11,884✔
328
                        lval.aggFn = afn
86✔
329
                        return AGGREGATE_FUNC
86✔
330
                }
86✔
331

332
                join, ok := joinTypes[tid]
11,712✔
333
                if ok {
11,729✔
334
                        lval.joinType = join
17✔
335
                        return JOINTYPE
17✔
336
                }
17✔
337

338
                tkn, ok := reservedWords[tid]
11,695✔
339
                if ok {
17,868✔
340
                        return tkn
6,173✔
341
                }
6,173✔
342

343
                lval.id = strings.ToLower(w)
5,522✔
344

5,522✔
345
                return IDENTIFIER
5,522✔
346
        }
347

348
        if isDoubleQuote(ch) {
9,810✔
349
                tail, err := l.readWord()
7✔
350
                if err != nil {
7✔
UNCOV
351
                        lval.err = err
×
UNCOV
352
                        return ERROR
×
UNCOV
353
                }
×
354

355
                if !isDoubleQuote(l.r.nextChar) {
8✔
356
                        lval.err = fmt.Errorf("double quote expected")
1✔
357
                        return ERROR
1✔
358
                }
1✔
359

360
                l.r.ReadByte() // consume ending quote
6✔
361

6✔
362
                lval.id = strings.ToLower(tail)
6✔
363
                return IDENTIFIER
6✔
364
        }
365

366
        if isNumber(ch) {
10,511✔
367
                tail, err := l.readNumber()
715✔
368
                if err != nil {
715✔
UNCOV
369
                        lval.err = err
×
UNCOV
370
                        return ERROR
×
371
                }
×
372
                // looking for a float
373
                if isDot(l.r.nextChar) {
739✔
374
                        l.r.ReadByte() // consume dot
24✔
375

24✔
376
                        decimalPart, err := l.readNumber()
24✔
377
                        if err != nil {
24✔
UNCOV
378
                                lval.err = err
×
UNCOV
379
                                return ERROR
×
UNCOV
380
                        }
×
381

382
                        val, err := strconv.ParseFloat(fmt.Sprintf("%c%s.%s", ch, tail, decimalPart), 64)
24✔
383
                        if err != nil {
25✔
384
                                lval.err = err
1✔
385
                                return ERROR
1✔
386
                        }
1✔
387

388
                        lval.float = val
23✔
389
                        return FLOAT
23✔
390
                }
391

392
                val, err := strconv.ParseUint(fmt.Sprintf("%c%s", ch, tail), 10, 64)
691✔
393
                if err != nil {
691✔
UNCOV
394
                        lval.err = err
×
UNCOV
395
                        return ERROR
×
UNCOV
396
                }
×
397

398
                lval.integer = val
691✔
399
                return INTEGER
691✔
400
        }
401

402
        if isComparison(ch) {
9,379✔
403
                tail, err := l.readComparison()
298✔
404
                if err != nil {
298✔
UNCOV
405
                        lval.err = err
×
406
                        return ERROR
×
407
                }
×
408

409
                op := fmt.Sprintf("%c%s", ch, tail)
298✔
410

298✔
411
                cmpOp, ok := cmpOps[op]
298✔
412
                if !ok {
298✔
UNCOV
413
                        lval.err = fmt.Errorf("invalid comparison operator %s", op)
×
UNCOV
414
                        return ERROR
×
UNCOV
415
                }
×
416

417
                lval.cmpOp = cmpOp
298✔
418
                return CMPOP
298✔
419
        }
420

421
        if isQuote(ch) {
9,154✔
422
                tail, err := l.readString()
371✔
423
                if err != nil {
371✔
UNCOV
424
                        lval.err = err
×
UNCOV
425
                        return ERROR
×
UNCOV
426
                }
×
427

428
                lval.str = tail
371✔
429
                return VARCHAR
371✔
430
        }
431

432
        if ch == ':' {
8,414✔
433
                ch, err := l.r.ReadByte()
2✔
434
                if err != nil {
2✔
435
                        lval.err = err
×
UNCOV
436
                        return ERROR
×
UNCOV
437
                }
×
438

439
                if ch != ':' {
2✔
UNCOV
440
                        lval.err = fmt.Errorf("colon expected")
×
UNCOV
441
                        return ERROR
×
UNCOV
442
                }
×
443

444
                return SCAST
2✔
445
        }
446

447
        if ch == '@' {
8,992✔
448
                if l.namedParamsType == UnnamedParamType {
583✔
449
                        lval.err = ErrEitherNamedOrUnnamedParams
1✔
450
                        return ERROR
1✔
451
                }
1✔
452

453
                if l.namedParamsType == NamedPositionalParamType {
582✔
454
                        lval.err = ErrEitherPosOrNonPosParams
1✔
455
                        return ERROR
1✔
456
                }
1✔
457

458
                l.namedParamsType = NamedNonPositionalParamType
580✔
459

580✔
460
                ch, err := l.r.NextByte()
580✔
461
                if err != nil {
580✔
UNCOV
462
                        lval.err = err
×
UNCOV
463
                        return ERROR
×
UNCOV
464
                }
×
465

466
                if !isLetter(ch) {
580✔
467
                        return ERROR
×
UNCOV
468
                }
×
469

470
                id, err := l.readWord()
580✔
471
                if err != nil {
580✔
UNCOV
472
                        lval.err = err
×
UNCOV
473
                        return ERROR
×
UNCOV
474
                }
×
475

476
                lval.id = strings.ToLower(id)
580✔
477

580✔
478
                return NPARAM
580✔
479
        }
480

481
        if ch == '$' {
7,870✔
482
                if l.namedParamsType == UnnamedParamType {
43✔
483
                        lval.err = ErrEitherNamedOrUnnamedParams
1✔
484
                        return ERROR
1✔
485
                }
1✔
486

487
                if l.namedParamsType == NamedNonPositionalParamType {
42✔
488
                        lval.err = ErrEitherPosOrNonPosParams
1✔
489
                        return ERROR
1✔
490
                }
1✔
491

492
                id, err := l.readNumber()
40✔
493
                if err != nil {
40✔
UNCOV
494
                        lval.err = err
×
UNCOV
495
                        return ERROR
×
UNCOV
496
                }
×
497

498
                pid, err := strconv.Atoi(id)
40✔
499
                if err != nil {
41✔
500
                        lval.err = err
1✔
501
                        return ERROR
1✔
502
                }
1✔
503

504
                if pid < 1 {
40✔
505
                        lval.err = ErrInvalidPositionalParameter
1✔
506
                        return ERROR
1✔
507
                }
1✔
508

509
                lval.pparam = pid
38✔
510

38✔
511
                l.namedParamsType = NamedPositionalParamType
38✔
512

38✔
513
                return PPARAM
38✔
514
        }
515

516
        if ch == '?' {
7,923✔
517
                if l.namedParamsType == NamedNonPositionalParamType || l.namedParamsType == NamedPositionalParamType {
139✔
518
                        lval.err = ErrEitherNamedOrUnnamedParams
2✔
519
                        return ERROR
2✔
520
                }
2✔
521

522
                l.paramsCount++
135✔
523
                lval.pparam = l.paramsCount
135✔
524

135✔
525
                l.namedParamsType = UnnamedParamType
135✔
526

135✔
527
                return PPARAM
135✔
528
        }
529

530
        if isDot(ch) {
7,747✔
531
                if isNumber(l.r.nextChar) { // looking for  a float
103✔
532
                        decimalPart, err := l.readNumber()
5✔
533
                        if err != nil {
5✔
534
                                lval.err = err
×
UNCOV
535
                                return ERROR
×
UNCOV
536
                        }
×
537
                        val, err := strconv.ParseFloat(fmt.Sprintf("%d.%s", 0, decimalPart), 64)
5✔
538
                        if err != nil {
5✔
UNCOV
539
                                lval.err = err
×
UNCOV
540
                                return ERROR
×
UNCOV
541
                        }
×
542
                        lval.float = val
5✔
543
                        return FLOAT
5✔
544
                }
545
                return DOT
93✔
546
        }
547

548
        return int(ch)
7,551✔
549
}
550

551
func (l *lexer) Error(err string) {
39✔
552
        l.err = fmt.Errorf("%s at position %d", err, l.r.ReadCount())
39✔
553
}
39✔
554

555
func (l *lexer) readWord() (string, error) {
13,271✔
556
        return l.readWhile(func(ch byte) bool {
80,051✔
557
                return isLetter(ch) || isNumber(ch)
66,780✔
558
        })
66,780✔
559
}
560

561
func (l *lexer) readNumber() (string, error) {
784✔
562
        return l.readWhile(isNumber)
784✔
563
}
784✔
564

565
func (l *lexer) readString() (string, error) {
464✔
566
        var b bytes.Buffer
464✔
567

464✔
568
        for {
4,012✔
569
                ch, err := l.r.ReadByte()
3,548✔
570
                if err != nil {
3,548✔
UNCOV
571
                        return "", err
×
UNCOV
572
                }
×
573

574
                nextCh, _ := l.r.NextByte()
3,548✔
575

3,548✔
576
                if isQuote(ch) {
4,014✔
577
                        if isQuote(nextCh) {
468✔
578
                                l.r.ReadByte() // consume escaped quote
2✔
579
                        } else {
466✔
580
                                break // string completely read
464✔
581
                        }
582
                }
583

584
                b.WriteByte(ch)
3,084✔
585
        }
586

587
        return b.String(), nil
464✔
588
}
589

590
func (l *lexer) readComparison() (string, error) {
298✔
591
        return l.readWhile(func(ch byte) bool {
648✔
592
                return isComparison(ch)
350✔
593
        })
350✔
594
}
595

596
func (l *lexer) readWhile(condFn func(b byte) bool) (string, error) {
14,353✔
597
        var b bytes.Buffer
14,353✔
598

14,353✔
599
        for {
93,031✔
600
                ch, err := l.r.NextByte()
78,678✔
601
                if err == io.EOF {
79,077✔
602
                        break
399✔
603
                }
604
                if err != nil {
78,279✔
UNCOV
605
                        return "", err
×
UNCOV
606
                }
×
607

608
                if !condFn(ch) {
92,233✔
609
                        break
13,954✔
610
                }
611

612
                ch, _ = l.r.ReadByte()
64,325✔
613
                b.WriteByte(ch)
64,325✔
614
        }
615

616
        return b.String(), nil
14,353✔
617
}
618

619
func isBLOBPrefix(ch byte) bool {
22,580✔
620
        return ch == 'x'
22,580✔
621
}
22,580✔
622

623
func isSeparator(ch byte) bool {
22,957✔
624
        return ch == ';'
22,957✔
625
}
22,957✔
626

627
func isLineBreak(ch byte) bool {
35,337✔
628
        return ch == '\r' || ch == '\n'
35,337✔
629
}
35,337✔
630

631
func isSpace(ch byte) bool {
34,540✔
632
        return ch == 32 || ch == 9 //SPACE or TAB
34,540✔
633
}
34,540✔
634

635
func isNumber(ch byte) bool {
35,801✔
636
        return '0' <= ch && ch <= '9'
35,801✔
637
}
35,801✔
638

639
func isLetter(ch byte) bool {
89,847✔
640
        return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_'
89,847✔
641
}
89,847✔
642

643
func isComparison(ch byte) bool {
9,431✔
644
        return ch == '!' || ch == '<' || ch == '=' || ch == '>'
9,431✔
645
}
9,431✔
646

647
func isQuote(ch byte) bool {
12,893✔
648
        return ch == 0x27
12,893✔
649
}
12,893✔
650

651
func isDoubleQuote(ch byte) bool {
9,810✔
652
        return ch == 0x22
9,810✔
653
}
9,810✔
654

655
func isDot(ch byte) bool {
8,364✔
656
        return ch == '.'
8,364✔
657
}
8,364✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc