turboquery 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/.editorconfig +21 -0
- data/.gitignore +20 -0
- data/.rubocop.yml +35 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +3 -0
- data/Rakefile +8 -0
- data/lib/turboquery/connection.rb +130 -0
- data/lib/turboquery/olap.rb +55 -0
- data/lib/turboquery/oltp.rb +24 -0
- data/lib/turboquery/remote_query.rb +144 -0
- data/lib/turboquery/table_mover.rb +43 -0
- data/lib/turboquery/version.rb +3 -0
- data/lib/turboquery.rb +42 -0
- data/turboquery.gemspec +24 -0
- metadata +132 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e52ed1a1ab83ae67ea386d648de86f4cb8852fa9
|
4
|
+
data.tar.gz: c8964efb1208aa21975ea6a6bd07680f2c7124e5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0ca6e4c6bdbb3118f6e1e948e06910c61abb5494564279d1f52fb76a0a309f565528bad532335a4ea2b2d9dd2be337f4ad75264e25d153e39918ee14366a40c5
|
7
|
+
data.tar.gz: a894f45341fd2f2b2c260b8df329703075cac03c4da53d73e12ec7d1f218e1fea184fd467d6d9dfa9765490a98195be846a8c23a366d1224ac6990789061184e
|
data/.editorconfig
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# editorconfig.org
|
2
|
+
root = true
|
3
|
+
|
4
|
+
[*]
|
5
|
+
indent_style = space
|
6
|
+
indent_size = 2
|
7
|
+
end_of_line = lf
|
8
|
+
charset = utf-8
|
9
|
+
trim_trailing_whitespace = true
|
10
|
+
insert_final_newline = true
|
11
|
+
|
12
|
+
[*.md]
|
13
|
+
trim_trailing_whitespace = false
|
14
|
+
|
15
|
+
[*.go]
|
16
|
+
indent_style = tab
|
17
|
+
indent_size = 4
|
18
|
+
|
19
|
+
[Makefile]
|
20
|
+
indent_style = tab
|
21
|
+
indent_size = 4
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Disable for db/schema.rb
|
2
|
+
AllCops:
|
3
|
+
Exclude:
|
4
|
+
- db/**/*
|
5
|
+
|
6
|
+
# Disable requirement for documentation
|
7
|
+
Documentation:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
# Allow extra lines around class body
|
11
|
+
EmptyLinesAroundClassBody:
|
12
|
+
Enabled: false
|
13
|
+
|
14
|
+
# Allow extra lines around module body
|
15
|
+
EmptyLinesAroundModuleBody:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
# Allow extra lines inside block body
|
19
|
+
EmptyLinesAroundBlockBody:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
# Use single quotes unless necessary
|
23
|
+
StringLiterals:
|
24
|
+
EnforcedStyle: single_quotes
|
25
|
+
|
26
|
+
# Only treat is_ as a predicate name
|
27
|
+
PredicateName:
|
28
|
+
NamePrefixBlacklist:
|
29
|
+
- is_
|
30
|
+
|
31
|
+
LineLength:
|
32
|
+
Enabled: false
|
33
|
+
|
34
|
+
ClassAndModuleChildren:
|
35
|
+
Enabled: false
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2015 Jacob Gillespie <jacobwgillespie@gmail.com>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person ob-
|
4
|
+
taining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without restric-
|
6
|
+
tion, including without limitation the rights to use, copy, modi-
|
7
|
+
fy, merge, publish, distribute, sublicense, and/or sell copies of
|
8
|
+
the Software, and to permit persons to whom the Software is fur-
|
9
|
+
nished to do so, subject to the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
16
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONIN-
|
17
|
+
FRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
19
|
+
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,3 @@
|
|
1
|
+
# turboquery [](https://travis-ci.org/playlist-media/turboquery) [](https://codeclimate.com/repos/54e54f5e69568049a90046d9/feed)
|
2
|
+
|
3
|
+
:gem: Turboquery executes PostgreSQL queries against your PostgreSQL database utilizing Redshift to massively speed up complex queries.
|
data/Rakefile
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
class Turboquery::Connection
|
4
|
+
|
5
|
+
def table_exists?(table = nil)
|
6
|
+
return false if table.nil?
|
7
|
+
connection.execute("
|
8
|
+
SELECT 1 AS exists
|
9
|
+
FROM information_schema.tables
|
10
|
+
WHERE table_schema = 'public'
|
11
|
+
AND table_name = '#{table}';
|
12
|
+
").count == 1
|
13
|
+
end
|
14
|
+
|
15
|
+
def dump_table_ddl(table)
|
16
|
+
set_env
|
17
|
+
schema = `pg_dump -i -s -x -o -O --no-tablespaces --no-security-labels -t #{Shellwords.escape(table)} #{Shellwords.escape(config[:database])}`
|
18
|
+
schema.empty? ? '' : schema.match(/CREATE TABLE[^;]+;/)[0]
|
19
|
+
end
|
20
|
+
|
21
|
+
def copy_table_to_s3(_table)
|
22
|
+
fail 'Not implemented'
|
23
|
+
end
|
24
|
+
|
25
|
+
def copy_s3_to_table(_key, _table)
|
26
|
+
fail 'Not implemented'
|
27
|
+
end
|
28
|
+
|
29
|
+
def execute(*args, &block)
|
30
|
+
connection.execute(*args, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def config
|
34
|
+
connection.instance_variable_get(:@config)
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_env
|
38
|
+
PsqlEnv.set(config)
|
39
|
+
end
|
40
|
+
|
41
|
+
def after_fork
|
42
|
+
self.class.after_fork
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.after_fork
|
46
|
+
AR.establish_connection
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def connection
|
52
|
+
AR.connection
|
53
|
+
end
|
54
|
+
|
55
|
+
def random_key
|
56
|
+
SecureRandom.hex(10)
|
57
|
+
end
|
58
|
+
|
59
|
+
def valid_objects(key)
|
60
|
+
Turboquery.s3_bucket.objects.to_a.select do |obj|
|
61
|
+
obj.key =~ /^#{key}/ && !(obj.key =~ /manifest$/)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def build_manifest(objects)
|
66
|
+
{
|
67
|
+
entries: objects.map do |obj|
|
68
|
+
{
|
69
|
+
url: "s3://#{obj.bucket_name}/#{obj.key}",
|
70
|
+
mandatory: true
|
71
|
+
}
|
72
|
+
end
|
73
|
+
}.to_json
|
74
|
+
end
|
75
|
+
|
76
|
+
def upload_manifest(key)
|
77
|
+
objects = valid_objects(key)
|
78
|
+
file = Tempfile.open('turboquery', Turboquery.tmp_path) do |f|
|
79
|
+
f.write(build_manifest(objects))
|
80
|
+
f
|
81
|
+
end
|
82
|
+
copy_file_to_s3(file.path, "#{key}manifest")
|
83
|
+
end
|
84
|
+
|
85
|
+
def copy_s3_to_tmp(key)
|
86
|
+
objects = valid_objects(key)
|
87
|
+
file = Tempfile.open('turboquery', Turboquery.tmp_path) do |f|
|
88
|
+
objects.each do |object|
|
89
|
+
f.write object.get.body.read
|
90
|
+
end
|
91
|
+
f
|
92
|
+
end
|
93
|
+
file.path
|
94
|
+
end
|
95
|
+
|
96
|
+
def copy_file_to_s3(filename, key)
|
97
|
+
object = Turboquery.s3_bucket.object(key)
|
98
|
+
object.upload_file(filename)
|
99
|
+
end
|
100
|
+
|
101
|
+
class AR < ActiveRecord::Base
|
102
|
+
self.abstract_class = true
|
103
|
+
end
|
104
|
+
|
105
|
+
module PsqlEnv
|
106
|
+
def self.set(config)
|
107
|
+
host(config) if config[:host]
|
108
|
+
port(config) if config[:port]
|
109
|
+
password(config) if config[:password]
|
110
|
+
username(config) if config[:username]
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.host(config)
|
114
|
+
ENV['PGHOST'] = config[:host]
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.port(config)
|
118
|
+
ENV['PGPORT'] = config[:port].to_s
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.password(config)
|
122
|
+
ENV['PGPASSWORD'] = config[:password].to_s
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.username(config)
|
126
|
+
ENV['PGUSER'] = config[:username].to_s
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class Turboquery::OLAP < Turboquery::Connection
|
2
|
+
def copy_table_to_s3(table)
|
3
|
+
copy_result_to_s3("SELECT * FROM #{table}")
|
4
|
+
end
|
5
|
+
|
6
|
+
def copy_result_to_s3(query)
|
7
|
+
key = random_key
|
8
|
+
execute("UNLOAD ('#{query}') TO 's3://#{Turboquery.s3_config['bucket']}/#{key}' #{copy_options};")
|
9
|
+
key
|
10
|
+
end
|
11
|
+
|
12
|
+
def copy_s3_to_table(key, table)
|
13
|
+
execute("COPY #{table} FROM 's3://#{Turboquery.s3_config['bucket']}/#{key}manifest' #{copy_options}
|
14
|
+
DATEFORMAT 'auto' TIMEFORMAT 'auto';")
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute_to_temporary_table(query)
|
18
|
+
key = random_key
|
19
|
+
sql = "CREATE TEMPORARY TABLE #{key} AS #{query}"
|
20
|
+
execute(sql)
|
21
|
+
key
|
22
|
+
end
|
23
|
+
|
24
|
+
def drop_table(key)
|
25
|
+
sql = "DROP TABLE IF EXISTS #{key}"
|
26
|
+
execute(sql)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.after_fork
|
30
|
+
ARRedshift.reconnect
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
def connection
|
36
|
+
ARRedshift.connection
|
37
|
+
end
|
38
|
+
|
39
|
+
def excape_single_quotes(str)
|
40
|
+
str.gsub(/'/, %q(\\\'))
|
41
|
+
end
|
42
|
+
|
43
|
+
def copy_options
|
44
|
+
"CREDENTIALS 'aws_access_key_id=#{Turboquery.s3_config['access_key_id']};aws_secret_access_key=#{Turboquery.s3_config['secret_access_key']}'
|
45
|
+
MANIFEST DELIMITER '\\t' NULL AS '\\\\N'"
|
46
|
+
end
|
47
|
+
|
48
|
+
class ARRedshift < ActiveRecord::Base
|
49
|
+
establish_connection DatabaseUrl.new(ENV['REDSHIFT_DATABASE_URL']).to_hash
|
50
|
+
|
51
|
+
def self.reconnect
|
52
|
+
establish_connection DatabaseUrl.new(ENV['REDSHIFT_DATABASE_URL']).to_hash
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
class Turboquery::OLTP < Turboquery::Connection
|
4
|
+
def copy_table_to_s3(table)
|
5
|
+
set_env
|
6
|
+
|
7
|
+
temp = Tempfile.new('turboquery', Turboquery.tmp_path)
|
8
|
+
command = "echo 'COPY #{table} TO STDOUT' | psql #{Shellwords.escape(config[:database])} > #{temp.path}"
|
9
|
+
Kernel.system(command)
|
10
|
+
|
11
|
+
key = random_key
|
12
|
+
copy_file_to_s3(temp.path, key)
|
13
|
+
upload_manifest(key)
|
14
|
+
key
|
15
|
+
end
|
16
|
+
|
17
|
+
def copy_s3_to_table(key, table)
|
18
|
+
set_env
|
19
|
+
path = copy_s3_to_tmp(key)
|
20
|
+
sql = "COPY #{table} FROM STDIN"
|
21
|
+
command = "cat #{Shellwords.escape(path)} | psql -c #{Shellwords.escape(sql)} #{Shellwords.escape(config[:database])}"
|
22
|
+
Kernel.system(command)
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
class Turboquery::RemoteQuery
|
2
|
+
|
3
|
+
INSERT_INTO_REGEX = /^INSERT INTO (.+) SELECT/i
|
4
|
+
|
5
|
+
attr_reader :query, :opts, :destination
|
6
|
+
|
7
|
+
def initialize(query, opts = {})
|
8
|
+
@query = query.dup
|
9
|
+
@table_mapping = opts[:table_mapping] || {}
|
10
|
+
@reverse_table_mapping = {}
|
11
|
+
@copy_tables = opts[:copy_tables] || []
|
12
|
+
@opts = opts
|
13
|
+
format_query
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_sql
|
17
|
+
query
|
18
|
+
end
|
19
|
+
|
20
|
+
def execute
|
21
|
+
# Copy any missing tables or requested tables
|
22
|
+
(missing_tables + @copy_tables).uniq.each { |t| copy_local_table_to_remote(t) }
|
23
|
+
|
24
|
+
# Run query and save to temporary table
|
25
|
+
execute_query
|
26
|
+
|
27
|
+
# Export results if no destination specified
|
28
|
+
return Turboquery.olap.execute("SELECT * FROM #{temp_table_key}") unless @destination
|
29
|
+
|
30
|
+
# Copy data from OLAP to OLTP
|
31
|
+
copy_remote_table_to_local(temp_table_key, @destination)
|
32
|
+
|
33
|
+
# Clean up temporary table
|
34
|
+
Turboquery.olap.execute("DROP TABLE #{temp_table_key}")
|
35
|
+
|
36
|
+
# Return success
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def temp_table_key
|
43
|
+
@temp_table_key ||= "turboquery_#{SecureRandom.hex(10)}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def insert_into
|
47
|
+
@insert_into ||= begin
|
48
|
+
match = query.match(INSERT_INTO_REGEX)
|
49
|
+
match.nil? ? nil : match[1]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def insert_into?
|
54
|
+
!insert_into.nil?
|
55
|
+
end
|
56
|
+
|
57
|
+
def format_query
|
58
|
+
@destination = insert_into || opts[:destination]
|
59
|
+
strip_insert_into if insert_into?
|
60
|
+
build_table_tokens
|
61
|
+
build_table_mapping
|
62
|
+
build_reverse_table_mapping
|
63
|
+
prefix_tables
|
64
|
+
end
|
65
|
+
|
66
|
+
def build_table_tokens
|
67
|
+
@table_tokens = locate_tables(PgQuery.parse(@query).parsetree[0])
|
68
|
+
end
|
69
|
+
|
70
|
+
def locate_tables(parsetree)
|
71
|
+
tables = []
|
72
|
+
|
73
|
+
return [] unless parsetree.is_a? Hash
|
74
|
+
|
75
|
+
parsetree.each do |key, val|
|
76
|
+
tables << { table: val, location: parsetree['location'] } if key == 'relname'
|
77
|
+
tables << locate_tables(val) if val.is_a? Hash
|
78
|
+
tables << val.map { |v| locate_tables(v) } if val.is_a? Array
|
79
|
+
end
|
80
|
+
|
81
|
+
tables.flatten
|
82
|
+
end
|
83
|
+
|
84
|
+
def build_table_mapping
|
85
|
+
@table_tokens.each do |table|
|
86
|
+
@table_mapping[table[:table]] ||= "turboquery_#{table[:table]}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def build_reverse_table_mapping
|
91
|
+
@table_mapping.each do |local, remote|
|
92
|
+
@reverse_table_mapping[remote] = local
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def prefix_tables
|
97
|
+
@table_tokens.reverse.each do |table|
|
98
|
+
@query[table[:location], table[:table].length] = @table_mapping[table[:table]]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def strip_insert_into
|
103
|
+
@query.gsub! INSERT_INTO_REGEX, 'SELECT'
|
104
|
+
end
|
105
|
+
|
106
|
+
def local_tables
|
107
|
+
@table_mapping.map { |k, _v| k }
|
108
|
+
end
|
109
|
+
|
110
|
+
def remote_tables
|
111
|
+
@table_mapping.map { |_k, v| v }
|
112
|
+
end
|
113
|
+
|
114
|
+
def missing_tables
|
115
|
+
remote_tables.select { |t| !Turboquery.olap.table_exists?(t) }
|
116
|
+
end
|
117
|
+
|
118
|
+
def copy_local_table_to_remote(table)
|
119
|
+
Turboquery::TableMover.new(
|
120
|
+
source: Turboquery.oltp,
|
121
|
+
destination: Turboquery.olap,
|
122
|
+
from_table: @reverse_table_mapping[table],
|
123
|
+
to_table: table
|
124
|
+
).move
|
125
|
+
end
|
126
|
+
|
127
|
+
def copy_remote_table_to_local(remote, local)
|
128
|
+
Turboquery::TableMover.new(
|
129
|
+
source: Turboquery.olap,
|
130
|
+
destination: Turboquery.oltp,
|
131
|
+
from_table: remote,
|
132
|
+
to_table: local
|
133
|
+
).move
|
134
|
+
end
|
135
|
+
|
136
|
+
def execute_query
|
137
|
+
if @destination
|
138
|
+
Turboquery.olap.execute("CREATE TABLE #{temp_table_key} AS #{@query}")
|
139
|
+
else
|
140
|
+
Turboquery.olap.execute("CREATE TEMPORARY TABLE #{temp_table_key} AS #{@query}")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Turboquery::TableMover
|
2
|
+
|
3
|
+
attr_accessor :source, :destination, :from_table, :to_table
|
4
|
+
|
5
|
+
def initialize(source:, destination:, from_table:, to_table:)
|
6
|
+
self.source = source
|
7
|
+
self.destination = destination
|
8
|
+
self.from_table = from_table
|
9
|
+
self.to_table = to_table
|
10
|
+
end
|
11
|
+
|
12
|
+
def move
|
13
|
+
create_destination unless destination_exists?
|
14
|
+
key = copy_source_to_s3
|
15
|
+
copy_s3_to_destination(key)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def source_exists?
|
21
|
+
source.table_exists?(from_table)
|
22
|
+
end
|
23
|
+
|
24
|
+
def destination_exists?
|
25
|
+
destination.table_exists?(to_table)
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_destination
|
29
|
+
structure = source.dump_table_ddl(from_table)
|
30
|
+
structure.gsub!(/^CREATE TABLE [^\(]+\(/, "CREATE TABLE #{to_table} (")
|
31
|
+
destination.execute(structure)
|
32
|
+
fail 'Unable to create destination table' unless destination_exists?
|
33
|
+
end
|
34
|
+
|
35
|
+
def copy_source_to_s3
|
36
|
+
source.copy_table_to_s3(from_table)
|
37
|
+
end
|
38
|
+
|
39
|
+
def copy_s3_to_destination(key)
|
40
|
+
destination.copy_s3_to_table(key, to_table)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
data/lib/turboquery.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module Turboquery
|
2
|
+
class << self
|
3
|
+
def query(sql, opts = {})
|
4
|
+
Turboquery::RemoteQuery.new(sql, opts).execute
|
5
|
+
end
|
6
|
+
|
7
|
+
def update_table(table)
|
8
|
+
Turboquery::TableMover.new(source: oltp, destination: olap, from_table: table, to_table: "turboquery_#{table}")
|
9
|
+
end
|
10
|
+
|
11
|
+
def oltp
|
12
|
+
@oltp ||= Turboquery::OLTP.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def olap
|
16
|
+
@olap ||= Turboquery::OLAP.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def s3_bucket
|
20
|
+
@s3_bucket ||= begin
|
21
|
+
Aws::S3::Resource.new(
|
22
|
+
region: 'us-east-1',
|
23
|
+
access_key_id: s3_config['access_key_id'],
|
24
|
+
secret_access_key: s3_config['secret_access_key']
|
25
|
+
).bucket(s3_config['bucket'])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def s3_config
|
30
|
+
Rails.application.secrets.turboquery
|
31
|
+
end
|
32
|
+
|
33
|
+
def tmp_path
|
34
|
+
ENV['TURBOQUERY_TMP_PATH'] || Rails.root.join('tmp')
|
35
|
+
end
|
36
|
+
|
37
|
+
def after_fork
|
38
|
+
olap.after_fork
|
39
|
+
oltp.after_fork
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/turboquery.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.expand_path('../lib/turboquery/version', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = 'turboquery'
|
5
|
+
spec.version = Turboquery::VERSION
|
6
|
+
spec.authors = ['Jacob Gillespie']
|
7
|
+
spec.email = ['jacobwgillespie@gmail.com']
|
8
|
+
spec.description = 'Execute with the power of Redshift'
|
9
|
+
spec.summary = 'Turboquery allows you to execute your Postgres queries on your Postgres data with Redshift.'
|
10
|
+
spec.homepage = 'https://github.com/jacobwgillespie/turboquery'
|
11
|
+
spec.license = 'MIT'
|
12
|
+
|
13
|
+
spec.files = `git ls-files`.split("\n")
|
14
|
+
spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
|
15
|
+
spec.test_files = spec.files.grep(/^(test|spec|features)\//)
|
16
|
+
spec.require_paths = ['lib']
|
17
|
+
|
18
|
+
spec.add_dependency 'activerecord', '~> 4.0'
|
19
|
+
spec.add_dependency 'pg_query'
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
spec.add_development_dependency 'minitest'
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: turboquery
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jacob Gillespie
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pg_query
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.6'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.6'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Execute with the power of Redshift
|
84
|
+
email:
|
85
|
+
- jacobwgillespie@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".editorconfig"
|
91
|
+
- ".gitignore"
|
92
|
+
- ".rubocop.yml"
|
93
|
+
- ".travis.yml"
|
94
|
+
- Gemfile
|
95
|
+
- LICENSE
|
96
|
+
- README.md
|
97
|
+
- Rakefile
|
98
|
+
- lib/turboquery.rb
|
99
|
+
- lib/turboquery/connection.rb
|
100
|
+
- lib/turboquery/olap.rb
|
101
|
+
- lib/turboquery/oltp.rb
|
102
|
+
- lib/turboquery/remote_query.rb
|
103
|
+
- lib/turboquery/table_mover.rb
|
104
|
+
- lib/turboquery/version.rb
|
105
|
+
- turboquery.gemspec
|
106
|
+
homepage: https://github.com/jacobwgillespie/turboquery
|
107
|
+
licenses:
|
108
|
+
- MIT
|
109
|
+
metadata: {}
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
requirements: []
|
125
|
+
rubyforge_project:
|
126
|
+
rubygems_version: 2.4.5
|
127
|
+
signing_key:
|
128
|
+
specification_version: 4
|
129
|
+
summary: Turboquery allows you to execute your Postgres queries on your Postgres data
|
130
|
+
with Redshift.
|
131
|
+
test_files: []
|
132
|
+
has_rdoc:
|