@@ -3,6 +3,7 @@ package sqlite
33import (
44 "errors"
55 "io"
6+ "strings"
67
78 meyer "github.com/sqlc-dev/meyer/ast"
89 "github.com/sqlc-dev/meyer/parser"
@@ -31,7 +32,7 @@ func (p *Parser) Parse(r io.Reader) ([]ast.Statement, error) {
3132 return nil , err
3233 }
3334 src := string (blob )
34- parsed , err := parseOptions .ParseString (src )
35+ parsed , err := parseOptions .ParseString (terminateNamedQueries ( src ) )
3536 if err != nil {
3637 return nil , normalizeErr (err )
3738 }
@@ -58,6 +59,35 @@ func (p *Parser) Parse(r io.Reader) ([]ast.Statement, error) {
5859 return stmts , nil
5960}
6061
62+ // terminateNamedQueries adds a virtual terminator before a subsequent sqlc
63+ // query annotation when the preceding query omitted one. sqlc annotations
64+ // delimit queries for the other engines, and a query file may therefore
65+ // contain multiple valid queries even though the complete SQLite script would
66+ // otherwise be invalid. Replacing the newline immediately before the
67+ // annotation preserves every byte offset reported by the parser.
68+ func terminateNamedQueries (src string ) string {
69+ terminated := []byte (src )
70+ for lineStart := 0 ; lineStart < len (terminated ); {
71+ lineEnd := strings .IndexByte (src [lineStart :], '\n' )
72+ if lineEnd < 0 {
73+ lineEnd = len (terminated )
74+ } else {
75+ lineEnd += lineStart
76+ }
77+ if lineStart > 0 && strings .HasPrefix (src [lineStart :lineEnd ], "-- name: " ) {
78+ i := lineStart - 1
79+ for i >= 0 && isSpace (terminated [i ]) {
80+ i --
81+ }
82+ if i >= 0 && terminated [i ] != ';' {
83+ terminated [lineStart - 1 ] = ';'
84+ }
85+ }
86+ lineStart = lineEnd + 1
87+ }
88+ return string (terminated )
89+ }
90+
6191// trimTerminator returns the end of stmt with its terminating semicolon, and
6292// any space before it, removed. A statement's span runs through the
6393// semicolon, but sqlc's statement text does not include it.
0 commit comments