sequel 5.30.0 → 5.31.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +20 -0
- data/README.rdoc +1 -1
- data/doc/postgresql.rdoc +71 -0
- data/doc/release_notes/5.31.0.txt +148 -0
- data/doc/testing.rdoc +0 -1
- data/lib/sequel/adapters/shared/postgres.rb +150 -5
- data/lib/sequel/adapters/shared/sqlite.rb +19 -3
- data/lib/sequel/extensions/migration.rb +1 -1
- data/lib/sequel/extensions/pg_enum.rb +5 -2
- data/lib/sequel/extensions/pg_hstore.rb +6 -0
- data/lib/sequel/extensions/schema_dumper.rb +10 -4
- data/lib/sequel/model/base.rb +12 -6
- data/lib/sequel/plugins/association_lazy_eager_option.rb +64 -0
- data/lib/sequel/plugins/forbid_lazy_load.rb +214 -0
- data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
- data/lib/sequel/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7419480f1c01043fb35c490c25cfbbcfaa9b5694ad41462f7af1c5e5e57bdf5
|
4
|
+
data.tar.gz: 7fe799521a27993775f197f15cc38b780c04a3d8d4a9a8ef931da1bea2308779
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3c37683caeb5390eaf614d481be330af8685a07250f4464487d45f0cc0758b94afc28c7008f6ba62f2945f92caf7dc83f2e43907d534e502d16c62e8b0d1c4d
|
7
|
+
data.tar.gz: 49eb980c909fb2490b7e0ec808c0594e84f80593baed607917cd40ed99abab69fc42daf8e7fc5d4691785a0681fa1689c9e1c5130813c0d89cb5e5d638e60cd8
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
=== 5.31.0 (2020-04-01)
|
2
|
+
|
3
|
+
* Fix alter_table drop_constraint :primary_key option on SQLite for non-integer primary keys (jeremyevans)
|
4
|
+
|
5
|
+
* Add skip_saving_columns plugin, which supports columns to skip when saving, and skips generated columns by default (joeosburn, jeremyevans) (#1681, #1682)
|
6
|
+
|
7
|
+
* Add support for creating partitioned tables in PostgreSQL 10+ using :partition_by and :partition_of options (jeremyevans)
|
8
|
+
|
9
|
+
* Dump generated columns as generated columns when using the schema_dumper with :same_db option on PostgreSQL 12+ (jeremyevans) (#1680)
|
10
|
+
|
11
|
+
* Ignore defaults for generated columns by default when using the schema dumper (jeremyevans) (#1680)
|
12
|
+
|
13
|
+
* Include generated columns in schema on SQLite 3.31+ (jeremyevans)
|
14
|
+
|
15
|
+
* Add :generated schema entry on PostgreSQL 12+ and SQLite 3.31+ for whether the columns is generated (jeremyevans)
|
16
|
+
|
17
|
+
* Add association_lazy_eager_option plugin for supporting :eager option for association method (jeremyevans)
|
18
|
+
|
19
|
+
* Add forbid_lazy_load plugin for forbidding lazy loading of associations, to help find N+1 issues (jeremyevans)
|
20
|
+
|
1
21
|
=== 5.30.0 (2020-03-01)
|
2
22
|
|
3
23
|
* Remove specs and old release notes from the gem to reduce gem size by over 40% (jeremyevans)
|
data/README.rdoc
CHANGED
@@ -894,7 +894,7 @@ in the most current release.
|
|
894
894
|
|
895
895
|
Sequel fully supports the currently supported versions of Ruby (MRI) and JRuby. It may
|
896
896
|
support unsupported versions of Ruby or JRuby, but such support may be dropped in any
|
897
|
-
minor version
|
897
|
+
minor version if keeping it becomes a support issue. The minimum Ruby version
|
898
898
|
required to run the current version of Sequel is 1.9.2.
|
899
899
|
|
900
900
|
== Maintainer
|
data/doc/postgresql.rdoc
CHANGED
@@ -139,6 +139,77 @@ conversion via a USING clause, and Sequel supports this using the <tt>:using</tt
|
|
139
139
|
# ALTER TABLE "table" ALTER COLUMN "unix_time" TYPE timestamp
|
140
140
|
# USING (CAST('epoch' AS timestamp) + (CAST('1 second' AS interval) * "unix_time"))
|
141
141
|
|
142
|
+
=== Creating Partitioned Tables
|
143
|
+
|
144
|
+
PostgreSQL allows marking tables as partitioned tables, and adding partitions to such tables. Sequel
|
145
|
+
offers support for this. You can create a partitioned table using the +:partition_by+ option and
|
146
|
+
+:partition_type+ options (the default partition type is range partitioning):
|
147
|
+
|
148
|
+
DB.create_table(:table1, partition_by: :column, partition_type: :range) do
|
149
|
+
Integer :id
|
150
|
+
Date :column
|
151
|
+
end
|
152
|
+
|
153
|
+
DB.create_table(:table2, partition_by: :column, partition_type: :list) do
|
154
|
+
Integer :id
|
155
|
+
String :column
|
156
|
+
end
|
157
|
+
|
158
|
+
DB.create_table(:table3, partition_by: :column, partition_type: :hash) do
|
159
|
+
Integer :id
|
160
|
+
Integer :column
|
161
|
+
end
|
162
|
+
|
163
|
+
To add partitions of other tables, you use the +:partition_of+ option. This option will use
|
164
|
+
a custom DSL specific to partitioning other tables. For range partitioning, you can use the
|
165
|
+
+from+ and +to+ methods to specify the inclusive beginning and exclusive ending of the
|
166
|
+
range of the partition. You can call the +minvalue+ and +maxvalue+ methods to get the minimum
|
167
|
+
and maximum values for the column(s) in the range, useful as arguments to +from+ and +to+:
|
168
|
+
|
169
|
+
DB.create_table(:table1a, partition_of: :table1) do
|
170
|
+
from minvalue
|
171
|
+
to 0
|
172
|
+
end
|
173
|
+
DB.create_table(:table1b, partition_of: :table1) do
|
174
|
+
from 0
|
175
|
+
to 100
|
176
|
+
end
|
177
|
+
DB.create_table(:table1c, partition_of: :table1) do
|
178
|
+
from 100
|
179
|
+
to maxvalue
|
180
|
+
end
|
181
|
+
|
182
|
+
For list partitioning, you use the +values_in+ method. You can also use the +default+ method
|
183
|
+
to mark a partition as the default partition:
|
184
|
+
|
185
|
+
DB.create_table(:table2a, partition_of: :table2) do
|
186
|
+
values_in 1, 2, 3
|
187
|
+
end
|
188
|
+
DB.create_table(:table2b, partition_of: :table2) do
|
189
|
+
values_in 4, 5, 6
|
190
|
+
end
|
191
|
+
DB.create_table(:table2c, partition_of: :table2) do
|
192
|
+
default
|
193
|
+
end
|
194
|
+
|
195
|
+
For hash partitioning, you use the +modulus+ and +remainder+ methods:
|
196
|
+
|
197
|
+
DB.create_table(:table3a, partition_of: :table3) do
|
198
|
+
modulus 3
|
199
|
+
remainder 0
|
200
|
+
end
|
201
|
+
DB.create_table(:table3b, partition_of: :table3) do
|
202
|
+
modulus 3
|
203
|
+
remainder 1
|
204
|
+
end
|
205
|
+
DB.create_table(:table3c, partition_of: :table3) do
|
206
|
+
modulus 3
|
207
|
+
remainder 2
|
208
|
+
end
|
209
|
+
|
210
|
+
There is currently no support for using custom column or table constraints in partitions of
|
211
|
+
other tables. Support may be added in the future.
|
212
|
+
|
142
213
|
=== Creating Unlogged Tables
|
143
214
|
|
144
215
|
PostgreSQL allows users to create unlogged tables, which are faster but not crash safe. Sequel
|
@@ -0,0 +1,148 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A forbid_lazy_load plugin has been added to forbid the lazy loading
|
4
|
+
of model associations if the current object was retreived with other
|
5
|
+
objects. This plugin helps detect N+1 query issues. This plugin
|
6
|
+
will raise an error if a lazy load is detected in such cases:
|
7
|
+
|
8
|
+
Album.plugin :forbid_lazy_load
|
9
|
+
Album.one_to_many :tracks
|
10
|
+
|
11
|
+
Album.each do |album|
|
12
|
+
album.tracks
|
13
|
+
# Could be N+1, raises Sequel::Plugins::ForbidLazyLoad::Error
|
14
|
+
end
|
15
|
+
|
16
|
+
Album.first.tracks
|
17
|
+
# Could not be N+1, no error raised
|
18
|
+
|
19
|
+
The forbid_lazy_load plugin is designed to be loaded into the base
|
20
|
+
model class (generally Sequel::Model), and can be loaded only in
|
21
|
+
test mode, or only in certain test mode configurations, so that it
|
22
|
+
does not have any production performance impact.
|
23
|
+
|
24
|
+
Note that an alternative approach that Sequel has supported for many
|
25
|
+
years is the tactical_eager_loading plugin, which automatically
|
26
|
+
eager loads when an N+1 query issue is detected.
|
27
|
+
|
28
|
+
* An association_lazy_eager_option plugin has been added which supports
|
29
|
+
the :eager option for the association method. If the association has
|
30
|
+
not been loaded, this eagerly loads the associations specified by the
|
31
|
+
:eager option when loading the association. If the association has
|
32
|
+
already been loaded, this option is ignored, with the assumption that
|
33
|
+
whatever loaded the association already used the correct eager
|
34
|
+
loading. Example:
|
35
|
+
|
36
|
+
Album.plugin :association_lazy_eager_option
|
37
|
+
Album.one_to_many :tracks
|
38
|
+
Track.many_to_one :artist
|
39
|
+
|
40
|
+
album = Album.first
|
41
|
+
album.tracks(:eager=>:artist)
|
42
|
+
# Loads tracks for album, then artist for each track (2 queries)
|
43
|
+
|
44
|
+
album.tracks(:eager=>:artist)
|
45
|
+
# No query issued as association is cached
|
46
|
+
|
47
|
+
You could previously have similar behavior for uncached associations
|
48
|
+
by passing a block to the association method and calling eager on
|
49
|
+
the yielded dataset. However, that would ignore any cached
|
50
|
+
association, causing redundant loading of the association in such
|
51
|
+
cases.
|
52
|
+
|
53
|
+
* On PostgreSQL 10+, creating partitioned tables and partitions of
|
54
|
+
other tables is now supported.
|
55
|
+
|
56
|
+
To create a partitioned table, use the :partition_by option:
|
57
|
+
|
58
|
+
DB.create_table(:table1, partition_by: :date_column,
|
59
|
+
partition_type: :range) do
|
60
|
+
Integer :id
|
61
|
+
Date :date_column
|
62
|
+
end
|
63
|
+
|
64
|
+
DB.create_table(:table2, partition_by: :string_column,
|
65
|
+
partition_type: :list) do
|
66
|
+
Integer :id
|
67
|
+
String :string_column
|
68
|
+
end
|
69
|
+
|
70
|
+
DB.create_table(:table3, partition_by: :int_column,
|
71
|
+
partition_type: :hash) do
|
72
|
+
Integer :id
|
73
|
+
Integer :int_column
|
74
|
+
end
|
75
|
+
|
76
|
+
To add partitions of other tables, use the :partition_of option.
|
77
|
+
This option will use a custom DSL specific to partitions of other
|
78
|
+
tables.
|
79
|
+
|
80
|
+
For range partitioning, you can use the from and to methods to
|
81
|
+
specify the inclusive beginning and exclusive ending of the range
|
82
|
+
of the partition. You can call the minvalue and maxvalue methods
|
83
|
+
to get the minimum and maximum values for the column(s) in the
|
84
|
+
range, useful as arguments to from and to:
|
85
|
+
|
86
|
+
DB.create_table(:table1a, partition_of: :table1) do
|
87
|
+
from minvalue
|
88
|
+
to 0
|
89
|
+
end
|
90
|
+
DB.create_table(:table1b, partition_of: :table1) do
|
91
|
+
from 0
|
92
|
+
to 100
|
93
|
+
end
|
94
|
+
DB.create_table(:table1c, partition_of: :table1) do
|
95
|
+
from 100
|
96
|
+
to maxvalue
|
97
|
+
end
|
98
|
+
|
99
|
+
For list partitioning, you use the values_in method. You can also
|
100
|
+
use the default method to mark a partition as the default partition:
|
101
|
+
|
102
|
+
DB.create_table(:table2a, partition_of: :table2) do
|
103
|
+
values_in 1, 2, 3
|
104
|
+
end
|
105
|
+
DB.create_table(:table2b, partition_of: :table2) do
|
106
|
+
values_in 4, 5, 6
|
107
|
+
end
|
108
|
+
DB.create_table(:table2c, partition_of: :table2) do
|
109
|
+
default
|
110
|
+
end
|
111
|
+
|
112
|
+
For hash partitioning, you use the modulus and remainder methods:
|
113
|
+
|
114
|
+
DB.create_table(:table3a, partition_of: :table3) do
|
115
|
+
modulus 3
|
116
|
+
remainder 0
|
117
|
+
end
|
118
|
+
DB.create_table(:table3b, partition_of: :table3) do
|
119
|
+
modulus 3
|
120
|
+
remainder 1
|
121
|
+
end
|
122
|
+
DB.create_table(:table3c, partition_of: :table3) do
|
123
|
+
modulus 3
|
124
|
+
remainder 2
|
125
|
+
end
|
126
|
+
|
127
|
+
* On PostgreSQL 12+ and SQLite 3.31+, column schema hashes now have
|
128
|
+
a :generated entry for whether the column is a generated column.
|
129
|
+
|
130
|
+
* The schema_dumper extension now dumps generated columns correctly
|
131
|
+
when using the :same_db option on PostgreSQL 12+.
|
132
|
+
|
133
|
+
* A skip_saving_columns plugin has been added. This allows skipping
|
134
|
+
saving of specific columns for the model. By default, it skips
|
135
|
+
saving of generated columns, but you can customize the columns
|
136
|
+
that it skips:
|
137
|
+
|
138
|
+
Album.plugin :skip_saving_columns
|
139
|
+
Album.skip_saving_columns = [:some_column]
|
140
|
+
|
141
|
+
= Other Improvements
|
142
|
+
|
143
|
+
* The alter_table drop_constraint :primary_key option on SQLite now
|
144
|
+
works correctly for non-integer primary keys.
|
145
|
+
|
146
|
+
* When an error is raised due to an irreversible migration, the error
|
147
|
+
message now includes the file containing the migration for easier
|
148
|
+
debugging.
|
data/doc/testing.rdoc
CHANGED
@@ -168,7 +168,6 @@ SEQUEL_INTEGER64 :: Use the integer64 extension when running the adapter or inte
|
|
168
168
|
SEQUEL_MODEL_PREPARED_STATEMENTS :: Use the prepared_statements plugin when running the specs
|
169
169
|
SEQUEL_MODEL_THROW_FAILURES :: Use the throw_failures plugin when running the specs
|
170
170
|
SEQUEL_NO_CACHE_ASSOCIATIONS :: Don't cache association metadata when running the specs
|
171
|
-
SEQUEL_NO_CHECK_SQLS :: Don't check for specific SQL syntax when running the specs
|
172
171
|
SEQUEL_CHECK_PENDING :: Try running all specs (note, can cause lockups for some adapters), and raise errors for skipped specs that don't fail
|
173
172
|
SEQUEL_NO_PENDING :: Don't skip any specs, try running all specs (note, can cause lockups for some adapters)
|
174
173
|
SEQUEL_PG_TIMESTAMPTZ :: Use the pg_timestamptz extension when running the postgres specs
|
@@ -134,6 +134,96 @@ module Sequel
|
|
134
134
|
end
|
135
135
|
end
|
136
136
|
|
137
|
+
# Generator used for creating tables that are partitions of other tables.
|
138
|
+
class CreatePartitionOfTableGenerator
|
139
|
+
MINVALUE = Sequel.lit('MINVALUE').freeze
|
140
|
+
MAXVALUE = Sequel.lit('MAXVALUE').freeze
|
141
|
+
|
142
|
+
def initialize(&block)
|
143
|
+
instance_exec(&block)
|
144
|
+
end
|
145
|
+
|
146
|
+
# The minimum value of the data type used in range partitions, useful
|
147
|
+
# as an argument to #from.
|
148
|
+
def minvalue
|
149
|
+
MINVALUE
|
150
|
+
end
|
151
|
+
|
152
|
+
# The minimum value of the data type used in range partitions, useful
|
153
|
+
# as an argument to #to.
|
154
|
+
def maxvalue
|
155
|
+
MAXVALUE
|
156
|
+
end
|
157
|
+
|
158
|
+
# Assumes range partitioning, sets the inclusive minimum value of the range for
|
159
|
+
# this partition.
|
160
|
+
def from(*v)
|
161
|
+
@from = v
|
162
|
+
end
|
163
|
+
|
164
|
+
# Assumes range partitioning, sets the exclusive maximum value of the range for
|
165
|
+
# this partition.
|
166
|
+
def to(*v)
|
167
|
+
@to = v
|
168
|
+
end
|
169
|
+
|
170
|
+
# Assumes list partitioning, sets the values to be included in this partition.
|
171
|
+
def values_in(*v)
|
172
|
+
@in = v
|
173
|
+
end
|
174
|
+
|
175
|
+
# Assumes hash partitioning, sets the modulus for this parition.
|
176
|
+
def modulus(v)
|
177
|
+
@modulus = v
|
178
|
+
end
|
179
|
+
|
180
|
+
# Assumes hash partitioning, sets the remainder for this parition.
|
181
|
+
def remainder(v)
|
182
|
+
@remainder = v
|
183
|
+
end
|
184
|
+
|
185
|
+
# Sets that this is a default partition, where values not in other partitions
|
186
|
+
# are stored.
|
187
|
+
def default
|
188
|
+
@default = true
|
189
|
+
end
|
190
|
+
|
191
|
+
# The from and to values of this partition for a range partition.
|
192
|
+
def range
|
193
|
+
[@from, @to]
|
194
|
+
end
|
195
|
+
|
196
|
+
# The values to include in this partition for a list partition.
|
197
|
+
def list
|
198
|
+
@in
|
199
|
+
end
|
200
|
+
|
201
|
+
# The modulus and remainder to use for this partition for a hash partition.
|
202
|
+
def hash_values
|
203
|
+
[@modulus, @remainder]
|
204
|
+
end
|
205
|
+
|
206
|
+
# Determine the appropriate partition type for this partition by which methods
|
207
|
+
# were called on it.
|
208
|
+
def partition_type
|
209
|
+
raise Error, "Unable to determine partition type, multiple different partitioning methods called" if [@from || @to, @list, @modulus || @remainder, @default].compact.length > 1
|
210
|
+
|
211
|
+
if @from || @to
|
212
|
+
raise Error, "must call both from and to when creating a partition of a table if calling either" unless @from && @to
|
213
|
+
:range
|
214
|
+
elsif @in
|
215
|
+
:list
|
216
|
+
elsif @modulus || @remainder
|
217
|
+
raise Error, "must call both modulus and remainder when creating a partition of a table if calling either" unless @modulus && @remainder
|
218
|
+
:hash
|
219
|
+
elsif @default
|
220
|
+
:default
|
221
|
+
else
|
222
|
+
raise Error, "unable to determine partition type, no partitioning methods called"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
137
227
|
# Error raised when Sequel determines a PostgreSQL exclusion constraint has been violated.
|
138
228
|
class ExclusionConstraintViolation < Sequel::ConstraintViolation; end
|
139
229
|
|
@@ -359,6 +449,16 @@ module Sequel
|
|
359
449
|
self << create_schema_sql(name, opts)
|
360
450
|
end
|
361
451
|
|
452
|
+
# Support partitions of tables using the :partition_of option.
|
453
|
+
def create_table(name, options=OPTS, &block)
|
454
|
+
if options[:partition_of]
|
455
|
+
create_partition_of_table_from_generator(name, CreatePartitionOfTableGenerator.new(&block), options)
|
456
|
+
return
|
457
|
+
end
|
458
|
+
|
459
|
+
super
|
460
|
+
end
|
461
|
+
|
362
462
|
# Create a trigger in the database. Arguments:
|
363
463
|
# table :: the table on which this trigger operates
|
364
464
|
# name :: the name of this trigger
|
@@ -1018,6 +1118,36 @@ module Sequel
|
|
1018
1118
|
"CREATE#{' OR REPLACE' if opts[:replace] && server_version >= 90000}#{' TRUSTED' if opts[:trusted]} LANGUAGE #{name}#{" HANDLER #{opts[:handler]}" if opts[:handler]}#{" VALIDATOR #{opts[:validator]}" if opts[:validator]}"
|
1019
1119
|
end
|
1020
1120
|
|
1121
|
+
# Create a partition of another table, used when the create_table with
|
1122
|
+
# the :partition_of option is given.
|
1123
|
+
def create_partition_of_table_from_generator(name, generator, options)
|
1124
|
+
execute_ddl(create_partition_of_table_sql(name, generator, options))
|
1125
|
+
end
|
1126
|
+
|
1127
|
+
# SQL for creating a partition of another table.
|
1128
|
+
def create_partition_of_table_sql(name, generator, options)
|
1129
|
+
sql = create_table_prefix_sql(name, options).dup
|
1130
|
+
|
1131
|
+
sql << " PARTITION OF #{quote_schema_table(options[:partition_of])}"
|
1132
|
+
|
1133
|
+
case generator.partition_type
|
1134
|
+
when :range
|
1135
|
+
from, to = generator.range
|
1136
|
+
sql << " FOR VALUES FROM #{literal(from)} TO #{literal(to)}"
|
1137
|
+
when :list
|
1138
|
+
sql << " FOR VALUES IN #{literal(generator.list)}"
|
1139
|
+
when :hash
|
1140
|
+
mod, remainder = generator.hash_values
|
1141
|
+
sql << " FOR VALUES WITH (MODULUS #{literal(mod)}, REMAINDER #{literal(remainder)})"
|
1142
|
+
when :default
|
1143
|
+
sql << " DEFAULT"
|
1144
|
+
end
|
1145
|
+
|
1146
|
+
sql << create_table_suffix_sql(name, options)
|
1147
|
+
|
1148
|
+
sql
|
1149
|
+
end
|
1150
|
+
|
1021
1151
|
# SQL for creating a schema.
|
1022
1152
|
def create_schema_sql(name, opts=OPTS)
|
1023
1153
|
"CREATE SCHEMA #{'IF NOT EXISTS ' if opts[:if_not_exists]}#{quote_identifier(name)}#{" AUTHORIZATION #{literal(opts[:owner])}" if opts[:owner]}"
|
@@ -1039,25 +1169,36 @@ module Sequel
|
|
1039
1169
|
"CREATE #{prefix_sql}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{options[:temp] ? quote_identifier(name) : quote_schema_table(name)}"
|
1040
1170
|
end
|
1041
1171
|
|
1172
|
+
# SQL for creating a table with PostgreSQL specific options
|
1042
1173
|
def create_table_sql(name, generator, options)
|
1043
|
-
|
1174
|
+
"#{super}#{create_table_suffix_sql(name, options)}"
|
1175
|
+
end
|
1176
|
+
|
1177
|
+
# Handle various PostgreSQl specific table extensions such as inheritance,
|
1178
|
+
# partitioning, tablespaces, and foreign tables.
|
1179
|
+
def create_table_suffix_sql(name, options)
|
1180
|
+
sql = String.new
|
1044
1181
|
|
1045
1182
|
if inherits = options[:inherits]
|
1046
|
-
sql
|
1183
|
+
sql << " INHERITS (#{Array(inherits).map{|t| quote_schema_table(t)}.join(', ')})"
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
if partition_by = options[:partition_by]
|
1187
|
+
sql << " PARTITION BY #{options[:partition_type]||'RANGE'} #{literal(Array(partition_by))}"
|
1047
1188
|
end
|
1048
1189
|
|
1049
1190
|
if on_commit = options[:on_commit]
|
1050
1191
|
raise(Error, "can't provide :on_commit without :temp to create_table") unless options[:temp]
|
1051
1192
|
raise(Error, "unsupported on_commit option: #{on_commit.inspect}") unless ON_COMMIT.has_key?(on_commit)
|
1052
|
-
sql
|
1193
|
+
sql << " ON COMMIT #{ON_COMMIT[on_commit]}"
|
1053
1194
|
end
|
1054
1195
|
|
1055
1196
|
if tablespace = options[:tablespace]
|
1056
|
-
sql
|
1197
|
+
sql << " TABLESPACE #{quote_identifier(tablespace)}"
|
1057
1198
|
end
|
1058
1199
|
|
1059
1200
|
if server = options[:foreign]
|
1060
|
-
sql
|
1201
|
+
sql << " SERVER #{quote_identifier(server)}"
|
1061
1202
|
if foreign_opts = options[:options]
|
1062
1203
|
sql << " OPTIONS (#{foreign_opts.map{|k, v| "#{k} #{literal(v.to_s)}"}.join(', ')})"
|
1063
1204
|
end
|
@@ -1273,6 +1414,10 @@ module Sequel
|
|
1273
1414
|
|
1274
1415
|
if server_version > 100000
|
1275
1416
|
ds = ds.select_append{pg_attribute[:attidentity]}
|
1417
|
+
|
1418
|
+
if server_version > 120000
|
1419
|
+
ds = ds.select_append{Sequel.~(pg_attribute[:attgenerated]=>'').as(:generated)}
|
1420
|
+
end
|
1276
1421
|
end
|
1277
1422
|
|
1278
1423
|
ds.map do |row|
|
@@ -184,7 +184,7 @@ module Sequel
|
|
184
184
|
|
185
185
|
# Dataset used for parsing schema
|
186
186
|
def _parse_pragma_ds(table_name, opts)
|
187
|
-
metadata_dataset.with_sql("PRAGMA
|
187
|
+
metadata_dataset.with_sql("PRAGMA table_#{'x' if sqlite_version > 33100}info(?)", input_identifier_meth(opts[:dataset]).call(table_name))
|
188
188
|
end
|
189
189
|
|
190
190
|
# Run all alter_table commands in a transaction. This is technically only
|
@@ -252,7 +252,12 @@ module Sequel
|
|
252
252
|
when :drop_constraint
|
253
253
|
case op[:type]
|
254
254
|
when :primary_key
|
255
|
-
duplicate_table(table)
|
255
|
+
duplicate_table(table) do |columns|
|
256
|
+
columns.each do |s|
|
257
|
+
s[:unique] = false if s[:primary_key]
|
258
|
+
s[:primary_key] = s[:auto_increment] = nil
|
259
|
+
end
|
260
|
+
end
|
256
261
|
when :foreign_key
|
257
262
|
if op[:columns]
|
258
263
|
duplicate_table(table, :skip_foreign_key_columns=>op[:columns])
|
@@ -420,7 +425,7 @@ module Sequel
|
|
420
425
|
unless unique_columns.empty?
|
421
426
|
unique_columns.map!{|c| quote_identifier(c)}
|
422
427
|
def_columns.each do |c|
|
423
|
-
c[:unique] = true if unique_columns.include?(quote_identifier(c[:name]))
|
428
|
+
c[:unique] = true if unique_columns.include?(quote_identifier(c[:name])) && c[:unique] != false
|
424
429
|
end
|
425
430
|
end
|
426
431
|
|
@@ -466,6 +471,15 @@ module Sequel
|
|
466
471
|
def parse_pragma(table_name, opts)
|
467
472
|
pks = 0
|
468
473
|
sch = _parse_pragma_ds(table_name, opts).map do |row|
|
474
|
+
if sqlite_version > 33100
|
475
|
+
# table_xinfo PRAGMA used, remove hidden columns
|
476
|
+
# that are not generated columns
|
477
|
+
if row[:generated] = (row.delete(:hidden) != 0)
|
478
|
+
next unless row[:type].end_with?(' GENERATED ALWAYS')
|
479
|
+
row[:type] = row[:type].sub(' GENERATED ALWAYS', '')
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
469
483
|
row.delete(:cid)
|
470
484
|
row[:allow_null] = row.delete(:notnull).to_i == 0
|
471
485
|
row[:default] = row.delete(:dflt_value)
|
@@ -482,6 +496,8 @@ module Sequel
|
|
482
496
|
row
|
483
497
|
end
|
484
498
|
|
499
|
+
sch.compact!
|
500
|
+
|
485
501
|
if pks > 1
|
486
502
|
# SQLite does not allow use of auto increment for tables
|
487
503
|
# with composite primary keys, so remove auto_increment
|
@@ -176,7 +176,7 @@ module Sequel
|
|
176
176
|
just_raise = true
|
177
177
|
end
|
178
178
|
if just_raise
|
179
|
-
Proc.new{raise Sequel::Error,
|
179
|
+
Proc.new{raise Sequel::Error, "irreversible migration method used in #{block.source_location.first}, you may need to write your own down method"}
|
180
180
|
else
|
181
181
|
actions = @actions.reverse
|
182
182
|
Proc.new do
|
@@ -40,8 +40,11 @@
|
|
40
40
|
# DB.schema(:table_name)
|
41
41
|
# [[:column_name, {:type=>:enum, :enum_values=>['value1', 'value2']}]]
|
42
42
|
#
|
43
|
-
#
|
44
|
-
#
|
43
|
+
# This extension integrates with the pg_array extension. If you plan
|
44
|
+
# to use arrays of enum types, load the pg_array extension before the
|
45
|
+
# pg_interval extension:
|
46
|
+
#
|
47
|
+
# DB.extension :pg_array, :pg_enum
|
45
48
|
#
|
46
49
|
# DB.create_table(:table_name) do
|
47
50
|
# column :column_name, 'enum_type_name[]'
|
@@ -74,6 +74,12 @@
|
|
74
74
|
#
|
75
75
|
# DB.extension :pg_hstore
|
76
76
|
#
|
77
|
+
# This extension integrates with the pg_array extension. If you plan
|
78
|
+
# to use arrays of hstore types, load the pg_array extension before the
|
79
|
+
# pg_interval extension:
|
80
|
+
#
|
81
|
+
# DB.extension :pg_array, :pg_hstore
|
82
|
+
#
|
77
83
|
# See the {schema modification guide}[rdoc-ref:doc/schema_modification.rdoc]
|
78
84
|
# for details on using hstore columns in CREATE/ALTER TABLE statements.
|
79
85
|
#
|
@@ -199,12 +199,18 @@ END_MIG
|
|
199
199
|
end
|
200
200
|
type = col_opts.delete(:type)
|
201
201
|
col_opts.delete(:size) if col_opts[:size].nil?
|
202
|
-
|
203
|
-
|
202
|
+
if schema[:generated]
|
203
|
+
if options[:same_db] && database_type == :postgres
|
204
|
+
col_opts[:generated_always_as] = column_schema_to_ruby_default_fallback(schema[:default], options)
|
205
|
+
end
|
204
206
|
else
|
205
|
-
schema[:ruby_default]
|
207
|
+
col_opts[:default] = if schema[:ruby_default].nil?
|
208
|
+
column_schema_to_ruby_default_fallback(schema[:default], options)
|
209
|
+
else
|
210
|
+
schema[:ruby_default]
|
211
|
+
end
|
212
|
+
col_opts.delete(:default) if col_opts[:default].nil?
|
206
213
|
end
|
207
|
-
col_opts.delete(:default) if col_opts[:default].nil?
|
208
214
|
col_opts[:null] = false if schema[:allow_null] == false
|
209
215
|
if table = schema[:table]
|
210
216
|
[:key, :on_delete, :on_update, :deferrable].each{|f| col_opts[f] = schema[f] if schema[f]}
|
data/lib/sequel/model/base.rb
CHANGED
@@ -1772,14 +1772,12 @@ module Sequel
|
|
1772
1772
|
before_update
|
1773
1773
|
columns = opts[:columns]
|
1774
1774
|
if columns.nil?
|
1775
|
-
if opts[:changed]
|
1776
|
-
|
1777
|
-
columns_updated = @values.reject{|k,v| !cc.include?(k)}
|
1778
|
-
cc.clear
|
1775
|
+
columns_updated = if opts[:changed]
|
1776
|
+
_save_update_changed_colums_hash
|
1779
1777
|
else
|
1780
|
-
|
1781
|
-
_clear_changed_columns(:update)
|
1778
|
+
_save_update_all_columns_hash
|
1782
1779
|
end
|
1780
|
+
_clear_changed_columns(:update)
|
1783
1781
|
else # update only the specified columns
|
1784
1782
|
columns = Array(columns)
|
1785
1783
|
columns_updated = @values.reject{|k, v| !columns.include?(k)}
|
@@ -1827,6 +1825,14 @@ module Sequel
|
|
1827
1825
|
v
|
1828
1826
|
end
|
1829
1827
|
|
1828
|
+
# Return a hash of values used when saving changed columns of an
|
1829
|
+
# existing object. Defaults to all of the objects current values
|
1830
|
+
# that are recorded as modified.
|
1831
|
+
def _save_update_changed_colums_hash
|
1832
|
+
cc = changed_columns
|
1833
|
+
@values.reject{|k,v| !cc.include?(k)}
|
1834
|
+
end
|
1835
|
+
|
1830
1836
|
# Validate the object if validating on save. Skips validation
|
1831
1837
|
# completely (including validation hooks) if
|
1832
1838
|
# skip_validation_on_save! has been called on the object,
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The association_lazy_eager_option plugin supports passing
|
6
|
+
# an +:eager+ option to an association method. If the related
|
7
|
+
# association is already cached, the cached version will be
|
8
|
+
# returned. If the association is not already cached, it will
|
9
|
+
# be loaded, and the value of the +:eager+ option will be used
|
10
|
+
# to perform an eager load of the given associations.
|
11
|
+
# the plural versions.
|
12
|
+
#
|
13
|
+
# With Sequel's default behavior, you can already perform an
|
14
|
+
# eager load when lazy loading using a block:
|
15
|
+
#
|
16
|
+
# obj.association{|ds| ds.eager(:nested_association)}
|
17
|
+
#
|
18
|
+
# However, this will ignore any cached version. In more
|
19
|
+
# complex software, the association may already be cached
|
20
|
+
# and have the nested association cached inside of it, and
|
21
|
+
# using this callback approach then requires 2 unnecessary
|
22
|
+
# queries. This plugin will not perform any queries if the
|
23
|
+
# association is already cached, preventing duplicate work.
|
24
|
+
# However, you should make sure that an already loaded
|
25
|
+
# association has the nested association already eagerly
|
26
|
+
# loaded.
|
27
|
+
#
|
28
|
+
# Usage:
|
29
|
+
#
|
30
|
+
# # Make all model subclasses support the :eager association
|
31
|
+
# # method option (called before loading subclasses)
|
32
|
+
# Sequel::Model.plugin :association_lazy_eager_option
|
33
|
+
#
|
34
|
+
# # Make the Album class support the :eager association
|
35
|
+
# # method option
|
36
|
+
# Album.plugin :association_lazy_eager_option
|
37
|
+
module AssociationLazyEagerOption
|
38
|
+
module InstanceMethods
|
39
|
+
# Return a dataset for the association after applying any dynamic callback.
|
40
|
+
def _associated_dataset(opts, dynamic_opts)
|
41
|
+
ds = super
|
42
|
+
|
43
|
+
if eager = dynamic_opts[:eager]
|
44
|
+
ds = ds.eager(eager)
|
45
|
+
end
|
46
|
+
|
47
|
+
ds
|
48
|
+
end
|
49
|
+
|
50
|
+
# A placeholder literalizer that can be used to load the association, or nil to not use one.
|
51
|
+
def _associated_object_loader(opts, dynamic_opts)
|
52
|
+
return if dynamic_opts[:eager]
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
# Whether to use a simple primary key lookup on the associated class when loading.
|
57
|
+
def load_with_primary_key_lookup?(opts, dynamic_opts)
|
58
|
+
return false if dynamic_opts[:eager]
|
59
|
+
super
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,214 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The forbid_lazy_load plugin forbids lazy loading of associations
|
6
|
+
# for objects in cases where the object wasn't loaded with a
|
7
|
+
# method that only returns a single object.
|
8
|
+
#
|
9
|
+
# The main reason for doing this is it makes it easier to detect
|
10
|
+
# N+1 query issues. Note that Sequel also offers a
|
11
|
+
# tactical_eager_loading plugin which will automatically eagerly
|
12
|
+
# load associations for all objects retrived in the same query
|
13
|
+
# if any object would attempt to lazily load an association. That
|
14
|
+
# approach may be simpler if you are trying to prevent N+1 issues,
|
15
|
+
# though it does retain more objects in memory.
|
16
|
+
#
|
17
|
+
# This plugin offers multiple different ways to forbid lazy
|
18
|
+
# loading. You can forbid lazy loading associations for individual
|
19
|
+
# model instances:
|
20
|
+
#
|
21
|
+
# obj = Album[1]
|
22
|
+
# obj.forbid_lazy_load
|
23
|
+
# obj.artist # raises Sequel::Plugins::ForbidLazyLoad::Error
|
24
|
+
#
|
25
|
+
# +forbid_lazy_load+ is automatically called on instances if the
|
26
|
+
# instances are loaded via a method such as Dataset#all,
|
27
|
+
# Dataset#each, and other methods that load multiple instances
|
28
|
+
# at once. These are the cases where lazily loading associations
|
29
|
+
# for such instances can cause N+1 issues.
|
30
|
+
#
|
31
|
+
# Album.all.first.artist
|
32
|
+
# objs.first.artist # raises Sequel::Plugins::ForbidLazyLoad::Error
|
33
|
+
#
|
34
|
+
# Album.each do |obj|
|
35
|
+
# obj.artist # raises Sequel::Plugins::ForbidLazyLoad::Error
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# Album[1].artist # no error
|
39
|
+
#
|
40
|
+
# Album.first.artist # no error
|
41
|
+
#
|
42
|
+
# You can allow lazy loading associations for an instance that it
|
43
|
+
# was previously forbidden for:
|
44
|
+
#
|
45
|
+
# obj = Album.all.first
|
46
|
+
# obj.allow_lazy_load
|
47
|
+
# obj.artist # no error
|
48
|
+
#
|
49
|
+
# You can forbid lazy loading associations on a per-call basis,
|
50
|
+
# even if lazy loading of associations is allowed for the instance:
|
51
|
+
#
|
52
|
+
# obj = Album[1]
|
53
|
+
# obj.artist(forbid_lazy_load: true)
|
54
|
+
# # raises Sequel::Plugins::ForbidLazyLoad::Error
|
55
|
+
#
|
56
|
+
# This also works for allowing lazy loading associations for a
|
57
|
+
# specific association load even if it is forbidden for the instance:
|
58
|
+
#
|
59
|
+
# obj = Album.all.first
|
60
|
+
# obj.artist(forbid_lazy_load: false)
|
61
|
+
# # nothing raised
|
62
|
+
#
|
63
|
+
# You can also forbid lazy loading on a per-association basis using the
|
64
|
+
# +:forbid_lazy_load+ association option with a +true+ value:
|
65
|
+
#
|
66
|
+
# Album.many_to_one :artist, forbid_lazy_load: true
|
67
|
+
# Album[1].artist # raises Sequel::Plugins::ForbidLazyLoad::Error
|
68
|
+
#
|
69
|
+
# However, you probably don't want to do this as it will forbid any
|
70
|
+
# lazy loading of the association, even if the loading could not
|
71
|
+
# result in an N+1 issue.
|
72
|
+
#
|
73
|
+
# On the flip side, you can allow lazy loading using the
|
74
|
+
# +:forbid_lazy_load+ association option with a +false+ value:
|
75
|
+
#
|
76
|
+
# Album.many_to_one :artist, forbid_lazy_load: false
|
77
|
+
# Album.all.first.artist # no error
|
78
|
+
#
|
79
|
+
# One reason to do this is when using a plugin like static_cache
|
80
|
+
# on the associated model, where a query is not actually issued
|
81
|
+
# when doing a lazy association load. To make that particular
|
82
|
+
# case easier, this plugin makes Model.finalize_associations
|
83
|
+
# automatically set the association option if the associated
|
84
|
+
# class uses the static_cache plugin.
|
85
|
+
#
|
86
|
+
# Note that even with this plugin, there can still be N+1 issues,
|
87
|
+
# such as:
|
88
|
+
#
|
89
|
+
# Album.each do |obj| # 1 query for all albums
|
90
|
+
# Artist[obj.artist_id] # 1 query per album for each artist
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# Usage:
|
94
|
+
#
|
95
|
+
# # Make all model subclasses support forbidding lazy load
|
96
|
+
# # (called before loading subclasses)
|
97
|
+
# Sequel::Model.plugin :forbid_lazy_load
|
98
|
+
#
|
99
|
+
# # Make the Album class support forbidding lazy load
|
100
|
+
# Album.plugin :forbid_lazy_load
|
101
|
+
module ForbidLazyLoad
|
102
|
+
# Error raised when attempting to lazy load an association when
|
103
|
+
# lazy loading has been forbidden.
|
104
|
+
class Error < StandardError
|
105
|
+
end
|
106
|
+
|
107
|
+
module ClassMethods
|
108
|
+
Plugins.def_dataset_methods(self, :forbid_lazy_load)
|
109
|
+
|
110
|
+
# If the static_cache plugin is used by the associated class for
|
111
|
+
# an association, allow lazy loading that association, since the
|
112
|
+
# lazy association load will use a hash table lookup and not a query.
|
113
|
+
def allow_lazy_load_for_static_cache_associations
|
114
|
+
if defined?(::Sequel::Plugins::StaticCache::ClassMethods)
|
115
|
+
@association_reflections.each_value do |ref|
|
116
|
+
if ref.associated_class.is_a?(::Sequel::Plugins::StaticCache::ClassMethods)
|
117
|
+
ref[:forbid_lazy_load] = false
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Allow lazy loading for static cache associations before finalizing.
|
124
|
+
def finalize_associations
|
125
|
+
allow_lazy_load_for_static_cache_associations
|
126
|
+
super
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
module InstanceMethods
|
131
|
+
# Set this model instance to allow lazy loading of associations.
|
132
|
+
def allow_lazy_load
|
133
|
+
@forbid_lazy_load = false
|
134
|
+
self
|
135
|
+
end
|
136
|
+
|
137
|
+
# Set this model instance to not allow lazy loading of associations.
|
138
|
+
def forbid_lazy_load
|
139
|
+
@forbid_lazy_load = true
|
140
|
+
self
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
# Allow lazy loading for objects returned by singular associations.
|
146
|
+
def _load_associated_object(opts, dynamic_opts)
|
147
|
+
# The implementation that loads these associations does
|
148
|
+
# .all.first, which would result in the object returned being
|
149
|
+
# marked as forbidding lazy load.
|
150
|
+
obj = super
|
151
|
+
obj.allow_lazy_load if obj.is_a?(InstanceMethods)
|
152
|
+
obj
|
153
|
+
end
|
154
|
+
|
155
|
+
# Raise an Error if lazy loading has been forbidden for
|
156
|
+
# the instance, association, or call.
|
157
|
+
def _load_associated_objects(opts, dynamic_opts=OPTS)
|
158
|
+
case dynamic_opts[:forbid_lazy_load]
|
159
|
+
when false
|
160
|
+
# nothing
|
161
|
+
when nil
|
162
|
+
unless dynamic_opts[:reload]
|
163
|
+
case opts[:forbid_lazy_load]
|
164
|
+
when nil
|
165
|
+
raise Error, "lazy loading forbidden for this object (association: #{opts.inspect}, object: #{inspect})" if @forbid_lazy_load
|
166
|
+
when false
|
167
|
+
# nothing
|
168
|
+
else
|
169
|
+
raise Error, "lazy loading forbidden for this association (#{opts.inspect})"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
else
|
173
|
+
raise Error, "lazy loading forbidden for this association method call (association: #{opts.inspect})"
|
174
|
+
end
|
175
|
+
|
176
|
+
super
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
module DatasetMethods
|
181
|
+
# Mark model instances retrieved in this call as forbidding lazy loading.
|
182
|
+
def each
|
183
|
+
if row_proc
|
184
|
+
super do |obj|
|
185
|
+
obj.forbid_lazy_load if obj.is_a?(InstanceMethods)
|
186
|
+
yield obj
|
187
|
+
end
|
188
|
+
else
|
189
|
+
super
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Mark model instances retrieved in this call as forbidding lazy loading.
|
194
|
+
def with_sql_each(sql)
|
195
|
+
if row_proc
|
196
|
+
super(sql) do |obj|
|
197
|
+
obj.forbid_lazy_load if obj.is_a?(InstanceMethods)
|
198
|
+
yield obj
|
199
|
+
end
|
200
|
+
else
|
201
|
+
super
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Mark model instances retrieved in this call as allowing lazy loading.
|
206
|
+
def with_sql_first(sql)
|
207
|
+
obj = super
|
208
|
+
obj.allow_lazy_load if obj.is_a?(InstanceMethods)
|
209
|
+
obj
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The skip_saving_columms plugin allows skipping specific columns when
|
6
|
+
# saving. By default, it skips columns that the database schema
|
7
|
+
# indicates are generated columns:
|
8
|
+
#
|
9
|
+
# # Assume id column, name column, and id2 generated column
|
10
|
+
# album = Album[1]
|
11
|
+
# album.id # => 1
|
12
|
+
# album.name # => 'X'
|
13
|
+
# album.id2 # => 2
|
14
|
+
# album.save
|
15
|
+
# # UPDATE album SET name = 'X' WHERE (id = 1)
|
16
|
+
#
|
17
|
+
# You can override which columns will be skipped:
|
18
|
+
#
|
19
|
+
# Album.skip_saving_columns = [:name]
|
20
|
+
# album.save
|
21
|
+
# # UPDATE album SET id2 = 2 WHERE (id = 1)
|
22
|
+
#
|
23
|
+
# The skipping happens for all usage of Model#save and callers of it (e.g.
|
24
|
+
# Model.create, Model.update). When using the plugin, the only way to get
|
25
|
+
# it to save a column marked for skipping is to explicitly specify it:
|
26
|
+
#
|
27
|
+
# album.save(columns: [:name, :id2])
|
28
|
+
# album.save
|
29
|
+
# # UPDATE album SET name = 'X', id2 = 2 WHERE (id = 1)
|
30
|
+
#
|
31
|
+
# Usage:
|
32
|
+
#
|
33
|
+
# # Support skipping saving columns in all Sequel::Model subclasses
|
34
|
+
# # (called before loading subclasses)
|
35
|
+
# Sequel::Model.plugin :skip_saving_columns
|
36
|
+
#
|
37
|
+
# # Support skipping saving columns in the Album class
|
38
|
+
# Album.plugin :skip_saving_columns
|
39
|
+
module SkipSavingColumns
|
40
|
+
# Setup skipping of the generated columns for a model with an existing dataset.
|
41
|
+
def self.configure(mod)
|
42
|
+
mod.instance_exec do
|
43
|
+
set_skip_saving_generated_columns if @dataset
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module ClassMethods
|
48
|
+
# An array of column symbols for columns to skip when saving.
|
49
|
+
attr_reader :skip_saving_columns
|
50
|
+
|
51
|
+
# Over the default array of columns to skip. Once overridden, future
|
52
|
+
# changes to the class's dataset and future subclasses will automatically
|
53
|
+
# use these overridden columns, instead of introspecting the database schema.
|
54
|
+
def skip_saving_columns=(v)
|
55
|
+
@_skip_saving_columns_no_override = true
|
56
|
+
@skip_saving_columns = v.dup.freeze
|
57
|
+
end
|
58
|
+
|
59
|
+
Plugins.after_set_dataset(self, :set_skip_saving_generated_columns)
|
60
|
+
Plugins.inherited_instance_variables(self, :@skip_saving_columns=>:dup, :@_skip_saving_columns_no_override=>nil)
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# If the skip saving columns has not been overridden, check the database
|
65
|
+
# schema and automatically skip any generated columns.
|
66
|
+
def set_skip_saving_generated_columns
|
67
|
+
return if @_skip_saving_columns_no_override
|
68
|
+
s = []
|
69
|
+
db_schema.each do |k, v|
|
70
|
+
s << k if v[:generated]
|
71
|
+
end
|
72
|
+
@skip_saving_columns = s.freeze
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
module InstanceMethods
|
78
|
+
private
|
79
|
+
|
80
|
+
# Skip the columns the model has marked to skip when inserting.
|
81
|
+
def _insert_values
|
82
|
+
_save_removed_skipped_columns(Hash[super])
|
83
|
+
end
|
84
|
+
|
85
|
+
# Skip the columns the model has marked to skip when updating
|
86
|
+
# all columns.
|
87
|
+
def _save_update_all_columns_hash
|
88
|
+
_save_removed_skipped_columns(super)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Skip the columns the model has marked to skip when updating
|
92
|
+
# only changed columns.
|
93
|
+
def _save_update_changed_colums_hash
|
94
|
+
_save_removed_skipped_columns(super)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Remove any columns the model has marked to skip when saving.
|
98
|
+
def _save_removed_skipped_columns(hash)
|
99
|
+
model.skip_saving_columns.each do |column|
|
100
|
+
hash.delete(column)
|
101
|
+
end
|
102
|
+
|
103
|
+
hash
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/lib/sequel/version.rb
CHANGED
@@ -6,7 +6,7 @@ module Sequel
|
|
6
6
|
|
7
7
|
# The minor version of Sequel. Bumped for every non-patch level
|
8
8
|
# release, generally around once a month.
|
9
|
-
MINOR =
|
9
|
+
MINOR = 31
|
10
10
|
|
11
11
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
12
12
|
# releases that fix regressions from previous versions.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.31.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-04-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -178,6 +178,7 @@ extra_rdoc_files:
|
|
178
178
|
- doc/release_notes/5.28.0.txt
|
179
179
|
- doc/release_notes/5.29.0.txt
|
180
180
|
- doc/release_notes/5.30.0.txt
|
181
|
+
- doc/release_notes/5.31.0.txt
|
181
182
|
files:
|
182
183
|
- CHANGELOG
|
183
184
|
- MIT-LICENSE
|
@@ -229,6 +230,7 @@ files:
|
|
229
230
|
- doc/release_notes/5.29.0.txt
|
230
231
|
- doc/release_notes/5.3.0.txt
|
231
232
|
- doc/release_notes/5.30.0.txt
|
233
|
+
- doc/release_notes/5.31.0.txt
|
232
234
|
- doc/release_notes/5.4.0.txt
|
233
235
|
- doc/release_notes/5.5.0.txt
|
234
236
|
- doc/release_notes/5.6.0.txt
|
@@ -417,6 +419,7 @@ files:
|
|
417
419
|
- lib/sequel/plugins/active_model.rb
|
418
420
|
- lib/sequel/plugins/after_initialize.rb
|
419
421
|
- lib/sequel/plugins/association_dependencies.rb
|
422
|
+
- lib/sequel/plugins/association_lazy_eager_option.rb
|
420
423
|
- lib/sequel/plugins/association_multi_add_remove.rb
|
421
424
|
- lib/sequel/plugins/association_pks.rb
|
422
425
|
- lib/sequel/plugins/association_proxies.rb
|
@@ -443,6 +446,7 @@ files:
|
|
443
446
|
- lib/sequel/plugins/empty_failure_backtraces.rb
|
444
447
|
- lib/sequel/plugins/error_splitter.rb
|
445
448
|
- lib/sequel/plugins/finder.rb
|
449
|
+
- lib/sequel/plugins/forbid_lazy_load.rb
|
446
450
|
- lib/sequel/plugins/force_encoding.rb
|
447
451
|
- lib/sequel/plugins/hook_class_methods.rb
|
448
452
|
- lib/sequel/plugins/input_transformer.rb
|
@@ -471,6 +475,7 @@ files:
|
|
471
475
|
- lib/sequel/plugins/single_table_inheritance.rb
|
472
476
|
- lib/sequel/plugins/singular_table_names.rb
|
473
477
|
- lib/sequel/plugins/skip_create_refresh.rb
|
478
|
+
- lib/sequel/plugins/skip_saving_columns.rb
|
474
479
|
- lib/sequel/plugins/split_values.rb
|
475
480
|
- lib/sequel/plugins/static_cache.rb
|
476
481
|
- lib/sequel/plugins/static_cache_cache.rb
|