sqlparse

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 3, 2026 License: MIT Imports: 1 Imported by: 0

Documentation

Overview

Package sqlparse provides a lightweight MySQL tokenizer.

It is not a full parser. Its job is to answer the questions guard and the editor ask — where does a statement start and end, what kind is it, does it have a top-level WHERE — with enough precision that literals, comments and quoted identifiers can never be mistaken for syntax.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AppendLimit

func AppendLimit(stmt Statement, n int) string

AppendLimit returns stmt's SQL with "LIMIT n" inserted, or the SQL unchanged when doing so would be unsafe.

Insertion is by token position rather than string concatenation, because the two shapes that break naive appending are common:

  • a trailing comment would swallow the clause
  • a locking clause (FOR UPDATE, LOCK IN SHARE MODE) must follow LIMIT, not precede it

Shapes this cannot place the clause in confidently — SELECT ... INTO — are returned untouched. An over-large result set is an inconvenience; a query rewritten into invalid SQL destroys trust in the tool.

func IsIdentifierByte

func IsIdentifierByte(c byte) bool

IsIdentifierByte reports whether a byte can appear in an unquoted MySQL identifier.

It is exported so that cursor movement in the editor uses the same notion of a word as the tokenizer does. Without that, "user_id" would be one word to completion and three to the arrow keys, and the difference would be impossible for a user to account for.

func QuoteIdentifier

func QuoteIdentifier(name string) string

QuoteIdentifier renders a name as a backtick-quoted MySQL identifier.

A backtick inside a name is escaped by doubling it, which is MySQL's own rule. Without this, a table called "we`ird" would end the quoted section early and turn the rest of the name into syntax.

It lives here rather than beside either of its callers because both the catalog and the query stream paste identifiers into statements, and a second copy of this rule is a second place for it to be got wrong.

Types

type CompletionContext

type CompletionContext struct {
	Kind CompletionKind
	// Prefix is the partial identifier already typed.
	Prefix string
	// Qualifier is the name before the dot, when Kind is CompleteQualified.
	Qualifier string
	// Tables are the statement's tables, so a qualifier can be resolved
	// without parsing again.
	Tables []TableRef
	// ReplaceFrom and ReplaceTo delimit the text a chosen candidate replaces.
	ReplaceFrom, ReplaceTo int
}

CompletionContext describes what to offer at a caret position.

func CompletionAt

func CompletionAt(sql string, offset int) CompletionContext

CompletionAt analyses the caret position within sql.

The whole statement is parsed, not just the text before the caret: in "SELECT u.| FROM users u" the meaning of "u" is established after the caret, and looking only backwards would leave it unresolvable.

type CompletionKind

type CompletionKind int

CompletionKind says what sort of name belongs at the caret.

const (
	// CompleteNone means nothing should be offered — inside a literal, a
	// comment, or before any clause has established a context.
	CompleteNone CompletionKind = iota
	// CompleteTable means a table name is expected.
	CompleteTable
	// CompleteColumn means a column of the statement's tables is expected.
	CompleteColumn
	// CompleteQualified means the caret follows "something.", where the
	// qualifier is either a table alias or a schema name.
	CompleteQualified
)

type Kind

type Kind int

Kind classifies a token.

const (
	// Word is a bare identifier or keyword.
	Word Kind = iota
	// Ident is a backtick-quoted identifier.
	Ident
	// String is a quoted literal.
	String
	// Number is a numeric literal.
	Number
	// Punct is an operator, separator or parenthesis.
	Punct
	// Comment is text the server ignores.
	Comment
)

func (Kind) String

func (k Kind) String() string

type Statement

type Statement struct {
	SQL      string
	Pos, End int
	Tokens   []Token
}

Statement is one SQL statement with its tokens and its span in the source buffer. The span lets the editor highlight exactly what will run.

func Parse

func Parse(sql string) Statement

Parse tokenizes a single statement.

func Split

func Split(sql string) []Statement

Split breaks sql into statements on unquoted semicolons. Blank statements and comment-only fragments are dropped.

func StatementAt

func StatementAt(sql string, offset int) (Statement, bool)

StatementAt returns the statement containing the byte offset, which is how Ctrl+Enter decides what to run.

Whitespace between statements attaches to the preceding one: a cursor resting just after "SELECT 1;" belongs to that statement, not to whatever comes next. Running the following statement there would be a surprise.

func (Statement) HasTopLevelLimit

func (s Statement) HasTopLevelLimit() bool

HasTopLevelLimit reports whether the statement already limits its result, which is what stops the auto-limit from overriding an explicit choice.

func (Statement) HasTopLevelWhere

func (s Statement) HasTopLevelWhere() bool

HasTopLevelWhere reports whether the statement is bounded by a WHERE clause of its own, ignoring any that belong to subqueries.

func (Statement) IsEmpty

func (s Statement) IsEmpty() bool

IsEmpty reports whether the statement carries no executable text.

func (Statement) Kind

func (s Statement) Kind() StmtKind

Kind reports what the statement does.

func (Statement) Verb added in v0.2.0

func (s Statement) Verb() string

Verb is the statement's leading keyword, upper-cased, or "" if it has none. Kind is the right question almost everywhere; this exists for the few places that have to tell two statements of one kind apart.

type StmtKind

type StmtKind int

StmtKind classifies a statement by what it does to the server.

const (
	// StmtOther is anything not recognised. Guard treats it as unsafe.
	StmtOther StmtKind = iota
	// StmtSelect reads rows.
	StmtSelect
	// StmtRead is a non-SELECT read: SHOW, DESCRIBE, EXPLAIN.
	StmtRead
	// StmtSession changes connection state: USE, SET, LOCK TABLES.
	StmtSession
	// StmtTransaction opens, ends or marks a transaction. It is separate from
	// StmtSession because the caller's answer differs: session state is lost,
	// whereas an abandoned transaction can hold locks after it is lost.
	StmtTransaction
	// StmtInsert adds rows (INSERT, REPLACE).
	StmtInsert
	// StmtUpdate modifies rows.
	StmtUpdate
	// StmtDelete removes rows and can be bounded by WHERE.
	StmtDelete
	// StmtTruncate empties a table and cannot be bounded or rolled back.
	StmtTruncate
	// StmtDrop removes a schema object.
	StmtDrop
	// StmtDDL is any other schema change: CREATE, ALTER, RENAME.
	StmtDDL
)

func (StmtKind) ReturnsRows added in v0.2.0

func (k StmtKind) ReturnsRows() bool

ReturnsRows reports whether a statement of this kind answers with a result set rather than with a count of what it changed.

The decision has to be made before the statement is sent: a write sent as a query yields a result set with no columns, and the count the server reported is gone by the time anyone could ask for it.

Anything not definitely a write is treated as returning rows. Sending a query as a query costs nothing when it turns out to have no rows, whereas sending something like CALL as a write discards the rows it did produce — so the uncertain case goes the way that cannot lose anything.

func (StmtKind) String

func (k StmtKind) String() string

type TableRef

type TableRef struct {
	Schema string
	Name   string
	Alias  string
	// Derived marks a subquery: it has an alias but no name to look up.
	Derived bool
}

TableRef is one entry of a FROM, JOIN, UPDATE or INSERT clause.

func ResolveQualifier

func ResolveQualifier(refs []TableRef, qualifier string) (TableRef, bool)

ResolveQualifier finds the table a qualifier stands for.

An alias wins; failing that, a table answers to its own name, which is what makes "users.id" work in a statement that never aliased users.

func TableRefs

func TableRefs(stmt Statement) []TableRef

TableRefs lists the tables a statement reads or writes.

Only the outermost level is walked. A derived table contributes its alias so that "s." resolves to something, but its inner tables are not in scope for the outer query and must not be offered.

type Token

type Token struct {
	Kind Kind
	Text string
	// Pos and End delimit the token in the input as a half-open byte range.
	Pos, End int
	// Depth is the parenthesis nesting level at the token's start. A
	// top-level clause has Depth 0; anything inside a subquery is deeper.
	Depth int
}

Token is a single lexical element with its source span.

func Tokenize

func Tokenize(sql string) []Token

Tokenize splits sql into tokens.

Version-hint comments (/*! ... */) are unwrapped rather than skipped: MySQL executes their contents, so guard has to see them.

func (Token) IsKeyword

func (t Token) IsKeyword(name string) bool

IsKeyword reports whether the token is a bare word equal to name, case-insensitively. Backtick-quoted identifiers never match, which is what makes `SELECT ` + "`where`" + ` FROM t` safe to analyse.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL