sequel 4.9.0 → 4.10.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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +79 -1
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/Rakefile +2 -12
  6. data/bin/sequel +1 -0
  7. data/doc/advanced_associations.rdoc +82 -25
  8. data/doc/association_basics.rdoc +21 -22
  9. data/doc/core_extensions.rdoc +1 -1
  10. data/doc/opening_databases.rdoc +7 -0
  11. data/doc/release_notes/4.10.0.txt +226 -0
  12. data/doc/security.rdoc +1 -0
  13. data/doc/testing.rdoc +7 -7
  14. data/doc/transactions.rdoc +8 -0
  15. data/lib/sequel/adapters/jdbc.rb +160 -168
  16. data/lib/sequel/adapters/jdbc/db2.rb +17 -18
  17. data/lib/sequel/adapters/jdbc/derby.rb +5 -28
  18. data/lib/sequel/adapters/jdbc/h2.rb +11 -22
  19. data/lib/sequel/adapters/jdbc/hsqldb.rb +31 -18
  20. data/lib/sequel/adapters/jdbc/jtds.rb +0 -15
  21. data/lib/sequel/adapters/jdbc/oracle.rb +36 -35
  22. data/lib/sequel/adapters/jdbc/postgresql.rb +72 -90
  23. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +18 -16
  24. data/lib/sequel/adapters/jdbc/sqlite.rb +7 -0
  25. data/lib/sequel/adapters/jdbc/sqlserver.rb +10 -30
  26. data/lib/sequel/adapters/jdbc/transactions.rb +5 -6
  27. data/lib/sequel/adapters/openbase.rb +1 -7
  28. data/lib/sequel/adapters/postgres.rb +1 -1
  29. data/lib/sequel/adapters/shared/access.rb +3 -6
  30. data/lib/sequel/adapters/shared/cubrid.rb +24 -9
  31. data/lib/sequel/adapters/shared/db2.rb +13 -5
  32. data/lib/sequel/adapters/shared/firebird.rb +16 -16
  33. data/lib/sequel/adapters/shared/informix.rb +2 -5
  34. data/lib/sequel/adapters/shared/mssql.rb +72 -63
  35. data/lib/sequel/adapters/shared/mysql.rb +72 -40
  36. data/lib/sequel/adapters/shared/oracle.rb +27 -15
  37. data/lib/sequel/adapters/shared/postgres.rb +24 -44
  38. data/lib/sequel/adapters/shared/progress.rb +1 -5
  39. data/lib/sequel/adapters/shared/sqlanywhere.rb +26 -18
  40. data/lib/sequel/adapters/shared/sqlite.rb +21 -6
  41. data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +8 -1
  42. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -2
  43. data/lib/sequel/adapters/utils/split_alter_table.rb +8 -0
  44. data/lib/sequel/core.rb +14 -9
  45. data/lib/sequel/database/dataset_defaults.rb +1 -0
  46. data/lib/sequel/database/misc.rb +12 -0
  47. data/lib/sequel/database/query.rb +4 -1
  48. data/lib/sequel/database/schema_methods.rb +3 -2
  49. data/lib/sequel/database/transactions.rb +47 -17
  50. data/lib/sequel/dataset/features.rb +12 -2
  51. data/lib/sequel/dataset/mutation.rb +2 -0
  52. data/lib/sequel/dataset/placeholder_literalizer.rb +12 -4
  53. data/lib/sequel/dataset/prepared_statements.rb +6 -0
  54. data/lib/sequel/dataset/query.rb +1 -1
  55. data/lib/sequel/dataset/sql.rb +132 -70
  56. data/lib/sequel/extensions/columns_introspection.rb +1 -1
  57. data/lib/sequel/extensions/null_dataset.rb +8 -4
  58. data/lib/sequel/extensions/pg_array.rb +4 -4
  59. data/lib/sequel/extensions/pg_row.rb +1 -0
  60. data/lib/sequel/model/associations.rb +468 -188
  61. data/lib/sequel/model/base.rb +88 -13
  62. data/lib/sequel/plugins/association_pks.rb +23 -64
  63. data/lib/sequel/plugins/auto_validations.rb +3 -2
  64. data/lib/sequel/plugins/dataset_associations.rb +1 -3
  65. data/lib/sequel/plugins/many_through_many.rb +18 -65
  66. data/lib/sequel/plugins/pg_array_associations.rb +97 -86
  67. data/lib/sequel/plugins/prepared_statements.rb +2 -1
  68. data/lib/sequel/plugins/prepared_statements_associations.rb +36 -27
  69. data/lib/sequel/plugins/rcte_tree.rb +12 -16
  70. data/lib/sequel/plugins/sharding.rb +21 -3
  71. data/lib/sequel/plugins/single_table_inheritance.rb +2 -1
  72. data/lib/sequel/plugins/subclasses.rb +1 -9
  73. data/lib/sequel/plugins/tactical_eager_loading.rb +9 -0
  74. data/lib/sequel/plugins/tree.rb +2 -2
  75. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  76. data/lib/sequel/version.rb +1 -1
  77. data/spec/adapters/mssql_spec.rb +57 -15
  78. data/spec/adapters/mysql_spec.rb +11 -0
  79. data/spec/bin_spec.rb +2 -2
  80. data/spec/core/database_spec.rb +38 -4
  81. data/spec/core/dataset_spec.rb +45 -7
  82. data/spec/core/placeholder_literalizer_spec.rb +17 -0
  83. data/spec/core/schema_spec.rb +6 -1
  84. data/spec/extensions/active_model_spec.rb +18 -9
  85. data/spec/extensions/association_pks_spec.rb +20 -18
  86. data/spec/extensions/association_proxies_spec.rb +9 -9
  87. data/spec/extensions/auto_validations_spec.rb +6 -0
  88. data/spec/extensions/columns_introspection_spec.rb +1 -0
  89. data/spec/extensions/constraint_validations_spec.rb +3 -1
  90. data/spec/extensions/many_through_many_spec.rb +191 -111
  91. data/spec/extensions/pg_array_associations_spec.rb +133 -103
  92. data/spec/extensions/prepared_statements_associations_spec.rb +23 -4
  93. data/spec/extensions/rcte_tree_spec.rb +35 -27
  94. data/spec/extensions/sequel_3_dataset_methods_spec.rb +0 -1
  95. data/spec/extensions/sharding_spec.rb +2 -2
  96. data/spec/extensions/tactical_eager_loading_spec.rb +4 -0
  97. data/spec/extensions/to_dot_spec.rb +1 -0
  98. data/spec/extensions/touch_spec.rb +2 -2
  99. data/spec/integration/associations_test.rb +130 -37
  100. data/spec/integration/dataset_test.rb +17 -0
  101. data/spec/integration/model_test.rb +17 -0
  102. data/spec/integration/schema_test.rb +14 -0
  103. data/spec/integration/transaction_test.rb +25 -1
  104. data/spec/model/association_reflection_spec.rb +63 -24
  105. data/spec/model/associations_spec.rb +104 -57
  106. data/spec/model/base_spec.rb +14 -1
  107. data/spec/model/class_dataset_methods_spec.rb +1 -0
  108. data/spec/model/eager_loading_spec.rb +221 -74
  109. data/spec/model/model_spec.rb +119 -1
  110. metadata +4 -2
@@ -145,6 +145,14 @@ module Sequel
145
145
  super && (server_version <= 50512 || server_version >= 50523)
146
146
  end
147
147
 
148
+ # Support fractional timestamps on MySQL 5.6.5+ if the :fractional_seconds
149
+ # Database option is used. Technically, MySQL 5.6.4+ supports them, but
150
+ # automatic initialization of datetime values wasn't supported to 5.6.5+,
151
+ # and this is related to that.
152
+ def supports_timestamp_usecs?
153
+ @supports_timestamp_usecs ||= server_version >= 50605 && typecast_value_boolean(opts[:fractional_seconds])
154
+ end
155
+
148
156
  # MySQL supports transaction isolation levels
149
157
  def supports_transaction_isolation_levels?
150
158
  true
@@ -185,7 +193,11 @@ module Sequel
185
193
  sql = super
186
194
  op[:table] = related
187
195
  op[:key] ||= primary_key_from_schema(related)
188
- sql << ", ADD FOREIGN KEY (#{quote_identifier(op[:name])})#{column_references_sql(op)}"
196
+ sql << ", ADD "
197
+ if constraint_name = op.delete(:foreign_key_constraint_name)
198
+ sql << "CONSTRAINT #{quote_identifier(constraint_name)} "
199
+ end
200
+ sql << "FOREIGN KEY (#{quote_identifier(op[:name])})#{column_references_sql(op)}"
189
201
  else
190
202
  super
191
203
  end
@@ -292,9 +304,8 @@ module Sequel
292
304
  # Use XA START to start a new prepared transaction if the :prepare
293
305
  # option is given.
294
306
  def begin_transaction(conn, opts=OPTS)
295
- if (s = opts[:prepare]) && (th = _trans(conn))[:savepoint_level] == 0
307
+ if (s = opts[:prepare]) && savepoint_level(conn) == 1
296
308
  log_connection_execute(conn, "XA START #{literal(s)}")
297
- th[:savepoint_level] += 1
298
309
  else
299
310
  super
300
311
  end
@@ -315,7 +326,7 @@ module Sequel
315
326
  # Prepare the XA transaction for a two-phase commit if the
316
327
  # :prepare option is given.
317
328
  def commit_transaction(conn, opts=OPTS)
318
- if (s = opts[:prepare]) && _trans(conn)[:savepoint_level] <= 1
329
+ if (s = opts[:prepare]) && savepoint_level(conn) <= 1
319
330
  log_connection_execute(conn, "XA END #{literal(s)}")
320
331
  log_connection_execute(conn, "XA PREPARE #{literal(s)}")
321
332
  else
@@ -421,7 +432,7 @@ module Sequel
421
432
 
422
433
  # Rollback the currently open XA transaction
423
434
  def rollback_transaction(conn, opts=OPTS)
424
- if (s = opts[:prepare]) && _trans(conn)[:savepoint_level] <= 1
435
+ if (s = opts[:prepare]) && savepoint_level(conn) <= 1
425
436
  log_connection_execute(conn, "XA END #{literal(s)}")
426
437
  log_connection_execute(conn, "XA PREPARE #{literal(s)}")
427
438
  log_connection_execute(conn, "XA ROLLBACK #{literal(s)}")
@@ -462,6 +473,12 @@ module Sequel
462
473
  end
463
474
  end
464
475
 
476
+ # Split DROP INDEX ops on MySQL 5.6+, as dropping them in the same
477
+ # statement as dropping a related foreign key causes an error.
478
+ def split_alter_table_op?(op)
479
+ server_version >= 50600 && (op[:op] == :drop_index || (op[:op] == :drop_constraint && op[:type] == :unique))
480
+ end
481
+
465
482
  # MySQL can combine multiple alter table ops into a single query.
466
483
  def supports_combining_alter_table_ops?
467
484
  true
@@ -496,7 +513,9 @@ module Sequel
496
513
  # MySQL has both datetime and timestamp classes, most people are going
497
514
  # to want datetime
498
515
  def type_literal_generic_datetime(column)
499
- if column[:default] == Sequel::CURRENT_TIMESTAMP
516
+ if supports_timestamp_usecs?
517
+ :'datetime(6)'
518
+ elsif column[:default] == Sequel::CURRENT_TIMESTAMP
500
519
  :timestamp
501
520
  else
502
521
  :datetime
@@ -504,9 +523,17 @@ module Sequel
504
523
  end
505
524
 
506
525
  # MySQL has both datetime and timestamp classes, most people are going
507
- # to want datetime
526
+ # to want datetime.
508
527
  def type_literal_generic_time(column)
509
- column[:only_time] ? :time : type_literal_generic_datetime(column)
528
+ if column[:only_time]
529
+ if supports_timestamp_usecs?
530
+ :'time(6)'
531
+ else
532
+ :time
533
+ end
534
+ else
535
+ type_literal_generic_datetime(column)
536
+ end
510
537
  end
511
538
 
512
539
  # MySQL doesn't have a true boolean class, so it uses tinyint(1)
@@ -522,10 +549,6 @@ module Sequel
522
549
  COMMA_SEPARATOR = ', '.freeze
523
550
  FOR_SHARE = ' LOCK IN SHARE MODE'.freeze
524
551
  SQL_CALC_FOUND_ROWS = ' SQL_CALC_FOUND_ROWS'.freeze
525
- DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'delete from where order limit')
526
- INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert ignore into columns values on_duplicate_key_update')
527
- SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct calc_found_rows columns from join where group having compounds order limit lock')
528
- UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'update ignore table set where order limit')
529
552
  APOS = Dataset::APOS
530
553
  APOS_RE = Dataset::APOS_RE
531
554
  DOUBLE_APOS = Dataset::DOUBLE_APOS
@@ -566,6 +589,15 @@ module Sequel
566
589
  BLOB_START = "0x".freeze
567
590
  EMPTY_BLOB = "''".freeze
568
591
  HSTAR = "H*".freeze
592
+ CURRENT_TIMESTAMP_56 = 'CURRENT_TIMESTAMP(6)'.freeze
593
+
594
+ # Comes directly from MySQL's documentation, used for queries with limits without offsets
595
+ ONLY_OFFSET = ",18446744073709551615".freeze
596
+
597
+ Dataset.def_sql_method(self, :delete, %w'delete from where order limit')
598
+ Dataset.def_sql_method(self, :insert, %w'insert ignore into columns values on_duplicate_key_update')
599
+ Dataset.def_sql_method(self, :select, %w'select distinct calc_found_rows columns from join where group having compounds order limit lock')
600
+ Dataset.def_sql_method(self, :update, %w'update ignore table set where order limit')
569
601
 
570
602
  include Sequel::Dataset::Replace
571
603
 
@@ -610,6 +642,18 @@ module Sequel
610
642
  end
611
643
  end
612
644
 
645
+ # MySQL's CURRENT_TIMESTAMP does not use fractional seconds,
646
+ # even if the database itself supports fractional seconds. If
647
+ # MySQL 5.6.4+ is being used, use a value that will return
648
+ # fractional seconds.
649
+ def constant_sql_append(sql, constant)
650
+ if constant == :CURRENT_TIMESTAMP && supports_timestamp_usecs?
651
+ sql << CURRENT_TIMESTAMP_56
652
+ else
653
+ super
654
+ end
655
+ end
656
+
613
657
  # Use GROUP BY instead of DISTINCT ON if arguments are provided.
614
658
  def distinct(*args)
615
659
  args.empty? ? super : group(*args)
@@ -706,13 +750,6 @@ module Sequel
706
750
  clone(:on_duplicate_key_update => args)
707
751
  end
708
752
 
709
- # MySQL specific syntax for inserting multiple values at once.
710
- def multi_insert_sql(columns, values)
711
- sql = LiteralString.new('VALUES ')
712
- expression_list_append(sql, values.map{|r| Array(r)})
713
- [insert_sql(columns, sql)]
714
- end
715
-
716
753
  # MySQL uses the nonstandard ` (backtick) for quoting identifiers.
717
754
  def quoted_identifier_append(sql, c)
718
755
  sql << BACKTICK << c.to_s.gsub(BACKTICK_RE, DOUBLE_BACKTICK) << BACKTICK
@@ -734,6 +771,11 @@ module Sequel
734
771
  false
735
772
  end
736
773
 
774
+ # MySQL does not support limits in correlated subqueries (or any subqueries that use IN).
775
+ def supports_limits_in_correlated_subqueries?
776
+ false
777
+ end
778
+
737
779
  # MySQL supports modifying joined datasets
738
780
  def supports_modifying_joins?
739
781
  true
@@ -754,7 +796,7 @@ module Sequel
754
796
  # ignores them. Also, using them seems to cause problems on 1.9. Since
755
797
  # they are ignored anyway, not using them is probably best.
756
798
  def supports_timestamp_usecs?
757
- false
799
+ db.supports_timestamp_usecs?
758
800
  end
759
801
 
760
802
  # Sets up the update methods to use UPDATE IGNORE.
@@ -769,11 +811,6 @@ module Sequel
769
811
 
770
812
  private
771
813
 
772
- # MySQL supports the ORDER BY and LIMIT clauses for DELETE statements
773
- def delete_clause_methods
774
- DELETE_CLAUSE_METHODS
775
- end
776
-
777
814
  # Consider the first table in the joined dataset is the table to delete
778
815
  # from, but include the others for the purposes of selecting rows.
779
816
  def delete_from_sql(sql)
@@ -788,12 +825,6 @@ module Sequel
788
825
  end
789
826
  end
790
827
 
791
- # MySQL supports the IGNORE and ON DUPLICATE KEY UPDATE clauses for INSERT statements
792
- def insert_clause_methods
793
- INSERT_CLAUSE_METHODS
794
- end
795
- alias replace_clause_methods insert_clause_methods
796
-
797
828
  # MySQL doesn't use the SQL standard DEFAULT VALUES.
798
829
  def insert_columns_sql(sql)
799
830
  values = opts[:values]
@@ -905,11 +936,17 @@ module Sequel
905
936
  BOOL_TRUE
906
937
  end
907
938
 
908
- # MySQL does not support the SQL WITH clause for SELECT statements
909
- def select_clause_methods
910
- SELECT_CLAUSE_METHODS
939
+ # MySQL supports multiple rows in INSERT.
940
+ def multi_insert_sql_strategy
941
+ :values
911
942
  end
912
-
943
+
944
+ def select_only_offset_sql(sql)
945
+ sql << LIMIT
946
+ literal_append(sql, @opts[:offset])
947
+ sql << ONLY_OFFSET
948
+ end
949
+
913
950
  # Support FOR SHARE locking when using the :share lock style.
914
951
  def select_lock_sql(sql)
915
952
  @opts[:lock] == :share ? (sql << FOR_SHARE) : super
@@ -920,11 +957,6 @@ module Sequel
920
957
  sql << SQL_CALC_FOUND_ROWS if opts[:calc_found_rows]
921
958
  end
922
959
 
923
- # MySQL supports the ORDER BY and LIMIT clauses for UPDATE statements
924
- def update_clause_methods
925
- UPDATE_CLAUSE_METHODS
926
- end
927
-
928
960
  # MySQL uses WITH ROLLUP syntax.
929
961
  def uses_with_rollup?
930
962
  true
@@ -247,7 +247,6 @@ module Sequel
247
247
  end
248
248
 
249
249
  module DatasetMethods
250
- SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with select distinct columns from join where group having compounds order lock')
251
250
  ROW_NUMBER_EXPRESSION = LiteralString.new('ROWNUM').freeze
252
251
  SPACE = Dataset::SPACE
253
252
  APOS = Dataset::APOS
@@ -259,9 +258,13 @@ module Sequel
259
258
  BOOL_FALSE = "'N'".freeze
260
259
  BOOL_TRUE = "'Y'".freeze
261
260
  HSTAR = "H*".freeze
262
- DUAL = ['DUAL'.freeze].freeze
261
+ DUAL = ' FROM DUAL'.freeze
263
262
  BITAND_PROC = lambda{|a, b| Sequel.lit(["CAST(BITAND(", ", ", ") AS INTEGER)"], a, b)}
264
263
 
264
+ include(Module.new do
265
+ Dataset.def_sql_method(self, :select, %w'with select distinct columns from join where group having compounds order lock')
266
+ end)
267
+
265
268
  def complex_expression_sql_append(sql, op, args)
266
269
  case op
267
270
  when :&
@@ -334,7 +337,7 @@ module Sequel
334
337
  def select_sql
335
338
  return super if @opts[:sql]
336
339
  if o = @opts[:offset]
337
- columns = clone(:append_sql=>'').columns
340
+ columns = clone(:append_sql=>'', :placeholder_literal_null=>true).columns
338
341
  dsa1 = dataset_alias(1)
339
342
  rn = row_number_column
340
343
  limit = @opts[:limit]
@@ -366,6 +369,10 @@ module Sequel
366
369
  true
367
370
  end
368
371
 
372
+ def supports_cte?(type=:select)
373
+ type == :select
374
+ end
375
+
369
376
  # Oracle supports GROUP BY CUBE
370
377
  def supports_group_cube?
371
378
  true
@@ -386,6 +393,16 @@ module Sequel
386
393
  false
387
394
  end
388
395
 
396
+ # Oracle does not support limits in correlated subqueries.
397
+ def supports_limits_in_correlated_subqueries?
398
+ false
399
+ end
400
+
401
+ # Oracle does not support offsets in correlated subqueries.
402
+ def supports_offsets_in_correlated_subqueries?
403
+ false
404
+ end
405
+
389
406
  # Oracle does not support SELECT *, column
390
407
  def supports_select_all_and_column?
391
408
  false
@@ -420,6 +437,10 @@ module Sequel
420
437
  TIMESTAMP_FORMAT
421
438
  end
422
439
 
440
+ def empty_from_sql
441
+ DUAL
442
+ end
443
+
423
444
  # If this dataset is associated with a sequence, return the most recently
424
445
  # inserted sequence value.
425
446
  def execute_insert(sql, opts=OPTS)
@@ -457,18 +478,9 @@ module Sequel
457
478
  BOOL_TRUE
458
479
  end
459
480
 
460
- # Use the Oracle-specific SQL clauses (no limit, since it is emulated).
461
- def select_clause_methods
462
- SELECT_CLAUSE_METHODS
463
- end
464
-
465
- # Modify the SQL to add the list of tables to select FROM
466
- # Oracle doesn't support select without FROM clause
467
- # so add the dummy DUAL table if the dataset doesn't select
468
- # from a table.
469
- def select_from_sql(sql)
470
- sql << FROM
471
- source_list_append(sql, @opts[:from] || DUAL)
481
+ # Oracle can insert multiple rows using a UNION
482
+ def multi_insert_sql_strategy
483
+ :union
472
484
  end
473
485
 
474
486
  # Oracle supports quoted function names.
@@ -410,6 +410,8 @@ module Sequel
410
410
  # any named types.
411
411
  def reset_conversion_procs
412
412
  @conversion_procs = get_conversion_procs
413
+ conversion_procs_updated
414
+ @conversion_procs
413
415
  end
414
416
 
415
417
  # Reset the primary key sequence for the given table, basing it on the
@@ -540,6 +542,7 @@ module Sequel
540
542
  convert_named_procs_to_procs(named_procs).each do |oid, pr|
541
543
  procs[oid] ||= pr
542
544
  end
545
+ conversion_procs_updated
543
546
  end
544
547
  end
545
548
 
@@ -596,7 +599,7 @@ module Sequel
596
599
  # If the :prepare option is given and we aren't in a savepoint,
597
600
  # prepare the transaction for a two-phase commit.
598
601
  def commit_transaction(conn, opts=OPTS)
599
- if (s = opts[:prepare]) && _trans(conn)[:savepoint_level] <= 1
602
+ if (s = opts[:prepare]) && savepoint_level(conn) <= 1
600
603
  log_connection_execute(conn, "PREPARE TRANSACTION #{literal(s)}")
601
604
  else
602
605
  super
@@ -658,6 +661,11 @@ module Sequel
658
661
  end
659
662
  end
660
663
 
664
+ # Callback used when conversion procs are updated.
665
+ def conversion_procs_updated
666
+ nil
667
+ end
668
+
661
669
  # Convert the hash of named conversion procs into a hash a oid conversion procs.
662
670
  def convert_named_procs_to_procs(named_procs)
663
671
  h = {}
@@ -674,6 +682,7 @@ module Sequel
674
682
  oids.each do |oid|
675
683
  procs[oid] = PG_TYPES[oid]
676
684
  end
685
+ conversion_procs_updated
677
686
  end
678
687
 
679
688
  EXCLUSION_CONSTRAINT_SQL_STATE = '23P01'.freeze
@@ -1094,27 +1103,19 @@ module Sequel
1094
1103
  BOOL_FALSE = 'false'.freeze
1095
1104
  BOOL_TRUE = 'true'.freeze
1096
1105
  COMMA_SEPARATOR = ', '.freeze
1097
- DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'delete from using where returning')
1098
- DELETE_CLAUSE_METHODS_91 = Dataset.clause_methods(:delete, %w'with delete from using where returning')
1099
1106
  EXCLUSIVE = 'EXCLUSIVE'.freeze
1100
1107
  EXPLAIN = 'EXPLAIN '.freeze
1101
1108
  EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
1102
1109
  FOR_SHARE = ' FOR SHARE'.freeze
1103
- INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert into columns values returning')
1104
- INSERT_CLAUSE_METHODS_91 = Dataset.clause_methods(:insert, %w'with insert into columns values returning')
1105
1110
  NULL = LiteralString.new('NULL').freeze
1106
1111
  PG_TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S".freeze
1107
1112
  QUERY_PLAN = 'QUERY PLAN'.to_sym
1108
1113
  ROW_EXCLUSIVE = 'ROW EXCLUSIVE'.freeze
1109
1114
  ROW_SHARE = 'ROW SHARE'.freeze
1110
- SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct columns from join where group having compounds order limit lock')
1111
- SELECT_CLAUSE_METHODS_84 = Dataset.clause_methods(:select, %w'with select distinct columns from join where group having window compounds order limit lock')
1112
1115
  SHARE = 'SHARE'.freeze
1113
1116
  SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
1114
1117
  SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
1115
1118
  SQL_WITH_RECURSIVE = "WITH RECURSIVE ".freeze
1116
- UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'update table set from where returning')
1117
- UPDATE_CLAUSE_METHODS_91 = Dataset.clause_methods(:update, %w'with update table set from where returning')
1118
1119
  SPACE = Dataset::SPACE
1119
1120
  FROM = Dataset::FROM
1120
1121
  APOS = Dataset::APOS
@@ -1133,6 +1134,11 @@ module Sequel
1133
1134
  EMPTY_STRING = ''.freeze
1134
1135
  LOCK_MODES = ['ACCESS SHARE', 'ROW SHARE', 'ROW EXCLUSIVE', 'SHARE UPDATE EXCLUSIVE', 'SHARE', 'SHARE ROW EXCLUSIVE', 'EXCLUSIVE', 'ACCESS EXCLUSIVE'].each{|s| s.freeze}
1135
1136
 
1137
+ Dataset.def_sql_method(self, :delete, [['if server_version >= 90100', %w'with delete from using where returning'], ['else', %w'delete from using where returning']])
1138
+ Dataset.def_sql_method(self, :insert, [['if server_version >= 90100', %w'with insert into columns values returning'], ['else', %w'insert into columns values returning']])
1139
+ Dataset.def_sql_method(self, :select, [['if server_version >= 80400', %w'with select distinct columns from join where group having window compounds order limit lock'], ['else', %w'select distinct columns from join where group having compounds order limit lock']])
1140
+ Dataset.def_sql_method(self, :update, [['if server_version >= 90100', %w'with update table set from where returning'], ['else', %w'update table set from where returning']])
1141
+
1136
1142
  # Shared methods for prepared statements when used with PostgreSQL databases.
1137
1143
  module PreparedStatementMethods
1138
1144
  # Override insert action to use RETURNING if the server supports it.
@@ -1279,11 +1285,12 @@ module Sequel
1279
1285
  nil
1280
1286
  end
1281
1287
 
1282
- # PostgreSQL allows inserting multiple rows at once.
1283
- def multi_insert_sql(columns, values)
1284
- sql = LiteralString.new('VALUES ')
1285
- expression_list_append(sql, values.map{|r| Array(r)})
1286
- [insert_sql(columns, sql)]
1288
+ def supports_cte?(type=:select)
1289
+ if type == :select
1290
+ server_version >= 80400
1291
+ else
1292
+ server_version >= 90100
1293
+ end
1287
1294
  end
1288
1295
 
1289
1296
  # PostgreSQL supports using the WITH clause in subqueries if it
@@ -1393,15 +1400,6 @@ module Sequel
1393
1400
  raise(InvalidOperation, "Joined datasets cannot be truncated") if opts[:join]
1394
1401
  end
1395
1402
 
1396
- # PostgreSQL allows deleting from joined datasets
1397
- def delete_clause_methods
1398
- if server_version >= 90100
1399
- DELETE_CLAUSE_METHODS_91
1400
- else
1401
- DELETE_CLAUSE_METHODS
1402
- end
1403
- end
1404
-
1405
1403
  # Only include the primary table in the main delete clause
1406
1404
  def delete_from_sql(sql)
1407
1405
  sql << FROM
@@ -1413,15 +1411,6 @@ module Sequel
1413
1411
  join_from_sql(:USING, sql)
1414
1412
  end
1415
1413
 
1416
- # PostgreSQL allows a RETURNING clause.
1417
- def insert_clause_methods
1418
- if server_version >= 90100
1419
- INSERT_CLAUSE_METHODS_91
1420
- else
1421
- INSERT_CLAUSE_METHODS
1422
- end
1423
- end
1424
-
1425
1414
  # Return the primary key to use for RETURNING in an INSERT statement
1426
1415
  def insert_pk
1427
1416
  if (f = opts[:from]) && !f.empty?
@@ -1479,9 +1468,9 @@ module Sequel
1479
1468
  BOOL_TRUE
1480
1469
  end
1481
1470
 
1482
- # The order of clauses in the SELECT SQL statement
1483
- def select_clause_methods
1484
- server_version >= 80400 ? SELECT_CLAUSE_METHODS_84 : SELECT_CLAUSE_METHODS
1471
+ # PostgreSQL supports multiple rows in INSERT.
1472
+ def multi_insert_sql_strategy
1473
+ :values
1485
1474
  end
1486
1475
 
1487
1476
  # PostgreSQL requires parentheses around compound datasets if they use
@@ -1537,15 +1526,6 @@ module Sequel
1537
1526
  SQL::StringExpression.new(:'||', *cols)
1538
1527
  end
1539
1528
 
1540
- # PostgreSQL splits the main table from the joined tables
1541
- def update_clause_methods
1542
- if server_version >= 90100
1543
- UPDATE_CLAUSE_METHODS_91
1544
- else
1545
- UPDATE_CLAUSE_METHODS
1546
- end
1547
- end
1548
-
1549
1529
  # Use FROM to specify additional tables in an update query
1550
1530
  def update_from_sql(sql)
1551
1531
  join_from_sql(:FROM, sql)