keywords

package
v1.10.3 Latest Latest
Warning

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

Go to latest
Published: Mar 13, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

README

Keywords Package

Overview

The keywords package provides SQL keyword recognition, categorization, and multi-dialect support. It enables the tokenizer and parser to correctly identify and classify SQL keywords across PostgreSQL, MySQL, SQL Server, Oracle, and SQLite dialects.

Key Features

  • Multi-Dialect Support: PostgreSQL, MySQL, SQL Server, Oracle, SQLite
  • Keyword Categorization: Reserved, DML, compound, window functions
  • Compound Keywords: GROUP BY, ORDER BY, LEFT JOIN, etc.
  • Case-Insensitive: Recognizes keywords in any case
  • Extensible: Support for adding custom keywords
  • Thread-Safe: All operations are safe for concurrent use

Core Types

Keywords

Main keyword registry:

type Keywords struct {
    dialect SQLDialect
    // Internal keyword maps
}
SQLDialect

Supported SQL dialects:

type SQLDialect int

const (
    PostgreSQL SQLDialect = iota
    MySQL
    SQLServer
    Oracle
    SQLite
    Generic  // SQL-99 standard keywords
)
KeywordCategory

Keyword classification:

type KeywordCategory int

const (
    CategoryReserved KeywordCategory = iota
    CategoryDML
    CategoryDDL
    CategoryFunction
    CategoryOperator
    CategoryDataType
)

Usage

Basic Keyword Recognition
package main

import (
    "github.com/ajitpratap0/GoSQLX/pkg/sql/keywords"
)

func main() {
    // Create keyword registry for PostgreSQL
    kw := keywords.New(keywords.PostgreSQL)

    // Check if word is a keyword
    if kw.IsKeyword("SELECT") {
        fmt.Println("SELECT is a keyword")
    }

    // Check if reserved
    if kw.IsReserved("TABLE") {
        fmt.Println("TABLE is reserved")
    }

    // Get keyword info
    keyword := kw.GetKeyword("JOIN")
    fmt.Printf("Type: %s, Category: %d\n", keyword.TokenType, keyword.Category)
}
Compound Keyword Detection
kw := keywords.New(keywords.Generic)

// Check compound keywords
if kw.IsCompoundKeyword("GROUP", "BY") {
    fmt.Println("GROUP BY is a compound keyword")
}

// Get compound keyword type
tokenType := kw.GetCompoundKeywordType("ORDER", "BY")
fmt.Printf("ORDER BY token type: %s\n", tokenType)
Dialect-Specific Keywords
// PostgreSQL-specific
pgKw := keywords.New(keywords.PostgreSQL)
if pgKw.IsKeyword("ILIKE") {
    fmt.Println("ILIKE is PostgreSQL-specific")
}

// MySQL-specific
myKw := keywords.New(keywords.MySQL)
if myKw.IsKeyword("UNSIGNED") {
    fmt.Println("UNSIGNED is MySQL-specific")
}

// SQLite-specific
sqliteKw := keywords.New(keywords.SQLite)
if sqliteKw.IsKeyword("AUTOINCREMENT") {
    fmt.Println("AUTOINCREMENT is SQLite-specific")
}

Keyword Categories

Reserved Keywords

Core SQL statement keywords:

SELECT, FROM, WHERE, INSERT, UPDATE, DELETE, CREATE, ALTER, DROP,
JOIN, INNER, LEFT, RIGHT, OUTER, FULL, CROSS, NATURAL,
GROUP, ORDER, HAVING, UNION, EXCEPT, INTERSECT,
WITH, RECURSIVE, AS, ON, USING,
WINDOW, PARTITION, OVER, ROWS, RANGE, etc.
DML Keywords

Data manipulation modifiers:

DISTINCT, ALL, FETCH, FIRST, NEXT, LAST, ONLY,
WITH TIES, NULLS, LIMIT, OFFSET, etc.
Compound Keywords

Multi-word keywords recognized as single tokens:

GROUP BY, ORDER BY,
LEFT JOIN, RIGHT JOIN, FULL JOIN, CROSS JOIN, NATURAL JOIN,
INNER JOIN, LEFT OUTER JOIN, RIGHT OUTER JOIN, FULL OUTER JOIN,
UNION ALL, WITH TIES, NULLS FIRST, NULLS LAST, etc.
Window Function Keywords

Window function names and modifiers:

ROW_NUMBER, RANK, DENSE_RANK, NTILE, PERCENT_RANK, CUME_DIST,
LAG, LEAD, FIRST_VALUE, LAST_VALUE, NTH_VALUE,
ROWS BETWEEN, RANGE BETWEEN, UNBOUNDED PRECEDING, CURRENT ROW, etc.

Dialect-Specific Keywords

PostgreSQL
pgKeywords := []string{
    "MATERIALIZED",      // Materialized views
    "ILIKE",             // Case-insensitive LIKE
    "SIMILAR",           // SIMILAR TO operator
    "FREEZE",            // VACUUM FREEZE
    "ANALYSE", "ANALYZE", // Statistics gathering
    "CONCURRENTLY",      // Concurrent operations
    "REINDEX",           // Index rebuilding
    "TOAST",             // TOAST storage
    "NOWAIT",            // Lock timeout
    "RECURSIVE",         // Recursive CTEs
    "RETURNING",         // RETURNING clause
}
MySQL
mysqlKeywords := []string{
    "BINARY",            // Binary collation
    "CHAR", "VARCHAR",   // Character types
    "DATETIME",          // DateTime type
    "DECIMAL",           // Decimal type
    "UNSIGNED",          // Unsigned modifier
    "ZEROFILL",          // Zero-fill display
    "FORCE",             // Force index
    "IGNORE",            // Ignore errors
    "INDEX", "KEY",      // Index keywords
    "KILL",              // Kill query
    "OPTION",            // Query options
    "PURGE",             // Purge logs
    "READ", "WRITE",     // Lock types
    "STATUS",            // Show status
    "VARIABLES",         // Show variables
}
SQLite
sqliteKeywords := []string{
    "ABORT",             // Transaction abort
    "ACTION",            // Foreign key action
    "AFTER",             // Trigger timing
    "ATTACH",            // Attach database
    "AUTOINCREMENT",     // Auto-increment
    "CONFLICT",          // Conflict resolution
    "DATABASE",          // Database keyword
    "DETACH",            // Detach database
    "EXCLUSIVE",         // Exclusive lock
    "INDEXED",           // Index hints
    "INSTEAD",           // INSTEAD OF trigger
    "PLAN",              // Query plan
    "QUERY",             // Query keyword
    "RAISE",             // Raise error
    "REPLACE",           // Replace operation
    "TEMP", "TEMPORARY", // Temporary objects
    "VACUUM",            // Database vacuum
    "VIRTUAL",           // Virtual tables
}

Functions

New

Create a keyword registry for a specific dialect:

func New(dialect SQLDialect) *Keywords
IsKeyword

Check if a word is a SQL keyword:

func (k *Keywords) IsKeyword(word string) bool
IsReserved

Check if a keyword is reserved:

func (k *Keywords) IsReserved(word string) bool
GetKeyword

Get detailed keyword information:

func (k *Keywords) GetKeyword(word string) *Keyword
GetTokenType

Get the token type for a keyword:

func (k *Keywords) GetTokenType(word string) string
IsCompoundKeyword

Check if two words form a compound keyword:

func (k *Keywords) IsCompoundKeyword(word1, word2 string) bool
GetCompoundKeywordType

Get the token type for a compound keyword:

func (k *Keywords) GetCompoundKeywordType(word1, word2 string) string
AddKeyword

Add a custom keyword (for extensions):

func (k *Keywords) AddKeyword(word string, tokenType string, category KeywordCategory)

Integration with Tokenizer

The keywords package is used by the tokenizer to identify SQL keywords:

// In tokenizer
kw := keywords.New(keywords.PostgreSQL)

// Check if identifier is actually a keyword
if kw.IsKeyword(identifierText) {
    tokenType = kw.GetTokenType(identifierText)
} else {
    tokenType = "IDENTIFIER"
}

Integration with Parser

The parser uses keyword information for syntax validation:

// Check if next token is a specific keyword
if p.currentToken.Type == "GROUP" {
    // Expecting "BY" for GROUP BY
    if p.peekToken.Type == "BY" {
        // Parse GROUP BY clause
    }
}

Case Sensitivity

All keyword matching is case-insensitive:

kw := keywords.New(keywords.Generic)

kw.IsKeyword("SELECT")  // true
kw.IsKeyword("select")  // true
kw.IsKeyword("Select")  // true
kw.IsKeyword("SeLeCt")  // true

Performance

  • Lookup Time: O(1) hash map lookups
  • Memory: Pre-allocated keyword maps
  • Thread-Safe: No synchronization overhead for reads
  • Cache-Friendly: Keywords stored in contiguous memory

Common Usage Patterns

1. Keyword Validation
func ValidateIdentifier(name string) error {
    kw := keywords.New(keywords.PostgreSQL)

    if kw.IsReserved(name) {
        return fmt.Errorf("%s is a reserved keyword", name)
    }

    return nil
}
2. SQL Formatter
func FormatKeyword(word string, style string) string {
    kw := keywords.New(keywords.Generic)

    if !kw.IsKeyword(word) {
        return word  // Not a keyword, return as-is
    }

    switch style {
    case "upper":
        return strings.ToUpper(word)
    case "lower":
        return strings.ToLower(word)
    case "title":
        return strings.Title(strings.ToLower(word))
    default:
        return word
    }
}
3. Syntax Highlighting
func HighlightSQL(sql string) string {
    kw := keywords.New(keywords.Generic)
    words := strings.Fields(sql)

    for i, word := range words {
        if kw.IsKeyword(word) {
            words[i] = fmt.Sprintf("<keyword>%s</keyword>", word)
        }
    }

    return strings.Join(words, " ")
}

Testing

Run keyword tests:

# All tests
go test -v ./pkg/sql/keywords/

# With race detection
go test -race ./pkg/sql/keywords/

# Specific dialects
go test -v -run TestPostgreSQLKeywords ./pkg/sql/keywords/
go test -v -run TestMySQLKeywords ./pkg/sql/keywords/
go test -v -run TestCompoundKeywords ./pkg/sql/keywords/

Best Practices

1. Create Once, Reuse
// GOOD: Create once at package level
var globalKeywords = keywords.New(keywords.PostgreSQL)

func IsKeyword(word string) bool {
    return globalKeywords.IsKeyword(word)
}

// BAD: Creating repeatedly
func IsKeyword(word string) bool {
    kw := keywords.New(keywords.PostgreSQL)  // Wasteful
    return kw.IsKeyword(word)
}
2. Use Appropriate Dialect
// Match your database
pgKeywords := keywords.New(keywords.PostgreSQL)   // For PostgreSQL
myKeywords := keywords.New(keywords.MySQL)        // For MySQL
genericKeywords := keywords.New(keywords.Generic) // For SQL-99 standard
3. Check Reserved Keywords for Identifiers
func ValidateTableName(name string) error {
    kw := keywords.New(keywords.PostgreSQL)

    if kw.IsReserved(name) {
        return fmt.Errorf("'%s' is a reserved keyword and cannot be used as a table name", name)
    }

    return nil
}
  • tokenizer: Uses keywords for token classification
  • parser: Uses keywords for syntax validation
  • models: Token type definitions

Documentation

Version History

  • v1.5.0: Added NULLS FIRST/LAST keywords
  • v1.4.0: Expanded PostgreSQL operator support
  • v1.3.0: Window function keywords
  • v1.2.0: CTE and set operation keywords
  • v1.0.0: Core keyword system with multi-dialect support

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

Examples

Constants

This section is empty.

Variables

View Source
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

View Source
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

View Source
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

View Source
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

View Source
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)

View Source
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

View Source
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

View Source
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

func IsValidDialect(name string) bool

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

type KeywordCategory map[string]models.TokenType

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 NewKeywords

func NewKeywords() *Keywords

NewKeywords creates a new Keywords instance

func (*Keywords) AddKeyword

func (k *Keywords) AddKeyword(keyword Keyword) error

func (*Keywords) GetCompoundKeywordType

func (k *Keywords) GetCompoundKeywordType(s string) (models.TokenType, bool)

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

func (k *Keywords) GetDMLKeywordType(s string) (models.TokenType, bool)

GetDMLKeywordType returns the token type for a DML keyword

func (*Keywords) GetKeyword

func (k *Keywords) GetKeyword(word string) (Keyword, bool)

func (*Keywords) GetKeywordType

func (k *Keywords) GetKeywordType(s string) models.TokenType

GetKeywordType returns the token type for a keyword

func (*Keywords) GetTokenType

func (k *Keywords) GetTokenType(word string) models.TokenType

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

func (k *Keywords) IsCompoundKeyword(s string) bool

IsCompoundKeyword checks if a string is a compound keyword

func (*Keywords) IsCompoundKeywordStart

func (k *Keywords) IsCompoundKeywordStart(word string) bool

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

func (k *Keywords) IsDMLKeyword(s string) bool

IsDMLKeyword checks if a given string is a DML keyword

func (*Keywords) IsKeyword

func (k *Keywords) IsKeyword(s string) bool

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

func (k *Keywords) IsReserved(s string) bool

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

Jump to

Keyboard shortcuts

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