activerecord-redshift-adapter 0.9.10 → 8.0.0.beta1

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.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  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 -1282
  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 +112 -98
  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