@@ -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)
5662type 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
7285type 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
86108type 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
245274func 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.
254292func (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
273328func (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+
763913func (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+
9501146func getTypeInfo (t string , logger StdLogger ) TypeInfo {
9511147 if strings .HasPrefix (t , apacheCassandraTypePrefix ) {
9521148 t = apacheToCassandraType (t )
0 commit comments