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

codenotary / immudb / 24236186926

10 Apr 2026 09:25AM UTC coverage: 89.169% (-0.09%) from 89.257%
24236186926

push

gh-ci

SimoneLazzaris
fix workflows

38207 of 42848 relevant lines covered (89.17%)

151869.81 hits per line

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

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

4
SPDX-License-Identifier: BUSL-1.1
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
    https://mariadb.com/bsl11/
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 keywords = map[string]int{
32
        "CREATE":         CREATE,
33
        "DROP":           DROP,
34
        "USE":            USE,
35
        "DATABASE":       DATABASE,
36
        "SNAPSHOT":       SNAPSHOT,
37
        "HISTORY":        HISTORY,
38
        "DIFF":           DIFF,
39
        "OF":             OF,
40
        "SINCE":          SINCE,
41
        "AFTER":          AFTER,
42
        "BEFORE":         BEFORE,
43
        "UNTIL":          UNTIL,
44
        "TABLE":          TABLE,
45
        "PRIMARY":        PRIMARY,
46
        "KEY":            KEY,
47
        "UNIQUE":         UNIQUE,
48
        "INDEX":          INDEX,
49
        "ON":             ON,
50
        "ALTER":          ALTER,
51
        "ADD":            ADD,
52
        "RENAME":         RENAME,
53
        "TO":             TO,
54
        "COLUMN":         COLUMN,
55
        "INSERT":         INSERT,
56
        "CONFLICT":       CONFLICT,
57
        "DO":             DO,
58
        "NOTHING":        NOTHING,
59
        "RETURNING":      RETURNING,
60
        "UPSERT":         UPSERT,
61
        "INTO":           INTO,
62
        "VALUES":         VALUES,
63
        "UPDATE":         UPDATE,
64
        "SET":            SET,
65
        "DELETE":         DELETE,
66
        "BEGIN":          BEGIN,
67
        "TRANSACTION":    TRANSACTION,
68
        "COMMIT":         COMMIT,
69
        "ROLLBACK":       ROLLBACK,
70
        "SELECT":         SELECT,
71
        "DISTINCT":       DISTINCT,
72
        "FROM":           FROM,
73
        "UNION":          UNION,
74
        "ALL":            ALL,
75
        "TX":             TX,
76
        "JOIN":           JOIN,
77
        "HAVING":         HAVING,
78
        "WHERE":          WHERE,
79
        "GROUP":          GROUP,
80
        "BY":             BY,
81
        "LIMIT":          LIMIT,
82
        "OFFSET":         OFFSET,
83
        "ORDER":          ORDER,
84
        "AS":             AS,
85
        "ASC":            ASC,
86
        "DESC":           DESC,
87
        "AND":            AND,
88
        "OR":             OR,
89
        "NOT":            NOT,
90
        "LIKE":           LIKE,
91
        "EXISTS":         EXISTS,
92
        "BETWEEN":        BETWEEN,
93
        "IN":             IN,
94
        "AUTO_INCREMENT": AUTO_INCREMENT,
95
        "NULL":           NULL,
96
        "IF":             IF,
97
        "IS":             IS,
98
        "CAST":           CAST,
99
        "::":             SCAST,
100
        "SHOW":           SHOW,
101
        "DATABASES":      DATABASES,
102
        "TABLES":         TABLES,
103
        "USERS":          USERS,
104
        "USER":           USER,
105
        "WITH":           WITH,
106
        "PASSWORD":       PASSWORD,
107
        "READ":           READ,
108
        "READWRITE":      READWRITE,
109
        "ADMIN":          ADMIN,
110
        "GRANT":          GRANT,
111
        "REVOKE":         REVOKE,
112
        "GRANTS":         GRANTS,
113
        "FOR":            FOR,
114
        "PRIVILEGES":     PRIVILEGES,
115
        "CHECK":          CHECK,
116
        "CONSTRAINT":     CONSTRAINT,
117
        "CASE":           CASE,
118
        "WHEN":           WHEN,
119
        "THEN":           THEN,
120
        "ELSE":           ELSE,
121
        "END":            END,
122
        "EXTRACT":        EXTRACT,
123
        "INTEGER":        INTEGER_TYPE,
124
        "BOOLEAN":        BOOLEAN_TYPE,
125
        "VARCHAR":        VARCHAR_TYPE,
126
        "TIMESTAMP":      TIMESTAMP_TYPE,
127
        "FLOAT":          FLOAT_TYPE,
128
        "BLOB":           BLOB_TYPE,
129
        "UUID":           UUID_TYPE,
130
        "JSON":           JSON_TYPE,
131
        "YEAR":           YEAR,
132
        "MONTH":          MONTH,
133
        "DAY":            DAY,
134
        "HOUR":           HOUR,
135
        "MINUTE":         MINUTE,
136
        "SECOND":         SECOND,
137
}
138

139
var joinTypes = map[string]JoinType{
140
        "INNER": InnerJoin,
141
        "LEFT":  LeftJoin,
142
        "RIGHT": RightJoin,
143
}
144

145
var aggregateFns = map[string]AggregateFn{
146
        "COUNT": COUNT,
147
        "SUM":   SUM,
148
        "MAX":   MAX,
149
        "MIN":   MIN,
150
        "AVG":   AVG,
151
}
152

153
var boolValues = map[string]bool{
154
        "TRUE":  true,
155
        "FALSE": false,
156
}
157

158
var cmpOps = map[string]CmpOperator{
159
        "=":  EQ,
160
        "!=": NE,
161
        "<>": NE,
162
        "<":  LT,
163
        "<=": LE,
164
        ">":  GT,
165
        ">=": GE,
166
}
167

168
var ErrEitherNamedOrUnnamedParams = errors.New("either named or unnamed params")
169
var ErrEitherPosOrNonPosParams = errors.New("either positional or non-positional named params")
170
var ErrInvalidPositionalParameter = errors.New("invalid positional parameter")
171

172
type positionalParamType int
173

174
const (
175
        NamedNonPositionalParamType positionalParamType = iota + 1
176
        NamedPositionalParamType
177
        UnnamedParamType
178
)
179

180
type lexer struct {
181
        r               *aheadByteReader
182
        err             error
183
        namedParamsType positionalParamType
184
        paramsCount     int
185
        result          []SQLStmt
186
}
187

188
type aheadByteReader struct {
189
        nextChar  byte
190
        nextErr   error
191
        r         io.ByteReader
192
        readCount int
193
}
194

195
func newAheadByteReader(r io.ByteReader) *aheadByteReader {
4,561✔
196
        ar := &aheadByteReader{r: r}
4,561✔
197
        ar.nextChar, ar.nextErr = r.ReadByte()
4,561✔
198
        return ar
4,561✔
199
}
4,561✔
200

201
func (ar *aheadByteReader) ReadByte() (byte, error) {
339,591✔
202
        defer func() {
679,182✔
203
                if ar.nextErr == nil {
674,868✔
204
                        ar.nextChar, ar.nextErr = ar.r.ReadByte()
335,277✔
205
                }
335,277✔
206
        }()
207

208
        ar.readCount++
339,591✔
209

339,591✔
210
        return ar.nextChar, ar.nextErr
339,591✔
211
}
212

213
func (ar *aheadByteReader) ReadCount() int {
151✔
214
        return ar.readCount
151✔
215
}
151✔
216

217
func (ar *aheadByteReader) NextByte() (byte, error) {
269,872✔
218
        return ar.nextChar, ar.nextErr
269,872✔
219
}
269,872✔
220

221
func ParseSQLString(sql string) ([]SQLStmt, error) {
1,150✔
222
        return ParseSQL(strings.NewReader(sql))
1,150✔
223
}
1,150✔
224

225
func ParseSQL(r io.ByteReader) ([]SQLStmt, error) {
4,561✔
226
        lexer := newLexer(r)
4,561✔
227

4,561✔
228
        yyParse(lexer)
4,561✔
229

4,561✔
230
        return lexer.result, lexer.err
4,561✔
231
}
4,561✔
232

233
func ParseExpFromString(exp string) (ValueExp, error) {
216✔
234
        stmt := fmt.Sprintf("SELECT * FROM t WHERE %s", exp)
216✔
235

216✔
236
        res, err := ParseSQLString(stmt)
216✔
237
        if err != nil {
216✔
238
                return nil, err
×
239
        }
×
240

241
        s := res[0].(*SelectStmt)
216✔
242
        return s.where, nil
216✔
243
}
244

245
func newLexer(r io.ByteReader) *lexer {
4,561✔
246
        return &lexer{
4,561✔
247
                r:   newAheadByteReader(r),
4,561✔
248
                err: nil,
4,561✔
249
        }
4,561✔
250
}
4,561✔
251

252
func (l *lexer) Lex(lval *yySymType) int {
76,663✔
253
        var ch byte
76,663✔
254
        var err error
76,663✔
255

76,663✔
256
        for {
196,335✔
257
                ch, err = l.r.ReadByte()
119,672✔
258
                if err == io.EOF {
123,986✔
259
                        return 0
4,314✔
260
                }
4,314✔
261
                if err != nil {
115,358✔
262
                        lval.err = err
×
263
                        return ERROR
×
264
                }
×
265

266
                if ch == '\t' {
119,871✔
267
                        continue
4,513✔
268
                }
269

270
                if ch == '/' && l.r.nextChar == '*' {
110,848✔
271
                        l.r.ReadByte()
3✔
272

3✔
273
                        for {
102✔
274
                                ch, err := l.r.ReadByte()
99✔
275
                                if err == io.EOF {
99✔
276
                                        break
×
277
                                }
278
                                if err != nil {
99✔
279
                                        lval.err = err
×
280
                                        return ERROR
×
281
                                }
×
282

283
                                if ch == '*' && l.r.nextChar == '/' {
102✔
284
                                        l.r.ReadByte() // consume closing slash
3✔
285
                                        break
3✔
286
                                }
287
                        }
288

289
                        continue
3✔
290
                }
291

292
                if isLineBreak(ch) {
112,178✔
293
                        if ch == '\r' && l.r.nextChar == '\n' {
1,337✔
294
                                l.r.ReadByte()
1✔
295
                        }
1✔
296
                        continue
1,336✔
297
                }
298

299
                if !isSpace(ch) {
181,855✔
300
                        break
72,349✔
301
                }
302
        }
303

304
        if isSeparator(ch) {
72,830✔
305
                return STMT_SEPARATOR
481✔
306
        }
481✔
307

308
        if ch == '-' && l.r.nextChar == '>' {
71,926✔
309
                l.r.ReadByte()
58✔
310
                return ARROW
58✔
311
        }
58✔
312

313
        if isBLOBPrefix(ch) && isQuote(l.r.nextChar) {
71,904✔
314
                l.r.ReadByte() // consume starting quote
94✔
315

94✔
316
                tail, err := l.readString()
94✔
317
                if err != nil {
94✔
318
                        lval.err = err
×
319
                        return ERROR
×
320
                }
×
321

322
                val, err := hex.DecodeString(tail)
94✔
323
                if err != nil {
94✔
324
                        lval.err = err
×
325
                        return ERROR
×
326
                }
×
327

328
                lval.blob = val
94✔
329
                return BLOB_LIT
94✔
330
        }
331

332
        if isLetter(ch) {
105,666✔
333
                tail, err := l.readWord()
33,950✔
334
                if err != nil {
33,950✔
335
                        lval.err = err
×
336
                        return ERROR
×
337
                }
×
338

339
                w := fmt.Sprintf("%c%s", ch, tail)
33,950✔
340
                tid := strings.ToUpper(w)
33,950✔
341

33,950✔
342
                val, ok := boolValues[tid]
33,950✔
343
                if ok {
34,199✔
344
                        lval.boolean = val
249✔
345
                        return BOOLEAN_LIT
249✔
346
                }
249✔
347

348
                afn, ok := aggregateFns[tid]
33,701✔
349
                if ok {
33,834✔
350
                        lval.aggFn = afn
133✔
351
                        return AGGREGATE_FUNC
133✔
352
                }
133✔
353

354
                join, ok := joinTypes[tid]
33,568✔
355
                if ok {
33,589✔
356
                        lval.joinType = join
21✔
357
                        return JOINTYPE
21✔
358
                }
21✔
359

360
                tkn, ok := keywords[tid]
33,547✔
361
                if ok {
51,418✔
362
                        lval.keyword = w
17,871✔
363
                        return tkn
17,871✔
364
                }
17,871✔
365

366
                lval.id = strings.ToLower(w)
15,676✔
367
                return IDENTIFIER
15,676✔
368
        }
369

370
        if isDoubleQuote(ch) {
37,773✔
371
                tail, err := l.readWord()
7✔
372
                if err != nil {
7✔
373
                        lval.err = err
×
374
                        return ERROR
×
375
                }
×
376

377
                if !isDoubleQuote(l.r.nextChar) {
8✔
378
                        lval.err = fmt.Errorf("double quote expected")
1✔
379
                        return ERROR
1✔
380
                }
1✔
381

382
                l.r.ReadByte() // consume ending quote
6✔
383

6✔
384
                lval.id = strings.ToLower(tail)
6✔
385
                return IDENTIFIER
6✔
386
        }
387

388
        if isNumber(ch) {
38,854✔
389
                tail, err := l.readNumber()
1,095✔
390
                if err != nil {
1,095✔
391
                        lval.err = err
×
392
                        return ERROR
×
393
                }
×
394
                // looking for a float
395
                if isDot(l.r.nextChar) {
1,161✔
396
                        l.r.ReadByte() // consume dot
66✔
397

66✔
398
                        decimalPart, err := l.readNumber()
66✔
399
                        if err != nil {
66✔
400
                                lval.err = err
×
401
                                return ERROR
×
402
                        }
×
403

404
                        val, err := strconv.ParseFloat(fmt.Sprintf("%c%s.%s", ch, tail, decimalPart), 64)
66✔
405
                        if err != nil {
67✔
406
                                lval.err = err
1✔
407
                                return ERROR
1✔
408
                        }
1✔
409

410
                        lval.float = val
65✔
411
                        return FLOAT_LIT
65✔
412
                }
413

414
                val, err := strconv.ParseUint(fmt.Sprintf("%c%s", ch, tail), 10, 64)
1,029✔
415
                if err != nil {
1,029✔
416
                        lval.err = err
×
417
                        return ERROR
×
418
                }
×
419

420
                lval.integer = val
1,029✔
421
                return INTEGER_LIT
1,029✔
422
        }
423

424
        if isComparison(ch) {
37,144✔
425
                tail, err := l.readComparison()
480✔
426
                if err != nil {
480✔
427
                        lval.err = err
×
428
                        return ERROR
×
429
                }
×
430

431
                op := fmt.Sprintf("%c%s", ch, tail)
480✔
432
                if op == "!~" {
481✔
433
                        return NOT_MATCHES_OP
1✔
434
                }
1✔
435

436
                cmpOp, ok := cmpOps[op]
479✔
437
                if !ok {
479✔
438
                        lval.err = fmt.Errorf("invalid comparison operator %s", op)
×
439
                        return ERROR
×
440
                }
×
441

442
                lval.cmpOp = cmpOp
479✔
443
                return CMPOP
479✔
444
        }
445

446
        if isQuote(ch) {
37,086✔
447
                tail, err := l.readString()
902✔
448
                if err != nil {
902✔
449
                        lval.err = err
×
450
                        return ERROR
×
451
                }
×
452

453
                lval.str = tail
902✔
454
                return VARCHAR_LIT
902✔
455
        }
456

457
        if ch == ':' {
35,308✔
458
                ch, err := l.r.ReadByte()
26✔
459
                if err != nil {
26✔
460
                        lval.err = err
×
461
                        return ERROR
×
462
                }
×
463

464
                if ch != ':' {
26✔
465
                        lval.err = fmt.Errorf("colon expected")
×
466
                        return ERROR
×
467
                }
×
468

469
                return SCAST
26✔
470
        }
471

472
        if ch == '@' {
42,592✔
473
                if l.namedParamsType == UnnamedParamType {
7,337✔
474
                        lval.err = ErrEitherNamedOrUnnamedParams
1✔
475
                        return ERROR
1✔
476
                }
1✔
477

478
                if l.namedParamsType == NamedPositionalParamType {
7,336✔
479
                        lval.err = ErrEitherPosOrNonPosParams
1✔
480
                        return ERROR
1✔
481
                }
1✔
482

483
                l.namedParamsType = NamedNonPositionalParamType
7,334✔
484

7,334✔
485
                ch, err := l.r.NextByte()
7,334✔
486
                if err != nil {
7,334✔
487
                        lval.err = err
×
488
                        return ERROR
×
489
                }
×
490

491
                if !isLetter(ch) {
7,334✔
492
                        return ERROR
×
493
                }
×
494

495
                id, err := l.readWord()
7,334✔
496
                if err != nil {
7,334✔
497
                        lval.err = err
×
498
                        return ERROR
×
499
                }
×
500

501
                lval.id = strings.ToLower(id)
7,334✔
502

7,334✔
503
                return NPARAM
7,334✔
504
        }
505

506
        if ch == '$' {
27,962✔
507
                if l.namedParamsType == UnnamedParamType {
43✔
508
                        lval.err = ErrEitherNamedOrUnnamedParams
1✔
509
                        return ERROR
1✔
510
                }
1✔
511

512
                if l.namedParamsType == NamedNonPositionalParamType {
42✔
513
                        lval.err = ErrEitherPosOrNonPosParams
1✔
514
                        return ERROR
1✔
515
                }
1✔
516

517
                id, err := l.readNumber()
40✔
518
                if err != nil {
40✔
519
                        lval.err = err
×
520
                        return ERROR
×
521
                }
×
522

523
                pid, err := strconv.Atoi(id)
40✔
524
                if err != nil {
41✔
525
                        lval.err = err
1✔
526
                        return ERROR
1✔
527
                }
1✔
528

529
                if pid < 1 {
40✔
530
                        lval.err = ErrInvalidPositionalParameter
1✔
531
                        return ERROR
1✔
532
                }
1✔
533

534
                lval.pparam = pid
38✔
535

38✔
536
                l.namedParamsType = NamedPositionalParamType
38✔
537

38✔
538
                return PPARAM
38✔
539
        }
540

541
        if ch == '?' {
28,017✔
542
                if l.namedParamsType == NamedNonPositionalParamType || l.namedParamsType == NamedPositionalParamType {
141✔
543
                        lval.err = ErrEitherNamedOrUnnamedParams
2✔
544
                        return ERROR
2✔
545
                }
2✔
546

547
                l.paramsCount++
137✔
548
                lval.pparam = l.paramsCount
137✔
549

137✔
550
                l.namedParamsType = UnnamedParamType
137✔
551

137✔
552
                return PPARAM
137✔
553
        }
554

555
        if isDot(ch) {
27,967✔
556
                if isNumber(l.r.nextChar) { // looking for  a float
233✔
557
                        decimalPart, err := l.readNumber()
5✔
558
                        if err != nil {
5✔
559
                                lval.err = err
×
560
                                return ERROR
×
561
                        }
×
562
                        val, err := strconv.ParseFloat(fmt.Sprintf("%d.%s", 0, decimalPart), 64)
5✔
563
                        if err != nil {
5✔
564
                                lval.err = err
×
565
                                return ERROR
×
566
                        }
×
567
                        lval.float = val
5✔
568
                        return FLOAT_LIT
5✔
569
                }
570
                return DOT
223✔
571
        }
572

573
        return int(ch)
27,511✔
574
}
575

576
func (l *lexer) Error(err string) {
151✔
577
        l.err = fmt.Errorf("%s at position %d", err, l.r.ReadCount())
151✔
578
}
151✔
579

580
func (l *lexer) readWord() (string, error) {
41,291✔
581
        return l.readWhile(func(ch byte) bool {
264,981✔
582
                return isLetter(ch) || isNumber(ch)
223,690✔
583
        })
223,690✔
584
}
585

586
func (l *lexer) readNumber() (string, error) {
1,206✔
587
        return l.readWhile(isNumber)
1,206✔
588
}
1,206✔
589

590
func (l *lexer) readString() (string, error) {
996✔
591
        var b bytes.Buffer
996✔
592

996✔
593
        for {
26,525✔
594
                ch, err := l.r.ReadByte()
25,529✔
595
                if err != nil {
25,529✔
596
                        return "", err
×
597
                }
×
598

599
                nextCh, _ := l.r.NextByte()
25,529✔
600

25,529✔
601
                if isQuote(ch) {
26,527✔
602
                        if isQuote(nextCh) {
1,000✔
603
                                l.r.ReadByte() // consume escaped quote
2✔
604
                        } else {
998✔
605
                                break // string completely read
996✔
606
                        }
607
                }
608

609
                b.WriteByte(ch)
24,533✔
610
        }
611

612
        return b.String(), nil
996✔
613
}
614

615
func (l *lexer) readComparison() (string, error) {
480✔
616
        return l.readWhile(func(ch byte) bool {
1,101✔
617
                return isComparison(ch)
621✔
618
        })
621✔
619
}
620

621
func (l *lexer) readWhile(condFn func(b byte) bool) (string, error) {
42,977✔
622
        var b bytes.Buffer
42,977✔
623

42,977✔
624
        for {
279,986✔
625
                ch, err := l.r.NextByte()
237,009✔
626
                if err == io.EOF {
237,957✔
627
                        break
948✔
628
                }
629
                if err != nil {
236,061✔
630
                        return "", err
×
631
                }
×
632

633
                if !condFn(ch) {
278,090✔
634
                        break
42,029✔
635
                }
636

637
                ch, _ = l.r.ReadByte()
194,032✔
638
                b.WriteByte(ch)
194,032✔
639
        }
640

641
        return b.String(), nil
42,977✔
642
}
643

644
func isBLOBPrefix(ch byte) bool {
71,810✔
645
        return ch == 'x'
71,810✔
646
}
71,810✔
647

648
func isSeparator(ch byte) bool {
72,349✔
649
        return ch == ';'
72,349✔
650
}
72,349✔
651

652
func isLineBreak(ch byte) bool {
110,842✔
653
        return ch == '\r' || ch == '\n'
110,842✔
654
}
110,842✔
655

656
func isSpace(ch byte) bool {
109,506✔
657
        return ch == 32 || ch == 9 //SPACE or TAB
109,506✔
658
}
109,506✔
659

660
func isNumber(ch byte) bool {
93,389✔
661
        return '0' <= ch && ch <= '9'
93,389✔
662
}
93,389✔
663

664
func isLetter(ch byte) bool {
302,740✔
665
        return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_'
302,740✔
666
}
302,740✔
667

668
func isComparison(ch byte) bool {
37,285✔
669
        return ch == '!' || ch == '<' || ch == '=' || ch == '>' || ch == '~'
37,285✔
670
}
37,285✔
671

672
func isQuote(ch byte) bool {
62,808✔
673
        return ch == 0x27
62,808✔
674
}
62,808✔
675

676
func isDoubleQuote(ch byte) bool {
37,773✔
677
        return ch == 0x22
37,773✔
678
}
37,773✔
679

680
func isDot(ch byte) bool {
28,834✔
681
        return ch == '.'
28,834✔
682
}
28,834✔
683

684
func newCreateTableStmt(
685
        name string,
686
        elems []TableElem,
687
        ifNotExists bool,
688
) *CreateTableStmt {
296✔
689
        colsSpecs := make([]*ColSpec, 0, 5)
296✔
690
        var checks []CheckConstraint
296✔
691

296✔
692
        var pk PrimaryKeyConstraint
296✔
693
        for _, e := range elems {
1,197✔
694
                switch c := e.(type) {
901✔
695
                case *ColSpec:
675✔
696
                        colsSpecs = append(colsSpecs, c)
675✔
697
                case PrimaryKeyConstraint:
216✔
698
                        pk = c
216✔
699
                case CheckConstraint:
10✔
700
                        if checks == nil {
15✔
701
                                checks = make([]CheckConstraint, 0, 5)
5✔
702
                        }
5✔
703
                        checks = append(checks, c)
10✔
704
                }
705
        }
706

707
        return &CreateTableStmt{
296✔
708
                ifNotExists: ifNotExists,
296✔
709
                table:       name,
296✔
710
                colsSpec:    colsSpecs,
296✔
711
                pkColNames:  pk,
296✔
712
                checks:      checks,
296✔
713
        }
296✔
714
}
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