[MySQL] Optimizer hints
This commit is contained in:
parent
c9967d151e
commit
f772f90336
12 changed files with 207 additions and 27 deletions
|
|
@ -16,11 +16,35 @@ type ClauseWithProjections interface {
|
||||||
Projections() ProjectionList
|
Projections() ProjectionList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OptimizerHint provides a way to optimize query execution per-statement basis
|
||||||
|
type OptimizerHint string
|
||||||
|
|
||||||
|
type optimizerHints []OptimizerHint
|
||||||
|
|
||||||
|
func (o optimizerHints) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
|
||||||
|
if len(o) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
out.WriteString("/*+")
|
||||||
|
for i, hint := range o {
|
||||||
|
if i > 0 {
|
||||||
|
out.WriteByte(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
out.WriteString(string(hint))
|
||||||
|
}
|
||||||
|
out.WriteString("*/")
|
||||||
|
}
|
||||||
|
|
||||||
// ClauseSelect struct
|
// ClauseSelect struct
|
||||||
type ClauseSelect struct {
|
type ClauseSelect struct {
|
||||||
Distinct bool
|
Distinct bool
|
||||||
DistinctOnColumns []ColumnExpression
|
DistinctOnColumns []ColumnExpression
|
||||||
ProjectionList []Projection
|
ProjectionList []Projection
|
||||||
|
|
||||||
|
// MySQL only
|
||||||
|
OptimizerHints optimizerHints
|
||||||
}
|
}
|
||||||
|
|
||||||
// Projections returns list of projections for select clause
|
// Projections returns list of projections for select clause
|
||||||
|
|
@ -32,6 +56,7 @@ func (s *ClauseSelect) Projections() ProjectionList {
|
||||||
func (s *ClauseSelect) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
|
func (s *ClauseSelect) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
|
||||||
out.NewLine()
|
out.NewLine()
|
||||||
out.WriteString("SELECT")
|
out.WriteString("SELECT")
|
||||||
|
s.OptimizerHints.Serialize(statementType, out, options...)
|
||||||
|
|
||||||
if s.Distinct {
|
if s.Distinct {
|
||||||
out.WriteString("DISTINCT")
|
out.WriteString("DISTINCT")
|
||||||
|
|
@ -286,12 +311,16 @@ func (s *ClauseSetStmtOperator) Serialize(statementType StatementType, out *SQLB
|
||||||
// ClauseUpdate struct
|
// ClauseUpdate struct
|
||||||
type ClauseUpdate struct {
|
type ClauseUpdate struct {
|
||||||
Table SerializerTable
|
Table SerializerTable
|
||||||
|
|
||||||
|
// MySQL only
|
||||||
|
OptimizerHints optimizerHints
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize serializes clause into SQLBuilder
|
// Serialize serializes clause into SQLBuilder
|
||||||
func (u *ClauseUpdate) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
|
func (u *ClauseUpdate) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
|
||||||
out.NewLine()
|
out.NewLine()
|
||||||
out.WriteString("UPDATE")
|
out.WriteString("UPDATE")
|
||||||
|
u.OptimizerHints.Serialize(statementType, out, options...)
|
||||||
|
|
||||||
if utils.IsNil(u.Table) {
|
if utils.IsNil(u.Table) {
|
||||||
panic("jet: table to update is nil")
|
panic("jet: table to update is nil")
|
||||||
|
|
@ -342,6 +371,9 @@ func (s *SetClause) Serialize(statementType StatementType, out *SQLBuilder, opti
|
||||||
type ClauseInsert struct {
|
type ClauseInsert struct {
|
||||||
Table SerializerTable
|
Table SerializerTable
|
||||||
Columns []Column
|
Columns []Column
|
||||||
|
|
||||||
|
// MySQL only
|
||||||
|
OptimizerHints optimizerHints
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetColumns gets list of columns for insert
|
// GetColumns gets list of columns for insert
|
||||||
|
|
@ -355,13 +387,15 @@ func (i *ClauseInsert) GetColumns() []Column {
|
||||||
|
|
||||||
// Serialize serializes clause into SQLBuilder
|
// Serialize serializes clause into SQLBuilder
|
||||||
func (i *ClauseInsert) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
|
func (i *ClauseInsert) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
|
||||||
out.NewLine()
|
|
||||||
out.WriteString("INSERT INTO")
|
|
||||||
|
|
||||||
if utils.IsNil(i.Table) {
|
if utils.IsNil(i.Table) {
|
||||||
panic("jet: table is nil for INSERT clause")
|
panic("jet: table is nil for INSERT clause")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out.NewLine()
|
||||||
|
out.WriteString("INSERT")
|
||||||
|
i.OptimizerHints.Serialize(statementType, out, options...)
|
||||||
|
out.WriteString("INTO")
|
||||||
|
|
||||||
i.Table.serialize(statementType, out)
|
i.Table.serialize(statementType, out)
|
||||||
|
|
||||||
if len(i.Columns) > 0 {
|
if len(i.Columns) > 0 {
|
||||||
|
|
@ -449,17 +483,17 @@ func (v *ClauseQuery) Serialize(statementType StatementType, out *SQLBuilder, op
|
||||||
// ClauseDelete struct
|
// ClauseDelete struct
|
||||||
type ClauseDelete struct {
|
type ClauseDelete struct {
|
||||||
Table SerializerTable
|
Table SerializerTable
|
||||||
|
|
||||||
|
// MySQL only
|
||||||
|
OptimizerHints optimizerHints
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize serializes clause into SQLBuilder
|
// Serialize serializes clause into SQLBuilder
|
||||||
func (d *ClauseDelete) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
|
func (d *ClauseDelete) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
|
||||||
out.NewLine()
|
out.NewLine()
|
||||||
out.WriteString("DELETE FROM")
|
out.WriteString("DELETE")
|
||||||
|
d.OptimizerHints.Serialize(statementType, out, options...)
|
||||||
if d.Table == nil {
|
out.WriteString("FROM")
|
||||||
panic("jet: nil table in DELETE clause")
|
|
||||||
}
|
|
||||||
|
|
||||||
d.Table.serialize(statementType, out, FallTrough(options)...)
|
d.Table.serialize(statementType, out, FallTrough(options)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import "github.com/go-jet/jet/v2/internal/jet"
|
||||||
type DeleteStatement interface {
|
type DeleteStatement interface {
|
||||||
Statement
|
Statement
|
||||||
|
|
||||||
|
OPTIMIZER_HINTS(hints ...OptimizerHint) DeleteStatement
|
||||||
|
|
||||||
USING(tables ...ReadableTable) DeleteStatement
|
USING(tables ...ReadableTable) DeleteStatement
|
||||||
WHERE(expression BoolExpression) DeleteStatement
|
WHERE(expression BoolExpression) DeleteStatement
|
||||||
ORDER_BY(orderByClauses ...OrderByClause) DeleteStatement
|
ORDER_BY(orderByClauses ...OrderByClause) DeleteStatement
|
||||||
|
|
@ -15,7 +17,7 @@ type DeleteStatement interface {
|
||||||
type deleteStatementImpl struct {
|
type deleteStatementImpl struct {
|
||||||
jet.SerializerStatement
|
jet.SerializerStatement
|
||||||
|
|
||||||
Delete jet.ClauseStatementBegin
|
Delete jet.ClauseDelete
|
||||||
Using jet.ClauseFrom
|
Using jet.ClauseFrom
|
||||||
Where jet.ClauseWhere
|
Where jet.ClauseWhere
|
||||||
OrderBy jet.ClauseOrderBy
|
OrderBy jet.ClauseOrderBy
|
||||||
|
|
@ -29,17 +31,22 @@ func newDeleteStatement(table Table) DeleteStatement {
|
||||||
&newDelete.Using,
|
&newDelete.Using,
|
||||||
&newDelete.Where,
|
&newDelete.Where,
|
||||||
&newDelete.OrderBy,
|
&newDelete.OrderBy,
|
||||||
&newDelete.Limit)
|
&newDelete.Limit,
|
||||||
|
)
|
||||||
|
|
||||||
newDelete.Delete.Name = "DELETE FROM"
|
newDelete.Delete.Table = table
|
||||||
newDelete.Using.Name = "USING"
|
newDelete.Using.Name = "USING"
|
||||||
newDelete.Delete.Tables = append(newDelete.Delete.Tables, table)
|
|
||||||
newDelete.Where.Mandatory = true
|
newDelete.Where.Mandatory = true
|
||||||
newDelete.Limit.Count = -1
|
newDelete.Limit.Count = -1
|
||||||
|
|
||||||
return newDelete
|
return newDelete
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *deleteStatementImpl) OPTIMIZER_HINTS(hints ...OptimizerHint) DeleteStatement {
|
||||||
|
d.Delete.OptimizerHints = hints
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
func (d *deleteStatementImpl) USING(tables ...ReadableTable) DeleteStatement {
|
func (d *deleteStatementImpl) USING(tables ...ReadableTable) DeleteStatement {
|
||||||
d.Using.Tables = readableTablesToSerializerList(tables)
|
d.Using.Tables = readableTablesToSerializerList(tables)
|
||||||
return d
|
return d
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import "github.com/go-jet/jet/v2/internal/jet"
|
||||||
type InsertStatement interface {
|
type InsertStatement interface {
|
||||||
Statement
|
Statement
|
||||||
|
|
||||||
|
OPTIMIZER_HINTS(hints ...OptimizerHint) InsertStatement
|
||||||
|
|
||||||
// Insert row of values
|
// Insert row of values
|
||||||
VALUES(value interface{}, values ...interface{}) InsertStatement
|
VALUES(value interface{}, values ...interface{}) InsertStatement
|
||||||
// Insert row of values, where value for each column is extracted from filed of structure data.
|
// Insert row of values, where value for each column is extracted from filed of structure data.
|
||||||
|
|
@ -22,7 +24,10 @@ type InsertStatement interface {
|
||||||
func newInsertStatement(table Table, columns []jet.Column) InsertStatement {
|
func newInsertStatement(table Table, columns []jet.Column) InsertStatement {
|
||||||
newInsert := &insertStatementImpl{}
|
newInsert := &insertStatementImpl{}
|
||||||
newInsert.SerializerStatement = jet.NewStatementImpl(Dialect, jet.InsertStatementType, newInsert,
|
newInsert.SerializerStatement = jet.NewStatementImpl(Dialect, jet.InsertStatementType, newInsert,
|
||||||
&newInsert.Insert, &newInsert.ValuesQuery, &newInsert.OnDuplicateKey)
|
&newInsert.Insert,
|
||||||
|
&newInsert.ValuesQuery,
|
||||||
|
&newInsert.OnDuplicateKey,
|
||||||
|
)
|
||||||
|
|
||||||
newInsert.Insert.Table = table
|
newInsert.Insert.Table = table
|
||||||
newInsert.Insert.Columns = columns
|
newInsert.Insert.Columns = columns
|
||||||
|
|
@ -38,6 +43,11 @@ type insertStatementImpl struct {
|
||||||
OnDuplicateKey onDuplicateKeyUpdateClause
|
OnDuplicateKey onDuplicateKeyUpdateClause
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (is *insertStatementImpl) OPTIMIZER_HINTS(hints ...OptimizerHint) InsertStatement {
|
||||||
|
is.Insert.OptimizerHints = hints
|
||||||
|
return is
|
||||||
|
}
|
||||||
|
|
||||||
func (is *insertStatementImpl) VALUES(value interface{}, values ...interface{}) InsertStatement {
|
func (is *insertStatementImpl) VALUES(value interface{}, values ...interface{}) InsertStatement {
|
||||||
is.ValuesQuery.Rows = append(is.ValuesQuery.Rows, jet.UnwindRowFromValues(value, values))
|
is.ValuesQuery.Rows = append(is.ValuesQuery.Rows, jet.UnwindRowFromValues(value, values))
|
||||||
return is
|
return is
|
||||||
|
|
|
||||||
19
mysql/optimizer_hints.go
Normal file
19
mysql/optimizer_hints.go
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/go-jet/jet/v2/internal/jet"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OptimizerHint provides a way to optimize query execution per-statement basis
|
||||||
|
type OptimizerHint = jet.OptimizerHint
|
||||||
|
|
||||||
|
// MAX_EXECUTION_TIME limits statement execution time
|
||||||
|
func MAX_EXECUTION_TIME(miliseconds int) OptimizerHint {
|
||||||
|
return OptimizerHint(fmt.Sprintf("MAX_EXECUTION_TIME(%d)", miliseconds))
|
||||||
|
}
|
||||||
|
|
||||||
|
// QB_NAME assigns name to query block
|
||||||
|
func QB_NAME(name string) OptimizerHint {
|
||||||
|
return OptimizerHint(fmt.Sprintf("QB_NAME(%s)", name))
|
||||||
|
}
|
||||||
|
|
@ -40,6 +40,8 @@ type SelectStatement interface {
|
||||||
jet.HasProjections
|
jet.HasProjections
|
||||||
Expression
|
Expression
|
||||||
|
|
||||||
|
OPTIMIZER_HINTS(hints ...OptimizerHint) SelectStatement
|
||||||
|
|
||||||
DISTINCT() SelectStatement
|
DISTINCT() SelectStatement
|
||||||
FROM(tables ...ReadableTable) SelectStatement
|
FROM(tables ...ReadableTable) SelectStatement
|
||||||
WHERE(expression BoolExpression) SelectStatement
|
WHERE(expression BoolExpression) SelectStatement
|
||||||
|
|
@ -65,9 +67,19 @@ func SELECT(projection Projection, projections ...Projection) SelectStatement {
|
||||||
|
|
||||||
func newSelectStatement(table ReadableTable, projections []Projection) SelectStatement {
|
func newSelectStatement(table ReadableTable, projections []Projection) SelectStatement {
|
||||||
newSelect := &selectStatementImpl{}
|
newSelect := &selectStatementImpl{}
|
||||||
newSelect.ExpressionStatement = jet.NewExpressionStatementImpl(Dialect, jet.SelectStatementType, newSelect, &newSelect.Select,
|
newSelect.ExpressionStatement = jet.NewExpressionStatementImpl(Dialect, jet.SelectStatementType, newSelect,
|
||||||
&newSelect.From, &newSelect.Where, &newSelect.GroupBy, &newSelect.Having, &newSelect.Window, &newSelect.OrderBy,
|
&newSelect.Select,
|
||||||
&newSelect.Limit, &newSelect.Offset, &newSelect.For, &newSelect.ShareLock)
|
&newSelect.From,
|
||||||
|
&newSelect.Where,
|
||||||
|
&newSelect.GroupBy,
|
||||||
|
&newSelect.Having,
|
||||||
|
&newSelect.Window,
|
||||||
|
&newSelect.OrderBy,
|
||||||
|
&newSelect.Limit,
|
||||||
|
&newSelect.Offset,
|
||||||
|
&newSelect.For,
|
||||||
|
&newSelect.ShareLock,
|
||||||
|
)
|
||||||
|
|
||||||
newSelect.Select.ProjectionList = projections
|
newSelect.Select.ProjectionList = projections
|
||||||
if table != nil {
|
if table != nil {
|
||||||
|
|
@ -100,6 +112,11 @@ type selectStatementImpl struct {
|
||||||
ShareLock jet.ClauseOptional
|
ShareLock jet.ClauseOptional
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *selectStatementImpl) OPTIMIZER_HINTS(hints ...OptimizerHint) SelectStatement {
|
||||||
|
s.Select.OptimizerHints = hints
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
func (s *selectStatementImpl) DISTINCT() SelectStatement {
|
func (s *selectStatementImpl) DISTINCT() SelectStatement {
|
||||||
s.Select.Distinct = true
|
s.Select.Distinct = true
|
||||||
return s
|
return s
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import "github.com/go-jet/jet/v2/internal/jet"
|
||||||
type UpdateStatement interface {
|
type UpdateStatement interface {
|
||||||
jet.Statement
|
jet.Statement
|
||||||
|
|
||||||
|
OPTIMIZER_HINTS(hints ...OptimizerHint) UpdateStatement
|
||||||
|
|
||||||
SET(value interface{}, values ...interface{}) UpdateStatement
|
SET(value interface{}, values ...interface{}) UpdateStatement
|
||||||
MODEL(data interface{}) UpdateStatement
|
MODEL(data interface{}) UpdateStatement
|
||||||
|
|
||||||
|
|
@ -36,6 +38,11 @@ func newUpdateStatement(table Table, columns []jet.Column) UpdateStatement {
|
||||||
return update
|
return update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *updateStatementImpl) OPTIMIZER_HINTS(hints ...OptimizerHint) UpdateStatement {
|
||||||
|
u.Update.OptimizerHints = hints
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
func (u *updateStatementImpl) SET(value interface{}, values ...interface{}) UpdateStatement {
|
func (u *updateStatementImpl) SET(value interface{}, values ...interface{}) UpdateStatement {
|
||||||
columnAssigment, isColumnAssigment := value.(ColumnAssigment)
|
columnAssigment, isColumnAssigment := value.(ColumnAssigment)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ type DeleteStatement interface {
|
||||||
type deleteStatementImpl struct {
|
type deleteStatementImpl struct {
|
||||||
jet.SerializerStatement
|
jet.SerializerStatement
|
||||||
|
|
||||||
Delete jet.ClauseStatementBegin
|
Delete jet.ClauseDelete
|
||||||
Using jet.ClauseFrom
|
Using jet.ClauseFrom
|
||||||
Where jet.ClauseWhere
|
Where jet.ClauseWhere
|
||||||
Returning jet.ClauseReturning
|
Returning jet.ClauseReturning
|
||||||
|
|
@ -28,8 +28,7 @@ func newDeleteStatement(table WritableTable) DeleteStatement {
|
||||||
&newDelete.Where,
|
&newDelete.Where,
|
||||||
&newDelete.Returning)
|
&newDelete.Returning)
|
||||||
|
|
||||||
newDelete.Delete.Name = "DELETE FROM"
|
newDelete.Delete.Table = table
|
||||||
newDelete.Delete.Tables = append(newDelete.Delete.Tables, table)
|
|
||||||
newDelete.Using.Name = "USING"
|
newDelete.Using.Name = "USING"
|
||||||
newDelete.Where.Mandatory = true
|
newDelete.Where.Mandatory = true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ type DeleteStatement interface {
|
||||||
type deleteStatementImpl struct {
|
type deleteStatementImpl struct {
|
||||||
jet.SerializerStatement
|
jet.SerializerStatement
|
||||||
|
|
||||||
Delete jet.ClauseStatementBegin
|
Delete jet.ClauseDelete
|
||||||
Where jet.ClauseWhere
|
Where jet.ClauseWhere
|
||||||
OrderBy jet.ClauseOrderBy
|
OrderBy jet.ClauseOrderBy
|
||||||
Limit jet.ClauseLimit
|
Limit jet.ClauseLimit
|
||||||
|
|
@ -32,8 +32,7 @@ func newDeleteStatement(table Table) DeleteStatement {
|
||||||
&newDelete.Returning,
|
&newDelete.Returning,
|
||||||
)
|
)
|
||||||
|
|
||||||
newDelete.Delete.Name = "DELETE FROM"
|
newDelete.Delete.Table = table
|
||||||
newDelete.Delete.Tables = append(newDelete.Delete.Tables, table)
|
|
||||||
newDelete.Where.Mandatory = true
|
newDelete.Where.Mandatory = true
|
||||||
newDelete.Limit.Count = -1
|
newDelete.Limit.Count = -1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package mysql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"github.com/go-jet/jet/v2/internal/testutils"
|
"github.com/go-jet/jet/v2/internal/testutils"
|
||||||
. "github.com/go-jet/jet/v2/mysql"
|
. "github.com/go-jet/jet/v2/mysql"
|
||||||
"github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/table"
|
"github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/table"
|
||||||
|
|
@ -98,3 +99,22 @@ WHERE (staff.staff_id != ?) AND (rental.rental_id < ?);
|
||||||
|
|
||||||
testutils.AssertExecAndRollback(t, stmt, db)
|
testutils.AssertExecAndRollback(t, stmt, db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeleteOptimizerHints(t *testing.T) {
|
||||||
|
|
||||||
|
stmt := Link.DELETE().
|
||||||
|
OPTIMIZER_HINTS(QB_NAME("deleteIns"), "MRR(link)").
|
||||||
|
WHERE(
|
||||||
|
Link.Name.IN(String("Gmail"), String("Outlook")),
|
||||||
|
)
|
||||||
|
|
||||||
|
testutils.AssertDebugStatementSql(t, stmt, `
|
||||||
|
DELETE /*+ QB_NAME(deleteIns) MRR(link) */ FROM test_sample.link
|
||||||
|
WHERE link.name IN ('Gmail', 'Outlook');
|
||||||
|
`)
|
||||||
|
|
||||||
|
testutils.ExecuteInTxAndRollback(t, db, func(tx *sql.Tx) {
|
||||||
|
_, err := stmt.Exec(tx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -370,3 +370,23 @@ func TestInsertWithExecContext(t *testing.T) {
|
||||||
|
|
||||||
require.Error(t, err, "context deadline exceeded")
|
require.Error(t, err, "context deadline exceeded")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInsertOptimizerHints(t *testing.T) {
|
||||||
|
|
||||||
|
stmt := Link.INSERT(Link.MutableColumns).
|
||||||
|
OPTIMIZER_HINTS(QB_NAME("qbIns"), "NO_ICP(link)").
|
||||||
|
MODEL(model.Link{
|
||||||
|
URL: "http://www.google.com",
|
||||||
|
Name: "Google",
|
||||||
|
})
|
||||||
|
|
||||||
|
testutils.AssertDebugStatementSql(t, stmt, `
|
||||||
|
INSERT /*+ QB_NAME(qbIns) NO_ICP(link) */ INTO test_sample.link (url, name, description)
|
||||||
|
VALUES ('http://www.google.com', 'Google', NULL);
|
||||||
|
`)
|
||||||
|
|
||||||
|
testutils.ExecuteInTxAndRollback(t, db, func(tx *sql.Tx) {
|
||||||
|
_, err := stmt.Exec(tx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1189,3 +1189,25 @@ ORDER BY film.film_id;
|
||||||
]
|
]
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSelectOptimizerHints(t *testing.T) {
|
||||||
|
|
||||||
|
stmt := SELECT(Actor.AllColumns).
|
||||||
|
OPTIMIZER_HINTS(MAX_EXECUTION_TIME(1), QB_NAME("mainQueryBlock"), "NO_ICP(actor)").
|
||||||
|
DISTINCT().
|
||||||
|
FROM(Actor)
|
||||||
|
|
||||||
|
testutils.AssertDebugStatementSql(t, stmt, `
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(1) QB_NAME(mainQueryBlock) NO_ICP(actor) */ DISTINCT actor.actor_id AS "actor.actor_id",
|
||||||
|
actor.first_name AS "actor.first_name",
|
||||||
|
actor.last_name AS "actor.last_name",
|
||||||
|
actor.last_update AS "actor.last_update"
|
||||||
|
FROM dvds.actor;
|
||||||
|
`)
|
||||||
|
|
||||||
|
var actors []model.Actor
|
||||||
|
|
||||||
|
err := stmt.QueryContext(context.Background(), db, &actors)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, actors, 200)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"github.com/go-jet/jet/v2/internal/testutils"
|
"github.com/go-jet/jet/v2/internal/testutils"
|
||||||
. "github.com/go-jet/jet/v2/mysql"
|
. "github.com/go-jet/jet/v2/mysql"
|
||||||
"github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/table"
|
. "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/table"
|
||||||
"github.com/go-jet/jet/v2/tests/.gentestdata/mysql/test_sample/model"
|
"github.com/go-jet/jet/v2/tests/.gentestdata/mysql/test_sample/model"
|
||||||
. "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/test_sample/table"
|
. "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/test_sample/table"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
@ -260,10 +260,10 @@ func TestUpdateExecContext(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateWithJoin(t *testing.T) {
|
func TestUpdateWithJoin(t *testing.T) {
|
||||||
statement := table.Staff.INNER_JOIN(table.Address, table.Address.AddressID.EQ(table.Staff.AddressID)).
|
statement := Staff.INNER_JOIN(Address, Address.AddressID.EQ(Staff.AddressID)).
|
||||||
UPDATE(table.Staff.LastName).
|
UPDATE(Staff.LastName).
|
||||||
SET(String("New staff name")).
|
SET(String("New staff name")).
|
||||||
WHERE(table.Staff.StaffID.EQ(Int(1)))
|
WHERE(Staff.StaffID.EQ(Int(1)))
|
||||||
|
|
||||||
testutils.AssertStatementSql(t, statement, `
|
testutils.AssertStatementSql(t, statement, `
|
||||||
UPDATE dvds.staff
|
UPDATE dvds.staff
|
||||||
|
|
@ -274,3 +274,29 @@ WHERE staff.staff_id = ?;
|
||||||
|
|
||||||
testutils.AssertExecAndRollback(t, statement, db)
|
testutils.AssertExecAndRollback(t, statement, db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpdateOptimizerHints(t *testing.T) {
|
||||||
|
|
||||||
|
stmt := Link.UPDATE(Link.AllColumns).
|
||||||
|
OPTIMIZER_HINTS(QB_NAME("qbInsert"), "MRR(link)").
|
||||||
|
MODEL(model.Link{
|
||||||
|
ID: 501,
|
||||||
|
URL: "http://www.duckduckgo.com",
|
||||||
|
Name: "DuckDuckGo",
|
||||||
|
}).
|
||||||
|
WHERE(Link.Name.EQ(String("Bing")))
|
||||||
|
|
||||||
|
testutils.AssertDebugStatementSql(t, stmt, `
|
||||||
|
UPDATE /*+ QB_NAME(qbInsert) MRR(link) */ test_sample.link
|
||||||
|
SET id = 501,
|
||||||
|
url = 'http://www.duckduckgo.com',
|
||||||
|
name = 'DuckDuckGo',
|
||||||
|
description = NULL
|
||||||
|
WHERE link.name = 'Bing';
|
||||||
|
`)
|
||||||
|
|
||||||
|
testutils.ExecuteInTxAndRollback(t, db, func(tx *sql.Tx) {
|
||||||
|
_, err := stmt.Exec(tx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue