rb_snowflake_client 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +52 -35
- data/lib/ruby_snowflake/client/http_connection_wrapper.rb +2 -0
- data/lib/ruby_snowflake/client/key_pair_jwt_auth_manager.rb +59 -0
- data/lib/ruby_snowflake/client.rb +33 -61
- data/lib/ruby_snowflake/row.rb +10 -5
- data/lib/ruby_snowflake/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a93f0b1e503e2d219db8b1d1133395ca484b45aaa1f91ceef013a50a116b973
|
4
|
+
data.tar.gz: c292c5b0d25a921f8deba6882fe288ed084aa07919fa0d056899d20b921612fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 28428d4cf4bd70a11fefad327a2ab14deff17e95bf28b999a6b84d67756b35d3207c8224d6a028b9998ca85b62a7226f4df2beddea50f30e9a91350d7df73b4b
|
7
|
+
data.tar.gz: ad55068885a29f1cae0181e52d66764651cb4f7d541e46680bd15644ed8c17f10dda8cb9356ae4948cdf9b249e0c726abc540e26b03e1b76f9f2c538a8251cce
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -13,6 +13,8 @@ This library is implemented in ruby and while it leverages some libraries that h
|
|
13
13
|
|
14
14
|
# Usage
|
15
15
|
|
16
|
+
## Create a client
|
17
|
+
|
16
18
|
Add to your Gemfile or use `gem install rb-snowflake-client`
|
17
19
|
```ruby
|
18
20
|
gem "rb-snowflake-client"
|
@@ -27,7 +29,7 @@ require "rb_snowflake_client"
|
|
27
29
|
# see: https://github.com/rinsed-org/pure-ruby-snowflake-client/blob/master/lib/ruby_snowflake/client.rb#L43
|
28
30
|
client = RubySnowflake::Client.new(
|
29
31
|
"https://yourinstance.region.snowflakecomputing.com", # insert your URL here
|
30
|
-
File.read("secrets/my_key.pem"), #
|
32
|
+
File.read("secrets/my_key.pem"), # your private key in PEM format (scroll down for instructions)
|
31
33
|
"snowflake-organization", # your account name (doesn't match your URL)
|
32
34
|
"snowflake-account", # typically your subdomain
|
33
35
|
"snowflake-user", # Your snowflake user
|
@@ -41,21 +43,22 @@ client = RubySnowflake::Client.new(
|
|
41
43
|
RubySnowflake::Client.from_env
|
42
44
|
```
|
43
45
|
Available ENV variables (see below in the config section for details)
|
44
|
-
`SNOWFLAKE_URI`
|
45
|
-
`SNOWFLAKE_PRIVATE_KEY_PATH`
|
46
|
-
|
47
|
-
`
|
48
|
-
`
|
49
|
-
`
|
50
|
-
`
|
51
|
-
`
|
52
|
-
`
|
53
|
-
`
|
54
|
-
`
|
55
|
-
`
|
56
|
-
`
|
57
|
-
`
|
58
|
-
|
46
|
+
- `SNOWFLAKE_URI`
|
47
|
+
- `SNOWFLAKE_PRIVATE_KEY_PATH` or `SNOWFLAKE_PRIVATE_KEY`
|
48
|
+
- Use either the key or the path. Key takes precedence if both are provided.
|
49
|
+
- `SNOWFLAKE_ORGANIZATION`
|
50
|
+
- `SNOWFLAKE_ACCOUNT`
|
51
|
+
- `SNOWFLAKE_USER`
|
52
|
+
- `SNOWFLAKE_DEFAULT_WAREHOUSE`
|
53
|
+
- `SNOWFLAKE_DEFAULT_DATABASE`
|
54
|
+
- `SNOWFLAKE_JWT_TOKEN_TTL`
|
55
|
+
- `SNOWFLAKE_CONNECTION_TIMEOUT`
|
56
|
+
- `SNOWFLAKE_MAX_CONNECTIONS`
|
57
|
+
- `SNOWFLAKE_MAX_THREADS_PER_QUERY`
|
58
|
+
- `SNOWFLAKE_THREAD_SCALE_FACTOR`
|
59
|
+
- `SNOWFLAKE_HTTP_RETRIES`
|
60
|
+
|
61
|
+
## Make queries
|
59
62
|
|
60
63
|
Once you have a client, make queries
|
61
64
|
```ruby
|
@@ -68,44 +71,58 @@ result.each do |row|
|
|
68
71
|
puts row["name"] # or case insensitive strings
|
69
72
|
puts row.to_h # and can produce a hash with keys/values
|
70
73
|
end
|
74
|
+
```
|
75
|
+
|
76
|
+
## Stream results
|
71
77
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
# available for you.
|
78
|
+
You can also stream results and not hold them all in memory. The client will prefetch the next data partition only. If you have some IO in your processing there should usually be data available for you.
|
79
|
+
|
80
|
+
```ruby
|
76
81
|
result = client.query("SELECT * FROM HUGETABLE", streaming: true)
|
77
82
|
result.each do |row|
|
78
83
|
puts row
|
79
84
|
end
|
85
|
+
```
|
80
86
|
|
87
|
+
## Switching databases
|
81
88
|
|
82
|
-
|
83
|
-
|
89
|
+
You can also overwrite the database specified in the initializer, and run your query with a different context.
|
90
|
+
|
91
|
+
```ruby
|
84
92
|
result = client.query("SELECT * FROM SECRET_TABLE", database: "OTHER_DB")
|
85
93
|
result.each do |row|
|
86
94
|
puts row
|
87
95
|
end
|
88
96
|
```
|
89
97
|
|
98
|
+
## Switching warehouses
|
99
|
+
|
100
|
+
Clients are not warehouse specific, you can override the default warehouse per query
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
client.query("SELECT * FROM BIGTABLE", warehouse: "FAST_WH")
|
104
|
+
```
|
105
|
+
|
90
106
|
# Configuration Options
|
91
107
|
|
92
|
-
The client supports the following configuration options, each with their own getter/setter except connection pool options which must be set at construction. Additionally, all except logger can be configured with environment variables (see above, but the pattern is like: "SNOWFLAKE_HTTP_RETRIES".
|
108
|
+
The client supports the following configuration options, each with their own getter/setter except connection pool options which must be set at construction. Additionally, all except logger can be configured with environment variables (see above, but the pattern is like: "SNOWFLAKE_HTTP_RETRIES". Configuration options can only be set on initiialization through `new` or `from_env`.
|
93
109
|
|
94
|
-
`logger` - takes any ruby logger (by default it's a std lib Logger.new(STDOUT), set at DEBUG level. Not available as an ENV variable config option
|
95
|
-
`log_level` - takes a log level, type is dependent on logger, for the default ruby Logger, use a level like `Logger::WARN`. Not available as an ENV variable config option.
|
96
|
-
`jwt_token_ttl` - The time to live set on JWT token in seconds, defaults to 3540 (59 minutes, the longest Snowflake supports is 60)
|
97
|
-
`connection_timeout` - The amount of time in seconds that the client's connection pool will wait before erroring in handing out a valid connection, defaults to 60 seconds
|
98
|
-
`max_connections` - The maximum number of http connections to hold open in the connection pool. If you use the client in a threaded context, you may need to increase this to be threads * client.max_threads_per_query, defaults to 16.
|
99
|
-
`max_threads_per_query` - The maximum number of threads the client should use to retreive data, per query, defaults to 8. If you want the client to act in a single threaded way, set this to 1
|
100
|
-
`thread_scale_factor` - When downloading a result set into memory, thread count is calculated by dividing a query's partition count by this number. For details on implementation see the code in `client.rb`.
|
101
|
-
`http_retries` - By default the client will retry common typically transient errors (http responses) twice, you can change the number of retries with this.
|
110
|
+
- `logger` - takes any ruby logger (by default it's a std lib Logger.new(STDOUT), set at DEBUG level. Not available as an ENV variable config option
|
111
|
+
- `log_level` - takes a log level, type is dependent on logger, for the default ruby Logger, use a level like `Logger::WARN`. Not available as an ENV variable config option.
|
112
|
+
- `jwt_token_ttl` - The time to live set on JWT token in seconds, defaults to 3540 (59 minutes, the longest Snowflake supports is 60).
|
113
|
+
- `connection_timeout` - The amount of time in seconds that the client's connection pool will wait before erroring in handing out a valid connection, defaults to 60 seconds
|
114
|
+
- `max_connections` - The maximum number of http connections to hold open in the connection pool. If you use the client in a threaded context, you may need to increase this to be threads * client.max_threads_per_query, defaults to 16.
|
115
|
+
- `max_threads_per_query` - The maximum number of threads the client should use to retreive data, per query, defaults to 8. If you want the client to act in a single threaded way, set this to 1
|
116
|
+
- `thread_scale_factor` - When downloading a result set into memory, thread count is calculated by dividing a query's partition count by this number. For details on implementation see the code in `client.rb`.
|
117
|
+
- `http_retries` - By default the client will retry common typically transient errors (http responses) twice, you can change the number of retries with this.
|
102
118
|
|
103
119
|
Example configuration:
|
104
120
|
```ruby
|
105
|
-
client = RubySnowflake::Client.from_env
|
106
|
-
|
107
|
-
|
108
|
-
|
121
|
+
client = RubySnowflake::Client.from_env(
|
122
|
+
logger: Rails.logger
|
123
|
+
max_connections: 24
|
124
|
+
http_retries 1
|
125
|
+
)
|
109
126
|
end
|
110
127
|
```
|
111
128
|
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jwt"
|
4
|
+
require "openssl"
|
5
|
+
require "concurrent"
|
6
|
+
|
7
|
+
module RubySnowflake
|
8
|
+
class Client
|
9
|
+
class KeyPairJwtAuthManager
|
10
|
+
# requires text of a PEM formatted RSA private key
|
11
|
+
def initialize(organization, account, user, private_key, jwt_token_ttl)
|
12
|
+
@organization = organization
|
13
|
+
@account = account
|
14
|
+
@user = user
|
15
|
+
@private_key_pem = private_key
|
16
|
+
@jwt_token_ttl = jwt_token_ttl
|
17
|
+
|
18
|
+
# start with an expired value to force creation
|
19
|
+
@token_expires_at = Time.now.to_i - 1
|
20
|
+
@token_semaphore = Concurrent::Semaphore.new(1)
|
21
|
+
end
|
22
|
+
|
23
|
+
def jwt_token
|
24
|
+
return @token unless jwt_token_expired?
|
25
|
+
|
26
|
+
@token_semaphore.acquire do
|
27
|
+
now = Time.now.to_i
|
28
|
+
@token_expires_at = now + @jwt_token_ttl
|
29
|
+
|
30
|
+
private_key = OpenSSL::PKey.read(@private_key_pem)
|
31
|
+
|
32
|
+
payload = {
|
33
|
+
:iss => "#{@organization.upcase}-#{@account.upcase}.#{@user}.#{public_key_fingerprint}",
|
34
|
+
:sub => "#{@organization.upcase}-#{@account.upcase}.#{@user}",
|
35
|
+
:iat => now,
|
36
|
+
:exp => @token_expires_at
|
37
|
+
}
|
38
|
+
|
39
|
+
@token = JWT.encode payload, private_key, "RS256"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def jwt_token_expired?
|
45
|
+
Time.now.to_i > @token_expires_at
|
46
|
+
end
|
47
|
+
|
48
|
+
def public_key_fingerprint
|
49
|
+
reutrn @public_key_fingerprint unless @public_key_fingerprint.nil?
|
50
|
+
|
51
|
+
public_key_der = OpenSSL::PKey::RSA.new(@private_key_pem).public_key.to_der
|
52
|
+
digest = OpenSSL::Digest::SHA256.new.digest(public_key_der)
|
53
|
+
fingerprint = Base64.strict_encode64(digest)
|
54
|
+
|
55
|
+
@public_key_fingerprint = "SHA256:#{fingerprint}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -5,22 +5,21 @@ require "benchmark"
|
|
5
5
|
require "concurrent"
|
6
6
|
require "connection_pool"
|
7
7
|
require "json"
|
8
|
-
require "jwt"
|
9
8
|
require "logger"
|
10
9
|
require "net/http"
|
11
10
|
require "oj"
|
12
|
-
require "openssl"
|
13
11
|
require "retryable"
|
14
12
|
require "securerandom"
|
15
13
|
require "uri"
|
16
14
|
|
17
15
|
|
18
|
-
require_relative "result"
|
19
|
-
require_relative "streaming_result"
|
20
16
|
require_relative "client/http_connection_wrapper"
|
17
|
+
require_relative "client/key_pair_jwt_auth_manager"
|
21
18
|
require_relative "client/single_thread_in_memory_strategy"
|
22
19
|
require_relative "client/streaming_result_strategy"
|
23
20
|
require_relative "client/threaded_in_memory_strategy"
|
21
|
+
require_relative "result"
|
22
|
+
require_relative "streaming_result"
|
24
23
|
|
25
24
|
module RubySnowflake
|
26
25
|
class Error < StandardError
|
@@ -55,11 +54,19 @@ module RubySnowflake
|
|
55
54
|
# how many times to retry common retryable HTTP responses (i.e. 429, 504)
|
56
55
|
DEFAULT_HTTP_RETRIES = 2
|
57
56
|
|
58
|
-
|
59
|
-
attr_reader :connection_timeout, :max_connections
|
60
|
-
attr_accessor :logger, :jwt_token_ttl, :max_threads_per_query, :thread_scale_factor, :http_retries
|
57
|
+
OJ_OPTIONS = { :bigdecimal_load => :bigdecimal }.freeze
|
61
58
|
|
62
|
-
|
59
|
+
# can't be set after initialization
|
60
|
+
attr_reader :connection_timeout, :max_connections, :logger, :max_threads_per_query, :thread_scale_factor, :http_retries
|
61
|
+
|
62
|
+
def self.from_env(logger: DEFAULT_LOGGER,
|
63
|
+
log_level: DEFAULT_LOG_LEVEL,
|
64
|
+
jwt_token_ttl: env_option("SNOWFLAKE_JWT_TOKEN_TTL", DEFAULT_JWT_TOKEN_TTL),
|
65
|
+
connection_timeout: env_option("SNOWFLAKE_CONNECTION_TIMEOUT", DEFAULT_CONNECTION_TIMEOUT ),
|
66
|
+
max_connections: env_option("SNOWFLAKE_MAX_CONNECTIONS", DEFAULT_MAX_CONNECTIONS ),
|
67
|
+
max_threads_per_query: env_option("SNOWFLAKE_MAX_THREADS_PER_QUERY", DEFAULT_MAX_THREADS_PER_QUERY),
|
68
|
+
thread_scale_factor: env_option("SNOWFLAKE_THREAD_SCALE_FACTOR", DEFAULT_THREAD_SCALE_FACTOR),
|
69
|
+
http_retries: env_option("SNOWFLAKE_HTTP_RETRIES", DEFAULT_HTTP_RETRIES))
|
63
70
|
private_key = ENV["SNOWFLAKE_PRIVATE_KEY"] || File.read(ENV["SNOWFLAKE_PRIVATE_KEY_PATH"])
|
64
71
|
|
65
72
|
new(
|
@@ -70,12 +77,14 @@ module RubySnowflake
|
|
70
77
|
ENV["SNOWFLAKE_USER"],
|
71
78
|
ENV["SNOWFLAKE_DEFAULT_WAREHOUSE"],
|
72
79
|
ENV["SNOWFLAKE_DEFAULT_DATABASE"],
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
80
|
+
logger: logger,
|
81
|
+
log_level: log_level,
|
82
|
+
jwt_token_ttl: jwt_token_ttl,
|
83
|
+
connection_timeout: connection_timeout,
|
84
|
+
max_connections: max_connections,
|
85
|
+
max_threads_per_query: max_threads_per_query,
|
86
|
+
thread_scale_factor: thread_scale_factor,
|
87
|
+
http_retries: http_retries,
|
79
88
|
)
|
80
89
|
end
|
81
90
|
|
@@ -91,27 +100,19 @@ module RubySnowflake
|
|
91
100
|
http_retries: DEFAULT_HTTP_RETRIES
|
92
101
|
)
|
93
102
|
@base_uri = uri
|
94
|
-
@
|
95
|
-
|
96
|
-
@account = account
|
97
|
-
@user = user
|
103
|
+
@key_pair_jwt_auth_manager =
|
104
|
+
KeyPairJwtAuthManager.new(organization, account, user, private_key, jwt_token_ttl)
|
98
105
|
@default_warehouse = default_warehouse
|
99
|
-
@public_key_fingerprint = public_key_fingerprint(@private_key_pem)
|
100
106
|
@default_database = default_database
|
101
107
|
|
102
108
|
# set defaults for config settings
|
103
109
|
@logger = logger
|
104
110
|
@logger.level = log_level
|
105
|
-
@jwt_token_ttl = jwt_token_ttl
|
106
111
|
@connection_timeout = connection_timeout
|
107
112
|
@max_connections = max_connections
|
108
113
|
@max_threads_per_query = max_threads_per_query
|
109
114
|
@thread_scale_factor = thread_scale_factor
|
110
115
|
@http_retries = http_retries
|
111
|
-
|
112
|
-
# start with an expired value to force creation
|
113
|
-
@token_expires_at = Time.now.to_i - 1
|
114
|
-
@token_semaphore = Concurrent::Semaphore.new(1)
|
115
116
|
end
|
116
117
|
|
117
118
|
def query(query, warehouse: nil, streaming: false, database: nil)
|
@@ -157,36 +158,19 @@ module RubySnowflake
|
|
157
158
|
@port ||= URI.parse(@base_uri).port
|
158
159
|
end
|
159
160
|
|
160
|
-
def
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
now = Time.now.to_i
|
165
|
-
@token_expires_at = now + @jwt_token_ttl
|
166
|
-
|
167
|
-
private_key = OpenSSL::PKey.read(@private_key_pem)
|
168
|
-
|
169
|
-
payload = {
|
170
|
-
:iss => "#{@organization.upcase}-#{@account.upcase}.#{@user}.#{@public_key_fingerprint}",
|
171
|
-
:sub => "#{@organization.upcase}-#{@account.upcase}.#{@user}",
|
172
|
-
:iat => now,
|
173
|
-
:exp => @token_expires_at
|
174
|
-
}
|
175
|
-
|
176
|
-
@token = JWT.encode payload, private_key, "RS256"
|
161
|
+
def handle_errors(response)
|
162
|
+
if response.code != "200"
|
163
|
+
raise BadResponseError.new({}),
|
164
|
+
"Bad response! Got code: #{response.code}, w/ message #{response.body}"
|
177
165
|
end
|
178
166
|
end
|
179
167
|
|
180
|
-
def jwt_token_expired?
|
181
|
-
Time.now.to_i > @token_expires_at
|
182
|
-
end
|
183
|
-
|
184
168
|
def request_with_auth_and_headers(connection, request_class, path, body=nil)
|
185
169
|
uri = URI.parse("#{@base_uri}#{path}")
|
186
170
|
request = request_class.new(uri)
|
187
171
|
request["Content-Type"] = "application/json"
|
188
172
|
request["Accept"] = "application/json"
|
189
|
-
request["Authorization"] = "Bearer #{jwt_token}"
|
173
|
+
request["Authorization"] = "Bearer #{@key_pair_jwt_auth_manager.jwt_token}"
|
190
174
|
request["X-Snowflake-Authorization-Token-Type"] = "KEYPAIR_JWT"
|
191
175
|
request.body = body unless body.nil?
|
192
176
|
|
@@ -230,7 +214,7 @@ module RubySnowflake
|
|
230
214
|
end
|
231
215
|
|
232
216
|
def retreive_result_set(response, streaming)
|
233
|
-
json_body = Oj.load(response.body,
|
217
|
+
json_body = Oj.load(response.body, OJ_OPTIONS)
|
234
218
|
statement_handle = json_body["statementHandle"]
|
235
219
|
num_threads = number_of_threads_to_use(json_body["resultSetMetaData"]["partitionInfo"].size)
|
236
220
|
retreive_proc = ->(index) { retreive_partition_data(statement_handle, index) }
|
@@ -254,8 +238,8 @@ module RubySnowflake
|
|
254
238
|
)
|
255
239
|
end
|
256
240
|
|
257
|
-
partition_json =
|
258
|
-
bm = Benchmark.measure { partition_json = Oj.load(partition_response.body,
|
241
|
+
partition_json = {}
|
242
|
+
bm = Benchmark.measure { partition_json = Oj.load(partition_response.body, OJ_OPTIONS) }
|
259
243
|
logger.debug { "JSON parsing took: #{bm.real}" }
|
260
244
|
partition_data = partition_json["data"]
|
261
245
|
|
@@ -265,17 +249,5 @@ module RubySnowflake
|
|
265
249
|
def number_of_threads_to_use(partition_count)
|
266
250
|
[[1, (partition_count / @thread_scale_factor.to_f).ceil].max, @max_threads_per_query].min
|
267
251
|
end
|
268
|
-
|
269
|
-
def oj_options
|
270
|
-
{ :bigdecimal_load => :bigdecimal }
|
271
|
-
end
|
272
|
-
|
273
|
-
def public_key_fingerprint(private_key_pem_string)
|
274
|
-
public_key_der = OpenSSL::PKey::RSA.new(private_key_pem_string).public_key.to_der
|
275
|
-
digest = OpenSSL::Digest::SHA256.new.digest(public_key_der)
|
276
|
-
fingerprint = Base64.strict_encode64(digest)
|
277
|
-
|
278
|
-
"SHA256:#{fingerprint}"
|
279
|
-
end
|
280
252
|
end
|
281
253
|
end
|
data/lib/ruby_snowflake/row.rb
CHANGED
@@ -31,14 +31,19 @@ module RubySnowflake
|
|
31
31
|
else
|
32
32
|
BigDecimal(@data[index]).round(@row_types[index][:scale])
|
33
33
|
end
|
34
|
+
|
35
|
+
# snowflake treats these all as 64 bit IEEE 754 floating point numbers, and will we too
|
34
36
|
when :float, :double, :"double precision", :real
|
35
|
-
# snowflake treats these all as 64 bit IEEE 754 floating point numbers, and will we too
|
36
37
|
Float(@data[index])
|
37
|
-
|
38
|
+
|
39
|
+
# Despite snowflake indicating that it sends the offset in minutes, the actual time in UTC
|
40
|
+
# is always sent in the first half of the data. If an offset is sent it looks like:
|
41
|
+
# "1641008096.123000000 1980"
|
42
|
+
# If there isn't one, it's just like this:
|
43
|
+
# "1641065696.123000000"
|
44
|
+
# in all cases, the actual time, in UTC is the float value, and the offset is ignorable
|
45
|
+
when :time, :datetime, :timestamp, :timestamp_ntz, :timestamp_ltz, :timestamp_tz
|
38
46
|
Time.strptime(@data[index], TIME_FORMAT).utc
|
39
|
-
when :timestamp_tz
|
40
|
-
timestamp, offset_minutes = @data[index].split(" ")
|
41
|
-
Time.strptime(@data[index], TIME_FORMAT).utc - (Integer(offset_minutes) * 60)
|
42
47
|
else
|
43
48
|
@data[index]
|
44
49
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rb_snowflake_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rinsed
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-12-
|
11
|
+
date: 2023-12-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -117,6 +117,7 @@ files:
|
|
117
117
|
- lib/rb_snowflake_client.rb
|
118
118
|
- lib/ruby_snowflake/client.rb
|
119
119
|
- lib/ruby_snowflake/client/http_connection_wrapper.rb
|
120
|
+
- lib/ruby_snowflake/client/key_pair_jwt_auth_manager.rb
|
120
121
|
- lib/ruby_snowflake/client/single_thread_in_memory_strategy.rb
|
121
122
|
- lib/ruby_snowflake/client/streaming_result_strategy.rb
|
122
123
|
- lib/ruby_snowflake/client/threaded_in_memory_strategy.rb
|