This is an automated email from the ASF dual-hosted git repository.
zhangduo pushed a commit to branch branch-2.5
in repository https://gitbox.apache.org/repos/asf/hbase.git
The following commit(s) were added to refs/heads/branch-2.5 by this push:
new 3f42dc72b51 HBASE-28549 Make shell commands support column qualifiers
with colons (#5849)
3f42dc72b51 is described below
commit 3f42dc72b5129a3bdc64965a425248c5b0c3fda0
Author: Junegunn Choi <[email protected]>
AuthorDate: Sat Jun 8 12:29:19 2024 +0900
HBASE-28549 Make shell commands support column qualifiers with colons
(#5849)
Signed-off-by: Duo Zhang <[email protected]>
(cherry picked from commit f136f0ace8a328f4a8489a66cb8397975b5777dd)
---
hbase-shell/src/main/ruby/hbase/table.rb | 50 ++++++++++++++++-----------
hbase-shell/src/test/ruby/hbase/table_test.rb | 36 +++++++++++++++++++
2 files changed, 65 insertions(+), 21 deletions(-)
diff --git a/hbase-shell/src/main/ruby/hbase/table.rb
b/hbase-shell/src/main/ruby/hbase/table.rb
index fc63785d689..0461f497d0f 100644
--- a/hbase-shell/src/main/ruby/hbase/table.rb
+++ b/hbase-shell/src/main/ruby/hbase/table.rb
@@ -135,7 +135,7 @@ EOF
# Put a cell 'value' at specified table/row/column
def _put_internal(row, column, value, timestamp = nil, args = {})
p = org.apache.hadoop.hbase.client.Put.new(row.to_s.to_java_bytes)
- family, qualifier = parse_column_name(column)
+ family, qualifier = split_column_name(column)
if args.any?
attributes = args[ATTRIBUTES]
set_attributes(p, attributes) if attributes
@@ -188,14 +188,14 @@ EOF
end
if column != ""
if column && all_version
- family, qualifier = parse_column_name(column)
+ family, qualifier = split_column_name(column)
if qualifier
d.addColumns(family, qualifier, timestamp)
else
d.addFamily(family, timestamp)
end
elsif column && !all_version
- family, qualifier = parse_column_name(column)
+ family, qualifier = split_column_name(column)
if qualifier
d.addColumn(family, qualifier, timestamp)
else
@@ -273,7 +273,7 @@ EOF
value = 1 if value.is_a?(Hash)
value ||= 1
incr =
org.apache.hadoop.hbase.client.Increment.new(row.to_s.to_java_bytes)
- family, qualifier = parse_column_name(column)
+ family, qualifier = split_column_name(column)
if args.any?
attributes = args[ATTRIBUTES]
visibility = args[VISIBILITY]
@@ -296,7 +296,7 @@ EOF
# appends the value atomically
def _append_internal(row, column, value, args = {})
append =
org.apache.hadoop.hbase.client.Append.new(row.to_s.to_java_bytes)
- family, qualifier = parse_column_name(column)
+ family, qualifier = split_column_name(column)
if args.any?
attributes = args[ATTRIBUTES]
visibility = args[VISIBILITY]
@@ -491,7 +491,7 @@ EOF
#----------------------------------------------------------------------------------------------
# Fetches and decodes a counter value from hbase
def _get_counter_internal(row, column)
- family, qualifier = parse_column_name(column.to_s)
+ family, qualifier = split_column_name(column.to_s)
# Format get request
get = org.apache.hadoop.hbase.client.Get.new(row.to_s.to_java_bytes)
get.addColumn(family, qualifier)
@@ -833,16 +833,16 @@ EOF
eval(converter_class).method(converter_method).call(bytes)
end
- def convert_bytes_with_position(bytes, offset, len, converter_class,
converter_method)
- # Avoid nil
- converter_class ||= 'org.apache.hadoop.hbase.util.Bytes'
- converter_method ||= 'toStringBinary'
- eval(converter_class).method(converter_method).call(bytes, offset, len)
- end
-
# store the information designating what part of a column should be
printed, and how
ColumnFormatSpec = Struct.new(:family, :qualifier, :converter)
+ # Use this instead of parse_column_name if the name cannot contain a
converter
+ private def split_column_name(column)
+ # NOTE: We use 'take(2)' instead of 'to_a' to avoid type coercion of the
nested byte arrays.
+ #
https://github.com/jruby/jruby/blob/9.3.13.0/core/src/main/java/org/jruby/java/proxies/ArrayJavaProxy.java#L484-L488
+
org.apache.hadoop.hbase.CellUtil.parseColumn(column.to_java_bytes).take(2)
+ end
+
##
# Parse the column specification for formatting used by shell commands
like :scan
#
@@ -856,21 +856,29 @@ EOF
# @param [String] column
# @return [ColumnFormatSpec] family, qualifier, and converter as Java bytes
private def parse_column_format_spec(column)
- split =
org.apache.hadoop.hbase.CellUtil.parseColumn(column.to_java_bytes)
- family = split[0]
- qualifier = nil
converter = nil
- if split.length > 1
- parts = org.apache.hadoop.hbase.CellUtil.parseColumn(split[1])
- qualifier = parts[0]
- if parts.length > 1
- converter = parts[1]
+ family, qualifier = split_column_name(column)
+ if qualifier
+ delim = org.apache.hadoop.hbase.KeyValue.getDelimiterInReverse(
+ qualifier, 0, qualifier.length,
org.apache.hadoop.hbase.KeyValue::COLUMN_FAMILY_DELIMITER
+ )
+ if delim >= 0
+ prefix, suffix = qualifier[0...delim], qualifier[delim+1..-1]
+ if converter?(suffix.to_s)
+ qualifier = prefix
+ converter = suffix
+ end
end
end
ColumnFormatSpec.new(family, qualifier, converter)
end
+ # Check if the expression can be a converter
+ private def converter?(expr)
+ expr =~ /^c\(.+\)\..+/ || Bytes.respond_to?(expr)
+ end
+
private def set_column_converter(family, qualifier, converter)
@converters["#{String.from_java_bytes(family)}:#{String.from_java_bytes(qualifier)}"]
= String.from_java_bytes(converter)
end
diff --git a/hbase-shell/src/test/ruby/hbase/table_test.rb
b/hbase-shell/src/test/ruby/hbase/table_test.rb
index 8ed6f663dbd..7b9a15fb841 100644
--- a/hbase-shell/src/test/ruby/hbase/table_test.rb
+++ b/hbase-shell/src/test/ruby/hbase/table_test.rb
@@ -242,6 +242,42 @@ module Hbase
define_test "get_counter should return nil for non-existent counters" do
assert_nil(@test_table._get_counter_internal(12345, 'x:qqqq'))
end
+
+ define_test "should work with qualifiers with colons" do
+ rowkey = "123"
+
+ # Two columns with multiple colons in their qualifiers with the same
prefix
+ col1 = "x:foo:bar:c1"
+ col2 = "x:foo:bar:c2"
+
+ # Make sure that no data is present
+ @test_table.deleteall(rowkey)
+
+ # Put two columns with colons in their qualifiers
+ @test_table.put(rowkey, col1,
org.apache.hadoop.hbase.util.Bytes.toBytes(1))
+ @test_table.put(rowkey, col2,
org.apache.hadoop.hbase.util.Bytes.toBytes(2))
+ assert_equal(2, @test_table._get_internal(rowkey).length)
+
+ # Increment the second column by 10 => 2 + 10 => 12
+ @test_table.incr(rowkey, col2, 10)
+ assert_equal(12, @test_table._get_counter_internal(rowkey, col2))
+
+ # Check the counter value using toLong converter
+ %w[:toLong :c(org.apache.hadoop.hbase.util.Bytes).toLong].each do
|suffix|
+ res = @test_table._get_internal(rowkey, { COLUMNS => [col2 + suffix] })
+ assert_not_nil(res)
+ assert_kind_of(Hash, res)
+ assert_not_nil(/value=12/.match(res[col2]))
+ end
+
+ # Delete the first column
+ @test_table.delete(rowkey, col1)
+ assert_equal(1, @test_table._get_internal(rowkey).length)
+
+ # Append twice to the deleted column
+ @test_table.append(rowkey, col1, '123')
+ assert_equal("123123", @test_table._append_internal(rowkey, col1, '123'))
+ end
end
# Complex data management methods tests