sqlanalysis

package
v3.23.1 Latest Latest
Warning

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

Go to latest
Published: Sep 14, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package sqlanalysis reads out of a SQL statement the four things semantic matching compares against: the operation, the tables touched, the columns and values constrained in the WHERE clause, and the values an INSERT or UPDATE writes.

It is deliberately regex-based rather than a parser. What the EXPECT clauses assert is shallow - which table, which operation, which columns - and a full grammar per dialect would be a large dependency to answer a small question. The cost of that choice is that exotic SQL reads as no SQL rather than as wrong SQL, which fails a spec loudly instead of matching the wrong mock.

Every proxy that speaks a SQL protocol uses this package. Before it existed the MySQL and PostgreSQL proxies each carried their own copy and the two had drifted: one captured a dotted table prefix and resolved binds, the other could not and did not. Differences that are genuinely about the database live on Dialect, where they are named; anything else is one behaviour.

Index

Constants

View Source
const PresentSentinel = "PRESENT"

PresentSentinel is the value reported for a column constrained by a bind whose value is not available. VERIFY_WHERE compares against it when a spec cares that a column was constrained but not what it was constrained to.

Variables

View Source
var (
	PostgreSQL = Dialect{
		Name:            "postgresql",
		Bind:            BindPositional,
		Quote:           `"`,
		SchemaQualified: true,
		Where:           ScopeWholeStatement,
		DedupeColumns:   true,
	}

	MySQL = Dialect{
		Name:            "mysql",
		Bind:            BindAnonymous,
		Quote:           "`",
		SchemaQualified: false,
		Where:           ScopeAfterWhere,
		DedupeColumns:   false,
	}

	Oracle = Dialect{
		Name: "oracle",
		Bind: BindNamed,

		Quote:           `"`,
		SchemaQualified: true,

		Where:         ScopeWholeStatement,
		DedupeColumns: true,
	}
)

Functions

func Operation

func Operation(query string) string

Operation returns the DML verb a statement performs, or "" if it performs none. WITH reports SELECT: a common table expression is a read however much machinery precedes it.

func Tables

func Tables(d Dialect, query string, known []string) []string

Tables returns which of the known table names a statement references. It is a containment test rather than a parse: the proxy already knows every table in the schema, so the question is which of them appear, and a name appearing only inside a longer identifier does not count.

Types

type BindStyle

type BindStyle int

BindStyle is how a dialect spells a placeholder in a statement.

const (
	// BindPositional is PostgreSQL's $1, $2 - resolvable against an ordered
	// list of parameter values.
	BindPositional BindStyle = iota
	// BindAnonymous is MySQL's ?, which carries no identity, so a value can
	// only be resolved by counting placeholders.
	BindAnonymous
	// BindNamed is Oracle's :Name, resolvable against a map.
	BindNamed
)

type Binds

type Binds struct {
	// Positional holds $1/? values in order.
	Positional []string
	// Named holds :Name values by name, without the leading colon.
	Named map[string]string
}

Binds carries whatever parameter values the proxy managed to recover. Both fields may be empty: a dialect that cannot recover values still reports which columns were constrained, using PresentSentinel for the value.

type Dialect

type Dialect struct {
	Name string

	// Bind is how a placeholder is spelled.
	Bind BindStyle

	// Quote holds the characters that may wrap an identifier, if any.
	Quote string

	// SchemaQualified is whether a table may be written schema.table, which
	// changes what an INSERT INTO target looks like and how a table reference
	// is recognised.
	SchemaQualified bool

	// Where is how much of the statement is scanned for conditions.
	Where WhereScope

	// DedupeColumns reports each constrained column once even when it appears
	// in several conditions.
	DedupeColumns bool
}

Dialect names the differences between databases that this package has to know about. Nothing else belongs here: a difference two proxies happen to have is drift to resolve, not a dialect.

type Result

type Result struct {
	Operation     string
	WhereColumns  []string
	WhereValues   map[string]string
	WrittenValues map[string]string
}

Result is what a statement says about itself.

func Analyze

func Analyze(d Dialect, query string, binds Binds) Result

Analyze reads a statement under a dialect.

type WhereScope

type WhereScope int

WhereScope is how much of a statement is scanned for WHERE conditions.

const (
	// ScopeWholeStatement scans everywhere, so a condition inside a subquery
	// or a JOIN ... ON is reported alongside the outer WHERE. Correct for a
	// dialect whose pagination nests the real query inside a wrapper.
	ScopeWholeStatement WhereScope = iota
	// ScopeAfterWhere scans only the text following WHERE, stopping at the
	// first ORDER/LIMIT/GROUP/HAVING.
	ScopeAfterWhere
)

Jump to

Keyboard shortcuts

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