sequel 3.21.0 → 3.39.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 +1061 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +103 -63
- data/Rakefile +59 -27
- data/bin/sequel +50 -26
- data/doc/active_record.rdoc +67 -51
- data/doc/advanced_associations.rdoc +189 -75
- data/doc/association_basics.rdoc +327 -79
- data/doc/cheat_sheet.rdoc +21 -21
- data/doc/core_extensions.rdoc +374 -0
- data/doc/dataset_basics.rdoc +10 -10
- data/doc/dataset_filtering.rdoc +54 -42
- data/doc/mass_assignment.rdoc +56 -0
- data/doc/migration.rdoc +89 -557
- data/doc/model_hooks.rdoc +80 -29
- data/doc/object_model.rdoc +547 -0
- data/doc/opening_databases.rdoc +114 -53
- data/doc/prepared_statements.rdoc +41 -15
- data/doc/querying.rdoc +137 -56
- data/doc/reflection.rdoc +36 -10
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/release_notes/3.31.0.txt +146 -0
- data/doc/release_notes/3.32.0.txt +202 -0
- data/doc/release_notes/3.33.0.txt +157 -0
- data/doc/release_notes/3.34.0.txt +671 -0
- data/doc/release_notes/3.35.0.txt +144 -0
- data/doc/release_notes/3.36.0.txt +245 -0
- data/doc/release_notes/3.37.0.txt +338 -0
- data/doc/release_notes/3.38.0.txt +234 -0
- data/doc/release_notes/3.39.0.txt +237 -0
- data/doc/schema_modification.rdoc +585 -0
- data/doc/sharding.rdoc +99 -8
- data/doc/sql.rdoc +154 -112
- data/doc/testing.rdoc +169 -0
- data/doc/thread_safety.rdoc +17 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/validations.rdoc +1 -1
- data/doc/virtual_rows.rdoc +78 -43
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +17 -9
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +17 -11
- data/lib/sequel/adapters/do/postgres.rb +2 -61
- data/lib/sequel/adapters/do/sqlite.rb +0 -10
- data/lib/sequel/adapters/do.rb +18 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +453 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/db2.rb +56 -0
- data/lib/sequel/adapters/jdbc/derby.rb +325 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
- data/lib/sequel/adapters/jdbc/h2.rb +73 -31
- data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
- data/lib/sequel/adapters/jdbc/informix.rb +21 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
- data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
- data/lib/sequel/adapters/jdbc/progress.rb +21 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
- data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +279 -97
- data/lib/sequel/adapters/mock.rb +372 -0
- data/lib/sequel/adapters/mysql.rb +125 -222
- data/lib/sequel/adapters/mysql2.rb +65 -56
- data/lib/sequel/adapters/odbc/mssql.rb +17 -9
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +349 -53
- data/lib/sequel/adapters/postgres.rb +368 -138
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +343 -0
- data/lib/sequel/adapters/shared/firebird.rb +221 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +401 -116
- data/lib/sequel/adapters/shared/mysql.rb +424 -89
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
- data/lib/sequel/adapters/shared/oracle.rb +257 -45
- data/lib/sequel/adapters/shared/postgres.rb +675 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +285 -96
- data/lib/sequel/adapters/sqlite.rb +101 -39
- data/lib/sequel/adapters/swift/mysql.rb +10 -12
- data/lib/sequel/adapters/swift/postgres.rb +13 -79
- data/lib/sequel/adapters/swift/sqlite.rb +9 -6
- data/lib/sequel/adapters/swift.rb +28 -19
- data/lib/sequel/adapters/tinytds.rb +174 -22
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
- data/lib/sequel/adapters/utils/pg_types.rb +81 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
- data/lib/sequel/ast_transformer.rb +194 -0
- data/lib/sequel/connection_pool/sharded_single.rb +11 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +33 -2
- data/lib/sequel/connection_pool/single.rb +5 -0
- data/lib/sequel/connection_pool/threaded.rb +43 -14
- data/lib/sequel/connection_pool.rb +10 -2
- data/lib/sequel/core.rb +143 -11
- data/lib/sequel/database/connecting.rb +38 -28
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +63 -2
- data/lib/sequel/database/logging.rb +7 -2
- data/lib/sequel/database/misc.rb +190 -46
- data/lib/sequel/database/query.rb +272 -110
- data/lib/sequel/database/schema_generator.rb +103 -36
- data/lib/sequel/database/schema_methods.rb +300 -67
- data/lib/sequel/dataset/actions.rb +344 -90
- data/lib/sequel/dataset/features.rb +101 -8
- data/lib/sequel/dataset/graph.rb +42 -15
- data/lib/sequel/dataset/misc.rb +52 -25
- data/lib/sequel/dataset/mutation.rb +12 -7
- data/lib/sequel/dataset/prepared_statements.rb +68 -30
- data/lib/sequel/dataset/query.rb +305 -54
- data/lib/sequel/dataset/sql.rb +720 -292
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/_pretty_table.rb +83 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
- data/lib/sequel/extensions/blank.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +72 -0
- data/lib/sequel/extensions/constraint_validations.rb +451 -0
- data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
- data/lib/sequel/extensions/eval_inspect.rb +173 -0
- data/lib/sequel/extensions/inflector.rb +4 -0
- data/lib/sequel/extensions/looser_typecasting.rb +5 -4
- data/lib/sequel/extensions/migration.rb +100 -11
- data/lib/sequel/extensions/named_timezones.rb +9 -0
- data/lib/sequel/extensions/null_dataset.rb +94 -0
- data/lib/sequel/extensions/pagination.rb +4 -0
- data/lib/sequel/extensions/pg_array.rb +537 -0
- data/lib/sequel/extensions/pg_array_ops.rb +263 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
- data/lib/sequel/extensions/pg_hstore.rb +313 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
- data/lib/sequel/extensions/pg_inet.rb +113 -0
- data/lib/sequel/extensions/pg_interval.rb +191 -0
- data/lib/sequel/extensions/pg_json.rb +208 -0
- data/lib/sequel/extensions/pg_range.rb +512 -0
- data/lib/sequel/extensions/pg_range_ops.rb +150 -0
- data/lib/sequel/extensions/pg_row.rb +572 -0
- data/lib/sequel/extensions/pg_row_ops.rb +182 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
- data/lib/sequel/extensions/pretty_table.rb +17 -72
- data/lib/sequel/extensions/query.rb +8 -4
- data/lib/sequel/extensions/query_literals.rb +79 -0
- data/lib/sequel/extensions/schema_caching.rb +76 -0
- data/lib/sequel/extensions/schema_dumper.rb +282 -76
- data/lib/sequel/extensions/select_remove.rb +39 -0
- data/lib/sequel/extensions/server_block.rb +140 -0
- data/lib/sequel/extensions/split_array_nil.rb +65 -0
- data/lib/sequel/extensions/sql_expr.rb +8 -110
- data/lib/sequel/extensions/string_date_time.rb +4 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
- data/lib/sequel/extensions/to_dot.rb +99 -83
- data/lib/sequel/model/associations.rb +1263 -409
- data/lib/sequel/model/base.rb +624 -171
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +24 -2
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model.rb +30 -11
- data/lib/sequel/no_core_ext.rb +2 -0
- data/lib/sequel/plugins/active_model.rb +13 -1
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/caching.rb +25 -18
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/composition.rb +44 -12
- data/lib/sequel/plugins/constraint_validations.rb +198 -0
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +72 -0
- data/lib/sequel/plugins/dirty.rb +214 -0
- data/lib/sequel/plugins/eager_each.rb +59 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/plugins/identity_map.rb +134 -15
- data/lib/sequel/plugins/instance_filters.rb +10 -0
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +58 -6
- data/lib/sequel/plugins/list.rb +13 -2
- data/lib/sequel/plugins/many_through_many.rb +103 -51
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
- data/lib/sequel/plugins/nested_attributes.rb +150 -66
- data/lib/sequel/plugins/optimistic_locking.rb +8 -0
- data/lib/sequel/plugins/pg_row.rb +121 -0
- data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
- data/lib/sequel/plugins/prepared_statements.rb +167 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +31 -17
- data/lib/sequel/plugins/schema.rb +8 -3
- data/lib/sequel/plugins/serialization.rb +98 -49
- data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
- data/lib/sequel/plugins/static_cache.rb +99 -0
- data/lib/sequel/plugins/subclasses.rb +29 -3
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/tree.rb +3 -3
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/plugins/validation_helpers.rb +55 -4
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +649 -122
- data/lib/sequel/timezones.rb +67 -40
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/firebird_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +194 -66
- data/spec/adapters/mysql_spec.rb +475 -306
- data/spec/adapters/oracle_spec.rb +92 -117
- data/spec/adapters/postgres_spec.rb +1982 -325
- data/spec/adapters/spec_helper.rb +9 -6
- data/spec/adapters/sqlite_spec.rb +203 -75
- data/spec/core/connection_pool_spec.rb +218 -93
- data/spec/core/database_spec.rb +956 -522
- data/spec/core/dataset_spec.rb +1631 -1242
- data/spec/core/expression_filters_spec.rb +690 -334
- data/spec/core/mock_adapter_spec.rb +453 -0
- data/spec/core/object_graph_spec.rb +88 -129
- data/spec/core/schema_generator_spec.rb +4 -4
- data/spec/core/schema_spec.rb +556 -53
- data/spec/core/spec_helper.rb +7 -48
- data/spec/core_extensions_spec.rb +626 -0
- data/spec/extensions/active_model_spec.rb +13 -0
- data/spec/extensions/arbitrary_servers_spec.rb +110 -0
- data/spec/extensions/association_autoreloading_spec.rb +18 -10
- data/spec/extensions/association_dependencies_spec.rb +15 -25
- data/spec/extensions/association_pks_spec.rb +66 -32
- data/spec/extensions/association_proxies_spec.rb +4 -4
- data/spec/extensions/boolean_readers_spec.rb +25 -25
- data/spec/extensions/caching_spec.rb +47 -51
- data/spec/extensions/class_table_inheritance_spec.rb +31 -83
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
- data/spec/extensions/constraint_validations_spec.rb +316 -0
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +88 -0
- data/spec/extensions/dirty_spec.rb +155 -0
- data/spec/extensions/eager_each_spec.rb +34 -0
- data/spec/extensions/eval_inspect_spec.rb +67 -0
- data/spec/extensions/force_encoding_spec.rb +4 -2
- data/spec/extensions/hook_class_methods_spec.rb +97 -128
- data/spec/extensions/identity_map_spec.rb +142 -24
- data/spec/extensions/inflector_spec.rb +0 -4
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +72 -0
- data/spec/extensions/json_serializer_spec.rb +49 -12
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +49 -40
- data/spec/extensions/looser_typecasting_spec.rb +7 -7
- data/spec/extensions/many_through_many_spec.rb +368 -254
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
- data/spec/extensions/migration_spec.rb +140 -35
- data/spec/extensions/named_timezones_spec.rb +29 -11
- data/spec/extensions/nested_attributes_spec.rb +268 -89
- data/spec/extensions/null_dataset_spec.rb +85 -0
- data/spec/extensions/optimistic_locking_spec.rb +24 -21
- data/spec/extensions/pg_array_ops_spec.rb +112 -0
- data/spec/extensions/pg_array_spec.rb +320 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
- data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
- data/spec/extensions/pg_hstore_spec.rb +194 -0
- data/spec/extensions/pg_inet_spec.rb +47 -0
- data/spec/extensions/pg_interval_spec.rb +72 -0
- data/spec/extensions/pg_json_spec.rb +99 -0
- data/spec/extensions/pg_range_ops_spec.rb +56 -0
- data/spec/extensions/pg_range_spec.rb +395 -0
- data/spec/extensions/pg_row_ops_spec.rb +58 -0
- data/spec/extensions/pg_row_plugin_spec.rb +49 -0
- data/spec/extensions/pg_row_spec.rb +323 -0
- data/spec/extensions/pg_statement_cache_spec.rb +208 -0
- data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
- data/spec/extensions/prepared_statements_spec.rb +87 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +7 -6
- data/spec/extensions/query_literals_spec.rb +167 -0
- data/spec/extensions/query_spec.rb +9 -3
- data/spec/extensions/rcte_tree_spec.rb +50 -43
- data/spec/extensions/schema_caching_spec.rb +41 -0
- data/spec/extensions/schema_dumper_spec.rb +434 -49
- data/spec/extensions/schema_spec.rb +30 -49
- data/spec/extensions/select_remove_spec.rb +38 -0
- data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
- data/spec/extensions/serialization_spec.rb +86 -48
- data/spec/extensions/server_block_spec.rb +90 -0
- data/spec/extensions/sharding_spec.rb +69 -143
- data/spec/extensions/single_table_inheritance_spec.rb +23 -29
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +34 -67
- data/spec/extensions/split_array_nil_spec.rb +24 -0
- data/spec/extensions/sql_expr_spec.rb +29 -60
- data/spec/extensions/static_cache_spec.rb +145 -0
- data/spec/extensions/subclasses_spec.rb +14 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +8 -11
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/typecast_on_load_spec.rb +9 -6
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +58 -67
- data/spec/extensions/validation_helpers_spec.rb +31 -37
- data/spec/extensions/xml_serializer_spec.rb +29 -4
- data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
- data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
- data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
- data/spec/integration/associations_test.rb +659 -21
- data/spec/integration/database_test.rb +11 -5
- data/spec/integration/dataset_test.rb +717 -129
- data/spec/integration/eager_loader_test.rb +38 -54
- data/spec/integration/migrator_test.rb +2 -3
- data/spec/integration/model_test.rb +81 -2
- data/spec/integration/plugin_test.rb +604 -147
- data/spec/integration/prepared_statement_test.rb +272 -128
- data/spec/integration/schema_test.rb +367 -57
- data/spec/integration/spec_helper.rb +42 -32
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +31 -8
- data/spec/model/association_reflection_spec.rb +207 -8
- data/spec/model/associations_spec.rb +1206 -693
- data/spec/model/base_spec.rb +334 -100
- data/spec/model/dataset_methods_spec.rb +45 -27
- data/spec/model/eager_loading_spec.rb +828 -692
- data/spec/model/hooks_spec.rb +355 -67
- data/spec/model/model_spec.rb +251 -179
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +506 -232
- data/spec/model/spec_helper.rb +16 -77
- data/spec/model/validations_spec.rb +38 -2
- data/spec/rcov.opts +2 -0
- metadata +209 -80
- data/spec/core/core_sql_spec.rb +0 -451
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
Dataset::NON_SQL_OPTIONS << :insert_ignore
|
|
3
|
+
Dataset::NON_SQL_OPTIONS << :update_ignore
|
|
3
4
|
Dataset::NON_SQL_OPTIONS << :on_duplicate_key_update
|
|
4
5
|
|
|
5
6
|
module MySQL
|
|
@@ -30,8 +31,9 @@ module Sequel
|
|
|
30
31
|
module DatabaseMethods
|
|
31
32
|
AUTO_INCREMENT = 'AUTO_INCREMENT'.freeze
|
|
32
33
|
CAST_TYPES = {String=>:CHAR, Integer=>:SIGNED, Time=>:DATETIME, DateTime=>:DATETIME, Numeric=>:DECIMAL, BigDecimal=>:DECIMAL, File=>:BINARY}
|
|
33
|
-
COLUMN_DEFINITION_ORDER = [:null, :default, :unique, :primary_key, :auto_increment, :references]
|
|
34
|
+
COLUMN_DEFINITION_ORDER = [:collate, :null, :default, :unique, :primary_key, :auto_increment, :references]
|
|
34
35
|
PRIMARY = 'PRIMARY'.freeze
|
|
36
|
+
MYSQL_TIMESTAMP_RE = /\ACURRENT_(?:DATE|TIMESTAMP)?\z/
|
|
35
37
|
|
|
36
38
|
# MySQL's cast rules are restrictive in that you can't just cast to any possible
|
|
37
39
|
# database type.
|
|
@@ -50,6 +52,36 @@ module Sequel
|
|
|
50
52
|
:mysql
|
|
51
53
|
end
|
|
52
54
|
|
|
55
|
+
# Use the Information Schema's KEY_COLUMN_USAGE table to get
|
|
56
|
+
# basic information on foreign key columns, but include the
|
|
57
|
+
# constraint name.
|
|
58
|
+
def foreign_key_list(table, opts={})
|
|
59
|
+
m = output_identifier_meth
|
|
60
|
+
im = input_identifier_meth
|
|
61
|
+
ds = metadata_dataset.
|
|
62
|
+
from(:INFORMATION_SCHEMA__KEY_COLUMN_USAGE).
|
|
63
|
+
where(:TABLE_NAME=>im.call(table)).
|
|
64
|
+
exclude(:CONSTRAINT_NAME=>'PRIMARY').
|
|
65
|
+
exclude(:REFERENCED_TABLE_NAME=>nil).
|
|
66
|
+
select(:CONSTRAINT_NAME___name, :COLUMN_NAME___column, :REFERENCED_TABLE_NAME___table, :REFERENCED_COLUMN_NAME___key)
|
|
67
|
+
|
|
68
|
+
h = {}
|
|
69
|
+
ds.each do |row|
|
|
70
|
+
if r = h[row[:name]]
|
|
71
|
+
r[:columns] << m.call(row[:column])
|
|
72
|
+
r[:key] << m.call(row[:key])
|
|
73
|
+
else
|
|
74
|
+
h[row[:name]] = {:name=>m.call(row[:name]), :columns=>[m.call(row[:column])], :table=>m.call(row[:table]), :key=>[m.call(row[:key])]}
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
h.values
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# MySQL namespaces indexes per table.
|
|
81
|
+
def global_index_namespace?
|
|
82
|
+
false
|
|
83
|
+
end
|
|
84
|
+
|
|
53
85
|
# Use SHOW INDEX FROM to get the index information for the
|
|
54
86
|
# table.
|
|
55
87
|
#
|
|
@@ -79,27 +111,31 @@ module Sequel
|
|
|
79
111
|
|
|
80
112
|
# Get version of MySQL server, used for determined capabilities.
|
|
81
113
|
def server_version
|
|
82
|
-
|
|
83
|
-
|
|
114
|
+
@server_version ||= begin
|
|
115
|
+
m = /(\d+)\.(\d+)\.(\d+)/.match(get(SQL::Function.new(:version)))
|
|
116
|
+
(m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
|
|
117
|
+
end
|
|
84
118
|
end
|
|
85
119
|
|
|
86
|
-
#
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
# * :server - Set the server to use
|
|
90
|
-
def tables(opts={})
|
|
91
|
-
m = output_identifier_meth
|
|
92
|
-
metadata_dataset.with_sql('SHOW TABLES').server(opts[:server]).map{|r| m.call(r.values.first)}
|
|
120
|
+
# MySQL supports CREATE TABLE IF NOT EXISTS syntax.
|
|
121
|
+
def supports_create_table_if_not_exists?
|
|
122
|
+
true
|
|
93
123
|
end
|
|
94
124
|
|
|
95
125
|
# MySQL supports prepared transactions (two-phase commit) using XA
|
|
96
126
|
def supports_prepared_transactions?
|
|
97
|
-
|
|
127
|
+
server_version >= 50000
|
|
98
128
|
end
|
|
99
129
|
|
|
100
130
|
# MySQL supports savepoints
|
|
101
131
|
def supports_savepoints?
|
|
102
|
-
|
|
132
|
+
server_version >= 50000
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# MySQL doesn't support savepoints inside prepared transactions in from
|
|
136
|
+
# 5.5.12 to 5.5.23, see http://bugs.mysql.com/bug.php?id=64374
|
|
137
|
+
def supports_savepoints_in_prepared_transactions?
|
|
138
|
+
super && (server_version <= 50512 || server_version >= 50523)
|
|
103
139
|
end
|
|
104
140
|
|
|
105
141
|
# MySQL supports transaction isolation levels
|
|
@@ -107,6 +143,14 @@ module Sequel
|
|
|
107
143
|
true
|
|
108
144
|
end
|
|
109
145
|
|
|
146
|
+
# Return an array of symbols specifying table names in the current database.
|
|
147
|
+
#
|
|
148
|
+
# Options:
|
|
149
|
+
# * :server - Set the server to use
|
|
150
|
+
def tables(opts={})
|
|
151
|
+
full_tables('BASE TABLE', opts)
|
|
152
|
+
end
|
|
153
|
+
|
|
110
154
|
# Changes the database in use by issuing a USE statement. I would be
|
|
111
155
|
# very careful if I used this.
|
|
112
156
|
def use(db_name)
|
|
@@ -116,19 +160,60 @@ module Sequel
|
|
|
116
160
|
self
|
|
117
161
|
end
|
|
118
162
|
|
|
163
|
+
# Return an array of symbols specifying view names in the current database.
|
|
164
|
+
#
|
|
165
|
+
# Options:
|
|
166
|
+
# * :server - Set the server to use
|
|
167
|
+
def views(opts={})
|
|
168
|
+
full_tables('VIEW', opts)
|
|
169
|
+
end
|
|
170
|
+
|
|
119
171
|
private
|
|
120
172
|
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
|
|
173
|
+
# Preprocess the array of operations. If it looks like some operations depend
|
|
174
|
+
# on results of earlier operations and may require reloading the schema to
|
|
175
|
+
# work correctly, split those operations into separate lists, and between each
|
|
176
|
+
# list, remove the cached schema so that the later operations deal with the
|
|
177
|
+
# then current table schema.
|
|
178
|
+
def apply_alter_table(name, ops)
|
|
179
|
+
modified_columns = []
|
|
180
|
+
op_groups = [[]]
|
|
181
|
+
ops.each do |op|
|
|
182
|
+
case op[:op]
|
|
183
|
+
when :add_column, :set_column_type, :set_column_null, :set_column_default
|
|
184
|
+
if modified_columns.include?(op[:name])
|
|
185
|
+
op_groups << []
|
|
186
|
+
else
|
|
187
|
+
modified_columns << op[:name]
|
|
188
|
+
end
|
|
189
|
+
when :rename_column
|
|
190
|
+
if modified_columns.include?(op[:name]) || modified_columns.include?(op[:new_name])
|
|
191
|
+
op_groups << []
|
|
192
|
+
end
|
|
193
|
+
modified_columns << op[:name] unless modified_columns.include?(op[:name])
|
|
194
|
+
modified_columns << op[:new_name] unless modified_columns.include?(op[:new_name])
|
|
195
|
+
end
|
|
196
|
+
op_groups.last << op
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
op_groups.each do |ops|
|
|
200
|
+
next if ops.empty?
|
|
201
|
+
alter_table_sql_list(name, ops).each{|sql| execute_ddl(sql)}
|
|
202
|
+
remove_cached_schema(name)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Use MySQL specific syntax for some alter table operations.
|
|
207
|
+
def alter_table_op_sql(table, op)
|
|
124
208
|
case op[:op]
|
|
125
209
|
when :add_column
|
|
126
210
|
if related = op.delete(:table)
|
|
127
|
-
sql = super
|
|
211
|
+
sql = super
|
|
128
212
|
op[:table] = related
|
|
129
|
-
|
|
213
|
+
op[:key] ||= primary_key_from_schema(related)
|
|
214
|
+
sql << ", ADD FOREIGN KEY (#{quote_identifier(op[:name])})#{column_references_sql(op)}"
|
|
130
215
|
else
|
|
131
|
-
super
|
|
216
|
+
super
|
|
132
217
|
end
|
|
133
218
|
when :rename_column, :set_column_type, :set_column_null, :set_column_default
|
|
134
219
|
o = op[:op]
|
|
@@ -139,13 +224,71 @@ module Sequel
|
|
|
139
224
|
opts[:null] = o == :set_column_null ? op[:null] : opts[:allow_null]
|
|
140
225
|
opts[:default] = o == :set_column_default ? op[:default] : opts[:ruby_default]
|
|
141
226
|
opts.delete(:default) if opts[:default] == nil
|
|
142
|
-
"
|
|
227
|
+
"CHANGE COLUMN #{quote_identifier(op[:name])} #{column_definition_sql(op.merge(opts))}"
|
|
228
|
+
when :drop_constraint
|
|
229
|
+
type = case op[:type]
|
|
230
|
+
when :primary_key
|
|
231
|
+
"DROP PRIMARY KEY"
|
|
232
|
+
when :foreign_key
|
|
233
|
+
"DROP FOREIGN KEY #{quote_identifier(op[:name])}"
|
|
234
|
+
when :unique
|
|
235
|
+
"DROP INDEX #{quote_identifier(op[:name])}"
|
|
236
|
+
end
|
|
237
|
+
when :add_constraint
|
|
238
|
+
if op[:type] == :foreign_key
|
|
239
|
+
op[:key] ||= primary_key_from_schema(op[:table])
|
|
240
|
+
end
|
|
241
|
+
super
|
|
242
|
+
else
|
|
243
|
+
super
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# MySQL server requires table names when dropping indexes.
|
|
248
|
+
def alter_table_sql(table, op)
|
|
249
|
+
case op[:op]
|
|
143
250
|
when :drop_index
|
|
144
251
|
"#{drop_index_sql(table, op)} ON #{quote_schema_table(table)}"
|
|
145
252
|
else
|
|
146
|
-
super
|
|
253
|
+
super
|
|
147
254
|
end
|
|
148
255
|
end
|
|
256
|
+
|
|
257
|
+
# Handle MySQL specific default format.
|
|
258
|
+
def column_schema_normalize_default(default, type)
|
|
259
|
+
if column_schema_default_string_type?(type)
|
|
260
|
+
return if [:date, :datetime, :time].include?(type) && MYSQL_TIMESTAMP_RE.match(default)
|
|
261
|
+
default = "'#{default.gsub("'", "''").gsub('\\', '\\\\')}'"
|
|
262
|
+
end
|
|
263
|
+
super(default, type)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Don't allow combining adding foreign key operations with other
|
|
267
|
+
# operations, since in some cases adding a foreign key constraint in
|
|
268
|
+
# the same query as other operations results in MySQL error 150.
|
|
269
|
+
def combinable_alter_table_op?(op)
|
|
270
|
+
super && !(op[:op] == :add_constraint && op[:type] == :foreign_key)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# The SQL queries to execute on initial connection
|
|
274
|
+
def mysql_connection_setting_sqls
|
|
275
|
+
sqls = []
|
|
276
|
+
|
|
277
|
+
# Increase timeout so mysql server doesn't disconnect us
|
|
278
|
+
# Value used by default is maximum allowed value on Windows.
|
|
279
|
+
sqls << "SET @@wait_timeout = #{opts[:timeout] || 2147483}"
|
|
280
|
+
|
|
281
|
+
# By default, MySQL 'where id is null' selects the last inserted id
|
|
282
|
+
sqls << "SET SQL_AUTO_IS_NULL=0" unless opts[:auto_is_null]
|
|
283
|
+
|
|
284
|
+
# If the user has specified one or more sql modes, enable them
|
|
285
|
+
if sql_mode = opts[:sql_mode]
|
|
286
|
+
sql_mode = Array(sql_mode).join(',').upcase
|
|
287
|
+
sqls << "SET sql_mode = '#{sql_mode}'"
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
sqls
|
|
291
|
+
end
|
|
149
292
|
|
|
150
293
|
# Use MySQL specific AUTO_INCREMENT text.
|
|
151
294
|
def auto_increment_sql
|
|
@@ -161,9 +304,9 @@ module Sequel
|
|
|
161
304
|
# Use XA START to start a new prepared transaction if the :prepare
|
|
162
305
|
# option is given.
|
|
163
306
|
def begin_transaction(conn, opts={})
|
|
164
|
-
if s = opts[:prepare]
|
|
307
|
+
if (s = opts[:prepare]) && (th = _trans(conn))[:savepoint_level] == 0
|
|
165
308
|
log_connection_execute(conn, "XA START #{literal(s)}")
|
|
166
|
-
|
|
309
|
+
th[:savepoint_level] += 1
|
|
167
310
|
else
|
|
168
311
|
super
|
|
169
312
|
end
|
|
@@ -184,7 +327,7 @@ module Sequel
|
|
|
184
327
|
# Prepare the XA transaction for a two-phase commit if the
|
|
185
328
|
# :prepare option is given.
|
|
186
329
|
def commit_transaction(conn, opts={})
|
|
187
|
-
if s = opts[:prepare]
|
|
330
|
+
if (s = opts[:prepare]) && _trans(conn)[:savepoint_level] <= 1
|
|
188
331
|
log_connection_execute(conn, "XA END #{literal(s)}")
|
|
189
332
|
log_connection_execute(conn, "XA PREPARE #{literal(s)}")
|
|
190
333
|
else
|
|
@@ -197,14 +340,50 @@ module Sequel
|
|
|
197
340
|
engine = options.fetch(:engine, Sequel::MySQL.default_engine)
|
|
198
341
|
charset = options.fetch(:charset, Sequel::MySQL.default_charset)
|
|
199
342
|
collate = options.fetch(:collate, Sequel::MySQL.default_collate)
|
|
343
|
+
generator.constraints.sort_by{|c| (c[:type] == :primary_key) ? -1 : 1}
|
|
344
|
+
|
|
345
|
+
key_proc = lambda do |t|
|
|
346
|
+
if t == name
|
|
347
|
+
if pk = generator.primary_key_name
|
|
348
|
+
[pk]
|
|
349
|
+
elsif !(pkc = generator.constraints.select{|con| con[:type] == :primary_key}).empty?
|
|
350
|
+
pkc.first[:columns]
|
|
351
|
+
end
|
|
352
|
+
else
|
|
353
|
+
primary_key_from_schema(t)
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
generator.constraints.each do |c|
|
|
358
|
+
if c[:type] == :foreign_key
|
|
359
|
+
c[:key] ||= key_proc.call(c[:table])
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
200
363
|
generator.columns.each do |c|
|
|
201
364
|
if t = c.delete(:table)
|
|
202
|
-
|
|
365
|
+
same_table = t == name
|
|
366
|
+
k = c[:key]
|
|
367
|
+
|
|
368
|
+
key ||= key_proc.call(t)
|
|
369
|
+
|
|
370
|
+
if same_table && !k.nil?
|
|
371
|
+
generator.constraints.unshift(:type=>:unique, :columns=>Array(k))
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
generator.foreign_key([c[:name]], t, c.merge(:name=>nil, :type=>:foreign_key, :key=>key))
|
|
203
375
|
end
|
|
204
376
|
end
|
|
377
|
+
|
|
205
378
|
"#{super}#{" ENGINE=#{engine}" if engine}#{" DEFAULT CHARSET=#{charset}" if charset}#{" DEFAULT COLLATE=#{collate}" if collate}"
|
|
206
379
|
end
|
|
207
380
|
|
|
381
|
+
# Backbone of the tables and views support using SHOW FULL TABLES.
|
|
382
|
+
def full_tables(type, opts)
|
|
383
|
+
m = output_identifier_meth
|
|
384
|
+
metadata_dataset.with_sql('SHOW FULL TABLES').server(opts[:server]).map{|r| m.call(r.values.first) if r.delete(:Table_type) == type}.compact
|
|
385
|
+
end
|
|
386
|
+
|
|
208
387
|
# MySQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
|
|
209
388
|
def identifier_input_method_default
|
|
210
389
|
nil
|
|
@@ -230,9 +409,14 @@ module Sequel
|
|
|
230
409
|
"CREATE #{index_type}INDEX #{index_name}#{using} ON #{quote_schema_table(table_name)} #{literal(index[:columns])}"
|
|
231
410
|
end
|
|
232
411
|
|
|
412
|
+
# Parse the schema for the given table to get an array of primary key columns
|
|
413
|
+
def primary_key_from_schema(table)
|
|
414
|
+
schema(table).select{|a| a[1][:primary_key]}.map{|a| a[0]}
|
|
415
|
+
end
|
|
416
|
+
|
|
233
417
|
# Rollback the currently open XA transaction
|
|
234
418
|
def rollback_transaction(conn, opts={})
|
|
235
|
-
if s = opts[:prepare]
|
|
419
|
+
if (s = opts[:prepare]) && _trans(conn)[:savepoint_level] <= 1
|
|
236
420
|
log_connection_execute(conn, "XA END #{literal(s)}")
|
|
237
421
|
log_connection_execute(conn, "XA PREPARE #{literal(s)}")
|
|
238
422
|
log_connection_execute(conn, "XA ROLLBACK #{literal(s)}")
|
|
@@ -241,16 +425,23 @@ module Sequel
|
|
|
241
425
|
end
|
|
242
426
|
end
|
|
243
427
|
|
|
244
|
-
# MySQL
|
|
245
|
-
def
|
|
246
|
-
|
|
428
|
+
# Recognize MySQL set type.
|
|
429
|
+
def schema_column_type(db_type)
|
|
430
|
+
case db_type
|
|
431
|
+
when /\Aset/io
|
|
432
|
+
:set
|
|
433
|
+
else
|
|
434
|
+
super
|
|
435
|
+
end
|
|
247
436
|
end
|
|
248
437
|
|
|
249
438
|
# Use the MySQL specific DESCRIBE syntax to get a table description.
|
|
250
439
|
def schema_parse_table(table_name, opts)
|
|
251
|
-
m = output_identifier_meth
|
|
252
|
-
im = input_identifier_meth
|
|
253
|
-
|
|
440
|
+
m = output_identifier_meth(opts[:dataset])
|
|
441
|
+
im = input_identifier_meth(opts[:dataset])
|
|
442
|
+
table = SQL::Identifier.new(im.call(table_name))
|
|
443
|
+
table = SQL::QualifiedIdentifier.new(im.call(opts[:schema]), table) if opts[:schema]
|
|
444
|
+
metadata_dataset.with_sql("DESCRIBE ?", table).map do |row|
|
|
254
445
|
row[:auto_increment] = true if row.delete(:Extra).to_s =~ /auto_increment/io
|
|
255
446
|
row[:allow_null] = row.delete(:Null) == 'YES'
|
|
256
447
|
row[:default] = row.delete(:Default)
|
|
@@ -262,6 +453,11 @@ module Sequel
|
|
|
262
453
|
end
|
|
263
454
|
end
|
|
264
455
|
|
|
456
|
+
# MySQL can combine multiple alter table ops into a single query.
|
|
457
|
+
def supports_combining_alter_table_ops?
|
|
458
|
+
true
|
|
459
|
+
end
|
|
460
|
+
|
|
265
461
|
# Respect the :size option if given to produce
|
|
266
462
|
# tinyblob, mediumblob, and longblob if :tiny,
|
|
267
463
|
# :medium, or :long is given.
|
|
@@ -281,13 +477,17 @@ module Sequel
|
|
|
281
477
|
# MySQL has both datetime and timestamp classes, most people are going
|
|
282
478
|
# to want datetime
|
|
283
479
|
def type_literal_generic_datetime(column)
|
|
284
|
-
:
|
|
480
|
+
if column[:default] == Sequel::CURRENT_TIMESTAMP
|
|
481
|
+
:timestamp
|
|
482
|
+
else
|
|
483
|
+
:datetime
|
|
484
|
+
end
|
|
285
485
|
end
|
|
286
486
|
|
|
287
487
|
# MySQL has both datetime and timestamp classes, most people are going
|
|
288
488
|
# to want datetime
|
|
289
489
|
def type_literal_generic_time(column)
|
|
290
|
-
column[:only_time] ? :time :
|
|
490
|
+
column[:only_time] ? :time : type_literal_generic_datetime(column)
|
|
291
491
|
end
|
|
292
492
|
|
|
293
493
|
# MySQL doesn't have a true boolean class, so it uses tinyint(1)
|
|
@@ -303,27 +503,82 @@ module Sequel
|
|
|
303
503
|
COMMA_SEPARATOR = ', '.freeze
|
|
304
504
|
FOR_SHARE = ' LOCK IN SHARE MODE'.freeze
|
|
305
505
|
SQL_CALC_FOUND_ROWS = ' SQL_CALC_FOUND_ROWS'.freeze
|
|
306
|
-
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'from where order limit')
|
|
307
|
-
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'ignore into columns values on_duplicate_key_update')
|
|
308
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'distinct calc_found_rows columns from join where group having compounds order limit lock')
|
|
309
|
-
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'table set where order limit')
|
|
506
|
+
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'delete from where order limit')
|
|
507
|
+
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert ignore into columns values on_duplicate_key_update')
|
|
508
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct calc_found_rows columns from join where group having compounds order limit lock')
|
|
509
|
+
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'update ignore table set where order limit')
|
|
510
|
+
APOS = Dataset::APOS
|
|
511
|
+
APOS_RE = Dataset::APOS_RE
|
|
512
|
+
DOUBLE_APOS = Dataset::DOUBLE_APOS
|
|
513
|
+
SPACE = Dataset::SPACE
|
|
514
|
+
PAREN_OPEN = Dataset::PAREN_OPEN
|
|
515
|
+
PAREN_CLOSE = Dataset::PAREN_CLOSE
|
|
516
|
+
NOT_SPACE = Dataset::NOT_SPACE
|
|
517
|
+
FROM = Dataset::FROM
|
|
518
|
+
INSERT = Dataset::INSERT
|
|
519
|
+
COMMA = Dataset::COMMA
|
|
520
|
+
LIMIT = Dataset::LIMIT
|
|
521
|
+
GROUP_BY = Dataset::GROUP_BY
|
|
522
|
+
REGEXP = 'REGEXP'.freeze
|
|
523
|
+
LIKE = 'LIKE'.freeze
|
|
524
|
+
BINARY = 'BINARY '.freeze
|
|
525
|
+
CONCAT = "CONCAT".freeze
|
|
526
|
+
CAST_BITCOMP_OPEN = "CAST(~".freeze
|
|
527
|
+
CAST_BITCOMP_CLOSE = " AS SIGNED INTEGER)".freeze
|
|
528
|
+
STRAIGHT_JOIN = 'STRAIGHT_JOIN'.freeze
|
|
529
|
+
NATURAL_LEFT_JOIN = 'NATURAL LEFT JOIN'.freeze
|
|
530
|
+
BACKTICK = '`'.freeze
|
|
531
|
+
BACKTICK_RE = /`/.freeze
|
|
532
|
+
DOUBLE_BACKTICK = '``'.freeze
|
|
533
|
+
EMPTY_COLUMNS = " ()".freeze
|
|
534
|
+
EMPTY_VALUES = " VALUES ()".freeze
|
|
535
|
+
IGNORE = " IGNORE".freeze
|
|
536
|
+
REPLACE = 'REPLACE'.freeze
|
|
537
|
+
ON_DUPLICATE_KEY_UPDATE = " ON DUPLICATE KEY UPDATE ".freeze
|
|
538
|
+
EQ_VALUES = '=VALUES('.freeze
|
|
539
|
+
EQ = '='.freeze
|
|
540
|
+
WITH_ROLLUP = ' WITH ROLLUP'.freeze
|
|
541
|
+
MATCH_AGAINST = ["(MATCH ".freeze, " AGAINST (".freeze, "))".freeze].freeze
|
|
542
|
+
MATCH_AGAINST_BOOLEAN = ["(MATCH ".freeze, " AGAINST (".freeze, " IN BOOLEAN MODE))".freeze].freeze
|
|
543
|
+
EXPLAIN = 'EXPLAIN '.freeze
|
|
544
|
+
EXPLAIN_EXTENDED = 'EXPLAIN EXTENDED '.freeze
|
|
545
|
+
BACKSLASH_RE = /\\/.freeze
|
|
546
|
+
QUAD_BACKSLASH = "\\\\\\\\".freeze
|
|
310
547
|
|
|
311
548
|
# MySQL specific syntax for LIKE/REGEXP searches, as well as
|
|
312
549
|
# string concatenation.
|
|
313
|
-
def
|
|
550
|
+
def complex_expression_sql_append(sql, op, args)
|
|
314
551
|
case op
|
|
552
|
+
when :IN, :"NOT IN"
|
|
553
|
+
ds = args.at(1)
|
|
554
|
+
if ds.is_a?(Sequel::Dataset) && ds.opts[:limit]
|
|
555
|
+
super(sql, op, [args.at(0), ds.from_self])
|
|
556
|
+
else
|
|
557
|
+
super
|
|
558
|
+
end
|
|
315
559
|
when :~, :'!~', :'~*', :'!~*', :LIKE, :'NOT LIKE', :ILIKE, :'NOT ILIKE'
|
|
316
|
-
|
|
560
|
+
sql << PAREN_OPEN
|
|
561
|
+
literal_append(sql, args.at(0))
|
|
562
|
+
sql << SPACE
|
|
563
|
+
sql << 'NOT ' if [:'NOT LIKE', :'NOT ILIKE', :'!~', :'!~*'].include?(op)
|
|
564
|
+
sql << ([:~, :'!~', :'~*', :'!~*'].include?(op) ? REGEXP : LIKE)
|
|
565
|
+
sql << SPACE
|
|
566
|
+
sql << BINARY if [:~, :'!~', :LIKE, :'NOT LIKE'].include?(op)
|
|
567
|
+
literal_append(sql, args.at(1))
|
|
568
|
+
sql << PAREN_CLOSE
|
|
317
569
|
when :'||'
|
|
318
570
|
if args.length > 1
|
|
319
|
-
|
|
571
|
+
sql << CONCAT
|
|
572
|
+
array_sql_append(sql, args)
|
|
320
573
|
else
|
|
321
|
-
|
|
574
|
+
literal_append(sql, args.at(0))
|
|
322
575
|
end
|
|
323
576
|
when :'B~'
|
|
324
|
-
|
|
577
|
+
sql << CAST_BITCOMP_OPEN
|
|
578
|
+
literal_append(sql, args.at(0))
|
|
579
|
+
sql << CAST_BITCOMP_CLOSE
|
|
325
580
|
else
|
|
326
|
-
super
|
|
581
|
+
super
|
|
327
582
|
end
|
|
328
583
|
end
|
|
329
584
|
|
|
@@ -340,6 +595,17 @@ module Sequel
|
|
|
340
595
|
clone(:calc_found_rows => true)
|
|
341
596
|
end
|
|
342
597
|
|
|
598
|
+
# Return the results of an EXPLAIN query as a string. Options:
|
|
599
|
+
# :extended :: Use EXPLAIN EXPTENDED instead of EXPLAIN if true.
|
|
600
|
+
def explain(opts={})
|
|
601
|
+
# Load the PrettyTable class, needed for explain output
|
|
602
|
+
Sequel.extension(:_pretty_table) unless defined?(Sequel::PrettyTable)
|
|
603
|
+
|
|
604
|
+
ds = db.send(:metadata_dataset).with_sql((opts[:extended] ? EXPLAIN_EXTENDED : EXPLAIN) + select_sql).naked
|
|
605
|
+
rows = ds.all
|
|
606
|
+
Sequel::PrettyTable.string(rows, ds.columns)
|
|
607
|
+
end
|
|
608
|
+
|
|
343
609
|
# Return a cloned dataset which will use LOCK IN SHARE MODE to lock returned rows.
|
|
344
610
|
def for_share
|
|
345
611
|
lock_style(:share)
|
|
@@ -351,8 +617,9 @@ module Sequel
|
|
|
351
617
|
end
|
|
352
618
|
|
|
353
619
|
# MySQL specific full text search syntax.
|
|
354
|
-
def full_text_sql(cols,
|
|
355
|
-
|
|
620
|
+
def full_text_sql(cols, terms, opts = {})
|
|
621
|
+
terms = terms.join(' ') if terms.is_a?(Array)
|
|
622
|
+
SQL::PlaceholderLiteralString.new((opts[:boolean] ? MATCH_AGAINST_BOOLEAN : MATCH_AGAINST), [Array(cols), terms])
|
|
356
623
|
end
|
|
357
624
|
|
|
358
625
|
# MySQL allows HAVING clause on ungrouped datasets.
|
|
@@ -372,9 +639,12 @@ module Sequel
|
|
|
372
639
|
# STRAIGHT_JOIN.
|
|
373
640
|
def join_type_sql(join_type)
|
|
374
641
|
case join_type
|
|
375
|
-
when :straight
|
|
376
|
-
|
|
377
|
-
|
|
642
|
+
when :straight
|
|
643
|
+
STRAIGHT_JOIN
|
|
644
|
+
when :natural_inner
|
|
645
|
+
NATURAL_LEFT_JOIN
|
|
646
|
+
else
|
|
647
|
+
super
|
|
378
648
|
end
|
|
379
649
|
end
|
|
380
650
|
|
|
@@ -389,7 +659,7 @@ module Sequel
|
|
|
389
659
|
def insert_ignore
|
|
390
660
|
clone(:insert_ignore=>true)
|
|
391
661
|
end
|
|
392
|
-
|
|
662
|
+
|
|
393
663
|
# Sets up the insert methods to use ON DUPLICATE KEY UPDATE
|
|
394
664
|
# If you pass no arguments, ALL fields will be
|
|
395
665
|
# updated with the new values. If you pass the fields you
|
|
@@ -415,25 +685,31 @@ module Sequel
|
|
|
415
685
|
|
|
416
686
|
# MySQL specific syntax for inserting multiple values at once.
|
|
417
687
|
def multi_insert_sql(columns, values)
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
# MySQL uses the number of rows actually modified in the update,
|
|
422
|
-
# instead of the number of matched by the filter.
|
|
423
|
-
def provides_accurate_rows_matched?
|
|
424
|
-
false
|
|
688
|
+
sql = LiteralString.new('VALUES ')
|
|
689
|
+
expression_list_append(sql, values.map{|r| Array(r)})
|
|
690
|
+
[insert_sql(columns, sql)]
|
|
425
691
|
end
|
|
426
692
|
|
|
427
693
|
# MySQL uses the nonstandard ` (backtick) for quoting identifiers.
|
|
428
|
-
def
|
|
429
|
-
|
|
694
|
+
def quoted_identifier_append(sql, c)
|
|
695
|
+
sql << BACKTICK << c.to_s.gsub(BACKTICK_RE, DOUBLE_BACKTICK) << BACKTICK
|
|
430
696
|
end
|
|
431
697
|
|
|
698
|
+
# Execute a REPLACE statement on the database.
|
|
699
|
+
def replace(*values)
|
|
700
|
+
execute_insert(replace_sql(*values))
|
|
701
|
+
end
|
|
702
|
+
|
|
432
703
|
# MySQL specific syntax for REPLACE (aka UPSERT, or update if exists,
|
|
433
704
|
# insert if it doesn't).
|
|
434
705
|
def replace_sql(*values)
|
|
435
706
|
clone(:replace=>true).insert_sql(*values)
|
|
436
707
|
end
|
|
708
|
+
|
|
709
|
+
# Replace multiple rows in a single query.
|
|
710
|
+
def multi_replace(*values)
|
|
711
|
+
clone(:replace=>true).multi_insert(*values)
|
|
712
|
+
end
|
|
437
713
|
|
|
438
714
|
# MySQL can emulate DISTINCT ON with its non-standard GROUP BY implementation,
|
|
439
715
|
# though the rows returned cannot be made deterministic through ordering.
|
|
@@ -441,6 +717,11 @@ module Sequel
|
|
|
441
717
|
true
|
|
442
718
|
end
|
|
443
719
|
|
|
720
|
+
# MySQL supports GROUP BY WITH ROLLUP (but not CUBE)
|
|
721
|
+
def supports_group_rollup?
|
|
722
|
+
true
|
|
723
|
+
end
|
|
724
|
+
|
|
444
725
|
# MySQL does not support INTERSECT or EXCEPT
|
|
445
726
|
def supports_intersect_except?
|
|
446
727
|
false
|
|
@@ -451,6 +732,17 @@ module Sequel
|
|
|
451
732
|
true
|
|
452
733
|
end
|
|
453
734
|
|
|
735
|
+
# MySQL's DISTINCT ON emulation using GROUP BY does not respect the
|
|
736
|
+
# queries ORDER BY clause.
|
|
737
|
+
def supports_ordered_distinct_on?
|
|
738
|
+
false
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
# MySQL supports pattern matching via regular expressions
|
|
742
|
+
def supports_regexp?
|
|
743
|
+
true
|
|
744
|
+
end
|
|
745
|
+
|
|
454
746
|
# MySQL does support fractional timestamps in literal timestamps, but it
|
|
455
747
|
# ignores them. Also, using them seems to cause problems on 1.9. Since
|
|
456
748
|
# they are ignored anyway, not using them is probably best.
|
|
@@ -458,13 +750,16 @@ module Sequel
|
|
|
458
750
|
false
|
|
459
751
|
end
|
|
460
752
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
#
|
|
464
|
-
|
|
465
|
-
|
|
753
|
+
# Sets up the update methods to use UPDATE IGNORE.
|
|
754
|
+
# Useful if you have a unique key and want to just skip
|
|
755
|
+
# updating rows that violate the unique key restriction.
|
|
756
|
+
#
|
|
757
|
+
# dataset.update_ignore.update({:name => 'a', :value => 1})
|
|
758
|
+
# # UPDATE IGNORE tablename SET name = 'a', value = 1
|
|
759
|
+
def update_ignore
|
|
760
|
+
clone(:update_ignore=>true)
|
|
466
761
|
end
|
|
467
|
-
|
|
762
|
+
|
|
468
763
|
private
|
|
469
764
|
|
|
470
765
|
# MySQL supports the ORDER BY and LIMIT clauses for DELETE statements
|
|
@@ -476,7 +771,10 @@ module Sequel
|
|
|
476
771
|
# from, but include the others for the purposes of selecting rows.
|
|
477
772
|
def delete_from_sql(sql)
|
|
478
773
|
if joined_dataset?
|
|
479
|
-
sql <<
|
|
774
|
+
sql << SPACE
|
|
775
|
+
source_list_append(sql, @opts[:from][0..0])
|
|
776
|
+
sql << FROM
|
|
777
|
+
source_list_append(sql, @opts[:from])
|
|
480
778
|
select_join_sql(sql)
|
|
481
779
|
else
|
|
482
780
|
super
|
|
@@ -493,7 +791,7 @@ module Sequel
|
|
|
493
791
|
def insert_columns_sql(sql)
|
|
494
792
|
values = opts[:values]
|
|
495
793
|
if values.is_a?(Array) && values.empty?
|
|
496
|
-
sql <<
|
|
794
|
+
sql << EMPTY_COLUMNS
|
|
497
795
|
else
|
|
498
796
|
super
|
|
499
797
|
end
|
|
@@ -501,19 +799,62 @@ module Sequel
|
|
|
501
799
|
|
|
502
800
|
# MySQL supports INSERT IGNORE INTO
|
|
503
801
|
def insert_ignore_sql(sql)
|
|
504
|
-
sql <<
|
|
802
|
+
sql << IGNORE if opts[:insert_ignore]
|
|
803
|
+
end
|
|
804
|
+
|
|
805
|
+
# MySQL supports UPDATE IGNORE
|
|
806
|
+
def update_ignore_sql(sql)
|
|
807
|
+
sql << IGNORE if opts[:update_ignore]
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
# If this is an replace instead of an insert, use replace instead
|
|
811
|
+
def insert_insert_sql(sql)
|
|
812
|
+
sql << (@opts[:replace] ? REPLACE : INSERT)
|
|
505
813
|
end
|
|
506
814
|
|
|
507
815
|
# MySQL supports INSERT ... ON DUPLICATE KEY UPDATE
|
|
508
816
|
def insert_on_duplicate_key_update_sql(sql)
|
|
509
|
-
|
|
817
|
+
if update_cols = opts[:on_duplicate_key_update]
|
|
818
|
+
update_vals = nil
|
|
819
|
+
|
|
820
|
+
if update_cols.empty?
|
|
821
|
+
update_cols = columns
|
|
822
|
+
elsif update_cols.last.is_a?(Hash)
|
|
823
|
+
update_vals = update_cols.last
|
|
824
|
+
update_cols = update_cols[0..-2]
|
|
825
|
+
end
|
|
826
|
+
|
|
827
|
+
sql << ON_DUPLICATE_KEY_UPDATE
|
|
828
|
+
c = false
|
|
829
|
+
co = COMMA
|
|
830
|
+
values = EQ_VALUES
|
|
831
|
+
endp = PAREN_CLOSE
|
|
832
|
+
update_cols.each do |col|
|
|
833
|
+
sql << co if c
|
|
834
|
+
quote_identifier_append(sql, col)
|
|
835
|
+
sql << values
|
|
836
|
+
quote_identifier_append(sql, col)
|
|
837
|
+
sql << endp
|
|
838
|
+
c ||= true
|
|
839
|
+
end
|
|
840
|
+
if update_vals
|
|
841
|
+
eq = EQ
|
|
842
|
+
update_vals.map do |col,v|
|
|
843
|
+
sql << co if c
|
|
844
|
+
quote_identifier_append(sql, col)
|
|
845
|
+
sql << eq
|
|
846
|
+
literal_append(sql, v)
|
|
847
|
+
c ||= true
|
|
848
|
+
end
|
|
849
|
+
end
|
|
850
|
+
end
|
|
510
851
|
end
|
|
511
852
|
|
|
512
853
|
# MySQL doesn't use the standard DEFAULT VALUES for empty values.
|
|
513
854
|
def insert_values_sql(sql)
|
|
514
855
|
values = opts[:values]
|
|
515
856
|
if values.is_a?(Array) && values.empty?
|
|
516
|
-
sql <<
|
|
857
|
+
sql << EMPTY_VALUES
|
|
517
858
|
else
|
|
518
859
|
super
|
|
519
860
|
end
|
|
@@ -521,7 +862,10 @@ module Sequel
|
|
|
521
862
|
|
|
522
863
|
# MySQL allows a LIMIT in DELETE and UPDATE statements.
|
|
523
864
|
def limit_sql(sql)
|
|
524
|
-
|
|
865
|
+
if l = @opts[:limit]
|
|
866
|
+
sql << LIMIT
|
|
867
|
+
literal_append(sql, @opts[:limit])
|
|
868
|
+
end
|
|
525
869
|
end
|
|
526
870
|
alias delete_limit_sql limit_sql
|
|
527
871
|
alias update_limit_sql limit_sql
|
|
@@ -531,30 +875,16 @@ module Sequel
|
|
|
531
875
|
BOOL_FALSE
|
|
532
876
|
end
|
|
533
877
|
|
|
878
|
+
# SQL fragment for String. Doubles \ and ' by default.
|
|
879
|
+
def literal_string_append(sql, v)
|
|
880
|
+
sql << APOS << v.gsub(BACKSLASH_RE, QUAD_BACKSLASH).gsub(APOS_RE, DOUBLE_APOS) << APOS
|
|
881
|
+
end
|
|
882
|
+
|
|
534
883
|
# Use 1 for true on MySQL
|
|
535
884
|
def literal_true
|
|
536
885
|
BOOL_TRUE
|
|
537
886
|
end
|
|
538
887
|
|
|
539
|
-
# MySQL specific syntax for ON DUPLICATE KEY UPDATE
|
|
540
|
-
def on_duplicate_key_update_sql
|
|
541
|
-
if update_cols = opts[:on_duplicate_key_update]
|
|
542
|
-
update_vals = nil
|
|
543
|
-
|
|
544
|
-
if update_cols.empty?
|
|
545
|
-
update_cols = columns
|
|
546
|
-
elsif update_cols.last.is_a?(Hash)
|
|
547
|
-
update_vals = update_cols.last
|
|
548
|
-
update_cols = update_cols[0..-2]
|
|
549
|
-
end
|
|
550
|
-
|
|
551
|
-
updating = update_cols.map{|c| "#{quote_identifier(c)}=VALUES(#{quote_identifier(c)})" }
|
|
552
|
-
updating += update_vals.map{|c,v| "#{quote_identifier(c)}=#{literal(v)}" } if update_vals
|
|
553
|
-
|
|
554
|
-
" ON DUPLICATE KEY UPDATE #{updating.join(COMMA_SEPARATOR)}"
|
|
555
|
-
end
|
|
556
|
-
end
|
|
557
|
-
|
|
558
888
|
# MySQL does not support the SQL WITH clause for SELECT statements
|
|
559
889
|
def select_clause_methods
|
|
560
890
|
SELECT_CLAUSE_METHODS
|
|
@@ -574,6 +904,11 @@ module Sequel
|
|
|
574
904
|
def update_clause_methods
|
|
575
905
|
UPDATE_CLAUSE_METHODS
|
|
576
906
|
end
|
|
907
|
+
|
|
908
|
+
# MySQL uses WITH ROLLUP syntax.
|
|
909
|
+
def uses_with_rollup?
|
|
910
|
+
true
|
|
911
|
+
end
|
|
577
912
|
end
|
|
578
913
|
end
|
|
579
914
|
end
|