Skip to content

Commit 5bcb70b

Browse files
committed
Virtual keyspace metadata support was immplemented
1 parent 974fa12 commit 5bcb70b

File tree

4 files changed

+343
-16
lines changed

4 files changed

+343
-16
lines changed

cassandra_test.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import (
4444
"time"
4545
"unicode"
4646

47-
inf "gopkg.in/inf.v0"
47+
"gopkg.in/inf.v0"
4848
)
4949

5050
func TestEmptyHosts(t *testing.T) {
@@ -2019,6 +2019,24 @@ func TestGetKeyspaceMetadata(t *testing.T) {
20192019
}
20202020
}
20212021

2022+
func TestGetVirtualKeyspaceMetadata(t *testing.T) {
2023+
session := createSession(t)
2024+
defer session.Close()
2025+
2026+
virtualKeyspaceMatadata, err := session.VirtualKeyspaceMetadata("system_views")
2027+
if err != nil {
2028+
t.Fatal(err)
2029+
}
2030+
if len(virtualKeyspaceMatadata.Tables) == 0 {
2031+
t.Fatal("virtualKeyspaceMatadata.Tables is empty")
2032+
}
2033+
for _, table := range virtualKeyspaceMatadata.Tables {
2034+
if table.Keyspace != "system_views" {
2035+
t.Fatalf("Expected table keyspace to be 'system_views' but got '%s'", table.Keyspace)
2036+
}
2037+
}
2038+
}
2039+
20222040
// Integration test of just querying for data from the system.schema_keyspace table where the keyspace DOES NOT exist.
20232041
func TestGetKeyspaceMetadataFails(t *testing.T) {
20242042
session := createSession(t)

metadata.go

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ type KeyspaceMetadata struct {
5252
UserTypes map[string]*UserTypeMetadata
5353
}
5454

55+
// schema metadata for a virtual keyspace
56+
type VirtualKeyspaceMetadata struct {
57+
Name string
58+
Tables map[string]*VirtualTableMetadata
59+
}
60+
5561
// schema metadata for a table (a.k.a. column family)
5662
type TableMetadata struct {
5763
Keyspace string
@@ -68,6 +74,13 @@ type TableMetadata struct {
6874
OrderedColumns []string
6975
}
7076

77+
type VirtualTableMetadata struct {
78+
Keyspace string
79+
Name string
80+
Comment string
81+
Columns map[string]*VirtualColumnMetadata
82+
}
83+
7184
// schema metadata for a column
7285
type ColumnMetadata struct {
7386
Keyspace string
@@ -82,6 +95,15 @@ type ColumnMetadata struct {
8295
Index ColumnIndexMetadata
8396
}
8497

98+
type VirtualColumnMetadata struct {
99+
Keyspace string
100+
Table string
101+
Name string
102+
ClusteringOrder string
103+
Kind ColumnKind
104+
Type TypeInfo
105+
}
106+
85107
// FunctionMetadata holds metadata for function constructs
86108
type FunctionMetadata struct {
87109
Keyspace string
@@ -240,6 +262,13 @@ type schemaDescriber struct {
240262
cache map[string]*KeyspaceMetadata
241263
}
242264

265+
// queries the cluster for schema information for a virtual keyspace
266+
type virtualSchemaDescriber struct {
267+
session *Session
268+
mu sync.Mutex
269+
cache map[string]*VirtualKeyspaceMetadata
270+
}
271+
243272
// creates a session bound schema describer which will query and cache
244273
// keyspace metadata
245274
func newSchemaDescriber(session *Session) *schemaDescriber {
@@ -249,6 +278,15 @@ func newSchemaDescriber(session *Session) *schemaDescriber {
249278
}
250279
}
251280

281+
// creates a session bound schema describer which will query and cache
282+
// virtual keyspace metadata
283+
func newVirtualSchemaDescriber(session *Session) *virtualSchemaDescriber {
284+
return &virtualSchemaDescriber{
285+
session: session,
286+
cache: map[string]*VirtualKeyspaceMetadata{},
287+
}
288+
}
289+
252290
// returns the cached KeyspaceMetadata held by the describer for the named
253291
// keyspace.
254292
func (s *schemaDescriber) getSchema(keyspaceName string) (*KeyspaceMetadata, error) {
@@ -269,6 +307,23 @@ func (s *schemaDescriber) getSchema(keyspaceName string) (*KeyspaceMetadata, err
269307
return metadata, nil
270308
}
271309

310+
// returns the cached VirtualKeyspaceMetadata held by the describer for the named
311+
// keyspace.
312+
func (s *virtualSchemaDescriber) getSchema(keyspaceName string) (*VirtualKeyspaceMetadata, error) {
313+
s.mu.Lock()
314+
defer s.mu.Unlock()
315+
metadata, found := s.cache[keyspaceName]
316+
if !found {
317+
err := s.refreshSchema(keyspaceName)
318+
if err != nil {
319+
return nil, err
320+
}
321+
metadata = s.cache[keyspaceName]
322+
}
323+
324+
return metadata, nil
325+
}
326+
272327
// clears the already cached keyspace metadata
273328
func (s *schemaDescriber) clearSchema(keyspaceName string) {
274329
s.mu.Lock()
@@ -323,6 +378,43 @@ func (s *schemaDescriber) refreshSchema(keyspaceName string) error {
323378
return nil
324379
}
325380

381+
// forcibly updates the current VirtualKeyspaceMetadata held by the virtual schema describer
382+
// for a given named keyspace.
383+
func (s *virtualSchemaDescriber) refreshSchema(keyspaceName string) error {
384+
var wg sync.WaitGroup
385+
386+
var (
387+
tables []VirtualTableMetadata
388+
columns []VirtualColumnMetadata
389+
tableErr, columnErr error
390+
)
391+
392+
wg.Add(2)
393+
go func() {
394+
defer wg.Done()
395+
tables, tableErr = getVirtualTableMetadata(s.session, keyspaceName)
396+
}()
397+
go func() {
398+
defer wg.Done()
399+
columns, columnErr = getVirtualColumnMetadata(s.session, keyspaceName)
400+
}()
401+
wg.Wait()
402+
403+
if columnErr != nil {
404+
return columnErr
405+
}
406+
if tableErr != nil {
407+
return tableErr
408+
}
409+
410+
keyspaceMetadata := &VirtualKeyspaceMetadata{Name: keyspaceName}
411+
compileVirtualMetadata(keyspaceMetadata, tables, columns)
412+
413+
s.cache[keyspaceName] = keyspaceMetadata
414+
415+
return nil
416+
}
417+
326418
// "compiles" derived information about keyspace, table, and column metadata
327419
// for a keyspace from the basic queried metadata objects returned by
328420
// getKeyspaceMetadata, getTableMetadata, and getColumnMetadata respectively;
@@ -417,6 +509,33 @@ func compileMetadata(
417509
}
418510
}
419511

512+
// "compiles" derived information about virtual keyspace, table, and column metadata
513+
// for a keyspace from the basic queried metadata objects returned by
514+
// getVirtualTableMetadata, and getVirtualColumnMetadata respectively;
515+
// Links the metadata objects together.
516+
func compileVirtualMetadata(
517+
keyspace *VirtualKeyspaceMetadata,
518+
tables []VirtualTableMetadata,
519+
columns []VirtualColumnMetadata,
520+
) {
521+
keyspace.Tables = make(map[string]*VirtualTableMetadata)
522+
for i := range tables {
523+
tables[i].Columns = make(map[string]*VirtualColumnMetadata)
524+
525+
keyspace.Tables[tables[i].Name] = &tables[i]
526+
}
527+
528+
for i := range columns {
529+
col := &columns[i]
530+
table, ok := keyspace.Tables[col.Table]
531+
if !ok {
532+
continue
533+
}
534+
535+
table.Columns[col.Name] = col
536+
}
537+
}
538+
420539
// Compiles derived information from TableMetadata which have had
421540
// ColumnMetadata added already. V1 protocol does not return as much
422541
// column metadata as V2+ (because V1 doesn't support the "type" column in the
@@ -760,6 +879,37 @@ func getTableMetadata(session *Session, keyspaceName string) ([]TableMetadata, e
760879
return tables, nil
761880
}
762881

882+
// query for only the table metadata in the specified keyspace from system_virtual_schema.tables
883+
func getVirtualTableMetadata(s *Session, keyspaceName string) ([]VirtualTableMetadata, error) {
884+
const stmt = `
885+
SELECT
886+
table_name,
887+
comment
888+
FROM system_virtual_schema.tables
889+
WHERE keyspace_name = ?`
890+
891+
tables := []VirtualTableMetadata{}
892+
table := VirtualTableMetadata{Keyspace: keyspaceName}
893+
894+
iter := s.control.query(stmt, keyspaceName)
895+
defer iter.Close()
896+
897+
if iter.err != nil {
898+
return nil, fmt.Errorf("failed to iterate virtual table metadata for keyspace: %w", iter.err)
899+
}
900+
901+
if iter.NumRows() == 0 {
902+
return nil, ErrKeyspaceDoesNotExist
903+
}
904+
905+
for iter.Scan(&table.Name, &table.Comment) {
906+
tables = append(tables, table)
907+
table = VirtualTableMetadata{Keyspace: keyspaceName}
908+
}
909+
910+
return tables, nil
911+
}
912+
763913
func (s *Session) scanColumnMetadataV1(keyspace string) ([]ColumnMetadata, error) {
764914
// V1 does not support the type column, and all returned rows are
765915
// of kind "regular".
@@ -947,6 +1097,52 @@ func getColumnMetadata(session *Session, keyspaceName string) ([]ColumnMetadata,
9471097
return columns, nil
9481098
}
9491099

1100+
// query for only the column metadata in the specified keyspace from system_virtual_schema.columns
1101+
func getVirtualColumnMetadata(s *Session, keyspaceName string) ([]VirtualColumnMetadata, error) {
1102+
const stmt = `
1103+
SELECT
1104+
table_name,
1105+
column_name,
1106+
clustering_order,
1107+
kind,
1108+
type
1109+
FROM system_virtual_schema.columns
1110+
WHERE keyspace_name = ?`
1111+
1112+
var columns []VirtualColumnMetadata
1113+
1114+
rows := s.control.query(stmt, keyspaceName).Scanner()
1115+
1116+
for rows.Next() {
1117+
var (
1118+
column = VirtualColumnMetadata{Keyspace: keyspaceName}
1119+
columnType string
1120+
)
1121+
1122+
err := rows.Scan(
1123+
&column.Table,
1124+
&column.Name,
1125+
&column.ClusteringOrder,
1126+
&column.Kind,
1127+
&columnType,
1128+
)
1129+
1130+
if err != nil {
1131+
return nil, err
1132+
}
1133+
1134+
column.Type = getCassandraType(columnType, s.logger)
1135+
1136+
columns = append(columns, column)
1137+
}
1138+
1139+
if err := rows.Err(); err != nil {
1140+
return nil, fmt.Errorf("failed to iterate virtual column metadata for keyspace: %w", err)
1141+
}
1142+
1143+
return columns, nil
1144+
}
1145+
9501146
func getTypeInfo(t string, logger StdLogger) TypeInfo {
9511147
if strings.HasPrefix(t, apacheCassandraTypePrefix) {
9521148
t = apacheToCassandraType(t)

0 commit comments

Comments
 (0)