hipster_sql_to_hbase 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +4 -0
- data/Gemfile +5 -0
- data/LICENSE +20 -0
- data/LICENSE.txt +20 -0
- data/README.md +39 -0
- data/README.rdoc +19 -0
- data/Rakefile +31 -0
- data/VERSION +1 -0
- data/hipster_sql_to_hbase.gemspec +55 -0
- data/lib/adapter/Hbase.thrift +914 -0
- data/lib/adapter/hbase.rb +59 -0
- data/lib/adapter/hbase/hbase.rb +2966 -0
- data/lib/adapter/hbase/hbase_constants.rb +14 -0
- data/lib/adapter/hbase/hbase_types.rb +282 -0
- data/lib/datatype_extras.rb +18 -0
- data/lib/executor.rb +91 -0
- data/lib/hipster_sql_to_hbase.rb +167 -0
- data/lib/result_tree_to_hbase_converter.rb +119 -0
- data/lib/result_tree_to_json_converter.rb +40 -0
- data/lib/sql_parser/sql.treetop +21 -0
- data/lib/sql_parser/sql_chars.treetop +5 -0
- data/lib/sql_parser/sql_create_table.treetop +47 -0
- data/lib/sql_parser/sql_datatypes.treetop +71 -0
- data/lib/sql_parser/sql_delete.treetop +64 -0
- data/lib/sql_parser/sql_drop_table.treetop +26 -0
- data/lib/sql_parser/sql_from_clause.treetop +12 -0
- data/lib/sql_parser/sql_group_by_clause.treetop +15 -0
- data/lib/sql_parser/sql_helpers.treetop +19 -0
- data/lib/sql_parser/sql_insert.treetop +118 -0
- data/lib/sql_parser/sql_key_value_pair.treetop +91 -0
- data/lib/sql_parser/sql_limit.treetop +7 -0
- data/lib/sql_parser/sql_order_by_clause.treetop +53 -0
- data/lib/sql_parser/sql_primitives.treetop +118 -0
- data/lib/sql_parser/sql_row_support.treetop +72 -0
- data/lib/sql_parser/sql_select.treetop +82 -0
- data/lib/sql_parser/sql_select_clause.treetop +17 -0
- data/lib/sql_parser/sql_show_tables.treetop +26 -0
- data/lib/sql_parser/sql_tokens.treetop +125 -0
- data/lib/sql_parser/sql_transaction.treetop +43 -0
- data/lib/sql_parser/sql_truncate.treetop +11 -0
- data/lib/sql_parser/sql_update.treetop +82 -0
- data/lib/sql_parser/sql_where_condition.treetop +46 -0
- data/lib/sql_treetop_load.rb +23 -0
- data/spec/hipster_sql_to_hbase_spec.rb +171 -0
- data/spec/spec_helper.rb +3 -0
- metadata +192 -0
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
require 'thrift'
|
3
|
+
require_relative "executor"
|
4
|
+
|
5
|
+
require File.join(File.dirname(__FILE__), 'adapter', 'hbase')
|
6
|
+
|
7
|
+
module HipsterSqlToHbase
|
8
|
+
|
9
|
+
# This class provides the method necessary to execute the Thrift result
|
10
|
+
# generated after parsing the SQL sentence.
|
11
|
+
class ThriftCallGroup < Array
|
12
|
+
@incr = false
|
13
|
+
def initialize(arr,incr=false)
|
14
|
+
arr.each do |v|
|
15
|
+
self << v
|
16
|
+
end
|
17
|
+
@incr = incr
|
18
|
+
end
|
19
|
+
def execute(host=nil,port=nil)
|
20
|
+
HipsterSqlToHbase::Executor.new().execute(self,host,port,@incr)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# This class takes care of all HBase (Thrift) conversion magic by transforming
|
25
|
+
# the ResultTree objects into ThriftCallGroup objects.
|
26
|
+
class ResultTreeToHbaseConverter
|
27
|
+
|
28
|
+
# Depending on the SQL sentence type, call the appropriate function.
|
29
|
+
def convert(result_tree)
|
30
|
+
send("#{result_tree[:query_type].to_s}_sentence",result_tree[:query_hash])
|
31
|
+
end
|
32
|
+
|
33
|
+
# When SQL sentence is an INSERT query generate the Thrift mutations according
|
34
|
+
# to the specified query values.
|
35
|
+
def insert_sentence(hash)
|
36
|
+
thrift_method = "mutateRow"
|
37
|
+
thrift_table = hash[:into]
|
38
|
+
thrift_calls = []
|
39
|
+
hash[:values].each do |value_set|
|
40
|
+
thrift_row = SecureRandom.uuid
|
41
|
+
thrift_mutations = []
|
42
|
+
i = 0
|
43
|
+
hash[:columns].each do |col|
|
44
|
+
thrift_mutations << HBase::Mutation.new(column: col, value: value_set[i].to_s)
|
45
|
+
i += 1
|
46
|
+
end
|
47
|
+
thrift_calls << {:method => thrift_method,:arguments => [thrift_table,thrift_row,thrift_mutations,{}]}
|
48
|
+
end
|
49
|
+
HipsterSqlToHbase::ThriftCallGroup.new(thrift_calls,true)
|
50
|
+
end
|
51
|
+
|
52
|
+
# When SQL sentence is a SELECT query generate the Thrift filters according
|
53
|
+
# to the specified query values.
|
54
|
+
def select_sentence(hash)
|
55
|
+
thrift_method = "getRowsByScanner"
|
56
|
+
thrift_table = hash[:from]
|
57
|
+
thrift_columns = hash[:select]
|
58
|
+
thrift_filters = recurse_where(hash[:where] || [])
|
59
|
+
|
60
|
+
HipsterSqlToHbase::ThriftCallGroup.new([{:method => thrift_method,:arguments => [thrift_table,thrift_columns,thrift_filters,{}]}])
|
61
|
+
end
|
62
|
+
|
63
|
+
# When SQL sentence is a CREATE TABLE query generate the Thrift column descriptors/families
|
64
|
+
# in accordance to the specified query values.
|
65
|
+
def create_table_sentence(hash)
|
66
|
+
thrift_method = "createTable"
|
67
|
+
thrift_table = hash[:table]
|
68
|
+
thrift_columns = []
|
69
|
+
hash[:columns].each do |col_name|
|
70
|
+
col_descriptor = Hbase::ColumnDescriptor.new
|
71
|
+
col_descriptor.name = col_name
|
72
|
+
thrift_columns << col_descriptor
|
73
|
+
end
|
74
|
+
|
75
|
+
HipsterSqlToHbase::ThriftCallGroup.new([{:method => thrift_method,:arguments => [thrift_table,thrift_columns]}])
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# Format the scanner filter for thrift based on the where clause(s)
|
81
|
+
# of a SELECT query.
|
82
|
+
def recurse_where(where_arr)
|
83
|
+
result_arr = []
|
84
|
+
where_arr.each do |val|
|
85
|
+
if val.is_a? Hash
|
86
|
+
result_arr << filters_from_key_value_pair(val)
|
87
|
+
elsif val.is_a? Array
|
88
|
+
result_arr << "(#{recurse_where(val)})"
|
89
|
+
elsif val.is_a? String
|
90
|
+
result_arr << val
|
91
|
+
else
|
92
|
+
raise "Recursive where undefined error."
|
93
|
+
end
|
94
|
+
end
|
95
|
+
result_arr.join(" ")
|
96
|
+
end
|
97
|
+
|
98
|
+
# Generate a Thrift QualifierFilter and ValueFilter from key value pair.
|
99
|
+
def filters_from_key_value_pair(kvp)
|
100
|
+
if (kvp[:condition].to_s != "LIKE")
|
101
|
+
"(ValueFilter(#{kvp[:condition]},'binary:#{kvp[:value]}') AND DependentColumnFilter('#{kvp[:column]}',''))"
|
102
|
+
else
|
103
|
+
kvp[:value] = Regexp.escape(kvp[:value])
|
104
|
+
kvp[:value].sub!(/^%/,"^.*")
|
105
|
+
kvp[:value].sub!(/%$/,".*$")
|
106
|
+
while kvp[:value].match(/([^\\]{1,1})%/)
|
107
|
+
kvp[:value].sub!(/([^\\]{1,1})%/,"#{$1}.*?")
|
108
|
+
end
|
109
|
+
kvp[:value].sub!(/^_/,"^.")
|
110
|
+
kvp[:value].sub!(/_$/,".$")
|
111
|
+
while kvp[:value].match(/([^\\]{1,1})_/)
|
112
|
+
kvp[:value].sub!(/([^\\]{1,1})_/,"#{$1}.")
|
113
|
+
end
|
114
|
+
"(ValueFilter(=,'regexstring:#{kvp[:value]}') AND DependentColumnFilter('#{kvp[:column]}',''))"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), 'adapter', 'hbase')
|
4
|
+
|
5
|
+
module HipsterSqlToHbase
|
6
|
+
class ResultTreeToJsonConverter
|
7
|
+
def convert(result_tree)
|
8
|
+
send("#{result_tree[:query_type].to_s}_sentence",result_tree[:query_hash])
|
9
|
+
end
|
10
|
+
def insert_sentence(hash)
|
11
|
+
table = hash[:into]
|
12
|
+
objects = []
|
13
|
+
hash[:values].each do |value_set|
|
14
|
+
object = {}
|
15
|
+
i = 0
|
16
|
+
hash[:columns].each do |col|
|
17
|
+
object[col.to_sym] = value_set[i]
|
18
|
+
i += 1
|
19
|
+
end
|
20
|
+
objects << object
|
21
|
+
end
|
22
|
+
JSON.generate({:write=>{:table=>table,:objects=>objects}})
|
23
|
+
end
|
24
|
+
def select_sentence
|
25
|
+
|
26
|
+
end
|
27
|
+
def create_table_sentence
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def qualifier_filters_from_cols(cols)
|
33
|
+
|
34
|
+
end
|
35
|
+
def value_filters_from_vals(vals)
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
grammar SQL
|
2
|
+
include SQLSelect
|
3
|
+
include SQLDelete
|
4
|
+
include SQLTruncate
|
5
|
+
include DropTable #TODO: Fix my name
|
6
|
+
include SQLTransaction
|
7
|
+
include SQLInsert
|
8
|
+
include SQLShowTables
|
9
|
+
include SQLCreateTable
|
10
|
+
|
11
|
+
rule sql_statement
|
12
|
+
select_expression /
|
13
|
+
delete /
|
14
|
+
truncate /
|
15
|
+
drop_table /
|
16
|
+
transaction_statement /
|
17
|
+
insert /
|
18
|
+
show_tables /
|
19
|
+
create_table
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
grammar SQLCreateTable
|
2
|
+
include SQLRowSupport
|
3
|
+
include SQLDataTypes
|
4
|
+
|
5
|
+
rule create_table
|
6
|
+
"CREATE" SPACE+ "TABLE" SPACE+ table_name SPACE+ OPEN_PARENS columns_and_datatypes CLOSE_PARENS {
|
7
|
+
def eval
|
8
|
+
options = {
|
9
|
+
:columns => columns_and_datatypes.eval,
|
10
|
+
:table_name => table_name.eval
|
11
|
+
}
|
12
|
+
|
13
|
+
options
|
14
|
+
end
|
15
|
+
def query_type
|
16
|
+
:create_table
|
17
|
+
end
|
18
|
+
def tree
|
19
|
+
values = eval
|
20
|
+
{
|
21
|
+
:table => values[:table_name],
|
22
|
+
:columns => values[:columns]
|
23
|
+
}
|
24
|
+
end
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
rule columns_and_datatypes
|
29
|
+
column_with_datatype COMMA columns_and_datatypes {
|
30
|
+
def eval
|
31
|
+
all = column_with_datatype.eval + columns_and_datatypes.eval
|
32
|
+
all.flatten!
|
33
|
+
all
|
34
|
+
end
|
35
|
+
}
|
36
|
+
/
|
37
|
+
column_with_datatype
|
38
|
+
end
|
39
|
+
|
40
|
+
rule column_with_datatype
|
41
|
+
SPACE* column_name SPACE+ datatype SPACE* {
|
42
|
+
def eval
|
43
|
+
[column_name.eval]
|
44
|
+
end
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
grammar SQLDataTypes
|
2
|
+
include SQLPrimitives
|
3
|
+
|
4
|
+
rule datatype
|
5
|
+
bit_field /
|
6
|
+
numeric_type /
|
7
|
+
char_type
|
8
|
+
end
|
9
|
+
|
10
|
+
rule char_type
|
11
|
+
char_field field_size_with_space?
|
12
|
+
end
|
13
|
+
|
14
|
+
rule char_field
|
15
|
+
VARCHAR_KEYWORD
|
16
|
+
end
|
17
|
+
|
18
|
+
rule numeric_type
|
19
|
+
int_type / decimal_type
|
20
|
+
end
|
21
|
+
|
22
|
+
rule decimal_type
|
23
|
+
decimal_field field_size_with_space? unsigned? zerofill?
|
24
|
+
end
|
25
|
+
|
26
|
+
rule decimal_field
|
27
|
+
REAL_KEYWORD /
|
28
|
+
DOUBLE_KEYWORD /
|
29
|
+
FLOAT_KEYWORD /
|
30
|
+
DECIMAL_KEYWORD /
|
31
|
+
NUMERIC_KEYWORD
|
32
|
+
end
|
33
|
+
|
34
|
+
rule int_type
|
35
|
+
int_field int_options
|
36
|
+
end
|
37
|
+
|
38
|
+
rule int_field
|
39
|
+
TINY_INT_KEYWORD /
|
40
|
+
SMALL_INT_KEYWORD /
|
41
|
+
INTEGER_KEYWORD /
|
42
|
+
INT_KEYWORD /
|
43
|
+
BIG_INT_KEYWORD
|
44
|
+
end
|
45
|
+
|
46
|
+
rule int_options
|
47
|
+
field_size_with_space? unsigned? zerofill?
|
48
|
+
end
|
49
|
+
|
50
|
+
rule bit_field
|
51
|
+
"BIT" field_size?
|
52
|
+
end
|
53
|
+
|
54
|
+
rule unsigned
|
55
|
+
SPACE UNSIGNED_KEYWORD
|
56
|
+
end
|
57
|
+
|
58
|
+
rule zerofill
|
59
|
+
SPACE ZEROFILL_KEYWORD
|
60
|
+
end
|
61
|
+
|
62
|
+
rule field_size_with_space
|
63
|
+
SPACE* field_size
|
64
|
+
end
|
65
|
+
|
66
|
+
rule field_size
|
67
|
+
OPEN_PARENS SPACE* integer SPACE* CLOSE_PARENS /
|
68
|
+
OPEN_PARENS SPACE* integer SPACE* COMMA SPACE* integer SPACE* CLOSE_PARENS
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
@@ -0,0 +1,64 @@
|
|
1
|
+
grammar SQLDelete
|
2
|
+
include SQLRowSupport
|
3
|
+
include SQLWhereCondition
|
4
|
+
include SQLOrderByClause
|
5
|
+
include SQLLimit
|
6
|
+
include SQLHelpers
|
7
|
+
|
8
|
+
rule delete
|
9
|
+
single_table_delete
|
10
|
+
end
|
11
|
+
|
12
|
+
rule single_table_delete
|
13
|
+
common_delete_clause
|
14
|
+
table_name
|
15
|
+
where_condition_or_empty
|
16
|
+
order_by_condition_or_empty
|
17
|
+
limit_condition_or_empty {
|
18
|
+
def eval
|
19
|
+
DeleteStatement.new(
|
20
|
+
table_name.eval,
|
21
|
+
where_condition_or_empty.eval,
|
22
|
+
order_by_condition_or_empty.eval,
|
23
|
+
limit_condition_or_empty.eval
|
24
|
+
)
|
25
|
+
end
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
rule common_delete_clause
|
30
|
+
"DELETE" SPACE
|
31
|
+
optional_delete_directives
|
32
|
+
"FROM" SPACE
|
33
|
+
end
|
34
|
+
|
35
|
+
rule optional_delete_directives
|
36
|
+
optional_low_priority
|
37
|
+
optional_quick
|
38
|
+
optional_ignore
|
39
|
+
end
|
40
|
+
|
41
|
+
rule optional_low_priority
|
42
|
+
low_priority / EMPTY_STRING
|
43
|
+
end
|
44
|
+
|
45
|
+
rule optional_quick
|
46
|
+
quick / EMPTY_STRING
|
47
|
+
end
|
48
|
+
|
49
|
+
rule optional_ignore
|
50
|
+
ignore / EMPTY_STRING
|
51
|
+
end
|
52
|
+
|
53
|
+
rule ignore
|
54
|
+
"IGNORE" SPACE
|
55
|
+
end
|
56
|
+
|
57
|
+
rule quick
|
58
|
+
"QUICK" SPACE
|
59
|
+
end
|
60
|
+
|
61
|
+
rule low_priority
|
62
|
+
"LOW_PRIORITY" SPACE
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
grammar DropTable
|
2
|
+
include SQLPrimitives
|
3
|
+
include SQLRowSupport
|
4
|
+
|
5
|
+
rule drop_table
|
6
|
+
"DROP" (temporary)? SPACE "TABLE" SPACE
|
7
|
+
table_name
|
8
|
+
(restrict / cascade)? {
|
9
|
+
def eval
|
10
|
+
DataStore.drop_table(table_name.eval.to_sym)
|
11
|
+
end
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
rule temporary
|
16
|
+
SPACE "TEMPORARY"
|
17
|
+
end
|
18
|
+
|
19
|
+
rule restrict
|
20
|
+
SPACE "RESTRICT"
|
21
|
+
end
|
22
|
+
|
23
|
+
rule cascade
|
24
|
+
SPACE "CASCADE"
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
grammar SQLGroupByClause
|
2
|
+
include SQLPrimitives
|
3
|
+
include SQLRowSupport
|
4
|
+
include SQLHelpers
|
5
|
+
|
6
|
+
rule group_by
|
7
|
+
"GROUP BY " one_or_more_column_names {
|
8
|
+
def eval
|
9
|
+
columns = one_or_more_column_names.eval
|
10
|
+
columns_as_syms = columns.map { |column| column.to_sym }
|
11
|
+
GroupBy.new(*columns_as_syms)
|
12
|
+
end
|
13
|
+
}
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
grammar SQLHelpers
|
2
|
+
rule where_condition_or_empty
|
3
|
+
SPACE where_condition {
|
4
|
+
def eval
|
5
|
+
where_condition.eval
|
6
|
+
end
|
7
|
+
}
|
8
|
+
/
|
9
|
+
EMPTY_STRING
|
10
|
+
end
|
11
|
+
|
12
|
+
rule limit_condition_or_empty
|
13
|
+
SPACE limit { def eval; limit.eval; end } / EMPTY_STRING
|
14
|
+
end
|
15
|
+
|
16
|
+
rule order_by_condition_or_empty
|
17
|
+
SPACE order_by { def eval; order_by.eval; end } / EMPTY_STRING
|
18
|
+
end
|
19
|
+
end
|