sequel 3.0.0 → 3.1.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 (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