github.com/datastax/go-cassandra-native-protocol@v0.0.0-20220706104457-5e8aad05cf90/client/system.go (about) 1 // Copyright 2020 DataStax 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package client 16 17 import ( 18 "bytes" 19 "net" 20 "strings" 21 22 "github.com/rs/zerolog/log" 23 24 "github.com/datastax/go-cassandra-native-protocol/datatype" 25 "github.com/datastax/go-cassandra-native-protocol/frame" 26 "github.com/datastax/go-cassandra-native-protocol/message" 27 "github.com/datastax/go-cassandra-native-protocol/primitive" 28 ) 29 30 // Creates a new RequestHandler to handle queries to system tables (system.local and system.peers). 31 func NewSystemTablesHandler(cluster string, datacenter string) RequestHandler { 32 return func(request *frame.Frame, conn *CqlServerConnection, _ RequestHandlerContext) (response *frame.Frame) { 33 if query, ok := request.Body.Message.(*message.Query); ok { 34 q := strings.TrimSpace(strings.ToLower(query.Query)) 35 q = strings.Join(strings.Fields(q), " ") // remove extra whitespace 36 if strings.HasPrefix(q, "select * from system.local") { 37 log.Debug().Msgf("%v: [system tables handler]: returning full system.local", conn) 38 response = fullSystemLocal(cluster, datacenter, request, conn) 39 } else if strings.HasPrefix(q, "select schema_version from system.local") { 40 log.Debug().Msgf("%v: [system tables handler]: returning schema_version", conn) 41 response = schemaVersion(request) 42 } else if strings.HasPrefix(q, "select cluster_name from system.local") { 43 log.Debug().Msgf("%v: [system tables handler]: returning cluster_name", conn) 44 response = clusterName(cluster, request) 45 } else if strings.Contains(q, "from system.peers") { 46 log.Debug().Msgf("%v: [system tables handler]: returning empty system.peers", conn) 47 response = emptySystemPeers(request) 48 } 49 } 50 return 51 } 52 } 53 54 var ( 55 keyColumn = &message.ColumnMetadata{Keyspace: "system", Table: "local", Name: "key", Type: datatype.Varchar} 56 broadcastAddressColumn = &message.ColumnMetadata{Keyspace: "system", Table: "local", Name: "broadcast_address", Type: datatype.Inet} 57 clusterNameColumn = &message.ColumnMetadata{Keyspace: "system", Table: "local", Name: "cluster_name", Type: datatype.Varchar} 58 cqlVersionColumn = &message.ColumnMetadata{Keyspace: "system", Table: "local", Name: "cql_version", Type: datatype.Varchar} 59 datacenterColumn = &message.ColumnMetadata{Keyspace: "system", Table: "local", Name: "data_center", Type: datatype.Varchar} 60 hostIdColumn = &message.ColumnMetadata{Keyspace: "system", Table: "local", Name: "host_id", Type: datatype.Uuid} 61 listenAddressColumn = &message.ColumnMetadata{Keyspace: "system", Table: "local", Name: "listen_address", Type: datatype.Inet} 62 partitionerColumn = &message.ColumnMetadata{Keyspace: "system", Table: "local", Name: "partitioner", Type: datatype.Varchar} 63 rackColumn = &message.ColumnMetadata{Keyspace: "system", Table: "local", Name: "rack", Type: datatype.Varchar} 64 releaseVersionColumn = &message.ColumnMetadata{Keyspace: "system", Table: "local", Name: "release_version", Type: datatype.Varchar} 65 rpcAddressColumn = &message.ColumnMetadata{Keyspace: "system", Table: "local", Name: "rpc_address", Type: datatype.Inet} 66 schemaVersionColumn = &message.ColumnMetadata{Keyspace: "system", Table: "local", Name: "schema_version", Type: datatype.Uuid} 67 tokensColumn = &message.ColumnMetadata{Keyspace: "system", Table: "local", Name: "tokens", Type: datatype.NewSet(datatype.Varchar)} 68 ) 69 70 // These columns are a subset of the total columns returned by OSS C* 3.11.2, and contain all the information that 71 // drivers need in order to establish the cluster topology and determine its characteristics. 72 var systemLocalColumns = []*message.ColumnMetadata{ 73 keyColumn, 74 broadcastAddressColumn, 75 clusterNameColumn, 76 cqlVersionColumn, 77 datacenterColumn, 78 hostIdColumn, 79 listenAddressColumn, 80 partitionerColumn, 81 rackColumn, 82 releaseVersionColumn, 83 rpcAddressColumn, 84 schemaVersionColumn, 85 tokensColumn, 86 } 87 88 var ( 89 keyValue = message.Column("local") 90 cqlVersionValue = message.Column("3.4.4") 91 hostIdValue = message.Column{0xC0, 0xD1, 0xD2, 0x1E, 0xBB, 0x01, 0x41, 0x96, 0x86, 0xDB, 0xBC, 0x31, 0x7B, 0xC1, 0x79, 0x6A} 92 partitionerValue = message.Column("org.apache.cassandra.dht.Murmur3Partitioner") 93 rackValue = message.Column("rack1") 94 releaseVersionValue = message.Column("3.11.2") 95 schemaVersionValue = message.Column{0xC0, 0xD1, 0xD2, 0x1E, 0xBB, 0x01, 0x41, 0x96, 0x86, 0xDB, 0xBC, 0x31, 0x7B, 0xC1, 0x79, 0x6A} 96 ) 97 98 func systemLocalRow(cluster string, datacenter string, addr net.Addr, version primitive.ProtocolVersion) message.Row { 99 addrBuf := &bytes.Buffer{} 100 // TODO replace the serialization code below when data type serialization is implemented 101 inetAddr := addr.(*net.TCPAddr).IP 102 if inetAddr.To4() != nil { 103 addrBuf.Write(inetAddr.To4()) 104 } else { 105 addrBuf.Write(inetAddr) 106 } 107 // emulate {'-9223372036854775808'} (entire ring) 108 tokensBuf := &bytes.Buffer{} 109 if version >= primitive.ProtocolVersion3 { 110 _ = primitive.WriteInt(1, tokensBuf) 111 _ = primitive.WriteInt(int32(len("-9223372036854775808")), tokensBuf) 112 } else { 113 _ = primitive.WriteShort(1, tokensBuf) 114 _ = primitive.WriteShort(uint16(len("-9223372036854775808")), tokensBuf) 115 } 116 tokensBuf.WriteString("-9223372036854775808") 117 return message.Row{ 118 keyValue, 119 addrBuf.Bytes(), 120 message.Column(cluster), 121 cqlVersionValue, 122 message.Column(datacenter), 123 hostIdValue, 124 addrBuf.Bytes(), 125 partitionerValue, 126 rackValue, 127 releaseVersionValue, 128 addrBuf.Bytes(), 129 schemaVersionValue, 130 tokensBuf.Bytes(), 131 } 132 } 133 134 func fullSystemLocal(cluster string, datacenter string, request *frame.Frame, conn *CqlServerConnection) *frame.Frame { 135 systemLocalRow := systemLocalRow(cluster, datacenter, conn.LocalAddr(), request.Header.Version) 136 msg := &message.RowsResult{ 137 Metadata: &message.RowsMetadata{ 138 ColumnCount: int32(len(systemLocalColumns)), 139 Columns: systemLocalColumns, 140 }, 141 Data: message.RowSet{systemLocalRow}, 142 } 143 return frame.NewFrame(request.Header.Version, request.Header.StreamId, msg) 144 } 145 146 func schemaVersion(request *frame.Frame) *frame.Frame { 147 msg := &message.RowsResult{ 148 Metadata: &message.RowsMetadata{ 149 ColumnCount: 1, 150 Columns: []*message.ColumnMetadata{schemaVersionColumn}, 151 }, 152 Data: message.RowSet{message.Row{schemaVersionValue}}, 153 } 154 return frame.NewFrame(request.Header.Version, request.Header.StreamId, msg) 155 } 156 157 func clusterName(cluster string, request *frame.Frame) *frame.Frame { 158 msg := &message.RowsResult{ 159 Metadata: &message.RowsMetadata{ 160 ColumnCount: 1, 161 Columns: []*message.ColumnMetadata{clusterNameColumn}, 162 }, 163 Data: message.RowSet{message.Row{message.Column(cluster)}}, 164 } 165 return frame.NewFrame(request.Header.Version, request.Header.StreamId, msg) 166 } 167 168 func emptySystemPeers(request *frame.Frame) *frame.Frame { 169 msg := &message.RowsResult{ 170 Metadata: &message.RowsMetadata{ColumnCount: 0}, 171 Data: message.RowSet{}, 172 } 173 return frame.NewFrame(request.Header.Version, request.Header.StreamId, msg) 174 }