-
Notifications
You must be signed in to change notification settings - Fork 865
/
AbstractTableMetadata.java
378 lines (346 loc) · 12.9 KB
/
AbstractTableMetadata.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.datastax.driver.core;
import com.google.common.base.Predicate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/** Base class for Tables and Materialized Views metadata. */
public abstract class AbstractTableMetadata {
static final Comparator<ColumnMetadata> columnMetadataComparator =
new Comparator<ColumnMetadata>() {
@Override
public int compare(ColumnMetadata c1, ColumnMetadata c2) {
return c1.getName().compareTo(c2.getName());
}
};
// comparator for ordering tables and views by name.
static final Comparator<AbstractTableMetadata> byNameComparator =
new Comparator<AbstractTableMetadata>() {
@Override
public int compare(AbstractTableMetadata o1, AbstractTableMetadata o2) {
return o1.getName().compareTo(o2.getName());
}
};
static final Predicate<ClusteringOrder> isAscending =
new Predicate<ClusteringOrder>() {
@Override
public boolean apply(ClusteringOrder o) {
return o == ClusteringOrder.ASC;
}
};
protected final KeyspaceMetadata keyspace;
protected final String name;
protected final UUID id;
protected final List<ColumnMetadata> partitionKey;
protected final List<ColumnMetadata> clusteringColumns;
protected final Map<String, ColumnMetadata> columns;
protected final TableOptionsMetadata options;
protected final List<ClusteringOrder> clusteringOrder;
protected final VersionNumber cassandraVersion;
protected AbstractTableMetadata(
KeyspaceMetadata keyspace,
String name,
UUID id,
List<ColumnMetadata> partitionKey,
List<ColumnMetadata> clusteringColumns,
Map<String, ColumnMetadata> columns,
TableOptionsMetadata options,
List<ClusteringOrder> clusteringOrder,
VersionNumber cassandraVersion) {
this.keyspace = keyspace;
this.name = name;
this.id = id;
this.partitionKey = partitionKey;
this.clusteringColumns = clusteringColumns;
this.columns = columns;
this.options = options;
this.clusteringOrder = clusteringOrder;
this.cassandraVersion = cassandraVersion;
}
/**
* Returns the name of this table.
*
* @return the name of this CQL table.
*/
public String getName() {
return name;
}
/**
* Returns the unique id of this table.
*
* <p>Note: this id is available in Cassandra 2.1 and above. It will be {@code null} for earlier
* versions.
*
* @return the unique id of the table.
*/
public UUID getId() {
return id;
}
/**
* Returns the keyspace this table belong to.
*
* @return the keyspace metadata of the keyspace this table belong to.
*/
public KeyspaceMetadata getKeyspace() {
return keyspace;
}
/**
* Returns metadata on a column of this table.
*
* @param name the name of the column to retrieve ({@code name} will be interpreted as a
* case-insensitive identifier unless enclosed in double-quotes, see {@link Metadata#quote}).
* @return the metadata for the column if it exists, or {@code null} otherwise.
*/
public ColumnMetadata getColumn(String name) {
return columns.get(Metadata.handleId(name));
}
/**
* Returns a list containing all the columns of this table.
*
* <p>The order of the columns in the list is consistent with the order of the columns returned by
* a {@code SELECT * FROM thisTable}: the first column is the partition key, next are the
* clustering columns in their defined order, and then the rest of the columns follow in
* alphabetic order.
*
* @return a list containing the metadata for the columns of this table.
*/
public List<ColumnMetadata> getColumns() {
return new ArrayList<ColumnMetadata>(columns.values());
}
/**
* Returns the list of columns composing the primary key for this table.
*
* <p>A table will always at least have a partition key (that may itself be one or more columns),
* so the returned list at least has one element.
*
* @return the list of columns composing the primary key for this table.
*/
public List<ColumnMetadata> getPrimaryKey() {
List<ColumnMetadata> pk =
new ArrayList<ColumnMetadata>(partitionKey.size() + clusteringColumns.size());
pk.addAll(partitionKey);
pk.addAll(clusteringColumns);
return pk;
}
/**
* Returns the list of columns composing the partition key for this table.
*
* <p>A table always has a partition key so the returned list has at least one element.
*
* @return the list of columns composing the partition key for this table.
*/
public List<ColumnMetadata> getPartitionKey() {
return Collections.unmodifiableList(partitionKey);
}
/**
* Returns the list of clustering columns for this table.
*
* @return the list of clustering columns for this table. If there is no clustering columns, an
* empty list is returned.
*/
public List<ColumnMetadata> getClusteringColumns() {
return Collections.unmodifiableList(clusteringColumns);
}
/**
* Returns the clustering order for this table.
*
* <p>The returned contains the clustering order of each clustering column. The {@code i}th
* element of the result correspond to the order (ascending or descending) of the {@code i}th
* clustering column (see {@link #getClusteringColumns}). Note that a table defined without any
* particular clustering order is equivalent to one for which all the clustering keys are in
* ascending order.
*
* @return a list with the clustering order for each clustering column.
*/
public List<ClusteringOrder> getClusteringOrder() {
return clusteringOrder;
}
/**
* Returns the options for this table.
*
* <p>This value will be null for virtual tables.
*
* @return the options for this table.
*/
public TableOptionsMetadata getOptions() {
return options;
}
/**
* Returns whether or not this table is a virtual table
*
* @return {@code true} if virtual keyspace, {@code false} otherwise.
*/
public boolean isVirtual() {
return getKeyspace().isVirtual();
}
void add(ColumnMetadata column) {
columns.put(column.getName(), column);
}
/**
* Returns a {@code String} containing CQL queries representing this table and the index on it.
*
* <p>In other words, this method returns the queries that would allow you to recreate the schema
* of this table, along with the indexes and views defined on this table, if any.
*
* <p>Note that the returned String is formatted to be human readable (for some definition of
* human readable at least).
*
* @return the CQL queries representing this table schema as a {code String}.
*/
public String exportAsString() {
StringBuilder sb = new StringBuilder();
sb.append(asCQLQuery(true));
return sb.toString();
}
/**
* Returns a CQL query representing this table.
*
* <p>This method returns a single 'CREATE TABLE' query with the options corresponding to this
* table definition.
*
* <p>Note that the returned string is a single line; the returned query is not formatted in any
* way.
*
* @return the 'CREATE TABLE' query corresponding to this table.
* @see #exportAsString
*/
public String asCQLQuery() {
return asCQLQuery(false);
}
protected abstract String asCQLQuery(boolean formatted);
protected StringBuilder appendOptions(StringBuilder sb, boolean formatted) {
// Options
if (options == null) {
return sb;
}
sb.append("WITH ");
if (options.isCompactStorage()) and(sb.append("COMPACT STORAGE"), formatted);
if (!clusteringOrder.isEmpty()) and(appendClusteringOrder(sb), formatted);
if (cassandraVersion.getMajor() < 4)
sb.append("read_repair_chance = ").append(options.getReadRepairChance());
else sb.append("read_repair = '").append(options.getReadRepair()).append('\'');
if (cassandraVersion.getMajor() < 4)
and(sb, formatted)
.append("dclocal_read_repair_chance = ")
.append(options.getLocalReadRepairChance());
if (cassandraVersion.getMajor() < 2
|| (cassandraVersion.getMajor() == 2 && cassandraVersion.getMinor() == 0))
and(sb, formatted).append("replicate_on_write = ").append(options.getReplicateOnWrite());
and(sb, formatted).append("gc_grace_seconds = ").append(options.getGcGraceInSeconds());
if (cassandraVersion.getMajor() > 3)
and(sb, formatted)
.append("additional_write_policy = '")
.append(options.getAdditionalWritePolicy())
.append('\'');
and(sb, formatted)
.append("bloom_filter_fp_chance = ")
.append(options.getBloomFilterFalsePositiveChance());
if (cassandraVersion.getMajor() < 2
|| cassandraVersion.getMajor() == 2 && cassandraVersion.getMinor() < 1)
and(sb, formatted)
.append("caching = '")
.append(options.getCaching().get("keys"))
.append('\'');
else and(sb, formatted).append("caching = ").append(formatOptionMap(options.getCaching()));
if (options.getComment() != null)
and(sb, formatted)
.append("comment = '")
.append(options.getComment().replace("'", "''"))
.append('\'');
and(sb, formatted).append("compaction = ").append(formatOptionMap(options.getCompaction()));
and(sb, formatted).append("compression = ").append(formatOptionMap(options.getCompression()));
if (cassandraVersion.getMajor() >= 2) {
and(sb, formatted).append("default_time_to_live = ").append(options.getDefaultTimeToLive());
and(sb, formatted)
.append("speculative_retry = '")
.append(options.getSpeculativeRetry())
.append('\'');
if (options.getIndexInterval() != null)
and(sb, formatted).append("index_interval = ").append(options.getIndexInterval());
}
if (cassandraVersion.getMajor() > 2
|| (cassandraVersion.getMajor() == 2 && cassandraVersion.getMinor() >= 1)) {
and(sb, formatted).append("min_index_interval = ").append(options.getMinIndexInterval());
and(sb, formatted).append("max_index_interval = ").append(options.getMaxIndexInterval());
}
if (cassandraVersion.getMajor() > 2) {
and(sb, formatted).append("crc_check_chance = ").append(options.getCrcCheckChance());
}
if (cassandraVersion.getMajor() > 3
|| (cassandraVersion.getMajor() == 3 && cassandraVersion.getMinor() >= 8)) {
and(sb, formatted).append("cdc = ").append(options.isCDC());
}
if (cassandraVersion.getMajor() > 1) {
and(sb, formatted)
.append("memtable_flush_period_in_ms = ")
.append(options.getMemtableFlushPeriodInMs());
}
sb.append(';');
return sb;
}
@Override
public String toString() {
if (keyspace.isVirtual()) {
return name;
}
return asCQLQuery();
}
private StringBuilder appendClusteringOrder(StringBuilder sb) {
sb.append("CLUSTERING ORDER BY (");
for (int i = 0; i < clusteringColumns.size(); i++) {
if (i > 0) sb.append(", ");
sb.append(Metadata.quoteIfNecessary(clusteringColumns.get(i).getName()))
.append(' ')
.append(clusteringOrder.get(i));
}
return sb.append(')');
}
private static String formatOptionMap(Map<String, String> m) {
StringBuilder sb = new StringBuilder();
sb.append("{ ");
boolean first = true;
for (Map.Entry<String, String> entry : m.entrySet()) {
if (first) first = false;
else sb.append(", ");
sb.append('\'').append(entry.getKey()).append('\'');
sb.append(" : ");
try {
sb.append(Integer.parseInt(entry.getValue()));
} catch (NumberFormatException e) {
sb.append('\'').append(entry.getValue()).append('\'');
}
}
sb.append(" }");
return sb.toString();
}
private StringBuilder and(StringBuilder sb, boolean formatted) {
return spaceOrNewLine(sb, formatted).append("AND ");
}
static StringBuilder newLine(StringBuilder sb, boolean formatted) {
if (formatted) sb.append('\n');
return sb;
}
static StringBuilder spaceOrNewLine(StringBuilder sb, boolean formatted) {
sb.append(formatted ? "\n " : ' ');
return sb;
}
}