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

codenotary / immudb / 6783902372

07 Nov 2023 11:34AM UTC coverage: 89.548% (-0.02%) from 89.571%
6783902372

push

gh-ci

jeroiraz
test(pkg/pgsql): unit testing for deallocate stmt

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

33645 of 37572 relevant lines covered (89.55%)

146027.19 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
}
99

100
var joinTypes = map[string]JoinType{
101
        "INNER": InnerJoin,
102
        "LEFT":  LeftJoin,
103
        "RIGHT": RightJoin,
104
}
105

106
var types = map[string]SQLValueType{
107
        "INTEGER":   IntegerType,
108
        "BOOLEAN":   BooleanType,
109
        "VARCHAR":   VarcharType,
110
        "UUID":      UUIDType,
111
        "BLOB":      BLOBType,
112
        "TIMESTAMP": TimestampType,
113
        "FLOAT":     Float64Type,
114
}
115

116
var aggregateFns = map[string]AggregateFn{
117
        "COUNT": COUNT,
118
        "SUM":   SUM,
119
        "MAX":   MAX,
120
        "MIN":   MIN,
121
        "AVG":   AVG,
122
}
123

124
var boolValues = map[string]bool{
125
        "TRUE":  true,
126
        "FALSE": false,
127
}
128

129
var cmpOps = map[string]CmpOperator{
130
        "=":  EQ,
131
        "!=": NE,
132
        "<>": NE,
133
        "<":  LT,
134
        "<=": LE,
135
        ">":  GT,
136
        ">=": GE,
137
}
138

139
var logicOps = map[string]LogicOperator{
140
        "AND": AND,
141
        "OR":  OR,
142
}
143

144
var ErrEitherNamedOrUnnamedParams = errors.New("either named or unnamed params")
145
var ErrEitherPosOrNonPosParams = errors.New("either positional or non-positional named params")
146
var ErrInvalidPositionalParameter = errors.New("invalid positional parameter")
147

148
type positionalParamType int
149

150
const (
151
        NamedNonPositionalParamType positionalParamType = iota + 1
152
        NamedPositionalParamType
153
        UnnamedParamType
154
)
155

156
type lexer struct {
157
        r               *aheadByteReader
158
        err             error
159
        namedParamsType positionalParamType
160
        paramsCount     int
161
        result          []SQLStmt
162
}
163

164
type aheadByteReader struct {
165
        nextChar  byte
166
        nextErr   error
167
        r         io.ByteReader
168
        readCount int
169
}
170

171
func newAheadByteReader(r io.ByteReader) *aheadByteReader {
1,755✔
172
        ar := &aheadByteReader{r: r}
1,755✔
173
        ar.nextChar, ar.nextErr = r.ReadByte()
1,755✔
174
        return ar
1,755✔
175
}
1,755✔
176

177
func (ar *aheadByteReader) ReadByte() (byte, error) {
107,377✔
178
        defer func() {
214,754✔
179
                if ar.nextErr == nil {
213,036✔
180
                        ar.nextChar, ar.nextErr = ar.r.ReadByte()
105,659✔
181
                }
105,659✔
182
        }()
183

184
        ar.readCount++
107,377✔
185

107,377✔
186
        return ar.nextChar, ar.nextErr
107,377✔
187
}
188

189
func (ar *aheadByteReader) ReadCount() int {
39✔
190
        return ar.readCount
39✔
191
}
39✔
192

193
func (ar *aheadByteReader) NextByte() (byte, error) {
82,411✔
194
        return ar.nextChar, ar.nextErr
82,411✔
195
}
82,411✔
196

197
func ParseString(sql string) ([]SQLStmt, error) {
98✔
198
        return Parse(strings.NewReader(sql))
98✔
199
}
98✔
200

201
func Parse(r io.ByteReader) ([]SQLStmt, error) {
1,755✔
202
        lexer := newLexer(r)
1,755✔
203

1,755✔
204
        yyParse(lexer)
1,755✔
205

1,755✔
206
        return lexer.result, lexer.err
1,755✔
207
}
1,755✔
208

209
func newLexer(r io.ByteReader) *lexer {
1,755✔
210
        return &lexer{
1,755✔
211
                r:   newAheadByteReader(r),
1,755✔
212
                err: nil,
1,755✔
213
        }
1,755✔
214
}
1,755✔
215

216
func (l *lexer) Lex(lval *yySymType) int {
24,609✔
217
        var ch byte
24,609✔
218
        var err error
24,609✔
219

24,609✔
220
        for {
64,218✔
221
                ch, err = l.r.ReadByte()
39,609✔
222
                if err == io.EOF {
41,327✔
223
                        return 0
1,718✔
224
                }
1,718✔
225
                if err != nil {
37,891✔
226
                        lval.err = err
×
227
                        return ERROR
×
228
                }
×
229

230
                if ch == '\t' {
40,557✔
231
                        continue
2,666✔
232
                }
233

234
                if ch == '/' && l.r.nextChar == '*' {
35,228✔
235
                        l.r.ReadByte()
3✔
236

3✔
237
                        for {
102✔
238
                                ch, err := l.r.ReadByte()
99✔
239
                                if err == io.EOF {
99✔
240
                                        break
×
241
                                }
242
                                if err != nil {
99✔
243
                                        lval.err = err
×
244
                                        return ERROR
×
245
                                }
×
246

247
                                if ch == '*' && l.r.nextChar == '/' {
102✔
248
                                        l.r.ReadByte() // consume closing slash
3✔
249
                                        break
3✔
250
                                }
251
                        }
252

253
                        continue
3✔
254
                }
255

256
                if isLineBreak(ch) {
36,019✔
257
                        if ch == '\r' && l.r.nextChar == '\n' {
798✔
258
                                l.r.ReadByte()
1✔
259
                        }
1✔
260
                        continue
797✔
261
                }
262

263
                if !isSpace(ch) {
57,316✔
264
                        break
22,891✔
265
                }
266
        }
267

268
        if isSeparator(ch) {
23,268✔
269
                return STMT_SEPARATOR
377✔
270
        }
377✔
271

272
        if isBLOBPrefix(ch) && isQuote(l.r.nextChar) {
22,607✔
273
                l.r.ReadByte() // consume starting quote
93✔
274

93✔
275
                tail, err := l.readString()
93✔
276
                if err != nil {
93✔
277
                        lval.err = err
×
278
                        return ERROR
×
279
                }
×
280

281
                val, err := hex.DecodeString(tail)
93✔
282
                if err != nil {
93✔
283
                        lval.err = err
×
284
                        return ERROR
×
285
                }
×
286

287
                lval.blob = val
93✔
288
                return BLOB
93✔
289
        }
290

291
        if isLetter(ch) {
35,048✔
292
                tail, err := l.readWord()
12,627✔
293
                if err != nil {
12,627✔
294
                        lval.err = err
×
295
                        return ERROR
×
296
                }
×
297

298
                w := fmt.Sprintf("%c%s", ch, tail)
12,627✔
299
                tid := strings.ToUpper(w)
12,627✔
300

12,627✔
301
                sqlType, ok := types[tid]
12,627✔
302
                if ok {
13,213✔
303
                        lval.sqlType = sqlType
586✔
304
                        return TYPE
586✔
305
                }
586✔
306

307
                val, ok := boolValues[tid]
12,041✔
308
                if ok {
12,225✔
309
                        lval.boolean = val
184✔
310
                        return BOOLEAN
184✔
311
                }
184✔
312

313
                lop, ok := logicOps[tid]
11,857✔
314
                if ok {
11,973✔
315
                        lval.logicOp = lop
116✔
316
                        return LOP
116✔
317
                }
116✔
318

319
                afn, ok := aggregateFns[tid]
11,741✔
320
                if ok {
11,827✔
321
                        lval.aggFn = afn
86✔
322
                        return AGGREGATE_FUNC
86✔
323
                }
86✔
324

325
                join, ok := joinTypes[tid]
11,655✔
326
                if ok {
11,672✔
327
                        lval.joinType = join
17✔
328
                        return JOINTYPE
17✔
329
                }
17✔
330

331
                tkn, ok := reservedWords[tid]
11,638✔
332
                if ok {
17,762✔
333
                        return tkn
6,124✔
334
                }
6,124✔
335

336
                lval.id = strings.ToLower(w)
5,514✔
337

5,514✔
338
                return IDENTIFIER
5,514✔
339
        }
340

341
        if isDoubleQuote(ch) {
9,801✔
342
                tail, err := l.readWord()
7✔
343
                if err != nil {
7✔
344
                        lval.err = err
×
345
                        return ERROR
×
346
                }
×
347

348
                if !isDoubleQuote(l.r.nextChar) {
8✔
349
                        lval.err = fmt.Errorf("double quote expected")
1✔
350
                        return ERROR
1✔
351
                }
1✔
352

353
                l.r.ReadByte() // consume ending quote
6✔
354

6✔
355
                lval.id = strings.ToLower(tail)
6✔
356
                return IDENTIFIER
6✔
357
        }
358

359
        if isNumber(ch) {
10,502✔
360
                tail, err := l.readNumber()
715✔
361
                if err != nil {
715✔
362
                        lval.err = err
×
363
                        return ERROR
×
364
                }
×
365
                // looking for a float
366
                if isDot(l.r.nextChar) {
739✔
367
                        l.r.ReadByte() // consume dot
24✔
368

24✔
369
                        decimalPart, err := l.readNumber()
24✔
370
                        if err != nil {
24✔
371
                                lval.err = err
×
372
                                return ERROR
×
373
                        }
×
374

375
                        val, err := strconv.ParseFloat(fmt.Sprintf("%c%s.%s", ch, tail, decimalPart), 64)
24✔
376
                        if err != nil {
25✔
377
                                lval.err = err
1✔
378
                                return ERROR
1✔
379
                        }
1✔
380

381
                        lval.float = val
23✔
382
                        return FLOAT
23✔
383
                }
384

385
                val, err := strconv.ParseUint(fmt.Sprintf("%c%s", ch, tail), 10, 64)
691✔
386
                if err != nil {
691✔
387
                        lval.err = err
×
388
                        return ERROR
×
389
                }
×
390

391
                lval.integer = val
691✔
392
                return INTEGER
691✔
393
        }
394

395
        if isComparison(ch) {
9,370✔
396
                tail, err := l.readComparison()
298✔
397
                if err != nil {
298✔
398
                        lval.err = err
×
399
                        return ERROR
×
400
                }
×
401

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

298✔
404
                cmpOp, ok := cmpOps[op]
298✔
405
                if !ok {
298✔
406
                        lval.err = fmt.Errorf("invalid comparison operator %s", op)
×
407
                        return ERROR
×
408
                }
×
409

410
                lval.cmpOp = cmpOp
298✔
411
                return CMPOP
298✔
412
        }
413

414
        if isQuote(ch) {
9,139✔
415
                tail, err := l.readString()
365✔
416
                if err != nil {
365✔
417
                        lval.err = err
×
418
                        return ERROR
×
419
                }
×
420

421
                lval.str = tail
365✔
422
                return VARCHAR
365✔
423
        }
424

425
        if ch == ':' {
8,411✔
426
                ch, err := l.r.ReadByte()
2✔
427
                if err != nil {
2✔
428
                        lval.err = err
×
429
                        return ERROR
×
430
                }
×
431

432
                if ch != ':' {
2✔
433
                        lval.err = fmt.Errorf("colon expected")
×
434
                        return ERROR
×
435
                }
×
436

437
                return SCAST
2✔
438
        }
439

440
        if ch == '@' {
8,989✔
441
                if l.namedParamsType == UnnamedParamType {
583✔
442
                        lval.err = ErrEitherNamedOrUnnamedParams
1✔
443
                        return ERROR
1✔
444
                }
1✔
445

446
                if l.namedParamsType == NamedPositionalParamType {
582✔
447
                        lval.err = ErrEitherPosOrNonPosParams
1✔
448
                        return ERROR
1✔
449
                }
1✔
450

451
                l.namedParamsType = NamedNonPositionalParamType
580✔
452

580✔
453
                ch, err := l.r.NextByte()
580✔
454
                if err != nil {
580✔
455
                        lval.err = err
×
456
                        return ERROR
×
457
                }
×
458

459
                if !isLetter(ch) {
580✔
460
                        return ERROR
×
461
                }
×
462

463
                id, err := l.readWord()
580✔
464
                if err != nil {
580✔
465
                        lval.err = err
×
466
                        return ERROR
×
467
                }
×
468

469
                lval.id = strings.ToLower(id)
580✔
470

580✔
471
                return NPARAM
580✔
472
        }
473

474
        if ch == '$' {
7,867✔
475
                if l.namedParamsType == UnnamedParamType {
43✔
476
                        lval.err = ErrEitherNamedOrUnnamedParams
1✔
477
                        return ERROR
1✔
478
                }
1✔
479

480
                if l.namedParamsType == NamedNonPositionalParamType {
42✔
481
                        lval.err = ErrEitherPosOrNonPosParams
1✔
482
                        return ERROR
1✔
483
                }
1✔
484

485
                id, err := l.readNumber()
40✔
486
                if err != nil {
40✔
487
                        lval.err = err
×
488
                        return ERROR
×
489
                }
×
490

491
                pid, err := strconv.Atoi(id)
40✔
492
                if err != nil {
41✔
493
                        lval.err = err
1✔
494
                        return ERROR
1✔
495
                }
1✔
496

497
                if pid < 1 {
40✔
498
                        lval.err = ErrInvalidPositionalParameter
1✔
499
                        return ERROR
1✔
500
                }
1✔
501

502
                lval.pparam = pid
38✔
503

38✔
504
                l.namedParamsType = NamedPositionalParamType
38✔
505

38✔
506
                return PPARAM
38✔
507
        }
508

509
        if ch == '?' {
7,920✔
510
                if l.namedParamsType == NamedNonPositionalParamType || l.namedParamsType == NamedPositionalParamType {
139✔
511
                        lval.err = ErrEitherNamedOrUnnamedParams
2✔
512
                        return ERROR
2✔
513
                }
2✔
514

515
                l.paramsCount++
135✔
516
                lval.pparam = l.paramsCount
135✔
517

135✔
518
                l.namedParamsType = UnnamedParamType
135✔
519

135✔
520
                return PPARAM
135✔
521
        }
522

523
        if isDot(ch) {
7,744✔
524
                if isNumber(l.r.nextChar) { // looking for  a float
103✔
525
                        decimalPart, err := l.readNumber()
5✔
526
                        if err != nil {
5✔
527
                                lval.err = err
×
528
                                return ERROR
×
529
                        }
×
530
                        val, err := strconv.ParseFloat(fmt.Sprintf("%d.%s", 0, decimalPart), 64)
5✔
531
                        if err != nil {
5✔
532
                                lval.err = err
×
533
                                return ERROR
×
534
                        }
×
535
                        lval.float = val
5✔
536
                        return FLOAT
5✔
537
                }
538
                return DOT
93✔
539
        }
540

541
        return int(ch)
7,548✔
542
}
543

544
func (l *lexer) Error(err string) {
39✔
545
        l.err = fmt.Errorf("%s at position %d", err, l.r.ReadCount())
39✔
546
}
39✔
547

548
func (l *lexer) readWord() (string, error) {
13,214✔
549
        return l.readWhile(func(ch byte) bool {
79,705✔
550
                return isLetter(ch) || isNumber(ch)
66,491✔
551
        })
66,491✔
552
}
553

554
func (l *lexer) readNumber() (string, error) {
784✔
555
        return l.readWhile(isNumber)
784✔
556
}
784✔
557

558
func (l *lexer) readString() (string, error) {
458✔
559
        var b bytes.Buffer
458✔
560

458✔
561
        for {
3,914✔
562
                ch, err := l.r.ReadByte()
3,456✔
563
                if err != nil {
3,456✔
564
                        return "", err
×
565
                }
×
566

567
                nextCh, _ := l.r.NextByte()
3,456✔
568

3,456✔
569
                if isQuote(ch) {
3,916✔
570
                        if isQuote(nextCh) {
462✔
571
                                l.r.ReadByte() // consume escaped quote
2✔
572
                        } else {
460✔
573
                                break // string completely read
458✔
574
                        }
575
                }
576

577
                b.WriteByte(ch)
2,998✔
578
        }
579

580
        return b.String(), nil
458✔
581
}
582

583
func (l *lexer) readComparison() (string, error) {
298✔
584
        return l.readWhile(func(ch byte) bool {
648✔
585
                return isComparison(ch)
350✔
586
        })
350✔
587
}
588

589
func (l *lexer) readWhile(condFn func(b byte) bool) (string, error) {
14,296✔
590
        var b bytes.Buffer
14,296✔
591

14,296✔
592
        for {
92,671✔
593
                ch, err := l.r.NextByte()
78,375✔
594
                if err == io.EOF {
78,760✔
595
                        break
385✔
596
                }
597
                if err != nil {
77,990✔
598
                        return "", err
×
599
                }
×
600

601
                if !condFn(ch) {
91,901✔
602
                        break
13,911✔
603
                }
604

605
                ch, _ = l.r.ReadByte()
64,079✔
606
                b.WriteByte(ch)
64,079✔
607
        }
608

609
        return b.String(), nil
14,296✔
610
}
611

612
func isBLOBPrefix(ch byte) bool {
22,514✔
613
        return ch == 'x'
22,514✔
614
}
22,514✔
615

616
func isSeparator(ch byte) bool {
22,891✔
617
        return ch == ';'
22,891✔
618
}
22,891✔
619

620
func isLineBreak(ch byte) bool {
35,222✔
621
        return ch == '\r' || ch == '\n'
35,222✔
622
}
35,222✔
623

624
func isSpace(ch byte) bool {
34,425✔
625
        return ch == 32 || ch == 9 //SPACE or TAB
34,425✔
626
}
34,425✔
627

628
func isNumber(ch byte) bool {
35,737✔
629
        return '0' <= ch && ch <= '9'
35,737✔
630
}
35,737✔
631

632
func isLetter(ch byte) bool {
89,492✔
633
        return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_'
89,492✔
634
}
89,492✔
635

636
func isComparison(ch byte) bool {
9,422✔
637
        return ch == '!' || ch == '<' || ch == '=' || ch == '>'
9,422✔
638
}
9,422✔
639

640
func isQuote(ch byte) bool {
12,786✔
641
        return ch == 0x27
12,786✔
642
}
12,786✔
643

644
func isDoubleQuote(ch byte) bool {
9,801✔
645
        return ch == 0x22
9,801✔
646
}
9,801✔
647

648
func isDot(ch byte) bool {
8,361✔
649
        return ch == '.'
8,361✔
650
}
8,361✔
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