presto-client 0.5.14 → 0.6.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/CODEOWNERS +1 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +18 -0
- data/.travis.yml +6 -6
- data/ChangeLog.md +160 -0
- data/Gemfile +4 -0
- data/LICENSE +202 -0
- data/README.md +23 -9
- data/Rakefile +4 -4
- data/lib/presto/client/faraday_client.rb +6 -1
- data/lib/presto/client/model_versions/303.rb +2574 -0
- data/lib/presto/client/model_versions/316.rb +2595 -0
- data/lib/presto/client/models.rb +3 -1
- data/lib/presto/client/query.rb +3 -5
- data/lib/presto/client/statement_client.rb +50 -36
- data/lib/presto/client/version.rb +1 -1
- data/modelgen/model_versions.rb +27 -3
- data/modelgen/modelgen.rb +16 -14
- data/modelgen/presto_models.rb +29 -9
- data/presto-client.gemspec +2 -1
- data/release.rb +56 -0
- data/spec/basic_query_spec.rb +82 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/statement_client_spec.rb +79 -18
- data/spec/tpch/q01.sql +21 -0
- data/spec/tpch/q02.sql +43 -0
- data/spec/tpch_query_spec.rb +41 -0
- metadata +33 -6
- data/ChangeLog +0 -171
data/lib/presto/client/models.rb
CHANGED
@@ -29,7 +29,9 @@ module Presto::Client
|
|
29
29
|
require 'presto/client/model_versions/0.173.rb'
|
30
30
|
require 'presto/client/model_versions/0.178.rb'
|
31
31
|
require 'presto/client/model_versions/0.205.rb'
|
32
|
+
require 'presto/client/model_versions/303.rb'
|
33
|
+
require 'presto/client/model_versions/316.rb'
|
32
34
|
|
33
|
-
Models = ModelVersions::
|
35
|
+
Models = ModelVersions::V316
|
34
36
|
|
35
37
|
end
|
data/lib/presto/client/query.rb
CHANGED
@@ -16,6 +16,7 @@
|
|
16
16
|
module Presto::Client
|
17
17
|
|
18
18
|
require 'faraday'
|
19
|
+
require 'faraday_middleware'
|
19
20
|
require 'presto/client/models'
|
20
21
|
require 'presto/client/errors'
|
21
22
|
require 'presto/client/faraday_client'
|
@@ -125,16 +126,13 @@ module Presto::Client
|
|
125
126
|
end
|
126
127
|
|
127
128
|
def close
|
128
|
-
@api.
|
129
|
+
@api.close
|
129
130
|
nil
|
130
131
|
end
|
131
132
|
|
132
133
|
def raise_if_failed
|
133
|
-
if @api.
|
134
|
+
if @api.client_aborted?
|
134
135
|
raise PrestoClientError, "Query aborted by user"
|
135
|
-
elsif @api.exception?
|
136
|
-
# query is gone
|
137
|
-
raise @api.exception
|
138
136
|
elsif @api.query_failed?
|
139
137
|
results = @api.current_results
|
140
138
|
error = results.error
|
@@ -31,8 +31,7 @@ module Presto::Client
|
|
31
31
|
|
32
32
|
@options = options
|
33
33
|
@query = query
|
34
|
-
@
|
35
|
-
@exception = nil
|
34
|
+
@state = :running
|
36
35
|
@retry_timeout = options[:retry_timeout] || 120
|
37
36
|
if model_version = @options[:model_version]
|
38
37
|
@models = ModelVersions.const_get("V#{model_version.gsub(".", "_")}")
|
@@ -76,7 +75,7 @@ module Presto::Client
|
|
76
75
|
|
77
76
|
# TODO error handling
|
78
77
|
if response.status != 200
|
79
|
-
|
78
|
+
exception! PrestoHttpError.new(response.status, "Failed to start query: #{response.body} (#{response.status})")
|
80
79
|
end
|
81
80
|
|
82
81
|
@results_headers = response.headers
|
@@ -91,14 +90,20 @@ module Presto::Client
|
|
91
90
|
!!@options[:debug]
|
92
91
|
end
|
93
92
|
|
94
|
-
def
|
95
|
-
@
|
93
|
+
def running?
|
94
|
+
@state == :running
|
96
95
|
end
|
97
96
|
|
98
|
-
|
97
|
+
def client_aborted?
|
98
|
+
@state == :client_aborted
|
99
|
+
end
|
100
|
+
|
101
|
+
def client_error?
|
102
|
+
@state == :client_error
|
103
|
+
end
|
99
104
|
|
100
|
-
def
|
101
|
-
@
|
105
|
+
def finished?
|
106
|
+
@state == :finished
|
102
107
|
end
|
103
108
|
|
104
109
|
def query_failed?
|
@@ -106,7 +111,7 @@ module Presto::Client
|
|
106
111
|
end
|
107
112
|
|
108
113
|
def query_succeeded?
|
109
|
-
@results.error == nil &&
|
114
|
+
@results.error == nil && finished?
|
110
115
|
end
|
111
116
|
|
112
117
|
def current_results
|
@@ -117,16 +122,29 @@ module Presto::Client
|
|
117
122
|
@results_headers
|
118
123
|
end
|
119
124
|
|
125
|
+
def query_id
|
126
|
+
@results.id
|
127
|
+
end
|
128
|
+
|
120
129
|
def has_next?
|
121
130
|
!!@results.next_uri
|
122
131
|
end
|
123
132
|
|
133
|
+
def exception!(e)
|
134
|
+
@state = :client_error
|
135
|
+
raise e
|
136
|
+
end
|
137
|
+
|
124
138
|
def advance
|
125
|
-
|
139
|
+
return false unless running?
|
140
|
+
|
141
|
+
unless has_next?
|
142
|
+
@state = :finished
|
126
143
|
return false
|
127
144
|
end
|
128
145
|
|
129
146
|
uri = @results.next_uri
|
147
|
+
|
130
148
|
response = faraday_get_with_retry(uri)
|
131
149
|
@results_headers = response.headers
|
132
150
|
@results = decode_model(uri, parse_body(response), @models::QueryResults)
|
@@ -150,8 +168,7 @@ module Presto::Client
|
|
150
168
|
if body.size > 1024 + 3
|
151
169
|
body = "#{body[0, 1024]}..."
|
152
170
|
end
|
153
|
-
|
154
|
-
raise @exception
|
171
|
+
exception! PrestoHttpError.new(500, "Presto API returned unexpected structure at #{uri}. Expected #{body_class} but got #{body}: #{e}")
|
155
172
|
end
|
156
173
|
end
|
157
174
|
|
@@ -166,8 +183,7 @@ module Presto::Client
|
|
166
183
|
JSON.parse(response.body, opts = JSON_OPTIONS)
|
167
184
|
end
|
168
185
|
rescue => e
|
169
|
-
|
170
|
-
raise @exception
|
186
|
+
exception! PrestoHttpError.new(500, "Presto API returned unexpected data format. #{e}")
|
171
187
|
end
|
172
188
|
end
|
173
189
|
|
@@ -184,8 +200,7 @@ module Presto::Client
|
|
184
200
|
# temporally error to retry
|
185
201
|
response = nil
|
186
202
|
rescue => e
|
187
|
-
|
188
|
-
raise @exception
|
203
|
+
exception! e
|
189
204
|
end
|
190
205
|
|
191
206
|
if response
|
@@ -195,8 +210,7 @@ module Presto::Client
|
|
195
210
|
|
196
211
|
if response.status != 503 # retry only if 503 Service Unavailable
|
197
212
|
# deterministic error
|
198
|
-
|
199
|
-
raise @exception
|
213
|
+
exception! PrestoHttpError.new(response.status, "Presto API error at #{uri} returned #{response.status}: #{response.body}")
|
200
214
|
end
|
201
215
|
end
|
202
216
|
|
@@ -204,18 +218,14 @@ module Presto::Client
|
|
204
218
|
|
205
219
|
attempts += 1
|
206
220
|
sleep attempts * 0.1
|
207
|
-
end while (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) < @retry_timeout &&
|
221
|
+
end while (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) < @retry_timeout && !client_aborted?
|
208
222
|
|
209
|
-
|
210
|
-
raise @exception
|
223
|
+
exception! PrestoHttpError.new(408, "Presto API error due to timeout")
|
211
224
|
end
|
212
225
|
|
213
226
|
def raise_if_timeout!
|
214
227
|
if @started_at
|
215
|
-
if
|
216
|
-
# query is already done
|
217
|
-
return
|
218
|
-
end
|
228
|
+
return if finished?
|
219
229
|
|
220
230
|
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - @started_at
|
221
231
|
|
@@ -234,30 +244,34 @@ module Presto::Client
|
|
234
244
|
|
235
245
|
def raise_timeout_error!
|
236
246
|
if query_id = @results && @results.id
|
237
|
-
|
247
|
+
exception! PrestoQueryTimeoutError.new("Query #{query_id} timed out")
|
238
248
|
else
|
239
|
-
|
249
|
+
exception! PrestoQueryTimeoutError.new("Query timed out")
|
240
250
|
end
|
241
251
|
end
|
242
252
|
|
243
253
|
def cancel_leaf_stage
|
244
|
-
if uri = @results.
|
245
|
-
|
254
|
+
if uri = @results.partial_cancel_uri
|
255
|
+
@faraday.delete do |req|
|
246
256
|
req.url uri
|
247
257
|
end
|
248
|
-
return response.status / 100 == 2
|
249
258
|
end
|
250
|
-
return false
|
251
259
|
end
|
252
260
|
|
253
261
|
def close
|
254
|
-
return
|
262
|
+
return unless running?
|
255
263
|
|
256
|
-
|
257
|
-
|
258
|
-
|
264
|
+
@state = :client_aborted
|
265
|
+
|
266
|
+
begin
|
267
|
+
if uri = @results.next_uri
|
268
|
+
@faraday.delete do |req|
|
269
|
+
req.url uri
|
270
|
+
end
|
271
|
+
end
|
272
|
+
rescue => e
|
273
|
+
end
|
259
274
|
|
260
|
-
@closed = true
|
261
275
|
nil
|
262
276
|
end
|
263
277
|
end
|
data/modelgen/model_versions.rb
CHANGED
@@ -131,6 +131,7 @@ module Presto::Client::ModelVersions
|
|
131
131
|
when "apply" then ApplyNode
|
132
132
|
when "assignUniqueId" then AssignUniqueId
|
133
133
|
when "lateralJoin" then LateralJoinNode
|
134
|
+
when "statisticsWriterNode" then StatisticsWriterNode
|
134
135
|
end
|
135
136
|
if model_class
|
136
137
|
node = model_class.decode(hash)
|
@@ -197,9 +198,25 @@ module Presto::Client::ModelVersions
|
|
197
198
|
end
|
198
199
|
obj = allocate
|
199
200
|
model_class = case hash["@type"]
|
200
|
-
when "
|
201
|
-
when "
|
202
|
-
when "
|
201
|
+
when "CreateTarget" then CreateTarget
|
202
|
+
when "InsertTarget" then InsertTarget
|
203
|
+
when "DeleteTarget" then DeleteTarget
|
204
|
+
end
|
205
|
+
if model_class
|
206
|
+
model_class.decode(hash)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
class << WriteStatisticsTarget =
|
212
|
+
Base.new(:type, :handle)
|
213
|
+
def decode(hash)
|
214
|
+
unless hash.is_a?(Hash)
|
215
|
+
raise TypeError, "Can't convert #{hash.class} to Hash"
|
216
|
+
end
|
217
|
+
obj = allocate
|
218
|
+
model_class = case hash["@type"]
|
219
|
+
when "WriteStatisticsHandle" then WriteStatisticsHandle
|
203
220
|
end
|
204
221
|
if model_class
|
205
222
|
model_class.decode(hash)
|
@@ -246,6 +263,13 @@ module Presto::Client::ModelVersions
|
|
246
263
|
end
|
247
264
|
end
|
248
265
|
|
266
|
+
class ResourceGroupId < Array
|
267
|
+
def initialize(array)
|
268
|
+
super()
|
269
|
+
concat(array)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
249
273
|
##
|
250
274
|
# Those model classes are automatically generated
|
251
275
|
#
|
data/modelgen/modelgen.rb
CHANGED
@@ -13,13 +13,13 @@ erb = ERB.new(File.read(template_path))
|
|
13
13
|
|
14
14
|
source_path = source_dir
|
15
15
|
|
16
|
-
predefined_simple_classes = %w[StageId TaskId Lifespan ConnectorSession]
|
17
|
-
predefined_models = %w[DistributionSnapshot PlanNode EquiJoinClause WriterTarget OperatorInfo HashCollisionsInfo]
|
16
|
+
predefined_simple_classes = %w[StageId TaskId Lifespan ConnectorSession ResourceGroupId]
|
17
|
+
predefined_models = %w[DistributionSnapshot PlanNode EquiJoinClause WriterTarget WriteStatisticsTarget OperatorInfo HashCollisionsInfo]
|
18
18
|
|
19
|
-
assume_primitive = %w[Object Type Long Symbol QueryId PlanNodeId PlanFragmentId MemoryPoolId TransactionId URI Duration DataSize DateTime ColumnHandle ConnectorTableHandle ConnectorOutputTableHandle ConnectorIndexHandle ConnectorColumnHandle ConnectorInsertTableHandle ConnectorTableLayoutHandle Expression FunctionCall TimeZoneKey Locale TypeSignature Frame TupleDomain<ColumnHandle> SerializableNativeValue ConnectorTransactionHandle OutputBufferId ConnectorPartitioningHandle NullableValue ConnectorId HostAddress JsonNode Node]
|
20
|
-
enum_types = %w[QueryState StageState TaskState QueueState PlanDistribution OutputPartitioning Step SortOrder BufferState NullPartitioning BlockedReason ParameterKind FunctionKind PartitionFunctionHandle Scope ErrorType DistributionType PipelineExecutionStrategy JoinType]
|
19
|
+
assume_primitive = %w[Object Type Long Symbol QueryId PlanNodeId PlanFragmentId MemoryPoolId TransactionId URI Duration DataSize DateTime ColumnHandle ConnectorTableHandle ConnectorOutputTableHandle ConnectorIndexHandle ConnectorColumnHandle ConnectorInsertTableHandle ConnectorTableLayoutHandle Expression FunctionCall TimeZoneKey Locale TypeSignature Frame TupleDomain<ColumnHandle> SerializableNativeValue ConnectorTransactionHandle OutputBufferId ConnectorPartitioningHandle NullableValue ConnectorId HostAddress JsonNode Node CatalogName QualifiedObjectName]
|
20
|
+
enum_types = %w[QueryState StageState TaskState QueueState PlanDistribution OutputPartitioning Step SortOrder BufferState NullPartitioning BlockedReason ParameterKind FunctionKind PartitionFunctionHandle Scope ErrorType DistributionType PipelineExecutionStrategy JoinType ExchangeNode.Type ColumnStatisticType TableStatisticType StageExecutionStrategy SemanticErrorCode]
|
21
21
|
|
22
|
-
root_models = %w[QueryResults QueryInfo] + %w[
|
22
|
+
root_models = %w[QueryResults QueryInfo BasicQueryInfo] + %w[
|
23
23
|
OutputNode
|
24
24
|
ProjectNode
|
25
25
|
TableScanNode
|
@@ -42,7 +42,6 @@ IndexJoinNode
|
|
42
42
|
IndexSourceNode
|
43
43
|
TableWriterNode
|
44
44
|
DeleteNode
|
45
|
-
MetadataDeleteNode
|
46
45
|
TableFinishNode
|
47
46
|
UnnestNode
|
48
47
|
ExchangeNode
|
@@ -54,6 +53,7 @@ ExplainAnalyzeNode
|
|
54
53
|
ApplyNode
|
55
54
|
AssignUniqueId
|
56
55
|
LateralJoinNode
|
56
|
+
StatisticsWriterNode
|
57
57
|
] + %w[
|
58
58
|
ExchangeClientStatus
|
59
59
|
LocalExchangeBufferInfo
|
@@ -62,7 +62,8 @@ SplitOperatorInfo
|
|
62
62
|
PartitionedOutputInfo
|
63
63
|
JoinOperatorInfo
|
64
64
|
WindowInfo
|
65
|
-
TableWriterInfo
|
65
|
+
TableWriterInfo
|
66
|
+
]
|
66
67
|
|
67
68
|
name_mapping = Hash[*%w[
|
68
69
|
StatementStats StageStats ClientStageStats
|
@@ -71,13 +72,14 @@ QueryResults Column ClientColumn
|
|
71
72
|
].each_slice(3).map { |x, y, z| [[x,y], z] }.flatten(1)]
|
72
73
|
|
73
74
|
path_mapping = Hash[*%w[
|
74
|
-
ClientColumn presto-client/src/main/java/
|
75
|
-
ClientStageStats presto-client/src/main/java/
|
76
|
-
Column presto-main/src/main/java/
|
77
|
-
QueryStats presto-main/src/main/java/
|
78
|
-
StageStats presto-main/src/main/java/
|
79
|
-
PartitionedOutputInfo presto-main/src/main/java/
|
80
|
-
TableWriterInfo presto-main/src/main/java/
|
75
|
+
ClientColumn presto-client/src/main/java/io/prestosql/client/Column.java
|
76
|
+
ClientStageStats presto-client/src/main/java/io/prestosql/client/StageStats.java
|
77
|
+
Column presto-main/src/main/java/io/prestosql/execution/Column.java
|
78
|
+
QueryStats presto-main/src/main/java/io/prestosql/execution/QueryStats.java
|
79
|
+
StageStats presto-main/src/main/java/io/prestosql/execution/StageStats.java
|
80
|
+
PartitionedOutputInfo presto-main/src/main/java/io/prestosql/operator/PartitionedOutputOperator.java
|
81
|
+
TableWriterInfo presto-main/src/main/java/io/prestosql/operator/TableWriterOperator.java
|
82
|
+
TableInfo presto-main/src/main/java/io/prestosql/execution/TableInfo.java
|
81
83
|
].map.with_index { |v,i| i % 2 == 0 ? v : (source_path + "/" + v) }]
|
82
84
|
|
83
85
|
# model => [ [key,nullable,type], ... ]
|
data/modelgen/presto_models.rb
CHANGED
@@ -3,13 +3,13 @@ module PrestoModels
|
|
3
3
|
require 'find'
|
4
4
|
require 'stringio'
|
5
5
|
|
6
|
-
PRIMITIVE_TYPES = %w[String boolean long int short byte double float Integer]
|
6
|
+
PRIMITIVE_TYPES = %w[String boolean long int short byte double float Integer Double Boolean]
|
7
7
|
ARRAY_PRIMITIVE_TYPES = PRIMITIVE_TYPES.map { |t| "#{t}[]" }
|
8
8
|
|
9
9
|
class Model < Struct.new(:name, :fields)
|
10
10
|
end
|
11
11
|
|
12
|
-
class Field < Struct.new(:key, :nullable, :array, :map, :type, :base_type, :map_value_base_type)
|
12
|
+
class Field < Struct.new(:key, :nullable, :array, :map, :type, :base_type, :map_value_base_type, :base_type_alias)
|
13
13
|
alias_method :nullable?, :nullable
|
14
14
|
alias_method :array?, :array
|
15
15
|
alias_method :map?, :map
|
@@ -47,10 +47,12 @@ module PrestoModels
|
|
47
47
|
|
48
48
|
private
|
49
49
|
|
50
|
-
PROPERTY_PATTERN = /@JsonProperty\(\"(\w+)\"\)\s+(@Nullable\s+)?([\w\<\>\[\]\,\s]+)\s+\w+/
|
50
|
+
PROPERTY_PATTERN = /@JsonProperty\(\"(\w+)\"\)\s+(@Nullable\s+)?([\w\<\>\[\]\,\s\.]+)\s+\w+/
|
51
51
|
CREATOR_PATTERN = /@JsonCreator[\s]+public[\s]+(static\s+)?(\w+)[\w\s]*\((?:\s*#{PROPERTY_PATTERN}\s*,?)+\)/
|
52
|
+
GENERIC_PATTERN = /(\w+)\<(\w+)\>/
|
52
53
|
|
53
|
-
def analyze_fields(model_name, creator_block)
|
54
|
+
def analyze_fields(model_name, creator_block, generic: nil)
|
55
|
+
model_name = "#{model_name}_#{generic}" if generic
|
54
56
|
extra = @extra_fields[model_name] || []
|
55
57
|
fields = creator_block.scan(PROPERTY_PATTERN).concat(extra).map do |key,nullable,type|
|
56
58
|
map = false
|
@@ -63,7 +65,7 @@ module PrestoModels
|
|
63
65
|
base_type = m[1]
|
64
66
|
map_value_base_type = m[2]
|
65
67
|
map = true
|
66
|
-
elsif m = /Optional<([\w\[\]]+)>/.match(type)
|
68
|
+
elsif m = /Optional<([\w\[\]\<\>]+)>/.match(type)
|
67
69
|
base_type = m[1]
|
68
70
|
nullable = true
|
69
71
|
elsif m = /OptionalInt/.match(type)
|
@@ -72,6 +74,9 @@ module PrestoModels
|
|
72
74
|
elsif m = /OptionalLong/.match(type)
|
73
75
|
base_type = 'Long'
|
74
76
|
nullable = true
|
77
|
+
elsif m = /OptionalDouble/.match(type)
|
78
|
+
base_type = 'Double'
|
79
|
+
nullable = true
|
75
80
|
elsif type =~ /\w+/
|
76
81
|
base_type = type
|
77
82
|
else
|
@@ -79,7 +84,16 @@ module PrestoModels
|
|
79
84
|
end
|
80
85
|
base_type = @name_mapping[[model_name, base_type]] || base_type
|
81
86
|
map_value_base_type = @name_mapping[[model_name, map_value_base_type]] || map_value_base_type
|
82
|
-
|
87
|
+
|
88
|
+
if generic
|
89
|
+
base_type = generic if base_type == 'T'
|
90
|
+
map_value_base_type = generic if map_value_base_type == 'T'
|
91
|
+
end
|
92
|
+
if m = GENERIC_PATTERN.match(base_type)
|
93
|
+
base_type_alias = "#{m[1]}_#{m[2]}"
|
94
|
+
end
|
95
|
+
|
96
|
+
Field.new(key, !!nullable, array, map, type, base_type, map_value_base_type, base_type_alias)
|
83
97
|
end
|
84
98
|
|
85
99
|
@models[model_name] = Model.new(model_name, fields)
|
@@ -92,9 +106,15 @@ module PrestoModels
|
|
92
106
|
return fields
|
93
107
|
end
|
94
108
|
|
95
|
-
def analyze_model(model_name, parent_model
|
109
|
+
def analyze_model(model_name, parent_model= nil, generic: nil)
|
96
110
|
return if @models[model_name] || @ignore_types.include?(model_name)
|
97
111
|
|
112
|
+
if m = GENERIC_PATTERN.match(model_name)
|
113
|
+
analyze_model(m[1], generic: m[2])
|
114
|
+
analyze_model(m[2])
|
115
|
+
return
|
116
|
+
end
|
117
|
+
|
98
118
|
path = find_class_file(model_name, parent_model)
|
99
119
|
java = File.read(path)
|
100
120
|
|
@@ -114,7 +134,7 @@ module PrestoModels
|
|
114
134
|
fields = analyze_fields(inner_model_name, m[0])
|
115
135
|
end
|
116
136
|
|
117
|
-
fields = analyze_fields(model_name, body)
|
137
|
+
fields = analyze_fields(model_name, body, generic: generic)
|
118
138
|
|
119
139
|
rescue => e
|
120
140
|
puts "Skipping model #{parent_model}/#{model_name}: #{e}"
|
@@ -214,7 +234,7 @@ module PrestoModels
|
|
214
234
|
elem_expr = convert_expression(field.base_type, field.base_type, "h")
|
215
235
|
expr << "hash[\"#{field.key}\"].map {|h| #{elem_expr} }"
|
216
236
|
else
|
217
|
-
expr << convert_expression(field.type, field.base_type, "hash[\"#{field.key}\"]")
|
237
|
+
expr << convert_expression(field.type, field.base_type_alias || field.base_type, "hash[\"#{field.key}\"]")
|
218
238
|
end
|
219
239
|
end
|
220
240
|
|