sequel 3.34.1 → 3.35.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 (101) hide show
  1. data/CHANGELOG +52 -0
  2. data/README.rdoc +3 -1
  3. data/Rakefile +2 -10
  4. data/doc/active_record.rdoc +1 -0
  5. data/doc/migration.rdoc +18 -7
  6. data/doc/model_hooks.rdoc +6 -0
  7. data/doc/opening_databases.rdoc +3 -0
  8. data/doc/prepared_statements.rdoc +0 -1
  9. data/doc/release_notes/3.35.0.txt +144 -0
  10. data/doc/schema_modification.rdoc +16 -1
  11. data/doc/thread_safety.rdoc +17 -0
  12. data/lib/sequel/adapters/do.rb +2 -2
  13. data/lib/sequel/adapters/do/postgres.rb +1 -52
  14. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  15. data/lib/sequel/adapters/firebird.rb +1 -1
  16. data/lib/sequel/adapters/ibmdb.rb +2 -2
  17. data/lib/sequel/adapters/jdbc.rb +23 -19
  18. data/lib/sequel/adapters/jdbc/db2.rb +0 -5
  19. data/lib/sequel/adapters/jdbc/derby.rb +29 -2
  20. data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
  21. data/lib/sequel/adapters/jdbc/h2.rb +1 -1
  22. data/lib/sequel/adapters/jdbc/hsqldb.rb +7 -0
  23. data/lib/sequel/adapters/jdbc/informix.rb +0 -5
  24. data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
  25. data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
  26. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -35
  27. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  28. data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
  29. data/lib/sequel/adapters/jdbc/transactions.rb +4 -4
  30. data/lib/sequel/adapters/mysql2.rb +1 -1
  31. data/lib/sequel/adapters/odbc.rb +3 -3
  32. data/lib/sequel/adapters/odbc/mssql.rb +14 -1
  33. data/lib/sequel/adapters/oracle.rb +6 -18
  34. data/lib/sequel/adapters/postgres.rb +36 -53
  35. data/lib/sequel/adapters/shared/db2.rb +16 -2
  36. data/lib/sequel/adapters/shared/mssql.rb +40 -9
  37. data/lib/sequel/adapters/shared/mysql.rb +16 -4
  38. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +2 -2
  39. data/lib/sequel/adapters/shared/oracle.rb +2 -0
  40. data/lib/sequel/adapters/shared/postgres.rb +135 -211
  41. data/lib/sequel/adapters/sqlite.rb +2 -2
  42. data/lib/sequel/adapters/swift.rb +1 -1
  43. data/lib/sequel/adapters/swift/postgres.rb +1 -71
  44. data/lib/sequel/adapters/tinytds.rb +3 -3
  45. data/lib/sequel/core.rb +27 -4
  46. data/lib/sequel/database/connecting.rb +7 -8
  47. data/lib/sequel/database/logging.rb +6 -1
  48. data/lib/sequel/database/misc.rb +20 -4
  49. data/lib/sequel/database/query.rb +38 -18
  50. data/lib/sequel/database/schema_generator.rb +5 -2
  51. data/lib/sequel/database/schema_methods.rb +34 -8
  52. data/lib/sequel/dataset/prepared_statements.rb +1 -1
  53. data/lib/sequel/dataset/sql.rb +18 -24
  54. data/lib/sequel/extensions/core_extensions.rb +0 -23
  55. data/lib/sequel/extensions/migration.rb +22 -8
  56. data/lib/sequel/extensions/pg_auto_parameterize.rb +4 -0
  57. data/lib/sequel/extensions/schema_dumper.rb +1 -1
  58. data/lib/sequel/model.rb +2 -2
  59. data/lib/sequel/model/associations.rb +95 -70
  60. data/lib/sequel/model/base.rb +16 -18
  61. data/lib/sequel/plugins/dirty.rb +214 -0
  62. data/lib/sequel/plugins/identity_map.rb +1 -1
  63. data/lib/sequel/plugins/json_serializer.rb +16 -1
  64. data/lib/sequel/plugins/many_through_many.rb +22 -32
  65. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +2 -2
  66. data/lib/sequel/plugins/prepared_statements.rb +22 -8
  67. data/lib/sequel/plugins/prepared_statements_associations.rb +2 -3
  68. data/lib/sequel/plugins/prepared_statements_with_pk.rb +1 -1
  69. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  70. data/lib/sequel/plugins/subclasses.rb +10 -2
  71. data/lib/sequel/plugins/timestamps.rb +1 -1
  72. data/lib/sequel/plugins/xml_serializer.rb +12 -1
  73. data/lib/sequel/sql.rb +1 -1
  74. data/lib/sequel/version.rb +2 -2
  75. data/spec/adapters/postgres_spec.rb +30 -79
  76. data/spec/core/database_spec.rb +46 -2
  77. data/spec/core/dataset_spec.rb +28 -22
  78. data/spec/core/schema_generator_spec.rb +1 -1
  79. data/spec/core/schema_spec.rb +51 -0
  80. data/spec/extensions/arbitrary_servers_spec.rb +0 -4
  81. data/spec/extensions/association_autoreloading_spec.rb +17 -0
  82. data/spec/extensions/association_proxies_spec.rb +4 -4
  83. data/spec/extensions/core_extensions_spec.rb +1 -24
  84. data/spec/extensions/dirty_spec.rb +155 -0
  85. data/spec/extensions/json_serializer_spec.rb +13 -0
  86. data/spec/extensions/migration_spec.rb +28 -15
  87. data/spec/extensions/named_timezones_spec.rb +6 -8
  88. data/spec/extensions/pg_auto_parameterize_spec.rb +6 -5
  89. data/spec/extensions/schema_dumper_spec.rb +3 -1
  90. data/spec/extensions/xml_serializer_spec.rb +13 -0
  91. data/spec/files/{transactionless_migrations → transaction_specified_migrations}/001_create_alt_basic.rb +1 -1
  92. data/spec/files/{transactionless_migrations → transaction_specified_migrations}/002_create_basic.rb +0 -0
  93. data/spec/files/{transaction_migrations → transaction_unspecified_migrations}/001_create_alt_basic.rb +0 -0
  94. data/spec/files/{transaction_migrations → transaction_unspecified_migrations}/002_create_basic.rb +0 -0
  95. data/spec/integration/associations_test.rb +5 -7
  96. data/spec/integration/dataset_test.rb +25 -7
  97. data/spec/integration/plugin_test.rb +1 -1
  98. data/spec/integration/schema_test.rb +16 -1
  99. data/spec/model/associations_spec.rb +2 -2
  100. metadata +14 -9
  101. data/lib/sequel/adapters/odbc/db2.rb +0 -17
@@ -41,7 +41,7 @@ module Sequel
41
41
  # arguments.
42
42
  def execute_prepared_statement(ps_name, opts, &block)
43
43
  args = opts[:arguments]
44
- ps = prepared_statements[ps_name]
44
+ ps = prepared_statement(ps_name)
45
45
  sql = ps.prepared_sql
46
46
  synchronize(opts[:server]) do |conn|
47
47
  unless conn.prepared_statements[ps_name] == sql
@@ -137,7 +137,7 @@ module Sequel
137
137
  ps.extend(PreparedStatementMethods)
138
138
  if name
139
139
  ps.prepared_statement_name = name
140
- db.prepared_statements[name] = ps
140
+ db.set_prepared_statement(name, ps)
141
141
  end
142
142
  ps
143
143
  end
@@ -217,6 +217,8 @@ module Sequel
217
217
  sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * power(2, #{literal b}))"}
218
218
  when :>>
219
219
  sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / power(2, #{literal b}))"}
220
+ when :%
221
+ sql << complex_expression_arg_pairs(args){|a, b| "MOD(#{literal(a)}, #{literal(b)})"}
220
222
  when :ILIKE, :'NOT ILIKE'
221
223
  sql << ILIKE_0
222
224
  literal_append(sql, args.at(0))
@@ -1,6 +1,4 @@
1
1
  module Sequel
2
- Dataset::NON_SQL_OPTIONS << :disable_insert_returning
3
-
4
2
  # Top level module for holding all PostgreSQL-related modules and classes
5
3
  # for Sequel. There are a few module level accessors that are added via
6
4
  # metaprogramming. These are:
@@ -45,25 +43,32 @@ module Sequel
45
43
  attr_accessor :client_min_messages
46
44
 
47
45
  # By default, Sequel forces the use of standard strings, so that
48
- # '\\' is interpreted as \\ and not \. While PostgreSQL defaults
49
- # to interpreting plain strings as extended strings, this will change
50
- # in a future version of PostgreSQL. Sequel assumes that SQL standard
51
- # strings will be used.
46
+ # '\\' is interpreted as \\ and not \. While PostgreSQL <9.1 defaults
47
+ # to interpreting plain strings, newer versions use standard strings by
48
+ # default. Sequel assumes that SQL standard strings will be used. Setting
49
+ # this to false means Sequel will use the database's default.
52
50
  attr_accessor :force_standard_strings
53
51
  end
54
52
 
55
- # Methods shared by adapter/connection instances.
56
- module AdapterMethods
57
- attr_writer :db
53
+ # Methods shared by Database instances that connect to PostgreSQL.
54
+ module DatabaseMethods
55
+ EXCLUDE_SCHEMAS = /pg_*|information_schema/i
56
+ PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
57
+ RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
58
+ SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
59
+ FOREIGN_KEY_LIST_ON_DELETE_MAP = {'a'.freeze=>:no_action, 'r'.freeze=>:restrict, 'c'.freeze=>:cascade, 'n'.freeze=>:set_null, 'd'.freeze=>:set_default}.freeze
58
60
 
59
- SELECT_CURRVAL = "SELECT currval('%s')".freeze
61
+ # SQL fragment for custom sequences (ones not created by serial primary key),
62
+ # Returning the schema and literal form of the sequence name, by parsing
63
+ # the column defaults table.
60
64
  SELECT_CUSTOM_SEQUENCE_SQL = (<<-end_sql
61
- SELECT '"' || name.nspname || '".' || CASE
65
+ SELECT name.nspname AS "schema",
66
+ CASE
62
67
  WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
63
68
  substr(split_part(def.adsrc, '''', 2),
64
69
  strpos(split_part(def.adsrc, '''', 2), '.')+1)
65
70
  ELSE split_part(def.adsrc, '''', 2)
66
- END
71
+ END AS "sequence"
67
72
  FROM pg_class t
68
73
  JOIN pg_namespace name ON (t.relnamespace = name.oid)
69
74
  JOIN pg_attribute attr ON (t.oid = attrelid)
@@ -74,8 +79,10 @@ module Sequel
74
79
  end_sql
75
80
  ).strip.gsub(/\s+/, ' ').freeze
76
81
 
82
+ # SQL fragment for determining primary key column for the given table. Only
83
+ # returns the first primary key if the table has a composite primary key.
77
84
  SELECT_PK_SQL = (<<-end_sql
78
- SELECT pg_attribute.attname
85
+ SELECT pg_attribute.attname AS pk
79
86
  FROM pg_class, pg_attribute, pg_index, pg_namespace
80
87
  WHERE pg_class.oid = pg_attribute.attrelid
81
88
  AND pg_class.relnamespace = pg_namespace.oid
@@ -85,8 +92,10 @@ module Sequel
85
92
  end_sql
86
93
  ).strip.gsub(/\s+/, ' ').freeze
87
94
 
95
+ # SQL fragment for getting sequence associated with table's
96
+ # primary key, assuming it was a serial primary key column.
88
97
  SELECT_SERIAL_SEQUENCE_SQL = (<<-end_sql
89
- SELECT '"' || name.nspname || '".' || seq.relname || ''
98
+ SELECT name.nspname AS "schema", seq.relname AS "sequence"
90
99
  FROM pg_class seq, pg_attribute attr, pg_depend dep,
91
100
  pg_namespace name, pg_constraint cons
92
101
  WHERE seq.oid = dep.objid
@@ -100,72 +109,6 @@ module Sequel
100
109
  end_sql
101
110
  ).strip.gsub(/\s+/, ' ').freeze
102
111
 
103
- # Depth of the current transaction on this connection, used
104
- # to implement multi-level transactions with savepoints.
105
- attr_accessor :transaction_depth
106
-
107
- # Apply connection settings for this connection. Currently, turns
108
- # standard_conforming_strings ON if Postgres.force_standard_strings
109
- # is true.
110
- def apply_connection_settings
111
- if Postgres.force_standard_strings
112
- # This setting will only work on PostgreSQL 8.2 or greater
113
- # and we don't know the server version at this point, so
114
- # try it unconditionally and rescue any errors.
115
- execute("SET standard_conforming_strings = ON") rescue nil
116
- end
117
- if cmm = Postgres.client_min_messages
118
- execute("SET client_min_messages = '#{cmm.to_s.upcase}'")
119
- end
120
- end
121
-
122
- # Get the last inserted value for the given sequence.
123
- def last_insert_id(sequence)
124
- sql = SELECT_CURRVAL % sequence
125
- execute(sql) do |r|
126
- val = single_value(r)
127
- return val.to_i if val
128
- end
129
- end
130
-
131
- # Get the primary key for the given table and schema. Both
132
- # should be provided as literal SQL strings, with schema
133
- # optionally nil.
134
- def primary_key(schema, table)
135
- sql = "#{SELECT_PK_SQL} AND pg_class.relname = #{table}"
136
- sql << "AND pg_namespace.nspname = #{schema}" if schema
137
- execute(sql) do |r|
138
- return single_value(r)
139
- end
140
- end
141
-
142
- # Get the primary key and sequence for the given table and schema.
143
- # Both should be provided as literal SQL strings, with schema
144
- # optionally nil.
145
- def sequence(schema, table)
146
- sql = "#{SELECT_SERIAL_SEQUENCE_SQL} AND seq.relname = #{table}"
147
- sql << " AND name.nspname = #{schema}" if schema
148
- execute(sql) do |r|
149
- seq = single_value(r)
150
- return seq if seq
151
- end
152
-
153
- sql = "#{SELECT_CUSTOM_SEQUENCE_SQL} AND t.relname = #{table}"
154
- sql << " AND name.nspname = #{schema}" if schema
155
- execute(sql) do |r|
156
- return single_value(r)
157
- end
158
- end
159
- end
160
-
161
- # Methods shared by Database instances that connect to PostgreSQL.
162
- module DatabaseMethods
163
- EXCLUDE_SCHEMAS = /pg_*|information_schema/i
164
- PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
165
- RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
166
- SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
167
- FOREIGN_KEY_LIST_ON_DELETE_MAP = {'a'.freeze=>:no_action, 'r'.freeze=>:restrict, 'c'.freeze=>:cascade, 'n'.freeze=>:set_null, 'd'.freeze=>:set_default}.freeze
168
-
169
112
  # Commit an existing prepared transaction with the given transaction
170
113
  # identifier string.
171
114
  def commit_prepared_transaction(transaction_id)
@@ -340,12 +283,11 @@ module Sequel
340
283
  join(:pg_index___ind, :indrelid=>:oid, im.call(table)=>:relname).
341
284
  join(:pg_class___indc, :oid=>:indexrelid).
342
285
  join(:pg_attribute___att, :attrelid=>:tab__oid, :attnum=>attnums).
343
- filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil).
286
+ filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil, :indisvalid=>true).
344
287
  order(:indc__relname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}, 32, :att__attnum)).
345
288
  select(:indc__relname___name, :ind__indisunique___unique, :att__attname___column)
346
289
 
347
290
  ds.join!(:pg_namespace___nsp, :oid=>:tab__relnamespace, :nspname=>schema.to_s) if schema
348
- ds.filter!(:indisvalid=>true) if server_version >= 80200
349
291
  ds.filter!(:indisready=>true, :indcheckxmin=>false) if server_version >= 80300
350
292
 
351
293
  indexes = {}
@@ -374,30 +316,39 @@ module Sequel
374
316
  # Return primary key for the given table.
375
317
  def primary_key(table, opts={})
376
318
  quoted_table = quote_schema_table(table)
377
- return @primary_keys[quoted_table] if @primary_keys.include?(quoted_table)
378
- @primary_keys[quoted_table] = if conn = opts[:conn]
379
- conn.primary_key(*schema_and_table_quoted_strings(table))
380
- else
381
- synchronize(opts[:server]){|con| con.primary_key(*schema_and_table_quoted_strings(table))}
319
+ @primary_keys.fetch(quoted_table) do
320
+ schema, table = schema_and_table(table)
321
+ sql = "#{SELECT_PK_SQL} AND pg_class.relname = #{literal(table)}"
322
+ sql << "AND pg_namespace.nspname = #{literal(schema)}" if schema
323
+ @primary_keys[quoted_table] = fetch(sql).single_value
382
324
  end
383
325
  end
384
326
 
385
327
  # Return the sequence providing the default for the primary key for the given table.
386
328
  def primary_key_sequence(table, opts={})
387
329
  quoted_table = quote_schema_table(table)
388
- return @primary_key_sequences[quoted_table] if @primary_key_sequences.include?(quoted_table)
389
- @primary_key_sequences[quoted_table] = if conn = opts[:conn]
390
- conn.sequence(*schema_and_table_quoted_strings(table))
391
- else
392
- synchronize(opts[:server]){|con| con.sequence(*schema_and_table_quoted_strings(table))}
330
+ @primary_key_sequences.fetch(quoted_table) do
331
+ schema, table = schema_and_table(table)
332
+ table = literal(table)
333
+ sql = "#{SELECT_SERIAL_SEQUENCE_SQL} AND seq.relname = #{table}"
334
+ sql << " AND name.nspname = #{literal(schema)}" if schema
335
+ unless pks = fetch(sql).single_record
336
+ sql = "#{SELECT_CUSTOM_SEQUENCE_SQL} AND t.relname = #{table}"
337
+ sql << " AND name.nspname = #{literal(schema)}" if schema
338
+ pks = fetch(sql).single_record
339
+ end
340
+
341
+ @primary_key_sequences[quoted_table] = if pks
342
+ literal(SQL::QualifiedIdentifier.new(pks[:schema], LiteralString.new(pks[:sequence])))
343
+ end
393
344
  end
394
345
  end
395
346
 
396
347
  # Reset the primary key sequence for the given table, baseing it on the
397
348
  # maximum current value of the table's primary key.
398
349
  def reset_primary_key_sequence(table)
399
- pk = SQL::Identifier.new(primary_key(table))
400
350
  return unless seq = primary_key_sequence(table)
351
+ pk = SQL::Identifier.new(primary_key(table))
401
352
  db = self
402
353
  seq_ds = db.from(LiteralString.new(seq))
403
354
  get{setval(seq, db[table].select{coalesce(max(pk)+seq_ds.select{:increment_by}, seq_ds.select(:min_value))}, false)}
@@ -428,25 +379,25 @@ module Sequel
428
379
  0
429
380
  end
430
381
  end
431
- warn 'Sequel support for PostgreSQL <8.2 is deprecated and will be removed in 3.35.0' if @server_version < 80200
382
+ warn 'Sequel no longer supports PostgreSQL <8.2, some things may not work' if @server_version < 80200
432
383
  @server_version
433
384
  end
434
385
 
435
- # PostgreSQL supports prepared transactions (two-phase commit) if
436
- # max_prepared_transactions is greater than 0.
437
- def supports_prepared_transactions?
438
- return @supports_prepared_transactions if defined?(@supports_prepared_transactions)
439
- @supports_prepared_transactions = self['SHOW max_prepared_transactions'].get.to_i > 0
440
- end
441
-
442
386
  # PostgreSQL supports CREATE TABLE IF NOT EXISTS on 9.1+
443
387
  def supports_create_table_if_not_exists?
444
388
  server_version >= 90100
445
389
  end
446
390
 
447
- # PostgreSQL supports DROP TABLE IF EXISTS on 8.2+
391
+ # PostgreSQL supports DROP TABLE IF EXISTS
448
392
  def supports_drop_table_if_exists?
449
- server_version >= 80200
393
+ true
394
+ end
395
+
396
+ # PostgreSQL supports prepared transactions (two-phase commit) if
397
+ # max_prepared_transactions is greater than 0.
398
+ def supports_prepared_transactions?
399
+ return @supports_prepared_transactions if defined?(@supports_prepared_transactions)
400
+ @supports_prepared_transactions = self['SHOW max_prepared_transactions'].get.to_i > 0
450
401
  end
451
402
 
452
403
  # PostgreSQL supports savepoints
@@ -459,6 +410,11 @@ module Sequel
459
410
  true
460
411
  end
461
412
 
413
+ # PostgreSQL supports transaction DDL statements.
414
+ def supports_transactional_ddl?
415
+ true
416
+ end
417
+
462
418
  # Array of symbols specifying table names in the current database.
463
419
  # The dataset used is yielded to the block if one is provided,
464
420
  # otherwise, an array of symbols of table names is returned.
@@ -491,13 +447,23 @@ module Sequel
491
447
  # If the :prepare option is given and we aren't in a savepoint,
492
448
  # prepare the transaction for a two-phase commit.
493
449
  def commit_transaction(conn, opts={})
494
- if (s = opts[:prepare]) && @transactions[conn][:savepoint_level] <= 1
450
+ if (s = opts[:prepare]) && _trans(conn)[:savepoint_level] <= 1
495
451
  log_connection_execute(conn, "PREPARE TRANSACTION #{literal(s)}")
496
452
  else
497
453
  super
498
454
  end
499
455
  end
500
456
 
457
+ # The SQL queries to execute when starting a new connection.
458
+ def connection_configuration_sqls
459
+ sqls = []
460
+ sqls << "SET standard_conforming_strings = ON" if Postgres.force_standard_strings
461
+ if cmm = Postgres.client_min_messages
462
+ sqls << "SET client_min_messages = '#{cmm.to_s.upcase}'"
463
+ end
464
+ sqls
465
+ end
466
+
501
467
  # SQL statement to create database function.
502
468
  def create_function_sql(name, definition, opts={})
503
469
  args = opts[:args]
@@ -604,39 +570,6 @@ module Sequel
604
570
  "CREATE #{unique}INDEX #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{filter}"
605
571
  end
606
572
 
607
- # The result of the insert for the given table and values. If values
608
- # is an array, assume the first column is the primary key and return
609
- # that. If values is a hash, lookup the primary key for the table. If
610
- # the primary key is present in the hash, return its value. Otherwise,
611
- # look up the sequence for the table's primary key. If one exists,
612
- # return the last value the of the sequence for the connection.
613
- def insert_result(conn, table, values)
614
- case values
615
- when Hash
616
- return nil unless pk = primary_key(table, :conn=>conn)
617
- if pk and pkv = values[pk.to_sym]
618
- pkv
619
- else
620
- begin
621
- if seq = primary_key_sequence(table, :conn=>conn)
622
- conn.last_insert_id(seq)
623
- end
624
- rescue Exception => e
625
- raise_error(e, :classes=>CONVERTED_EXCEPTIONS) unless RE_CURRVAL_ERROR.match(e.message)
626
- end
627
- end
628
- when Array
629
- values.first
630
- else
631
- nil
632
- end
633
- end
634
-
635
- # Don't log, since logging is done by the underlying connection.
636
- def log_connection_execute(conn, sql)
637
- conn.execute(sql)
638
- end
639
-
640
573
  # Backbone of the tables and views support.
641
574
  def pg_class_relname(type, opts)
642
575
  ds = metadata_dataset.from(:pg_class).filter(:relkind=>type).select(:relname).exclude(SQL::StringExpression.like(:relname, SYSTEM_TABLE_REGEXP)).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
@@ -666,13 +599,6 @@ module Sequel
666
599
  "ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
667
600
  end
668
601
 
669
- # Split the table into a schema and table, and return the values as quoted strings for usage
670
- # in querying the system tables.
671
- def schema_and_table_quoted_strings(table)
672
- schema, table = schema_and_table(table)
673
- [(literal(schema) if schema), literal(table)]
674
- end
675
-
676
602
  # PostgreSQL's autoincrementing primary keys are of type integer or bigint
677
603
  # using a nextval function call as a default.
678
604
  def schema_autoincrementing_primary_key?(schema)
@@ -758,15 +684,13 @@ module Sequel
758
684
  BOOL_FALSE = 'false'.freeze
759
685
  BOOL_TRUE = 'true'.freeze
760
686
  COMMA_SEPARATOR = ', '.freeze
761
- DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'delete from using where')
762
- DELETE_CLAUSE_METHODS_82 = Dataset.clause_methods(:delete, %w'delete from using where returning')
687
+ DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'delete from using where returning')
763
688
  DELETE_CLAUSE_METHODS_91 = Dataset.clause_methods(:delete, %w'with delete from using where returning')
764
689
  EXCLUSIVE = 'EXCLUSIVE'.freeze
765
690
  EXPLAIN = 'EXPLAIN '.freeze
766
691
  EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
767
692
  FOR_SHARE = ' FOR SHARE'.freeze
768
- INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert into columns values')
769
- INSERT_CLAUSE_METHODS_82 = Dataset.clause_methods(:insert, %w'insert into columns values returning')
693
+ INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert into columns values returning')
770
694
  INSERT_CLAUSE_METHODS_91 = Dataset.clause_methods(:insert, %w'with insert into columns values returning')
771
695
  LOCK = 'LOCK TABLE %s IN %s MODE'.freeze
772
696
  NULL = LiteralString.new('NULL').freeze
@@ -780,8 +704,7 @@ module Sequel
780
704
  SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
781
705
  SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
782
706
  SQL_WITH_RECURSIVE = "WITH RECURSIVE ".freeze
783
- UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'update table set from where')
784
- UPDATE_CLAUSE_METHODS_82 = Dataset.clause_methods(:update, %w'update table set from where returning')
707
+ UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'update table set from where returning')
785
708
  UPDATE_CLAUSE_METHODS_91 = Dataset.clause_methods(:update, %w'with update table set from where returning')
786
709
  SPACE = Dataset::SPACE
787
710
  FROM = Dataset::FROM
@@ -802,7 +725,7 @@ module Sequel
802
725
  module PreparedStatementMethods
803
726
  # Override insert action to use RETURNING if the server supports it.
804
727
  def run
805
- if @prepared_type == :insert && supports_insert_select?
728
+ if @prepared_type == :insert
806
729
  fetch_rows(prepared_sql){|r| return r.values.first}
807
730
  else
808
731
  super
@@ -811,22 +734,12 @@ module Sequel
811
734
 
812
735
  def prepared_sql
813
736
  return @prepared_sql if @prepared_sql
814
- @opts[:returning] = insert_pk if @prepared_type == :insert && supports_insert_select?
737
+ @opts[:returning] = insert_pk if @prepared_type == :insert
815
738
  super
816
739
  @prepared_sql
817
740
  end
818
741
  end
819
742
 
820
- # Add the disable_insert_returning! mutation method
821
- def self.extended(obj)
822
- obj.def_mutation_method(:disable_insert_returning)
823
- end
824
-
825
- # Add the disable_insert_returning! mutation method
826
- def self.included(mod)
827
- mod.def_mutation_method(:disable_insert_returning)
828
- end
829
-
830
743
  # Return the results of an EXPLAIN ANALYZE query as a string
831
744
  def analyze
832
745
  explain(:analyze=>true)
@@ -849,12 +762,6 @@ module Sequel
849
762
  end
850
763
  end
851
764
 
852
- # Disable the use of INSERT RETURNING, even if the server supports it
853
- def disable_insert_returning
854
- warn("disable_insert_returning is deprecated and will be removed in Sequel 3.35.0")
855
- clone(:disable_insert_returning=>true)
856
- end
857
-
858
765
  # Return the results of an EXPLAIN query as a string
859
766
  def explain(opts={})
860
767
  with_sql((opts[:analyze] ? EXPLAIN_ANALYZE : EXPLAIN) + select_sql).map(QUERY_PLAN).join(CRLF)
@@ -876,26 +783,23 @@ module Sequel
876
783
  # Insert given values into the database.
877
784
  def insert(*values)
878
785
  if @opts[:returning]
786
+ # already know which columns to return, let the standard code
787
+ # handle it
879
788
  super
880
- elsif !@opts[:sql] && supports_insert_select?
881
- returning(insert_pk).insert(*values){|r| return r.values.first}
882
- elsif (f = opts[:from]) && !f.empty?
883
- v = if values.size == 1
884
- values.first
885
- elsif values.size == 2 && values.all?{|v0| v0.is_a?(Array)}
886
- Hash[*values.first.zip(values.last).flatten]
887
- else
888
- values
889
- end
890
- execute_insert(insert_sql(*values), :table=>f.first, :values=>v)
891
- else
789
+ elsif @opts[:sql]
790
+ # raw SQL used, so don't know which table is being inserted
791
+ # into, and therefore can't determine primary key. Run the
792
+ # insert statement and return nil.
892
793
  super
794
+ nil
795
+ else
796
+ # Force the use of RETURNING with the primary key value.
797
+ returning(insert_pk).insert(*values){|r| return r.values.first}
893
798
  end
894
799
  end
895
800
 
896
801
  # Insert a record returning the record inserted
897
802
  def insert_select(*values)
898
- return unless supports_insert_select?
899
803
  returning.insert(*values){|r| return r}
900
804
  end
901
805
 
@@ -913,11 +817,8 @@ module Sequel
913
817
  nil
914
818
  end
915
819
 
916
- # For PostgreSQL version > 8.2, allow inserting multiple rows at once.
820
+ # PostgreSQL allows inserting multiple rows at once.
917
821
  def multi_insert_sql(columns, values)
918
- return super if server_version < 80200
919
-
920
- # postgresql 8.2 introduces support for multi-row insert
921
822
  sql = LiteralString.new('VALUES ')
922
823
  expression_list_append(sql, values.map{|r| Array(r)})
923
824
  [insert_sql(columns, sql)]
@@ -939,12 +840,9 @@ module Sequel
939
840
  true
940
841
  end
941
842
 
843
+ # Returning is always supported.
942
844
  def supports_returning?(type)
943
- if type == :insert
944
- server_version >= 80200 && !opts[:disable_insert_returning]
945
- else
946
- server_version >= 80200
947
- end
845
+ true
948
846
  end
949
847
 
950
848
  # PostgreSQL supports timezones in literal timestamps
@@ -957,6 +855,29 @@ module Sequel
957
855
  server_version >= 80400
958
856
  end
959
857
 
858
+ # Truncates the dataset. Returns nil.
859
+ #
860
+ # Options:
861
+ # :cascade :: whether to use the CASCADE option, useful when truncating
862
+ # tables with Foreign Keys.
863
+ # :only :: truncate using ONLY, so child tables are unaffected
864
+ # :restart :: use RESTART IDENTITY to restart any related sequences
865
+ #
866
+ # :only and :restart only work correctly on PostgreSQL 8.4+.
867
+ #
868
+ # Usage:
869
+ # DB[:table].truncate # TRUNCATE TABLE "table"
870
+ # # => nil
871
+ # DB[:table].truncate(:cascade => true, :only=>true, :restart=>true) # TRUNCATE TABLE ONLY "table" RESTART IDENTITY CASCADE
872
+ # # => nil
873
+ def truncate(opts = {})
874
+ if opts.empty?
875
+ super()
876
+ else
877
+ clone(:truncate_opts=>opts).truncate
878
+ end
879
+ end
880
+
960
881
  # Return a clone of the dataset with an addition named window that can be referenced in window functions.
961
882
  def window(name, opts)
962
883
  clone(:window=>(@opts[:window]||[]) + [[name, SQL::Window.new(opts)]])
@@ -969,17 +890,13 @@ module Sequel
969
890
  # is only set to return a single columns, return an array of just that column.
970
891
  # Otherwise, return an array of hashes.
971
892
  def _import(columns, values, opts={})
972
- if server_version >= 80200
973
- if opts[:return] == :primary_key && !@opts[:returning]
974
- returning(insert_pk)._import(columns, values, opts)
975
- elsif @opts[:returning]
976
- statements = multi_insert_sql(columns, values)
977
- @db.transaction(opts.merge(:server=>@opts[:server])) do
978
- statements.map{|st| returning_fetch_rows(st)}
979
- end.first.map{|v| v.length == 1 ? v.values.first : v}
980
- else
981
- super
982
- end
893
+ if @opts[:returning]
894
+ statements = multi_insert_sql(columns, values)
895
+ @db.transaction(opts.merge(:server=>@opts[:server])) do
896
+ statements.map{|st| returning_fetch_rows(st)}
897
+ end.first.map{|v| v.length == 1 ? v.values.first : v}
898
+ elsif opts[:return] == :primary_key
899
+ returning(insert_pk)._import(columns, values, opts)
983
900
  else
984
901
  super
985
902
  end
@@ -987,12 +904,22 @@ module Sequel
987
904
 
988
905
  private
989
906
 
907
+ # Format TRUNCATE statement with PostgreSQL specific options.
908
+ def _truncate_sql(table)
909
+ to = @opts[:truncate_opts] || {}
910
+ "TRUNCATE TABLE#{' ONLY' if to[:only]} #{table}#{' RESTART IDENTITY' if to[:restart]}#{' CASCADE' if to[:cascade]}"
911
+ end
912
+
913
+ # Allow truncation of multiple source tables.
914
+ def check_truncation_allowed!
915
+ raise(InvalidOperation, "Grouped datasets cannot be truncated") if opts[:group]
916
+ raise(InvalidOperation, "Joined datasets cannot be truncated") if opts[:join]
917
+ end
918
+
990
919
  # PostgreSQL allows deleting from joined datasets
991
920
  def delete_clause_methods
992
- if (sv = server_version) >= 90100
921
+ if server_version >= 90100
993
922
  DELETE_CLAUSE_METHODS_91
994
- elsif sv >= 80200
995
- DELETE_CLAUSE_METHODS_82
996
923
  else
997
924
  DELETE_CLAUSE_METHODS
998
925
  end
@@ -1013,8 +940,6 @@ module Sequel
1013
940
  def insert_clause_methods
1014
941
  if server_version >= 90100
1015
942
  INSERT_CLAUSE_METHODS_91
1016
- elsif server_version >= 80200
1017
- INSERT_CLAUSE_METHODS_82
1018
943
  else
1019
944
  INSERT_CLAUSE_METHODS
1020
945
  end
@@ -1022,8 +947,9 @@ module Sequel
1022
947
 
1023
948
  # Return the primary key to use for RETURNING in an INSERT statement
1024
949
  def insert_pk
1025
- pk = db.primary_key(opts[:from].first) if opts[:from] && !opts[:from].empty?
1026
- pk ? Sequel::SQL::Identifier.new(pk) : NULL
950
+ if (f = opts[:from]) && !f.empty? && (pk = db.primary_key(f.first))
951
+ Sequel::SQL::Identifier.new(pk)
952
+ end
1027
953
  end
1028
954
 
1029
955
  # For multiple table support, PostgreSQL requires at least
@@ -1126,10 +1052,8 @@ module Sequel
1126
1052
 
1127
1053
  # PostgreSQL splits the main table from the joined tables
1128
1054
  def update_clause_methods
1129
- if (sv = server_version) >= 90100
1055
+ if server_version >= 90100
1130
1056
  UPDATE_CLAUSE_METHODS_91
1131
- elsif sv >= 80200
1132
- UPDATE_CLAUSE_METHODS_82
1133
1057
  else
1134
1058
  UPDATE_CLAUSE_METHODS
1135
1059
  end