You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by st...@apache.org on 2008/06/12 08:00:36 UTC
svn commit: r666965 - in /hadoop/hbase/trunk/bin: Formatter.rb HBase.rb
hirb.rb
Author: stack
Date: Wed Jun 11 23:00:35 2008
New Revision: 666965
URL: http://svn.apache.org/viewvc?rev=666965&view=rev
Log:
HBASE-487 Replace hql w/ a hbase-friendly jirb or jython shell; First cut at DDL and Admin implementations; create, drop, list, etc.
Added:
hadoop/hbase/trunk/bin/Formatter.rb
hadoop/hbase/trunk/bin/HBase.rb
Modified:
hadoop/hbase/trunk/bin/hirb.rb
Added: hadoop/hbase/trunk/bin/Formatter.rb
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/bin/Formatter.rb?rev=666965&view=auto
==============================================================================
--- hadoop/hbase/trunk/bin/Formatter.rb (added)
+++ hadoop/hbase/trunk/bin/Formatter.rb Wed Jun 11 23:00:35 2008
@@ -0,0 +1,112 @@
+# Results formatter
+module Formatter
+ class Formatter
+ # Base abstract class for results formatting.
+ def initialize(o, w = 80)
+ raise TypeError.new("Type %s of parameter %s is not IO" % [o.class, o]) \
+ unless o.instance_of? IO
+ @out = o
+ @maxWidth = w
+ @rowCount = 0
+ end
+
+ def header(args = [])
+ row(args) if args.length > 0
+ @rowCount = 0
+ end
+
+ def row(args = [])
+ if not args or args.length == 0
+ # Print out nothing
+ return
+ end
+ # TODO: Look at the type. Is it RowResult?
+ if args.length == 1
+ splits = split(@maxWidth, dump(args[0]))
+ for l in splits
+ output(@maxWidth, l)
+ puts
+ end
+ elsif args.length == 2
+ col1width = 8
+ col2width = 70
+ splits1 = split(col1width, dump(args[0]))
+ splits2 = split(col2width, dump(args[1]))
+ biggest = (splits2.length > splits1.length)? splits2.length: splits1.length
+ index = 0
+ while index < biggest
+ @out.print(" ")
+ output(col1width, splits1[index])
+ @out.print(" ")
+ output(col2width, splits2[index])
+ index += 1
+ puts
+ end
+ else
+ # Print a space to set off multi-column rows
+ print ' '
+ first = true
+ for e in args
+ @out.print " " unless first
+ first = false
+ @out.print e
+ end
+ puts
+ end
+ @rowCount += 1
+ end
+
+ def split(width, str)
+ result = []
+ index = 0
+ while index < str.length do
+ result << str.slice(index, index + width)
+ index += width
+ end
+ result
+ end
+
+ def dump(str)
+ # Remove double-quotes added by 'dump'.
+ return str.dump.slice(1, str.length)
+ end
+
+ def output(width, str)
+ # Make up a spec for printf
+ spec = "%%-%d.%ds" % [width, width]
+ @out.printf(spec % str)
+ end
+
+ def footer(startTime = nil)
+ if not startTime
+ return
+ end
+ # Only output elapsed time and row count if startTime passed
+ @out.puts("%d row(s) in %s seconds" % [@rowCount, Time.now - startTime])
+ end
+ end
+
+
+ class Console < Formatter
+ end
+
+ class XHTMLFormatter < Formatter
+ # http://www.germane-software.com/software/rexml/doc/classes/REXML/Document.html
+ # http://www.crummy.com/writing/RubyCookbook/test_results/75942.html
+ end
+
+ class JSON < Formatter
+ end
+
+ # Do a bit of testing.
+ if $0 == __FILE__
+ formatter = Console.new(STDOUT)
+ formatter.header(['a', 'b'])
+ formatter.row(['a', 'b'])
+ formatter.row(['xxxxxxxxx xxxxxxxxxxx xxxxxxxxxxx xxxxxxxxxxxx xxxxxxxxx xxxxxxxxxxxx xxxxxxxxxxxxxxx xxxxxxxxx xxxxxxxxxxxxxx'])
+ formatter.row(['yyyyyy yyyyyy yyyyy yyy', 'xxxxxxxxx xxxxxxxxxxx xxxxxxxxxxx xxxxxxxxxxxx xxxxxxxxx xxxxxxxxxxxx xxxxxxxxxxxxxxx xxxxxxxxx xxxxxxxxxxxxxx xxx xx x xx xxx xx xx xx x xx x x xxx x x xxx x x xx x x x x x x xx '])
+ formatter.footer()
+ end
+end
+
+
Added: hadoop/hbase/trunk/bin/HBase.rb
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/bin/HBase.rb?rev=666965&view=auto
==============================================================================
--- hadoop/hbase/trunk/bin/HBase.rb (added)
+++ hadoop/hbase/trunk/bin/HBase.rb Wed Jun 11 23:00:35 2008
@@ -0,0 +1,93 @@
+# HBase ruby classes
+
+module HBase
+ # Constants needed as keys creating tables, etc.
+ NAME = "NAME"
+ MAX_VERSIONS = "MAX_VERSIONS"
+ MAX_LENGTH = "MAX_LENGTH"
+ TTL = "TTL"
+ BLOOMFILTER = "BLOOMFILTER"
+ COMPRESSION_TYPE = "COMPRESSION_TYPE"
+ # TODO: Add table options here.
+
+ class Admin
+ def initialize(configuration, formatter)
+ @admin = HBaseAdmin.new(configuration)
+ @formatter = formatter
+ end
+
+ def list
+ now = Time.now
+ @formatter.header()
+ for t in @admin.listTables()
+ @formatter.row([t.getNameAsString()])
+ end
+ @formatter.footer(now)
+ end
+
+ def exists(tableName)
+ now = Time.now
+ @formatter.header()
+ @formatter.row([@admin.tableExists(tableName)])
+ @formatter.footer(now)
+ end
+
+ def enable(tableName)
+ # TODO: Need an isEnabled method
+ now = Time.now
+ @admin.enableTable(tableName)
+ @formatter.header()
+ @formatter.footer(now)
+ end
+
+ def disable(tableName)
+ # TODO: Need an isDisabled method
+ now = Time.now
+ @admin.disableTable(tableName)
+ @formatter.header()
+ @formatter.footer(now)
+ end
+
+ def drop(tableName)
+ now = Time.now
+ @admin.deleteTable(tableName)
+ @formatter.header()
+ @formatter.row(["Deleted %s" % tableName])
+ @formatter.footer(now)
+ end
+
+ def create(tableName, args)
+ now = Time.now
+ # Pass table name and an array of Hashes. Later, test the last
+ # array to see if its table options rather than column family spec.
+ raise TypeError.new("Table name must be of type String") \
+ unless tableName.instance_of? String
+ # For now presume all the rest of the args are column family
+ # hash specifications. TODO: Add table options handling.
+ htd = HTableDescriptor.new(tableName)
+ for arg in args
+ raise TypeError.new(arg.class.to_s + " of " + arg.to_s + " is not of Hash type") \
+ unless arg.instance_of? Hash
+ name = arg[NAME]
+ raise ArgumentError.new("Column family " + arg + " must have a name at least") \
+ unless name
+ # TODO: Process all other parameters for column family
+ # Check the family name for colon. Add it if missing.
+ index = name.index(':')
+ if not index
+ # Add a colon. If already a colon, its in the right place,
+ # or an exception will come up out of the addFamily
+ name << ':'
+ end
+ htd.addFamily(HColumnDescriptor.new(name))
+ end
+ @admin.createTable(htd)
+ @formatter.header()
+ @formatter.row(["Created %s" % tableName])
+ @formatter.footer(now)
+ end
+ end
+
+ class Table
+ end
+end
Modified: hadoop/hbase/trunk/bin/hirb.rb
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/bin/hirb.rb?rev=666965&r1=666964&r2=666965&view=diff
==============================================================================
--- hadoop/hbase/trunk/bin/hirb.rb (original)
+++ hadoop/hbase/trunk/bin/hirb.rb Wed Jun 11 23:00:35 2008
@@ -4,11 +4,11 @@
# TODO: Process command-line arguments: e.g. --master= or -Dhbase.etc and --formatter
# or read hbase shell configurations from irbrc
-# TODO: Read from environment which outputter to use (outputter should
-# be able to output to a passed Stream as well as STDIN and STDOUT)
+
# TODO: Write a base class for formatters with ascii, xhtml, and json subclasses.
-# Run the java magic include and import basic HBase types.
+# Run the java magic include and import basic HBase types that will help ease
+# hbase hacking.
include Java
import org.apache.hadoop.hbase.HBaseConfiguration
import org.apache.hadoop.hbase.client.HTable
@@ -21,52 +21,137 @@
# Some goodies for hirb. Should these be left up to the user's discretion?
require 'irb/completion'
+# Add the $HBASE_HOME/bin directory, the presumed location of this script,
+# to the ruby load path so I can load up my HBase ruby modules
+$LOAD_PATH.unshift File.dirname($PROGRAM_NAME)
+require 'Formatter'
+require 'HBase'
+
+# A HERE document used outputting shell command-line options.
+@cmdline_help = <<HERE
+HBase Shell command-line options:
+ format Formatter outputting results: console | html. Default: console.
+ master HBase master shell should connect to: e.g --master=example:60000.
+HERE
+
+# See if there are args for us. If any, read and then strip from ARGV
+# so they don't go through to irb.
+master = nil
+@formatter = Formatter::Console.new(STDOUT)
+found = []
+for arg in ARGV
+ if arg =~ /^--master=(.+)/i
+ master = $1
+ found.push(arg)
+ elsif arg =~ /^--format=(.+)/i
+ format = $1
+ if format =~ /^html$/i
+ @formatter = Formatter::XHTML.new(STDOUT)
+ elsif format =~ /^console$/i
+ # This is default
+ else
+ raise ArgumentError.new("Unsupported format " + arg)
+ end
+ elsif arg == '-h' || arg == '--help'
+ puts @cmdline_help
+ exit
+ end
+end
+for arg in found
+ ARGV.delete(arg)
+end
+
+# Setup the HBase module. Create a configuration. If a master, set it.
+@configuration = HBaseConfiguration.new()
+@configuration.set("hbase.master", master) if master
+# Do lazy create of admin. If we are pointed at bad master, will hang
+# shell on startup trying to connect.
+@admin = nil
+
+# Promote all HBase constants to be constants of this module.
+for c in HBase.constants
+ if c == c.upcase
+ eval("%s = \"%s\"" % [c, c])
+ end
+end
+
+# TODO: Add table options here.
+
+# General Shell Commands: help and version
def help
- puts 'HBase Shell Commands:'
- puts ' version Output HBase version'
- puts ARGV.inspect
+ # Format is command name and then short description
+ # TODO: Can't do 'help COMMAND'. Interpreter runs help and then the command
+ commands = {'version' => 'Output HBase version',
+ 'list' => 'List all tables',
+ # The help string in the below is carefully formatted to wrap nicely in
+ # our dumb Console formatter
+ 'create' => "Create table; pass a table name, a dictionary of \
+specifications per column family, and optionally, named parameters of table \
+options. Dictionaries are specified with curly-brackets, uppercase keys, a '=>'\
+key/value delimiter and then a value. Named parameters are like dict- \
+ionary elements with uppercase names and a '=>' delimiter. E.g. To \
+create a table named 'table1' with an alternate maximum region size \
+and a single family named 'family1' with an alternate maximum cells: \
+create 'table1' {NAME =>'family1', MAX_NUM_VERSIONS => 5}, REGION_SIZE => 12345",
+ 'enable' => "Enable named table",
+ 'disable' => "Disable named table",
+ 'exists' => "Does named table exist",
+ }
+ @formatter.header(["HBase Shell Commands:"])
+ # TODO: Add general note that all names must be quoted and a general
+ # description of dictionary so create doesn't have to be so long.
+ for k, v in commands.sort
+ @formatter.row([k, v])
+ end
+ @formatter.footer()
end
def version
- "Version: #{org.apache.hadoop.hbase.util.VersionInfo.getVersion()},\
+ @formatter.header()
+ @formatter.row(["Version: #{org.apache.hadoop.hbase.util.VersionInfo.getVersion()},\
r#{org.apache.hadoop.hbase.util.VersionInfo.getRevision()},\
- #{org.apache.hadoop.hbase.util.VersionInfo.getDate()}"
-end
-
-# general
-
-def list
- puts "Not implemented yet"
+ #{org.apache.hadoop.hbase.util.VersionInfo.getDate()}"])
+ @formatter.footer()
end
# DDL
def create(table_name, *args)
- puts "Not impemented yet"
+ @admin = HBase::Admin.new(@configuration, @formatter) unless @admin
+ @admin.create(table_name, args)
end
def drop(table_name)
- puts "Not implemented yet"
+ @admin = HBase::Admin.new(@configuration, @formatter) unless @admin
+ @admin.drop(table_name)
end
def alter(table_name, *args)
puts "Not implemented yet"
end
-# admin
+# Administration
+
+def list
+ @admin = HBase::Admin.new(@configuration, @formatter) unless @admin
+ @admin.list()
+end
def enable(table_name)
- puts "Not implemented yet"
+ @admin = HBase::Admin.new(@configuration, @formatter) unless @admin
+ @admin.enable(table_name)
end
def disable(table_name)
- puts "Not implemented yet"
-end
-
-def truncate(table_name)
- puts "Not implemented yet"
+ @admin = HBase::Admin.new(@configuration, @formatter) unless @admin
+ @admin.disable(table_name)
end
+def exists(table_name)
+ @admin = HBase::Admin.new(@configuration, @formatter) unless @admin
+ @admin.exists(table_name)
+end
+
# CRUD
def get(table_name, row_key, *args)
@@ -85,58 +170,60 @@
puts "Not implemented yet"
end
-
-
# Output a banner message that tells users where to go for help
# TODO: Test that we're in irb context. For now presume it.
# TODO: Test that we are in shell context.
puts "HBase Shell; type 'hbase<RETURN>' for the list of supported HBase commands"
-puts version
+version
require "irb"
-IRB::ExtendCommandBundle.instance_variable_get("@EXTEND_COMMANDS").delete_if{|x| x.first == :irb_help}
+# IRB::ExtendCommandBundle.instance_variable_get("@EXTEND_COMMANDS").delete_if{|x| x.first == :irb_help}
module IRB
module ExtendCommandBundle
-
+ # These are attempts at blocking the complaint about :irb_help on startup.
+ # @EXTEND_COMMANDS.delete_if{|x| x[0] == :irb_help}
+ # @EXTEND_COMMANDS.each{|x| x[3][1] = OVERRIDE_ALL if x[0] == :irb_help}
+ # @EXTEND_COMMANDS.each{|x| puts x if x[0] == :irb_help}
end
-
+
+ class HIRB < Irb
+ # Subclass irb so can intercept methods
+
+ def output_value
+ # Suppress output if last_value is 'nil'
+ # Otherwise, when user types help, get ugly 'nil'
+ # after all output.
+ if @context.last_value
+ super
+ end
+ end
+ end
+
def IRB.start(ap_path = nil)
$0 = File::basename(ap_path, ".rb") if ap_path
IRB.setup(ap_path)
-
- @CONF[:PROMPT][:HBASE] = {
- :PROMPT_I => "hbase> ",
- :PROMPT_N => "hbase> ",
- :PROMPT_S => nil,
- :PROMPT_C => "?> ",
- :RETURN => "%s\n"
- }
- @CONF[:PROMPT_MODE] = :HBASE
-
+ @CONF[:IRB_NAME]="hbase"
if @CONF[:SCRIPT]
- irb = Irb.new(nil, @CONF[:SCRIPT])
+ hirb = HIRB.new(nil, @CONF[:SCRIPT])
else
- irb = Irb.new
+ hirb = HIRB.new
end
- @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
- @CONF[:MAIN_CONTEXT] = irb.context
+ @CONF[:IRB_RC].call(hirb.context) if @CONF[:IRB_RC]
+ @CONF[:MAIN_CONTEXT] = hirb.context
trap("SIGINT") do
- irb.signal_handle
+ hirb.signal_handle
end
catch(:IRB_EXIT) do
- irb.eval_input
+ hirb.eval_input
end
end
end
-
-# .delete_if{|x| x.first == :irb_help}.inspect
-
IRB.start