Documentation
¶
Overview ¶
Package keywords provides SQL keyword definitions and categorization for multiple SQL dialects.
This package offers comprehensive SQL keyword management with support for multiple database dialects including PostgreSQL, MySQL, SQL Server, Oracle, and SQLite. It handles keyword categorization, case-insensitive matching, and dialect-specific extensions.
Key Features ¶
- Multi-dialect keyword support (PostgreSQL, MySQL, SQLite, SQL Server, Oracle)
- Case-insensitive keyword matching (SQL standard behavior)
- Comprehensive keyword categorization (reserved, DML, DDL, window functions)
- Compound keyword recognition (e.g., "GROUP BY", "GROUPING SETS")
- v1.6.0 PostgreSQL extensions (LATERAL, FILTER, RETURNING, MATERIALIZED)
- Window function keywords (OVER, PARTITION BY, ROWS, RANGE, etc.)
- SQL-99 grouping operations (ROLLUP, CUBE, GROUPING SETS)
- MERGE statement support (SQL:2003 F312)
Keyword Categories ¶
Keywords are organized into several categories:
- Reserved Keywords: Cannot be used as identifiers (SELECT, FROM, WHERE, etc.)
- Table Alias Reserved: Keywords reserved specifically for table alias context
- DML Keywords: Data Manipulation Language keywords (INSERT, UPDATE, DELETE)
- DDL Keywords: Data Definition Language keywords (CREATE, ALTER, DROP)
- Window Function Keywords: Window function specific keywords (OVER, PARTITION BY, etc.)
- Aggregate Keywords: Aggregate function keywords (COUNT, SUM, AVG, MIN, MAX)
- Compound Keywords: Multi-word keywords (GROUP BY, ORDER BY, GROUPING SETS)
SQL Dialects ¶
The package supports multiple SQL dialects with dialect-specific keywords:
- DialectGeneric: Standard SQL keywords common across all dialects
- DialectPostgreSQL: PostgreSQL-specific keywords (ILIKE, MATERIALIZED, LATERAL, RETURNING)
- DialectMySQL: MySQL-specific keywords (ZEROFILL, UNSIGNED, FORCE)
- DialectSQLite: SQLite-specific keywords (AUTOINCREMENT, VACUUM)
New in v1.6.0 ¶
PostgreSQL Extensions:
- LATERAL: Correlated subqueries in FROM clause
- FILTER: Conditional aggregation (SQL:2003 T612)
- RETURNING: Return modified rows from INSERT/UPDATE/DELETE
- MATERIALIZED: Materialized view support
- DISTINCT ON: PostgreSQL-specific row selection
DDL Operations:
- TRUNCATE: TRUNCATE TABLE statement (SQL:2008)
- FETCH: FETCH FIRST/NEXT clause (SQL-99 F861, F862)
- OFFSET: Result set pagination
Grouping Operations:
- ROLLUP: Hierarchical subtotals (SQL-99 T431)
- CUBE: All possible grouping combinations (SQL-99 T431)
- GROUPING SETS: Explicit grouping combinations (SQL-99 T431)
Basic Usage ¶
Create a keywords instance and check for keyword recognition:
// Create keywords for generic SQL dialect
kw := keywords.New(keywords.DialectGeneric, true)
// Check if a word is a keyword
if kw.IsKeyword("SELECT") {
fmt.Println("SELECT is a keyword")
}
// Get the token type for a keyword
tokenType := kw.GetTokenType("WHERE")
fmt.Printf("Token type: %v\n", tokenType)
// Check if a keyword is reserved
if kw.IsReserved("FROM") {
fmt.Println("FROM is reserved")
}
Dialect-Specific Keywords ¶
Use dialect-specific keyword recognition for PostgreSQL, MySQL, or SQLite:
// PostgreSQL dialect
pgKw := keywords.New(keywords.DialectPostgreSQL, true)
if pgKw.IsKeyword("LATERAL") {
fmt.Println("LATERAL is a PostgreSQL keyword")
}
// MySQL dialect
mysqlKw := keywords.New(keywords.DialectMySQL, true)
if mysqlKw.IsKeyword("ZEROFILL") {
fmt.Println("ZEROFILL is a MySQL keyword")
}
// SQLite dialect
sqliteKw := keywords.New(keywords.DialectSQLite, true)
if sqliteKw.IsKeyword("AUTOINCREMENT") {
fmt.Println("AUTOINCREMENT is a SQLite keyword")
}
Case-Insensitive Matching ¶
All keyword matching is case-insensitive by default, following SQL standard behavior:
kw := keywords.New(keywords.DialectGeneric, true)
// All of these are recognized as the same keyword
kw.IsKeyword("SELECT") // true
kw.IsKeyword("select") // true
kw.IsKeyword("Select") // true
kw.IsKeyword("SeLeCt") // true
Token Type Mapping ¶
Keywords map to specific token types for the parser:
kw := keywords.New(keywords.DialectGeneric, true)
// Get token type for keywords
selectType := kw.GetTokenType("SELECT") // models.TokenTypeSelect
fromType := kw.GetTokenType("FROM") // models.TokenTypeFrom
whereType := kw.GetTokenType("WHERE") // models.TokenTypeWhere
lateralType := kw.GetTokenType("LATERAL") // models.TokenTypeLateral (v1.6.0)
Compound Keywords ¶
Recognize multi-word SQL keywords:
kw := keywords.New(keywords.DialectGeneric, true) // Check compound keywords compoundKws := kw.GetCompoundKeywords() // Examples of compound keywords: // - "GROUP BY" // - "ORDER BY" // - "GROUPING SETS" (SQL-99) // - "MATERIALIZED VIEW" (PostgreSQL) // - "IF NOT EXISTS" // - "PARTITION BY"
Reserved vs Non-Reserved Keywords ¶
The package distinguishes between reserved and non-reserved keywords:
kw := keywords.New(keywords.DialectGeneric, true)
// Reserved keywords (cannot be used as identifiers)
kw.IsReserved("SELECT") // true - reserved
kw.IsReserved("FROM") // true - reserved
kw.IsReserved("WHERE") // true - reserved
// Non-reserved keywords (can be used as identifiers in some contexts)
kw.IsReserved("ROW_NUMBER") // false - window function name
kw.IsReserved("RANK") // false - window function name
kw.IsReserved("LAG") // false - window function name
Window Function Support ¶
Full support for SQL-99 window function keywords:
kw := keywords.New(keywords.DialectGeneric, true)
// Window specification keywords
kw.GetTokenType("OVER") // OVER clause
kw.GetTokenType("PARTITION") // PARTITION BY
kw.GetTokenType("ROWS") // ROWS frame mode
kw.GetTokenType("RANGE") // RANGE frame mode
// Frame boundary keywords
kw.GetTokenType("CURRENT") // CURRENT ROW
kw.GetTokenType("UNBOUNDED") // UNBOUNDED PRECEDING/FOLLOWING
kw.GetTokenType("PRECEDING") // N PRECEDING
kw.GetTokenType("FOLLOWING") // N FOLLOWING
// Window function names (non-reserved)
kw.IsKeyword("ROW_NUMBER") // true
kw.IsKeyword("RANK") // true
kw.IsKeyword("DENSE_RANK") // true
kw.IsKeyword("NTILE") // true
kw.IsKeyword("LAG") // true
kw.IsKeyword("LEAD") // true
kw.IsKeyword("FIRST_VALUE") // true
kw.IsKeyword("LAST_VALUE") // true
PostgreSQL JSON Operators ¶
While JSON operators (->>, @>, etc.) are handled by the tokenizer as operators rather than keywords, dialect-specific keyword support enables proper parsing of PostgreSQL JSON features in context.
Performance Considerations ¶
Keyword lookup is optimized with:
- Pre-computed hash maps for O(1) keyword lookup
- Case-insensitive matching with uppercase normalization
- Minimal memory footprint with shared keyword definitions
- No allocations during keyword checking operations
Thread Safety ¶
Keywords instances are safe for concurrent read access after initialization. Create separate instances for different dialects rather than modifying a shared instance.
Integration with Tokenizer ¶
This package is used by the tokenizer (pkg/sql/tokenizer) to classify words as keywords and assign appropriate token types during lexical analysis.
import (
"github.com/ajitpratap0/GoSQLX/pkg/sql/keywords"
"github.com/ajitpratap0/GoSQLX/pkg/sql/tokenizer"
)
// Create keywords for PostgreSQL
kw := keywords.New(keywords.DialectPostgreSQL, true)
// Create tokenizer with keyword support
tkz := tokenizer.GetTokenizer()
defer tokenizer.PutTokenizer(tkz)
// Tokenizer uses keywords to classify tokens
tokens, err := tkz.Tokenize([]byte("SELECT * FROM users WHERE active = true"))
SQL Standards Compliance ¶
The keyword definitions follow SQL standards:
- SQL-92: Core reserved keywords (SELECT, FROM, WHERE, etc.)
- SQL-99: Window functions, ROLLUP, CUBE, GROUPING SETS
- SQL:2003: MERGE statements, FILTER clause
- SQL:2008: TRUNCATE TABLE, FETCH FIRST/NEXT
- PostgreSQL 12+: LATERAL, MATERIALIZED, JSON operators
See Also ¶
- pkg/models: Token type definitions
- pkg/sql/tokenizer: Lexical analysis using keywords
- pkg/sql/parser: Parser using token types from keywords
- docs/SQL_COMPATIBILITY.md: Complete SQL compatibility matrix
Package keywords provides SQL keyword definitions and categorization for multiple SQL dialects. It includes reserved words, DDL/DML keywords, dialect-specific extensions, and window function keywords.
This file contains the core keyword collections and the New() constructor for creating keyword instances with dialect-specific support. See doc.go for comprehensive package documentation and examples.
Example ¶
Example demonstrates basic keyword detection and token type identification.
package main
import (
"fmt"
"github.com/ajitpratap0/GoSQLX/pkg/sql/keywords"
)
func main() {
// Create a keywords instance for generic SQL dialect
kw := keywords.New(keywords.DialectGeneric, true)
// Check if a word is a SQL keyword
if kw.IsKeyword("SELECT") {
fmt.Println("SELECT is a keyword")
}
// Get the token type for a keyword
tokenType := kw.GetTokenType("WHERE")
fmt.Printf("WHERE token type: %v\n", tokenType)
// Check if a keyword is reserved
if kw.IsReserved("FROM") {
fmt.Println("FROM is a reserved keyword")
}
}
Output: SELECT is a keyword WHERE token type: WHERE FROM is a reserved keyword
Example (CaseInsensitivity) ¶
Example_caseInsensitivity demonstrates case-insensitive keyword matching.
package main
import (
"fmt"
"github.com/ajitpratap0/GoSQLX/pkg/sql/keywords"
)
func main() {
// Create keywords with case-insensitive matching (default)
kw := keywords.New(keywords.DialectGeneric, true)
// All of these should match
examples := []string{"SELECT", "select", "Select", "SeLeCt"}
for _, word := range examples {
if kw.IsKeyword(word) {
fmt.Printf("%s is recognized as SELECT keyword\n", word)
}
}
}
Output: SELECT is recognized as SELECT keyword select is recognized as SELECT keyword Select is recognized as SELECT keyword SeLeCt is recognized as SELECT keyword
Example (DialectComparison) ¶
Example_dialectComparison demonstrates differences between SQL dialects.
package main
import (
"fmt"
"github.com/ajitpratap0/GoSQLX/pkg/sql/keywords"
)
func main() {
// Generic SQL
generic := keywords.New(keywords.DialectGeneric, true)
// PostgreSQL
postgres := keywords.New(keywords.DialectPostgreSQL, true)
// MySQL
mysql := keywords.New(keywords.DialectMySQL, true)
// Check dialect-specific keywords
testWords := []string{"ILIKE", "ZEROFILL", "MATERIALIZED"}
for _, word := range testWords {
genericMatch := generic.IsKeyword(word)
postgresMatch := postgres.IsKeyword(word)
mysqlMatch := mysql.IsKeyword(word)
fmt.Printf("%s: Generic=%v, PostgreSQL=%v, MySQL=%v\n",
word, genericMatch, postgresMatch, mysqlMatch)
}
}
Output: ILIKE: Generic=false, PostgreSQL=true, MySQL=false ZEROFILL: Generic=false, PostgreSQL=false, MySQL=true MATERIALIZED: Generic=false, PostgreSQL=true, MySQL=false
Example (DialectSupport) ¶
Example_dialectSupport demonstrates SQL dialect-specific keyword recognition.
package main
import (
"fmt"
"github.com/ajitpratap0/GoSQLX/pkg/sql/keywords"
)
func main() {
// Create keywords instance for PostgreSQL
pgKw := keywords.New(keywords.DialectPostgreSQL, true)
// PostgreSQL-specific keywords
if pgKw.IsKeyword("ILIKE") {
fmt.Println("ILIKE is a PostgreSQL keyword")
}
// Create keywords instance for MySQL
mysqlKw := keywords.New(keywords.DialectMySQL, true)
// MySQL-specific keywords
if mysqlKw.IsKeyword("ZEROFILL") {
fmt.Println("ZEROFILL is a MySQL keyword")
}
// Create keywords instance for SQLite
sqliteKw := keywords.New(keywords.DialectSQLite, true)
// SQLite-specific keywords
if sqliteKw.IsKeyword("AUTOINCREMENT") {
fmt.Println("AUTOINCREMENT is a SQLite keyword")
}
}
Output: ILIKE is a PostgreSQL keyword ZEROFILL is a MySQL keyword AUTOINCREMENT is a SQLite keyword
Example (ReservedVsNonReserved) ¶
Example_reservedVsNonReserved demonstrates distinction between reserved and non-reserved keywords.
package main
import (
"fmt"
"github.com/ajitpratap0/GoSQLX/pkg/sql/keywords"
)
func main() {
kw := keywords.New(keywords.DialectGeneric, true)
// Reserved keywords (cannot be used as identifiers)
reserved := []string{"SELECT", "FROM", "WHERE", "JOIN"}
fmt.Println("Reserved keywords:")
for _, word := range reserved {
if kw.IsReserved(word) {
fmt.Printf(" - %s\n", word)
}
}
// Non-reserved keywords (window functions - can be used as identifiers in some contexts)
nonReserved := []string{"ROW_NUMBER", "RANK", "DENSE_RANK"}
fmt.Println("Non-reserved keywords:")
for _, word := range nonReserved {
if kw.IsKeyword(word) && !kw.IsReserved(word) {
fmt.Printf(" - %s\n", word)
}
}
}
Output: Reserved keywords: - SELECT - FROM - WHERE - JOIN Non-reserved keywords: - ROW_NUMBER - RANK - DENSE_RANK
Example (TokenTypeMapping) ¶
Example_tokenTypeMapping demonstrates mapping keywords to token types.
package main
import (
"fmt"
"github.com/ajitpratap0/GoSQLX/pkg/models"
"github.com/ajitpratap0/GoSQLX/pkg/sql/keywords"
)
func main() {
kw := keywords.New(keywords.DialectGeneric, true)
// Different keywords map to different token types
keywords := map[string]models.TokenType{
"SELECT": models.TokenTypeSelect,
"FROM": models.TokenTypeFrom,
"WHERE": models.TokenTypeWhere,
"JOIN": models.TokenTypeJoin,
"GROUP": models.TokenTypeGroup,
"ORDER": models.TokenTypeOrder,
}
for word, expectedType := range keywords {
actualType := kw.GetTokenType(word)
if actualType == expectedType {
fmt.Printf("%s → %v ✓\n", word, actualType)
}
}
}
Output: SELECT → SELECT ✓ FROM → FROM ✓ WHERE → WHERE ✓ JOIN → JOIN ✓ GROUP → GROUP ✓ ORDER → ORDER ✓
Index ¶
- Variables
- func IsValidDialect(name string) bool
- type Keyword
- type KeywordCategory
- type Keywords
- func (k *Keywords) AddKeyword(keyword Keyword) error
- func (k *Keywords) GetCompoundKeywordType(s string) (models.TokenType, bool)
- func (k *Keywords) GetCompoundKeywords() KeywordCategory
- func (k *Keywords) GetDMLKeywordType(s string) (models.TokenType, bool)
- func (k *Keywords) GetKeyword(word string) (Keyword, bool)
- func (k *Keywords) GetKeywordType(s string) models.TokenType
- func (k *Keywords) GetTokenType(word string) models.TokenType
- func (k *Keywords) IsCompoundKeyword(s string) bool
- func (k *Keywords) IsCompoundKeywordStart(word string) bool
- func (k *Keywords) IsDMLKeyword(s string) bool
- func (k *Keywords) IsKeyword(s string) bool
- func (k *Keywords) IsReserved(s string) bool
- type SQLDialect
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ADDITIONAL_KEYWORDS = []Keyword{ {Word: "BETWEEN", Type: models.TokenTypeBetween, Reserved: true, ReservedForTableAlias: false}, {Word: "IS", Type: models.TokenTypeIs, Reserved: true, ReservedForTableAlias: false}, {Word: "NULL", Type: models.TokenTypeNull, Reserved: true, ReservedForTableAlias: false}, {Word: "TRUE", Type: models.TokenTypeTrue, Reserved: true, ReservedForTableAlias: false}, {Word: "FALSE", Type: models.TokenTypeFalse, Reserved: true, ReservedForTableAlias: false}, {Word: "ASC", Type: models.TokenTypeAsc, Reserved: true, ReservedForTableAlias: false}, {Word: "DESC", Type: models.TokenTypeDesc, Reserved: true, ReservedForTableAlias: false}, {Word: "CASE", Type: models.TokenTypeCase, Reserved: true, ReservedForTableAlias: false}, {Word: "WHEN", Type: models.TokenTypeWhen, Reserved: true, ReservedForTableAlias: false}, {Word: "THEN", Type: models.TokenTypeThen, Reserved: true, ReservedForTableAlias: false}, {Word: "ELSE", Type: models.TokenTypeElse, Reserved: true, ReservedForTableAlias: false}, {Word: "END", Type: models.TokenTypeEnd, Reserved: true, ReservedForTableAlias: false}, {Word: "CAST", Type: models.TokenTypeCast, Reserved: true, ReservedForTableAlias: false}, {Word: "INTERVAL", Type: models.TokenTypeInterval, Reserved: true, ReservedForTableAlias: false}, {Word: "ROW_NUMBER", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "RANK", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "DENSE_RANK", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "NTILE", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "LAG", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "LEAD", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "FIRST_VALUE", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "LAST_VALUE", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "ROLLUP", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false}, {Word: "CUBE", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false}, {Word: "GROUPING", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false}, {Word: "SETS", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false}, {Word: "FILTER", Type: models.TokenTypeFilter, Reserved: true, ReservedForTableAlias: false}, {Word: "ARRAY", Type: models.TokenTypeArray, Reserved: true, ReservedForTableAlias: false}, {Word: "WITHIN", Type: models.TokenTypeWithin, Reserved: true, ReservedForTableAlias: false}, {Word: "MERGE", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "USING", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "MATCHED", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false}, {Word: "SOURCE", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "TARGET", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "CREATE", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "DROP", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "ALTER", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "TABLE", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "ADD", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "COLUMN", Type: models.TokenTypeColumn, Reserved: true, ReservedForTableAlias: true}, {Word: "CONSTRAINT", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "RENAME", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "TO", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "INDEX", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "REFRESH", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "CONCURRENTLY", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false}, {Word: "CASCADE", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false}, {Word: "RESTRICT", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false}, {Word: "TEMPORARY", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false}, {Word: "REPLACE", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false}, {Word: "EXISTS", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false}, {Word: "IF", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false}, {Word: "HASH", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "LIST", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "VALUES", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false}, {Word: "LESS", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "THAN", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "MAXVALUE", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "TABLESPACE", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "CHECK", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false}, {Word: "OPTION", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "CASCADED", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "LOCAL", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "TRUNCATE", Type: models.TokenTypeTruncate, Reserved: true, ReservedForTableAlias: true}, {Word: "RESTART", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "CONTINUE", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "IDENTITY", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "SHARE", Type: models.TokenTypeShare, Reserved: true, ReservedForTableAlias: false}, {Word: "NOWAIT", Type: models.TokenTypeNoWait, Reserved: true, ReservedForTableAlias: false}, {Word: "SKIP", Type: models.TokenTypeSkip, Reserved: true, ReservedForTableAlias: false}, {Word: "LOCKED", Type: models.TokenTypeLocked, Reserved: true, ReservedForTableAlias: false}, {Word: "OF", Type: models.TokenTypeOf, Reserved: true, ReservedForTableAlias: false}, }
ADDITIONAL_KEYWORDS contains SQL keywords that are reserved but not specifically reserved for table aliases. These include expression keywords (BETWEEN, IS, NULL), window function names (ROW_NUMBER, RANK, LAG, LEAD), grouping operations (ROLLUP, CUBE, GROUPING SETS), and DDL/DML keywords.
v1.6.0 additions: FILTER, MERGE, MATERIALIZED, TRUNCATE, FETCH-related keywords
var MYSQL_SPECIFIC = []Keyword{ {Word: "BINARY", Type: models.TokenTypeKeyword}, {Word: "CHAR", Type: models.TokenTypeKeyword}, {Word: "DATETIME", Type: models.TokenTypeKeyword}, {Word: "DECIMAL", Type: models.TokenTypeKeyword}, {Word: "UNSIGNED", Type: models.TokenTypeKeyword}, {Word: "ZEROFILL", Type: models.TokenTypeKeyword}, {Word: "FORCE", Type: models.TokenTypeKeyword}, {Word: "IGNORE", Type: models.TokenTypeKeyword}, {Word: "INDEX", Type: models.TokenTypeKeyword}, {Word: "KEY", Type: models.TokenTypeKeyword}, {Word: "KEYS", Type: models.TokenTypeKeyword}, {Word: "KILL", Type: models.TokenTypeKeyword}, {Word: "OPTION", Type: models.TokenTypeKeyword}, {Word: "PURGE", Type: models.TokenTypeKeyword}, {Word: "READ", Type: models.TokenTypeKeyword}, {Word: "WRITE", Type: models.TokenTypeKeyword}, {Word: "STATUS", Type: models.TokenTypeKeyword}, {Word: "VARIABLES", Type: models.TokenTypeKeyword}, }
MYSQL_SPECIFIC contains MySQL-specific keywords and extensions. These keywords are recognized when using DialectMySQL.
Examples: ZEROFILL, UNSIGNED, FORCE, IGNORE
var ORACLE_SPECIFIC = []Keyword{ {Word: "ROWNUM", Type: models.TokenTypeKeyword}, {Word: "ROWID", Type: models.TokenTypeKeyword}, {Word: "SYSDATE", Type: models.TokenTypeKeyword}, {Word: "SYSTIMESTAMP", Type: models.TokenTypeKeyword}, {Word: "DUAL", Type: models.TokenTypeKeyword}, {Word: "NOCYCLE", Type: models.TokenTypeKeyword}, {Word: "PRIOR", Type: models.TokenTypeKeyword}, {Word: "LEVEL", Type: models.TokenTypeKeyword}, {Word: "CONNECT_BY_ROOT", Type: models.TokenTypeKeyword}, {Word: "NVL", Type: models.TokenTypeKeyword}, {Word: "NVL2", Type: models.TokenTypeKeyword}, {Word: "DECODE", Type: models.TokenTypeKeyword}, {Word: "GREATEST", Type: models.TokenTypeKeyword}, {Word: "LEAST", Type: models.TokenTypeKeyword}, {Word: "LPAD", Type: models.TokenTypeKeyword}, {Word: "RPAD", Type: models.TokenTypeKeyword}, {Word: "INSTR", Type: models.TokenTypeKeyword}, {Word: "SUBSTR", Type: models.TokenTypeKeyword}, {Word: "TRUNC", Type: models.TokenTypeKeyword}, {Word: "ROUND", Type: models.TokenTypeKeyword}, {Word: "MOD", Type: models.TokenTypeKeyword}, {Word: "NCHAR", Type: models.TokenTypeKeyword}, {Word: "VARCHAR2", Type: models.TokenTypeKeyword}, {Word: "NUMBER", Type: models.TokenTypeKeyword}, {Word: "CLOB", Type: models.TokenTypeKeyword}, {Word: "BLOB", Type: models.TokenTypeKeyword}, {Word: "NCLOB", Type: models.TokenTypeKeyword}, {Word: "RAW", Type: models.TokenTypeKeyword}, {Word: "LONG", Type: models.TokenTypeKeyword}, {Word: "PIPELINED", Type: models.TokenTypeKeyword}, {Word: "BULK", Type: models.TokenTypeKeyword}, {Word: "COLLECT", Type: models.TokenTypeKeyword}, {Word: "FORALL", Type: models.TokenTypeKeyword}, {Word: "EXCEPTION", Type: models.TokenTypeKeyword}, {Word: "PRAGMA", Type: models.TokenTypeKeyword}, {Word: "SEQUENCE", Type: models.TokenTypeKeyword}, {Word: "NEXTVAL", Type: models.TokenTypeKeyword}, {Word: "CURRVAL", Type: models.TokenTypeKeyword}, {Word: "NOCACHE", Type: models.TokenTypeKeyword}, {Word: "NOCOPY", Type: models.TokenTypeKeyword}, {Word: "SUBPARTITION", Type: models.TokenTypeKeyword}, {Word: "STORAGE", Type: models.TokenTypeKeyword}, {Word: "FLASHBACK", Type: models.TokenTypeKeyword}, {Word: "VERSIONS", Type: models.TokenTypeKeyword}, {Word: "SCN", Type: models.TokenTypeKeyword}, {Word: "TIMESTAMP", Type: models.TokenTypeKeyword}, {Word: "MINVALUE", Type: models.TokenTypeKeyword}, {Word: "INCREMENT", Type: models.TokenTypeKeyword}, {Word: "NOMINVALUE", Type: models.TokenTypeKeyword}, {Word: "NOMAXVALUE", Type: models.TokenTypeKeyword}, }
ORACLE_SPECIFIC contains Oracle Database-specific keywords and extensions. These keywords are recognized when using DialectOracle.
Examples: ROWNUM, ROWID, SYSDATE, DUAL, CONNECT BY, NVL, DECODE
var POSTGRESQL_SPECIFIC = []Keyword{ {Word: "MATERIALIZED", Type: models.TokenTypeKeyword}, {Word: "ILIKE", Type: models.TokenTypeKeyword}, {Word: "SIMILAR", Type: models.TokenTypeKeyword}, {Word: "FREEZE", Type: models.TokenTypeKeyword}, {Word: "ANALYSE", Type: models.TokenTypeKeyword}, {Word: "ANALYZE", Type: models.TokenTypeKeyword}, {Word: "CONCURRENTLY", Type: models.TokenTypeKeyword}, {Word: "REINDEX", Type: models.TokenTypeKeyword}, {Word: "TOAST", Type: models.TokenTypeKeyword}, {Word: "NOWAIT", Type: models.TokenTypeKeyword}, {Word: "RECURSIVE", Type: models.TokenTypeKeyword}, {Word: "RETURNING", Type: models.TokenTypeKeyword}, }
POSTGRESQL_SPECIFIC contains PostgreSQL-specific keywords and extensions. These keywords are recognized when using DialectPostgreSQL.
v1.6.0 additions: MATERIALIZED, LATERAL (already in base keywords), RETURNING (in base) Examples: ILIKE, MATERIALIZED, SIMILAR, FREEZE, RECURSIVE, RETURNING
var RESERVED_FOR_TABLE_ALIAS = []Keyword{ {Word: "AS", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "WITH", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "EXPLAIN", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "ANALYZE", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "SELECT", Type: models.TokenTypeSelect, Reserved: true, ReservedForTableAlias: true}, {Word: "WHERE", Type: models.TokenTypeWhere, Reserved: true, ReservedForTableAlias: true}, {Word: "GROUP", Type: models.TokenTypeGroup, Reserved: true, ReservedForTableAlias: true}, {Word: "SORT", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "HAVING", Type: models.TokenTypeHaving, Reserved: true, ReservedForTableAlias: true}, {Word: "ORDER", Type: models.TokenTypeOrder, Reserved: true, ReservedForTableAlias: true}, {Word: "PIVOT", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "UNPIVOT", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "TOP", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "LATERAL", Type: models.TokenTypeLateral, Reserved: true, ReservedForTableAlias: true}, {Word: "VIEW", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "LIMIT", Type: models.TokenTypeLimit, Reserved: true, ReservedForTableAlias: true}, {Word: "OFFSET", Type: models.TokenTypeOffset, Reserved: true, ReservedForTableAlias: true}, {Word: "FETCH", Type: models.TokenTypeFetch, Reserved: true, ReservedForTableAlias: true}, {Word: "UNION", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "EXCEPT", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "INTERSECT", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "MINUS", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "ON", Type: models.TokenTypeOn, Reserved: true, ReservedForTableAlias: true}, {Word: "JOIN", Type: models.TokenTypeJoin, Reserved: true, ReservedForTableAlias: true}, {Word: "INNER", Type: models.TokenTypeInner, Reserved: true, ReservedForTableAlias: true}, {Word: "CROSS", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "FULL", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "LEFT", Type: models.TokenTypeLeft, Reserved: true, ReservedForTableAlias: true}, {Word: "RIGHT", Type: models.TokenTypeRight, Reserved: true, ReservedForTableAlias: true}, {Word: "NATURAL", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "USING", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "CLUSTER", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "DISTRIBUTE", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "GLOBAL", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "ANTI", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "SEMI", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "RETURNING", Type: models.TokenTypeReturning, Reserved: true, ReservedForTableAlias: true}, {Word: "ASOF", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "MATCH_CONDITION", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "OUTER", Type: models.TokenTypeOuter, Reserved: true, ReservedForTableAlias: true}, {Word: "SET", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "QUALIFY", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "WINDOW", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "END", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "FOR", Type: models.TokenTypeFor, Reserved: true, ReservedForTableAlias: true}, {Word: "PARTITION", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "PREWHERE", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "SETTINGS", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "FORMAT", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "START", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "CONNECT", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "AND", Type: models.TokenTypeAnd, Reserved: true, ReservedForTableAlias: true}, {Word: "LIKE", Type: models.TokenTypeLike, Reserved: true, ReservedForTableAlias: true}, {Word: "ASC", Type: models.TokenTypeAsc, Reserved: true, ReservedForTableAlias: true}, {Word: "MATCH_RECOGNIZE", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "SAMPLE", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "TABLESAMPLE", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true}, {Word: "FROM", Type: models.TokenTypeFrom, Reserved: true, ReservedForTableAlias: true}, {Word: "BY", Type: models.TokenTypeBy, Reserved: true, ReservedForTableAlias: true}, {Word: "OR", Type: models.TokenTypeOr, Reserved: true, ReservedForTableAlias: true}, {Word: "NOT", Type: models.TokenTypeNot, Reserved: true, ReservedForTableAlias: true}, {Word: "IN", Type: models.TokenTypeIn, Reserved: true, ReservedForTableAlias: true}, {Word: "COUNT", Type: models.TokenTypeCount, Reserved: true, ReservedForTableAlias: true}, {Word: "SUM", Type: models.TokenTypeSum, Reserved: true, ReservedForTableAlias: true}, {Word: "AVG", Type: models.TokenTypeAvg, Reserved: true, ReservedForTableAlias: true}, {Word: "MIN", Type: models.TokenTypeMin, Reserved: true, ReservedForTableAlias: true}, {Word: "MAX", Type: models.TokenTypeMax, Reserved: true, ReservedForTableAlias: true}, {Word: "OVER", Type: models.TokenTypeOver, Reserved: true, ReservedForTableAlias: true}, {Word: "ROWS", Type: models.TokenTypeRows, Reserved: true, ReservedForTableAlias: true}, {Word: "RANGE", Type: models.TokenTypeRange, Reserved: true, ReservedForTableAlias: true}, {Word: "CURRENT", Type: models.TokenTypeCurrent, Reserved: true, ReservedForTableAlias: true}, {Word: "ROW", Type: models.TokenTypeRow, Reserved: true, ReservedForTableAlias: true}, {Word: "UNBOUNDED", Type: models.TokenTypeUnbounded, Reserved: true, ReservedForTableAlias: true}, {Word: "PRECEDING", Type: models.TokenTypePreceding, Reserved: true, ReservedForTableAlias: true}, {Word: "FOLLOWING", Type: models.TokenTypeFollowing, Reserved: true, ReservedForTableAlias: true}, {Word: "NEXT", Type: models.TokenTypeNext, Reserved: true, ReservedForTableAlias: true}, {Word: "FIRST", Type: models.TokenTypeFirst, Reserved: true, ReservedForTableAlias: true}, {Word: "ONLY", Type: models.TokenTypeOnly, Reserved: true, ReservedForTableAlias: true}, {Word: "TIES", Type: models.TokenTypeTies, Reserved: true, ReservedForTableAlias: true}, {Word: "PERCENT", Type: models.TokenTypePercent, Reserved: true, ReservedForTableAlias: true}, }
RESERVED_FOR_TABLE_ALIAS contains keywords that cannot be used as table aliases. These keywords are reserved in the context of table aliasing and will cause syntax errors if used without the AS keyword in most SQL dialects.
Examples: SELECT, FROM, WHERE, JOIN, LATERAL (v1.6.0), RETURNING (v1.6.0)
var SNOWFLAKE_SPECIFIC = []Keyword{ {Word: "VARIANT", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "OBJECT", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "FLATTEN", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "PARSE_JSON", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "STRIP_NULL_VALUE", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "TYPEOF", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "CHANGES", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "STREAM", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "TASK", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "PIPE", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "STAGE", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "FILE_FORMAT", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "WAREHOUSE", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "DATABASE", Type: models.TokenTypeDatabase, Reserved: false, ReservedForTableAlias: false}, {Word: "CLONE", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "UNDROP", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false}, {Word: "RECLUSTER", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "BEFORE", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "AT", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "TIMESTAMP", Type: models.TokenTypeTimestamp, Reserved: false, ReservedForTableAlias: false}, {Word: "STATEMENT", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "COPY", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false}, {Word: "PUT", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "GET", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "REMOVE", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "ROLE", Type: models.TokenTypeRole, Reserved: false, ReservedForTableAlias: false}, {Word: "GRANT", Type: models.TokenTypeGrant, Reserved: true, ReservedForTableAlias: false}, {Word: "REVOKE", Type: models.TokenTypeRevoke, Reserved: true, ReservedForTableAlias: false}, {Word: "OWNERSHIP", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "IFF", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "IFNULL", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "NVL", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "NVL2", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "ZEROIFNULL", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "EQUAL_NULL", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "TRY_CAST", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "TRY_TO_NUMBER", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "TRY_TO_DATE", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "RESULT_SCAN", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "GENERATOR", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "ROWCOUNT", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "LAST_QUERY_ID", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, {Word: "SYSTEM", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false}, }
SNOWFLAKE_SPECIFIC contains Snowflake-specific keywords and extensions. These keywords are recognized when using DialectSnowflake.
These extend the base SQL keywords with Snowflake-specific syntax for:
- Semi-structured data types (VARIANT, OBJECT)
- Semi-structured data functions (FLATTEN, PARSE_JSON, TYPEOF)
- Snowflake-specific clauses (CHANGES, STREAM, TASK, PIPE, STAGE)
- Snowflake DDL (WAREHOUSE, CLONE, UNDROP, RECLUSTER)
- Time travel (BEFORE, AT, STATEMENT)
- Data loading (COPY, PUT, GET, REMOVE)
- Access control (ROLE, GRANT, REVOKE, OWNERSHIP)
- Snowflake functions (IFF, IFNULL, NVL, NVL2, TRY_CAST, etc.)
Keywords that already exist in the base keyword set (RESERVED_FOR_TABLE_ALIAS or ADDITIONAL_KEYWORDS) are NOT duplicated here. The following base keywords overlap with Snowflake features but are already defined:
- ARRAY (TokenTypeArray), LATERAL (TokenTypeLateral), QUALIFY, SAMPLE
- CLUSTER, OFFSET (TokenTypeOffset), SHARE (TokenTypeShare), LIST
Examples: VARIANT, FLATTEN, WAREHOUSE, CLONE, IFF, RESULT_SCAN
var SQLITE_SPECIFIC = []Keyword{ {Word: "ABORT", Type: models.TokenTypeKeyword}, {Word: "ACTION", Type: models.TokenTypeKeyword}, {Word: "AFTER", Type: models.TokenTypeKeyword}, {Word: "ATTACH", Type: models.TokenTypeKeyword}, {Word: "AUTOINCREMENT", Type: models.TokenTypeKeyword}, {Word: "CONFLICT", Type: models.TokenTypeKeyword}, {Word: "DATABASE", Type: models.TokenTypeKeyword}, {Word: "DETACH", Type: models.TokenTypeKeyword}, {Word: "EXCLUSIVE", Type: models.TokenTypeKeyword}, {Word: "INDEXED", Type: models.TokenTypeKeyword}, {Word: "INSTEAD", Type: models.TokenTypeKeyword}, {Word: "PLAN", Type: models.TokenTypeKeyword}, {Word: "PRAGMA", Type: models.TokenTypeKeyword}, {Word: "QUERY", Type: models.TokenTypeKeyword}, {Word: "RAISE", Type: models.TokenTypeKeyword}, {Word: "REPLACE", Type: models.TokenTypeKeyword}, {Word: "ROWID", Type: models.TokenTypeKeyword}, {Word: "TEMP", Type: models.TokenTypeKeyword}, {Word: "TEMPORARY", Type: models.TokenTypeKeyword}, {Word: "VACUUM", Type: models.TokenTypeKeyword}, {Word: "VIRTUAL", Type: models.TokenTypeKeyword}, {Word: "WITHOUT", Type: models.TokenTypeKeyword}, }
SQLITE_SPECIFIC contains SQLite-specific keywords and extensions. These keywords are recognized when using DialectSQLite.
Examples: AUTOINCREMENT, VACUUM, ATTACH, DETACH, PRAGMA
var SQLSERVER_SPECIFIC = []Keyword{ {Word: "NOLOCK", Type: models.TokenTypeKeyword}, {Word: "ROWLOCK", Type: models.TokenTypeKeyword}, {Word: "UPDLOCK", Type: models.TokenTypeKeyword}, {Word: "HOLDLOCK", Type: models.TokenTypeKeyword}, {Word: "READPAST", Type: models.TokenTypeKeyword}, {Word: "GETDATE", Type: models.TokenTypeKeyword}, {Word: "GETUTCDATE", Type: models.TokenTypeKeyword}, {Word: "SYSDATETIME", Type: models.TokenTypeKeyword}, {Word: "NEWID", Type: models.TokenTypeKeyword}, {Word: "ISNULL", Type: models.TokenTypeKeyword}, {Word: "NVARCHAR", Type: models.TokenTypeKeyword}, {Word: "DATETIME2", Type: models.TokenTypeKeyword}, {Word: "UNIQUEIDENTIFIER", Type: models.TokenTypeKeyword}, {Word: "INSERTED", Type: models.TokenTypeKeyword}, {Word: "DELETED", Type: models.TokenTypeKeyword}, {Word: "SCOPE_IDENTITY", Type: models.TokenTypeKeyword}, {Word: "OUTPUT", Type: models.TokenTypeKeyword}, }
SQLSERVER_SPECIFIC contains SQL Server (T-SQL) specific keywords and extensions. These keywords are recognized when using DialectSQLServer.
Examples: NOLOCK, ROWLOCK, ISNULL, NVARCHAR, SCOPE_IDENTITY
Functions ¶
func IsValidDialect ¶ added in v1.9.3
IsValidDialect reports whether name is a recognised SQL dialect identifier. An empty string is also considered valid (meaning "use the default dialect").
Example:
keywords.IsValidDialect("mysql") // true
keywords.IsValidDialect("fakesql") // false
keywords.IsValidDialect("") // true (default)
Types ¶
type Keyword ¶
type Keyword struct {
Word string // The keyword string (uppercase normalized)
Type models.TokenType // Token type for this keyword
Reserved bool // True if keyword cannot be used as identifier
ReservedForTableAlias bool // True if keyword cannot be used as table alias
}
Keyword represents a SQL keyword with its properties and reservation status.
Each keyword has multiple attributes that determine how it can be used:
- Word: The keyword string (e.g., "SELECT", "LATERAL")
- Type: The token type assigned to this keyword (models.TokenType)
- Reserved: Whether the keyword is reserved and cannot be used as an identifier
- ReservedForTableAlias: Whether the keyword cannot be used as a table alias
Example:
selectKeyword := Keyword{
Word: "SELECT",
Type: models.TokenTypeSelect,
Reserved: true,
ReservedForTableAlias: true,
}
rankFunction := Keyword{
Word: "RANK",
Type: models.TokenTypeKeyword,
Reserved: false, // Window function names are non-reserved
ReservedForTableAlias: false,
}
func DialectKeywords ¶ added in v1.9.3
func DialectKeywords(dialect SQLDialect) []Keyword
DialectKeywords returns the additional keywords for a specific dialect. This is a convenience function for retrieving dialect-specific keyword lists without constructing a full Keywords instance.
Returns nil for DialectGeneric and unrecognized dialects.
Example:
snowflakeKws := keywords.DialectKeywords(keywords.DialectSnowflake)
for _, kw := range snowflakeKws {
fmt.Println(kw.Word)
}
type KeywordCategory ¶
KeywordCategory represents a category of SQL keywords mapped to their token types. Each category groups related keywords together (e.g., DML keywords, compound keywords).
type Keywords ¶
type Keywords struct {
// Keyword categories
DMLKeywords KeywordCategory
CompoundKeywords KeywordCategory
// contains filtered or unexported fields
}
Keywords holds all SQL keyword categories and configuration for a specific SQL dialect.
This is the main structure for keyword management, containing:
- Keyword categorization (DML, compound keywords)
- Complete keyword mapping to token types
- Reserved keyword tracking
- Dialect-specific configuration
- Case sensitivity settings
Use New() to create a properly initialized Keywords instance:
kw := keywords.New(keywords.DialectPostgreSQL, true)
func New ¶
func New(dialect SQLDialect, ignoreCase bool) *Keywords
New creates a new Keywords instance with the specified SQL dialect and case sensitivity.
The dialect parameter determines which dialect-specific keywords to include:
- DialectGeneric: Standard SQL keywords only
- DialectPostgreSQL: Includes PostgreSQL extensions (ILIKE, LATERAL, MATERIALIZED, RETURNING)
- DialectMySQL: Includes MySQL extensions (ZEROFILL, UNSIGNED, FORCE)
- DialectSQLite: Includes SQLite extensions (AUTOINCREMENT, VACUUM)
The ignoreCase parameter controls case sensitivity, though it's always set to true internally as SQL keywords are case-insensitive by standard.
Example:
// Create PostgreSQL keyword instance
kw := keywords.New(keywords.DialectPostgreSQL, true)
if kw.IsKeyword("LATERAL") {
fmt.Println("LATERAL is a PostgreSQL keyword")
}
func (*Keywords) AddKeyword ¶
func (*Keywords) GetCompoundKeywordType ¶
GetCompoundKeywordType returns the token type for a compound keyword
func (*Keywords) GetCompoundKeywords ¶
func (k *Keywords) GetCompoundKeywords() KeywordCategory
GetCompoundKeywords returns the compound keywords map. Compound keywords are multi-word SQL keywords like "GROUP BY", "ORDER BY", "GROUPING SETS", "MATERIALIZED VIEW", etc.
Example:
kw := keywords.New(keywords.DialectGeneric, true)
compounds := kw.GetCompoundKeywords()
for keyword, tokenType := range compounds {
fmt.Printf("%s -> %v\n", keyword, tokenType)
}
func (*Keywords) GetDMLKeywordType ¶
GetDMLKeywordType returns the token type for a DML keyword
func (*Keywords) GetKeywordType ¶
GetKeywordType returns the token type for a keyword
func (*Keywords) GetTokenType ¶
GetTokenType returns the token type for a given keyword. If the word is not a recognized keyword, it returns models.TokenTypeWord.
The lookup is case-insensitive when the Keywords instance was created with case-insensitive matching (default behavior).
Example:
kw := keywords.New(keywords.DialectGeneric, true)
tokenType := kw.GetTokenType("SELECT") // models.TokenTypeSelect
tokenType = kw.GetTokenType("select") // models.TokenTypeSelect (case-insensitive)
tokenType = kw.GetTokenType("unknown") // models.TokenTypeWord
func (*Keywords) IsCompoundKeyword ¶
IsCompoundKeyword checks if a string is a compound keyword
func (*Keywords) IsCompoundKeywordStart ¶
IsCompoundKeywordStart checks if a word can start a compound keyword. This is useful during tokenization to determine if lookahead is needed to recognize multi-word keywords.
Example:
kw := keywords.New(keywords.DialectGeneric, true)
kw.IsCompoundKeywordStart("GROUP") // true - could be "GROUP BY"
kw.IsCompoundKeywordStart("SELECT") // false - not a compound keyword start
func (*Keywords) IsDMLKeyword ¶
IsDMLKeyword checks if a given string is a DML keyword
func (*Keywords) IsKeyword ¶
IsKeyword checks if a string is a recognized SQL keyword. Returns true if the word is found in the keyword map, false otherwise.
The check is case-insensitive when the Keywords instance was created with case-insensitive matching (default).
Example:
kw := keywords.New(keywords.DialectGeneric, true)
kw.IsKeyword("SELECT") // true
kw.IsKeyword("select") // true (case-insensitive)
kw.IsKeyword("unknown") // false
func (*Keywords) IsReserved ¶
IsReserved checks if a keyword is reserved and cannot be used as an identifier. Reserved keywords include SQL statements (SELECT, INSERT), clauses (WHERE, FROM), and other keywords that have special meaning in SQL syntax.
Example:
kw := keywords.New(keywords.DialectGeneric, true)
kw.IsReserved("SELECT") // true - reserved keyword
kw.IsReserved("ROW_NUMBER") // false - window function (non-reserved)
type SQLDialect ¶
type SQLDialect string
SQLDialect represents different SQL database dialects. Each dialect may have specific keywords that are not part of standard SQL.
const ( // DialectUnknown represents an unknown or unspecified SQL dialect DialectUnknown SQLDialect = "unknown" // DialectGeneric represents standard SQL keywords common to all dialects DialectGeneric SQLDialect = "generic" // DialectMySQL represents MySQL-specific keywords and extensions DialectMySQL SQLDialect = "mysql" // DialectPostgreSQL represents PostgreSQL-specific keywords and extensions. // v1.6.0 includes: MATERIALIZED, ILIKE, LATERAL, RETURNING, and more. DialectPostgreSQL SQLDialect = "postgresql" // DialectSQLite represents SQLite-specific keywords and extensions DialectSQLite SQLDialect = "sqlite" // DialectSQLServer represents SQL Server-specific keywords and extensions DialectSQLServer SQLDialect = "sqlserver" // DialectOracle represents Oracle-specific keywords and extensions DialectOracle SQLDialect = "oracle" // DialectSnowflake represents Snowflake-specific keywords and extensions. // Includes semi-structured data types (VARIANT, OBJECT), Snowflake objects // (WAREHOUSE, STREAM, TASK, PIPE, STAGE), time travel (BEFORE, AT), // data loading (COPY, PUT, GET), and Snowflake-specific functions (IFF, NVL, etc.) DialectSnowflake SQLDialect = "snowflake" // DialectBigQuery represents Google BigQuery-specific keywords and extensions DialectBigQuery SQLDialect = "bigquery" // DialectRedshift represents Amazon Redshift-specific keywords and extensions DialectRedshift SQLDialect = "redshift" )
func AllDialects ¶ added in v1.9.3
func AllDialects() []SQLDialect
AllDialects returns all supported SQL dialect identifiers. This includes both fully implemented dialects (with dialect-specific keywords) and placeholder dialects that currently use only the base keyword set.
Example:
for _, d := range keywords.AllDialects() {
fmt.Println(d)
}
func DetectDialect ¶ added in v1.9.3
func DetectDialect(sql string) SQLDialect
DetectDialect attempts to identify the SQL dialect from SQL text. It analyzes the SQL string for dialect-specific keywords and patterns, returning the most likely dialect based on weighted keyword analysis.
The detection uses a scoring system where each dialect-specific keyword contributes a weight to its dialect's score. The dialect with the highest total score wins. If no dialect-specific patterns are found, DialectGeneric is returned.
Detection heuristics include:
- Snowflake: QUALIFY, FLATTEN, VARIANT, WAREHOUSE, CLONE, UNDROP, RESULT_SCAN, IFF
- PostgreSQL: ILIKE, RETURNING, DISTINCT ON, MATERIALIZED
- MySQL: ZEROFILL, UNSIGNED, AUTO_INCREMENT, FORCE INDEX
- SQL Server: NOLOCK, TOP, NVARCHAR, GETDATE
- Oracle: ROWNUM, CONNECT BY, SYSDATE, DECODE
- SQLite: AUTOINCREMENT, GLOB, VACUUM
The function also performs syntactic checks for identifier quoting styles:
- Backtick identifiers (`) suggest MySQL
- Square bracket identifiers ([]) suggest SQL Server
- Double-colon type casting (::) suggests PostgreSQL
Example:
dialect := keywords.DetectDialect("SELECT * FROM users QUALIFY ROW_NUMBER() OVER (ORDER BY id) = 1")
// dialect == DialectSnowflake
dialect = keywords.DetectDialect("SELECT DISTINCT ON (dept) * FROM emp")
// dialect == DialectPostgreSQL
dialect = keywords.DetectDialect("SELECT * FROM users")
// dialect == DialectGeneric