Skip to content

Commit 9d70df4

Browse files
committed
CASSGO-92: add StatementMetadata, NewLogField
StatementMetadata can be called on a session to get the bind, result, and pk information for a given query. Previously this wasn't publicly exposed but is necessary for some implementations of HostSelectionPolicy like token-aware. This might also be useful for CI tooling or runtime analysis of queries and the types of columns. NewLogField is a simple method to return a LogField with name and a value. This method has the downside of having interface boxing but is simpler than making a method per type. We could always expose a method per type later but this felt simpler for now. Finally, session init was cleaned up to prevent a HostSelectionPolicy from causing a panic if it tried to make a query during init. Patch by James Hartig for CASSGO-92
1 parent f3e2b39 commit 9d70df4

File tree

6 files changed

+263
-225
lines changed

6 files changed

+263
-225
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## Unreleased
9+
10+
### Added
11+
12+
- Session.StatementMetadata (CASSGO-92)
13+
- NewLogField (CASSGO-92)
14+
15+
### Fixed
16+
17+
- Prevent panic with queries during session init (CASSGO-92)
18+
819
## [2.0.0]
920

1021
### Removed

cassandra_test.go

Lines changed: 72 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2794,66 +2794,62 @@ func TestKeyspaceMetadata(t *testing.T) {
27942794
}
27952795

27962796
// Integration test of the routing key calculation
2797-
func TestRoutingKey(t *testing.T) {
2797+
func TestRoutingStatementMetadata(t *testing.T) {
27982798
session := createSession(t)
27992799
defer session.Close()
28002800

2801-
if err := createTable(session, "CREATE TABLE gocql_test.test_single_routing_key (first_id int, second_id int, PRIMARY KEY (first_id, second_id))"); err != nil {
2801+
if err := createTable(session, "CREATE TABLE gocql_test.test_single_routing_key (first_id int, second_id varchar, PRIMARY KEY (first_id, second_id))"); err != nil {
28022802
t.Fatalf("failed to create table with error '%v'", err)
28032803
}
2804-
if err := createTable(session, "CREATE TABLE gocql_test.test_composite_routing_key (first_id int, second_id int, PRIMARY KEY ((first_id, second_id)))"); err != nil {
2804+
if err := createTable(session, "CREATE TABLE gocql_test.test_composite_routing_key (first_id int, second_id varchar, PRIMARY KEY ((first_id, second_id)))"); err != nil {
28052805
t.Fatalf("failed to create table with error '%v'", err)
28062806
}
28072807

2808-
routingKeyInfo, err := session.routingKeyInfo(context.Background(), "SELECT * FROM test_single_routing_key WHERE second_id=? AND first_id=?", "")
2808+
meta, err := session.routingStatementMetadata(context.Background(), "SELECT * FROM test_single_routing_key WHERE second_id=? AND first_id=?", "")
28092809
if err != nil {
2810-
t.Fatalf("failed to get routing key info due to error: %v", err)
2810+
t.Fatalf("failed to get routing statement metadata due to error: %v", err)
28112811
}
2812-
if routingKeyInfo == nil {
2813-
t.Fatal("Expected routing key info, but was nil")
2812+
if meta == nil {
2813+
t.Fatal("Expected routing statement metadata, but was nil")
28142814
}
2815-
if len(routingKeyInfo.indexes) != 1 {
2816-
t.Fatalf("Expected routing key indexes length to be 1 but was %d", len(routingKeyInfo.indexes))
2815+
if len(meta.PKBindColumnIndexes) != 1 {
2816+
t.Fatalf("Expected routing statement metadata PKBindColumnIndexes length to be 1 but was %d", len(meta.PKBindColumnIndexes))
28172817
}
2818-
if routingKeyInfo.indexes[0] != 1 {
2819-
t.Errorf("Expected routing key index[0] to be 1 but was %d", routingKeyInfo.indexes[0])
2818+
if meta.PKBindColumnIndexes[0] != 1 {
2819+
t.Errorf("Expected routing statement metadata PKBindColumnIndexes[0] to be 1 but was %d", meta.PKBindColumnIndexes[0])
28202820
}
2821-
if len(routingKeyInfo.types) != 1 {
2822-
t.Fatalf("Expected routing key types length to be 1 but was %d", len(routingKeyInfo.types))
2821+
if len(meta.BindColumns) != 2 {
2822+
t.Fatalf("Expected routing statement metadata BindColumns length to be 2 but was %d", len(meta.BindColumns))
28232823
}
2824-
if routingKeyInfo.types[0] == nil {
2825-
t.Fatal("Expected routing key types[0] to be non-nil")
2824+
if meta.BindColumns[0].TypeInfo.Type() != TypeVarchar {
2825+
t.Fatalf("Expected routing statement metadata BindColumns[0].TypeInfo.Type to be %v but was %v", TypeVarchar, meta.BindColumns[0].TypeInfo.Type())
28262826
}
2827-
if routingKeyInfo.types[0].Type() != TypeInt {
2828-
t.Fatalf("Expected routing key types[0].Type to be %v but was %v", TypeInt, routingKeyInfo.types[0].Type())
2827+
if meta.BindColumns[1].TypeInfo.Type() != TypeInt {
2828+
t.Fatalf("Expected routing statement metadata BindColumns[1].TypeInfo.Type to be %v but was %v", TypeInt, meta.BindColumns[1].TypeInfo.Type())
28292829
}
2830-
2831-
// verify the cache is working
2832-
routingKeyInfo, err = session.routingKeyInfo(context.Background(), "SELECT * FROM test_single_routing_key WHERE second_id=? AND first_id=?", "")
2833-
if err != nil {
2834-
t.Fatalf("failed to get routing key info due to error: %v", err)
2830+
if len(meta.ResultColumns) != 2 {
2831+
t.Fatalf("Expected routing statement metadata ResultColumns length to be 2 but was %d", len(meta.ResultColumns))
28352832
}
2836-
if len(routingKeyInfo.indexes) != 1 {
2837-
t.Fatalf("Expected routing key indexes length to be 1 but was %d", len(routingKeyInfo.indexes))
2833+
if meta.ResultColumns[0].Name != "first_id" {
2834+
t.Fatalf("Expected routing statement metadata ResultColumns[0].Name to be %v but was %v", "first_id", meta.ResultColumns[0].Name)
28382835
}
2839-
if routingKeyInfo.indexes[0] != 1 {
2840-
t.Errorf("Expected routing key index[0] to be 1 but was %d", routingKeyInfo.indexes[0])
2836+
if meta.ResultColumns[0].TypeInfo.Type() != TypeInt {
2837+
t.Fatalf("Expected routing statement metadata ResultColumns[0].TypeInfo.Type to be %v but was %v", TypeInt, meta.ResultColumns[0].TypeInfo.Type())
28412838
}
2842-
if len(routingKeyInfo.types) != 1 {
2843-
t.Fatalf("Expected routing key types length to be 1 but was %d", len(routingKeyInfo.types))
2839+
if meta.ResultColumns[1].Name != "second_id" {
2840+
t.Fatalf("Expected routing statement metadata ResultColumns[1].Name to be %v but was %v", "second_id", meta.ResultColumns[1].Name)
28442841
}
2845-
if routingKeyInfo.types[0] == nil {
2846-
t.Fatal("Expected routing key types[0] to be non-nil")
2842+
if meta.ResultColumns[1].TypeInfo.Type() != TypeVarchar {
2843+
t.Fatalf("Expected routing statement metadata ResultColumns[1].TypeInfo.Type to be %v but was %v", TypeVarchar, meta.ResultColumns[1].TypeInfo.Type())
28472844
}
2848-
if routingKeyInfo.types[0].Type() != TypeInt {
2849-
t.Fatalf("Expected routing key types[0] to be %v but was %v", TypeInt, routingKeyInfo.types[0].Type())
2850-
}
2851-
cacheSize := session.routingKeyInfoCache.lru.Len()
2845+
2846+
// verify the cache is working
2847+
cacheSize := session.routingMetadataCache.lru.Len()
28522848
if cacheSize != 1 {
28532849
t.Errorf("Expected cache size to be 1 but was %d", cacheSize)
28542850
}
28552851

2856-
query := newInternalQuery(session.Query("SELECT * FROM test_single_routing_key WHERE second_id=? AND first_id=?", 1, 2), nil)
2852+
query := newInternalQuery(session.Query("SELECT * FROM test_single_routing_key WHERE second_id=? AND first_id=?", "1", 2), nil)
28572853
routingKey, err := query.GetRoutingKey()
28582854
if err != nil {
28592855
t.Fatalf("Failed to get routing key due to error: %v", err)
@@ -2863,50 +2859,59 @@ func TestRoutingKey(t *testing.T) {
28632859
t.Errorf("Expected routing key %v but was %v", expectedRoutingKey, routingKey)
28642860
}
28652861

2866-
routingKeyInfo, err = session.routingKeyInfo(context.Background(), "SELECT * FROM test_composite_routing_key WHERE second_id=? AND first_id=?", "")
2862+
meta, err = session.routingStatementMetadata(context.Background(), "SELECT * FROM test_composite_routing_key WHERE second_id=? AND first_id=?", "")
28672863
if err != nil {
2868-
t.Fatalf("failed to get routing key info due to error: %v", err)
2864+
t.Fatalf("failed to get routing statement metadata due to error: %v", err)
2865+
}
2866+
if meta == nil {
2867+
t.Fatal("Expected routing statement metadata, but was nil")
2868+
}
2869+
if len(meta.PKBindColumnIndexes) != 2 {
2870+
t.Fatalf("Expected routing statement metadata PKBindColumnIndexes length to be 2 but was %d", len(meta.PKBindColumnIndexes))
2871+
}
2872+
if meta.PKBindColumnIndexes[0] != 1 {
2873+
t.Errorf("Expected routing statement metadata PKBindColumnIndexes[0] to be 1 but was %d", meta.PKBindColumnIndexes[0])
28692874
}
2870-
if routingKeyInfo == nil {
2871-
t.Fatal("Expected routing key info, but was nil")
2875+
if meta.PKBindColumnIndexes[1] != 0 {
2876+
t.Errorf("Expected routing statement metadata PKBindColumnIndexes[1] to be 0 but was %d", meta.PKBindColumnIndexes[1])
28722877
}
2873-
if len(routingKeyInfo.indexes) != 2 {
2874-
t.Fatalf("Expected routing key indexes length to be 2 but was %d", len(routingKeyInfo.indexes))
2878+
if len(meta.BindColumns) != 2 {
2879+
t.Fatalf("Expected routing statement metadata BindColumns length to be 2 but was %d", len(meta.BindColumns))
28752880
}
2876-
if routingKeyInfo.indexes[0] != 1 {
2877-
t.Errorf("Expected routing key index[0] to be 1 but was %d", routingKeyInfo.indexes[0])
2881+
if meta.BindColumns[0].TypeInfo.Type() != TypeVarchar {
2882+
t.Fatalf("Expected routing statement metadata BindColumns[0].TypeInfo.Type to be %v but was %v", TypeVarchar, meta.BindColumns[0].TypeInfo.Type())
28782883
}
2879-
if routingKeyInfo.indexes[1] != 0 {
2880-
t.Errorf("Expected routing key index[1] to be 0 but was %d", routingKeyInfo.indexes[1])
2884+
if meta.BindColumns[1].TypeInfo.Type() != TypeInt {
2885+
t.Fatalf("Expected routing statement metadata BindColumns[1].TypeInfo.Type to be %v but was %v", TypeInt, meta.BindColumns[1].TypeInfo.Type())
28812886
}
2882-
if len(routingKeyInfo.types) != 2 {
2883-
t.Fatalf("Expected routing key types length to be 1 but was %d", len(routingKeyInfo.types))
2887+
if len(meta.ResultColumns) != 2 {
2888+
t.Fatalf("Expected routing statement metadata ResultColumns length to be 2 but was %d", len(meta.ResultColumns))
28842889
}
2885-
if routingKeyInfo.types[0] == nil {
2886-
t.Fatal("Expected routing key types[0] to be non-nil")
2890+
if meta.ResultColumns[0].Name != "first_id" {
2891+
t.Fatalf("Expected routing statement metadata ResultColumns[0].Name to be %v but was %v", "first_id", meta.ResultColumns[0].Name)
28872892
}
2888-
if routingKeyInfo.types[0].Type() != TypeInt {
2889-
t.Fatalf("Expected routing key types[0] to be %v but was %v", TypeInt, routingKeyInfo.types[0].Type())
2893+
if meta.ResultColumns[0].TypeInfo.Type() != TypeInt {
2894+
t.Fatalf("Expected routing statement metadata ResultColumns[0].TypeInfo.Type to be %v but was %v", TypeInt, meta.ResultColumns[0].TypeInfo.Type())
28902895
}
2891-
if routingKeyInfo.types[1] == nil {
2892-
t.Fatal("Expected routing key types[1] to be non-nil")
2896+
if meta.ResultColumns[1].Name != "second_id" {
2897+
t.Fatalf("Expected routing statement metadata ResultColumns[1].Name to be %v but was %v", "second_id", meta.ResultColumns[1].Name)
28932898
}
2894-
if routingKeyInfo.types[1].Type() != TypeInt {
2895-
t.Fatalf("Expected routing key types[0] to be %v but was %v", TypeInt, routingKeyInfo.types[1].Type())
2899+
if meta.ResultColumns[1].TypeInfo.Type() != TypeVarchar {
2900+
t.Fatalf("Expected routing statement metadata ResultColumns[1].TypeInfo.Type to be %v but was %v", TypeVarchar, meta.ResultColumns[1].TypeInfo.Type())
28962901
}
28972902

2898-
query = newInternalQuery(session.Query("SELECT * FROM test_composite_routing_key WHERE second_id=? AND first_id=?", 1, 2), nil)
2903+
query = newInternalQuery(session.Query("SELECT * FROM test_composite_routing_key WHERE second_id=? AND first_id=?", "1", 2), nil)
28992904
routingKey, err = query.GetRoutingKey()
29002905
if err != nil {
29012906
t.Fatalf("Failed to get routing key due to error: %v", err)
29022907
}
2903-
expectedRoutingKey = []byte{0, 4, 0, 0, 0, 2, 0, 0, 4, 0, 0, 0, 1, 0}
2908+
expectedRoutingKey = []byte{0, 4, 0, 0, 0, 2, 0, 0, 1, 49, 0}
29042909
if !reflect.DeepEqual(expectedRoutingKey, routingKey) {
29052910
t.Errorf("Expected routing key %v but was %v", expectedRoutingKey, routingKey)
29062911
}
29072912

29082913
// verify the cache is working
2909-
cacheSize = session.routingKeyInfoCache.lru.Len()
2914+
cacheSize = session.routingMetadataCache.lru.Len()
29102915
if cacheSize != 2 {
29112916
t.Errorf("Expected cache size to be 2 but was %d", cacheSize)
29122917
}
@@ -3956,17 +3961,17 @@ func TestRoutingKeyCacheUsesOverriddenKeyspace(t *testing.T) {
39563961
t.Fatal(err)
39573962
}
39583963

3959-
getRoutingKeyInfo := func(key string) *routingKeyInfo {
3964+
getStatementMetadata := func(key string) *StatementMetadata {
39603965
t.Helper()
3961-
session.routingKeyInfoCache.mu.Lock()
3962-
value, ok := session.routingKeyInfoCache.lru.Get(key)
3966+
session.routingMetadataCache.mu.Lock()
3967+
value, ok := session.routingMetadataCache.lru.Get(key)
39633968
if !ok {
39643969
t.Fatalf("routing key not found in cache for key %v", key)
39653970
}
3966-
session.routingKeyInfoCache.mu.Unlock()
3971+
session.routingMetadataCache.mu.Unlock()
39673972

39683973
inflight := value.(*inflightCachedEntry)
3969-
return inflight.value.(*routingKeyInfo)
3974+
return inflight.value.(*StatementMetadata)
39703975
}
39713976

39723977
const insertQuery = "INSERT INTO routing_key_cache_uses_overridden_ks (id) VALUES (?)"
@@ -3979,8 +3984,8 @@ func TestRoutingKeyCacheUsesOverriddenKeyspace(t *testing.T) {
39793984
require.NoError(t, err)
39803985

39813986
// Ensuring that the cache contains the query with default ks
3982-
routingKeyInfo1 := getRoutingKeyInfo("gocql_test" + b1.Entries[0].Stmt)
3983-
require.Equal(t, "gocql_test", routingKeyInfo1.keyspace)
3987+
meta1 := getStatementMetadata("gocql_test" + b1.Entries[0].Stmt)
3988+
require.Equal(t, "gocql_test", meta1.Keyspace)
39843989

39853990
// Running batch in gocql_test_routing_key_cache ks
39863991
b2 := session.Batch(LoggedBatch)
@@ -3991,8 +3996,8 @@ func TestRoutingKeyCacheUsesOverriddenKeyspace(t *testing.T) {
39913996
require.NoError(t, err)
39923997

39933998
// Ensuring that the cache contains the query with gocql_test_routing_key_cache ks
3994-
routingKeyInfo2 := getRoutingKeyInfo("gocql_test_routing_key_cache" + b2.Entries[0].Stmt)
3995-
require.Equal(t, "gocql_test_routing_key_cache", routingKeyInfo2.keyspace)
3999+
meta2 := getStatementMetadata("gocql_test_routing_key_cache" + b2.Entries[0].Stmt)
4000+
require.Equal(t, "gocql_test_routing_key_cache", meta2.Keyspace)
39964001

39974002
const selectStmt = "SELECT * FROM routing_key_cache_uses_overridden_ks WHERE id=?"
39984003

frame.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,12 +1046,11 @@ func (f *framer) readTypeInfo() (TypeInfo, error) {
10461046
type preparedMetadata struct {
10471047
resultMetadata
10481048

1049-
// proto v4+
1050-
pkeyColumns []int
1051-
1052-
keyspace string
1053-
1054-
table string
1049+
// pkeyColumns is only present in protocol v4+
1050+
pkeyColumns []int
1051+
supportsPKeyColumns bool
1052+
keyspace string
1053+
table string
10551054
}
10561055

10571056
func (r preparedMetadata) String() string {
@@ -1090,6 +1089,7 @@ func (f *framer) parsePreparedMetadata() (preparedMetadata, error) {
10901089
pkeys[i] = int(c)
10911090
}
10921091
meta.pkeyColumns = pkeys
1092+
meta.supportsPKeyColumns = true
10931093
}
10941094

10951095
if meta.flags&flagHasMorePages == flagHasMorePages {

logger.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,13 @@ type LogField struct {
222222
Value LogFieldValue
223223
}
224224

225+
// NewLogField creates a new LogField with the given name and value. Currently
226+
// supports the following types: string, int, bool, fmt.Stringer, error, net.IP.
227+
// If the type is not supported, it'll use the fmt package to print the value.
228+
func NewLogField(name string, value interface{}) LogField {
229+
return newLogFieldAny(name, value)
230+
}
231+
225232
func newLogField(name string, value LogFieldValue) LogField {
226233
return LogField{
227234
Name: name,
@@ -267,6 +274,27 @@ func newLogFieldBool(name string, value bool) LogField {
267274
return newLogField(name, logFieldValueBool(value))
268275
}
269276

277+
func newLogFieldAny(name string, value interface{}) LogField {
278+
switch v := value.(type) {
279+
case string:
280+
return newLogFieldString(name, v)
281+
case int:
282+
return newLogFieldInt(name, v)
283+
case bool:
284+
return newLogFieldBool(name, v)
285+
case error:
286+
return newLogFieldError(name, v)
287+
case net.IP:
288+
return newLogFieldIp(name, v)
289+
case LogFieldValue:
290+
return newLogField(name, v)
291+
case fmt.Stringer:
292+
return newLogFieldStringer(name, v)
293+
default:
294+
return newLogFieldString(name, fmt.Sprintf("%v", v))
295+
}
296+
}
297+
270298
type StructuredLogger interface {
271299
Error(msg string, fields ...LogField)
272300
Warning(msg string, fields ...LogField)

query_executor.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -423,18 +423,18 @@ func (q *internalQuery) GetRoutingKey() ([]byte, error) {
423423
}
424424

425425
// try to determine the routing key
426-
routingKeyInfo, err := q.session.routingKeyInfo(q.Context(), q.qryOpts.stmt, q.qryOpts.keyspace)
426+
meta, err := q.session.routingStatementMetadata(q.Context(), q.qryOpts.stmt, q.qryOpts.keyspace)
427427
if err != nil {
428428
return nil, err
429429
}
430430

431-
if routingKeyInfo != nil {
431+
if meta != nil {
432432
q.routingInfo.mu.Lock()
433-
q.routingInfo.keyspace = routingKeyInfo.keyspace
434-
q.routingInfo.table = routingKeyInfo.table
433+
q.routingInfo.keyspace = meta.Keyspace
434+
q.routingInfo.table = meta.Table
435435
q.routingInfo.mu.Unlock()
436436
}
437-
return createRoutingKey(routingKeyInfo, q.qryOpts.values)
437+
return createRoutingKey(meta, q.qryOpts.values)
438438
}
439439

440440
func (q *internalQuery) Keyspace() string {
@@ -643,19 +643,19 @@ func (b *internalBatch) GetRoutingKey() ([]byte, error) {
643643
return nil, nil
644644
}
645645
// try to determine the routing key
646-
routingKeyInfo, err := b.session.routingKeyInfo(b.Context(), entry.Stmt, b.batchOpts.keyspace)
646+
meta, err := b.session.routingStatementMetadata(b.Context(), entry.Stmt, b.batchOpts.keyspace)
647647
if err != nil {
648648
return nil, err
649649
}
650650

651-
if routingKeyInfo != nil {
651+
if meta != nil {
652652
b.routingInfo.mu.Lock()
653-
b.routingInfo.keyspace = routingKeyInfo.keyspace
654-
b.routingInfo.table = routingKeyInfo.table
653+
b.routingInfo.keyspace = meta.Keyspace
654+
b.routingInfo.table = meta.Table
655655
b.routingInfo.mu.Unlock()
656656
}
657657

658-
return createRoutingKey(routingKeyInfo, entry.Args)
658+
return createRoutingKey(meta, entry.Args)
659659
}
660660

661661
func (b *internalBatch) Keyspace() string {

0 commit comments

Comments
 (0)