olaf 0.3.0 → 1.0.1
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.
- checksums.yaml +4 -4
- data/README.md +20 -10
- data/lib/olaf/drivers/big_query.rb +52 -10
- data/lib/olaf/query_definition/class_methods.rb +7 -7
- data/lib/olaf/query_definition.rb +2 -2
- data/lib/olaf.rb +7 -8
- data/olaf.gemspec +2 -4
- data/test/olaf_configure_test.rb +14 -14
- data/test/olaf_execute_test.rb +2 -2
- data/test/olaf_lazy_loading_test.rb +2 -4
- metadata +3 -18
- data/lib/olaf/drivers/snowflake.rb +0 -28
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dd476121e33e97e071c41dc3e75a8128eddee118c93f0ec93acd61fa167b0523
|
|
4
|
+
data.tar.gz: 843da2abba59ade1dc644b47a4575abf77102ed7a58dd0ce1c45e6f7ba6f7968
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 577d617e9e02f3bbaa858425e70e6d87803af2a9d2764626d30e6d0fda0bce130171b33566e642354e30990ffe19d7e9b4b8c55f48f29d63712a1e81743eb31c
|
|
7
|
+
data.tar.gz: 39da006e922a15ad3fa88a7c7c45979aff8eeb8e13b6e4c5340ee52608f54cf4930b4314e4925c2103ed7559604f664fbb18f2dd8084903605075e1d9f908293
|
data/README.md
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Olaf
|
|
2
2
|
|
|
3
|
-
Olaf is a small Ruby wrapper for warehouse queries, running them in
|
|
4
|
-
in BigQuery.
|
|
3
|
+
Olaf is a small Ruby wrapper for warehouse queries, running them in BigQuery.
|
|
5
4
|
|
|
6
5
|

|
|
7
6
|
|
|
@@ -13,7 +12,6 @@ and only when it is used, so add to your `Gemfile` the ones you configure:
|
|
|
13
12
|
|
|
14
13
|
| Driver | Gems | Also needs |
|
|
15
14
|
| ----------------- | ----------------------- | ----------------------- |
|
|
16
|
-
| `Olaf::Snowflake` | `sequel`, `ruby-odbc` | the ODBC system library |
|
|
17
15
|
| `Olaf::BigQuery` | `google-cloud-bigquery` | |
|
|
18
16
|
| `Olaf::Fake` | — | |
|
|
19
17
|
|
|
@@ -34,7 +32,7 @@ control in the code and in tests.
|
|
|
34
32
|
class FetchUsers
|
|
35
33
|
include Olaf::QueryDefinition
|
|
36
34
|
|
|
37
|
-
template './
|
|
35
|
+
template './big_query/users_in_department.sql'
|
|
38
36
|
|
|
39
37
|
argument :department_id
|
|
40
38
|
|
|
@@ -52,7 +50,6 @@ Olaf.execute(query)
|
|
|
52
50
|
One driver, which every query runs on:
|
|
53
51
|
|
|
54
52
|
```ruby
|
|
55
|
-
Olaf.configure(user: 'olaf') # Olaf::Snowflake, the default
|
|
56
53
|
Olaf.configure(olaf_driver: Olaf::Fake) # ideal for testing
|
|
57
54
|
```
|
|
58
55
|
|
|
@@ -62,10 +59,10 @@ run on the `default:`, the first one given when it is not specified:
|
|
|
62
59
|
```ruby
|
|
63
60
|
Olaf.configure(
|
|
64
61
|
drivers: {
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
big_query: Olaf::BigQuery.new(project: 'carwow', labels: { service: 'flatmin' }),
|
|
63
|
+
fake: Olaf::Fake.new
|
|
67
64
|
},
|
|
68
|
-
default: :
|
|
65
|
+
default: :big_query
|
|
69
66
|
)
|
|
70
67
|
|
|
71
68
|
class FetchUsers
|
|
@@ -91,7 +88,8 @@ Olaf.instance.register_result(FetchUsers, [{ id: 42 }], with: { department_id: 1
|
|
|
91
88
|
```
|
|
92
89
|
|
|
93
90
|
Whichever driver runs the query, a failure it reports is wrapped into
|
|
94
|
-
`Olaf::QueryExecutionError`, carrying the query metadata.
|
|
91
|
+
`Olaf::QueryExecutionError`, carrying the query metadata. A BigQuery connection
|
|
92
|
+
that keeps dropping can be the exception: see below.
|
|
95
93
|
|
|
96
94
|
## BigQuery
|
|
97
95
|
|
|
@@ -100,7 +98,8 @@ Olaf::BigQuery.new(
|
|
|
100
98
|
project: 'carwow',
|
|
101
99
|
credentials: JSON.parse(ENV['BIGQUERY_CREDENTIALS']), # ambient Google credentials when omitted
|
|
102
100
|
maximum_bytes_billed: 5 * Olaf::BigQuery::GIGABYTE, # default: 1 GiB
|
|
103
|
-
labels: { service: 'flatmin', country: 'uk' }
|
|
101
|
+
labels: { service: 'flatmin', country: 'uk' },
|
|
102
|
+
retry_delays: [1, 2] # default: retry twice, after 1s then 2s
|
|
104
103
|
)
|
|
105
104
|
```
|
|
106
105
|
|
|
@@ -117,6 +116,17 @@ The query runs as a job, so that it can be capped and labelled:
|
|
|
117
116
|
```ruby
|
|
118
117
|
driver :big_query, maximum_bytes_billed: 10 * Olaf::BigQuery::GIGABYTE
|
|
119
118
|
```
|
|
119
|
+
* A call whose connection drops mid-request is retried once after each of the
|
|
120
|
+
`retry_delays`. Waiting for the job is retried on its own, so a dropped status
|
|
121
|
+
check doesn't start a second job. A drop arrives in one of two forms, and both
|
|
122
|
+
are retried:
|
|
123
|
+
* A bare `OpenSSL::SSL::SSLError`, which is how google-apis-core 0.x reports
|
|
124
|
+
an SSL read that ends early. Once the retries run out it is raised as it is,
|
|
125
|
+
not wrapped, so a job runner such as Sidekiq can retry it in turn.
|
|
126
|
+
* A `Google::Cloud::Error` caused by a `Google::Apis::TransmissionError`.
|
|
127
|
+
google-apis-core 1.x reports every dropped connection this way, and 0.x
|
|
128
|
+
does the same for a reset connection. Once the retries run out it is wrapped
|
|
129
|
+
into `Olaf::QueryExecutionError`, like any other error the API reports.
|
|
120
130
|
|
|
121
131
|
`Olaf.execute` returns the rows. `Olaf.instance(:big_query).run(query)` returns
|
|
122
132
|
the finished job instead, for its statistics:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Not a dependency of the gem: consumers of this driver add it themselves.
|
|
2
2
|
require 'google/cloud/bigquery'
|
|
3
|
+
require 'securerandom'
|
|
3
4
|
|
|
4
5
|
# The API client debug-logs whole response bodies, which for a warehouse query
|
|
5
6
|
# is the entire result set.
|
|
@@ -17,14 +18,17 @@ module Olaf
|
|
|
17
18
|
LABEL_FORMAT = /[^a-z0-9_-]/.freeze
|
|
18
19
|
LABEL_MAX_LENGTH = 63
|
|
19
20
|
|
|
21
|
+
RETRY_DELAYS = [1, 2].freeze
|
|
22
|
+
|
|
20
23
|
# Credentials default to the ambient Google ones, and the client is built
|
|
21
24
|
# from the arguments unless one is given.
|
|
22
25
|
def initialize(project: nil, credentials: nil, maximum_bytes_billed: DEFAULT_MAXIMUM_BYTES_BILLED,
|
|
23
|
-
labels: {}, client: nil)
|
|
26
|
+
labels: {}, retry_delays: RETRY_DELAYS, client: nil)
|
|
24
27
|
@project = project
|
|
25
28
|
@credentials = credentials
|
|
26
29
|
@maximum_bytes_billed = maximum_bytes_billed
|
|
27
30
|
@labels = labels
|
|
31
|
+
@retry_delays = retry_delays
|
|
28
32
|
@client = client
|
|
29
33
|
end
|
|
30
34
|
|
|
@@ -35,15 +39,11 @@ module Olaf
|
|
|
35
39
|
# Returns the finished job, so callers can read its stats (bytes billed,
|
|
36
40
|
# cache hit) on top of its rows.
|
|
37
41
|
#
|
|
38
|
-
# @raises Olaf::QueryExecutionError
|
|
42
|
+
# @raises Olaf::QueryExecutionError, or the OpenSSL::SSL::SSLError of a
|
|
43
|
+
# connection that keeps dropping, which google-apis-core 0.x doesn't wrap
|
|
39
44
|
def run(olaf_query)
|
|
40
|
-
job =
|
|
41
|
-
|
|
42
|
-
params: params(olaf_query),
|
|
43
|
-
maximum_bytes_billed: maximum_bytes_billed(olaf_query),
|
|
44
|
-
labels: labels(olaf_query)
|
|
45
|
-
)
|
|
46
|
-
job.wait_until_done!
|
|
45
|
+
job = start_job(olaf_query)
|
|
46
|
+
retrying_dropped_connections { job.wait_until_done! }
|
|
47
47
|
|
|
48
48
|
raise QueryExecutionError.new(job.error.to_h['message'].to_s, olaf_query) if job.failed?
|
|
49
49
|
|
|
@@ -58,9 +58,51 @@ module Olaf
|
|
|
58
58
|
@client ||= Google::Cloud::Bigquery.new(project: @project, credentials: @credentials)
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
+
# A connection can drop after BigQuery has started the job, so every attempt
|
|
62
|
+
# reuses one job ID: BigQuery refuses the retry as a duplicate, and the job
|
|
63
|
+
# already started is loaded rather than running and billing the query twice.
|
|
64
|
+
# If the lookup can't find the job, as happens for one in a single region,
|
|
65
|
+
# the refusal is re-raised.
|
|
66
|
+
def start_job(olaf_query)
|
|
67
|
+
job_id = "job_#{SecureRandom.urlsafe_base64(21)}"
|
|
68
|
+
|
|
69
|
+
retrying_dropped_connections do
|
|
70
|
+
client.query_job(
|
|
71
|
+
sql(olaf_query),
|
|
72
|
+
params: params(olaf_query),
|
|
73
|
+
maximum_bytes_billed: maximum_bytes_billed(olaf_query),
|
|
74
|
+
labels: labels(olaf_query),
|
|
75
|
+
job_id:
|
|
76
|
+
)
|
|
77
|
+
rescue Google::Cloud::AlreadyExistsError
|
|
78
|
+
client.job(job_id) || raise
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# google-cloud-bigquery switches off google-apis-core's retries.
|
|
83
|
+
def retrying_dropped_connections
|
|
84
|
+
@retry_delays.each do |delay|
|
|
85
|
+
return yield
|
|
86
|
+
rescue StandardError => error
|
|
87
|
+
raise unless dropped_connection?(error)
|
|
88
|
+
|
|
89
|
+
sleep delay
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
yield
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# google-apis-core 1.x reports every dropped connection as a TransmissionError,
|
|
96
|
+
# and 0.x does the same for a reset connection; google-cloud-bigquery wraps it.
|
|
97
|
+
def dropped_connection?(error)
|
|
98
|
+
error.is_a?(OpenSSL::SSL::SSLError) ||
|
|
99
|
+
(error.is_a?(Google::Cloud::Error) && error.cause.is_a?(Google::Apis::TransmissionError))
|
|
100
|
+
end
|
|
101
|
+
|
|
61
102
|
# Google::Cloud::Error covers everything the API reports, Google::Auth::Error
|
|
62
103
|
# credentials missing or expired before a call is made. The second one only
|
|
63
|
-
# exists in googleauth 1.14 and later.
|
|
104
|
+
# exists in googleauth 1.14 and later. A bare OpenSSL::SSL::SSLError is left
|
|
105
|
+
# out, so once its retries run out the caller's job runner can still retry it.
|
|
64
106
|
def wrapped_errors
|
|
65
107
|
@wrapped_errors ||= [
|
|
66
108
|
Google::Cloud::Error,
|
|
@@ -3,7 +3,7 @@ module Olaf
|
|
|
3
3
|
module ClassMethods
|
|
4
4
|
# Creates a new instance of the Query defined and validates
|
|
5
5
|
# the parameters passed, leaving the instance in a ready-to-execute state.
|
|
6
|
-
# @return
|
|
6
|
+
# @return Olaf::QueryDefinition instance
|
|
7
7
|
def prepare(**vars)
|
|
8
8
|
new(**vars).prepare
|
|
9
9
|
end
|
|
@@ -15,9 +15,9 @@ module Olaf
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
# Define an argument for the query matching a placeholder in the template.
|
|
18
|
-
# The
|
|
19
|
-
#
|
|
20
|
-
#
|
|
18
|
+
# The driver binds the value passed as a query parameter. Sometimes, we need
|
|
19
|
+
# to pass an argument as literal to substitute it into the SQL instead
|
|
20
|
+
# (i.e. sending a table_name)
|
|
21
21
|
#
|
|
22
22
|
# options - Hash config for each argument
|
|
23
23
|
# :as - Argument Type
|
|
@@ -26,7 +26,7 @@ module Olaf
|
|
|
26
26
|
# Example:
|
|
27
27
|
#
|
|
28
28
|
# class OneQuery
|
|
29
|
-
# include
|
|
29
|
+
# include Olaf::QueryDefinition
|
|
30
30
|
#
|
|
31
31
|
# template 'reports/one.sql'
|
|
32
32
|
#
|
|
@@ -48,7 +48,7 @@ module Olaf
|
|
|
48
48
|
# Example:
|
|
49
49
|
#
|
|
50
50
|
# class OneQuery
|
|
51
|
-
# include
|
|
51
|
+
# include Olaf::QueryDefinition
|
|
52
52
|
#
|
|
53
53
|
# template 'reports/one.sql'
|
|
54
54
|
# end
|
|
@@ -93,7 +93,7 @@ module Olaf
|
|
|
93
93
|
# Example:
|
|
94
94
|
#
|
|
95
95
|
# class OneQuery
|
|
96
|
-
# include
|
|
96
|
+
# include Olaf::QueryDefinition
|
|
97
97
|
#
|
|
98
98
|
# row_object MyOwnObject
|
|
99
99
|
# end
|
|
@@ -20,8 +20,8 @@ module Olaf
|
|
|
20
20
|
#
|
|
21
21
|
# @return QueryDefinition instance
|
|
22
22
|
#
|
|
23
|
-
# @raises
|
|
24
|
-
# @raises
|
|
23
|
+
# @raises Olaf::UndefinedArgumentsError
|
|
24
|
+
# @raises Olaf::MissingArgumentsError
|
|
25
25
|
def prepare
|
|
26
26
|
@sql_template ||= File.read(self.class.template)
|
|
27
27
|
|
data/lib/olaf.rb
CHANGED
|
@@ -5,21 +5,19 @@ module Olaf
|
|
|
5
5
|
# Autoloaded: each driver pulls in its own client library.
|
|
6
6
|
autoload :BigQuery, File.expand_path('olaf/drivers/big_query', __dir__)
|
|
7
7
|
autoload :Fake, File.expand_path('olaf/drivers/fake', __dir__)
|
|
8
|
-
autoload :Snowflake, File.expand_path('olaf/drivers/snowflake', __dir__)
|
|
9
8
|
|
|
10
9
|
DEFAULT_DRIVER = :default
|
|
11
10
|
|
|
12
11
|
# Configures the drivers used to execute queries, either a single one built
|
|
13
12
|
# from the arguments given:
|
|
14
13
|
#
|
|
15
|
-
# Olaf.configure(user: 'olaf') # Olaf::Snowflake, the default
|
|
16
14
|
# Olaf.configure(olaf_driver: Olaf::Fake) # ideal for testing
|
|
17
15
|
#
|
|
18
16
|
# or several instantiated ones, which queries pick by name with `driver`:
|
|
19
17
|
#
|
|
20
18
|
# Olaf.configure(
|
|
21
|
-
# drivers: {
|
|
22
|
-
# default: :
|
|
19
|
+
# drivers: { big_query: Olaf::BigQuery.new(project: 'carwow'), fake: Olaf::Fake.new },
|
|
20
|
+
# default: :big_query
|
|
23
21
|
# )
|
|
24
22
|
#
|
|
25
23
|
# @return the default Olaf driver instance
|
|
@@ -36,7 +34,9 @@ module Olaf
|
|
|
36
34
|
|
|
37
35
|
[drivers.dup, default_driver]
|
|
38
36
|
else
|
|
39
|
-
|
|
37
|
+
raise ArgumentError, 'Pass `drivers:` or a single `olaf_driver:` to configure' unless olaf_driver
|
|
38
|
+
|
|
39
|
+
[{ DEFAULT_DRIVER => olaf_driver.new(**args) }, DEFAULT_DRIVER]
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
instance
|
|
@@ -64,9 +64,8 @@ module Olaf
|
|
|
64
64
|
# what keeps `Olaf::Fake` covering every query in tests.
|
|
65
65
|
#
|
|
66
66
|
# @return Olaf driver instance
|
|
67
|
-
# * Olaf::Fake
|
|
68
|
-
# * Olaf::
|
|
69
|
-
# * Olaf::BigQuery - google-cloud-bigquery driver to run queries in BigQuery
|
|
67
|
+
# * Olaf::Fake - Ideal for testing
|
|
68
|
+
# * Olaf::BigQuery - google-cloud-bigquery driver to run queries in BigQuery
|
|
70
69
|
#
|
|
71
70
|
def self.instance(name = nil)
|
|
72
71
|
drivers = @drivers || raise('You need to configure Olaf before using it!')
|
data/olaf.gemspec
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Gem::Specification.new do |s|
|
|
2
2
|
s.name = 'olaf'
|
|
3
|
-
s.version = '0.
|
|
3
|
+
s.version = '1.0.1'
|
|
4
4
|
s.date = Time.now.strftime('%Y-%m-%d')
|
|
5
5
|
s.summary = 'Ruby wrapper for warehouse queries.'
|
|
6
6
|
s.authors = ['Emiliano Mancuso']
|
|
@@ -18,9 +18,7 @@ Gem::Specification.new do |s|
|
|
|
18
18
|
|
|
19
19
|
# Drivers load their own client libraries, so consumers only install the ones
|
|
20
20
|
# they configure:
|
|
21
|
-
# Olaf::
|
|
22
|
-
# Olaf::BigQuery - google-cloud-bigquery
|
|
21
|
+
# Olaf::BigQuery - google-cloud-bigquery
|
|
23
22
|
s.add_development_dependency 'google-cloud-bigquery', '~> 1.64'
|
|
24
|
-
s.add_development_dependency 'sequel', '~> 5.37'
|
|
25
23
|
s.add_development_dependency 'test-unit', '~> 3.3'
|
|
26
24
|
end
|
data/test/olaf_configure_test.rb
CHANGED
|
@@ -20,10 +20,10 @@ class OlafConfigureTest < Test::Unit::TestCase
|
|
|
20
20
|
assert_equal Olaf.instance.config, { other: 'configs', like: 'user and passwd' }
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
def test_configure_requires_a_driver
|
|
24
|
+
assert_raise ArgumentError do
|
|
25
|
+
Olaf.configure(random: 'stuff')
|
|
26
|
+
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def test_configure_returns_the_default_driver
|
|
@@ -31,33 +31,33 @@ class OlafConfigureTest < Test::Unit::TestCase
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def test_configure_registers_several_drivers
|
|
34
|
-
|
|
34
|
+
postgres = MockDriver.new
|
|
35
35
|
big_query = MockDriver.new
|
|
36
36
|
|
|
37
|
-
Olaf.configure(drivers: {
|
|
37
|
+
Olaf.configure(drivers: { postgres: postgres, big_query: big_query }, default: :postgres)
|
|
38
38
|
|
|
39
|
-
assert_equal Olaf.instance,
|
|
40
|
-
assert_equal Olaf.instance(:
|
|
39
|
+
assert_equal Olaf.instance, postgres
|
|
40
|
+
assert_equal Olaf.instance(:postgres), postgres
|
|
41
41
|
assert_equal Olaf.instance(:big_query), big_query
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def test_configure_defaults_to_the_first_driver_registered
|
|
45
|
-
|
|
45
|
+
postgres = MockDriver.new
|
|
46
46
|
|
|
47
|
-
Olaf.configure(drivers: {
|
|
47
|
+
Olaf.configure(drivers: { postgres: postgres, big_query: MockDriver.new })
|
|
48
48
|
|
|
49
|
-
assert_equal Olaf.instance,
|
|
49
|
+
assert_equal Olaf.instance, postgres
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def test_configure_rejects_a_default_that_was_not_registered
|
|
53
53
|
assert_raise_message(/Unknown default driver :redshift/) do
|
|
54
|
-
Olaf.configure(drivers: {
|
|
54
|
+
Olaf.configure(drivers: { postgres: MockDriver.new }, default: :redshift)
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def test_configure_rejects_both_forms_at_once
|
|
59
59
|
assert_raise ArgumentError do
|
|
60
|
-
Olaf.configure(olaf_driver: MockDriver, drivers: {
|
|
60
|
+
Olaf.configure(olaf_driver: MockDriver, drivers: { postgres: MockDriver.new })
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
|
|
@@ -74,7 +74,7 @@ class OlafConfigureTest < Test::Unit::TestCase
|
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
def test_instance_raises_for_a_driver_that_was_not_registered
|
|
77
|
-
Olaf.configure(drivers: {
|
|
77
|
+
Olaf.configure(drivers: { postgres: MockDriver.new, big_query: MockDriver.new })
|
|
78
78
|
|
|
79
79
|
assert_raise Olaf::UnknownDriverError do
|
|
80
80
|
Olaf.instance(:redshift)
|
data/test/olaf_execute_test.rb
CHANGED
|
@@ -81,7 +81,7 @@ class OlafExecuteTest < Test::Unit::TestCase
|
|
|
81
81
|
default_driver = RecordingDriver.new
|
|
82
82
|
other_driver = RecordingDriver.new
|
|
83
83
|
|
|
84
|
-
Olaf.configure(drivers: { big_query: other_driver,
|
|
84
|
+
Olaf.configure(drivers: { big_query: other_driver, postgres: default_driver }, default: :postgres)
|
|
85
85
|
|
|
86
86
|
Olaf.execute(@query_instance)
|
|
87
87
|
|
|
@@ -98,7 +98,7 @@ class OlafExecuteTest < Test::Unit::TestCase
|
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
def test_execute_raises_when_the_driver_declared_is_not_configured
|
|
101
|
-
Olaf.configure(drivers: { default: RecordingDriver.new,
|
|
101
|
+
Olaf.configure(drivers: { default: RecordingDriver.new, postgres: RecordingDriver.new })
|
|
102
102
|
|
|
103
103
|
@query.driver :big_query
|
|
104
104
|
|
|
@@ -12,15 +12,13 @@ class OlafLazyLoadingTest < Test::Unit::TestCase
|
|
|
12
12
|
assert_equal loaded_after('require "olaf"; Olaf.configure(olaf_driver: Olaf::Fake)'), []
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
def
|
|
16
|
-
assert_equal loaded_after('require "olaf"; Olaf.configure(
|
|
15
|
+
def test_configuring_the_big_query_driver_loads_its_client_only
|
|
16
|
+
assert_equal loaded_after('require "olaf"; Olaf.configure(olaf_driver: Olaf::BigQuery)'), ['google-cloud-bigquery']
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
private
|
|
20
20
|
|
|
21
21
|
DEPENDENCIES = {
|
|
22
|
-
'sequel' => 'Sequel',
|
|
23
|
-
'odbc_utf8' => 'ODBC',
|
|
24
22
|
'google-cloud-bigquery' => 'Google::Cloud::Bigquery'
|
|
25
23
|
}.freeze
|
|
26
24
|
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: olaf
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Emiliano Mancuso
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-10-20 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: google-cloud-bigquery
|
|
@@ -23,20 +23,6 @@ dependencies:
|
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '1.64'
|
|
26
|
-
- !ruby/object:Gem::Dependency
|
|
27
|
-
name: sequel
|
|
28
|
-
requirement: !ruby/object:Gem::Requirement
|
|
29
|
-
requirements:
|
|
30
|
-
- - "~>"
|
|
31
|
-
- !ruby/object:Gem::Version
|
|
32
|
-
version: '5.37'
|
|
33
|
-
type: :development
|
|
34
|
-
prerelease: false
|
|
35
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
-
requirements:
|
|
37
|
-
- - "~>"
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: '5.37'
|
|
40
26
|
- !ruby/object:Gem::Dependency
|
|
41
27
|
name: test-unit
|
|
42
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -62,7 +48,6 @@ files:
|
|
|
62
48
|
- lib/olaf.rb
|
|
63
49
|
- lib/olaf/drivers/big_query.rb
|
|
64
50
|
- lib/olaf/drivers/fake.rb
|
|
65
|
-
- lib/olaf/drivers/snowflake.rb
|
|
66
51
|
- lib/olaf/errors.rb
|
|
67
52
|
- lib/olaf/query_definition.rb
|
|
68
53
|
- lib/olaf/query_definition/class_methods.rb
|
|
@@ -90,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
90
75
|
- !ruby/object:Gem::Version
|
|
91
76
|
version: '0'
|
|
92
77
|
requirements: []
|
|
93
|
-
rubygems_version:
|
|
78
|
+
rubygems_version: 3.6.9
|
|
94
79
|
specification_version: 4
|
|
95
80
|
summary: Ruby wrapper for warehouse queries.
|
|
96
81
|
test_files:
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
require 'sequel'
|
|
2
|
-
|
|
3
|
-
module Olaf
|
|
4
|
-
class Snowflake
|
|
5
|
-
WRAPPED_ERRORS = [Sequel::DatabaseError].freeze
|
|
6
|
-
|
|
7
|
-
def initialize(**config)
|
|
8
|
-
@config = config
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def fetch(olaf_query)
|
|
12
|
-
conn.fetch(olaf_query.sql_template, **olaf_query.variables).all
|
|
13
|
-
rescue *WRAPPED_ERRORS => error
|
|
14
|
-
raise QueryExecutionError.new(error.message, olaf_query)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
private
|
|
18
|
-
|
|
19
|
-
def conn
|
|
20
|
-
# Only opening a connection needs the ODBC system library.
|
|
21
|
-
@conn ||= begin
|
|
22
|
-
require 'odbc_utf8'
|
|
23
|
-
|
|
24
|
-
Sequel.odbc('snowflake', **@config)
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|