sequel 3.18.0 → 3.19.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +18 -0
- data/{COPYING → MIT-LICENSE} +0 -0
- data/Rakefile +2 -2
- data/doc/release_notes/3.19.0.txt +67 -0
- data/doc/sharding.rdoc +25 -0
- data/lib/sequel/adapters/amalgalite.rb +1 -1
- data/lib/sequel/adapters/dbi.rb +1 -1
- data/lib/sequel/adapters/firebird.rb +1 -1
- data/lib/sequel/adapters/informix.rb +1 -1
- data/lib/sequel/adapters/jdbc.rb +27 -12
- data/lib/sequel/adapters/mysql.rb +42 -17
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +1 -1
- data/lib/sequel/adapters/postgres.rb +30 -17
- data/lib/sequel/adapters/shared/mssql.rb +2 -2
- data/lib/sequel/adapters/shared/sqlite.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +39 -16
- data/lib/sequel/adapters/swift/postgres.rb +1 -1
- data/lib/sequel/dataset/actions.rb +2 -2
- data/lib/sequel/dataset/query.rb +3 -3
- data/lib/sequel/model/associations.rb +2 -0
- data/lib/sequel/model/base.rb +2 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/schema.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +1 -1
- data/lib/sequel/plugins/typecast_on_load.rb +1 -1
- data/lib/sequel/plugins/validation_class_methods.rb +32 -3
- data/lib/sequel/version.rb +1 -1
- data/spec/extensions/validation_class_methods_spec.rb +20 -1
- data/spec/integration/associations_test.rb +20 -0
- data/spec/integration/database_test.rb +6 -0
- data/spec/integration/prepared_statement_test.rb +58 -1
- data/spec/integration/schema_test.rb +1 -1
- data/spec/integration/type_test.rb +1 -1
- data/spec/model/associations_spec.rb +46 -2
- data/spec/model/model_spec.rb +11 -0
- metadata +8 -6
data/CHANGELOG
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
=== 3.19.0 (2011-01-03)
|
2
|
+
|
3
|
+
* Handle Date and DateTime types in prepared statements when using the jdbc adapter (jeremyevans)
|
4
|
+
|
5
|
+
* Handle Date, DateTime, Time, SQL::Blob, true, and false in prepared statements when using the SQLite adapter (jeremyevans)
|
6
|
+
|
7
|
+
* Use varbinary(max) instead of image for the generic blob type on MSSQL (jeremyevans)
|
8
|
+
|
9
|
+
* Close prepared statements when disconnecting when using SQLite (jeremyevans)
|
10
|
+
|
11
|
+
* Allow reflecting on validations in the validation_class_methods plugin (jeremyevans)
|
12
|
+
|
13
|
+
* Allow passing a primary key value to the add_* association method (gucki)
|
14
|
+
|
15
|
+
* When typecasting model column values, check the classes of the new and existing values (jeremyevans)
|
16
|
+
|
17
|
+
* Improve type translation performance in the postgres, mysql, and sqlite adapters by using methods instead of procs (jeremyevans)
|
18
|
+
|
1
19
|
=== 3.18.0 (2010-12-01)
|
2
20
|
|
3
21
|
* Allow the user to control how the connection pool deals with attempts to access shards that aren't configured (jeremyevans)
|
data/{COPYING → MIT-LICENSE}
RENAMED
File without changes
|
data/Rakefile
CHANGED
@@ -43,7 +43,7 @@ end
|
|
43
43
|
Rake::RDocTask.new do |rdoc|
|
44
44
|
rdoc.rdoc_dir = "rdoc"
|
45
45
|
rdoc.options += RDOC_OPTS
|
46
|
-
rdoc.rdoc_files.add %w"README.rdoc CHANGELOG
|
46
|
+
rdoc.rdoc_files.add %w"README.rdoc CHANGELOG MIT-LICENSE lib/**/*.rb doc/*.rdoc doc/release_notes/*.txt"
|
47
47
|
end
|
48
48
|
|
49
49
|
### Website
|
@@ -59,7 +59,7 @@ task :website_rdoc=>[:website_rdoc_main, :website_rdoc_adapters, :website_rdoc_p
|
|
59
59
|
Rake::RDocTask.new(:website_rdoc_main) do |rdoc|
|
60
60
|
rdoc.rdoc_dir = "www/public/rdoc"
|
61
61
|
rdoc.options += RDOC_OPTS
|
62
|
-
rdoc.rdoc_files.add %w"README.rdoc CHANGELOG
|
62
|
+
rdoc.rdoc_files.add %w"README.rdoc CHANGELOG MIT-LICENSE lib/*.rb lib/sequel/*.rb lib/sequel/{connection_pool,dataset,database,model}/*.rb doc/*.rdoc doc/release_notes/*.txt lib/sequel/extensions/migration.rb"
|
63
63
|
end
|
64
64
|
|
65
65
|
Rake::RDocTask.new(:website_rdoc_adapters) do |rdoc|
|
@@ -0,0 +1,67 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* The add_* association methods now accept a primary key, and
|
4
|
+
associates the receiver to the associated model object with that
|
5
|
+
primary key:
|
6
|
+
|
7
|
+
artist.add_album(42)
|
8
|
+
# equivalent to: artist.add_album(Album[42])
|
9
|
+
|
10
|
+
* The validation_class_methods plugin now has the ability to
|
11
|
+
reflect on validations:
|
12
|
+
|
13
|
+
Album.plugin :validation_class_methods
|
14
|
+
Album.validates_acceptance_of(:a)
|
15
|
+
Album.validation_reflections
|
16
|
+
# => {:a=>[[:acceptance, {:tag=>:acceptance, :allow_nil=>true,
|
17
|
+
:message=>"is not accepted", :accept=>"1"}]]}
|
18
|
+
|
19
|
+
= Other Improvements
|
20
|
+
|
21
|
+
* In the postgres, mysql, and sqlite adapters, typecasting now uses
|
22
|
+
methods instead of procs. Since methods aren't closures (while
|
23
|
+
procs are), this makes typecasting faster (up to 15%).
|
24
|
+
|
25
|
+
* When typecasting model column values, the classes of the new and
|
26
|
+
existing values are checked in addition to the values themselves.
|
27
|
+
Previously, if the new and existing values were equal (i.e. 1.0
|
28
|
+
and 1), it wouldn't update the value. Now, if the classes are
|
29
|
+
different, it always updates the value.
|
30
|
+
|
31
|
+
* Date and DateTime objects are now handled correctly when using
|
32
|
+
prepared statements/bound variables in the jdbc adapter.
|
33
|
+
|
34
|
+
* Date, DateTime, Time, true, false, and SQL::Blob objects are now
|
35
|
+
handled correctly when using prepared statements/bound variables
|
36
|
+
in the sqlite adapter.
|
37
|
+
|
38
|
+
* Sequel now uses varbinary(max) instead of image for the generic
|
39
|
+
File (blob) type on Microsoft SQL Server. This makes it possible
|
40
|
+
to use an SQL::Blob object as a prepared statement argument.
|
41
|
+
|
42
|
+
* Sequel now handles blobs better in the Amalgalite adapter.
|
43
|
+
|
44
|
+
* When disconnecting a connection using the sqlite adapter, all
|
45
|
+
open prepared statements are now closed first. Previously,
|
46
|
+
attempting to disconnect a connection with open prepared statements
|
47
|
+
resulted in an error.
|
48
|
+
|
49
|
+
* The license file has been renamed from COPYING to MIT-LICENSE, to
|
50
|
+
make it easier to determine at a glance which license is used.
|
51
|
+
|
52
|
+
= Backwards Compatibility
|
53
|
+
|
54
|
+
* Because Sequel switched the generic File type from image to
|
55
|
+
varbinary(max) on Microsoft SQL Server, any migrations/schema
|
56
|
+
modification methods that used the File type will now result in a
|
57
|
+
different column type than before.
|
58
|
+
|
59
|
+
* The MYSQL_TYPE_PROCS, PG_TYPE_PROCS, and SQLITE_TYPE_PROCS
|
60
|
+
constants have been removed from the mysql, postgres, and sqlite
|
61
|
+
adapters, respectively. The UNIX_EPOCH_TIME_FORMAT and
|
62
|
+
FALSE_VALUES constants have also been removed from the sqlite
|
63
|
+
adapter.
|
64
|
+
|
65
|
+
* Typecasting in the sqlite adapters now uses to_i and to_f instead
|
66
|
+
of Integer() and Float() with rescues. If you put non-numeric
|
67
|
+
data in numeric columns on SQLite, this could cause problems.
|
data/doc/sharding.rdoc
CHANGED
@@ -112,6 +112,31 @@ the shard to use. This is fairly easy using a Sequel::Model:
|
|
112
112
|
|
113
113
|
Rainbow.plaintext_for_hash("e580726d31f6e1ad216ffd87279e536d1f74e606")
|
114
114
|
|
115
|
+
The connection pool can be further controlled to change how it handles attempts
|
116
|
+
to access shards that haven't been configured. The default is
|
117
|
+
to assume the :default shard. However, you can specify a
|
118
|
+
different shard using the :servers_hash option when connecting
|
119
|
+
to the database:
|
120
|
+
|
121
|
+
DB = Sequel.connect(..., :servers_hash=>Hash.new(:some_shard))
|
122
|
+
|
123
|
+
You can also use this feature to raise an exception if an
|
124
|
+
unconfigured shard is used:
|
125
|
+
|
126
|
+
DB = Sequel.connect(..., :servers_hash=>Hash.new{raise ...})
|
127
|
+
|
128
|
+
If you specify a :servers_hash option to raise an exception for non configured
|
129
|
+
shards you should also explicitly specify a :read_only entry in your :servers option
|
130
|
+
for the case where a shard is not specified. In most cases it is sufficient
|
131
|
+
to make the :read_only entry the same as the :default shard:
|
132
|
+
|
133
|
+
servers = {:read_only => {}}
|
134
|
+
(('0'..'9').to_a + ('a'..'f').to_a).each do |hex|
|
135
|
+
servers[hex.to_sym] = {:host=>"hash_host_#{hex}"}
|
136
|
+
end
|
137
|
+
DB=Sequel.connect('postgres://hash_host/hashes', :servers=>servers,
|
138
|
+
:servers_hash=>Hash.new{raise Exception.new("Invalid Server")})
|
139
|
+
|
115
140
|
=== Sharding Plugin
|
116
141
|
|
117
142
|
Sequel comes with a sharding plugin that makes it easy to use sharding with model objects.
|
@@ -36,7 +36,7 @@ module Sequel
|
|
36
36
|
# type doesn't match a known type, just return the value.
|
37
37
|
def result_value_of(declared_type, value)
|
38
38
|
if value.is_a?(::Amalgalite::Blob)
|
39
|
-
SQL::Blob.new(value.
|
39
|
+
SQL::Blob.new(value.to_s)
|
40
40
|
elsif value.is_a?(String) && declared_type
|
41
41
|
(meth = self.class.sql_to_method(declared_type.downcase)) ? send(meth, value) : value
|
42
42
|
else
|
data/lib/sequel/adapters/dbi.rb
CHANGED
@@ -201,7 +201,7 @@ module Sequel
|
|
201
201
|
|
202
202
|
# Yield all rows returned by executing the given SQL and converting
|
203
203
|
# the types.
|
204
|
-
def fetch_rows(sql
|
204
|
+
def fetch_rows(sql)
|
205
205
|
execute(sql) do |s|
|
206
206
|
begin
|
207
207
|
@columns = s.fields.map{|c| output_identifier(c.name)}
|
@@ -37,7 +37,7 @@ module Sequel
|
|
37
37
|
class Dataset < Sequel::Dataset
|
38
38
|
SELECT_CLAUSE_METHODS = clause_methods(:select, %w'limit distinct columns from join where having group compounds order')
|
39
39
|
|
40
|
-
def fetch_rows(sql
|
40
|
+
def fetch_rows(sql)
|
41
41
|
execute(sql) do |cursor|
|
42
42
|
begin
|
43
43
|
col_map = nil
|
data/lib/sequel/adapters/jdbc.rb
CHANGED
@@ -191,7 +191,7 @@ module Sequel
|
|
191
191
|
return execute_prepared_statement(sql, opts, &block) if [Symbol, Dataset].any?{|c| sql.is_a?(c)}
|
192
192
|
synchronize(opts[:server]) do |conn|
|
193
193
|
statement(conn) do |stmt|
|
194
|
-
if
|
194
|
+
if block
|
195
195
|
yield log_yield(sql){stmt.executeQuery(sql)}
|
196
196
|
else
|
197
197
|
case opts[:type]
|
@@ -338,14 +338,25 @@ module Sequel
|
|
338
338
|
JavaxNaming::InitialContext.new.lookup(jndi_name).connection
|
339
339
|
end
|
340
340
|
|
341
|
-
# Support
|
342
|
-
def
|
343
|
-
|
344
|
-
|
345
|
-
|
341
|
+
# Support Date objects used in bound variables
|
342
|
+
def java_sql_date(date)
|
343
|
+
java.sql.Date.new(Time.local(date.year, date.month, date.day).to_i * 1000)
|
344
|
+
end
|
345
|
+
|
346
|
+
# Support DateTime objects used in bound variables
|
347
|
+
def java_sql_datetime(datetime)
|
348
|
+
ts = java.sql.Timestamp.new(Time.local(datetime.year, datetime.month, datetime.day, datetime.hour, datetime.min, datetime.sec).to_i * 1000)
|
349
|
+
ts.setNanos((datetime.sec_fraction * (RUBY_VERSION >= '1.9.0' ? 1000000000 : 86400000000000)).to_i)
|
346
350
|
ts
|
347
351
|
end
|
348
352
|
|
353
|
+
# Support fractional seconds for Time objects used in bound variables
|
354
|
+
def java_sql_timestamp(time)
|
355
|
+
ts = java.sql.Timestamp.new(time.to_i * 1000)
|
356
|
+
ts.setNanos(RUBY_VERSION >= '1.9.0' ? time.nsec : time.usec * 1000)
|
357
|
+
ts
|
358
|
+
end
|
359
|
+
|
349
360
|
# Log the given SQL and then execute it on the connection, used by
|
350
361
|
# the transaction code.
|
351
362
|
def log_connection_execute(conn, sql)
|
@@ -389,18 +400,22 @@ module Sequel
|
|
389
400
|
cps.setBytes(i, arg.to_java_bytes)
|
390
401
|
when String
|
391
402
|
cps.setString(i, arg)
|
392
|
-
when Date, Java::JavaSql::Date
|
393
|
-
cps.setDate(i, arg)
|
394
|
-
when DateTime, Java::JavaSql::Timestamp
|
395
|
-
cps.setTimestamp(i, arg)
|
396
|
-
when Time
|
397
|
-
cps.setTimestamp(i, java_sql_timestamp(arg))
|
398
403
|
when Float
|
399
404
|
cps.setDouble(i, arg)
|
400
405
|
when TrueClass, FalseClass
|
401
406
|
cps.setBoolean(i, arg)
|
402
407
|
when nil
|
403
408
|
cps.setNull(i, JavaSQL::Types::NULL)
|
409
|
+
when DateTime
|
410
|
+
cps.setTimestamp(i, java_sql_datetime(arg))
|
411
|
+
when Date
|
412
|
+
cps.setDate(i, java_sql_date(arg))
|
413
|
+
when Time
|
414
|
+
cps.setTimestamp(i, java_sql_timestamp(arg))
|
415
|
+
when Java::JavaSql::Timestamp
|
416
|
+
cps.setTimestamp(i, arg)
|
417
|
+
when Java::JavaSql::Date
|
418
|
+
cps.setDate(i, arg)
|
404
419
|
else
|
405
420
|
cps.setObject(i, arg)
|
406
421
|
end
|
@@ -10,33 +10,58 @@ Sequel.require %w'shared/mysql utils/stored_procedures', 'adapters'
|
|
10
10
|
module Sequel
|
11
11
|
# Module for holding all MySQL-related classes and modules for Sequel.
|
12
12
|
module MySQL
|
13
|
-
|
14
|
-
|
13
|
+
TYPE_TRANSLATOR = tt = Class.new do
|
14
|
+
def boolean(s) s.to_i != 0 end
|
15
|
+
def blob(s) ::Sequel::SQL::Blob.new(s) end
|
16
|
+
def integer(s) s.to_i end
|
17
|
+
def float(s) s.to_f end
|
18
|
+
def decimal(s) ::BigDecimal.new(s) end
|
19
|
+
def date(s) ::Sequel.string_to_date(s) end
|
20
|
+
def time(s) ::Sequel.string_to_time(s) end
|
21
|
+
def timestamp(s) ::Sequel.database_to_application_timestamp(s) end
|
22
|
+
def date_conv(s) ::Sequel::MySQL.convert_date_time(:string_to_date, s) end
|
23
|
+
def time_conv(s) ::Sequel::MySQL.convert_date_time(:string_to_time, s) end
|
24
|
+
def timestamp_conv(s) ::Sequel::MySQL.convert_date_time(:database_to_application_timestamp, s) end
|
25
|
+
end.new
|
15
26
|
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
[
|
20
|
-
[2, 3, 8, 9, 13, 247, 248] =>
|
21
|
-
[4, 5] =>
|
22
|
-
[
|
23
|
-
|
24
|
-
[11] => lambda{|v| convert_date_time(:string_to_time, v)}, # time
|
25
|
-
[249, 250, 251, 252] => lambda{|v| Sequel::SQL::Blob.new(v)} # blob
|
26
|
-
}
|
27
|
-
MYSQL_TYPE_PROCS.each do |k,v|
|
27
|
+
# Hash with integer keys and callable values for converting MySQL types.
|
28
|
+
MYSQL_TYPES = {}
|
29
|
+
{
|
30
|
+
[0, 246] => tt.method(:decimal),
|
31
|
+
[2, 3, 8, 9, 13, 247, 248] => tt.method(:integer),
|
32
|
+
[4, 5] => tt.method(:float),
|
33
|
+
[249, 250, 251, 252] => tt.method(:blob)
|
34
|
+
}.each do |k,v|
|
28
35
|
k.each{|n| MYSQL_TYPES[n] = v}
|
29
36
|
end
|
30
|
-
|
31
|
-
|
37
|
+
|
38
|
+
# Modify the type translator used for the tinyint type based
|
39
|
+
# on the value given.
|
40
|
+
def self.convert_tinyint_to_bool=(v)
|
41
|
+
MYSQL_TYPES[1] = TYPE_TRANSLATOR.method(v ? :boolean : :integer)
|
42
|
+
@convert_tinyint_to_bool = v
|
43
|
+
end
|
44
|
+
self.convert_tinyint_to_bool = convert_tinyint_to_bool
|
32
45
|
|
33
46
|
class << self
|
34
47
|
# By default, Sequel raises an exception if in invalid date or time is used.
|
35
48
|
# However, if this is set to nil or :nil, the adapter treats dates
|
36
49
|
# like 0000-00-00 and times like 838:00:00 as nil values. If set to :string,
|
37
50
|
# it returns the strings as is.
|
38
|
-
|
51
|
+
attr_reader :convert_invalid_date_time
|
52
|
+
end
|
53
|
+
|
54
|
+
# Modify the type translators for the date, time, and timestamp types
|
55
|
+
# depending on the value given.
|
56
|
+
def self.convert_invalid_date_time=(v)
|
57
|
+
MYSQL_TYPES[11] = TYPE_TRANSLATOR.method(v == false ? :time : :time_conv)
|
58
|
+
m = TYPE_TRANSLATOR.method(v == false ? :date : :date_conv)
|
59
|
+
[10, 14].each{|i| MYSQL_TYPES[i] = m}
|
60
|
+
m = TYPE_TRANSLATOR.method(v == false ? :timestamp : :timestamp_conv)
|
61
|
+
[7, 12].each{|i| MYSQL_TYPES[i] = m}
|
62
|
+
@convert_invalid_date_time = v
|
39
63
|
end
|
64
|
+
self.convert_invalid_date_time = false
|
40
65
|
|
41
66
|
# If convert_invalid_date_time is nil, :nil, or :string and
|
42
67
|
# the conversion raises an InvalidValue exception, return v
|
data/lib/sequel/adapters/odbc.rb
CHANGED
@@ -87,7 +87,7 @@ module Sequel
|
|
87
87
|
ODBC_DATE_FORMAT = "{d '%Y-%m-%d'}".freeze
|
88
88
|
TIMESTAMP_FORMAT="{ts '%Y-%m-%d %H:%M:%S'}".freeze
|
89
89
|
|
90
|
-
def fetch_rows(sql
|
90
|
+
def fetch_rows(sql)
|
91
91
|
execute(sql) do |s|
|
92
92
|
i = -1
|
93
93
|
cols = s.columns(true).map{|c| [output_identifier(c.name), i+=1]}
|
@@ -88,32 +88,45 @@ module Sequel
|
|
88
88
|
module Postgres
|
89
89
|
CONVERTED_EXCEPTIONS << PGError
|
90
90
|
|
91
|
-
|
92
|
-
|
91
|
+
TYPE_TRANSLATOR = tt = Class.new do
|
92
|
+
def boolean(s) s == 't' end
|
93
|
+
def bytea(s) ::Sequel::SQL::Blob.new(Adapter.unescape_bytea(s)) end
|
94
|
+
def integer(s) s.to_i end
|
95
|
+
def float(s) s.to_f end
|
96
|
+
def numeric(s) ::BigDecimal.new(s) end
|
97
|
+
def date(s) ::Sequel.string_to_date(s) end
|
98
|
+
def date_iso(s) ::Date.new(*s.split("-").map{|x| x.to_i}) end
|
99
|
+
def time(s) ::Sequel.string_to_time(s) end
|
100
|
+
def timestamp(s) ::Sequel.database_to_application_timestamp(s) end
|
101
|
+
end.new
|
93
102
|
|
94
|
-
#
|
95
|
-
|
96
|
-
|
97
|
-
[
|
98
|
-
[
|
99
|
-
[
|
100
|
-
[
|
101
|
-
[
|
102
|
-
[1083, 1266] =>
|
103
|
-
[1114, 1184] =>
|
104
|
-
}
|
105
|
-
PG_TYPE_PROCS.each do |k,v|
|
103
|
+
# Hash with integer keys and callable values for converting PostgreSQL types.
|
104
|
+
PG_TYPES = {}
|
105
|
+
{
|
106
|
+
[16] => tt.method(:boolean),
|
107
|
+
[17] => tt.method(:bytea),
|
108
|
+
[20, 21, 22, 23, 26] => tt.method(:integer),
|
109
|
+
[700, 701] => tt.method(:float),
|
110
|
+
[790, 1700] => tt.method(:numeric),
|
111
|
+
[1083, 1266] => tt.method(:time),
|
112
|
+
[1114, 1184] => tt.method(:timestamp)
|
113
|
+
}.each do |k,v|
|
106
114
|
k.each{|n| PG_TYPES[n] = v}
|
107
115
|
end
|
108
116
|
|
109
|
-
@use_iso_date_format = true
|
110
|
-
|
111
117
|
class << self
|
112
118
|
# As an optimization, Sequel sets the date style to ISO, so that PostgreSQL provides
|
113
119
|
# the date in a known format that Sequel can parse faster. This can be turned off
|
114
120
|
# if you require a date style other than ISO.
|
115
|
-
|
121
|
+
attr_reader :use_iso_date_format
|
122
|
+
end
|
123
|
+
|
124
|
+
# Modify the type translator for the date type depending on the value given.
|
125
|
+
def self.use_iso_date_format=(v)
|
126
|
+
PG_TYPES[1082] = TYPE_TRANSLATOR.method(v ? :date_iso : :date)
|
127
|
+
@use_iso_date_format = v
|
116
128
|
end
|
129
|
+
self.use_iso_date_format = true
|
117
130
|
|
118
131
|
# PGconn subclass for connection specific methods used with the
|
119
132
|
# pg, postgres, or postgres-pr driver.
|
@@ -181,9 +181,9 @@ module Sequel
|
|
181
181
|
:bit
|
182
182
|
end
|
183
183
|
|
184
|
-
# MSSQL uses
|
184
|
+
# MSSQL uses varbinary(max) type for blobs
|
185
185
|
def type_literal_generic_file(column)
|
186
|
-
:
|
186
|
+
:'varbinary(max)'
|
187
187
|
end
|
188
188
|
|
189
189
|
# support for clustered index type
|
@@ -10,27 +10,36 @@ module Sequel
|
|
10
10
|
# Top level module for holding all SQLite-related modules and classes
|
11
11
|
# for Sequel.
|
12
12
|
module SQLite
|
13
|
-
|
13
|
+
TYPE_TRANSLATOR = tt = Class.new do
|
14
|
+
FALSE_VALUES = %w'0 false f no n'.freeze
|
15
|
+
def boolean(s) !FALSE_VALUES.include?(s.downcase) end
|
16
|
+
def blob(s) ::Sequel::SQL::Blob.new(s) end
|
17
|
+
def integer(s) s.to_i end
|
18
|
+
def float(s) s.to_f end
|
19
|
+
def numeric(s) ::BigDecimal.new(s) rescue s end
|
20
|
+
def date(s) ::Sequel.string_to_date(s) end
|
21
|
+
def time(s) ::Sequel.string_to_time(s) end
|
22
|
+
def timestamp(s) ::Sequel.database_to_application_timestamp(s) end
|
23
|
+
end.new
|
24
|
+
|
25
|
+
# Hash with string keys and callable values for converting SQLite types.
|
14
26
|
SQLITE_TYPES = {}
|
15
|
-
|
16
|
-
|
17
|
-
%w'
|
18
|
-
%w'
|
19
|
-
%w'
|
20
|
-
%w'
|
21
|
-
%w'
|
22
|
-
%w'
|
23
|
-
%w'
|
24
|
-
|
25
|
-
}
|
26
|
-
SQLITE_TYPE_PROCS.each do |k,v|
|
27
|
+
{
|
28
|
+
%w'timestamp datetime' => tt.method(:timestamp),
|
29
|
+
%w'date' => tt.method(:date),
|
30
|
+
%w'time' => tt.method(:time),
|
31
|
+
%w'bit bool boolean' => tt.method(:boolean),
|
32
|
+
%w'integer smallint mediumint int bigint' => tt.method(:integer),
|
33
|
+
%w'numeric decimal money' => tt.method(:numeric),
|
34
|
+
%w'float double real dec fixed' + ['double precision'] => tt.method(:float),
|
35
|
+
%w'blob' => tt.method(:blob)
|
36
|
+
}.each do |k,v|
|
27
37
|
k.each{|n| SQLITE_TYPES[n] = v}
|
28
38
|
end
|
29
39
|
|
30
40
|
# Database class for SQLite databases used with Sequel and the
|
31
41
|
# ruby-sqlite3 driver.
|
32
42
|
class Database < Sequel::Database
|
33
|
-
UNIX_EPOCH_TIME_FORMAT = /\A\d+\z/.freeze
|
34
43
|
include ::Sequel::SQLite::DatabaseMethods
|
35
44
|
|
36
45
|
set_adapter_scheme :sqlite
|
@@ -138,11 +147,24 @@ module Sequel
|
|
138
147
|
o
|
139
148
|
end
|
140
149
|
|
150
|
+
def prepared_statement_argument(arg)
|
151
|
+
case arg
|
152
|
+
when Date, DateTime, Time, TrueClass, FalseClass
|
153
|
+
literal(arg)[1...-1]
|
154
|
+
when SQL::Blob
|
155
|
+
arg.to_blob
|
156
|
+
else
|
157
|
+
arg
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
141
161
|
# Execute a prepared statement on the database using the given name.
|
142
162
|
def execute_prepared_statement(conn, type, name, opts, &block)
|
143
163
|
ps = prepared_statements[name]
|
144
164
|
sql = ps.prepared_sql
|
145
165
|
args = opts[:arguments]
|
166
|
+
ps_args = {}
|
167
|
+
args.each{|k, v| ps_args[k] = prepared_statement_argument(v)}
|
146
168
|
if cpsa = conn.prepared_statements[name]
|
147
169
|
cps, cps_sql = cpsa
|
148
170
|
if cps_sql != sql
|
@@ -155,9 +177,9 @@ module Sequel
|
|
155
177
|
conn.prepared_statements[name] = [cps, sql]
|
156
178
|
end
|
157
179
|
if block
|
158
|
-
log_yield("Executing prepared statement #{name}", args){cps.execute(
|
180
|
+
log_yield("Executing prepared statement #{name}", args){cps.execute(ps_args, &block)}
|
159
181
|
else
|
160
|
-
log_yield("Executing prepared statement #{name}", args){cps.execute!(
|
182
|
+
log_yield("Executing prepared statement #{name}", args){cps.execute!(ps_args){|r|}}
|
161
183
|
case type
|
162
184
|
when :insert
|
163
185
|
conn.last_insert_row_id
|
@@ -174,6 +196,7 @@ module Sequel
|
|
174
196
|
|
175
197
|
# Disconnect given connections from the database.
|
176
198
|
def disconnect_connection(c)
|
199
|
+
c.prepared_statements.each_value{|v| v.first.close}
|
177
200
|
c.close
|
178
201
|
end
|
179
202
|
end
|
@@ -133,11 +133,11 @@ module Sequel
|
|
133
133
|
get(1).nil?
|
134
134
|
end
|
135
135
|
|
136
|
-
# Executes a select query and fetches records,
|
136
|
+
# Executes a select query and fetches records, yielding each record to the
|
137
137
|
# supplied block. The yielded records should be hashes with symbol keys.
|
138
138
|
# This method should probably should not be called by user code, use +each+
|
139
139
|
# instead.
|
140
|
-
def fetch_rows(sql
|
140
|
+
def fetch_rows(sql)
|
141
141
|
raise NotImplemented, NOTIMPL_MSG
|
142
142
|
end
|
143
143
|
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -418,10 +418,10 @@ module Sequel
|
|
418
418
|
table_name = table_alias || table
|
419
419
|
end
|
420
420
|
|
421
|
-
join = if expr.nil? and !
|
421
|
+
join = if expr.nil? and !block
|
422
422
|
SQL::JoinClause.new(type, table, table_alias)
|
423
423
|
elsif using_join
|
424
|
-
raise(Sequel::Error, "can't use a block if providing an array of symbols as expr") if
|
424
|
+
raise(Sequel::Error, "can't use a block if providing an array of symbols as expr") if block
|
425
425
|
SQL::JoinUsingClause.new(expr, type, table, table_alias)
|
426
426
|
else
|
427
427
|
last_alias ||= @opts[:last_joined_table] || first_source_alias
|
@@ -432,7 +432,7 @@ module Sequel
|
|
432
432
|
[k,v]
|
433
433
|
end
|
434
434
|
end
|
435
|
-
if
|
435
|
+
if block
|
436
436
|
expr2 = yield(table_name, last_alias, @opts[:join] || [])
|
437
437
|
expr = expr ? SQL::BooleanExpression.new(:AND, expr, expr2) : expr2
|
438
438
|
end
|
@@ -1067,6 +1067,8 @@ module Sequel
|
|
1067
1067
|
klass = opts.associated_class
|
1068
1068
|
if o.is_a?(Hash)
|
1069
1069
|
o = klass.new(o)
|
1070
|
+
elsif o.is_a?(Integer) || o.is_a?(String) || o.is_a?(Array)
|
1071
|
+
o = klass[o]
|
1070
1072
|
elsif !o.is_a?(klass)
|
1071
1073
|
raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}")
|
1072
1074
|
end
|
data/lib/sequel/model/base.rb
CHANGED
@@ -205,7 +205,7 @@ module Sequel
|
|
205
205
|
# Artist.server!(:server1)
|
206
206
|
def def_dataset_method(*args, &block)
|
207
207
|
raise(Error, "No arguments given") if args.empty?
|
208
|
-
if
|
208
|
+
if block
|
209
209
|
raise(Error, "Defining a dataset method using a block requires only one argument") if args.length > 1
|
210
210
|
meth = args.first
|
211
211
|
@dataset_methods[meth] = block
|
@@ -729,7 +729,7 @@ module Sequel
|
|
729
729
|
# If the column isn't in @values, we can't assume it is
|
730
730
|
# NULL in the database, so assume it has changed.
|
731
731
|
v = typecast_value(column, value)
|
732
|
-
if new? || !@values.include?(column) || v != @values[column]
|
732
|
+
if new? || !@values.include?(column) || v != (c = @values[column]) || v.class != c.class
|
733
733
|
changed_columns << column unless changed_columns.include?(column)
|
734
734
|
@values[column] = v
|
735
735
|
end
|
@@ -68,7 +68,7 @@ module Sequel
|
|
68
68
|
# The class_table_inheritance plugin requires the lazy_attributes plugin
|
69
69
|
# to handle lazily-loaded attributes for subclass instances returned
|
70
70
|
# by superclass methods.
|
71
|
-
def self.apply(model, opts={}
|
71
|
+
def self.apply(model, opts={})
|
72
72
|
model.plugin :lazy_attributes
|
73
73
|
end
|
74
74
|
|
@@ -81,7 +81,7 @@ module Sequel
|
|
81
81
|
# * :table_map - Hash with class name symbol keys and table name symbol
|
82
82
|
# values. Necessary if the implicit table name for the model class
|
83
83
|
# does not match the database table name
|
84
|
-
def self.configure(model, opts={}
|
84
|
+
def self.configure(model, opts={})
|
85
85
|
model.instance_eval do
|
86
86
|
m = method(:constantize)
|
87
87
|
@cti_base_model = self
|
@@ -20,7 +20,7 @@ module Sequel
|
|
20
20
|
module ClassMethods
|
21
21
|
# Creates table, using the column information from set_schema.
|
22
22
|
def create_table(*args, &block)
|
23
|
-
set_schema(*args, &block) if
|
23
|
+
set_schema(*args, &block) if block
|
24
24
|
db.create_table(table_name, :generator=>@schema)
|
25
25
|
@db_schema = get_db_schema(true)
|
26
26
|
columns
|
@@ -20,7 +20,7 @@ module Sequel
|
|
20
20
|
# the model object, you'll get Date objects instead of strings.
|
21
21
|
module TypecastOnLoad
|
22
22
|
# Call add_typecast_on_load_columns on the passed column arguments.
|
23
|
-
def self.configure(model, *columns
|
23
|
+
def self.configure(model, *columns)
|
24
24
|
model.instance_eval do
|
25
25
|
@typecast_on_load_columns ||= []
|
26
26
|
add_typecast_on_load_columns(*columns)
|
@@ -6,7 +6,9 @@ module Sequel
|
|
6
6
|
# for the legacy class-level validation methods (e.g. validates_presence_of :column).
|
7
7
|
#
|
8
8
|
# It is recommended to use the validation_helpers plugin instead of this one,
|
9
|
-
# as it is less complex and more flexible.
|
9
|
+
# as it is less complex and more flexible. However, this plugin provides reflection
|
10
|
+
# support, since it is class-level, while the instance-level validation_helpers
|
11
|
+
# plugin does not.
|
10
12
|
#
|
11
13
|
# Usage:
|
12
14
|
#
|
@@ -21,14 +23,21 @@ module Sequel
|
|
21
23
|
model.class_eval do
|
22
24
|
@validation_mutex = Mutex.new
|
23
25
|
@validations = {}
|
26
|
+
@validation_reflections = {}
|
24
27
|
end
|
25
28
|
end
|
26
29
|
|
27
30
|
module ClassMethods
|
28
|
-
# A hash of
|
31
|
+
# A hash of validations for this model class. Keys are column symbols,
|
29
32
|
# values are arrays of validation procs.
|
30
33
|
attr_reader :validations
|
31
34
|
|
35
|
+
# A hash of validation reflections for this model class. Keys are column
|
36
|
+
# symbols, values are an array of two element arrays, with the first element
|
37
|
+
# being the validation type symbol and the second being a hash of validation
|
38
|
+
# options.
|
39
|
+
attr_reader :validation_reflections
|
40
|
+
|
32
41
|
# The Generator class is used to generate validation definitions using
|
33
42
|
# the validates {} idiom.
|
34
43
|
class Generator
|
@@ -49,12 +58,16 @@ module Sequel
|
|
49
58
|
!validations.empty?
|
50
59
|
end
|
51
60
|
|
52
|
-
# Setup the validations hash in the subclass
|
61
|
+
# Setup the validations and validation_reflections hash in the subclass.
|
53
62
|
def inherited(subclass)
|
54
63
|
super
|
64
|
+
vr = @validation_reflections
|
55
65
|
subclass.class_eval do
|
56
66
|
@validation_mutex = Mutex.new
|
57
67
|
@validations = {}
|
68
|
+
h = {}
|
69
|
+
vr.each{|k,v| h[k] = v.dup}
|
70
|
+
@validation_reflections = h
|
58
71
|
end
|
59
72
|
end
|
60
73
|
|
@@ -115,6 +128,7 @@ module Sequel
|
|
115
128
|
:accept => '1',
|
116
129
|
:tag => :acceptance,
|
117
130
|
}.merge!(extract_options!(atts))
|
131
|
+
reflect_validation(:acceptance, opts, atts)
|
118
132
|
atts << opts
|
119
133
|
validates_each(*atts) do |o, a, v|
|
120
134
|
o.errors.add(a, opts[:message]) unless v == opts[:accept]
|
@@ -136,6 +150,7 @@ module Sequel
|
|
136
150
|
:message => 'is not confirmed',
|
137
151
|
:tag => :confirmation,
|
138
152
|
}.merge!(extract_options!(atts))
|
153
|
+
reflect_validation(:confirmation, opts, atts)
|
139
154
|
atts << opts
|
140
155
|
validates_each(*atts) do |o, a, v|
|
141
156
|
o.errors.add(a, opts[:message]) unless v == o.send(:"#{a}_confirmation")
|
@@ -205,6 +220,7 @@ module Sequel
|
|
205
220
|
raise ArgumentError, "A regular expression must be supplied as the :with option of the options hash"
|
206
221
|
end
|
207
222
|
|
223
|
+
reflect_validation(:format, opts, atts)
|
208
224
|
atts << opts
|
209
225
|
validates_each(*atts) do |o, a, v|
|
210
226
|
o.errors.add(a, opts[:message]) unless v.to_s =~ opts[:with]
|
@@ -231,6 +247,7 @@ module Sequel
|
|
231
247
|
}.merge!(extract_options!(atts))
|
232
248
|
|
233
249
|
opts[:tag] ||= ([:length] + [:maximum, :minimum, :is, :within].reject{|x| !opts.include?(x)}).join('-').to_sym
|
250
|
+
reflect_validation(:length, opts, atts)
|
234
251
|
atts << opts
|
235
252
|
validates_each(*atts) do |o, a, v|
|
236
253
|
if m = opts[:maximum]
|
@@ -261,6 +278,7 @@ module Sequel
|
|
261
278
|
opts = {
|
262
279
|
:tag => :not_string,
|
263
280
|
}.merge!(extract_options!(atts))
|
281
|
+
reflect_validation(:not_string, opts, atts)
|
264
282
|
atts << opts
|
265
283
|
validates_each(*atts) do |o, a, v|
|
266
284
|
if v.is_a?(String)
|
@@ -286,6 +304,7 @@ module Sequel
|
|
286
304
|
:message => 'is not a number',
|
287
305
|
:tag => :numericality,
|
288
306
|
}.merge!(extract_options!(atts))
|
307
|
+
reflect_validation(:numericality, opts, atts)
|
289
308
|
atts << opts
|
290
309
|
validates_each(*atts) do |o, a, v|
|
291
310
|
begin
|
@@ -310,6 +329,7 @@ module Sequel
|
|
310
329
|
:message => 'is not present',
|
311
330
|
:tag => :presence,
|
312
331
|
}.merge!(extract_options!(atts))
|
332
|
+
reflect_validation(:presence, opts, atts)
|
313
333
|
atts << opts
|
314
334
|
validates_each(*atts) do |o, a, v|
|
315
335
|
o.errors.add(a, opts[:message]) if v.blank? && v != false
|
@@ -327,6 +347,7 @@ module Sequel
|
|
327
347
|
raise ArgumentError, "The :in parameter is required, and respond to include?"
|
328
348
|
end
|
329
349
|
opts[:message] ||= "is not in range or set: #{opts[:in].inspect}"
|
350
|
+
reflect_validation(:inclusion, opts, atts)
|
330
351
|
atts << opts
|
331
352
|
validates_each(*atts) do |o, a, v|
|
332
353
|
o.errors.add(a, opts[:message]) unless opts[:in].include?(v)
|
@@ -355,6 +376,7 @@ module Sequel
|
|
355
376
|
:tag => :uniqueness,
|
356
377
|
}.merge!(extract_options!(atts))
|
357
378
|
|
379
|
+
reflect_validation(:uniqueness, opts, atts)
|
358
380
|
atts << opts
|
359
381
|
validates_each(*atts) do |o, a, v|
|
360
382
|
error_field = a
|
@@ -391,6 +413,13 @@ module Sequel
|
|
391
413
|
array.last.is_a?(Hash) ? array.pop : {}
|
392
414
|
end
|
393
415
|
|
416
|
+
# Add the validation reflection to the class's validations.
|
417
|
+
def reflect_validation(type, opts, atts)
|
418
|
+
atts.each do |att|
|
419
|
+
(validation_reflections[att] ||= []) << [type, opts]
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
394
423
|
# Handle the :if option for validations
|
395
424
|
def validation_if_proc(o, i)
|
396
425
|
case i
|
data/lib/sequel/version.rb
CHANGED
@@ -3,7 +3,7 @@ module Sequel
|
|
3
3
|
MAJOR = 3
|
4
4
|
# The minor version of Sequel. Bumped for every non-patch level
|
5
5
|
# release, generally around once a month.
|
6
|
-
MINOR =
|
6
|
+
MINOR = 19
|
7
7
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
8
8
|
# releases that fix regressions from previous versions.
|
9
9
|
TINY = 0
|
@@ -9,11 +9,30 @@ describe Sequel::Model do
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
specify "should respond to
|
12
|
+
specify "should respond to validations, has_validations?, and validation_reflections" do
|
13
13
|
@c.should respond_to(:validations)
|
14
14
|
@c.should respond_to(:has_validations?)
|
15
|
+
@c.should respond_to(:validation_reflections)
|
15
16
|
end
|
16
17
|
|
18
|
+
specify "should be able to reflect on validations" do
|
19
|
+
@c.validation_reflections.should == {}
|
20
|
+
@c.validates_acceptance_of(:a)
|
21
|
+
@c.validation_reflections.should == {:a=>[[:acceptance, {:tag=>:acceptance, :message=>"is not accepted", :allow_nil=>true, :accept=>"1"}]]}
|
22
|
+
@c.validates_presence_of(:a)
|
23
|
+
@c.validation_reflections[:a].length.should == 2
|
24
|
+
@c.validation_reflections[:a].last.should == [:presence, {:tag=>:presence, :message=>"is not present"}]
|
25
|
+
end
|
26
|
+
|
27
|
+
specify "should handle validation reflections correctly when subclassing" do
|
28
|
+
@c.validates_acceptance_of(:a)
|
29
|
+
c = Class.new(@c)
|
30
|
+
c.validation_reflections.map{|k,v| k}.should == [:a]
|
31
|
+
c.validates_presence_of(:a)
|
32
|
+
@c.validation_reflections.should == {:a=>[[:acceptance, {:tag=>:acceptance, :message=>"is not accepted", :allow_nil=>true, :accept=>"1"}]]}
|
33
|
+
c.validation_reflections[:a].last.should == [:presence, {:tag=>:presence, :message=>"is not present"}]
|
34
|
+
end
|
35
|
+
|
17
36
|
specify "should acccept validation definitions using validates_each" do
|
18
37
|
@c.validates_each(:xx, :yy) {|o, a, v| o.errors[a] << 'too low' if v < 50}
|
19
38
|
o = @c.new
|
@@ -196,6 +196,16 @@ describe "Sequel::Model Simple Associations" do
|
|
196
196
|
@album.tags_dataset.first[:name].should == 'T2'
|
197
197
|
end
|
198
198
|
|
199
|
+
specify "should have add method accept primary key and add related records" do
|
200
|
+
@artist.remove_all_albums
|
201
|
+
@artist.add_album(@album.id)
|
202
|
+
@artist.albums_dataset.first[:id].should == @album.id
|
203
|
+
|
204
|
+
@album.remove_all_tags
|
205
|
+
@album.add_tag(@tag.id)
|
206
|
+
@album.tags_dataset.first[:id].should == @tag.id
|
207
|
+
end
|
208
|
+
|
199
209
|
specify "should have remove method accept primary key and remove related album" do
|
200
210
|
@artist.add_album(@album)
|
201
211
|
@artist.reload.remove_album(@album.id)
|
@@ -287,6 +297,16 @@ describe "Sequel::Model Composite Key Associations" do
|
|
287
297
|
@album.tags_dataset.first[:name].should == 'T2'
|
288
298
|
end
|
289
299
|
|
300
|
+
specify "should have add method accept primary key and add related records" do
|
301
|
+
@artist.remove_all_albums
|
302
|
+
@artist.add_album([@album.id1, @album.id2])
|
303
|
+
@artist.albums_dataset.first.pk.should == [@album.id1, @album.id2]
|
304
|
+
|
305
|
+
@album.remove_all_tags
|
306
|
+
@album.add_tag([@tag.id1, @tag.id2])
|
307
|
+
@album.tags_dataset.first.pk.should == [@tag.id1, @tag.id2]
|
308
|
+
end
|
309
|
+
|
290
310
|
specify "should have remove method accept primary key and remove related album" do
|
291
311
|
@artist.add_album(@album)
|
292
312
|
@artist.reload.remove_album([@album.id1, @album.id2])
|
@@ -8,6 +8,12 @@ describe Sequel::Database do
|
|
8
8
|
INTEGRATION_DB.pool.size.should == 0
|
9
9
|
end
|
10
10
|
|
11
|
+
specify "should provide disconnect functionality after preparing a connection" do
|
12
|
+
INTEGRATION_DB['SELECT 1'].prepare(:first, :a).call
|
13
|
+
INTEGRATION_DB.disconnect
|
14
|
+
INTEGRATION_DB.pool.size.should == 0
|
15
|
+
end
|
16
|
+
|
11
17
|
specify "should raise Sequel::DatabaseError on invalid SQL" do
|
12
18
|
proc{INTEGRATION_DB << "SELECT"}.should raise_error(Sequel::DatabaseError)
|
13
19
|
end
|
@@ -12,7 +12,6 @@ describe "Prepared Statements and Bound Arguments" do
|
|
12
12
|
@ds.meta_def(:ba) do |sym|
|
13
13
|
prepared_arg_placeholder == '$' ? :"#{sym}__int" : sym
|
14
14
|
end
|
15
|
-
clear_sqls
|
16
15
|
end
|
17
16
|
after do
|
18
17
|
INTEGRATION_DB.drop_table(:items)
|
@@ -211,3 +210,61 @@ describe "Prepared Statements and Bound Arguments" do
|
|
211
210
|
end
|
212
211
|
end
|
213
212
|
end
|
213
|
+
|
214
|
+
describe "Bound Argument Types" do
|
215
|
+
before do
|
216
|
+
INTEGRATION_DB.create_table!(:items) do
|
217
|
+
primary_key :id
|
218
|
+
Date :d
|
219
|
+
DateTime :dt
|
220
|
+
File :file
|
221
|
+
String :s
|
222
|
+
Time :t
|
223
|
+
Float :f
|
224
|
+
TrueClass :b
|
225
|
+
end
|
226
|
+
@ds = INTEGRATION_DB[:items]
|
227
|
+
@vs = {:d=>Date.civil(2010, 10, 11), :dt=>DateTime.civil(2010, 10, 12, 13, 14, 15), :f=>1.0, :s=>'str', :t=>Time.at(20101010), :file=>Sequel::SQL::Blob.new('blob'), :b=>true}
|
228
|
+
@ds.insert(@vs)
|
229
|
+
@ds.meta_def(:ba) do |sym, type|
|
230
|
+
prepared_arg_placeholder == '$' ? :"#{sym}__#{type}" : sym
|
231
|
+
end
|
232
|
+
end
|
233
|
+
after do
|
234
|
+
Sequel.datetime_class = Time
|
235
|
+
if INTEGRATION_DB.adapter_scheme == :jdbc && INTEGRATION_DB.database_type == :sqlite
|
236
|
+
INTEGRATION_DB.synchronize{|c| c.prepared_statements.each{|k, ps| ps[1].close}.clear}
|
237
|
+
end
|
238
|
+
INTEGRATION_DB.drop_table(:items)
|
239
|
+
end
|
240
|
+
|
241
|
+
cspecify "should handle date type", [:do, :sqlite], :mssql, [:jdbc, :sqlite] do
|
242
|
+
@ds.filter(:d=>@ds.ba(:$x, :date)).prepare(:first, :ps_date).call(:x=>@vs[:d])[:d].should == @vs[:d]
|
243
|
+
end
|
244
|
+
|
245
|
+
cspecify "should handle datetime type", [:do], [:mysql2], [:swift], [:jdbc, :sqlite] do
|
246
|
+
Sequel.datetime_class = DateTime
|
247
|
+
@ds.filter(:dt=>@ds.ba(:$x, :timestamp)).prepare(:first, :ps_datetime).call(:x=>@vs[:dt])[:dt].should == @vs[:dt]
|
248
|
+
end
|
249
|
+
|
250
|
+
cspecify "should handle time type", [:do], [:jdbc, :sqlite] do
|
251
|
+
@ds.filter(:t=>@ds.ba(:$x, :timestamp)).prepare(:first, :ps_time).call(:x=>@vs[:t])[:t].should == @vs[:t]
|
252
|
+
end
|
253
|
+
|
254
|
+
cspecify "should handle blob type", [:swift], [:jdbc, :postgres] do
|
255
|
+
@ds.filter(:file=>@ds.ba(:$x, :bytea)).prepare(:first, :ps_blob).call(:x=>@vs[:file])[:file].should == @vs[:file]
|
256
|
+
end
|
257
|
+
|
258
|
+
specify "should handle float type" do
|
259
|
+
@ds.filter(:f=>@ds.ba(:$x, :"double precision")).prepare(:first, :ps_float).call(:x=>@vs[:f])[:f].should == @vs[:f]
|
260
|
+
end
|
261
|
+
|
262
|
+
specify "should handle string type" do
|
263
|
+
@ds.filter(:s=>@ds.ba(:$x, :text)).prepare(:first, :ps_string).call(:x=>@vs[:s])[:s].should == @vs[:s]
|
264
|
+
end
|
265
|
+
|
266
|
+
cspecify "should handle boolean type", [:do, :sqlite], [:odbc, :mssql], [:jdbc, :sqlite] do
|
267
|
+
@ds.filter(:b=>@ds.ba(:$x, :boolean)).prepare(:first, :ps_string).call(:x=>@vs[:b])[:b].should == @vs[:b]
|
268
|
+
end
|
269
|
+
end unless INTEGRATION_DB.adapter_scheme == :swift && INTEGRATION_DB.database_type == :postgres
|
270
|
+
|
@@ -9,10 +9,10 @@ describe "Database schema parser" do
|
|
9
9
|
clear_sqls
|
10
10
|
end
|
11
11
|
after do
|
12
|
-
INTEGRATION_DB.drop_table(:items) if INTEGRATION_DB.table_exists?(:items)
|
13
12
|
INTEGRATION_DB.identifier_output_method = @iom
|
14
13
|
INTEGRATION_DB.identifier_input_method = @iim
|
15
14
|
INTEGRATION_DB.default_schema = @defsch
|
15
|
+
INTEGRATION_DB.drop_table(:items) if INTEGRATION_DB.table_exists?(:items)
|
16
16
|
end
|
17
17
|
|
18
18
|
specify "should handle a database with a identifier_output_method" do
|
@@ -79,7 +79,7 @@ describe "Supported types" do
|
|
79
79
|
ds.first[:tim].strftime('%Y%m%d%H%M%S').should == t.strftime('%Y%m%d%H%M%S')
|
80
80
|
end
|
81
81
|
|
82
|
-
cspecify "should support generic file type", [:do], [:odbc, :mssql], [:mysql2], [:swift] do
|
82
|
+
cspecify "should support generic file type", [:do], [:odbc, :mssql], [:mysql2], [:swift], [:jdbc, :postgres] do
|
83
83
|
ds = create_items_table_with_column(:name, File)
|
84
84
|
ds.insert(:name => ("a\0"*300).to_sequel_blob)
|
85
85
|
ds.all.should == [{:name=>("a\0"*300).to_sequel_blob}]
|
@@ -1077,7 +1077,11 @@ describe Sequel::Model, "one_to_many" do
|
|
1077
1077
|
@c1 = Class.new(Sequel::Model(:attributes)) do
|
1078
1078
|
def _refresh(ds); end
|
1079
1079
|
unrestrict_primary_key
|
1080
|
-
columns :id, :node_id, :y
|
1080
|
+
columns :id, :node_id, :y, :z
|
1081
|
+
|
1082
|
+
def self.[](id)
|
1083
|
+
load(id.is_a?(Array) ? {:id => id[0], :z => id[1]} : {:id => id})
|
1084
|
+
end
|
1081
1085
|
end
|
1082
1086
|
|
1083
1087
|
@c2 = Class.new(Sequel::Model(:nodes)) do
|
@@ -1228,6 +1232,14 @@ describe Sequel::Model, "one_to_many" do
|
|
1228
1232
|
MODEL_DB.sqls.first.should =~ /INSERT INTO attributes \((node_)?id, (node_)?id\) VALUES \(1?234, 1?234\)/
|
1229
1233
|
end
|
1230
1234
|
|
1235
|
+
it "should accept a primary key for the add_ method" do
|
1236
|
+
@c2.one_to_many :attributes, :class => @c1
|
1237
|
+
n = @c2.new(:id => 1234)
|
1238
|
+
MODEL_DB.reset
|
1239
|
+
@c1.load(:node_id => 1234, :id => 234).should == n.add_attribute(234)
|
1240
|
+
MODEL_DB.sqls.first.should == "UPDATE attributes SET node_id = 1234 WHERE (id = 234)"
|
1241
|
+
end
|
1242
|
+
|
1231
1243
|
it "should raise an error in the add_ method if the passed associated object is not of the correct type" do
|
1232
1244
|
@c2.one_to_many :attributes, :class => @c1
|
1233
1245
|
proc{@c2.new(:id => 1234).add_attribute(@c2.new)}.should raise_error(Sequel::Error)
|
@@ -1286,6 +1298,16 @@ describe Sequel::Model, "one_to_many" do
|
|
1286
1298
|
a.should == n.add_attribute(a)
|
1287
1299
|
MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id = 1234|y = 5), (node_id = 1234|y = 5) WHERE \(id = 2345\)/
|
1288
1300
|
end
|
1301
|
+
|
1302
|
+
it "should have add_ method accept a composite key" do
|
1303
|
+
@c1.set_primary_key :id, :z
|
1304
|
+
@c2.one_to_many :attributes, :class => @c1, :key =>[:node_id, :y], :primary_key=>[:id, :x]
|
1305
|
+
|
1306
|
+
n = @c2.load(:id => 1234, :x=>5)
|
1307
|
+
a = @c1.load(:id => 2345, :z => 8, :node_id => 1234, :y=>5)
|
1308
|
+
a.should == n.add_attribute([2345, 8])
|
1309
|
+
MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id|y) = (1234|5), (node_id|y) = (1234|5) WHERE \(\((id|z) = (2345|8)\) AND \((id|z) = (2345|8)\)\)/
|
1310
|
+
end
|
1289
1311
|
|
1290
1312
|
it "should have remove_ method respect composite keys" do
|
1291
1313
|
@c2.one_to_many :attributes, :class => @c1, :key =>[:node_id, :y], :primary_key=>[:id, :x]
|
@@ -1834,9 +1856,12 @@ describe Sequel::Model, "many_to_many" do
|
|
1834
1856
|
def self.to_s; 'Attribute'; end
|
1835
1857
|
columns :id, :y
|
1836
1858
|
def _refresh(ds)
|
1837
|
-
self.id = 1
|
1838
1859
|
self
|
1839
1860
|
end
|
1861
|
+
|
1862
|
+
def self.[](id)
|
1863
|
+
load(id.is_a?(Array) ? {:id => id[0], :y => id[1]} : {:id => id})
|
1864
|
+
end
|
1840
1865
|
end
|
1841
1866
|
|
1842
1867
|
@c2 = Class.new(Sequel::Model(:nodes)) do
|
@@ -2063,6 +2088,17 @@ describe Sequel::Model, "many_to_many" do
|
|
2063
2088
|
].should(include(MODEL_DB.sqls.first))
|
2064
2089
|
end
|
2065
2090
|
|
2091
|
+
it "should define an add_ method that works with a primary key" do
|
2092
|
+
@c2.many_to_many :attributes, :class => @c1
|
2093
|
+
|
2094
|
+
n = @c2.load(:id => 1234)
|
2095
|
+
a = @c1.load(:id => 2345)
|
2096
|
+
a.should == n.add_attribute(2345)
|
2097
|
+
['INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (1234, 2345)',
|
2098
|
+
'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (2345, 1234)'
|
2099
|
+
].should(include(MODEL_DB.sqls.first))
|
2100
|
+
end
|
2101
|
+
|
2066
2102
|
it "should allow passing a hash to the add_ method which creates a new record" do
|
2067
2103
|
@c2.many_to_many :attributes, :class => @c1
|
2068
2104
|
|
@@ -2148,6 +2184,14 @@ describe Sequel::Model, "many_to_many" do
|
|
2148
2184
|
v.should == true
|
2149
2185
|
end
|
2150
2186
|
end
|
2187
|
+
|
2188
|
+
it "should have the add_ method respect composite keys" do
|
2189
|
+
@c2.many_to_many :attributes, :class => @c1, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :left_primary_key=>[:id, :x], :right_primary_key=>[:id, :y]
|
2190
|
+
n = @c2.load(:id => 1234, :x=>5)
|
2191
|
+
a = @c1.load(:id => 2345, :y=>8)
|
2192
|
+
a.should == n.add_attribute([2345, 8])
|
2193
|
+
MODEL_DB.sqls.first.should =~ /INSERT INTO attributes_nodes \([lr][12], [lr][12], [lr][12], [lr][12]\) VALUES \((1234|5|2345|8), (1234|5|2345|8), (1234|5|2345|8), (1234|5|2345|8)\)/
|
2194
|
+
end
|
2151
2195
|
|
2152
2196
|
it "should have the remove_ method respect the :left_primary_key and :right_primary_key options" do
|
2153
2197
|
@c2.many_to_many :attributes, :class => @c1, :left_primary_key=>:xxx, :right_primary_key=>:yyy
|
data/spec/model/model_spec.rb
CHANGED
@@ -452,6 +452,17 @@ describe Sequel::Model, "attribute accessors" do
|
|
452
452
|
o.x = '34'
|
453
453
|
o.x.should == 34
|
454
454
|
end
|
455
|
+
|
456
|
+
it "should typecast if the new value is the same as the existing but has a different class" do
|
457
|
+
@c.set_dataset(@dataset.select(:y))
|
458
|
+
o = @c.new
|
459
|
+
|
460
|
+
o.x = 34
|
461
|
+
o.x = 34.0
|
462
|
+
o.x.should == 34.0
|
463
|
+
o.x = 34
|
464
|
+
o.x.should == 34
|
465
|
+
end
|
455
466
|
end
|
456
467
|
|
457
468
|
describe Sequel::Model, ".[]" do
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 75
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 3
|
8
|
-
-
|
8
|
+
- 19
|
9
9
|
- 0
|
10
|
-
version: 3.
|
10
|
+
version: 3.19.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jeremy Evans
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2011-01-03 00:00:00 -08:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
@@ -28,7 +28,7 @@ extensions: []
|
|
28
28
|
extra_rdoc_files:
|
29
29
|
- README.rdoc
|
30
30
|
- CHANGELOG
|
31
|
-
-
|
31
|
+
- MIT-LICENSE
|
32
32
|
- doc/advanced_associations.rdoc
|
33
33
|
- doc/cheat_sheet.rdoc
|
34
34
|
- doc/dataset_filtering.rdoc
|
@@ -82,8 +82,9 @@ extra_rdoc_files:
|
|
82
82
|
- doc/release_notes/3.16.0.txt
|
83
83
|
- doc/release_notes/3.17.0.txt
|
84
84
|
- doc/release_notes/3.18.0.txt
|
85
|
+
- doc/release_notes/3.19.0.txt
|
85
86
|
files:
|
86
|
-
-
|
87
|
+
- MIT-LICENSE
|
87
88
|
- CHANGELOG
|
88
89
|
- README.rdoc
|
89
90
|
- Rakefile
|
@@ -131,6 +132,7 @@ files:
|
|
131
132
|
- doc/release_notes/3.16.0.txt
|
132
133
|
- doc/release_notes/3.17.0.txt
|
133
134
|
- doc/release_notes/3.18.0.txt
|
135
|
+
- doc/release_notes/3.19.0.txt
|
134
136
|
- doc/sharding.rdoc
|
135
137
|
- doc/sql.rdoc
|
136
138
|
- doc/virtual_rows.rdoc
|