sequel 3.6.0 → 3.7.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 CHANGED
@@ -1,3 +1,31 @@
1
+ === 3.7.0 (2009-12-01)
2
+
3
+ * Add Dataset#sequence to the shared Oracle Adapter, for returning autogenerated primary key values on insert (jeremyevans) (#280)
4
+
5
+ * Bring support for modifying joined datasets into Sequel proper, supported on MySQL and PostgreSQL (jeremyevans)
6
+
7
+ * No longer use native autoreconnection in the mysql adapter (jeremyevans)
8
+
9
+ * Add NULL, NOTNULL, TRUE, SQLTRUE, FALSE, and SQLFALSE constants (jeremyevans)
10
+
11
+ * Add Dataset #select_map, #select_order_map, and #select_hash (jeremyevans)
12
+
13
+ * Make Dataset#group_and_count handle arguments other than Symbols (jeremyevans)
14
+
15
+ * Add :only_if_modified option to validates_unique method in validation_helpers plugin (jeremyevans)
16
+
17
+ * Allow specifying the dataset alias via :alias option when using union/intersect/except (jeremyevans)
18
+
19
+ * Allow Model#destroy to take an options hash and respect a :transaction option (john_firebaugh)
20
+
21
+ * If a transaction is being used, raise_on_save_failure is false, and a before hook returns false, rollback the transaction (john_firebaugh, jeremyevans)
22
+
23
+ * In the schema_dumper, explicitly specify the :type option if it isn't Integer (jeremyevans)
24
+
25
+ * On postgres, use bigserial type if :type=>Bignum is given as an option to primary_key (jeremyevans)
26
+
27
+ * Use READ_DEFAULT_GROUP in the mysql adapter to load the options in the client section of the my.cnf file (crohr)
28
+
1
29
  === 3.6.0 (2009-11-02)
2
30
 
3
31
  * Make the MSSQL shared adapter correctly parse the column schema information for tables in the non-default database schema (rohit.namjoshi)
data/Rakefile CHANGED
@@ -55,11 +55,9 @@ task :uninstall=>[:clean] do
55
55
  sh %{sudo gem uninstall #{NAME}}
56
56
  end
57
57
 
58
- desc "Upload sequel gem to rubyforge"
58
+ desc "Upload sequel gem to gemcutter"
59
59
  task :release=>[:package] do
60
- sh %{rubyforge login}
61
- sh %{rubyforge add_release sequel #{NAME} #{VERS.call} pkg/#{NAME}-#{VERS.call}.tgz}
62
- sh %{rubyforge add_file sequel #{NAME} #{VERS.call} pkg/#{NAME}-#{VERS.call}.gem}
60
+ sh %{gem push pkg/#{NAME}-#{VERS.call}.gem}
63
61
  end
64
62
 
65
63
  ### RDoc
@@ -116,7 +114,6 @@ begin
116
114
  spec_opts = lambda do
117
115
  lib_dir = File.join(File.dirname(__FILE__), 'lib')
118
116
  ENV['RUBYLIB'] ? (ENV['RUBYLIB'] += ":#{lib_dir}") : (ENV['RUBYLIB'] = lib_dir)
119
- File.read("spec/spec.opts").split("\n")
120
117
  end
121
118
 
122
119
  rcov_opts = lambda do
@@ -126,7 +123,7 @@ begin
126
123
  desc "Run core and model specs with coverage"
127
124
  Spec::Rake::SpecTask.new("spec_coverage") do |t|
128
125
  t.spec_files = Dir["spec/{core,model}/*_spec.rb"]
129
- t.spec_opts = spec_opts.call
126
+ spec_opts.call
130
127
  t.rcov, t.rcov_opts = rcov_opts.call
131
128
  end
132
129
 
@@ -134,44 +131,44 @@ begin
134
131
  task :default => [:spec]
135
132
  Spec::Rake::SpecTask.new("spec") do |t|
136
133
  t.spec_files = Dir["spec/{core,model}/*_spec.rb"]
137
- t.spec_opts = spec_opts.call
134
+ spec_opts.call
138
135
  end
139
136
 
140
137
  desc "Run core specs"
141
138
  Spec::Rake::SpecTask.new("spec_core") do |t|
142
139
  t.spec_files = Dir["spec/core/*_spec.rb"]
143
- t.spec_opts = spec_opts.call
140
+ spec_opts.call
144
141
  end
145
142
 
146
143
  desc "Run model specs"
147
144
  Spec::Rake::SpecTask.new("spec_model") do |t|
148
145
  t.spec_files = Dir["spec/model/*_spec.rb"]
149
- t.spec_opts = spec_opts.call
146
+ spec_opts.call
150
147
  end
151
148
 
152
149
  desc "Run extension/plugin specs"
153
150
  Spec::Rake::SpecTask.new("spec_plugin") do |t|
154
151
  t.spec_files = Dir["spec/extensions/*_spec.rb"]
155
- t.spec_opts = spec_opts.call
152
+ spec_opts.call
156
153
  end
157
154
 
158
155
  desc "Run extention/plugin specs with coverage"
159
156
  Spec::Rake::SpecTask.new("spec_plugin_cov") do |t|
160
157
  t.spec_files = Dir["spec/extensions/*_spec.rb"]
161
- t.spec_opts = spec_opts.call
158
+ spec_opts.call
162
159
  t.rcov, t.rcov_opts = rcov_opts.call
163
160
  end
164
161
 
165
162
  desc "Run integration tests"
166
163
  Spec::Rake::SpecTask.new("integration") do |t|
167
164
  t.spec_files = Dir["spec/integration/*_test.rb"]
168
- t.spec_opts = spec_opts.call
165
+ spec_opts.call
169
166
  end
170
167
 
171
168
  desc "Run integration tests with coverage"
172
169
  Spec::Rake::SpecTask.new("integration_cov") do |t|
173
170
  t.spec_files = Dir["spec/integration/*_test.rb"]
174
- t.spec_opts = spec_opts.call
171
+ spec_opts.call
175
172
  t.rcov, t.rcov_opts = rcov_opts.call
176
173
  end
177
174
 
@@ -179,13 +176,13 @@ begin
179
176
  desc "Run #{adapter} specs"
180
177
  Spec::Rake::SpecTask.new("spec_#{adapter}") do |t|
181
178
  t.spec_files = ["spec/adapters/#{adapter}_spec.rb"] + Dir["spec/integration/*_test.rb"]
182
- t.spec_opts = spec_opts.call
179
+ spec_opts.call
183
180
  end
184
181
 
185
182
  desc "Run #{adapter} specs with coverage"
186
183
  Spec::Rake::SpecTask.new("spec_#{adapter}_cov") do |t|
187
184
  t.spec_files = ["spec/adapters/#{adapter}_spec.rb"] + Dir["spec/integration/*_test.rb"]
188
- t.spec_opts = spec_opts.call
185
+ spec_opts.call
189
186
  t.rcov, t.rcov_opts = rcov_opts.call
190
187
  end
191
188
  end
@@ -0,0 +1,179 @@
1
+ New Features
2
+ ------------
3
+
4
+ * Sequel now has support for deleting and updating joined datasets
5
+ on MySQL and PostgreSQL. Previously, Sequel only supported this to
6
+ a limited extent on Microsoft SQL Server, and support there has been
7
+ improved as well.
8
+
9
+ This allows you to do:
10
+
11
+ DB.create_table!(:a){Integer :a; Integer :d}
12
+ DB.create_table!(:b){Integer :b; Integer :e}
13
+ DB.create_table!(:c){Integer :c; Integer :f}
14
+
15
+ # Insert some rows
16
+
17
+ ds = DB.from(:a, :b).
18
+ join(:c, :c=>:e.identifier).
19
+ where(:d=>:b)
20
+ ds.where(:f=>6).update(:a => 10)
21
+ ds.where(:f=>5).delete
22
+
23
+ Which will set the a column to 10 for all rows in table a, where
24
+ an associated row in table c (through table b) has a value of 6 for
25
+ column f. It will delete rows from table a where an associated row
26
+ in table c (through table b) has a value of 5 for column f.
27
+
28
+ Sequel assumes the that first FROM table is the table being
29
+ updated/deleted. MySQL and Microsoft SQL Server do not require
30
+ multiple FROM tables, but PostgreSQL does.
31
+
32
+ * Dataset #select_map, #select_order_map, and #select_hash
33
+ convenience methods were added for quickly creating arrays and
34
+ hashes from a dataset.
35
+
36
+ select_map and select_order_map both return arrays of values for the
37
+ column specified. The column can be specified either via an argument
38
+ or a block, similar to Dataset#get. Both accept any valid objects as
39
+ arguments.
40
+
41
+ select_hash returns a hash. It requires two symbol arguments, but
42
+ can handle implicit qualifiers or aliases in the symbols.
43
+
44
+ Neither of these methods offer any new functionality, they just cut
45
+ down on the number of required key strokes:
46
+
47
+ select_map(:column) # select(:column).map(:column)
48
+ select_order_map(:column) # select(:column).order(:column).
49
+ # map(:column)
50
+ select_hash(:key_column, :value_column)
51
+ # select(:key_column, :value_column).
52
+ # to_hash(:key_column, :value_column)
53
+
54
+ * The NULL, NOTNULL, TRUE, SQLTRUE, FALSE, and SQLFALSE constants
55
+ were added to Sequel::SQL::Constants. This allows you to do:
56
+
57
+ include Sequel::SQL::Constants
58
+ DB[:table].where(:a=>'1', :b=>NOTNULL)
59
+
60
+ Previously, the shortest way to do this was:
61
+
62
+ DB[:table].where(:a=>'1').exclude(:b=>nil)
63
+
64
+ It may make the code more descriptive:
65
+
66
+ DB[:table].where(:b=>NULL)
67
+ # compared to
68
+ DB[:table].where(:b=>nil)
69
+
70
+ This gives the option to use SQL terminology instead of ruby
71
+ terminology.
72
+
73
+ The other advantage of using the constants it that they handle
74
+ operators and methods like other Sequel::SQL objects:
75
+
76
+ NULL & SQLFALSE # BooleanExpression => "(NULL AND FALSE)"
77
+ nil & false # false
78
+
79
+ NULL + :a # NumericExpression => "(NULL + a)"
80
+ nil + :a # raises NoMethodError
81
+ NULL.sql_string + :a # StringExpression => "(NULL || a)"
82
+ NULL.as(:b) # AliasedExpression => "NULL AS b"
83
+
84
+ For complex systems that want to represent SQL boolean objects
85
+ in ruby (where you don't know exactly how they'll be used), using
86
+ the constants is recommended.
87
+
88
+ In order not to be too verbose, including Sequel::SQL::Constants
89
+ is recommended. It's not done by default, but you can still
90
+ reference the constants under the main Sequel module by default
91
+ (e.g. Sequel::NULL).
92
+
93
+ * The validates_unique method in the validation_helpers plugin now
94
+ supports an :only_if_modified option, which should speed up the
95
+ common case where the unique attribute is not modified for an
96
+ existing record. It's not on by default, since it's possible the
97
+ database could be changed between retrieving the model object and
98
+ updating it.
99
+
100
+ * The Dataest #union, #intersect, and #except methods now accept an
101
+ :alias option that it used as the alias for the returned dataset.
102
+
103
+ DB[:table].union(DB[:old_table], :alias=>:table)
104
+
105
+ * Model#destroy now supports a :transaction option, similar to
106
+ Model#save.
107
+
108
+ * The shared Oracle adapter now supports Dataset#sequence for
109
+ returning autogenerated primary key values on insert from a
110
+ related sequence.
111
+
112
+ This makes Oracle work correctly when using models, with
113
+ something like the following:
114
+
115
+ class Album < Sequel::Model
116
+ set_dataset dataset.sequence(:seq_albums_id)
117
+ end
118
+
119
+ You currently need to call Dataset#sequence in every model
120
+ class where the underlying table uses a sequence to generate
121
+ primary key values.
122
+
123
+ Other Improvements
124
+ ------------------
125
+
126
+ * In Model #save and #destroy when using transactions and when
127
+ raise_on_save_failure is false, ensure that transactions are rolled
128
+ back if a before hook returns false.
129
+
130
+ * Dataset#group_and_count now handles arguments other than Symbols.
131
+ A previous change to the method raised an exception if a Symbol was
132
+ not provided. It also handles AliasedExpressions natively, so the
133
+ following works correctly:
134
+
135
+ DB[:table].group_and_count(:column.as(:alias))
136
+
137
+ * Sequel no longer uses native autoreconnection in the mysql adapter.
138
+ Native autoreconnection has problems with prepared statements,
139
+ where a new native connection is used behind Sequel's back, so
140
+ Sequel thinks the prepared statement has already been defined on
141
+ the connection, when it fact it hasn't. Any other changes that
142
+ affect the state of the connection will be lost when native
143
+ autoreconnection is used as well.
144
+
145
+ Sequel's connection pool already handles reconnection if it detects
146
+ a disconnection. This commit also adds an additional exception
147
+ message to recognize as a disconnect. If there other exception
148
+ messages related to disconnects, please post them on the Sequel
149
+ mailing list.
150
+
151
+ * The schema_dumper plugin now specifies the :type option for primary
152
+ key if it isn't Integer.
153
+
154
+ * On PostgreSQL, the bigserial type is used if :type=>Bignum is
155
+ given as an option to primary key. This makes it operate more
156
+ similarly to other adapters that support autoincrementing 64-bit
157
+ integer primary keys.
158
+
159
+ * The native mysql adapter will now attempt to load options in the
160
+ [client] section of the my.cnf file.
161
+
162
+ * The rake spec tasks for the project now work correctly with RSpec
163
+ 1.2.9.
164
+
165
+ Backwards Compatibility
166
+ -----------------------
167
+
168
+ * Dataset::GET_ERROR_MSG and Dataset::MAP_ERROR_MSG constants were
169
+ removed. Both were replaced with Dataset::ARG_BLOCK_ERROR_MSG.
170
+
171
+ * The behavior of the Model#save_failure private instance method was
172
+ modified. It now always raises an exception, and validation
173
+ failures no longer call it.
174
+
175
+ * The internals of how autogenerated primary key metadata is stored
176
+ when creating tables on PostgreSQL has been modified.
177
+
178
+ * The native MySQL adapter no longer sets the OPT_LOCAL_INFILE option
179
+ to "client" on the native connection.
@@ -15,6 +15,7 @@ called. This is best shown by example:
15
15
  ds.filter{column > 1} # column > 1
16
16
  ds.filter{table__column > 1} # table.column > 1
17
17
  ds.filter{function(1) > 1} # function(1) > 1
18
+ ds.select{[column1, sum(column2).as(sum)]} # column1, sum(column2) AS sum
18
19
  ds.select{version{}} # version()
19
20
  ds.select{count(:*){}} # count(*)
20
21
  ds.select{count(:distinct, col1){}} # count(DISTINCT col1)
@@ -54,3 +55,5 @@ that. If you want to create identifiers or qualified identifiers
54
55
  with the same name as existing local variables, make sure ruby
55
56
  knows it is a method call instead of a local variable reference
56
57
  by not ommiting the parentheses at the end of the method call.
58
+ If you want to return multiple arguments (common for select and
59
+ order), have the block return an array of arguments.
@@ -11,13 +11,16 @@ module Sequel
11
11
  # it returns the strings as is. It is false by default, which means that
12
12
  # invalid dates and times will raise errors.
13
13
  #
14
- # Sequel::MySQL.convert_invalid_date_time = true
14
+ # Sequel::MySQL.convert_invalid_date_time = nil
15
15
  #
16
16
  # Sequel converts the column type tinyint(1) to a boolean by default when
17
17
  # using the native MySQL adapter. You can turn off the conversion to use
18
18
  # tinyint as an integer:
19
19
  #
20
- # Sequel.convert_tinyint_to_bool = false
20
+ # Sequel::MySQL.convert_tinyint_to_bool = false
21
+ #
22
+ # In most cases these settings need to be made after the adapter has been
23
+ # loaded, since the Sequel::MySQL module probably won't exist before then.
21
24
  module MySQL
22
25
  # Mapping of type numbers to conversion procs
23
26
  MYSQL_TYPES = {}
@@ -67,7 +70,7 @@ module Sequel
67
70
  include Sequel::MySQL::DatabaseMethods
68
71
 
69
72
  # Mysql::Error messages that indicate the current connection should be disconnected
70
- MYSQL_DATABASE_DISCONNECT_ERRORS = /\A(Commands out of sync; you can't run this command now\z|Can't connect to local MySQL server through socket)/
73
+ MYSQL_DATABASE_DISCONNECT_ERRORS = /\A(Commands out of sync; you can't run this command now|Can't connect to local MySQL server through socket|MySQL server has gone away)/
71
74
 
72
75
  set_adapter_scheme :mysql
73
76
 
@@ -93,7 +96,8 @@ module Sequel
93
96
  def connect(server)
94
97
  opts = server_opts(server)
95
98
  conn = Mysql.init
96
- conn.options(Mysql::OPT_LOCAL_INFILE, "client")
99
+ # reads additional options defined under the [client] tag in the mysql configuration file
100
+ conn.options(Mysql::READ_DEFAULT_GROUP, "client")
97
101
  if encoding = opts[:encoding] || opts[:charset]
98
102
  # set charset _before_ the connect. using an option instead of "SET (NAMES|CHARACTER_SET_*)" works across reconnects
99
103
  conn.options(Mysql::SET_CHARSET_NAME, encoding)
@@ -120,7 +124,6 @@ module Sequel
120
124
  attr_accessor :prepared_statements
121
125
  end
122
126
  conn.prepared_statements = {}
123
- conn.reconnect = true
124
127
  conn
125
128
  end
126
129
 
@@ -323,6 +323,11 @@ module Sequel
323
323
  false
324
324
  end
325
325
 
326
+ # MSSQL 2005+ supports modifying joined datasets
327
+ def supports_modifying_joins?
328
+ true
329
+ end
330
+
326
331
  # MSSQL does not support multiple columns for the IN/NOT IN operators
327
332
  def supports_multiple_column_in?
328
333
  false
@@ -335,17 +340,26 @@ module Sequel
335
340
 
336
341
  private
337
342
 
338
- # MSSQL can modify joined datasets
339
- def check_modification_allowed!
340
- raise(InvalidOperation, "Grouped datasets cannot be modified") if opts[:group]
341
- end
342
-
343
343
  # MSSQL supports the OUTPUT clause for DELETE statements.
344
344
  # It also allows prepending a WITH clause.
345
345
  def delete_clause_methods
346
346
  DELETE_CLAUSE_METHODS
347
347
  end
348
348
 
349
+ # Only include the primary table in the main delete clause
350
+ def delete_from_sql(sql)
351
+ sql << " FROM #{source_list(@opts[:from][0..0])}"
352
+ end
353
+
354
+ # MSSQL supports FROM clauses in DELETE and UPDATE statements.
355
+ def delete_from2_sql(sql)
356
+ if joined_dataset?
357
+ select_from_sql(sql)
358
+ select_join_sql(sql)
359
+ end
360
+ end
361
+ alias update_from_sql delete_from2_sql
362
+
349
363
  # Handle the with clause for delete, insert, and update statements
350
364
  # to be the same as the insert statement.
351
365
  def delete_with_sql(sql)
@@ -361,16 +375,6 @@ module Sequel
361
375
  sprintf(".%03d", usec/1000)
362
376
  end
363
377
 
364
- # MSSQL supports FROM clauses in DELETE and UPDATE statements.
365
- def from_sql(sql)
366
- if (opts[:from].is_a?(Array) && opts[:from].size > 1) || opts[:join]
367
- select_from_sql(sql)
368
- select_join_sql(sql)
369
- end
370
- end
371
- alias delete_from2_sql from_sql
372
- alias update_from_sql from_sql
373
-
374
378
  # MSSQL supports the OUTPUT clause for INSERT statements.
375
379
  # It also allows prepending a WITH clause.
376
380
  def insert_clause_methods
@@ -441,6 +445,11 @@ module Sequel
441
445
  def update_clause_methods
442
446
  UPDATE_CLAUSE_METHODS
443
447
  end
448
+
449
+ # Only include the primary table in the main update clause
450
+ def update_table_sql(sql)
451
+ sql << " #{source_list(@opts[:from][0..0])}"
452
+ end
444
453
  end
445
454
  end
446
455
  end
@@ -327,6 +327,11 @@ module Sequel
327
327
  false
328
328
  end
329
329
 
330
+ # MySQL supports modifying joined datasets
331
+ def supports_modifying_joins?
332
+ true
333
+ end
334
+
330
335
  # MySQL does support fractional timestamps in literal timestamps, but it
331
336
  # ignores them. Also, using them seems to cause problems on 1.9. Since
332
337
  # they are ignored anyway, not using them is probably best.
@@ -347,6 +352,17 @@ module Sequel
347
352
  def delete_clause_methods
348
353
  DELETE_CLAUSE_METHODS
349
354
  end
355
+
356
+ # Consider the first table in the joined dataset is the table to delete
357
+ # from, but include the others for the purposes of selecting rows.
358
+ def delete_from_sql(sql)
359
+ if joined_dataset?
360
+ sql << " #{source_list(@opts[:from][0..0])} FROM #{source_list(@opts[:from])}"
361
+ select_join_sql(sql)
362
+ else
363
+ super
364
+ end
365
+ end
350
366
 
351
367
  # MySQL supports the IGNORE and ON DUPLICATE KEY UPDATE clauses for INSERT statements
352
368
  def insert_clause_methods