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
|
@@ -6,21 +6,27 @@ module Sequel
|
|
|
6
6
|
class Database < Sequel::Database
|
|
7
7
|
include Sequel::MSSQL::DatabaseMethods
|
|
8
8
|
set_adapter_scheme :tinytds
|
|
9
|
+
|
|
10
|
+
# Choose whether to use unicode strings on initialization
|
|
11
|
+
def initialize(*)
|
|
12
|
+
super
|
|
13
|
+
set_mssql_unicode_strings
|
|
14
|
+
end
|
|
9
15
|
|
|
10
|
-
# Transfer the :
|
|
11
|
-
# :dataserver and :username options.
|
|
16
|
+
# Transfer the :user option to the :username option.
|
|
12
17
|
def connect(server)
|
|
13
18
|
opts = server_opts(server)
|
|
14
|
-
opts[:dataserver] = opts[:host]
|
|
15
19
|
opts[:username] = opts[:user]
|
|
16
|
-
TinyTds::Client.new(opts)
|
|
17
|
-
|
|
20
|
+
c = TinyTds::Client.new(opts)
|
|
21
|
+
|
|
22
|
+
if (ts = opts[:textsize])
|
|
23
|
+
sql = "SET TEXTSIZE #{typecast_value_integer(ts)}"
|
|
24
|
+
log_yield(sql){c.execute(sql)}
|
|
25
|
+
end
|
|
18
26
|
|
|
19
|
-
|
|
20
|
-
def dataset(opts = nil)
|
|
21
|
-
TinyTDS::Dataset.new(self, opts)
|
|
27
|
+
c
|
|
22
28
|
end
|
|
23
|
-
|
|
29
|
+
|
|
24
30
|
# Execute the given +sql+ on the server. If the :return option
|
|
25
31
|
# is present, its value should be a method symbol that is called
|
|
26
32
|
# on the TinyTds::Result object returned from executing the
|
|
@@ -32,13 +38,36 @@ module Sequel
|
|
|
32
38
|
begin
|
|
33
39
|
m = opts[:return]
|
|
34
40
|
r = nil
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
if (args = opts[:arguments]) && !args.empty?
|
|
42
|
+
types = []
|
|
43
|
+
values = []
|
|
44
|
+
args.each_with_index do |(k, v), i|
|
|
45
|
+
v, type = ps_arg_type(v)
|
|
46
|
+
types << "@#{k} #{type}"
|
|
47
|
+
values << "@#{k} = #{v}"
|
|
48
|
+
end
|
|
49
|
+
case m
|
|
50
|
+
when :do
|
|
51
|
+
sql = "#{sql}; SELECT @@ROWCOUNT AS AffectedRows"
|
|
52
|
+
single_value = true
|
|
53
|
+
when :insert
|
|
54
|
+
sql = "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"
|
|
55
|
+
single_value = true
|
|
56
|
+
end
|
|
57
|
+
sql = "EXEC sp_executesql N'#{c.escape(sql)}', N'#{c.escape(types.join(', '))}', #{values.join(', ')}"
|
|
58
|
+
log_yield(sql) do
|
|
59
|
+
r = c.execute(sql)
|
|
60
|
+
r.each{|row| return row.values.first} if single_value
|
|
61
|
+
end
|
|
62
|
+
else
|
|
63
|
+
log_yield(sql) do
|
|
64
|
+
r = c.execute(sql)
|
|
65
|
+
return r.send(m) if m
|
|
66
|
+
end
|
|
38
67
|
end
|
|
39
68
|
yield(r) if block_given?
|
|
40
69
|
rescue TinyTds::Error => e
|
|
41
|
-
raise_error(e)
|
|
70
|
+
raise_error(e, :disconnect=>!c.active?)
|
|
42
71
|
ensure
|
|
43
72
|
r.cancel if r && c.sqlsent?
|
|
44
73
|
end
|
|
@@ -74,51 +103,174 @@ module Sequel
|
|
|
74
103
|
super
|
|
75
104
|
end
|
|
76
105
|
|
|
77
|
-
#
|
|
78
|
-
def
|
|
79
|
-
|
|
106
|
+
# tiny_tds uses TinyTds::Error as the base error class.
|
|
107
|
+
def database_error_classes
|
|
108
|
+
[TinyTds::Error]
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Return true if the :conn argument is present and not active.
|
|
112
|
+
def disconnect_error?(e, opts)
|
|
113
|
+
super || (opts[:conn] && !opts[:conn].active?)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Dispose of any possible results of execution.
|
|
117
|
+
def log_connection_execute(conn, sql)
|
|
118
|
+
log_yield(sql){conn.execute(sql).each}
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Return a 2 element array with the literal value and type to use
|
|
122
|
+
# in the prepared statement call for the given value and connection.
|
|
123
|
+
def ps_arg_type(v)
|
|
124
|
+
case v
|
|
125
|
+
when Fixnum
|
|
126
|
+
[v, 'int']
|
|
127
|
+
when Bignum
|
|
128
|
+
[v, 'bigint']
|
|
129
|
+
when Float
|
|
130
|
+
[v, 'double precision']
|
|
131
|
+
when Numeric
|
|
132
|
+
[v, 'numeric']
|
|
133
|
+
when Time
|
|
134
|
+
if v.is_a?(SQLTime)
|
|
135
|
+
[literal(v), 'time']
|
|
136
|
+
else
|
|
137
|
+
[literal(v), 'datetime']
|
|
138
|
+
end
|
|
139
|
+
when DateTime
|
|
140
|
+
[literal(v), 'datetime']
|
|
141
|
+
when Date
|
|
142
|
+
[literal(v), 'date']
|
|
143
|
+
when nil
|
|
144
|
+
['NULL', 'nvarchar(max)']
|
|
145
|
+
when true
|
|
146
|
+
['1', 'int']
|
|
147
|
+
when false
|
|
148
|
+
['0', 'int']
|
|
149
|
+
when SQL::Blob
|
|
150
|
+
[literal(v), 'varbinary(max)']
|
|
151
|
+
else
|
|
152
|
+
[literal(v), 'nvarchar(max)']
|
|
153
|
+
end
|
|
80
154
|
end
|
|
81
155
|
end
|
|
82
156
|
|
|
83
157
|
class Dataset < Sequel::Dataset
|
|
84
158
|
include Sequel::MSSQL::DatasetMethods
|
|
159
|
+
|
|
160
|
+
Database::DatasetClass = self
|
|
161
|
+
|
|
162
|
+
# SQLite already supports named bind arguments, so use directly.
|
|
163
|
+
module ArgumentMapper
|
|
164
|
+
include Sequel::Dataset::ArgumentMapper
|
|
165
|
+
|
|
166
|
+
protected
|
|
167
|
+
|
|
168
|
+
# Return a hash with the same values as the given hash,
|
|
169
|
+
# but with the keys converted to strings.
|
|
170
|
+
def map_to_prepared_args(hash)
|
|
171
|
+
args = {}
|
|
172
|
+
hash.each{|k,v| args[k.to_s.gsub('.', '__')] = v}
|
|
173
|
+
args
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
private
|
|
177
|
+
|
|
178
|
+
# SQLite uses a : before the name of the argument for named
|
|
179
|
+
# arguments.
|
|
180
|
+
def prepared_arg(k)
|
|
181
|
+
LiteralString.new("@#{k.to_s.gsub('.', '__')}")
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Always assume a prepared argument.
|
|
185
|
+
def prepared_arg?(k)
|
|
186
|
+
true
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# SQLite prepared statement uses a new prepared statement each time
|
|
191
|
+
# it is called, but it does use the bind arguments.
|
|
192
|
+
module PreparedStatementMethods
|
|
193
|
+
include ArgumentMapper
|
|
194
|
+
|
|
195
|
+
private
|
|
196
|
+
|
|
197
|
+
# Run execute_select on the database with the given SQL and the stored
|
|
198
|
+
# bind arguments.
|
|
199
|
+
def execute(sql, opts={}, &block)
|
|
200
|
+
super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
|
204
|
+
def execute_dui(sql, opts={}, &block)
|
|
205
|
+
super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
|
209
|
+
def execute_insert(sql, opts={}, &block)
|
|
210
|
+
super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
85
213
|
|
|
86
214
|
# Yield hashes with symbol keys, attempting to optimize for
|
|
87
215
|
# various cases.
|
|
88
216
|
def fetch_rows(sql)
|
|
89
217
|
execute(sql) do |result|
|
|
90
218
|
each_opts = {:cache_rows=>false}
|
|
91
|
-
each_opts[:timezone] = :utc if
|
|
92
|
-
offset = @opts[:offset]
|
|
93
|
-
|
|
94
|
-
if
|
|
219
|
+
each_opts[:timezone] = :utc if db.timezone == :utc
|
|
220
|
+
rn = row_number_column if offset = @opts[:offset]
|
|
221
|
+
columns = cols = result.fields.map{|c| output_identifier(c)}
|
|
222
|
+
if offset
|
|
223
|
+
rn = row_number_column
|
|
224
|
+
columns = columns.dup
|
|
225
|
+
columns.delete(rn)
|
|
226
|
+
end
|
|
227
|
+
@columns = columns
|
|
228
|
+
#if identifier_output_method
|
|
95
229
|
each_opts[:as] = :array
|
|
96
230
|
result.each(each_opts) do |r|
|
|
97
231
|
h = {}
|
|
98
232
|
cols.zip(r).each{|k, v| h[k] = v}
|
|
99
|
-
h.delete(
|
|
233
|
+
h.delete(rn) if rn
|
|
100
234
|
yield h
|
|
101
235
|
end
|
|
236
|
+
=begin
|
|
237
|
+
# Temporarily disable this optimization, as tiny_tds uses string keys
|
|
238
|
+
# if result.fields is called before result.each(:symbolize_keys=>true).
|
|
239
|
+
# See https://github.com/rails-sqlserver/tiny_tds/issues/57
|
|
102
240
|
else
|
|
103
241
|
each_opts[:symbolize_keys] = true
|
|
104
242
|
if offset
|
|
105
243
|
result.each(each_opts) do |r|
|
|
106
|
-
r.delete(
|
|
244
|
+
r.delete(rn) if rn
|
|
107
245
|
yield r
|
|
108
246
|
end
|
|
109
247
|
else
|
|
110
|
-
result.each(each_opts, &
|
|
248
|
+
result.each(each_opts, &Proc.new)
|
|
111
249
|
end
|
|
112
250
|
end
|
|
251
|
+
=end
|
|
113
252
|
end
|
|
114
253
|
self
|
|
115
254
|
end
|
|
116
255
|
|
|
256
|
+
# Create a named prepared statement that is stored in the
|
|
257
|
+
# database (and connection) for reuse.
|
|
258
|
+
def prepare(type, name=nil, *values)
|
|
259
|
+
ps = to_prepared_statement(type, values)
|
|
260
|
+
ps.extend(PreparedStatementMethods)
|
|
261
|
+
if name
|
|
262
|
+
ps.prepared_statement_name = name
|
|
263
|
+
db.set_prepared_statement(name, ps)
|
|
264
|
+
end
|
|
265
|
+
ps
|
|
266
|
+
end
|
|
267
|
+
|
|
117
268
|
private
|
|
118
269
|
|
|
119
270
|
# Properly escape the given string +v+.
|
|
120
|
-
def
|
|
121
|
-
|
|
271
|
+
def literal_string_append(sql, v)
|
|
272
|
+
sql << (mssql_unicode_strings ? UNICODE_STRING_START : APOS)
|
|
273
|
+
sql << db.synchronize{|c| c.escape(v)}.gsub(BACKSLASH_CRLF_RE, BACKSLASH_CRLF_REPLACE) << APOS
|
|
122
274
|
end
|
|
123
275
|
end
|
|
124
276
|
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module EmulateOffsetWithRowNumber
|
|
3
|
+
# When a subselect that uses :offset is used in IN or NOT IN,
|
|
4
|
+
# use a nested subselect that only includes the first column
|
|
5
|
+
# instead of the ROW_NUMBER column added by the emulated offset support.
|
|
6
|
+
def complex_expression_sql_append(sql, op, args)
|
|
7
|
+
case op
|
|
8
|
+
when :IN, :"NOT IN"
|
|
9
|
+
ds = args.at(1)
|
|
10
|
+
if ds.is_a?(Sequel::Dataset) && ds.opts[:offset]
|
|
11
|
+
c = ds.opts[:select].first
|
|
12
|
+
case c
|
|
13
|
+
when Symbol
|
|
14
|
+
t, cl, a = split_symbol(c)
|
|
15
|
+
if a
|
|
16
|
+
c = SQL::Identifier.new(a)
|
|
17
|
+
elsif t
|
|
18
|
+
c = SQL::Identifier.new(cl)
|
|
19
|
+
end
|
|
20
|
+
when SQL::AliasedExpression
|
|
21
|
+
c = SQL::Identifier.new(c.aliaz)
|
|
22
|
+
when SQL::QualifiedIdentifier
|
|
23
|
+
c = SQL::Identifier.new(c.column)
|
|
24
|
+
end
|
|
25
|
+
super(sql, op, [args.at(0), ds.from_self.select(c)])
|
|
26
|
+
else
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
else
|
|
30
|
+
super
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Emulate OFFSET support with the ROW_NUMBER window function
|
|
35
|
+
#
|
|
36
|
+
# The implementation is ugly, cloning the current dataset and modifying
|
|
37
|
+
# the clone to add a ROW_NUMBER window function (and some other things),
|
|
38
|
+
# then using the modified clone in a subselect which is selected from.
|
|
39
|
+
#
|
|
40
|
+
# If offset is used, an order must be provided, because the use of ROW_NUMBER
|
|
41
|
+
# requires an order.
|
|
42
|
+
def select_sql
|
|
43
|
+
return super unless o = @opts[:offset]
|
|
44
|
+
|
|
45
|
+
order = @opts[:order] || default_offset_order
|
|
46
|
+
if order.nil? || order.empty?
|
|
47
|
+
raise(Error, "#{db.database_type} requires an order be provided if using an offset")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
dsa1 = dataset_alias(1)
|
|
51
|
+
rn = row_number_column
|
|
52
|
+
sql = @opts[:append_sql] || ''
|
|
53
|
+
subselect_sql_append(sql, unlimited.
|
|
54
|
+
unordered.
|
|
55
|
+
select_append{ROW_NUMBER(:over, :order=>order){}.as(rn)}.
|
|
56
|
+
from_self(:alias=>dsa1).
|
|
57
|
+
limit(@opts[:limit]).
|
|
58
|
+
where(SQL::Identifier.new(rn) > o).
|
|
59
|
+
order(rn))
|
|
60
|
+
sql
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
# The default order to use for datasets with offsets, if no order is defined.
|
|
66
|
+
# By default, orders by all of the columns in the dataset.
|
|
67
|
+
def default_offset_order
|
|
68
|
+
clone(:append_sql=>'').columns
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# This emulation adds an extra row number column that should be
|
|
72
|
+
# eliminated.
|
|
73
|
+
def offset_returns_row_number_column?
|
|
74
|
+
true
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module Postgres
|
|
3
|
+
NAN = 0.0/0.0
|
|
4
|
+
PLUS_INFINITY = 1.0/0.0
|
|
5
|
+
MINUS_INFINITY = -1.0/0.0
|
|
6
|
+
NAN_STR = 'NaN'.freeze
|
|
7
|
+
PLUS_INFINITY_STR = 'Infinity'.freeze
|
|
8
|
+
MINUS_INFINITY_STR = '-Infinity'.freeze
|
|
9
|
+
TRUE_STR = 't'.freeze
|
|
10
|
+
DASH_STR = '-'.freeze
|
|
11
|
+
|
|
12
|
+
TYPE_TRANSLATOR = tt = Class.new do
|
|
13
|
+
def boolean(s) s == TRUE_STR end
|
|
14
|
+
def integer(s) s.to_i end
|
|
15
|
+
def float(s)
|
|
16
|
+
case s
|
|
17
|
+
when NAN_STR
|
|
18
|
+
NAN
|
|
19
|
+
when PLUS_INFINITY_STR
|
|
20
|
+
PLUS_INFINITY
|
|
21
|
+
when MINUS_INFINITY_STR
|
|
22
|
+
MINUS_INFINITY
|
|
23
|
+
else
|
|
24
|
+
s.to_f
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
def date(s) ::Date.new(*s.split(DASH_STR).map{|x| x.to_i}) end
|
|
28
|
+
def bytea(str)
|
|
29
|
+
str = if str =~ /\A\\x/
|
|
30
|
+
# PostgreSQL 9.0+ bytea hex format
|
|
31
|
+
str[2..-1].gsub(/(..)/){|s| s.to_i(16).chr}
|
|
32
|
+
else
|
|
33
|
+
# Historical PostgreSQL bytea escape format
|
|
34
|
+
str.gsub(/\\(\\|'|[0-3][0-7][0-7])/) {|s|
|
|
35
|
+
if s.size == 2 then s[1,1] else s[1,3].oct.chr end
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
::Sequel::SQL::Blob.new(str)
|
|
39
|
+
end
|
|
40
|
+
end.new
|
|
41
|
+
|
|
42
|
+
# Type OIDs for string types used by PostgreSQL. These types don't
|
|
43
|
+
# have conversion procs associated with them (since the data is
|
|
44
|
+
# already in the form of a string).
|
|
45
|
+
STRING_TYPES = [18, 19, 25, 1042, 1043]
|
|
46
|
+
|
|
47
|
+
# Hash with type name strings/symbols and callable values for converting PostgreSQL types.
|
|
48
|
+
# Non-builtin types that don't have fixed numbers should use this to register
|
|
49
|
+
# conversion procs.
|
|
50
|
+
PG_NAMED_TYPES = {} unless defined?(PG_NAMED_TYPES)
|
|
51
|
+
|
|
52
|
+
# Hash with integer keys and callable values for converting PostgreSQL types.
|
|
53
|
+
PG_TYPES = {} unless defined?(PG_TYPES)
|
|
54
|
+
|
|
55
|
+
{
|
|
56
|
+
[16] => tt.method(:boolean),
|
|
57
|
+
[17] => tt.method(:bytea),
|
|
58
|
+
[20, 21, 23, 26] => tt.method(:integer),
|
|
59
|
+
[700, 701] => tt.method(:float),
|
|
60
|
+
[1700] => ::BigDecimal.method(:new),
|
|
61
|
+
[1083, 1266] => ::Sequel.method(:string_to_time),
|
|
62
|
+
[1184, 1114] => ::Sequel.method(:database_to_application_timestamp),
|
|
63
|
+
}.each do |k,v|
|
|
64
|
+
k.each{|n| PG_TYPES[n] = v}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class << self
|
|
68
|
+
# As an optimization, Sequel sets the date style to ISO, so that PostgreSQL provides
|
|
69
|
+
# the date in a known format that Sequel can parse faster. This can be turned off
|
|
70
|
+
# if you require a date style other than ISO.
|
|
71
|
+
attr_reader :use_iso_date_format
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Modify the type translator for the date type depending on the value given.
|
|
75
|
+
def self.use_iso_date_format=(v)
|
|
76
|
+
PG_TYPES[1082] = v ? TYPE_TRANSLATOR.method(:date) : Sequel.method(:string_to_date)
|
|
77
|
+
@use_iso_date_format = v
|
|
78
|
+
end
|
|
79
|
+
self.use_iso_date_format = true
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Sequel::Database::SplitAlterTable
|
|
2
|
+
private
|
|
3
|
+
|
|
4
|
+
# Preprocess the array of operations. If it looks like some operations depend
|
|
5
|
+
# on results of earlier operations and may require reloading the schema to
|
|
6
|
+
# work correctly, split those operations into separate lists, and between each
|
|
7
|
+
# list, remove the cached schema so that the later operations deal with the
|
|
8
|
+
# then current table schema.
|
|
9
|
+
def apply_alter_table(name, ops)
|
|
10
|
+
modified_columns = []
|
|
11
|
+
op_groups = [[]]
|
|
12
|
+
ops.each do |op|
|
|
13
|
+
case op[:op]
|
|
14
|
+
when :add_column, :set_column_type, :set_column_null, :set_column_default
|
|
15
|
+
if modified_columns.include?(op[:name])
|
|
16
|
+
op_groups << []
|
|
17
|
+
else
|
|
18
|
+
modified_columns << op[:name]
|
|
19
|
+
end
|
|
20
|
+
when :rename_column
|
|
21
|
+
if modified_columns.include?(op[:name]) || modified_columns.include?(op[:new_name])
|
|
22
|
+
op_groups << []
|
|
23
|
+
end
|
|
24
|
+
modified_columns << op[:name] unless modified_columns.include?(op[:name])
|
|
25
|
+
modified_columns << op[:new_name] unless modified_columns.include?(op[:new_name])
|
|
26
|
+
end
|
|
27
|
+
op_groups.last << op
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
op_groups.each do |ops|
|
|
31
|
+
next if ops.empty?
|
|
32
|
+
alter_table_sql_list(name, ops).each{|sql| execute_ddl(sql)}
|
|
33
|
+
remove_cached_schema(name)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Dataset
|
|
3
3
|
module StoredProcedureMethods
|
|
4
|
-
SQL_QUERY_TYPE = Hash.new{|h,k| h[k] = k}
|
|
5
|
-
SQL_QUERY_TYPE[:first] = SQL_QUERY_TYPE[:all] = :select
|
|
6
|
-
|
|
7
4
|
# The name of the stored procedure to call
|
|
8
5
|
attr_accessor :sproc_name
|
|
9
6
|
|
|
@@ -44,14 +41,7 @@ module Sequel
|
|
|
44
41
|
# ignored anyway).
|
|
45
42
|
def sproc_type=(type)
|
|
46
43
|
@sproc_type = type
|
|
47
|
-
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
private
|
|
51
|
-
|
|
52
|
-
# The type of query (:select, :insert, :delete, :update).
|
|
53
|
-
def sql_query_type
|
|
54
|
-
SQL_QUERY_TYPE[@sproc_type]
|
|
44
|
+
@opts[:sql] = ''
|
|
55
45
|
end
|
|
56
46
|
end
|
|
57
47
|
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
# The +ASTTransformer+ class is designed to handle the abstract syntax trees
|
|
3
|
+
# that Sequel uses internally and produce modified copies of them. By itself
|
|
4
|
+
# it only produces a straight copy. It's designed to be subclassed and have
|
|
5
|
+
# subclasses returned modified copies of the specific nodes that need to
|
|
6
|
+
# be modified.
|
|
7
|
+
class ASTTransformer
|
|
8
|
+
# Return +obj+ or a potentially transformed version of it.
|
|
9
|
+
def transform(obj)
|
|
10
|
+
v(obj)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
# Recursive version that handles all of Sequel's internal object types
|
|
16
|
+
# and produces copies of them.
|
|
17
|
+
def v(o)
|
|
18
|
+
case o
|
|
19
|
+
when Symbol, Numeric, String, Class, TrueClass, FalseClass, NilClass
|
|
20
|
+
o
|
|
21
|
+
when Array
|
|
22
|
+
o.map{|x| v(x)}
|
|
23
|
+
when Hash
|
|
24
|
+
h = {}
|
|
25
|
+
o.each{|k, val| h[v(k)] = v(val)}
|
|
26
|
+
h
|
|
27
|
+
when SQL::ComplexExpression
|
|
28
|
+
SQL::ComplexExpression.new(o.op, *v(o.args))
|
|
29
|
+
when SQL::Identifier
|
|
30
|
+
SQL::Identifier.new(v(o.value))
|
|
31
|
+
when SQL::QualifiedIdentifier
|
|
32
|
+
SQL::QualifiedIdentifier.new(v(o.table), v(o.column))
|
|
33
|
+
when SQL::OrderedExpression
|
|
34
|
+
SQL::OrderedExpression.new(v(o.expression), o.descending, :nulls=>o.nulls)
|
|
35
|
+
when SQL::AliasedExpression
|
|
36
|
+
SQL::AliasedExpression.new(v(o.expression), o.aliaz)
|
|
37
|
+
when SQL::CaseExpression
|
|
38
|
+
args = [v(o.conditions), v(o.default)]
|
|
39
|
+
args << v(o.expression) if o.expression?
|
|
40
|
+
SQL::CaseExpression.new(*args)
|
|
41
|
+
when SQL::Cast
|
|
42
|
+
SQL::Cast.new(v(o.expr), o.type)
|
|
43
|
+
when SQL::Function
|
|
44
|
+
SQL::Function.new(o.f, *v(o.args))
|
|
45
|
+
when SQL::Subscript
|
|
46
|
+
SQL::Subscript.new(v(o.f), v(o.sub))
|
|
47
|
+
when SQL::WindowFunction
|
|
48
|
+
SQL::WindowFunction.new(v(o.function), v(o.window))
|
|
49
|
+
when SQL::Window
|
|
50
|
+
opts = o.opts.dup
|
|
51
|
+
opts[:partition] = v(opts[:partition]) if opts[:partition]
|
|
52
|
+
opts[:order] = v(opts[:order]) if opts[:order]
|
|
53
|
+
SQL::Window.new(opts)
|
|
54
|
+
when SQL::PlaceholderLiteralString
|
|
55
|
+
args = if o.args.is_a?(Hash)
|
|
56
|
+
h = {}
|
|
57
|
+
o.args.each{|k,val| h[k] = v(val)}
|
|
58
|
+
h
|
|
59
|
+
else
|
|
60
|
+
v(o.args)
|
|
61
|
+
end
|
|
62
|
+
SQL::PlaceholderLiteralString.new(o.str, args, o.parens)
|
|
63
|
+
when SQL::JoinOnClause
|
|
64
|
+
SQL::JoinOnClause.new(v(o.on), o.join_type, v(o.table), v(o.table_alias))
|
|
65
|
+
when SQL::JoinUsingClause
|
|
66
|
+
SQL::JoinUsingClause.new(v(o.using), o.join_type, v(o.table), v(o.table_alias))
|
|
67
|
+
when SQL::JoinClause
|
|
68
|
+
SQL::JoinClause.new(o.join_type, v(o.table), v(o.table_alias))
|
|
69
|
+
when SQL::Wrapper
|
|
70
|
+
SQL::Wrapper.new(v(o.value))
|
|
71
|
+
else
|
|
72
|
+
o
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Handles qualifying existing datasets, so that unqualified columns
|
|
78
|
+
# in the dataset are qualified with a given table name.
|
|
79
|
+
class Qualifier < ASTTransformer
|
|
80
|
+
# Store the dataset to use as the basis for qualification,
|
|
81
|
+
# and the table used to qualify unqualified columns.
|
|
82
|
+
def initialize(ds, table)
|
|
83
|
+
@ds = ds
|
|
84
|
+
@table = table
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
# Turn <tt>SQL::Identifier</tt>s and symbols that aren't implicitly
|
|
90
|
+
# qualified into <tt>SQL::QualifiedIdentifier</tt>s. For symbols that
|
|
91
|
+
# are not implicitly qualified by are implicitly aliased, return an
|
|
92
|
+
# <tt>SQL::AliasedExpression</tt>s with a qualified version of the symbol.
|
|
93
|
+
def v(o)
|
|
94
|
+
case o
|
|
95
|
+
when Symbol
|
|
96
|
+
t, column, aliaz = @ds.send(:split_symbol, o)
|
|
97
|
+
if t
|
|
98
|
+
o
|
|
99
|
+
elsif aliaz
|
|
100
|
+
SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(@table, SQL::Identifier.new(column)), aliaz)
|
|
101
|
+
else
|
|
102
|
+
SQL::QualifiedIdentifier.new(@table, o)
|
|
103
|
+
end
|
|
104
|
+
when SQL::Identifier
|
|
105
|
+
SQL::QualifiedIdentifier.new(@table, o)
|
|
106
|
+
when SQL::QualifiedIdentifier, SQL::JoinClause
|
|
107
|
+
# Return these directly, so we don't accidentally qualify symbols in them.
|
|
108
|
+
o
|
|
109
|
+
else
|
|
110
|
+
super
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# +Unbinder+ is used to take a dataset filter and return a modified version
|
|
116
|
+
# that unbinds already bound values and returns a dataset with bound value
|
|
117
|
+
# placeholders and a hash of bind values. You can then prepare the dataset
|
|
118
|
+
# and use the bound variables to execute it with the same values.
|
|
119
|
+
#
|
|
120
|
+
# This class only does a limited form of unbinding where the variable names
|
|
121
|
+
# and values can be associated unambiguously. The only cases it handles
|
|
122
|
+
# are <tt>SQL::ComplexExpression<tt> with an operator in +UNBIND_OPS+, a
|
|
123
|
+
# first argument that's an instance of a member of +UNBIND_KEY_CLASSES+, and
|
|
124
|
+
# a second argument that's an instance of a member of +UNBIND_VALUE_CLASSES+.
|
|
125
|
+
#
|
|
126
|
+
# So it can handle cases like:
|
|
127
|
+
#
|
|
128
|
+
# DB.filter(:a=>1).exclude(:b=>2).where{c > 3}
|
|
129
|
+
#
|
|
130
|
+
# But it cannot handle cases like:
|
|
131
|
+
#
|
|
132
|
+
# DB.filter(:a + 1 < 0)
|
|
133
|
+
class Unbinder < ASTTransformer
|
|
134
|
+
# The <tt>SQL::ComplexExpression<tt> operates that will be considered
|
|
135
|
+
# for transformation.
|
|
136
|
+
UNBIND_OPS = [:'=', :'!=', :<, :>, :<=, :>=]
|
|
137
|
+
|
|
138
|
+
# The key classes (first argument of the ComplexExpression) that will
|
|
139
|
+
# considered for transformation.
|
|
140
|
+
UNBIND_KEY_CLASSES = [Symbol, SQL::Identifier, SQL::QualifiedIdentifier]
|
|
141
|
+
|
|
142
|
+
# The value classes (second argument of the ComplexExpression) that
|
|
143
|
+
# will be considered for transformation.
|
|
144
|
+
UNBIND_VALUE_CLASSES = [Numeric, String, Date, Time]
|
|
145
|
+
|
|
146
|
+
# The hash of bind variables that were extracted from the dataset filter.
|
|
147
|
+
attr_reader :binds
|
|
148
|
+
|
|
149
|
+
# Intialize an empty +binds+ hash.
|
|
150
|
+
def initialize
|
|
151
|
+
@binds = {}
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
private
|
|
155
|
+
|
|
156
|
+
# Create a suitable bound variable key for the object, which should be
|
|
157
|
+
# an instance of one of the +UNBIND_KEY_CLASSES+.
|
|
158
|
+
def bind_key(obj)
|
|
159
|
+
case obj
|
|
160
|
+
when Symbol, String
|
|
161
|
+
obj
|
|
162
|
+
when SQL::Identifier
|
|
163
|
+
bind_key(obj.value)
|
|
164
|
+
when SQL::QualifiedIdentifier
|
|
165
|
+
:"#{bind_key(obj.table)}.#{bind_key(obj.column)}"
|
|
166
|
+
else
|
|
167
|
+
raise Error, "unhandled object in Sequel::Unbinder#bind_key: #{obj}"
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Handle <tt>SQL::ComplexExpression</tt> instances with suitable ops
|
|
172
|
+
# and arguments, substituting the value with a bound variable placeholder
|
|
173
|
+
# and assigning it an entry in the +binds+ hash with a matching key.
|
|
174
|
+
def v(o)
|
|
175
|
+
if o.is_a?(SQL::ComplexExpression) && UNBIND_OPS.include?(o.op)
|
|
176
|
+
l, r = o.args
|
|
177
|
+
l = l.value if l.is_a?(Sequel::SQL::Wrapper)
|
|
178
|
+
r = r.value if r.is_a?(Sequel::SQL::Wrapper)
|
|
179
|
+
if UNBIND_KEY_CLASSES.any?{|c| l.is_a?(c)} && UNBIND_VALUE_CLASSES.any?{|c| r.is_a?(c)} && !r.is_a?(LiteralString)
|
|
180
|
+
key = bind_key(l)
|
|
181
|
+
if (old = binds[key]) && old != r
|
|
182
|
+
raise UnbindDuplicate, "two different values for #{key.inspect}: #{[r, old].inspect}"
|
|
183
|
+
end
|
|
184
|
+
binds[key] = r
|
|
185
|
+
SQL::ComplexExpression.new(o.op, l, :"$#{key}")
|
|
186
|
+
else
|
|
187
|
+
super
|
|
188
|
+
end
|
|
189
|
+
else
|
|
190
|
+
super
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|