torque-postgresql 4.0.0.rc1 → 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.
- checksums.yaml +4 -4
- data/lib/generators/torque/function_generator.rb +13 -0
- data/lib/generators/torque/templates/function.sql.erb +4 -0
- data/lib/generators/torque/templates/type.sql.erb +2 -0
- data/lib/generators/torque/templates/view.sql.erb +3 -0
- data/lib/generators/torque/type_generator.rb +13 -0
- data/lib/generators/torque/view_generator.rb +16 -0
- data/lib/torque/postgresql/adapter/database_statements.rb +48 -10
- data/lib/torque/postgresql/adapter/schema_definitions.rb +22 -0
- data/lib/torque/postgresql/adapter/schema_dumper.rb +47 -1
- data/lib/torque/postgresql/adapter/schema_statements.rb +45 -0
- data/lib/torque/postgresql/arel/nodes.rb +14 -0
- data/lib/torque/postgresql/arel/visitors.rb +4 -0
- data/lib/torque/postgresql/attributes/builder/full_text_search.rb +16 -28
- data/lib/torque/postgresql/base.rb +2 -1
- data/lib/torque/postgresql/config.rb +35 -1
- data/lib/torque/postgresql/function.rb +33 -0
- data/lib/torque/postgresql/railtie.rb +26 -1
- data/lib/torque/postgresql/relation/auxiliary_statement.rb +7 -2
- data/lib/torque/postgresql/relation/buckets.rb +124 -0
- data/lib/torque/postgresql/relation/distinct_on.rb +7 -2
- data/lib/torque/postgresql/relation/inheritance.rb +18 -8
- data/lib/torque/postgresql/relation/join_series.rb +112 -0
- data/lib/torque/postgresql/relation/merger.rb +17 -3
- data/lib/torque/postgresql/relation.rb +18 -28
- data/lib/torque/postgresql/version.rb +1 -1
- data/lib/torque/postgresql/versioned_commands/command_migration.rb +146 -0
- data/lib/torque/postgresql/versioned_commands/generator.rb +57 -0
- data/lib/torque/postgresql/versioned_commands/migration_context.rb +83 -0
- data/lib/torque/postgresql/versioned_commands/migrator.rb +39 -0
- data/lib/torque/postgresql/versioned_commands/schema_table.rb +101 -0
- data/lib/torque/postgresql/versioned_commands.rb +161 -0
- data/spec/fixtures/migrations/20250101000001_create_users.rb +0 -0
- data/spec/fixtures/migrations/20250101000002_create_function_count_users_v1.sql +0 -0
- data/spec/fixtures/migrations/20250101000003_create_internal_users.rb +0 -0
- data/spec/fixtures/migrations/20250101000004_update_function_count_users_v2.sql +0 -0
- data/spec/fixtures/migrations/20250101000005_create_view_all_users_v1.sql +0 -0
- data/spec/fixtures/migrations/20250101000006_create_type_user_id_v1.sql +0 -0
- data/spec/fixtures/migrations/20250101000007_remove_function_count_users_v2.sql +0 -0
- data/spec/initialize.rb +9 -0
- data/spec/schema.rb +2 -4
- data/spec/spec_helper.rb +6 -1
- data/spec/tests/full_text_seach_test.rb +30 -2
- data/spec/tests/relation_spec.rb +229 -0
- data/spec/tests/schema_spec.rb +4 -1
- data/spec/tests/versioned_commands_spec.rb +513 -0
- metadata +33 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d80bb198f5645c35915440f05a4ad083830221de27983c4c7b936c394e7bcbbb
|
4
|
+
data.tar.gz: a73a8de3a63b806e08f42109a3bb703e7ce19a20ae55a317c7fb763215612396
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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,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
|
@@ -154,19 +154,10 @@ module Torque
|
|
154
154
|
|
155
155
|
# Build the query for allowed schemas
|
156
156
|
def user_defined_schemas_sql
|
157
|
-
conditions = []
|
158
|
-
conditions << <<-SQL.squish if schemas_blacklist.any?
|
159
|
-
nspname NOT LIKE ALL (ARRAY['#{schemas_blacklist.join("', '")}'])
|
160
|
-
SQL
|
161
|
-
|
162
|
-
conditions << <<-SQL.squish if schemas_whitelist.any?
|
163
|
-
nspname LIKE ANY (ARRAY['#{schemas_whitelist.join("', '")}'])
|
164
|
-
SQL
|
165
|
-
|
166
157
|
<<-SQL.squish
|
167
158
|
SELECT nspname
|
168
159
|
FROM pg_catalog.pg_namespace
|
169
|
-
WHERE 1=1 AND #{
|
160
|
+
WHERE 1=1 AND #{filter_by_schema.join(' AND ')}
|
170
161
|
ORDER BY oid
|
171
162
|
SQL
|
172
163
|
end
|
@@ -191,6 +182,53 @@ module Torque
|
|
191
182
|
SQL
|
192
183
|
end
|
193
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("', '")}'])
|
228
|
+
SQL
|
229
|
+
conditions
|
230
|
+
end
|
231
|
+
|
194
232
|
end
|
195
233
|
end
|
196
234
|
end
|
@@ -49,6 +49,28 @@ module Torque
|
|
49
49
|
end
|
50
50
|
end
|
51
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
|
+
|
72
|
+
end
|
73
|
+
|
52
74
|
ActiveRecord::ConnectionAdapters::PostgreSQL::Table.include ColumnMethods
|
53
75
|
ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition.include TableDefinition
|
54
76
|
end
|
@@ -12,6 +12,15 @@ module Torque
|
|
12
12
|
(?:\s*,\s*'([A-D])')?
|
13
13
|
/ix
|
14
14
|
|
15
|
+
def initialize(*)
|
16
|
+
super
|
17
|
+
|
18
|
+
if with_versioned_commands?
|
19
|
+
@versioned_commands = VersionedCommands::SchemaTable.new(@connection.pool)
|
20
|
+
@ignore_tables << @versioned_commands.table_name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
15
24
|
def dump(stream) # :nodoc:
|
16
25
|
@connection.dump_mode!
|
17
26
|
super
|
@@ -22,6 +31,13 @@ module Torque
|
|
22
31
|
|
23
32
|
private
|
24
33
|
|
34
|
+
def types(stream) # :nodoc:
|
35
|
+
super
|
36
|
+
|
37
|
+
versioned_commands(stream, :type)
|
38
|
+
versioned_commands(stream, :function)
|
39
|
+
end
|
40
|
+
|
25
41
|
def tables(stream) # :nodoc:
|
26
42
|
around_tables(stream) { dump_tables(stream) }
|
27
43
|
end
|
@@ -30,6 +46,7 @@ module Torque
|
|
30
46
|
functions(stream) if fx_functions_position == :beginning
|
31
47
|
|
32
48
|
yield
|
49
|
+
versioned_commands(stream, :view, true)
|
33
50
|
|
34
51
|
functions(stream) if fx_functions_position == :end
|
35
52
|
triggers(stream) if defined?(::Fx::SchemaDumper::Trigger)
|
@@ -70,7 +87,7 @@ module Torque
|
|
70
87
|
# dump foreign keys at the end to make sure all dependent tables exist.
|
71
88
|
if @connection.supports_foreign_keys?
|
72
89
|
foreign_keys_stream = StringIO.new
|
73
|
-
sorted_tables.each do |tbl|
|
90
|
+
sorted_tables.each do |(tbl, *)|
|
74
91
|
foreign_keys(tbl, foreign_keys_stream)
|
75
92
|
end
|
76
93
|
|
@@ -147,6 +164,35 @@ module Torque
|
|
147
164
|
settings.to_h.transform_values(&:inspect)
|
148
165
|
end
|
149
166
|
|
167
|
+
# Simply add all versioned commands to the stream
|
168
|
+
def versioned_commands(stream, type, add_newline = false)
|
169
|
+
return unless with_versioned_commands?
|
170
|
+
|
171
|
+
list = @versioned_commands.versions_of(type.to_s)
|
172
|
+
return if list.empty?
|
173
|
+
|
174
|
+
existing = list_existing_versioned_commands(type)
|
175
|
+
|
176
|
+
stream.puts if add_newline
|
177
|
+
stream.puts " # These are #{type.to_s.pluralize} managed by versioned commands"
|
178
|
+
list.each do |(name, version)|
|
179
|
+
next if existing.exclude?(name)
|
180
|
+
|
181
|
+
stream.puts " create_#{type} \"#{name}\", version: #{version}"
|
182
|
+
end
|
183
|
+
stream.puts unless add_newline
|
184
|
+
end
|
185
|
+
|
186
|
+
def list_existing_versioned_commands(type)
|
187
|
+
@connection.list_versioned_commands(type).each_with_object(Set.new) do |entry, set|
|
188
|
+
set << (entry.first == 'public' ? entry.last : entry.join('_'))
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def with_versioned_commands?
|
193
|
+
PostgreSQL.config.versioned_commands.enabled
|
194
|
+
end
|
195
|
+
|
150
196
|
def fx_functions_position
|
151
197
|
return unless defined?(::Fx::SchemaDumper::Function)
|
152
198
|
Fx.configuration.dump_functions_at_beginning_of_schema ? :beginning : :end
|
@@ -137,6 +137,51 @@ module Torque
|
|
137
137
|
super + [:schema, :inherits]
|
138
138
|
end
|
139
139
|
|
140
|
+
# Add proper support for schema load when using versioned commands
|
141
|
+
def assume_migrated_upto_version(version)
|
142
|
+
return super unless PostgreSQL.config.versioned_commands.enabled
|
143
|
+
return super if (commands = pool.migration_context.migration_commands).empty?
|
144
|
+
|
145
|
+
version = version.to_i
|
146
|
+
migration_context = pool.migration_context
|
147
|
+
migrated = migration_context.get_all_versions
|
148
|
+
versions = migration_context.migrations.map(&:version)
|
149
|
+
|
150
|
+
inserting = (versions - migrated).select { |v| v < version }
|
151
|
+
inserting << version unless migrated.include?(version)
|
152
|
+
return if inserting.empty?
|
153
|
+
|
154
|
+
duplicated = inserting.tally.filter_map { |v, count| v if count > 1 }
|
155
|
+
raise <<~MSG.squish if duplicated.present?
|
156
|
+
Duplicate migration #{duplicated.first}.
|
157
|
+
Please renumber your migrations to resolve the conflict.
|
158
|
+
MSG
|
159
|
+
|
160
|
+
VersionedCommands::SchemaTable.new(pool).create_table
|
161
|
+
execute insert_versions_sql(inserting)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Add proper support for schema load when using versioned commands
|
165
|
+
def insert_versions_sql(versions)
|
166
|
+
return super unless PostgreSQL.config.versioned_commands.enabled
|
167
|
+
|
168
|
+
commands = pool.migration_context.migration_commands.select do |migration|
|
169
|
+
versions.include?(migration.version)
|
170
|
+
end
|
171
|
+
|
172
|
+
return super if commands.empty?
|
173
|
+
|
174
|
+
table = quote_table_name(VersionedCommands::SchemaTable.new(pool).table_name)
|
175
|
+
|
176
|
+
sql = super(versions - commands.map(&:version))
|
177
|
+
sql << "\nINSERT INTO #{table} (version, type, object_name) VALUES\n"
|
178
|
+
sql << commands.map do |m|
|
179
|
+
+"(#{quote(m.version)}, #{quote(m.type)}, #{quote(m.object_name)})"
|
180
|
+
end.join(",\n")
|
181
|
+
sql << ";"
|
182
|
+
sql
|
183
|
+
end
|
184
|
+
|
140
185
|
private
|
141
186
|
|
142
187
|
# Remove the schema from the sequence name
|
@@ -19,6 +19,20 @@ module Torque
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
+
class Ref < ::Arel::Nodes::Unary
|
23
|
+
attr_reader :reference
|
24
|
+
alias to_s expr
|
25
|
+
|
26
|
+
def initialize(expr, reference = nil)
|
27
|
+
@reference = reference
|
28
|
+
super expr
|
29
|
+
end
|
30
|
+
|
31
|
+
def as(other)
|
32
|
+
@reference&.as(other) || super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
22
36
|
end
|
23
37
|
|
24
38
|
::Arel.define_singleton_method(:array) do |*values, cast: nil|
|
@@ -25,6 +25,10 @@ module Torque
|
|
25
25
|
end
|
26
26
|
|
27
27
|
## TORQUE VISITORS
|
28
|
+
def visit_Torque_PostgreSQL_Arel_Nodes_Ref(o, collector)
|
29
|
+
collector << quote_table_name(o.expr)
|
30
|
+
end
|
31
|
+
|
28
32
|
# Allow casting any node
|
29
33
|
def visit_Torque_PostgreSQL_Arel_Nodes_Cast(o, collector)
|
30
34
|
visit(o.left, collector) << '::' << o.right
|
@@ -6,7 +6,7 @@ module Torque
|
|
6
6
|
module Builder
|
7
7
|
class FullTextSearch
|
8
8
|
attr_accessor :klass, :attribute, :options, :klass_module,
|
9
|
-
:default_rank, :default_order, :default_language
|
9
|
+
:default_rank, :default_mode, :default_order, :default_language
|
10
10
|
|
11
11
|
def initialize(klass, attribute, options = {})
|
12
12
|
@klass = klass
|
@@ -14,6 +14,7 @@ module Torque
|
|
14
14
|
@options = options
|
15
15
|
|
16
16
|
@default_rank = options[:with_rank] == true ? 'rank' : options[:with_rank]&.to_s
|
17
|
+
@default_mode = options[:mode] || PostgreSQL.config.full_text_search.default_mode
|
17
18
|
|
18
19
|
@default_order =
|
19
20
|
case options[:order]
|
@@ -53,30 +54,6 @@ module Torque
|
|
53
54
|
end
|
54
55
|
|
55
56
|
# Creates a class method as the scope that builds the full text search
|
56
|
-
#
|
57
|
-
# def full_text_search(value, order: :asc, rank: :rank, language: 'english', phrase: true)
|
58
|
-
# attr = arel_table["search_vector"]
|
59
|
-
# fn = ::Torque::PostgreSQL::FN
|
60
|
-
#
|
61
|
-
# lang = language.to_s if !language.is_a?(::Symbol)
|
62
|
-
# lang ||= arel_table[language.to_s].pg_cast(:regconfig) if has_attribute?(language)
|
63
|
-
# lang ||= public_send(language) if respond_to?(language)
|
64
|
-
#
|
65
|
-
# raise ArgumentError, <<~MSG.squish if lang.nil?
|
66
|
-
# Unable to determine language from #{language.inspect}.
|
67
|
-
# MSG
|
68
|
-
#
|
69
|
-
# value = fn.bind(:value, value.to_s, attr.type_caster)
|
70
|
-
# lang = fn.bind(:lang, lang, attr.type_caster) if lang.is_a?(::String)
|
71
|
-
#
|
72
|
-
# query = fn.public_send(phrase ? :phraseto_tsquery : :to_tsquery, lang, value)
|
73
|
-
# ranker = fn.ts_rank(attr, query) if rank || order
|
74
|
-
#
|
75
|
-
# result = where(fn.infix(:"@@", attr, query))
|
76
|
-
# result = result.order(ranker.public_send(order == :desc ? :desc : :asc)) if order
|
77
|
-
# result.select_extra_values += [ranker.as(rank == true ? 'rank' : rank.to_s)] if rank
|
78
|
-
# result
|
79
|
-
# end
|
80
57
|
def add_scope_to_module
|
81
58
|
klass_module.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
82
59
|
def #{scope_name}(value#{scope_args})
|
@@ -87,14 +64,25 @@ module Torque
|
|
87
64
|
lang ||= arel_table[language.to_s] if has_attribute?(language)
|
88
65
|
lang ||= public_send(language) if respond_to?(language)
|
89
66
|
|
90
|
-
|
67
|
+
function = {
|
68
|
+
default: :to_tsquery,
|
69
|
+
phrase: :phraseto_tsquery,
|
70
|
+
plain: :plainto_tsquery,
|
71
|
+
web: :websearch_to_tsquery,
|
72
|
+
}[mode.to_sym]
|
73
|
+
|
74
|
+
raise ::ArgumentError, <<~MSG.squish if lang.blank?
|
91
75
|
Unable to determine language from \#{language.inspect}.
|
92
76
|
MSG
|
93
77
|
|
78
|
+
raise ::ArgumentError, <<~MSG.squish if function.nil?
|
79
|
+
Invalid mode \#{mode.inspect} for full text search.
|
80
|
+
MSG
|
81
|
+
|
94
82
|
value = fn.bind(:value, value.to_s, attr.type_caster)
|
95
83
|
lang = fn.bind(:lang, lang, attr.type_caster) if lang.is_a?(::String)
|
96
84
|
|
97
|
-
query = fn.public_send(
|
85
|
+
query = fn.public_send(function, lang, value)
|
98
86
|
ranker = fn.ts_rank(attr, query) if rank || order
|
99
87
|
|
100
88
|
result = where(fn.infix(:"@@", attr, query))
|
@@ -111,7 +99,7 @@ module Torque
|
|
111
99
|
args << ", order: #{default_order.inspect}"
|
112
100
|
args << ", rank: #{default_rank.inspect}"
|
113
101
|
args << ", language: #{default_language.inspect}"
|
114
|
-
args << ",
|
102
|
+
args << ", mode: :#{default_mode}"
|
115
103
|
args
|
116
104
|
end
|
117
105
|
end
|
@@ -17,7 +17,8 @@ module Torque
|
|
17
17
|
end
|
18
18
|
|
19
19
|
class_methods do
|
20
|
-
delegate :distinct_on, :with, :itself_only, :cast_records,
|
20
|
+
delegate :distinct_on, :with, :itself_only, :cast_records, :join_series,
|
21
|
+
:buckets, to: :all
|
21
22
|
|
22
23
|
# Make sure that table name is an instance of TableName class
|
23
24
|
def reset_table_name
|
@@ -22,6 +22,12 @@ module Torque
|
|
22
22
|
# same configuration is set to true
|
23
23
|
config.eager_load = false
|
24
24
|
|
25
|
+
# Add support for joining any query/association with a generated series
|
26
|
+
config.join_series = true
|
27
|
+
|
28
|
+
# Add support for querying and calculating histogram buckets
|
29
|
+
config.buckets = true
|
30
|
+
|
25
31
|
# Set a list of irregular model name when associated with table names
|
26
32
|
config.irregular_models = {}
|
27
33
|
def config.irregular_models=(hash)
|
@@ -290,6 +296,14 @@ module Torque
|
|
290
296
|
# Defines the default language when generating search vector columns
|
291
297
|
fts.default_language = 'english'
|
292
298
|
|
299
|
+
# Defines the default mode to be used when generating full text search
|
300
|
+
# queries. It can be one of the following:
|
301
|
+
# - :default (to_tsquery)
|
302
|
+
# - :phrase (phraseto_tsquery)
|
303
|
+
# - :plain (plainto_tsquery)
|
304
|
+
# - :web (websearch_to_tsquery)
|
305
|
+
fts.default_mode = :phrase
|
306
|
+
|
293
307
|
# Defines the default index type to be used when creating search vector.
|
294
308
|
# It still requires that the column requests an index
|
295
309
|
fts.default_index_type = :gin
|
@@ -303,7 +317,7 @@ module Torque
|
|
303
317
|
builder.enabled = %i[regexp arel_attribute enumerator_lazy]
|
304
318
|
|
305
319
|
# When active, values provided to array attributes will be handled more
|
306
|
-
#
|
320
|
+
# friendly. It will use the +ANY+ operator on a equality check and
|
307
321
|
# overlaps when the given value is an array
|
308
322
|
builder.handle_array_attributes = false
|
309
323
|
|
@@ -318,5 +332,25 @@ module Torque
|
|
318
332
|
|
319
333
|
end
|
320
334
|
|
335
|
+
# Configure versioned commands features
|
336
|
+
config.nested(:versioned_commands) do |vs|
|
337
|
+
|
338
|
+
# This is a feature that developers must explicitly opt-in. It is designed
|
339
|
+
# in a way that prevents a large impact on Rails' original migrations
|
340
|
+
# behavior. But, it is still a feature that everyone may not need, and
|
341
|
+
# some may complain about the additional schema table, which also uses
|
342
|
+
# inheritance
|
343
|
+
vs.enabled = false
|
344
|
+
|
345
|
+
# Define the list of commands that are going to be versioned by this
|
346
|
+
# method
|
347
|
+
vs.types = %i[function type view]
|
348
|
+
|
349
|
+
# The name of the table that will inherit from +schema_migrations+ and
|
350
|
+
# store the list of versioned commands that have been executed
|
351
|
+
vs.table_name = 'schema_versioned_commands'
|
352
|
+
|
353
|
+
end
|
354
|
+
|
321
355
|
end
|
322
356
|
end
|
@@ -25,6 +25,14 @@ module Torque
|
|
25
25
|
bind(arel_attribute.name, value, arel_attribute.type_caster)
|
26
26
|
end
|
27
27
|
|
28
|
+
# A facilitator to create a bind param with a specific type
|
29
|
+
def bind_type(value, type = nil, name: 'value', cast: nil)
|
30
|
+
type ||= ruby_type_to_model_type(value)
|
31
|
+
type = ActiveModel::Type.lookup(type) if type.is_a?(Symbol)
|
32
|
+
result = bind(name, value, type)
|
33
|
+
cast ? result.pg_cast(cast) : result
|
34
|
+
end
|
35
|
+
|
28
36
|
# A facilitator to create an infix operation
|
29
37
|
def infix(op, left, right)
|
30
38
|
::Arel::Nodes::InfixOperation.new(op, left, right)
|
@@ -38,6 +46,12 @@ module Torque
|
|
38
46
|
args.reduce { |left, right| infix(:"||", left, right) }
|
39
47
|
end
|
40
48
|
|
49
|
+
# A simple helper to trick Rails into producing the right SQL for
|
50
|
+
# grouping operations
|
51
|
+
def group_by(arel, name)
|
52
|
+
Arel::Nodes::Ref.new(name.to_s, arel)
|
53
|
+
end
|
54
|
+
|
41
55
|
# As of now, this indicates that it supports any direct calls, since
|
42
56
|
# the idea is to simply map to an Arel function with the same name,
|
43
57
|
# without checking if it actually exists
|
@@ -53,6 +67,25 @@ module Torque
|
|
53
67
|
::Arel::Nodes::NamedFunction.new(name.to_s.upcase, args)
|
54
68
|
end
|
55
69
|
|
70
|
+
private
|
71
|
+
|
72
|
+
def ruby_type_to_model_type(value)
|
73
|
+
case value
|
74
|
+
when Integer then :integer
|
75
|
+
when Float then :float
|
76
|
+
when String then :string
|
77
|
+
when Time, ActiveSupport::TimeWithZone then :time
|
78
|
+
when TrueClass, FalseClass then :boolean
|
79
|
+
when DateTime then :datetime
|
80
|
+
when Date then :date
|
81
|
+
when BigDecimal then :decimal
|
82
|
+
when ActiveSupport::Duration
|
83
|
+
Adapter::OID::Interval.new
|
84
|
+
else
|
85
|
+
raise ArgumentError, "Cannot infer type from value: #{value.inspect}."
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
56
89
|
end
|
57
90
|
end
|
58
91
|
|
@@ -19,6 +19,17 @@ module Torque
|
|
19
19
|
ActiveRecord::Base.belongs_to_many_required_by_default =
|
20
20
|
torque_config.associations.belongs_to_many_required_by_default
|
21
21
|
|
22
|
+
## General features
|
23
|
+
if torque_config.join_series
|
24
|
+
require_relative 'relation/join_series'
|
25
|
+
Relation.include(Relation::JoinSeries)
|
26
|
+
end
|
27
|
+
|
28
|
+
if torque_config.buckets
|
29
|
+
require_relative 'relation/buckets'
|
30
|
+
Relation.include(Relation::Buckets)
|
31
|
+
end
|
32
|
+
|
22
33
|
## Schemas Enabled Setup
|
23
34
|
if (config = torque_config.schemas).enabled
|
24
35
|
require_relative 'adapter/schema_overrides'
|
@@ -109,7 +120,21 @@ module Torque
|
|
109
120
|
PostgreSQL::Arel.build_operations(torque_config.arel.infix_operators)
|
110
121
|
if (mod = torque_config.arel.expose_function_helper_on&.to_s)
|
111
122
|
parent, _, name = mod.rpartition('::')
|
112
|
-
parent.constantize
|
123
|
+
parent = parent ? parent.constantize : ::Object
|
124
|
+
|
125
|
+
raise ArgumentError, <<~MSG.squish if parent.const_defined?(name)
|
126
|
+
Unable to expose Arel function helper on #{mod} because the constant
|
127
|
+
#{name} is already defined on #{parent}. Please choose a different name.
|
128
|
+
MSG
|
129
|
+
|
130
|
+
parent.const_set(name, PostgreSQL::FN)
|
131
|
+
end
|
132
|
+
|
133
|
+
## Versioned Commands Setup
|
134
|
+
if (config = torque_config.versioned_commands).enabled
|
135
|
+
require_relative 'versioned_commands'
|
136
|
+
|
137
|
+
ActiveRecord::Schema::Definition.include(Adapter::Definition)
|
113
138
|
end
|
114
139
|
|
115
140
|
# Make sure to load all the types that are handled by this gem on
|
@@ -6,9 +6,14 @@ module Torque
|
|
6
6
|
module AuxiliaryStatement
|
7
7
|
|
8
8
|
# :nodoc:
|
9
|
-
def auxiliary_statements_values
|
9
|
+
def auxiliary_statements_values
|
10
|
+
@values.fetch(:auxiliary_statements, FROZEN_EMPTY_ARRAY)
|
11
|
+
end
|
10
12
|
# :nodoc:
|
11
|
-
def auxiliary_statements_values=(value)
|
13
|
+
def auxiliary_statements_values=(value)
|
14
|
+
assert_modifiable!
|
15
|
+
@values[:auxiliary_statements] = value
|
16
|
+
end
|
12
17
|
|
13
18
|
# Set use of an auxiliary statement
|
14
19
|
def with(*args, **settings)
|