sequel 3.6.0 → 3.7.0

Sign up to get free protection for your applications and to get access to all the features.
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