sequel 3.21.0 → 3.41.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 +1131 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +106 -65
- data/Rakefile +59 -27
- data/bin/sequel +50 -26
- data/doc/active_record.rdoc +68 -52
- data/doc/advanced_associations.rdoc +201 -75
- data/doc/association_basics.rdoc +327 -79
- data/doc/bin_sequel.rdoc +144 -0
- 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 +576 -0
- data/doc/opening_databases.rdoc +121 -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/release_notes/3.40.0.txt +73 -0
- data/doc/release_notes/3.41.0.txt +155 -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/access.rb +334 -0
- data/lib/sequel/adapters/ado/mssql.rb +18 -12
- data/lib/sequel/adapters/ado.rb +69 -21
- data/lib/sequel/adapters/amalgalite.rb +17 -14
- data/lib/sequel/adapters/cubrid.rb +143 -0
- data/lib/sequel/adapters/db2.rb +178 -85
- data/lib/sequel/adapters/dbi.rb +19 -21
- 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 +20 -39
- data/lib/sequel/adapters/firebird.rb +33 -210
- data/lib/sequel/adapters/ibmdb.rb +452 -0
- data/lib/sequel/adapters/informix.rb +6 -29
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/cubrid.rb +52 -0
- data/lib/sequel/adapters/jdbc/db2.rb +78 -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 +68 -31
- data/lib/sequel/adapters/jdbc/hsqldb.rb +189 -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 +16 -28
- 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 +72 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +308 -114
- data/lib/sequel/adapters/mock.rb +372 -0
- data/lib/sequel/adapters/mysql.rb +130 -230
- data/lib/sequel/adapters/mysql2.rb +64 -63
- data/lib/sequel/adapters/odbc/mssql.rb +17 -9
- data/lib/sequel/adapters/odbc.rb +29 -10
- data/lib/sequel/adapters/openbase.rb +7 -11
- data/lib/sequel/adapters/oracle.rb +364 -57
- data/lib/sequel/adapters/postgres.rb +379 -145
- data/lib/sequel/adapters/shared/access.rb +236 -11
- data/lib/sequel/adapters/shared/cubrid.rb +216 -0
- data/lib/sequel/adapters/shared/db2.rb +353 -0
- data/lib/sequel/adapters/shared/firebird.rb +231 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +412 -116
- data/lib/sequel/adapters/shared/mysql.rb +396 -89
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +166 -0
- data/lib/sequel/adapters/shared/oracle.rb +262 -45
- data/lib/sequel/adapters/shared/postgres.rb +688 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +317 -96
- data/lib/sequel/adapters/sqlite.rb +109 -46
- 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 +31 -27
- data/lib/sequel/adapters/tinytds.rb +177 -25
- 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/split_alter_table.rb +36 -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 +23 -13
- data/lib/sequel/connection_pool/sharded_threaded.rb +64 -13
- data/lib/sequel/connection_pool/single.rb +11 -3
- data/lib/sequel/connection_pool/threaded.rb +69 -20
- data/lib/sequel/connection_pool.rb +20 -18
- data/lib/sequel/core.rb +143 -11
- data/lib/sequel/database/connecting.rb +66 -29
- 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 +192 -51
- data/lib/sequel/database/query.rb +299 -114
- data/lib/sequel/database/schema_generator.rb +103 -36
- data/lib/sequel/database/schema_methods.rb +310 -69
- 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 +322 -68
- data/lib/sequel/dataset/sql.rb +716 -293
- 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/connection_validator.rb +109 -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 +539 -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 +334 -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 +212 -0
- data/lib/sequel/extensions/pg_range.rb +513 -0
- data/lib/sequel/extensions/pg_range_ops.rb +150 -0
- data/lib/sequel/extensions/pg_row.rb +576 -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 +137 -16
- 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 +77 -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 +168 -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 +57 -12
- 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/touch.rb +18 -6
- 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 +2 -1
- data/lib/sequel/plugins/validation_helpers.rb +57 -4
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +721 -141
- 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 +53 -39
- data/spec/adapters/mssql_spec.rb +261 -66
- data/spec/adapters/mysql_spec.rb +597 -352
- data/spec/adapters/oracle_spec.rb +92 -117
- data/spec/adapters/postgres_spec.rb +2103 -341
- data/spec/adapters/spec_helper.rb +16 -6
- data/spec/adapters/sqlite_spec.rb +237 -76
- data/spec/core/connection_pool_spec.rb +270 -141
- data/spec/core/database_spec.rb +980 -524
- data/spec/core/dataset_spec.rb +1658 -1243
- data/spec/core/expression_filters_spec.rb +741 -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 +227 -33
- 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/connection_validator_spec.rb +118 -0
- 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 +324 -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 +200 -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 +340 -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 +65 -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 +53 -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 +77 -68
- data/spec/extensions/validation_helpers_spec.rb +48 -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 +25 -5
- data/spec/integration/dataset_test.rb +729 -141
- 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 +871 -160
- data/spec/integration/prepared_statement_test.rb +272 -128
- data/spec/integration/schema_test.rb +377 -57
- data/spec/integration/spec_helper.rb +49 -32
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +40 -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 +223 -81
- data/spec/core/core_sql_spec.rb +0 -451
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# The query_literals extension changes Sequel's default behavior of
|
|
2
|
+
# the select, order and group methods so that if the first argument
|
|
3
|
+
# is a regular string, it is treated as a literal string, with the
|
|
4
|
+
# rest of the arguments (if any) treated as placeholder values. This
|
|
5
|
+
# allows you to write code such as:
|
|
6
|
+
#
|
|
7
|
+
# DB[:table].select('a, b, ?', 2).group('a, b').order('c')
|
|
8
|
+
#
|
|
9
|
+
# The default Sequel behavior would literalize that as:
|
|
10
|
+
#
|
|
11
|
+
# SELECT 'a, b, ?', 2 FROM table GROUP BY 'a, b' ORDER BY 'c'
|
|
12
|
+
#
|
|
13
|
+
# Using this extension changes the literalization to:
|
|
14
|
+
#
|
|
15
|
+
# SELECT a, b, 2, FROM table GROUP BY a, b ORDER BY c
|
|
16
|
+
#
|
|
17
|
+
# This extension makes select, group, and order methods operate
|
|
18
|
+
# like filter methods, which support the same interface.
|
|
19
|
+
#
|
|
20
|
+
# There are very few places where Sequel's default behavior is
|
|
21
|
+
# desirable in this area, but for backwards compatibility, the
|
|
22
|
+
# defaults won't be changed until the next major release.
|
|
23
|
+
#
|
|
24
|
+
# You can load this extension into specific datasets:
|
|
25
|
+
#
|
|
26
|
+
# ds = DB[:table]
|
|
27
|
+
# ds.extension(:query_literals)
|
|
28
|
+
#
|
|
29
|
+
# Or you can load it into all of a database's datasets, which
|
|
30
|
+
# is probably the desired behavior if you are using this extension:
|
|
31
|
+
#
|
|
32
|
+
# DB.extension(:query_literals)
|
|
33
|
+
|
|
34
|
+
module Sequel
|
|
35
|
+
# The QueryLiterals module can be used to make select, group, and
|
|
36
|
+
# order methods operate similar to the filter methods if the first
|
|
37
|
+
# argument is a plain string, treating it like a literal string,
|
|
38
|
+
# with any remaining arguments treated as placeholder values.
|
|
39
|
+
#
|
|
40
|
+
# This adds such support to the following methods: select, select_append,
|
|
41
|
+
# select_group, select_more, group, group_and_count, order, order_append,
|
|
42
|
+
# and order_more.
|
|
43
|
+
#
|
|
44
|
+
# Note that if you pass a block to these methods, it will use the default
|
|
45
|
+
# implementation without the special literal handling.
|
|
46
|
+
module QueryLiterals
|
|
47
|
+
%w'select select_append select_group select_more group group_and_count order order_append order_more'.each do |m|
|
|
48
|
+
class_eval(<<-END, __FILE__, __LINE__ + 1)
|
|
49
|
+
def #{m}(*args)
|
|
50
|
+
if !block_given? && (l = query_literal(args))
|
|
51
|
+
super(l)
|
|
52
|
+
else
|
|
53
|
+
super
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
END
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
# If the first argument is a plain string, return a literal string
|
|
62
|
+
# if there are no additional args or a placeholder literal string with
|
|
63
|
+
# the remaining args. Otherwise, return nil.
|
|
64
|
+
def query_literal(args)
|
|
65
|
+
case (s = args[0])
|
|
66
|
+
when LiteralString, SQL::Blob
|
|
67
|
+
nil
|
|
68
|
+
when String
|
|
69
|
+
if args.length == 1
|
|
70
|
+
LiteralString.new(s)
|
|
71
|
+
else
|
|
72
|
+
SQL::PlaceholderLiteralString.new(s, args[1..-1])
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
Dataset.register_extension(:query_literals, QueryLiterals)
|
|
79
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# The schema_caching extension adds a few methods to Sequel::Database
|
|
2
|
+
# that make it easy to dump the parsed schema information to a file,
|
|
3
|
+
# and load it from that file. Loading the schema information from a
|
|
4
|
+
# dumped file is faster than parsing it from the database, so this
|
|
5
|
+
# can save bootup time for applications with large numbers of models.
|
|
6
|
+
#
|
|
7
|
+
# Basic usage in application code:
|
|
8
|
+
#
|
|
9
|
+
# Sequel.extension :schema_caching
|
|
10
|
+
#
|
|
11
|
+
# DB = Sequel.connect('...')
|
|
12
|
+
#
|
|
13
|
+
# DB.load_schema_cache('/path/to/schema.dump')
|
|
14
|
+
#
|
|
15
|
+
# # load model files
|
|
16
|
+
#
|
|
17
|
+
# Then, whenever the database schema is modified, write a new cached
|
|
18
|
+
# file. You can do that with <tt>bin/sequel</tt>'s -S option:
|
|
19
|
+
#
|
|
20
|
+
# bin/sequel -S /path/to/schema.dump postgres://...
|
|
21
|
+
#
|
|
22
|
+
# Alternatively, if you don't want to dump the schema information for
|
|
23
|
+
# all tables, and you don't worry about race conditions, you can
|
|
24
|
+
# choose to use the following in your application code:
|
|
25
|
+
#
|
|
26
|
+
# Sequel.extension :schema_caching
|
|
27
|
+
#
|
|
28
|
+
# DB = Sequel.connect('...')
|
|
29
|
+
#
|
|
30
|
+
# DB.load_schema_cache?('/path/to/schema.dump')
|
|
31
|
+
#
|
|
32
|
+
# # load model files
|
|
33
|
+
#
|
|
34
|
+
# DB.dump_schema_cache?('/path/to/schema.dump')
|
|
35
|
+
#
|
|
36
|
+
# With this method, you just have to delete the schema dump file if
|
|
37
|
+
# the schema is modified, and the application will recreate it for you
|
|
38
|
+
# using just the tables that your models use.
|
|
39
|
+
#
|
|
40
|
+
# Note that it is up to the application to ensure that the dumped
|
|
41
|
+
# cached schema reflects the current state of the database. Sequel
|
|
42
|
+
# does no checking to ensure this, as checking would take time and the
|
|
43
|
+
# purpose of this code is to take a shortcut.
|
|
44
|
+
#
|
|
45
|
+
# The cached schema is dumped in Marshal format, since it is the fastest
|
|
46
|
+
# and it handles all ruby objects used in the schema hash. Because of this,
|
|
47
|
+
# you should not attempt to load the schema from a untrusted file.
|
|
48
|
+
|
|
49
|
+
module Sequel
|
|
50
|
+
class Database
|
|
51
|
+
# Dump the cached schema to the filename given in Marshal format.
|
|
52
|
+
def dump_schema_cache(file)
|
|
53
|
+
File.open(file, 'wb'){|f| f.write(Marshal.dump(@schemas))}
|
|
54
|
+
nil
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Dump the cached schema to the filename given unless the file
|
|
58
|
+
# already exists.
|
|
59
|
+
def dump_schema_cache?(file)
|
|
60
|
+
dump_schema_cache(file) unless File.exist?(file)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Replace the schema cache with the data from the given file, which
|
|
64
|
+
# should be in Marshal format.
|
|
65
|
+
def load_schema_cache(file)
|
|
66
|
+
@schemas = Marshal.load(File.read(file))
|
|
67
|
+
nil
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Replace the schema cache with the data from the given file if the
|
|
71
|
+
# file exists.
|
|
72
|
+
def load_schema_cache?(file)
|
|
73
|
+
load_schema_cache(file) if File.exist?(file)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -3,47 +3,86 @@
|
|
|
3
3
|
# database (which can be the same type or a different type than
|
|
4
4
|
# the current database). The main interface is through
|
|
5
5
|
# Sequel::Database#dump_schema_migration.
|
|
6
|
+
#
|
|
7
|
+
# To load the extension:
|
|
8
|
+
#
|
|
9
|
+
# Sequel.extension :schema_dumper
|
|
10
|
+
|
|
11
|
+
Sequel.extension :eval_inspect
|
|
6
12
|
|
|
7
13
|
module Sequel
|
|
8
14
|
class Database
|
|
15
|
+
# Dump foreign key constraints for all tables as a migration. This complements
|
|
16
|
+
# the :foreign_keys=>false option to dump_schema_migration. This only dumps
|
|
17
|
+
# the constraints (not the columns) using alter_table/add_foreign_key with an
|
|
18
|
+
# array of columns.
|
|
19
|
+
#
|
|
20
|
+
# Note that the migration this produces does not have a down
|
|
21
|
+
# block, so you cannot reverse it.
|
|
22
|
+
def dump_foreign_key_migration(options={})
|
|
23
|
+
ts = tables(options)
|
|
24
|
+
<<END_MIG
|
|
25
|
+
Sequel.migration do
|
|
26
|
+
change do
|
|
27
|
+
#{ts.sort_by{|t| t.to_s}.map{|t| dump_table_foreign_keys(t)}.reject{|x| x == ''}.join("\n\n").gsub(/^/o, ' ')}
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
END_MIG
|
|
31
|
+
end
|
|
32
|
+
|
|
9
33
|
# Dump indexes for all tables as a migration. This complements
|
|
10
34
|
# the :indexes=>false option to dump_schema_migration. Options:
|
|
11
|
-
#
|
|
12
|
-
#
|
|
35
|
+
# :same_db :: Create a dump for the same database type, so
|
|
36
|
+
# don't ignore errors if the index statements fail.
|
|
37
|
+
# :index_names :: If set to false, don't record names of indexes. If
|
|
38
|
+
# set to :namespace, prepend the table name to the index name if the
|
|
39
|
+
# database does not use a global index namespace.
|
|
13
40
|
def dump_indexes_migration(options={})
|
|
14
41
|
ts = tables(options)
|
|
15
42
|
<<END_MIG
|
|
16
43
|
Sequel.migration do
|
|
17
|
-
|
|
44
|
+
change do
|
|
18
45
|
#{ts.sort_by{|t| t.to_s}.map{|t| dump_table_indexes(t, :add_index, options)}.reject{|x| x == ''}.join("\n\n").gsub(/^/o, ' ')}
|
|
19
46
|
end
|
|
20
|
-
|
|
21
|
-
down do
|
|
22
|
-
#{ts.sort_by{|t| t.to_s}.map{|t| dump_table_indexes(t, :drop_index, options)}.reject{|x| x == ''}.join("\n\n").gsub(/^/o, ' ')}
|
|
23
|
-
end
|
|
24
47
|
end
|
|
25
48
|
END_MIG
|
|
26
49
|
end
|
|
27
50
|
|
|
28
51
|
# Return a string that contains a Sequel::Migration subclass that when
|
|
29
52
|
# run would recreate the database structure. Options:
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
53
|
+
# :same_db :: Don't attempt to translate database types to ruby types.
|
|
54
|
+
# If this isn't set to true, all database types will be translated to
|
|
55
|
+
# ruby types, but there is no guarantee that the migration generated
|
|
56
|
+
# will yield the same type. Without this set, types that aren't
|
|
57
|
+
# recognized will be translated to a string-like type.
|
|
58
|
+
# :foreign_keys :: If set to false, don't dump foreign_keys (they can be
|
|
59
|
+
# added later via #dump_foreign_key_migration)
|
|
60
|
+
# :indexes :: If set to false, don't dump indexes (they can be added
|
|
61
|
+
# later via #dump_index_migration).
|
|
62
|
+
# :index_names :: If set to false, don't record names of indexes. If
|
|
63
|
+
# set to :namespace, prepend the table name to the index name.
|
|
37
64
|
def dump_schema_migration(options={})
|
|
38
|
-
|
|
65
|
+
options = options.dup
|
|
66
|
+
if options[:indexes] == false && !options.has_key?(:foreign_keys)
|
|
67
|
+
# Unless foreign_keys option is specifically set, disable if indexes
|
|
68
|
+
# are disabled, as foreign keys that point to non-primary keys rely
|
|
69
|
+
# on unique indexes being created first
|
|
70
|
+
options[:foreign_keys] = false
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
ts = sort_dumped_tables(tables(options), options)
|
|
74
|
+
skipped_fks = if sfk = options[:skipped_foreign_keys]
|
|
75
|
+
# Handle skipped foreign keys by adding them at the end via
|
|
76
|
+
# alter_table/add_foreign_key. Note that skipped foreign keys
|
|
77
|
+
# probably result in a broken down migration.
|
|
78
|
+
sfka = sfk.sort_by{|table, fks| table.to_s}.map{|table, fks| dump_add_fk_constraints(table, fks.values)}
|
|
79
|
+
sfka.join("\n\n").gsub(/^/o, ' ') unless sfka.empty?
|
|
80
|
+
end
|
|
81
|
+
|
|
39
82
|
<<END_MIG
|
|
40
83
|
Sequel.migration do
|
|
41
|
-
|
|
42
|
-
#{ts.
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
down do
|
|
46
|
-
drop_table(#{ts.sort_by{|t| t.to_s}.inspect[1...-1]})
|
|
84
|
+
change do
|
|
85
|
+
#{ts.map{|t| dump_table_schema(t, options)}.join("\n\n").gsub(/^/o, ' ')}#{"\n \n" if skipped_fks}#{skipped_fks}
|
|
47
86
|
end
|
|
48
87
|
end
|
|
49
88
|
END_MIG
|
|
@@ -53,50 +92,34 @@ END_MIG
|
|
|
53
92
|
# table's schema. Takes the same options as dump_schema_migration.
|
|
54
93
|
def dump_table_schema(table, options={})
|
|
55
94
|
table = table.value.to_s if table.is_a?(SQL::Identifier)
|
|
56
|
-
|
|
57
|
-
s = schema(table).dup
|
|
58
|
-
pks = s.find_all{|x| x.last[:primary_key] == true}.map{|x| x.first}
|
|
59
|
-
options = options.merge(:single_pk=>true) if pks.length == 1
|
|
60
|
-
m = method(:column_schema_to_generator_opts)
|
|
61
|
-
im = method(:index_to_generator_opts)
|
|
62
|
-
begin
|
|
63
|
-
indexes = indexes(table).sort_by{|k,v| k.to_s} if options[:indexes] != false
|
|
64
|
-
rescue Sequel::NotImplemented
|
|
65
|
-
nil
|
|
66
|
-
end
|
|
67
|
-
gen = Schema::Generator.new(self) do
|
|
68
|
-
s.each{|name, info| send(*m.call(name, info, options))}
|
|
69
|
-
primary_key(pks) if !@primary_key && pks.length > 0
|
|
70
|
-
indexes.each{|iname, iopts| send(:index, iopts[:columns], im.call(table, iname, iopts))} if indexes
|
|
71
|
-
end
|
|
95
|
+
gen = dump_table_generator(table, options)
|
|
72
96
|
commands = [gen.dump_columns, gen.dump_constraints, gen.dump_indexes].reject{|x| x == ''}.join("\n\n")
|
|
73
|
-
"create_table(#{table.inspect}#{', :ignore_index_errors=>true' if !options[:same_db] && options[:indexes] != false &&
|
|
97
|
+
"create_table(#{table.inspect}#{', :ignore_index_errors=>true' if !options[:same_db] && options[:indexes] != false && !gen.indexes.empty?}) do\n#{commands.gsub(/^/o, ' ')}\nend"
|
|
74
98
|
end
|
|
75
99
|
|
|
76
100
|
private
|
|
77
101
|
|
|
78
|
-
# If a database default exists and can't be converted,
|
|
79
|
-
# method modified
|
|
80
|
-
# :same_db option is used.
|
|
102
|
+
# If a database default exists and can't be converted, and we are dumping with :same_db,
|
|
103
|
+
# return a string with the inspect method modified a literal string is created if the code is evaled.
|
|
81
104
|
def column_schema_to_ruby_default_fallback(default, options)
|
|
82
105
|
if default.is_a?(String) && options[:same_db] && use_column_schema_to_ruby_default_fallback?
|
|
83
|
-
default = default.
|
|
106
|
+
default = default.dup
|
|
84
107
|
def default.inspect
|
|
85
|
-
"#{super}
|
|
108
|
+
"Sequel::LiteralString.new(#{super})"
|
|
86
109
|
end
|
|
87
110
|
default
|
|
88
111
|
end
|
|
89
112
|
end
|
|
90
113
|
|
|
91
|
-
#
|
|
92
|
-
|
|
93
|
-
def column_schema_to_generator_opts(name, schema, options)
|
|
114
|
+
# Recreate the column in the passed Schema::Generator from the given name and parsed database schema.
|
|
115
|
+
def recreate_column(name, schema, gen, options)
|
|
94
116
|
if options[:single_pk] && schema_autoincrementing_primary_key?(schema)
|
|
95
117
|
type_hash = options[:same_db] ? {:type=>schema[:db_type]} : column_schema_to_ruby_type(schema)
|
|
118
|
+
[:table, :key, :on_delete, :on_update, :deferrable].each{|f| type_hash[f] = schema[f] if schema[f]}
|
|
96
119
|
if type_hash == {:type=>Integer} || type_hash == {:type=>"integer"}
|
|
97
|
-
|
|
120
|
+
gen.primary_key(name)
|
|
98
121
|
else
|
|
99
|
-
|
|
122
|
+
gen.primary_key(name, type_hash)
|
|
100
123
|
end
|
|
101
124
|
else
|
|
102
125
|
col_opts = options[:same_db] ? {:type=>schema[:db_type]} : column_schema_to_ruby_type(schema)
|
|
@@ -109,7 +132,16 @@ END_MIG
|
|
|
109
132
|
end
|
|
110
133
|
col_opts.delete(:default) if col_opts[:default].nil?
|
|
111
134
|
col_opts[:null] = false if schema[:allow_null] == false
|
|
112
|
-
|
|
135
|
+
if table = schema[:table]
|
|
136
|
+
[:key, :on_delete, :on_update, :deferrable].each{|f| col_opts[f] = schema[f] if schema[f]}
|
|
137
|
+
col_opts[:type] = type unless type == Integer || type == 'integer'
|
|
138
|
+
gen.foreign_key(name, table, col_opts)
|
|
139
|
+
else
|
|
140
|
+
gen.column(name, type, col_opts)
|
|
141
|
+
if [Integer, Bignum, Float].include?(type) && schema[:db_type] =~ / unsigned\z/io
|
|
142
|
+
gen.check(Sequel::SQL::Identifier.new(name) >= 0)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
113
145
|
end
|
|
114
146
|
end
|
|
115
147
|
|
|
@@ -118,13 +150,19 @@ END_MIG
|
|
|
118
150
|
# database type is not recognized, return it as a String type.
|
|
119
151
|
def column_schema_to_ruby_type(schema)
|
|
120
152
|
case t = schema[:db_type].downcase
|
|
121
|
-
when /\A(
|
|
122
|
-
|
|
123
|
-
|
|
153
|
+
when /\A(medium|small)?int(?:eger)?(?:\((\d+)\))?( unsigned)?\z/o
|
|
154
|
+
if !$1 && $2 && $2.to_i >= 10 && $3
|
|
155
|
+
# Unsigned integer type with 10 digits can potentially contain values which
|
|
156
|
+
# don't fit signed integer type, so use bigint type in target database.
|
|
157
|
+
{:type=>Bignum}
|
|
158
|
+
else
|
|
159
|
+
{:type=>Integer}
|
|
160
|
+
end
|
|
161
|
+
when /\Atinyint(?:\((\d+)\))?(?: unsigned)?\z/o
|
|
124
162
|
{:type =>schema[:type] == :boolean ? TrueClass : Integer}
|
|
125
163
|
when /\Abigint(?:\((?:\d+)\))?(?: unsigned)?\z/o
|
|
126
164
|
{:type=>Bignum}
|
|
127
|
-
when /\A(?:real|float|double(?: precision)?)\z/o
|
|
165
|
+
when /\A(?:real|float|double(?: precision)?|double\(\d+,\d+\)(?: unsigned)?)\z/o
|
|
128
166
|
{:type=>Float}
|
|
129
167
|
when 'boolean'
|
|
130
168
|
{:type=>TrueClass}
|
|
@@ -149,34 +187,191 @@ END_MIG
|
|
|
149
187
|
{:type=>BigDecimal, :size=>(s.empty? ? nil : s)}
|
|
150
188
|
when /\A(?:bytea|(?:tiny|medium|long)?blob|(?:var)?binary)(?:\((\d+)\))?\z/o
|
|
151
189
|
{:type=>File, :size=>($1.to_i if $1)}
|
|
152
|
-
when
|
|
190
|
+
when /\A(?:year|(?:int )?identity)\z/o
|
|
153
191
|
{:type=>Integer}
|
|
154
192
|
else
|
|
155
193
|
{:type=>String}
|
|
156
194
|
end
|
|
157
195
|
end
|
|
158
196
|
|
|
197
|
+
# For the table and foreign key metadata array, return an alter_table
|
|
198
|
+
# string that would add the foreign keys if run in a migration.
|
|
199
|
+
def dump_add_fk_constraints(table, fks)
|
|
200
|
+
sfks = "alter_table(#{table.inspect}) do\n"
|
|
201
|
+
sfks << create_table_generator do
|
|
202
|
+
fks.sort_by{|fk| fk[:columns].map{|c| c.to_s}}.each do |fk|
|
|
203
|
+
foreign_key fk[:columns], fk
|
|
204
|
+
end
|
|
205
|
+
end.dump_constraints.gsub(/^foreign_key /, ' add_foreign_key ')
|
|
206
|
+
sfks << "\nend"
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# For the table given, get the list of foreign keys and return an alter_table
|
|
210
|
+
# string that would add the foreign keys if run in a migration.
|
|
211
|
+
def dump_table_foreign_keys(table, options={})
|
|
212
|
+
begin
|
|
213
|
+
fks = foreign_key_list(table, options).sort_by{|fk| fk[:columns].map{|c| c.to_s}}
|
|
214
|
+
rescue Sequel::NotImplemented
|
|
215
|
+
return ''
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
if fks.empty?
|
|
219
|
+
''
|
|
220
|
+
else
|
|
221
|
+
dump_add_fk_constraints(table, fks)
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Return a Schema::Generator object that will recreate the
|
|
226
|
+
# table's schema. Takes the same options as dump_schema_migration.
|
|
227
|
+
def dump_table_generator(table, options={})
|
|
228
|
+
table = table.value.to_s if table.is_a?(SQL::Identifier)
|
|
229
|
+
raise(Error, "must provide table as a Symbol, String, or Sequel::SQL::Identifier") unless [String, Symbol].any?{|c| table.is_a?(c)}
|
|
230
|
+
s = schema(table).dup
|
|
231
|
+
pks = s.find_all{|x| x.last[:primary_key] == true}.map{|x| x.first}
|
|
232
|
+
options = options.merge(:single_pk=>true) if pks.length == 1
|
|
233
|
+
m = method(:recreate_column)
|
|
234
|
+
im = method(:index_to_generator_opts)
|
|
235
|
+
|
|
236
|
+
if options[:indexes] != false
|
|
237
|
+
begin
|
|
238
|
+
indexes = indexes(table).sort_by{|k,v| k.to_s}
|
|
239
|
+
rescue Sequel::NotImplemented
|
|
240
|
+
nil
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
if options[:foreign_keys] != false
|
|
245
|
+
begin
|
|
246
|
+
fk_list = foreign_key_list(table)
|
|
247
|
+
|
|
248
|
+
if (sfk = options[:skipped_foreign_keys]) && (sfkt = sfk[table])
|
|
249
|
+
fk_list.delete_if{|fk| sfkt.has_key?(fk[:columns])}
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
composite_fks, single_fks = fk_list.partition{|h| h[:columns].length > 1}
|
|
253
|
+
fk_hash = {}
|
|
254
|
+
|
|
255
|
+
single_fks.each do |fk|
|
|
256
|
+
column = fk.delete(:columns).first
|
|
257
|
+
fk.delete(:name)
|
|
258
|
+
fk_hash[column] = fk
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
s = s.map do |name, info|
|
|
262
|
+
if fk_info = fk_hash[name]
|
|
263
|
+
[name, fk_info.merge(info)]
|
|
264
|
+
else
|
|
265
|
+
[name, info]
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
rescue Sequel::NotImplemented
|
|
269
|
+
nil
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
create_table_generator do
|
|
274
|
+
s.each{|name, info| m.call(name, info, self, options)}
|
|
275
|
+
primary_key(pks) if !@primary_key && pks.length > 0
|
|
276
|
+
indexes.each{|iname, iopts| send(:index, iopts[:columns], im.call(table, iname, iopts, options))} if indexes
|
|
277
|
+
composite_fks.each{|fk| send(:foreign_key, fk[:columns], fk)} if composite_fks
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
159
281
|
# Return a string that containing add_index/drop_index method calls for
|
|
160
282
|
# creating the index migration.
|
|
161
283
|
def dump_table_indexes(table, meth, options={})
|
|
162
|
-
|
|
284
|
+
begin
|
|
285
|
+
indexes = indexes(table).sort_by{|k,v| k.to_s}
|
|
286
|
+
rescue Sequel::NotImplemented
|
|
287
|
+
return ''
|
|
288
|
+
end
|
|
163
289
|
im = method(:index_to_generator_opts)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
indexes.each{|iname, iopts| send(:index, iopts[:columns], im.call(table, iname, iopts))}
|
|
290
|
+
gen = create_table_generator do
|
|
291
|
+
indexes.each{|iname, iopts| send(:index, iopts[:columns], im.call(table, iname, iopts, options))}
|
|
167
292
|
end
|
|
168
293
|
gen.dump_indexes(meth=>table, :ignore_errors=>!options[:same_db])
|
|
169
294
|
end
|
|
170
295
|
|
|
171
296
|
# Convert the parsed index information into options to the Generators index method.
|
|
172
|
-
def index_to_generator_opts(table, name, index_opts)
|
|
297
|
+
def index_to_generator_opts(table, name, index_opts, options={})
|
|
173
298
|
h = {}
|
|
174
|
-
|
|
299
|
+
if options[:index_names] != false && default_index_name(table, index_opts[:columns]) != name.to_s
|
|
300
|
+
if options[:index_names] == :namespace && !global_index_namespace?
|
|
301
|
+
h[:name] = "#{table}_#{name}".to_sym
|
|
302
|
+
else
|
|
303
|
+
h[:name] = name
|
|
304
|
+
end
|
|
305
|
+
end
|
|
175
306
|
h[:unique] = true if index_opts[:unique]
|
|
176
307
|
h
|
|
177
308
|
end
|
|
309
|
+
|
|
310
|
+
# Sort the tables so that referenced tables are created before tables that
|
|
311
|
+
# reference them, and then by name. If foreign keys are disabled, just sort by name.
|
|
312
|
+
def sort_dumped_tables(tables, options={})
|
|
313
|
+
sort_topologically = if options[:foreign_keys] != false
|
|
314
|
+
begin
|
|
315
|
+
foreign_key_list(:some_table_that_does_not_exist)
|
|
316
|
+
true
|
|
317
|
+
rescue Sequel::NotImplemented
|
|
318
|
+
false
|
|
319
|
+
rescue
|
|
320
|
+
true
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
if sort_topologically
|
|
325
|
+
table_fks = {}
|
|
326
|
+
tables.each{|t| table_fks[t] = foreign_key_list(t)}
|
|
327
|
+
# Remove self referential foreign keys, not important when sorting.
|
|
328
|
+
table_fks.each{|t, fks| fks.delete_if{|fk| fk[:table] == t}}
|
|
329
|
+
tables, skipped_foreign_keys = sort_dumped_tables_topologically(table_fks, [])
|
|
330
|
+
options[:skipped_foreign_keys] = skipped_foreign_keys
|
|
331
|
+
tables
|
|
332
|
+
else
|
|
333
|
+
tables.sort_by{|t| t.to_s}
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# Do a topological sort of tables, so that referenced tables
|
|
338
|
+
# come before referencing tables. Returns an array of sorted
|
|
339
|
+
# tables and a hash of skipped foreign keys. The hash will be
|
|
340
|
+
# empty unless there are circular dependencies.
|
|
341
|
+
def sort_dumped_tables_topologically(table_fks, sorted_tables)
|
|
342
|
+
skipped_foreign_keys = {}
|
|
343
|
+
|
|
344
|
+
until table_fks.empty?
|
|
345
|
+
this_loop = []
|
|
346
|
+
|
|
347
|
+
table_fks.each do |table, fks|
|
|
348
|
+
fks.delete_if{|fk| !table_fks.has_key?(fk[:table])}
|
|
349
|
+
this_loop << table if fks.empty?
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
if this_loop.empty?
|
|
353
|
+
# No tables were changed this round, there must be a circular dependency.
|
|
354
|
+
# Break circular dependency by picking the table with the least number of
|
|
355
|
+
# outstanding foreign keys and skipping those foreign keys.
|
|
356
|
+
# The skipped foreign keys will be added at the end of the
|
|
357
|
+
# migration.
|
|
358
|
+
skip_table, skip_fks = table_fks.sort_by{|table, fks| [fks.length, table.to_s]}.first
|
|
359
|
+
skip_fks_hash = skipped_foreign_keys[skip_table] = {}
|
|
360
|
+
skip_fks.each{|fk| skip_fks_hash[fk[:columns]] = fk}
|
|
361
|
+
this_loop << skip_table
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# Add sorted tables from this loop to the final list
|
|
365
|
+
sorted_tables.concat(this_loop.sort_by{|t| t.to_s})
|
|
366
|
+
|
|
367
|
+
# Remove tables that were handled this loop
|
|
368
|
+
this_loop.each{|t| table_fks.delete(t)}
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
[sorted_tables, skipped_foreign_keys]
|
|
372
|
+
end
|
|
178
373
|
|
|
179
|
-
# Don't use
|
|
374
|
+
# Don't use a literal string fallback on MySQL, since the defaults it uses aren't
|
|
180
375
|
# valid literal SQL values.
|
|
181
376
|
def use_column_schema_to_ruby_default_fallback?
|
|
182
377
|
database_type != :mysql
|
|
@@ -190,6 +385,10 @@ END_MIG
|
|
|
190
385
|
def dump_columns
|
|
191
386
|
strings = []
|
|
192
387
|
cols = columns.dup
|
|
388
|
+
cols.each do |x|
|
|
389
|
+
x.delete(:on_delete) if x[:on_delete] == :no_action
|
|
390
|
+
x.delete(:on_update) if x[:on_update] == :no_action
|
|
391
|
+
end
|
|
193
392
|
if pkn = primary_key_name
|
|
194
393
|
cols.delete_if{|x| x[:name] == pkn}
|
|
195
394
|
pk = @primary_key.dup
|
|
@@ -200,12 +399,17 @@ END_MIG
|
|
|
200
399
|
cols.each do |c|
|
|
201
400
|
c = c.dup
|
|
202
401
|
name = c.delete(:name)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
"#{type.name} #{name.inspect}#{opts}"
|
|
402
|
+
strings << if table = c.delete(:table)
|
|
403
|
+
c.delete(:type) if c[:type] == Integer || c[:type] == 'integer'
|
|
404
|
+
"foreign_key #{name.inspect}, #{table.inspect}#{opts_inspect(c)}"
|
|
207
405
|
else
|
|
208
|
-
|
|
406
|
+
type = c.delete(:type)
|
|
407
|
+
opts = opts_inspect(c)
|
|
408
|
+
if type.is_a?(Class)
|
|
409
|
+
"#{type.name} #{name.inspect}#{opts}"
|
|
410
|
+
else
|
|
411
|
+
"column #{name.inspect}, #{type.inspect}#{opts}"
|
|
412
|
+
end
|
|
209
413
|
end
|
|
210
414
|
end
|
|
211
415
|
strings.join("\n")
|
|
@@ -226,6 +430,13 @@ END_MIG
|
|
|
226
430
|
else
|
|
227
431
|
"#{name ? "constraint #{name.inspect}," : 'check'} #{c[:check].map{|x| x.inspect}.join(', ')}"
|
|
228
432
|
end
|
|
433
|
+
when :foreign_key
|
|
434
|
+
c.delete(:on_delete) if c[:on_delete] == :no_action
|
|
435
|
+
c.delete(:on_update) if c[:on_update] == :no_action
|
|
436
|
+
c.delete(:deferrable) unless c[:deferrable]
|
|
437
|
+
cols = c.delete(:columns)
|
|
438
|
+
table = c.delete(:table)
|
|
439
|
+
"#{type} #{cols.inspect}, #{table.inspect}#{opts_inspect(c)}"
|
|
229
440
|
else
|
|
230
441
|
cols = c.delete(:columns)
|
|
231
442
|
"#{type} #{cols.inspect}#{opts_inspect(c)}"
|
|
@@ -251,24 +462,19 @@ END_MIG
|
|
|
251
462
|
"index #{cols.inspect}#{opts_inspect(c)}"
|
|
252
463
|
end
|
|
253
464
|
end
|
|
465
|
+
is = is.reverse if options[:drop_index]
|
|
254
466
|
is.join("\n")
|
|
255
467
|
end
|
|
256
468
|
|
|
257
469
|
private
|
|
258
470
|
|
|
471
|
+
# Return a string that converts the given options into one
|
|
472
|
+
# suitable for literal ruby code, handling default values
|
|
473
|
+
# that don't default to a literal interpretation.
|
|
259
474
|
def opts_inspect(opts)
|
|
260
475
|
if opts[:default]
|
|
261
476
|
opts = opts.dup
|
|
262
|
-
de =
|
|
263
|
-
when BigDecimal, Sequel::SQL::Blob
|
|
264
|
-
"#{d.class.name}.new(#{d.to_s.inspect})"
|
|
265
|
-
when DateTime, Date
|
|
266
|
-
"#{d.class.name}.parse(#{d.to_s.inspect})"
|
|
267
|
-
when Time
|
|
268
|
-
"#{d.class.name}.parse(#{d.strftime('%H:%M:%S').inspect})"
|
|
269
|
-
else
|
|
270
|
-
d.inspect
|
|
271
|
-
end
|
|
477
|
+
de = Sequel.eval_inspect(opts.delete(:default))
|
|
272
478
|
", :default=>#{de}#{", #{opts.inspect[1...-1]}" if opts.length > 0}"
|
|
273
479
|
else
|
|
274
480
|
", #{opts.inspect[1...-1]}" if opts.length > 0
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# The select_remove extension adds Sequel::Dataset#select_remove for removing existing selected
|
|
2
|
+
# columns from a dataset. It's not part of Sequel core as it is rarely needed and has
|
|
3
|
+
# some corner cases where it can't work correctly.
|
|
4
|
+
#
|
|
5
|
+
# To load the extension:
|
|
6
|
+
#
|
|
7
|
+
# Sequel.extension :select_remove
|
|
8
|
+
|
|
9
|
+
module Sequel
|
|
10
|
+
class Dataset
|
|
11
|
+
# Remove columns from the list of selected columns. If any of the currently selected
|
|
12
|
+
# columns use expressions/aliases, this will remove selected columns with the given
|
|
13
|
+
# aliases. It will also remove entries from the selection that match exactly:
|
|
14
|
+
#
|
|
15
|
+
# # Assume columns a, b, and c in items table
|
|
16
|
+
# DB[:items] # SELECT * FROM items
|
|
17
|
+
# DB[:items].select_remove(:c) # SELECT a, b FROM items
|
|
18
|
+
# DB[:items].select(:a, :b___c, :c___b).select_remove(:c) # SELECT a, c AS b FROM items
|
|
19
|
+
# DB[:items].select(:a, :b___c, :c___b).select_remove(:c___b) # SELECT a, b AS c FROM items
|
|
20
|
+
#
|
|
21
|
+
# Note that there are a few cases where this method may not work correctly:
|
|
22
|
+
#
|
|
23
|
+
# * This dataset joins multiple tables and does not have an existing explicit selection.
|
|
24
|
+
# In this case, the code will currently use unqualified column names for all columns
|
|
25
|
+
# the dataset returns, except for the columns given.
|
|
26
|
+
# * This dataset has an existing explicit selection containing an item that returns
|
|
27
|
+
# multiple database columns (e.g. Sequel.expr(:table).*, Sequel.lit('column1, column2')). In this case,
|
|
28
|
+
# the behavior is undefined and this method should not be used.
|
|
29
|
+
#
|
|
30
|
+
# There may be other cases where this method does not work correctly, use it with caution.
|
|
31
|
+
def select_remove(*cols)
|
|
32
|
+
if (sel = @opts[:select]) && !sel.empty?
|
|
33
|
+
select(*(columns.zip(sel).reject{|c, s| cols.include?(c)}.map{|c, s| s} - cols))
|
|
34
|
+
else
|
|
35
|
+
select(*(columns - cols))
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|