Skip to content
8 changes: 4 additions & 4 deletions internal/cmd/shim.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,13 @@ func pluginQueries(r *compiler.Result) []*plugin.Query {
}
}
out = append(out, &plugin.Query{
Name: q.Name,
Cmd: q.Cmd,
Name: q.Metadata.Name,
Cmd: q.Metadata.Cmd,
Text: q.SQL,
Comments: q.Comments,
Comments: q.Metadata.Comments,
Columns: columns,
Params: params,
Filename: q.Filename,
Filename: q.Metadata.Filename,
InsertIntoTable: iit,
})
}
Expand Down
2 changes: 1 addition & 1 deletion internal/cmd/vet.go
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ func (c *checker) checkSQL(ctx context.Context, s config.SQL) error {
req := codeGenRequest(result, combo)
cfg := vetConfig(req)
for i, query := range req.Queries {
if result.Queries[i].Flags[QueryFlagSqlcVetDisable] {
if result.Queries[i].Metadata.Flags[QueryFlagSqlcVetDisable] {
if debug.Active {
log.Printf("Skipping vet rules for query: %s\n", query.Name)
}
Expand Down
11 changes: 6 additions & 5 deletions internal/compiler/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,15 @@ func (c *Compiler) parseQueries(o opts.Parser) (*Result, error) {
merr.Add(filename, src, loc, err)
continue
}
if query.Name != "" {
if _, exists := set[query.Name]; exists {
merr.Add(filename, src, stmt.Raw.Pos(), fmt.Errorf("duplicate query name: %s", query.Name))
queryName := query.Metadata.Name
if queryName != "" {
if _, exists := set[queryName]; exists {
merr.Add(filename, src, stmt.Raw.Pos(), fmt.Errorf("duplicate query name: %s", queryName))
continue
}
set[query.Name] = struct{}{}
set[queryName] = struct{}{}
}
query.Filename = filepath.Base(filename)
query.Metadata.Filename = filepath.Base(filename)
if query != nil {
q = append(q, query)
}
Expand Down
17 changes: 5 additions & 12 deletions internal/compiler/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@ func (c *Compiler) parseQuery(stmt ast.Node, src string, o opts.Parser) (*Query,
return nil, errors.New("missing semicolon at end of file")
}

name, cmd, err := metadata.ParseQueryNameAndType(strings.TrimSpace(rawSQL), c.parser.CommentSyntax())
md, err := metadata.ParseQueryMetadata(rawSQL, c.parser.CommentSyntax())
if err != nil {
return nil, err
}
if err := validate.Cmd(raw.Stmt, name, cmd); err != nil {

if err := validate.Cmd(raw.Stmt, md.Name, md.Cmd); err != nil {
return nil, err
}

Expand Down Expand Up @@ -85,22 +86,14 @@ func (c *Compiler) parseQuery(stmt ast.Node, src string, o opts.Parser) (*Query,
}
}

trimmed, comments, err := source.StripComments(expanded)
if err != nil {
return nil, err
}

flags, err := metadata.ParseQueryFlags(comments)
trimmed, err := source.StripComments(expanded)
if err != nil {
return nil, err
}

return &Query{
RawStmt: raw,
Cmd: cmd,
Comments: comments,
Name: name,
Flags: flags,
Metadata: md,
Params: anlys.Parameters,
Columns: anlys.Columns,
SQL: trimmed,
Expand Down
9 changes: 2 additions & 7 deletions internal/compiler/query.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package compiler

import (
"github.com/sqlc-dev/sqlc/internal/metadata"
"github.com/sqlc-dev/sqlc/internal/sql/ast"
)

Expand Down Expand Up @@ -41,15 +42,9 @@ type Column struct {

type Query struct {
SQL string
Name string
Cmd string // TODO: Pick a better name. One of: one, many, exec, execrows, copyFrom
Flags map[string]bool
Metadata metadata.Metadata
Columns []*Column
Params []Parameter
Comments []string

// XXX: Hack
Filename string

// Needed for CopyFrom
InsertIntoTable *ast.TableName
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# package querytest
query.sql:1:1: invalid query comment: -- name: ListFoos
query.sql:1:1: missing query type [':one', ':many', ':exec', ':execrows', ':execlastid', ':execresult', ':copyfrom', 'batchexec', 'batchmany', 'batchone']: -- name: ListFoos
query.sql:5:1: invalid query comment: -- name: ListFoos :one :many
query.sql:8:1: invalid query type: :two
query.sql:11:1: query "DeleteFoo" specifies parameter ":one" without containing a RETURNING clause
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# package querytest
query.sql:1:1: invalid query comment: -- name: ListFoos
query.sql:1:1: missing query type [':one', ':many', ':exec', ':execrows', ':execlastid', ':execresult', ':copyfrom', 'batchexec', 'batchmany', 'batchone']: -- name: ListFoos
query.sql:5:1: invalid query comment: -- name: ListFoos :one :many
query.sql:8:1: invalid query type: :two
query.sql:11:1: query "DeleteFoo" specifies parameter ":one" without containing a RETURNING clause
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# package querytest
query.sql:1:1: invalid query comment: -- name: ListFoos
query.sql:1:1: missing query type [':one', ':many', ':exec', ':execrows', ':execlastid', ':execresult', ':copyfrom', 'batchexec', 'batchmany', 'batchone']: -- name: ListFoos
query.sql:5:1: invalid query comment: -- name: ListFoos :one :many
query.sql:8:1: invalid query type: :two
query.sql:11:1: query "DeleteFoo" specifies parameter ":one" without containing a RETURNING clause
Expand Down
106 changes: 79 additions & 27 deletions internal/metadata/meta.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
package metadata

import (
"bufio"
"fmt"
"strings"
"unicode"
)

type Metadata struct {
Name string
Cmd string
Comments []string
Params map[string]string
Flags map[string]bool

Filename string
}

type CommentSyntax struct {
Dash bool
Hash bool
Expand Down Expand Up @@ -44,8 +55,12 @@ func validateQueryName(name string) error {
return nil
}

func ParseQueryNameAndType(t string, commentStyle CommentSyntax) (string, string, error) {
for _, line := range strings.Split(t, "\n") {
func ParseQueryMetadata(rawSql string, commentStyle CommentSyntax) (Metadata, error) {
md := Metadata{}
s := bufio.NewScanner(strings.NewReader(strings.TrimSpace(rawSql)))
var comments []string
for s.Scan() {
line := s.Text()
var prefix string
if strings.HasPrefix(line, "--") {
if !commentStyle.Dash {
Expand All @@ -68,55 +83,92 @@ func ParseQueryNameAndType(t string, commentStyle CommentSyntax) (string, string
if prefix == "" {
continue
}

rest := line[len(prefix):]
rest = strings.TrimSuffix(rest, "*/")
comments = append(comments, rest)

if !strings.HasPrefix(strings.TrimSpace(rest), "name") {
continue
}
if !strings.Contains(rest, ":") {
continue
}
if !strings.HasPrefix(rest, " name: ") {
return "", "", fmt.Errorf("invalid metadata: %s", line)
return md, fmt.Errorf("invalid metadata: %s", line)
}

part := strings.Split(strings.TrimSpace(line), " ")
if prefix == "/*" {
part = part[:len(part)-1] // removes the trailing "*/" element
}
if len(part) == 2 {
return "", "", fmt.Errorf("missing query type [':one', ':many', ':exec', ':execrows', ':execlastid', ':execresult', ':copyfrom', 'batchexec', 'batchmany', 'batchone']: %s", line)
comments = comments[:len(comments)-1] // Remove tha name line from returned comments

parts := strings.Split(strings.TrimSpace(rest), " ")

if len(parts) == 2 {
return md, fmt.Errorf("missing query type [':one', ':many', ':exec', ':execrows', ':execlastid', ':execresult', ':copyfrom', 'batchexec', 'batchmany', 'batchone']: %s", line)
}
if len(part) != 4 {
return "", "", fmt.Errorf("invalid query comment: %s", line)
if len(parts) > 3 {
return md, fmt.Errorf("invalid query comment: %s", line)
}
queryName := part[2]
queryType := strings.TrimSpace(part[3])
queryName := parts[1]
queryType := parts[2]
switch queryType {
case CmdOne, CmdMany, CmdExec, CmdExecResult, CmdExecRows, CmdExecLastId, CmdCopyFrom, CmdBatchExec, CmdBatchMany, CmdBatchOne:
default:
return "", "", fmt.Errorf("invalid query type: %s", queryType)
return md, fmt.Errorf("invalid query type: %s", queryType)
}
if err := validateQueryName(queryName); err != nil {
return "", "", err
return md, err
}
return queryName, queryType, nil
md.Name = queryName
md.Cmd = queryType
}

md.Comments = comments

var err error
md.Params, md.Flags, err = parseParamsAndFlags(md.Comments)
if err != nil {
return md, err
}
return "", "", nil

return md, s.Err()
}

func ParseQueryFlags(comments []string) (map[string]bool, error) {
func parseParamsAndFlags(comments []string) (map[string]string, map[string]bool, error) {
params := make(map[string]string)
flags := make(map[string]bool)

for _, line := range comments {
cleanLine := strings.TrimPrefix(line, "--")
cleanLine = strings.TrimPrefix(cleanLine, "/*")
cleanLine = strings.TrimPrefix(cleanLine, "#")
cleanLine = strings.TrimSuffix(cleanLine, "*/")
cleanLine = strings.TrimSpace(cleanLine)
if strings.HasPrefix(cleanLine, "@") {
flagName := strings.SplitN(cleanLine, " ", 2)[0]
flags[flagName] = true
s := bufio.NewScanner(strings.NewReader(line))
s.Split(bufio.ScanWords)

s.Scan()
token := s.Text()

if !strings.HasPrefix(token, "@") {
continue
}

switch token {
case "@param":
s.Scan()
name := s.Text()
var rest []string
for s.Scan() {
paramToken := s.Text()
if paramToken == "*/" {
break
}
rest = append(rest, paramToken)
}
params[name] = strings.Join(rest, " ")
default:
flags[token] = true
}

if s.Err() != nil {
return params, flags, s.Err()
}
}
return flags, nil

return params, flags, nil
}
Loading