big_query_adapter 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/LICENSE +21 -0
- data/README.md +51 -0
- data/Rakefile +10 -0
- data/big_query_adapter.gemspec +31 -0
- data/bin/console +13 -0
- data/bin/setup +8 -0
- data/docker-compose.yml +17 -0
- data/lib/active_record/connection_adapters/big_query_adapter.rb +141 -0
- data/lib/big_query_adapter.rb +2 -0
- data/lib/big_query_adapter/connection.rb +106 -0
- data/lib/big_query_adapter/database_statements.rb +47 -0
- data/lib/big_query_adapter/error.rb +4 -0
- data/lib/big_query_adapter/quoting.rb +14 -0
- data/lib/big_query_adapter/schema_statements.rb +69 -0
- data/lib/big_query_adapter/version.rb +3 -0
- metadata +146 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ff8793c82931ac9adc5f02f8ebf19020aa93d35a
|
4
|
+
data.tar.gz: ec244155405cc0d2c4f5c5f58a8698ebe8e4b97b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5ce19466cf43782c07a92da4661159236e97e8ed6834aa45095a4c8ded1c1778b7b155871fa7c2b5d5681bba3569fd21113205a8ba35e609b6655abbe9277e7a
|
7
|
+
data.tar.gz: ef3f534b1b345a10763a319f0f30a6653cc412ffc605b5497aaedd37b351da6f9985cc9432ad12f2130c755762b7c7b47ddbdf080eedccf765ec42c784b0f209
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2017 Localytics http://www.localytics.com
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# BigQueryAdapter
|
2
|
+
|
3
|
+
EXPERIMENTAL VERSION:
|
4
|
+
|
5
|
+
Features so far:
|
6
|
+
- perform select queries in a bigquery table, using
|
7
|
+
- possibility to generate schema
|
8
|
+
|
9
|
+
Inspiration and fork of the project:
|
10
|
+
|
11
|
+
[localytics odbc activerecord adapter](https://github.com/localytics/odbc_adapter)
|
12
|
+
[blog post](http://www.blrice.net/blog/2016/04/09/one-rails-app-with-many-databases/)
|
13
|
+
|
14
|
+
## Usage
|
15
|
+
|
16
|
+
Add this line to your application's Gemfile:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
gem 'big_query_adapter'
|
20
|
+
```
|
21
|
+
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
options = { adapter: 'big_query' }
|
25
|
+
options[:keyfile] = "path-to-key-file"
|
26
|
+
options[:project] = "your-project-id"
|
27
|
+
options[:datasets] = [] # specify dataset for faster schema generation
|
28
|
+
ActiveRecord::Base.establish_connection(options)
|
29
|
+
```
|
30
|
+
|
31
|
+
TODO: add example
|
32
|
+
|
33
|
+
In the meanwhile check this post:
|
34
|
+
|
35
|
+
[One rails app with many databases](http://www.blrice.net/blog/2016/04/09/one-rails-app-with-many-databases/)
|
36
|
+
|
37
|
+
## Development
|
38
|
+
|
39
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
40
|
+
|
41
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
42
|
+
|
43
|
+
## Contributing
|
44
|
+
|
45
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/big_query_adapter. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
46
|
+
|
47
|
+
|
48
|
+
## License
|
49
|
+
|
50
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
51
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'big_query_adapter/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'big_query_adapter'
|
9
|
+
spec.version = BigQueryAdapter::VERSION
|
10
|
+
spec.authors = ['pedrocarmona']
|
11
|
+
spec.email = ['pcarmona1990@gmail.com']
|
12
|
+
|
13
|
+
spec.summary = 'An ActiveRecord Google BigQuery adapter'
|
14
|
+
spec.homepage = 'https://github.com/pedrocarmona/big_query_adapter'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = 'exe'
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ['lib']
|
23
|
+
|
24
|
+
spec.add_dependency 'google-cloud-bigquery', '~> 0.27.1'
|
25
|
+
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.13'
|
27
|
+
spec.add_development_dependency 'rake', '~> 12.0'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
29
|
+
spec.add_development_dependency 'rubocop', '~> 0.48'
|
30
|
+
spec.add_development_dependency 'simplecov', '~> 0.14'
|
31
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'big_query_adapter'
|
5
|
+
|
6
|
+
options = { adapter: 'big_query' }
|
7
|
+
options[:keyfile] = ENV.fetch('BIGQUERY_KEYFILE') if ENV['BIGQUERY_KEYFILE']
|
8
|
+
options[:project] = ENV.fetch('BIGQUERY_PROJECT') if ENV['BIGQUERY_PROJECT']
|
9
|
+
options[:datasets] = []
|
10
|
+
ActiveRecord::Base.establish_connection(options)
|
11
|
+
|
12
|
+
require 'pry'
|
13
|
+
Pry.start
|
data/bin/setup
ADDED
data/docker-compose.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
version: '2'
|
2
|
+
services:
|
3
|
+
app:
|
4
|
+
image: ruby:2.3.1
|
5
|
+
working_dir: /usr/src/app
|
6
|
+
environment:
|
7
|
+
TMPDIR: /usr/src/app/tmp
|
8
|
+
BUNDLE_PATH: /usr/src/app/.bundle
|
9
|
+
BUNDLE_CONFIG: /usr/src/app/bundle/config
|
10
|
+
BIGQUERY_KEYFILE: "/etc/app/keyfile.json"
|
11
|
+
BIGQUERY_PROJECT: "--replace-me--"
|
12
|
+
volumes:
|
13
|
+
- .:/usr/src/app
|
14
|
+
- ./etc/:/etc/app/
|
15
|
+
|
16
|
+
|
17
|
+
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'arel/visitors/bind_visitor'
|
3
|
+
|
4
|
+
require 'big_query_adapter/connection'
|
5
|
+
require 'big_query_adapter/database_statements'
|
6
|
+
require 'big_query_adapter/error'
|
7
|
+
require 'big_query_adapter/quoting'
|
8
|
+
require 'big_query_adapter/schema_statements'
|
9
|
+
|
10
|
+
require 'big_query_adapter/version'
|
11
|
+
|
12
|
+
module ActiveRecord
|
13
|
+
# no-doc
|
14
|
+
class Base
|
15
|
+
class << self
|
16
|
+
# Build a new BigQuery connection with the given configuration.
|
17
|
+
def big_query_connection(config)
|
18
|
+
config = config.symbolize_keys
|
19
|
+
params = {
|
20
|
+
keyfile: config[:keyfile],
|
21
|
+
project: config[:project],
|
22
|
+
datasets: config[:datasets]
|
23
|
+
}
|
24
|
+
connection = ::BigQueryAdapter::Connection.new(params)
|
25
|
+
ConnectionAdapters::BigQueryAdapter
|
26
|
+
.new(connection, logger, config, {})
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module ConnectionAdapters
|
32
|
+
# Adapter in the active record namespace
|
33
|
+
class BigQueryAdapter < AbstractAdapter
|
34
|
+
include ::BigQueryAdapter::DatabaseStatements
|
35
|
+
include ::BigQueryAdapter::Quoting
|
36
|
+
include ::BigQueryAdapter::SchemaStatements
|
37
|
+
|
38
|
+
ADAPTER_NAME = 'BigQuery'.freeze
|
39
|
+
|
40
|
+
ERR_DUPLICATE_KEY_VALUE = 23_505
|
41
|
+
ERR_QUERY_TIMED_OUT = 57_014
|
42
|
+
ERR_QUERY_TIMED_OUT_MESSAGE = /Query has timed out/
|
43
|
+
|
44
|
+
# The object that stores the information that is fetched from the DBMS
|
45
|
+
# when a connection is first established.
|
46
|
+
attr_reader :database_metadata
|
47
|
+
|
48
|
+
def initialize(connection, logger, config, database_metadata)
|
49
|
+
super(connection, logger, config)
|
50
|
+
@database_metadata = database_metadata
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns the human-readable name of the adapter.
|
54
|
+
def adapter_name
|
55
|
+
ADAPTER_NAME
|
56
|
+
end
|
57
|
+
|
58
|
+
# CONNECTION MANAGEMENT ====================================
|
59
|
+
|
60
|
+
# Checks whether the connection to the database is still active. This
|
61
|
+
# includes checking whether the database is actually capable of
|
62
|
+
# responding, i.e. whether the connection isn't stale.
|
63
|
+
def active?
|
64
|
+
@connection.run('SELECT TRUE AS active')
|
65
|
+
end
|
66
|
+
|
67
|
+
# Disconnects from the database if already connected, and establishes a
|
68
|
+
# new connection with the database.
|
69
|
+
def reconnect!
|
70
|
+
disconnect!
|
71
|
+
@connection = Base.big_query_connection(@config)
|
72
|
+
super
|
73
|
+
end
|
74
|
+
alias reset! reconnect!
|
75
|
+
|
76
|
+
# Disconnects from the database if already connected. Otherwise, this
|
77
|
+
# method does nothing.
|
78
|
+
def disconnect!
|
79
|
+
false
|
80
|
+
end
|
81
|
+
|
82
|
+
protected
|
83
|
+
|
84
|
+
# Build the type map for ActiveRecord
|
85
|
+
def initialize_type_map(map)
|
86
|
+
super
|
87
|
+
end
|
88
|
+
|
89
|
+
# Translate an exception from the native DBMS to something usable by
|
90
|
+
# ActiveRecord.
|
91
|
+
def translate_exception(exception, message)
|
92
|
+
error_number = exception.message[/^\d+/].to_i
|
93
|
+
|
94
|
+
if error_number == ERR_DUPLICATE_KEY_VALUE
|
95
|
+
ActiveRecord::RecordNotUnique.new(message, exception)
|
96
|
+
# rubocop:disable Metrics/LineLength
|
97
|
+
elsif error_number == ERR_QUERY_TIMED_OUT || exception.message =~ ERR_QUERY_TIMED_OUT_MESSAGE
|
98
|
+
::BigQueryAdapter::QueryTimeoutError.new(message, exception)
|
99
|
+
# rubocop:enable Metrics/LineLength
|
100
|
+
else
|
101
|
+
super
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# no-doc
|
106
|
+
class BindSubstitution < Arel::Visitors::ToSql
|
107
|
+
include Arel::Visitors::BindVisitor
|
108
|
+
end
|
109
|
+
|
110
|
+
# Using a BindVisitor so that the SQL string gets substituted before it is
|
111
|
+
# sent to the DBMS (to attempt to get as much coverage as possible for
|
112
|
+
# DBMSs we don't support).
|
113
|
+
def arel_visitor
|
114
|
+
BindSubstitution.new(self)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Explicitly turning off prepared_statements in the null adapter because
|
118
|
+
# there isn't really a standard on which substitution character to use.
|
119
|
+
def prepared_statements
|
120
|
+
false
|
121
|
+
end
|
122
|
+
|
123
|
+
# Turning off support for migrations because there is no information to
|
124
|
+
# go off of for what syntax the DBMS will expect.
|
125
|
+
def supports_migrations?
|
126
|
+
false
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
# Can't use the built-in ActiveRecord map#alias_type because it doesn't
|
132
|
+
# work with non-string keys, and in our case the keys are (almost) all
|
133
|
+
# numeric
|
134
|
+
def alias_type(map, new_type, old_type)
|
135
|
+
map.register_type(new_type) do |_, *args|
|
136
|
+
map.lookup(old_type, *args)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'google/cloud/bigquery'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
module BigQueryAdapter
|
5
|
+
# Driver for bigquery connection
|
6
|
+
class Connection
|
7
|
+
Result = Struct.new(:columns, :rows)
|
8
|
+
|
9
|
+
attr_reader :project
|
10
|
+
|
11
|
+
def initialize(project:, keyfile:, timeout: nil, datasets: [])
|
12
|
+
@project = project
|
13
|
+
@bigquery = Google::Cloud::Bigquery.new(
|
14
|
+
project: project,
|
15
|
+
keyfile: keyfile
|
16
|
+
)
|
17
|
+
@dataset_ids = datasets
|
18
|
+
@timeout = timeout.to_i if timeout
|
19
|
+
end
|
20
|
+
|
21
|
+
def run(statement)
|
22
|
+
columns = []
|
23
|
+
rows = []
|
24
|
+
|
25
|
+
options = {}
|
26
|
+
options[:timeout] = @timeout if @timeout
|
27
|
+
results = @bigquery.query(statement, options) # ms
|
28
|
+
if results.complete?
|
29
|
+
columns = results.first.keys.map(&:to_s) unless results.empty?
|
30
|
+
rows = results.map(&:values)
|
31
|
+
end
|
32
|
+
|
33
|
+
Result.new(columns, rows)
|
34
|
+
end
|
35
|
+
|
36
|
+
def tables
|
37
|
+
table_refs
|
38
|
+
.map { |table_ref| table_ref_name(table_ref) }
|
39
|
+
.group_by { |table_ref_name| table_ref_wildcard_name(table_ref_name) }
|
40
|
+
.keys
|
41
|
+
end
|
42
|
+
|
43
|
+
def columns(table_name)
|
44
|
+
table_schema = table_schema(table_name)
|
45
|
+
return [] if table_schema.fields.nil?
|
46
|
+
table_schema.fields
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def table_ref_name(table_ref)
|
52
|
+
"#{table_ref.project_id}.#{table_ref.dataset_id}.#{table_ref.table_id}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def table_ref_wildcard_name(table_ref_name)
|
56
|
+
if partitioned_table?(table_ref_name)
|
57
|
+
base_name = table_ref_name.split('_')
|
58
|
+
base_name.pop
|
59
|
+
base_name.join('_') << '_*'
|
60
|
+
else
|
61
|
+
table_ref_name
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def partitioned_table?(table_ref_name)
|
66
|
+
return false if table_ref_name.split('_').size < 2
|
67
|
+
date_str = table_ref_name.split('_').last
|
68
|
+
date = Date.strptime(date_str, '%Y%m%d')
|
69
|
+
return !date.nil?
|
70
|
+
rescue StandardError => _error
|
71
|
+
return nil
|
72
|
+
end
|
73
|
+
|
74
|
+
def datasets
|
75
|
+
return @bigquery.datasets if @dataset_ids.empty?
|
76
|
+
@bigquery.datasets.select do |dataset|
|
77
|
+
@dataset_ids.include?(dataset.dataset_id)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def table_refs
|
82
|
+
datasets
|
83
|
+
.map(&:tables)
|
84
|
+
.flat_map { |table_list| table_list.map(&:table_ref) }
|
85
|
+
end
|
86
|
+
|
87
|
+
def table_ref(table_name)
|
88
|
+
if table_name.ends_with?('_*')
|
89
|
+
table_name = table_name[0...-1]
|
90
|
+
table_refs.find do |table_ref|
|
91
|
+
table_ref_name(table_ref) =~ /#{table_name}[0-9]{8}/
|
92
|
+
end
|
93
|
+
else
|
94
|
+
table_refs.find { |table_ref| table_ref_name(table_ref) == table_name }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def table_schema(table_name)
|
99
|
+
table_ref = table_ref(table_name)
|
100
|
+
@bigquery.service.get_table(
|
101
|
+
table_ref.dataset_id,
|
102
|
+
table_ref.table_id
|
103
|
+
).schema
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module BigQueryAdapter
|
2
|
+
# Includes helper methods
|
3
|
+
module DatabaseStatements
|
4
|
+
NATIVE_DATABASE_TYPES = {
|
5
|
+
boolean: { name: 'BOOL' },
|
6
|
+
integer: { name: 'INTEGER' },
|
7
|
+
float: { name: 'FLOAT' },
|
8
|
+
string: { name: 'STRING' },
|
9
|
+
datetime: { name: 'DATETIME' },
|
10
|
+
date: { name: 'DATE' },
|
11
|
+
timestamp: { name: 'TIMESTAMP' },
|
12
|
+
time: { name: 'TIME' }
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
def native_database_types
|
16
|
+
NATIVE_DATABASE_TYPES
|
17
|
+
end
|
18
|
+
|
19
|
+
def valid_type?(type) # :nodoc:
|
20
|
+
!native_database_types[type].nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
# Executes the SQL statement in the context of this connection.
|
24
|
+
# Returns the number of rows affected.
|
25
|
+
def execute(sql, name = nil, _binds = [])
|
26
|
+
log(sql, name) do
|
27
|
+
@connection.do(sql)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Executes +sql+ statement in the context of this connection using
|
32
|
+
# +binds+ as the bind substitutes. +name+ is logged along with
|
33
|
+
# the executed +sql+ statement.
|
34
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
35
|
+
def exec_query(sql, name = 'SQL', _binds = [], prepare: false)
|
36
|
+
log(sql, name) do
|
37
|
+
result = @connection.run(sql)
|
38
|
+
ActiveRecord::Result.new(result.columns, result.rows)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
42
|
+
|
43
|
+
def supports_ddl_transactions
|
44
|
+
false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module BigQueryAdapter
|
2
|
+
# Includes helper methods
|
3
|
+
module Quoting
|
4
|
+
# Quotes a string, escaping any ' (single quote) characters.
|
5
|
+
def quote_string(string)
|
6
|
+
string.gsub(/\'/, "''")
|
7
|
+
end
|
8
|
+
|
9
|
+
# Quotes the table name. Defaults to column name quoting.
|
10
|
+
def quote_table_name(table_name)
|
11
|
+
"`#{table_name}`"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module BigQueryAdapter
|
2
|
+
# Includes helper methods
|
3
|
+
module SchemaStatements
|
4
|
+
# Returns an array of table names, for database tables visible on the
|
5
|
+
# current connection.
|
6
|
+
def tables(_name = nil)
|
7
|
+
raw_connection.tables
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns an array of view names defined in the database.
|
11
|
+
def views
|
12
|
+
[]
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns an array of indexes for the given table.
|
16
|
+
def indexes(_table_name, _name = nil)
|
17
|
+
[]
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns an array of Column objects for the table specified by
|
21
|
+
# +table_name+.
|
22
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
23
|
+
def columns(table_name, _name = nil)
|
24
|
+
result = @connection.columns(table_name.to_s)
|
25
|
+
|
26
|
+
result.each_with_object([]) do |field, cols|
|
27
|
+
col_name = field.name
|
28
|
+
col_sql_type = native_database_types.invert[name: field.type]
|
29
|
+
col_nullable = (field.mode == 'NULLABLE')
|
30
|
+
|
31
|
+
args = { sql_type: col_sql_type, type: col_sql_type, limit: nil }
|
32
|
+
args[:scale] = nil
|
33
|
+
args[:precision] = nil
|
34
|
+
|
35
|
+
sql_type_metadata =
|
36
|
+
ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(**args)
|
37
|
+
|
38
|
+
cols << ActiveRecord::ConnectionAdapters::Column.new(
|
39
|
+
col_name,
|
40
|
+
nil,
|
41
|
+
sql_type_metadata,
|
42
|
+
col_nullable,
|
43
|
+
table_name
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
48
|
+
|
49
|
+
# Returns just a table's primary key
|
50
|
+
def primary_key(_table_name)
|
51
|
+
[]
|
52
|
+
end
|
53
|
+
|
54
|
+
def foreign_keys(_table_name)
|
55
|
+
[]
|
56
|
+
end
|
57
|
+
|
58
|
+
# Ensure it's shorter than the maximum identifier length for the current
|
59
|
+
# dbms
|
60
|
+
def index_name(table_name, options)
|
61
|
+
maximum = database_metadata.max_identifier_len || 255
|
62
|
+
super(table_name, options)[0...maximum]
|
63
|
+
end
|
64
|
+
|
65
|
+
def current_database
|
66
|
+
database_metadata.database_name.strip
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
metadata
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: big_query_adapter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- pedrocarmona
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-08-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: google-cloud-bigquery
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.27.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.27.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.13'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.13'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '12.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '12.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.48'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.48'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.14'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.14'
|
97
|
+
description:
|
98
|
+
email:
|
99
|
+
- pcarmona1990@gmail.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".travis.yml"
|
106
|
+
- Gemfile
|
107
|
+
- LICENSE
|
108
|
+
- README.md
|
109
|
+
- Rakefile
|
110
|
+
- big_query_adapter.gemspec
|
111
|
+
- bin/console
|
112
|
+
- bin/setup
|
113
|
+
- docker-compose.yml
|
114
|
+
- lib/active_record/connection_adapters/big_query_adapter.rb
|
115
|
+
- lib/big_query_adapter.rb
|
116
|
+
- lib/big_query_adapter/connection.rb
|
117
|
+
- lib/big_query_adapter/database_statements.rb
|
118
|
+
- lib/big_query_adapter/error.rb
|
119
|
+
- lib/big_query_adapter/quoting.rb
|
120
|
+
- lib/big_query_adapter/schema_statements.rb
|
121
|
+
- lib/big_query_adapter/version.rb
|
122
|
+
homepage: https://github.com/pedrocarmona/big_query_adapter
|
123
|
+
licenses:
|
124
|
+
- MIT
|
125
|
+
metadata: {}
|
126
|
+
post_install_message:
|
127
|
+
rdoc_options: []
|
128
|
+
require_paths:
|
129
|
+
- lib
|
130
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
requirements: []
|
141
|
+
rubyforge_project:
|
142
|
+
rubygems_version: 2.6.8
|
143
|
+
signing_key:
|
144
|
+
specification_version: 4
|
145
|
+
summary: An ActiveRecord Google BigQuery adapter
|
146
|
+
test_files: []
|