mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-29 18:42:15 +00:00
Read pull request "feat(database): add statement and CTE changes (#11069)" This pull request introduces new functionality for handling database changes using statements and Common Table Expressions (CTEs). ### Summary This PR accomplishes the following: - **Statement and CTE Changes**: Adds new types and functions in change.go for handling more complex database changes. This includes: - `NewChangeToNull`: A change that sets a column to `NULL`. - `NewChangeToColumn`: A change that sets a column's value to the value of another column. - `NewIncrementColumnChange`: A change that increments a column's value by 1. - `NewChangeToStatement`: A change that sets a column's value to the result of a subquery. - `NewCTEChange`: A change that uses a Common Table Expression (CTE) to perform more complex updates. - **Testing**: Adds comprehensive tests in change_test.go to cover the new functionality and ensure the correctness of the generated SQL statements and arguments. - **Ignoring AI Files**: Updates the .gitignore to exclude files generated by AI tools. This work enhances the flexibility of the database layer, allowing for more complex and efficient database operations.
259 lines
6.7 KiB
Go
259 lines
6.7 KiB
Go
package database
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/muhlemmer/gu"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestChangeWrite(t *testing.T) {
|
|
type want struct {
|
|
stmt string
|
|
args []any
|
|
}
|
|
for _, test := range []struct {
|
|
name string
|
|
change Change
|
|
want want
|
|
}{
|
|
{
|
|
name: "change",
|
|
change: NewChange(NewColumn("table", "column"), "value"),
|
|
want: want{
|
|
stmt: "column = $1",
|
|
args: []any{"value"},
|
|
},
|
|
},
|
|
{
|
|
name: "change ptr to null",
|
|
change: NewChangePtr[int](NewColumn("table", "column"), nil),
|
|
want: want{
|
|
stmt: "column = NULL",
|
|
args: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "change ptr to value",
|
|
change: NewChangePtr(NewColumn("table", "column"), gu.Ptr(42)),
|
|
want: want{
|
|
stmt: "column = $1",
|
|
args: []any{42},
|
|
},
|
|
},
|
|
{
|
|
name: "multiple changes",
|
|
change: NewChanges(
|
|
NewChange(NewColumn("table", "column1"), "value1"),
|
|
NewChangePtr[int](NewColumn("table", "column2"), nil),
|
|
NewChange(NewColumn("table", "column3"), 123),
|
|
),
|
|
want: want{
|
|
stmt: "column1 = $1, column2 = NULL, column3 = $2",
|
|
args: []any{"value1", 123},
|
|
},
|
|
},
|
|
{
|
|
name: "increment change",
|
|
change: NewIncrementColumnChange(NewColumn("table", "counter")),
|
|
want: want{
|
|
stmt: "counter = counter + 1",
|
|
},
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
var builder StatementBuilder
|
|
test.change.Write(&builder)
|
|
assert.Equal(t, test.want.stmt, builder.String())
|
|
require.Len(t, builder.Args(), len(test.want.args))
|
|
for i, arg := range test.want.args {
|
|
assert.Equal(t, arg, builder.Args()[i])
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestChangeToStatement(t *testing.T) {
|
|
type want struct {
|
|
stmt string
|
|
args []any
|
|
}
|
|
for _, test := range []struct {
|
|
name string
|
|
prefillBuilder func(builder *StatementBuilder)
|
|
change *changeToStatement
|
|
want want
|
|
}{
|
|
{
|
|
name: "change to statement",
|
|
change: NewChangeToStatement(NewColumn("table", "column"), func(builder *StatementBuilder) {
|
|
builder.WriteString("SELECT 1")
|
|
}).(*changeToStatement),
|
|
want: want{
|
|
stmt: "column = (SELECT 1)",
|
|
args: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "change to statement with args",
|
|
change: NewChangeToStatement(NewColumn("table", "column"), func(builder *StatementBuilder) {
|
|
builder.WriteString("SELECT ")
|
|
builder.WriteArg(42)
|
|
}).(*changeToStatement),
|
|
want: want{
|
|
stmt: "column = (SELECT $1)",
|
|
args: []any{42},
|
|
},
|
|
},
|
|
{
|
|
name: "change to statement with existing builder args",
|
|
prefillBuilder: func(builder *StatementBuilder) {
|
|
builder.WriteString("UPDATE table SET ")
|
|
NewChanges(
|
|
NewChange(NewColumn("table", "field1"), "asdf"),
|
|
NewChangeToNull(NewColumn("table", "field2")),
|
|
NewChangeToColumn(NewColumn("table", "field3"), NewColumn("table", "field4")),
|
|
).Write(builder)
|
|
builder.WriteString(", ")
|
|
},
|
|
change: NewChangeToStatement(NewColumn("table", "column"), func(builder *StatementBuilder) {
|
|
builder.WriteString("SELECT ")
|
|
builder.WriteArg(42)
|
|
builder.WriteString(" FROM other_table WHERE ")
|
|
NewBooleanCondition(NewColumn("table", "id"), true).Write(builder)
|
|
}).(*changeToStatement),
|
|
want: want{
|
|
stmt: "UPDATE table SET field1 = $1, field2 = NULL, field3 = table.field4, column = (SELECT $2 FROM other_table WHERE table.id = $3)",
|
|
args: []any{"asdf", 42, true},
|
|
},
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
var builder StatementBuilder
|
|
if test.prefillBuilder != nil {
|
|
test.prefillBuilder(&builder)
|
|
}
|
|
test.change.Write(&builder)
|
|
assert.Equal(t, test.want.stmt, builder.String())
|
|
assert.Equal(t, builder.Args(), test.want.args)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCTEChange(t *testing.T) {
|
|
type want struct {
|
|
stmt string
|
|
args []any
|
|
}
|
|
for _, test := range []struct {
|
|
name string
|
|
prefillBuilder func(builder *StatementBuilder)
|
|
afterCTE func(builder *StatementBuilder)
|
|
change *cteChange
|
|
want want
|
|
}{
|
|
{
|
|
name: "only CTE change",
|
|
change: NewCTEChange(
|
|
func(builder *StatementBuilder) {
|
|
builder.WriteString("SELECT 1 AS value")
|
|
},
|
|
nil,
|
|
).(*cteChange),
|
|
want: want{
|
|
stmt: "WITH cte AS (SELECT 1 AS value) ",
|
|
args: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "with existing CTE",
|
|
prefillBuilder: func(builder *StatementBuilder) {
|
|
builder.WriteString("existing_cte AS (SELECT ")
|
|
builder.WriteArg(42)
|
|
builder.WriteString(" AS reason), ")
|
|
},
|
|
change: NewCTEChange(
|
|
func(builder *StatementBuilder) {
|
|
builder.WriteString("SELECT ")
|
|
Columns{
|
|
NewColumn("table", "column1"),
|
|
NewColumn("table", "column2"),
|
|
}.WriteQualified(builder)
|
|
builder.WriteString(", ")
|
|
builder.WriteArgs("asdf", 123, false, NullInstruction)
|
|
},
|
|
nil,
|
|
).(*cteChange),
|
|
want: want{
|
|
stmt: "WITH existing_cte AS (SELECT $1 AS reason), cte AS (SELECT table.column1, table.column2, $2, $3, $4, NULL) ",
|
|
args: []any{42, "asdf", 123, false},
|
|
},
|
|
},
|
|
{
|
|
name: "CTE change with after CTE statements",
|
|
change: NewCTEChange(
|
|
func(builder *StatementBuilder) {
|
|
builder.WriteString("SELECT ")
|
|
builder.WriteArg(1)
|
|
builder.WriteString(" AS value")
|
|
},
|
|
nil,
|
|
).(*cteChange),
|
|
afterCTE: func(builder *StatementBuilder) {
|
|
builder.WriteString("SELECT * FROM cte ")
|
|
},
|
|
want: want{
|
|
stmt: "WITH cte AS (SELECT $1 AS value) SELECT * FROM cte ",
|
|
args: []any{1},
|
|
},
|
|
},
|
|
{
|
|
name: "CTE change with column change after CTE",
|
|
change: NewCTEChange(
|
|
func(builder *StatementBuilder) {
|
|
builder.WriteString("SELECT ")
|
|
builder.WriteArg(1)
|
|
builder.WriteString(" AS value")
|
|
},
|
|
func(name string) Change {
|
|
return NewChangeToStatement(NewColumn("table", "field"), func(builder *StatementBuilder) {
|
|
builder.WriteString("SELECT * FROM ")
|
|
builder.WriteString(name)
|
|
builder.WriteString(" WHERE ")
|
|
NewColumnCondition(NewColumn(name, "value"), NewColumn("table", "value")).Write(builder)
|
|
})
|
|
},
|
|
).(*cteChange),
|
|
afterCTE: func(builder *StatementBuilder) {
|
|
builder.WriteString("UPDATE test SET ")
|
|
},
|
|
want: want{
|
|
stmt: "WITH cte AS (SELECT $1 AS value) UPDATE test SET field = (SELECT * FROM cte WHERE cte.value = table.value)",
|
|
args: []any{1},
|
|
},
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
var builder StatementBuilder
|
|
builder.WriteString("WITH ")
|
|
if test.prefillBuilder != nil {
|
|
test.prefillBuilder(&builder)
|
|
}
|
|
|
|
builder.WriteString("cte AS (")
|
|
test.change.SetName("cte")
|
|
test.change.WriteCTE(&builder)
|
|
builder.WriteString(") ")
|
|
|
|
if test.afterCTE != nil {
|
|
test.afterCTE(&builder)
|
|
}
|
|
test.change.Write(&builder)
|
|
|
|
assert.Equal(t, test.want.stmt, builder.String())
|
|
assert.Equal(t, builder.Args(), test.want.args)
|
|
})
|
|
}
|
|
}
|