sequel 4.3.0 → 4.4.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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +34 -0
  3. data/README.rdoc +7 -7
  4. data/Rakefile +2 -2
  5. data/doc/active_record.rdoc +2 -2
  6. data/doc/association_basics.rdoc +21 -7
  7. data/doc/bin_sequel.rdoc +2 -2
  8. data/doc/cheat_sheet.rdoc +2 -1
  9. data/doc/dataset_basics.rdoc +1 -1
  10. data/doc/dataset_filtering.rdoc +1 -1
  11. data/doc/migration.rdoc +2 -2
  12. data/doc/object_model.rdoc +2 -2
  13. data/doc/opening_databases.rdoc +13 -1
  14. data/doc/querying.rdoc +9 -4
  15. data/doc/release_notes/4.4.0.txt +92 -0
  16. data/doc/schema_modification.rdoc +1 -1
  17. data/doc/security.rdoc +2 -2
  18. data/doc/sql.rdoc +3 -3
  19. data/doc/thread_safety.rdoc +1 -1
  20. data/doc/validations.rdoc +1 -1
  21. data/doc/virtual_rows.rdoc +1 -1
  22. data/lib/sequel/adapters/jdbc.rb +85 -19
  23. data/lib/sequel/adapters/jdbc/db2.rb +1 -1
  24. data/lib/sequel/adapters/jdbc/derby.rb +1 -1
  25. data/lib/sequel/adapters/jdbc/h2.rb +2 -2
  26. data/lib/sequel/adapters/jdbc/hsqldb.rb +7 -0
  27. data/lib/sequel/adapters/jdbc/jtds.rb +1 -1
  28. data/lib/sequel/adapters/jdbc/oracle.rb +1 -1
  29. data/lib/sequel/adapters/jdbc/postgresql.rb +34 -3
  30. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +57 -0
  31. data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -2
  32. data/lib/sequel/adapters/oracle.rb +1 -1
  33. data/lib/sequel/adapters/shared/db2.rb +5 -0
  34. data/lib/sequel/adapters/shared/oracle.rb +41 -4
  35. data/lib/sequel/adapters/shared/sqlanywhere.rb +458 -0
  36. data/lib/sequel/adapters/sqlanywhere.rb +177 -0
  37. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +11 -3
  38. data/lib/sequel/core.rb +4 -4
  39. data/lib/sequel/database/connecting.rb +1 -1
  40. data/lib/sequel/database/query.rb +1 -1
  41. data/lib/sequel/database/schema_generator.rb +1 -1
  42. data/lib/sequel/database/schema_methods.rb +2 -2
  43. data/lib/sequel/dataset.rb +1 -1
  44. data/lib/sequel/dataset/actions.rb +2 -0
  45. data/lib/sequel/dataset/graph.rb +1 -1
  46. data/lib/sequel/dataset/prepared_statements.rb +1 -1
  47. data/lib/sequel/dataset/query.rb +37 -16
  48. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  49. data/lib/sequel/extensions/date_arithmetic.rb +2 -2
  50. data/lib/sequel/extensions/migration.rb +1 -1
  51. data/lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb +5 -4
  52. data/lib/sequel/extensions/pg_array.rb +2 -2
  53. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  54. data/lib/sequel/extensions/pg_hstore.rb +2 -2
  55. data/lib/sequel/extensions/pg_hstore_ops.rb +2 -2
  56. data/lib/sequel/extensions/pg_json.rb +2 -2
  57. data/lib/sequel/extensions/pg_json_ops.rb +2 -2
  58. data/lib/sequel/extensions/pg_range.rb +2 -2
  59. data/lib/sequel/extensions/pg_range_ops.rb +2 -2
  60. data/lib/sequel/extensions/pg_row.rb +2 -2
  61. data/lib/sequel/extensions/pg_row_ops.rb +3 -3
  62. data/lib/sequel/model.rb +1 -1
  63. data/lib/sequel/model/associations.rb +106 -17
  64. data/lib/sequel/model/base.rb +23 -19
  65. data/lib/sequel/plugins/json_serializer.rb +1 -1
  66. data/lib/sequel/plugins/many_through_many.rb +14 -6
  67. data/lib/sequel/plugins/pg_array_associations.rb +28 -0
  68. data/lib/sequel/plugins/rcte_tree.rb +1 -1
  69. data/lib/sequel/plugins/serialization.rb +11 -0
  70. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  71. data/lib/sequel/plugins/table_select.rb +41 -0
  72. data/lib/sequel/plugins/tree.rb +1 -1
  73. data/lib/sequel/sql.rb +2 -2
  74. data/lib/sequel/version.rb +1 -1
  75. data/spec/adapters/oracle_spec.rb +22 -1
  76. data/spec/adapters/postgres_spec.rb +31 -48
  77. data/spec/adapters/sqlanywhere_spec.rb +170 -0
  78. data/spec/core/dataset_spec.rb +109 -0
  79. data/spec/core/object_graph_spec.rb +7 -0
  80. data/spec/extensions/constraint_validations_spec.rb +7 -0
  81. data/spec/extensions/core_refinements_spec.rb +1 -1
  82. data/spec/extensions/many_through_many_spec.rb +65 -0
  83. data/spec/extensions/pg_array_associations_spec.rb +44 -0
  84. data/spec/extensions/rcte_tree_spec.rb +3 -3
  85. data/spec/extensions/spec_helper.rb +1 -1
  86. data/spec/extensions/table_select_spec.rb +71 -0
  87. data/spec/integration/associations_test.rb +279 -7
  88. data/spec/integration/dataset_test.rb +13 -4
  89. data/spec/integration/schema_test.rb +12 -14
  90. data/spec/model/associations_spec.rb +472 -3
  91. data/spec/model/class_dataset_methods_spec.rb +1 -0
  92. data/spec/model/model_spec.rb +10 -0
  93. metadata +10 -2
@@ -0,0 +1,177 @@
1
+ require 'sqlanywhere'
2
+
3
+ Sequel.require %w'shared/sqlanywhere', 'adapters'
4
+
5
+ module Sequel
6
+ # Module for holding all SqlAnywhere-related classes and modules for Sequel.
7
+ module SqlAnywhere
8
+
9
+ class SQLAnywhereException < StandardError
10
+ attr_reader :errno
11
+ attr_reader :sql
12
+
13
+ def initialize(message, errno, sql)
14
+ super(message)
15
+ @errno = errno
16
+ @sql = sql
17
+ end
18
+ end
19
+
20
+ TYPE_TRANSLATOR = tt = Class.new do
21
+ def blob(s) ::Sequel::SQL::Blob.new(s) end
22
+ def boolean(s) s.to_i != 0 end
23
+ def date(s) ::Date.strptime(s) end
24
+ def decimal(s) ::BigDecimal.new(s) end
25
+ def time(s) ::Sequel.string_to_time(s) end
26
+ end.new
27
+
28
+ SQLANYWHERE_TYPES = {}
29
+ {
30
+ [0, 484] => tt.method(:decimal),
31
+ [384] => tt.method(:date),
32
+ [388] => tt.method(:time),
33
+ [500] => tt.method(:boolean),
34
+ [524, 528] => tt.method(:blob)
35
+ }.each do |k,v|
36
+ k.each{|n| SQLANYWHERE_TYPES[n] = v}
37
+ end
38
+
39
+ # Database class for SQLAnywhere databases used with Sequel.
40
+ class Database < Sequel::Database
41
+ include Sequel::SqlAnywhere::DatabaseMethods
42
+
43
+ DEFAULT_CONFIG = { :user => 'dba', :password => 'sql' }
44
+
45
+ attr_accessor :api
46
+
47
+ set_adapter_scheme :sqlanywhere
48
+
49
+ def connect(server)
50
+ opts = server_opts(server)
51
+ unless conn_string = opts[:conn_string]
52
+ conn_string = []
53
+ conn_string << "Host=#{opts[:host]}#{":#{opts[:port]}" if opts[:port]}" if opts[:host]
54
+ conn_string << "DBN=#{opts[:database]}" if opts[:database]
55
+ conn_string << "UID=#{opts[:user]}" if opts[:user]
56
+ conn_string << "Password=#{opts[:password]}" if opts[:password]
57
+ conn_string << "CommLinks=#{opts[:commlinks]}" if opts[:commlinks]
58
+ conn_string << "ConnectionName=#{opts[:connection_name]}" if opts[:connection_name]
59
+ conn_string << "CharSet=#{opts[:encoding]}" if opts[:encoding]
60
+ conn_string << "Idle=0" # Prevent the server from disconnecting us if we're idle for >240mins (by default)
61
+ conn_string << nil
62
+ conn_string = conn_string.join(';')
63
+ end
64
+
65
+ conn = @api.sqlany_new_connection
66
+ raise LoadError, "Could not connect" unless conn && @api.sqlany_connect(conn, conn_string) == 1
67
+
68
+ if Sequel.application_timezone == :utc
69
+ @api.sqlany_execute_immediate(conn, "SET TEMPORARY OPTION time_zone_adjustment=0")
70
+ end
71
+
72
+ conn
73
+ end
74
+
75
+ # Closes given database connection.
76
+ def disconnect_connection(c)
77
+ @api.sqlany_disconnect(c)
78
+ end
79
+
80
+ # Returns number of rows affected
81
+ def execute_dui(sql, opts=OPTS)
82
+ synchronize do |conn|
83
+ _execute(conn, :rows, sql, opts)
84
+ end
85
+ end
86
+
87
+ def execute(sql, opts=OPTS, &block)
88
+ synchronize do |conn|
89
+ _execute(conn, :select, sql, opts, &block)
90
+ end
91
+ end
92
+
93
+ def execute_insert(sql, opts=OPTS)
94
+ synchronize do |conn|
95
+ _execute(conn, :insert, sql, opts)
96
+ end
97
+ end
98
+
99
+ private
100
+
101
+ LAST_INSERT_ID = 'SELECT @@IDENTITY'.freeze
102
+ def _execute(conn, type, sql, opts)
103
+ unless rs = log_yield(sql){@api.sqlany_execute_direct(conn, sql)}
104
+ result, errstr = @api.sqlany_error(conn)
105
+ raise_error(SQLAnywhereException.new(errstr, result, sql))
106
+ end
107
+
108
+ case type
109
+ when :select
110
+ yield rs if block_given?
111
+ when :rows
112
+ return @api.sqlany_affected_rows(rs)
113
+ when :insert
114
+ _execute(conn, :select, LAST_INSERT_ID, opts){|r| return @api.sqlany_get_column(r, 0)[1] if r && @api.sqlany_fetch_next(r) == 1}
115
+ end
116
+ ensure
117
+ @api.sqlany_commit(conn) unless in_transaction?
118
+ @api.sqlany_free_stmt(rs) if rs
119
+ end
120
+
121
+ def adapter_initialize
122
+ @conversion_procs = SQLANYWHERE_TYPES.dup
123
+ @conversion_procs[392] = method(:to_application_timestamp_sa)
124
+ @api = SQLAnywhere::SQLAnywhereInterface.new
125
+ raise LoadError, "Could not load SQLAnywhere DBCAPI library" if SQLAnywhere::API.sqlany_initialize_interface(@api) == 0
126
+ raise LoadError, "Could not initialize SQLAnywhere DBCAPI library" if @api.sqlany_init == 0
127
+ end
128
+
129
+ def log_connection_execute(conn, sql)
130
+ _execute(conn, nil, sql, OPTS)
131
+ end
132
+ end
133
+
134
+ # Dataset class for SqlAnywhere datasets accessed via the native driver.
135
+ class Dataset < Sequel::Dataset
136
+ include Sequel::SqlAnywhere::DatasetMethods
137
+
138
+ Database::DatasetClass = self
139
+
140
+ # Yield all rows matching this dataset. If the dataset is set to
141
+ # split multiple statements, yield arrays of hashes one per statement
142
+ # instead of yielding results for all statements as hashes.
143
+ def fetch_rows(sql)
144
+ db = @db
145
+ cps = db.conversion_procs
146
+ api = db.api
147
+ execute(sql) do |rs|
148
+ convert = (convert_smallint_to_bool and db.convert_smallint_to_bool)
149
+ col_infos = []
150
+ api.sqlany_num_cols(rs).times do |i|
151
+ _, _, name, _, type = api.sqlany_get_column_info(rs, i)
152
+ cp = if type == 500
153
+ cps[500] if convert
154
+ else
155
+ cps[type]
156
+ end
157
+ col_infos << [i, output_identifier(name), cp]
158
+ end
159
+
160
+ @columns = col_infos.map{|a| a[1]}
161
+
162
+ if rs
163
+ while api.sqlany_fetch_next(rs) == 1
164
+ h = {}
165
+ col_infos.each do |i, name, cp|
166
+ _, v = api.sqlany_get_column(rs, i)
167
+ h[name] = cp && v ? cp[v] : v
168
+ end
169
+ yield h
170
+ end
171
+ end
172
+ end
173
+ self
174
+ end
175
+ end
176
+ end
177
+ end
@@ -11,9 +11,12 @@ module Sequel
11
11
  def select_sql
12
12
  return super unless o = @opts[:offset]
13
13
 
14
- order = @opts[:order] || default_offset_order
15
- if order.nil? || order.empty?
16
- raise(Error, "#{db.database_type} requires an order be provided if using an offset")
14
+ order = @opts[:order]
15
+ if require_offset_order?
16
+ order ||= default_offset_order
17
+ if order.nil? || order.empty?
18
+ raise(Error, "#{db.database_type} requires an order be provided if using an offset")
19
+ end
17
20
  end
18
21
 
19
22
  columns = clone(:append_sql=>'').columns
@@ -38,5 +41,10 @@ module Sequel
38
41
  def default_offset_order
39
42
  clone(:append_sql=>'').columns
40
43
  end
44
+
45
+ # Whether an order is required when using offset emulation via ROW_NUMBER, true by default.
46
+ def require_offset_order?
47
+ true
48
+ end
41
49
  end
42
50
  end
@@ -17,8 +17,8 @@
17
17
  #
18
18
  # Sequel.sqlite('blog.db'){|db| puts db[:users].count}
19
19
  #
20
- # For a more expanded introduction, see the {README}[link:files/README_rdoc.html].
21
- # For a quicker introduction, see the {cheat sheet}[link:files/doc/cheat_sheet_rdoc.html].
20
+ # For a more expanded introduction, see the {README}[rdoc-ref:README.rdoc].
21
+ # For a quicker introduction, see the {cheat sheet}[rdoc-ref:doc/cheat_sheet.rdoc].
22
22
  module Sequel
23
23
  @convert_two_digit_years = true
24
24
  @datetime_class = Time
@@ -89,8 +89,8 @@ module Sequel
89
89
  #
90
90
  # Sequel.connect('sqlite://blog.db'){|db| puts db[:users].count}
91
91
  #
92
- # For details, see the {"Connecting to a Database" guide}[link:files/doc/opening_databases_rdoc.html].
93
- # To set up a master/slave or sharded database connection, see the {"Master/Slave Databases and Sharding" guide}[link:files/doc/sharding_rdoc.html].
92
+ # For details, see the {"Connecting to a Database" guide}[rdoc-ref:doc/opening_databases.rdoc].
93
+ # To set up a master/slave or sharded database connection, see the {"Master/Slave Databases and Sharding" guide}[rdoc-ref:doc/sharding.rdoc].
94
94
  def self.connect(*args, &block)
95
95
  Database.connect(*args, &block)
96
96
  end
@@ -6,7 +6,7 @@ module Sequel
6
6
  # ---------------------
7
7
 
8
8
  # Array of supported database adapters
9
- ADAPTERS = %w'ado amalgalite cubrid db2 dbi do firebird ibmdb informix jdbc mock mysql mysql2 odbc openbase oracle postgres sqlite swift tinytds'.collect{|x| x.to_sym}
9
+ ADAPTERS = %w'ado amalgalite cubrid db2 dbi do firebird ibmdb informix jdbc mock mysql mysql2 odbc openbase oracle postgres sqlanywhere sqlite swift tinytds'.collect{|x| x.to_sym}
10
10
 
11
11
  @single_threaded = false
12
12
 
@@ -6,7 +6,7 @@ module Sequel
6
6
  # ---------------------
7
7
 
8
8
  STRING_DEFAULT_RE = /\A'(.*)'\z/
9
- CURRENT_TIMESTAMP_RE = /now|CURRENT|getdate|\ADate\(\)\z/io
9
+ CURRENT_TIMESTAMP_RE = /now|today|CURRENT|getdate|\ADate\(\)\z/io
10
10
  COLUMN_SCHEMA_DATETIME_TYPES = [:date, :datetime]
11
11
  COLUMN_SCHEMA_STRING_TYPES = [:string, :blob, :date, :datetime, :time, :enum, :set, :interval]
12
12
 
@@ -13,7 +13,7 @@ module Sequel
13
13
  # the column method, which makes for a nicer DSL.
14
14
  #
15
15
  # For more information on Sequel's support for schema modification, see
16
- # the {"Schema Modification" guide}[link:files/doc/schema_modification_rdoc.html].
16
+ # the {"Schema Modification" guide}[rdoc-ref:doc/schema_modification.rdoc].
17
17
  class CreateTableGenerator
18
18
  # Classes specifying generic types that Sequel will convert to database-specific types.
19
19
  GENERIC_TYPES=[String, Integer, Fixnum, Bignum, Float, Numeric, BigDecimal,
@@ -71,7 +71,7 @@ module Sequel
71
71
  # definitions using <tt>create_table</tt>, and +add_index+ accepts all the options
72
72
  # available for index definition.
73
73
  #
74
- # See <tt>Schema::AlterTableGenerator</tt> and the {"Migrations and Schema Modification" guide}[link:files/doc/migration_rdoc.html].
74
+ # See <tt>Schema::AlterTableGenerator</tt> and the {"Migrations and Schema Modification" guide}[rdoc-ref:doc/migration.rdoc].
75
75
  def alter_table(name, generator=nil, &block)
76
76
  generator ||= alter_table_generator(&block)
77
77
  remove_cached_schema(name)
@@ -159,7 +159,7 @@ module Sequel
159
159
  # :inherits :: Inherit from a different tables. An array can be
160
160
  # specified to inherit from multiple tables.
161
161
  #
162
- # See <tt>Schema::Generator</tt> and the {"Schema Modification" guide}[link:files/doc/schema_modification_rdoc.html].
162
+ # See <tt>Schema::Generator</tt> and the {"Schema Modification" guide}[rdoc-ref:doc/schema_modification.rdoc].
163
163
  def create_table(name, options=OPTS, &block)
164
164
  remove_cached_schema(name)
165
165
  options = {:generator=>options} if options.is_a?(Schema::CreateTableGenerator)
@@ -21,7 +21,7 @@ module Sequel
21
21
  # Datasets are Enumerable objects, so they can be manipulated using any
22
22
  # of the Enumerable methods, such as map, inject, etc.
23
23
  #
24
- # For more information, see the {"Dataset Basics" guide}[link:files/doc/dataset_basics_rdoc.html].
24
+ # For more information, see the {"Dataset Basics" guide}[rdoc-ref:doc/dataset_basics.rdoc].
25
25
  class Dataset
26
26
  OPTS = Sequel::OPTS
27
27
 
@@ -271,6 +271,8 @@ module Sequel
271
271
  # :commit_every :: Open a new transaction for every given number of records.
272
272
  # For example, if you provide a value of 50, will commit
273
273
  # after every 50 records.
274
+ # :return :: When the :value is :primary_key, returns an array of
275
+ # autoincremented primary key values for the rows inserted.
274
276
  # :server :: Set the server/shard to use for the transaction and insert
275
277
  # queries.
276
278
  # :slice :: Same as :commit_every, :commit_every takes precedence.
@@ -44,7 +44,7 @@ module Sequel
44
44
  # some metadata about the join that makes it important to use +graph+ instead
45
45
  # of +join_table+.
46
46
  # :table_alias :: The alias to use for the table. If not specified, doesn't
47
- # alias the table. You will get an error if the the alias (or table) name is
47
+ # alias the table. You will get an error if the alias (or table) name is
48
48
  # used more than once.
49
49
  def graph(dataset, join_conditions = nil, options = OPTS, &block)
50
50
  # Allow the use of a dataset or symbol as the first argument
@@ -3,7 +3,7 @@ module Sequel
3
3
  # ---------------------
4
4
  # :section: 8 - Methods related to prepared statements or bound variables
5
5
  # On some adapters, these use native prepared statements and bound variables, on others
6
- # support is emulated. For details, see the {"Prepared Statements/Bound Variables" guide}[link:files/doc/prepared_statements_rdoc.html].
6
+ # support is emulated. For details, see the {"Prepared Statements/Bound Variables" guide}[rdoc-ref:doc/prepared_statements.rdoc].
7
7
  # ---------------------
8
8
 
9
9
  PREPARED_ARG_PLACEHOLDER = LiteralString.new('?').freeze
@@ -36,7 +36,7 @@ module Sequel
36
36
  QUERY_METHODS = (<<-METHS).split.map{|x| x.to_sym} + JOIN_METHODS
37
37
  add_graph_aliases and distinct except exclude exclude_having exclude_where
38
38
  filter for_update from from_self graph grep group group_and_count group_by having intersect invert
39
- limit lock_style naked or order order_append order_by order_more order_prepend qualify
39
+ limit lock_style naked offset or order order_append order_by order_more order_prepend qualify
40
40
  reverse reverse_order select select_all select_append select_group select_more server
41
41
  set_graph_aliases unfiltered ungraphed ungrouped union
42
42
  unlimited unordered where with with_recursive with_sql
@@ -524,6 +524,7 @@ module Sequel
524
524
  return from_self.limit(l, o) if @opts[:sql]
525
525
 
526
526
  if l.is_a?(Range)
527
+ no_offset = false
527
528
  o = l.first
528
529
  l = l.last - l.first + (l.exclude_end? ? 0 : 1)
529
530
  end
@@ -531,17 +532,10 @@ module Sequel
531
532
  if l.is_a?(Integer)
532
533
  raise(Error, 'Limits must be greater than or equal to 1') unless l >= 1
533
534
  end
534
- opts = {:limit => l}
535
- if o
536
- o = o.to_i if o.is_a?(String) && !o.is_a?(LiteralString)
537
- if o.is_a?(Integer)
538
- raise(Error, 'Offsets must be greater than or equal to 0') unless o >= 0
539
- end
540
- opts[:offset] = o
541
- elsif !no_offset
542
- opts[:offset] = nil
543
- end
544
- clone(opts)
535
+
536
+ ds = clone(:limit=>l)
537
+ ds = ds.offset(o) unless no_offset
538
+ ds
545
539
  end
546
540
 
547
541
  # Returns a cloned dataset with the given lock style. If style is a
@@ -568,6 +562,19 @@ module Sequel
568
562
  ds.row_proc = nil
569
563
  ds
570
564
  end
565
+
566
+ # Returns a copy of the dataset with a specified order. Can be safely combined with limit.
567
+ # If you call limit with an offset, it will override override the offset if you've called
568
+ # offset first.
569
+ #
570
+ # DB[:items].offset(10) # SELECT * FROM items OFFSET 10
571
+ def offset(o)
572
+ o = o.to_i if o.is_a?(String) && !o.is_a?(LiteralString)
573
+ if o.is_a?(Integer)
574
+ raise(Error, 'Offsets must be greater than or equal to 0') unless o >= 0
575
+ end
576
+ clone(:offset => o)
577
+ end
571
578
 
572
579
  # Adds an alternate filter to an existing filter using OR. If no filter
573
580
  # exists an +Error+ is raised.
@@ -842,7 +849,7 @@ module Sequel
842
849
  # where also accepts a block, which should return one of the above argument
843
850
  # types, and is treated the same way. This block yields a virtual row object,
844
851
  # which is easy to use to create identifiers and functions. For more details
845
- # on the virtual row support, see the {"Virtual Rows" guide}[link:files/doc/virtual_rows_rdoc.html]
852
+ # on the virtual row support, see the {"Virtual Rows" guide}[rdoc-ref:doc/virtual_rows.rdoc]
846
853
  #
847
854
  # If both a block and regular argument are provided, they get ANDed together.
848
855
  #
@@ -871,7 +878,7 @@ module Sequel
871
878
  # software = dataset.where(:category => 'software').where{price < 100}
872
879
  # # SELECT * FROM items WHERE ((category = 'software') AND (price < 100))
873
880
  #
874
- # See the the {"Dataset Filtering" guide}[link:files/doc/dataset_filtering_rdoc.html] for more examples and details.
881
+ # See the {"Dataset Filtering" guide}[rdoc-ref:doc/dataset_filtering.rdoc] for more examples and details.
875
882
  def where(*cond, &block)
876
883
  _filter(:where, *cond, &block)
877
884
  end
@@ -961,10 +968,24 @@ module Sequel
961
968
  !(@opts.collect{|k,v| k unless v.nil?}.compact & opts).empty?
962
969
  end
963
970
 
964
- # Whether this dataset is a simple SELECT * FROM table.
971
+ # Whether this dataset is a simple select from an underlying table, such as:
972
+ #
973
+ # SELECT * FROM table
974
+ # SELECT table.* FROM table
965
975
  def simple_select_all?
966
976
  o = @opts.reject{|k,v| v.nil? || NON_SQL_OPTIONS.include?(k)}
967
- o.length == 1 && (f = o[:from]) && f.length == 1 && (f.first.is_a?(Symbol) || f.first.is_a?(SQL::AliasedExpression))
977
+ if (f = o[:from]) && f.length == 1 && (f.first.is_a?(Symbol) || f.first.is_a?(SQL::AliasedExpression))
978
+ case o.length
979
+ when 1
980
+ true
981
+ when 2
982
+ (s = o[:select]) && s.length == 1 && s.first.is_a?(SQL::ColumnAll)
983
+ else
984
+ false
985
+ end
986
+ else
987
+ false
988
+ end
968
989
  end
969
990
 
970
991
  private
@@ -401,10 +401,10 @@ module Sequel
401
401
  end
402
402
 
403
403
  ds = from(:sequel_constraint_validations)
404
- ds.multi_insert(rows.flatten)
405
404
  unless drop_rows.empty?
406
405
  ds.where([:table, :constraint_name]=>drop_rows).delete
407
406
  end
407
+ ds.multi_insert(rows.flatten)
408
408
  end
409
409
 
410
410
  # Add the constraint to the generator, including a NOT NULL constraint
@@ -91,9 +91,9 @@ module Sequel
91
91
  each_valid_interval_unit(h, MYSQL_DURATION_UNITS) do |value, sql_unit|
92
92
  expr = Sequel.function(:DATE_ADD, expr, Sequel.lit(["INTERVAL ", " "], value, sql_unit))
93
93
  end
94
- when :mssql, :h2, :access
94
+ when :mssql, :h2, :access, :sqlanywhere
95
95
  units = case db_type
96
- when :mssql
96
+ when :mssql, :sqlanywhere
97
97
  MSSQL_DURATION_UNITS
98
98
  when :h2
99
99
  H2_DURATION_UNITS
@@ -373,7 +373,7 @@ module Sequel
373
373
  migrator_class(directory).new(db, directory, opts).is_current?
374
374
  end
375
375
 
376
- # Migrates the supplied database using the migration files in the the specified directory. Options:
376
+ # Migrates the supplied database using the migration files in the specified directory. Options:
377
377
  # :allow_missing_migration_files :: Don't raise an error if there are missing migration files.
378
378
  # :column :: The column in the :table argument storing the migration version (default: :version).
379
379
  # :current :: The current version of the database. If not given, it is retrieved from the database