sequel 4.5.0 → 4.6.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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +14 -0
  3. data/README.rdoc +0 -1
  4. data/doc/mssql_stored_procedures.rdoc +43 -0
  5. data/doc/release_notes/3.18.0.txt +2 -3
  6. data/doc/release_notes/3.9.0.txt +1 -1
  7. data/doc/release_notes/4.6.0.txt +30 -0
  8. data/doc/security.rdoc +7 -0
  9. data/lib/sequel/adapters/jdbc/h2.rb +4 -4
  10. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -0
  11. data/lib/sequel/adapters/oracle.rb +1 -0
  12. data/lib/sequel/adapters/shared/mssql.rb +94 -1
  13. data/lib/sequel/adapters/shared/mysql.rb +4 -4
  14. data/lib/sequel/adapters/shared/postgres.rb +4 -4
  15. data/lib/sequel/adapters/tinytds.rb +22 -4
  16. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -2
  17. data/lib/sequel/database/dataset_defaults.rb +1 -1
  18. data/lib/sequel/model/associations.rb +1 -1
  19. data/lib/sequel/version.rb +1 -1
  20. data/spec/adapters/mssql_spec.rb +35 -0
  21. data/spec/adapters/oracle_spec.rb +4 -4
  22. data/spec/adapters/postgres_spec.rb +93 -93
  23. data/spec/adapters/spec_helper.rb +3 -1
  24. data/spec/bin_spec.rb +2 -0
  25. data/spec/core/database_spec.rb +22 -22
  26. data/spec/core/dataset_spec.rb +8 -8
  27. data/spec/core/expression_filters_spec.rb +1 -1
  28. data/spec/core/mock_adapter_spec.rb +2 -2
  29. data/spec/core/schema_generator_spec.rb +3 -3
  30. data/spec/core/spec_helper.rb +3 -1
  31. data/spec/core_extensions_spec.rb +3 -1
  32. data/spec/extensions/auto_validations_spec.rb +17 -17
  33. data/spec/extensions/caching_spec.rb +4 -4
  34. data/spec/extensions/error_splitter_spec.rb +1 -1
  35. data/spec/extensions/hook_class_methods_spec.rb +6 -6
  36. data/spec/extensions/migration_spec.rb +53 -53
  37. data/spec/extensions/pagination_spec.rb +9 -9
  38. data/spec/extensions/pg_array_associations_spec.rb +2 -2
  39. data/spec/extensions/pg_array_spec.rb +2 -2
  40. data/spec/extensions/pg_hstore_spec.rb +15 -15
  41. data/spec/extensions/pg_interval_spec.rb +3 -3
  42. data/spec/extensions/pg_range_spec.rb +20 -20
  43. data/spec/extensions/pg_row_spec.rb +1 -1
  44. data/spec/extensions/schema_caching_spec.rb +3 -3
  45. data/spec/extensions/spec_helper.rb +3 -1
  46. data/spec/extensions/static_cache_spec.rb +16 -16
  47. data/spec/extensions/tree_spec.rb +8 -8
  48. data/spec/extensions/validation_class_methods_spec.rb +10 -10
  49. data/spec/integration/database_test.rb +3 -3
  50. data/spec/integration/dataset_test.rb +6 -0
  51. data/spec/integration/migrator_test.rb +67 -67
  52. data/spec/integration/model_test.rb +2 -2
  53. data/spec/integration/schema_test.rb +1 -1
  54. data/spec/integration/spec_helper.rb +3 -1
  55. data/spec/integration/transaction_test.rb +2 -2
  56. data/spec/model/association_reflection_spec.rb +4 -4
  57. data/spec/model/associations_spec.rb +1 -1
  58. data/spec/model/class_dataset_methods_spec.rb +1 -1
  59. data/spec/model/eager_loading_spec.rb +7 -0
  60. data/spec/model/model_spec.rb +4 -4
  61. data/spec/model/record_spec.rb +20 -20
  62. data/spec/model/spec_helper.rb +2 -1
  63. data/spec/model/validations_spec.rb +1 -1
  64. data/spec/rspec_helper.rb +18 -0
  65. metadata +8 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ab0c1f081253f4b43e1a46f2132e9b516e2a67ba
4
- data.tar.gz: e6b2cd60d962bb23d4b6b03a5a1ef33f81762bd0
3
+ metadata.gz: 4da9d52fb246c1f0e83b88184dd82d4bf7b609cd
4
+ data.tar.gz: 041e624b4f998ebc9835898be941fadc12fda8c4
5
5
  SHA512:
6
- metadata.gz: d31e482c17d09830fc9895d61d3b9bc3f1bc527fe8939ea7856c98ce4b077ad842e73f8e7982708276898e6353b769fe281bdbb8becd82863b3f8e5e6c58fd7a
7
- data.tar.gz: 4c9791b19cab0084aead4cf4a57a5c822d937ad449208c5f10d4b64a8cc7f89c0effd799b1761f540fd1778fd4d8c399034d716dabd3141b6aae1551062df8ac
6
+ metadata.gz: d965c77370235e2ceee16aca1e1193b01d2eb259b179307a51f8218778dda40c4fb35cea667568ce7cfa4d5f95297396a2876ae744d3638d359b81dfa63a019a
7
+ data.tar.gz: 052c8d3db3bd66279b50cf455e6928eb9e753e49f273399f85d5d0e2fe238ecc33c97a1a95d1b56f5bca9cb670c1fedb9eeb95fdb68232ac032647eb370b7992
data/CHANGELOG CHANGED
@@ -1,3 +1,17 @@
1
+ === 4.6.0 (2014-01-02)
2
+
3
+ * Add Database#call_mssql_sproc on MSSQL for calling stored procedures and handling output parameters (jrgns, jeremyevans) (#748)
4
+
5
+ * Handle RuntimeErrors raised by oci8 in the oracle adapter (jeremyevans)
6
+
7
+ * Support OFFSET/FETCH on Microsoft SQL Server 2012 (jeremyevans)
8
+
9
+ * Support :server option for Database#{commit,rollback}_prepared_transaction on PostgreSQL, MySQL, and H2 (jeremyevans) (#743)
10
+
11
+ * Do not attempt to eager load and raise an exception when doing Model.eager(...).naked.all (jeremyevans)
12
+
13
+ * Recognize a couple additional disconnect errors in the jdbc/postgresql adapter (jeremyevans) (#742)
14
+
1
15
  === 4.5.0 (2013-12-02)
2
16
 
3
17
  * Support :on_commit=>(:drop|:delete_rows|:preserve_rows) options when creating temp tables on PostgreSQL (rosenfeld) (#737)
data/README.rdoc CHANGED
@@ -19,7 +19,6 @@ toolkit for Ruby.
19
19
  == Resources
20
20
 
21
21
  * {Website}[http://sequel.jeremyevans.net]
22
- * {Blog}[http://sequel.heroku.com]
23
22
  * {Source code}[http://github.com/jeremyevans/sequel]
24
23
  * {Bug tracking}[http://github.com/jeremyevans/sequel/issues]
25
24
  * {Google group}[http://groups.google.com/group/sequel-talk]
@@ -0,0 +1,43 @@
1
+ = Stored Procedures in MSSQL
2
+
3
+ This guide documents the workaround implemented to allow executing stored procedures
4
+ in MSSQL, as well as getting the value of output variables.
5
+
6
+ == Simple Execution
7
+
8
+ The following stored procedure is used as an example:
9
+
10
+ CREATE PROCEDURE dbo.SequelTest(
11
+ @Input varchar(25),
12
+ @Output int OUTPUT
13
+ )
14
+ AS
15
+ SET @Output = LEN(@Input)
16
+ RETURN 0
17
+
18
+ Execute it as follows:
19
+
20
+ DB.call_mssql_sproc(:SequelTest, {:args => ['Input String', :output]})
21
+
22
+ Use the +:output+ symbol to denote an output variable. The result will contain a
23
+ hash of the output variables, as well as the result code and number of affected rows:
24
+
25
+ {:result => 0, :numrows => 1, :var1 => "1"}
26
+
27
+ Output variables will be strings by default. To specify their type, include the
28
+ SQL type:
29
+
30
+ DB.call_mssql_sproc(:SequelTest, {:args => ['Input String', [:output, 'int']]})
31
+
32
+ Result:
33
+
34
+ {:result => 0, :numrows => 1, :var1 => 1}
35
+
36
+ Output variables will be named +var#{n}+ where n is their zero indexed position
37
+ in the parameter list. To name the output variable, include their name:
38
+
39
+ DB.call_mssql_sproc(:SequelTest, {:args => ['Input String', [:output, nil, 'Output']]})
40
+
41
+ Result:
42
+
43
+ {:result => 0, :numrows => 1, :output => "1"}
@@ -52,9 +52,8 @@
52
52
  the graphviz dot program in order to create visualizations
53
53
  of the dataset's abstract syntax tree. Examples:
54
54
 
55
- * http://sequel.heroku.com/images/to_dot_simple.gif
56
- * http://sequel.heroku.com/images/to_dot_complex.gif
57
- * http://imgpaste.com/i/lxngy.gif
55
+ * http://sequel.jeremyevans.net/images/to_dot_simple.gif
56
+ * http://sequel.jeremyevans.net/images/to_dot_complex.gif
58
57
 
59
58
  Both the to_dot extension and reversible migrations support
60
59
  were inspired by Aaron Patterson's recent work on ActiveRecord
@@ -230,4 +230,4 @@ Backwards Compatibility
230
230
  Other News
231
231
  ----------
232
232
 
233
- * Sequel now has an official blog at http://sequel.heroku.com.
233
+ * Sequel now has an official blog at http://sequel.jeremyevans.net/blog.html.
@@ -0,0 +1,30 @@
1
+ = New Features
2
+
3
+ * Database#call_mssql_sproc is now available for calling
4
+ stored procedures on Microsoft SQL Server, including the use
5
+ of output parameters.
6
+
7
+ * The Database#{commit,rollback}_prepared_transaction methods now
8
+ support a :server option for the server on which to operate.
9
+
10
+ = Other Improvements
11
+
12
+ * On Microsoft SQL Server 2012, the native OFFSET/FETCH support
13
+ is now used for offsets, instead of emulating support via the
14
+ ROW_NUMBER window function.
15
+
16
+ * Eager loading is now skipped when doing eager(...).naked.all on
17
+ a model dataset, instead of raising an error. This can fix issues
18
+ when the eager_each plugin is used.
19
+
20
+ * A couple additional disconnection errors are now detected in the
21
+ jdbc/postgresql adapter.
22
+
23
+ * The tinytds adapter now handles returning rows when the fields
24
+ are not immediately available.
25
+
26
+ * RuntimeErrors raised by oci8 are now handled correctly in the
27
+ oracle adapter.
28
+
29
+ * Sequel's specs now work with RSpec 3, while still running
30
+ correctly on RSpec 1.3 and 2.
data/doc/security.rdoc CHANGED
@@ -168,6 +168,13 @@ Instead, you should do:
168
168
  The Sequel::Dataset#lock_style method also treats an input string
169
169
  as SQL code. This method should not be called with user input.
170
170
 
171
+ ==== SQL Type Names
172
+
173
+ In general, most places where Sequel needs to use an SQL type that should
174
+ be specified by the user, it allows you to use a ruby string, and that
175
+ string is used verbatim as the SQL type. You should not use user input
176
+ for type strings.
177
+
171
178
  === SQL Identifier Injections
172
179
 
173
180
  Usually, Sequel treats ruby symbols as SQL identifiers, and ruby
@@ -9,8 +9,8 @@ module Sequel
9
9
 
10
10
  # Commit an existing prepared transaction with the given transaction
11
11
  # identifier string.
12
- def commit_prepared_transaction(transaction_id)
13
- run("COMMIT TRANSACTION #{transaction_id}")
12
+ def commit_prepared_transaction(transaction_id, opts=OPTS)
13
+ run("COMMIT TRANSACTION #{transaction_id}", opts)
14
14
  end
15
15
 
16
16
  # H2 uses the :h2 database type.
@@ -20,8 +20,8 @@ module Sequel
20
20
 
21
21
  # Rollback an existing prepared transaction with the given transaction
22
22
  # identifier string.
23
- def rollback_prepared_transaction(transaction_id)
24
- run("ROLLBACK TRANSACTION #{transaction_id}")
23
+ def rollback_prepared_transaction(transaction_id, opts=OPTS)
24
+ run("ROLLBACK TRANSACTION #{transaction_id}", opts)
25
25
  end
26
26
 
27
27
  # H2 uses an IDENTITY type
@@ -83,6 +83,10 @@ module Sequel
83
83
 
84
84
  private
85
85
 
86
+ def disconnect_error?(exception, opts)
87
+ super || exception.message =~ /\AThis connection has been closed\.\z|\AFATAL: terminating connection due to administrator command\z/
88
+ end
89
+
86
90
  # Use setNull for nil arguments as the default behavior of setString
87
91
  # with nil doesn't appear to work correctly on PostgreSQL.
88
92
  def set_ps_arg_nil(cps, i)
@@ -143,6 +143,7 @@ module Sequel
143
143
  end
144
144
 
145
145
  def database_specific_error_class(exception, opts)
146
+ return super unless exception.respond_to?(:code)
146
147
  case exception.code
147
148
  when 1400, 1407
148
149
  NotNullConstraintViolation
@@ -34,6 +34,51 @@ module Sequel
34
34
  # to :integer.
35
35
  DECIMAL_TYPE_RE = /number|numeric|decimal/io
36
36
 
37
+ # Execute the given stored procedure with the given name.
38
+ #
39
+ # Options:
40
+ # :args :: Array of arguments to stored procedure. Output parameters to
41
+ # the function are specified using :output. You can also name
42
+ # output parameters and provide a type by using an array containing
43
+ # :output, the type name, and the parameter name.
44
+ # :server :: The server/shard on which to execute the procedure.
45
+ #
46
+ # Examples:
47
+ #
48
+ # DB.call_mssql_sproc(:SequelTest, {:args => ['input arg', :output]})
49
+ # DB.call_mssql_sproc(:SequelTest, {:args => ['input arg', [:output, 'int', 'varname']]})
50
+ def call_mssql_sproc(name, opts=OPTS)
51
+ args = opts[:args] || []
52
+ names = ['@RC AS RESULT', '@@ROWCOUNT AS NUMROWS']
53
+ declarations = ['@RC int']
54
+ values = []
55
+
56
+ args.each_with_index do |v, i|
57
+ if v.is_a?(Array)
58
+ v, type, select = v
59
+ else
60
+ type = select = nil
61
+ end
62
+
63
+ if v == :output
64
+ type = "nvarchar(max)" unless type
65
+ varname = "var#{i}" unless varname
66
+ select ||= varname
67
+ names << "@#{varname} AS #{quote_identifier(select)}"
68
+ declarations << "@#{varname} #{type}"
69
+ values << "@#{varname} OUTPUT"
70
+ else
71
+ values << literal(v)
72
+ end
73
+ end
74
+
75
+ sql = "DECLARE #{declarations.join(', ')}; EXECUTE @RC = #{name} #{values.join(', ')}; SELECT #{names.join(', ')}"
76
+
77
+ ds = dataset.with_sql(sql)
78
+ ds = ds.server(opts[:server]) if opts[:server]
79
+ ds.first
80
+ end
81
+
37
82
  # Microsoft SQL Server uses the :mssql type.
38
83
  def database_type
39
84
  :mssql
@@ -457,6 +502,10 @@ module Sequel
457
502
  FORMAT_DATE = "'%Y%m%d'".freeze
458
503
  CROSS_APPLY = 'CROSS APPLY'.freeze
459
504
  OUTER_APPLY = 'OUTER APPLY'.freeze
505
+ OFFSET = " OFFSET ".freeze
506
+ ROWS = " ROWS".freeze
507
+ ROWS_ONLY = " ROWS ONLY".freeze
508
+ FETCH_NEXT = " FETCH NEXT ".freeze
460
509
 
461
510
  Sequel::Dataset.def_mutation_method(:disable_insert_output, :output, :module=>self)
462
511
 
@@ -607,6 +656,18 @@ module Sequel
607
656
  sql << BRACKET_OPEN << name.to_s.gsub(/\]/, DOUBLE_BRACKET_CLOSE) << BRACKET_CLOSE
608
657
  end
609
658
 
659
+ # On MSSQL 2012+ add a default order to the current dataset if an offset is used.
660
+ # The default offset emulation using a subquery would be used in the unordered
661
+ # case by default, and that also adds a default order, so it's better to just
662
+ # avoid the subquery.
663
+ def select_sql
664
+ if @opts[:offset] && !@opts[:order] && is_2012_or_later?
665
+ order(1).select_sql
666
+ else
667
+ super
668
+ end
669
+ end
670
+
610
671
  # The version of the database server.
611
672
  def server_version
612
673
  db.server_version(@opts[:server])
@@ -703,6 +764,11 @@ module Sequel
703
764
  server_version >= 10000000
704
765
  end
705
766
 
767
+ # Whether we are using SQL Server 2012 or later.
768
+ def is_2012_or_later?
769
+ server_version >= 11000000
770
+ end
771
+
706
772
  # Use strict ISO-8601 format with T between date and time,
707
773
  # since that is the format that is multilanguage and not
708
774
  # DATEFORMAT dependent.
@@ -730,6 +796,11 @@ module Sequel
730
796
  end
731
797
  end
732
798
  alias update_from_sql delete_from2_sql
799
+
800
+ # Microsoft SQL Server 2012 has native support for offsets, but only for ordered datasets.
801
+ def emulate_offset_with_row_number?
802
+ super && !(is_2012_or_later? && @opts[:order])
803
+ end
733
804
 
734
805
  # Return the first primary key for the current table. If this table has
735
806
  # multiple primary keys, this will only return one of them. Used by #_import.
@@ -800,7 +871,8 @@ module Sequel
800
871
  BOOL_TRUE
801
872
  end
802
873
 
803
- # MSSQL adds the limit before the columns
874
+ # MSSQL adds the limit before the columns, except on 2012+ when using
875
+ # offsets on ordered queries.
804
876
  def select_clause_methods
805
877
  SELECT_CLAUSE_METHODS
806
878
  end
@@ -816,6 +888,8 @@ module Sequel
816
888
  # to allow the limit to be a bound variable.
817
889
  def select_limit_sql(sql)
818
890
  if l = @opts[:limit]
891
+ return if is_2012_or_later? && @opts[:order] && @opts[:offset]
892
+
819
893
  if is_2005_or_later?
820
894
  sql << TOP_PAREN
821
895
  literal_append(sql, l)
@@ -840,6 +914,25 @@ module Sequel
840
914
  end
841
915
  end
842
916
 
917
+ # On 2012+ when there is an order with an offset, append the offset (and possible
918
+ # limit) at the end of the order clause.
919
+ def select_order_sql(sql)
920
+ super
921
+ if is_2012_or_later? && @opts[:order]
922
+ if o = @opts[:offset]
923
+ sql << OFFSET
924
+ literal_append(sql, o)
925
+ sql << ROWS
926
+
927
+ if l = @opts[:limit]
928
+ sql << FETCH_NEXT
929
+ literal_append(sql, l)
930
+ sql << ROWS_ONLY
931
+ end
932
+ end
933
+ end
934
+ end
935
+
843
936
  # SQL fragment for MSSQL's OUTPUT clause.
844
937
  def output_sql(sql)
845
938
  return unless supports_output_clause?
@@ -50,8 +50,8 @@ module Sequel
50
50
 
51
51
  # Commit an existing prepared transaction with the given transaction
52
52
  # identifier string.
53
- def commit_prepared_transaction(transaction_id)
54
- run("XA COMMIT #{literal(transaction_id)}")
53
+ def commit_prepared_transaction(transaction_id, opts=OPTS)
54
+ run("XA COMMIT #{literal(transaction_id)}", opts)
55
55
  end
56
56
 
57
57
  # MySQL uses the :mysql database type
@@ -112,8 +112,8 @@ module Sequel
112
112
 
113
113
  # Rollback an existing prepared transaction with the given transaction
114
114
  # identifier string.
115
- def rollback_prepared_transaction(transaction_id)
116
- run("XA ROLLBACK #{literal(transaction_id)}")
115
+ def rollback_prepared_transaction(transaction_id, opts=OPTS)
116
+ run("XA ROLLBACK #{literal(transaction_id)}", opts)
117
117
  end
118
118
 
119
119
  # Get version of MySQL server, used for determined capabilities.
@@ -156,8 +156,8 @@ module Sequel
156
156
 
157
157
  # Commit an existing prepared transaction with the given transaction
158
158
  # identifier string.
159
- def commit_prepared_transaction(transaction_id)
160
- run("COMMIT PREPARED #{literal(transaction_id)}")
159
+ def commit_prepared_transaction(transaction_id, opts=OPTS)
160
+ run("COMMIT PREPARED #{literal(transaction_id)}", opts)
161
161
  end
162
162
 
163
163
  # Creates the function in the database. Arguments:
@@ -426,8 +426,8 @@ module Sequel
426
426
 
427
427
  # Rollback an existing prepared transaction with the given transaction
428
428
  # identifier string.
429
- def rollback_prepared_transaction(transaction_id)
430
- run("ROLLBACK PREPARED #{literal(transaction_id)}")
429
+ def rollback_prepared_transaction(transaction_id, opts=OPTS)
430
+ run("ROLLBACK PREPARED #{literal(transaction_id)}", opts)
431
431
  end
432
432
 
433
433
  # PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
@@ -230,11 +230,29 @@ module Sequel
230
230
  # various cases.
231
231
  def fetch_rows(sql)
232
232
  execute(sql) do |result|
233
- @columns = result.fields.map!{|c| output_identifier(c)}
234
- if db.timezone == :utc
235
- result.each(:timezone=>:utc){|r| yield r}
233
+ columns = result.fields.map!{|c| output_identifier(c)}
234
+ if columns.empty?
235
+ args = []
236
+ args << {:timezone=>:utc} if db.timezone == :utc
237
+ cols = nil
238
+ result.each(*args) do |r|
239
+ unless cols
240
+ cols = result.fields.map{|c| [c, output_identifier(c)]}
241
+ @columns = columns = cols.map{|c| c.last}
242
+ end
243
+ h = {}
244
+ cols.each do |s, sym|
245
+ h[sym] = r[s]
246
+ end
247
+ yield h
248
+ end
236
249
  else
237
- result.each{|r| yield r}
250
+ @columns = columns
251
+ if db.timezone == :utc
252
+ result.each(:timezone=>:utc){|r| yield r}
253
+ else
254
+ result.each{|r| yield r}
255
+ end
238
256
  end
239
257
  end
240
258
  self
@@ -9,8 +9,9 @@ module Sequel
9
9
  # If offset is used, an order must be provided, because the use of ROW_NUMBER
10
10
  # requires an order.
11
11
  def select_sql
12
- return super unless o = @opts[:offset]
12
+ return super unless emulate_offset_with_row_number?
13
13
 
14
+ offset = @opts[:offset]
14
15
  order = @opts[:order]
15
16
  if require_offset_order?
16
17
  order ||= default_offset_order
@@ -29,7 +30,7 @@ module Sequel
29
30
  from_self(:alias=>dsa1).
30
31
  select(*columns).
31
32
  limit(@opts[:limit]).
32
- where(SQL::Identifier.new(rn) > o).
33
+ where(SQL::Identifier.new(rn) > offset).
33
34
  order(rn))
34
35
  sql
35
36
  end
@@ -46,5 +47,10 @@ module Sequel
46
47
  def require_offset_order?
47
48
  true
48
49
  end
50
+
51
+ # Whether to use ROW_NUMBER to emulate offsets
52
+ def emulate_offset_with_row_number?
53
+ @opts[:offset]
54
+ end
49
55
  end
50
56
  end