torque-postgresql 4.0.1 → 4.1.1

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 (218) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +52 -0
  3. data/lib/generators/torque/skill_generator.rb +17 -0
  4. data/lib/generators/torque/templates/trigger.sql.erb +3 -0
  5. data/lib/generators/torque/trigger_generator.rb +13 -0
  6. data/lib/torque/postgresql/adapter/database_statements.rb +98 -7
  7. data/lib/torque/postgresql/adapter/inheritance_statements.rb +297 -0
  8. data/lib/torque/postgresql/adapter/oid/array.rb +13 -0
  9. data/lib/torque/postgresql/adapter/oid/box.rb +1 -1
  10. data/lib/torque/postgresql/adapter/oid/circle.rb +1 -1
  11. data/lib/torque/postgresql/adapter/oid/composite.rb +117 -0
  12. data/lib/torque/postgresql/adapter/oid/enum.rb +1 -1
  13. data/lib/torque/postgresql/adapter/oid/enum_set.rb +2 -2
  14. data/lib/torque/postgresql/adapter/oid/interval.rb +1 -1
  15. data/lib/torque/postgresql/adapter/oid/line.rb +1 -1
  16. data/lib/torque/postgresql/adapter/oid/lquery.rb +70 -0
  17. data/lib/torque/postgresql/adapter/oid/ltree.rb +111 -0
  18. data/lib/torque/postgresql/adapter/oid/segment.rb +1 -1
  19. data/lib/torque/postgresql/adapter/oid/struct.rb +150 -0
  20. data/lib/torque/postgresql/adapter/oid/struct_list.rb +52 -0
  21. data/lib/torque/postgresql/adapter/oid/struct_set.rb +38 -0
  22. data/lib/torque/postgresql/adapter/oid.rb +13 -0
  23. data/lib/torque/postgresql/adapter/quoting.rb +11 -2
  24. data/lib/torque/postgresql/adapter/schema_definitions.rb +51 -0
  25. data/lib/torque/postgresql/adapter/schema_dumper.rb +27 -0
  26. data/lib/torque/postgresql/adapter/schema_statements.rb +207 -0
  27. data/lib/torque/postgresql/adapter.rb +2 -5
  28. data/lib/torque/postgresql/arel/nodes.rb +114 -1
  29. data/lib/torque/postgresql/arel/visitors.rb +65 -8
  30. data/lib/torque/postgresql/associations/join_dependency.rb +55 -0
  31. data/lib/torque/postgresql/associations.rb +3 -0
  32. data/lib/torque/postgresql/attributes/base.rb +97 -0
  33. data/lib/torque/postgresql/attributes/composite.rb +98 -0
  34. data/lib/torque/postgresql/attributes/enum.rb +13 -7
  35. data/lib/torque/postgresql/attributes/lquery.rb +120 -0
  36. data/lib/torque/postgresql/attributes/ltree.rb +121 -0
  37. data/lib/torque/postgresql/attributes/simple_enum.rb +72 -0
  38. data/lib/torque/postgresql/attributes/struct.rb +137 -0
  39. data/lib/torque/postgresql/base.rb +26 -15
  40. data/lib/torque/postgresql/config.rb +100 -15
  41. data/lib/torque/postgresql/geometry_builder.rb +4 -10
  42. data/lib/torque/postgresql/inheritance/expander.rb +66 -0
  43. data/lib/torque/postgresql/inheritance/record.rb +52 -0
  44. data/lib/torque/postgresql/inheritance.rb +141 -68
  45. data/lib/torque/postgresql/migration/command_recorder.rb +76 -0
  46. data/lib/torque/postgresql/predicate_builder/arel_attribute_handler.rb +1 -1
  47. data/lib/torque/postgresql/predicate_builder/array_handler.rb +16 -8
  48. data/lib/torque/postgresql/predicate_builder/composite_handler.rb +122 -0
  49. data/lib/torque/postgresql/predicate_builder/ltree_handler.rb +112 -0
  50. data/lib/torque/postgresql/predicate_builder/struct_handler.rb +88 -0
  51. data/lib/torque/postgresql/predicate_builder.rb +53 -0
  52. data/lib/torque/postgresql/predicate_table.rb +61 -0
  53. data/lib/torque/postgresql/railtie.rb +52 -0
  54. data/lib/torque/postgresql/relation/auxiliary_statement.rb +7 -3
  55. data/lib/torque/postgresql/relation/buckets.rb +2 -1
  56. data/lib/torque/postgresql/relation/cross_join.rb +193 -0
  57. data/lib/torque/postgresql/relation/inheritance.rb +145 -28
  58. data/lib/torque/postgresql/relation/join_series.rb +1 -0
  59. data/lib/torque/postgresql/relation/merger.rb +30 -5
  60. data/lib/torque/postgresql/relation/segments.rb +140 -0
  61. data/lib/torque/postgresql/relation.rb +32 -12
  62. data/lib/torque/postgresql/schema_cache.rb +1 -0
  63. data/lib/torque/postgresql/validations.rb +29 -0
  64. data/lib/torque/postgresql/version.rb +1 -1
  65. data/lib/torque/postgresql/versioned_commands/command_migration.rb +24 -1
  66. data/lib/torque/postgresql/versioned_commands.rb +39 -0
  67. data/lib/torque/postgresql.rb +4 -0
  68. data/sig/generators/torque/function_generator.rbs +9 -0
  69. data/sig/generators/torque/skill_generator.rbs +7 -0
  70. data/sig/generators/torque/trigger_generator.rbs +9 -0
  71. data/sig/generators/torque/type_generator.rbs +9 -0
  72. data/sig/generators/torque/view_generator.rbs +9 -0
  73. data/sig/manifest.yaml +7 -0
  74. data/sig/torque/postgresql/adapter/database_statements.rbs +42 -0
  75. data/sig/torque/postgresql/adapter/inheritance_statements.rbs +36 -0
  76. data/sig/torque/postgresql/adapter/oid/array.rbs +20 -0
  77. data/sig/torque/postgresql/adapter/oid/box.rbs +28 -0
  78. data/sig/torque/postgresql/adapter/oid/circle.rbs +33 -0
  79. data/sig/torque/postgresql/adapter/oid/composite.rbs +35 -0
  80. data/sig/torque/postgresql/adapter/oid/enum.rbs +28 -0
  81. data/sig/torque/postgresql/adapter/oid/enum_set.rbs +19 -0
  82. data/sig/torque/postgresql/adapter/oid/interval.rbs +20 -0
  83. data/sig/torque/postgresql/adapter/oid/line.rbs +33 -0
  84. data/sig/torque/postgresql/adapter/oid/lquery.rbs +21 -0
  85. data/sig/torque/postgresql/adapter/oid/ltree.rbs +32 -0
  86. data/sig/torque/postgresql/adapter/oid/range.rbs +28 -0
  87. data/sig/torque/postgresql/adapter/oid/segment.rbs +38 -0
  88. data/sig/torque/postgresql/adapter/oid/struct.rbs +41 -0
  89. data/sig/torque/postgresql/adapter/oid/struct_list.rbs +20 -0
  90. data/sig/torque/postgresql/adapter/oid/struct_set.rbs +18 -0
  91. data/sig/torque/postgresql/adapter/oid.rbs +9 -0
  92. data/sig/torque/postgresql/adapter/quoting.rbs +20 -0
  93. data/sig/torque/postgresql/adapter/schema_creation.rbs +13 -0
  94. data/sig/torque/postgresql/adapter/schema_definitions.rbs +55 -0
  95. data/sig/torque/postgresql/adapter/schema_dumper.rbs +37 -0
  96. data/sig/torque/postgresql/adapter/schema_overrides.rbs +46 -0
  97. data/sig/torque/postgresql/adapter/schema_statements.rbs +50 -0
  98. data/sig/torque/postgresql/adapter.rbs +24 -0
  99. data/sig/torque/postgresql/arel/infix_operation.rbs +79 -0
  100. data/sig/torque/postgresql/arel/join_source.rbs +19 -0
  101. data/sig/torque/postgresql/arel/nodes.rbs +83 -0
  102. data/sig/torque/postgresql/arel/operations.rbs +28 -0
  103. data/sig/torque/postgresql/arel/select_manager.rbs +15 -0
  104. data/sig/torque/postgresql/arel/visitors.rbs +30 -0
  105. data/sig/torque/postgresql/arel.rbs +6 -0
  106. data/sig/torque/postgresql/associations/association_scope.rbs +28 -0
  107. data/sig/torque/postgresql/associations/belongs_to_many_association.rbs +51 -0
  108. data/sig/torque/postgresql/associations/builder/belongs_to_many.rbs +24 -0
  109. data/sig/torque/postgresql/associations/builder/has_many.rbs +15 -0
  110. data/sig/torque/postgresql/associations/builder.rbs +8 -0
  111. data/sig/torque/postgresql/associations/foreign_association.rbs +16 -0
  112. data/sig/torque/postgresql/associations/join_dependency.rbs +16 -0
  113. data/sig/torque/postgresql/associations/preloader/association.rbs +32 -0
  114. data/sig/torque/postgresql/associations/preloader/loader_query.rbs +20 -0
  115. data/sig/torque/postgresql/associations/preloader.rbs +8 -0
  116. data/sig/torque/postgresql/associations.rbs +17 -0
  117. data/sig/torque/postgresql/attributes/base.rbs +37 -0
  118. data/sig/torque/postgresql/attributes/builder/enum.rbs +38 -0
  119. data/sig/torque/postgresql/attributes/builder/full_text_search.rbs +27 -0
  120. data/sig/torque/postgresql/attributes/builder/period.rbs +106 -0
  121. data/sig/torque/postgresql/attributes/builder.rbs +19 -0
  122. data/sig/torque/postgresql/attributes/composite.rbs +28 -0
  123. data/sig/torque/postgresql/attributes/enum.rbs +69 -0
  124. data/sig/torque/postgresql/attributes/enum_set.rbs +75 -0
  125. data/sig/torque/postgresql/attributes/full_text_search.rbs +13 -0
  126. data/sig/torque/postgresql/attributes/lazy.rbs +17 -0
  127. data/sig/torque/postgresql/attributes/lquery.rbs +48 -0
  128. data/sig/torque/postgresql/attributes/ltree.rbs +46 -0
  129. data/sig/torque/postgresql/attributes/period.rbs +13 -0
  130. data/sig/torque/postgresql/attributes/simple_enum.rbs +39 -0
  131. data/sig/torque/postgresql/attributes/struct.rbs +38 -0
  132. data/sig/torque/postgresql/attributes.rbs +6 -0
  133. data/sig/torque/postgresql/autosave_association.rbs +16 -0
  134. data/sig/torque/postgresql/auxiliary_statement/recursive.rbs +22 -0
  135. data/sig/torque/postgresql/auxiliary_statement/settings.rbs +63 -0
  136. data/sig/torque/postgresql/auxiliary_statement.rbs +57 -0
  137. data/sig/torque/postgresql/base.rbs +41 -0
  138. data/sig/torque/postgresql/collector.rbs +7 -0
  139. data/sig/torque/postgresql/config.rbs +9 -0
  140. data/sig/torque/postgresql/function.rbs +21 -0
  141. data/sig/torque/postgresql/geometry_builder.rbs +20 -0
  142. data/sig/torque/postgresql/i18n.rbs +15 -0
  143. data/sig/torque/postgresql/inheritance/expander.rbs +23 -0
  144. data/sig/torque/postgresql/inheritance/record.rbs +20 -0
  145. data/sig/torque/postgresql/inheritance.rbs +47 -0
  146. data/sig/torque/postgresql/insert_all.rbs +22 -0
  147. data/sig/torque/postgresql/migration/command_recorder.rbs +30 -0
  148. data/sig/torque/postgresql/migration.rbs +6 -0
  149. data/sig/torque/postgresql/predicate_builder/arel_attribute_handler.rbs +15 -0
  150. data/sig/torque/postgresql/predicate_builder/array_handler.rbs +26 -0
  151. data/sig/torque/postgresql/predicate_builder/composite_handler.rbs +26 -0
  152. data/sig/torque/postgresql/predicate_builder/enumerator_lazy_handler.rbs +18 -0
  153. data/sig/torque/postgresql/predicate_builder/ltree_handler.rbs +36 -0
  154. data/sig/torque/postgresql/predicate_builder/regexp_handler.rbs +16 -0
  155. data/sig/torque/postgresql/predicate_builder/struct_handler.rbs +24 -0
  156. data/sig/torque/postgresql/predicate_builder.rbs +23 -0
  157. data/sig/torque/postgresql/predicate_table.rbs +24 -0
  158. data/sig/torque/postgresql/railtie.rbs +10 -0
  159. data/sig/torque/postgresql/reflection/abstract_reflection.rbs +22 -0
  160. data/sig/torque/postgresql/reflection/association_reflection.rbs +18 -0
  161. data/sig/torque/postgresql/reflection/belongs_to_many_reflection.rbs +30 -0
  162. data/sig/torque/postgresql/reflection/has_many_reflection.rbs +14 -0
  163. data/sig/torque/postgresql/reflection/runtime_reflection.rbs +18 -0
  164. data/sig/torque/postgresql/reflection/through_reflection.rbs +14 -0
  165. data/sig/torque/postgresql/reflection.rbs +11 -0
  166. data/sig/torque/postgresql/relation/auxiliary_statement.rbs +29 -0
  167. data/sig/torque/postgresql/relation/buckets.rbs +35 -0
  168. data/sig/torque/postgresql/relation/cross_join.rbs +63 -0
  169. data/sig/torque/postgresql/relation/distinct_on.rbs +16 -0
  170. data/sig/torque/postgresql/relation/inheritance.rbs +48 -0
  171. data/sig/torque/postgresql/relation/join_series.rbs +28 -0
  172. data/sig/torque/postgresql/relation/merger.rbs +23 -0
  173. data/sig/torque/postgresql/relation/segments.rbs +27 -0
  174. data/sig/torque/postgresql/relation.rbs +48 -0
  175. data/sig/torque/postgresql/schema_cache/bound_schema_reflection.rbs +14 -0
  176. data/sig/torque/postgresql/schema_cache/inheritance.rbs +12 -0
  177. data/sig/torque/postgresql/schema_cache/schema_reflection.rbs +14 -0
  178. data/sig/torque/postgresql/schema_cache.rbs +43 -0
  179. data/sig/torque/postgresql/table_name.rbs +20 -0
  180. data/sig/torque/postgresql/validations.rbs +13 -0
  181. data/sig/torque/postgresql/version.rbs +5 -0
  182. data/sig/torque/postgresql/versioned_commands/command_migration.rbs +54 -0
  183. data/sig/torque/postgresql/versioned_commands/generator.rbs +17 -0
  184. data/sig/torque/postgresql/versioned_commands/migration_context.rbs +30 -0
  185. data/sig/torque/postgresql/versioned_commands/migrator.rbs +19 -0
  186. data/sig/torque/postgresql/versioned_commands/schema_table.rbs +23 -0
  187. data/sig/torque/postgresql/versioned_commands.rbs +25 -0
  188. data/sig/torque/postgresql.rbs +26 -0
  189. data/skills/torque-postgresql/SKILL.md +70 -0
  190. data/spec/fixtures/migrations/20250101000008_create_trigger_audit_users_v1.sql +3 -0
  191. data/spec/initialize.rb +29 -2
  192. data/spec/mocks/cache_query.rb +13 -1
  193. data/spec/models/author.rb +1 -1
  194. data/spec/models/comment.rb +2 -0
  195. data/spec/models/place.rb +2 -0
  196. data/spec/models/profile.rb +31 -0
  197. data/spec/rbs_helper.rb +8 -0
  198. data/spec/schema.rb +35 -4
  199. data/spec/spec_helper.rb +1 -0
  200. data/spec/tests/arel_spec.rb +3 -3
  201. data/spec/tests/auxiliary_statement_spec.rb +12 -0
  202. data/spec/tests/buckets_spec.rb +122 -0
  203. data/spec/tests/composite_spec.rb +867 -0
  204. data/spec/tests/cross_join_spec.rb +185 -0
  205. data/spec/tests/distinct_on_spec.rb +5 -0
  206. data/spec/tests/enum_set_spec.rb +4 -0
  207. data/spec/tests/enum_spec.rb +20 -4
  208. data/spec/tests/geometric_builder_spec.rb +16 -5
  209. data/spec/tests/has_many_spec.rb +2 -1
  210. data/spec/tests/interval_spec.rb +6 -0
  211. data/spec/tests/ltree_spec.rb +886 -0
  212. data/spec/tests/predicate_builder_spec.rb +120 -0
  213. data/spec/tests/relation_spec.rb +25 -69
  214. data/spec/tests/segments_spec.rb +264 -0
  215. data/spec/tests/struct_spec.rb +1170 -0
  216. data/spec/tests/table_inheritance_spec.rb +1037 -56
  217. data/spec/tests/versioned_commands_spec.rb +145 -0
  218. metadata +198 -2
@@ -17,8 +17,31 @@ module Torque
17
17
  end
18
18
 
19
19
  class_methods do
20
- delegate :distinct_on, :with, :itself_only, :cast_records, :join_series,
21
- :buckets, to: :all
20
+ delegate :distinct_on, :with, :itself_only, :expand_records, :join_series,
21
+ :buckets, :segments, to: :all
22
+
23
+ # The Arel node for a path into a document column, typed by the struct
24
+ # class that backs the column when there is one
25
+ def arel_property_of(column, *path)
26
+ type = Adapter::OID.unwrap(attribute_types[column.to_s])
27
+ handler = PredicateBuilder::StructHandler.new(predicate_builder)
28
+ handler.property_for(arel_table[column], type, path)
29
+ end
30
+
31
+ # Composite values are objects that can be invalid on their own, so the
32
+ # attributes backed by one are validated alongside the record
33
+ def load_schema!
34
+ super
35
+ return unless PostgreSQL.config.composite.enabled &&
36
+ Adapter::OID.const_defined?(:Composite)
37
+
38
+ attribute_types.each do |name, type|
39
+ next if Adapter::OID::Composite.from(type).nil?
40
+ next if _validators[name.to_sym].any?(Validations::NestedValidator)
41
+
42
+ validates(name, nested: true)
43
+ end
44
+ end
22
45
 
23
46
  # Make sure that table name is an instance of TableName class
24
47
  def reset_table_name
@@ -27,24 +50,12 @@ module Torque
27
50
  end
28
51
 
29
52
  # Whenever the base model is inherited, add a list of auxiliary
30
- # statements like the one that loads inherited records' relname
53
+ # statements
31
54
  def inherited(subclass)
32
55
  super
33
56
 
34
57
  subclass.class_attribute(:auxiliary_statements_list)
35
58
  subclass.auxiliary_statements_list = {}
36
-
37
- record_class = ActiveRecord::Relation._record_class_attribute
38
-
39
- # Define the dynamic attribute that returns the same information as
40
- # the one provided by the auxiliary statement
41
- subclass.dynamic_attribute(record_class) do
42
- klass = self.class
43
- next klass.table_name unless klass.physically_inheritances?
44
-
45
- query = klass.unscoped.where(subclass.primary_key => id)
46
- query.pluck(klass.arel_table['tableoid'].pg_cast('regclass')).first
47
- end
48
59
  end
49
60
 
50
61
  # Specifies a one-to-many association. The following methods for
@@ -2,19 +2,29 @@
2
2
 
3
3
  module Torque
4
4
  module PostgreSQL
5
- include ActiveSupport::Configurable
5
+ # Stores a version check for compatibility purposes
6
+ AR810 = (ActiveRecord.gem_version >= Gem::Version.new('8.1.0'))
6
7
 
7
- # Use the same logger as the Active Record one
8
- def self.logger
9
- ActiveRecord::Base.logger
8
+ class << self
9
+ def config
10
+ @config ||= ActiveSupport::InheritableOptions.new
11
+ end
12
+
13
+ def configure
14
+ yield config
15
+ end
16
+
17
+ # Use the same logger as the Active Record one
18
+ def logger
19
+ ActiveRecord::Base.logger
20
+ end
10
21
  end
11
22
 
12
23
  # Allow nested configurations
13
- # :TODO: Rely on +inheritable_copy+ to make nested configurations
14
24
  config.define_singleton_method(:nested) do |name, &block|
15
- klass = Class.new(ActiveSupport::Configurable::Configuration).new
16
- block.call(klass) if block
17
- send("#{name}=", klass)
25
+ nested = ActiveSupport::InheritableOptions.new
26
+ block&.call(nested)
27
+ self[name] = nested
18
28
  end
19
29
 
20
30
  # Set if any information that requires querying and searching or collecting
@@ -28,10 +38,13 @@ module Torque
28
38
  # Add support for querying and calculating histogram buckets
29
39
  config.buckets = true
30
40
 
41
+ # Add support for segmenting calculations by named conditions
42
+ config.segments = true
43
+
31
44
  # Set a list of irregular model name when associated with table names
32
45
  config.irregular_models = {}
33
46
  def config.irregular_models=(hash)
34
- PostgreSQL.config[:irregular_models] = hash.map do |(table, model)|
47
+ self[:irregular_models] = hash.map do |(table, model)|
35
48
  [table.to_s, model.to_s]
36
49
  end.to_h
37
50
  end
@@ -131,6 +144,27 @@ module Torque
131
144
 
132
145
  end
133
146
 
147
+ # Configure composite type features
148
+ config.nested(:composite) do |composite|
149
+
150
+ # Enables composite types handler by this gem
151
+ composite.enabled = true
152
+
153
+ # Specify the namespace of each composite type class
154
+ composite.namespace = nil
155
+
156
+ # Set a list of irregular class names when associated with composite
157
+ # types
158
+ def composite.irregular_types=(hash)
159
+ self[:irregular_types] = hash.map do |type, klass|
160
+ [type.to_s, klass.to_s]
161
+ end.to_h
162
+ end
163
+
164
+ composite.irregular_types = {}
165
+
166
+ end
167
+
134
168
  # Configure geometry data types
135
169
  config.nested(:geometry) do |geometry|
136
170
 
@@ -172,10 +206,21 @@ module Torque
172
206
  # the name of the table that actually holds the record
173
207
  inheritance.record_class_column_name = :_record_class
174
208
 
175
- # Determines the name of the column used when identifying that the loaded
176
- # records should be casted to its correctly model. This will be TRUE for
177
- # the records mentioned on `cast_records`
178
- inheritance.auto_cast_column_name = :_auto_cast
209
+ end
210
+
211
+ # Configure struct features
212
+ config.nested(:struct) do |struct|
213
+
214
+ # Enables the JSON(B) columns backed by ActiveModel classes handler
215
+ struct.enabled = true
216
+
217
+ # The name of the method to be used on any ActiveRecord::Base to
218
+ # initialize model-based struct features
219
+ struct.base_method = :struct_for
220
+
221
+ # Whether struct classes reject properties that they don't declare
222
+ # by default
223
+ struct.default_strict = true
179
224
 
180
225
  end
181
226
 
@@ -260,12 +305,35 @@ module Torque
260
305
 
261
306
  end
262
307
 
308
+ # Configure label tree features
309
+ config.nested(:ltree) do |ltree|
310
+
311
+ # Enables the ltree and lquery data types handler by this gem
312
+ ltree.enabled = true
313
+
314
+ # A hash of replacements applied to every label provided by the
315
+ # application before it is sent to the database, so that a source that
316
+ # does not satisfy PostgreSQL's rules on its own can still be used. Dashes, for
317
+ # example, are only accepted as of PostgreSQL 16
318
+ # { '-' => '_' } turns dashes into underscores
319
+ # { '-' => '' } drops dashes
320
+ # It never applies to values read from the database
321
+ ltree.sanitize = nil
322
+
323
+ # The name of a method that, whenever the given object responds to it, is
324
+ # used to translate that object into a path or a pattern. It is what lets
325
+ # any class be used wherever one is expected, on assignment, on a
326
+ # condition, and while comparing one path to another
327
+ ltree.compatible_method = :to_tree_path
328
+
329
+ end
330
+
263
331
  # Configure arel additional features
264
332
  config.nested(:arel) do |arel|
265
333
 
266
334
  # When provided, the initializer will expose the Arel function helper on
267
335
  # the given module
268
- config.expose_function_helper_on = nil
336
+ arel.expose_function_helper_on = nil
269
337
 
270
338
  # List of Arel INFIX operators that will be made available for using as
271
339
  # methods on Arel::Nodes::Node and Arel::Attribute
@@ -279,6 +347,8 @@ module Torque
279
347
  'doesnt_right_extend' => '&<',
280
348
  'doesnt_left_extend' => '&>',
281
349
  'adjacent_to' => '-|-',
350
+ 'matches_lquery' => '~',
351
+ 'matches_any_lquery' => '?',
282
352
  }
283
353
 
284
354
  end
@@ -344,7 +414,7 @@ module Torque
344
414
 
345
415
  # Define the list of commands that are going to be versioned by this
346
416
  # method
347
- vs.types = %i[function type view]
417
+ vs.types = %i[function type view trigger]
348
418
 
349
419
  # The name of the table that will inherit from +schema_migrations+ and
350
420
  # store the list of versioned commands that have been executed
@@ -352,5 +422,20 @@ module Torque
352
422
 
353
423
  end
354
424
 
425
+ # Configure cross join features
426
+ config.nested(:cross_join) do |cross_join|
427
+
428
+ # This is an experimental feature that developers must explicitly opt-in.
429
+ # TODO: hook into how conditions and columns are resolved, so that the
430
+ # name of a joined set can shadow a real column
431
+ cross_join.enabled = false
432
+
433
+ # The name of the column added by +with_ordinality+ when it is given
434
+ # +true+ instead of a name. It matches what PostgreSQL calls the column
435
+ # when it is not named at all
436
+ cross_join.ordinality_column = :ordinality
437
+
438
+ end
439
+
355
440
  end
356
441
  end
@@ -24,8 +24,7 @@ module Torque
24
24
  case value
25
25
  when ::String
26
26
  return if value.blank?
27
- value.gsub!(DESTRUCTOR, '')
28
- build_klass(*value.split(','))
27
+ build_klass(*value.gsub(DESTRUCTOR, '').split(','))
29
28
  when ::Hash
30
29
  build_klass(*value.symbolize_keys.slice(*pieces).values)
31
30
  when ::Array
@@ -49,8 +48,8 @@ module Torque
49
48
  parts = parts&.compact&.flatten
50
49
  return if parts.blank?
51
50
 
52
- raise 'Invalid format' if parts.size < pieces.size
53
- format(formation, *parts.first(pieces.size).map(&number_serializer))
51
+ parts = ::Array.new(pieces.size) { |index| parts[index] }
52
+ format(formation, *parts.map(&number_serializer))
54
53
  end
55
54
 
56
55
  def deserialize(value)
@@ -81,13 +80,8 @@ module Torque
81
80
 
82
81
  def build_klass(*args)
83
82
  return nil if args.empty?
84
- check_invalid_format!(args)
85
83
 
86
- config_class.new(*args.try(:first, pieces.size)&.map(&:to_f))
87
- end
88
-
89
- def check_invalid_format!(args)
90
- raise 'Invalid format' if args.size < pieces.size
84
+ config_class.new(*args.first(pieces.size).map(&:to_f))
91
85
  end
92
86
  end
93
87
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Torque
4
+ module PostgreSQL
5
+ module Inheritance
6
+ class Expander
7
+
8
+ def initialize(model, records, targets)
9
+ @model = model
10
+ @records = records
11
+ @targets = targets
12
+ end
13
+
14
+ # Load the columns that are missing from each partial record using one
15
+ # query per inherited table
16
+ def call
17
+ pending.each do |klass, records|
18
+ amend(klass, records, fetch(klass, records.map(&:id)))
19
+ end
20
+
21
+ @records
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :model, :targets
27
+
28
+ def pending
29
+ @records.select do |record|
30
+ record.partial_record? && targets.include?(record.class)
31
+ end.group_by(&:class)
32
+ end
33
+
34
+ def extra_columns(klass)
35
+ klass.attribute_names - model.attribute_names
36
+ end
37
+
38
+ def fetch(klass, ids)
39
+ columns = [klass.primary_key, *extra_columns(klass)]
40
+ table = klass.arel_table
41
+ query = klass.unscoped.itself_only.select(*columns.map { |name| table[name] })
42
+
43
+ klass.lease_connection.select_all(
44
+ query.where(klass.primary_key => ids).arel,
45
+ "#{klass.name} Expand",
46
+ ).to_a.index_by { |row| row[klass.primary_key] }
47
+ end
48
+
49
+ def amend(klass, records, rows)
50
+ columns = extra_columns(klass)
51
+
52
+ records.each do |record|
53
+ row = rows[record.id]
54
+ next if row.nil?
55
+
56
+ attributes = record.instance_variable_get(:@attributes)
57
+ columns.each { |name| attributes.write_from_database(name, row[name]) }
58
+
59
+ record.send(:mark_as_full_record!)
60
+ end
61
+ end
62
+
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Torque
4
+ module PostgreSQL
5
+ module Inheritance
6
+ module Record
7
+
8
+ # Whether the record was loaded from one of its ancestor tables and
9
+ # therefore is missing the columns that only exist on its own table
10
+ def partial_record?
11
+ @partial_record == true
12
+ end
13
+
14
+ # Reload always queries the record's own table, which produces a
15
+ # complete and writable record
16
+ def reload(*)
17
+ return super unless partial_record?
18
+
19
+ super.tap { mark_as_full_record! }
20
+ end
21
+
22
+ # Deleting a row only needs its primary key, so the missing columns of
23
+ # a partial record are no reason to refuse it
24
+ def destroy
25
+ return super unless partial_record?
26
+
27
+ without_readonly { super }
28
+ end
29
+
30
+ private
31
+
32
+ def without_readonly
33
+ @readonly = false
34
+ yield
35
+ ensure
36
+ @readonly = true
37
+ end
38
+
39
+ def mark_as_partial_record!
40
+ @partial_record = true
41
+ readonly!
42
+ end
43
+
44
+ def mark_as_full_record!
45
+ @partial_record = false
46
+ @readonly = false
47
+ end
48
+
49
+ end
50
+ end
51
+ end
52
+ end