viking-sequel 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|