activerecord-redshift-adapter 0.9.12 → 8.0.0.beta2
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 +5 -13
- data/LICENSE +25 -1
- data/README.md +29 -86
- data/lib/active_record/connection_adapters/redshift_7_0/array_parser.rb +92 -0
- data/lib/active_record/connection_adapters/redshift_7_0/column.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_7_0/database_statements.rb +232 -0
- data/lib/active_record/connection_adapters/redshift_7_0/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/redshift_7_0/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/redshift_7_0/oid/json.rb +41 -0
- data/lib/active_record/connection_adapters/redshift_7_0/oid/jsonb.rb +25 -0
- data/lib/active_record/connection_adapters/redshift_7_0/oid/type_map_initializer.rb +62 -0
- data/lib/active_record/connection_adapters/redshift_7_0/oid.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_7_0/quoting.rb +99 -0
- data/lib/active_record/connection_adapters/redshift_7_0/referential_integrity.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_7_0/schema_definitions.rb +70 -0
- data/lib/active_record/connection_adapters/redshift_7_0/schema_dumper.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_7_0/schema_statements.rb +421 -0
- data/lib/active_record/connection_adapters/redshift_7_0/type_metadata.rb +39 -0
- data/lib/active_record/connection_adapters/redshift_7_0/utils.rb +81 -0
- data/lib/active_record/connection_adapters/redshift_7_0_adapter.rb +765 -0
- data/lib/active_record/connection_adapters/redshift_7_1/array_parser.rb +92 -0
- data/lib/active_record/connection_adapters/redshift_7_1/column.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_7_1/database_statements.rb +180 -0
- data/lib/active_record/connection_adapters/redshift_7_1/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/redshift_7_1/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/redshift_7_1/oid/json.rb +41 -0
- data/lib/active_record/connection_adapters/redshift_7_1/oid/jsonb.rb +25 -0
- data/lib/active_record/connection_adapters/redshift_7_1/oid/type_map_initializer.rb +62 -0
- data/lib/active_record/connection_adapters/redshift_7_1/oid.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_7_1/quoting.rb +161 -0
- data/lib/active_record/connection_adapters/redshift_7_1/referential_integrity.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_7_1/schema_definitions.rb +70 -0
- data/lib/active_record/connection_adapters/redshift_7_1/schema_dumper.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_7_1/schema_statements.rb +422 -0
- data/lib/active_record/connection_adapters/redshift_7_1/type_metadata.rb +43 -0
- data/lib/active_record/connection_adapters/redshift_7_1/utils.rb +81 -0
- data/lib/active_record/connection_adapters/redshift_7_1_adapter.rb +844 -0
- data/lib/active_record/connection_adapters/redshift_7_2/array_parser.rb +92 -0
- data/lib/active_record/connection_adapters/redshift_7_2/column.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_7_2/database_statements.rb +180 -0
- data/lib/active_record/connection_adapters/redshift_7_2/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/redshift_7_2/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/redshift_7_2/oid/json.rb +41 -0
- data/lib/active_record/connection_adapters/redshift_7_2/oid/jsonb.rb +25 -0
- data/lib/active_record/connection_adapters/redshift_7_2/oid/type_map_initializer.rb +62 -0
- data/lib/active_record/connection_adapters/redshift_7_2/oid.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_7_2/quoting.rb +164 -0
- data/lib/active_record/connection_adapters/redshift_7_2/referential_integrity.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_7_2/schema_definitions.rb +70 -0
- data/lib/active_record/connection_adapters/redshift_7_2/schema_dumper.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_7_2/schema_statements.rb +422 -0
- data/lib/active_record/connection_adapters/redshift_7_2/type_metadata.rb +43 -0
- data/lib/active_record/connection_adapters/redshift_7_2/utils.rb +81 -0
- data/lib/active_record/connection_adapters/redshift_7_2_adapter.rb +844 -0
- data/lib/active_record/connection_adapters/redshift_8_0/array_parser.rb +92 -0
- data/lib/active_record/connection_adapters/redshift_8_0/column.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_8_0/database_statements.rb +181 -0
- data/lib/active_record/connection_adapters/redshift_8_0/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/redshift_8_0/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/redshift_8_0/oid/json.rb +41 -0
- data/lib/active_record/connection_adapters/redshift_8_0/oid/jsonb.rb +25 -0
- data/lib/active_record/connection_adapters/redshift_8_0/oid/type_map_initializer.rb +62 -0
- data/lib/active_record/connection_adapters/redshift_8_0/oid.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_8_0/quoting.rb +164 -0
- data/lib/active_record/connection_adapters/redshift_8_0/referential_integrity.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_8_0/schema_definitions.rb +70 -0
- data/lib/active_record/connection_adapters/redshift_8_0/schema_dumper.rb +17 -0
- data/lib/active_record/connection_adapters/redshift_8_0/schema_statements.rb +422 -0
- data/lib/active_record/connection_adapters/redshift_8_0/type_metadata.rb +43 -0
- data/lib/active_record/connection_adapters/redshift_8_0/utils.rb +81 -0
- data/lib/active_record/connection_adapters/redshift_8_0_adapter.rb +843 -0
- data/lib/active_record/connection_adapters/redshift_adapter.rb +13 -1286
- data/lib/active_record/tasks/redshift_7_0_tasks.rb +148 -0
- data/lib/active_record/tasks/redshift_7_1_tasks.rb +151 -0
- data/lib/active_record/tasks/redshift_7_2_tasks.rb +151 -0
- data/lib/active_record/tasks/redshift_8_0_tasks.rb +151 -0
- data/lib/active_record/tasks/redshift_tasks.rb +13 -0
- data/lib/activerecord-redshift-adapter.rb +13 -0
- metadata +110 -84
- data/.gitignore +0 -26
- data/Gemfile +0 -14
- data/Rakefile +0 -26
- data/activerecord-redshift-adapter.gemspec +0 -24
- data/lib/activerecord_redshift/table_manager.rb +0 -230
- data/lib/activerecord_redshift_adapter/version.rb +0 -4
- data/lib/activerecord_redshift_adapter.rb +0 -4
- data/lib/monkeypatch_activerecord.rb +0 -195
- data/lib/monkeypatch_arel.rb +0 -96
- data/spec/active_record/base_spec.rb +0 -37
- data/spec/active_record/connection_adapters/redshift_adapter_spec.rb +0 -97
- data/spec/dummy/config/database.example.yml +0 -12
- data/spec/spec_helper.rb +0 -33
@@ -0,0 +1,148 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Based on https://github.com/rails/rails/blob/v7.0.8.4/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
|
4
|
+
|
5
|
+
require "tempfile"
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
module Tasks # :nodoc:
|
9
|
+
class RedshiftDatabaseTasks # :nodoc:
|
10
|
+
DEFAULT_ENCODING = ENV["CHARSET"] || "utf8"
|
11
|
+
ON_ERROR_STOP_1 = "ON_ERROR_STOP=1"
|
12
|
+
SQL_COMMENT_BEGIN = "--"
|
13
|
+
|
14
|
+
delegate :connection, :establish_connection, :clear_active_connections!,
|
15
|
+
to: ActiveRecord::Base
|
16
|
+
|
17
|
+
def self.using_database_configurations?
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(db_config)
|
22
|
+
@db_config = db_config
|
23
|
+
@configuration_hash = db_config.configuration_hash
|
24
|
+
end
|
25
|
+
|
26
|
+
def create(master_established = false)
|
27
|
+
establish_master_connection unless master_established
|
28
|
+
connection.create_database(db_config.database, configuration_hash.merge(encoding: encoding))
|
29
|
+
establish_connection(db_config)
|
30
|
+
end
|
31
|
+
|
32
|
+
def drop
|
33
|
+
establish_master_connection
|
34
|
+
connection.drop_database(db_config.database)
|
35
|
+
end
|
36
|
+
|
37
|
+
def charset
|
38
|
+
connection.encoding
|
39
|
+
end
|
40
|
+
|
41
|
+
def collation
|
42
|
+
connection.collation
|
43
|
+
end
|
44
|
+
|
45
|
+
def purge
|
46
|
+
clear_active_connections!
|
47
|
+
drop
|
48
|
+
create true
|
49
|
+
end
|
50
|
+
|
51
|
+
def structure_dump(filename, extra_flags)
|
52
|
+
search_path = \
|
53
|
+
case ActiveRecord.dump_schemas
|
54
|
+
when :schema_search_path
|
55
|
+
configuration_hash[:schema_search_path]
|
56
|
+
when :all
|
57
|
+
nil
|
58
|
+
when String
|
59
|
+
ActiveRecord.dump_schemas
|
60
|
+
end
|
61
|
+
|
62
|
+
args = ["--schema-only", "--no-privileges", "--no-owner"]
|
63
|
+
args.concat(["--file", filename])
|
64
|
+
|
65
|
+
args.concat(Array(extra_flags)) if extra_flags
|
66
|
+
|
67
|
+
unless search_path.blank?
|
68
|
+
args += search_path.split(",").map do |part|
|
69
|
+
"--schema=#{part.strip}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
|
74
|
+
if ignore_tables.any?
|
75
|
+
args += ignore_tables.flat_map { |table| ["-T", table] }
|
76
|
+
end
|
77
|
+
|
78
|
+
args << db_config.database
|
79
|
+
run_cmd("pg_dump", args, "dumping")
|
80
|
+
remove_sql_header_comments(filename)
|
81
|
+
File.open(filename, "a") { |f| f << "SET search_path TO #{connection.schema_search_path};\n\n" }
|
82
|
+
end
|
83
|
+
|
84
|
+
def structure_load(filename, extra_flags)
|
85
|
+
args = ["--set", ON_ERROR_STOP_1, "--quiet", "--no-psqlrc", "--output", File::NULL, "--file", filename]
|
86
|
+
args.concat(Array(extra_flags)) if extra_flags
|
87
|
+
args << db_config.database
|
88
|
+
run_cmd("psql", args, "loading")
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
attr_reader :db_config, :configuration_hash
|
93
|
+
|
94
|
+
def encoding
|
95
|
+
configuration_hash[:encoding] || DEFAULT_ENCODING
|
96
|
+
end
|
97
|
+
|
98
|
+
def establish_master_connection
|
99
|
+
establish_connection configuration_hash.merge(
|
100
|
+
database: "redshift",
|
101
|
+
schema_search_path: "public"
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
def psql_env
|
106
|
+
{}.tap do |env|
|
107
|
+
env["PGHOST"] = db_config.host if db_config.host
|
108
|
+
env["PGPORT"] = configuration_hash[:port].to_s if configuration_hash[:port]
|
109
|
+
env["PGPASSWORD"] = configuration_hash[:password].to_s if configuration_hash[:password]
|
110
|
+
env["PGUSER"] = configuration_hash[:username].to_s if configuration_hash[:username]
|
111
|
+
env["PGSSLMODE"] = configuration_hash[:sslmode].to_s if configuration_hash[:sslmode]
|
112
|
+
env["PGSSLCERT"] = configuration_hash[:sslcert].to_s if configuration_hash[:sslcert]
|
113
|
+
env["PGSSLKEY"] = configuration_hash[:sslkey].to_s if configuration_hash[:sslkey]
|
114
|
+
env["PGSSLROOTCERT"] = configuration_hash[:sslrootcert].to_s if configuration_hash[:sslrootcert]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def run_cmd(cmd, args, action)
|
119
|
+
fail run_cmd_error(cmd, args, action) unless Kernel.system(psql_env, cmd, *args)
|
120
|
+
end
|
121
|
+
|
122
|
+
def run_cmd_error(cmd, args, action)
|
123
|
+
msg = +"failed to execute:\n"
|
124
|
+
msg << "#{cmd} #{args.join(' ')}\n\n"
|
125
|
+
msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
|
126
|
+
msg
|
127
|
+
end
|
128
|
+
|
129
|
+
def remove_sql_header_comments(filename)
|
130
|
+
removing_comments = true
|
131
|
+
tempfile = Tempfile.open("uncommented_structure.sql")
|
132
|
+
begin
|
133
|
+
File.foreach(filename) do |line|
|
134
|
+
unless removing_comments && (line.start_with?(SQL_COMMENT_BEGIN) || line.blank?)
|
135
|
+
tempfile << line
|
136
|
+
removing_comments = false
|
137
|
+
end
|
138
|
+
end
|
139
|
+
ensure
|
140
|
+
tempfile.close
|
141
|
+
end
|
142
|
+
FileUtils.cp(tempfile.path, filename)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
DatabaseTasks.register_task(/redshift/, RedshiftDatabaseTasks)
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Based on https://github.com/rails/rails/blob/v7.1.4/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
|
4
|
+
|
5
|
+
require "tempfile"
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
module Tasks # :nodoc:
|
9
|
+
class RedshiftDatabaseTasks # :nodoc:
|
10
|
+
DEFAULT_ENCODING = ENV["CHARSET"] || "utf8"
|
11
|
+
ON_ERROR_STOP_1 = "ON_ERROR_STOP=1"
|
12
|
+
SQL_COMMENT_BEGIN = "--"
|
13
|
+
|
14
|
+
def self.using_database_configurations?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(db_config)
|
19
|
+
@db_config = db_config
|
20
|
+
@configuration_hash = db_config.configuration_hash
|
21
|
+
end
|
22
|
+
|
23
|
+
def create(connection_already_established = false)
|
24
|
+
establish_connection(public_schema_config) unless connection_already_established
|
25
|
+
connection.create_database(db_config.database, configuration_hash.merge(encoding: encoding))
|
26
|
+
establish_connection
|
27
|
+
end
|
28
|
+
|
29
|
+
def drop
|
30
|
+
establish_connection(public_schema_config)
|
31
|
+
connection.drop_database(db_config.database)
|
32
|
+
end
|
33
|
+
|
34
|
+
def charset
|
35
|
+
connection.encoding
|
36
|
+
end
|
37
|
+
|
38
|
+
def collation
|
39
|
+
connection.collation
|
40
|
+
end
|
41
|
+
|
42
|
+
def purge
|
43
|
+
ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
|
44
|
+
drop
|
45
|
+
create true
|
46
|
+
end
|
47
|
+
|
48
|
+
def structure_dump(filename, extra_flags)
|
49
|
+
search_path = \
|
50
|
+
case ActiveRecord.dump_schemas
|
51
|
+
when :schema_search_path
|
52
|
+
configuration_hash[:schema_search_path]
|
53
|
+
when :all
|
54
|
+
nil
|
55
|
+
when String
|
56
|
+
ActiveRecord.dump_schemas
|
57
|
+
end
|
58
|
+
|
59
|
+
args = ["--schema-only", "--no-privileges", "--no-owner"]
|
60
|
+
args.concat(["--file", filename])
|
61
|
+
|
62
|
+
args.concat(Array(extra_flags)) if extra_flags
|
63
|
+
|
64
|
+
unless search_path.blank?
|
65
|
+
args += search_path.split(",").map do |part|
|
66
|
+
"--schema=#{part.strip}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
|
71
|
+
if ignore_tables.any?
|
72
|
+
ignore_tables = connection.data_sources.select { |table| ignore_tables.any? { |pattern| pattern === table } }
|
73
|
+
args += ignore_tables.flat_map { |table| ["-T", table] }
|
74
|
+
end
|
75
|
+
|
76
|
+
args << db_config.database
|
77
|
+
run_cmd("pg_dump", args, "dumping")
|
78
|
+
remove_sql_header_comments(filename)
|
79
|
+
File.open(filename, "a") { |f| f << "SET search_path TO #{connection.schema_search_path};\n\n" }
|
80
|
+
end
|
81
|
+
|
82
|
+
def structure_load(filename, extra_flags)
|
83
|
+
args = ["--set", ON_ERROR_STOP_1, "--quiet", "--no-psqlrc", "--output", File::NULL, "--file", filename]
|
84
|
+
args.concat(Array(extra_flags)) if extra_flags
|
85
|
+
args << db_config.database
|
86
|
+
run_cmd("psql", args, "loading")
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
attr_reader :db_config, :configuration_hash
|
91
|
+
|
92
|
+
def connection
|
93
|
+
ActiveRecord::Base.connection
|
94
|
+
end
|
95
|
+
|
96
|
+
def establish_connection(config = db_config)
|
97
|
+
ActiveRecord::Base.establish_connection(config)
|
98
|
+
end
|
99
|
+
|
100
|
+
def encoding
|
101
|
+
configuration_hash[:encoding] || DEFAULT_ENCODING
|
102
|
+
end
|
103
|
+
|
104
|
+
def public_schema_config
|
105
|
+
configuration_hash.merge(database: "redshift", schema_search_path: "public")
|
106
|
+
end
|
107
|
+
|
108
|
+
def psql_env
|
109
|
+
{}.tap do |env|
|
110
|
+
env["PGHOST"] = db_config.host if db_config.host
|
111
|
+
env["PGPORT"] = configuration_hash[:port].to_s if configuration_hash[:port]
|
112
|
+
env["PGPASSWORD"] = configuration_hash[:password].to_s if configuration_hash[:password]
|
113
|
+
env["PGUSER"] = configuration_hash[:username].to_s if configuration_hash[:username]
|
114
|
+
env["PGSSLMODE"] = configuration_hash[:sslmode].to_s if configuration_hash[:sslmode]
|
115
|
+
env["PGSSLCERT"] = configuration_hash[:sslcert].to_s if configuration_hash[:sslcert]
|
116
|
+
env["PGSSLKEY"] = configuration_hash[:sslkey].to_s if configuration_hash[:sslkey]
|
117
|
+
env["PGSSLROOTCERT"] = configuration_hash[:sslrootcert].to_s if configuration_hash[:sslrootcert]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def run_cmd(cmd, args, action)
|
122
|
+
fail run_cmd_error(cmd, args, action) unless Kernel.system(psql_env, cmd, *args)
|
123
|
+
end
|
124
|
+
|
125
|
+
def run_cmd_error(cmd, args, action)
|
126
|
+
msg = +"failed to execute:\n"
|
127
|
+
msg << "#{cmd} #{args.join(' ')}\n\n"
|
128
|
+
msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
|
129
|
+
msg
|
130
|
+
end
|
131
|
+
|
132
|
+
def remove_sql_header_comments(filename)
|
133
|
+
removing_comments = true
|
134
|
+
tempfile = Tempfile.open("uncommented_structure.sql")
|
135
|
+
begin
|
136
|
+
File.foreach(filename) do |line|
|
137
|
+
unless removing_comments && (line.start_with?(SQL_COMMENT_BEGIN) || line.blank?)
|
138
|
+
tempfile << line
|
139
|
+
removing_comments = false
|
140
|
+
end
|
141
|
+
end
|
142
|
+
ensure
|
143
|
+
tempfile.close
|
144
|
+
end
|
145
|
+
FileUtils.cp(tempfile.path, filename)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
DatabaseTasks.register_task(/redshift/, RedshiftDatabaseTasks)
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Based on https://github.com/rails/rails/blob/v7.2.1/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
|
4
|
+
|
5
|
+
require "tempfile"
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
module Tasks # :nodoc:
|
9
|
+
class RedshiftDatabaseTasks # :nodoc:
|
10
|
+
DEFAULT_ENCODING = ENV["CHARSET"] || "utf8"
|
11
|
+
ON_ERROR_STOP_1 = "ON_ERROR_STOP=1"
|
12
|
+
SQL_COMMENT_BEGIN = "--"
|
13
|
+
|
14
|
+
def self.using_database_configurations?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(db_config)
|
19
|
+
@db_config = db_config
|
20
|
+
@configuration_hash = db_config.configuration_hash
|
21
|
+
end
|
22
|
+
|
23
|
+
def create(connection_already_established = false)
|
24
|
+
establish_connection(public_schema_config) unless connection_already_established
|
25
|
+
connection.create_database(db_config.database, configuration_hash.merge(encoding: encoding))
|
26
|
+
establish_connection
|
27
|
+
end
|
28
|
+
|
29
|
+
def drop
|
30
|
+
establish_connection(public_schema_config)
|
31
|
+
connection.drop_database(db_config.database)
|
32
|
+
end
|
33
|
+
|
34
|
+
def charset
|
35
|
+
connection.encoding
|
36
|
+
end
|
37
|
+
|
38
|
+
def collation
|
39
|
+
connection.collation
|
40
|
+
end
|
41
|
+
|
42
|
+
def purge
|
43
|
+
ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
|
44
|
+
drop
|
45
|
+
create true
|
46
|
+
end
|
47
|
+
|
48
|
+
def structure_dump(filename, extra_flags)
|
49
|
+
search_path = \
|
50
|
+
case ActiveRecord.dump_schemas
|
51
|
+
when :schema_search_path
|
52
|
+
configuration_hash[:schema_search_path]
|
53
|
+
when :all
|
54
|
+
nil
|
55
|
+
when String
|
56
|
+
ActiveRecord.dump_schemas
|
57
|
+
end
|
58
|
+
|
59
|
+
args = ["--schema-only", "--no-privileges", "--no-owner"]
|
60
|
+
args.concat(["--file", filename])
|
61
|
+
|
62
|
+
args.concat(Array(extra_flags)) if extra_flags
|
63
|
+
|
64
|
+
unless search_path.blank?
|
65
|
+
args += search_path.split(",").map do |part|
|
66
|
+
"--schema=#{part.strip}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
|
71
|
+
if ignore_tables.any?
|
72
|
+
ignore_tables = connection.data_sources.select { |table| ignore_tables.any? { |pattern| pattern === table } }
|
73
|
+
args += ignore_tables.flat_map { |table| ["-T", table] }
|
74
|
+
end
|
75
|
+
|
76
|
+
args << db_config.database
|
77
|
+
run_cmd("pg_dump", args, "dumping")
|
78
|
+
remove_sql_header_comments(filename)
|
79
|
+
File.open(filename, "a") { |f| f << "SET search_path TO #{connection.schema_search_path};\n\n" }
|
80
|
+
end
|
81
|
+
|
82
|
+
def structure_load(filename, extra_flags)
|
83
|
+
args = ["--set", ON_ERROR_STOP_1, "--quiet", "--no-psqlrc", "--output", File::NULL, "--file", filename]
|
84
|
+
args.concat(Array(extra_flags)) if extra_flags
|
85
|
+
args << db_config.database
|
86
|
+
run_cmd("psql", args, "loading")
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
attr_reader :db_config, :configuration_hash
|
91
|
+
|
92
|
+
def connection
|
93
|
+
ActiveRecord::Base.lease_connection
|
94
|
+
end
|
95
|
+
|
96
|
+
def establish_connection(config = db_config)
|
97
|
+
ActiveRecord::Base.establish_connection(config)
|
98
|
+
end
|
99
|
+
|
100
|
+
def encoding
|
101
|
+
configuration_hash[:encoding] || DEFAULT_ENCODING
|
102
|
+
end
|
103
|
+
|
104
|
+
def public_schema_config
|
105
|
+
configuration_hash.merge(database: "redshift", schema_search_path: "public")
|
106
|
+
end
|
107
|
+
|
108
|
+
def psql_env
|
109
|
+
{}.tap do |env|
|
110
|
+
env["PGHOST"] = db_config.host if db_config.host
|
111
|
+
env["PGPORT"] = configuration_hash[:port].to_s if configuration_hash[:port]
|
112
|
+
env["PGPASSWORD"] = configuration_hash[:password].to_s if configuration_hash[:password]
|
113
|
+
env["PGUSER"] = configuration_hash[:username].to_s if configuration_hash[:username]
|
114
|
+
env["PGSSLMODE"] = configuration_hash[:sslmode].to_s if configuration_hash[:sslmode]
|
115
|
+
env["PGSSLCERT"] = configuration_hash[:sslcert].to_s if configuration_hash[:sslcert]
|
116
|
+
env["PGSSLKEY"] = configuration_hash[:sslkey].to_s if configuration_hash[:sslkey]
|
117
|
+
env["PGSSLROOTCERT"] = configuration_hash[:sslrootcert].to_s if configuration_hash[:sslrootcert]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def run_cmd(cmd, args, action)
|
122
|
+
fail run_cmd_error(cmd, args, action) unless Kernel.system(psql_env, cmd, *args)
|
123
|
+
end
|
124
|
+
|
125
|
+
def run_cmd_error(cmd, args, action)
|
126
|
+
msg = +"failed to execute:\n"
|
127
|
+
msg << "#{cmd} #{args.join(' ')}\n\n"
|
128
|
+
msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
|
129
|
+
msg
|
130
|
+
end
|
131
|
+
|
132
|
+
def remove_sql_header_comments(filename)
|
133
|
+
removing_comments = true
|
134
|
+
tempfile = Tempfile.open("uncommented_structure.sql")
|
135
|
+
begin
|
136
|
+
File.foreach(filename) do |line|
|
137
|
+
unless removing_comments && (line.start_with?(SQL_COMMENT_BEGIN) || line.blank?)
|
138
|
+
tempfile << line
|
139
|
+
removing_comments = false
|
140
|
+
end
|
141
|
+
end
|
142
|
+
ensure
|
143
|
+
tempfile.close
|
144
|
+
end
|
145
|
+
FileUtils.cp(tempfile.path, filename)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
DatabaseTasks.register_task(/redshift/, RedshiftDatabaseTasks)
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Based on https://github.com/rails/rails/blob/v7.2.1/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
|
4
|
+
|
5
|
+
require "tempfile"
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
module Tasks # :nodoc:
|
9
|
+
class RedshiftDatabaseTasks # :nodoc:
|
10
|
+
DEFAULT_ENCODING = ENV["CHARSET"] || "utf8"
|
11
|
+
ON_ERROR_STOP_1 = "ON_ERROR_STOP=1"
|
12
|
+
SQL_COMMENT_BEGIN = "--"
|
13
|
+
|
14
|
+
def self.using_database_configurations?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(db_config)
|
19
|
+
@db_config = db_config
|
20
|
+
@configuration_hash = db_config.configuration_hash
|
21
|
+
end
|
22
|
+
|
23
|
+
def create(connection_already_established = false)
|
24
|
+
establish_connection(public_schema_config) unless connection_already_established
|
25
|
+
connection.create_database(db_config.database, configuration_hash.merge(encoding: encoding))
|
26
|
+
establish_connection
|
27
|
+
end
|
28
|
+
|
29
|
+
def drop
|
30
|
+
establish_connection(public_schema_config)
|
31
|
+
connection.drop_database(db_config.database)
|
32
|
+
end
|
33
|
+
|
34
|
+
def charset
|
35
|
+
connection.encoding
|
36
|
+
end
|
37
|
+
|
38
|
+
def collation
|
39
|
+
connection.collation
|
40
|
+
end
|
41
|
+
|
42
|
+
def purge
|
43
|
+
ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
|
44
|
+
drop
|
45
|
+
create true
|
46
|
+
end
|
47
|
+
|
48
|
+
def structure_dump(filename, extra_flags)
|
49
|
+
search_path = \
|
50
|
+
case ActiveRecord.dump_schemas
|
51
|
+
when :schema_search_path
|
52
|
+
configuration_hash[:schema_search_path]
|
53
|
+
when :all
|
54
|
+
nil
|
55
|
+
when String
|
56
|
+
ActiveRecord.dump_schemas
|
57
|
+
end
|
58
|
+
|
59
|
+
args = ["--schema-only", "--no-privileges", "--no-owner"]
|
60
|
+
args.concat(["--file", filename])
|
61
|
+
|
62
|
+
args.concat(Array(extra_flags)) if extra_flags
|
63
|
+
|
64
|
+
unless search_path.blank?
|
65
|
+
args += search_path.split(",").map do |part|
|
66
|
+
"--schema=#{part.strip}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
|
71
|
+
if ignore_tables.any?
|
72
|
+
ignore_tables = connection.data_sources.select { |table| ignore_tables.any? { |pattern| pattern === table } }
|
73
|
+
args += ignore_tables.flat_map { |table| ["-T", table] }
|
74
|
+
end
|
75
|
+
|
76
|
+
args << db_config.database
|
77
|
+
run_cmd("pg_dump", args, "dumping")
|
78
|
+
remove_sql_header_comments(filename)
|
79
|
+
File.open(filename, "a") { |f| f << "SET search_path TO #{connection.schema_search_path};\n\n" }
|
80
|
+
end
|
81
|
+
|
82
|
+
def structure_load(filename, extra_flags)
|
83
|
+
args = ["--set", ON_ERROR_STOP_1, "--quiet", "--no-psqlrc", "--output", File::NULL, "--file", filename]
|
84
|
+
args.concat(Array(extra_flags)) if extra_flags
|
85
|
+
args << db_config.database
|
86
|
+
run_cmd("psql", args, "loading")
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
attr_reader :db_config, :configuration_hash
|
91
|
+
|
92
|
+
def connection
|
93
|
+
ActiveRecord::Base.lease_connection
|
94
|
+
end
|
95
|
+
|
96
|
+
def establish_connection(config = db_config)
|
97
|
+
ActiveRecord::Base.establish_connection(config)
|
98
|
+
end
|
99
|
+
|
100
|
+
def encoding
|
101
|
+
configuration_hash[:encoding] || DEFAULT_ENCODING
|
102
|
+
end
|
103
|
+
|
104
|
+
def public_schema_config
|
105
|
+
configuration_hash.merge(database: "redshift", schema_search_path: "public")
|
106
|
+
end
|
107
|
+
|
108
|
+
def psql_env
|
109
|
+
{}.tap do |env|
|
110
|
+
env["PGHOST"] = db_config.host if db_config.host
|
111
|
+
env["PGPORT"] = configuration_hash[:port].to_s if configuration_hash[:port]
|
112
|
+
env["PGPASSWORD"] = configuration_hash[:password].to_s if configuration_hash[:password]
|
113
|
+
env["PGUSER"] = configuration_hash[:username].to_s if configuration_hash[:username]
|
114
|
+
env["PGSSLMODE"] = configuration_hash[:sslmode].to_s if configuration_hash[:sslmode]
|
115
|
+
env["PGSSLCERT"] = configuration_hash[:sslcert].to_s if configuration_hash[:sslcert]
|
116
|
+
env["PGSSLKEY"] = configuration_hash[:sslkey].to_s if configuration_hash[:sslkey]
|
117
|
+
env["PGSSLROOTCERT"] = configuration_hash[:sslrootcert].to_s if configuration_hash[:sslrootcert]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def run_cmd(cmd, args, action)
|
122
|
+
fail run_cmd_error(cmd, args, action) unless Kernel.system(psql_env, cmd, *args)
|
123
|
+
end
|
124
|
+
|
125
|
+
def run_cmd_error(cmd, args, action)
|
126
|
+
msg = +"failed to execute:\n"
|
127
|
+
msg << "#{cmd} #{args.join(' ')}\n\n"
|
128
|
+
msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
|
129
|
+
msg
|
130
|
+
end
|
131
|
+
|
132
|
+
def remove_sql_header_comments(filename)
|
133
|
+
removing_comments = true
|
134
|
+
tempfile = Tempfile.open("uncommented_structure.sql")
|
135
|
+
begin
|
136
|
+
File.foreach(filename) do |line|
|
137
|
+
unless removing_comments && (line.start_with?(SQL_COMMENT_BEGIN) || line.blank?)
|
138
|
+
tempfile << line
|
139
|
+
removing_comments = false
|
140
|
+
end
|
141
|
+
end
|
142
|
+
ensure
|
143
|
+
tempfile.close
|
144
|
+
end
|
145
|
+
FileUtils.cp(tempfile.path, filename)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
DatabaseTasks.register_task(/redshift/, RedshiftDatabaseTasks)
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if ActiveRecord.version >= Gem::Version.new('8.0.0')
|
4
|
+
require_relative 'redshift_8_0_tasks'
|
5
|
+
elsif ActiveRecord.version >= Gem::Version.new('7.2.0')
|
6
|
+
require_relative 'redshift_7_2_tasks'
|
7
|
+
elsif ActiveRecord.version >= Gem::Version.new('7.1.0')
|
8
|
+
require_relative 'redshift_7_1_tasks'
|
9
|
+
elsif ActiveRecord.version >= Gem::Version.new('7.0.0')
|
10
|
+
require_relative 'redshift_7_0_tasks'
|
11
|
+
else
|
12
|
+
raise 'no compatible version of ActiveRecord detected'
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ActiverecordRedshiftAdapter
|
2
|
+
class Railtie < ::Rails::Railtie
|
3
|
+
initializer "activerecord-redshift-adapter.setup" do
|
4
|
+
ActiveSupport.on_load(:active_record) do
|
5
|
+
# The adapter registration API was introduced in Rails 7.2.0 in
|
6
|
+
# https://github.com/rails/rails/commit/009c7e74117690f0dbe200188a929b345c9306c1
|
7
|
+
next unless ActiveRecord.version >= Gem::Version.new('7.2.0')
|
8
|
+
|
9
|
+
ActiveRecord::ConnectionAdapters.register('redshift', 'ActiveRecord::ConnectionAdapters::RedshiftAdapter')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|