sequel 4.3.0 → 4.4.0

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