Skip to content

Commit b3d1073

Browse files
authored
Add CREATE/ALTER/DROP SETTINGS PROFILE and SHOW CREATE SETTINGS PROFILE support (#101)
1 parent 90cb7dc commit b3d1073

19 files changed

Lines changed: 271 additions & 271 deletions

File tree

ast/ast.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,47 @@ func (s *ShowCreateQuotaQuery) Pos() token.Position { return s.Position }
848848
func (s *ShowCreateQuotaQuery) End() token.Position { return s.Position }
849849
func (s *ShowCreateQuotaQuery) statementNode() {}
850850

851+
// CreateSettingsProfileQuery represents a CREATE SETTINGS PROFILE statement.
852+
type CreateSettingsProfileQuery struct {
853+
Position token.Position `json:"-"`
854+
Names []string `json:"names,omitempty"`
855+
}
856+
857+
func (c *CreateSettingsProfileQuery) Pos() token.Position { return c.Position }
858+
func (c *CreateSettingsProfileQuery) End() token.Position { return c.Position }
859+
func (c *CreateSettingsProfileQuery) statementNode() {}
860+
861+
// AlterSettingsProfileQuery represents an ALTER SETTINGS PROFILE statement.
862+
type AlterSettingsProfileQuery struct {
863+
Position token.Position `json:"-"`
864+
Names []string `json:"names,omitempty"`
865+
}
866+
867+
func (a *AlterSettingsProfileQuery) Pos() token.Position { return a.Position }
868+
func (a *AlterSettingsProfileQuery) End() token.Position { return a.Position }
869+
func (a *AlterSettingsProfileQuery) statementNode() {}
870+
871+
// DropSettingsProfileQuery represents a DROP SETTINGS PROFILE statement.
872+
type DropSettingsProfileQuery struct {
873+
Position token.Position `json:"-"`
874+
Names []string `json:"names,omitempty"`
875+
IfExists bool `json:"if_exists,omitempty"`
876+
}
877+
878+
func (d *DropSettingsProfileQuery) Pos() token.Position { return d.Position }
879+
func (d *DropSettingsProfileQuery) End() token.Position { return d.Position }
880+
func (d *DropSettingsProfileQuery) statementNode() {}
881+
882+
// ShowCreateSettingsProfileQuery represents a SHOW CREATE SETTINGS PROFILE statement.
883+
type ShowCreateSettingsProfileQuery struct {
884+
Position token.Position `json:"-"`
885+
Names []string `json:"names,omitempty"`
886+
}
887+
888+
func (s *ShowCreateSettingsProfileQuery) Pos() token.Position { return s.Position }
889+
func (s *ShowCreateSettingsProfileQuery) End() token.Position { return s.Position }
890+
func (s *ShowCreateSettingsProfileQuery) statementNode() {}
891+
851892
// CreateIndexQuery represents a CREATE INDEX statement.
852893
type CreateIndexQuery struct {
853894
Position token.Position `json:"-"`

internal/explain/explain.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,20 @@ func Node(sb *strings.Builder, node interface{}, depth int) {
127127
fmt.Fprintf(sb, "%sShowPrivilegesQuery\n", indent)
128128
case *ast.ShowCreateQuotaQuery:
129129
fmt.Fprintf(sb, "%sSHOW CREATE QUOTA query\n", indent)
130+
case *ast.CreateSettingsProfileQuery:
131+
fmt.Fprintf(sb, "%sCreateSettingsProfileQuery\n", indent)
132+
case *ast.AlterSettingsProfileQuery:
133+
// ALTER SETTINGS PROFILE uses CreateSettingsProfileQuery in ClickHouse's explain
134+
fmt.Fprintf(sb, "%sCreateSettingsProfileQuery\n", indent)
135+
case *ast.DropSettingsProfileQuery:
136+
fmt.Fprintf(sb, "%sDROP SETTINGS PROFILE query\n", indent)
137+
case *ast.ShowCreateSettingsProfileQuery:
138+
// Use PROFILES (plural) when multiple profiles are specified
139+
if len(n.Names) > 1 {
140+
fmt.Fprintf(sb, "%sSHOW CREATE SETTINGS PROFILES query\n", indent)
141+
} else {
142+
fmt.Fprintf(sb, "%sSHOW CREATE SETTINGS PROFILE query\n", indent)
143+
}
130144
case *ast.ShowGrantsQuery:
131145
fmt.Fprintf(sb, "%sShowGrantsQuery\n", indent)
132146
case *ast.GrantQuery:

parser/parser.go

Lines changed: 202 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,28 @@ func (p *Parser) parseStatement() ast.Statement {
123123
case token.CREATE:
124124
return p.parseCreate()
125125
case token.DROP:
126+
// Check for DROP SETTINGS PROFILE
127+
if p.peekIs(token.SETTINGS) {
128+
return p.parseDropSettingsProfile()
129+
}
130+
// Check for DROP PROFILE
131+
if p.peek.Token == token.IDENT && strings.ToUpper(p.peek.Value) == "PROFILE" {
132+
return p.parseDropSettingsProfile()
133+
}
126134
return p.parseDrop()
127135
case token.ALTER:
128136
// Check for ALTER USER
129137
if p.peekIs(token.USER) {
130138
return p.parseAlterUser()
131139
}
140+
// Check for ALTER SETTINGS PROFILE
141+
if p.peekIs(token.SETTINGS) {
142+
return p.parseAlterSettingsProfile()
143+
}
144+
// Check for ALTER PROFILE
145+
if p.peek.Token == token.IDENT && strings.ToUpper(p.peek.Value) == "PROFILE" {
146+
return p.parseAlterSettingsProfile()
147+
}
132148
return p.parseAlter()
133149
case token.TRUNCATE:
134150
return p.parseTruncate()
@@ -1314,6 +1330,9 @@ func (p *Parser) parseCreate() ast.Statement {
13141330
create.CreateUser = true
13151331
p.nextToken()
13161332
p.parseCreateUser(create)
1333+
case token.SETTINGS:
1334+
// CREATE SETTINGS PROFILE
1335+
return p.parseCreateSettingsProfile(pos)
13171336
case token.IDENT:
13181337
// Handle CREATE DICTIONARY, CREATE RESOURCE, CREATE WORKLOAD, CREATE NAMED COLLECTION, etc.
13191338
identUpper := strings.ToUpper(p.current.Value)
@@ -1330,7 +1349,10 @@ func (p *Parser) parseCreate() ast.Statement {
13301349
p.nextToken()
13311350
}
13321351
p.parseCreateGeneric(create)
1333-
case "RESOURCE", "WORKLOAD", "POLICY", "ROLE", "QUOTA", "PROFILE":
1352+
case "PROFILE":
1353+
// CREATE PROFILE (without SETTINGS keyword)
1354+
return p.parseCreateSettingsProfile(pos)
1355+
case "RESOURCE", "WORKLOAD", "POLICY", "ROLE", "QUOTA":
13341356
// Skip these statements - just consume tokens until semicolon
13351357
p.parseCreateGeneric(create)
13361358
default:
@@ -1866,6 +1888,182 @@ func (p *Parser) parseCreateGeneric(create *ast.CreateQuery) {
18661888
}
18671889
}
18681890

1891+
func (p *Parser) parseCreateSettingsProfile(pos token.Position) *ast.CreateSettingsProfileQuery {
1892+
query := &ast.CreateSettingsProfileQuery{
1893+
Position: pos,
1894+
}
1895+
1896+
// Skip SETTINGS if present (CREATE SETTINGS PROFILE vs CREATE PROFILE)
1897+
if p.currentIs(token.SETTINGS) {
1898+
p.nextToken()
1899+
}
1900+
1901+
// Skip PROFILE
1902+
if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "PROFILE" {
1903+
p.nextToken()
1904+
}
1905+
1906+
// Handle IF NOT EXISTS
1907+
if p.currentIs(token.IF) {
1908+
p.nextToken()
1909+
if p.currentIs(token.NOT) {
1910+
p.nextToken()
1911+
}
1912+
if p.currentIs(token.EXISTS) {
1913+
p.nextToken()
1914+
}
1915+
}
1916+
1917+
// Parse profile names (can be multiple: s1, s2, s3)
1918+
for {
1919+
name := p.parseIdentifierName()
1920+
if name != "" {
1921+
query.Names = append(query.Names, name)
1922+
}
1923+
if p.currentIs(token.COMMA) {
1924+
p.nextToken()
1925+
continue
1926+
}
1927+
break
1928+
}
1929+
1930+
// Skip the rest of the statement (SETTINGS, TO, etc.)
1931+
for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) {
1932+
p.nextToken()
1933+
}
1934+
1935+
return query
1936+
}
1937+
1938+
func (p *Parser) parseDropSettingsProfile() *ast.DropSettingsProfileQuery {
1939+
query := &ast.DropSettingsProfileQuery{
1940+
Position: p.current.Pos,
1941+
}
1942+
1943+
p.nextToken() // skip DROP
1944+
1945+
// Skip SETTINGS if present (DROP SETTINGS PROFILE vs DROP PROFILE)
1946+
if p.currentIs(token.SETTINGS) {
1947+
p.nextToken()
1948+
}
1949+
1950+
// Skip PROFILE
1951+
if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "PROFILE" {
1952+
p.nextToken()
1953+
}
1954+
1955+
// Handle IF EXISTS
1956+
if p.currentIs(token.IF) {
1957+
p.nextToken()
1958+
if p.currentIs(token.EXISTS) {
1959+
query.IfExists = true
1960+
p.nextToken()
1961+
}
1962+
}
1963+
1964+
// Parse profile names (can be multiple: s1, s2, s3)
1965+
for {
1966+
name := p.parseIdentifierName()
1967+
if name != "" {
1968+
query.Names = append(query.Names, name)
1969+
}
1970+
if p.currentIs(token.COMMA) {
1971+
p.nextToken()
1972+
continue
1973+
}
1974+
break
1975+
}
1976+
1977+
// Skip the rest of the statement
1978+
for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) {
1979+
p.nextToken()
1980+
}
1981+
1982+
return query
1983+
}
1984+
1985+
func (p *Parser) parseAlterSettingsProfile() *ast.AlterSettingsProfileQuery {
1986+
query := &ast.AlterSettingsProfileQuery{
1987+
Position: p.current.Pos,
1988+
}
1989+
1990+
p.nextToken() // skip ALTER
1991+
1992+
// Skip SETTINGS if present (ALTER SETTINGS PROFILE vs ALTER PROFILE)
1993+
if p.currentIs(token.SETTINGS) {
1994+
p.nextToken()
1995+
}
1996+
1997+
// Skip PROFILE
1998+
if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "PROFILE" {
1999+
p.nextToken()
2000+
}
2001+
2002+
// Handle IF EXISTS
2003+
if p.currentIs(token.IF) {
2004+
p.nextToken()
2005+
if p.currentIs(token.EXISTS) {
2006+
p.nextToken()
2007+
}
2008+
}
2009+
2010+
// Parse profile names (can be multiple: s1, s2, s3)
2011+
for {
2012+
name := p.parseIdentifierName()
2013+
if name != "" {
2014+
query.Names = append(query.Names, name)
2015+
}
2016+
if p.currentIs(token.COMMA) {
2017+
p.nextToken()
2018+
continue
2019+
}
2020+
break
2021+
}
2022+
2023+
// Skip the rest of the statement (SETTINGS, RENAME TO, etc.)
2024+
for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) {
2025+
p.nextToken()
2026+
}
2027+
2028+
return query
2029+
}
2030+
2031+
func (p *Parser) parseShowCreateSettingsProfile(pos token.Position) *ast.ShowCreateSettingsProfileQuery {
2032+
query := &ast.ShowCreateSettingsProfileQuery{
2033+
Position: pos,
2034+
}
2035+
2036+
// Skip SETTINGS if present (SHOW CREATE SETTINGS PROFILE vs SHOW CREATE PROFILE)
2037+
if p.currentIs(token.SETTINGS) {
2038+
p.nextToken()
2039+
}
2040+
2041+
// Skip PROFILE
2042+
if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "PROFILE" {
2043+
p.nextToken()
2044+
}
2045+
2046+
// Parse profile names (can be multiple: s1, s2, s3)
2047+
for {
2048+
name := p.parseIdentifierName()
2049+
if name != "" {
2050+
query.Names = append(query.Names, name)
2051+
}
2052+
if p.currentIs(token.COMMA) {
2053+
p.nextToken()
2054+
continue
2055+
}
2056+
break
2057+
}
2058+
2059+
// Skip the rest of the statement
2060+
for !p.currentIs(token.EOF) && !p.currentIs(token.SEMICOLON) {
2061+
p.nextToken()
2062+
}
2063+
2064+
return query
2065+
}
2066+
18692067
func (p *Parser) parseCreateDictionary(create *ast.CreateQuery) {
18702068
// Handle IF NOT EXISTS
18712069
if p.currentIs(token.IF) {
@@ -3358,6 +3556,9 @@ func (p *Parser) parseShow() ast.Statement {
33583556
p.nextToken()
33593557
}
33603558
return &ast.ShowCreateQuotaQuery{Position: pos, Name: name}
3559+
} else if p.currentIs(token.SETTINGS) || (p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "PROFILE") {
3560+
// SHOW CREATE SETTINGS PROFILE or SHOW CREATE PROFILE
3561+
return p.parseShowCreateSettingsProfile(pos)
33613562
} else if p.currentIs(token.IDENT) && strings.ToUpper(p.current.Value) == "DICTIONARY" {
33623563
show.ShowType = ast.ShowCreateDictionary
33633564
p.nextToken()

0 commit comments

Comments
 (0)