sequel 2.10.0 → 2.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/CHANGELOG +51 -1
  2. data/README.rdoc +2 -2
  3. data/Rakefile +2 -2
  4. data/doc/advanced_associations.rdoc +6 -18
  5. data/doc/release_notes/1.0.txt +38 -0
  6. data/doc/release_notes/1.1.txt +143 -0
  7. data/doc/release_notes/1.3.txt +101 -0
  8. data/doc/release_notes/1.4.0.txt +53 -0
  9. data/doc/release_notes/1.5.0.txt +155 -0
  10. data/doc/release_notes/2.0.0.txt +298 -0
  11. data/doc/release_notes/2.1.0.txt +271 -0
  12. data/doc/release_notes/2.10.0.txt +328 -0
  13. data/doc/release_notes/2.11.0.txt +215 -0
  14. data/doc/release_notes/2.2.0.txt +253 -0
  15. data/doc/release_notes/2.3.0.txt +88 -0
  16. data/doc/release_notes/2.4.0.txt +106 -0
  17. data/doc/release_notes/2.5.0.txt +137 -0
  18. data/doc/release_notes/2.6.0.txt +157 -0
  19. data/doc/release_notes/2.7.0.txt +166 -0
  20. data/doc/release_notes/2.8.0.txt +171 -0
  21. data/doc/release_notes/2.9.0.txt +97 -0
  22. data/lib/sequel_core/adapters/ado.rb +3 -0
  23. data/lib/sequel_core/adapters/db2.rb +0 -11
  24. data/lib/sequel_core/adapters/dbi.rb +0 -11
  25. data/lib/sequel_core/adapters/do.rb +0 -12
  26. data/lib/sequel_core/adapters/firebird.rb +21 -16
  27. data/lib/sequel_core/adapters/informix.rb +1 -11
  28. data/lib/sequel_core/adapters/jdbc.rb +1 -13
  29. data/lib/sequel_core/adapters/jdbc/h2.rb +3 -11
  30. data/lib/sequel_core/adapters/jdbc/mysql.rb +0 -17
  31. data/lib/sequel_core/adapters/jdbc/postgresql.rb +3 -15
  32. data/lib/sequel_core/adapters/mysql.rb +31 -27
  33. data/lib/sequel_core/adapters/odbc.rb +34 -28
  34. data/lib/sequel_core/adapters/openbase.rb +0 -11
  35. data/lib/sequel_core/adapters/oracle.rb +11 -9
  36. data/lib/sequel_core/adapters/postgres.rb +14 -17
  37. data/lib/sequel_core/adapters/shared/mssql.rb +6 -15
  38. data/lib/sequel_core/adapters/shared/mysql.rb +29 -14
  39. data/lib/sequel_core/adapters/shared/oracle.rb +4 -0
  40. data/lib/sequel_core/adapters/shared/postgres.rb +30 -35
  41. data/lib/sequel_core/adapters/shared/progress.rb +4 -0
  42. data/lib/sequel_core/adapters/shared/sqlite.rb +73 -13
  43. data/lib/sequel_core/adapters/sqlite.rb +8 -18
  44. data/lib/sequel_core/adapters/utils/date_format.rb +21 -0
  45. data/lib/sequel_core/{dataset → adapters/utils}/stored_procedures.rb +0 -0
  46. data/lib/sequel_core/{dataset → adapters/utils}/unsupported.rb +0 -0
  47. data/lib/sequel_core/core_ext.rb +1 -1
  48. data/lib/sequel_core/core_sql.rb +9 -4
  49. data/lib/sequel_core/database.rb +63 -62
  50. data/lib/sequel_core/dataset.rb +9 -4
  51. data/lib/sequel_core/dataset/convenience.rb +10 -9
  52. data/lib/sequel_core/dataset/prepared_statements.rb +1 -1
  53. data/lib/sequel_core/dataset/sql.rb +130 -36
  54. data/lib/sequel_core/schema/sql.rb +2 -2
  55. data/lib/sequel_core/sql.rb +44 -51
  56. data/lib/sequel_core/version.rb +1 -1
  57. data/lib/sequel_model/associations.rb +25 -17
  58. data/lib/sequel_model/base.rb +35 -7
  59. data/lib/sequel_model/caching.rb +1 -6
  60. data/lib/sequel_model/record.rb +23 -5
  61. data/lib/sequel_model/validations.rb +20 -5
  62. data/spec/adapters/firebird_spec.rb +6 -1
  63. data/spec/adapters/mysql_spec.rb +12 -0
  64. data/spec/adapters/postgres_spec.rb +2 -2
  65. data/spec/adapters/sqlite_spec.rb +81 -2
  66. data/spec/integration/dataset_test.rb +2 -2
  67. data/spec/integration/type_test.rb +12 -2
  68. data/spec/sequel_core/core_sql_spec.rb +46 -12
  69. data/spec/sequel_core/database_spec.rb +24 -12
  70. data/spec/sequel_core/dataset_spec.rb +82 -32
  71. data/spec/sequel_core/schema_spec.rb +16 -0
  72. data/spec/sequel_model/associations_spec.rb +89 -0
  73. data/spec/sequel_model/base_spec.rb +66 -0
  74. data/spec/sequel_model/eager_loading_spec.rb +32 -0
  75. data/spec/sequel_model/record_spec.rb +9 -9
  76. data/spec/sequel_model/spec_helper.rb +3 -0
  77. data/spec/sequel_model/validations_spec.rb +63 -3
  78. metadata +41 -4
@@ -75,17 +75,6 @@ module Sequel
75
75
  class Dataset < Sequel::Dataset
76
76
  MAX_COL_SIZE = 256
77
77
 
78
- def literal(v)
79
- case v
80
- when Time
81
- literal(v.iso8601)
82
- when Date, DateTime
83
- literal(v.to_s)
84
- else
85
- super
86
- end
87
- end
88
-
89
78
  def fetch_rows(sql)
90
79
  execute(sql) do |sth|
91
80
  @column_info = get_column_info(sth)
@@ -76,17 +76,6 @@ module Sequel
76
76
  end
77
77
 
78
78
  class Dataset < Sequel::Dataset
79
- def literal(v)
80
- case v
81
- when Time
82
- literal(v.iso8601)
83
- when Date, DateTime
84
- literal(v.to_s)
85
- else
86
- super
87
- end
88
- end
89
-
90
79
  def fetch_rows(sql, &block)
91
80
  execute(sql) do |s|
92
81
  begin
@@ -175,18 +175,6 @@ module Sequel
175
175
 
176
176
  # Dataset class for Sequel::DataObjects::Database objects.
177
177
  class Dataset < Sequel::Dataset
178
- # Handle the usual time class overrides.
179
- def literal(v)
180
- case v
181
- when Time
182
- literal(v.iso8601)
183
- when Date, DateTime
184
- literal(v.to_s)
185
- else
186
- super
187
- end
188
- end
189
-
190
178
  # Execute the SQL on the database and yield the rows as hashes
191
179
  # with symbol keys.
192
180
  def fetch_rows(sql)
@@ -1,4 +1,5 @@
1
1
  require 'fb'
2
+ require 'sequel_core/adapters/utils/unsupported'
2
3
 
3
4
  module Sequel
4
5
  # The Sequel Firebird adapter requires the ruby fb driver located at
@@ -214,6 +215,7 @@ module Sequel
214
215
 
215
216
  BOOL_TRUE = '1'.freeze
216
217
  BOOL_FALSE = '0'.freeze
218
+ NULL = LiteralString.new('NULL').freeze
217
219
  COMMA_SEPARATOR = ', '.freeze
218
220
  FIREBIRD_TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S".freeze
219
221
  SELECT_CLAUSE_ORDER = %w'distinct limit columns from join where group having compounds order'.freeze
@@ -239,7 +241,7 @@ module Sequel
239
241
  # Insert given values into the database.
240
242
  def insert(*values)
241
243
  if !@opts[:sql]
242
- single_value(:sql=>insert_returning_pk_sql(*values))
244
+ single_value(default_server_opts(:sql=>insert_returning_pk_sql(*values)))
243
245
  else
244
246
  execute_insert(insert_sql(*values), :table=>opts[:from].first,
245
247
  :values=>values.size == 1 ? values.first : values)
@@ -249,7 +251,7 @@ module Sequel
249
251
  # Use the RETURNING clause to return the primary key of the inserted record, if it exists
250
252
  def insert_returning_pk_sql(*values)
251
253
  pk = db.primary_key(opts[:from].first)
252
- insert_returning_sql(pk ? Sequel::SQL::Identifier.new(pk) : 'NULL'.lit, *values)
254
+ insert_returning_sql(pk ? Sequel::SQL::Identifier.new(pk) : NULL, *values)
253
255
  end
254
256
 
255
257
  # Use the RETURNING clause to return the columns listed in returning.
@@ -259,20 +261,7 @@ module Sequel
259
261
 
260
262
  # Insert a record returning the record inserted
261
263
  def insert_select(*values)
262
- single_record(:naked=>true, :sql=>insert_returning_sql(nil, *values))
263
- end
264
-
265
- def literal(v)
266
- case v
267
- when Time, DateTime
268
- "#{v.strftime(FIREBIRD_TIMESTAMP_FORMAT)}.#{sprintf("%04d",v.usec / 100)}'"
269
- when TrueClass
270
- BOOL_TRUE
271
- when FalseClass
272
- BOOL_FALSE
273
- else
274
- super
275
- end
264
+ single_record(default_server_opts(:naked=>true, :sql=>insert_returning_sql(nil, *values)))
276
265
  end
277
266
 
278
267
  # The order of clauses in the SELECT SQL statement
@@ -293,6 +282,22 @@ module Sequel
293
282
  m
294
283
  end
295
284
  end
285
+
286
+ def literal_datetime(v)
287
+ "#{v.strftime(FIREBIRD_TIMESTAMP_FORMAT)}.#{sprintf("%04d",(v.sec_fraction * 864000000))}'"
288
+ end
289
+
290
+ def literal_false
291
+ BOOL_FALSE
292
+ end
293
+
294
+ def literal_time(v)
295
+ "#{v.strftime(FIREBIRD_TIMESTAMP_FORMAT)}.#{sprintf("%04d",v.usec / 100)}'"
296
+ end
297
+
298
+ def literal_true
299
+ BOOL_TRUE
300
+ end
296
301
  end
297
302
  end
298
303
  end
@@ -1,3 +1,4 @@
1
+ require 'sequel_core/adapters/utils/unsupported'
1
2
  require 'informix'
2
3
 
3
4
  module Sequel
@@ -39,17 +40,6 @@ module Sequel
39
40
 
40
41
  SELECT_CLAUSE_ORDER = %w'limit distinct columns from join where having group compounds order'.freeze
41
42
 
42
- def literal(v)
43
- case v
44
- when Time
45
- literal(v.iso8601)
46
- when Date, DateTime
47
- literal(v.to_s)
48
- else
49
- super
50
- end
51
- end
52
-
53
43
  def fetch_rows(sql, &block)
54
44
  execute(sql) do |cursor|
55
45
  begin
@@ -1,5 +1,5 @@
1
1
  require 'java'
2
- require 'sequel_core/dataset/stored_procedures'
2
+ require 'sequel_core/adapters/utils/stored_procedures'
3
3
 
4
4
  module Sequel
5
5
  # Houses Sequel's JDBC support when running on JRuby.
@@ -414,18 +414,6 @@ module Sequel
414
414
  self
415
415
  end
416
416
 
417
- # Use the ISO values for dates and times.
418
- def literal(v)
419
- case v
420
- when Time
421
- literal(v.iso8601)
422
- when Date, DateTime, Java::JavaSql::Timestamp, Java::JavaSql::Date
423
- literal(v.to_s)
424
- else
425
- super
426
- end
427
- end
428
-
429
417
  # Create a named prepared statement that is stored in the
430
418
  # database (and connection) for reuse.
431
419
  def prepare(type, name=nil, values=nil)
@@ -1,3 +1,5 @@
1
+ require 'sequel_core/adapters/utils/date_format'
2
+
1
3
  module Sequel
2
4
  module JDBC
3
5
  # Database and Dataset support for H2 databases accessed via JDBC.
@@ -52,17 +54,7 @@ module Sequel
52
54
 
53
55
  # Dataset class for H2 datasets accessed via JDBC.
54
56
  class Dataset < JDBC::Dataset
55
- # Use H2 syntax for Date, DateTime, and Time types.
56
- def literal(v)
57
- case v
58
- when Date
59
- v.strftime("DATE '%Y-%m-%d'")
60
- when DateTime, Time
61
- v.strftime("TIMESTAMP '%Y-%m-%d %H:%M:%S'")
62
- else
63
- super
64
- end
65
- end
57
+ include Dataset::SQLStandardDateFormat
66
58
  end
67
59
  end
68
60
  end
@@ -46,27 +46,10 @@ module Sequel
46
46
  execute_insert(insert_sql(*values))
47
47
  end
48
48
 
49
- # Handle time types correctly
50
- def literal(v)
51
- case v
52
- when Time, DateTime
53
- v.strftime("'%Y-%m-%d %H:%M:%S'")
54
- else
55
- super
56
- end
57
- end
58
-
59
49
  # Use execute_insert to execute the replace_sql.
60
50
  def replace(*args)
61
51
  execute_insert(replace_sql(*args))
62
52
  end
63
-
64
- private
65
-
66
- # Call execute_insert on the database.
67
- def execute_insert(sql, opts={})
68
- @db.execute_insert(sql, {:server=>@opts[:server] || :default}.merge(opts))
69
- end
70
53
  end
71
54
  end
72
55
  end
@@ -91,21 +91,9 @@ module Sequel
91
91
  ps
92
92
  end
93
93
 
94
- # Convert Java::JavaSql::Timestamps correctly, and handle Strings
95
- # similar to the native postgres adapter.
96
- def literal(v)
97
- case v
98
- when LiteralString
99
- v
100
- when SQL::Blob
101
- super
102
- when String
103
- db.synchronize{|c| "'#{c.escape_string(v)}'"}
104
- when Java::JavaSql::Timestamp
105
- "TIMESTAMP #{literal(v.to_s)}"
106
- else
107
- super
108
- end
94
+ # Literalize strings similar to the native postgres adapter
95
+ def literal_string(v)
96
+ db.synchronize{|c| "'#{c.escape_string(v)}'"}
109
97
  end
110
98
  end
111
99
  end
@@ -1,6 +1,6 @@
1
1
  require 'mysql'
2
2
  require 'sequel_core/adapters/shared/mysql'
3
- require 'sequel_core/dataset/stored_procedures'
3
+ require 'sequel_core/adapters/utils/stored_procedures'
4
4
 
5
5
  module Sequel
6
6
  # Module for holding all MySQL-related classes and modules for Sequel.
@@ -27,10 +27,10 @@ module Sequel
27
27
  246 => :to_d, # MYSQL_TYPE_NEWDECIMAL
28
28
  247 => :to_i, # MYSQL_TYPE_ENUM
29
29
  248 => :to_i, # MYSQL_TYPE_SET
30
- 249 => :to_blob, # MYSQL_TYPE_TINY_BLOB
31
- 250 => :to_blob, # MYSQL_TYPE_MEDIUM_BLOB
32
- 251 => :to_blob, # MYSQL_TYPE_LONG_BLOB
33
- 252 => :to_blob, # MYSQL_TYPE_BLOB
30
+ 249 => :to_sequel_blob, # MYSQL_TYPE_TINY_BLOB
31
+ 250 => :to_sequel_blob, # MYSQL_TYPE_MEDIUM_BLOB
32
+ 251 => :to_sequel_blob, # MYSQL_TYPE_LONG_BLOB
33
+ 252 => :to_sequel_blob, # MYSQL_TYPE_BLOB
34
34
  # 253 => :to_s, # MYSQL_TYPE_VAR_STRING
35
35
  # 254 => :to_s, # MYSQL_TYPE_STRING
36
36
  # 255 => :to_s # MYSQL_TYPE_GEOMETRY
@@ -45,19 +45,30 @@ module Sequel
45
45
  # Support stored procedures on MySQL
46
46
  def call_sproc(name, opts={}, &block)
47
47
  args = opts[:args] || []
48
- execute("CALL #{name}(#{literal(args) unless args.empty?})", opts.merge(:sproc=>false), &block)
48
+ execute("CALL #{name}#{args.empty? ? '()' : literal(args)}", opts.merge(:sproc=>false), &block)
49
49
  end
50
50
 
51
51
  # Connect to the database. In addition to the usual database options,
52
52
  # the following options have effect:
53
53
  #
54
- # * :encoding, :charset - Set all the related character sets for this
54
+ # * :auto_is_null - Set to true to use MySQL default behavior of having
55
+ # a filter for an autoincrement column equals NULL to return the last
56
+ # inserted row.
57
+ # * :charset - Same as :encoding (:encoding takes precendence)
58
+ # * :compress - Set to false to not compress results from the server
59
+ # * :encoding - Set all the related character sets for this
55
60
  # connection (connection, client, database, server, and results).
56
61
  # * :socket - Use a unix socket file instead of connecting via TCP/IP.
62
+ # * :timeout - Set the timeout in seconds before the server will
63
+ # disconnect this connection.
57
64
  def connect(server)
58
65
  opts = server_opts(server)
59
66
  conn = Mysql.init
60
67
  conn.options(Mysql::OPT_LOCAL_INFILE, "client")
68
+ if encoding = opts[:encoding] || opts[:charset]
69
+ # set charset _before_ the connect. using an option instead of "SET (NAMES|CHARACTER_SET_*)" works across reconnects
70
+ conn.options(Mysql::SET_CHARSET_NAME, encoding)
71
+ end
61
72
  conn.real_connect(
62
73
  opts[:host] || 'localhost',
63
74
  opts[:user],
@@ -67,16 +78,16 @@ module Sequel
67
78
  opts[:socket],
68
79
  Mysql::CLIENT_MULTI_RESULTS +
69
80
  Mysql::CLIENT_MULTI_STATEMENTS +
70
- Mysql::CLIENT_COMPRESS
81
+ (opts[:compress] == false ? 0 : Mysql::CLIENT_COMPRESS)
71
82
  )
83
+
84
+ # increase timeout so mysql server doesn't disconnect us
85
+ conn.query("set @@wait_timeout = #{opts[:timeout] || 2592000}")
86
+
87
+ # By default, MySQL 'where id is null' selects the last inserted id
88
+ conn.query("set SQL_AUTO_IS_NULL=0") unless opts[:auto_is_null]
89
+
72
90
  conn.query_with_result = false
73
- if encoding = opts[:encoding] || opts[:charset]
74
- conn.query("set character_set_connection = '#{encoding}'")
75
- conn.query("set character_set_client = '#{encoding}'")
76
- conn.query("set character_set_database = '#{encoding}'")
77
- conn.query("set character_set_server = '#{encoding}'")
78
- conn.query("set character_set_results = '#{encoding}'")
79
- end
80
91
  conn.meta_eval{attr_accessor :prepared_statements}
81
92
  conn.prepared_statements = {}
82
93
  conn.reconnect = true
@@ -288,18 +299,6 @@ module Sequel
288
299
  execute_dui(insert_sql(*values)){|c| c.insert_id}
289
300
  end
290
301
 
291
- # Handle correct quoting of strings using ::MySQL.quote.
292
- def literal(v)
293
- case v
294
- when LiteralString
295
- v
296
- when String
297
- "'#{::Mysql.quote(v)}'"
298
- else
299
- super
300
- end
301
- end
302
-
303
302
  # Store the given type of prepared statement in the associated database
304
303
  # with the given name.
305
304
  def prepare(type, name=nil, values=nil)
@@ -349,6 +348,11 @@ module Sequel
349
348
  super(sql, {:type=>:dui}.merge(opts), &block)
350
349
  end
351
350
 
351
+ # Handle correct quoting of strings using ::MySQL.quote.
352
+ def literal_string(v)
353
+ "'#{::Mysql.quote(v)}'"
354
+ end
355
+
352
356
  # Extend the dataset with the MySQL stored procedure methods.
353
357
  def prepare_extend_sproc(ds)
354
358
  ds.extend(StoredProcedureMethods)
@@ -100,25 +100,6 @@ module Sequel
100
100
  ODBC_TIMESTAMP_AFTER_SECONDS =
101
101
  ODBC_TIMESTAMP_FORMAT.index( '%S' ).succ - ODBC_TIMESTAMP_FORMAT.length
102
102
  ODBC_DATE_FORMAT = "{d '%Y-%m-%d'}".freeze
103
-
104
- def literal(v)
105
- case v
106
- when true
107
- BOOL_TRUE
108
- when false
109
- BOOL_FALSE
110
- when Time, DateTime
111
- formatted = v.strftime(ODBC_TIMESTAMP_FORMAT)
112
- usec = (Time === v ? v.usec : (v.sec_fraction * 86400000000))
113
- formatted.insert(ODBC_TIMESTAMP_AFTER_SECONDS, ".#{(usec.to_f/1000).round}") if usec >= 1000
114
- formatted
115
- when Date
116
- v.strftime(ODBC_DATE_FORMAT)
117
- else
118
- super
119
- end
120
- end
121
-
122
103
  UNTITLED_COLUMN = 'untitled_%d'.freeze
123
104
 
124
105
  def fetch_rows(sql, &block)
@@ -141,15 +122,7 @@ module Sequel
141
122
  end
142
123
 
143
124
  private
144
-
145
- def hash_row(row)
146
- hash = {}
147
- row.each_with_index do |v, idx|
148
- hash[@columns[idx]] = convert_odbc_value(v)
149
- end
150
- hash
151
- end
152
-
125
+
153
126
  def convert_odbc_value(v)
154
127
  # When fetching a result set, the Ruby ODBC driver converts all ODBC
155
128
  # SQL types to an equivalent Ruby type; with the exception of
@@ -169,6 +142,39 @@ module Sequel
169
142
  v
170
143
  end
171
144
  end
145
+
146
+ def hash_row(row)
147
+ hash = {}
148
+ row.each_with_index do |v, idx|
149
+ hash[@columns[idx]] = convert_odbc_value(v)
150
+ end
151
+ hash
152
+ end
153
+
154
+ def literal_date(v)
155
+ v.strftime(ODBC_DATE_FORMAT)
156
+ end
157
+
158
+ def literal_datetime(v)
159
+ formatted = v.strftime(ODBC_TIMESTAMP_FORMAT)
160
+ usec = v.sec_fraction * 86400000000
161
+ formatted.insert(ODBC_TIMESTAMP_AFTER_SECONDS, ".#{(usec.to_f/1000).round}") if usec >= 1000
162
+ formatted
163
+ end
164
+
165
+ def literal_false
166
+ BOOL_FALSE
167
+ end
168
+
169
+ def literal_true
170
+ BOOL_TRUE
171
+ end
172
+
173
+ def literal_time(v)
174
+ formatted = v.strftime(ODBC_TIMESTAMP_FORMAT)
175
+ formatted.insert(ODBC_TIMESTAMP_AFTER_SECONDS, ".#{(v.usec.to_f/1000).round}") if usec >= 1000
176
+ formatted
177
+ end
172
178
  end
173
179
  end
174
180
  end