activerecord-redshift-adapter 0.9.12 → 8.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +5 -13
  2. data/LICENSE +25 -1
  3. data/README.md +28 -86
  4. data/lib/active_record/connection_adapters/redshift_7_0/array_parser.rb +92 -0
  5. data/lib/active_record/connection_adapters/redshift_7_0/column.rb +17 -0
  6. data/lib/active_record/connection_adapters/redshift_7_0/database_statements.rb +232 -0
  7. data/lib/active_record/connection_adapters/redshift_7_0/oid/date_time.rb +36 -0
  8. data/lib/active_record/connection_adapters/redshift_7_0/oid/decimal.rb +15 -0
  9. data/lib/active_record/connection_adapters/redshift_7_0/oid/json.rb +41 -0
  10. data/lib/active_record/connection_adapters/redshift_7_0/oid/jsonb.rb +25 -0
  11. data/lib/active_record/connection_adapters/redshift_7_0/oid/type_map_initializer.rb +62 -0
  12. data/lib/active_record/connection_adapters/redshift_7_0/oid.rb +17 -0
  13. data/lib/active_record/connection_adapters/redshift_7_0/quoting.rb +99 -0
  14. data/lib/active_record/connection_adapters/redshift_7_0/referential_integrity.rb +17 -0
  15. data/lib/active_record/connection_adapters/redshift_7_0/schema_definitions.rb +70 -0
  16. data/lib/active_record/connection_adapters/redshift_7_0/schema_dumper.rb +17 -0
  17. data/lib/active_record/connection_adapters/redshift_7_0/schema_statements.rb +421 -0
  18. data/lib/active_record/connection_adapters/redshift_7_0/type_metadata.rb +39 -0
  19. data/lib/active_record/connection_adapters/redshift_7_0/utils.rb +81 -0
  20. data/lib/active_record/connection_adapters/redshift_7_0_adapter.rb +768 -0
  21. data/lib/active_record/connection_adapters/redshift_7_1/array_parser.rb +92 -0
  22. data/lib/active_record/connection_adapters/redshift_7_1/column.rb +17 -0
  23. data/lib/active_record/connection_adapters/redshift_7_1/database_statements.rb +180 -0
  24. data/lib/active_record/connection_adapters/redshift_7_1/oid/date_time.rb +36 -0
  25. data/lib/active_record/connection_adapters/redshift_7_1/oid/decimal.rb +15 -0
  26. data/lib/active_record/connection_adapters/redshift_7_1/oid/json.rb +41 -0
  27. data/lib/active_record/connection_adapters/redshift_7_1/oid/jsonb.rb +25 -0
  28. data/lib/active_record/connection_adapters/redshift_7_1/oid/type_map_initializer.rb +62 -0
  29. data/lib/active_record/connection_adapters/redshift_7_1/oid.rb +17 -0
  30. data/lib/active_record/connection_adapters/redshift_7_1/quoting.rb +161 -0
  31. data/lib/active_record/connection_adapters/redshift_7_1/referential_integrity.rb +17 -0
  32. data/lib/active_record/connection_adapters/redshift_7_1/schema_definitions.rb +70 -0
  33. data/lib/active_record/connection_adapters/redshift_7_1/schema_dumper.rb +17 -0
  34. data/lib/active_record/connection_adapters/redshift_7_1/schema_statements.rb +422 -0
  35. data/lib/active_record/connection_adapters/redshift_7_1/type_metadata.rb +43 -0
  36. data/lib/active_record/connection_adapters/redshift_7_1/utils.rb +81 -0
  37. data/lib/active_record/connection_adapters/redshift_7_1_adapter.rb +847 -0
  38. data/lib/active_record/connection_adapters/redshift_7_2/array_parser.rb +92 -0
  39. data/lib/active_record/connection_adapters/redshift_7_2/column.rb +17 -0
  40. data/lib/active_record/connection_adapters/redshift_7_2/database_statements.rb +180 -0
  41. data/lib/active_record/connection_adapters/redshift_7_2/oid/date_time.rb +36 -0
  42. data/lib/active_record/connection_adapters/redshift_7_2/oid/decimal.rb +15 -0
  43. data/lib/active_record/connection_adapters/redshift_7_2/oid/json.rb +41 -0
  44. data/lib/active_record/connection_adapters/redshift_7_2/oid/jsonb.rb +25 -0
  45. data/lib/active_record/connection_adapters/redshift_7_2/oid/type_map_initializer.rb +62 -0
  46. data/lib/active_record/connection_adapters/redshift_7_2/oid.rb +17 -0
  47. data/lib/active_record/connection_adapters/redshift_7_2/quoting.rb +164 -0
  48. data/lib/active_record/connection_adapters/redshift_7_2/referential_integrity.rb +17 -0
  49. data/lib/active_record/connection_adapters/redshift_7_2/schema_definitions.rb +70 -0
  50. data/lib/active_record/connection_adapters/redshift_7_2/schema_dumper.rb +17 -0
  51. data/lib/active_record/connection_adapters/redshift_7_2/schema_statements.rb +422 -0
  52. data/lib/active_record/connection_adapters/redshift_7_2/type_metadata.rb +43 -0
  53. data/lib/active_record/connection_adapters/redshift_7_2/utils.rb +81 -0
  54. data/lib/active_record/connection_adapters/redshift_7_2_adapter.rb +847 -0
  55. data/lib/active_record/connection_adapters/redshift_8_0/array_parser.rb +92 -0
  56. data/lib/active_record/connection_adapters/redshift_8_0/column.rb +17 -0
  57. data/lib/active_record/connection_adapters/redshift_8_0/database_statements.rb +181 -0
  58. data/lib/active_record/connection_adapters/redshift_8_0/oid/date_time.rb +36 -0
  59. data/lib/active_record/connection_adapters/redshift_8_0/oid/decimal.rb +15 -0
  60. data/lib/active_record/connection_adapters/redshift_8_0/oid/json.rb +41 -0
  61. data/lib/active_record/connection_adapters/redshift_8_0/oid/jsonb.rb +25 -0
  62. data/lib/active_record/connection_adapters/redshift_8_0/oid/type_map_initializer.rb +62 -0
  63. data/lib/active_record/connection_adapters/redshift_8_0/oid.rb +17 -0
  64. data/lib/active_record/connection_adapters/redshift_8_0/quoting.rb +164 -0
  65. data/lib/active_record/connection_adapters/redshift_8_0/referential_integrity.rb +17 -0
  66. data/lib/active_record/connection_adapters/redshift_8_0/schema_definitions.rb +70 -0
  67. data/lib/active_record/connection_adapters/redshift_8_0/schema_dumper.rb +17 -0
  68. data/lib/active_record/connection_adapters/redshift_8_0/schema_statements.rb +422 -0
  69. data/lib/active_record/connection_adapters/redshift_8_0/type_metadata.rb +43 -0
  70. data/lib/active_record/connection_adapters/redshift_8_0/utils.rb +81 -0
  71. data/lib/active_record/connection_adapters/redshift_8_0_adapter.rb +846 -0
  72. data/lib/active_record/connection_adapters/redshift_adapter.rb +13 -1286
  73. data/lib/active_record/tasks/redshift_7_0_tasks.rb +148 -0
  74. data/lib/active_record/tasks/redshift_7_1_tasks.rb +151 -0
  75. data/lib/active_record/tasks/redshift_7_2_tasks.rb +151 -0
  76. data/lib/active_record/tasks/redshift_8_0_tasks.rb +151 -0
  77. data/lib/active_record/tasks/redshift_tasks.rb +13 -0
  78. data/lib/activerecord-redshift-adapter.rb +13 -0
  79. metadata +110 -84
  80. data/.gitignore +0 -26
  81. data/Gemfile +0 -14
  82. data/Rakefile +0 -26
  83. data/activerecord-redshift-adapter.gemspec +0 -24
  84. data/lib/activerecord_redshift/table_manager.rb +0 -230
  85. data/lib/activerecord_redshift_adapter/version.rb +0 -4
  86. data/lib/activerecord_redshift_adapter.rb +0 -4
  87. data/lib/monkeypatch_activerecord.rb +0 -195
  88. data/lib/monkeypatch_arel.rb +0 -96
  89. data/spec/active_record/base_spec.rb +0 -37
  90. data/spec/active_record/connection_adapters/redshift_adapter_spec.rb +0 -97
  91. data/spec/dummy/config/database.example.yml +0 -12
  92. 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