presto-client 0.1.0 → 0.2.0

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.
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)