sequel 4.34.0 → 4.35.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +30 -0
- data/Rakefile +14 -17
- data/doc/object_model.rdoc +4 -4
- data/doc/release_notes/4.35.0.txt +130 -0
- data/doc/schema_modification.rdoc +8 -3
- data/doc/security.rdoc +3 -3
- data/lib/sequel/adapters/ado.rb +2 -2
- data/lib/sequel/adapters/ado/access.rb +6 -6
- data/lib/sequel/adapters/ado/mssql.rb +2 -2
- data/lib/sequel/adapters/amalgalite.rb +6 -6
- data/lib/sequel/adapters/cubrid.rb +4 -4
- data/lib/sequel/adapters/do.rb +2 -2
- data/lib/sequel/adapters/do/mysql.rb +1 -1
- data/lib/sequel/adapters/do/postgres.rb +1 -1
- data/lib/sequel/adapters/do/sqlite3.rb +1 -1
- data/lib/sequel/adapters/ibmdb.rb +6 -6
- data/lib/sequel/adapters/jdbc.rb +15 -15
- data/lib/sequel/adapters/jdbc/db2.rb +1 -1
- data/lib/sequel/adapters/jdbc/derby.rb +3 -3
- data/lib/sequel/adapters/jdbc/h2.rb +3 -3
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -2
- data/lib/sequel/adapters/jdbc/mssql.rb +1 -1
- data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
- data/lib/sequel/adapters/jdbc/oracle.rb +1 -1
- data/lib/sequel/adapters/jdbc/postgresql.rb +2 -2
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/jdbc/sqlite.rb +1 -1
- data/lib/sequel/adapters/jdbc/transactions.rb +10 -10
- data/lib/sequel/adapters/mock.rb +1 -1
- data/lib/sequel/adapters/mysql.rb +2 -2
- data/lib/sequel/adapters/mysql2.rb +2 -2
- data/lib/sequel/adapters/odbc.rb +2 -2
- data/lib/sequel/adapters/odbc/mssql.rb +2 -2
- data/lib/sequel/adapters/oracle.rb +9 -9
- data/lib/sequel/adapters/postgres.rb +3 -3
- data/lib/sequel/adapters/shared/mssql.rb +36 -8
- data/lib/sequel/adapters/shared/oracle.rb +15 -0
- data/lib/sequel/adapters/shared/postgres.rb +22 -1
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +7 -7
- data/lib/sequel/adapters/swift.rb +3 -3
- data/lib/sequel/adapters/swift/mysql.rb +1 -1
- data/lib/sequel/adapters/swift/postgres.rb +1 -1
- data/lib/sequel/adapters/swift/sqlite.rb +1 -1
- data/lib/sequel/adapters/tinytds.rb +5 -7
- data/lib/sequel/database/logging.rb +18 -3
- data/lib/sequel/database/misc.rb +19 -8
- data/lib/sequel/database/schema_generator.rb +7 -2
- data/lib/sequel/database/schema_methods.rb +9 -2
- data/lib/sequel/database/transactions.rb +52 -18
- data/lib/sequel/dataset/actions.rb +24 -19
- data/lib/sequel/dataset/features.rb +5 -0
- data/lib/sequel/dataset/query.rb +6 -0
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/error_sql.rb +3 -3
- data/lib/sequel/extensions/pg_range.rb +10 -1
- data/lib/sequel/extensions/schema_dumper.rb +8 -5
- data/lib/sequel/extensions/server_logging.rb +61 -0
- data/lib/sequel/extensions/sql_comments.rb +91 -0
- data/lib/sequel/model/associations.rb +40 -8
- data/lib/sequel/model/base.rb +19 -5
- data/lib/sequel/plugins/class_table_inheritance.rb +12 -0
- data/lib/sequel/plugins/delay_add_association.rb +1 -0
- data/lib/sequel/plugins/json_serializer.rb +10 -2
- data/lib/sequel/version.rb +1 -1
- data/spec/adapter_spec.rb +4 -0
- data/spec/adapters/mysql_spec.rb +1 -1
- data/spec/adapters/postgres_spec.rb +3 -2
- data/spec/core/connection_pool_spec.rb +2 -0
- data/spec/core/database_spec.rb +49 -0
- data/spec/core/dataset_spec.rb +25 -1
- data/spec/core/mock_adapter_spec.rb +3 -1
- data/spec/core/schema_generator_spec.rb +1 -1
- data/spec/core_model_spec.rb +2 -0
- data/spec/core_spec.rb +1 -0
- data/spec/extensions/delay_add_association_spec.rb +22 -0
- data/spec/extensions/json_serializer_spec.rb +6 -0
- data/spec/extensions/pg_range_spec.rb +30 -2
- data/spec/extensions/schema_dumper_spec.rb +3 -2
- data/spec/extensions/server_logging_spec.rb +45 -0
- data/spec/extensions/sql_comments_spec.rb +27 -0
- data/spec/files/reversible_migrations/006_reversible.rb +10 -0
- data/spec/files/reversible_migrations/007_reversible.rb +10 -0
- data/spec/integration/dataset_test.rb +28 -2
- data/spec/integration/migrator_test.rb +23 -1
- data/spec/integration/schema_test.rb +12 -32
- data/spec/integration/transaction_test.rb +10 -0
- data/spec/integration/type_test.rb +1 -1
- data/spec/model/eager_loading_spec.rb +16 -0
- data/spec/model/record_spec.rb +9 -0
- data/spec/model_no_assoc_spec.rb +1 -0
- data/spec/model_spec.rb +1 -0
- data/spec/plugin_spec.rb +1 -0
- metadata +16 -2
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The server_logging extension makes the logger include the server/shard
|
4
|
+
# the query was issued on. This makes it easier to use the logs when
|
5
|
+
# using sharding.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
#
|
9
|
+
# DB.opts[:server]
|
10
|
+
# # {:read_only=>{}, :b=>{}}
|
11
|
+
# DB.extension :server_logging
|
12
|
+
# DB[:a].all
|
13
|
+
# # (0.000005s) (conn: 1014942550, server: read_only) SELECT * FROM a
|
14
|
+
# DB[:a].server(:b).all
|
15
|
+
# # (0.000004s) (conn: 997304100, server: b) SELECT * FROM a
|
16
|
+
# DB[:a].insert
|
17
|
+
# # (0.000004s) (conn: 1014374750, server: default) INSERT INTO a DEFAULT VALUES
|
18
|
+
#
|
19
|
+
# In order for the server/shard to be correct for all connections, you need to
|
20
|
+
# use this before connections to the database are made, or you need to call
|
21
|
+
# <tt>Database#disconnect</tt> after loading this extension.
|
22
|
+
#
|
23
|
+
# Related module: Sequel::ServerLogging
|
24
|
+
|
25
|
+
#
|
26
|
+
module Sequel
|
27
|
+
module ServerLogging
|
28
|
+
# Initialize the hash mapping connections to shards, and turn on logging
|
29
|
+
# of connection info unless it has specifically been turned off.
|
30
|
+
def self.extended(db)
|
31
|
+
db.instance_exec do
|
32
|
+
@server_connection_map ||= {}
|
33
|
+
self.log_connection_info = true if log_connection_info.nil?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# When setting up a new connection, associate the connection with the
|
38
|
+
# shard.
|
39
|
+
def connect(server)
|
40
|
+
conn = super
|
41
|
+
Sequel.synchronize{@server_connection_map[conn] = server}
|
42
|
+
conn
|
43
|
+
end
|
44
|
+
|
45
|
+
# When disconnecting a connection, remove the related connection from the mapping.
|
46
|
+
def disconnect_connection(conn)
|
47
|
+
super
|
48
|
+
ensure
|
49
|
+
Sequel.synchronize{@server_connection_map.delete(conn)}
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
# Include the server with the connection's id.
|
55
|
+
def connection_info(conn)
|
56
|
+
"(conn: #{conn.__id__}, server: #{Sequel.synchronize{@server_connection_map[conn]}}) "
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
Database.register_extension(:server_logging, ServerLogging)
|
61
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The sql_comments extension adds Dataset#comment to the datasets,
|
4
|
+
# allowing you to set SQL comments in the resulting query. These
|
5
|
+
# comments are appended to the end of the SQL query:
|
6
|
+
#
|
7
|
+
# ds = DB[:table].comment("Some Comment").all
|
8
|
+
# # SELECT * FROM table -- Some Comment
|
9
|
+
# #
|
10
|
+
#
|
11
|
+
# As you can see, this uses single line SQL comments (--) suffixed
|
12
|
+
# by a newline. This # plugin transforms all consecutive
|
13
|
+
# whitespace in the comment to # a single string:
|
14
|
+
#
|
15
|
+
# ds = DB[:table].comment("Some\r\nComment Here").all
|
16
|
+
# # SELECT * FROM table -- Some Comment Here
|
17
|
+
# #
|
18
|
+
#
|
19
|
+
# The reason for the prefixing and suffixing by newlines is to
|
20
|
+
# work correctly when used in subqueries:
|
21
|
+
#
|
22
|
+
# ds = DB[:table].comment("Some\r\nComment Here")
|
23
|
+
# ds.where(:id=>ds).all
|
24
|
+
# # SELECT * FROM table WHERE (id IN (SELECT * FROM table -- Some Comment Here
|
25
|
+
# # )) -- Some Comment Here
|
26
|
+
# #
|
27
|
+
#
|
28
|
+
# In addition to working on SELECT queries, it also works when
|
29
|
+
# inserting, updating, and deleting.
|
30
|
+
#
|
31
|
+
# Due to the use of single line SQL comments and converting all
|
32
|
+
# whitespace to spaces, this should correctly handle even
|
33
|
+
# malicious input. However, it would be unwise to rely on that,
|
34
|
+
# you should probably attempt to ensure that the argument given
|
35
|
+
# to Dataset#comment is not derived from user input.
|
36
|
+
#
|
37
|
+
# You can load this extension into specific datasets:
|
38
|
+
#
|
39
|
+
# ds = DB[:table]
|
40
|
+
# ds = ds.extension(:sql_comments)
|
41
|
+
#
|
42
|
+
# Or you can load it into all of a database's datasets, which
|
43
|
+
# is probably the desired behavior if you are using this extension:
|
44
|
+
#
|
45
|
+
# DB.extension(:sql_comments)
|
46
|
+
#
|
47
|
+
# Note that Microsoft Access does not support inline comments,
|
48
|
+
# and attempting to use comments on it will result in SQL syntax
|
49
|
+
# errors.
|
50
|
+
#
|
51
|
+
# Related module: Sequel::SQLComments
|
52
|
+
|
53
|
+
#
|
54
|
+
module Sequel
|
55
|
+
module SQLComments
|
56
|
+
# Return a modified copy of the dataset that will use the given comment.
|
57
|
+
# To uncomment a commented dataset, pass nil as the argument.
|
58
|
+
def comment(comment)
|
59
|
+
clone(:comment=>(format_sql_comment(comment) if comment))
|
60
|
+
end
|
61
|
+
|
62
|
+
%w'select insert update delete'.each do |type|
|
63
|
+
define_method(:"#{type}_sql") do |*a|
|
64
|
+
sql = super(*a)
|
65
|
+
if comment = @opts[:comment]
|
66
|
+
# This assumes that the comment stored in the dataset has
|
67
|
+
# already been formatted. If not, this could result in SQL
|
68
|
+
# injection.
|
69
|
+
#
|
70
|
+
# Additionally, due to the use of an SQL comment, if any
|
71
|
+
# SQL is appened to the query after the comment is added,
|
72
|
+
# it will become part of the comment unless it is preceded
|
73
|
+
# by a newline.
|
74
|
+
sql << comment
|
75
|
+
end
|
76
|
+
sql
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# Format the comment. For maximum compatibility, this uses a
|
83
|
+
# single line SQL comment, and converts all consecutive whitespace
|
84
|
+
# in the comment to a single space.
|
85
|
+
def format_sql_comment(comment)
|
86
|
+
" -- #{comment.to_s.gsub(/\s+/, ' ')}\n"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
Dataset.register_extension(:sql_comments, SQLComments)
|
91
|
+
end
|
@@ -2194,14 +2194,7 @@ module Sequel
|
|
2194
2194
|
|
2195
2195
|
# Add the given associated object to the given association
|
2196
2196
|
def add_associated_object(opts, o, *args)
|
2197
|
-
|
2198
|
-
if o.is_a?(Hash)
|
2199
|
-
o = klass.new(o)
|
2200
|
-
elsif o.is_a?(Integer) || o.is_a?(String) || o.is_a?(Array)
|
2201
|
-
o = klass.with_pk!(o)
|
2202
|
-
elsif !o.is_a?(klass)
|
2203
|
-
raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}")
|
2204
|
-
end
|
2197
|
+
o = make_add_associated_object(opts, o)
|
2205
2198
|
raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
|
2206
2199
|
ensure_associated_primary_key(opts, o, *args)
|
2207
2200
|
return if run_association_callbacks(opts, :before_add, o) == false
|
@@ -2317,6 +2310,25 @@ module Sequel
|
|
2317
2310
|
opts.send(:cached_fetch, :many_to_one_pk_lookup){opts.primary_key == opts.associated_class.primary_key}
|
2318
2311
|
end
|
2319
2312
|
|
2313
|
+
# Convert the input of the add_* association method into an associated object. For
|
2314
|
+
# hashes, this creates a new object using the hash. For integers, strings, and arrays,
|
2315
|
+
# assume the value specifies a primary key, and lookup an existing object with that primary key.
|
2316
|
+
# Otherwise, if the object is not already an instance of the class, raise an exception.
|
2317
|
+
def make_add_associated_object(opts, o)
|
2318
|
+
klass = opts.associated_class
|
2319
|
+
|
2320
|
+
case o
|
2321
|
+
when Hash
|
2322
|
+
klass.new(o)
|
2323
|
+
when Integer, String, Array
|
2324
|
+
klass.with_pk!(o)
|
2325
|
+
when klass
|
2326
|
+
o
|
2327
|
+
else
|
2328
|
+
raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}")
|
2329
|
+
end
|
2330
|
+
end
|
2331
|
+
|
2320
2332
|
# Remove all associated objects from the given association
|
2321
2333
|
def remove_all_associated_objects(opts, *args)
|
2322
2334
|
raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
|
@@ -2650,6 +2662,26 @@ END
|
|
2650
2662
|
end
|
2651
2663
|
end
|
2652
2664
|
|
2665
|
+
# If the dataset is being eagerly loaded, default to calling all
|
2666
|
+
# instead of each.
|
2667
|
+
def to_hash(key_column=nil, value_column=nil, opts=OPTS)
|
2668
|
+
if (@opts[:eager_graph] || @opts[:eager]) && !opts.has_key?(:all)
|
2669
|
+
opts = Hash[opts]
|
2670
|
+
opts[:all] = true
|
2671
|
+
end
|
2672
|
+
super
|
2673
|
+
end
|
2674
|
+
|
2675
|
+
# If the dataset is being eagerly loaded, default to calling all
|
2676
|
+
# instead of each.
|
2677
|
+
def to_hash_groups(key_column, value_column=nil, opts=OPTS)
|
2678
|
+
if (@opts[:eager_graph] || @opts[:eager]) && !opts.has_key?(:all)
|
2679
|
+
opts = Hash[opts]
|
2680
|
+
opts[:all] = true
|
2681
|
+
end
|
2682
|
+
super
|
2683
|
+
end
|
2684
|
+
|
2653
2685
|
# Do not attempt to split the result set into associations,
|
2654
2686
|
# just return results as simple objects. This is useful if you
|
2655
2687
|
# want to use eager_graph as a shortcut to have all of the joins
|
data/lib/sequel/model/base.rb
CHANGED
@@ -1402,17 +1402,31 @@ module Sequel
|
|
1402
1402
|
@values.keys
|
1403
1403
|
end
|
1404
1404
|
|
1405
|
-
# Refresh this record using +for_update+
|
1406
|
-
# This can be used to make sure no other
|
1407
|
-
# same time.
|
1405
|
+
# Refresh this record using +for_update+ (by default, or the specified style when given)
|
1406
|
+
# unless this is a new record. Returns self. This can be used to make sure no other
|
1407
|
+
# process is updating the record at the same time.
|
1408
|
+
#
|
1409
|
+
# If style is a string, it will be used directly. You should never pass a string
|
1410
|
+
# to this method that is derived from user input, as that can lead to
|
1411
|
+
# SQL injection.
|
1412
|
+
#
|
1413
|
+
# A symbol may be used for database independent locking behavior, but
|
1414
|
+
# all supported symbols have separate methods (e.g. for_update).
|
1415
|
+
#
|
1408
1416
|
#
|
1409
1417
|
# a = Artist[1]
|
1410
1418
|
# Artist.db.transaction do
|
1411
1419
|
# a.lock!
|
1412
1420
|
# a.update(:name=>'A')
|
1413
1421
|
# end
|
1414
|
-
|
1415
|
-
|
1422
|
+
#
|
1423
|
+
# a = Artist[2]
|
1424
|
+
# Artist.db.transaction do
|
1425
|
+
# a.lock!('FOR NO KEY UPDATE')
|
1426
|
+
# a.update(:name=>'B')
|
1427
|
+
# end
|
1428
|
+
def lock!(style=:update)
|
1429
|
+
_refresh(this.lock_style(style)) unless new?
|
1416
1430
|
self
|
1417
1431
|
end
|
1418
1432
|
|
@@ -84,6 +84,18 @@ module Sequel
|
|
84
84
|
# a.values # {:id=>1, name=>'S', :kind=>'CEO'}
|
85
85
|
# a.refresh.values # {:id=>1, name=>'S', :kind=>'Executive', :num_staff=>4, :num_managers=>2}
|
86
86
|
#
|
87
|
+
# You can also load directly from a subclass:
|
88
|
+
#
|
89
|
+
# a = Executive.first
|
90
|
+
# a.values # {:id=>1, name=>'S', :kind=>'Executive', :num_staff=>4, :num_managers=>2}
|
91
|
+
#
|
92
|
+
# Note that when loading from a subclass, because the subclass dataset uses a JOIN,
|
93
|
+
# if you are referencing the primary key column, you need to disambiguate the reference
|
94
|
+
# by explicitly qualifying it:
|
95
|
+
#
|
96
|
+
# a = Executive.where(:id=>1).first # database error
|
97
|
+
# a = Executive.where(:executives__id=>1).first # no error
|
98
|
+
#
|
87
99
|
# = Usage
|
88
100
|
#
|
89
101
|
# # Use the default of storing the class name in the sti_key
|
@@ -38,6 +38,7 @@ module Sequel
|
|
38
38
|
# current object.
|
39
39
|
def add_associated_object(opts, o, *args)
|
40
40
|
if opts.dataset_need_primary_key? && new?
|
41
|
+
o = make_add_associated_object(opts, o)
|
41
42
|
delay_validate_associated_object(opts, o)
|
42
43
|
send(opts[:name]) << o
|
43
44
|
after_create_hook{super(opts, o, *args)}
|
@@ -300,8 +300,16 @@ module Sequel
|
|
300
300
|
if inc.is_a?(Hash)
|
301
301
|
inc.each do |k, v|
|
302
302
|
v = v.empty? ? [] : [v]
|
303
|
-
|
304
|
-
|
303
|
+
|
304
|
+
objs = send(k)
|
305
|
+
|
306
|
+
is_array = if r = model.association_reflection(k)
|
307
|
+
r.returns_array?
|
308
|
+
else
|
309
|
+
objs.is_a?(Array)
|
310
|
+
end
|
311
|
+
|
312
|
+
h[k.to_s] = if is_array
|
305
313
|
objs.map{|obj| Literal.new(Sequel.object_to_json(obj, *v))}
|
306
314
|
else
|
307
315
|
Literal.new(Sequel.object_to_json(objs, *v))
|
data/lib/sequel/version.rb
CHANGED
@@ -5,7 +5,7 @@ module Sequel
|
|
5
5
|
MAJOR = 4
|
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 = 35
|
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
|
data/spec/adapters/mysql_spec.rb
CHANGED
@@ -528,7 +528,7 @@ describe "A MySQL database" do
|
|
528
528
|
|
529
529
|
it "should have set_column_type support keep existing options" do
|
530
530
|
@db.create_table(:items){Integer :id, :null=>false, :default=>5}
|
531
|
-
@db.alter_table(:items){set_column_type :id, Bignum}
|
531
|
+
@db.alter_table(:items){set_column_type :id, :Bignum}
|
532
532
|
check_sqls do
|
533
533
|
@db.sqls.must_equal ["CREATE TABLE `items` (`id` integer NOT NULL DEFAULT 5)", "DESCRIBE `items`", "ALTER TABLE `items` CHANGE COLUMN `id` `id` bigint NOT NULL DEFAULT 5"]
|
534
534
|
end
|
@@ -945,7 +945,7 @@ describe "A PostgreSQL database" do
|
|
945
945
|
@db.create_table!(:posts){primary_key :a, :type=>Fixnum}
|
946
946
|
@db[:posts].insert.must_equal 1
|
947
947
|
@db[:posts].insert.must_equal 2
|
948
|
-
@db.create_table!(:posts){primary_key :a, :type
|
948
|
+
@db.create_table!(:posts){primary_key :a, :type=>:Bignum}
|
949
949
|
@db[:posts].insert.must_equal 1
|
950
950
|
@db[:posts].insert.must_equal 2
|
951
951
|
end
|
@@ -973,6 +973,7 @@ describe "A PostgreSQL database" do
|
|
973
973
|
@db[:posts].insert(:title=>'ruby scooby', :body=>'x')
|
974
974
|
|
975
975
|
@db[:posts].full_text_search(:title, 'rails').all.must_equal [{:title=>'ruby rails', :body=>'yowsa'}]
|
976
|
+
@db[:posts].full_text_search(:title, 'rails', :headline=>true).all.must_equal [{:title=>'ruby rails', :body=>'yowsa', :headline=>'ruby <b>rails</b>'}]
|
976
977
|
@db[:posts].full_text_search([:title, :body], ['yowsa', 'rails']).all.must_equal [:title=>'ruby rails', :body=>'yowsa']
|
977
978
|
@db[:posts].full_text_search(:title, 'scooby', :language => 'french').all.must_equal [{:title=>'ruby scooby', :body=>'x'}]
|
978
979
|
|
@@ -997,7 +998,7 @@ describe "A PostgreSQL database" do
|
|
997
998
|
@db[:posts].insert(:title=>t1)
|
998
999
|
@db[:posts].insert(:title=>t2)
|
999
1000
|
@db[:posts].full_text_search(:title, 'ruby & sequel', :rank=>true).select_map(:title).must_equal [t2, t1]
|
1000
|
-
end
|
1001
|
+
end if DB.server_version >= 80300
|
1001
1002
|
|
1002
1003
|
it "should support spatial indexes" do
|
1003
1004
|
@db.create_table(:posts){box :geom; spatial_index [:geom]}
|
@@ -8,6 +8,8 @@ mock_db = lambda do |*a, &b|
|
|
8
8
|
if b2 = a.shift
|
9
9
|
(class << db; self end).send(:define_method, :disconnect_connection){|c| b2.arity == 1 ? b2.call(c) : b2.call}
|
10
10
|
end
|
11
|
+
# Work around JRuby Issue #3854
|
12
|
+
(class << db; self end).send(:public, :connect, :disconnect_connection)
|
11
13
|
db
|
12
14
|
end
|
13
15
|
|
data/spec/core/database_spec.rb
CHANGED
@@ -341,6 +341,35 @@ describe "Database#log_yield" do
|
|
341
341
|
end
|
342
342
|
end
|
343
343
|
|
344
|
+
describe "Database#log_connection_yield" do
|
345
|
+
before do
|
346
|
+
@o = Object.new
|
347
|
+
def @o.logs; @logs || []; end
|
348
|
+
def @o.to_ary; [self]; end
|
349
|
+
def @o.method_missing(*args); (@logs ||= []) << args; end
|
350
|
+
@conn = Object.new
|
351
|
+
@db = Sequel::Database.new(:logger=>@o)
|
352
|
+
end
|
353
|
+
|
354
|
+
it "should log SQL to the loggers" do
|
355
|
+
@db.log_connection_yield("some SQL", @conn){}
|
356
|
+
@o.logs.length.must_equal 1
|
357
|
+
@o.logs.first.length.must_equal 2
|
358
|
+
@o.logs.first.first.must_equal :info
|
359
|
+
@o.logs.first.last.must_match(/some SQL\z/)
|
360
|
+
@o.logs.first.last.wont_match(/\(conn: -?\d+\) some SQL\z/)
|
361
|
+
end
|
362
|
+
|
363
|
+
it "should include connection information when logging" do
|
364
|
+
@db.log_connection_info = true
|
365
|
+
@db.log_connection_yield("some SQL", @conn){}
|
366
|
+
@o.logs.length.must_equal 1
|
367
|
+
@o.logs.first.length.must_equal 2
|
368
|
+
@o.logs.first.first.must_equal :info
|
369
|
+
@o.logs.first.last.must_match(/\(conn: -?\d+\) some SQL\z/)
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
344
373
|
describe "Database#uri" do
|
345
374
|
before do
|
346
375
|
@c = Class.new(Sequel::Database) do
|
@@ -993,6 +1022,15 @@ describe "Database#transaction with savepoint support" do
|
|
993
1022
|
a.must_equal [1, 1]
|
994
1023
|
end
|
995
1024
|
|
1025
|
+
it "should automatically use a savepoint if :rollback=>:always given inside a transaction" do
|
1026
|
+
@db.transaction do
|
1027
|
+
@db.transaction(:rollback=>:always) do
|
1028
|
+
@db.get(1)
|
1029
|
+
end
|
1030
|
+
end
|
1031
|
+
@db.sqls.must_equal ["BEGIN", "SAVEPOINT autopoint_1", "SELECT 1 AS v LIMIT 1", "ROLLBACK TO SAVEPOINT autopoint_1", "COMMIT"]
|
1032
|
+
end
|
1033
|
+
|
996
1034
|
it "should support :retry_on option for automatically retrying transactions inside an :auto_savepoint transaction" do
|
997
1035
|
a = []
|
998
1036
|
@db.transaction(:auto_savepoint=>true) do
|
@@ -1058,6 +1096,17 @@ describe "Database#transaction without savepoint support" do
|
|
1058
1096
|
@db.sqls.must_equal ['BEGIN', 'COMMIT']
|
1059
1097
|
end
|
1060
1098
|
|
1099
|
+
it "should automatically use a savepoint if :rollback=>:always given inside a transaction" do
|
1100
|
+
proc do
|
1101
|
+
@db.transaction do
|
1102
|
+
@db.transaction(:rollback=>:always) do
|
1103
|
+
@db.get(1)
|
1104
|
+
end
|
1105
|
+
end
|
1106
|
+
end.must_raise Sequel::Error
|
1107
|
+
@db.sqls.must_equal ["BEGIN", "ROLLBACK"]
|
1108
|
+
end
|
1109
|
+
|
1061
1110
|
include DatabaseTransactionSpecs
|
1062
1111
|
end
|
1063
1112
|
|
data/spec/core/dataset_spec.rb
CHANGED
@@ -1822,10 +1822,17 @@ describe "Dataset#to_hash_groups" do
|
|
1822
1822
|
@d.to_hash_groups([:a, :b]).must_equal([1, 2] => [{:a => 1, :b => 2}], [3, 4] => [{:a => 3, :b => 4}], [1, 6] => [{:a => 1, :b => 6}], [7, 4] => [{:a => 7, :b => 4}])
|
1823
1823
|
end
|
1824
1824
|
|
1825
|
-
it "should accept
|
1825
|
+
it "should accept a :hash option into which entries can be merged" do
|
1826
1826
|
@d.to_hash_groups(:a, :b, :hash => (tmp = {})).must_be_same_as(tmp)
|
1827
1827
|
end
|
1828
1828
|
|
1829
|
+
it "should accept an :all option to use all into which entries can be merged" do
|
1830
|
+
called = false
|
1831
|
+
meta_def(@d, :post_load){|_| called = true}
|
1832
|
+
@d.to_hash_groups(:a, :b, :all=>true)
|
1833
|
+
called.must_equal true
|
1834
|
+
end
|
1835
|
+
|
1829
1836
|
it "should not call the row_proc if two arguments are given" do
|
1830
1837
|
@d.row_proc = proc{|r| h = {}; r.keys.each{|k| h[k] = r[k] * 2}; h}
|
1831
1838
|
@d.to_hash_groups(:a, :b).must_equal(1 => [2, 6], 3 => [4], 7 => [4])
|
@@ -4534,6 +4541,23 @@ describe "Dataset#lock_style and for_update" do
|
|
4534
4541
|
end
|
4535
4542
|
end
|
4536
4543
|
|
4544
|
+
describe "Dataset#skip_locked" do
|
4545
|
+
before do
|
4546
|
+
@ds = Sequel.mock.dataset.from(:t).for_update
|
4547
|
+
end
|
4548
|
+
|
4549
|
+
it "should raise an error if not supported" do
|
4550
|
+
proc{@ds.skip_locked}.must_raise Sequel::Error
|
4551
|
+
end
|
4552
|
+
|
4553
|
+
it "should skipped locked rows if supported" do
|
4554
|
+
def @ds.supports_skip_locked?; true end
|
4555
|
+
def @ds.select_lock_sql(sql) super; sql << " SKIP LOCKED" if @opts[:skip_locked] end
|
4556
|
+
@ds.sql.must_equal "SELECT * FROM t FOR UPDATE"
|
4557
|
+
@ds.skip_locked.sql.must_equal "SELECT * FROM t FOR UPDATE SKIP LOCKED"
|
4558
|
+
end
|
4559
|
+
end
|
4560
|
+
|
4537
4561
|
describe "Custom ASTTransformer" do
|
4538
4562
|
it "should transform given objects" do
|
4539
4563
|
c = Class.new(Sequel::ASTTransformer) do
|