torque-postgresql 3.4.1 → 4.0.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.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/torque/function_generator.rb +13 -0
  3. data/lib/generators/torque/templates/function.sql.erb +4 -0
  4. data/lib/generators/torque/templates/type.sql.erb +2 -0
  5. data/lib/generators/torque/templates/view.sql.erb +3 -0
  6. data/lib/generators/torque/type_generator.rb +13 -0
  7. data/lib/generators/torque/view_generator.rb +16 -0
  8. data/lib/torque/postgresql/adapter/database_statements.rb +111 -94
  9. data/lib/torque/postgresql/adapter/oid/array.rb +17 -0
  10. data/lib/torque/postgresql/adapter/oid/line.rb +2 -6
  11. data/lib/torque/postgresql/adapter/oid/range.rb +4 -4
  12. data/lib/torque/postgresql/adapter/oid.rb +1 -23
  13. data/lib/torque/postgresql/adapter/quoting.rb +13 -7
  14. data/lib/torque/postgresql/adapter/schema_creation.rb +7 -28
  15. data/lib/torque/postgresql/adapter/schema_definitions.rb +58 -0
  16. data/lib/torque/postgresql/adapter/schema_dumper.rb +136 -34
  17. data/lib/torque/postgresql/adapter/schema_overrides.rb +45 -0
  18. data/lib/torque/postgresql/adapter/schema_statements.rb +109 -49
  19. data/lib/torque/postgresql/arel/infix_operation.rb +15 -28
  20. data/lib/torque/postgresql/arel/nodes.rb +16 -2
  21. data/lib/torque/postgresql/arel/operations.rb +7 -1
  22. data/lib/torque/postgresql/arel/visitors.rb +7 -9
  23. data/lib/torque/postgresql/associations/association_scope.rb +23 -31
  24. data/lib/torque/postgresql/associations/belongs_to_many_association.rb +25 -0
  25. data/lib/torque/postgresql/associations/builder/belongs_to_many.rb +16 -0
  26. data/lib/torque/postgresql/attributes/builder/enum.rb +12 -9
  27. data/lib/torque/postgresql/attributes/builder/full_text_search.rb +109 -0
  28. data/lib/torque/postgresql/attributes/builder/period.rb +21 -21
  29. data/lib/torque/postgresql/attributes/builder.rb +49 -11
  30. data/lib/torque/postgresql/attributes/enum.rb +7 -7
  31. data/lib/torque/postgresql/attributes/enum_set.rb +7 -7
  32. data/lib/torque/postgresql/attributes/full_text_search.rb +19 -0
  33. data/lib/torque/postgresql/attributes/period.rb +2 -2
  34. data/lib/torque/postgresql/attributes.rb +0 -4
  35. data/lib/torque/postgresql/auxiliary_statement/recursive.rb +3 -3
  36. data/lib/torque/postgresql/base.rb +5 -11
  37. data/lib/torque/postgresql/collector.rb +1 -1
  38. data/lib/torque/postgresql/config.rb +129 -5
  39. data/lib/torque/postgresql/function.rb +94 -0
  40. data/lib/torque/postgresql/inheritance.rb +52 -36
  41. data/lib/torque/postgresql/predicate_builder/arel_attribute_handler.rb +33 -0
  42. data/lib/torque/postgresql/predicate_builder/array_handler.rb +47 -0
  43. data/lib/torque/postgresql/predicate_builder/enumerator_lazy_handler.rb +37 -0
  44. data/lib/torque/postgresql/predicate_builder/regexp_handler.rb +21 -0
  45. data/lib/torque/postgresql/predicate_builder.rb +35 -0
  46. data/lib/torque/postgresql/railtie.rb +137 -30
  47. data/lib/torque/postgresql/reflection/abstract_reflection.rb +12 -44
  48. data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +4 -0
  49. data/lib/torque/postgresql/reflection/has_many_reflection.rb +4 -0
  50. data/lib/torque/postgresql/reflection/runtime_reflection.rb +1 -1
  51. data/lib/torque/postgresql/relation/auxiliary_statement.rb +7 -2
  52. data/lib/torque/postgresql/relation/buckets.rb +124 -0
  53. data/lib/torque/postgresql/relation/distinct_on.rb +7 -2
  54. data/lib/torque/postgresql/relation/inheritance.rb +22 -15
  55. data/lib/torque/postgresql/relation/join_series.rb +112 -0
  56. data/lib/torque/postgresql/relation/merger.rb +17 -3
  57. data/lib/torque/postgresql/relation.rb +24 -38
  58. data/lib/torque/postgresql/schema_cache.rb +6 -12
  59. data/lib/torque/postgresql/version.rb +1 -1
  60. data/lib/torque/postgresql/versioned_commands/command_migration.rb +146 -0
  61. data/lib/torque/postgresql/versioned_commands/generator.rb +57 -0
  62. data/lib/torque/postgresql/versioned_commands/migration_context.rb +83 -0
  63. data/lib/torque/postgresql/versioned_commands/migrator.rb +39 -0
  64. data/lib/torque/postgresql/versioned_commands/schema_table.rb +101 -0
  65. data/lib/torque/postgresql/versioned_commands.rb +161 -0
  66. data/lib/torque/postgresql.rb +2 -1
  67. data/spec/fixtures/migrations/20250101000001_create_users.rb +0 -0
  68. data/spec/fixtures/migrations/20250101000002_create_function_count_users_v1.sql +0 -0
  69. data/spec/fixtures/migrations/20250101000003_create_internal_users.rb +0 -0
  70. data/spec/fixtures/migrations/20250101000004_update_function_count_users_v2.sql +0 -0
  71. data/spec/fixtures/migrations/20250101000005_create_view_all_users_v1.sql +0 -0
  72. data/spec/fixtures/migrations/20250101000006_create_type_user_id_v1.sql +0 -0
  73. data/spec/fixtures/migrations/20250101000007_remove_function_count_users_v2.sql +0 -0
  74. data/spec/initialize.rb +67 -0
  75. data/spec/mocks/cache_query.rb +21 -21
  76. data/spec/mocks/create_table.rb +6 -26
  77. data/spec/schema.rb +17 -12
  78. data/spec/spec_helper.rb +11 -2
  79. data/spec/tests/arel_spec.rb +32 -7
  80. data/spec/tests/auxiliary_statement_spec.rb +3 -3
  81. data/spec/tests/belongs_to_many_spec.rb +72 -5
  82. data/spec/tests/enum_set_spec.rb +12 -11
  83. data/spec/tests/enum_spec.rb +4 -2
  84. data/spec/tests/full_text_seach_test.rb +280 -0
  85. data/spec/tests/function_spec.rb +42 -0
  86. data/spec/tests/has_many_spec.rb +21 -8
  87. data/spec/tests/interval_spec.rb +1 -7
  88. data/spec/tests/period_spec.rb +61 -61
  89. data/spec/tests/predicate_builder_spec.rb +132 -0
  90. data/spec/tests/relation_spec.rb +229 -0
  91. data/spec/tests/schema_spec.rb +6 -9
  92. data/spec/tests/table_inheritance_spec.rb +25 -26
  93. data/spec/tests/versioned_commands_spec.rb +513 -0
  94. metadata +64 -39
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c61aaf577483288d5c9a9af30704402529c86318cbdc312f567b643955c1443e
4
- data.tar.gz: ccb9930f10cd48eb08448ecd99275fd7ca7ef54443fab81476c6b3a57c03e394
3
+ metadata.gz: d80bb198f5645c35915440f05a4ad083830221de27983c4c7b936c394e7bcbbb
4
+ data.tar.gz: a73a8de3a63b806e08f42109a3bb703e7ce19a20ae55a317c7fb763215612396
5
5
  SHA512:
6
- metadata.gz: 34ed5cb4bb34b33de90020a7053378378b28ff0f45cc7a33e53084fa86c9069ab7694890fa0cac1ab19a85f5ee825cc317d3406cf8f98d4336ff416a2bae092b
7
- data.tar.gz: 22771a0e4b1ce1486ae0ae6700c81cbbf84b227bb78b1a7163be2d2948b7161e822bb32dfdcc399c103348e2ab89f8cdda04112f988d303fd0735e178a605043
6
+ metadata.gz: 42a366f328d284ed37c601e0007a32f65b6615f69185b44fb82e0b943ab77a9e5b810189e00f2f200cf9e9b22e373bf33798c5496c76cfbfcd236bcc8bb923fe
7
+ data.tar.gz: 45385a880da9922eeb72c4d7e2adb29c26081865747c080973d409e61866a027cccd14a4276c8e0f95daf34d4d59c74a0a1c7cd7aecbb611763734cd12d3e064
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'torque/postgresql/versioned_commands/generator'
4
+
5
+ module Torque
6
+ module Generators
7
+ class FunctionGenerator < Rails::Generators::Base
8
+ include Torque::PostgreSQL::VersionedCommands::Generator
9
+
10
+ alias create_function_file create_migration_file
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ CREATE OR REPLACE FUNCTION <%= name %>()
2
+ RETURNS void AS $$
3
+ -- Function body goes here
4
+ $$ LANGUAGE sql;
@@ -0,0 +1,2 @@
1
+ DROP TYPE IF EXISTS <%= name %>;
2
+ CREATE TYPE <%= name %>;
@@ -0,0 +1,3 @@
1
+ <%= "DROP MATERIALIZED VIEW IF EXISTS #{name};\n" if options[:materialized] %>CREATE <%= options[:materialized] ? 'MATERIALIZED' : 'OR REPLACE' %> VIEW <%= name %> AS (
2
+ -- View body goes here
3
+ );
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'torque/postgresql/versioned_commands/generator'
4
+
5
+ module Torque
6
+ module Generators
7
+ class TypeGenerator < Rails::Generators::Base
8
+ include Torque::PostgreSQL::VersionedCommands::Generator
9
+
10
+ alias create_type_file create_migration_file
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'torque/postgresql/versioned_commands/generator'
4
+
5
+ module Torque
6
+ module Generators
7
+ class ViewGenerator < Rails::Generators::Base
8
+ include Torque::PostgreSQL::VersionedCommands::Generator
9
+
10
+ class_option :materialized, type: :boolean, aliases: %i(--m), default: false,
11
+ desc: 'Use materialized view instead of regular view'
12
+
13
+ alias create_view_file create_migration_file
14
+ end
15
+ end
16
+ end
@@ -5,7 +5,7 @@ module Torque
5
5
  module Adapter
6
6
  module DatabaseStatements
7
7
 
8
- EXTENDED_DATABASE_TYPES = %i(enum enum_set interval)
8
+ EXTENDED_DATABASE_TYPES = %i[enum enum_set interval]
9
9
 
10
10
  # Switch between dump mode or not
11
11
  def dump_mode!
@@ -48,8 +48,8 @@ module Torque
48
48
  def schema_exists?(name, filtered: true)
49
49
  return user_defined_schemas.include?(name.to_s) if filtered
50
50
 
51
- query_value(<<-SQL) == 1
52
- SELECT 1 FROM pg_catalog.pg_namespace WHERE nspname = '#{name}'
51
+ query_value(<<-SQL, "SCHEMA") == 1
52
+ SELECT 1 FROM pg_catalog.pg_namespace WHERE nspname = #{quote(name)}
53
53
  SQL
54
54
  end
55
55
 
@@ -59,96 +59,75 @@ module Torque
59
59
  end
60
60
  alias data_type_exists? type_exists?
61
61
 
62
- # Configure the interval format
63
- def configure_connection
64
- super
65
- execute("SET SESSION IntervalStyle TO 'iso_8601'", 'SCHEMA')
66
- end
67
-
68
- # Since enums create new types, type map needs to be rebooted to include
69
- # the new ones, both normal and array one
70
- def create_enum(name, *)
71
- super
72
-
73
- oid = query_value("SELECT #{quote(name)}::regtype::oid", "SCHEMA").to_i
74
- load_additional_types([oid])
75
- end
76
-
77
62
  # Change some of the types being mapped
78
63
  def initialize_type_map(m = type_map)
79
64
  super
80
- m.register_type 'box', OID::Box.new
81
- m.register_type 'circle', OID::Circle.new
82
- m.register_type 'interval', OID::Interval.new
83
- m.register_type 'line', OID::Line.new
84
- m.register_type 'segment', OID::Segment.new
85
65
 
86
- m.alias_type 'regclass', 'varchar'
66
+ if PostgreSQL.config.geometry.enabled
67
+ m.register_type 'box', OID::Box.new
68
+ m.register_type 'circle', OID::Circle.new
69
+ m.register_type 'line', OID::Line.new
70
+ m.register_type 'segment', OID::Segment.new
71
+ end
72
+
73
+ if PostgreSQL.config.interval.enabled
74
+ m.register_type 'interval', OID::Interval.new
75
+ end
87
76
  end
88
77
 
89
78
  # :nodoc:
90
79
  def load_additional_types(oids = nil)
80
+ type_map.alias_type 'regclass', 'varchar'
81
+ type_map.alias_type 'regconfig', 'varchar'
91
82
  super
92
83
  torque_load_additional_types(oids)
93
84
  end
94
85
 
95
86
  # Add the composite types to be loaded too.
96
87
  def torque_load_additional_types(oids = nil)
97
- filter = ("AND a.typelem::integer IN (%s)" % oids.join(', ')) if oids
98
-
99
- query = <<-SQL
100
- SELECT a.typelem AS oid, t.typname, t.typelem,
101
- t.typdelim, t.typbasetype, t.typtype,
102
- t.typarray
103
- FROM pg_type t
104
- INNER JOIN pg_type a ON (a.oid = t.typarray)
105
- LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
106
- WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
107
- AND t.typtype IN ( 'e' )
108
- #{filter}
109
- AND NOT EXISTS(
110
- SELECT 1 FROM pg_catalog.pg_type el
111
- WHERE el.oid = t.typelem AND el.typarray = t.oid
112
- )
113
- AND (t.typrelid = 0 OR (
114
- SELECT c.relkind = 'c' FROM pg_catalog.pg_class c
115
- WHERE c.oid = t.typrelid
116
- ))
88
+ return unless torque_load_additional_types?
89
+
90
+ # Types: (b)ase, (c)omposite, (d)omain, (e)num, (p)seudotype, (r)ange
91
+ # (m)ultirange
92
+
93
+ query = <<~SQL
94
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput,
95
+ r.rngsubtype, t.typtype, t.typbasetype, t.typarray
96
+ FROM pg_type as t
97
+ LEFT JOIN pg_range as r ON oid = rngtypid
98
+ LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
99
+ WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
117
100
  SQL
118
101
 
119
- execute_and_clear(query, 'SCHEMA', []) do |records|
120
- records.each { |row| OID::Enum.create(row, type_map) }
102
+ if oids
103
+ query += " AND t.oid IN (%s)" % oids.join(", ")
104
+ else
105
+ query += " AND t.typtype IN ('e')"
106
+ end
107
+
108
+ options = { allow_retry: true, materialize_transactions: false }
109
+ internal_execute(query, 'SCHEMA', **options).each do |row|
110
+ if row['typtype'] == 'e' && PostgreSQL.config.enum.enabled
111
+ OID::Enum.create(row, type_map)
112
+ end
121
113
  end
122
114
  end
123
115
 
116
+ def torque_load_additional_types?
117
+ PostgreSQL.config.enum.enabled
118
+ end
119
+
124
120
  # Gets a list of user defined types.
125
121
  # You can even choose the +category+ filter
126
122
  def user_defined_types(*categories)
127
- category_condition = categories.present? \
128
- ? "AND t.typtype IN ('#{categories.join("', '")}')" \
129
- : "AND t.typtype NOT IN ('b', 'd')"
130
-
131
- select_all(<<-SQL, 'SCHEMA').rows.to_h
132
- SELECT t.typname AS name,
133
- CASE t.typtype
134
- WHEN 'e' THEN 'enum'
135
- END AS type
136
- FROM pg_type t
137
- LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
123
+ categories = categories.compact.presence || %w[c e p r m]
124
+
125
+ query(<<-SQL, 'SCHEMA').to_h
126
+ SELECT t.typname, t.typtype
127
+ FROM pg_type as t
128
+ LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
138
129
  WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
139
- #{category_condition}
140
- AND NOT EXISTS(
141
- SELECT 1
142
- FROM pg_catalog.pg_type el
143
- WHERE el.oid = t.typelem
144
- AND el.typarray = t.oid
145
- )
146
- AND (t.typrelid = 0 OR (
147
- SELECT c.relkind = 'c'
148
- FROM pg_catalog.pg_class c
149
- WHERE c.oid = t.typrelid
150
- ))
151
- ORDER BY t.typtype DESC
130
+ AND t.typtype IN ('#{categories.join("', '")}')
152
131
  SQL
153
132
  end
154
133
 
@@ -175,19 +154,10 @@ module Torque
175
154
 
176
155
  # Build the query for allowed schemas
177
156
  def user_defined_schemas_sql
178
- conditions = []
179
- conditions << <<-SQL.squish if schemas_blacklist.any?
180
- nspname NOT LIKE ALL (ARRAY['#{schemas_blacklist.join("', '")}'])
181
- SQL
182
-
183
- conditions << <<-SQL.squish if schemas_whitelist.any?
184
- nspname LIKE ANY (ARRAY['#{schemas_whitelist.join("', '")}'])
185
- SQL
186
-
187
157
  <<-SQL.squish
188
158
  SELECT nspname
189
159
  FROM pg_catalog.pg_namespace
190
- WHERE 1=1 AND #{conditions.join(' AND ')}
160
+ WHERE 1=1 AND #{filter_by_schema.join(' AND ')}
191
161
  ORDER BY oid
192
162
  SQL
193
163
  end
@@ -195,21 +165,68 @@ module Torque
195
165
  # Get the list of columns, and their definition, but only from the
196
166
  # actual table, does not include columns that comes from inherited table
197
167
  def column_definitions(table_name)
198
- local = 'AND a.attislocal' if @_dump_mode
199
-
200
- query(<<-SQL, 'SCHEMA')
201
- SELECT a.attname, format_type(a.atttypid, a.atttypmod),
202
- pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
203
- c.collname, col_description(a.attrelid, a.attnum) AS comment,
204
- #{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
205
- FROM pg_attribute a
206
- LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
207
- LEFT JOIN pg_type t ON a.atttypid = t.oid
208
- LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
209
- WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
210
- AND a.attnum > 0 AND NOT a.attisdropped #{local}
211
- ORDER BY a.attnum
168
+ query(<<~SQL, "SCHEMA")
169
+ SELECT a.attname, format_type(a.atttypid, a.atttypmod),
170
+ pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
171
+ c.collname, col_description(a.attrelid, a.attnum) AS comment,
172
+ #{supports_identity_columns? ? 'attidentity' : quote('')} AS identity,
173
+ #{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
174
+ FROM pg_attribute a
175
+ LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
176
+ LEFT JOIN pg_type t ON a.atttypid = t.oid
177
+ LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
178
+ WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
179
+ AND a.attnum > 0 AND NOT a.attisdropped
180
+ #{'AND a.attislocal' if @_dump_mode}
181
+ ORDER BY a.attnum
182
+ SQL
183
+ end
184
+
185
+ # Get all possible schema entries that can be created via versioned
186
+ # commands of the provided type. Mostly for covering removals and not
187
+ # dump them
188
+ def list_versioned_commands(type)
189
+ query =
190
+ case type
191
+ when :function
192
+ <<-SQL.squish
193
+ SELECT n.nspname AS schema, p.proname AS name
194
+ FROM pg_catalog.pg_proc p
195
+ INNER JOIN pg_namespace n ON n.oid = p.pronamespace
196
+ WHERE 1=1 AND #{filter_by_schema.join(' AND ')};
197
+ SQL
198
+ when :type
199
+ <<-SQL.squish
200
+ SELECT n.nspname AS schema, t.typname AS name
201
+ FROM pg_type t
202
+ INNER JOIN pg_namespace n ON n.oid = t.typnamespace
203
+ WHERE 1=1 AND t.typtype NOT IN ('e')
204
+ AND #{filter_by_schema.join(' AND ')};
205
+ SQL
206
+ when :view
207
+ <<-SQL.squish
208
+ SELECT n.nspname AS schema, c.relname AS name
209
+ FROM pg_class c
210
+ INNER JOIN pg_namespace n ON n.oid = c.relnamespace
211
+ WHERE 1=1 AND c.relkind IN ('v', 'm')
212
+ AND #{filter_by_schema.join(' AND ')};
213
+ SQL
214
+ end
215
+
216
+ select_rows(query, 'SCHEMA')
217
+ end
218
+
219
+ # Build the condition for filtering by schema
220
+ def filter_by_schema
221
+ conditions = []
222
+ conditions << <<-SQL.squish if schemas_blacklist.any?
223
+ nspname NOT LIKE ALL (ARRAY['#{schemas_blacklist.join("', '")}'])
224
+ SQL
225
+
226
+ conditions << <<-SQL.squish if schemas_whitelist.any?
227
+ nspname LIKE ANY (ARRAY['#{schemas_whitelist.join("', '")}'])
212
228
  SQL
229
+ conditions
213
230
  end
214
231
 
215
232
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Torque
4
+ module PostgreSQL
5
+ module Adapter
6
+ module OID
7
+ module Array
8
+ def force_equality?(value)
9
+ PostgreSQL.config.predicate_builder.handle_array_attributes ? false : super
10
+ end
11
+ end
12
+
13
+ ::ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array.prepend(Array)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -7,9 +7,7 @@ module Torque
7
7
  alias c= intercept=
8
8
 
9
9
  def a=(value)
10
- self.slope = vertical? \
11
- ? Float::INFINITY \
12
- : Rational(value, b)
10
+ self.slope = vertical? ? Float::INFINITY : Rational(value, b)
13
11
  end
14
12
 
15
13
  def a
@@ -17,9 +15,7 @@ module Torque
17
15
  end
18
16
 
19
17
  def b=(value)
20
- self.slope = value.zero? \
21
- ? Float::INFINITY \
22
- : Rational(a, value)
18
+ self.slope = value.zero? ? Float::INFINITY : Rational(a, value)
23
19
  end
24
20
 
25
21
  def b
@@ -7,7 +7,7 @@ module Torque
7
7
  class Range < ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Range
8
8
  HASH_PICK = %i[from start end to].freeze
9
9
 
10
- module Comparasion
10
+ module Comparison
11
11
  def <=>(other)
12
12
  return super unless other.acts_like?(:date) || other.acts_like?(:time)
13
13
  other = other.to_time if other.acts_like?(:date)
@@ -17,9 +17,9 @@ module Torque
17
17
 
18
18
  def cast_value(value)
19
19
  case value
20
- when Array
20
+ when ::Array
21
21
  cast_custom(value[0], value[1])
22
- when Hash
22
+ when ::Hash
23
23
  pieces = value.with_indifferent_access.values_at(*HASH_PICK)
24
24
  cast_custom(pieces[0] || pieces[1], pieces[2] || pieces[3])
25
25
  else
@@ -54,7 +54,7 @@ module Torque
54
54
  ::ActiveRecord::ConnectionAdapters::PostgreSQL::OID.send(:remove_const, :Range)
55
55
  ::ActiveRecord::ConnectionAdapters::PostgreSQL::OID.const_set(:Range, Range)
56
56
 
57
- ::Float.prepend(Range::Comparasion)
57
+ ::Float.prepend(Range::Comparison)
58
58
  end
59
59
  end
60
60
  end
@@ -1,24 +1,2 @@
1
- require_relative 'oid/box'
2
- require_relative 'oid/circle'
3
- require_relative 'oid/enum'
4
- require_relative 'oid/enum_set'
5
- require_relative 'oid/interval'
6
- require_relative 'oid/line'
1
+ require_relative 'oid/array'
7
2
  require_relative 'oid/range'
8
- require_relative 'oid/segment'
9
-
10
- module Torque
11
- module PostgreSQL
12
- module Adapter
13
- module OID
14
- end
15
-
16
- ActiveRecord::Type.register(:box, OID::Box, adapter: :postgresql)
17
- ActiveRecord::Type.register(:circle, OID::Circle, adapter: :postgresql)
18
- ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
19
- ActiveRecord::Type.register(:enum_set, OID::EnumSet, adapter: :postgresql)
20
- ActiveRecord::Type.register(:line, OID::Line, adapter: :postgresql)
21
- ActiveRecord::Type.register(:segment, OID::Segment, adapter: :postgresql)
22
- end
23
- end
24
- end
@@ -4,21 +4,27 @@ module Torque
4
4
  module PostgreSQL
5
5
  module Adapter
6
6
  module Quoting
7
+ QUOTED_TYPE_NAMES = Concurrent::Map.new
7
8
 
8
9
  Name = ActiveRecord::ConnectionAdapters::PostgreSQL::Name
9
10
  Column = ActiveRecord::ConnectionAdapters::PostgreSQL::Column
10
11
  ColumnDefinition = ActiveRecord::ConnectionAdapters::ColumnDefinition
12
+ Utils = ActiveRecord::ConnectionAdapters::PostgreSQL::Utils
11
13
 
12
14
  # Quotes type names for use in SQL queries.
13
- def quote_type_name(string, schema = nil)
14
- name_schema, table = string.to_s.scan(/[^".\s]+|"[^"]*"/)
15
- if table.nil?
16
- table = name_schema
17
- name_schema = nil
15
+ def quote_type_name(name, *args)
16
+ QUOTED_TYPE_NAMES[args] ||= begin
17
+ name = name.to_s
18
+ args << 'public' if args.empty? && !name.include?('.')
19
+ quote_identifier_name(name, *args)
18
20
  end
21
+ end
19
22
 
20
- schema = schema || name_schema || 'public'
21
- Name.new(schema, table).quoted
23
+ # Make sure to support all sorts of different compositions of names
24
+ def quote_identifier_name(name, schema = nil)
25
+ name = Utils.extract_schema_qualified_name(name.to_s) unless name.is_a?(Name)
26
+ name.instance_variable_set(:@schema, Utils.unquote_identifier(schema.to_s)) if schema
27
+ name.quoted.freeze
22
28
  end
23
29
 
24
30
  def quote_default_expression(value, column)
@@ -3,39 +3,18 @@ module Torque
3
3
  module Adapter
4
4
  module SchemaCreation
5
5
 
6
- # Redefine original table creation command to ensure PostgreSQL standard
7
- def visit_TableDefinition(o)
8
- create_sql = +"CREATE#{table_modifier_in_create(o)} TABLE "
9
- create_sql << "IF NOT EXISTS " if o.if_not_exists
10
- create_sql << "#{quote_table_name(o.name)} "
11
-
12
- statements = o.columns.map { |c| accept c }
13
- statements << accept(o.primary_keys) if o.primary_keys
14
-
15
- if supports_indexes_in_create?
16
- statements.concat(o.indexes.map { |c, o| index_in_create(o.name, c, o) })
17
- end
18
-
19
- if @conn.supports_foreign_keys?
20
- statements.concat(o.foreign_keys.map { |fk| accept fk })
21
- end
22
-
23
- if respond_to?(:supports_check_constraints?) && supports_check_constraints?
24
- statements.concat(o.check_constraints.map { |chk| accept chk })
25
- end
26
-
27
- create_sql << "(#{statements.join(', ')})" \
28
- if statements.present? || o.inherits.present?
29
-
30
- add_table_options!(create_sql, o)
31
-
6
+ # Inherits are now setup via table options, but keep the implementation
7
+ # supported by this gem
8
+ def add_table_options!(create_sql, o)
32
9
  if o.inherits.present?
10
+ # Make sure we always have parenthesis
11
+ create_sql << '()' unless create_sql[-1] == ')'
12
+
33
13
  tables = o.inherits.map(&method(:quote_table_name))
34
14
  create_sql << " INHERITS ( #{tables.join(' , ')} )"
35
15
  end
36
16
 
37
- create_sql << " AS #{to_sql(o.as)}" if o.as
38
- create_sql
17
+ super(create_sql, o)
39
18
  end
40
19
  end
41
20
 
@@ -3,7 +3,26 @@
3
3
  module Torque
4
4
  module PostgreSQL
5
5
  module Adapter
6
+ module ColumnMethods
7
+
8
+ # Adds a search language column to the table. See +add_search_language+
9
+ def search_language(*names, **options)
10
+ raise ArgumentError, "Missing column name(s) for search_language" if names.empty?
11
+ names.each { |name| column(name, :regconfig, **options) }
12
+ end
13
+
14
+ # Add a search vector column to the table. See +add_search_vector+
15
+ def search_vector(*names, columns:, **options)
16
+ raise ArgumentError, "Missing column name(s) for search_vector" if names.empty?
17
+ options = Attributes::Builder.search_vector_options(columns: columns, **options)
18
+ names.each { |name| column(name, :virtual, **options) }
19
+ end
20
+
21
+ end
22
+
6
23
  module TableDefinition
24
+ include ColumnMethods
25
+
7
26
  attr_reader :inherits
8
27
 
9
28
  def initialize(*args, **options)
@@ -12,8 +31,47 @@ module Torque
12
31
  @inherits = Array.wrap(options.delete(:inherits)).flatten.compact \
13
32
  if options.key?(:inherits)
14
33
  end
34
+
35
+ def set_primary_key(tn, id, primary_key, *, **)
36
+ super unless @inherits.present? && primary_key.blank? && id == :primary_key
37
+ end
38
+
39
+ private
40
+
41
+ def create_column_definition(name, type, options)
42
+ if type == :enum_set
43
+ type = :enum
44
+ options ||= {}
45
+ options[:array] = true
46
+ end
47
+
48
+ super(name, type, options)
49
+ end
50
+ end
51
+
52
+ # Add exclusive support for versioned commands when importing from schema
53
+ # dump. This ensures that such methods are not available in regular
54
+ # migrations.
55
+ module Definition
56
+
57
+ def create_function(name, version:, dir: pool.migrations_paths)
58
+ return super unless VersionedCommands.valid_type?(:function)
59
+ execute VersionedCommands.fetch_command(dir, :function, name, version)
60
+ end
61
+
62
+ def create_type(name, version:, dir: pool.migrations_paths)
63
+ return super unless VersionedCommands.valid_type?(:type)
64
+ execute VersionedCommands.fetch_command(dir, :type, name, version)
65
+ end
66
+
67
+ def create_view(name, version:, dir: pool.migrations_paths)
68
+ return super unless VersionedCommands.valid_type?(:view)
69
+ execute VersionedCommands.fetch_command(dir, :view, name, version)
70
+ end
71
+
15
72
  end
16
73
 
74
+ ActiveRecord::ConnectionAdapters::PostgreSQL::Table.include ColumnMethods
17
75
  ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition.include TableDefinition
18
76
  end
19
77
  end