sequel 3.0.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/CHANGELOG +100 -0
  2. data/README.rdoc +3 -3
  3. data/bin/sequel +102 -19
  4. data/doc/reflection.rdoc +83 -0
  5. data/doc/release_notes/3.1.0.txt +406 -0
  6. data/lib/sequel/adapters/ado.rb +11 -0
  7. data/lib/sequel/adapters/amalgalite.rb +5 -20
  8. data/lib/sequel/adapters/do.rb +44 -36
  9. data/lib/sequel/adapters/firebird.rb +29 -43
  10. data/lib/sequel/adapters/jdbc.rb +17 -27
  11. data/lib/sequel/adapters/mysql.rb +35 -40
  12. data/lib/sequel/adapters/odbc.rb +4 -23
  13. data/lib/sequel/adapters/oracle.rb +22 -19
  14. data/lib/sequel/adapters/postgres.rb +6 -15
  15. data/lib/sequel/adapters/shared/mssql.rb +1 -1
  16. data/lib/sequel/adapters/shared/mysql.rb +29 -10
  17. data/lib/sequel/adapters/shared/oracle.rb +6 -8
  18. data/lib/sequel/adapters/shared/postgres.rb +28 -72
  19. data/lib/sequel/adapters/shared/sqlite.rb +5 -3
  20. data/lib/sequel/adapters/sqlite.rb +5 -20
  21. data/lib/sequel/adapters/utils/savepoint_transactions.rb +80 -0
  22. data/lib/sequel/adapters/utils/unsupported.rb +0 -12
  23. data/lib/sequel/core.rb +12 -3
  24. data/lib/sequel/core_sql.rb +1 -8
  25. data/lib/sequel/database.rb +107 -43
  26. data/lib/sequel/database/schema_generator.rb +1 -0
  27. data/lib/sequel/database/schema_methods.rb +38 -4
  28. data/lib/sequel/dataset.rb +6 -0
  29. data/lib/sequel/dataset/convenience.rb +2 -2
  30. data/lib/sequel/dataset/graph.rb +2 -2
  31. data/lib/sequel/dataset/prepared_statements.rb +3 -8
  32. data/lib/sequel/dataset/sql.rb +93 -19
  33. data/lib/sequel/extensions/blank.rb +2 -1
  34. data/lib/sequel/extensions/inflector.rb +4 -3
  35. data/lib/sequel/extensions/migration.rb +13 -2
  36. data/lib/sequel/extensions/pagination.rb +4 -0
  37. data/lib/sequel/extensions/pretty_table.rb +4 -0
  38. data/lib/sequel/extensions/query.rb +4 -0
  39. data/lib/sequel/extensions/schema_dumper.rb +100 -24
  40. data/lib/sequel/extensions/string_date_time.rb +3 -4
  41. data/lib/sequel/model.rb +2 -1
  42. data/lib/sequel/model/associations.rb +96 -38
  43. data/lib/sequel/model/base.rb +14 -14
  44. data/lib/sequel/model/plugins.rb +32 -21
  45. data/lib/sequel/plugins/caching.rb +13 -15
  46. data/lib/sequel/plugins/identity_map.rb +107 -0
  47. data/lib/sequel/plugins/lazy_attributes.rb +65 -0
  48. data/lib/sequel/plugins/many_through_many.rb +188 -0
  49. data/lib/sequel/plugins/schema.rb +13 -0
  50. data/lib/sequel/plugins/serialization.rb +53 -37
  51. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  52. data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
  53. data/lib/sequel/plugins/validation_class_methods.rb +28 -7
  54. data/lib/sequel/plugins/validation_helpers.rb +31 -24
  55. data/lib/sequel/sql.rb +16 -0
  56. data/lib/sequel/version.rb +1 -1
  57. data/spec/adapters/ado_spec.rb +47 -1
  58. data/spec/adapters/firebird_spec.rb +39 -36
  59. data/spec/adapters/mysql_spec.rb +25 -9
  60. data/spec/adapters/postgres_spec.rb +11 -24
  61. data/spec/core/database_spec.rb +54 -13
  62. data/spec/core/dataset_spec.rb +147 -29
  63. data/spec/core/object_graph_spec.rb +6 -1
  64. data/spec/core/schema_spec.rb +34 -0
  65. data/spec/core/spec_helper.rb +0 -2
  66. data/spec/extensions/caching_spec.rb +7 -0
  67. data/spec/extensions/identity_map_spec.rb +158 -0
  68. data/spec/extensions/lazy_attributes_spec.rb +113 -0
  69. data/spec/extensions/many_through_many_spec.rb +813 -0
  70. data/spec/extensions/migration_spec.rb +4 -4
  71. data/spec/extensions/schema_dumper_spec.rb +114 -13
  72. data/spec/extensions/schema_spec.rb +19 -3
  73. data/spec/extensions/serialization_spec.rb +28 -0
  74. data/spec/extensions/single_table_inheritance_spec.rb +25 -1
  75. data/spec/extensions/spec_helper.rb +2 -7
  76. data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
  77. data/spec/extensions/validation_class_methods_spec.rb +10 -5
  78. data/spec/integration/dataset_test.rb +39 -6
  79. data/spec/integration/eager_loader_test.rb +7 -7
  80. data/spec/integration/spec_helper.rb +0 -1
  81. data/spec/integration/transaction_test.rb +28 -1
  82. data/spec/model/association_reflection_spec.rb +29 -3
  83. data/spec/model/associations_spec.rb +1 -0
  84. data/spec/model/eager_loading_spec.rb +70 -1
  85. data/spec/model/plugins_spec.rb +236 -50
  86. data/spec/model/spec_helper.rb +0 -2
  87. metadata +18 -5
@@ -63,30 +63,11 @@ module Sequel
63
63
  end
64
64
  alias_method :do, :execute_dui
65
65
 
66
- # Support single level transactions on ODBC
67
- def transaction(opts={})
68
- synchronize(opts[:server]) do |conn|
69
- return yield(conn) if @transactions.include?(Thread.current)
70
- log_info(begin_transaction_sql)
71
- conn.do(begin_transaction_sql)
72
- begin
73
- @transactions << Thread.current
74
- yield(conn)
75
- rescue ::Exception => e
76
- log_info(rollback_transaction_sql)
77
- conn.do(rollback_transaction_sql)
78
- transaction_error(e)
79
- ensure
80
- unless e
81
- log_info(commit_transaction_sql)
82
- conn.do(commit_transaction_sql)
83
- end
84
- @transactions.delete(Thread.current)
85
- end
86
- end
87
- end
88
-
89
66
  private
67
+
68
+ def connection_execute_method
69
+ :do
70
+ end
90
71
 
91
72
  def disconnect_connection(c)
92
73
  c.disconnect
@@ -75,31 +75,34 @@ module Sequel
75
75
  end
76
76
  end
77
77
  end
78
- alias do execute
79
-
80
- def transaction(opts={})
81
- synchronize(opts[:server]) do |conn|
82
- return yield(conn) if @transactions.include?(Thread.current)
83
- conn.autocommit = false
84
- begin
85
- @transactions << Thread.current
86
- yield(conn)
87
- rescue => e
88
- conn.rollback
89
- raise e unless Rollback === e
90
- ensure
91
- conn.commit unless e
92
- conn.autocommit = true
93
- @transactions.delete(Thread.current)
94
- end
95
- end
96
- end
78
+ alias_method :do, :execute
97
79
 
98
80
  private
81
+
82
+ def begin_transaction(conn)
83
+ log_info(TRANSACTION_BEGIN)
84
+ conn.autocommit = false
85
+ conn
86
+ end
87
+
88
+ def commit_transaction(conn)
89
+ log_info(TRANSACTION_COMMIT)
90
+ conn.commit
91
+ end
99
92
 
100
93
  def disconnect_connection(c)
101
94
  c.logoff
102
95
  end
96
+
97
+ def remove_transaction(conn)
98
+ conn.autocommit = true
99
+ super
100
+ end
101
+
102
+ def rollback_transaction(conn)
103
+ log_info(TRANSACTION_ROLLBACK)
104
+ conn.rollback
105
+ end
103
106
  end
104
107
 
105
108
  class Dataset < Sequel::Dataset
@@ -115,6 +115,11 @@ module Sequel
115
115
  include Sequel::Postgres::AdapterMethods
116
116
  self.translate_results = false if respond_to?(:translate_results=)
117
117
 
118
+ # Hash of prepared statements for this connection. Keys are
119
+ # string names of the server side prepared statement, and values
120
+ # are SQL strings.
121
+ attr_reader(:prepared_statements) if SEQUEL_POSTGRES_USES_PG
122
+
118
123
  # Apply connection settings for this connection. Current sets
119
124
  # the date style to ISO in order make Date object creation in ruby faster,
120
125
  # if Postgres.use_iso_date_format is true.
@@ -125,6 +130,7 @@ module Sequel
125
130
  @db.log_info(sql)
126
131
  execute(sql)
127
132
  end
133
+ @prepared_statements = {} if SEQUEL_POSTGRES_USES_PG
128
134
  end
129
135
 
130
136
  # Execute the given SQL with this connection. If a block is given,
@@ -151,21 +157,6 @@ module Sequel
151
157
  end
152
158
  end
153
159
 
154
- # Reapply the connection settings if the connection is reset.
155
- def reset(*args, &block)
156
- super(*args, &block)
157
- apply_connection_settings
158
- end
159
-
160
- if SEQUEL_POSTGRES_USES_PG
161
- # Hash of prepared statements for this connection. Keys are
162
- # string names of the server side prepared statement, and values
163
- # are SQL strings.
164
- def prepared_statements
165
- @prepared_statements ||= {}
166
- end
167
- end
168
-
169
160
  private
170
161
 
171
162
  # Return the requested values for the given row.
@@ -67,7 +67,7 @@ module Sequel
67
67
 
68
68
  def multi_insert_sql(columns, values)
69
69
  values = values.map {|r| "SELECT #{expression_list(r)}" }.join(" UNION ALL ")
70
- ["INSERT INTO #{source_list(@opts[:from])} (#{identifier_list(columns)}) #{values}"]
70
+ ["#{insert_sql_base}#{source_list(@opts[:from])} (#{identifier_list(columns)}) #{values}"]
71
71
  end
72
72
 
73
73
  # Allows you to do .nolock on a query
@@ -1,4 +1,4 @@
1
- Sequel.require 'adapters/utils/unsupported'
1
+ Sequel.require %w'unsupported savepoint_transactions', 'adapters/utils'
2
2
 
3
3
  module Sequel
4
4
  class Database
@@ -14,6 +14,8 @@ module Sequel
14
14
  # Methods shared by Database instances that connect to MySQL,
15
15
  # currently supported by the native and JDBC adapters.
16
16
  module DatabaseMethods
17
+ include Sequel::Database::SavepointTransactions
18
+
17
19
  AUTO_INCREMENT = 'AUTO_INCREMENT'.freeze
18
20
  CAST_TYPES = {String=>:CHAR, Integer=>:SIGNED, Time=>:DATETIME, DateTime=>:DATETIME, Numeric=>:DECIMAL, BigDecimal=>:DECIMAL, File=>:BINARY}
19
21
  PRIMARY = 'PRIMARY'.freeze
@@ -33,17 +35,22 @@ module Sequel
33
35
  # Values are subhashes with two keys, :columns and :unique. The value of :columns
34
36
  # is an array of symbols of column names. The value of :unique is true or false
35
37
  # depending on if the index is unique.
38
+ #
39
+ # Does not include the primary key index or indexes on partial keys.
36
40
  def indexes(table)
37
41
  indexes = {}
42
+ remove_indexes = []
38
43
  m = output_identifier_meth
39
44
  im = input_identifier_meth
40
45
  metadata_dataset.with_sql("SHOW INDEX FROM ?", SQL::Identifier.new(im.call(table))).each do |r|
41
46
  name = r[:Key_name]
42
47
  next if name == PRIMARY
43
- i = indexes[m.call(name)] ||= {:columns=>[], :unique=>r[:Non_unique] != 1}
48
+ name = m.call(name)
49
+ remove_indexes << name if r[:Sub_part]
50
+ i = indexes[name] ||= {:columns=>[], :unique=>r[:Non_unique] != 1}
44
51
  i[:columns] << m.call(r[:Column_name])
45
52
  end
46
- indexes
53
+ indexes.reject{|k,v| remove_indexes.include?(k)}
47
54
  end
48
55
 
49
56
  # Get version of MySQL server, used for determined capabilities.
@@ -66,7 +73,7 @@ module Sequel
66
73
  def use(db_name)
67
74
  disconnect
68
75
  @opts[:database] = db_name if self << "USE #{db_name}"
69
- @schemas = nil
76
+ @schemas = {}
70
77
  self
71
78
  end
72
79
 
@@ -105,6 +112,13 @@ module Sequel
105
112
  AUTO_INCREMENT
106
113
  end
107
114
 
115
+ # MySQL doesn't allow default values on text columns, so ignore if it the
116
+ # generic text type is used
117
+ def column_definition_sql(column)
118
+ column.delete(:default) if column[:type] == File || (column[:type] == String && column[:text] == true)
119
+ super
120
+ end
121
+
108
122
  # Handle MySQL specific syntax for column references
109
123
  def column_references_sql(column)
110
124
  "#{", FOREIGN KEY (#{quote_identifier(column[:name])})" unless column[:type] == :check}#{super(column)}"
@@ -238,11 +252,6 @@ module Sequel
238
252
  _filter(:having, *cond, &block)
239
253
  end
240
254
 
241
- # MySQL doesn't use the SQL standard DEFAULT VALUES.
242
- def insert_default_values_sql
243
- "INSERT INTO #{source_list(@opts[:from])} () VALUES ()"
244
- end
245
-
246
255
  # Transforms an CROSS JOIN to an INNER JOIN if the expr is not nil.
247
256
  # Raises an error on use of :full_outer type, since MySQL doesn't support it.
248
257
  def join_table(type, table, expr=nil, table_alias={})
@@ -312,7 +321,7 @@ module Sequel
312
321
  update_string = update_cols.map{|c| "#{quote_identifier(c)}=VALUES(#{quote_identifier(c)})"}.join(COMMA_SEPARATOR)
313
322
  end
314
323
  values = values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)
315
- ["INSERT#{' IGNORE' if opts[:insert_ignore]} INTO #{source_list(@opts[:from])} (#{identifier_list(columns)}) VALUES #{values}#{" ON DUPLICATE KEY UPDATE #{update_string}" if update_string}"]
324
+ ["#{insert_sql_base}#{source_list(@opts[:from])} (#{identifier_list(columns)}) VALUES #{values}#{" ON DUPLICATE KEY UPDATE #{update_string}" if update_string}"]
316
325
  end
317
326
 
318
327
  # MySQL uses the nonstandard ` (backtick) for quoting identifiers.
@@ -366,6 +375,16 @@ module Sequel
366
375
 
367
376
  private
368
377
 
378
+ # MySQL supports INSERT IGNORE INTO
379
+ def insert_sql_base
380
+ "INSERT #{'IGNORE ' if opts[:insert_ignore]}INTO "
381
+ end
382
+
383
+ # MySQL doesn't use the SQL standard DEFAULT VALUES.
384
+ def insert_default_values_sql
385
+ "#{insert_sql_base}#{source_list(@opts[:from])} () VALUES ()"
386
+ end
387
+
369
388
  # Use MySQL Timestamp format
370
389
  def literal_datetime(v)
371
390
  v.strftime(TIMESTAMP_FORMAT)
@@ -10,14 +10,6 @@ module Sequel
10
10
  self << create_sequence_sql(name, opts)
11
11
  end
12
12
 
13
- def create_table(name, options={}, &block)
14
- options = {:generator=>options} if options.is_a?(Schema::Generator)
15
- generator = options[:generator] || Schema::Generator.new(self, &block)
16
- drop_statement, create_statements = create_table_sql_list(name, generator, options)
17
- (execute_ddl(drop_statement) rescue nil) if drop_statement
18
- (create_statements + index_sql_list(name, generator.indexes)).each{|sql| execute_ddl(sql)}
19
- end
20
-
21
13
  def create_trigger(*args)
22
14
  self << create_trigger_sql(*args)
23
15
  end
@@ -55,6 +47,12 @@ module Sequel
55
47
  "CREATE SEQUENCE #{quote_identifier(name)} start with #{opts [:start_with]||1} increment by #{opts[:increment_by]||1} nomaxvalue"
56
48
  end
57
49
 
50
+ def create_table_from_generator(name, generator, options)
51
+ drop_statement, create_statements = create_table_sql_list(name, generator, options)
52
+ (execute_ddl(drop_statement) rescue nil) if drop_statement
53
+ create_statements.each{|sql| execute_ddl(sql)}
54
+ end
55
+
58
56
  def create_table_sql_list(name, generator, options={})
59
57
  statements = [create_table_sql(name, generator, options)]
60
58
  drop_seq_statement = nil
@@ -1,3 +1,5 @@
1
+ Sequel.require 'adapters/utils/savepoint_transactions'
2
+
1
3
  module Sequel
2
4
  # Top level module for holding all PostgreSQL-related modules and classes
3
5
  # for Sequel. There are a few module level accessors that are added via
@@ -163,14 +165,13 @@ module Sequel
163
165
 
164
166
  # Methods shared by Database instances that connect to PostgreSQL.
165
167
  module DatabaseMethods
168
+ include Sequel::Database::SavepointTransactions
169
+
166
170
  PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
167
171
  RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
168
172
  SQL_BEGIN = 'BEGIN'.freeze
169
- SQL_SAVEPOINT = 'SAVEPOINT autopoint_%d'.freeze
170
173
  SQL_COMMIT = 'COMMIT'.freeze
171
- SQL_ROLLBACK_TO_SAVEPOINT = 'ROLLBACK TO SAVEPOINT autopoint_%d'.freeze
172
174
  SQL_ROLLBACK = 'ROLLBACK'.freeze
173
- SQL_RELEASE_SAVEPOINT = 'RELEASE SAVEPOINT autopoint_%d'.freeze
174
175
  SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
175
176
 
176
177
  # Creates the function in the database. Arguments:
@@ -269,6 +270,9 @@ module Sequel
269
270
  # Values are subhashes with two keys, :columns and :unique. The value of :columns
270
271
  # is an array of symbols of column names. The value of :unique is true or false
271
272
  # depending on if the index is unique.
273
+ #
274
+ # This will not output any partial indexes, indexes that aren't valid or ready,
275
+ # or indexes that contain anything other than simple column references.
272
276
  def indexes(table)
273
277
  m = output_identifier_meth
274
278
  im = input_identifier_meth
@@ -278,8 +282,7 @@ module Sequel
278
282
  join(:pg_index___ind, :indrelid=>:oid, im.call(table)=>:relname).
279
283
  join(:pg_class___indc, :oid=>:indexrelid).
280
284
  join(:pg_attribute___att, :attrelid=>:tab__oid, :attnum=>SQL::Function.new(:ANY, :ind__indkey)).
281
- filter(:indc__relkind=>'i', :ind__indisprimary=>false).
282
- exclude(0=>SQL::Function.new(:ANY, :ind__indkey)).
285
+ filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil, :indisvalid=>true, :indisready=>true, :indcheckxmin=>false).
283
286
  order(:indc__relname, (0...32).map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}.case(32, :att__attnum)).
284
287
  select(:indc__relname___name, :ind__indisunique___unique, :att__attname___column)
285
288
 
@@ -324,7 +327,7 @@ module Sequel
324
327
  # maximum current value of the table's primary key.
325
328
  def reset_primary_key_sequence(table)
326
329
  pk = SQL::Identifier.new(primary_key(table))
327
- seq = primary_key_sequence(table)
330
+ return unless seq = primary_key_sequence(table)
328
331
  db = self
329
332
  seq_ds = db.from(seq.lit)
330
333
  get{setval(seq, db[table].select{coalesce(max(pk)+seq_ds.select{:increment_by}, seq_ds.select(:min_value))}, false)}
@@ -369,60 +372,10 @@ module Sequel
369
372
  # * :schema - The schema to search (default_schema by default)
370
373
  # * :server - The server to use
371
374
  def tables(opts={})
372
- ds = metadata_dataset.from(:pg_class).filter(:relkind=>'r').select(:relname).exclude(SQL::StringExpression.like(:relname, SYSTEM_TABLE_REGEXP)).server(opts[:server])
373
- ds.join!(:pg_namespace, :oid=>:relnamespace, :nspname=>(opts[:schema]||default_schema).to_s) if opts[:schema] || default_schema
375
+ ds = metadata_dataset.from(:pg_class).filter(:relkind=>'r').select(:relname).exclude(SQL::StringExpression.like(:relname, SYSTEM_TABLE_REGEXP)).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace, :nspname=>(opts[:schema]||default_schema||'public').to_s)
374
376
  m = output_identifier_meth
375
377
  block_given? ? yield(ds) : ds.map{|r| m.call(r[:relname])}
376
378
  end
377
-
378
- # PostgreSQL supports multi-level transactions using save points.
379
- # To use a savepoint instead of reusing the current transaction,
380
- # use the :savepoint=>true option.
381
- def transaction(opts={})
382
- synchronize(opts[:server]) do |conn|
383
- return yield(conn) if @transactions.include?(Thread.current) and !opts[:savepoint]
384
- conn.transaction_depth ||= 0
385
- if conn.transaction_depth > 0
386
- log_info(SQL_SAVEPOINT % conn.transaction_depth)
387
- conn.execute(SQL_SAVEPOINT % conn.transaction_depth)
388
- else
389
- log_info(SQL_BEGIN)
390
- conn.execute(SQL_BEGIN)
391
- end
392
- begin
393
- conn.transaction_depth += 1
394
- @transactions << Thread.current
395
- yield conn
396
- rescue ::Exception => e
397
- if conn.transaction_depth > 1
398
- log_info(SQL_ROLLBACK_TO_SAVEPOINT % [conn.transaction_depth - 1])
399
- conn.execute(SQL_ROLLBACK_TO_SAVEPOINT % [conn.transaction_depth - 1])
400
- else
401
- log_info(SQL_ROLLBACK)
402
- conn.execute(SQL_ROLLBACK) rescue nil
403
- @transactions.delete(Thread.current)
404
- end
405
- transaction_error(e, *CONVERTED_EXCEPTIONS)
406
- ensure
407
- unless e
408
- begin
409
- if conn.transaction_depth > 1
410
- log_info(SQL_RELEASE_SAVEPOINT % [conn.transaction_depth - 1])
411
- conn.execute(SQL_RELEASE_SAVEPOINT % [conn.transaction_depth - 1])
412
- else
413
- log_info(SQL_COMMIT)
414
- conn.execute(SQL_COMMIT)
415
- @transactions.delete(Thread.current)
416
- end
417
- rescue => e
418
- log_info(e.message)
419
- raise_error(e, :classes=>CONVERTED_EXCEPTIONS)
420
- end
421
- end
422
- conn.transaction_depth -= 1
423
- end
424
- end
425
- end
426
379
 
427
380
  private
428
381
 
@@ -459,6 +412,11 @@ module Sequel
459
412
  "CREATE TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
460
413
  end
461
414
 
415
+ # The errors that the main adapters can raise, depends on the adapter being used
416
+ def database_error_classes
417
+ CONVERTED_EXCEPTIONS
418
+ end
419
+
462
420
  # SQL for dropping a function from the database.
463
421
  def drop_function_sql(name, opts={})
464
422
  "DROP FUNCTION#{' IF EXISTS' if opts[:if_exists]} #{name}#{sql_function_args(opts[:args])}#{' CASCADE' if opts[:cascade]}"
@@ -694,11 +652,12 @@ module Sequel
694
652
 
695
653
  # Insert given values into the database.
696
654
  def insert(*values)
697
- if !@opts[:sql] and !@opts[:disable_insert_returning] and server_version >= 80200
698
- clone(default_server_opts(:sql=>insert_returning_pk_sql(*values))).single_value
655
+ if @opts[:sql]
656
+ execute_insert(insert_sql(*values))
657
+ elsif @opts[:disable_insert_returning] || server_version < 80200
658
+ execute_insert(insert_sql(*values), :table=>opts[:from].first, :values=>values.size == 1 ? values.first : values)
699
659
  else
700
- execute_insert(insert_sql(*values), :table=>opts[:from].first,
701
- :values=>values.size == 1 ? values.first : values)
660
+ clone(default_server_opts(:sql=>insert_returning_pk_sql(*values))).single_value
702
661
  end
703
662
  end
704
663
 
@@ -731,50 +690,47 @@ module Sequel
731
690
 
732
691
  # postgresql 8.2 introduces support for multi-row insert
733
692
  values = values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)
734
- ["INSERT INTO #{source_list(@opts[:from])} (#{identifier_list(columns)}) VALUES #{values}"]
693
+ ["#{insert_sql_base}#{source_list(@opts[:from])} (#{identifier_list(columns)}) VALUES #{values}"]
735
694
  end
736
695
 
737
696
  private
738
697
 
739
698
  # Use the RETURNING clause to return the primary key of the inserted record, if it exists
740
699
  def insert_returning_pk_sql(*values)
741
- pk = db.primary_key(opts[:from].first)
700
+ pk = db.primary_key(opts[:from].first) if opts[:from] && !opts[:from].empty?
742
701
  insert_returning_sql(pk ? Sequel::SQL::Identifier.new(pk) : NULL, *values)
743
702
  end
744
703
 
704
+ # Use a generic blob quoting method, hopefully overridden in one of the subadapter methods
745
705
  def literal_blob(v)
746
706
  "'#{v.gsub(/[\000-\037\047\134\177-\377]/){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"}}'"
747
707
  end
748
708
 
709
+ # PostgreSQL supports fractional timestamps
749
710
  def literal_datetime(v)
750
711
  "#{v.strftime(PG_TIMESTAMP_FORMAT)}.#{sprintf("%06d", (v.sec_fraction * 86400000000).to_i)}'"
751
712
  end
752
713
 
714
+ # PostgreSQL uses FALSE for false values
753
715
  def literal_false
754
716
  BOOL_FALSE
755
717
  end
756
718
 
719
+ # Assume that SQL standard quoting is on, per Sequel's defaults
757
720
  def literal_string(v)
758
721
  "'#{v.gsub("'", "''")}'"
759
722
  end
760
723
 
724
+ # PostgreSQL uses FALSE for false values
761
725
  def literal_true
762
726
  BOOL_TRUE
763
727
  end
764
728
 
729
+ # PostgreSQL supports fractional times
765
730
  def literal_time(v)
766
731
  "#{v.strftime(PG_TIMESTAMP_FORMAT)}.#{sprintf("%06d",v.usec)}'"
767
732
  end
768
733
 
769
- # PostgreSQL is smart and can use parantheses around all datasets to get
770
- # the correct answers.
771
- def select_compounds_sql(sql)
772
- return unless @opts[:compounds]
773
- @opts[:compounds].each do |type, dataset, all|
774
- sql.replace("(#{sql} #{type.to_s.upcase}#{' ALL' if all} #{subselect_sql(dataset)})")
775
- end
776
- end
777
-
778
734
  # The order of clauses in the SELECT SQL statement
779
735
  def select_clause_order
780
736
  SELECT_CLAUSE_ORDER