HBASE-6894 Adding metadata to a table in the shell is both arcane and painful
git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1406800 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
e4a99e9794
commit
4ae5cc07c1
|
@ -781,13 +781,15 @@ public class HTableDescriptor implements WritableComparable<HTableDescriptor> {
|
||||||
if (reservedKeys.isEmpty() && configKeys.isEmpty()) return s;
|
if (reservedKeys.isEmpty() && configKeys.isEmpty()) return s;
|
||||||
|
|
||||||
// step 2: printing
|
// step 2: printing
|
||||||
s.append(", {METHOD => 'table_att'");
|
s.append(", {TABLE_ATTRIBUTES => {");
|
||||||
|
|
||||||
// print all reserved keys first
|
// print all reserved keys first
|
||||||
|
boolean printCommaForAttr = false;
|
||||||
for (ImmutableBytesWritable k : reservedKeys) {
|
for (ImmutableBytesWritable k : reservedKeys) {
|
||||||
String key = Bytes.toString(k.get());
|
String key = Bytes.toString(k.get());
|
||||||
String value = Bytes.toString(values.get(k).get());
|
String value = Bytes.toString(values.get(k).get());
|
||||||
s.append(", ");
|
if (printCommaForAttr) s.append(", ");
|
||||||
|
printCommaForAttr = true;
|
||||||
s.append(key);
|
s.append(key);
|
||||||
s.append(" => ");
|
s.append(" => ");
|
||||||
s.append('\'').append(value).append('\'');
|
s.append('\'').append(value).append('\'');
|
||||||
|
@ -795,15 +797,16 @@ public class HTableDescriptor implements WritableComparable<HTableDescriptor> {
|
||||||
|
|
||||||
if (!configKeys.isEmpty()) {
|
if (!configKeys.isEmpty()) {
|
||||||
// print all non-reserved, advanced config keys as a separate subset
|
// print all non-reserved, advanced config keys as a separate subset
|
||||||
s.append(", ");
|
if (printCommaForAttr) s.append(", ");
|
||||||
|
printCommaForAttr = true;
|
||||||
s.append(HConstants.CONFIG).append(" => ");
|
s.append(HConstants.CONFIG).append(" => ");
|
||||||
s.append("{");
|
s.append("{");
|
||||||
boolean printComma = false;
|
boolean printCommaForCfg = false;
|
||||||
for (ImmutableBytesWritable k : configKeys) {
|
for (ImmutableBytesWritable k : configKeys) {
|
||||||
String key = Bytes.toString(k.get());
|
String key = Bytes.toString(k.get());
|
||||||
String value = Bytes.toString(values.get(k).get());
|
String value = Bytes.toString(values.get(k).get());
|
||||||
if (printComma) s.append(", ");
|
if (printCommaForCfg) s.append(", ");
|
||||||
printComma = true;
|
printCommaForCfg = true;
|
||||||
s.append('\'').append(key).append('\'');
|
s.append('\'').append(key).append('\'');
|
||||||
s.append(" => ");
|
s.append(" => ");
|
||||||
s.append('\'').append(value).append('\'');
|
s.append('\'').append(value).append('\'');
|
||||||
|
@ -811,8 +814,7 @@ public class HTableDescriptor implements WritableComparable<HTableDescriptor> {
|
||||||
s.append("}");
|
s.append("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
s.append('}'); // end METHOD
|
s.append("}}"); // end METHOD
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -186,9 +186,7 @@ module Hbase
|
||||||
|
|
||||||
# Flatten params array
|
# Flatten params array
|
||||||
args = args.flatten.compact
|
args = args.flatten.compact
|
||||||
|
has_columns = false
|
||||||
# Fail if no column families defined
|
|
||||||
raise(ArgumentError, "Table must have at least one column family") if args.empty?
|
|
||||||
|
|
||||||
# Start defining the table
|
# Start defining the table
|
||||||
htd = org.apache.hadoop.hbase.HTableDescriptor.new(table_name)
|
htd = org.apache.hadoop.hbase.HTableDescriptor.new(table_name)
|
||||||
|
@ -199,69 +197,70 @@ module Hbase
|
||||||
unless arg.kind_of?(String) || arg.kind_of?(Hash)
|
unless arg.kind_of?(String) || arg.kind_of?(Hash)
|
||||||
raise(ArgumentError, "#{arg.class} of #{arg.inspect} is not of Hash or String type")
|
raise(ArgumentError, "#{arg.class} of #{arg.inspect} is not of Hash or String type")
|
||||||
end
|
end
|
||||||
|
|
||||||
if arg.kind_of?(String)
|
# First, handle all the cases where arg is a column family.
|
||||||
# the arg is a string, default action is to add a column to the table
|
if arg.kind_of?(String) or arg.has_key?(NAME)
|
||||||
|
# If the arg is a string, default action is to add a column to the table.
|
||||||
|
# If arg has a name, it must also be a column descriptor.
|
||||||
htd.addFamily(hcd(arg, htd))
|
htd.addFamily(hcd(arg, htd))
|
||||||
else
|
has_columns = true
|
||||||
# arg is a hash. 4 possibilities:
|
next
|
||||||
if (arg.has_key?(SPLITS) or arg.has_key?(SPLITS_FILE))
|
end
|
||||||
if arg.has_key?(SPLITS_FILE)
|
|
||||||
unless File.exist?(arg[SPLITS_FILE])
|
# Get rid of the "METHOD", which is deprecated for create.
|
||||||
raise(ArgumentError, "Splits file #{arg[SPLITS_FILE]} doesn't exist")
|
# We'll do whatever it used to do below if it's table_att.
|
||||||
end
|
if (method = arg.delete(METHOD))
|
||||||
arg[SPLITS] = []
|
|
||||||
File.foreach(arg[SPLITS_FILE]) do |line|
|
|
||||||
arg[SPLITS].push(line.strip())
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
splits = Java::byte[][arg[SPLITS].size].new
|
|
||||||
idx = 0
|
|
||||||
arg[SPLITS].each do |split|
|
|
||||||
splits[idx] = org.apache.hadoop.hbase.util.Bytes.toBytesBinary(split)
|
|
||||||
idx = idx + 1
|
|
||||||
end
|
|
||||||
elsif (arg.has_key?(NUMREGIONS) or arg.has_key?(SPLITALGO))
|
|
||||||
# (1) deprecated region pre-split API
|
|
||||||
raise(ArgumentError, "Column family configuration should be specified in a separate clause") if arg.has_key?(NAME)
|
|
||||||
raise(ArgumentError, "Number of regions must be specified") unless arg.has_key?(NUMREGIONS)
|
|
||||||
raise(ArgumentError, "Split algorithm must be specified") unless arg.has_key?(SPLITALGO)
|
|
||||||
raise(ArgumentError, "Number of regions must be greater than 1") unless arg[NUMREGIONS] > 1
|
|
||||||
num_regions = arg[NUMREGIONS]
|
|
||||||
split_algo = RegionSplitter.newSplitAlgoInstance(@conf, arg[SPLITALGO])
|
|
||||||
splits = split_algo.split(JInteger.valueOf(num_regions))
|
|
||||||
elsif (method = arg.delete(METHOD))
|
|
||||||
# (2) table_att modification
|
|
||||||
raise(ArgumentError, "table_att is currently the only supported method") unless method == 'table_att'
|
raise(ArgumentError, "table_att is currently the only supported method") unless method == 'table_att'
|
||||||
raise(ArgumentError, "NUMREGIONS & SPLITALGO must both be specified") unless arg.has_key?(NUMREGIONS) == arg.has_key?(split_algo)
|
end
|
||||||
htd.setMaxFileSize(JLong.valueOf(arg[MAX_FILESIZE])) if arg[MAX_FILESIZE]
|
|
||||||
htd.setReadOnly(JBoolean.valueOf(arg[READONLY])) if arg[READONLY]
|
# The hash is not a column family. Figure out what's in it.
|
||||||
htd.setMemStoreFlushSize(JLong.valueOf(arg[MEMSTORE_FLUSHSIZE])) if arg[MEMSTORE_FLUSHSIZE]
|
# First, handle splits.
|
||||||
htd.setDeferredLogFlush(JBoolean.valueOf(arg[DEFERRED_LOG_FLUSH])) if arg[DEFERRED_LOG_FLUSH]
|
if arg.has_key?(SPLITS_FILE)
|
||||||
htd.setValue(COMPRESSION_COMPACT, arg[COMPRESSION_COMPACT]) if arg[COMPRESSION_COMPACT]
|
splits_file = arg.delete(SPLITS_FILE)
|
||||||
if arg[NUMREGIONS]
|
unless File.exist?(splits_file)
|
||||||
raise(ArgumentError, "Number of regions must be greater than 1") unless arg[NUMREGIONS] > 1
|
raise(ArgumentError, "Splits file #{splits_file} doesn't exist")
|
||||||
num_regions = arg[NUMREGIONS]
|
end
|
||||||
split_algo = RegionSplitter.newSplitAlgoInstance(@conf, arg[SPLITALGO])
|
arg[SPLITS] = []
|
||||||
splits = split_algo.split(JInteger.valueOf(num_regions))
|
File.foreach(splits_file) do |line|
|
||||||
end
|
arg[SPLITS].push(line.strip())
|
||||||
if arg[CONFIG]
|
|
||||||
raise(ArgumentError, "#{CONFIG} must be a Hash type") unless arg.kind_of?(Hash)
|
|
||||||
for k,v in arg[CONFIG]
|
|
||||||
v = v.to_s unless v.nil?
|
|
||||||
htd.setValue(k, v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
# (3) column family spec
|
|
||||||
descriptor = hcd(arg, htd)
|
|
||||||
htd.setValue(COMPRESSION_COMPACT, arg[COMPRESSION_COMPACT]) if arg[COMPRESSION_COMPACT]
|
|
||||||
htd.addFamily(hcd(arg, htd))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
|
if arg.has_key?(SPLITS)
|
||||||
|
splits = Java::byte[][arg[SPLITS].size].new
|
||||||
|
idx = 0
|
||||||
|
arg.delete(SPLITS).each do |split|
|
||||||
|
splits[idx] = org.apache.hadoop.hbase.util.Bytes.toBytesBinary(split)
|
||||||
|
idx = idx + 1
|
||||||
|
end
|
||||||
|
elsif arg.has_key?(NUMREGIONS) or arg.has_key?(SPLITALGO)
|
||||||
|
# deprecated region pre-split API; if one of the above is specified, will be ignored.
|
||||||
|
raise(ArgumentError, "Number of regions must be specified") unless arg.has_key?(NUMREGIONS)
|
||||||
|
raise(ArgumentError, "Split algorithm must be specified") unless arg.has_key?(SPLITALGO)
|
||||||
|
raise(ArgumentError, "Number of regions must be greater than 1") unless arg[NUMREGIONS] > 1
|
||||||
|
num_regions = arg.delete(NUMREGIONS)
|
||||||
|
split_algo = RegionSplitter.newSplitAlgoInstance(@conf, arg.delete(SPLITALGO))
|
||||||
|
splits = split_algo.split(JInteger.valueOf(num_regions))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Done with splits; apply formerly-table_att parameters.
|
||||||
|
htd.setOwnerString(arg.delete(OWNER)) if arg[OWNER]
|
||||||
|
htd.setMaxFileSize(JLong.valueOf(arg.delete(MAX_FILESIZE))) if arg[MAX_FILESIZE]
|
||||||
|
htd.setReadOnly(JBoolean.valueOf(arg.delete(READONLY))) if arg[READONLY]
|
||||||
|
htd.setMemStoreFlushSize(JLong.valueOf(arg.delete(MEMSTORE_FLUSHSIZE))) if arg[MEMSTORE_FLUSHSIZE]
|
||||||
|
htd.setDeferredLogFlush(JBoolean.valueOf(arg.delete(DEFERRED_LOG_FLUSH))) if arg[DEFERRED_LOG_FLUSH]
|
||||||
|
if arg[CONFIG]
|
||||||
|
apply_config(htd, arg.delete(CONFIG))
|
||||||
|
end
|
||||||
|
|
||||||
|
arg.each_key do |ignored_key|
|
||||||
|
puts("An argument ignored (unknown or overridden): %s" % [ ignored_key ])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fail if no column families defined
|
||||||
|
raise(ArgumentError, "Table must have at least one column family") if !has_columns
|
||||||
|
|
||||||
if splits.nil?
|
if splits.nil?
|
||||||
# Perform the create table call
|
# Perform the create table call
|
||||||
@admin.createTable(htd)
|
@admin.createTable(htd)
|
||||||
|
@ -368,133 +367,130 @@ module Hbase
|
||||||
|
|
||||||
# Process all args
|
# Process all args
|
||||||
args.each do |arg|
|
args.each do |arg|
|
||||||
|
|
||||||
|
|
||||||
# Normalize args to support column name only alter specs
|
# Normalize args to support column name only alter specs
|
||||||
arg = { NAME => arg } if arg.kind_of?(String)
|
arg = { NAME => arg } if arg.kind_of?(String)
|
||||||
|
|
||||||
# Normalize args to support shortcut delete syntax
|
# Normalize args to support shortcut delete syntax
|
||||||
arg = { METHOD => 'delete', NAME => arg['delete'] } if arg['delete']
|
arg = { METHOD => 'delete', NAME => arg['delete'] } if arg['delete']
|
||||||
|
|
||||||
# No method parameter, try to use the args as a column definition
|
# There are 3 possible options.
|
||||||
unless method = arg.delete(METHOD)
|
# 1) Column family spec. Distinguished by having a NAME and no METHOD.
|
||||||
# Note that we handle owner here, and also below (see (2)) as part of the "METHOD => 'table_att'" table attributes.
|
method = arg.delete(METHOD)
|
||||||
# In other words, if OWNER is specified, then METHOD is set to table_att.
|
if method == nil and arg.has_key?(NAME)
|
||||||
# alter 'tablename', {OWNER => 'username'} (that is, METHOD => 'table_att' is not specified).
|
|
||||||
if arg[OWNER]
|
|
||||||
htd.setOwnerString(arg[OWNER])
|
|
||||||
@admin.modifyTable(table_name.to_java_bytes, htd)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
descriptor = hcd(arg, htd)
|
descriptor = hcd(arg, htd)
|
||||||
|
|
||||||
if arg[COMPRESSION_COMPACT]
|
|
||||||
descriptor.setValue(COMPRESSION_COMPACT, arg[COMPRESSION_COMPACT])
|
|
||||||
end
|
|
||||||
column_name = descriptor.getNameAsString
|
column_name = descriptor.getNameAsString
|
||||||
|
|
||||||
# If column already exist, then try to alter it. Create otherwise.
|
# If column already exist, then try to alter it. Create otherwise.
|
||||||
if htd.hasFamily(column_name.to_java_bytes)
|
if htd.hasFamily(column_name.to_java_bytes)
|
||||||
@admin.modifyColumn(table_name, descriptor)
|
@admin.modifyColumn(table_name, descriptor)
|
||||||
if wait == true
|
|
||||||
puts "Updating all regions with the new schema..."
|
|
||||||
alter_status(table_name)
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
@admin.addColumn(table_name, descriptor)
|
@admin.addColumn(table_name, descriptor)
|
||||||
if wait == true
|
|
||||||
puts "Updating all regions with the new schema..."
|
|
||||||
alter_status(table_name)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
# Delete column family
|
|
||||||
if method == "delete"
|
|
||||||
raise(ArgumentError, "NAME parameter missing for delete method") unless arg[NAME]
|
|
||||||
@admin.deleteColumn(table_name, arg[NAME])
|
|
||||||
if wait == true
|
if wait == true
|
||||||
puts "Updating all regions with the new schema..."
|
puts "Updating all regions with the new schema..."
|
||||||
alter_status(table_name)
|
alter_status(table_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# We bypass descriptor when adding column families; refresh it to apply other args correctly.
|
||||||
|
htd = @admin.getTableDescriptor(table_name.to_java_bytes)
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
# Change table attributes
|
# 2) Method other than table_att, with some args.
|
||||||
if method == "table_att"
|
name = arg.delete(NAME)
|
||||||
htd.setMaxFileSize(JLong.valueOf(arg[MAX_FILESIZE])) if arg[MAX_FILESIZE]
|
if method != nil and method != "table_att"
|
||||||
htd.setReadOnly(JBoolean.valueOf(arg[READONLY])) if arg[READONLY]
|
# Delete column family
|
||||||
htd.setMemStoreFlushSize(JLong.valueOf(arg[MEMSTORE_FLUSHSIZE])) if arg[MEMSTORE_FLUSHSIZE]
|
if method == "delete"
|
||||||
htd.setDeferredLogFlush(JBoolean.valueOf(arg[DEFERRED_LOG_FLUSH])) if arg[DEFERRED_LOG_FLUSH]
|
raise(ArgumentError, "NAME parameter missing for delete method") unless name
|
||||||
# (2) Here, we handle the alternate syntax of ownership setting, where method => 'table_att' is specified.
|
@admin.deleteColumn(table_name, name)
|
||||||
htd.setOwnerString(arg[OWNER]) if arg[OWNER]
|
# Unset table attributes
|
||||||
|
elsif method == "table_att_unset"
|
||||||
# set a coprocessor attribute
|
raise(ArgumentError, "NAME parameter missing for table_att_unset method") unless name
|
||||||
if arg.kind_of?(Hash)
|
if (htd.getValue(name) == nil)
|
||||||
arg.each do |key, value|
|
raise ArgumentError, "Can not find attribute: #{name}"
|
||||||
k = String.new(key) # prepare to strip
|
|
||||||
k.strip!
|
|
||||||
|
|
||||||
if (k =~ /coprocessor/i)
|
|
||||||
# validate coprocessor specs
|
|
||||||
v = String.new(value)
|
|
||||||
v.strip!
|
|
||||||
if !(v =~ /^([^\|]*)\|([^\|]+)\|[\s]*([\d]*)[\s]*(\|.*)?$/)
|
|
||||||
raise ArgumentError, "Coprocessor value doesn't match spec: #{v}"
|
|
||||||
end
|
|
||||||
|
|
||||||
# generate a coprocessor ordinal by checking max id of existing cps
|
|
||||||
maxId = 0
|
|
||||||
htd.getValues().each do |k1, v1|
|
|
||||||
attrName = org.apache.hadoop.hbase.util.Bytes.toString(k1.get())
|
|
||||||
# a cp key is coprocessor$(\d)
|
|
||||||
if (attrName =~ /coprocessor\$(\d+)/i)
|
|
||||||
ids = attrName.scan(/coprocessor\$(\d+)/i)
|
|
||||||
maxId = ids[0][0].to_i if ids[0][0].to_i > maxId
|
|
||||||
end
|
|
||||||
end
|
|
||||||
maxId += 1
|
|
||||||
htd.setValue(k + "\$" + maxId.to_s, value)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
htd.remove(name.to_java_bytes)
|
||||||
|
|
||||||
if arg[CONFIG]
|
|
||||||
raise(ArgumentError, "#{CONFIG} must be a Hash type") unless arg.kind_of?(Hash)
|
|
||||||
for k,v in arg[CONFIG]
|
|
||||||
v = v.to_s unless v.nil?
|
|
||||||
htd.setValue(k, v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@admin.modifyTable(table_name.to_java_bytes, htd)
|
|
||||||
if wait == true
|
|
||||||
puts "Updating all regions with the new schema..."
|
|
||||||
alter_status(table_name)
|
|
||||||
end
|
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
# Unset table attributes
|
|
||||||
if method == "table_att_unset"
|
|
||||||
if arg.kind_of?(Hash)
|
|
||||||
if (!arg[NAME])
|
|
||||||
next
|
|
||||||
end
|
|
||||||
if (htd.getValue(arg[NAME]) == nil)
|
|
||||||
raise ArgumentError, "Can not find attribute: #{arg[NAME]}"
|
|
||||||
end
|
|
||||||
htd.remove(arg[NAME].to_java_bytes)
|
|
||||||
@admin.modifyTable(table_name.to_java_bytes, htd)
|
@admin.modifyTable(table_name.to_java_bytes, htd)
|
||||||
if wait == true
|
# Unknown method
|
||||||
puts "Updating all regions with the new schema..."
|
else
|
||||||
alter_status(table_name)
|
raise ArgumentError, "Unknown method: #{method}"
|
||||||
|
end
|
||||||
|
|
||||||
|
arg.each_key do |unknown_key|
|
||||||
|
puts("Unknown argument ignored: %s" % [unknown_key])
|
||||||
|
end
|
||||||
|
|
||||||
|
if wait == true
|
||||||
|
puts "Updating all regions with the new schema..."
|
||||||
|
alter_status(table_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
if method == "delete"
|
||||||
|
# We bypass descriptor when deleting column families; refresh it to apply other args correctly.
|
||||||
|
htd = @admin.getTableDescriptor(table_name.to_java_bytes)
|
||||||
|
end
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
# 3) Some args for the table, optionally with METHOD => table_att (deprecated)
|
||||||
|
raise(ArgumentError, "NAME argument in an unexpected place") if name
|
||||||
|
htd.setOwnerString(arg.delete(OWNER)) if arg[OWNER]
|
||||||
|
apply_config(htd, arg.delete(CONFIG)) if arg[CONFIG]
|
||||||
|
htd.setMaxFileSize(JLong.valueOf(arg.delete(MAX_FILESIZE))) if arg[MAX_FILESIZE]
|
||||||
|
htd.setReadOnly(JBoolean.valueOf(arg.delete(READONLY))) if arg[READONLY]
|
||||||
|
htd.setMemStoreFlushSize(JLong.valueOf(arg.delete(MEMSTORE_FLUSHSIZE))) if arg[MEMSTORE_FLUSHSIZE]
|
||||||
|
htd.setDeferredLogFlush(JBoolean.valueOf(arg.delete(DEFERRED_LOG_FLUSH))) if arg[DEFERRED_LOG_FLUSH]
|
||||||
|
|
||||||
|
# set a coprocessor attribute
|
||||||
|
valid_coproc_keys = []
|
||||||
|
if arg.kind_of?(Hash)
|
||||||
|
arg.each do |key, value|
|
||||||
|
k = String.new(key) # prepare to strip
|
||||||
|
k.strip!
|
||||||
|
|
||||||
|
if (k =~ /coprocessor/i)
|
||||||
|
# validate coprocessor specs
|
||||||
|
v = String.new(value)
|
||||||
|
v.strip!
|
||||||
|
if !(v =~ /^([^\|]*)\|([^\|]+)\|[\s]*([\d]*)[\s]*(\|.*)?$/)
|
||||||
|
raise ArgumentError, "Coprocessor value doesn't match spec: #{v}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# generate a coprocessor ordinal by checking max id of existing cps
|
||||||
|
maxId = 0
|
||||||
|
htd.getValues().each do |k1, v1|
|
||||||
|
attrName = org.apache.hadoop.hbase.util.Bytes.toString(k1.get())
|
||||||
|
# a cp key is coprocessor$(\d)
|
||||||
|
if (attrName =~ /coprocessor\$(\d+)/i)
|
||||||
|
ids = attrName.scan(/coprocessor\$(\d+)/i)
|
||||||
|
maxId = ids[0][0].to_i if ids[0][0].to_i > maxId
|
||||||
|
end
|
||||||
|
end
|
||||||
|
maxId += 1
|
||||||
|
htd.setValue(k + "\$" + maxId.to_s, value)
|
||||||
|
valid_coproc_keys << key
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
valid_coproc_keys.each do |key|
|
||||||
|
arg.delete(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
@admin.modifyTable(table_name.to_java_bytes, htd)
|
||||||
|
|
||||||
|
arg.each_key do |unknown_key|
|
||||||
|
puts("Unknown argument ignored: %s" % [unknown_key])
|
||||||
|
end
|
||||||
|
|
||||||
|
if wait == true
|
||||||
|
puts "Updating all regions with the new schema..."
|
||||||
|
alter_status(table_name)
|
||||||
|
end
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
# Unknown method
|
|
||||||
raise ArgumentError, "Unknown method: #{method}"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -555,7 +551,7 @@ module Hbase
|
||||||
def exists?(table_name)
|
def exists?(table_name)
|
||||||
@admin.tableExists(table_name)
|
@admin.tableExists(table_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
#----------------------------------------------------------------------------------------------
|
#----------------------------------------------------------------------------------------------
|
||||||
# Is table enabled
|
# Is table enabled
|
||||||
def enabled?(table_name)
|
def enabled?(table_name)
|
||||||
|
@ -574,24 +570,25 @@ module Hbase
|
||||||
# String arg, single parameter constructor
|
# String arg, single parameter constructor
|
||||||
return org.apache.hadoop.hbase.HColumnDescriptor.new(arg) if arg.kind_of?(String)
|
return org.apache.hadoop.hbase.HColumnDescriptor.new(arg) if arg.kind_of?(String)
|
||||||
|
|
||||||
raise(ArgumentError, "Column family #{arg} must have a name") unless name = arg[NAME]
|
raise(ArgumentError, "Column family #{arg} must have a name") unless name = arg.delete(NAME)
|
||||||
|
|
||||||
family = htd.getFamily(name.to_java_bytes)
|
family = htd.getFamily(name.to_java_bytes)
|
||||||
# create it if it's a new family
|
# create it if it's a new family
|
||||||
family ||= org.apache.hadoop.hbase.HColumnDescriptor.new(name.to_java_bytes)
|
family ||= org.apache.hadoop.hbase.HColumnDescriptor.new(name.to_java_bytes)
|
||||||
|
|
||||||
family.setBlockCacheEnabled(JBoolean.valueOf(arg[org.apache.hadoop.hbase.HColumnDescriptor::BLOCKCACHE])) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::BLOCKCACHE)
|
family.setBlockCacheEnabled(JBoolean.valueOf(arg.delete(org.apache.hadoop.hbase.HColumnDescriptor::BLOCKCACHE))) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::BLOCKCACHE)
|
||||||
family.setScope(JInteger.valueOf(arg[org.apache.hadoop.hbase.HColumnDescriptor::REPLICATION_SCOPE])) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::REPLICATION_SCOPE)
|
family.setScope(JInteger.valueOf(arg.delete(org.apache.hadoop.hbase.HColumnDescriptor::REPLICATION_SCOPE))) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::REPLICATION_SCOPE)
|
||||||
family.setInMemory(JBoolean.valueOf(arg[org.apache.hadoop.hbase.HColumnDescriptor::IN_MEMORY])) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::IN_MEMORY)
|
family.setInMemory(JBoolean.valueOf(arg.delete(org.apache.hadoop.hbase.HColumnDescriptor::IN_MEMORY))) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::IN_MEMORY)
|
||||||
family.setTimeToLive(JInteger.valueOf(arg[org.apache.hadoop.hbase.HColumnDescriptor::TTL])) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::TTL)
|
family.setTimeToLive(JInteger.valueOf(arg.delete(org.apache.hadoop.hbase.HColumnDescriptor::TTL))) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::TTL)
|
||||||
family.setDataBlockEncoding(org.apache.hadoop.hbase.io.encoding.DataBlockEncoding.valueOf(arg[org.apache.hadoop.hbase.HColumnDescriptor::DATA_BLOCK_ENCODING])) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::DATA_BLOCK_ENCODING)
|
family.setDataBlockEncoding(org.apache.hadoop.hbase.io.encoding.DataBlockEncoding.valueOf(arg.delete(org.apache.hadoop.hbase.HColumnDescriptor::DATA_BLOCK_ENCODING))) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::DATA_BLOCK_ENCODING)
|
||||||
family.setEncodeOnDisk(JBoolean.valueOf(arg[org.apache.hadoop.hbase.HColumnDescriptor::ENCODE_ON_DISK])) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::ENCODE_ON_DISK)
|
family.setEncodeOnDisk(JBoolean.valueOf(arg.delete(org.apache.hadoop.hbase.HColumnDescriptor::ENCODE_ON_DISK))) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::ENCODE_ON_DISK)
|
||||||
family.setBlocksize(JInteger.valueOf(arg[org.apache.hadoop.hbase.HColumnDescriptor::BLOCKSIZE])) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::BLOCKSIZE)
|
family.setBlocksize(JInteger.valueOf(arg.delete(org.apache.hadoop.hbase.HColumnDescriptor::BLOCKSIZE))) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::BLOCKSIZE)
|
||||||
family.setMaxVersions(JInteger.valueOf(arg[org.apache.hadoop.hbase.HColumnDescriptor::VERSIONS])) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::VERSIONS)
|
family.setMaxVersions(JInteger.valueOf(arg.delete(org.apache.hadoop.hbase.HColumnDescriptor::VERSIONS))) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::VERSIONS)
|
||||||
family.setMinVersions(JInteger.valueOf(arg[org.apache.hadoop.hbase.HColumnDescriptor::MIN_VERSIONS])) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::MIN_VERSIONS)
|
family.setMinVersions(JInteger.valueOf(arg.delete(org.apache.hadoop.hbase.HColumnDescriptor::MIN_VERSIONS))) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::MIN_VERSIONS)
|
||||||
family.setKeepDeletedCells(JBoolean.valueOf(arg[org.apache.hadoop.hbase.HColumnDescriptor::KEEP_DELETED_CELLS])) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::KEEP_DELETED_CELLS)
|
family.setKeepDeletedCells(JBoolean.valueOf(arg.delete(org.apache.hadoop.hbase.HColumnDescriptor::KEEP_DELETED_CELLS))) if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::KEEP_DELETED_CELLS)
|
||||||
|
family.setValue(COMPRESSION_COMPACT, arg.delete(COMPRESSION_COMPACT)) if arg.include?(COMPRESSION_COMPACT)
|
||||||
if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::BLOOMFILTER)
|
if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::BLOOMFILTER)
|
||||||
bloomtype = arg[org.apache.hadoop.hbase.HColumnDescriptor::BLOOMFILTER].upcase
|
bloomtype = arg.delete(org.apache.hadoop.hbase.HColumnDescriptor::BLOOMFILTER).upcase
|
||||||
unless org.apache.hadoop.hbase.regionserver.StoreFile::BloomType.constants.include?(bloomtype)
|
unless org.apache.hadoop.hbase.regionserver.StoreFile::BloomType.constants.include?(bloomtype)
|
||||||
raise(ArgumentError, "BloomFilter type #{bloomtype} is not supported. Use one of " + org.apache.hadoop.hbase.regionserver.StoreFile::BloomType.constants.join(" "))
|
raise(ArgumentError, "BloomFilter type #{bloomtype} is not supported. Use one of " + org.apache.hadoop.hbase.regionserver.StoreFile::BloomType.constants.join(" "))
|
||||||
else
|
else
|
||||||
|
@ -599,7 +596,7 @@ module Hbase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::COMPRESSION)
|
if arg.include?(org.apache.hadoop.hbase.HColumnDescriptor::COMPRESSION)
|
||||||
compression = arg[org.apache.hadoop.hbase.HColumnDescriptor::COMPRESSION].upcase
|
compression = arg.delete(org.apache.hadoop.hbase.HColumnDescriptor::COMPRESSION).upcase
|
||||||
unless org.apache.hadoop.hbase.io.hfile.Compression::Algorithm.constants.include?(compression)
|
unless org.apache.hadoop.hbase.io.hfile.Compression::Algorithm.constants.include?(compression)
|
||||||
raise(ArgumentError, "Compression #{compression} is not supported. Use one of " + org.apache.hadoop.hbase.io.hfile.Compression::Algorithm.constants.join(" "))
|
raise(ArgumentError, "Compression #{compression} is not supported. Use one of " + org.apache.hadoop.hbase.io.hfile.Compression::Algorithm.constants.join(" "))
|
||||||
else
|
else
|
||||||
|
@ -607,13 +604,14 @@ module Hbase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if arg[CONFIG]
|
if config = arg.delete(CONFIG)
|
||||||
raise(ArgumentError, "#{CONFIG} must be a Hash type") unless arg.kind_of?(Hash)
|
apply_config(family, config)
|
||||||
for k,v in arg[CONFIG]
|
|
||||||
v = v.to_s unless v.nil?
|
|
||||||
family.setValue(k, v)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
arg.each_key do |unknown_key|
|
||||||
|
puts("Unknown argument ignored for column family %s: %s" % [name, unknown_key])
|
||||||
|
end
|
||||||
|
|
||||||
return family
|
return family
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -639,5 +637,15 @@ module Hbase
|
||||||
put.add(org.apache.hadoop.hbase.HConstants::CATALOG_FAMILY, org.apache.hadoop.hbase.HConstants::REGIONINFO_QUALIFIER, org.apache.hadoop.hbase.util.Writables.getBytes(hri))
|
put.add(org.apache.hadoop.hbase.HConstants::CATALOG_FAMILY, org.apache.hadoop.hbase.HConstants::REGIONINFO_QUALIFIER, org.apache.hadoop.hbase.util.Writables.getBytes(hri))
|
||||||
meta.put(put)
|
meta.put(put)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Apply config to table/column descriptor
|
||||||
|
def apply_config(descriptor, config)
|
||||||
|
raise(ArgumentError, "#{CONFIG} must be a Hash type") unless config.kind_of?(Hash)
|
||||||
|
for k,v in config
|
||||||
|
v = v.to_s unless v.nil?
|
||||||
|
descriptor.setValue(k, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,34 +22,35 @@ module Shell
|
||||||
class Alter < Command
|
class Alter < Command
|
||||||
def help
|
def help
|
||||||
return <<-EOF
|
return <<-EOF
|
||||||
Alter column family schema; pass table name and a dictionary
|
Alter a table. Table must be disabled to be altered (see help 'disable').
|
||||||
specifying new column family schema. Dictionaries are described
|
You can add/modify/delete column families, as well as change table
|
||||||
on the main help command output. Dictionary must include name
|
configuration. Column families work similarly to create; column family
|
||||||
of column family to alter. For example,
|
spec can either be a name string, or a dictionary with NAME attribute.
|
||||||
|
Dictionaries are described on the main help command output.
|
||||||
|
|
||||||
To change or add the 'f1' column family in table 't1' from defaults
|
For example, to change or add the 'f1' column family in table 't1' from
|
||||||
to instead keep a maximum of 5 cell VERSIONS, do:
|
current value to keep a maximum of 5 cell VERSIONS, do:
|
||||||
|
|
||||||
hbase> alter 't1', NAME => 'f1', VERSIONS => 5
|
hbase> alter 't1', NAME => 'f1', VERSIONS => 5
|
||||||
|
|
||||||
To delete the 'f1' column family in table 't1', do:
|
You can operate on several column families:
|
||||||
|
|
||||||
|
hbase> alter 't1', 'f1', {NAME => 'f2', IN_MEMORY => true}, {NAME => 'f3', VERSIONS => 5}
|
||||||
|
|
||||||
|
To delete the 'f1' column family in table 't1', use one of:
|
||||||
|
|
||||||
hbase> alter 't1', NAME => 'f1', METHOD => 'delete'
|
hbase> alter 't1', NAME => 'f1', METHOD => 'delete'
|
||||||
|
|
||||||
or a shorter version:
|
|
||||||
|
|
||||||
hbase> alter 't1', 'delete' => 'f1'
|
hbase> alter 't1', 'delete' => 'f1'
|
||||||
|
|
||||||
You can also change table-scope attributes like MAX_FILESIZE
|
You can also change table-scope attributes like MAX_FILESIZE, READONLY,
|
||||||
MEMSTORE_FLUSHSIZE, READONLY, and DEFERRED_LOG_FLUSH.
|
MEMSTORE_FLUSHSIZE, DEFERRED_LOG_FLUSH, etc. These can be put at the end;
|
||||||
|
for example, to change the max size of a region to 128MB, do:
|
||||||
|
|
||||||
For example, to change the max size of a family to 128MB, do:
|
hbase> alter 't1', MAX_FILESIZE => '134217728'
|
||||||
|
|
||||||
hbase> alter 't1', METHOD => 'table_att', MAX_FILESIZE => '134217728'
|
|
||||||
|
|
||||||
You can add a table coprocessor by setting a table coprocessor attribute:
|
You can add a table coprocessor by setting a table coprocessor attribute:
|
||||||
|
|
||||||
hbase> alter 't1', METHOD => 'table_att',
|
hbase> alter 't1',
|
||||||
'coprocessor'=>'hdfs:///foo.jar|com.foo.FooRegionObserver|1001|arg1=1,arg2=2'
|
'coprocessor'=>'hdfs:///foo.jar|com.foo.FooRegionObserver|1001|arg1=1,arg2=2'
|
||||||
|
|
||||||
Since you can have multiple coprocessors configured for a table, a
|
Since you can have multiple coprocessors configured for a table, a
|
||||||
|
@ -69,7 +70,9 @@ You can also remove a table-scope attribute:
|
||||||
|
|
||||||
There could be more than one alteration in one command:
|
There could be more than one alteration in one command:
|
||||||
|
|
||||||
hbase> alter 't1', {NAME => 'f1'}, {NAME => 'f2', METHOD => 'delete'}
|
hbase> alter 't1', { NAME => 'f1', VERSIONS => 3 },
|
||||||
|
{ MAX_FILESIZE => '134217728' }, { METHOD => 'delete', NAME => 'f2' },
|
||||||
|
OWNER => 'johndoe', CONFIG => { 'mykey' => 'myvalue' }
|
||||||
EOF
|
EOF
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,11 @@ module Shell
|
||||||
class Create < Command
|
class Create < Command
|
||||||
def help
|
def help
|
||||||
return <<-EOF
|
return <<-EOF
|
||||||
Create table; pass table name, a dictionary of specifications per
|
Creates a table. Pass a table name, and a set of column family
|
||||||
column family, and optionally a dictionary of table configuration.
|
specifications (at least one), and, optionally, table configuration.
|
||||||
Dictionaries are described below in the GENERAL NOTES section.
|
Column specification can be a simple string (name), or a dictionary
|
||||||
|
(dictionaries are described below in main help output), necessarily
|
||||||
|
including NAME attribute.
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
hbase> create 't1', {NAME => 'f1', VERSIONS => 5}
|
hbase> create 't1', {NAME => 'f1', VERSIONS => 5}
|
||||||
|
@ -32,18 +34,23 @@ Examples:
|
||||||
hbase> # The above in shorthand would be the following:
|
hbase> # The above in shorthand would be the following:
|
||||||
hbase> create 't1', 'f1', 'f2', 'f3'
|
hbase> create 't1', 'f1', 'f2', 'f3'
|
||||||
hbase> create 't1', {NAME => 'f1', VERSIONS => 1, TTL => 2592000, BLOCKCACHE => true}
|
hbase> create 't1', {NAME => 'f1', VERSIONS => 1, TTL => 2592000, BLOCKCACHE => true}
|
||||||
hbase> create 't1', 'f1', {SPLITS => ['10', '20', '30', '40']}
|
|
||||||
hbase> create 't1', 'f1', {SPLITS_FILE => 'splits.txt'}
|
Table configuration options can be put at the end.
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
hbase> create 't1', 'f1', SPLITS => ['10', '20', '30', '40']
|
||||||
|
hbase> create 't1', 'f1', SPLITS_FILE => 'splits.txt', OWNER => 'johndoe'
|
||||||
|
hbase> create 't1', {NAME => 'f1', VERSIONS => 5}, CONFIG => { 'mykey' => 'myvalue' }
|
||||||
hbase> # Optionally pre-split the table into NUMREGIONS, using
|
hbase> # Optionally pre-split the table into NUMREGIONS, using
|
||||||
hbase> # SPLITALGO ("HexStringSplit", "UniformSplit" or classname)
|
hbase> # SPLITALGO ("HexStringSplit", "UniformSplit" or classname)
|
||||||
hbase> create 't1', 'f1', {NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
|
hbase> create 't1', 'f1', {NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
|
||||||
|
|
||||||
You can also keep around a reference to the created table:
|
You can also keep around a reference to the created table:
|
||||||
|
|
||||||
hbase> t1 = create 't1', 'f1'
|
hbase> t1 = create 't1', 'f1'
|
||||||
|
|
||||||
Which gives you a reference to the table named 't1', on which you can then
|
Which gives you a reference to the table named 't1', on which you can then
|
||||||
call methods.
|
call methods.
|
||||||
EOF
|
EOF
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -148,6 +148,13 @@ module Hbase
|
||||||
admin.create(@create_test_name)
|
admin.create(@create_test_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
define_test "create should fail without columns when called with options" do
|
||||||
|
drop_test_table(@create_test_name)
|
||||||
|
assert_raise(ArgumentError) do
|
||||||
|
admin.create(@create_test_name, { OWNER => 'a' })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
define_test "create should work with string column args" do
|
define_test "create should work with string column args" do
|
||||||
drop_test_table(@create_test_name)
|
drop_test_table(@create_test_name)
|
||||||
|
@ -160,12 +167,28 @@ module Hbase
|
||||||
admin.create(@create_test_name, { NAME => 'a'}, { NAME => 'b'})
|
admin.create(@create_test_name, { NAME => 'a'}, { NAME => 'b'})
|
||||||
assert_equal(['a:', 'b:'], table(@create_test_name).get_all_columns.sort)
|
assert_equal(['a:', 'b:'], table(@create_test_name).get_all_columns.sort)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
define_test "create should be able to set table options" do
|
||||||
|
drop_test_table(@create_test_name)
|
||||||
|
admin.create(@create_test_name, 'a', 'b', 'MAX_FILESIZE' => 12345678, OWNER => '987654321')
|
||||||
|
assert_equal(['a:', 'b:'], table(@create_test_name).get_all_columns.sort)
|
||||||
|
assert_match(/12345678/, admin.describe(@create_test_name))
|
||||||
|
assert_match(/987654321/, admin.describe(@create_test_name))
|
||||||
|
end
|
||||||
|
|
||||||
|
define_test "create should ignore table_att" do
|
||||||
|
drop_test_table(@create_test_name)
|
||||||
|
admin.create(@create_test_name, 'a', 'b', METHOD => 'table_att', OWNER => '987654321')
|
||||||
|
assert_equal(['a:', 'b:'], table(@create_test_name).get_all_columns.sort)
|
||||||
|
assert_match(/987654321/, admin.describe(@create_test_name))
|
||||||
|
end
|
||||||
|
|
||||||
define_test "create should work with SPLITALGO" do
|
define_test "create should work with SPLITALGO" do
|
||||||
drop_test_table(@create_test_name)
|
drop_test_table(@create_test_name)
|
||||||
admin.create(@create_test_name, 'a', 'b', {NUMREGIONS => 10, SPLITALGO => 'HexStringSplit'})
|
admin.create(@create_test_name, 'a', 'b', {NUMREGIONS => 10, SPLITALGO => 'HexStringSplit'})
|
||||||
assert_equal(['a:', 'b:'], table(@create_test_name).get_all_columns.sort)
|
assert_equal(['a:', 'b:'], table(@create_test_name).get_all_columns.sort)
|
||||||
end
|
end
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
define_test "describe should fail for non-existent tables" do
|
define_test "describe should fail for non-existent tables" do
|
||||||
|
@ -253,9 +276,10 @@ module Hbase
|
||||||
|
|
||||||
define_test "alter should support more than one alteration in one call" do
|
define_test "alter should support more than one alteration in one call" do
|
||||||
assert_equal(['x:', 'y:'], table(@test_name).get_all_columns.sort)
|
assert_equal(['x:', 'y:'], table(@test_name).get_all_columns.sort)
|
||||||
admin.alter(@test_name, true, { NAME => 'z' }, { METHOD => 'delete', NAME => 'y' })
|
admin.alter(@test_name, true, { NAME => 'z' }, { METHOD => 'delete', NAME => 'y' }, 'MAX_FILESIZE' => 12345678)
|
||||||
admin.enable(@test_name)
|
admin.enable(@test_name)
|
||||||
assert_equal(['x:', 'z:'], table(@test_name).get_all_columns.sort)
|
assert_equal(['x:', 'z:'], table(@test_name).get_all_columns.sort)
|
||||||
|
assert_match(/12345678/, admin.describe(@test_name))
|
||||||
end
|
end
|
||||||
|
|
||||||
define_test 'alter should support shortcut DELETE alter specs' do
|
define_test 'alter should support shortcut DELETE alter specs' do
|
||||||
|
@ -269,6 +293,11 @@ module Hbase
|
||||||
assert_match(/12345678/, admin.describe(@test_name))
|
assert_match(/12345678/, admin.describe(@test_name))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
define_test "alter should be able to change table options w/o table_att" do
|
||||||
|
admin.alter(@test_name, true, 'MAX_FILESIZE' => 12345678)
|
||||||
|
assert_match(/12345678/, admin.describe(@test_name))
|
||||||
|
end
|
||||||
|
|
||||||
define_test "alter should be able to change coprocessor attributes" do
|
define_test "alter should be able to change coprocessor attributes" do
|
||||||
drop_test_table(@test_name)
|
drop_test_table(@test_name)
|
||||||
create_test_table(@test_name)
|
create_test_table(@test_name)
|
||||||
|
|
Loading…
Reference in New Issue