sequel 2.7.1 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/CHANGELOG +56 -0
  2. data/README +1 -0
  3. data/Rakefile +1 -1
  4. data/lib/sequel_core.rb +9 -16
  5. data/lib/sequel_core/adapters/ado.rb +6 -15
  6. data/lib/sequel_core/adapters/db2.rb +8 -10
  7. data/lib/sequel_core/adapters/dbi.rb +6 -4
  8. data/lib/sequel_core/adapters/informix.rb +21 -22
  9. data/lib/sequel_core/adapters/jdbc.rb +69 -10
  10. data/lib/sequel_core/adapters/jdbc/postgresql.rb +1 -0
  11. data/lib/sequel_core/adapters/mysql.rb +81 -13
  12. data/lib/sequel_core/adapters/odbc.rb +32 -4
  13. data/lib/sequel_core/adapters/openbase.rb +6 -5
  14. data/lib/sequel_core/adapters/oracle.rb +23 -7
  15. data/lib/sequel_core/adapters/postgres.rb +42 -32
  16. data/lib/sequel_core/adapters/shared/mssql.rb +37 -62
  17. data/lib/sequel_core/adapters/shared/mysql.rb +22 -7
  18. data/lib/sequel_core/adapters/shared/oracle.rb +27 -48
  19. data/lib/sequel_core/adapters/shared/postgres.rb +64 -43
  20. data/lib/sequel_core/adapters/shared/progress.rb +31 -0
  21. data/lib/sequel_core/adapters/shared/sqlite.rb +15 -4
  22. data/lib/sequel_core/adapters/sqlite.rb +6 -14
  23. data/lib/sequel_core/connection_pool.rb +47 -13
  24. data/lib/sequel_core/database.rb +60 -35
  25. data/lib/sequel_core/database/schema.rb +4 -4
  26. data/lib/sequel_core/dataset.rb +12 -3
  27. data/lib/sequel_core/dataset/convenience.rb +4 -13
  28. data/lib/sequel_core/dataset/prepared_statements.rb +30 -28
  29. data/lib/sequel_core/dataset/sql.rb +144 -85
  30. data/lib/sequel_core/dataset/stored_procedures.rb +75 -0
  31. data/lib/sequel_core/dataset/unsupported.rb +31 -0
  32. data/lib/sequel_core/exceptions.rb +6 -0
  33. data/lib/sequel_core/schema/generator.rb +4 -3
  34. data/lib/sequel_core/schema/sql.rb +41 -23
  35. data/lib/sequel_core/sql.rb +29 -1
  36. data/lib/sequel_model/associations.rb +1 -1
  37. data/lib/sequel_model/record.rb +31 -28
  38. data/spec/adapters/mysql_spec.rb +37 -4
  39. data/spec/adapters/oracle_spec.rb +26 -4
  40. data/spec/adapters/sqlite_spec.rb +7 -0
  41. data/spec/integration/prepared_statement_test.rb +24 -0
  42. data/spec/integration/schema_test.rb +1 -1
  43. data/spec/sequel_core/connection_pool_spec.rb +49 -2
  44. data/spec/sequel_core/core_sql_spec.rb +9 -2
  45. data/spec/sequel_core/database_spec.rb +64 -14
  46. data/spec/sequel_core/dataset_spec.rb +105 -7
  47. data/spec/sequel_core/schema_spec.rb +40 -12
  48. data/spec/sequel_core/spec_helper.rb +1 -0
  49. data/spec/sequel_model/spec_helper.rb +1 -0
  50. metadata +6 -3
@@ -14,7 +14,7 @@ module Sequel
14
14
  include Schema::SQL
15
15
 
16
16
  # Array of supported database adapters
17
- ADAPTERS = %w'ado db2 dbi informix jdbc mysql odbc odbc_mssql openbase oracle postgres sqlite'.collect{|x| x.to_sym}
17
+ ADAPTERS = %w'ado db2 dbi informix jdbc mysql odbc openbase oracle postgres sqlite'.collect{|x| x.to_sym}
18
18
 
19
19
  SQL_BEGIN = 'BEGIN'.freeze
20
20
  SQL_COMMIT = 'COMMIT'.freeze
@@ -29,6 +29,12 @@ module Sequel
29
29
  # Whether to quote identifiers (columns and tables) by default
30
30
  @@quote_identifiers = true
31
31
 
32
+ # Whether to upcase identifiers (columns and tables) by default
33
+ @@upcase_identifiers = nil
34
+
35
+ # The default schema to use
36
+ attr_accessor :default_schema
37
+
32
38
  # Array of SQL loggers to use for this database
33
39
  attr_accessor :loggers
34
40
 
@@ -38,26 +44,41 @@ module Sequel
38
44
  # The connection pool for this database
39
45
  attr_reader :pool
40
46
 
41
- # Whether to quote identifiers (columns and tables) for this database
42
- attr_writer :quote_identifiers
43
-
44
47
  # The prepared statement objects for this database, keyed by name
45
48
  attr_reader :prepared_statements
46
49
 
50
+ # Whether to quote identifiers (columns and tables) for this database
51
+ attr_writer :quote_identifiers
52
+
53
+ # Whether to upcase identifiers (columns and tables) for this database
54
+ attr_writer :upcase_identifiers
55
+
47
56
  # Constructs a new instance of a database connection with the specified
48
57
  # options hash.
49
58
  #
50
59
  # Sequel::Database is an abstract class that is not useful by itself.
60
+ #
61
+ # Takes the following options:
62
+ # * :default_schema : The default schema to use, should generally be nil
63
+ # * :disconnection_proc: A proc used to disconnect the connection.
64
+ # * :loggers : An array of loggers to use.
65
+ # * :quote_identifiers : Whether to quote identifiers
66
+ # * :single_threaded : Whether to use a single-threaded connection pool
67
+ #
68
+ # All options given are also passed to the ConnectionPool. If a block
69
+ # is given, it is used as the connection_proc for the ConnectionPool.
51
70
  def initialize(opts = {}, &block)
52
71
  @opts = opts
53
72
 
54
73
  @quote_identifiers = opts.include?(:quote_identifiers) ? opts[:quote_identifiers] : @@quote_identifiers
55
74
  @single_threaded = opts.include?(:single_threaded) ? opts[:single_threaded] : @@single_threaded
56
75
  @schemas = nil
76
+ @default_schema = opts[:default_schema]
57
77
  @prepared_statements = {}
58
78
  @transactions = []
59
79
  @pool = (@single_threaded ? SingleThreadedPool : ConnectionPool).new(connection_pool_default_options.merge(opts), &block)
60
80
  @pool.connection_proc = proc{|server| connect(server)} unless block
81
+ @pool.disconnection_proc = proc{|conn| disconnect_connection(conn)} unless opts[:disconnection_proc]
61
82
 
62
83
  @loggers = Array(opts[:logger]) + Array(opts[:loggers])
63
84
  ::Sequel::DATABASES.push(self)
@@ -142,6 +163,12 @@ module Sequel
142
163
  @@single_threaded = value
143
164
  end
144
165
 
166
+ # Sets the default quote_identifiers mode for new databases.
167
+ # See Sequel.quote_identifiers=.
168
+ def self.upcase_identifiers=(value)
169
+ @@upcase_identifiers = value
170
+ end
171
+
145
172
  ### Private Class Methods ###
146
173
 
147
174
  # Sets the adapter scheme for the Database class. Call this method in
@@ -212,10 +239,10 @@ module Sequel
212
239
  ds = Sequel::Dataset.new(self)
213
240
  end
214
241
 
215
- # Disconnects from the database. This method should be overridden by
216
- # descendants.
242
+ # Disconnects all available connections from the connection pool. If any
243
+ # connections are currently in use, they will not be disconnected.
217
244
  def disconnect
218
- raise NotImplementedError, "#disconnect should be overridden by adapters"
245
+ pool.disconnect
219
246
  end
220
247
 
221
248
  # Executes the given SQL. This method should be overridden in descendants.
@@ -250,9 +277,8 @@ module Sequel
250
277
  # DB.fetch('SELECT * FROM items WHERE name = ?', my_name).print
251
278
  def fetch(sql, *args, &block)
252
279
  ds = dataset
253
- sql = sql.gsub('?') {|m| ds.literal(args.shift)}
254
- ds.opts[:sql] = sql
255
- ds.fetch_rows(sql, &block) if block
280
+ ds.opts[:sql] = Sequel::SQL::PlaceholderLiteralString.new(sql, args)
281
+ ds.each(&block) if block
256
282
  ds
257
283
  end
258
284
  alias_method :>>, :fetch
@@ -335,17 +361,19 @@ module Sequel
335
361
  @pool.hold(server || :default, &block)
336
362
  end
337
363
 
338
- # Returns true if a table with the given name exists.
364
+ # Returns true if a table with the given name exists. This requires a query
365
+ # to the database unless this database object already has the schema for
366
+ # the given table name.
339
367
  def table_exists?(name)
340
- begin
341
- if respond_to?(:tables)
342
- tables.include?(name.to_sym)
343
- else
368
+ if @schemas && @schemas[name]
369
+ true
370
+ else
371
+ begin
344
372
  from(name).first
345
373
  true
374
+ rescue
375
+ false
346
376
  end
347
- rescue
348
- false
349
377
  end
350
378
  end
351
379
 
@@ -458,6 +486,12 @@ module Sequel
458
486
  end
459
487
  end
460
488
 
489
+ # Returns true if the database upcases identifiers.
490
+ def upcase_identifiers?
491
+ return @upcase_identifiers unless @upcase_identifiers.nil?
492
+ @upcase_identifiers = @opts.include?(:upcase_identifiers) ? @opts[:upcase_identifiers] : (@@upcase_identifiers.nil? ? upcase_identifiers_default : @@upcase_identifiers)
493
+ end
494
+
461
495
  # Returns the URI identifying the database.
462
496
  # This method can raise an error if the database used options
463
497
  # instead of a connection string.
@@ -496,11 +530,6 @@ module Sequel
496
530
  {}
497
531
  end
498
532
 
499
- # Sequel doesn't use database schema's by default.
500
- def default_schema
501
- nil
502
- end
503
-
504
533
  # SQL to ROLLBACK a transaction.
505
534
  def rollback_transaction_sql
506
535
  SQL_ROLLBACK
@@ -520,19 +549,7 @@ module Sequel
520
549
 
521
550
  # Split the schema information from the table
522
551
  def schema_and_table(table_name)
523
- case table_name
524
- when Symbol
525
- s, t, a = dataset.send(:split_symbol, table_name)
526
- [s||default_schema, t]
527
- when SQL::QualifiedIdentifier
528
- [table_name.table, table_name.column]
529
- when SQL::Identifier
530
- [default_schema, table_name.value]
531
- when String
532
- [default_schema, table_name]
533
- else
534
- raise Error, 'table_name should be a Symbol, SQL::QualifiedIdentifier, SQL::Identifier, or String'
535
- end
552
+ schema_utility_dataset.schema_and_table(table_name)
536
553
  end
537
554
 
538
555
  # Return the options for the given server by merging the generic
@@ -559,6 +576,14 @@ module Sequel
559
576
  def transaction_error(e, *classes)
560
577
  raise_error(e, :classes=>classes) unless Error::Rollback === e
561
578
  end
579
+
580
+ # Sets whether to upcase identifiers by default. Should be
581
+ # overridden in subclasses for databases that fold unquoted
582
+ # identifiers to lower case instead of uppercase, such as
583
+ # MySQL, PostgreSQL, and SQLite.
584
+ def upcase_identifiers_default
585
+ true
586
+ end
562
587
  end
563
588
  end
564
589
 
@@ -40,7 +40,7 @@ module Sequel
40
40
  #
41
41
  # See Schema::AlterTableGenerator.
42
42
  def alter_table(name, generator=nil, &block)
43
- @schemas.delete(name.to_sym) if @schemas
43
+ remove_cached_schema(name)
44
44
  generator ||= Schema::AlterTableGenerator.new(self, &block)
45
45
  alter_table_sql_list(name, generator.operations).flatten.each {|sql| execute_ddl(sql)}
46
46
  end
@@ -71,7 +71,7 @@ module Sequel
71
71
  # DB.create_or_replace_view(:cheap_items, "SELECT * FROM items WHERE price < 100")
72
72
  # DB.create_or_replace_view(:ruby_items, DB[:items].filter(:category => 'ruby'))
73
73
  def create_or_replace_view(name, source)
74
- @schemas.delete(name.to_sym) if @schemas
74
+ remove_cached_schema(name)
75
75
  source = source.sql if source.is_a?(Dataset)
76
76
  execute_ddl("CREATE OR REPLACE VIEW #{quote_identifier(name)} AS #{source}")
77
77
  end
@@ -109,7 +109,7 @@ module Sequel
109
109
  # DB.drop_table(:posts, :comments)
110
110
  def drop_table(*names)
111
111
  names.each do |n|
112
- @schemas.delete(n.is_a?(String) ? n.to_sym : n) if @schemas
112
+ remove_cached_schema(n)
113
113
  execute_ddl(drop_table_sql(n))
114
114
  end
115
115
  end
@@ -119,7 +119,7 @@ module Sequel
119
119
  # DB.drop_view(:cheap_items)
120
120
  def drop_view(*names)
121
121
  names.each do |n|
122
- @schemas.delete(n.to_sym) if @schemas
122
+ remove_cached_schema(n)
123
123
  execute_ddl("DROP VIEW #{quote_identifier(n)}")
124
124
  end
125
125
  end
@@ -1,4 +1,4 @@
1
- %w'callback convenience pagination prepared_statements query schema sql'.each do |f|
1
+ %w'callback convenience pagination prepared_statements query schema sql unsupported'.each do |f|
2
2
  require "sequel_core/dataset/#{f}"
3
3
  end
4
4
 
@@ -75,13 +75,16 @@ module Sequel
75
75
  # The hash of options for this dataset, keys are symbols.
76
76
  attr_accessor :opts
77
77
 
78
+ # Whether to quote identifiers for this dataset
79
+ attr_writer :quote_identifiers
80
+
78
81
  # The row_proc for this database, should be a Proc that takes
79
82
  # a single hash argument and returns the object you want to
80
83
  # fetch_rows to return.
81
84
  attr_accessor :row_proc
82
85
 
83
- # Whether to quote identifiers for this dataset
84
- attr_writer :quote_identifiers
86
+ # Whether to upcase identifiers for this dataset
87
+ attr_writer :upcase_identifiers
85
88
 
86
89
  # Constructs a new instance of a dataset with an associated database and
87
90
  # options. Datasets are usually constructed by invoking Database methods:
@@ -97,6 +100,7 @@ module Sequel
97
100
  def initialize(db, opts = nil)
98
101
  @db = db
99
102
  @quote_identifiers = db.quote_identifiers? if db.respond_to?(:quote_identifiers?)
103
+ @upcase_identifiers = db.upcase_identifiers? if db.respond_to?(:upcase_identifiers?)
100
104
  @opts = opts || {}
101
105
  @row_proc = nil
102
106
  @transform = nil
@@ -415,6 +419,11 @@ module Sequel
415
419
  end
416
420
  end
417
421
 
422
+ # Whether this dataset upcases identifiers.
423
+ def upcase_identifiers?
424
+ @upcase_identifiers
425
+ end
426
+
418
427
  # Updates values for the dataset. The returned value is generally the
419
428
  # number of rows updated, but that is adapter dependent.
420
429
  def update(*args)
@@ -198,20 +198,11 @@ module Sequel
198
198
  # if the dataset has fixed SQL or selects from another dataset
199
199
  # or more than one table.
200
200
  def table_exists?
201
- if @opts[:sql]
202
- raise Sequel::Error, "this dataset has fixed SQL"
203
- end
204
-
205
- if @opts[:from].size != 1
206
- raise Sequel::Error, "this dataset selects from multiple sources"
207
- end
208
-
201
+ raise(Sequel::Error, "this dataset has fixed SQL") if @opts[:sql]
202
+ raise(Sequel::Error, "this dataset selects from multiple sources") if @opts[:from].size != 1
209
203
  t = @opts[:from].first
210
- if t.is_a?(Dataset)
211
- raise Sequel::Error, "this dataset selects from a sub query"
212
- end
213
-
214
- @db.table_exists?(t.to_sym)
204
+ raise(Sequel::Error, "this dataset selects from a sub query") if t.is_a?(Dataset)
205
+ @db.table_exists?(t)
215
206
  end
216
207
 
217
208
  # Returns a string in CSV format containing the dataset records. By
@@ -25,11 +25,10 @@ module Sequel
25
25
  end
26
26
 
27
27
  # Override the given *_sql method based on the type, and
28
- # cache the result of the sql. This requires that the object
29
- # that includes this module implement prepared_args_hash.
28
+ # cache the result of the sql.
30
29
  def prepared_sql
31
30
  return @prepared_sql if @prepared_sql
32
- @prepared_args = prepared_args_hash
31
+ @prepared_args ||= []
33
32
  @prepared_sql = super
34
33
  meta_def("#{sql_query_type}_sql"){|*args| prepared_sql}
35
34
  @prepared_sql
@@ -37,6 +36,7 @@ module Sequel
37
36
 
38
37
  private
39
38
 
39
+ # The type of query (:select, :insert, :delete, :update).
40
40
  def sql_query_type
41
41
  SQL_QUERY_TYPE[@prepared_type]
42
42
  end
@@ -51,7 +51,7 @@ module Sequel
51
51
  module PreparedStatementMethods
52
52
  PLACEHOLDER_RE = /\A\$(.*)\z/
53
53
 
54
- # The type of prepared statement, should be one of :select,
54
+ # The type of prepared statement, should be one of :select, :first,
55
55
  # :insert, :update, or :delete
56
56
  attr_accessor :prepared_type
57
57
 
@@ -136,6 +136,15 @@ module Sequel
136
136
  def prepared_arg(k)
137
137
  @prepared_args[k]
138
138
  end
139
+
140
+ # Use a clone of the dataset extended with prepared statement
141
+ # support and using the same argument hash so that you can use
142
+ # bind variables/prepared arguments in subselects.
143
+ def subselect_sql(ds)
144
+ ps = ds.prepare(:select)
145
+ ps.prepared_args = prepared_args
146
+ ps.prepared_sql
147
+ end
139
148
  end
140
149
 
141
150
  # Default implementation for an argument mapper that uses
@@ -151,26 +160,15 @@ module Sequel
151
160
  # Keys in the input hash that are used more than once in the query
152
161
  # have multiple entries in the output array.
153
162
  def map_to_prepared_args(hash)
154
- array = []
155
- @prepared_args.each{|k,vs| vs.each{|v| array[v] = hash[k]}}
156
- array
163
+ @prepared_args.map{|v| hash[v]}
157
164
  end
158
165
 
159
166
  private
160
167
 
161
- # Uses a separate array of each key, holding the positions
162
- # in the output array (necessary to support arguments
163
- # that are used more than once).
164
- def prepared_args_hash
165
- Hash.new{|h,k| h[k] = Array.new}
166
- end
167
-
168
168
  # Associates the argument with name k with the next position in
169
169
  # the output array.
170
170
  def prepared_arg(k)
171
- @max_prepared_arg ||= 0
172
- @prepared_args[k] << @max_prepared_arg
173
- @max_prepared_arg += 1
171
+ @prepared_args << k
174
172
  prepared_arg_placeholder
175
173
  end
176
174
  end
@@ -181,7 +179,7 @@ module Sequel
181
179
  # insert or update (if one of those types is used),
182
180
  # which may contain placeholders.
183
181
  def call(type, bind_variables={}, values=nil)
184
- to_prepared_statement(type, values).call(bind_variables)
182
+ prepare(type, nil, values).call(bind_variables)
185
183
  end
186
184
 
187
185
  # Prepare an SQL statement for later execution. This returns
@@ -193,17 +191,13 @@ module Sequel
193
191
  # ps = prepare(:select, :select_by_name)
194
192
  # ps.call(:name=>'Blah')
195
193
  # db.call(:select_by_name, :name=>'Blah')
196
- def prepare(type, name, values=nil)
197
- db.prepared_statements[name] = to_prepared_statement(type, values)
194
+ def prepare(type, name=nil, values=nil)
195
+ ps = to_prepared_statement(type, values)
196
+ db.prepared_statements[name] = ps if name
197
+ ps
198
198
  end
199
199
 
200
- private
201
-
202
- # The argument placeholder. Most databases used unnumbered
203
- # arguments with question marks, so that is the default.
204
- def prepared_arg_placeholder
205
- PREPARED_ARG_PLACEHOLDER
206
- end
200
+ protected
207
201
 
208
202
  # Return a cloned copy of the current dataset extended with
209
203
  # PreparedStatementMethods, setting the type and modify values.
@@ -214,5 +208,13 @@ module Sequel
214
208
  ps.prepared_modify_values = values
215
209
  ps
216
210
  end
211
+
212
+ private
213
+
214
+ # The argument placeholder. Most databases used unnumbered
215
+ # arguments with question marks, so that is the default.
216
+ def prepared_arg_placeholder
217
+ PREPARED_ARG_PLACEHOLDER
218
+ end
217
219
  end
218
- end
220
+ end
@@ -12,6 +12,7 @@ module Sequel
12
12
  NULL = "NULL".freeze
13
13
  QUESTION_MARK = '?'.freeze
14
14
  STOCK_COUNT_OPTS = {:select => ["COUNT(*)".lit], :order => nil}.freeze
15
+ SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having intersect union except order limit'.freeze
15
16
  TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S'".freeze
16
17
  TWO_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::TWO_ARITY_OPERATORS
17
18
  WILDCARD = '*'.freeze
@@ -46,7 +47,7 @@ module Sequel
46
47
 
47
48
  # SQL fragment for specifying all columns in a given table.
48
49
  def column_all_sql(ca)
49
- "#{quote_identifier(ca.table)}.*"
50
+ "#{quote_schema_table(ca.table)}.*"
50
51
  end
51
52
 
52
53
  # SQL fragment for complex expressions
@@ -80,9 +81,7 @@ module Sequel
80
81
  def delete_sql(opts = nil)
81
82
  opts = opts ? @opts.merge(opts) : @opts
82
83
 
83
- if sql = opts[:sql]
84
- return sql
85
- end
84
+ return static_sql(opts[:sql]) if opts[:sql]
86
85
 
87
86
  if opts[:group]
88
87
  raise Error::InvalidOperation, "Grouped datasets cannot be deleted from"
@@ -122,12 +121,12 @@ module Sequel
122
121
  clone(clause => cond)
123
122
  end
124
123
 
125
- # Returns an EXISTS clause for the dataset.
124
+ # Returns an EXISTS clause for the dataset as a LiteralString.
126
125
  #
127
126
  # DB.select(1).where(DB[:items].exists).sql
128
127
  # #=> "SELECT 1 WHERE EXISTS (SELECT * FROM items)"
129
128
  def exists(opts = nil)
130
- "EXISTS (#{select_sql(opts)})"
129
+ "EXISTS (#{select_sql(opts)})".lit
131
130
  end
132
131
 
133
132
  # Returns a copy of the dataset with the given conditions imposed upon it.
@@ -270,9 +269,7 @@ module Sequel
270
269
  # dataset.insert_sql(:a => 1, :b => 2) #=>
271
270
  # 'INSERT INTO items (a, b) VALUES (1, 2)'
272
271
  def insert_sql(*values)
273
- if sql = @opts[:sql]
274
- return sql
275
- end
272
+ return static_sql(@opts[:sql]) if @opts[:sql]
276
273
 
277
274
  from = source_list(@opts[:from])
278
275
  case values.size
@@ -494,7 +491,7 @@ module Sequel
494
491
  when Date
495
492
  v.strftime(DATE_FORMAT)
496
493
  when Dataset
497
- "(#{v.sql})"
494
+ "(#{subselect_sql(v)})"
498
495
  else
499
496
  raise Error, "can't express #{v.inspect} as a SQL literal"
500
497
  end
@@ -555,31 +552,43 @@ module Sequel
555
552
  "#{literal(oe.expression)} #{oe.descending ? 'DESC' : 'ASC'}"
556
553
  end
557
554
 
555
+ # SQL fragment for a literal string with placeholders
556
+ def placeholder_literal_string_sql(pls)
557
+ args = pls.args.dup
558
+ s = pls.str.gsub(QUESTION_MARK){literal(args.shift)}
559
+ s = "(#{s})" if pls.parens
560
+ s
561
+ end
562
+
558
563
  # SQL fragment for the qualifed identifier, specifying
559
564
  # a table and a column (or schema and table).
560
565
  def qualified_identifier_sql(qcr)
561
- [qcr.table, qcr.column].map{|x| x.is_one_of?(SQL::QualifiedIdentifier, SQL::Identifier) ? literal(x) : quote_identifier(x)}.join('.')
566
+ [qcr.table, qcr.column].map{|x| x.is_one_of?(SQL::QualifiedIdentifier, SQL::Identifier, Symbol) ? literal(x) : quote_identifier(x)}.join('.')
562
567
  end
563
568
 
564
569
  # Adds quoting to identifiers (columns and tables). If identifiers are not
565
570
  # being quoted, returns name as a string. If identifiers are being quoted
566
571
  # quote the name with quoted_identifier.
567
572
  def quote_identifier(name)
568
- quote_identifiers? ? quoted_identifier(name) : name.to_s
573
+ name = name.to_s
574
+ name = name.upcase if upcase_identifiers?
575
+ name = quoted_identifier(name) if quote_identifiers?
576
+ name
569
577
  end
570
578
  alias_method :quote_column_ref, :quote_identifier
571
579
 
580
+ # Separates the schema from the table and returns a string with them
581
+ # quoted (if quoting identifiers)
582
+ def quote_schema_table(table)
583
+ schema, table = schema_and_table(table)
584
+ "#{"#{quote_identifier(schema)}." if schema}#{quote_identifier(table)}"
585
+ end
586
+
572
587
  # This method quotes the given name with the SQL standard double quote.
573
- # It uppercases the name given to conform with the SQL standard. This
574
588
  # should be overridden by subclasses to provide quoting not matching the
575
- # SQL standard, such as backtick (used by MySQL and SQLite), or where
576
- # lowercase is the default for unquoted identifiers (PostgreSQL).
577
- #
578
- # If you are using a database such as Oracle that defaults to uppercase
579
- # but you are using lower case identifiers, you should override this
580
- # method to not upcase the name for those identifiers.
589
+ # SQL standard, such as backtick (used by MySQL and SQLite).
581
590
  def quoted_identifier(name)
582
- "\"#{name.to_s.upcase}\""
591
+ "\"#{name}\""
583
592
  end
584
593
 
585
594
  # Returns a copy of the dataset with the order reversed. If no order is
@@ -589,6 +598,24 @@ module Sequel
589
598
  end
590
599
  alias_method :reverse, :reverse_order
591
600
 
601
+ # Split the schema information from the table
602
+ def schema_and_table(table_name)
603
+ sch = db.default_schema if db
604
+ case table_name
605
+ when Symbol
606
+ s, t, a = split_symbol(table_name)
607
+ [s||sch, t]
608
+ when SQL::QualifiedIdentifier
609
+ [table_name.table, table_name.column]
610
+ when SQL::Identifier
611
+ [sch, table_name.value]
612
+ when String
613
+ [sch, table_name]
614
+ else
615
+ raise Error, 'table_name should be a Symbol, SQL::QualifiedIdentifier, SQL::Identifier, or String'
616
+ end
617
+ end
618
+
592
619
  # Returns a copy of the dataset with the columns selected changed
593
620
  # to the given columns.
594
621
  def select(*columns)
@@ -610,63 +637,9 @@ module Sequel
610
637
  # options.
611
638
  def select_sql(opts = nil)
612
639
  opts = opts ? @opts.merge(opts) : @opts
613
-
614
- if sql = opts[:sql]
615
- return sql
616
- end
617
-
618
- columns = opts[:select]
619
- select_columns = columns ? column_list(columns) : WILDCARD
620
-
621
- if distinct = opts[:distinct]
622
- distinct_clause = distinct.empty? ? "DISTINCT" : "DISTINCT ON (#{expression_list(distinct)})"
623
- sql = "SELECT #{distinct_clause} #{select_columns}"
624
- else
625
- sql = "SELECT #{select_columns}"
626
- end
627
-
628
- if opts[:from]
629
- sql << " FROM #{source_list(opts[:from])}"
630
- end
631
-
632
- if join = opts[:join]
633
- join.each{|j| sql << literal(j)}
634
- end
635
-
636
- if where = opts[:where]
637
- sql << " WHERE #{literal(where)}"
638
- end
639
-
640
- if group = opts[:group]
641
- sql << " GROUP BY #{expression_list(group)}"
642
- end
643
-
644
- if having = opts[:having]
645
- sql << " HAVING #{literal(having)}"
646
- end
647
-
648
- if order = opts[:order]
649
- sql << " ORDER BY #{expression_list(order)}"
650
- end
651
-
652
- if limit = opts[:limit]
653
- sql << " LIMIT #{limit}"
654
- if offset = opts[:offset]
655
- sql << " OFFSET #{offset}"
656
- end
657
- end
658
-
659
- if union = opts[:union]
660
- sql << (opts[:union_all] ? \
661
- " UNION ALL #{union.sql}" : " UNION #{union.sql}")
662
- elsif intersect = opts[:intersect]
663
- sql << (opts[:intersect_all] ? \
664
- " INTERSECT ALL #{intersect.sql}" : " INTERSECT #{intersect.sql}")
665
- elsif except = opts[:except]
666
- sql << (opts[:except_all] ? \
667
- " EXCEPT ALL #{except.sql}" : " EXCEPT #{except.sql}")
668
- end
669
-
640
+ return static_sql(opts[:sql]) if opts[:sql]
641
+ sql = 'SELECT'
642
+ select_clause_order.each{|x| send("select_#{x}_sql", sql, opts)}
670
643
  sql
671
644
  end
672
645
 
@@ -733,9 +706,7 @@ module Sequel
733
706
  def update_sql(values = {}, opts = nil)
734
707
  opts = opts ? @opts.merge(opts) : @opts
735
708
 
736
- if sql = opts[:sql]
737
- return sql
738
- end
709
+ return static_sql(opts[:sql]) if opts[:sql]
739
710
 
740
711
  if opts[:group]
741
712
  raise Error::InvalidOperation, "A grouped dataset cannot be updated"
@@ -788,7 +759,7 @@ module Sequel
788
759
  # Converts an array of column names into a comma seperated string of
789
760
  # column names. If the array is empty, a wildcard (*) is returned.
790
761
  def column_list(columns)
791
- if columns.empty?
762
+ if columns.blank?
792
763
  WILDCARD
793
764
  else
794
765
  m = columns.map do |i|
@@ -817,7 +788,7 @@ module Sequel
817
788
  SQL::BooleanExpression.from_value_pairs(expr)
818
789
  when Array
819
790
  if String === expr[0]
820
- filter_expr(expr.shift.gsub(QUESTION_MARK){literal(expr.shift)}.lit)
791
+ SQL::PlaceholderLiteralString.new(expr.shift, expr, true)
821
792
  else
822
793
  SQL::BooleanExpression.from_value_pairs(expr)
823
794
  end
@@ -876,14 +847,90 @@ module Sequel
876
847
  def qualified_column_name(column, table)
877
848
  if Symbol === column
878
849
  c_table, column, c_alias = split_symbol(column)
879
- schema, table, t_alias = split_symbol(table) if Symbol === table
880
- c_table ||= t_alias || table
850
+ unless c_table
851
+ case table
852
+ when Symbol
853
+ schema, table, t_alias = split_symbol(table)
854
+ t_alias ||= Sequel::SQL::QualifiedIdentifier.new(schema, table) if schema
855
+ when Sequel::SQL::AliasedExpression
856
+ t_alias = table.aliaz
857
+ end
858
+ c_table = t_alias || table
859
+ end
881
860
  ::Sequel::SQL::QualifiedIdentifier.new(c_table, column)
882
861
  else
883
862
  column
884
863
  end
885
864
  end
886
865
 
866
+ # The order of methods to call to build the SELECT SQL statement
867
+ def select_clause_order
868
+ SELECT_CLAUSE_ORDER
869
+ end
870
+
871
+ # Modify the sql to add the columns selected
872
+ def select_columns_sql(sql, opts)
873
+ sql << " #{column_list(opts[:select])}"
874
+ end
875
+
876
+ # Modify the sql to add the DISTINCT modifier
877
+ def select_distinct_sql(sql, opts)
878
+ if distinct = opts[:distinct]
879
+ sql << " DISTINCT#{" ON (#{expression_list(distinct)})" unless distinct.empty?}"
880
+ end
881
+ end
882
+
883
+ # Modify the sql to add a dataset to the EXCEPT clause
884
+ def select_except_sql(sql, opts)
885
+ sql << " EXCEPT#{' ALL' if opts[:except_all]} #{opts[:except].sql}" if opts[:except]
886
+ end
887
+
888
+ # Modify the sql to add the list of tables to select FROM
889
+ def select_from_sql(sql, opts)
890
+ sql << " FROM #{source_list(opts[:from])}" if opts[:from]
891
+ end
892
+
893
+ # Modify the sql to add the expressions to GROUP BY
894
+ def select_group_sql(sql, opts)
895
+ sql << " GROUP BY #{expression_list(opts[:group])}" if opts[:group]
896
+ end
897
+
898
+ # Modify the sql to add the filter criteria in the HAVING clause
899
+ def select_having_sql(sql, opts)
900
+ sql << " HAVING #{literal(opts[:having])}" if opts[:having]
901
+ end
902
+
903
+ # Modify the sql to add a dataset to the INTERSECT clause
904
+ def select_intersect_sql(sql, opts)
905
+ sql << " INTERSECT#{' ALL' if opts[:intersect_all]} #{opts[:intersect].sql}" if opts[:intersect]
906
+ end
907
+
908
+ # Modify the sql to add the list of tables to JOIN to
909
+ def select_join_sql(sql, opts)
910
+ opts[:join].each{|j| sql << literal(j)} if opts[:join]
911
+ end
912
+
913
+ # Modify the sql to limit the number of rows returned and offset
914
+ def select_limit_sql(sql, opts)
915
+ sql << " LIMIT #{opts[:limit]}" if opts[:limit]
916
+ sql << " OFFSET #{opts[:offset]}" if opts[:offset]
917
+ end
918
+
919
+ # Modify the sql to add the expressions to ORDER BY
920
+ def select_order_sql(sql, opts)
921
+ sql << " ORDER BY #{expression_list(opts[:order])}" if opts[:order]
922
+ end
923
+
924
+ # Modify the sql to add a dataset to the UNION clause
925
+ def select_union_sql(sql, opts)
926
+ sql << " UNION#{' ALL' if opts[:union_all]} #{opts[:union].sql}" if opts[:union]
927
+ end
928
+
929
+ # Modify the sql to add the filter criteria in the WHERE clause
930
+ def select_where_sql(sql, opts)
931
+ sql << " WHERE #{literal(opts[:where])}" if opts[:where]
932
+ end
933
+
887
934
  # Converts an array of source names into into a comma separated list.
888
935
  def source_list(source)
889
936
  if source.nil? || source.empty?
@@ -920,7 +967,19 @@ module Sequel
920
967
  end
921
968
  end
922
969
 
923
- # SQL fragement specifying a table name.
970
+ # SQL to use if this dataset uses static SQL. Since static SQL
971
+ # can be a PlaceholderLiteralString in addition to a String,
972
+ # we literalize nonstrings.
973
+ def static_sql(sql)
974
+ sql.is_a?(String) ? sql : literal(sql)
975
+ end
976
+
977
+ # SQL fragment for a subselect using the given database's SQL.
978
+ def subselect_sql(ds)
979
+ ds.sql
980
+ end
981
+
982
+ # SQL fragment specifying a table name.
924
983
  def table_ref(t)
925
984
  case t
926
985
  when Dataset