@@ -50,6 +50,12 @@ type KeyspaceMetadata struct {
5050 UserTypes map [string ]* UserTypeMetadata
5151}
5252
53+ // schema metadata for a virtual keyspace
54+ type VirtualKeyspaceMetadata struct {
55+ Name string
56+ Tables map [string ]* VirtualTableMetadata
57+ }
58+
5359// schema metadata for a table (a.k.a. column family)
5460type TableMetadata struct {
5561 Keyspace string
@@ -66,6 +72,13 @@ type TableMetadata struct {
6672 OrderedColumns []string
6773}
6874
75+ type VirtualTableMetadata struct {
76+ Keyspace string
77+ Name string
78+ Comment string
79+ Columns map [string ]* VirtualColumnMetadata
80+ }
81+
6982// schema metadata for a column
7083type ColumnMetadata struct {
7184 Keyspace string
@@ -80,6 +93,15 @@ type ColumnMetadata struct {
8093 Index ColumnIndexMetadata
8194}
8295
96+ type VirtualColumnMetadata struct {
97+ Keyspace string
98+ Table string
99+ Name string
100+ ClusteringOrder string
101+ Kind ColumnKind
102+ Type TypeInfo
103+ }
104+
83105// FunctionMetadata holds metadata for function constructs
84106type FunctionMetadata struct {
85107 Keyspace string
@@ -231,6 +253,13 @@ type schemaDescriber struct {
231253 cache map [string ]* KeyspaceMetadata
232254}
233255
256+ // queries the cluster for schema information for a virtual keyspace
257+ type virtualSchemaDescriber struct {
258+ session * Session
259+ mu sync.Mutex
260+ cache map [string ]* VirtualKeyspaceMetadata
261+ }
262+
234263// creates a session bound schema describer which will query and cache
235264// keyspace metadata
236265func newSchemaDescriber (session * Session ) * schemaDescriber {
@@ -240,6 +269,15 @@ func newSchemaDescriber(session *Session) *schemaDescriber {
240269 }
241270}
242271
272+ // creates a session bound schema describer which will query and cache
273+ // virtual keyspace metadata
274+ func newVirtualSchemaDescriber (session * Session ) * virtualSchemaDescriber {
275+ return & virtualSchemaDescriber {
276+ session : session ,
277+ cache : map [string ]* VirtualKeyspaceMetadata {},
278+ }
279+ }
280+
243281// returns the cached KeyspaceMetadata held by the describer for the named
244282// keyspace.
245283func (s * schemaDescriber ) getSchema (keyspaceName string ) (* KeyspaceMetadata , error ) {
@@ -260,6 +298,23 @@ func (s *schemaDescriber) getSchema(keyspaceName string) (*KeyspaceMetadata, err
260298 return metadata , nil
261299}
262300
301+ // returns the cached VirtualKeyspaceMetadata held by the describer for the named
302+ // keyspace.
303+ func (s * virtualSchemaDescriber ) getSchema (keyspaceName string ) (* VirtualKeyspaceMetadata , error ) {
304+ s .mu .Lock ()
305+ defer s .mu .Unlock ()
306+ metadata , found := s .cache [keyspaceName ]
307+ if ! found {
308+ err := s .refreshSchema (keyspaceName )
309+ if err != nil {
310+ return nil , err
311+ }
312+ metadata = s .cache [keyspaceName ]
313+ }
314+
315+ return metadata , nil
316+ }
317+
263318// clears the already cached keyspace metadata
264319func (s * schemaDescriber ) clearSchema (keyspaceName string ) {
265320 s .mu .Lock ()
@@ -314,6 +369,43 @@ func (s *schemaDescriber) refreshSchema(keyspaceName string) error {
314369 return nil
315370}
316371
372+ // forcibly updates the current VirtualKeyspaceMetadata held by the virtual schema describer
373+ // for a given named keyspace.
374+ func (s * virtualSchemaDescriber ) refreshSchema (keyspaceName string ) error {
375+ var wg sync.WaitGroup
376+
377+ var (
378+ tables []VirtualTableMetadata
379+ columns []VirtualColumnMetadata
380+ tableErr , columnErr error
381+ )
382+
383+ wg .Add (2 )
384+ go func () {
385+ defer wg .Done ()
386+ tables , tableErr = getVirtualTableMetadata (s .session , keyspaceName )
387+ }()
388+ go func () {
389+ defer wg .Done ()
390+ columns , columnErr = getVirtualColumnMetadata (s .session , keyspaceName )
391+ }()
392+ wg .Wait ()
393+
394+ if columnErr != nil {
395+ return columnErr
396+ }
397+ if tableErr != nil {
398+ return tableErr
399+ }
400+
401+ keyspaceMetadata := & VirtualKeyspaceMetadata {Name : keyspaceName }
402+ compileVirtualMetadata (keyspaceMetadata , tables , columns )
403+
404+ s .cache [keyspaceName ] = keyspaceMetadata
405+
406+ return nil
407+ }
408+
317409// "compiles" derived information about keyspace, table, and column metadata
318410// for a keyspace from the basic queried metadata objects returned by
319411// getKeyspaceMetadata, getTableMetadata, and getColumnMetadata respectively;
@@ -395,6 +487,33 @@ func compileMetadata(
395487 }
396488}
397489
490+ // "compiles" derived information about virtual keyspace, table, and column metadata
491+ // for a keyspace from the basic queried metadata objects returned by
492+ // getVirtualTableMetadata, and getVirtualColumnMetadata respectively;
493+ // Links the metadata objects together.
494+ func compileVirtualMetadata (
495+ keyspace * VirtualKeyspaceMetadata ,
496+ tables []VirtualTableMetadata ,
497+ columns []VirtualColumnMetadata ,
498+ ) {
499+ keyspace .Tables = make (map [string ]* VirtualTableMetadata )
500+ for i := range tables {
501+ tables [i ].Columns = make (map [string ]* VirtualColumnMetadata )
502+
503+ keyspace .Tables [tables [i ].Name ] = & tables [i ]
504+ }
505+
506+ for i := range columns {
507+ col := & columns [i ]
508+ table , ok := keyspace .Tables [col .Table ]
509+ if ! ok {
510+ continue
511+ }
512+
513+ table .Columns [col .Name ] = col
514+ }
515+ }
516+
398517// Compiles derived information from TableMetadata which have had
399518// ColumnMetadata added already. V1 protocol does not return as much
400519// column metadata as V2+ (because V1 doesn't support the "type" column in the
@@ -738,6 +857,37 @@ func getTableMetadata(session *Session, keyspaceName string) ([]TableMetadata, e
738857 return tables , nil
739858}
740859
860+ // query for only the table metadata in the specified keyspace from system_virtual_schema.tables
861+ func getVirtualTableMetadata (s * Session , keyspaceName string ) ([]VirtualTableMetadata , error ) {
862+ const stmt = `
863+ SELECT
864+ table_name,
865+ comment
866+ FROM system_virtual_schema.tables
867+ WHERE keyspace_name = ?`
868+
869+ tables := []VirtualTableMetadata {}
870+ table := VirtualTableMetadata {Keyspace : keyspaceName }
871+
872+ iter := s .control .query (stmt , keyspaceName )
873+ defer iter .Close ()
874+
875+ if iter .err != nil {
876+ return nil , fmt .Errorf ("failed to iterate virtual table metadata for keyspace: %w" , iter .err )
877+ }
878+
879+ if iter .NumRows () == 0 {
880+ return nil , ErrKeyspaceDoesNotExist
881+ }
882+
883+ for iter .Scan (& table .Name , & table .Comment ) {
884+ tables = append (tables , table )
885+ table = VirtualTableMetadata {Keyspace : keyspaceName }
886+ }
887+
888+ return tables , nil
889+ }
890+
741891func (s * Session ) scanColumnMetadataV1 (keyspace string ) ([]ColumnMetadata , error ) {
742892 // V1 does not support the type column, and all returned rows are
743893 // of kind "regular".
@@ -925,6 +1075,52 @@ func getColumnMetadata(session *Session, keyspaceName string) ([]ColumnMetadata,
9251075 return columns , nil
9261076}
9271077
1078+ // query for only the column metadata in the specified keyspace from system_virtual_schema.columns
1079+ func getVirtualColumnMetadata (s * Session , keyspaceName string ) ([]VirtualColumnMetadata , error ) {
1080+ const stmt = `
1081+ SELECT
1082+ table_name,
1083+ column_name,
1084+ clustering_order,
1085+ kind,
1086+ type
1087+ FROM system_virtual_schema.columns
1088+ WHERE keyspace_name = ?`
1089+
1090+ var columns []VirtualColumnMetadata
1091+
1092+ rows := s .control .query (stmt , keyspaceName ).Scanner ()
1093+
1094+ for rows .Next () {
1095+ var (
1096+ column = VirtualColumnMetadata {Keyspace : keyspaceName }
1097+ columnType string
1098+ )
1099+
1100+ err := rows .Scan (
1101+ & column .Table ,
1102+ & column .Name ,
1103+ & column .ClusteringOrder ,
1104+ & column .Kind ,
1105+ & columnType ,
1106+ )
1107+
1108+ if err != nil {
1109+ return nil , err
1110+ }
1111+
1112+ column .Type = getCassandraType (columnType , s .logger )
1113+
1114+ columns = append (columns , column )
1115+ }
1116+
1117+ if err := rows .Err (); err != nil {
1118+ return nil , fmt .Errorf ("failed to iterate virtual column metadata for keyspace: %w" , err )
1119+ }
1120+
1121+ return columns , nil
1122+ }
1123+
9281124func getTypeInfo (t string , logger StdLogger ) TypeInfo {
9291125 if strings .HasPrefix (t , apacheCassandraTypePrefix ) {
9301126 t = apacheToCassandraType (t )
0 commit comments