keywords

package
v1.6.0 Latest Latest
Warning

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

Go to latest
Published: Dec 11, 2025 License: AGPL-3.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. It includes reserved words, DDL/DML keywords, dialect-specific extensions, and window function keywords.

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: "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: "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},
}
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 keywords

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 keywords

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.TokenTypeKeyword, 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 keywords that can't be used as table aliases

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: "QUERY", Type: models.TokenTypeKeyword},
	{Word: "RAISE", Type: models.TokenTypeKeyword},
	{Word: "REPLACE", Type: models.TokenTypeKeyword},
	{Word: "TEMP", Type: models.TokenTypeKeyword},
	{Word: "TEMPORARY", Type: models.TokenTypeKeyword},
	{Word: "VACUUM", Type: models.TokenTypeKeyword},
	{Word: "VIRTUAL", Type: models.TokenTypeKeyword},
}

SQLite specific keywords

Functions

This section is empty.

Types

type Keyword

type Keyword struct {
	Word                  string
	Type                  models.TokenType
	Reserved              bool
	ReservedForTableAlias bool
}

Keyword represents a SQL keyword with its properties

type KeywordCategory

type KeywordCategory map[string]models.TokenType

KeywordCategory represents a category of SQL 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

func New

func New(dialect SQLDialect, ignoreCase bool) *Keywords

addKeywordsWithCategory is a helper method to add multiple keywords New creates a new Keywords instance with the specified dialect and case sensitivity

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

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

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

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 keyword

func (*Keywords) IsReserved

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

IsReserved checks if a keyword is reserved

type SQLDialect

type SQLDialect string

SQLDialect represents different SQL dialects

const (
	DialectUnknown    SQLDialect = "unknown"
	DialectGeneric    SQLDialect = "generic"
	DialectMySQL      SQLDialect = "mysql"
	DialectPostgreSQL SQLDialect = "postgresql"
	DialectSQLite     SQLDialect = "sqlite"
)

Jump to

Keyboard shortcuts

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