sequel 4.1.1 → 4.2.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/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
|
+
|