sequel 3.18.0 → 3.19.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.
- 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
|