1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
// SPDX-FileCopyrightText: 2026 Stefan Majewsky <majewsky@gmx.net>
// SPDX-License-Identifier: Apache-2.0
package internal_test
import (
"reflect"
"testing"
"time"
"go.xyrillian.de/oblast/info"
"go.xyrillian.de/oblast/internal"
"go.xyrillian.de/oblast/internal/assert"
)
func TestPlanFieldTraversal(t *testing.T) {
type Log struct {
info.TableNameIs `db:"log_entries"`
info.PrimaryKeyIs `db:"id"`
ID int64 `db:"id,auto"`
CreatedAt time.Time `db:"created_at"`
Message string
private1 bool `db:"private1"` //nolint:unused
Ignored any `db:"-"`
}
// assert on interface implementations
var (
_ info.IsTable = Log{}
_ info.IsTableWithPrimaryKey = Log{}
)
// check that the plan for Log:
// 1. has no IndexByColumnName entries for marker types
// 2. uses the field name as a column name for "Message"
// 3. ignores "private1" because it cannot be written through reflection
// 4. ignores "Ignored" because its column name is "-"
// 5. recognizes "id" as an autofilled column
plan, err := internal.BuildPlan(reflect.TypeFor[Log](), internal.PostgresDialect{})
if err != nil {
t.Error(err)
}
assert.Equal(t, plan.TableName, "log_entries")
assert.DeepEqual(t, plan.AllColumnNames, []string{"id", "created_at", "Message"})
assert.DeepEqual(t, plan.PrimaryKeyColumnNames, []string{"id"})
assert.DeepEqual(t, plan.AutoColumnNames, []string{"id"})
assert.DeepEqual(t, plan.IndexByColumnName, map[string][]int{
"id": {2},
"created_at": {3},
"Message": {4},
})
assert.Equal(t, plan.Insert.Query,
`INSERT INTO "log_entries" ("created_at", "Message") VALUES ($1, $2) RETURNING "id"`,
)
assert.DeepEqual(t, plan.Insert.ArgumentIndexes, [][]int{{3}, {4}})
assert.Equal(t, plan.Update.Query,
`UPDATE "log_entries" SET "created_at" = $1, "Message" = $2 WHERE "id" = $3`,
)
assert.DeepEqual(t, plan.Update.ArgumentIndexes, [][]int{{3}, {4}, {2}})
assert.Equal(t, plan.Delete.Query,
`DELETE FROM "log_entries" WHERE "id" = $1`,
)
assert.DeepEqual(t, plan.Delete.ArgumentIndexes, [][]int{{2}})
type record struct {
Log
Foo bool `db:"foo"`
private2 bool `db:"private2"` //nolint:unused
}
// check that the plan for record:
// 1. works at all, even though it as a whole is an unexported type
// 2. traverses into Log and includes all of its fields as well
// 3. completely ignores the marker types in type Log
plan, err = internal.BuildPlan(reflect.TypeFor[record](), internal.PostgresDialect{})
if err != nil {
t.Error(err)
}
assert.Equal(t, plan.TableName, "")
assert.DeepEqual(t, plan.AllColumnNames, []string{"id", "created_at", "Message", "foo"})
assert.DeepEqual(t, plan.PrimaryKeyColumnNames, nil)
assert.DeepEqual(t, plan.AutoColumnNames, []string{"id"}) // this is okay, it does not bear significance in practice since no queries are generated
assert.DeepEqual(t, plan.IndexByColumnName, map[string][]int{
"id": {0, 2},
"created_at": {0, 3},
"Message": {0, 4},
"foo": {1},
})
assert.Equal(t, plan.Insert.Query, "")
assert.Equal(t, plan.Update.Query, "")
assert.Equal(t, plan.Delete.Query, "")
}
|