Some checks failed
kotlin / test (push) Has been skipped
python / test (push) Has been skipped
typescript / test (push) Has been skipped
go / build darwin/amd64 (push) Has been cancelled
go / build linux/amd64 (push) Has been cancelled
go / build windows/amd64 (push) Has been cancelled
go / build darwin/arm64 (push) Has been cancelled
go / build linux/arm64 (push) Has been cancelled
go / build windows/arm64 (push) Has been cancelled
go / test (push) Has been cancelled
go / vuln_check (push) Has been cancelled
126 lines
2.3 KiB
Go
126 lines
2.3 KiB
Go
package clickhouse
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"io"
|
|
|
|
"github.com/sqlc-dev/doubleclick/parser"
|
|
|
|
"github.com/sqlc-dev/sqlc/internal/source"
|
|
"github.com/sqlc-dev/sqlc/internal/sql/ast"
|
|
)
|
|
|
|
func NewParser() *Parser {
|
|
return &Parser{}
|
|
}
|
|
|
|
type Parser struct{}
|
|
|
|
func (p *Parser) Parse(r io.Reader) ([]ast.Statement, error) {
|
|
blob, err := io.ReadAll(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ctx := context.Background()
|
|
stmtNodes, err := parser.Parse(ctx, bytes.NewReader(blob))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var stmts []ast.Statement
|
|
loc := 0
|
|
for _, stmt := range stmtNodes {
|
|
start := stmt.Pos().Offset - 1
|
|
if start < loc {
|
|
start = loc
|
|
}
|
|
end := statementEnd(blob, start)
|
|
|
|
converter := &cc{}
|
|
out := converter.convert(stmt)
|
|
if _, ok := out.(*ast.TODO); ok {
|
|
loc = end
|
|
continue
|
|
}
|
|
|
|
stmts = append(stmts, ast.Statement{
|
|
Raw: &ast.RawStmt{
|
|
Stmt: out,
|
|
StmtLocation: loc,
|
|
StmtLen: end - loc,
|
|
},
|
|
})
|
|
loc = end
|
|
}
|
|
|
|
return stmts, nil
|
|
}
|
|
|
|
func statementEnd(blob []byte, start int) int {
|
|
for i := start; i < len(blob); i++ {
|
|
switch blob[i] {
|
|
case '\'', '"', '`':
|
|
i = skipQuoted(blob, i)
|
|
case '-':
|
|
if i+1 < len(blob) && blob[i+1] == '-' {
|
|
i = skipLineComment(blob, i)
|
|
}
|
|
case '#':
|
|
i = skipLineComment(blob, i)
|
|
case '/':
|
|
if i+1 < len(blob) && blob[i+1] == '*' {
|
|
i = skipBlockComment(blob, i)
|
|
}
|
|
case ';':
|
|
return i + 1
|
|
}
|
|
}
|
|
return len(blob)
|
|
}
|
|
|
|
func skipQuoted(blob []byte, i int) int {
|
|
q := blob[i]
|
|
for j := i + 1; j < len(blob); j++ {
|
|
switch blob[j] {
|
|
case '\\':
|
|
j++
|
|
case q:
|
|
if j+1 < len(blob) && blob[j+1] == q {
|
|
j++
|
|
continue
|
|
}
|
|
return j
|
|
}
|
|
}
|
|
return len(blob) - 1
|
|
}
|
|
|
|
func skipLineComment(blob []byte, i int) int {
|
|
for j := i; j < len(blob); j++ {
|
|
if blob[j] == '\n' {
|
|
return j
|
|
}
|
|
}
|
|
return len(blob) - 1
|
|
}
|
|
|
|
func skipBlockComment(blob []byte, i int) int {
|
|
for j := i + 2; j < len(blob); j++ {
|
|
if blob[j] == '*' && j+1 < len(blob) && blob[j+1] == '/' {
|
|
return j + 1
|
|
}
|
|
}
|
|
return len(blob) - 1
|
|
}
|
|
|
|
// https://clickhouse.com/docs/en/sql-reference/syntax#comments
|
|
func (p *Parser) CommentSyntax() source.CommentSyntax {
|
|
return source.CommentSyntax{
|
|
Dash: true, // -- comment
|
|
SlashStar: true, // /* comment */
|
|
Hash: true, // # comment (ClickHouse supports this)
|
|
}
|
|
}
|