1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase;
20
21 import org.apache.hadoop.hbase.classification.InterfaceAudience;
22 import org.apache.hadoop.hbase.classification.InterfaceStability;
23 import org.apache.hadoop.hbase.KeyValue.KVComparator;
24 import org.apache.hadoop.hbase.util.Bytes;
25
26 import java.nio.ByteBuffer;
27 import java.nio.charset.StandardCharsets;
28 import java.util.Arrays;
29 import java.util.Set;
30 import java.util.concurrent.CopyOnWriteArraySet;
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 @InterfaceAudience.Public
57 @InterfaceStability.Evolving
58 public final class TableName implements Comparable<TableName> {
59
60
61 private static final Set<TableName> tableCache = new CopyOnWriteArraySet<TableName>();
62
63
64
65 public final static char NAMESPACE_DELIM = ':';
66
67
68
69
70
71 public static final String VALID_NAMESPACE_REGEX =
72 "(?:[_\\p{Digit}\\p{IsAlphabetic}]+)";
73
74 public static final String VALID_TABLE_QUALIFIER_REGEX =
75 "(?:[_\\p{Digit}\\p{IsAlphabetic}][-_.\\p{Digit}\\p{IsAlphabetic}]*)";
76
77
78 public static final String VALID_USER_TABLE_REGEX =
79 "(?:(?:(?:"+VALID_NAMESPACE_REGEX+"\\"+NAMESPACE_DELIM+")?)" +
80 "(?:"+VALID_TABLE_QUALIFIER_REGEX+"))";
81
82
83 public static final TableName META_TABLE_NAME =
84 valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "meta");
85
86
87 public static final TableName NAMESPACE_TABLE_NAME =
88 valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "namespace");
89
90
91 public static final TableName BACKUP_TABLE_NAME =
92 valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "backup");
93
94 public static final String OLD_META_STR = ".META.";
95 public static final String OLD_ROOT_STR = "-ROOT-";
96
97
98 public static final String DISALLOWED_TABLE_NAME = "zookeeper";
99
100
101
102
103
104
105 public static final TableName OLD_ROOT_TABLE_NAME = getADummyTableName(OLD_ROOT_STR);
106
107
108
109 public static final TableName OLD_META_TABLE_NAME = getADummyTableName(OLD_META_STR);
110
111 private final byte[] name;
112 private final String nameAsString;
113 private final byte[] namespace;
114 private final String namespaceAsString;
115 private final byte[] qualifier;
116 private final String qualifierAsString;
117 private final boolean systemTable;
118 private final int hashCode;
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139 public static byte [] isLegalFullyQualifiedTableName(final byte[] tableName) {
140 if (tableName == null || tableName.length <= 0) {
141 throw new IllegalArgumentException("Name is null or empty");
142 }
143
144 int namespaceDelimIndex = com.google.common.primitives.Bytes.lastIndexOf(tableName,
145 (byte) NAMESPACE_DELIM);
146 if (namespaceDelimIndex < 0){
147 isLegalTableQualifierName(tableName);
148 } else {
149 isLegalNamespaceName(tableName, 0, namespaceDelimIndex);
150 isLegalTableQualifierName(tableName, namespaceDelimIndex + 1, tableName.length);
151 }
152 return tableName;
153 }
154
155 public static byte [] isLegalTableQualifierName(final byte[] qualifierName) {
156 isLegalTableQualifierName(qualifierName, 0, qualifierName.length, false);
157 return qualifierName;
158 }
159
160 public static byte [] isLegalTableQualifierName(final byte[] qualifierName, boolean isSnapshot) {
161 isLegalTableQualifierName(qualifierName, 0, qualifierName.length, isSnapshot);
162 return qualifierName;
163 }
164
165
166
167
168
169
170
171
172
173
174
175 public static void isLegalTableQualifierName(final byte[] qualifierName,
176 int start,
177 int end) {
178 isLegalTableQualifierName(qualifierName, start, end, false);
179 }
180
181 public static void isLegalTableQualifierName(final byte[] qualifierName,
182 int start,
183 int end,
184 boolean isSnapshot) {
185 if(end - start < 1) {
186 throw new IllegalArgumentException(isSnapshot ? "Snapshot" : "Table" + " qualifier must not be empty");
187 }
188
189 if (qualifierName[start] == '.' || qualifierName[start] == '-') {
190 throw new IllegalArgumentException("Illegal first character <" + qualifierName[start] +
191 "> at 0. " + (isSnapshot ? "Snapshot" : "User-space table") +
192 " qualifiers can only start with 'alphanumeric " +
193 "characters' from any language: " +
194 Bytes.toString(qualifierName, start, end));
195 }
196
197 String qualifierString = new String(
198 qualifierName, start, (end - start), StandardCharsets.UTF_8);
199 if (qualifierString.equals(DISALLOWED_TABLE_NAME)) {
200
201
202 throw new IllegalArgumentException("Tables may not be named '" + DISALLOWED_TABLE_NAME + "'");
203 }
204 for (int i = 0; i < qualifierString.length(); i++) {
205
206 char c = qualifierString.charAt(i);
207
208
209
210 if (Character.isAlphabetic(c) || Character.isDigit(c) || c == '_' || c == '-' || c == '.') {
211 continue;
212 }
213 throw new IllegalArgumentException("Illegal character code:" + (int) c + ", <" + c + "> at " +
214 i + ". " + (isSnapshot ? "Snapshot" : "User-space table") +
215 " qualifiers may only contain 'alphanumeric characters' and digits: " +
216 qualifierString);
217 }
218 }
219
220 public static void isLegalNamespaceName(byte[] namespaceName) {
221 isLegalNamespaceName(namespaceName, 0, namespaceName.length);
222 }
223
224
225
226
227 public static void isLegalNamespaceName(final byte[] namespaceName,
228 final int start,
229 final int end) {
230 if(end - start < 1) {
231 throw new IllegalArgumentException("Namespace name must not be empty");
232 }
233 String nsString = new String(namespaceName, start, (end - start), StandardCharsets.UTF_8);
234 if (nsString.equals(DISALLOWED_TABLE_NAME)) {
235
236
237 throw new IllegalArgumentException("Tables may not be named '" + DISALLOWED_TABLE_NAME + "'");
238 }
239 for (int i = 0; i < nsString.length(); i++) {
240
241 char c = nsString.charAt(i);
242
243
244 if (Character.isAlphabetic(c) || Character.isDigit(c)|| c == '_') {
245 continue;
246 }
247 throw new IllegalArgumentException("Illegal character <" + c +
248 "> at " + i + ". Namespaces may only contain " +
249 "'alphanumeric characters' from any language and digits: " + nsString);
250 }
251 }
252
253 public byte[] getName() {
254 return name;
255 }
256
257 public String getNameAsString() {
258 return nameAsString;
259 }
260
261 public byte[] getNamespace() {
262 return namespace;
263 }
264
265 public String getNamespaceAsString() {
266 return namespaceAsString;
267 }
268
269
270
271
272
273
274 public String getNameWithNamespaceInclAsString() {
275 if(getNamespaceAsString().equals(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR)) {
276 return NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR +
277 TableName.NAMESPACE_DELIM + getNameAsString();
278 }
279 return getNameAsString();
280 }
281
282 public byte[] getQualifier() {
283 return qualifier;
284 }
285
286 public String getQualifierAsString() {
287 return qualifierAsString;
288 }
289
290 public byte[] toBytes() {
291 return name;
292 }
293
294 public boolean isSystemTable() {
295 return systemTable;
296 }
297
298 @Override
299 public String toString() {
300 return nameAsString;
301 }
302
303
304
305
306
307 private TableName(ByteBuffer namespace, ByteBuffer qualifier) throws IllegalArgumentException {
308 this.qualifier = new byte[qualifier.remaining()];
309 qualifier.duplicate().get(this.qualifier);
310 this.qualifierAsString = Bytes.toString(this.qualifier);
311
312 if (qualifierAsString.equals(OLD_ROOT_STR)) {
313 throw new IllegalArgumentException(OLD_ROOT_STR + " has been deprecated.");
314 }
315 if (qualifierAsString.equals(OLD_META_STR)) {
316 throw new IllegalArgumentException(OLD_META_STR + " no longer exists. The table has been " +
317 "renamed to " + META_TABLE_NAME);
318 }
319
320 if (Bytes.equals(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME, namespace)) {
321
322 this.namespace = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME;
323 this.namespaceAsString = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR;
324 this.systemTable = false;
325
326
327 this.nameAsString = qualifierAsString;
328 this.name = this.qualifier;
329 } else {
330 if (Bytes.equals(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME, namespace)) {
331 this.namespace = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME;
332 this.namespaceAsString = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR;
333 this.systemTable = true;
334 } else {
335 this.namespace = new byte[namespace.remaining()];
336 namespace.duplicate().get(this.namespace);
337 this.namespaceAsString = Bytes.toString(this.namespace);
338 this.systemTable = false;
339 }
340 this.nameAsString = namespaceAsString + NAMESPACE_DELIM + qualifierAsString;
341 this.name = Bytes.toBytes(nameAsString);
342 }
343
344 this.hashCode = nameAsString.hashCode();
345
346 isLegalNamespaceName(this.namespace);
347 isLegalTableQualifierName(this.qualifier);
348 }
349
350
351
352
353 private TableName(String qualifier) {
354 this.qualifier = Bytes.toBytes(qualifier);
355 this.qualifierAsString = qualifier;
356
357 this.namespace = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME;
358 this.namespaceAsString = NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR;
359 this.systemTable = true;
360
361
362
363 this.nameAsString = namespaceAsString + NAMESPACE_DELIM + qualifierAsString;
364 this.name = this.qualifier;
365
366 this.hashCode = nameAsString.hashCode();
367 }
368
369
370
371
372
373
374
375
376 private static TableName createTableNameIfNecessary(ByteBuffer bns, ByteBuffer qns) {
377 for (TableName tn : tableCache) {
378 if (Bytes.equals(tn.getQualifier(), qns) && Bytes.equals(tn.getNamespace(), bns)) {
379 return tn;
380 }
381 }
382
383 TableName newTable = new TableName(bns, qns);
384 if (tableCache.add(newTable)) {
385 return newTable;
386 }
387
388
389 for (TableName tn : tableCache) {
390 if (Bytes.equals(tn.getQualifier(), qns) && Bytes.equals(tn.getNamespace(), bns)) {
391 return tn;
392 }
393 }
394
395 throw new IllegalStateException(newTable + " was supposed to be in the cache");
396 }
397
398
399
400
401
402
403
404 private static TableName getADummyTableName(String qualifier) {
405 return new TableName(qualifier);
406 }
407
408
409 public static TableName valueOf(String namespaceAsString, String qualifierAsString) {
410 if (namespaceAsString == null || namespaceAsString.length() < 1) {
411 namespaceAsString = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR;
412 }
413
414 for (TableName tn : tableCache) {
415 if (qualifierAsString.equals(tn.getQualifierAsString()) &&
416 namespaceAsString.equals(tn.getNamespaceAsString())) {
417 return tn;
418 }
419 }
420
421 return createTableNameIfNecessary(
422 ByteBuffer.wrap(Bytes.toBytes(namespaceAsString)),
423 ByteBuffer.wrap(Bytes.toBytes(qualifierAsString)));
424 }
425
426
427
428
429
430
431
432 public static TableName valueOf(byte[] fullName) throws IllegalArgumentException{
433 for (TableName tn : tableCache) {
434 if (Arrays.equals(tn.getName(), fullName)) {
435 return tn;
436 }
437 }
438
439 final int namespaceDelimIndex = Bytes.toString(fullName).lastIndexOf(NAMESPACE_DELIM);
440
441 if (namespaceDelimIndex < 0) {
442 return createTableNameIfNecessary(
443 ByteBuffer.wrap(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME),
444 ByteBuffer.wrap(fullName));
445 } else {
446 final String fullNameStr = Bytes.toString(fullName);
447
448 String ns = fullNameStr.substring(0, namespaceDelimIndex);
449 String qualifier = fullNameStr.substring(namespaceDelimIndex + 1);
450 return createTableNameIfNecessary(
451 ByteBuffer.wrap(Bytes.toBytes(ns)),
452 ByteBuffer.wrap(Bytes.toBytes(qualifier)));
453 }
454 }
455
456
457
458
459
460
461 public static TableName valueOf(String name) {
462 for (TableName tn : tableCache) {
463 if (name.equals(tn.getNameAsString())) {
464 return tn;
465 }
466 }
467
468 final int namespaceDelimIndex = name.indexOf(NAMESPACE_DELIM);
469
470 if (namespaceDelimIndex < 0) {
471 return createTableNameIfNecessary(
472 ByteBuffer.wrap(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME),
473 ByteBuffer.wrap(Bytes.toBytes(name)));
474 } else {
475
476 String ns = name.substring(0, namespaceDelimIndex);
477 String qualifier = name.substring(namespaceDelimIndex + 1);
478 return createTableNameIfNecessary(
479 ByteBuffer.wrap(Bytes.toBytes(ns)),
480 ByteBuffer.wrap(Bytes.toBytes(qualifier)));
481 }
482 }
483
484
485 public static TableName valueOf(byte[] namespace, byte[] qualifier) {
486 if (namespace == null || namespace.length < 1) {
487 namespace = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME;
488 }
489
490 for (TableName tn : tableCache) {
491 if (Arrays.equals(tn.getQualifier(), qualifier) &&
492 Arrays.equals(tn.getNamespace(), namespace)) {
493 return tn;
494 }
495 }
496
497 return createTableNameIfNecessary(
498 ByteBuffer.wrap(namespace), ByteBuffer.wrap(qualifier));
499 }
500
501 public static TableName valueOf(ByteBuffer namespace, ByteBuffer qualifier) {
502 if (namespace == null || namespace.remaining() < 1) {
503 return createTableNameIfNecessary(
504 ByteBuffer.wrap(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME), qualifier);
505 }
506
507 return createTableNameIfNecessary(namespace, qualifier);
508 }
509
510 @Override
511 public boolean equals(Object o) {
512 if (this == o) return true;
513 if (o == null || getClass() != o.getClass()) return false;
514
515 TableName tableName = (TableName) o;
516
517 return o.hashCode() == hashCode && nameAsString.equals(tableName.nameAsString);
518 }
519
520 @Override
521 public int hashCode() {
522 return hashCode;
523 }
524
525
526
527
528 @Override
529 public int compareTo(TableName tableName) {
530 if (this == tableName) return 0;
531 if (this.hashCode < tableName.hashCode()) {
532 return -1;
533 }
534 if (this.hashCode > tableName.hashCode()) {
535 return 1;
536 }
537 return this.nameAsString.compareTo(tableName.getNameAsString());
538 }
539
540
541
542
543
544
545
546
547 @InterfaceAudience.Private
548 @Deprecated
549 public KVComparator getRowComparator() {
550 if(TableName.META_TABLE_NAME.equals(this)) {
551 return KeyValue.META_COMPARATOR;
552 }
553 return KeyValue.COMPARATOR;
554 }
555 }