sequel 5.3.0 → 5.4.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 +5 -5
- data/CHANGELOG +30 -0
- data/bin/sequel +13 -0
- data/doc/cheat_sheet.rdoc +1 -0
- data/doc/dataset_filtering.rdoc +1 -1
- data/doc/querying.rdoc +8 -11
- data/doc/release_notes/5.4.0.txt +80 -0
- data/doc/testing.rdoc +2 -0
- data/lib/sequel/adapters/shared/db2.rb +6 -5
- data/lib/sequel/adapters/shared/mssql.rb +5 -8
- data/lib/sequel/adapters/shared/mysql.rb +4 -8
- data/lib/sequel/adapters/shared/oracle.rb +1 -1
- data/lib/sequel/adapters/shared/postgres.rb +5 -3
- data/lib/sequel/adapters/shared/sqlanywhere.rb +1 -6
- data/lib/sequel/adapters/shared/sqlite.rb +2 -0
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/schema_methods.rb +10 -1
- data/lib/sequel/dataset/query.rb +1 -2
- data/lib/sequel/extensions/date_arithmetic.rb +27 -10
- data/lib/sequel/extensions/datetime_parse_to_time.rb +37 -0
- data/lib/sequel/extensions/index_caching.rb +107 -0
- data/lib/sequel/extensions/null_dataset.rb +3 -1
- data/lib/sequel/extensions/pg_timestamptz.rb +26 -0
- data/lib/sequel/model/base.rb +2 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +11 -3
- data/lib/sequel/plugins/json_serializer.rb +2 -2
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +1 -1
- data/spec/adapters/spec_helper.rb +3 -0
- data/spec/adapters/sqlite_spec.rb +1 -1
- data/spec/bin_spec.rb +9 -0
- data/spec/core/connection_pool_spec.rb +2 -2
- data/spec/core/dataset_spec.rb +1 -6
- data/spec/extensions/class_table_inheritance_spec.rb +52 -2
- data/spec/extensions/date_arithmetic_spec.rb +15 -1
- data/spec/extensions/datetime_parse_to_time_spec.rb +169 -0
- data/spec/extensions/index_caching_spec.rb +66 -0
- data/spec/extensions/json_serializer_spec.rb +5 -0
- data/spec/extensions/null_dataset_spec.rb +5 -0
- data/spec/extensions/pg_extended_date_support_spec.rb +4 -0
- data/spec/extensions/pg_timestamptz_spec.rb +17 -0
- data/spec/extensions/xml_serializer_spec.rb +7 -0
- data/spec/integration/dataset_test.rb +6 -0
- data/spec/integration/prepared_statement_test.rb +1 -1
- data/spec/integration/schema_test.rb +19 -17
- data/spec/integration/spec_helper.rb +4 -0
- data/spec/model/record_spec.rb +28 -0
- metadata +11 -3
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# This switches the default parsing of strings into Time values
|
4
|
+
# from using Time.parse to using DateTime.parse.to_time. This
|
5
|
+
# fixes issues when the times being parsed have no timezone
|
6
|
+
# information, the implicit timezone for the Database instance
|
7
|
+
# is set to +:utc+, and the timestamps being used include values
|
8
|
+
# not valid in the local timezone, such as during a daylight
|
9
|
+
# savings time switch.
|
10
|
+
#
|
11
|
+
# To load the extension:
|
12
|
+
#
|
13
|
+
# Sequel.extension :datetime_parse_to_time
|
14
|
+
|
15
|
+
#
|
16
|
+
module Sequel::DateTimeParseToTime
|
17
|
+
private
|
18
|
+
|
19
|
+
# Use DateTime.parse.to_time to do the conversion if the input a string and is assumed to
|
20
|
+
# be in UTC and there is no offset information in the string.
|
21
|
+
def convert_input_timestamp(v, input_timezone)
|
22
|
+
if v.is_a?(String) && datetime_class == Time && input_timezone == :utc && !Date._parse(v).has_key?(:offset)
|
23
|
+
t = DateTime.parse(v).to_time
|
24
|
+
case application_timezone
|
25
|
+
when nil, :local
|
26
|
+
t = t.localtime
|
27
|
+
end
|
28
|
+
t
|
29
|
+
else
|
30
|
+
super
|
31
|
+
end
|
32
|
+
rescue => e
|
33
|
+
raise convert_exception_class(e, Sequel::InvalidValue)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
Sequel.extend(Sequel::DateTimeParseToTime)
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The index_caching extension adds a few methods to Sequel::Database
|
4
|
+
# that make it easy to dump information about database indexes to a file,
|
5
|
+
# and load it from that file. Loading index information from a
|
6
|
+
# dumped file is faster than parsing it from the database, so this
|
7
|
+
# can save bootup time for applications with large numbers of index.
|
8
|
+
#
|
9
|
+
# Basic usage in application code:
|
10
|
+
#
|
11
|
+
# DB = Sequel.connect('...')
|
12
|
+
# DB.extension :index_caching
|
13
|
+
# DB.load_index_cache('/path/to/index_cache.dump')
|
14
|
+
#
|
15
|
+
# # load model files
|
16
|
+
#
|
17
|
+
# Then, whenever database indicies are modified, write a new cached
|
18
|
+
# file. You can do that with <tt>bin/sequel</tt>'s -X option:
|
19
|
+
#
|
20
|
+
# bin/sequel -X /path/to/index_cache.dump postgres://...
|
21
|
+
#
|
22
|
+
# Alternatively, if you don't want to dump the index information for
|
23
|
+
# all tables, and you don't worry about race conditions, you can
|
24
|
+
# choose to use the following in your application code:
|
25
|
+
#
|
26
|
+
# DB = Sequel.connect('...')
|
27
|
+
# DB.extension :index_caching
|
28
|
+
# DB.load_index_cache?('/path/to/index_cache.dump')
|
29
|
+
#
|
30
|
+
# # load model files
|
31
|
+
#
|
32
|
+
# DB.dump_index_cache?('/path/to/index_cache.dump')
|
33
|
+
#
|
34
|
+
# With this method, you just have to delete the index dump file if
|
35
|
+
# the schema is modified, and the application will recreate it for you
|
36
|
+
# using just the tables that your models use.
|
37
|
+
#
|
38
|
+
# Note that it is up to the application to ensure that the dumped
|
39
|
+
# index cache reflects the current state of the database. Sequel
|
40
|
+
# does no checking to ensure this, as checking would take time and the
|
41
|
+
# purpose of this code is to take a shortcut.
|
42
|
+
#
|
43
|
+
# The index cache is dumped in Marshal format, since it is the fastest
|
44
|
+
# and it handles all ruby objects used in the indexes hash. Because of this,
|
45
|
+
# you should not attempt to load from an untrusted file.
|
46
|
+
#
|
47
|
+
# Related module: Sequel::IndexCaching
|
48
|
+
|
49
|
+
#
|
50
|
+
module Sequel
|
51
|
+
module IndexCaching
|
52
|
+
# Set index cache to the empty hash.
|
53
|
+
def self.extended(db)
|
54
|
+
db.instance_variable_set(:@indexes, {})
|
55
|
+
end
|
56
|
+
|
57
|
+
# Remove the index cache for the given schema name
|
58
|
+
def remove_cached_schema(table)
|
59
|
+
k = quote_schema_table(table)
|
60
|
+
Sequel.synchronize{@indexes.delete(k)}
|
61
|
+
super
|
62
|
+
end
|
63
|
+
|
64
|
+
# Dump the index cache to the filename given in Marshal format.
|
65
|
+
def dump_index_cache(file)
|
66
|
+
File.open(file, 'wb'){|f| f.write(Marshal.dump(@indexes))}
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
# Dump the index cache to the filename given unless the file
|
71
|
+
# already exists.
|
72
|
+
def dump_index_cache?(file)
|
73
|
+
dump_index_cache(file) unless File.exist?(file)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Replace the index cache with the data from the given file, which
|
77
|
+
# should be in Marshal format.
|
78
|
+
def load_index_cache(file)
|
79
|
+
@indexes = Marshal.load(File.read(file))
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
|
83
|
+
# Replace the index cache with the data from the given file if the
|
84
|
+
# file exists.
|
85
|
+
def load_index_cache?(file)
|
86
|
+
load_index_cache(file) if File.exist?(file)
|
87
|
+
end
|
88
|
+
|
89
|
+
# If no options are provided and there is cached index information for
|
90
|
+
# the table, return the cached information instead of querying the
|
91
|
+
# database.
|
92
|
+
def indexes(table, opts=OPTS)
|
93
|
+
return super unless opts.empty?
|
94
|
+
|
95
|
+
quoted_name = literal(table)
|
96
|
+
if v = Sequel.synchronize{@indexes[quoted_name]}
|
97
|
+
return v
|
98
|
+
end
|
99
|
+
|
100
|
+
result = super
|
101
|
+
Sequel.synchronize{@indexes[quoted_name] = result}
|
102
|
+
result
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
Database.register_extension(:index_caching, IndexCaching)
|
107
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The pg_timestamptz extension changes the default timestamp
|
4
|
+
# type for the database to be +timestamptz+ (+timestamp with time zone+)
|
5
|
+
# instead of +timestamp+ (+timestamp without time zone+). This is
|
6
|
+
# recommended if you are dealing with multiple timezones in your application.
|
7
|
+
#
|
8
|
+
# To load the extension into the database:
|
9
|
+
#
|
10
|
+
# DB.extension :pg_timestamptz
|
11
|
+
#
|
12
|
+
# Related module: Sequel::Postgres::Timestamptz
|
13
|
+
|
14
|
+
#
|
15
|
+
module Sequel
|
16
|
+
module Postgres
|
17
|
+
module Timestamptz
|
18
|
+
# Use timestamptz by default for generic timestamp value.
|
19
|
+
def type_literal_generic_datetime(column)
|
20
|
+
:timestamptz
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Database.register_extension(:pg_timestamptz, Postgres::Timestamptz)
|
26
|
+
end
|
data/lib/sequel/model/base.rb
CHANGED
@@ -1635,8 +1635,8 @@ module Sequel
|
|
1635
1635
|
# the record should be refreshed from the database.
|
1636
1636
|
def _insert
|
1637
1637
|
ds = _insert_dataset
|
1638
|
-
if _use_insert_select?(ds) && (h = _insert_select_raw(ds))
|
1639
|
-
_save_set_values(h)
|
1638
|
+
if _use_insert_select?(ds) && !(h = _insert_select_raw(ds)).nil?
|
1639
|
+
_save_set_values(h) if h
|
1640
1640
|
nil
|
1641
1641
|
else
|
1642
1642
|
iid = _insert_raw(ds)
|
@@ -196,6 +196,8 @@ module Sequel
|
|
196
196
|
# :key_chooser :: proc returning key for the provided model instance
|
197
197
|
# :table_map :: Hash with class name symbols keys mapping to table name symbol values.
|
198
198
|
# Overrides implicit table names.
|
199
|
+
# :ignore_subclass_columns :: Array with column names as symbols that are ignored
|
200
|
+
# on all sub-classes.
|
199
201
|
def self.configure(model, opts = OPTS)
|
200
202
|
SingleTableInheritance.configure model, opts[:key], opts
|
201
203
|
|
@@ -206,6 +208,7 @@ module Sequel
|
|
206
208
|
@cti_table_columns = columns
|
207
209
|
@cti_table_map = opts[:table_map] || {}
|
208
210
|
@cti_alias = opts[:alias] || @dataset.first_source
|
211
|
+
@cti_ignore_subclass_columns = opts[:ignore_subclass_columns] || []
|
209
212
|
end
|
210
213
|
end
|
211
214
|
|
@@ -232,17 +235,22 @@ module Sequel
|
|
232
235
|
# the implicit naming is incorrect.
|
233
236
|
attr_reader :cti_table_map
|
234
237
|
|
238
|
+
# An array of columns that may be duplicated in sub-classes. The
|
239
|
+
# primary key column is always allowed to be duplicated
|
240
|
+
attr_reader :cti_ignore_subclass_columns
|
241
|
+
|
235
242
|
# Freeze CTI information when freezing model class.
|
236
243
|
def freeze
|
237
244
|
@cti_models.freeze
|
238
245
|
@cti_tables.freeze
|
239
246
|
@cti_table_columns.freeze
|
240
247
|
@cti_table_map.freeze
|
248
|
+
@cti_ignore_subclass_columns.freeze
|
241
249
|
|
242
250
|
super
|
243
251
|
end
|
244
252
|
|
245
|
-
Plugins.inherited_instance_variables(self, :@cti_models=>nil, :@cti_tables=>nil, :@cti_table_columns=>nil, :@cti_instance_dataset=>nil, :@cti_table_map=>nil, :@cti_alias=>nil)
|
253
|
+
Plugins.inherited_instance_variables(self, :@cti_models=>nil, :@cti_tables=>nil, :@cti_table_columns=>nil, :@cti_instance_dataset=>nil, :@cti_table_map=>nil, :@cti_alias=>nil, :@cti_ignore_subclass_columns=>nil)
|
246
254
|
|
247
255
|
def inherited(subclass)
|
248
256
|
ds = sti_dataset
|
@@ -273,10 +281,10 @@ module Sequel
|
|
273
281
|
if cti_tables.length == 1
|
274
282
|
ds = ds.select(*self.columns.map{|cc| Sequel.qualify(cti_table_name, Sequel.identifier(cc))})
|
275
283
|
end
|
276
|
-
cols = columns - [pk]
|
284
|
+
cols = (columns - [pk]) - cti_ignore_subclass_columns
|
277
285
|
dup_cols = cols & ds.columns
|
278
286
|
unless dup_cols.empty?
|
279
|
-
raise Error, "class_table_inheritance with duplicate column names (other than the primary key column) is not supported, make sure tables have unique column names"
|
287
|
+
raise Error, "class_table_inheritance with duplicate column names (other than the primary key column) is not supported, make sure tables have unique column names (duplicate columns: #{dup_cols}). If this is desired, specify these columns in the :ignore_subclass_columns option when initializing the plugin"
|
280
288
|
end
|
281
289
|
sel_app = cols.map{|cc| Sequel.qualify(table, Sequel.identifier(cc))}
|
282
290
|
@sti_dataset = ds = ds.join(table, pk=>pk).select_append(*sel_app)
|
@@ -406,7 +406,7 @@ module Sequel
|
|
406
406
|
end
|
407
407
|
end
|
408
408
|
|
409
|
-
res = if row_proc
|
409
|
+
res = if row_proc || @opts[:eager_graph]
|
410
410
|
array = if opts[:array]
|
411
411
|
opts = opts.dup
|
412
412
|
opts.delete(:array)
|
@@ -414,7 +414,7 @@ module Sequel
|
|
414
414
|
all
|
415
415
|
end
|
416
416
|
array.map{|obj| Literal.new(Sequel.object_to_json(obj, opts, &opts[:instance_block]))}
|
417
|
-
|
417
|
+
else
|
418
418
|
all
|
419
419
|
end
|
420
420
|
|
@@ -389,7 +389,7 @@ module Sequel
|
|
389
389
|
# as well as the :array_root_name option for specifying the name of
|
390
390
|
# the root node that contains the nodes for all of the instances.
|
391
391
|
def to_xml(opts=OPTS)
|
392
|
-
raise(Sequel::Error, "Dataset#to_xml") unless row_proc
|
392
|
+
raise(Sequel::Error, "Dataset#to_xml") unless row_proc || @opts[:eager_graph]
|
393
393
|
x = model.xml_builder(opts)
|
394
394
|
name_proc = model.xml_serialize_name_proc(opts)
|
395
395
|
array = if opts[:array]
|
data/lib/sequel/version.rb
CHANGED
@@ -5,7 +5,7 @@ module Sequel
|
|
5
5
|
MAJOR = 5
|
6
6
|
# The minor version of Sequel. Bumped for every non-patch level
|
7
7
|
# release, generally around once a month.
|
8
|
-
MINOR =
|
8
|
+
MINOR = 4
|
9
9
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
10
10
|
# releases that fix regressions from previous versions.
|
11
11
|
TINY = 0
|
@@ -511,7 +511,7 @@ describe "A PostgreSQL dataset" do
|
|
511
511
|
|
512
512
|
it "should support :using when altering a column's type" do
|
513
513
|
@db.create_table!(:atest){Integer :t}
|
514
|
-
@db[:atest].insert(
|
514
|
+
@db[:atest].insert(1262404000)
|
515
515
|
@db.alter_table(:atest){set_column_type :t, Time, :using=>Sequel.cast('epoch', Time) + Sequel.cast('1 second', :interval) * :t}
|
516
516
|
@db[:atest].get(Sequel.extract(:year, :t)).must_equal 2010
|
517
517
|
end
|
@@ -32,6 +32,9 @@ end
|
|
32
32
|
IDENTIFIER_MANGLING = !!ENV['SEQUEL_IDENTIFIER_MANGLING'] unless defined?(IDENTIFIER_MANGLING)
|
33
33
|
DB.extension(:identifier_mangling) if IDENTIFIER_MANGLING
|
34
34
|
|
35
|
+
DB.extension(:pg_timestamptz) if ENV['SEQUEL_PG_TIMESTAMPTZ']
|
36
|
+
DB.extension :index_caching if ENV['SEQUEL_INDEX_CACHING']
|
37
|
+
|
35
38
|
if dch = ENV['SEQUEL_DUPLICATE_COLUMNS_HANDLER']
|
36
39
|
DB.extension :duplicate_columns_handler
|
37
40
|
DB.opts[:on_duplicate_columns] = dch.to_sym unless dch.empty?
|
@@ -554,7 +554,7 @@ describe "A SQLite database" do
|
|
554
554
|
@db.add_index :test3, :b
|
555
555
|
@db.add_index :test3, [:b, :a]
|
556
556
|
@db.drop_column :test3, :b
|
557
|
-
@db.indexes(:test3).must_equal(:
|
557
|
+
@db.indexes(:test3)[:test3_a_index].must_equal(:unique=>false, :columns=>[:a])
|
558
558
|
end
|
559
559
|
|
560
560
|
it "should have support for various #transaction modes" do
|
data/spec/bin_spec.rb
CHANGED
@@ -183,6 +183,15 @@ END
|
|
183
183
|
Marshal.load(File.read(TMP_FILE)).must_equal("`a`"=>[[:a, {:type=>:integer, :db_type=>"integer", :ruby_default=>nil, :allow_null=>true, :default=>nil, :primary_key=>false}]])
|
184
184
|
end
|
185
185
|
|
186
|
+
it "-X should dump the index cache" do
|
187
|
+
bin(:args=>"-X #{TMP_FILE}").must_equal ''
|
188
|
+
Marshal.load(File.read(TMP_FILE)).must_equal({})
|
189
|
+
DB.create_table(:a){Integer :id}
|
190
|
+
DB.create_table(:b){Integer :b, index: {name: "idx_test", unique: true}}
|
191
|
+
bin(:args=>"-X #{TMP_FILE}").must_equal ''
|
192
|
+
Marshal.load(File.read(TMP_FILE)).must_equal("`a`"=>{}, "`b`"=>{:idx_test=>{:unique=>true, :columns=>[:b]}})
|
193
|
+
end
|
194
|
+
|
186
195
|
it "-t should output full backtraces on error" do
|
187
196
|
bin(:args=>'-c "lambda{lambda{lambda{raise \'foo\'}.call}.call}.call"', :stderr=>true).count("\n").must_be :<, 3
|
188
197
|
bin(:args=>'-t -c "lambda{lambda{lambda{raise \'foo\'}.call}.call}.call"', :stderr=>true).count("\n").must_be :>, 3
|
@@ -510,7 +510,7 @@ ThreadedConnectionPoolSpecs = shared_description do
|
|
510
510
|
@pool.hold{|cc| cc.must_equal c}
|
511
511
|
@pool.hold do |cc|
|
512
512
|
cc.must_equal c
|
513
|
-
Thread.new{@pool.hold{|cc2| cc2.must_equal c2}}
|
513
|
+
Thread.new{@pool.hold{|cc2| _(cc2).must_equal c2}}.join
|
514
514
|
end
|
515
515
|
end
|
516
516
|
|
@@ -523,7 +523,7 @@ ThreadedConnectionPoolSpecs = shared_description do
|
|
523
523
|
@pool.hold{|cc| cc.must_equal c}
|
524
524
|
@pool.hold do |cc|
|
525
525
|
cc.must_equal c2
|
526
|
-
Thread.new{@pool.hold{|cc2| cc2.must_equal c}}
|
526
|
+
Thread.new{@pool.hold{|cc2| _(cc2).must_equal c}}.join
|
527
527
|
end
|
528
528
|
end
|
529
529
|
|
data/spec/core/dataset_spec.rb
CHANGED
@@ -378,14 +378,9 @@ describe "Dataset#where" do
|
|
378
378
|
@dataset.where(nil).sql.must_equal "SELECT * FROM test WHERE NULL"
|
379
379
|
end
|
380
380
|
|
381
|
-
deprecated "should handle nil block result has no existing filter" do
|
382
|
-
@dataset.where{nil}.sql.must_equal "SELECT * FROM test"
|
383
|
-
end
|
384
|
-
|
385
|
-
# SEQUEL54
|
386
381
|
it "should handle nil block result has no existing filter" do
|
387
382
|
@dataset.where{nil}.sql.must_equal "SELECT * FROM test WHERE NULL"
|
388
|
-
end
|
383
|
+
end
|
389
384
|
|
390
385
|
it "should just clone if given an empty array or hash argument" do
|
391
386
|
@dataset.where({}).sql.must_equal @dataset.sql
|
@@ -6,7 +6,7 @@ describe "class_table_inheritance plugin" do
|
|
6
6
|
def @db.supports_schema_parsing?() true end
|
7
7
|
def @db.schema(table, opts={})
|
8
8
|
{:employees=>[[:id, {:primary_key=>true, :type=>:integer}], [:name, {:type=>:string}], [:kind, {:type=>:string}]],
|
9
|
-
:managers=>[[:id, {:type=>:integer}], [:num_staff, {:type=>:integer}]],
|
9
|
+
:managers=>[[:id, {:type=>:integer}], [:num_staff, {:type=>:integer}] ],
|
10
10
|
:executives=>[[:id, {:type=>:integer}], [:num_managers, {:type=>:integer}]],
|
11
11
|
:staff=>[[:id, {:type=>:integer}], [:manager_id, {:type=>:integer}]],
|
12
12
|
}[table.is_a?(Sequel::Dataset) ? table.first_source_table : table]
|
@@ -486,7 +486,7 @@ describe "class_table_inheritance plugin without sti_key with :alias option" do
|
|
486
486
|
end
|
487
487
|
|
488
488
|
describe "class_table_inheritance plugin with duplicate columns" do
|
489
|
-
it "should raise error" do
|
489
|
+
it "should raise error if no columns are explicitly ignored" do
|
490
490
|
@db = Sequel.mock(:autoid=>proc{|sql| 1})
|
491
491
|
def @db.supports_schema_parsing?() true end
|
492
492
|
def @db.schema(table, opts={})
|
@@ -510,6 +510,56 @@ describe "class_table_inheritance plugin with duplicate columns" do
|
|
510
510
|
end
|
511
511
|
proc{class ::Manager < Employee; end}.must_raise Sequel::Error
|
512
512
|
end
|
513
|
+
|
514
|
+
describe "with certain sub-class columns ignored" do
|
515
|
+
before do
|
516
|
+
@db = Sequel.mock(:autoid=>proc{|sql| 1})
|
517
|
+
def @db.supports_schema_parsing?() true end
|
518
|
+
def @db.schema(table, opts={})
|
519
|
+
{:employees=>[[:id, {:primary_key=>true, :type=>:integer}], [:name, {:type=>:string}], [:kind, {:type=>:string}], [:updated_at, {:type=>:datetime}]],
|
520
|
+
:managers=>[[:id, {:type=>:integer}], [:num_staff, {:type=>:integer}], [:updated_at, {:type=>:datetime}], [:another_duplicate_column, {:type=>:integer}]],
|
521
|
+
:executives=>[[:id, {:type=>:integer}], [:num_managers, {:type=>:integer}], [:updated_at, {:type=>:datetime}], [:another_duplicate_column, {:type=>:integer}]],
|
522
|
+
}[table.is_a?(Sequel::Dataset) ? table.first_source_table : table]
|
523
|
+
end
|
524
|
+
@db.extend_datasets do
|
525
|
+
def columns
|
526
|
+
{[:employees]=>[:id, :name, :kind, :updated_at],
|
527
|
+
[:managers]=>[:id, :num_staff, :updated_at, :another_duplicate_column],
|
528
|
+
[:executives]=>[:id, :num_managers, :updated_at, :another_duplicate_column],
|
529
|
+
[:employees, :managers]=>[:id, :name, :kind, :updated_at, :num_staff],
|
530
|
+
}[opts[:from] + (opts[:join] || []).map{|x| x.table}]
|
531
|
+
end
|
532
|
+
end
|
533
|
+
class ::Employee < Sequel::Model(@db)
|
534
|
+
def _save_refresh; @values[:id] = 1 end
|
535
|
+
def self.columns
|
536
|
+
dataset.columns || dataset.opts[:from].first.expression.columns
|
537
|
+
end
|
538
|
+
plugin :class_table_inheritance, :ignore_subclass_columns=>[:updated_at]
|
539
|
+
end
|
540
|
+
class ::Manager < Employee
|
541
|
+
Manager.cti_ignore_subclass_columns.push(:another_duplicate_column)
|
542
|
+
end
|
543
|
+
class ::Executive < Manager; end
|
544
|
+
end
|
545
|
+
|
546
|
+
it "should not use the ignored column in a sub-class subquery" do
|
547
|
+
Employee.dataset.sql.must_equal 'SELECT * FROM employees'
|
548
|
+
Manager.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, employees.updated_at, managers.num_staff, managers.another_duplicate_column FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees'
|
549
|
+
Executive.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, employees.updated_at, managers.num_staff, managers.another_duplicate_column, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id)) AS employees'
|
550
|
+
end
|
551
|
+
|
552
|
+
it "should include schema for columns for tables for ancestor classes" do
|
553
|
+
Employee.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :updated_at=>{:type=>:datetime})
|
554
|
+
Manager.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :updated_at=>{:type=>:datetime}, :num_staff=>{:type=>:integer}, :another_duplicate_column=>{:type=>:integer})
|
555
|
+
Executive.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :updated_at=>{:type=>:datetime}, :num_staff=>{:type=>:integer}, :another_duplicate_column=>{:type=>:integer}, :num_managers=>{:type=>:integer})
|
556
|
+
end
|
557
|
+
|
558
|
+
after do
|
559
|
+
Object.send(:remove_const, :Executive)
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
513
563
|
after do
|
514
564
|
Object.send(:remove_const, :Manager)
|
515
565
|
Object.send(:remove_const, :Employee)
|