-
Notifications
You must be signed in to change notification settings - Fork 96
* Added validation of WithTxControl option in non-interactive methods… #1738
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
base: master
Are you sure you want to change the base?
Changes from all commits
253f801
813e20e
0b6e2a3
da31bba
8d2094e
57df8ae
0c1d3ab
6520719
50f36af
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -2,6 +2,7 @@ package query | |||||||||||||||
|
||||||||||||||||
import ( | ||||||||||||||||
"context" | ||||||||||||||||
"errors" | ||||||||||||||||
"time" | ||||||||||||||||
|
||||||||||||||||
"github.com/ydb-platform/ydb-go-genproto/Ydb_Query_V1" | ||||||||||||||||
|
@@ -20,6 +21,7 @@ import ( | |||||||||||||||
"github.com/ydb-platform/ydb-go-sdk/v3/internal/types" | ||||||||||||||||
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext" | ||||||||||||||||
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" | ||||||||||||||||
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xreflect" | ||||||||||||||||
"github.com/ydb-platform/ydb-go-sdk/v3/query" | ||||||||||||||||
"github.com/ydb-platform/ydb-go-sdk/v3/retry" | ||||||||||||||||
"github.com/ydb-platform/ydb-go-sdk/v3/trace" | ||||||||||||||||
|
@@ -32,6 +34,8 @@ var ( | |||||||||||||||
_ sessionPool = (*pool.Pool[*Session, Session])(nil) | ||||||||||||||||
) | ||||||||||||||||
|
||||||||||||||||
var errNoCommit = xerrors.Wrap(errors.New("WithTxControl option is not allowed without CommitTx() option in Client methods, as these methods are non-interactive. You can either add the CommitTx() option to TxControl or use query.*TxControl methods (e.g., query.SnapshotReadOnlyTxControl) which already include the commit flag")) //nolint:lll | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider splitting the error message or adding inline comments to clarify the error rationale for future maintainers.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||||
|
||||||||||||||||
type ( | ||||||||||||||||
sessionPool interface { | ||||||||||||||||
closer.Closer | ||||||||||||||||
|
@@ -173,6 +177,10 @@ func (c *Client) ExecuteScript( | |||||||||||||||
), | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
if err := checkTxControlWithCommit(settings.TxControl()); err != nil { | ||||||||||||||||
return nil, err | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
request, grpcOpts, err := executeQueryScriptRequest(q, settings) | ||||||||||||||||
if err != nil { | ||||||||||||||||
return op, xerrors.WithStackTrace(err) | ||||||||||||||||
|
@@ -320,6 +328,10 @@ func (c *Client) QueryRow(ctx context.Context, q string, opts ...options.Execute | |||||||||||||||
|
||||||||||||||||
settings := options.ExecuteSettings(opts...) | ||||||||||||||||
|
||||||||||||||||
if err := checkTxControlWithCommit(settings.TxControl()); err != nil { | ||||||||||||||||
return nil, err | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
onDone := trace.QueryOnQueryRow(c.config.Trace(), &ctx, | ||||||||||||||||
stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/query.(*Client).QueryRow"), | ||||||||||||||||
q, settings.Label(), | ||||||||||||||||
|
@@ -366,6 +378,11 @@ func (c *Client) Exec(ctx context.Context, q string, opts ...options.Execute) (f | |||||||||||||||
defer cancel() | ||||||||||||||||
|
||||||||||||||||
settings := options.ExecuteSettings(opts...) | ||||||||||||||||
|
||||||||||||||||
if err := checkTxControlWithCommit(settings.TxControl()); err != nil { | ||||||||||||||||
return err | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
onDone := trace.QueryOnExec(c.config.Trace(), &ctx, | ||||||||||||||||
stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/query.(*Client).Exec"), | ||||||||||||||||
q, | ||||||||||||||||
|
@@ -415,6 +432,11 @@ func (c *Client) Query(ctx context.Context, q string, opts ...options.Execute) ( | |||||||||||||||
defer cancel() | ||||||||||||||||
|
||||||||||||||||
settings := options.ExecuteSettings(opts...) | ||||||||||||||||
|
||||||||||||||||
if err := checkTxControlWithCommit(settings.TxControl()); err != nil { | ||||||||||||||||
return nil, err | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
onDone := trace.QueryOnQuery(c.config.Trace(), &ctx, | ||||||||||||||||
stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/query.(*Client).Query"), | ||||||||||||||||
q, settings.Label(), | ||||||||||||||||
|
@@ -470,6 +492,10 @@ func (c *Client) QueryResultSet( | |||||||||||||||
err error | ||||||||||||||||
) | ||||||||||||||||
|
||||||||||||||||
if err := checkTxControlWithCommit(settings.TxControl()); err != nil { | ||||||||||||||||
return nil, err | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
onDone := trace.QueryOnQueryResultSet(c.config.Trace(), &ctx, | ||||||||||||||||
stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/query.(*Client).QueryResultSet"), | ||||||||||||||||
q, settings.Label(), | ||||||||||||||||
|
@@ -612,6 +638,15 @@ func New(ctx context.Context, cc grpc.ClientConnInterface, cfg *config.Config) * | |||||||||||||||
} | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
// checkTxControlWithCommit validates the transaction control object to ensure it includes a commit flag. | ||||||||||||||||
func checkTxControlWithCommit(txControl options.TxControl) error { | ||||||||||||||||
if !xreflect.IsContainsNilPointer(txControl) && !txControl.Commit() { | ||||||||||||||||
return xerrors.WithStackTrace(errNoCommit) | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
return nil | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
func poolTrace(t *trace.Query) *pool.Trace { | ||||||||||||||||
return &pool.Trace{ | ||||||||||||||||
OnNew: func(ctx *context.Context, call stack.Caller) func(limit int) { | ||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package xreflect | ||
|
||
import "reflect" | ||
|
||
func IsContainsNilPointer(v any) bool { | ||
if v == nil { | ||
return true | ||
} | ||
|
||
rVal := reflect.ValueOf(v) | ||
|
||
return isValPointToNil(rVal) | ||
} | ||
|
||
func isValPointToNil(v reflect.Value) bool { | ||
kind := v.Kind() | ||
var res bool | ||
switch kind { | ||
case reflect.Slice: | ||
return false | ||
case reflect.Chan, reflect.Func, reflect.Map, reflect.UnsafePointer: | ||
res = v.IsNil() | ||
case reflect.Pointer, reflect.Interface: | ||
elem := v.Elem() | ||
if v.IsNil() { | ||
return true | ||
} | ||
res = isValPointToNil(elem) | ||
} | ||
|
||
return res | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package xreflect | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
func TestIsContainsNilPointer(t *testing.T) { | ||
var nilIntPointer *int | ||
vInterface := nilIntPointer | ||
|
||
// Test cases for different nil and non-nil scenarios | ||
tests := []struct { | ||
name string | ||
input any | ||
expected bool | ||
}{ | ||
{ | ||
name: "nil interface", | ||
input: nil, | ||
expected: true, | ||
}, | ||
{ | ||
name: "nil pointer to int", | ||
input: (*int)(nil), | ||
expected: true, | ||
}, | ||
{ | ||
name: "non-nil pointer to int", | ||
input: new(int), | ||
expected: false, | ||
}, | ||
{ | ||
name: "nil slice", | ||
input: []int(nil), | ||
expected: false, | ||
}, | ||
{ | ||
name: "empty slice", | ||
input: []int{}, | ||
expected: false, | ||
}, | ||
{ | ||
name: "nil map", | ||
input: map[string]int(nil), | ||
expected: true, | ||
}, | ||
{ | ||
name: "empty map", | ||
input: map[string]int{}, | ||
expected: false, | ||
}, | ||
{ | ||
name: "nil channel", | ||
input: (chan int)(nil), | ||
expected: true, | ||
}, | ||
{ | ||
name: "non-nil channel", | ||
input: make(chan int), | ||
expected: false, | ||
}, | ||
{ | ||
name: "nil function", | ||
input: (func())(nil), | ||
expected: true, | ||
}, | ||
{ | ||
name: "nested nil pointer", | ||
input: &nilIntPointer, | ||
expected: true, | ||
}, | ||
{ | ||
name: "interface with stored nil pointer", | ||
input: vInterface, | ||
expected: true, | ||
}, | ||
{ | ||
name: "non-nil interface value", | ||
input: interface{}("test"), | ||
expected: false, | ||
}, | ||
} | ||
|
||
// Execute all test cases | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if got := IsContainsNilPointer(tt.input); got != tt.expected { | ||
t.Errorf("IsContainsNilPointer() = %v, want %v", got, tt.expected) | ||
} | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,9 +38,9 @@ func TestDatabaseSqlWithTxControl(t *testing.T) { | |
ydb.WithTxControl( | ||
tx.WithTxControlHook(ctx, func(txControl *tx.Control) { | ||
hookCalled = true | ||
require.Equal(t, tx.SerializableReadWriteTxControl(), txControl) | ||
require.Equal(t, tx.SerializableReadWriteTxControl(tx.CommitTx()), txControl) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have added an explicit CommitTx to the test. |
||
}), | ||
tx.SerializableReadWriteTxControl(), | ||
tx.SerializableReadWriteTxControl(tx.CommitTx()), | ||
), | ||
db, func(ctx context.Context, cc *sql.Conn) error { | ||
_, err := db.QueryContext(ctx, "SELECT 1") | ||
|
@@ -56,9 +56,9 @@ func TestDatabaseSqlWithTxControl(t *testing.T) { | |
ydb.WithTxControl( | ||
tx.WithTxControlHook(ctx, func(txControl *tx.Control) { | ||
hookCalled = true | ||
require.Equal(t, tx.SerializableReadWriteTxControl(), txControl) | ||
require.Equal(t, tx.SerializableReadWriteTxControl(tx.CommitTx()), txControl) | ||
}), | ||
tx.SerializableReadWriteTxControl(), | ||
tx.SerializableReadWriteTxControl(tx.CommitTx()), | ||
), | ||
db, func(ctx context.Context, cc *sql.Conn) error { | ||
_, err := db.QueryContext(ctx, "SELECT 1") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,7 +45,7 @@ DECLARE $val AS UUID; | |
SELECT CAST($val AS Utf8)`, | ||
query.WithIdempotent(), | ||
query.WithParameters(ydb.ParamsBuilder().Param("$val").UUIDWithIssue1501Value(id).Build()), | ||
query.WithTxControl(tx.SerializableReadWriteTxControl()), | ||
query.WithTxControl(tx.SnapshotReadOnlyTxControl()), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pay attention to the changes. I have changed txControl in tests |
||
) | ||
|
||
require.NoError(t, err) | ||
|
@@ -71,7 +71,7 @@ DECLARE $val AS Text; | |
SELECT CAST($val AS UUID)`, | ||
query.WithIdempotent(), | ||
query.WithParameters(ydb.ParamsBuilder().Param("$val").Text(idString).Build()), | ||
query.WithTxControl(tx.SerializableReadWriteTxControl()), | ||
query.WithTxControl(tx.SnapshotReadOnlyTxControl()), | ||
) | ||
|
||
require.NoError(t, err) | ||
|
@@ -97,7 +97,7 @@ DECLARE $val AS Text; | |
SELECT CAST($val AS UUID)`, | ||
query.WithIdempotent(), | ||
query.WithParameters(ydb.ParamsBuilder().Param("$val").Text(idString).Build()), | ||
query.WithTxControl(tx.SerializableReadWriteTxControl()), | ||
query.WithTxControl(tx.SnapshotReadOnlyTxControl()), | ||
) | ||
|
||
require.NoError(t, err) | ||
|
@@ -125,7 +125,7 @@ DECLARE $val AS Text; | |
SELECT CAST($val AS UUID)`, | ||
query.WithIdempotent(), | ||
query.WithParameters(ydb.ParamsBuilder().Param("$val").Text(idString).Build()), | ||
query.WithTxControl(tx.SerializableReadWriteTxControl()), | ||
query.WithTxControl(tx.SnapshotReadOnlyTxControl()), | ||
) | ||
|
||
require.NoError(t, err) | ||
|
@@ -151,7 +151,7 @@ DECLARE $val AS Text; | |
SELECT CAST($val AS UUID)`, | ||
query.WithIdempotent(), | ||
query.WithParameters(ydb.ParamsBuilder().Param("$val").Text(idString).Build()), | ||
query.WithTxControl(tx.SerializableReadWriteTxControl()), | ||
query.WithTxControl(tx.SnapshotReadOnlyTxControl()), | ||
) | ||
|
||
require.NoError(t, err) | ||
|
@@ -180,7 +180,7 @@ DECLARE $val AS UUID; | |
SELECT $val`, | ||
query.WithIdempotent(), | ||
query.WithParameters(ydb.ParamsBuilder().Param("$val").UUIDWithIssue1501Value(id).Build()), | ||
query.WithTxControl(tx.SerializableReadWriteTxControl()), | ||
query.WithTxControl(tx.SnapshotReadOnlyTxControl()), | ||
) | ||
|
||
require.NoError(t, err) | ||
|
@@ -207,7 +207,7 @@ DECLARE $val AS UUID; | |
|
||
SELECT CAST($val AS Utf8)`, | ||
query.WithIdempotent(), | ||
query.WithTxControl(query.SerializableReadWriteTxControl()), | ||
query.WithTxControl(query.SnapshotReadOnlyTxControl()), | ||
query.WithParameters(ydb.ParamsBuilder().Param("$val").Uuid(id).Build()), | ||
) | ||
|
||
|
@@ -233,7 +233,7 @@ DECLARE $val AS Utf8; | |
SELECT CAST($val AS UUID)`, | ||
query.WithIdempotent(), | ||
query.WithParameters(ydb.ParamsBuilder().Param("$val").Text(idString).Build()), | ||
query.WithTxControl(query.SerializableReadWriteTxControl()), | ||
query.WithTxControl(query.SnapshotReadOnlyTxControl()), | ||
) | ||
|
||
require.NoError(t, err) | ||
|
@@ -261,7 +261,7 @@ DECLARE $val AS UUID; | |
SELECT $val`, | ||
query.WithIdempotent(), | ||
query.WithParameters(ydb.ParamsBuilder().Param("$val").Uuid(id).Build()), | ||
query.WithTxControl(query.SerializableReadWriteTxControl()), | ||
query.WithTxControl(query.SnapshotReadOnlyTxControl()), | ||
) | ||
|
||
require.NoError(t, err) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pay attention to the line. I have changed the txcontrol in the example.