Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ContextualMods, moved Hooks and QueryModFunc to base package #287

Merged
merged 1 commit into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Added error constants for matching against both specific and generic unique constraint errors raised by the underlying database driver. (thanks @mbezhanov)
- Added support for regular expressions in the `only` and `except` table filters. (thanks @mbezhanov)
- Added `ContextualMods` which are similar to regular mods but take a context argument. They are applied whenever the query is built.
This makes it cleaner to do certain things, like populating the select columns of a model if none was explicitly added.
The previous way this was done was unreliable since using `q.MustBuild()` would not add the columns while `bob.MustBuild(q)` will add them correctly.

### Changed

Expand All @@ -53,6 +56,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The `Name()` and `NameAs()` methods of Views/Tables no longer need the context argument since the context will be passed when writing the expression. The API then becomes cleaner.
- Preloading mods no longer need to store a context internally. `SetLoadContext()` and `GetLoadContext()` have removed.
- The `ToExpr` field in `orm.RelSide` which was used for preloading is no longer needed and has been removed.
- Moved `orm.Hooks` to `bob.Hooks` since it should not be limited to only ORM queries.
- Moved `mods.QueryModFunc` to `bob.ModFunc` since it should be available to all packages.

### Removed

Expand Down
75 changes: 37 additions & 38 deletions dialect/mysql/dialect/hints.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"strings"

"github.com/stephenafamo/bob"
"github.com/stephenafamo/bob/mods"
)

type hints struct {
Expand All @@ -26,259 +25,259 @@ type hintable interface{ AppendHint(string) }

func QBName[Q hintable](name string) bob.Mod[Q] {
hint := fmt.Sprintf("QB_NAME(%s)", name)
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func SetVar[Q hintable](statement string) bob.Mod[Q] {
hint := fmt.Sprintf("SET_VAR(%s)", statement)
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func MaxExecutionTime[Q hintable](n int) bob.Mod[Q] {
hint := fmt.Sprintf("MAX_EXECUTION_TIME(%d)", n)
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func ResourceGroup[Q hintable](name string) bob.Mod[Q] {
hint := fmt.Sprintf("RESOURCE_GROUP(%s)", name)
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func BKA[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("BKA(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func NoBKA[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("NO_BKA(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func BNL[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("BNL(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func NoBNL[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("NO_BNL(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func DerivedConditionPushdown[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("DERIVED_CONDITION_PUSHDOWN(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func NoDerivedConditionPushdown[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("NO_DERIVED_CONDITION_PUSHDOWN(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func HashJoin[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("HASH_JOIN(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func NoHashJoin[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("NO_HASH_JOIN(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func Merge[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("MERGE(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func NoMerge[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("NO_MERGE(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func Index[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("INDEX(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func NoIndex[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("NO_INDEX(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func GroupIndex[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("GROUP_INDEX(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func NoGroupIndex[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("NO_GROUP_INDEX(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func JoinIndex[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("JOIN_INDEX(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func NoJoinIndex[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("NO_JOIN_INDEX(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func OrderIndex[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("ORDER_INDEX(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func NoOrderIndex[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("NO_ORDER_INDEX(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func IndexMerge[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("INDEX_MERGE(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func NoIndexMerge[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("NO_INDEX_MERGE(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func MRR[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("MRR(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func NoMRR[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("NO_MRR(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func NoICP[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("NO_ICP(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func NoRangeOptimazation[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("NO_RANGE_OPTIMAZATION(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func SkipScan[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("SKIP_SCAN(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func NoSkipScan[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("NO_SKIP_SCAN(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func Semijoin[Q hintable](strategy ...string) bob.Mod[Q] {
hint := fmt.Sprintf("SEMIJOIN(%s)", strings.Join(strategy, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func NoSemijoin[Q hintable](strategy ...string) bob.Mod[Q] {
hint := fmt.Sprintf("NO_SEMIJOIN(%s)", strings.Join(strategy, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func Subquery[Q hintable](strategy string) bob.Mod[Q] {
hint := fmt.Sprintf("SUBQUERY(%s)", strategy)
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func JoinFixedOrder[Q hintable](name string) bob.Mod[Q] {
hint := fmt.Sprintf("JOIN_FIXED_ORDER(%s)", name)
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func JoinOrder[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("JOIN_ORDER(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func JoinPrefix[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("JOIN_PREFIX(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}

func JoinSuffix[Q hintable](tables ...string) bob.Mod[Q] {
hint := fmt.Sprintf("JOIN_SUFFIX(%s)", strings.Join(tables, ", "))
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendHint(hint)
})
}
2 changes: 1 addition & 1 deletion dialect/mysql/dialect/mods.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ func (f FromChain[Q]) ForceIndexForGroupBy(first string, others ...string) FromC
}

func Partition[Q interface{ AppendPartition(...string) }](partitions ...string) bob.Mod[Q] {
return mods.QueryModFunc[Q](func(q Q) {
return bob.ModFunc[Q](func(q Q) {
q.AppendPartition(partitions...)
})
}
Expand Down
5 changes: 5 additions & 0 deletions dialect/mysql/dialect/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type SelectQuery struct {
clause.Offset
clause.For
bob.Load
bob.ContextualModdable[*SelectQuery]
}

func (s *SelectQuery) SetInto(i any) {
Expand All @@ -39,6 +40,10 @@ func (s SelectQuery) WriteSQL(ctx context.Context, w io.Writer, d bob.Dialect, s
var args []any
var err error

if ctx, err = s.RunContextualMods(ctx, &s); err != nil {
return nil, err
}

withArgs, err := bob.ExpressIf(ctx, w, d, start+len(args), s.With,
len(s.With.CTEs) > 0, "\n", "")
if err != nil {
Expand Down
10 changes: 5 additions & 5 deletions dialect/mysql/dm/qm.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,25 @@ func Recursive(r bool) bob.Mod[*dialect.DeleteQuery] {
}

func LowPriority() bob.Mod[*dialect.DeleteQuery] {
return mods.QueryModFunc[*dialect.DeleteQuery](func(i *dialect.DeleteQuery) {
return bob.ModFunc[*dialect.DeleteQuery](func(i *dialect.DeleteQuery) {
i.AppendModifier("LOW_PRIORITY")
})
}

func Quick() bob.Mod[*dialect.DeleteQuery] {
return mods.QueryModFunc[*dialect.DeleteQuery](func(i *dialect.DeleteQuery) {
return bob.ModFunc[*dialect.DeleteQuery](func(i *dialect.DeleteQuery) {
i.AppendModifier("QUICK")
})
}

func Ignore() bob.Mod[*dialect.DeleteQuery] {
return mods.QueryModFunc[*dialect.DeleteQuery](func(i *dialect.DeleteQuery) {
return bob.ModFunc[*dialect.DeleteQuery](func(i *dialect.DeleteQuery) {
i.AppendModifier("IGNORE")
})
}

func From(name any, partitions ...string) bob.Mod[*dialect.DeleteQuery] {
return mods.QueryModFunc[*dialect.DeleteQuery](func(u *dialect.DeleteQuery) {
return bob.ModFunc[*dialect.DeleteQuery](func(u *dialect.DeleteQuery) {
u.Tables = append(u.Tables, clause.Table{
Expression: name,
Partitions: partitions,
Expand All @@ -43,7 +43,7 @@ func From(name any, partitions ...string) bob.Mod[*dialect.DeleteQuery] {
}

func FromAs(name any, alias string, partitions ...string) bob.Mod[*dialect.DeleteQuery] {
return mods.QueryModFunc[*dialect.DeleteQuery](func(u *dialect.DeleteQuery) {
return bob.ModFunc[*dialect.DeleteQuery](func(u *dialect.DeleteQuery) {
u.Tables = append(u.Tables, clause.Table{
Expression: name,
Alias: alias,
Expand Down
Loading
Loading