presto-client 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -0
- data/Gemfile.lock +38 -0
- data/README.md +34 -0
- data/Rakefile +13 -0
- data/lib/presto-client.rb +1 -0
- data/lib/presto/client.rb +23 -0
- data/lib/presto/client/client.rb +35 -0
- data/lib/presto/client/models.rb +194 -0
- data/lib/presto/client/query.rb +85 -0
- data/lib/presto/client/statement_client.rb +168 -0
- data/lib/presto/client/version.rb +20 -0
- data/presto-client.gemspec +28 -0
- data/presto-client.rb +420 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/statement_client_spec.rb +39 -0
- metadata +146 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,38 @@
|
|
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)
|
data/README.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Presto client library for Ruby
|
2
|
+
|
3
|
+
Presto is a distributed SQL query engine for big data:
|
4
|
+
https://github.com/facebook/presto
|
5
|
+
|
6
|
+
This is a client library for Ruby to run queries on Presto.
|
7
|
+
|
8
|
+
## Example
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
require 'presto-client'
|
12
|
+
|
13
|
+
# create a client object
|
14
|
+
client = PrestoClient::Client.new(
|
15
|
+
server: "localhost:8880",
|
16
|
+
user: "frsyuki",
|
17
|
+
catalog: "native",
|
18
|
+
schema: "default",
|
19
|
+
)
|
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|
|
31
|
+
p row
|
32
|
+
}
|
33
|
+
```
|
34
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'presto/client'
|
@@ -0,0 +1,23 @@
|
|
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
|
17
|
+
module Client
|
18
|
+
|
19
|
+
require 'presto/client/version'
|
20
|
+
require 'presto/client/client'
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,35 @@
|
|
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
|
+
|
18
|
+
require 'presto/client/models'
|
19
|
+
require 'presto/client/query'
|
20
|
+
|
21
|
+
class Client
|
22
|
+
def initialize(options)
|
23
|
+
@session = ClientSession.new(options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def query(query)
|
27
|
+
Query.start(@session, query)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.new(*args)
|
32
|
+
Client.new(*args)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,194 @@
|
|
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
|
+
|
18
|
+
class Column
|
19
|
+
attr_reader :name
|
20
|
+
attr_reader :type
|
21
|
+
|
22
|
+
def initialize(options={})
|
23
|
+
@name = options[:name]
|
24
|
+
@type = options[:type]
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.decode_hash(hash)
|
28
|
+
new(
|
29
|
+
name: hash["name"],
|
30
|
+
type: hash["type"],
|
31
|
+
)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class ClientSession
|
36
|
+
def initialize(options)
|
37
|
+
@server = options[:server]
|
38
|
+
@user = options[:user]
|
39
|
+
@source = options[:source]
|
40
|
+
@catalog = options[:catalog]
|
41
|
+
@schema = options[:schema]
|
42
|
+
@debug = !!options[:debug]
|
43
|
+
end
|
44
|
+
|
45
|
+
attr_reader :server
|
46
|
+
attr_reader :user
|
47
|
+
attr_reader :source
|
48
|
+
attr_reader :catalog
|
49
|
+
attr_reader :schema
|
50
|
+
|
51
|
+
def debug?
|
52
|
+
@debug
|
53
|
+
end
|
54
|
+
end
|
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
|
108
|
+
|
109
|
+
class StatementStats
|
110
|
+
attr_reader :state
|
111
|
+
attr_reader :scheduled
|
112
|
+
attr_reader :nodes
|
113
|
+
attr_reader :total_splits
|
114
|
+
attr_reader :queued_splits
|
115
|
+
attr_reader :running_splits
|
116
|
+
attr_reader :completed_splits
|
117
|
+
attr_reader :user_time_millis
|
118
|
+
attr_reader :cpu_time_millis
|
119
|
+
attr_reader :wall_time_millis
|
120
|
+
attr_reader :processed_rows
|
121
|
+
attr_reader :processed_bytes
|
122
|
+
#attr_reader :root_stage
|
123
|
+
|
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
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.decode_hash(hash)
|
141
|
+
new(
|
142
|
+
state: hash["state"],
|
143
|
+
scheduled: hash["scheduled"],
|
144
|
+
nodes: hash["nodes"],
|
145
|
+
total_splits: hash["totalSplits"],
|
146
|
+
queued_splits: hash["queuedSplits"],
|
147
|
+
running_splits: hash["runningSplits"],
|
148
|
+
completed_splits: hash["completedSplits"],
|
149
|
+
user_time_millis: hash["userTimeMillis"],
|
150
|
+
cpu_time_millis: hash["cpuTimeMillis"],
|
151
|
+
wall_time_millis: hash["wallTimeMillis"],
|
152
|
+
processed_rows: hash["processedRows"],
|
153
|
+
processed_bytes: hash["processedBytes"],
|
154
|
+
#root_stage: StageStats.decode_hash(hash["rootStage"]),
|
155
|
+
)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
class QueryResults
|
160
|
+
attr_reader :id
|
161
|
+
attr_reader :info_uri
|
162
|
+
attr_reader :partial_cache_uri
|
163
|
+
attr_reader :next_uri
|
164
|
+
attr_reader :columns
|
165
|
+
attr_reader :data
|
166
|
+
attr_reader :stats
|
167
|
+
attr_reader :error
|
168
|
+
|
169
|
+
def initialize(options={})
|
170
|
+
@id = options[:id]
|
171
|
+
@info_uri = options[:info_uri]
|
172
|
+
@partial_cache_uri = options[:partial_cache_uri]
|
173
|
+
@next_uri = options[:next_uri]
|
174
|
+
@columns = options[:columns]
|
175
|
+
@data = options[:data]
|
176
|
+
@stats = options[:stats]
|
177
|
+
@error = options[:error]
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.decode_hash(hash)
|
181
|
+
new(
|
182
|
+
id: hash["id"],
|
183
|
+
info_uri: hash["infoUri"],
|
184
|
+
partial_cache_uri: hash["partialCancelUri"],
|
185
|
+
next_uri: hash["nextUri"],
|
186
|
+
columns: hash["columns"] ? hash["columns"].map {|h| Column.decode_hash(h) } : nil,
|
187
|
+
data: hash["data"],
|
188
|
+
stats: StatementStats.decode_hash(hash["stats"]),
|
189
|
+
error: hash["error"], # TODO
|
190
|
+
)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
@@ -0,0 +1,85 @@
|
|
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
|
+
|
18
|
+
require 'faraday'
|
19
|
+
require 'presto/client/models'
|
20
|
+
require 'presto/client/statement_client'
|
21
|
+
|
22
|
+
class Query
|
23
|
+
def self.start(session, query)
|
24
|
+
faraday = Faraday.new(url: "http://#{session.server}") do |faraday|
|
25
|
+
#faraday.request :url_encoded
|
26
|
+
faraday.response :logger
|
27
|
+
faraday.adapter Faraday.default_adapter
|
28
|
+
end
|
29
|
+
|
30
|
+
new StatementClient.new(faraday, session, query)
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(client)
|
34
|
+
@client = client
|
35
|
+
end
|
36
|
+
|
37
|
+
def wait_for_data
|
38
|
+
while @client.has_next? && @client.current_results.data == nil
|
39
|
+
@client.advance
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private :wait_for_data
|
44
|
+
|
45
|
+
def columns
|
46
|
+
wait_for_data
|
47
|
+
|
48
|
+
raise_error unless @client.query_succeeded?
|
49
|
+
|
50
|
+
return @client.current_results.columns
|
51
|
+
end
|
52
|
+
|
53
|
+
def each_row(&block)
|
54
|
+
wait_for_data
|
55
|
+
|
56
|
+
raise_error unless @client.query_succeeded?
|
57
|
+
|
58
|
+
if self.columns == nil
|
59
|
+
raise "Query #{@client.current_results.id} has no columns"
|
60
|
+
end
|
61
|
+
|
62
|
+
begin
|
63
|
+
if data = @client.current_results.data
|
64
|
+
data.each(&block)
|
65
|
+
end
|
66
|
+
@client.advance
|
67
|
+
end while @client.has_next?
|
68
|
+
end
|
69
|
+
|
70
|
+
def raise_error
|
71
|
+
if @client.closed?
|
72
|
+
raise "Query aborted by user"
|
73
|
+
elsif @client.exception?
|
74
|
+
raise "Query is gone: #{@client.exception}"
|
75
|
+
elsif @client.query_failed?
|
76
|
+
results = @client.current_results
|
77
|
+
# TODO error location
|
78
|
+
raise "Query #{results.id} failed: #{results.error}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private :raise_error
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,168 @@
|
|
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
|
+
|
18
|
+
require 'multi_json'
|
19
|
+
require 'presto/client/models'
|
20
|
+
|
21
|
+
module PrestoHeaders
|
22
|
+
PRESTO_USER = "X-Presto-User"
|
23
|
+
PRESTO_SOURCE = "X-Presto-Source"
|
24
|
+
PRESTO_CATALOG = "X-Presto-Catalog"
|
25
|
+
PRESTO_SCHEMA = "X-Presto-Schema"
|
26
|
+
|
27
|
+
PRESTO_CURRENT_STATE = "X-Presto-Current-State"
|
28
|
+
PRESTO_MAX_WAIT = "X-Presto-Max-Wait"
|
29
|
+
PRESTO_MAX_SIZE = "X-Presto-Max-Size"
|
30
|
+
PRESTO_PAGE_SEQUENCE_ID = "X-Presto-Page-Sequence-Id"
|
31
|
+
end
|
32
|
+
|
33
|
+
class StatementClient
|
34
|
+
HEADERS = {
|
35
|
+
"User-Agent" => "presto-ruby/#{VERSION}"
|
36
|
+
}
|
37
|
+
|
38
|
+
def initialize(faraday, session, query)
|
39
|
+
@faraday = faraday
|
40
|
+
@faraday.headers.merge!(HEADERS)
|
41
|
+
|
42
|
+
@session = session
|
43
|
+
@query = query
|
44
|
+
@closed = false
|
45
|
+
@exception = nil
|
46
|
+
post_query_request!
|
47
|
+
end
|
48
|
+
|
49
|
+
def post_query_request!
|
50
|
+
response = @faraday.post do |req|
|
51
|
+
req.url "/v1/statement"
|
52
|
+
|
53
|
+
if v = @session.user
|
54
|
+
req.headers[PrestoHeaders::PRESTO_USER] = v
|
55
|
+
end
|
56
|
+
if v = @session.source
|
57
|
+
req.headers[PrestoHeaders::PRESTO_SOURCE] = v
|
58
|
+
end
|
59
|
+
if v = @session.catalog
|
60
|
+
req.headers[PrestoHeaders::PRESTO_CATALOG] = v
|
61
|
+
end
|
62
|
+
if v = @session.schema
|
63
|
+
req.headers[PrestoHeaders::PRESTO_SCHEMA] = v
|
64
|
+
end
|
65
|
+
|
66
|
+
req.body = @query
|
67
|
+
end
|
68
|
+
|
69
|
+
# TODO error handling
|
70
|
+
if response.status != 200
|
71
|
+
raise "Failed to start query: #{response.body}" # TODO error class
|
72
|
+
end
|
73
|
+
|
74
|
+
body = response.body
|
75
|
+
hash = MultiJson.load(body)
|
76
|
+
@results = QueryResults.decode_hash(hash)
|
77
|
+
end
|
78
|
+
|
79
|
+
private :post_query_request!
|
80
|
+
|
81
|
+
attr_reader :query
|
82
|
+
|
83
|
+
def debug?
|
84
|
+
@session.debug?
|
85
|
+
end
|
86
|
+
|
87
|
+
def closed?
|
88
|
+
@closed
|
89
|
+
end
|
90
|
+
|
91
|
+
attr_reader :exception
|
92
|
+
|
93
|
+
def exception?
|
94
|
+
@exception
|
95
|
+
end
|
96
|
+
|
97
|
+
def query_failed?
|
98
|
+
@results.error != nil
|
99
|
+
end
|
100
|
+
|
101
|
+
def query_succeeded?
|
102
|
+
@results.error == nil && !@exception && !@closed
|
103
|
+
end
|
104
|
+
|
105
|
+
def current_results
|
106
|
+
@results
|
107
|
+
end
|
108
|
+
|
109
|
+
def has_next?
|
110
|
+
!!@results.next_uri
|
111
|
+
end
|
112
|
+
|
113
|
+
def advance
|
114
|
+
if closed? || !has_next?
|
115
|
+
return false
|
116
|
+
end
|
117
|
+
uri = @results.next_uri
|
118
|
+
|
119
|
+
start = Time.now
|
120
|
+
attempts = 0
|
121
|
+
|
122
|
+
begin
|
123
|
+
begin
|
124
|
+
response = @faraday.get do |req|
|
125
|
+
req.url uri
|
126
|
+
end
|
127
|
+
rescue => e
|
128
|
+
@exception = e
|
129
|
+
raise @exception
|
130
|
+
end
|
131
|
+
|
132
|
+
if response.status == 200 && !response.body.to_s.empty?
|
133
|
+
@results = QueryResults.decode_hash(MultiJson.load(response.body))
|
134
|
+
return true
|
135
|
+
end
|
136
|
+
|
137
|
+
if response.status != 503 # retry on 503 Service Unavailable
|
138
|
+
# deterministic error
|
139
|
+
@exception = StandardError.new("Error fetching next at #{uri} returned #{response.status}: #{response.body}") # TODO error class
|
140
|
+
raise @exception
|
141
|
+
end
|
142
|
+
|
143
|
+
attempts += 1
|
144
|
+
sleep attempts * 0.1
|
145
|
+
end while (Time.now - start) < 2*60*60 && !@closed
|
146
|
+
|
147
|
+
@exception = StandardError.new("Error fetching next") # TODO error class
|
148
|
+
raise @exception
|
149
|
+
end
|
150
|
+
|
151
|
+
def close
|
152
|
+
return if @closed
|
153
|
+
|
154
|
+
# cancel running statement
|
155
|
+
if uri = @results.next_uri
|
156
|
+
# TODO error handling
|
157
|
+
# TODO make async reqeust and ignore response
|
158
|
+
@faraday.delete do |req|
|
159
|
+
req.url uri
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
@closed = true
|
164
|
+
nil
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
@@ -0,0 +1,20 @@
|
|
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
|
17
|
+
module Client
|
18
|
+
VERSION = "0.1.0"
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.expand_path 'lib/presto/client/version', File.dirname(__FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = "presto-client"
|
5
|
+
gem.version = Presto::Client::VERSION
|
6
|
+
|
7
|
+
gem.authors = ["Sadayuki Furuhashi"]
|
8
|
+
gem.email = ["sf@treasure-data.com"]
|
9
|
+
gem.description = %q{Presto client library}
|
10
|
+
gem.summary = %q{Presto client library}
|
11
|
+
gem.homepage = "https://github.com/treasure-data/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.3"
|
21
|
+
|
22
|
+
gem.add_dependency "faraday", ["~> 0.8.8"]
|
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
|
+
end
|
data/presto-client.rb
ADDED
@@ -0,0 +1,420 @@
|
|
1
|
+
|
2
|
+
module PrestoClient
|
3
|
+
VERSION = "0.1.0"
|
4
|
+
|
5
|
+
require 'faraday'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
class ClientSession
|
9
|
+
def initialize(options)
|
10
|
+
@server = options[:server]
|
11
|
+
@user = options[:user]
|
12
|
+
@source = options[:source]
|
13
|
+
@catalog = options[:catalog]
|
14
|
+
@schema = options[:schema]
|
15
|
+
@debug = !!options[:debug]
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :server
|
19
|
+
attr_reader :user
|
20
|
+
attr_reader :source
|
21
|
+
attr_reader :catalog
|
22
|
+
attr_reader :schema
|
23
|
+
|
24
|
+
def debug?
|
25
|
+
@debug
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
#class StageStats
|
30
|
+
# attr_reader :stage_id
|
31
|
+
# attr_reader :state
|
32
|
+
# attr_reader :done
|
33
|
+
# attr_reader :nodes
|
34
|
+
# attr_reader :total_splits
|
35
|
+
# attr_reader :queued_splits
|
36
|
+
# attr_reader :running_splits
|
37
|
+
# attr_reader :completed_splits
|
38
|
+
# attr_reader :user_time_millis
|
39
|
+
# attr_reader :cpu_time_millis
|
40
|
+
# attr_reader :wall_time_millis
|
41
|
+
# attr_reader :processed_rows
|
42
|
+
# attr_reader :processed_bytes
|
43
|
+
# attr_reader :sub_stages
|
44
|
+
#
|
45
|
+
# def initialize(options={})
|
46
|
+
# @stage_id = options[:stage_id]
|
47
|
+
# @state = options[:state]
|
48
|
+
# @done = options[:done]
|
49
|
+
# @nodes = options[:nodes]
|
50
|
+
# @total_splits = options[:total_splits]
|
51
|
+
# @queued_splits = options[:queued_splits]
|
52
|
+
# @running_splits = options[:running_splits]
|
53
|
+
# @completed_splits = options[:completed_splits]
|
54
|
+
# @user_time_millis = options[:user_time_millis]
|
55
|
+
# @cpu_time_millis = options[:cpu_time_millis]
|
56
|
+
# @wall_time_millis = options[:wall_time_millis]
|
57
|
+
# @processed_rows = options[:processed_rows]
|
58
|
+
# @processed_bytes = options[:processed_bytes]
|
59
|
+
# @sub_stages = options[:sub_stages]
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# def self.decode_hash(hash)
|
63
|
+
# new(
|
64
|
+
# stage_id: hash["stageId"],
|
65
|
+
# state: hash["state"],
|
66
|
+
# done: hash["done"],
|
67
|
+
# nodes: hash["nodes"],
|
68
|
+
# total_splits: hash["totalSplits"],
|
69
|
+
# queued_splits: hash["queuedSplits"],
|
70
|
+
# running_splits: hash["runningSplits"],
|
71
|
+
# completed_splits: hash["completedSplits"],
|
72
|
+
# user_time_millis: hash["userTimeMillis"],
|
73
|
+
# cpu_time_millis: hash["cpuTimeMillis"],
|
74
|
+
# wall_time_millis: hash["wallTimeMillis"],
|
75
|
+
# processed_rows: hash["processedRows"],
|
76
|
+
# processed_bytes: hash["processedBytes"],
|
77
|
+
# sub_stages: hash["subStages"].map {|h| StageStats.decode_hash(h) },
|
78
|
+
# )
|
79
|
+
# end
|
80
|
+
#end
|
81
|
+
|
82
|
+
class StatementStats
|
83
|
+
attr_reader :state
|
84
|
+
attr_reader :scheduled
|
85
|
+
attr_reader :nodes
|
86
|
+
attr_reader :total_splits
|
87
|
+
attr_reader :queued_splits
|
88
|
+
attr_reader :running_splits
|
89
|
+
attr_reader :completed_splits
|
90
|
+
attr_reader :user_time_millis
|
91
|
+
attr_reader :cpu_time_millis
|
92
|
+
attr_reader :wall_time_millis
|
93
|
+
attr_reader :processed_rows
|
94
|
+
attr_reader :processed_bytes
|
95
|
+
#attr_reader :root_stage
|
96
|
+
|
97
|
+
def initialize(options={})
|
98
|
+
@state = state
|
99
|
+
@scheduled = scheduled
|
100
|
+
@nodes = nodes
|
101
|
+
@total_splits = total_splits
|
102
|
+
@queued_splits = queued_splits
|
103
|
+
@running_splits = running_splits
|
104
|
+
@completed_splits = completed_splits
|
105
|
+
@user_time_millis = user_time_millis
|
106
|
+
@cpu_time_millis = cpu_time_millis
|
107
|
+
@wall_time_millis = wall_time_millis
|
108
|
+
@processed_rows = processed_rows
|
109
|
+
@processed_bytes = processed_bytes
|
110
|
+
#@root_stage = root_stage
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.decode_hash(hash)
|
114
|
+
new(
|
115
|
+
state: hash["state"],
|
116
|
+
scheduled: hash["scheduled"],
|
117
|
+
nodes: hash["nodes"],
|
118
|
+
total_splits: hash["totalSplits"],
|
119
|
+
queued_splits: hash["queuedSplits"],
|
120
|
+
running_splits: hash["runningSplits"],
|
121
|
+
completed_splits: hash["completedSplits"],
|
122
|
+
user_time_millis: hash["userTimeMillis"],
|
123
|
+
cpu_time_millis: hash["cpuTimeMillis"],
|
124
|
+
wall_time_millis: hash["wallTimeMillis"],
|
125
|
+
processed_rows: hash["processedRows"],
|
126
|
+
processed_bytes: hash["processedBytes"],
|
127
|
+
#root_stage: StageStats.decode_hash(hash["rootStage"]),
|
128
|
+
)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class Column
|
133
|
+
attr_reader :name
|
134
|
+
attr_reader :type
|
135
|
+
|
136
|
+
def initialize(options={})
|
137
|
+
@name = options[:name]
|
138
|
+
@type = options[:type]
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.decode_hash(hash)
|
142
|
+
new(
|
143
|
+
name: hash["name"],
|
144
|
+
type: hash["type"],
|
145
|
+
)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class QueryResults
|
150
|
+
attr_reader :id
|
151
|
+
attr_reader :info_uri
|
152
|
+
attr_reader :partial_cache_uri
|
153
|
+
attr_reader :next_uri
|
154
|
+
attr_reader :columns
|
155
|
+
attr_reader :data
|
156
|
+
attr_reader :stats
|
157
|
+
attr_reader :error
|
158
|
+
|
159
|
+
def initialize(options={})
|
160
|
+
@id = options[:id]
|
161
|
+
@info_uri = options[:info_uri]
|
162
|
+
@partial_cache_uri = options[:partial_cache_uri]
|
163
|
+
@next_uri = options[:next_uri]
|
164
|
+
@columns = options[:columns]
|
165
|
+
@data = options[:data]
|
166
|
+
@stats = options[:stats]
|
167
|
+
@error = options[:error]
|
168
|
+
end
|
169
|
+
|
170
|
+
def self.decode_hash(hash)
|
171
|
+
new(
|
172
|
+
id: hash["id"],
|
173
|
+
info_uri: hash["infoUri"],
|
174
|
+
partial_cache_uri: hash["partialCancelUri"],
|
175
|
+
next_uri: hash["nextUri"],
|
176
|
+
columns: hash["columns"] ? hash["columns"].map {|h| Column.decode_hash(h) } : nil,
|
177
|
+
data: hash["data"]
|
178
|
+
stats: StatementStats.decode_hash(hash["stats"]),
|
179
|
+
error: hash["error"], # TODO
|
180
|
+
)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
module PrestoHeaders
|
185
|
+
PRESTO_USER = "X-Presto-User"
|
186
|
+
PRESTO_SOURCE = "X-Presto-Source"
|
187
|
+
PRESTO_CATALOG = "X-Presto-Catalog"
|
188
|
+
PRESTO_SCHEMA = "X-Presto-Schema"
|
189
|
+
|
190
|
+
PRESTO_CURRENT_STATE = "X-Presto-Current-State"
|
191
|
+
PRESTO_MAX_WAIT = "X-Presto-Max-Wait"
|
192
|
+
PRESTO_MAX_SIZE = "X-Presto-Max-Size"
|
193
|
+
PRESTO_PAGE_SEQUENCE_ID = "X-Presto-Page-Sequence-Id"
|
194
|
+
end
|
195
|
+
|
196
|
+
class StatementClient
|
197
|
+
HEADERS = {
|
198
|
+
"User-Agent" => "presto-ruby/#{VERSION}"
|
199
|
+
}
|
200
|
+
|
201
|
+
def initialize(faraday, session, query)
|
202
|
+
@faraday = faraday
|
203
|
+
@faraday.headers.merge!(HEADERS)
|
204
|
+
|
205
|
+
@session = session
|
206
|
+
@query = query
|
207
|
+
@closed = false
|
208
|
+
@exception = nil
|
209
|
+
post_query_request!
|
210
|
+
end
|
211
|
+
|
212
|
+
def post_query_request!
|
213
|
+
response = @faraday.post do |req|
|
214
|
+
req.url "/v1/statement"
|
215
|
+
|
216
|
+
if v = @session.user
|
217
|
+
req.headers[PrestoHeaders::PRESTO_USER] = v
|
218
|
+
end
|
219
|
+
if v = @session.source
|
220
|
+
req.headers[PrestoHeaders::PRESTO_SOURCE] = v
|
221
|
+
end
|
222
|
+
if catalog = @session.catalog
|
223
|
+
req.headers[PrestoHeaders::PRESTO_CATALOG] = catalog
|
224
|
+
end
|
225
|
+
if v = @session.schema
|
226
|
+
req.headers[PrestoHeaders::PRESTO_SCHEMA] = v
|
227
|
+
end
|
228
|
+
|
229
|
+
req.body = @query
|
230
|
+
end
|
231
|
+
|
232
|
+
# TODO error handling
|
233
|
+
if response.status != 200
|
234
|
+
raise "Failed to start query: #{response.body}" # TODO error class
|
235
|
+
end
|
236
|
+
|
237
|
+
body = response.body
|
238
|
+
hash = JSON.parse(body)
|
239
|
+
@results = QueryResults.decode_hash(hash)
|
240
|
+
end
|
241
|
+
|
242
|
+
private :post_query_request!
|
243
|
+
|
244
|
+
attr_reader :query
|
245
|
+
|
246
|
+
def debug?
|
247
|
+
@session.debug?
|
248
|
+
end
|
249
|
+
|
250
|
+
def closed?
|
251
|
+
@closed
|
252
|
+
end
|
253
|
+
|
254
|
+
attr_reader :exception
|
255
|
+
|
256
|
+
def exception?
|
257
|
+
@exception
|
258
|
+
end
|
259
|
+
|
260
|
+
def query_failed?
|
261
|
+
@results.error != nil
|
262
|
+
end
|
263
|
+
|
264
|
+
def query_succeeded?
|
265
|
+
@results.error == nil && !@exception && !@closed
|
266
|
+
end
|
267
|
+
|
268
|
+
def current_results
|
269
|
+
@results
|
270
|
+
end
|
271
|
+
|
272
|
+
def has_next?
|
273
|
+
!!@results.next_uri
|
274
|
+
end
|
275
|
+
|
276
|
+
def advance
|
277
|
+
if closed? || !has_next?
|
278
|
+
return false
|
279
|
+
end
|
280
|
+
uri = @results.next_uri
|
281
|
+
|
282
|
+
start = Time.now
|
283
|
+
attempts = 0
|
284
|
+
|
285
|
+
begin
|
286
|
+
begin
|
287
|
+
response = @faraday.get do |req|
|
288
|
+
req.url uri
|
289
|
+
end
|
290
|
+
rescue => e
|
291
|
+
@exception = e
|
292
|
+
raise @exception
|
293
|
+
end
|
294
|
+
|
295
|
+
if response.status == 200 && !response.body.to_s.empty?
|
296
|
+
@results = QueryResults.decode_hash(JSON.parse(response.body))
|
297
|
+
return true
|
298
|
+
end
|
299
|
+
|
300
|
+
if response.status != 503 # retry on 503 Service Unavailable
|
301
|
+
# deterministic error
|
302
|
+
@exception = StandardError.new("Error fetching next at #{uri} returned #{response.status}: #{response.body}") # TODO error class
|
303
|
+
raise @exception
|
304
|
+
end
|
305
|
+
|
306
|
+
attempts += 1
|
307
|
+
sleep attempts * 0.1
|
308
|
+
end while (Time.now - start) < 2*60*60 && !@closed
|
309
|
+
|
310
|
+
@exception = StandardError.new("Error fetching next") # TODO error class
|
311
|
+
raise @exception
|
312
|
+
end
|
313
|
+
|
314
|
+
def close
|
315
|
+
return if @closed
|
316
|
+
|
317
|
+
# cancel running statement
|
318
|
+
if uri = @results.next_uri
|
319
|
+
# TODO error handling
|
320
|
+
# TODO make async reqeust and ignore response
|
321
|
+
@faraday.delete do |req|
|
322
|
+
req.url uri
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
@closed = true
|
327
|
+
nil
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
class Query
|
332
|
+
def self.start(session, query)
|
333
|
+
faraday = Faraday.new(url: "http://#{session.server}") do |faraday|
|
334
|
+
faraday.request :url_encoded
|
335
|
+
faraday.response :logger
|
336
|
+
faraday.adapter Faraday.default_adapter
|
337
|
+
end
|
338
|
+
|
339
|
+
new StatementClient.new(faraday, session, query)
|
340
|
+
end
|
341
|
+
|
342
|
+
def initialize(client)
|
343
|
+
@client = client
|
344
|
+
end
|
345
|
+
|
346
|
+
def wait_for_data
|
347
|
+
while @client.has_next? && @client.current_results.data == nil
|
348
|
+
@client.advance
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
private :wait_for_data
|
353
|
+
|
354
|
+
def columns
|
355
|
+
wait_for_data
|
356
|
+
|
357
|
+
raise_error unless @client.query_succeeded?
|
358
|
+
|
359
|
+
return @client.current_results.columns
|
360
|
+
end
|
361
|
+
|
362
|
+
def each_row(&block)
|
363
|
+
wait_for_data
|
364
|
+
|
365
|
+
raise_error unless @client.query_succeeded?
|
366
|
+
|
367
|
+
if self.columns == nil
|
368
|
+
raise "Query #{@client.current_results.id} has no columns"
|
369
|
+
end
|
370
|
+
|
371
|
+
begin
|
372
|
+
if data = @client.current_results.data
|
373
|
+
data.each(&block)
|
374
|
+
end
|
375
|
+
@client.advance
|
376
|
+
end while @client.has_next?
|
377
|
+
end
|
378
|
+
|
379
|
+
def raise_error
|
380
|
+
if @client.closed?
|
381
|
+
raise "Query aborted by user"
|
382
|
+
elsif @client.exception?
|
383
|
+
raise "Query is gone: #{@client.exception}"
|
384
|
+
elsif @client.query_failed?
|
385
|
+
results = @client.current_results
|
386
|
+
# TODO error location
|
387
|
+
raise "Query #{results.id} failed: #{results.error}"
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
private :raise_error
|
392
|
+
end
|
393
|
+
|
394
|
+
class Client
|
395
|
+
def initialize(options)
|
396
|
+
@session = ClientSession.new(options)
|
397
|
+
end
|
398
|
+
|
399
|
+
def query(query)
|
400
|
+
Query.start(@session, query)
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
require 'pp'
|
406
|
+
|
407
|
+
client = PrestoClient::Client.new(
|
408
|
+
server: "localhost:8880",
|
409
|
+
user: "frsyuki",
|
410
|
+
catalog: "native",
|
411
|
+
schema: "default",
|
412
|
+
debug: true
|
413
|
+
)
|
414
|
+
|
415
|
+
q = client.query("select * from sys.query")
|
416
|
+
p q.columns
|
417
|
+
q.each_row {|row|
|
418
|
+
p row
|
419
|
+
}
|
420
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
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 'json'
|
12
|
+
require 'webmock/rspec'
|
13
|
+
|
14
|
+
require 'presto-client'
|
15
|
+
include Presto::Client
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Presto::Client::StatementClient do
|
4
|
+
let :session do
|
5
|
+
session = ClientSession.new(
|
6
|
+
server: "localhost",
|
7
|
+
user: "frsyuki",
|
8
|
+
catalog: "native",
|
9
|
+
schema: "default",
|
10
|
+
debug: true,
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
let :query do
|
15
|
+
"select * from sys.node"
|
16
|
+
end
|
17
|
+
|
18
|
+
let :response_json do
|
19
|
+
{
|
20
|
+
id: "queryid",
|
21
|
+
stats: {}
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
it do
|
26
|
+
stub_request(:post, "localhost/v1/statement").
|
27
|
+
with(body: query,
|
28
|
+
headers: {
|
29
|
+
"User-Agent" => "presto-ruby/#{VERSION}",
|
30
|
+
"X-Presto-Catalog" => session.catalog,
|
31
|
+
"X-Presto-Schema" => session.schema,
|
32
|
+
"X-Presto-User" => session.user,
|
33
|
+
}).to_return(body: response_json.to_json)
|
34
|
+
|
35
|
+
faraday = Faraday.new(url: "http://localhost")
|
36
|
+
StatementClient.new(faraday, session, query)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
metadata
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: presto-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Sadayuki Furuhashi
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-01-07 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: faraday
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.8.8
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.8.8
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: multi_json
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.9.2
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.9.2
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.13.0
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.13.0
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: webmock
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 1.16.1
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.16.1
|
94
|
+
description: Presto client library
|
95
|
+
email:
|
96
|
+
- sf@treasure-data.com
|
97
|
+
executables: []
|
98
|
+
extensions: []
|
99
|
+
extra_rdoc_files: []
|
100
|
+
files:
|
101
|
+
- Gemfile
|
102
|
+
- Gemfile.lock
|
103
|
+
- README.md
|
104
|
+
- Rakefile
|
105
|
+
- lib/presto-client.rb
|
106
|
+
- lib/presto/client.rb
|
107
|
+
- lib/presto/client/client.rb
|
108
|
+
- lib/presto/client/models.rb
|
109
|
+
- lib/presto/client/query.rb
|
110
|
+
- lib/presto/client/statement_client.rb
|
111
|
+
- lib/presto/client/version.rb
|
112
|
+
- presto-client.gemspec
|
113
|
+
- presto-client.rb
|
114
|
+
- spec/spec_helper.rb
|
115
|
+
- spec/statement_client_spec.rb
|
116
|
+
homepage: https://github.com/treasure-data/presto-client-ruby
|
117
|
+
licenses:
|
118
|
+
- Apache 2.0
|
119
|
+
post_install_message:
|
120
|
+
rdoc_options: []
|
121
|
+
require_paths:
|
122
|
+
- lib
|
123
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
124
|
+
none: false
|
125
|
+
requirements:
|
126
|
+
- - ! '>='
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: 1.9.3
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
none: false
|
131
|
+
requirements:
|
132
|
+
- - ! '>='
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
segments:
|
136
|
+
- 0
|
137
|
+
hash: 1002266789771022757
|
138
|
+
requirements: []
|
139
|
+
rubyforge_project:
|
140
|
+
rubygems_version: 1.8.23
|
141
|
+
signing_key:
|
142
|
+
specification_version: 3
|
143
|
+
summary: Presto client library
|
144
|
+
test_files:
|
145
|
+
- spec/spec_helper.rb
|
146
|
+
- spec/statement_client_spec.rb
|