sequel 3.34.1 → 3.35.0

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