From 4137dd87c523427dcc5372574dba39e1a75af0e8 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 20:05:30 +0000 Subject: [PATCH] endtoend: pin sqlite query text with multi-byte UTF-8 comments The ANTLR-based sqlite parser mixed rune-counted and byte-counted offsets when slicing query text out of the source file, so a multi-byte character in a comment silently truncated the generated query string and leaked fragments of neighboring statements into it. The meyer parser tracks byte offsets throughout, which fixed this; this case keeps it fixed. Covers the three reported shapes: an em dash in a comment directly above the annotation, a standalone comment between two queries, and CJK comments. Fixes #4523 Fixes #4235 Fixes #4372 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_017bkCyEYanANjRyamjUF8ZP --- .../multibyte_comment/sqlite/go/db.go | 31 +++++++ .../multibyte_comment/sqlite/go/models.go | 11 +++ .../multibyte_comment/sqlite/go/query.sql.go | 85 +++++++++++++++++++ .../multibyte_comment/sqlite/query.sql | 20 +++++ .../multibyte_comment/sqlite/schema.sql | 5 ++ .../multibyte_comment/sqlite/sqlc.json | 12 +++ 6 files changed, 164 insertions(+) create mode 100644 internal/endtoend/testdata/multibyte_comment/sqlite/go/db.go create mode 100644 internal/endtoend/testdata/multibyte_comment/sqlite/go/models.go create mode 100644 internal/endtoend/testdata/multibyte_comment/sqlite/go/query.sql.go create mode 100644 internal/endtoend/testdata/multibyte_comment/sqlite/query.sql create mode 100644 internal/endtoend/testdata/multibyte_comment/sqlite/schema.sql create mode 100644 internal/endtoend/testdata/multibyte_comment/sqlite/sqlc.json diff --git a/internal/endtoend/testdata/multibyte_comment/sqlite/go/db.go b/internal/endtoend/testdata/multibyte_comment/sqlite/go/db.go new file mode 100644 index 0000000000..80dd6ab1f6 --- /dev/null +++ b/internal/endtoend/testdata/multibyte_comment/sqlite/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 + +package querytest + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/multibyte_comment/sqlite/go/models.go b/internal/endtoend/testdata/multibyte_comment/sqlite/go/models.go new file mode 100644 index 0000000000..da152e22e1 --- /dev/null +++ b/internal/endtoend/testdata/multibyte_comment/sqlite/go/models.go @@ -0,0 +1,11 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 + +package querytest + +type Item struct { + ID int64 + Name string + CapRead string +} diff --git a/internal/endtoend/testdata/multibyte_comment/sqlite/go/query.sql.go b/internal/endtoend/testdata/multibyte_comment/sqlite/go/query.sql.go new file mode 100644 index 0000000000..af8e925bb7 --- /dev/null +++ b/internal/endtoend/testdata/multibyte_comment/sqlite/go/query.sql.go @@ -0,0 +1,85 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 +// source: query.sql + +package querytest + +import ( + "context" +) + +const createItem = `-- name: CreateItem :exec +INSERT INTO items (name, cap_read) +VALUES (?, ?) +` + +type CreateItemParams struct { + Name string + CapRead string +} + +// 当新建条目时,需要写入名称和权限 +func (q *Queries) CreateItem(ctx context.Context, arg CreateItemParams) error { + _, err := q.db.ExecContext(ctx, createItem, arg.Name, arg.CapRead) + return err +} + +const deleteItem = `-- name: DeleteItem :one +DELETE FROM items WHERE id = ? RETURNING id, name, cap_read +` + +func (q *Queries) DeleteItem(ctx context.Context, id int64) (Item, error) { + row := q.db.QueryRowContext(ctx, deleteItem, id) + var i Item + err := row.Scan(&i.ID, &i.Name, &i.CapRead) + return i, err +} + +const listItems = `-- name: ListItems :many + +SELECT id, name, cap_read +FROM items WHERE cap_read = 'anonymous' ORDER BY name +` + +// Multi-byte UTF-8 in comments must not shift the byte offsets used to slice +// query text out of the source file (#4523, #4235, #4372). +// an em dash right here — must not truncate the ORDER BY below +func (q *Queries) ListItems(ctx context.Context) ([]Item, error) { + rows, err := q.db.QueryContext(ctx, listItems) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Item + for rows.Next() { + var i Item + if err := rows.Scan(&i.ID, &i.Name, &i.CapRead); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateItem = `-- name: UpdateItem :exec + +UPDATE items SET name = ? WHERE id = ? +` + +type UpdateItemParams struct { + Name string + ID int64 +} + +// section — divider between queries +func (q *Queries) UpdateItem(ctx context.Context, arg UpdateItemParams) error { + _, err := q.db.ExecContext(ctx, updateItem, arg.Name, arg.ID) + return err +} diff --git a/internal/endtoend/testdata/multibyte_comment/sqlite/query.sql b/internal/endtoend/testdata/multibyte_comment/sqlite/query.sql new file mode 100644 index 0000000000..6a191d9b57 --- /dev/null +++ b/internal/endtoend/testdata/multibyte_comment/sqlite/query.sql @@ -0,0 +1,20 @@ +-- Multi-byte UTF-8 in comments must not shift the byte offsets used to slice +-- query text out of the source file (#4523, #4235, #4372). + +-- name: ListItems :many +-- an em dash right here — must not truncate the ORDER BY below +SELECT id, name, cap_read +FROM items WHERE cap_read = 'anonymous' ORDER BY name; + +-- section — divider between queries + +-- name: UpdateItem :exec +UPDATE items SET name = ? WHERE id = ?; + +-- name: CreateItem :exec +-- 当新建条目时,需要写入名称和权限 +INSERT INTO items (name, cap_read) +VALUES (?, ?); + +-- name: DeleteItem :one +DELETE FROM items WHERE id = ? RETURNING id, name, cap_read; diff --git a/internal/endtoend/testdata/multibyte_comment/sqlite/schema.sql b/internal/endtoend/testdata/multibyte_comment/sqlite/schema.sql new file mode 100644 index 0000000000..cb0dedc01a --- /dev/null +++ b/internal/endtoend/testdata/multibyte_comment/sqlite/schema.sql @@ -0,0 +1,5 @@ +CREATE TABLE items ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + cap_read TEXT NOT NULL +); diff --git a/internal/endtoend/testdata/multibyte_comment/sqlite/sqlc.json b/internal/endtoend/testdata/multibyte_comment/sqlite/sqlc.json new file mode 100644 index 0000000000..cd66df063b --- /dev/null +++ b/internal/endtoend/testdata/multibyte_comment/sqlite/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "sqlite", + "name": "querytest", + "schema": "schema.sql", + "queries": "query.sql" + } + ] +}