lib/sequel/adapters/shared/mysql.rb in sequel-3.1.0 vs lib/sequel/adapters/shared/mysql.rb in sequel-3.2.0
- old
+ new
@@ -1,23 +1,15 @@
-Sequel.require %w'unsupported savepoint_transactions', 'adapters/utils'
-
module Sequel
- class Database
- # Keep default column_references_sql for add_foreign_key support
- alias default_column_references_sql column_references_sql
- end
module MySQL
class << self
# Set the default options used for CREATE TABLE
attr_accessor :default_charset, :default_collate, :default_engine
end
# Methods shared by Database instances that connect to MySQL,
# currently supported by the native and JDBC adapters.
module DatabaseMethods
- include Sequel::Database::SavepointTransactions
-
AUTO_INCREMENT = 'AUTO_INCREMENT'.freeze
CAST_TYPES = {String=>:CHAR, Integer=>:SIGNED, Time=>:DATETIME, DateTime=>:DATETIME, Numeric=>:DECIMAL, BigDecimal=>:DECIMAL, File=>:BINARY}
PRIMARY = 'PRIMARY'.freeze
# MySQL's cast rules are restrictive in that you can't just cast to any possible
@@ -66,10 +58,15 @@
def tables(opts={})
m = output_identifier_meth
metadata_dataset.with_sql('SHOW TABLES').server(opts[:server]).map{|r| m.call(r.values.first)}
end
+ # MySQL supports savepoints
+ def supports_savepoints?
+ true
+ end
+
# Changes the database in use by issuing a USE statement. I would be
# very careful if I used this.
def use(db_name)
disconnect
@opts[:database] = db_name if self << "USE #{db_name}"
@@ -85,23 +82,23 @@
case op[:op]
when :add_column
if related = op.delete(:table)
sql = super(table, op)
op[:table] = related
- [sql, "ALTER TABLE #{quote_schema_table(table)} ADD FOREIGN KEY (#{quote_identifier(op[:name])})#{default_column_references_sql(op)}"]
+ [sql, "ALTER TABLE #{quote_schema_table(table)} ADD FOREIGN KEY (#{quote_identifier(op[:name])})#{column_references_sql(op)}"]
else
super(table, op)
end
when :rename_column, :set_column_type, :set_column_null, :set_column_default
o = op[:op]
opts = schema(table).find{|x| x.first == op[:name]}
old_opts = opts ? opts.last : {}
name = o == :rename_column ? op[:new_name] : op[:name]
type = o == :set_column_type ? op[:type] : old_opts[:db_type]
null = o == :set_column_null ? op[:null] : old_opts[:allow_null]
- default = o == :set_column_default ? op[:default] : (old_opts[:default].lit if old_opts[:default])
- "ALTER TABLE #{quote_schema_table(table)} CHANGE COLUMN #{quote_identifier(op[:name])} #{column_definition_sql(:name=>name, :type=>type, :null=>null, :default=>default)}"
+ default = o == :set_column_default ? op[:default] : old_opts[:ruby_default]
+ "ALTER TABLE #{quote_schema_table(table)} CHANGE COLUMN #{quote_identifier(op[:name])} #{column_definition_sql(op.merge(:name=>name, :type=>type, :null=>null, :default=>default))}"
when :drop_index
"#{drop_index_sql(table, op)} ON #{quote_schema_table(table)}"
else
super(table, op)
end
@@ -117,13 +114,13 @@
def column_definition_sql(column)
column.delete(:default) if column[:type] == File || (column[:type] == String && column[:text] == true)
super
end
- # Handle MySQL specific syntax for column references
- def column_references_sql(column)
- "#{", FOREIGN KEY (#{quote_identifier(column[:name])})" unless column[:type] == :check}#{super(column)}"
+ # MySQL doesn't handle references as column constraints, it must use a separate table constraint
+ def column_references_column_constraint_sql(column)
+ "#{", FOREIGN KEY (#{quote_identifier(column[:name])})" unless column[:type] == :check}#{column_references_sql(column)}"
end
# Use MySQL specific syntax for engine type and character encoding
def create_table_sql(name, generator, options = {})
engine = options.include?(:engine) ? options[:engine] : Sequel::MySQL.default_engine
@@ -197,16 +194,15 @@
end
end
# Dataset methods shared by datasets that use MySQL databases.
module DatasetMethods
- include Dataset::UnsupportedIntersectExcept
-
BOOL_TRUE = '1'.freeze
BOOL_FALSE = '0'.freeze
TIMESTAMP_FORMAT = "'%Y-%m-%d %H:%M:%S'".freeze
COMMA_SEPARATOR = ', '.freeze
+ SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having compounds order limit'.freeze
# MySQL specific syntax for LIKE/REGEXP searches, as well as
# string concatenation.
def complex_expression_sql(op, args)
case op
@@ -229,16 +225,10 @@
sql << " ORDER BY #{expression_list(opts[:order])}" if opts[:order]
sql << " LIMIT #{opts[:limit]}" if opts[:limit]
sql
end
- # MySQL doesn't support DISTINCT ON
- def distinct(*columns)
- raise(Error, "DISTINCT ON not supported by MySQL") unless columns.empty?
- super
- end
-
# Adds full text filter
def full_text_search(cols, terms, opts = {})
filter(full_text_sql(cols, terms, opts))
end
@@ -311,19 +301,15 @@
# ON DUPLICATE KEY UPDATE value=VALUES(value)
#
def on_duplicate_key_update(*args)
clone(:on_duplicate_key_update => args)
end
-
+
# MySQL specific syntax for inserting multiple values at once.
def multi_insert_sql(columns, values)
- if update_cols = opts[:on_duplicate_key_update]
- update_cols = columns if update_cols.empty?
- update_string = update_cols.map{|c| "#{quote_identifier(c)}=VALUES(#{quote_identifier(c)})"}.join(COMMA_SEPARATOR)
- end
values = values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)
- ["#{insert_sql_base}#{source_list(@opts[:from])} (#{identifier_list(columns)}) VALUES #{values}#{" ON DUPLICATE KEY UPDATE #{update_string}" if update_string}"]
+ ["#{insert_sql_base}#{source_list(@opts[:from])} (#{identifier_list(columns)}) VALUES #{values}#{insert_sql_suffix}"]
end
# MySQL uses the nonstandard ` (backtick) for quoting identifiers.
def quoted_identifier(c)
"`#{c}`"
@@ -363,10 +349,20 @@
end
end
end
end
+ # does not support DISTINCT ON
+ def supports_distinct_on?
+ false
+ end
+
+ # MySQL does not support INTERSECT or EXCEPT
+ def supports_intersect_except?
+ false
+ end
+
# MySQL supports ORDER and LIMIT clauses in UPDATE statements.
def update_sql(values)
sql = super
sql << " ORDER BY #{expression_list(opts[:order])}" if opts[:order]
sql << " LIMIT #{opts[:limit]}" if opts[:limit]
@@ -378,10 +374,15 @@
# MySQL supports INSERT IGNORE INTO
def insert_sql_base
"INSERT #{'IGNORE ' if opts[:insert_ignore]}INTO "
end
+ # MySQL supports INSERT ... ON DUPLICATE KEY UPDATE
+ def insert_sql_suffix
+ on_duplicate_key_update_sql if opts[:on_duplicate_key_update]
+ end
+
# MySQL doesn't use the SQL standard DEFAULT VALUES.
def insert_default_values_sql
"#{insert_sql_base}#{source_list(@opts[:from])} () VALUES ()"
end
@@ -401,9 +402,33 @@
end
# Use 1 for true on MySQL
def literal_true
BOOL_TRUE
+ end
+
+ # MySQL specific syntax for ON DUPLICATE KEY UPDATE
+ def on_duplicate_key_update_sql
+ if update_cols = opts[:on_duplicate_key_update]
+ update_vals = nil
+
+ if update_cols.empty?
+ update_cols = columns
+ elsif update_cols.last.is_a?(Hash)
+ update_vals = update_cols.last
+ update_cols = update_cols[0..-2]
+ end
+
+ updating = update_cols.map{|c| "#{quote_identifier(c)}=VALUES(#{quote_identifier(c)})" }
+ updating += update_vals.map{|c,v| "#{quote_identifier(c)}=#{literal(v)}" } if update_vals
+
+ " ON DUPLICATE KEY UPDATE #{updating.join(COMMA_SEPARATOR)}"
+ end
+ end
+
+ # MySQL does not support the SQL WITH clause
+ def select_clause_order
+ SELECT_CLAUSE_ORDER
end
end
end
end