sequel 3.27.0 → 3.28.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/CHANGELOG +96 -0
  2. data/README.rdoc +2 -2
  3. data/Rakefile +1 -1
  4. data/doc/association_basics.rdoc +48 -0
  5. data/doc/opening_databases.rdoc +29 -5
  6. data/doc/prepared_statements.rdoc +1 -0
  7. data/doc/release_notes/3.28.0.txt +304 -0
  8. data/doc/testing.rdoc +42 -0
  9. data/doc/transactions.rdoc +97 -0
  10. data/lib/sequel/adapters/db2.rb +95 -65
  11. data/lib/sequel/adapters/firebird.rb +25 -219
  12. data/lib/sequel/adapters/ibmdb.rb +440 -0
  13. data/lib/sequel/adapters/jdbc.rb +12 -0
  14. data/lib/sequel/adapters/jdbc/as400.rb +0 -7
  15. data/lib/sequel/adapters/jdbc/db2.rb +49 -0
  16. data/lib/sequel/adapters/jdbc/firebird.rb +34 -0
  17. data/lib/sequel/adapters/jdbc/oracle.rb +2 -27
  18. data/lib/sequel/adapters/jdbc/transactions.rb +34 -0
  19. data/lib/sequel/adapters/mysql.rb +10 -15
  20. data/lib/sequel/adapters/odbc.rb +1 -2
  21. data/lib/sequel/adapters/odbc/db2.rb +5 -5
  22. data/lib/sequel/adapters/postgres.rb +71 -11
  23. data/lib/sequel/adapters/shared/db2.rb +290 -0
  24. data/lib/sequel/adapters/shared/firebird.rb +214 -0
  25. data/lib/sequel/adapters/shared/mssql.rb +18 -75
  26. data/lib/sequel/adapters/shared/mysql.rb +13 -0
  27. data/lib/sequel/adapters/shared/postgres.rb +52 -36
  28. data/lib/sequel/adapters/shared/sqlite.rb +32 -36
  29. data/lib/sequel/adapters/sqlite.rb +4 -8
  30. data/lib/sequel/adapters/tinytds.rb +7 -3
  31. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +55 -0
  32. data/lib/sequel/core.rb +1 -1
  33. data/lib/sequel/database/connecting.rb +1 -1
  34. data/lib/sequel/database/misc.rb +6 -5
  35. data/lib/sequel/database/query.rb +1 -1
  36. data/lib/sequel/database/schema_generator.rb +2 -1
  37. data/lib/sequel/dataset/actions.rb +149 -33
  38. data/lib/sequel/dataset/features.rb +44 -7
  39. data/lib/sequel/dataset/misc.rb +9 -1
  40. data/lib/sequel/dataset/prepared_statements.rb +2 -2
  41. data/lib/sequel/dataset/query.rb +63 -10
  42. data/lib/sequel/dataset/sql.rb +22 -5
  43. data/lib/sequel/model.rb +3 -3
  44. data/lib/sequel/model/associations.rb +250 -27
  45. data/lib/sequel/model/base.rb +10 -16
  46. data/lib/sequel/plugins/many_through_many.rb +34 -2
  47. data/lib/sequel/plugins/prepared_statements_with_pk.rb +1 -1
  48. data/lib/sequel/sql.rb +94 -51
  49. data/lib/sequel/version.rb +1 -1
  50. data/spec/adapters/db2_spec.rb +146 -0
  51. data/spec/adapters/postgres_spec.rb +74 -6
  52. data/spec/adapters/spec_helper.rb +1 -0
  53. data/spec/adapters/sqlite_spec.rb +11 -0
  54. data/spec/core/database_spec.rb +7 -0
  55. data/spec/core/dataset_spec.rb +180 -17
  56. data/spec/core/expression_filters_spec.rb +107 -41
  57. data/spec/core/spec_helper.rb +11 -0
  58. data/spec/extensions/many_through_many_spec.rb +115 -1
  59. data/spec/extensions/prepared_statements_with_pk_spec.rb +3 -3
  60. data/spec/integration/associations_test.rb +193 -15
  61. data/spec/integration/database_test.rb +4 -2
  62. data/spec/integration/dataset_test.rb +215 -19
  63. data/spec/integration/plugin_test.rb +8 -5
  64. data/spec/integration/prepared_statement_test.rb +91 -98
  65. data/spec/integration/schema_test.rb +27 -11
  66. data/spec/integration/spec_helper.rb +10 -0
  67. data/spec/integration/type_test.rb +2 -2
  68. data/spec/model/association_reflection_spec.rb +91 -0
  69. data/spec/model/associations_spec.rb +13 -0
  70. data/spec/model/base_spec.rb +8 -21
  71. data/spec/model/eager_loading_spec.rb +243 -9
  72. data/spec/model/model_spec.rb +15 -2
  73. metadata +16 -4
@@ -0,0 +1,214 @@
1
+ module Sequel
2
+ module Firebird
3
+ module DatabaseMethods
4
+ AUTO_INCREMENT = ''.freeze
5
+ TEMPORARY = 'GLOBAL TEMPORARY '.freeze
6
+
7
+ def clear_primary_key(*tables)
8
+ tables.each{|t| @primary_keys.delete(dataset.send(:input_identifier, t))}
9
+ end
10
+
11
+ def create_trigger(*args)
12
+ self << create_trigger_sql(*args)
13
+ end
14
+
15
+ def database_type
16
+ :firebird
17
+ end
18
+
19
+ def drop_sequence(name)
20
+ self << drop_sequence_sql(name)
21
+ end
22
+
23
+ def drop_table(*names)
24
+ clear_primary_key(*names)
25
+ super
26
+ end
27
+
28
+ # Return primary key for the given table.
29
+ def primary_key(table)
30
+ t = dataset.send(:input_identifier, table)
31
+ @primary_keys.fetch(t) do
32
+ pk = fetch("SELECT RDB$FIELD_NAME FROM RDB$INDEX_SEGMENTS NATURAL JOIN RDB$RELATION_CONSTRAINTS WHERE RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' AND RDB$RELATION_NAME = ?", t).single_value
33
+ @primary_keys[t] = dataset.send(:output_identifier, pk.rstrip) if pk
34
+ end
35
+ end
36
+
37
+ def restart_sequence(*args)
38
+ self << restart_sequence_sql(*args)
39
+ end
40
+
41
+ def sequences(opts={})
42
+ ds = self[:"rdb$generators"].server(opts[:server]).filter(:"rdb$system_flag" => 0).select(:"rdb$generator_name")
43
+ block_given? ? yield(ds) : ds.map{|r| ds.send(:output_identifier, r[:"rdb$generator_name"])}
44
+ end
45
+
46
+ def tables(opts={})
47
+ tables_or_views(0, opts)
48
+ end
49
+
50
+ def views(opts={})
51
+ tables_or_views(1, opts)
52
+ end
53
+
54
+ private
55
+
56
+ # Use Firebird specific syntax for add column
57
+ def alter_table_sql(table, op)
58
+ case op[:op]
59
+ when :add_column
60
+ "ALTER TABLE #{quote_schema_table(table)} ADD #{column_definition_sql(op)}"
61
+ when :drop_column
62
+ "ALTER TABLE #{quote_schema_table(table)} DROP #{column_definition_sql(op)}"
63
+ when :rename_column
64
+ "ALTER TABLE #{quote_schema_table(table)} ALTER #{quote_identifier(op[:name])} TO #{quote_identifier(op[:new_name])}"
65
+ when :set_column_type
66
+ "ALTER TABLE #{quote_schema_table(table)} ALTER #{quote_identifier(op[:name])} TYPE #{type_literal(op)}"
67
+ else
68
+ super(table, op)
69
+ end
70
+ end
71
+
72
+ def auto_increment_sql()
73
+ AUTO_INCREMENT
74
+ end
75
+
76
+ def create_sequence_sql(name, opts={})
77
+ "CREATE SEQUENCE #{quote_identifier(name)}"
78
+ end
79
+
80
+ # Firebird gets an override because of the mess of creating a
81
+ # sequence and trigger for auto-incrementing primary keys.
82
+ def create_table_from_generator(name, generator, options)
83
+ drop_statement, create_statements = create_table_sql_list(name, generator, options)
84
+ (execute_ddl(drop_statement) rescue nil) if drop_statement
85
+ create_statements.each{|sql| execute_ddl(sql)}
86
+ end
87
+
88
+ def create_table_sql_list(name, generator, options={})
89
+ statements = [create_table_sql(name, generator, options)]
90
+ drop_seq_statement = nil
91
+ generator.columns.each do |c|
92
+ if c[:auto_increment]
93
+ c[:sequence_name] ||= "seq_#{name}_#{c[:name]}"
94
+ unless c[:create_sequence] == false
95
+ drop_seq_statement = drop_sequence_sql(c[:sequence_name])
96
+ statements << create_sequence_sql(c[:sequence_name])
97
+ statements << restart_sequence_sql(c[:sequence_name], {:restart_position => c[:sequence_start_position]}) if c[:sequence_start_position]
98
+ end
99
+ unless c[:create_trigger] == false
100
+ c[:trigger_name] ||= "BI_#{name}_#{c[:name]}"
101
+ c[:quoted_name] = quote_identifier(c[:name])
102
+ trigger_definition = <<-END
103
+ begin
104
+ if ((new.#{c[:quoted_name]} is null) or (new.#{c[:quoted_name]} = 0)) then
105
+ begin
106
+ new.#{c[:quoted_name]} = next value for #{c[:sequence_name]};
107
+ end
108
+ end
109
+ END
110
+ statements << create_trigger_sql(name, c[:trigger_name], trigger_definition, {:events => [:insert]})
111
+ end
112
+ end
113
+ end
114
+ [drop_seq_statement, statements]
115
+ end
116
+
117
+ def create_trigger_sql(table, name, definition, opts={})
118
+ events = opts[:events] ? Array(opts[:events]) : [:insert, :update, :delete]
119
+ whence = opts[:after] ? 'AFTER' : 'BEFORE'
120
+ inactive = opts[:inactive] ? 'INACTIVE' : 'ACTIVE'
121
+ position = opts.fetch(:position, 0)
122
+ sql = <<-end_sql
123
+ CREATE TRIGGER #{quote_identifier(name)} for #{quote_identifier(table)}
124
+ #{inactive} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} position #{position}
125
+ as #{definition}
126
+ end_sql
127
+ sql
128
+ end
129
+
130
+ def drop_sequence_sql(name)
131
+ "DROP SEQUENCE #{quote_identifier(name)}"
132
+ end
133
+
134
+ def restart_sequence_sql(name, opts={})
135
+ seq_name = quote_identifier(name)
136
+ "ALTER SEQUENCE #{seq_name} RESTART WITH #{opts[:restart_position]}"
137
+ end
138
+
139
+ def tables_or_views(type, opts)
140
+ ds = self[:"rdb$relations"].server(opts[:server]).filter(:"rdb$relation_type" => type, Sequel::SQL::Function.new(:COALESCE, :"rdb$system_flag", 0) => 0).select(:"rdb$relation_name")
141
+ ds.map{|r| ds.send(:output_identifier, r[:"rdb$relation_name"].rstrip)}
142
+ end
143
+
144
+ def type_literal_generic_string(column)
145
+ column[:text] ? :"BLOB SUB_TYPE TEXT" : super
146
+ end
147
+ end
148
+
149
+ module DatasetMethods
150
+ BOOL_TRUE = '1'.freeze
151
+ BOOL_FALSE = '0'.freeze
152
+ NULL = LiteralString.new('NULL').freeze
153
+ COMMA_SEPARATOR = ', '.freeze
154
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with distinct limit columns from join where group having compounds order')
155
+ INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'into columns values returning')
156
+
157
+ # Insert given values into the database.
158
+ def insert(*values)
159
+ if @opts[:sql] || @opts[:returning]
160
+ super
161
+ elsif supports_insert_select?
162
+ returning(insert_pk).insert(*values){|r| return r.values.first}
163
+ end
164
+ end
165
+
166
+ # Insert a record returning the record inserted
167
+ def insert_select(*values)
168
+ returning.insert(*values){|r| return r}
169
+ end
170
+
171
+ def requires_sql_standard_datetimes?
172
+ true
173
+ end
174
+
175
+ def supports_insert_select?
176
+ true
177
+ end
178
+
179
+ # Firebird does not support INTERSECT or EXCEPT
180
+ def supports_intersect_except?
181
+ false
182
+ end
183
+
184
+ private
185
+
186
+ def insert_clause_methods
187
+ INSERT_CLAUSE_METHODS
188
+ end
189
+
190
+ def insert_pk(*values)
191
+ pk = db.primary_key(opts[:from].first)
192
+ pk ? Sequel::SQL::Identifier.new(pk) : NULL
193
+ end
194
+
195
+ def literal_false
196
+ BOOL_FALSE
197
+ end
198
+
199
+ def literal_true
200
+ BOOL_TRUE
201
+ end
202
+
203
+ # The order of clauses in the SELECT SQL statement
204
+ def select_clause_methods
205
+ SELECT_CLAUSE_METHODS
206
+ end
207
+
208
+ def select_limit_sql(sql)
209
+ sql << " FIRST #{@opts[:limit]}" if @opts[:limit]
210
+ sql << " SKIP #{@opts[:offset]}" if @opts[:offset]
211
+ end
212
+ end
213
+ end
214
+ end
@@ -1,3 +1,5 @@
1
+ Sequel.require 'adapters/utils/emulate_offset_with_row_number'
2
+
1
3
  module Sequel
2
4
  Dataset::NON_SQL_OPTIONS << :disable_insert_output
3
5
  module MSSQL
@@ -231,6 +233,8 @@ module Sequel
231
233
  end
232
234
 
233
235
  module DatasetMethods
236
+ include EmulateOffsetWithRowNumber
237
+
234
238
  BOOL_TRUE = '1'.freeze
235
239
  BOOL_FALSE = '0'.freeze
236
240
  COMMA_SEPARATOR = ', '.freeze
@@ -242,6 +246,7 @@ module Sequel
242
246
  UPDLOCK = ' WITH (UPDLOCK)'.freeze
243
247
  WILDCARD = LiteralString.new('*').freeze
244
248
  CONSTANT_MAP = {:CURRENT_DATE=>'CAST(CURRENT_TIMESTAMP AS DATE)'.freeze, :CURRENT_TIME=>'CAST(CURRENT_TIMESTAMP AS TIME)'.freeze}
249
+ EXTRACT_MAP = {:year=>"yy", :month=>"m", :day=>"d", :hour=>"hh", :minute=>"n", :second=>"s"}
245
250
 
246
251
  # Allow overriding of the mssql_unicode_strings option at the dataset level.
247
252
  attr_accessor :mssql_unicode_strings
@@ -252,29 +257,6 @@ module Sequel
252
257
  @mssql_unicode_strings = db.mssql_unicode_strings
253
258
  end
254
259
 
255
- # Ugly hack. While MSSQL supports TRUE and FALSE values, you can't
256
- # actually specify them directly in SQL. Unfortunately, you also cannot
257
- # use an integer value when a boolean is required. Also unforunately, you
258
- # cannot use an expression that yields a boolean type in cases where in an
259
- # integer type is needed, such as inserting into a bit field (the closest thing
260
- # MSSQL has to a boolean).
261
- #
262
- # In filters, SQL::BooleanConstants are used more, while in other places
263
- # the ruby true/false values are used more, so use expressions that return booleans
264
- # for SQL::BooleanConstants, and 1/0 for other places.
265
- # The correct fix for this would require separate literalization paths for
266
- # filters compared to other values, but that's more work than I want to do right now.
267
- def boolean_constant_sql(constant)
268
- case constant
269
- when true
270
- '(1 = 1)'
271
- when false
272
- '(1 = 0)'
273
- else
274
- super
275
- end
276
- end
277
-
278
260
  # MSSQL uses + for string concatenation, and LIKE is case insensitive by default.
279
261
  def complex_expression_sql(op, args)
280
262
  case op
@@ -288,6 +270,13 @@ module Sequel
288
270
  "(#{literal(args[0])} * POWER(2, #{literal(args[1])}))"
289
271
  when :>>
290
272
  "(#{literal(args[0])} / POWER(2, #{literal(args[1])}))"
273
+ when :extract
274
+ part = args.at(0)
275
+ raise(Sequel::Error, "unsupported extract argument: #{part.inspect}") unless format = EXTRACT_MAP[part]
276
+ expr = literal(args.at(1))
277
+ s = "datepart(#{format}, #{expr})"
278
+ s = "CAST((#{s} + datepart(ns, #{expr})/1000000000.0) AS double precision)" if part == :second
279
+ s
291
280
  else
292
281
  super(op, args)
293
282
  end
@@ -330,14 +319,6 @@ module Sequel
330
319
  clone(:into => table)
331
320
  end
332
321
 
333
- # SQL Server does not support CTEs on subqueries, so move any CTEs
334
- # on joined datasets to the top level. The user is responsible for
335
- # resolving any name clashes this may cause.
336
- def join_table(type, table, expr=nil, table_alias={}, &block)
337
- return super unless Dataset === table && table.opts[:with]
338
- clone(:with => (opts[:with] || []) + table.opts[:with]).join_table(type, table.clone(:with => nil), expr, table_alias, &block)
339
- end
340
-
341
322
  # MSSQL uses a UNION ALL statement to insert multiple values at once.
342
323
  def multi_insert_sql(columns, values)
343
324
  [insert_sql(columns, LiteralString.new(values.map {|r| "SELECT #{expression_list(r)}" }.join(" UNION ALL ")))]
@@ -383,29 +364,6 @@ module Sequel
383
364
  "[#{name}]"
384
365
  end
385
366
 
386
- # MSSQL Requires the use of the ROW_NUMBER window function to emulate
387
- # an offset. This implementation requires MSSQL 2005 or greater (offset
388
- # can't be emulated well in MSSQL 2000).
389
- #
390
- # The implementation is ugly, cloning the current dataset and modifying
391
- # the clone to add a ROW_NUMBER window function (and some other things),
392
- # then using the modified clone in a subselect which is selected from.
393
- #
394
- # If offset is used, an order must be provided, because the use of ROW_NUMBER
395
- # requires an order.
396
- def select_sql
397
- return super unless o = @opts[:offset]
398
- raise(Error, 'MSSQL requires an order be provided if using an offset') unless order = @opts[:order]
399
- dsa1 = dataset_alias(1)
400
- rn = row_number_column
401
- subselect_sql(unlimited.
402
- unordered.
403
- select_append{ROW_NUMBER(:over, :order=>order){}.as(rn)}.
404
- from_self(:alias=>dsa1).
405
- limit(@opts[:limit]).
406
- where(SQL::Identifier.new(rn) > o))
407
- end
408
-
409
367
  # The version of the database server.
410
368
  def server_version
411
369
  db.server_version(@opts[:server])
@@ -450,6 +408,11 @@ module Sequel
450
408
  def supports_window_functions?
451
409
  true
452
410
  end
411
+
412
+ # MSSQL cannot use WHERE 1.
413
+ def supports_where_true?
414
+ false
415
+ end
453
416
 
454
417
  protected
455
418
  # MSSQL does not allow ordering in sub-clauses unless 'top' (limit) is specified
@@ -458,6 +421,7 @@ module Sequel
458
421
  end
459
422
 
460
423
  private
424
+
461
425
  def is_2005_or_later?
462
426
  server_version >= 9000000
463
427
  end
@@ -490,22 +454,6 @@ module Sequel
490
454
  alias insert_with_sql delete_with_sql
491
455
  alias update_with_sql delete_with_sql
492
456
 
493
- # Special case when true or false is provided directly to filter.
494
- def filter_expr(expr)
495
- if block_given?
496
- super
497
- else
498
- case expr
499
- when true
500
- Sequel::TRUE
501
- when false
502
- Sequel::FALSE
503
- else
504
- super
505
- end
506
- end
507
- end
508
-
509
457
  # MSSQL raises an error if you try to provide more than 3 decimal places
510
458
  # for a fractional timestamp. This probably doesn't work for smalldatetime
511
459
  # fields.
@@ -552,11 +500,6 @@ module Sequel
552
500
  BOOL_TRUE
553
501
  end
554
502
 
555
- # The alias to use for the row_number column when emulating OFFSET
556
- def row_number_column
557
- :x_sequel_row_number_x
558
- end
559
-
560
503
  # MSSQL adds the limit before the columns
561
504
  def select_clause_methods
562
505
  SELECT_CLAUSE_METHODS
@@ -342,6 +342,13 @@ module Sequel
342
342
  # string concatenation.
343
343
  def complex_expression_sql(op, args)
344
344
  case op
345
+ when :IN, :"NOT IN"
346
+ ds = args.at(1)
347
+ if ds.is_a?(Sequel::Dataset) && ds.opts[:limit]
348
+ super(op, [args.at(0), ds.from_self])
349
+ else
350
+ super
351
+ end
345
352
  when :~, :'!~', :'~*', :'!~*', :LIKE, :'NOT LIKE', :ILIKE, :'NOT ILIKE'
346
353
  "(#{literal(args.at(0))} #{'NOT ' if [:'NOT LIKE', :'NOT ILIKE', :'!~', :'!~*'].include?(op)}#{[:~, :'!~', :'~*', :'!~*'].include?(op) ? 'REGEXP' : 'LIKE'} #{'BINARY ' if [:~, :'!~', :LIKE, :'NOT LIKE'].include?(op)}#{literal(args.at(1))})"
347
354
  when :'||'
@@ -481,6 +488,12 @@ module Sequel
481
488
  true
482
489
  end
483
490
 
491
+ # MySQL's DISTINCT ON emulation using GROUP BY does not respect the
492
+ # queries ORDER BY clause.
493
+ def supports_ordered_distinct_on?
494
+ false
495
+ end
496
+
484
497
  # MySQL does support fractional timestamps in literal timestamps, but it
485
498
  # ignores them. Also, using them seems to cause problems on 1.9. Since
486
499
  # they are ignored anyway, not using them is probably best.
@@ -358,6 +358,11 @@ module Sequel
358
358
  @supports_prepared_transactions = self['SHOW max_prepared_transactions'].get.to_i > 0
359
359
  end
360
360
 
361
+ # PostgreSQL supports CREATE TABLE IF NOT EXISTS on 9.1+
362
+ def supports_create_table_if_not_exists?
363
+ server_version >= 90100
364
+ end
365
+
361
366
  # PostgreSQL supports savepoints
362
367
  def supports_savepoints?
363
368
  true
@@ -625,11 +630,14 @@ module Sequel
625
630
  BOOL_TRUE = 'true'.freeze
626
631
  COMMA_SEPARATOR = ', '.freeze
627
632
  DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'from using where')
633
+ DELETE_CLAUSE_METHODS_91 = Dataset.clause_methods(:delete, %w'with from using where returning')
628
634
  EXCLUSIVE = 'EXCLUSIVE'.freeze
629
635
  EXPLAIN = 'EXPLAIN '.freeze
630
636
  EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
631
637
  FOR_SHARE = ' FOR SHARE'.freeze
632
- INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'into columns values returning_select')
638
+ INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'into columns values')
639
+ INSERT_CLAUSE_METHODS_82 = Dataset.clause_methods(:insert, %w'into columns values returning')
640
+ INSERT_CLAUSE_METHODS_91 = Dataset.clause_methods(:insert, %w'with into columns values returning')
633
641
  LOCK = 'LOCK TABLE %s IN %s MODE'.freeze
634
642
  NULL = LiteralString.new('NULL').freeze
635
643
  PG_TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S".freeze
@@ -643,17 +651,23 @@ module Sequel
643
651
  SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
644
652
  SQL_WITH_RECURSIVE = "WITH RECURSIVE ".freeze
645
653
  UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'table set from where')
654
+ UPDATE_CLAUSE_METHODS_91 = Dataset.clause_methods(:update, %w'with table set from where returning')
646
655
 
647
656
  # Shared methods for prepared statements when used with PostgreSQL databases.
648
657
  module PreparedStatementMethods
649
658
  # Override insert action to use RETURNING if the server supports it.
659
+ def run(&block)
660
+ if @prepared_type == :insert && supports_insert_select?
661
+ fetch_rows(prepared_sql){|r| return r.values.first}
662
+ else
663
+ super
664
+ end
665
+ end
666
+
650
667
  def prepared_sql
651
668
  return @prepared_sql if @prepared_sql
669
+ @opts[:returning] = insert_pk if @prepared_type == :insert && supports_insert_select?
652
670
  super
653
- if @prepared_type == :insert and !@opts[:disable_insert_returning] and server_version >= 80200
654
- @prepared_sql = insert_returning_pk_sql(*@prepared_modify_values)
655
- meta_def(:insert_returning_pk_sql){|*args| prepared_sql}
656
- end
657
671
  @prepared_sql
658
672
  end
659
673
  end
@@ -707,25 +721,20 @@ module Sequel
707
721
  end
708
722
 
709
723
  # Insert given values into the database.
710
- def insert(*values)
711
- if @opts[:sql]
712
- execute_insert(insert_sql(*values))
713
- elsif @opts[:disable_insert_returning] || server_version < 80200
714
- execute_insert(insert_sql(*values), :table=>opts[:from].first, :values=>values.size == 1 ? values.first : values)
724
+ def insert(*values, &block)
725
+ if @opts[:sql] || @opts[:returning]
726
+ super
727
+ elsif supports_insert_select?
728
+ returning(insert_pk).insert(*values){|r| return r.values.first}
715
729
  else
716
- clone(default_server_opts(:sql=>insert_returning_pk_sql(*values))).single_value
730
+ execute_insert(insert_sql(*values), :table=>opts[:from].first, :values=>values.size == 1 ? values.first : values)
717
731
  end
718
732
  end
719
733
 
720
- # Use the RETURNING clause to return the columns listed in returning.
721
- def insert_returning_sql(returning, *values)
722
- "#{insert_sql(*values)} RETURNING #{column_list(Array(returning))}"
723
- end
724
-
725
734
  # Insert a record returning the record inserted
726
735
  def insert_select(*values)
727
736
  return unless supports_insert_select?
728
- naked.clone(default_server_opts(:sql=>insert_returning_sql(nil, *values))).single_record
737
+ returning.insert(*values){|r| return r}
729
738
  end
730
739
 
731
740
  # Locks all tables in the dataset's FROM clause (but not in JOINs) with
@@ -750,21 +759,30 @@ module Sequel
750
759
  [insert_sql(columns, LiteralString.new('VALUES ' + values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)))]
751
760
  end
752
761
 
762
+ # PostgreSQL supports using the WITH clause in subqueries if it
763
+ # supports using WITH at all (i.e. on PostgreSQL 8.4+).
764
+ def supports_cte_in_subqueries?
765
+ supports_cte?
766
+ end
767
+
753
768
  # DISTINCT ON is a PostgreSQL extension
754
769
  def supports_distinct_on?
755
770
  true
756
771
  end
757
772
 
758
- # PostgreSQL support insert_select using the RETURNING clause.
759
- def supports_insert_select?
760
- server_version >= 80200 && !opts[:disable_insert_returning]
761
- end
762
-
763
773
  # PostgreSQL supports modifying joined datasets
764
774
  def supports_modifying_joins?
765
775
  true
766
776
  end
767
777
 
778
+ def supports_returning?(type)
779
+ if type == :insert
780
+ server_version >= 80200 && !opts[:disable_insert_returning]
781
+ else
782
+ server_version >= 90100
783
+ end
784
+ end
785
+
768
786
  # PostgreSQL supports timezones in literal timestamps
769
787
  def supports_timestamp_timezones?
770
788
  true
@@ -784,7 +802,7 @@ module Sequel
784
802
 
785
803
  # PostgreSQL allows deleting from joined datasets
786
804
  def delete_clause_methods
787
- DELETE_CLAUSE_METHODS
805
+ server_version >= 90100 ? DELETE_CLAUSE_METHODS_91 : DELETE_CLAUSE_METHODS
788
806
  end
789
807
 
790
808
  # Only include the primary table in the main delete clause
@@ -799,23 +817,21 @@ module Sequel
799
817
 
800
818
  # PostgreSQL allows a RETURNING clause.
801
819
  def insert_clause_methods
802
- INSERT_CLAUSE_METHODS
820
+ if server_version >= 90100
821
+ INSERT_CLAUSE_METHODS_91
822
+ elsif server_version >= 80200
823
+ INSERT_CLAUSE_METHODS_82
824
+ else
825
+ INSERT_CLAUSE_METHODS
826
+ end
803
827
  end
804
828
 
805
- # Use the RETURNING clause to return the primary key of the inserted record, if it exists
806
- def insert_returning_pk_sql(*values)
829
+ # Return the primary key to use for RETURNING in an INSERT statement
830
+ def insert_pk
807
831
  pk = db.primary_key(opts[:from].first) if opts[:from] && !opts[:from].empty?
808
- insert_returning_sql(pk ? Sequel::SQL::Identifier.new(pk) : NULL, *values)
832
+ pk ? Sequel::SQL::Identifier.new(pk) : NULL
809
833
  end
810
834
 
811
- # Add a RETURNING clause if it is set and the database supports it and
812
- # this dataset hasn't disabled it.
813
- def insert_returning_select_sql(sql)
814
- if supports_insert_select? && opts.has_key?(:returning)
815
- sql << " RETURNING #{column_list(Array(opts[:returning]))}"
816
- end
817
- end
818
-
819
835
  # For multiple table support, PostgreSQL requires at least
820
836
  # two from tables, with joins allowed.
821
837
  def join_from_sql(type, sql)
@@ -882,7 +898,7 @@ module Sequel
882
898
 
883
899
  # PostgreSQL splits the main table from the joined tables
884
900
  def update_clause_methods
885
- UPDATE_CLAUSE_METHODS
901
+ server_version >= 90100 ? UPDATE_CLAUSE_METHODS_91 : UPDATE_CLAUSE_METHODS
886
902
  end
887
903
 
888
904
  # Use FROM to specify additional tables in an update query