Skip to content

Commit 8008af5

Browse files
committed
Cast decimals to ruby BigDecimals
This is particularly a problem since [0.198](https://prestodb.io/docs/current/release/release-0.198.html), where decimal literals are now interpreted as `DECIMAL` by default, which means a query like `SELECT 1.0` was coming through as a String.
1 parent d7507f6 commit 8008af5

File tree

2 files changed

+57
-4
lines changed

2 files changed

+57
-4
lines changed

lib/presto/client/statement_client.rb

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#
1616
module Presto::Client
1717

18+
require 'bigdecimal'
1819
require 'json'
1920
require 'msgpack'
2021
require 'presto/client/models'
@@ -42,7 +43,8 @@ def initialize(faraday, query, options, next_uri=nil)
4243

4344
if next_uri
4445
response = faraday_get_with_retry(next_uri)
45-
@results = @models::QueryResults.decode(parse_body(response))
46+
decoded = @models::QueryResults.decode(parse_body(response))
47+
@results = cast_results(decoded)
4648
else
4749
post_query_request!
4850
end
@@ -69,7 +71,8 @@ def post_query_request!
6971
raise PrestoHttpError.new(response.status, "Failed to start query: #{response.body} (#{response.status})")
7072
end
7173

72-
@results = decode_model(uri, parse_body(response), @models::QueryResults)
74+
decoded = decode_model(uri, parse_body(response), @models::QueryResults)
75+
@results = cast_results(decoded)
7376
end
7477

7578
private :post_query_request!
@@ -113,7 +116,8 @@ def advance
113116
uri = @results.next_uri
114117

115118
response = faraday_get_with_retry(uri)
116-
@results = decode_model(uri, parse_body(response), @models::QueryResults)
119+
decoded = decode_model(uri, parse_body(response), @models::QueryResults)
120+
@results = cast_results(decoded)
117121

118122
return true
119123
end
@@ -124,6 +128,26 @@ def query_info
124128
decode_model(uri, parse_body(response), @models::QueryInfo)
125129
end
126130

131+
def cast_results(query_results)
132+
data = query_results.data
133+
columns = query_results.columns
134+
return query_results if data.nil? || columns.nil?
135+
136+
decimal_indices = columns.each_with_index.select { |c, i| c.type.start_with?('decimal(') }.map(&:last)
137+
if decimal_indices.empty?
138+
query_results
139+
else
140+
new_data = data.map do |row|
141+
copy = row.dup
142+
decimal_indices.each do |index|
143+
copy[index] = BigDecimal(copy[index])
144+
end
145+
copy
146+
end
147+
query_results.class.new(query_results.to_h.merge(data: new_data))
148+
end
149+
end
150+
127151
def decode_model(uri, hash, body_class)
128152
begin
129153
body_class.decode(hash)

spec/statement_client_spec.rb

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,36 @@
4343
StatementClient.new(faraday, query, options)
4444
end
4545

46+
it "returns results correctly" do
47+
query = 'select 1 as i, 1.0 as d'
48+
stub_request(:post, "localhost/v1/statement").
49+
with(body: query,
50+
headers: {
51+
"User-Agent" => "presto-ruby/#{VERSION}",
52+
"X-Presto-Catalog" => options[:catalog],
53+
"X-Presto-Schema" => options[:schema],
54+
"X-Presto-User" => options[:user],
55+
"X-Presto-Language" => options[:language],
56+
"X-Presto-Time-Zone" => options[:time_zone],
57+
}).to_return(body: <<-JSON)
58+
{"id":"queryid","infoUri":"http://localhost/ui/query.html?queryid","nextUri":"http://localhost/v1/statement/queryid/1","stats":{"state":"QUEUED","queued":true,"scheduled":false,"nodes":0,"totalSplits":0,"queuedSplits":0,"runningSplits":0,"completedSplits":0,"userTimeMillis":0,"cpuTimeMillis":0,"wallTimeMillis":0,"queuedTimeMillis":0,"elapsedTimeMillis":0,"processedRows":0,"processedBytes":0,"peakMemoryBytes":0}}
59+
JSON
60+
client = StatementClient.new(faraday, query, options)
61+
client.current_results.data.should be_nil
62+
63+
stub_request(:get, "http://localhost/v1/statement/queryid/1").to_return(body: <<-JSON)
64+
{"id":"20180927_190533_00177_setx5","infoUri":"http://localhost/ui/query.html?queryid","partialCancelUri":"http://192.168.128.77:8091/v1/stage/queryid.0","nextUri":"http://localhost/v1/statement/queryid/2","columns":[{"name":"i","type":"integer","typeSignature":{"rawType":"integer","typeArguments":[],"literalArguments":[],"arguments":[]}},{"name":"d","type":"decimal(2,1)","typeSignature":{"rawType":"decimal","typeArguments":[],"literalArguments":[],"arguments":[{"kind":"LONG_LITERAL","value":2},{"kind":"LONG_LITERAL","value":1}]}}],"data":[[1,"1.0"]],"stats":{"state":"RUNNING","queued":false,"scheduled":true,"nodes":1,"totalSplits":17,"queuedSplits":17,"runningSplits":0,"completedSplits":0,"userTimeMillis":0,"cpuTimeMillis":0,"wallTimeMillis":0,"queuedTimeMillis":1,"elapsedTimeMillis":121,"processedRows":0,"processedBytes":0,"peakMemoryBytes":0,"rootStage":{"stageId":"0","state":"RUNNING","done":false,"nodes":1,"totalSplits":17,"queuedSplits":17,"runningSplits":0,"completedSplits":0,"userTimeMillis":0,"cpuTimeMillis":0,"wallTimeMillis":0,"processedRows":0,"processedBytes":0,"subStages":[]},"progressPercentage":0.0}}
65+
JSON
66+
client.advance.should be_true
67+
client.current_results.data.should == [[1, BigDecimal('1.0')]]
68+
69+
stub_request(:get, "http://localhost/v1/statement/queryid/2").to_return(body: <<-JSON)
70+
{"id":"queryid","infoUri":"http://localhost/ui/query.html?queryid","columns":[{"name":"i","type":"integer","typeSignature":{"rawType":"integer","typeArguments":[],"literalArguments":[],"arguments":[]}},{"name":"d","type":"decimal(2,1)","typeSignature":{"rawType":"decimal","typeArguments":[],"literalArguments":[],"arguments":[{"kind":"LONG_LITERAL","value":2},{"kind":"LONG_LITERAL","value":1}]}}],"stats":{"state":"FINISHED","queued":false,"scheduled":true,"nodes":1,"totalSplits":17,"queuedSplits":0,"runningSplits":0,"completedSplits":17,"userTimeMillis":4,"cpuTimeMillis":5,"wallTimeMillis":109,"queuedTimeMillis":1,"elapsedTimeMillis":128,"processedRows":0,"processedBytes":0,"peakMemoryBytes":0,"rootStage":{"stageId":"0","state":"FINISHED","done":true,"nodes":1,"totalSplits":17,"queuedSplits":0,"runningSplits":0,"completedSplits":17,"userTimeMillis":4,"cpuTimeMillis":5,"wallTimeMillis":109,"processedRows":1,"processedBytes":0,"subStages":[]},"progressPercentage":100.0}}
71+
JSON
72+
client.advance.should be_true
73+
client.current_results.data.should be_nil
74+
end
75+
4676
let :response_json2 do
4777
{
4878
id: "queryid",
@@ -402,4 +432,3 @@
402432
StatementClient.new(faraday, query, options)
403433
end
404434
end
405-

0 commit comments

Comments
 (0)