viking-sequel 3.10.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3134 -0
- data/COPYING +19 -0
- data/README.rdoc +723 -0
- data/Rakefile +193 -0
- data/bin/sequel +196 -0
- data/doc/advanced_associations.rdoc +644 -0
- data/doc/cheat_sheet.rdoc +218 -0
- data/doc/dataset_basics.rdoc +106 -0
- data/doc/dataset_filtering.rdoc +158 -0
- data/doc/opening_databases.rdoc +296 -0
- data/doc/prepared_statements.rdoc +104 -0
- data/doc/reflection.rdoc +84 -0
- data/doc/release_notes/1.0.txt +38 -0
- data/doc/release_notes/1.1.txt +143 -0
- data/doc/release_notes/1.3.txt +101 -0
- data/doc/release_notes/1.4.0.txt +53 -0
- data/doc/release_notes/1.5.0.txt +155 -0
- data/doc/release_notes/2.0.0.txt +298 -0
- data/doc/release_notes/2.1.0.txt +271 -0
- data/doc/release_notes/2.10.0.txt +328 -0
- data/doc/release_notes/2.11.0.txt +215 -0
- data/doc/release_notes/2.12.0.txt +534 -0
- data/doc/release_notes/2.2.0.txt +253 -0
- data/doc/release_notes/2.3.0.txt +88 -0
- data/doc/release_notes/2.4.0.txt +106 -0
- data/doc/release_notes/2.5.0.txt +137 -0
- data/doc/release_notes/2.6.0.txt +157 -0
- data/doc/release_notes/2.7.0.txt +166 -0
- data/doc/release_notes/2.8.0.txt +171 -0
- data/doc/release_notes/2.9.0.txt +97 -0
- data/doc/release_notes/3.0.0.txt +221 -0
- data/doc/release_notes/3.1.0.txt +406 -0
- data/doc/release_notes/3.10.0.txt +286 -0
- data/doc/release_notes/3.2.0.txt +268 -0
- data/doc/release_notes/3.3.0.txt +192 -0
- data/doc/release_notes/3.4.0.txt +325 -0
- data/doc/release_notes/3.5.0.txt +510 -0
- data/doc/release_notes/3.6.0.txt +366 -0
- data/doc/release_notes/3.7.0.txt +179 -0
- data/doc/release_notes/3.8.0.txt +151 -0
- data/doc/release_notes/3.9.0.txt +233 -0
- data/doc/schema.rdoc +36 -0
- data/doc/sharding.rdoc +113 -0
- data/doc/virtual_rows.rdoc +205 -0
- data/lib/sequel.rb +1 -0
- data/lib/sequel/adapters/ado.rb +90 -0
- data/lib/sequel/adapters/ado/mssql.rb +30 -0
- data/lib/sequel/adapters/amalgalite.rb +176 -0
- data/lib/sequel/adapters/db2.rb +139 -0
- data/lib/sequel/adapters/dbi.rb +113 -0
- data/lib/sequel/adapters/do.rb +188 -0
- data/lib/sequel/adapters/do/mysql.rb +49 -0
- data/lib/sequel/adapters/do/postgres.rb +91 -0
- data/lib/sequel/adapters/do/sqlite.rb +40 -0
- data/lib/sequel/adapters/firebird.rb +283 -0
- data/lib/sequel/adapters/informix.rb +77 -0
- data/lib/sequel/adapters/jdbc.rb +587 -0
- data/lib/sequel/adapters/jdbc/as400.rb +58 -0
- data/lib/sequel/adapters/jdbc/h2.rb +133 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
- data/lib/sequel/adapters/mysql.rb +421 -0
- data/lib/sequel/adapters/odbc.rb +143 -0
- data/lib/sequel/adapters/odbc/mssql.rb +42 -0
- data/lib/sequel/adapters/openbase.rb +64 -0
- data/lib/sequel/adapters/oracle.rb +131 -0
- data/lib/sequel/adapters/postgres.rb +504 -0
- data/lib/sequel/adapters/shared/mssql.rb +490 -0
- data/lib/sequel/adapters/shared/mysql.rb +498 -0
- data/lib/sequel/adapters/shared/oracle.rb +195 -0
- data/lib/sequel/adapters/shared/postgres.rb +830 -0
- data/lib/sequel/adapters/shared/progress.rb +44 -0
- data/lib/sequel/adapters/shared/sqlite.rb +389 -0
- data/lib/sequel/adapters/sqlite.rb +224 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
- data/lib/sequel/connection_pool.rb +99 -0
- data/lib/sequel/connection_pool/sharded_single.rb +84 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
- data/lib/sequel/connection_pool/single.rb +29 -0
- data/lib/sequel/connection_pool/threaded.rb +150 -0
- data/lib/sequel/core.rb +293 -0
- data/lib/sequel/core_sql.rb +241 -0
- data/lib/sequel/database.rb +1079 -0
- data/lib/sequel/database/schema_generator.rb +327 -0
- data/lib/sequel/database/schema_methods.rb +203 -0
- data/lib/sequel/database/schema_sql.rb +320 -0
- data/lib/sequel/dataset.rb +32 -0
- data/lib/sequel/dataset/actions.rb +441 -0
- data/lib/sequel/dataset/features.rb +86 -0
- data/lib/sequel/dataset/graph.rb +254 -0
- data/lib/sequel/dataset/misc.rb +119 -0
- data/lib/sequel/dataset/mutation.rb +64 -0
- data/lib/sequel/dataset/prepared_statements.rb +227 -0
- data/lib/sequel/dataset/query.rb +709 -0
- data/lib/sequel/dataset/sql.rb +996 -0
- data/lib/sequel/exceptions.rb +51 -0
- data/lib/sequel/extensions/blank.rb +43 -0
- data/lib/sequel/extensions/inflector.rb +242 -0
- data/lib/sequel/extensions/looser_typecasting.rb +21 -0
- data/lib/sequel/extensions/migration.rb +239 -0
- data/lib/sequel/extensions/named_timezones.rb +61 -0
- data/lib/sequel/extensions/pagination.rb +100 -0
- data/lib/sequel/extensions/pretty_table.rb +82 -0
- data/lib/sequel/extensions/query.rb +52 -0
- data/lib/sequel/extensions/schema_dumper.rb +271 -0
- data/lib/sequel/extensions/sql_expr.rb +122 -0
- data/lib/sequel/extensions/string_date_time.rb +46 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
- data/lib/sequel/metaprogramming.rb +9 -0
- data/lib/sequel/model.rb +120 -0
- data/lib/sequel/model/associations.rb +1514 -0
- data/lib/sequel/model/base.rb +1069 -0
- data/lib/sequel/model/default_inflections.rb +45 -0
- data/lib/sequel/model/errors.rb +39 -0
- data/lib/sequel/model/exceptions.rb +21 -0
- data/lib/sequel/model/inflections.rb +162 -0
- data/lib/sequel/model/plugins.rb +70 -0
- data/lib/sequel/plugins/active_model.rb +59 -0
- data/lib/sequel/plugins/association_dependencies.rb +103 -0
- data/lib/sequel/plugins/association_proxies.rb +41 -0
- data/lib/sequel/plugins/boolean_readers.rb +53 -0
- data/lib/sequel/plugins/caching.rb +141 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
- data/lib/sequel/plugins/composition.rb +138 -0
- data/lib/sequel/plugins/force_encoding.rb +72 -0
- data/lib/sequel/plugins/hook_class_methods.rb +126 -0
- data/lib/sequel/plugins/identity_map.rb +116 -0
- data/lib/sequel/plugins/instance_filters.rb +98 -0
- data/lib/sequel/plugins/instance_hooks.rb +57 -0
- data/lib/sequel/plugins/lazy_attributes.rb +77 -0
- data/lib/sequel/plugins/many_through_many.rb +208 -0
- data/lib/sequel/plugins/nested_attributes.rb +206 -0
- data/lib/sequel/plugins/optimistic_locking.rb +81 -0
- data/lib/sequel/plugins/rcte_tree.rb +281 -0
- data/lib/sequel/plugins/schema.rb +66 -0
- data/lib/sequel/plugins/serialization.rb +166 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
- data/lib/sequel/plugins/subclasses.rb +45 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
- data/lib/sequel/plugins/timestamps.rb +87 -0
- data/lib/sequel/plugins/touch.rb +118 -0
- data/lib/sequel/plugins/typecast_on_load.rb +72 -0
- data/lib/sequel/plugins/validation_class_methods.rb +405 -0
- data/lib/sequel/plugins/validation_helpers.rb +223 -0
- data/lib/sequel/sql.rb +1020 -0
- data/lib/sequel/timezones.rb +161 -0
- data/lib/sequel/version.rb +12 -0
- data/lib/sequel_core.rb +1 -0
- data/lib/sequel_model.rb +1 -0
- data/spec/adapters/firebird_spec.rb +407 -0
- data/spec/adapters/informix_spec.rb +97 -0
- data/spec/adapters/mssql_spec.rb +403 -0
- data/spec/adapters/mysql_spec.rb +1019 -0
- data/spec/adapters/oracle_spec.rb +286 -0
- data/spec/adapters/postgres_spec.rb +969 -0
- data/spec/adapters/spec_helper.rb +51 -0
- data/spec/adapters/sqlite_spec.rb +432 -0
- data/spec/core/connection_pool_spec.rb +808 -0
- data/spec/core/core_sql_spec.rb +417 -0
- data/spec/core/database_spec.rb +1662 -0
- data/spec/core/dataset_spec.rb +3827 -0
- data/spec/core/expression_filters_spec.rb +595 -0
- data/spec/core/object_graph_spec.rb +296 -0
- data/spec/core/schema_generator_spec.rb +159 -0
- data/spec/core/schema_spec.rb +830 -0
- data/spec/core/spec_helper.rb +56 -0
- data/spec/core/version_spec.rb +7 -0
- data/spec/extensions/active_model_spec.rb +76 -0
- data/spec/extensions/association_dependencies_spec.rb +127 -0
- data/spec/extensions/association_proxies_spec.rb +50 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/boolean_readers_spec.rb +92 -0
- data/spec/extensions/caching_spec.rb +250 -0
- data/spec/extensions/class_table_inheritance_spec.rb +252 -0
- data/spec/extensions/composition_spec.rb +194 -0
- data/spec/extensions/force_encoding_spec.rb +117 -0
- data/spec/extensions/hook_class_methods_spec.rb +470 -0
- data/spec/extensions/identity_map_spec.rb +202 -0
- data/spec/extensions/inflector_spec.rb +181 -0
- data/spec/extensions/instance_filters_spec.rb +55 -0
- data/spec/extensions/instance_hooks_spec.rb +133 -0
- data/spec/extensions/lazy_attributes_spec.rb +153 -0
- data/spec/extensions/looser_typecasting_spec.rb +39 -0
- data/spec/extensions/many_through_many_spec.rb +884 -0
- data/spec/extensions/migration_spec.rb +332 -0
- data/spec/extensions/named_timezones_spec.rb +72 -0
- data/spec/extensions/nested_attributes_spec.rb +396 -0
- data/spec/extensions/optimistic_locking_spec.rb +100 -0
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/extensions/rcte_tree_spec.rb +205 -0
- data/spec/extensions/schema_dumper_spec.rb +357 -0
- data/spec/extensions/schema_spec.rb +127 -0
- data/spec/extensions/serialization_spec.rb +209 -0
- data/spec/extensions/single_table_inheritance_spec.rb +96 -0
- data/spec/extensions/spec_helper.rb +91 -0
- data/spec/extensions/sql_expr_spec.rb +89 -0
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/extensions/subclasses_spec.rb +52 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
- data/spec/extensions/thread_local_timezones_spec.rb +45 -0
- data/spec/extensions/timestamps_spec.rb +150 -0
- data/spec/extensions/touch_spec.rb +155 -0
- data/spec/extensions/typecast_on_load_spec.rb +69 -0
- data/spec/extensions/validation_class_methods_spec.rb +984 -0
- data/spec/extensions/validation_helpers_spec.rb +438 -0
- data/spec/integration/associations_test.rb +281 -0
- data/spec/integration/database_test.rb +26 -0
- data/spec/integration/dataset_test.rb +963 -0
- data/spec/integration/eager_loader_test.rb +734 -0
- data/spec/integration/model_test.rb +130 -0
- data/spec/integration/plugin_test.rb +814 -0
- data/spec/integration/prepared_statement_test.rb +213 -0
- data/spec/integration/schema_test.rb +361 -0
- data/spec/integration/spec_helper.rb +73 -0
- data/spec/integration/timezone_test.rb +55 -0
- data/spec/integration/transaction_test.rb +122 -0
- data/spec/integration/type_test.rb +96 -0
- data/spec/model/association_reflection_spec.rb +175 -0
- data/spec/model/associations_spec.rb +2633 -0
- data/spec/model/base_spec.rb +418 -0
- data/spec/model/dataset_methods_spec.rb +78 -0
- data/spec/model/eager_loading_spec.rb +1391 -0
- data/spec/model/hooks_spec.rb +240 -0
- data/spec/model/inflector_spec.rb +26 -0
- data/spec/model/model_spec.rb +593 -0
- data/spec/model/plugins_spec.rb +236 -0
- data/spec/model/record_spec.rb +1500 -0
- data/spec/model/spec_helper.rb +97 -0
- data/spec/model/validations_spec.rb +153 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec_config.rb.example +10 -0
- metadata +346 -0
@@ -0,0 +1,498 @@
|
|
1
|
+
module Sequel
|
2
|
+
Dataset::NON_SQL_OPTIONS << :insert_ignore
|
3
|
+
Dataset::NON_SQL_OPTIONS << :on_duplicate_key_update
|
4
|
+
|
5
|
+
module MySQL
|
6
|
+
class << self
|
7
|
+
# Set the default charset used for CREATE TABLE. You can pass the
|
8
|
+
# :charset option to create_table to override this setting.
|
9
|
+
attr_accessor :default_charset
|
10
|
+
|
11
|
+
# Set the default collation used for CREATE TABLE. You can pass the
|
12
|
+
# :collate option to create_table to override this setting.
|
13
|
+
attr_accessor :default_collate
|
14
|
+
|
15
|
+
# Set the default engine used for CREATE TABLE. You can pass the
|
16
|
+
# :engine option to create_table to override this setting.
|
17
|
+
attr_accessor :default_engine
|
18
|
+
end
|
19
|
+
|
20
|
+
# Methods shared by Database instances that connect to MySQL,
|
21
|
+
# currently supported by the native and JDBC adapters.
|
22
|
+
module DatabaseMethods
|
23
|
+
AUTO_INCREMENT = 'AUTO_INCREMENT'.freeze
|
24
|
+
CAST_TYPES = {String=>:CHAR, Integer=>:SIGNED, Time=>:DATETIME, DateTime=>:DATETIME, Numeric=>:DECIMAL, BigDecimal=>:DECIMAL, File=>:BINARY}
|
25
|
+
PRIMARY = 'PRIMARY'.freeze
|
26
|
+
|
27
|
+
# MySQL's cast rules are restrictive in that you can't just cast to any possible
|
28
|
+
# database type.
|
29
|
+
def cast_type_literal(type)
|
30
|
+
CAST_TYPES[type] || super
|
31
|
+
end
|
32
|
+
|
33
|
+
# MySQL uses the :mysql database type
|
34
|
+
def database_type
|
35
|
+
:mysql
|
36
|
+
end
|
37
|
+
|
38
|
+
# Return a hash containing index information. Hash keys are index name symbols.
|
39
|
+
# Values are subhashes with two keys, :columns and :unique. The value of :columns
|
40
|
+
# is an array of symbols of column names. The value of :unique is true or false
|
41
|
+
# depending on if the index is unique.
|
42
|
+
#
|
43
|
+
# Does not include the primary key index or indexes on partial keys.
|
44
|
+
def indexes(table)
|
45
|
+
indexes = {}
|
46
|
+
remove_indexes = []
|
47
|
+
m = output_identifier_meth
|
48
|
+
im = input_identifier_meth
|
49
|
+
metadata_dataset.with_sql("SHOW INDEX FROM ?", SQL::Identifier.new(im.call(table))).each do |r|
|
50
|
+
name = r[:Key_name]
|
51
|
+
next if name == PRIMARY
|
52
|
+
name = m.call(name)
|
53
|
+
remove_indexes << name if r[:Sub_part]
|
54
|
+
i = indexes[name] ||= {:columns=>[], :unique=>r[:Non_unique] != 1}
|
55
|
+
i[:columns] << m.call(r[:Column_name])
|
56
|
+
end
|
57
|
+
indexes.reject{|k,v| remove_indexes.include?(k)}
|
58
|
+
end
|
59
|
+
|
60
|
+
# Get version of MySQL server, used for determined capabilities.
|
61
|
+
def server_version
|
62
|
+
m = /(\d+)\.(\d+)\.(\d+)/.match(get(SQL::Function.new(:version)))
|
63
|
+
@server_version ||= (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
|
64
|
+
end
|
65
|
+
|
66
|
+
# Return an array of symbols specifying table names in the current database.
|
67
|
+
#
|
68
|
+
# Options:
|
69
|
+
# * :server - Set the server to use
|
70
|
+
def tables(opts={})
|
71
|
+
m = output_identifier_meth
|
72
|
+
metadata_dataset.with_sql('SHOW TABLES').server(opts[:server]).map{|r| m.call(r.values.first)}
|
73
|
+
end
|
74
|
+
|
75
|
+
# MySQL supports savepoints
|
76
|
+
def supports_savepoints?
|
77
|
+
true
|
78
|
+
end
|
79
|
+
|
80
|
+
# Changes the database in use by issuing a USE statement. I would be
|
81
|
+
# very careful if I used this.
|
82
|
+
def use(db_name)
|
83
|
+
disconnect
|
84
|
+
@opts[:database] = db_name if self << "USE #{db_name}"
|
85
|
+
@schemas = {}
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# Use MySQL specific syntax for rename column, set column type, and
|
92
|
+
# drop index cases.
|
93
|
+
def alter_table_sql(table, op)
|
94
|
+
case op[:op]
|
95
|
+
when :add_column
|
96
|
+
if related = op.delete(:table)
|
97
|
+
sql = super(table, op)
|
98
|
+
op[:table] = related
|
99
|
+
[sql, "ALTER TABLE #{quote_schema_table(table)} ADD FOREIGN KEY (#{quote_identifier(op[:name])})#{column_references_sql(op)}"]
|
100
|
+
else
|
101
|
+
super(table, op)
|
102
|
+
end
|
103
|
+
when :rename_column, :set_column_type, :set_column_null, :set_column_default
|
104
|
+
o = op[:op]
|
105
|
+
opts = schema(table).find{|x| x.first == op[:name]}
|
106
|
+
opts = opts ? opts.last.dup : {}
|
107
|
+
opts[:name] = o == :rename_column ? op[:new_name] : op[:name]
|
108
|
+
opts[:type] = o == :set_column_type ? op[:type] : opts[:db_type]
|
109
|
+
opts[:null] = o == :set_column_null ? op[:null] : opts[:allow_null]
|
110
|
+
opts[:default] = o == :set_column_default ? op[:default] : opts[:ruby_default]
|
111
|
+
opts.delete(:default) if opts[:default] == nil
|
112
|
+
"ALTER TABLE #{quote_schema_table(table)} CHANGE COLUMN #{quote_identifier(op[:name])} #{column_definition_sql(op.merge(opts))}"
|
113
|
+
when :drop_index
|
114
|
+
"#{drop_index_sql(table, op)} ON #{quote_schema_table(table)}"
|
115
|
+
else
|
116
|
+
super(table, op)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Use MySQL specific AUTO_INCREMENT text.
|
121
|
+
def auto_increment_sql
|
122
|
+
AUTO_INCREMENT
|
123
|
+
end
|
124
|
+
|
125
|
+
# MySQL doesn't allow default values on text columns, so ignore if it the
|
126
|
+
# generic text type is used
|
127
|
+
def column_definition_sql(column)
|
128
|
+
column.delete(:default) if column[:type] == File || (column[:type] == String && column[:text] == true)
|
129
|
+
super
|
130
|
+
end
|
131
|
+
|
132
|
+
# Use MySQL specific syntax for engine type and character encoding
|
133
|
+
def create_table_sql(name, generator, options = {})
|
134
|
+
engine = options.fetch(:engine, Sequel::MySQL.default_engine)
|
135
|
+
charset = options.fetch(:charset, Sequel::MySQL.default_charset)
|
136
|
+
collate = options.fetch(:collate, Sequel::MySQL.default_collate)
|
137
|
+
generator.columns.each do |c|
|
138
|
+
if t = c.delete(:table)
|
139
|
+
generator.foreign_key([c[:name]], t, c.merge(:name=>nil, :type=>:foreign_key))
|
140
|
+
end
|
141
|
+
end
|
142
|
+
"#{super}#{" ENGINE=#{engine}" if engine}#{" DEFAULT CHARSET=#{charset}" if charset}#{" DEFAULT COLLATE=#{collate}" if collate}"
|
143
|
+
end
|
144
|
+
|
145
|
+
# MySQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
|
146
|
+
def identifier_input_method_default
|
147
|
+
nil
|
148
|
+
end
|
149
|
+
|
150
|
+
# MySQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on output.
|
151
|
+
def identifier_output_method_default
|
152
|
+
nil
|
153
|
+
end
|
154
|
+
|
155
|
+
# Handle MySQL specific index SQL syntax
|
156
|
+
def index_definition_sql(table_name, index)
|
157
|
+
index_name = quote_identifier(index[:name] || default_index_name(table_name, index[:columns]))
|
158
|
+
index_type = case index[:type]
|
159
|
+
when :full_text
|
160
|
+
"FULLTEXT "
|
161
|
+
when :spatial
|
162
|
+
"SPATIAL "
|
163
|
+
else
|
164
|
+
using = " USING #{index[:type]}" unless index[:type] == nil
|
165
|
+
"UNIQUE " if index[:unique]
|
166
|
+
end
|
167
|
+
"CREATE #{index_type}INDEX #{index_name}#{using} ON #{quote_schema_table(table_name)} #{literal(index[:columns])}"
|
168
|
+
end
|
169
|
+
|
170
|
+
# MySQL treats integer primary keys as autoincrementing.
|
171
|
+
def schema_autoincrementing_primary_key?(schema)
|
172
|
+
super and schema[:db_type] =~ /int/io
|
173
|
+
end
|
174
|
+
|
175
|
+
# Use the MySQL specific DESCRIBE syntax to get a table description.
|
176
|
+
def schema_parse_table(table_name, opts)
|
177
|
+
m = output_identifier_meth
|
178
|
+
im = input_identifier_meth
|
179
|
+
metadata_dataset.with_sql("DESCRIBE ?", SQL::Identifier.new(im.call(table_name))).map do |row|
|
180
|
+
row.delete(:Extra)
|
181
|
+
row[:allow_null] = row.delete(:Null) == 'YES'
|
182
|
+
row[:default] = row.delete(:Default)
|
183
|
+
row[:primary_key] = row.delete(:Key) == 'PRI'
|
184
|
+
row[:default] = nil if blank_object?(row[:default])
|
185
|
+
row[:db_type] = row.delete(:Type)
|
186
|
+
row[:type] = schema_column_type(row[:db_type])
|
187
|
+
[m.call(row.delete(:Field)), row]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Respect the :size option if given to produce
|
192
|
+
# tinyblob, mediumblob, and longblob if :tiny,
|
193
|
+
# :medium, or :long is given.
|
194
|
+
def type_literal_generic_file(column)
|
195
|
+
case column[:size]
|
196
|
+
when :tiny # < 2^8 bytes
|
197
|
+
:tinyblob
|
198
|
+
when :medium # < 2^24 bytes
|
199
|
+
:mediumblob
|
200
|
+
when :long # < 2^32 bytes
|
201
|
+
:longblob
|
202
|
+
else # 2^16 bytes
|
203
|
+
:blob
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# MySQL has both datetime and timestamp classes, most people are going
|
208
|
+
# to want datetime
|
209
|
+
def type_literal_generic_datetime(column)
|
210
|
+
:datetime
|
211
|
+
end
|
212
|
+
|
213
|
+
# MySQL has both datetime and timestamp classes, most people are going
|
214
|
+
# to want datetime
|
215
|
+
def type_literal_generic_time(column)
|
216
|
+
column[:only_time] ? :time : :datetime
|
217
|
+
end
|
218
|
+
|
219
|
+
# MySQL doesn't have a true boolean class, so it uses tinyint(1)
|
220
|
+
def type_literal_generic_trueclass(column)
|
221
|
+
:'tinyint(1)'
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Dataset methods shared by datasets that use MySQL databases.
|
226
|
+
module DatasetMethods
|
227
|
+
BOOL_TRUE = '1'.freeze
|
228
|
+
BOOL_FALSE = '0'.freeze
|
229
|
+
COMMA_SEPARATOR = ', '.freeze
|
230
|
+
FOR_SHARE = ' LOCK IN SHARE MODE'.freeze
|
231
|
+
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'from where order limit')
|
232
|
+
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'ignore into columns values on_duplicate_key_update')
|
233
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'distinct columns from join where group having compounds order limit lock')
|
234
|
+
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'table set where order limit')
|
235
|
+
|
236
|
+
# MySQL specific syntax for LIKE/REGEXP searches, as well as
|
237
|
+
# string concatenation.
|
238
|
+
def complex_expression_sql(op, args)
|
239
|
+
case op
|
240
|
+
when :~, :'!~', :'~*', :'!~*', :LIKE, :'NOT LIKE', :ILIKE, :'NOT ILIKE'
|
241
|
+
"(#{literal(args.at(0))} #{'NOT ' if [:'NOT LIKE', :'NOT ILIKE', :'!~', :'!~*'].include?(op)}#{[:~, :'!~', :'~*', :'!~*'].include?(op) ? 'REGEXP' : 'LIKE'} #{'BINARY ' if [:~, :'!~', :LIKE, :'NOT LIKE'].include?(op)}#{literal(args.at(1))})"
|
242
|
+
when :'||'
|
243
|
+
if args.length > 1
|
244
|
+
"CONCAT(#{args.collect{|a| literal(a)}.join(', ')})"
|
245
|
+
else
|
246
|
+
literal(args.at(0))
|
247
|
+
end
|
248
|
+
else
|
249
|
+
super(op, args)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# Use GROUP BY instead of DISTINCT ON if arguments are provided.
|
254
|
+
def distinct(*args)
|
255
|
+
args.empty? ? super : group(*args)
|
256
|
+
end
|
257
|
+
|
258
|
+
# Return a cloned dataset which will use LOCK IN SHARE MODE to lock returned rows.
|
259
|
+
def for_share
|
260
|
+
lock_style(:share)
|
261
|
+
end
|
262
|
+
|
263
|
+
# Adds full text filter
|
264
|
+
def full_text_search(cols, terms, opts = {})
|
265
|
+
filter(full_text_sql(cols, terms, opts))
|
266
|
+
end
|
267
|
+
|
268
|
+
# MySQL specific full text search syntax.
|
269
|
+
def full_text_sql(cols, term, opts = {})
|
270
|
+
"MATCH #{literal(Array(cols))} AGAINST (#{literal(Array(term).join(' '))}#{" IN BOOLEAN MODE" if opts[:boolean]})"
|
271
|
+
end
|
272
|
+
|
273
|
+
# MySQL allows HAVING clause on ungrouped datasets.
|
274
|
+
def having(*cond, &block)
|
275
|
+
_filter(:having, *cond, &block)
|
276
|
+
end
|
277
|
+
|
278
|
+
# Transforms an CROSS JOIN to an INNER JOIN if the expr is not nil.
|
279
|
+
# Raises an error on use of :full_outer type, since MySQL doesn't support it.
|
280
|
+
def join_table(type, table, expr=nil, table_alias={})
|
281
|
+
type = :inner if (type == :cross) && !expr.nil?
|
282
|
+
raise(Sequel::Error, "MySQL doesn't support FULL OUTER JOIN") if type == :full_outer
|
283
|
+
super(type, table, expr, table_alias)
|
284
|
+
end
|
285
|
+
|
286
|
+
# Transforms :natural_inner to NATURAL LEFT JOIN and straight to
|
287
|
+
# STRAIGHT_JOIN.
|
288
|
+
def join_type_sql(join_type)
|
289
|
+
case join_type
|
290
|
+
when :straight then 'STRAIGHT_JOIN'
|
291
|
+
when :natural_inner then 'NATURAL LEFT JOIN'
|
292
|
+
else super
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
# Sets up multi_insert or import to use INSERT IGNORE.
|
297
|
+
# Useful if you have a unique key and want to just skip
|
298
|
+
# inserting rows that violate the unique key restriction.
|
299
|
+
#
|
300
|
+
# Example:
|
301
|
+
#
|
302
|
+
# dataset.insert_ignore.multi_insert(
|
303
|
+
# [{:name => 'a', :value => 1}, {:name => 'b', :value => 2}]
|
304
|
+
# )
|
305
|
+
#
|
306
|
+
# INSERT IGNORE INTO tablename (name, value) VALUES (a, 1), (b, 2)
|
307
|
+
#
|
308
|
+
def insert_ignore
|
309
|
+
clone(:insert_ignore=>true)
|
310
|
+
end
|
311
|
+
|
312
|
+
# Sets up multi_insert or import to use ON DUPLICATE KEY UPDATE
|
313
|
+
# If you pass no arguments, ALL fields will be
|
314
|
+
# updated with the new values. If you pass the fields you
|
315
|
+
# want then ONLY those field will be updated.
|
316
|
+
#
|
317
|
+
# Useful if you have a unique key and want to update
|
318
|
+
# inserting rows that violate the unique key restriction.
|
319
|
+
#
|
320
|
+
# Examples:
|
321
|
+
#
|
322
|
+
# dataset.on_duplicate_key_update.multi_insert(
|
323
|
+
# [{:name => 'a', :value => 1}, {:name => 'b', :value => 2}]
|
324
|
+
# )
|
325
|
+
#
|
326
|
+
# INSERT INTO tablename (name, value) VALUES (a, 1), (b, 2)
|
327
|
+
# ON DUPLICATE KEY UPDATE name=VALUES(name), value=VALUES(value)
|
328
|
+
#
|
329
|
+
# dataset.on_duplicate_key_update(:value).multi_insert(
|
330
|
+
# [{:name => 'a', :value => 1}, {:name => 'b', :value => 2}]
|
331
|
+
# )
|
332
|
+
#
|
333
|
+
# INSERT INTO tablename (name, value) VALUES (a, 1), (b, 2)
|
334
|
+
# ON DUPLICATE KEY UPDATE value=VALUES(value)
|
335
|
+
#
|
336
|
+
def on_duplicate_key_update(*args)
|
337
|
+
clone(:on_duplicate_key_update => args)
|
338
|
+
end
|
339
|
+
|
340
|
+
# MySQL specific syntax for inserting multiple values at once.
|
341
|
+
def multi_insert_sql(columns, values)
|
342
|
+
[insert_sql(columns, LiteralString.new('VALUES ' + values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)))]
|
343
|
+
end
|
344
|
+
|
345
|
+
# MySQL uses the number of rows actually modified in the update,
|
346
|
+
# instead of the number of matched by the filter.
|
347
|
+
def provides_accurate_rows_matched?
|
348
|
+
false
|
349
|
+
end
|
350
|
+
|
351
|
+
# MySQL uses the nonstandard ` (backtick) for quoting identifiers.
|
352
|
+
def quoted_identifier(c)
|
353
|
+
"`#{c}`"
|
354
|
+
end
|
355
|
+
|
356
|
+
# MySQL specific syntax for REPLACE (aka UPSERT, or update if exists,
|
357
|
+
# insert if it doesn't).
|
358
|
+
def replace_sql(*values)
|
359
|
+
clone(:replace=>true).insert_sql(*values)
|
360
|
+
end
|
361
|
+
|
362
|
+
# MySQL can emulate DISTINCT ON with its non-standard GROUP BY implementation,
|
363
|
+
# though the rows returned cannot be made deterministic through ordering.
|
364
|
+
def supports_distinct_on?
|
365
|
+
true
|
366
|
+
end
|
367
|
+
|
368
|
+
# MySQL does not support INTERSECT or EXCEPT
|
369
|
+
def supports_intersect_except?
|
370
|
+
false
|
371
|
+
end
|
372
|
+
|
373
|
+
# MySQL supports modifying joined datasets
|
374
|
+
def supports_modifying_joins?
|
375
|
+
true
|
376
|
+
end
|
377
|
+
|
378
|
+
# MySQL does support fractional timestamps in literal timestamps, but it
|
379
|
+
# ignores them. Also, using them seems to cause problems on 1.9. Since
|
380
|
+
# they are ignored anyway, not using them is probably best.
|
381
|
+
def supports_timestamp_usecs?
|
382
|
+
false
|
383
|
+
end
|
384
|
+
|
385
|
+
protected
|
386
|
+
|
387
|
+
# If this is an replace instead of an insert, use replace instead
|
388
|
+
def _insert_sql
|
389
|
+
@opts[:replace] ? clause_sql(:replace) : super
|
390
|
+
end
|
391
|
+
|
392
|
+
private
|
393
|
+
|
394
|
+
# MySQL supports the ORDER BY and LIMIT clauses for DELETE statements
|
395
|
+
def delete_clause_methods
|
396
|
+
DELETE_CLAUSE_METHODS
|
397
|
+
end
|
398
|
+
|
399
|
+
# Consider the first table in the joined dataset is the table to delete
|
400
|
+
# from, but include the others for the purposes of selecting rows.
|
401
|
+
def delete_from_sql(sql)
|
402
|
+
if joined_dataset?
|
403
|
+
sql << " #{source_list(@opts[:from][0..0])} FROM #{source_list(@opts[:from])}"
|
404
|
+
select_join_sql(sql)
|
405
|
+
else
|
406
|
+
super
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
# MySQL supports the IGNORE and ON DUPLICATE KEY UPDATE clauses for INSERT statements
|
411
|
+
def insert_clause_methods
|
412
|
+
INSERT_CLAUSE_METHODS
|
413
|
+
end
|
414
|
+
alias replace_clause_methods insert_clause_methods
|
415
|
+
|
416
|
+
# MySQL doesn't use the SQL standard DEFAULT VALUES.
|
417
|
+
def insert_columns_sql(sql)
|
418
|
+
values = opts[:values]
|
419
|
+
if values.is_a?(Array) && values.empty?
|
420
|
+
sql << " ()"
|
421
|
+
else
|
422
|
+
super
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
# MySQL supports INSERT IGNORE INTO
|
427
|
+
def insert_ignore_sql(sql)
|
428
|
+
sql << " IGNORE" if opts[:insert_ignore]
|
429
|
+
end
|
430
|
+
|
431
|
+
# MySQL supports INSERT ... ON DUPLICATE KEY UPDATE
|
432
|
+
def insert_on_duplicate_key_update_sql(sql)
|
433
|
+
sql << on_duplicate_key_update_sql if opts[:on_duplicate_key_update]
|
434
|
+
end
|
435
|
+
|
436
|
+
# MySQL doesn't use the standard DEFAULT VALUES for empty values.
|
437
|
+
def insert_values_sql(sql)
|
438
|
+
values = opts[:values]
|
439
|
+
if values.is_a?(Array) && values.empty?
|
440
|
+
sql << " VALUES ()"
|
441
|
+
else
|
442
|
+
super
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
# MySQL allows a LIMIT in DELETE and UPDATE statements.
|
447
|
+
def limit_sql(sql)
|
448
|
+
sql << " LIMIT #{@opts[:limit]}" if @opts[:limit]
|
449
|
+
end
|
450
|
+
alias delete_limit_sql limit_sql
|
451
|
+
alias update_limit_sql limit_sql
|
452
|
+
|
453
|
+
# Use 0 for false on MySQL
|
454
|
+
def literal_false
|
455
|
+
BOOL_FALSE
|
456
|
+
end
|
457
|
+
|
458
|
+
# Use 1 for true on MySQL
|
459
|
+
def literal_true
|
460
|
+
BOOL_TRUE
|
461
|
+
end
|
462
|
+
|
463
|
+
# MySQL specific syntax for ON DUPLICATE KEY UPDATE
|
464
|
+
def on_duplicate_key_update_sql
|
465
|
+
if update_cols = opts[:on_duplicate_key_update]
|
466
|
+
update_vals = nil
|
467
|
+
|
468
|
+
if update_cols.empty?
|
469
|
+
update_cols = columns
|
470
|
+
elsif update_cols.last.is_a?(Hash)
|
471
|
+
update_vals = update_cols.last
|
472
|
+
update_cols = update_cols[0..-2]
|
473
|
+
end
|
474
|
+
|
475
|
+
updating = update_cols.map{|c| "#{quote_identifier(c)}=VALUES(#{quote_identifier(c)})" }
|
476
|
+
updating += update_vals.map{|c,v| "#{quote_identifier(c)}=#{literal(v)}" } if update_vals
|
477
|
+
|
478
|
+
" ON DUPLICATE KEY UPDATE #{updating.join(COMMA_SEPARATOR)}"
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
# MySQL does not support the SQL WITH clause for SELECT statements
|
483
|
+
def select_clause_methods
|
484
|
+
SELECT_CLAUSE_METHODS
|
485
|
+
end
|
486
|
+
|
487
|
+
# Support FOR SHARE locking when using the :share lock style.
|
488
|
+
def select_lock_sql(sql)
|
489
|
+
@opts[:lock] == :share ? (sql << FOR_SHARE) : super
|
490
|
+
end
|
491
|
+
|
492
|
+
# MySQL supports the ORDER BY and LIMIT clauses for UPDATE statements
|
493
|
+
def update_clause_methods
|
494
|
+
UPDATE_CLAUSE_METHODS
|
495
|
+
end
|
496
|
+
end
|
497
|
+
end
|
498
|
+
end
|