sequel 2.8.0 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/CHANGELOG +34 -0
  2. data/COPYING +1 -1
  3. data/Rakefile +1 -1
  4. data/bin/sequel +12 -5
  5. data/doc/advanced_associations.rdoc +17 -3
  6. data/lib/sequel_core/adapters/informix.rb +1 -1
  7. data/lib/sequel_core/adapters/postgres.rb +12 -2
  8. data/lib/sequel_core/adapters/shared/mssql.rb +3 -1
  9. data/lib/sequel_core/adapters/shared/mysql.rb +14 -0
  10. data/lib/sequel_core/adapters/shared/oracle.rb +7 -6
  11. data/lib/sequel_core/adapters/shared/postgres.rb +170 -3
  12. data/lib/sequel_core/adapters/shared/progress.rb +1 -1
  13. data/lib/sequel_core/adapters/shared/sqlite.rb +11 -6
  14. data/lib/sequel_core/adapters/sqlite.rb +8 -0
  15. data/lib/sequel_core/dataset/sql.rb +23 -19
  16. data/lib/sequel_core/dataset/unsupported.rb +12 -0
  17. data/lib/sequel_core/schema/sql.rb +3 -1
  18. data/lib/sequel_model.rb +1 -1
  19. data/lib/sequel_model/associations.rb +1 -1
  20. data/lib/sequel_model/base.rb +2 -1
  21. data/lib/sequel_model/dataset_methods.rb +1 -1
  22. data/lib/sequel_model/eager_loading.rb +1 -1
  23. data/lib/sequel_model/exceptions.rb +7 -0
  24. data/lib/sequel_model/record.rb +22 -13
  25. data/lib/sequel_model/validations.rb +8 -4
  26. data/spec/adapters/mysql_spec.rb +17 -0
  27. data/spec/adapters/postgres_spec.rb +69 -0
  28. data/spec/adapters/sqlite_spec.rb +38 -3
  29. data/spec/integration/dataset_test.rb +51 -0
  30. data/spec/integration/schema_test.rb +4 -0
  31. data/spec/sequel_core/core_ext_spec.rb +2 -2
  32. data/spec/sequel_core/dataset_spec.rb +35 -1
  33. data/spec/sequel_core/schema_spec.rb +7 -0
  34. data/spec/sequel_model/association_reflection_spec.rb +13 -13
  35. data/spec/sequel_model/hooks_spec.rb +9 -5
  36. data/spec/sequel_model/validations_spec.rb +1 -1
  37. metadata +3 -2
data/CHANGELOG CHANGED
@@ -1,3 +1,37 @@
1
+ === 2.9.0 (2009-01-12)
2
+
3
+ * Add -L option to sequel command line tool to load all .rb files in the given directory (pkondzior, jeremyevans)
4
+
5
+ * Fix Dataset#destroy for model datasets that can't handle nested queries (jeremyevans)
6
+
7
+ * Improve the error messages in parts of Sequel::Model (jeremyevans, pusewicz)
8
+
9
+ * Much better support for Dataset#{union,except,intersect}, allowing chaining and respecting order (jeremyevans)
10
+
11
+ * Default to logging only WARNING level messages when connecting to PostgreSQL (jeremyevans)
12
+
13
+ * Fix add_foreign_key for MySQL (jeremyevans, aphyr)
14
+
15
+ * Correctly literalize BigDecimal NaN and (+-)Infinity values (#256) (jeremyevans)
16
+
17
+ * Make Sequel raise an Error if you attempt to subclass Sequel::Model before setting up a database connection (jeremyevans)
18
+
19
+ * Add Sequel::BeforeHookFailed exception to be raised when a record fails because a before hook fails (bougyman)
20
+
21
+ * Add Sequel::ValidationFailed exception to be raised when a record fails because a validation fails (bougyman)
22
+
23
+ * Make Database#schema raise an error if given a table that doesn't exist (jeremyevans) (#255)
24
+
25
+ * Make Model#inspect call Model#inspect_values private method for easier overloading (bougyman)
26
+
27
+ * Add methods to create and drop functions, triggers, and procedural languages on PostgreSQL (jeremyevans)
28
+
29
+ * Fix Dataset#count when using UNION, EXCEPT, or INTERSECT (jeremyevans)
30
+
31
+ * Make SQLite keep table's primary key information when dropping columns (jmhodges)
32
+
33
+ * Support dropping indicies on SQLite (jmhodges)
34
+
1
35
  === 2.8.0 (2008-12-05)
2
36
 
3
37
  * Support drop column operations inside a transaction on sqlite (jeremyevans)
data/COPYING CHANGED
@@ -1,5 +1,5 @@
1
1
  Copyright (c) 2007-2008 Sharon Rosner
2
- Copyright (c) 2008 Jeremy Evans
2
+ Copyright (c) 2008-2009 Jeremy Evans
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  of this software and associated documentation files (the "Software"), to
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ require "fileutils"
12
12
  include FileUtils
13
13
 
14
14
  NAME = 'sequel'
15
- VERS = '2.8.0'
15
+ VERS = '2.9.0'
16
16
  CLEAN.include ["**/.*.sw?", "pkg", ".config", "rdoc", "coverage", "www/public/*.html"]
17
17
  RDOC_OPTS = ["--quiet", "--line-numbers", "--inline-source", '--title', \
18
18
  'Sequel: The Database Toolkit for Ruby', '--main', 'README']
data/bin/sequel CHANGED
@@ -10,6 +10,7 @@ env = nil
10
10
  logfile = nil
11
11
  migrate_dir = nil
12
12
  migrate_ver = nil
13
+ load_dir = nil
13
14
 
14
15
  opts = OptionParser.new do |opts|
15
16
  opts.banner = "Sequel: The Database Toolkit for Ruby"
@@ -29,10 +30,6 @@ opts = OptionParser.new do |opts|
29
30
  exit
30
31
  end
31
32
 
32
- opts.on("-l", "--log logfile", "log SQL statements to log file") do |v|
33
- logfile = v
34
- end
35
-
36
33
  opts.on("-e", "--env ENV", "use environment config for database") do |v|
37
34
  env = v
38
35
  end
@@ -41,6 +38,14 @@ opts = OptionParser.new do |opts|
41
38
  echo = true
42
39
  end
43
40
 
41
+ opts.on("-l", "--log logfile", "log SQL statements to log file") do |v|
42
+ logfile = v
43
+ end
44
+
45
+ opts.on("-L", "--load-dir DIR", "loads all *.rb from specifed directory") do |v|
46
+ load_dir = v
47
+ end
48
+
44
49
  opts.on("-m", "--migrate-directory DIR", "run the migrations in directory") do |v|
45
50
  migrate_dir = v
46
51
  end
@@ -48,7 +53,7 @@ opts = OptionParser.new do |opts|
48
53
  opts.on("-M", "--migrate-version VER", "migrate the database to version given") do |v|
49
54
  migrate_ver = Integer(v)
50
55
  end
51
-
56
+
52
57
  opts.on_tail("-v", "--version", "Show version") do
53
58
  class << Gem; attr_accessor :loaded_specs; end
54
59
  begin
@@ -101,6 +106,8 @@ rescue => e
101
106
  exit 1
102
107
  end
103
108
 
109
+ Dir["#{load_dir}/**/*.rb"].each{|f| load(f)} if load_dir
110
+
104
111
  require 'irb'
105
112
  puts "Your database is stored in DB..."
106
113
  IRB.start
@@ -229,7 +229,7 @@ Sequel::Model:
229
229
  @author.books
230
230
 
231
231
  If you use an association other than belongs_to in the associated model, things
232
- are a bit more involved:
232
+ are a bit more involved (has_many :through a has_many association):
233
233
 
234
234
  ActiveRecord:
235
235
 
@@ -317,8 +317,18 @@ firm.invoices.first.client doesn't do another query to get the firm/client.
317
317
 
318
318
  ===Polymorphic Associations
319
319
 
320
- Polymorphic associations are really a design flaw, but if you are stuck with
321
- them, Sequel can handle it.
320
+ Polymorphic associations are really a design flaw. The only advantage
321
+ polymorphic associations offer is that they require fewer join tables.
322
+
323
+ Proof by Reductio ad absurdum: If fewer join tables are preferable, then surely
324
+ fewer tables and columns are preferrable, so you might as well store all of
325
+ your data in a single column in a single table if you think polymorphic
326
+ associations are a good idea.
327
+
328
+ Compelling Argument: Polymorphic associations are more complex than normal
329
+ associations, and they break referential integrity, so the only reason you
330
+ should use them is if you are already stuck with an existing design that
331
+ uses them. You should never use them in new code.
322
332
 
323
333
  ActiveRecord:
324
334
 
@@ -426,6 +436,10 @@ powerful.
426
436
 
427
437
  ===many_to_one/one_to_many not referencing primary key
428
438
 
439
+ This can now be handled easily in Sequel using the :primary_key association
440
+ option. However, this example shows how the association was possible before
441
+ the introduction of that option.
442
+
429
443
  Let's say you have two tables, invoices and clients, where each client is
430
444
  associated with many invoices. However, instead of using the client's primary
431
445
  key, the invoice is associated to the client by name (this is bad database
@@ -37,7 +37,7 @@ module Sequel
37
37
  class Dataset < Sequel::Dataset
38
38
  include UnsupportedIntersectExcept
39
39
 
40
- SELECT_CLAUSE_ORDER = %w'limit distinct columns from join where having group union order'.freeze
40
+ SELECT_CLAUSE_ORDER = %w'limit distinct columns from join where having group compounds order'.freeze
41
41
 
42
42
  def literal(v)
43
43
  case v
@@ -79,8 +79,6 @@ rescue LoadError => e
79
79
  end
80
80
 
81
81
  module Sequel
82
- # Top level module for holding all PostgreSQL-related modules and classes
83
- # for Sequel.
84
82
  module Postgres
85
83
  CONVERTED_EXCEPTIONS << PGError
86
84
 
@@ -105,12 +103,19 @@ module Sequel
105
103
  }
106
104
 
107
105
  @use_iso_date_format = true
106
+ @client_min_messages = :warning
108
107
 
109
108
  # As an optimization, Sequel sets the date style to ISO, so that PostgreSQL provides
110
109
  # the date in a known format that Sequel can parse faster. This can be turned off
111
110
  # if you require a date style other than ISO.
112
111
  metaattr_accessor :use_iso_date_format
113
112
 
113
+ # By default, Sequel sets the minimum level of log messages sent to the client
114
+ # to WARNING, where PostgreSQL uses a default of NOTICE. This is to avoid a lot
115
+ # of mostly useless messages when running migrations, such as a couple of lines
116
+ # for every serial primary key field.
117
+ metaattr_accessor :client_min_messages
118
+
114
119
  # PGconn subclass for connection specific methods used with the
115
120
  # pg, postgres, or postgres-pr driver.
116
121
  class Adapter < ::PGconn
@@ -127,6 +132,11 @@ module Sequel
127
132
  @db.log_info(sql)
128
133
  execute(sql)
129
134
  end
135
+ if cmm = Postgres.client_min_messages
136
+ sql = "SET client_min_messages = '#{cmm.to_s.upcase}'"
137
+ @db.log_info(sql)
138
+ execute(sql)
139
+ end
130
140
  end
131
141
 
132
142
  # Execute the given SQL with this connection. If a block is given,
@@ -37,7 +37,7 @@ module Sequel
37
37
  module DatasetMethods
38
38
  include Dataset::UnsupportedIntersectExcept
39
39
 
40
- SELECT_CLAUSE_ORDER = %w'limit distinct columns from with join where group order having union'.freeze
40
+ SELECT_CLAUSE_ORDER = %w'limit distinct columns from with join where group order having compounds'.freeze
41
41
 
42
42
  def complex_expression_sql(op, args)
43
43
  case op
@@ -54,6 +54,8 @@ module Sequel
54
54
 
55
55
  def literal(v)
56
56
  case v
57
+ when LiteralString
58
+ v
57
59
  when String
58
60
  "N#{super}"
59
61
  when Time
@@ -1,4 +1,10 @@
1
1
  module Sequel
2
+ module Schema
3
+ module SQL
4
+ # Keep default column_references_sql for add_foreign_key support
5
+ alias default_column_references_sql column_references_sql
6
+ end
7
+ end
2
8
  module MySQL
3
9
  # Methods shared by Database instances that connect to MySQL,
4
10
  # currently supported by the native and JDBC adapters.
@@ -15,6 +21,14 @@ module Sequel
15
21
  # drop index cases.
16
22
  def alter_table_sql(table, op)
17
23
  case op[:op]
24
+ when :add_column
25
+ if related = op.delete(:table)
26
+ sql = super(table, op)
27
+ op[:table] = related
28
+ [sql, "ALTER TABLE #{quote_schema_table(table)} ADD FOREIGN KEY (#{quote_identifier(op[:name])})#{default_column_references_sql(op)}"]
29
+ else
30
+ super(table, op)
31
+ end
18
32
  when :rename_column
19
33
  "ALTER TABLE #{quote_schema_table(table)} CHANGE COLUMN #{quote_identifier(op[:name])} #{quote_identifier(op[:new_name])} #{type_literal(op)}"
20
34
  when :set_column_type
@@ -15,7 +15,13 @@ module Sequel
15
15
  module DatasetMethods
16
16
  include Dataset::UnsupportedIntersectExceptAll
17
17
 
18
- SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having union intersect except order limit'.freeze
18
+ SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having compounds order limit'.freeze
19
+
20
+ # Oracle uses MINUS instead of EXCEPT, and doesn't support EXCEPT ALL
21
+ def except(dataset, all = false)
22
+ raise(Sequel::Error, "EXCEPT ALL not supported") if all
23
+ compound_clone(:minus, dataset, all)
24
+ end
19
25
 
20
26
  def empty?
21
27
  db[:dual].where(exists).get(1) == nil
@@ -41,11 +47,6 @@ module Sequel
41
47
  end
42
48
  end
43
49
 
44
- # Oracle uses MINUS instead of EXCEPT, and doesn't support EXCEPT ALL
45
- def select_except_sql(sql, opts)
46
- sql << " MINUS #{opts[:except].sql}" if opts[:except]
47
- end
48
-
49
50
  # Oracle requires a subselect to do limit and offset
50
51
  def select_limit_sql(sql, opts)
51
52
  if limit = opts[:limit]
@@ -1,4 +1,32 @@
1
1
  module Sequel
2
+ # Top level module for holding all PostgreSQL-related modules and classes
3
+ # for Sequel. There are a few module level accessors that are added via
4
+ # metaprogramming. These are:
5
+ # * client_min_messages (only available when using the native adapter) -
6
+ # Change the minimum level of messages that PostgreSQL will send to the
7
+ # the client. The PostgreSQL default is NOTICE, the Sequel default is
8
+ # WARNING. Set to nil to not change the server default.
9
+ # * force_standard_strings - Set to false to not force the use of
10
+ # standard strings
11
+ # * use_iso_date_format (only available when using the native adapter) -
12
+ # Set to false to not change the date format to
13
+ # ISO. This disables one of Sequel's optimizations.
14
+ #
15
+ # Changes in these settings only affect future connections. To make
16
+ # sure that they are applied, they should generally be called right
17
+ # after the Database object is instantiated and before a connection
18
+ # is actually made. For example, to use whatever the server defaults are:
19
+ #
20
+ # DB = Sequel.postgres(...)
21
+ # Sequel::Postgres.client_min_messages = nil
22
+ # Sequel::Postgres.force_standard_strings = false
23
+ # Sequel::Postgres.use_iso_date_format = false
24
+ # # A connection to the server is not made until here
25
+ # DB[:t].all
26
+ #
27
+ # The reason they can't be done earlier is that the Sequel::Postgres
28
+ # module is not loaded until a Database object which uses PostgreSQL
29
+ # is created.
2
30
  module Postgres
3
31
  # Array of exceptions that need to be converted. JDBC
4
32
  # uses NativeExceptions, the native adapter uses PGError.
@@ -7,7 +35,7 @@ module Sequel
7
35
  @force_standard_strings = true
8
36
 
9
37
  # By default, Sequel forces the use of standard strings, so that
10
- # '\\' is interpreted as '\\' and not '\\'. While PostgreSQL defaults
38
+ # '\\' is interpreted as \\ and not \. While PostgreSQL defaults
11
39
  # to interpreting plain strings as extended strings, this will change
12
40
  # in a future version of PostgreSQL. Sequel assumes that SQL standard
13
41
  # strings will be used.
@@ -127,12 +155,122 @@ module Sequel
127
155
  SQL_ROLLBACK = 'ROLLBACK'.freeze
128
156
  SQL_RELEASE_SAVEPOINT = 'RELEASE SAVEPOINT autopoint_%d'.freeze
129
157
  SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
158
+
159
+ # Creates the function in the database. See create_function_sql for arguments.
160
+ def create_function(*args)
161
+ self << create_function_sql(*args)
162
+ end
163
+
164
+ # SQL statement to create database function. Arguments:
165
+ # * name : name of the function to create
166
+ # * definition : string definition of the function, or object file for a dynamically loaded C function.
167
+ # * opts : options hash:
168
+ # * :args : function arguments, can be either a symbol or string specifying a type or an array of 1-3 elements:
169
+ # * element 1 : argument data type
170
+ # * element 2 : argument name
171
+ # * element 3 : argument mode (e.g. in, out, inout)
172
+ # * :behavior : Should be IMMUTABLE, STABLE, or VOLATILE. PostgreSQL assumes VOLATILE by default.
173
+ # * :cost : The estimated cost of the function, used by the query planner.
174
+ # * :language : The language the function uses. SQL is the default.
175
+ # * :link_symbol : For a dynamically loaded see function, the function's link symbol if different from the definition argument.
176
+ # * :returns : The data type returned by the function. If you are using OUT or INOUT argument modes, this is ignored.
177
+ # Otherwise, if this is not specified, void is used by default to specify the function is not supposed to return a value.
178
+ # * :rows : The estimated number of rows the function will return. Only use if the function returns SETOF something.
179
+ # * :security_definer : Makes the privileges of the function the same as the privileges of the user who defined the function instead of
180
+ # the privileges of the user who runs the function. There are security implications when doing this, see the PostgreSQL documentation.
181
+ # * :set : Configuration variables to set while the function is being run, can be a hash or an array of two pairs. search_path is
182
+ # often used here if :security_definer is used.
183
+ # * :strict : Makes the function return NULL when any argument is NULL.
184
+ def create_function_sql(name, definition, opts={})
185
+ args = opts[:args]
186
+ if !opts[:args].is_a?(Array) || !opts[:args].any?{|a| Array(a).length == 3 and %w'OUT INOUT'.include?(a[2].to_s)}
187
+ returns = opts[:returns] || 'void'
188
+ end
189
+ language = opts[:language] || 'SQL'
190
+ <<-END
191
+ CREATE#{' OR REPLACE' if opts[:replace]} FUNCTION #{name}#{sql_function_args(args)}
192
+ #{"RETURNS #{returns}" if returns}
193
+ LANGUAGE #{language}
194
+ #{opts[:behavior].to_s.upcase if opts[:behavior]}
195
+ #{'STRICT' if opts[:strict]}
196
+ #{'SECURITY DEFINER' if opts[:security_definer]}
197
+ #{"COST #{opts[:cost]}" if opts[:cost]}
198
+ #{"ROWS #{opts[:rows]}" if opts[:rows]}
199
+ #{opts[:set].map{|k,v| " SET #{k} = #{v}"}.join("\n") if opts[:set]}
200
+ AS #{literal(definition.to_s)}#{", #{literal(opts[:link_symbol].to_s)}" if opts[:link_symbol]}
201
+ END
202
+ end
203
+
204
+ # Create the procedural language in the database. See create_language_sql for arguments.
205
+ def create_language(*args)
206
+ self << create_language_sql(*args)
207
+ end
208
+
209
+ # SQL for creating a procedural language. Arguments:
210
+ # * name : Name of the procedural language (e.g. plpgsql)
211
+ # * opts : options hash:
212
+ # * :handler : The name of a previously registered function used as a call handler for this language.
213
+ # * :trusted : Marks the language being created as trusted, allowing unprivileged users to create functions using this language.
214
+ # * :validator : The name of previously registered function used as a validator of functions defined in this language.
215
+ def create_language_sql(name, opts={})
216
+ "CREATE#{' TRUSTED' if opts[:trusted]} LANGUAGE #{name}#{" HANDLER #{opts[:handler]}" if opts[:handler]}#{" VALIDATOR #{opts[:validator]}" if opts[:validator]}"
217
+ end
218
+
219
+ # Create a trigger in the database. See create_trigger_sql for arguments.
220
+ def create_trigger(*args)
221
+ self << create_trigger_sql(*args)
222
+ end
223
+
224
+ # SQL for creating a database trigger. Arguments:
225
+ # * table : the table on which this trigger operates
226
+ # * name : the name of this trigger
227
+ # * function : the function to call for this trigger, which should return type trigger.
228
+ # * opts : options hash:
229
+ # * :after : Calls the trigger after execution instead of before.
230
+ # * :args : An argument or array of arguments to pass to the function.
231
+ # * :each_row : Calls the trigger for each row instead of for each statement.
232
+ # * :events : Can be :insert, :update, :delete, or an array of any of those. Calls the trigger whenever that type of statement is used. By default,
233
+ # the trigger is called for insert, update, or delete.
234
+ def create_trigger_sql(table, name, function, opts={})
235
+ events = opts[:events] ? Array(opts[:events]) : [:insert, :update, :delete]
236
+ whence = opts[:after] ? 'AFTER' : 'BEFORE'
237
+ "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(', ')})"
238
+ end
130
239
 
131
240
  # The default schema to use if none is specified (default: public)
132
241
  def default_schema
133
242
  @default_schema ||= :public
134
243
  end
135
244
 
245
+ # Drops the function from the database. See drop_function_sql for arguments.
246
+ def drop_function(*args)
247
+ self << drop_function_sql(*args)
248
+ end
249
+
250
+ # SQL for dropping a function from the database. Arguments:
251
+ # * name : name of the function to drop
252
+ # * opts : options hash:
253
+ # * :args : The arguments for the function. See create_function_sql.
254
+ # * :cascade : Drop other objects depending on this function.
255
+ # * :if_exists : Don't raise an error if the function doesn't exist.
256
+ def drop_function_sql(name, opts={})
257
+ "DROP FUNCTION#{' IF EXISTS' if opts[:if_exists]} #{name}#{sql_function_args(opts[:args])}#{' CASCADE' if opts[:cascade]}"
258
+ end
259
+
260
+ # Drops a procedural language from the database. See drop_language_sql for arguments.
261
+ def drop_language(*args)
262
+ self << drop_language_sql(*args)
263
+ end
264
+
265
+ # SQL for dropping a procedural language from the database. Arguments:
266
+ # * name : name of the procedural language to drop
267
+ # * opts : options hash:
268
+ # * :cascade : Drop other objects depending on this function.
269
+ # * :if_exists : Don't raise an error if the function doesn't exist.
270
+ def drop_language_sql(name, opts={})
271
+ "DROP LANGUAGE#{' IF EXISTS' if opts[:if_exists]} #{name}#{' CASCADE' if opts[:cascade]}"
272
+ end
273
+
136
274
  # Remove the cached entries for primary keys and sequences when dropping a table.
137
275
  def drop_table(*names)
138
276
  names.each do |name|
@@ -148,6 +286,21 @@ module Sequel
148
286
  "DROP TABLE #{quote_schema_table(name)} CASCADE"
149
287
  end
150
288
 
289
+ # Drops a trigger from the database. See drop_trigger_sql for arguments.
290
+ def drop_trigger(*args)
291
+ self << drop_trigger_sql(*args)
292
+ end
293
+
294
+ # SQL for dropping a trigger from the database. Arguments:
295
+ # * table : table from which to drop the trigger
296
+ # * name : name of the trigger to drop
297
+ # * opts : options hash:
298
+ # * :cascade : Drop other objects depending on this function.
299
+ # * :if_exists : Don't raise an error if the function doesn't exist.
300
+ def drop_trigger_sql(table, name, opts={})
301
+ "DROP TRIGGER#{' IF EXISTS' if opts[:if_exists]} #{name} ON #{quote_schema_table(table)}#{' CASCADE' if opts[:cascade]}"
302
+ end
303
+
151
304
  # PostgreSQL specific index SQL.
152
305
  def index_definition_sql(table_name, index)
153
306
  index_name = index[:name] || default_index_name(table_name, index[:columns])
@@ -375,6 +528,11 @@ module Sequel
375
528
  end
376
529
  ds
377
530
  end
531
+
532
+ # Turns an array of argument specifiers into an SQL fragment used for function arguments. See create_function_sql.
533
+ def sql_function_args(args)
534
+ "(#{Array(args).map{|a| Array(a).reverse.join(' ')}.join(', ')})"
535
+ end
378
536
  end
379
537
 
380
538
  # Instance methods for datasets that connect to a PostgreSQL database.
@@ -394,7 +552,7 @@ module Sequel
394
552
  QUERY_PLAN = 'QUERY PLAN'.to_sym
395
553
  ROW_EXCLUSIVE = 'ROW EXCLUSIVE'.freeze
396
554
  ROW_SHARE = 'ROW SHARE'.freeze
397
- SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having intersect union except order limit lock'.freeze
555
+ SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having compounds order limit lock'.freeze
398
556
  SHARE = 'SHARE'.freeze
399
557
  SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
400
558
  SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
@@ -476,7 +634,7 @@ module Sequel
476
634
  when LiteralString
477
635
  v
478
636
  when SQL::Blob
479
- db.synchronize{|c| "E'#{c.escape_bytea(v)}'"}
637
+ db.synchronize{|c| "'#{c.escape_bytea(v)}'"}
480
638
  when String
481
639
  db.synchronize{|c| "'#{c.escape_string(v)}'"}
482
640
  when Time
@@ -526,6 +684,15 @@ module Sequel
526
684
  pk = db.primary_key(opts[:from].first)
527
685
  insert_returning_sql(pk ? Sequel::SQL::Identifier.new(pk) : 'NULL'.lit, *values)
528
686
  end
687
+
688
+ # PostgreSQL is smart and can use parantheses around all datasets to get
689
+ # the correct answers.
690
+ def select_compounds_sql(sql, opts)
691
+ return unless opts[:compounds]
692
+ opts[:compounds].each do |type, dataset, all|
693
+ sql.replace("(#{sql} #{type.to_s.upcase}#{' ALL' if all} #{subselect_sql(dataset)})")
694
+ end
695
+ end
529
696
 
530
697
  # The order of clauses in the SELECT SQL statement
531
698
  def select_clause_order