Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions pkg/sql/parser/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,23 @@ func (p *Parser) parseDescribeStatement() (ast.Statement, error) {
return desc, nil
}

// Snowflake: DESCRIBE TABLE <name>, DESCRIBE VIEW <name>, DESCRIBE STAGE
// <name>, etc. Also MySQL's DESCRIBE <db>.<table>. Accept and consume a
// leading object-kind keyword (TABLE, VIEW, DATABASE, SCHEMA) before the
// name so we don't fail on "DESCRIBE TABLE users".
if p.isType(models.TokenTypeTable) || p.isType(models.TokenTypeView) ||
p.isType(models.TokenTypeDatabase) ||
strings.EqualFold(p.currentToken.Token.Value, "SCHEMA") ||
strings.EqualFold(p.currentToken.Token.Value, "STAGE") ||
strings.EqualFold(p.currentToken.Token.Value, "STREAM") ||
strings.EqualFold(p.currentToken.Token.Value, "TASK") ||
strings.EqualFold(p.currentToken.Token.Value, "PIPE") ||
strings.EqualFold(p.currentToken.Token.Value, "FUNCTION") ||
strings.EqualFold(p.currentToken.Token.Value, "PROCEDURE") ||
strings.EqualFold(p.currentToken.Token.Value, "WAREHOUSE") {
p.advance()
}

name, err := p.parseQualifiedName()
if err != nil {
return nil, p.expectedError("table name")
Expand Down
33 changes: 32 additions & 1 deletion pkg/sql/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,9 @@ func (p *Parser) parseStatement() (ast.Statement, error) {
case models.TokenTypeShow:
p.advance()
return p.parseShowStatement()
case models.TokenTypeDescribe, models.TokenTypeExplain:
case models.TokenTypeDescribe, models.TokenTypeExplain, models.TokenTypeDesc:
// DESC is the ORDER-BY sort-direction token but also a synonym for
// DESCRIBE at statement position (Oracle, Snowflake, MySQL).
p.advance()
return p.parseDescribeStatement()
case models.TokenTypeReplace:
Expand All @@ -713,10 +715,39 @@ func (p *Parser) parseStatement() (ast.Statement, error) {
p.advance()
return p.parsePragmaStatement()
}
// Snowflake session-context switches: USE [WAREHOUSE|DATABASE|SCHEMA|ROLE] <name>.
// USE is not tokenized as a keyword; dispatch by value in the Snowflake dialect.
if p.dialect == string(keywords.DialectSnowflake) &&
strings.EqualFold(p.currentToken.Token.Value, "USE") {
return p.parseSnowflakeUseStatement()
}
}
return nil, p.expectedError("statement")
}

// parseSnowflakeUseStatement parses:
//
// USE [WAREHOUSE | DATABASE | SCHEMA | ROLE] <name>
//
// The object-kind keyword is optional (plain "USE <name>" switches the current
// database). We parse-only; the statement is represented as a DescribeStatement
// placeholder until a dedicated UseStatement node is introduced.
func (p *Parser) parseSnowflakeUseStatement() (ast.Statement, error) {
p.advance() // Consume USE
// Optional object kind.
switch strings.ToUpper(p.currentToken.Token.Value) {
case "WAREHOUSE", "DATABASE", "SCHEMA", "ROLE":
p.advance()
}
name, err := p.parseQualifiedName()
if err != nil {
return nil, p.expectedError("name after USE")
}
stmt := ast.GetDescribeStatement()
stmt.TableName = "USE " + name
return stmt, nil
}

// NewParser creates a new parser with optional configuration.
func NewParser(opts ...ParserOption) *Parser {
p := &Parser{}
Expand Down
39 changes: 39 additions & 0 deletions pkg/sql/parser/snowflake_top_level_stmts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2026 GoSQLX Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");

package parser_test

import (
"testing"

"github.com/ajitpratap0/GoSQLX/pkg/gosqlx"
"github.com/ajitpratap0/GoSQLX/pkg/sql/keywords"
)

// TestSnowflakeUseAndDescribe verifies Snowflake session-context statements
// (USE WAREHOUSE/DATABASE/SCHEMA/ROLE) and DESCRIBE/DESC with object-kind
// prefixes. Regression for #483.
func TestSnowflakeUseAndDescribe(t *testing.T) {
queries := []string{
`USE WAREHOUSE compute_wh`,
`USE DATABASE my_db`,
`USE SCHEMA analytics`,
`USE ROLE analyst`,
`USE my_db`,
`USE my_db.public`,
`DESCRIBE TABLE users`,
`DESCRIBE VIEW user_summary`,
`DESCRIBE STAGE my_stage`,
`DESC TABLE users`,
`DESC users`,
}
for _, q := range queries {
q := q
t.Run(q, func(t *testing.T) {
if _, err := gosqlx.ParseWithDialect(q, keywords.DialectSnowflake); err != nil {
t.Fatalf("parse failed: %v", err)
}
})
}
}
Loading