sequel 4.1.1 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +32 -0
- data/doc/opening_databases.rdoc +4 -0
- data/doc/release_notes/4.2.0.txt +129 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +5 -0
- data/lib/sequel/adapters/mysql2.rb +2 -1
- data/lib/sequel/adapters/postgres.rb +8 -4
- data/lib/sequel/adapters/shared/db2.rb +5 -0
- data/lib/sequel/adapters/shared/mssql.rb +15 -4
- data/lib/sequel/adapters/shared/mysql.rb +1 -0
- data/lib/sequel/adapters/shared/oracle.rb +1 -1
- data/lib/sequel/adapters/shared/postgres.rb +10 -0
- data/lib/sequel/adapters/shared/sqlite.rb +5 -0
- data/lib/sequel/database/features.rb +6 -1
- data/lib/sequel/database/schema_methods.rb +3 -7
- data/lib/sequel/dataset/actions.rb +3 -4
- data/lib/sequel/dataset/features.rb +5 -0
- data/lib/sequel/dataset/misc.rb +28 -3
- data/lib/sequel/dataset/mutation.rb +37 -11
- data/lib/sequel/dataset/prepared_statements.rb +1 -3
- data/lib/sequel/dataset/query.rb +12 -3
- data/lib/sequel/dataset/sql.rb +12 -6
- data/lib/sequel/deprecated.rb +1 -1
- data/lib/sequel/extensions/columns_introspection.rb +1 -1
- data/lib/sequel/extensions/core_extensions.rb +0 -2
- data/lib/sequel/extensions/empty_array_ignore_nulls.rb +1 -1
- data/lib/sequel/extensions/filter_having.rb +1 -1
- data/lib/sequel/extensions/from_block.rb +31 -0
- data/lib/sequel/extensions/graph_each.rb +1 -1
- data/lib/sequel/extensions/hash_aliases.rb +1 -1
- data/lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb +78 -0
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_loose_count.rb +32 -0
- data/lib/sequel/extensions/pg_static_cache_updater.rb +133 -0
- data/lib/sequel/extensions/pretty_table.rb +1 -1
- data/lib/sequel/extensions/query.rb +3 -1
- data/lib/sequel/extensions/query_literals.rb +1 -1
- data/lib/sequel/extensions/select_remove.rb +1 -1
- data/lib/sequel/extensions/sequel_3_dataset_methods.rb +1 -1
- data/lib/sequel/extensions/set_overrides.rb +1 -1
- data/lib/sequel/model.rb +1 -1
- data/lib/sequel/model/base.rb +20 -6
- data/lib/sequel/model/exceptions.rb +1 -1
- data/lib/sequel/plugins/composition.rb +9 -0
- data/lib/sequel/plugins/dirty.rb +19 -8
- data/lib/sequel/plugins/instance_filters.rb +9 -0
- data/lib/sequel/plugins/serialization.rb +9 -0
- data/lib/sequel/plugins/serialization_modification_detection.rb +9 -0
- data/lib/sequel/plugins/static_cache.rb +96 -28
- data/lib/sequel/version.rb +2 -2
- data/spec/adapters/mssql_spec.rb +1 -1
- data/spec/adapters/postgres_spec.rb +70 -0
- data/spec/core/dataset_spec.rb +58 -1
- data/spec/core/deprecated_spec.rb +1 -1
- data/spec/core/schema_spec.rb +18 -0
- data/spec/extensions/composition_spec.rb +7 -0
- data/spec/extensions/dirty_spec.rb +9 -0
- data/spec/extensions/from_block_spec.rb +21 -0
- data/spec/extensions/instance_filters_spec.rb +6 -0
- data/spec/extensions/pg_loose_count_spec.rb +17 -0
- data/spec/extensions/pg_static_cache_updater_spec.rb +80 -0
- data/spec/extensions/query_spec.rb +8 -0
- data/spec/extensions/serialization_modification_detection_spec.rb +9 -0
- data/spec/extensions/serialization_spec.rb +7 -0
- data/spec/extensions/set_overrides_spec.rb +12 -0
- data/spec/extensions/static_cache_spec.rb +314 -154
- data/spec/integration/dataset_test.rb +12 -2
- data/spec/integration/schema_test.rb +13 -0
- data/spec/model/record_spec.rb +74 -0
- metadata +13 -3
data/lib/sequel/dataset/misc.rb
CHANGED
@@ -40,6 +40,13 @@ module Sequel
|
|
40
40
|
def eql?(o)
|
41
41
|
self == o
|
42
42
|
end
|
43
|
+
|
44
|
+
# Similar to #clone, but returns an unfrozen clone if the receiver is frozen.
|
45
|
+
def dup
|
46
|
+
o = clone
|
47
|
+
o.opts.delete(:frozen)
|
48
|
+
o
|
49
|
+
end
|
43
50
|
|
44
51
|
# Yield a dataset for each server in the connection pool that is tied to that server.
|
45
52
|
# Intended for use in sharded environments where all servers need to be modified
|
@@ -58,6 +65,17 @@ module Sequel
|
|
58
65
|
def escape_like(string)
|
59
66
|
string.gsub(/[\\%_]/){|m| "\\#{m}"}
|
60
67
|
end
|
68
|
+
|
69
|
+
# Sets the frozen flag on the dataset, so you can't modify it. Returns the receiver.
|
70
|
+
def freeze
|
71
|
+
@opts[:frozen] = true
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
# Whether the object is frozen.
|
76
|
+
def frozen?
|
77
|
+
@opts[:frozen]
|
78
|
+
end
|
61
79
|
|
62
80
|
# Alias of +first_source_alias+
|
63
81
|
def first_source
|
@@ -142,9 +160,7 @@ module Sequel
|
|
142
160
|
# Returns a string representation of the dataset including the class name
|
143
161
|
# and the corresponding SQL select statement.
|
144
162
|
def inspect
|
145
|
-
|
146
|
-
c = c.superclass while c.name.nil? || c.name == ''
|
147
|
-
"#<#{c.name}: #{sql.inspect}>"
|
163
|
+
"#<#{visible_class_name}: #{sql.inspect}>"
|
148
164
|
end
|
149
165
|
|
150
166
|
# The alias to use for the row_number column, used when emulating OFFSET
|
@@ -207,5 +223,14 @@ module Sequel
|
|
207
223
|
table_alias
|
208
224
|
end
|
209
225
|
end
|
226
|
+
|
227
|
+
private
|
228
|
+
|
229
|
+
# Return the class name for this dataset, but skip anonymous classes
|
230
|
+
def visible_class_name
|
231
|
+
c = self.class
|
232
|
+
c = c.superclass while c.name.nil? || c.name == ''
|
233
|
+
c.name
|
234
|
+
end
|
210
235
|
end
|
211
236
|
end
|
@@ -6,7 +6,7 @@ module Sequel
|
|
6
6
|
# ---------------------
|
7
7
|
|
8
8
|
# All methods that should have a ! method added that modifies the receiver.
|
9
|
-
MUTATION_METHODS = QUERY_METHODS - [:
|
9
|
+
MUTATION_METHODS = QUERY_METHODS - [:naked, :from_self]
|
10
10
|
|
11
11
|
# Setup mutation (e.g. filter!) methods. These operate the same as the
|
12
12
|
# non-! methods, but replace the options of the current dataset with the
|
@@ -26,18 +26,9 @@ module Sequel
|
|
26
26
|
# Add the mutation methods via metaprogramming
|
27
27
|
def_mutation_method(*MUTATION_METHODS)
|
28
28
|
|
29
|
-
# Set the method to call on identifiers going into the database for this dataset
|
30
|
-
attr_writer :identifier_input_method
|
31
|
-
|
32
|
-
# Set the method to call on identifiers coming the database for this dataset
|
33
|
-
attr_writer :identifier_output_method
|
34
|
-
|
35
|
-
# Whether to quote identifiers for this dataset
|
36
|
-
attr_writer :quote_identifiers
|
37
|
-
|
38
29
|
# The row_proc for this database, should be any object that responds to +call+ with
|
39
30
|
# a single hash argument and returns the object you want #each to return.
|
40
|
-
|
31
|
+
attr_reader :row_proc
|
41
32
|
|
42
33
|
# Load an extension into the receiver. In addition to requiring the extension file, this
|
43
34
|
# also modifies the dataset to work with the extension (usually extending it with a
|
@@ -45,6 +36,7 @@ module Sequel
|
|
45
36
|
# extension does not have specific support for Database objects, an Error will be raised.
|
46
37
|
# Returns self.
|
47
38
|
def extension!(*exts)
|
39
|
+
raise_if_frozen!
|
48
40
|
Sequel.extension(*exts)
|
49
41
|
exts.each do |ext|
|
50
42
|
if pr = Sequel.synchronize{EXTENSIONS[ext]}
|
@@ -58,24 +50,58 @@ module Sequel
|
|
58
50
|
|
59
51
|
# Avoid self-referential dataset by cloning.
|
60
52
|
def from_self!(*args, &block)
|
53
|
+
raise_if_frozen!
|
61
54
|
@opts = clone.from_self(*args, &block).opts
|
62
55
|
self
|
63
56
|
end
|
64
57
|
|
58
|
+
# Set the method to call on identifiers going into the database for this dataset
|
59
|
+
def identifier_input_method=(v)
|
60
|
+
raise_if_frozen!
|
61
|
+
@identifier_input_method = v
|
62
|
+
end
|
63
|
+
|
64
|
+
# Set the method to call on identifiers coming the database for this dataset
|
65
|
+
def identifier_output_method=(v)
|
66
|
+
raise_if_frozen!
|
67
|
+
@identifier_output_method = v
|
68
|
+
end
|
69
|
+
|
65
70
|
# Remove the row_proc from the current dataset.
|
66
71
|
def naked!
|
72
|
+
raise_if_frozen!
|
67
73
|
self.row_proc = nil
|
68
74
|
self
|
69
75
|
end
|
70
76
|
|
77
|
+
# Set whether to quote identifiers for this dataset
|
78
|
+
def quote_identifiers=(v)
|
79
|
+
raise_if_frozen!
|
80
|
+
@quote_identifiers = v
|
81
|
+
end
|
82
|
+
|
83
|
+
# Override the row_proc for this dataset
|
84
|
+
def row_proc=(v)
|
85
|
+
raise_if_frozen!
|
86
|
+
@row_proc = v
|
87
|
+
end
|
88
|
+
|
71
89
|
private
|
72
90
|
|
73
91
|
# Modify the receiver with the results of sending the meth, args, and block
|
74
92
|
# to the receiver and merging the options of the resulting dataset into
|
75
93
|
# the receiver's options.
|
76
94
|
def mutation_method(meth, *args, &block)
|
95
|
+
raise_if_frozen!
|
77
96
|
@opts = send(meth, *args, &block).opts
|
78
97
|
self
|
79
98
|
end
|
99
|
+
|
100
|
+
# Raise a RuntimeError if the receiver is frozen
|
101
|
+
def raise_if_frozen!
|
102
|
+
if frozen?
|
103
|
+
raise RuntimeError, "can't modify frozen #{visible_class_name}"
|
104
|
+
end
|
105
|
+
end
|
80
106
|
end
|
81
107
|
end
|
@@ -118,9 +118,7 @@ module Sequel
|
|
118
118
|
# with the prepared SQL it represents (which in general won't have
|
119
119
|
# substituted variables).
|
120
120
|
def inspect
|
121
|
-
|
122
|
-
c = c.superclass while c.name.nil? || c.name == ''
|
123
|
-
"<#{c.name}/PreparedStatement #{prepared_sql.inspect}>"
|
121
|
+
"<#{visible_class_name}/PreparedStatement #{prepared_sql.inspect}>"
|
124
122
|
end
|
125
123
|
|
126
124
|
protected
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -36,12 +36,11 @@ module Sequel
|
|
36
36
|
QUERY_METHODS = (<<-METHS).split.map{|x| x.to_sym} + JOIN_METHODS
|
37
37
|
add_graph_aliases and distinct except exclude exclude_having exclude_where
|
38
38
|
filter for_update from from_self graph grep group group_and_count group_by having intersect invert
|
39
|
-
limit lock_style naked or order order_append order_by order_more order_prepend
|
39
|
+
limit lock_style naked or order order_append order_by order_more order_prepend qualify
|
40
40
|
reverse reverse_order select select_all select_append select_group select_more server
|
41
|
-
|
41
|
+
set_graph_aliases unfiltered ungraphed ungrouped union
|
42
42
|
unlimited unordered where with with_recursive with_sql
|
43
43
|
METHS
|
44
|
-
# REMOVE40: query paginate set_defaults set_overrides
|
45
44
|
|
46
45
|
# Register an extension callback for Dataset objects. ext should be the
|
47
46
|
# extension name symbol, and mod should either be a Module that the
|
@@ -501,6 +500,16 @@ module Sequel
|
|
501
500
|
class_eval("def #{jtype}_join(table); raise(Sequel::Error, '#{jtype}_join does not accept join table blocks') if block_given?; join_table(:#{jtype}, table) end", __FILE__, __LINE__)
|
502
501
|
end
|
503
502
|
|
503
|
+
# Marks this dataset as a lateral dataset. If used in another dataset's FROM
|
504
|
+
# or JOIN clauses, it will surround the subquery with LATERAL to enable it
|
505
|
+
# to deal with previous tables in the query:
|
506
|
+
#
|
507
|
+
# DB.from(:a, DB[:b].where(:a__c=>:b__d).lateral)
|
508
|
+
# # SELECT * FROM a, LATERAL (SELECT * FROM b WHERE (a.c = b.d))
|
509
|
+
def lateral
|
510
|
+
clone(:lateral=>true)
|
511
|
+
end
|
512
|
+
|
504
513
|
# If given an integer, the dataset will contain only the first l results.
|
505
514
|
# If given a range, it will contain only those at offsets within that
|
506
515
|
# range. If a second argument is given, it is used as an offset. To use
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -242,6 +242,7 @@ module Sequel
|
|
242
242
|
INTO = " INTO ".freeze
|
243
243
|
IS_LITERALS = {nil=>'NULL'.freeze, true=>'TRUE'.freeze, false=>'FALSE'.freeze}.freeze
|
244
244
|
IS_OPERATORS = ::Sequel::SQL::ComplexExpression::IS_OPERATORS
|
245
|
+
LATERAL = 'LATERAL '.freeze
|
245
246
|
LIKE_OPERATORS = ::Sequel::SQL::ComplexExpression::LIKE_OPERATORS
|
246
247
|
LIMIT = " LIMIT ".freeze
|
247
248
|
N_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::N_ARITY_OPERATORS
|
@@ -533,12 +534,16 @@ module Sequel
|
|
533
534
|
str = pls.str
|
534
535
|
sql << PAREN_OPEN if pls.parens
|
535
536
|
if args.is_a?(Hash)
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
537
|
+
if args.empty?
|
538
|
+
sql << str
|
539
|
+
else
|
540
|
+
re = /:(#{args.keys.map{|k| Regexp.escape(k.to_s)}.join('|')})\b/
|
541
|
+
loop do
|
542
|
+
previous, q, str = str.partition(re)
|
543
|
+
sql << previous
|
544
|
+
literal_append(sql, args[($1||q[1..-1].to_s).to_sym]) unless q.empty?
|
545
|
+
break if str.empty?
|
546
|
+
end
|
542
547
|
end
|
543
548
|
elsif str.is_a?(Array)
|
544
549
|
len = args.length
|
@@ -1043,6 +1048,7 @@ module Sequel
|
|
1043
1048
|
|
1044
1049
|
# SQL fragment for Dataset. Does a subselect inside parantheses.
|
1045
1050
|
def literal_dataset_append(sql, v)
|
1051
|
+
sql << LATERAL if v.opts[:lateral]
|
1046
1052
|
sql << PAREN_OPEN
|
1047
1053
|
subselect_sql_append(sql, v)
|
1048
1054
|
sql << PAREN_CLOSE
|
data/lib/sequel/deprecated.rb
CHANGED
@@ -37,7 +37,7 @@ module Sequel
|
|
37
37
|
# Print the message and possibly backtrace to the output.
|
38
38
|
def self.deprecate(method, instead=nil)
|
39
39
|
return unless output
|
40
|
-
message = instead ? "#{method} is deprecated and will be removed in Sequel
|
40
|
+
message = instead ? "#{method} is deprecated and will be removed in a future version of Sequel. #{instead}." : method
|
41
41
|
message = "#{prefix}#{message}" if prefix
|
42
42
|
output.puts(message)
|
43
43
|
case b = backtrace_filter
|
@@ -13,7 +13,7 @@
|
|
13
13
|
# You can load this extension into specific datasets:
|
14
14
|
#
|
15
15
|
# ds = DB[:table]
|
16
|
-
# ds.extension(:empty_array_ignore_nulls)
|
16
|
+
# ds = ds.extension(:empty_array_ignore_nulls)
|
17
17
|
#
|
18
18
|
# Or you can load it into all of a database's datasets, which
|
19
19
|
# is probably the desired behavior if you are using this extension:
|
@@ -7,7 +7,7 @@
|
|
7
7
|
# You can load this extension into specific datasets:
|
8
8
|
#
|
9
9
|
# ds = DB[:table]
|
10
|
-
# ds.extension(:filter_having)
|
10
|
+
# ds = ds.extension(:filter_having)
|
11
11
|
#
|
12
12
|
# Or you can load it into all of a database's datasets, which
|
13
13
|
# is probably the desired behavior if you are using this extension:
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# The from_block extension changes Database#from so that blocks given
|
2
|
+
# to it are treated as virtual rows applying to the FROM clause,
|
3
|
+
# instead of virtual rows applying to the WHERE clause. This will
|
4
|
+
# probably be made the default in the next major version of Sequel.
|
5
|
+
#
|
6
|
+
# This makes it easier to use table returning functions:
|
7
|
+
#
|
8
|
+
# DB.from{table_function(1)}
|
9
|
+
# # SELECT * FROM table_function(1)
|
10
|
+
#
|
11
|
+
# To load the extension into the database:
|
12
|
+
#
|
13
|
+
# DB.extension :from_block
|
14
|
+
|
15
|
+
module Sequel
|
16
|
+
module Database::FromBlock
|
17
|
+
# If a block is given, make it affect the FROM clause:
|
18
|
+
# DB.from{table_function(1)}
|
19
|
+
# # SELECT * FROM table_function(1)
|
20
|
+
def from(*args, &block)
|
21
|
+
if block
|
22
|
+
@default_dataset.from(*args, &block)
|
23
|
+
else
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Database.register_extension(:from_block, Database::FromBlock)
|
30
|
+
end
|
31
|
+
|
@@ -8,7 +8,7 @@
|
|
8
8
|
# You can load this extension into specific datasets:
|
9
9
|
#
|
10
10
|
# ds = DB[:table]
|
11
|
-
# ds.extension(:graph_each)
|
11
|
+
# ds = ds.extension(:graph_each)
|
12
12
|
#
|
13
13
|
# Or you can load it into all of a database's datasets, which
|
14
14
|
# is probably the desired behavior if you are using this extension:
|
@@ -7,7 +7,7 @@
|
|
7
7
|
# You can load this extension into specific datasets:
|
8
8
|
#
|
9
9
|
# ds = DB[:table]
|
10
|
-
# ds.extension(:hash_aliases)
|
10
|
+
# ds = ds.extension(:hash_aliases)
|
11
11
|
#
|
12
12
|
# Or you can load it into all of a database's datasets, which
|
13
13
|
# is probably the desired behavior if you are using this extension:
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# The mssql_emulate_lateral_with_apply extension converts
|
2
|
+
# queries that use LATERAL into queries that use CROSS/OUTER
|
3
|
+
# APPLY, allowing code that works on databases that support
|
4
|
+
# LATERAL via Dataset#lateral to run on Microsoft SQL Server.
|
5
|
+
#
|
6
|
+
# This is available as a separate extension instead of
|
7
|
+
# integrated into the Microsoft SQL Server support because
|
8
|
+
# few people need it and there is a performance hit to
|
9
|
+
# code that doesn't use it.
|
10
|
+
#
|
11
|
+
# It is possible there are cases where this emulation does
|
12
|
+
# not work. Users should probably verify that correct
|
13
|
+
# results are returned when using this extension.
|
14
|
+
#
|
15
|
+
# You can load this extension into specific datasets:
|
16
|
+
#
|
17
|
+
# ds = DB[:table]
|
18
|
+
# ds = ds.extension(:mssql_emulate_lateral_with_apply)
|
19
|
+
#
|
20
|
+
# Or you can load it into all of a database's datasets:
|
21
|
+
#
|
22
|
+
# DB.extension(:mssql_emulate_lateral_with_apply)
|
23
|
+
|
24
|
+
module Sequel
|
25
|
+
module MSSQL
|
26
|
+
module EmulateLateralWithApply
|
27
|
+
# If the table is a dataset that uses LATERAL,
|
28
|
+
# convert it to a CROSS APPLY if it is a INNER
|
29
|
+
# or CROSS JOIN, and an OUTER APPLY if it is a
|
30
|
+
# LEFT JOIN.
|
31
|
+
def join_table(type, table, expr=nil, *)
|
32
|
+
if table.is_a?(Dataset) && table.opts[:lateral]
|
33
|
+
table = table.clone(:lateral=>nil)
|
34
|
+
case type
|
35
|
+
when :inner
|
36
|
+
type = :cross_apply
|
37
|
+
table = table.where(expr)
|
38
|
+
expr = nil
|
39
|
+
when :cross
|
40
|
+
type = :cross_apply
|
41
|
+
when :left, :left_outer
|
42
|
+
type = :outer_apply
|
43
|
+
table = table.where(expr)
|
44
|
+
expr = nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
# When a FROM entry uses a LATERAL subquery,
|
51
|
+
# convert that entry into a CROSS APPLY.
|
52
|
+
def from(*source, &block)
|
53
|
+
virtual_row_columns(source, block)
|
54
|
+
lateral, source = source.partition{|t| t.is_a?(Sequel::Dataset) && t.opts[:lateral] || (t.is_a?(Sequel::SQL::AliasedExpression) && t.expression.is_a?(Sequel::Dataset) && t.expression.opts[:lateral])} unless source.empty?
|
55
|
+
return super(*source, &nil) if !lateral || lateral.empty?
|
56
|
+
|
57
|
+
ds = from(*source)
|
58
|
+
lateral.each do |l|
|
59
|
+
l = if l.is_a?(Sequel::SQL::AliasedExpression)
|
60
|
+
l.expression.clone(:lateral=>nil).as(l.aliaz)
|
61
|
+
else
|
62
|
+
l.clone(:lateral=>nil)
|
63
|
+
end
|
64
|
+
ds = ds.cross_apply(l)
|
65
|
+
end
|
66
|
+
ds
|
67
|
+
end
|
68
|
+
|
69
|
+
# MSSQL can emulate lateral subqueries via CROSS/OUTER APPLY
|
70
|
+
# when using this extension.
|
71
|
+
def supports_lateral_subqueries?
|
72
|
+
true
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
Dataset.register_extension(:mssql_emulate_lateral_with_apply, MSSQL::EmulateLateralWithApply)
|
78
|
+
end
|
@@ -7,7 +7,7 @@
|
|
7
7
|
# You can load this extension into specific datasets:
|
8
8
|
#
|
9
9
|
# ds = DB[:table]
|
10
|
-
# ds.extension(:pagination)
|
10
|
+
# ds = ds.extension(:pagination)
|
11
11
|
#
|
12
12
|
# Or you can load it into all of a database's datasets, which
|
13
13
|
# is probably the desired behavior if you are using this extension:
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# The pg_loose_count extension looks at the table statistics
|
2
|
+
# in the PostgreSQL system tables to get a fast approximate
|
3
|
+
# count of the number of rows in a given table:
|
4
|
+
#
|
5
|
+
# DB.loose_count(:table) # => 123456
|
6
|
+
#
|
7
|
+
# It can also support schema qualified tables:
|
8
|
+
#
|
9
|
+
# DB.loose_count(:schema__table) # => 123456
|
10
|
+
#
|
11
|
+
# How accurate this count is depends on the number of rows
|
12
|
+
# added/deleted from the table since the last time it was
|
13
|
+
# analyzed.
|
14
|
+
#
|
15
|
+
# To load the extension into the database:
|
16
|
+
#
|
17
|
+
# DB.extension :pg_loose_count
|
18
|
+
|
19
|
+
module Sequel
|
20
|
+
module Postgres
|
21
|
+
module LooseCount
|
22
|
+
# Look at the table statistics for the given table to get
|
23
|
+
# an approximate count of the number of rows.
|
24
|
+
def loose_count(table)
|
25
|
+
from(:pg_class).where(:oid=>regclass_oid(table)).get(Sequel.cast(:reltuples, Integer))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
Database.register_extension(:pg_loose_count, Postgres::LooseCount)
|
31
|
+
end
|
32
|
+
|