aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md6
-rw-r--r--oblast.go8
-rw-r--r--plan.go22
-rw-r--r--query_test.go5
4 files changed, 31 insertions, 10 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e63da04..cdc9516 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,12 @@ SPDX-FileCopyrightText: 2026 Stefan Majewsky <majewsky@gmx.net>
SPDX-License-Identifier: Apache-2.0
-->
+# v0.2.0 (TBD)
+
+Changes:
+
+- Add func StructTagKeyIs.
+
# v0.1.0 (2026-04-18)
Initial release. This release has code quality worthy of a 1.x version number,
diff --git a/oblast.go b/oblast.go
index 52c0cfd..338f245 100644
--- a/oblast.go
+++ b/oblast.go
@@ -123,6 +123,14 @@ func PrimaryKeyIs(columnNames ...string) PlanOption {
return func(opts *planOpts) { opts.PrimaryKeyColumnNames = columnNames }
}
+// StructTagKeyIs is a PlanOption for record types that allows renaming the struct tag key that Oblast inspects from its default value of "db".
+// For example, providing StructTagKeyIs("oblast") means that a struct tag like `db:",auto"` must be written as `oblast:",auto"` instead.
+//
+// This is useful when migrating from or to another ORM library that uses the same `db:"..."` tag as Oblast, but with conflicting semantics.
+func StructTagKeyIs(key string) PlanOption {
+ return func(opts *planOpts) { opts.StructTagKey = key }
+}
+
// Handle is an interface for functions providing direct DB access.
// It covers methods provided by both *sql.DB and *sql.Tx, thus allowing functions using it to be used both within and outside of transactions.
type Handle interface {
diff --git a/plan.go b/plan.go
index 8b7ec1d..9c4da54 100644
--- a/plan.go
+++ b/plan.go
@@ -49,6 +49,7 @@ type plannedQuery struct {
// planOpts holds additional arguments to buildPlan().
type planOpts struct {
+ StructTagKey string // defaults to "db"
TableName string
PrimaryKeyColumnNames []string
}
@@ -59,6 +60,11 @@ func buildPlan(t reflect.Type, dialect Dialect, opts planOpts) (plan, error) {
return plan{}, fmt.Errorf("expected struct type, but got kind %q", t.Kind().String())
}
+ // apply defaults to planOpts fields
+ if opts.StructTagKey == "" {
+ opts.StructTagKey = "db"
+ }
+
var p = plan{
TypeName: t.Name(),
TableName: opts.TableName,
@@ -85,7 +91,7 @@ func buildPlan(t reflect.Type, dialect Dialect, opts planOpts) (plan, error) {
// recurse into struct fields (i.e. ignore the struct itself and consider its members instead)
// unless the field itself has a `db:"..."` tag
if field.Type.Kind() == reflect.Struct || (field.Type.Kind() == reflect.Pointer && field.Type.Elem().Kind() == reflect.Struct) {
- if field.Tag.Get("db") == "" {
+ if field.Tag.Get(opts.StructTagKey) == "" {
indexesOfUnusedTransparentStructs = append(indexesOfUnusedTransparentStructs, field.Index)
if field.Type.Kind() == reflect.Pointer {
// remember that, when scanning into a record of type `t`, we need to write a non-nil zeroed struct into this field
@@ -105,7 +111,7 @@ func buildPlan(t reflect.Type, dialect Dialect, opts planOpts) (plan, error) {
}
// check `db:"..."` tag, ignore fields that are declared with column name "-"
- tags := strings.Split(strings.TrimSpace(field.Tag.Get("db")), ",")
+ tags := strings.Split(strings.TrimSpace(field.Tag.Get(opts.StructTagKey)), ",")
columnName, extraTags := cmp.Or(tags[0], field.Name), tags[1:]
if columnName == "-" {
continue
@@ -113,8 +119,8 @@ func buildPlan(t reflect.Type, dialect Dialect, opts planOpts) (plan, error) {
if otherIndex := p.IndexByColumnName[columnName]; otherIndex != nil {
return plan{}, fmt.Errorf(
- "duplicate tag `db:%q` on field index %v, but also on field index %v",
- columnName, otherIndex, field.Index,
+ "duplicate tag `%s:%q` on field index %v, but also on field index %v",
+ opts.StructTagKey, columnName, otherIndex, field.Index,
)
}
p.IndexByColumnName[columnName] = field.Index
@@ -134,7 +140,7 @@ func buildPlan(t reflect.Type, dialect Dialect, opts planOpts) (plan, error) {
case "auto":
p.AutoColumnNames = append(p.AutoColumnNames, columnName)
default:
- return plan{}, fmt.Errorf("unknown option `db:%q` on field %q", ","+tag, field.Name)
+ return plan{}, fmt.Errorf("unknown option `%s:%q` on field %q", opts.StructTagKey, ","+tag, field.Name)
}
}
}
@@ -146,8 +152,8 @@ func buildPlan(t reflect.Type, dialect Dialect, opts planOpts) (plan, error) {
for _, index := range indexesOfUnusedTransparentStructs {
field := t.FieldByIndex(index)
return plan{}, fmt.Errorf(
- "field %q of type %s does not contain any mapped fields (to map this whole field to a DB column, add an explicit `db:\"...\"` tag)",
- field.Name, field.Type.String(),
+ "field %q of type %s does not contain any mapped fields (to map this whole field to a DB column, add an explicit `%s:\"...\"` tag)",
+ field.Name, field.Type.String(), opts.StructTagKey,
)
}
@@ -160,7 +166,7 @@ func buildPlan(t reflect.Type, dialect Dialect, opts planOpts) (plan, error) {
for _, columnName := range p.PrimaryKeyColumnNames {
_, ok := p.IndexByColumnName[columnName]
if !ok {
- return plan{}, fmt.Errorf("no field has tag `db:%q`, but a field of this name was declared in the primary key", columnName)
+ return plan{}, fmt.Errorf("no field has tag `%s:%q`, but a field of this name was declared in the primary key", opts.StructTagKey, columnName)
}
}
diff --git a/query_test.go b/query_test.go
index 7dde757..2d14a88 100644
--- a/query_test.go
+++ b/query_test.go
@@ -19,11 +19,12 @@ func TestInsertBasicUsingLastInsertId(t *testing.T) {
db := sql.OpenDB(md)
type basicRecord struct {
- ID int64 `db:"id,auto"`
- Name string `db:"name"`
+ ID int64 `oblast:"id,auto"`
+ Name string `oblast:"name"`
}
store := oblast.MustNewStore[basicRecord](
oblast.SqliteDialect(),
+ oblast.StructTagKeyIs("oblast"), // this test also randomly provides coverage for this option
oblast.TableNameIs("basic_records"),
oblast.PrimaryKeyIs("id"),
)