sequel 3.43.0 → 3.44.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 (73) hide show
  1. data/CHANGELOG +32 -0
  2. data/doc/association_basics.rdoc +10 -3
  3. data/doc/release_notes/3.37.0.txt +1 -1
  4. data/doc/release_notes/3.44.0.txt +152 -0
  5. data/lib/sequel/adapters/ado.rb +0 -6
  6. data/lib/sequel/adapters/db2.rb +0 -3
  7. data/lib/sequel/adapters/dbi.rb +0 -6
  8. data/lib/sequel/adapters/ibmdb.rb +0 -3
  9. data/lib/sequel/adapters/jdbc.rb +8 -12
  10. data/lib/sequel/adapters/jdbc/as400.rb +2 -18
  11. data/lib/sequel/adapters/jdbc/derby.rb +10 -0
  12. data/lib/sequel/adapters/jdbc/h2.rb +10 -0
  13. data/lib/sequel/adapters/jdbc/hsqldb.rb +10 -0
  14. data/lib/sequel/adapters/jdbc/sqlite.rb +5 -0
  15. data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -4
  16. data/lib/sequel/adapters/mock.rb +5 -0
  17. data/lib/sequel/adapters/mysql2.rb +4 -13
  18. data/lib/sequel/adapters/odbc.rb +0 -5
  19. data/lib/sequel/adapters/oracle.rb +3 -5
  20. data/lib/sequel/adapters/postgres.rb +2 -1
  21. data/lib/sequel/adapters/shared/access.rb +10 -0
  22. data/lib/sequel/adapters/shared/cubrid.rb +9 -0
  23. data/lib/sequel/adapters/shared/db2.rb +10 -0
  24. data/lib/sequel/adapters/shared/mssql.rb +10 -0
  25. data/lib/sequel/adapters/shared/mysql.rb +14 -0
  26. data/lib/sequel/adapters/shared/oracle.rb +15 -0
  27. data/lib/sequel/adapters/shared/postgres.rb +26 -2
  28. data/lib/sequel/adapters/shared/sqlite.rb +15 -0
  29. data/lib/sequel/adapters/tinytds.rb +5 -32
  30. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +2 -37
  31. data/lib/sequel/core.rb +3 -3
  32. data/lib/sequel/database/misc.rb +40 -4
  33. data/lib/sequel/database/query.rb +1 -1
  34. data/lib/sequel/database/schema_methods.rb +33 -12
  35. data/lib/sequel/dataset/actions.rb +51 -2
  36. data/lib/sequel/dataset/features.rb +0 -6
  37. data/lib/sequel/dataset/sql.rb +1 -1
  38. data/lib/sequel/exceptions.rb +22 -7
  39. data/lib/sequel/extensions/columns_introspection.rb +30 -5
  40. data/lib/sequel/extensions/pg_auto_parameterize.rb +9 -0
  41. data/lib/sequel/model/associations.rb +50 -37
  42. data/lib/sequel/model/base.rb +30 -1
  43. data/lib/sequel/plugins/eager_each.rb +17 -21
  44. data/lib/sequel/plugins/identity_map.rb +2 -1
  45. data/lib/sequel/plugins/many_through_many.rb +1 -1
  46. data/lib/sequel/plugins/single_table_inheritance.rb +2 -2
  47. data/lib/sequel/plugins/tactical_eager_loading.rb +1 -1
  48. data/lib/sequel/sql.rb +4 -2
  49. data/lib/sequel/version.rb +1 -1
  50. data/spec/adapters/postgres_spec.rb +32 -2
  51. data/spec/adapters/sqlite_spec.rb +20 -0
  52. data/spec/core/database_spec.rb +40 -0
  53. data/spec/core/dataset_spec.rb +91 -4
  54. data/spec/core/mock_adapter_spec.rb +2 -1
  55. data/spec/core/schema_generator_spec.rb +4 -0
  56. data/spec/core/schema_spec.rb +9 -3
  57. data/spec/extensions/association_dependencies_spec.rb +3 -3
  58. data/spec/extensions/columns_introspection_spec.rb +28 -2
  59. data/spec/extensions/eager_each_spec.rb +0 -1
  60. data/spec/extensions/identity_map_spec.rb +3 -2
  61. data/spec/extensions/migration_spec.rb +6 -0
  62. data/spec/extensions/prepared_statements_associations_spec.rb +2 -2
  63. data/spec/extensions/rcte_tree_spec.rb +2 -2
  64. data/spec/extensions/single_table_inheritance_spec.rb +3 -0
  65. data/spec/extensions/tactical_eager_loading_spec.rb +1 -1
  66. data/spec/extensions/validation_class_methods_spec.rb +8 -0
  67. data/spec/integration/associations_test.rb +4 -4
  68. data/spec/integration/database_test.rb +68 -20
  69. data/spec/integration/dataset_test.rb +48 -0
  70. data/spec/integration/schema_test.rb +25 -1
  71. data/spec/model/associations_spec.rb +21 -8
  72. data/spec/model/dataset_methods_spec.rb +58 -18
  73. metadata +4 -2
@@ -30,6 +30,11 @@ module Sequel
30
30
 
31
31
  private
32
32
 
33
+ DATABASE_ERROR_REGEXPS = {/Abort due to constraint violation/ => ConstraintViolation}.freeze
34
+ def database_error_regexps
35
+ DATABASE_ERROR_REGEXPS
36
+ end
37
+
33
38
  # Use last_insert_rowid() to get the last inserted id.
34
39
  def last_insert_id(conn, opts={})
35
40
  statement(conn) do |stmt|
@@ -18,7 +18,7 @@ module Sequel
18
18
  # than getObject() for this column avoids the problem.
19
19
  # Reference: http://social.msdn.microsoft.com/Forums/en/sqldataaccess/thread/20df12f3-d1bf-4526-9daa-239a83a8e435
20
20
  module MetadataDatasetMethods
21
- def process_result_set_convert(cols, result, rn)
21
+ def process_result_set_convert(cols, result)
22
22
  while result.next
23
23
  row = {}
24
24
  cols.each do |n, i, p|
@@ -40,18 +40,16 @@ module Sequel
40
40
  v
41
41
  end
42
42
  end
43
- row.delete(rn) if rn
44
43
  yield row
45
44
  end
46
45
  end
47
46
 
48
- def process_result_set_no_convert(cols, result, rn)
47
+ def process_result_set_no_convert(cols, result)
49
48
  while result.next
50
49
  row = {}
51
50
  cols.each do |n, i|
52
51
  row[n] = (n == :is_autoincrement ? result.getString(i) : result.getObject(i))
53
52
  end
54
- row.delete(rn) if rn
55
53
  yield row
56
54
  end
57
55
  end
@@ -66,6 +66,11 @@ module Sequel
66
66
  db.instance_eval do
67
67
  @primary_key_sequences = {}
68
68
  end
69
+ end,
70
+ 'mssql' => lambda do |db|
71
+ db.instance_eval do
72
+ @server_version = 10000000
73
+ end
69
74
  end
70
75
  }
71
76
 
@@ -138,21 +138,12 @@ module Sequel
138
138
  # Yield all rows matching this dataset.
139
139
  def fetch_rows(sql)
140
140
  execute(sql) do |r|
141
- if identifier_output_method
142
- cols = r.fields
143
- @columns = cols2 = cols.map{|c| output_identifier(c.to_s)}
144
- cs = cols.zip(cols2)
145
- r.each(:cast_booleans=>convert_tinyint_to_bool?) do |row|
146
- h = {}
147
- cs.each do |a, b|
148
- h[b] = row[a]
149
- end
150
- yield h
151
- end
141
+ @columns = if identifier_output_method
142
+ r.fields.map!{|c| output_identifier(c.to_s)}
152
143
  else
153
- @columns = r.fields
154
- r.each(:cast_booleans=>convert_tinyint_to_bool?){|h| yield h}
144
+ r.fields
155
145
  end
146
+ r.each(:cast_booleans=>convert_tinyint_to_bool?){|h| yield h}
156
147
  end
157
148
  self
158
149
  end
@@ -105,16 +105,11 @@ module Sequel
105
105
  i = -1
106
106
  cols = s.columns(true).map{|c| [output_identifier(c.name), i+=1]}
107
107
  columns = cols.map{|c| c.at(0)}
108
- if opts[:offset] && offset_returns_row_number_column?
109
- rn = row_number_column
110
- columns.delete(rn)
111
- end
112
108
  @columns = columns
113
109
  if rows = s.fetch_all
114
110
  rows.each do |row|
115
111
  hash = {}
116
112
  cols.each{|n,i| hash[n] = convert_odbc_value(row[i])}
117
- hash.delete(rn) if rn
118
113
  yield hash
119
114
  end
120
115
  end
@@ -37,7 +37,9 @@ module Sequel
37
37
  dbname = opts[:host]
38
38
  end
39
39
  conn = OCI8.new(opts[:user], opts[:password], dbname, opts[:privilege])
40
- conn.prefetch_rows = typecast_value_integer(opts.fetch(:prefetch_rows, 100))
40
+ if prefetch_rows = opts.fetch(:prefetch_rows, 100)
41
+ conn.prefetch_rows = typecast_value_integer(prefetch_rows)
42
+ end
41
43
  conn.autocommit = true
42
44
  conn.non_blocking = true
43
45
 
@@ -383,18 +385,14 @@ module Sequel
383
385
 
384
386
  def fetch_rows(sql)
385
387
  execute(sql) do |cursor|
386
- offset = @opts[:offset]
387
- rn = row_number_column
388
388
  cps = db.conversion_procs
389
389
  cols = columns = cursor.get_col_names.map{|c| output_identifier(c)}
390
390
  metadata = cursor.column_metadata
391
391
  cm = cols.zip(metadata).map{|c, m| [c, cps[m.data_type]]}
392
- columns = cols.reject{|x| x == rn} if offset
393
392
  @columns = columns
394
393
  while r = cursor.fetch
395
394
  row = {}
396
395
  r.zip(cm).each{|v, (c, cp)| row[c] = ((v && cp) ? cp.call(v) : v)}
397
- row.delete(rn) if offset
398
396
  yield row
399
397
  end
400
398
  end
@@ -289,7 +289,8 @@ module Sequel
289
289
 
290
290
  # +copy_into+ uses PostgreSQL's +COPY FROM STDIN+ SQL statement to do very fast inserts
291
291
  # into a table using input preformatting in either CSV or PostgreSQL text format.
292
- # This method is only supported if pg is the underlying ruby driver. This method should only be called if you want
292
+ # This method is only supported if pg 0.14.0+ is the underlying ruby driver.
293
+ # This method should only be called if you want
293
294
  # results returned to the client. If you are using +COPY FROM+
294
295
  # with a filename, you should just use +run+ instead of this method.
295
296
  #
@@ -43,6 +43,16 @@ module Sequel
43
43
  run(ds.into(name).sql)
44
44
  end
45
45
 
46
+ DATABASE_ERROR_REGEXPS = {
47
+ /The changes you requested to the table were not successful because they would create duplicate values in the index, primary key, or relationship/ => UniqueConstraintViolation,
48
+ /You cannot add or change a record because a related record is required|The record cannot be deleted or changed because table/ => ForeignKeyConstraintViolation,
49
+ /One or more values are prohibited by the validation rule/ => CheckConstraintViolation,
50
+ /You must enter a value in the .+ field|cannot contain a Null value because the Required property for this field is set to True/ => NotNullConstraintViolation,
51
+ }.freeze
52
+ def database_error_regexps
53
+ DATABASE_ERROR_REGEXPS
54
+ end
55
+
46
56
  # The SQL to drop an index for the table.
47
57
  def drop_index_sql(table, op)
48
58
  "DROP INDEX #{quote_identifier(op[:name] || default_index_name(table, op[:columns]))} ON #{quote_schema_table(table)}"
@@ -126,6 +126,15 @@ module Sequel
126
126
  :query
127
127
  end
128
128
 
129
+ DATABASE_ERROR_REGEXPS = {
130
+ /Operation would have caused one or more unique constraint violations/ => UniqueConstraintViolation,
131
+ /The constraint of the foreign key .+ is invalid|Update\/Delete operations are restricted by the foreign key/ => ForeignKeyConstraintViolation,
132
+ /cannot be made NULL/ => NotNullConstraintViolation,
133
+ }.freeze
134
+ def database_error_regexps
135
+ DATABASE_ERROR_REGEXPS
136
+ end
137
+
129
138
  # CUBRID is case insensitive, so don't modify identifiers
130
139
  def identifier_input_method_default
131
140
  nil
@@ -154,6 +154,16 @@ module Sequel
154
154
  end
155
155
  end
156
156
 
157
+ DATABASE_ERROR_REGEXPS = {
158
+ /DB2 SQL Error: SQLCODE=-803, SQLSTATE=23505|One or more values in the INSERT statement, UPDATE statement, or foreign key update caused by a DELETE statement are not valid because the primary key, unique constraint or unique index/ => UniqueConstraintViolation,
159
+ /DB2 SQL Error: (SQLCODE=-530, SQLSTATE=23503|SQLCODE=-532, SQLSTATE=23504)|The insert or update value of the FOREIGN KEY .+ is not equal to any value of the parent key of the parent table|A parent row cannot be deleted because the relationship .+ restricts the deletion/ => ForeignKeyConstraintViolation,
160
+ /DB2 SQL Error: SQLCODE=-545, SQLSTATE=23513|The requested operation is not allowed because a row does not satisfy the check constraint/ => CheckConstraintViolation,
161
+ /DB2 SQL Error: SQLCODE=-407, SQLSTATE=23502|Assignment of a NULL value to a NOT NULL column/ => NotNullConstraintViolation,
162
+ }.freeze
163
+ def database_error_regexps
164
+ DATABASE_ERROR_REGEXPS
165
+ end
166
+
157
167
  # DB2 has issues with quoted identifiers, so
158
168
  # turn off database quoting by default.
159
169
  def quote_identifiers_default
@@ -238,6 +238,16 @@ module Sequel
238
238
  run(ds.into(name).sql)
239
239
  end
240
240
 
241
+ DATABASE_ERROR_REGEXPS = {
242
+ /Violation of UNIQUE KEY constraint/ => UniqueConstraintViolation,
243
+ /conflicted with the (FOREIGN KEY.*|REFERENCE) constraint/ => ForeignKeyConstraintViolation,
244
+ /conflicted with the CHECK constraint/ => CheckConstraintViolation,
245
+ /column does not allow nulls/ => NotNullConstraintViolation,
246
+ }.freeze
247
+ def database_error_regexps
248
+ DATABASE_ERROR_REGEXPS
249
+ end
250
+
241
251
  # The name of the constraint for setting the default value on the table and column.
242
252
  # The SQL used to select default constraints utilizes MSSQL catalog views which were introduced in 2005.
243
253
  # This method intentionally does not support MSSQL 2000.
@@ -350,6 +350,15 @@ module Sequel
350
350
  "#{super}#{" ENGINE=#{engine}" if engine}#{" DEFAULT CHARSET=#{charset}" if charset}#{" DEFAULT COLLATE=#{collate}" if collate}"
351
351
  end
352
352
 
353
+ DATABASE_ERROR_REGEXPS = {
354
+ /Duplicate entry .+ for key/ => UniqueConstraintViolation,
355
+ /foreign key constraint fails/ => ForeignKeyConstraintViolation,
356
+ /cannot be null/ => NotNullConstraintViolation,
357
+ }.freeze
358
+ def database_error_regexps
359
+ DATABASE_ERROR_REGEXPS
360
+ end
361
+
353
362
  # Backbone of the tables and views support using SHOW FULL TABLES.
354
363
  def full_tables(type, opts)
355
364
  m = output_identifier_meth
@@ -430,6 +439,11 @@ module Sequel
430
439
  true
431
440
  end
432
441
 
442
+ # MySQL supports CREATE OR REPLACE VIEW.
443
+ def supports_create_or_replace_view?
444
+ true
445
+ end
446
+
433
447
  # Respect the :size option if given to produce
434
448
  # tinyblob, mediumblob, and longblob if :tiny,
435
449
  # :medium, or :long is given.
@@ -136,6 +136,16 @@ module Sequel
136
136
  sql
137
137
  end
138
138
 
139
+ DATABASE_ERROR_REGEXPS = {
140
+ /unique constraint .+ violated/ => UniqueConstraintViolation,
141
+ /integrity constraint .+ violated/ => ForeignKeyConstraintViolation,
142
+ /check constraint .+ violated/ => CheckConstraintViolation,
143
+ /cannot insert NULL into|cannot update .+ to NULL/ => NotNullConstraintViolation,
144
+ }.freeze
145
+ def database_error_regexps
146
+ DATABASE_ERROR_REGEXPS
147
+ end
148
+
139
149
  def default_sequence_name(table, column)
140
150
  "seq_#{table}_#{column}"
141
151
  end
@@ -160,6 +170,11 @@ module Sequel
160
170
  end
161
171
  end
162
172
 
173
+ # Oracle supports CREATE OR REPLACE VIEW.
174
+ def supports_create_or_replace_view?
175
+ true
176
+ end
177
+
163
178
  # Oracle's integer/:number type handles larger values than
164
179
  # most other databases's bigint types, so it should be
165
180
  # safe to use for Bignum.
@@ -53,14 +53,14 @@ module Sequel
53
53
  end
54
54
 
55
55
  class CreateTableGenerator < Sequel::Schema::Generator
56
- # Add an exclusion constraint when creating the table. elements should be
56
+ # Add an exclusion constraint when creating the table. Elements should be
57
57
  # an array of 2 element arrays, with the first element being the column or
58
58
  # expression the exclusion constraint is applied to, and the second element
59
59
  # being the operator to use for the column/expression to check for exclusion.
60
60
  #
61
61
  # Example:
62
62
  #
63
- # exclusion_constraint([[:col1, '&&'], [:col2, '=']])
63
+ # exclude([[:col1, '&&'], [:col2, '=']])
64
64
  # # EXCLUDE USING gist (col1 WITH &&, col2 WITH =)
65
65
  #
66
66
  # Options supported:
@@ -89,6 +89,9 @@ module Sequel
89
89
  end
90
90
  end
91
91
 
92
+ # Error raised when Sequel determines a PostgreSQL exclusion constraint has been violated.
93
+ class ExclusionConstraintViolation < Sequel::ConstraintViolation; end
94
+
92
95
  # Methods shared by Database instances that connect to PostgreSQL.
93
96
  module DatabaseMethods
94
97
  EXCLUDE_SCHEMAS = /pg_*|information_schema/i
@@ -627,6 +630,17 @@ module Sequel
627
630
  end
628
631
  end
629
632
 
633
+ DATABASE_ERROR_REGEXPS = {
634
+ /duplicate key value violates unique constraint/ => UniqueConstraintViolation,
635
+ /violates foreign key constraint/ => ForeignKeyConstraintViolation,
636
+ /violates check constraint/ => CheckConstraintViolation,
637
+ /violates not-null constraint/ => NotNullConstraintViolation,
638
+ /conflicting key value violates exclusion constraint/ => ExclusionConstraintViolation,
639
+ }.freeze
640
+ def database_error_regexps
641
+ DATABASE_ERROR_REGEXPS
642
+ end
643
+
630
644
  # SQL for doing fast table insert from stdin.
631
645
  def copy_into_sql(table, opts)
632
646
  sql = "COPY #{literal(table)}"
@@ -717,6 +731,11 @@ module Sequel
717
731
  "CREATE TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
718
732
  end
719
733
 
734
+ # DDL fragment for initial part of CREATE VIEW statement
735
+ def create_view_prefix_sql(name, options)
736
+ "CREATE #{'OR REPLACE 'if options[:replace]}#{'TEMPORARY 'if options[:temp]}VIEW #{quote_schema_table(name)}"
737
+ end
738
+
720
739
  # The errors that the main adapters can raise, depends on the adapter being used
721
740
  def database_error_classes
722
741
  CONVERTED_EXCEPTIONS
@@ -919,6 +938,11 @@ module Sequel
919
938
  true
920
939
  end
921
940
 
941
+ # PostgreSQL supports CREATE OR REPLACE VIEW.
942
+ def supports_create_or_replace_view?
943
+ true
944
+ end
945
+
922
946
  # Handle bigserial type if :serial option is present
923
947
  def type_literal_generic_bignum(column)
924
948
  column[:serial] ? :bigserial : super
@@ -312,6 +312,21 @@ module Sequel
312
312
  ps
313
313
  end
314
314
 
315
+ # SQLite support creating temporary views.
316
+ def create_view_prefix_sql(name, options)
317
+ "CREATE #{'TEMPORARY 'if options[:temp]}VIEW #{quote_schema_table(name)}"
318
+ end
319
+
320
+ DATABASE_ERROR_REGEXPS = {
321
+ /is not unique\z/ => UniqueConstraintViolation,
322
+ /foreign key constraint failed\z/ => ForeignKeyConstraintViolation,
323
+ /\A(SQLITE ERROR 19 \(CONSTRAINT\) : )?constraint failed\z/ => ConstraintViolation,
324
+ /may not be NULL\z/ => NotNullConstraintViolation,
325
+ }.freeze
326
+ def database_error_regexps
327
+ DATABASE_ERROR_REGEXPS
328
+ end
329
+
315
330
  # The array of column schema hashes for the current columns in the table
316
331
  def defined_columns_for(table)
317
332
  cols = parse_pragma(table, {})
@@ -18,6 +18,7 @@ module Sequel
18
18
  opts = server_opts(server)
19
19
  opts[:username] = opts[:user]
20
20
  c = TinyTds::Client.new(opts)
21
+ c.query_options.merge!(:cache_rows=>false)
21
22
 
22
23
  if (ts = opts[:textsize])
23
24
  sql = "SET TEXTSIZE #{typecast_value_integer(ts)}"
@@ -215,40 +216,12 @@ module Sequel
215
216
  # various cases.
216
217
  def fetch_rows(sql)
217
218
  execute(sql) do |result|
218
- each_opts = {:cache_rows=>false}
219
- each_opts[:timezone] = :utc if db.timezone == :utc
220
- rn = row_number_column if offset = @opts[:offset]
221
- columns = cols = result.fields.map{|c| output_identifier(c)}
222
- if offset
223
- rn = row_number_column
224
- columns = columns.dup
225
- columns.delete(rn)
226
- end
227
- @columns = columns
228
- #if identifier_output_method
229
- each_opts[:as] = :array
230
- result.each(each_opts) do |r|
231
- h = {}
232
- cols.zip(r).each{|k, v| h[k] = v}
233
- h.delete(rn) if rn
234
- yield h
235
- end
236
- =begin
237
- # Temporarily disable this optimization, as tiny_tds uses string keys
238
- # if result.fields is called before result.each(:symbolize_keys=>true).
239
- # See https://github.com/rails-sqlserver/tiny_tds/issues/57
219
+ @columns = result.fields.map!{|c| output_identifier(c)}
220
+ if db.timezone == :utc
221
+ result.each(:timezone=>:utc){|r| yield r}
240
222
  else
241
- each_opts[:symbolize_keys] = true
242
- if offset
243
- result.each(each_opts) do |r|
244
- r.delete(rn) if rn
245
- yield r
246
- end
247
- else
248
- result.each(each_opts, &Proc.new)
249
- end
223
+ result.each{|r| yield r}
250
224
  end
251
- =end
252
225
  end
253
226
  self
254
227
  end
@@ -1,36 +1,5 @@
1
1
  module Sequel
2
2
  module EmulateOffsetWithRowNumber
3
- # When a subselect that uses :offset is used in IN or NOT IN,
4
- # use a nested subselect that only includes the first column
5
- # instead of the ROW_NUMBER column added by the emulated offset support.
6
- def complex_expression_sql_append(sql, op, args)
7
- case op
8
- when :IN, :"NOT IN"
9
- ds = args.at(1)
10
- if ds.is_a?(Sequel::Dataset) && ds.opts[:offset]
11
- c = ds.opts[:select].first
12
- case c
13
- when Symbol
14
- t, cl, a = split_symbol(c)
15
- if a
16
- c = SQL::Identifier.new(a)
17
- elsif t
18
- c = SQL::Identifier.new(cl)
19
- end
20
- when SQL::AliasedExpression
21
- c = SQL::Identifier.new(c.aliaz)
22
- when SQL::QualifiedIdentifier
23
- c = SQL::Identifier.new(c.column)
24
- end
25
- super(sql, op, [args.at(0), ds.from_self.select(c)])
26
- else
27
- super
28
- end
29
- else
30
- super
31
- end
32
- end
33
-
34
3
  # Emulate OFFSET support with the ROW_NUMBER window function
35
4
  #
36
5
  # The implementation is ugly, cloning the current dataset and modifying
@@ -47,6 +16,7 @@ module Sequel
47
16
  raise(Error, "#{db.database_type} requires an order be provided if using an offset")
48
17
  end
49
18
 
19
+ columns = clone(:append_sql=>'').columns
50
20
  dsa1 = dataset_alias(1)
51
21
  rn = row_number_column
52
22
  sql = @opts[:append_sql] || ''
@@ -54,6 +24,7 @@ module Sequel
54
24
  unordered.
55
25
  select_append{ROW_NUMBER(:over, :order=>order){}.as(rn)}.
56
26
  from_self(:alias=>dsa1).
27
+ select(*columns).
57
28
  limit(@opts[:limit]).
58
29
  where(SQL::Identifier.new(rn) > o).
59
30
  order(rn))
@@ -67,11 +38,5 @@ module Sequel
67
38
  def default_offset_order
68
39
  clone(:append_sql=>'').columns
69
40
  end
70
-
71
- # This emulation adds an extra row number column that should be
72
- # eliminated.
73
- def offset_returns_row_number_column?
74
- true
75
- end
76
41
  end
77
42
  end