presto-client-legacy 0.4.17
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/.gitignore +4 -0
- data/.travis.yml +13 -0
- data/ChangeLog +89 -0
- data/Gemfile +2 -0
- data/README.md +86 -0
- data/Rakefile +26 -0
- data/lib/presto/client/client.rb +74 -0
- data/lib/presto/client/errors.rb +42 -0
- data/lib/presto/client/models.rb +1521 -0
- data/lib/presto/client/query.rb +176 -0
- data/lib/presto/client/statement_client.rb +259 -0
- data/lib/presto/client/version.rb +20 -0
- data/lib/presto/client.rb +23 -0
- data/lib/presto-client-legacy.rb +1 -0
- data/modelgen/modelgen.rb +91 -0
- data/modelgen/models.rb +230 -0
- data/modelgen/presto_models.rb +228 -0
- data/presto-client-legacy.gemspec +29 -0
- data/spec/client_spec.rb +75 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/statement_client_spec.rb +183 -0
- metadata +160 -0
data/modelgen/models.rb
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
#
|
2
|
+
# Presto client for Ruby
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
module PrestoLegacy::Client
|
17
|
+
|
18
|
+
####
|
19
|
+
## lib/presto/client/models.rb is automatically generated using "rake modelgen" command.
|
20
|
+
## You should not edit this file directly. To modify the class definitions, edit
|
21
|
+
## modelgen/models.rb file and run "rake modelgen".
|
22
|
+
##
|
23
|
+
|
24
|
+
module Models
|
25
|
+
class Base < Struct
|
26
|
+
class << self
|
27
|
+
alias_method :new_struct, :new
|
28
|
+
|
29
|
+
def new(*args)
|
30
|
+
new_struct(*args) do
|
31
|
+
# make it immutable
|
32
|
+
undef_method :"[]="
|
33
|
+
members.each do |m|
|
34
|
+
undef_method :"#{m}="
|
35
|
+
end
|
36
|
+
|
37
|
+
# replace constructor to receive hash instead of array
|
38
|
+
alias_method :initialize_struct, :initialize
|
39
|
+
|
40
|
+
def initialize(params={})
|
41
|
+
initialize_struct(*members.map {|m| params[m] })
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class StageId < String
|
49
|
+
def initialize(str)
|
50
|
+
super
|
51
|
+
splitted = split('.', 2)
|
52
|
+
@query_id = splitted[0]
|
53
|
+
@id = splitted[1]
|
54
|
+
end
|
55
|
+
|
56
|
+
attr_reader :query_id, :id
|
57
|
+
end
|
58
|
+
|
59
|
+
class TaskId < String
|
60
|
+
def initialize(str)
|
61
|
+
super
|
62
|
+
splitted = split('.', 3)
|
63
|
+
@stage_id = StageId.new("#{splitted[0]}.#{splitted[1]}")
|
64
|
+
@query_id = @stage_id.query_id
|
65
|
+
@id = splitted[2]
|
66
|
+
end
|
67
|
+
|
68
|
+
attr_reader :query_id, :stage_id, :id
|
69
|
+
end
|
70
|
+
|
71
|
+
class ConnectorSession < Hash
|
72
|
+
def initialize(hash)
|
73
|
+
super()
|
74
|
+
merge!(hash)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
module PlanNode
|
79
|
+
def self.decode(hash)
|
80
|
+
unless hash.is_a?(Hash)
|
81
|
+
raise TypeError, "Can't convert #{hash.class} to Hash"
|
82
|
+
end
|
83
|
+
model_class = case hash["@type"]
|
84
|
+
when "output" then OutputNode
|
85
|
+
when "project" then ProjectNode
|
86
|
+
when "tablescan" then TableScanNode
|
87
|
+
when "values" then ValuesNode
|
88
|
+
when "aggregation" then AggregationNode
|
89
|
+
when "markDistinct" then MarkDistinctNode
|
90
|
+
when "filter" then FilterNode
|
91
|
+
when "window" then WindowNode
|
92
|
+
when "rowNumber" then RowNumberNode
|
93
|
+
when "topnRowNumber" then TopNRowNumberNode
|
94
|
+
when "limit" then LimitNode
|
95
|
+
when "distinctlimit" then DistinctLimitNode
|
96
|
+
when "topn" then TopNNode
|
97
|
+
when "sample" then SampleNode
|
98
|
+
when "sort" then SortNode
|
99
|
+
when "remoteSource" then RemoteSourceNode
|
100
|
+
when "join" then JoinNode
|
101
|
+
when "semijoin" then SemiJoinNode
|
102
|
+
when "indexjoin" then IndexJoinNode
|
103
|
+
when "indexsource" then IndexSourceNode
|
104
|
+
when "tablewriter" then TableWriterNode
|
105
|
+
when "delete" then DeleteNode
|
106
|
+
when "metadatadelete" then MetadataDeleteNode
|
107
|
+
when "tablecommit" then TableFinishNode
|
108
|
+
when "unnest" then UnnestNode
|
109
|
+
when "exchange" then ExchangeNode
|
110
|
+
when "union" then UnionNode
|
111
|
+
when "scalar" then EnforceSingleRowNode
|
112
|
+
end
|
113
|
+
if model_class
|
114
|
+
node = model_class.decode(hash)
|
115
|
+
class << node
|
116
|
+
attr_accessor :plan_node_type
|
117
|
+
end
|
118
|
+
node.plan_node_type = hash['@type']
|
119
|
+
node
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# io.airlift.stats.Distribution.DistributionSnapshot
|
125
|
+
class << DistributionSnapshot =
|
126
|
+
Base.new(:max_error, :count, :total, :p01, :p05, :p10, :p25, :p50, :p75, :p90, :p95, :p99, :min, :max)
|
127
|
+
def decode(hash)
|
128
|
+
unless hash.is_a?(Hash)
|
129
|
+
raise TypeError, "Can't convert #{hash.class} to Hash"
|
130
|
+
end
|
131
|
+
obj = allocate
|
132
|
+
obj.send(:initialize_struct,
|
133
|
+
hash["maxError"],
|
134
|
+
hash["count"],
|
135
|
+
hash["total"],
|
136
|
+
hash["p01"],
|
137
|
+
hash["p05"],
|
138
|
+
hash["p10"],
|
139
|
+
hash["p25"],
|
140
|
+
hash["p50"],
|
141
|
+
hash["p75"],
|
142
|
+
hash["p90"],
|
143
|
+
hash["p95"],
|
144
|
+
hash["p99"],
|
145
|
+
hash["min"],
|
146
|
+
hash["max"],
|
147
|
+
)
|
148
|
+
obj
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# This is a hybrid of JoinNode.EquiJoinClause and IndexJoinNode.EquiJoinClause
|
153
|
+
class << EquiJoinClause =
|
154
|
+
Base.new(:left, :right, :probe, :index)
|
155
|
+
def decode(hash)
|
156
|
+
unless hash.is_a?(Hash)
|
157
|
+
raise TypeError, "Can't convert #{hash.class} to Hash"
|
158
|
+
end
|
159
|
+
obj = allocate
|
160
|
+
obj.send(:initialize_struct,
|
161
|
+
hash["left"],
|
162
|
+
hash["right"],
|
163
|
+
hash["probe"],
|
164
|
+
hash["index"],
|
165
|
+
)
|
166
|
+
obj
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
class << WriterTarget =
|
171
|
+
Base.new(:type, :handle)
|
172
|
+
def decode(hash)
|
173
|
+
unless hash.is_a?(Hash)
|
174
|
+
raise TypeError, "Can't convert #{hash.class} to Hash"
|
175
|
+
end
|
176
|
+
obj = allocate
|
177
|
+
model_class = case hash["@type"]
|
178
|
+
when "CreateHandle" then OutputTableHandle
|
179
|
+
when "InsertHandle" then InsertTableHandle
|
180
|
+
when "DeleteHandle" then TableHandle
|
181
|
+
end
|
182
|
+
obj.send(:initialize_struct,
|
183
|
+
hash["@type"],
|
184
|
+
model_class.decode(hash['handle'])
|
185
|
+
)
|
186
|
+
obj
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
class << DeleteHandle =
|
191
|
+
Base.new(:handle)
|
192
|
+
def decode(hash)
|
193
|
+
unless hash.is_a?(Hash)
|
194
|
+
raise TypeError, "Can't convert #{hash.class} to Hash"
|
195
|
+
end
|
196
|
+
obj = allocate
|
197
|
+
obj.send(:initialize_struct,
|
198
|
+
TableHandle.decode(hash['handle'])
|
199
|
+
)
|
200
|
+
obj
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
# A missing JsonCreator in Presto
|
206
|
+
class << PageBufferInfo =
|
207
|
+
Base.new(:partition, :buffered_pages, :queued_pages, :buffered_bytes, :pages_added)
|
208
|
+
def decode(hash)
|
209
|
+
unless hash.is_a?(Hash)
|
210
|
+
raise TypeError, "Can't convert #{hash.class} to Hash"
|
211
|
+
end
|
212
|
+
obj = allocate
|
213
|
+
obj.send(:initialize_struct,
|
214
|
+
hash["partition"],
|
215
|
+
hash["bufferedPages"],
|
216
|
+
hash["queuedPages"],
|
217
|
+
hash["bufferedBytes"],
|
218
|
+
hash["pagesAdded"],
|
219
|
+
)
|
220
|
+
obj
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
##
|
225
|
+
# Those model classes are automatically generated
|
226
|
+
#
|
227
|
+
|
228
|
+
<%= @contents %>
|
229
|
+
end
|
230
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
|
2
|
+
module PrestoModels
|
3
|
+
require 'find'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
PRIMITIVE_TYPES = %w[String boolean long int short byte double float Integer]
|
7
|
+
|
8
|
+
class Model < Struct.new(:name, :fields)
|
9
|
+
end
|
10
|
+
|
11
|
+
class Field < Struct.new(:key, :nullable, :array, :map, :type, :base_type, :map_value_base_type)
|
12
|
+
alias_method :nullable?, :nullable
|
13
|
+
alias_method :array?, :array
|
14
|
+
alias_method :map?, :map
|
15
|
+
|
16
|
+
def name
|
17
|
+
@name ||= key.gsub(/[A-Z]/) {|f| "_#{f.downcase}" }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class ModelAnalysisError < StandardError
|
22
|
+
end
|
23
|
+
|
24
|
+
class ModelAnalyzer
|
25
|
+
def initialize(source_path, options={})
|
26
|
+
@source_path = source_path
|
27
|
+
@ignore_types = PRIMITIVE_TYPES + (options[:skip_models] || [])
|
28
|
+
@path_mapping = options[:path_mapping] || {}
|
29
|
+
@name_mapping = options[:name_mapping] || {}
|
30
|
+
@models = {}
|
31
|
+
@skipped_models = []
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_reader :skipped_models
|
35
|
+
|
36
|
+
def models
|
37
|
+
@models.values.sort_by {|model| model.name }
|
38
|
+
end
|
39
|
+
|
40
|
+
def analyze(root_models)
|
41
|
+
root_models.each {|model_name|
|
42
|
+
analyze_model(model_name)
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
PROPERTY_PATTERN = /@JsonProperty\(\"(\w+)\"\)\s+(@Nullable\s+)?([\w\<\>\,\s]+)\s+(\w+)/
|
49
|
+
CREATOR_PATTERN = /@JsonCreator[\w\s]+\((?:\s*#{PROPERTY_PATTERN}\s*,?)+\)/
|
50
|
+
|
51
|
+
def analyze_model(model_name, parent_model = nil)
|
52
|
+
return if @models[model_name] || @ignore_types.include?(model_name)
|
53
|
+
|
54
|
+
path = find_class_file(model_name, parent_model)
|
55
|
+
java = File.read(path)
|
56
|
+
|
57
|
+
m = CREATOR_PATTERN.match(java)
|
58
|
+
unless m
|
59
|
+
raise ModelAnalysisError, "Can't find JsonCreator of a model class #{model_name} of #{parent_model} at #{path}"
|
60
|
+
end
|
61
|
+
|
62
|
+
fields = m[0].scan(PROPERTY_PATTERN).map do |key,nullable,type,field|
|
63
|
+
map = false
|
64
|
+
array = false
|
65
|
+
nullable = !!nullable
|
66
|
+
if m = /(?:List|Set)<(\w+)>/.match(type)
|
67
|
+
base_type = m[1]
|
68
|
+
array = true
|
69
|
+
elsif m = /(?:Map|ListMultimap)<(\w+),\s*(\w+)>/.match(type)
|
70
|
+
base_type = m[1]
|
71
|
+
map_value_base_type = m[2]
|
72
|
+
map = true
|
73
|
+
elsif m = /Optional<(\w+)>/.match(type)
|
74
|
+
base_type = m[1]
|
75
|
+
nullable = true
|
76
|
+
elsif m = /OptionalInt/.match(type)
|
77
|
+
base_type = 'Integer'
|
78
|
+
nullable = true
|
79
|
+
elsif type =~ /\w+/
|
80
|
+
base_type = type
|
81
|
+
else
|
82
|
+
raise ModelAnalysisError, "Unsupported type #{type} in model #{model_name}"
|
83
|
+
end
|
84
|
+
base_type = @name_mapping[[model_name, base_type]] || base_type
|
85
|
+
map_value_base_type = @name_mapping[[model_name, map_value_base_type]] || map_value_base_type
|
86
|
+
Field.new(key, !!nullable, array, map, type, base_type, map_value_base_type)
|
87
|
+
end
|
88
|
+
|
89
|
+
@models[model_name] = Model.new(model_name, fields)
|
90
|
+
|
91
|
+
# recursive call
|
92
|
+
fields.each do |field|
|
93
|
+
analyze_model(field.base_type, model_name)
|
94
|
+
analyze_model(field.map_value_base_type, model_name) if field.map_value_base_type
|
95
|
+
end
|
96
|
+
|
97
|
+
rescue => e
|
98
|
+
puts "Skipping model #{parent_model}/#{model_name}: #{e}"
|
99
|
+
@skipped_models << model_name
|
100
|
+
end
|
101
|
+
|
102
|
+
def find_class_file(model_name, parent_model)
|
103
|
+
return @path_mapping[model_name] if @path_mapping.has_key? model_name
|
104
|
+
|
105
|
+
@source_files ||= Find.find(@source_path).to_a
|
106
|
+
pattern = /\/#{model_name}.java$/
|
107
|
+
matched = @source_files.find_all {|path| path =~ pattern }
|
108
|
+
if matched.empty?
|
109
|
+
raise ModelAnalysisError, "Model class #{model_name} is not found"
|
110
|
+
end
|
111
|
+
if matched.size == 1
|
112
|
+
return matched.first
|
113
|
+
else
|
114
|
+
raise ModelAnalysisError, "Model class #{model_name} of #{parent_model} found multiple match #{matched}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class ModelFormatter
|
120
|
+
def initialize(options={})
|
121
|
+
@indent = options[:indent] || ' '
|
122
|
+
@base_indent_count = options[:base_indent_count] || 0
|
123
|
+
@struct_class = options[:struct_class] || 'Struct'
|
124
|
+
@special_struct_initialize_method = options[:special_struct_initialize_method]
|
125
|
+
@primitive_types = PRIMITIVE_TYPES + (options[:primitive_types] || [])
|
126
|
+
@skip_types = options[:skip_types] || []
|
127
|
+
@simple_classes = options[:simple_classes]
|
128
|
+
@enum_types = options[:enum_types]
|
129
|
+
@special_types = options[:special_types] || {}
|
130
|
+
@data = StringIO.new
|
131
|
+
end
|
132
|
+
|
133
|
+
def contents
|
134
|
+
@data.string
|
135
|
+
end
|
136
|
+
|
137
|
+
def format(models)
|
138
|
+
@models = models
|
139
|
+
models.each do |model|
|
140
|
+
@model = model
|
141
|
+
|
142
|
+
puts_with_indent 0, "class << #{model.name} ="
|
143
|
+
puts_with_indent 2, "#{@struct_class}.new(#{model.fields.map {|f| ":#{f.name}" }.join(', ')})"
|
144
|
+
format_decode
|
145
|
+
puts_with_indent 0, "end"
|
146
|
+
line
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def line
|
153
|
+
@data.puts ""
|
154
|
+
end
|
155
|
+
|
156
|
+
def puts_with_indent(n, str)
|
157
|
+
@data.puts "#{@indent * (@base_indent_count + n)}#{str}"
|
158
|
+
end
|
159
|
+
|
160
|
+
def format_decode
|
161
|
+
puts_with_indent 1, "def decode(hash)"
|
162
|
+
|
163
|
+
puts_with_indent 2, "unless hash.is_a?(Hash)"
|
164
|
+
puts_with_indent 3, "raise TypeError, \"Can't convert \#{hash.class} to Hash\""
|
165
|
+
puts_with_indent 2, "end"
|
166
|
+
|
167
|
+
if @special_struct_initialize_method
|
168
|
+
puts_with_indent 2, "obj = allocate"
|
169
|
+
puts_with_indent 2, "obj.send(:#{@special_struct_initialize_method},"
|
170
|
+
else
|
171
|
+
puts_with_indent 2, "new("
|
172
|
+
end
|
173
|
+
|
174
|
+
@model.fields.each do |field|
|
175
|
+
next if @skip_types.include?(field.base_type) || @skip_types.include?(field.map_value_base_type)
|
176
|
+
|
177
|
+
if @primitive_types.include?(field.base_type) && !field.map?
|
178
|
+
expr = "hash[\"#{field.key}\"]"
|
179
|
+
else
|
180
|
+
expr = ""
|
181
|
+
expr << "hash[\"#{field.key}\"] && " #if field.nullable?
|
182
|
+
|
183
|
+
if field.map?
|
184
|
+
key_expr = convert_expression(field.base_type, field.base_type, "k")
|
185
|
+
value_expr = convert_expression(field.map_value_base_type, field.map_value_base_type, "v")
|
186
|
+
if key_expr == 'k' && value_expr == 'v'
|
187
|
+
expr = "hash[\"#{field.key}\"]"
|
188
|
+
else
|
189
|
+
expr << "Hash[hash[\"#{field.key}\"].to_a.map! {|k,v| [#{key_expr}, #{value_expr}] }]"
|
190
|
+
end
|
191
|
+
elsif field.array?
|
192
|
+
elem_expr = convert_expression(field.base_type, field.base_type, "h")
|
193
|
+
expr << "hash[\"#{field.key}\"].map {|h| #{elem_expr} }"
|
194
|
+
else
|
195
|
+
expr << convert_expression(field.type, field.base_type, "hash[\"#{field.key}\"]")
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
#comment = "# #{field.base_type}#{field.array? ? '[]' : ''} #{field.key}"
|
200
|
+
#puts_with_indent 3, "#{expr}, #{comment}"
|
201
|
+
puts_with_indent 3, "#{expr},"
|
202
|
+
end
|
203
|
+
|
204
|
+
puts_with_indent 2, ")"
|
205
|
+
|
206
|
+
if @special_struct_initialize_method
|
207
|
+
puts_with_indent 2, "obj"
|
208
|
+
end
|
209
|
+
|
210
|
+
puts_with_indent 1, "end"
|
211
|
+
end
|
212
|
+
|
213
|
+
def convert_expression(type, base_type, key)
|
214
|
+
if @special_types[type]
|
215
|
+
special.call(key)
|
216
|
+
elsif @enum_types.include?(type)
|
217
|
+
"#{key}.downcase.to_sym"
|
218
|
+
elsif @primitive_types.include?(base_type)
|
219
|
+
key
|
220
|
+
elsif @simple_classes.include?(base_type)
|
221
|
+
"#{base_type}.new(#{key})"
|
222
|
+
else # model class
|
223
|
+
"#{base_type}.decode(#{key})"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.expand_path 'lib/presto/client/version', File.dirname(__FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = "presto-client-legacy"
|
5
|
+
gem.version = PrestoLegacy::Client::VERSION
|
6
|
+
|
7
|
+
gem.authors = ["Sadayuki Furuhash", "Kai Sasaki"]
|
8
|
+
gem.email = ["sf@treasure-data.com", "lewuathe@me.com"]
|
9
|
+
gem.description = %q{Presto client library for legacy presto version}
|
10
|
+
gem.summary = %q{Presto client library for legacy presto version (before 0.151)}
|
11
|
+
gem.homepage = "https://github.com/Lewuathe/presto-client-ruby"
|
12
|
+
gem.license = "Apache-2.0"
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split($\)
|
15
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
gem.has_rdoc = false
|
19
|
+
|
20
|
+
gem.required_ruby_version = ">= 1.9.1"
|
21
|
+
|
22
|
+
gem.add_dependency "faraday", [">= 0.8.8", "< 0.10.0"]
|
23
|
+
gem.add_dependency "multi_json", ["~> 1.0"]
|
24
|
+
|
25
|
+
gem.add_development_dependency "rake", [">= 0.9.2"]
|
26
|
+
gem.add_development_dependency "rspec", ["~> 2.13.0"]
|
27
|
+
gem.add_development_dependency "webmock", ["~> 1.16.1"]
|
28
|
+
gem.add_development_dependency "simplecov", ["~> 0.10.0"]
|
29
|
+
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PrestoLegacy::Client::Client do
|
4
|
+
let(:client) { PrestoLegacy::Client.new({}) }
|
5
|
+
|
6
|
+
describe 'rehashes' do
|
7
|
+
let(:columns) do
|
8
|
+
[
|
9
|
+
Models::Column.new(name: 'animal', type: 'string'),
|
10
|
+
Models::Column.new(name: 'score', type: 'integer'),
|
11
|
+
Models::Column.new(name: 'name', type: 'string')
|
12
|
+
]
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'multiple rows' do
|
16
|
+
rows = [
|
17
|
+
['dog', 1, 'Lassie'],
|
18
|
+
['horse', 5, 'Mr. Ed'],
|
19
|
+
['t-rex', 37, 'Doug']
|
20
|
+
]
|
21
|
+
client.stub(:run).and_return([columns, rows])
|
22
|
+
|
23
|
+
rehashed = client.run_with_names('fake query')
|
24
|
+
|
25
|
+
rehashed.length.should == 3
|
26
|
+
|
27
|
+
rehashed[0]['animal'].should == 'dog'
|
28
|
+
rehashed[0]['score'].should == 1
|
29
|
+
rehashed[0]['name'].should == 'Lassie'
|
30
|
+
|
31
|
+
rehashed[0].values[0].should == 'dog'
|
32
|
+
rehashed[0].values[1].should == 1
|
33
|
+
rehashed[0].values[2].should == 'Lassie'
|
34
|
+
|
35
|
+
rehashed[1]['animal'].should == 'horse'
|
36
|
+
rehashed[1]['score'].should == 5
|
37
|
+
rehashed[1]['name'].should == 'Mr. Ed'
|
38
|
+
|
39
|
+
rehashed[1].values[0].should == 'horse'
|
40
|
+
rehashed[1].values[1].should == 5
|
41
|
+
rehashed[1].values[2].should == 'Mr. Ed'
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'empty results' do
|
45
|
+
rows = []
|
46
|
+
client.stub(:run).and_return([columns, rows])
|
47
|
+
|
48
|
+
rehashed = client.run_with_names('fake query')
|
49
|
+
|
50
|
+
rehashed.length.should == 0
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'handles too few result columns' do
|
54
|
+
rows = [['wrong', 'count']]
|
55
|
+
client.stub(:run).and_return([columns, rows])
|
56
|
+
|
57
|
+
client.run_with_names('fake query').should == [{
|
58
|
+
"animal" => "wrong",
|
59
|
+
"score" => "count",
|
60
|
+
"name" => nil,
|
61
|
+
}]
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'handles too many result columns' do
|
65
|
+
rows = [['wrong', 'count', 'too', 'much', 'columns']]
|
66
|
+
client.stub(:run).and_return([columns, rows])
|
67
|
+
|
68
|
+
client.run_with_names('fake query').should == [{
|
69
|
+
"animal" => "wrong",
|
70
|
+
"score" => "count",
|
71
|
+
"name" => 'too',
|
72
|
+
}]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :test)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'simplecov'
|
12
|
+
SimpleCov.start
|
13
|
+
|
14
|
+
require 'json'
|
15
|
+
require 'webmock/rspec'
|
16
|
+
|
17
|
+
require 'presto-client-legacy'
|
18
|
+
include PrestoLegacy::Client
|