You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
SELECT ... WHERE tenant_id = ? AND shop_id = ? AND deleted_at IS NULLUPDATE ... WHERE user_condition AND tenant_id = ? AND shop_id = ? AND deleted_at IS NULLDELETE ... WHERE user_condition AND tenant_id = ? AND shop_id = ? AND deleted_at IS NULL
For inserts, scope fields are auto-filled when they are not explicitly provided.
Transaction Support
orm.DB also implements orm.TransactionalConnection when the wrapped
connection supports transactions:
db.orm_begin()!
sql db {
insert User{name: 'new'}
}!
db.orm_commit()!
Scope filters continue to apply inside transactions.
Why This Design
No global state
Scope data lives on the orm.DB value, so each request can carry its own scoped
connection.
No changes to the SQL DSL
sql db { ... } already works with any value implementing orm.Connection. orm.DB implements the same interface, so the compiler-facing DSL does not need
a new syntax.
Supports more than tenant filtering
The same mechanism supports tenancy, soft delete, row-level access control, and
other automatic filters.
Explicit escape hatch
db.unscoped() gives a controlled way to bypass all or selected filters without
temporarily mutating global state.
Expected Benefits
Thread-safe request-level scope isolation
Multiple filters instead of one hardcoded tenant field
Works with existing sql blocks
Keeps raw database connections backward-compatible
Allows static default filters and explicit dynamic filters
Removes tenant-filter-specific logic from individual DB backends
Problem
The current
@[tenant_filter]ORM feature stores tenant state in a mutable__globalvariable. This causes several concrete problems:state without locks or TLS.
request in web servers.
applications often need multiple automatic filters, such as:
tenant_id = ?org_id = ?shop_id = ?deleted_at IS NULLfiltering.
Proposal:
orm.DB+DataScopeIntroduce
orm.DB, a small wrapper around anyorm.Connection.orm.DBcarries aDataScope, and automatically applies its filters to ORMqueries before delegating to the wrapped connection.
Then normal
sqlblocks work unchanged:The query is automatically scoped:
Core API
Static By Default, Dynamic When Needed
Most scope filters have a fixed SQL shape. For example,
tenant_id = ?shouldbe known from the scope definition and can be treated as static.
orm.QueryFilter{ field: 'tenant_id' value: orm.Primitive(tenant_id) }The value can still be runtime data. The static part is the filter shape:
field + operator.
For filters that need runtime behavior, use
mode: .dynamic:This allows mixed scopes:
tenant_idcan be handled as a stable/default filter.shop_idcan be applied dynamically based on request role, shop access, ormiddleware logic.
Bypassing Scope Filters
db.unscoped()returns a neworm.DBvalue. It does not mutate the original DB.Skip all filters:
Skip one filter:
Skip multiple filters:
This works for
select,insert,update, anddelete.Table-Level Opt-Out
A table can opt out of all automatic scope filters:
Queries for this table ignore
DataScope.Multi-Level Tenancy Example
Generated behavior:
For inserts, scope fields are auto-filled when they are not explicitly provided.
Transaction Support
orm.DBalso implementsorm.TransactionalConnectionwhen the wrappedconnection supports transactions:
Scope filters continue to apply inside transactions.
Why This Design
No global state
Scope data lives on the
orm.DBvalue, so each request can carry its own scopedconnection.
No changes to the SQL DSL
sql db { ... }already works with any value implementingorm.Connection.orm.DBimplements the same interface, so the compiler-facing DSL does not needa new syntax.
Supports more than tenant filtering
The same mechanism supports tenancy, soft delete, row-level access control, and
other automatic filters.
Explicit escape hatch
db.unscoped()gives a controlled way to bypass all or selected filters withouttemporarily mutating global state.
Expected Benefits
sqlblocksProposed Solution
No response
Other Information
No response
Acknowledgements
Version used
V 0.5.1 e02966e
Environment details (OS name and version, etc.)
Note
You can use the 👍 reaction to increase the issue's priority for developers.
Please note that only the 👍 reaction to the issue itself counts as a vote.
Other reactions and those to comments will not be taken into account.