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,168 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module Plugins
|
|
3
|
+
# The prepared_statements plugin modifies the model to use prepared statements for
|
|
4
|
+
# instance level deletes and saves, as well as class level lookups by
|
|
5
|
+
# primary key.
|
|
6
|
+
#
|
|
7
|
+
# Note that this plugin is unsafe in some circumstances, as it can allow up to
|
|
8
|
+
# 2^N prepared statements to be created for each type of insert and update query, where
|
|
9
|
+
# N is the number of colums in the table. It is recommended that you use the
|
|
10
|
+
# +prepared_statements_safe+ plugin in addition to this plugin to reduce the number
|
|
11
|
+
# of prepared statements that can be created, unless you tightly control how your
|
|
12
|
+
# model instances are saved.
|
|
13
|
+
#
|
|
14
|
+
# This plugin does not work correctly with the instance filters plugin
|
|
15
|
+
# or the update_primary_key plugin.
|
|
16
|
+
#
|
|
17
|
+
# Usage:
|
|
18
|
+
#
|
|
19
|
+
# # Make all model subclasses use prepared statements (called before loading subclasses)
|
|
20
|
+
# Sequel::Model.plugin :prepared_statements
|
|
21
|
+
#
|
|
22
|
+
# # Make the Album class use prepared statements
|
|
23
|
+
# Album.plugin :prepared_statements
|
|
24
|
+
module PreparedStatements
|
|
25
|
+
# Synchronize access to the integer sequence so that no two calls get the same integer.
|
|
26
|
+
MUTEX = Mutex.new
|
|
27
|
+
|
|
28
|
+
i = 0
|
|
29
|
+
# This plugin names prepared statements uniquely using an integer sequence, this
|
|
30
|
+
# lambda returns the next integer to use.
|
|
31
|
+
NEXT = lambda{MUTEX.synchronize{i += 1}}
|
|
32
|
+
|
|
33
|
+
# Setup the datastructure used to hold the prepared statements in the model.
|
|
34
|
+
def self.apply(model)
|
|
35
|
+
model.instance_variable_set(:@prepared_statements, :insert=>{}, :insert_select=>{}, :update=>{}, :lookup_sql=>{}, :fixed=>{})
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module ClassMethods
|
|
39
|
+
# Setup the datastructure used to hold the prepared statements in the subclass.
|
|
40
|
+
def inherited(subclass)
|
|
41
|
+
super
|
|
42
|
+
subclass.instance_variable_set(:@prepared_statements, :insert=>{}, :insert_select=>{}, :update=>{}, :lookup_sql=>{}, :fixed=>{})
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
# Create a prepared statement based on the given dataset with a unique name for the given
|
|
48
|
+
# type of query and values.
|
|
49
|
+
def prepare_statement(ds, type, vals={})
|
|
50
|
+
ps = ds.prepare(type, :"smpsp_#{NEXT.call}", vals)
|
|
51
|
+
ps.log_sql = true
|
|
52
|
+
ps
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Return a sorted array of columns for use as a hash key.
|
|
56
|
+
def prepared_columns(cols)
|
|
57
|
+
RUBY_VERSION >= '1.9' ? cols.sort : cols.sort_by{|c| c.to_s}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Return a prepared statement that can be used to delete a row from this model's dataset.
|
|
61
|
+
def prepared_delete
|
|
62
|
+
cached_prepared_statement(:fixed, :delete){prepare_statement(filter(prepared_statement_key_array(primary_key)), :delete)}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Return a prepared statement that can be used to insert a row using the given columns.
|
|
66
|
+
def prepared_insert(cols)
|
|
67
|
+
cached_prepared_statement(:insert, prepared_columns(cols)){prepare_statement(dataset, :insert, prepared_statement_key_hash(cols))}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Return a prepared statement that can be used to insert a row using the given columns
|
|
71
|
+
# and return that column values for the row created.
|
|
72
|
+
def prepared_insert_select(cols)
|
|
73
|
+
if dataset.supports_insert_select?
|
|
74
|
+
cached_prepared_statement(:insert_select, prepared_columns(cols)){prepare_statement(naked.clone(:server=>dataset.opts.fetch(:server, :default)), :insert_select, prepared_statement_key_hash(cols))}
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Return a prepared statement that can be used to lookup a row solely based on the primary key.
|
|
79
|
+
def prepared_lookup
|
|
80
|
+
cached_prepared_statement(:fixed, :lookup){prepare_statement(filter(prepared_statement_key_array(primary_key)), :first)}
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Return a prepared statement that can be used to refresh a row to get new column values after insertion.
|
|
84
|
+
def prepared_refresh
|
|
85
|
+
cached_prepared_statement(:fixed, :refresh){prepare_statement(naked.clone(:server=>dataset.opts.fetch(:server, :default)).filter(prepared_statement_key_array(primary_key)), :first)}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Return an array of two element arrays with the column symbol as the first entry and the
|
|
89
|
+
# placeholder symbol as the second entry.
|
|
90
|
+
def prepared_statement_key_array(keys)
|
|
91
|
+
if dataset.requires_placeholder_type_specifiers?
|
|
92
|
+
sch = db_schema
|
|
93
|
+
Array(keys).map do |k|
|
|
94
|
+
if (s = sch[k]) && (t = s[:type])
|
|
95
|
+
[k, :"$#{k}__#{t}"]
|
|
96
|
+
else
|
|
97
|
+
[k, :"$#{k}"]
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
else
|
|
101
|
+
Array(keys).map{|k| [k, :"$#{k}"]}
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Return a hash mapping column symbols to placeholder symbols.
|
|
106
|
+
def prepared_statement_key_hash(keys)
|
|
107
|
+
Hash[*(prepared_statement_key_array(keys).flatten)]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Return a prepared statement that can be used to update row using the given columns.
|
|
111
|
+
def prepared_update(cols)
|
|
112
|
+
cached_prepared_statement(:update, prepared_columns(cols)){prepare_statement(filter(prepared_statement_key_array(primary_key)), :update, prepared_statement_key_hash(cols))}
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Use a prepared statement to query the database for the row matching the given primary key.
|
|
116
|
+
def primary_key_lookup(pk)
|
|
117
|
+
prepared_lookup.call(primary_key_hash(pk))
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
# If a prepared statement has already been cached for the given type and subtype,
|
|
123
|
+
# return it. Otherwise, yield to the block to get the prepared statement, and cache it.
|
|
124
|
+
def cached_prepared_statement(type, subtype)
|
|
125
|
+
h = @prepared_statements[type]
|
|
126
|
+
Sequel.synchronize do
|
|
127
|
+
if v = h[subtype]
|
|
128
|
+
return v end
|
|
129
|
+
end
|
|
130
|
+
ps = yield
|
|
131
|
+
Sequel.synchronize{h[subtype] = ps}
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
module InstanceMethods
|
|
136
|
+
private
|
|
137
|
+
|
|
138
|
+
# Use a prepared statement to delete the row.
|
|
139
|
+
def _delete_without_checking
|
|
140
|
+
model.send(:prepared_delete).call(pk_hash)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Use a prepared statement to insert the values into the model's dataset.
|
|
144
|
+
def _insert_raw(ds)
|
|
145
|
+
model.send(:prepared_insert, @values.keys).call(@values)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Use a prepared statement to insert the values into the model's dataset
|
|
149
|
+
# and return the new column values.
|
|
150
|
+
def _insert_select_raw(ds)
|
|
151
|
+
if ps = model.send(:prepared_insert_select, @values.keys)
|
|
152
|
+
ps.call(@values)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Use a prepared statement to refresh this model's column values.
|
|
157
|
+
def _refresh_get(ds)
|
|
158
|
+
model.send(:prepared_refresh).call(pk_hash)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Use a prepared statement to update this model's columns in the database.
|
|
162
|
+
def _update_without_checking(columns)
|
|
163
|
+
model.send(:prepared_update, columns.keys).call(columns.merge(pk_hash))
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module Plugins
|
|
3
|
+
# The prepared_statements_associations plugin modifies the regular association
|
|
4
|
+
# load method to use a cached prepared statement to load the associations.
|
|
5
|
+
# It will not work on all associations, but it should skip the use of prepared
|
|
6
|
+
# statements for associations where it will not work, assuming you load the
|
|
7
|
+
# plugin before defining the associations.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
#
|
|
11
|
+
# # Make all model subclasses more safe when using prepared statements (called before loading subclasses)
|
|
12
|
+
# Sequel::Model.plugin :prepared_statements_associations
|
|
13
|
+
#
|
|
14
|
+
# # Make the Album class more safe when using prepared statements
|
|
15
|
+
# Album.plugin :prepared_statements_associations
|
|
16
|
+
module PreparedStatementsAssociations
|
|
17
|
+
# Synchronize access to the integer sequence so that no two calls get the same integer.
|
|
18
|
+
MUTEX = Mutex.new
|
|
19
|
+
|
|
20
|
+
i = 0
|
|
21
|
+
# This plugin names prepared statements uniquely using an integer sequence, this
|
|
22
|
+
# lambda returns the next integer to use.
|
|
23
|
+
NEXT = lambda{MUTEX.synchronize{i += 1}}
|
|
24
|
+
|
|
25
|
+
module ClassMethods
|
|
26
|
+
# Disable prepared statement use if a block is given, or the :dataset or :conditions
|
|
27
|
+
# options are used, or you are cloning an association.
|
|
28
|
+
def associate(type, name, opts = {}, &block)
|
|
29
|
+
if block || opts[:dataset] || opts[:conditions] || (opts[:clone] && association_reflection(opts[:clone])[:prepared_statement] == false)
|
|
30
|
+
opts = opts.merge(:prepared_statement=>false)
|
|
31
|
+
end
|
|
32
|
+
super(type, name, opts, &block)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
module InstanceMethods
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# Return a bound variable hash that maps the keys in +ks+ (qualified by the +table+)
|
|
40
|
+
# to the values of the results of sending the methods in +vs+.
|
|
41
|
+
def association_bound_variable_hash(table, ks, vs)
|
|
42
|
+
Hash[*ks.zip(vs).map{|k, v| [:"#{table}.#{k}", send(v)]}.flatten]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Given an association reflection, return a bound variable hash for the given
|
|
46
|
+
# association for this instance's values.
|
|
47
|
+
def association_bound_variables(opts)
|
|
48
|
+
case opts[:type]
|
|
49
|
+
when :many_to_one
|
|
50
|
+
association_bound_variable_hash(opts.associated_class.table_name, opts.primary_keys, opts[:keys])
|
|
51
|
+
when :one_to_many
|
|
52
|
+
association_bound_variable_hash(opts.associated_class.table_name, opts[:keys], opts[:primary_keys])
|
|
53
|
+
when :many_to_many
|
|
54
|
+
association_bound_variable_hash(opts.join_table_alias, opts[:left_keys], opts[:left_primary_keys])
|
|
55
|
+
when :many_through_many
|
|
56
|
+
association_bound_variable_hash(opts.final_reverse_edge[:alias], Array(opts[:left_key]), opts[:left_primary_keys])
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Given an association reflection, return and cache a prepared statement for this association such
|
|
61
|
+
# that, given appropriate bound variables, the prepared statement will work correctly for any
|
|
62
|
+
# instance.
|
|
63
|
+
def association_prepared_statement(opts)
|
|
64
|
+
opts.send(:cached_fetch, :prepared_statement) do
|
|
65
|
+
ps = _associated_dataset(opts, {}).unbind.first.prepare(opts.returns_array? ? :select : :first, :"smpsap_#{NEXT.call}")
|
|
66
|
+
ps.log_sql = true
|
|
67
|
+
ps
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# If a prepared statement can be used to load the associated objects, execute it to retrieve them. Otherwise,
|
|
72
|
+
# fall back to the default implementation.
|
|
73
|
+
def _load_associated_objects(opts, dynamic_opts={})
|
|
74
|
+
if !opts.can_have_associated_objects?(self) || dynamic_opts[:callback] || (ps = opts[:prepared_statement]) == false
|
|
75
|
+
super
|
|
76
|
+
else
|
|
77
|
+
if bv = association_bound_variables(opts)
|
|
78
|
+
(ps || association_prepared_statement(opts)).call(bv)
|
|
79
|
+
else
|
|
80
|
+
super
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module Plugins
|
|
3
|
+
# The prepared_statements_safe plugin modifies the model to reduce the number of
|
|
4
|
+
# prepared statements that can be created, by setting as many columns as possible
|
|
5
|
+
# before creating, and by changing +save_changes+ to save all columns instead of
|
|
6
|
+
# just the changed ones.
|
|
7
|
+
#
|
|
8
|
+
# This plugin depends on the +prepared_statements+ plugin.
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
#
|
|
12
|
+
# # Make all model subclasses more safe when using prepared statements (called before loading subclasses)
|
|
13
|
+
# Sequel::Model.plugin :prepared_statements_safe
|
|
14
|
+
#
|
|
15
|
+
# # Make the Album class more safe when using prepared statements
|
|
16
|
+
# Album.plugin :prepared_statements_safe
|
|
17
|
+
module PreparedStatementsSafe
|
|
18
|
+
# Depend on the prepared_statements plugin
|
|
19
|
+
def self.apply(model)
|
|
20
|
+
model.plugin(:prepared_statements)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Set the column defaults to use when creating on the model.
|
|
24
|
+
def self.configure(model)
|
|
25
|
+
model.send(:set_prepared_statements_column_defaults)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
module ClassMethods
|
|
29
|
+
# A hash with column symbol keys and default values. Instance's
|
|
30
|
+
# values are merged into this hash before creating to reduce the
|
|
31
|
+
# number of free columns (columns that may or may not be present
|
|
32
|
+
# in the INSERT statement), as the number of prepared statements
|
|
33
|
+
# that can be created is 2^N (where N is the number of free columns).
|
|
34
|
+
attr_reader :prepared_statements_column_defaults
|
|
35
|
+
|
|
36
|
+
def inherited(subclass)
|
|
37
|
+
super
|
|
38
|
+
subclass.instance_variable_set(:@prepared_statements_column_defaults, @prepared_statements_column_defaults) if @prepared_statements_column_defaults && !subclass.prepared_statements_column_defaults
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Set the column defaults to use when creating on the subclass.
|
|
42
|
+
def set_dataset(*)
|
|
43
|
+
x = super
|
|
44
|
+
set_prepared_statements_column_defaults
|
|
45
|
+
x
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
# Set the column defaults based on the database schema. All columns
|
|
51
|
+
# are set to a default value unless they are a primary key column or
|
|
52
|
+
# they don't have a parseable default.
|
|
53
|
+
def set_prepared_statements_column_defaults
|
|
54
|
+
if db_schema
|
|
55
|
+
h = {}
|
|
56
|
+
db_schema.each do |k, v|
|
|
57
|
+
h[k] = v[:ruby_default] if (v[:ruby_default] || !v[:default]) && !v[:primary_key]
|
|
58
|
+
end
|
|
59
|
+
@prepared_statements_column_defaults = h
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
module InstanceMethods
|
|
65
|
+
# Merge the current values into the default values to reduce the number
|
|
66
|
+
# of free columns.
|
|
67
|
+
def before_create
|
|
68
|
+
if v = model.prepared_statements_column_defaults
|
|
69
|
+
set_values(v.merge(values))
|
|
70
|
+
end
|
|
71
|
+
super
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Always do a full save of all columns to reduce the number of prepared
|
|
75
|
+
# statements that can be used.
|
|
76
|
+
def save_changes(opts={})
|
|
77
|
+
save(opts) || false if modified?
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module Plugins
|
|
3
|
+
# The prepared_statements_with_pk plugin allows Dataset#with_pk for model datasets
|
|
4
|
+
# to use prepared statements by extract the values of previously bound variables
|
|
5
|
+
# using <tt>Dataset#unbind</tt>, and attempting to use a prepared statement if the
|
|
6
|
+
# variables can be unbound correctly. See +Unbinder+ for details about what types of
|
|
7
|
+
# dataset filters can be unbound correctly.
|
|
8
|
+
#
|
|
9
|
+
# This plugin depends on the +prepared_statements+ plugin and should be considered unsafe.
|
|
10
|
+
# Unbinding dataset values cannot be done correctly in all cases, and use of this plugin
|
|
11
|
+
# in cases where not there are variables that are not unbound can lead to an denial of
|
|
12
|
+
# service attack by allocating an arbitrary number of prepared statements. You have been
|
|
13
|
+
# warned.
|
|
14
|
+
#
|
|
15
|
+
# Usage:
|
|
16
|
+
#
|
|
17
|
+
# # Make all model subclasses use prepared statements for Dataset#with_pk (called before loading subclasses)
|
|
18
|
+
# Sequel::Model.plugin :prepared_statements_with_pk
|
|
19
|
+
#
|
|
20
|
+
# # Make the Album class use prepared statements for Dataset#with_pk
|
|
21
|
+
# Album.plugin :prepared_statements_with_pk
|
|
22
|
+
module PreparedStatementsWithPk
|
|
23
|
+
# Depend on the prepared_statements plugin
|
|
24
|
+
def self.apply(model)
|
|
25
|
+
model.plugin(:prepared_statements)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
module ClassMethods
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
# Return a prepared statement that can be used to lookup a row given a dataset for the row matching
|
|
32
|
+
# the primary key.
|
|
33
|
+
def prepared_lookup_dataset(ds)
|
|
34
|
+
cached_prepared_statement(:lookup_sql, ds.sql){prepare_statement(ds.filter(prepared_statement_key_array(primary_key).map{|k, v| [SQL::QualifiedIdentifier.new(ds.model.table_name, k), v]}), :first)}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module DatasetMethods
|
|
39
|
+
# Use a prepared statement to find a row with the matching primary key
|
|
40
|
+
# inside this dataset.
|
|
41
|
+
def with_pk(pk)
|
|
42
|
+
begin
|
|
43
|
+
ds, bv = unbind
|
|
44
|
+
rescue UnbindDuplicate
|
|
45
|
+
super
|
|
46
|
+
else
|
|
47
|
+
begin
|
|
48
|
+
bv = bv.merge!(model.primary_key_hash(pk)){|k, v1, v2| ((v1 == v2) ? v1 : raise(UnbindDuplicate))}
|
|
49
|
+
rescue UnbindDuplicate
|
|
50
|
+
super
|
|
51
|
+
else
|
|
52
|
+
model.send(:prepared_lookup_dataset, ds).call(bv)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -109,7 +109,15 @@ module Sequel
|
|
|
109
109
|
ka = opts[:key_alias] ||= :x_root_x
|
|
110
110
|
t = opts[:cte_name] ||= :t
|
|
111
111
|
opts[:reciprocal] = nil
|
|
112
|
-
c_all =
|
|
112
|
+
c_all = if model.dataset.recursive_cte_requires_column_aliases?
|
|
113
|
+
# Work around Oracle/ruby-oci8 bug that returns integers as BigDecimals in recursive queries.
|
|
114
|
+
conv_bd = model.db.database_type == :oracle
|
|
115
|
+
col_aliases = model.dataset.columns
|
|
116
|
+
model_table = model.table_name
|
|
117
|
+
col_aliases.map{|c| SQL::QualifiedIdentifier.new(model_table, c)}
|
|
118
|
+
else
|
|
119
|
+
[SQL::ColumnAll.new(model.table_name)]
|
|
120
|
+
end
|
|
113
121
|
|
|
114
122
|
a = opts.merge(opts.fetch(:ancestors, {}))
|
|
115
123
|
ancestors = a.fetch(:name, :ancestors)
|
|
@@ -125,9 +133,9 @@ module Sequel
|
|
|
125
133
|
end
|
|
126
134
|
table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
|
|
127
135
|
model.from(t => table_alias).
|
|
128
|
-
with_recursive(t, base_ds.select_all,
|
|
129
|
-
recursive_ds.
|
|
130
|
-
|
|
136
|
+
with_recursive(t, col_aliases ? base_ds.select(*col_aliases) : base_ds.select_all,
|
|
137
|
+
recursive_ds.select(*c_all),
|
|
138
|
+
:args=>col_aliases)
|
|
131
139
|
end
|
|
132
140
|
aal = Array(a[:after_load])
|
|
133
141
|
aal << proc do |m, ancs|
|
|
@@ -152,7 +160,7 @@ module Sequel
|
|
|
152
160
|
end
|
|
153
161
|
a[:after_load] ||= aal
|
|
154
162
|
a[:eager_loader] ||= proc do |eo|
|
|
155
|
-
id_map = eo[:
|
|
163
|
+
id_map = eo[:id_map]
|
|
156
164
|
parent_map = {}
|
|
157
165
|
children_map = {}
|
|
158
166
|
eo[:rows].each do |obj|
|
|
@@ -163,9 +171,9 @@ module Sequel
|
|
|
163
171
|
end
|
|
164
172
|
r = model.association_reflection(ancestors)
|
|
165
173
|
base_case = model.filter(prkey=>id_map.keys).
|
|
166
|
-
select(SQL::AliasedExpression.new(prkey, ka), c_all)
|
|
174
|
+
select(SQL::AliasedExpression.new(prkey, ka), *c_all)
|
|
167
175
|
recursive_case = model.join(t, key=>prkey).
|
|
168
|
-
select(SQL::QualifiedIdentifier.new(t, ka), c_all)
|
|
176
|
+
select(SQL::QualifiedIdentifier.new(t, ka), *c_all)
|
|
169
177
|
if c = r[:conditions]
|
|
170
178
|
(base_case, recursive_case) = [base_case, recursive_case].collect do |ds|
|
|
171
179
|
(c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.filter(*c) : ds.filter(c)
|
|
@@ -175,7 +183,8 @@ module Sequel
|
|
|
175
183
|
elds = model.eager_loading_dataset(r,
|
|
176
184
|
model.from(t => table_alias).
|
|
177
185
|
with_recursive(t, base_case,
|
|
178
|
-
recursive_case
|
|
186
|
+
recursive_case,
|
|
187
|
+
:args=>(([ka] + col_aliases) if col_aliases)),
|
|
179
188
|
r.select,
|
|
180
189
|
eo[:associations], eo)
|
|
181
190
|
elds = elds.select_append(ka) unless elds.opts[:select] == nil
|
|
@@ -192,7 +201,9 @@ module Sequel
|
|
|
192
201
|
(children_map[obj[key]] ||= []) << obj
|
|
193
202
|
end
|
|
194
203
|
|
|
195
|
-
|
|
204
|
+
kv = obj.values.delete(ka)
|
|
205
|
+
kv = kv.to_i if conv_bd && kv.is_a?(BigDecimal)
|
|
206
|
+
if roots = id_map[kv]
|
|
196
207
|
roots.each do |root|
|
|
197
208
|
root.associations[ancestors] << obj
|
|
198
209
|
end
|
|
@@ -222,9 +233,9 @@ module Sequel
|
|
|
222
233
|
end
|
|
223
234
|
table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
|
|
224
235
|
model.from(t => table_alias).
|
|
225
|
-
with_recursive(t, base_ds.select_all,
|
|
226
|
-
recursive_ds.
|
|
227
|
-
|
|
236
|
+
with_recursive(t, col_aliases ? base_ds.select(*col_aliases) : base_ds.select_all,
|
|
237
|
+
recursive_ds.select(*c_all),
|
|
238
|
+
:args=>col_aliases)
|
|
228
239
|
end
|
|
229
240
|
dal = Array(d[:after_load])
|
|
230
241
|
dal << proc do |m, descs|
|
|
@@ -248,7 +259,7 @@ module Sequel
|
|
|
248
259
|
end
|
|
249
260
|
d[:after_load] = dal
|
|
250
261
|
d[:eager_loader] ||= proc do |eo|
|
|
251
|
-
id_map = eo[:
|
|
262
|
+
id_map = eo[:id_map]
|
|
252
263
|
associations = eo[:associations]
|
|
253
264
|
parent_map = {}
|
|
254
265
|
children_map = {}
|
|
@@ -259,9 +270,9 @@ module Sequel
|
|
|
259
270
|
end
|
|
260
271
|
r = model.association_reflection(descendants)
|
|
261
272
|
base_case = model.filter(key=>id_map.keys).
|
|
262
|
-
select(SQL::AliasedExpression.new(key, ka), c_all)
|
|
273
|
+
select(SQL::AliasedExpression.new(key, ka), *c_all)
|
|
263
274
|
recursive_case = model.join(t, prkey=>key).
|
|
264
|
-
select(SQL::QualifiedIdentifier.new(t, ka), c_all)
|
|
275
|
+
select(SQL::QualifiedIdentifier.new(t, ka), *c_all)
|
|
265
276
|
if c = r[:conditions]
|
|
266
277
|
(base_case, recursive_case) = [base_case, recursive_case].collect do |ds|
|
|
267
278
|
(c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.filter(*c) : ds.filter(c)
|
|
@@ -276,7 +287,8 @@ module Sequel
|
|
|
276
287
|
end
|
|
277
288
|
table_alias = model.dataset.schema_and_table(model.table_name)[1].to_sym
|
|
278
289
|
elds = model.eager_loading_dataset(r,
|
|
279
|
-
model.from(t => table_alias).with_recursive(t, base_case, recursive_case
|
|
290
|
+
model.from(t => table_alias).with_recursive(t, base_case, recursive_case,
|
|
291
|
+
:args=>(([ka] + col_aliases + (level ? [la] : [])) if col_aliases)),
|
|
280
292
|
r.select,
|
|
281
293
|
associations, eo)
|
|
282
294
|
elds = elds.select_append(ka) unless elds.opts[:select] == nil
|
|
@@ -296,7 +308,9 @@ module Sequel
|
|
|
296
308
|
parent_map[opk] = obj
|
|
297
309
|
end
|
|
298
310
|
|
|
299
|
-
|
|
311
|
+
kv = obj.values.delete(ka)
|
|
312
|
+
kv = kv.to_i if conv_bd && kv.is_a?(BigDecimal)
|
|
313
|
+
if root = id_map[kv].first
|
|
300
314
|
root.associations[descendants] << obj
|
|
301
315
|
end
|
|
302
316
|
|
|
@@ -29,7 +29,7 @@ module Sequel
|
|
|
29
29
|
# Drops the table if it exists and then runs create_table. Should probably
|
|
30
30
|
# not be used except in testing.
|
|
31
31
|
def create_table!(*args, &block)
|
|
32
|
-
drop_table
|
|
32
|
+
drop_table?
|
|
33
33
|
create_table(*args, &block)
|
|
34
34
|
end
|
|
35
35
|
|
|
@@ -38,11 +38,16 @@ module Sequel
|
|
|
38
38
|
create_table(*args, &block) unless table_exists?
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
# Drops table.
|
|
41
|
+
# Drops table. If the table doesn't exist, this will probably raise an error.
|
|
42
42
|
def drop_table
|
|
43
43
|
db.drop_table(table_name)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
# Drops table if it already exists, do nothing if it doesn't exist.
|
|
47
|
+
def drop_table?
|
|
48
|
+
db.drop_table?(table_name)
|
|
49
|
+
end
|
|
50
|
+
|
|
46
51
|
# Returns table schema created with set_schema for direct descendant of Model.
|
|
47
52
|
# Does not retreive schema information from the database, see db_schema if you
|
|
48
53
|
# want that.
|
|
@@ -61,7 +66,7 @@ module Sequel
|
|
|
61
66
|
# test code or simple examples.
|
|
62
67
|
def set_schema(name = nil, &block)
|
|
63
68
|
set_dataset(db[name]) if name
|
|
64
|
-
@schema =
|
|
69
|
+
@schema = db.create_table_generator(&block)
|
|
65
70
|
set_primary_key(@schema.primary_key_name) if @schema.primary_key_name
|
|
66
71
|
end
|
|
67
72
|
|