Skip to content

Commit 4f5d0fb

Browse files
Ajit Pratap Singhclaude
authored andcommitted
fix(mariadb): code review fixes — pool comments, expressionNode docs, PERIOD FOR SYSTEM_TIME test
- Add '// zero all fields' comment to ReleaseCreateSequenceStatement and ReleaseAlterSequenceStatement - Add design-choice comments on expressionNode() for ForSystemTimeClause, ConnectByClause, PeriodDefinition - Add TestMariaDB_CreateTable_PeriodForSystemTime to cover PERIOD FOR SYSTEM_TIME parsing - Fix parsePeriodDefinition to use parseColumnName so SYSTEM_TIME (reserved keyword) is accepted as period name - Add GENERATED ALWAYS AS ROW START/END column constraint parsing in parseColumnConstraint (MariaDB system-versioned columns) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent c80cffe commit 4f5d0fb

5 files changed

Lines changed: 79 additions & 3 deletions

File tree

pkg/sql/ast/ast.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1938,6 +1938,9 @@ type ForSystemTimeClause struct {
19381938
Pos models.Location // Source position of the FOR keyword (1-based line and column)
19391939
}
19401940

1941+
// expressionNode satisfies the Expression interface so ForSystemTimeClause can be
1942+
// stored in TableReference.ForSystemTime without a separate interface type.
1943+
// Semantically it is a table-level clause, not a scalar expression.
19411944
func (c *ForSystemTimeClause) expressionNode() {}
19421945
func (c ForSystemTimeClause) TokenLiteral() string { return "FOR SYSTEM_TIME" }
19431946
func (c ForSystemTimeClause) Children() []Node {
@@ -1965,6 +1968,9 @@ type PeriodDefinition struct {
19651968
Pos models.Location // Source position of the PERIOD FOR keyword (1-based line and column)
19661969
}
19671970

1971+
// expressionNode satisfies the Expression interface so PeriodDefinition can be
1972+
// stored in CreateTableStatement.PeriodDefinitions without a separate interface type.
1973+
// Semantically it is a table column constraint, not a scalar expression.
19681974
func (p *PeriodDefinition) expressionNode() {}
19691975
func (p PeriodDefinition) TokenLiteral() string { return "PERIOD FOR" }
19701976
func (p PeriodDefinition) Children() []Node {
@@ -1994,6 +2000,9 @@ type ConnectByClause struct {
19942000
Pos models.Location // Source position of the CONNECT BY keyword (1-based line and column)
19952001
}
19962002

2003+
// expressionNode satisfies the Expression interface so ConnectByClause can be
2004+
// stored in SelectStatement.ConnectBy without a separate interface type.
2005+
// Semantically it is a query-level clause, not a scalar expression.
19972006
func (c *ConnectByClause) expressionNode() {}
19982007
func (c ConnectByClause) TokenLiteral() string { return "CONNECT BY" }
19992008
func (c ConnectByClause) Children() []Node {

pkg/sql/ast/pool.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1814,7 +1814,7 @@ func NewCreateSequenceStatement() *CreateSequenceStatement {
18141814

18151815
// ReleaseCreateSequenceStatement returns a CreateSequenceStatement to the pool.
18161816
func ReleaseCreateSequenceStatement(s *CreateSequenceStatement) {
1817-
*s = CreateSequenceStatement{}
1817+
*s = CreateSequenceStatement{} // zero all fields
18181818
createSequencePool.Put(s)
18191819
}
18201820

@@ -1837,6 +1837,6 @@ func NewAlterSequenceStatement() *AlterSequenceStatement {
18371837

18381838
// ReleaseAlterSequenceStatement returns an AlterSequenceStatement to the pool.
18391839
func ReleaseAlterSequenceStatement(s *AlterSequenceStatement) {
1840-
*s = AlterSequenceStatement{}
1840+
*s = AlterSequenceStatement{} // zero all fields
18411841
alterSequencePool.Put(s)
18421842
}

pkg/sql/parser/ddl_columns.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
package parser
1919

2020
import (
21+
"strings"
22+
2123
goerrors "github.com/ajitpratap0/GoSQLX/pkg/errors"
2224
"github.com/ajitpratap0/GoSQLX/pkg/models"
2325
"github.com/ajitpratap0/GoSQLX/pkg/sql/ast"
@@ -247,6 +249,34 @@ func (p *Parser) parseColumnConstraint() (*ast.ColumnConstraint, bool, error) {
247249
return constraint, true, nil
248250
}
249251

252+
// GENERATED ALWAYS AS ROW START / ROW END (MariaDB system-versioned columns)
253+
// Syntax: GENERATED ALWAYS AS ROW START | ROW END
254+
if strings.EqualFold(p.currentToken.Token.Value, "GENERATED") {
255+
p.advance() // Consume GENERATED
256+
// Optional ALWAYS
257+
if strings.EqualFold(p.currentToken.Token.Value, "ALWAYS") {
258+
p.advance() // Consume ALWAYS
259+
}
260+
// Expect AS
261+
if !p.isType(models.TokenTypeAs) {
262+
return nil, false, p.expectedError("AS after GENERATED [ALWAYS]")
263+
}
264+
p.advance() // Consume AS
265+
// Expect ROW
266+
if !p.isType(models.TokenTypeRow) {
267+
return nil, false, p.expectedError("ROW after GENERATED [ALWAYS] AS")
268+
}
269+
p.advance() // Consume ROW
270+
// Expect START or END
271+
rowRole := strings.ToUpper(p.currentToken.Token.Value)
272+
if rowRole != "START" && rowRole != "END" {
273+
return nil, false, p.expectedError("START or END after GENERATED [ALWAYS] AS ROW")
274+
}
275+
p.advance() // Consume START or END
276+
constraint.Type = "GENERATED ALWAYS AS ROW " + rowRole
277+
return constraint, true, nil
278+
}
279+
250280
// No constraint found
251281
return nil, false, nil
252282
}

pkg/sql/parser/mariadb.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,8 @@ func (p *Parser) parsePeriodDefinition() (*ast.PeriodDefinition, error) {
441441
}
442442
p.advance()
443443

444-
name := p.parseIdent()
444+
// Use parseColumnName so that reserved-keyword period names like SYSTEM_TIME are accepted.
445+
name := p.parseColumnName()
445446
if name == nil || name.Name == "" {
446447
return nil, p.expectedError("period name")
447448
}

pkg/sql/parser/mariadb_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,3 +318,39 @@ func TestMariaDB_SQLFiles(t *testing.T) {
318318
})
319319
}
320320
}
321+
322+
func TestMariaDB_CreateTable_PeriodForSystemTime(t *testing.T) {
323+
sql := `CREATE TABLE t (
324+
id INT,
325+
row_start DATETIME(6) GENERATED ALWAYS AS ROW START,
326+
row_end DATETIME(6) GENERATED ALWAYS AS ROW END,
327+
PERIOD FOR SYSTEM_TIME(row_start, row_end)
328+
) WITH SYSTEM VERSIONING`
329+
tree, err := parser.ParseWithDialect(sql, keywords.DialectMariaDB)
330+
if err != nil {
331+
t.Fatalf("unexpected error: %v", err)
332+
}
333+
if len(tree.Statements) != 1 {
334+
t.Fatalf("expected 1 statement, got %d", len(tree.Statements))
335+
}
336+
stmt, ok := tree.Statements[0].(*ast.CreateTableStatement)
337+
if !ok {
338+
t.Fatalf("expected CreateTableStatement, got %T", tree.Statements[0])
339+
}
340+
if len(stmt.PeriodDefinitions) == 0 {
341+
t.Fatal("expected at least one PeriodDefinition")
342+
}
343+
pd := stmt.PeriodDefinitions[0]
344+
if pd.Name == nil || !strings.EqualFold(pd.Name.Name, "SYSTEM_TIME") {
345+
t.Errorf("expected period name SYSTEM_TIME, got %v", pd.Name)
346+
}
347+
if pd.StartCol == nil || pd.StartCol.Name != "row_start" {
348+
t.Errorf("expected StartCol=row_start, got %v", pd.StartCol)
349+
}
350+
if pd.EndCol == nil || pd.EndCol.Name != "row_end" {
351+
t.Errorf("expected EndCol=row_end, got %v", pd.EndCol)
352+
}
353+
if !stmt.WithSystemVersioning {
354+
t.Error("expected WithSystemVersioning = true")
355+
}
356+
}

0 commit comments

Comments
 (0)