presto-client 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ Gemfile.lock
2
+ tmp/*
3
+ pkg/*
data/ChangeLog ADDED
@@ -0,0 +1,13 @@
1
+
2
+ 2014-01-22 version 0.2.0:
3
+
4
+ * Added Query#cancel
5
+ * Added Query#close
6
+ * Added Client#run
7
+ * Changed required_ruby_version from 1.9.3 to 1.9.1
8
+
9
+
10
+ 2014-01-07 version 0.1.0:
11
+
12
+ * First release
13
+
data/README.md CHANGED
@@ -10,25 +10,32 @@ This is a client library for Ruby to run queries on Presto.
10
10
  ```ruby
11
11
  require 'presto-client'
12
12
 
13
- # create a client object
14
- client = PrestoClient::Client.new(
13
+ # create a client object:
14
+ client = Presto::Client.new(
15
15
  server: "localhost:8880",
16
16
  user: "frsyuki",
17
17
  catalog: "native",
18
18
  schema: "default",
19
19
  )
20
20
 
21
- # start running a query on presto
22
- q = client.query("select * from sys.query")
23
-
24
- # wait for completion and get columns
25
- q.columns.each {|column|
26
- puts "column: #{column.name}.#{column.type}"
27
- }
28
-
29
- # get query results
30
- q.each_row {|row|
21
+ # run a query and get results:
22
+ columns, rows = client.run("select * from sys.node")
23
+ rows.each {|row|
31
24
  p row
32
25
  }
26
+
27
+ # another way to run a query and fetch results streamingly:
28
+ # start running a query on presto
29
+ client.query("select * from sys.node") do |q|
30
+ # wait for completion and get columns
31
+ q.columns.each {|column|
32
+ puts "column: #{column.name}.#{column.type}"
33
+ }
34
+
35
+ # get query results
36
+ q.each_row {|row|
37
+ p row
38
+ }
39
+ end
33
40
  ```
34
41
 
@@ -23,8 +23,30 @@ module Presto::Client
23
23
  @session = ClientSession.new(options)
24
24
  end
25
25
 
26
- def query(query)
27
- Query.start(@session, query)
26
+ def query(query, &block)
27
+ if block
28
+ q = Query.start(@session, query)
29
+ begin
30
+ yield q
31
+ ensure
32
+ q.close
33
+ end
34
+ else
35
+ return Query.start(@session, query)
36
+ end
37
+ end
38
+
39
+ def run(query)
40
+ q = Query.start(@session, query)
41
+ begin
42
+ columns = q.columns
43
+ if columns.empty?
44
+ return [], []
45
+ end
46
+ return columns, q.rows
47
+ ensure
48
+ q.close
49
+ end
28
50
  end
29
51
  end
30
52
 
@@ -0,0 +1,42 @@
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 Presto::Client
17
+ class PrestoError < StandardError
18
+ end
19
+
20
+ class PrestoHttpError < PrestoError
21
+ def initialize(status, message)
22
+ super(message)
23
+ @status = status
24
+ end
25
+
26
+ attr_reader :status
27
+ end
28
+
29
+ class PrestoClientError < PrestoError
30
+ end
31
+
32
+ class PrestoQueryError < PrestoError
33
+ def initialize(message, query_id, error_code, failure_info)
34
+ super(message)
35
+ @query_id = query_id
36
+ @error_code = error_code
37
+ @failure_info = failure_info
38
+ end
39
+
40
+ attr_reader :error_code, :failure_info
41
+ end
42
+ end
@@ -53,58 +53,58 @@ module Presto::Client
53
53
  end
54
54
  end
55
55
 
56
- #class StageStats
57
- # attr_reader :stage_id
58
- # attr_reader :state
59
- # attr_reader :done
60
- # attr_reader :nodes
61
- # attr_reader :total_splits
62
- # attr_reader :queued_splits
63
- # attr_reader :running_splits
64
- # attr_reader :completed_splits
65
- # attr_reader :user_time_millis
66
- # attr_reader :cpu_time_millis
67
- # attr_reader :wall_time_millis
68
- # attr_reader :processed_rows
69
- # attr_reader :processed_bytes
70
- # attr_reader :sub_stages
71
- #
72
- # def initialize(options={})
73
- # @stage_id = options[:stage_id]
74
- # @state = options[:state]
75
- # @done = options[:done]
76
- # @nodes = options[:nodes]
77
- # @total_splits = options[:total_splits]
78
- # @queued_splits = options[:queued_splits]
79
- # @running_splits = options[:running_splits]
80
- # @completed_splits = options[:completed_splits]
81
- # @user_time_millis = options[:user_time_millis]
82
- # @cpu_time_millis = options[:cpu_time_millis]
83
- # @wall_time_millis = options[:wall_time_millis]
84
- # @processed_rows = options[:processed_rows]
85
- # @processed_bytes = options[:processed_bytes]
86
- # @sub_stages = options[:sub_stages]
87
- # end
88
- #
89
- # def self.decode_hash(hash)
90
- # new(
91
- # stage_id: hash["stageId"],
92
- # state: hash["state"],
93
- # done: hash["done"],
94
- # nodes: hash["nodes"],
95
- # total_splits: hash["totalSplits"],
96
- # queued_splits: hash["queuedSplits"],
97
- # running_splits: hash["runningSplits"],
98
- # completed_splits: hash["completedSplits"],
99
- # user_time_millis: hash["userTimeMillis"],
100
- # cpu_time_millis: hash["cpuTimeMillis"],
101
- # wall_time_millis: hash["wallTimeMillis"],
102
- # processed_rows: hash["processedRows"],
103
- # processed_bytes: hash["processedBytes"],
104
- # sub_stages: hash["subStages"].map {|h| StageStats.decode_hash(h) },
105
- # )
106
- # end
107
- #end
56
+ class StageStats
57
+ attr_reader :stage_id
58
+ attr_reader :state
59
+ attr_reader :done
60
+ attr_reader :nodes
61
+ attr_reader :total_splits
62
+ attr_reader :queued_splits
63
+ attr_reader :running_splits
64
+ attr_reader :completed_splits
65
+ attr_reader :user_time_millis
66
+ attr_reader :cpu_time_millis
67
+ attr_reader :wall_time_millis
68
+ attr_reader :processed_rows
69
+ attr_reader :processed_bytes
70
+ attr_reader :sub_stages
71
+
72
+ def initialize(options={})
73
+ @stage_id = options[:stage_id]
74
+ @state = options[:state]
75
+ @done = options[:done]
76
+ @nodes = options[:nodes]
77
+ @total_splits = options[:total_splits]
78
+ @queued_splits = options[:queued_splits]
79
+ @running_splits = options[:running_splits]
80
+ @completed_splits = options[:completed_splits]
81
+ @user_time_millis = options[:user_time_millis]
82
+ @cpu_time_millis = options[:cpu_time_millis]
83
+ @wall_time_millis = options[:wall_time_millis]
84
+ @processed_rows = options[:processed_rows]
85
+ @processed_bytes = options[:processed_bytes]
86
+ @sub_stages = options[:sub_stages]
87
+ end
88
+
89
+ def self.decode_hash(hash)
90
+ new(
91
+ stage_id: hash["stageId"],
92
+ state: hash["state"],
93
+ done: hash["done"],
94
+ nodes: hash["nodes"],
95
+ total_splits: hash["totalSplits"],
96
+ queued_splits: hash["queuedSplits"],
97
+ running_splits: hash["runningSplits"],
98
+ completed_splits: hash["completedSplits"],
99
+ user_time_millis: hash["userTimeMillis"],
100
+ cpu_time_millis: hash["cpuTimeMillis"],
101
+ wall_time_millis: hash["wallTimeMillis"],
102
+ processed_rows: hash["processedRows"],
103
+ processed_bytes: hash["processedBytes"],
104
+ sub_stages: hash["subStages"] && hash["subStages"].map {|h| StageStats.decode_hash(h) },
105
+ )
106
+ end
107
+ end
108
108
 
109
109
  class StatementStats
110
110
  attr_reader :state
@@ -119,22 +119,22 @@ module Presto::Client
119
119
  attr_reader :wall_time_millis
120
120
  attr_reader :processed_rows
121
121
  attr_reader :processed_bytes
122
- #attr_reader :root_stage
122
+ attr_reader :root_stage
123
123
 
124
124
  def initialize(options={})
125
- @state = state
126
- @scheduled = scheduled
127
- @nodes = nodes
128
- @total_splits = total_splits
129
- @queued_splits = queued_splits
130
- @running_splits = running_splits
131
- @completed_splits = completed_splits
132
- @user_time_millis = user_time_millis
133
- @cpu_time_millis = cpu_time_millis
134
- @wall_time_millis = wall_time_millis
135
- @processed_rows = processed_rows
136
- @processed_bytes = processed_bytes
137
- #@root_stage = root_stage
125
+ @state = options[:state]
126
+ @scheduled = options[:scheduled]
127
+ @nodes = options[:nodes]
128
+ @total_splits = options[:total_splits]
129
+ @queued_splits = options[:queued_splits]
130
+ @running_splits = options[:running_splits]
131
+ @completed_splits = options[:completed_splits]
132
+ @user_time_millis = options[:user_time_millis]
133
+ @cpu_time_millis = options[:cpu_time_millis]
134
+ @wall_time_millis = options[:wall_time_millis]
135
+ @processed_rows = options[:processed_rows]
136
+ @processed_bytes = options[:processed_bytes]
137
+ @root_stage = options[:root_stage]
138
138
  end
139
139
 
140
140
  def self.decode_hash(hash)
@@ -151,7 +151,79 @@ module Presto::Client
151
151
  wall_time_millis: hash["wallTimeMillis"],
152
152
  processed_rows: hash["processedRows"],
153
153
  processed_bytes: hash["processedBytes"],
154
- #root_stage: StageStats.decode_hash(hash["rootStage"]),
154
+ root_stage: hash["rootStage"] && StageStats.decode_hash(hash["rootStage"]),
155
+ )
156
+ end
157
+ end
158
+
159
+ class ErrorLocation
160
+ attr_reader :line_number
161
+ attr_reader :column_number
162
+
163
+ def initialize(options={})
164
+ @line_number = options[:line_number]
165
+ @column_number = options[:column_number]
166
+ end
167
+
168
+ def self.decode_hash(hash)
169
+ new(
170
+ line_number: hash["lineNumber"],
171
+ column_number: hash["columnNumber"],
172
+ )
173
+ end
174
+ end
175
+
176
+ class FailureInfo
177
+ attr_reader :type
178
+ attr_reader :message
179
+ attr_reader :cause
180
+ attr_reader :suppressed
181
+ attr_reader :stack
182
+ attr_reader :error_location
183
+
184
+ def initialize(options={})
185
+ @type = options[:type]
186
+ @message = options[:message]
187
+ @cause = options[:cause]
188
+ @suppressed = options[:suppressed]
189
+ @stack = options[:stack]
190
+ @error_location = options[:error_location]
191
+ end
192
+
193
+ def self.decode_hash(hash)
194
+ new(
195
+ type: hash["type"],
196
+ message: hash["message"],
197
+ cause: hash["cause"],
198
+ suppressed: hash["suppressed"] && hash["suppressed"].map {|h| FailureInfo.decode_hash(h) },
199
+ stack: hash["stack"],
200
+ error_location: hash["errorLocation"] && ErrorLocation.decode_hash(hash["errorLocation"]),
201
+ )
202
+ end
203
+ end
204
+
205
+ class QueryError
206
+ attr_reader :message
207
+ attr_reader :sql_state
208
+ attr_reader :error_code
209
+ attr_reader :error_location
210
+ attr_reader :failure_info
211
+
212
+ def initialize(options={})
213
+ @message = options[:message]
214
+ @sql_state = options[:sql_state]
215
+ @error_code = options[:error_code]
216
+ @error_location = options[:error_location]
217
+ @failure_info = options[:failure_info]
218
+ end
219
+
220
+ def self.decode_hash(hash)
221
+ new(
222
+ message: hash["message"],
223
+ sql_state: hash["sqlState"],
224
+ error_code: hash["errorCode"],
225
+ error_location: hash["errorLocation"] && ErrorLocation.decode_hash(hash["errorLocation"]),
226
+ failure_info: hash["failureInfo"] && FailureInfo.decode_hash(hash["failureInfo"]),
155
227
  )
156
228
  end
157
229
  end
@@ -183,10 +255,10 @@ module Presto::Client
183
255
  info_uri: hash["infoUri"],
184
256
  partial_cache_uri: hash["partialCancelUri"],
185
257
  next_uri: hash["nextUri"],
186
- columns: hash["columns"] ? hash["columns"].map {|h| Column.decode_hash(h) } : nil,
258
+ columns: hash["columns"] && hash["columns"].map {|h| Column.decode_hash(h) },
187
259
  data: hash["data"],
188
- stats: StatementStats.decode_hash(hash["stats"]),
189
- error: hash["error"], # TODO
260
+ stats: hash["stats"] && StatementStats.decode_hash(hash["stats"]),
261
+ error: hash["error"] && QueryError.decode_hash(hash["error"]),
190
262
  )
191
263
  end
192
264
  end
@@ -17,6 +17,7 @@ module Presto::Client
17
17
 
18
18
  require 'faraday'
19
19
  require 'presto/client/models'
20
+ require 'presto/client/errors'
20
21
  require 'presto/client/statement_client'
21
22
 
22
23
  class Query
@@ -34,6 +35,8 @@ module Presto::Client
34
35
  @client = client
35
36
  end
36
37
 
38
+ attr_reader :client
39
+
37
40
  def wait_for_data
38
41
  while @client.has_next? && @client.current_results.data == nil
39
42
  @client.advance
@@ -50,32 +53,59 @@ module Presto::Client
50
53
  return @client.current_results.columns
51
54
  end
52
55
 
56
+ def rows
57
+ rows = []
58
+ each_row_chunk {|chunk|
59
+ rows.concat(chunk)
60
+ }
61
+ return rows
62
+ end
63
+
53
64
  def each_row(&block)
65
+ each_row_chunk {|chunk|
66
+ chunk.each(&block)
67
+ }
68
+ end
69
+
70
+ def each_row_chunk(&block)
54
71
  wait_for_data
55
72
 
56
73
  raise_error unless @client.query_succeeded?
57
74
 
58
75
  if self.columns == nil
59
- raise "Query #{@client.current_results.id} has no columns"
76
+ raise PrestoError, "Query #{@client.current_results.id} has no columns"
60
77
  end
61
78
 
62
79
  begin
63
80
  if data = @client.current_results.data
64
- data.each(&block)
81
+ block.call(data)
65
82
  end
66
83
  @client.advance
67
84
  end while @client.has_next?
68
85
  end
69
86
 
87
+ def cancel
88
+ @client.cancel_leaf_stage
89
+ end
90
+
91
+ def close
92
+ @client.cancel_leaf_stage
93
+ nil
94
+ end
95
+
70
96
  def raise_error
71
97
  if @client.closed?
72
- raise "Query aborted by user"
98
+ raise PrestoClientError, "Query aborted by user"
73
99
  elsif @client.exception?
74
- raise "Query is gone: #{@client.exception}"
100
+ # query is gone
101
+ raise @client.exception
75
102
  elsif @client.query_failed?
76
103
  results = @client.current_results
77
- # TODO error location
78
- raise "Query #{results.id} failed: #{results.error}"
104
+ error = results.error
105
+ unless error
106
+ raise PrestoQueryError.new("Query #{results.id} failed: (unknown reason)", results.id, nil, nil)
107
+ end
108
+ raise PrestoQueryError.new("Query #{results.id} failed: #{error.message}", results.id, error.error_code, error.failure_info)
79
109
  end
80
110
  end
81
111
 
@@ -17,6 +17,7 @@ module Presto::Client
17
17
 
18
18
  require 'multi_json'
19
19
  require 'presto/client/models'
20
+ require 'presto/client/errors'
20
21
 
21
22
  module PrestoHeaders
22
23
  PRESTO_USER = "X-Presto-User"
@@ -68,7 +69,7 @@ module Presto::Client
68
69
 
69
70
  # TODO error handling
70
71
  if response.status != 200
71
- raise "Failed to start query: #{response.body}" # TODO error class
72
+ raise PrestoHttpError.new(response.status, "Failed to start query: #{response.body}")
72
73
  end
73
74
 
74
75
  body = response.body
@@ -136,7 +137,7 @@ module Presto::Client
136
137
 
137
138
  if response.status != 503 # retry on 503 Service Unavailable
138
139
  # deterministic error
139
- @exception = StandardError.new("Error fetching next at #{uri} returned #{response.status}: #{response.body}") # TODO error class
140
+ @exception = PrestoHttpError.new(response.status, "Error fetching next at #{uri} returned #{response.status}: #{response.body}")
140
141
  raise @exception
141
142
  end
142
143
 
@@ -144,21 +145,26 @@ module Presto::Client
144
145
  sleep attempts * 0.1
145
146
  end while (Time.now - start) < 2*60*60 && !@closed
146
147
 
147
- @exception = StandardError.new("Error fetching next") # TODO error class
148
+ @exception = PrestoHttpError.new(408, "Error fetching next due to timeout")
148
149
  raise @exception
149
150
  end
150
151
 
151
- def close
152
- return if @closed
153
-
154
- # cancel running statement
152
+ def cancel_leaf_stage
155
153
  if uri = @results.next_uri
156
- # TODO error handling
157
- # TODO make async reqeust and ignore response
158
- @faraday.delete do |req|
154
+ response = @faraday.delete do |req|
159
155
  req.url uri
160
156
  end
157
+ return response.status / 100 == 2
161
158
  end
159
+ return false
160
+ end
161
+
162
+ def close
163
+ return if @closed
164
+
165
+ # cancel running statement
166
+ # TODO make async reqeust and ignore response?
167
+ cancel_leaf_stage
162
168
 
163
169
  @closed = true
164
170
  nil
@@ -15,6 +15,6 @@
15
15
  #
16
16
  module Presto
17
17
  module Client
18
- VERSION = "0.1.0"
18
+ VERSION = "0.2.0"
19
19
  end
20
20
  end
@@ -17,7 +17,7 @@ Gem::Specification.new do |gem|
17
17
  gem.require_paths = ["lib"]
18
18
  gem.has_rdoc = false
19
19
 
20
- gem.required_ruby_version = ">= 1.9.3"
20
+ gem.required_ruby_version = ">= 1.9.1"
21
21
 
22
22
  gem.add_dependency "faraday", ["~> 0.8.8"]
23
23
  gem.add_dependency "multi_json", ["~> 1.0"]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: presto-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-01-07 00:00:00.000000000 Z
12
+ date: 2014-01-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: faraday
@@ -98,13 +98,15 @@ executables: []
98
98
  extensions: []
99
99
  extra_rdoc_files: []
100
100
  files:
101
+ - .gitignore
102
+ - ChangeLog
101
103
  - Gemfile
102
- - Gemfile.lock
103
104
  - README.md
104
105
  - Rakefile
105
106
  - lib/presto-client.rb
106
107
  - lib/presto/client.rb
107
108
  - lib/presto/client/client.rb
109
+ - lib/presto/client/errors.rb
108
110
  - lib/presto/client/models.rb
109
111
  - lib/presto/client/query.rb
110
112
  - lib/presto/client/statement_client.rb
@@ -125,7 +127,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
125
127
  requirements:
126
128
  - - ! '>='
127
129
  - !ruby/object:Gem::Version
128
- version: 1.9.3
130
+ version: 1.9.1
129
131
  required_rubygems_version: !ruby/object:Gem::Requirement
130
132
  none: false
131
133
  requirements:
@@ -134,7 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
136
  version: '0'
135
137
  segments:
136
138
  - 0
137
- hash: 1002266789771022757
139
+ hash: 453544593424709681
138
140
  requirements: []
139
141
  rubyforge_project:
140
142
  rubygems_version: 1.8.23
data/Gemfile.lock DELETED
@@ -1,38 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- presto-client (0.1.0)
5
- faraday (~> 0.8.8)
6
- multi_json (~> 1.0)
7
-
8
- GEM
9
- remote: https://rubygems.org/
10
- specs:
11
- addressable (2.3.5)
12
- crack (0.3.2)
13
- diff-lcs (1.2.4)
14
- faraday (0.8.8)
15
- multipart-post (~> 1.2.0)
16
- multi_json (1.8.2)
17
- multipart-post (1.2.0)
18
- rake (10.1.1)
19
- rspec (2.13.0)
20
- rspec-core (~> 2.13.0)
21
- rspec-expectations (~> 2.13.0)
22
- rspec-mocks (~> 2.13.0)
23
- rspec-core (2.13.1)
24
- rspec-expectations (2.13.0)
25
- diff-lcs (>= 1.1.3, < 2.0)
26
- rspec-mocks (2.13.1)
27
- webmock (1.16.1)
28
- addressable (>= 2.2.7)
29
- crack (>= 0.3.2)
30
-
31
- PLATFORMS
32
- ruby
33
-
34
- DEPENDENCIES
35
- presto-client!
36
- rake (>= 0.9.2)
37
- rspec (~> 2.13.0)
38
- webmock (~> 1.16.1)