sequel 3.21.0 → 3.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +1061 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +103 -63
- data/Rakefile +59 -27
- data/bin/sequel +50 -26
- data/doc/active_record.rdoc +67 -51
- data/doc/advanced_associations.rdoc +189 -75
- data/doc/association_basics.rdoc +327 -79
- data/doc/cheat_sheet.rdoc +21 -21
- data/doc/core_extensions.rdoc +374 -0
- data/doc/dataset_basics.rdoc +10 -10
- data/doc/dataset_filtering.rdoc +54 -42
- data/doc/mass_assignment.rdoc +56 -0
- data/doc/migration.rdoc +89 -557
- data/doc/model_hooks.rdoc +80 -29
- data/doc/object_model.rdoc +547 -0
- data/doc/opening_databases.rdoc +114 -53
- data/doc/prepared_statements.rdoc +41 -15
- data/doc/querying.rdoc +137 -56
- data/doc/reflection.rdoc +36 -10
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/release_notes/3.31.0.txt +146 -0
- data/doc/release_notes/3.32.0.txt +202 -0
- data/doc/release_notes/3.33.0.txt +157 -0
- data/doc/release_notes/3.34.0.txt +671 -0
- data/doc/release_notes/3.35.0.txt +144 -0
- data/doc/release_notes/3.36.0.txt +245 -0
- data/doc/release_notes/3.37.0.txt +338 -0
- data/doc/release_notes/3.38.0.txt +234 -0
- data/doc/release_notes/3.39.0.txt +237 -0
- data/doc/schema_modification.rdoc +585 -0
- data/doc/sharding.rdoc +99 -8
- data/doc/sql.rdoc +154 -112
- data/doc/testing.rdoc +169 -0
- data/doc/thread_safety.rdoc +17 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/validations.rdoc +1 -1
- data/doc/virtual_rows.rdoc +78 -43
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +17 -9
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +17 -11
- data/lib/sequel/adapters/do/postgres.rb +2 -61
- data/lib/sequel/adapters/do/sqlite.rb +0 -10
- data/lib/sequel/adapters/do.rb +18 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +453 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/db2.rb +56 -0
- data/lib/sequel/adapters/jdbc/derby.rb +325 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
- data/lib/sequel/adapters/jdbc/h2.rb +73 -31
- data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
- data/lib/sequel/adapters/jdbc/informix.rb +21 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
- data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
- data/lib/sequel/adapters/jdbc/progress.rb +21 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
- data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +279 -97
- data/lib/sequel/adapters/mock.rb +372 -0
- data/lib/sequel/adapters/mysql.rb +125 -222
- data/lib/sequel/adapters/mysql2.rb +65 -56
- data/lib/sequel/adapters/odbc/mssql.rb +17 -9
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +349 -53
- data/lib/sequel/adapters/postgres.rb +368 -138
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +343 -0
- data/lib/sequel/adapters/shared/firebird.rb +221 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +401 -116
- data/lib/sequel/adapters/shared/mysql.rb +424 -89
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
- data/lib/sequel/adapters/shared/oracle.rb +257 -45
- data/lib/sequel/adapters/shared/postgres.rb +675 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +285 -96
- data/lib/sequel/adapters/sqlite.rb +101 -39
- data/lib/sequel/adapters/swift/mysql.rb +10 -12
- data/lib/sequel/adapters/swift/postgres.rb +13 -79
- data/lib/sequel/adapters/swift/sqlite.rb +9 -6
- data/lib/sequel/adapters/swift.rb +28 -19
- data/lib/sequel/adapters/tinytds.rb +174 -22
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
- data/lib/sequel/adapters/utils/pg_types.rb +81 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
- data/lib/sequel/ast_transformer.rb +194 -0
- data/lib/sequel/connection_pool/sharded_single.rb +11 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +33 -2
- data/lib/sequel/connection_pool/single.rb +5 -0
- data/lib/sequel/connection_pool/threaded.rb +43 -14
- data/lib/sequel/connection_pool.rb +10 -2
- data/lib/sequel/core.rb +143 -11
- data/lib/sequel/database/connecting.rb +38 -28
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +63 -2
- data/lib/sequel/database/logging.rb +7 -2
- data/lib/sequel/database/misc.rb +190 -46
- data/lib/sequel/database/query.rb +272 -110
- data/lib/sequel/database/schema_generator.rb +103 -36
- data/lib/sequel/database/schema_methods.rb +300 -67
- data/lib/sequel/dataset/actions.rb +344 -90
- data/lib/sequel/dataset/features.rb +101 -8
- data/lib/sequel/dataset/graph.rb +42 -15
- data/lib/sequel/dataset/misc.rb +52 -25
- data/lib/sequel/dataset/mutation.rb +12 -7
- data/lib/sequel/dataset/prepared_statements.rb +68 -30
- data/lib/sequel/dataset/query.rb +305 -54
- data/lib/sequel/dataset/sql.rb +720 -292
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/_pretty_table.rb +83 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
- data/lib/sequel/extensions/blank.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +72 -0
- data/lib/sequel/extensions/constraint_validations.rb +451 -0
- data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
- data/lib/sequel/extensions/eval_inspect.rb +173 -0
- data/lib/sequel/extensions/inflector.rb +4 -0
- data/lib/sequel/extensions/looser_typecasting.rb +5 -4
- data/lib/sequel/extensions/migration.rb +100 -11
- data/lib/sequel/extensions/named_timezones.rb +9 -0
- data/lib/sequel/extensions/null_dataset.rb +94 -0
- data/lib/sequel/extensions/pagination.rb +4 -0
- data/lib/sequel/extensions/pg_array.rb +537 -0
- data/lib/sequel/extensions/pg_array_ops.rb +263 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
- data/lib/sequel/extensions/pg_hstore.rb +313 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
- data/lib/sequel/extensions/pg_inet.rb +113 -0
- data/lib/sequel/extensions/pg_interval.rb +191 -0
- data/lib/sequel/extensions/pg_json.rb +208 -0
- data/lib/sequel/extensions/pg_range.rb +512 -0
- data/lib/sequel/extensions/pg_range_ops.rb +150 -0
- data/lib/sequel/extensions/pg_row.rb +572 -0
- data/lib/sequel/extensions/pg_row_ops.rb +182 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
- data/lib/sequel/extensions/pretty_table.rb +17 -72
- data/lib/sequel/extensions/query.rb +8 -4
- data/lib/sequel/extensions/query_literals.rb +79 -0
- data/lib/sequel/extensions/schema_caching.rb +76 -0
- data/lib/sequel/extensions/schema_dumper.rb +282 -76
- data/lib/sequel/extensions/select_remove.rb +39 -0
- data/lib/sequel/extensions/server_block.rb +140 -0
- data/lib/sequel/extensions/split_array_nil.rb +65 -0
- data/lib/sequel/extensions/sql_expr.rb +8 -110
- data/lib/sequel/extensions/string_date_time.rb +4 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
- data/lib/sequel/extensions/to_dot.rb +99 -83
- data/lib/sequel/model/associations.rb +1263 -409
- data/lib/sequel/model/base.rb +624 -171
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +24 -2
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model.rb +30 -11
- data/lib/sequel/no_core_ext.rb +2 -0
- data/lib/sequel/plugins/active_model.rb +13 -1
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/caching.rb +25 -18
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/composition.rb +44 -12
- data/lib/sequel/plugins/constraint_validations.rb +198 -0
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +72 -0
- data/lib/sequel/plugins/dirty.rb +214 -0
- data/lib/sequel/plugins/eager_each.rb +59 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/plugins/identity_map.rb +134 -15
- data/lib/sequel/plugins/instance_filters.rb +10 -0
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +58 -6
- data/lib/sequel/plugins/list.rb +13 -2
- data/lib/sequel/plugins/many_through_many.rb +103 -51
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
- data/lib/sequel/plugins/nested_attributes.rb +150 -66
- data/lib/sequel/plugins/optimistic_locking.rb +8 -0
- data/lib/sequel/plugins/pg_row.rb +121 -0
- data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
- data/lib/sequel/plugins/prepared_statements.rb +167 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +31 -17
- data/lib/sequel/plugins/schema.rb +8 -3
- data/lib/sequel/plugins/serialization.rb +98 -49
- data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
- data/lib/sequel/plugins/static_cache.rb +99 -0
- data/lib/sequel/plugins/subclasses.rb +29 -3
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/tree.rb +3 -3
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/plugins/validation_helpers.rb +55 -4
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +649 -122
- data/lib/sequel/timezones.rb +67 -40
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/firebird_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +194 -66
- data/spec/adapters/mysql_spec.rb +475 -306
- data/spec/adapters/oracle_spec.rb +92 -117
- data/spec/adapters/postgres_spec.rb +1982 -325
- data/spec/adapters/spec_helper.rb +9 -6
- data/spec/adapters/sqlite_spec.rb +203 -75
- data/spec/core/connection_pool_spec.rb +218 -93
- data/spec/core/database_spec.rb +956 -522
- data/spec/core/dataset_spec.rb +1631 -1242
- data/spec/core/expression_filters_spec.rb +690 -334
- data/spec/core/mock_adapter_spec.rb +453 -0
- data/spec/core/object_graph_spec.rb +88 -129
- data/spec/core/schema_generator_spec.rb +4 -4
- data/spec/core/schema_spec.rb +556 -53
- data/spec/core/spec_helper.rb +7 -48
- data/spec/core_extensions_spec.rb +626 -0
- data/spec/extensions/active_model_spec.rb +13 -0
- data/spec/extensions/arbitrary_servers_spec.rb +110 -0
- data/spec/extensions/association_autoreloading_spec.rb +18 -10
- data/spec/extensions/association_dependencies_spec.rb +15 -25
- data/spec/extensions/association_pks_spec.rb +66 -32
- data/spec/extensions/association_proxies_spec.rb +4 -4
- data/spec/extensions/boolean_readers_spec.rb +25 -25
- data/spec/extensions/caching_spec.rb +47 -51
- data/spec/extensions/class_table_inheritance_spec.rb +31 -83
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
- data/spec/extensions/constraint_validations_spec.rb +316 -0
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +88 -0
- data/spec/extensions/dirty_spec.rb +155 -0
- data/spec/extensions/eager_each_spec.rb +34 -0
- data/spec/extensions/eval_inspect_spec.rb +67 -0
- data/spec/extensions/force_encoding_spec.rb +4 -2
- data/spec/extensions/hook_class_methods_spec.rb +97 -128
- data/spec/extensions/identity_map_spec.rb +142 -24
- data/spec/extensions/inflector_spec.rb +0 -4
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +72 -0
- data/spec/extensions/json_serializer_spec.rb +49 -12
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +49 -40
- data/spec/extensions/looser_typecasting_spec.rb +7 -7
- data/spec/extensions/many_through_many_spec.rb +368 -254
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
- data/spec/extensions/migration_spec.rb +140 -35
- data/spec/extensions/named_timezones_spec.rb +29 -11
- data/spec/extensions/nested_attributes_spec.rb +268 -89
- data/spec/extensions/null_dataset_spec.rb +85 -0
- data/spec/extensions/optimistic_locking_spec.rb +24 -21
- data/spec/extensions/pg_array_ops_spec.rb +112 -0
- data/spec/extensions/pg_array_spec.rb +320 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
- data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
- data/spec/extensions/pg_hstore_spec.rb +194 -0
- data/spec/extensions/pg_inet_spec.rb +47 -0
- data/spec/extensions/pg_interval_spec.rb +72 -0
- data/spec/extensions/pg_json_spec.rb +99 -0
- data/spec/extensions/pg_range_ops_spec.rb +56 -0
- data/spec/extensions/pg_range_spec.rb +395 -0
- data/spec/extensions/pg_row_ops_spec.rb +58 -0
- data/spec/extensions/pg_row_plugin_spec.rb +49 -0
- data/spec/extensions/pg_row_spec.rb +323 -0
- data/spec/extensions/pg_statement_cache_spec.rb +208 -0
- data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
- data/spec/extensions/prepared_statements_spec.rb +87 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +7 -6
- data/spec/extensions/query_literals_spec.rb +167 -0
- data/spec/extensions/query_spec.rb +9 -3
- data/spec/extensions/rcte_tree_spec.rb +50 -43
- data/spec/extensions/schema_caching_spec.rb +41 -0
- data/spec/extensions/schema_dumper_spec.rb +434 -49
- data/spec/extensions/schema_spec.rb +30 -49
- data/spec/extensions/select_remove_spec.rb +38 -0
- data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
- data/spec/extensions/serialization_spec.rb +86 -48
- data/spec/extensions/server_block_spec.rb +90 -0
- data/spec/extensions/sharding_spec.rb +69 -143
- data/spec/extensions/single_table_inheritance_spec.rb +23 -29
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +34 -67
- data/spec/extensions/split_array_nil_spec.rb +24 -0
- data/spec/extensions/sql_expr_spec.rb +29 -60
- data/spec/extensions/static_cache_spec.rb +145 -0
- data/spec/extensions/subclasses_spec.rb +14 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +8 -11
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/typecast_on_load_spec.rb +9 -6
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +58 -67
- data/spec/extensions/validation_helpers_spec.rb +31 -37
- data/spec/extensions/xml_serializer_spec.rb +29 -4
- data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
- data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
- data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
- data/spec/integration/associations_test.rb +659 -21
- data/spec/integration/database_test.rb +11 -5
- data/spec/integration/dataset_test.rb +717 -129
- data/spec/integration/eager_loader_test.rb +38 -54
- data/spec/integration/migrator_test.rb +2 -3
- data/spec/integration/model_test.rb +81 -2
- data/spec/integration/plugin_test.rb +604 -147
- data/spec/integration/prepared_statement_test.rb +272 -128
- data/spec/integration/schema_test.rb +367 -57
- data/spec/integration/spec_helper.rb +42 -32
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +31 -8
- data/spec/model/association_reflection_spec.rb +207 -8
- data/spec/model/associations_spec.rb +1206 -693
- data/spec/model/base_spec.rb +334 -100
- data/spec/model/dataset_methods_spec.rb +45 -27
- data/spec/model/eager_loading_spec.rb +828 -692
- data/spec/model/hooks_spec.rb +355 -67
- data/spec/model/model_spec.rb +251 -179
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +506 -232
- data/spec/model/spec_helper.rb +16 -77
- data/spec/model/validations_spec.rb +38 -2
- data/spec/rcov.opts +2 -0
- metadata +209 -80
- data/spec/core/core_sql_spec.rb +0 -451
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
# The pg_range extension adds support for the PostgreSQL 9.2+ range
|
|
2
|
+
# types to Sequel. PostgreSQL range types are similar to ruby's
|
|
3
|
+
# Range class, representating an array of values. However, they
|
|
4
|
+
# are more flexible than ruby's ranges, allowing exclusive beginnings
|
|
5
|
+
# and endings (ruby's range only allows exclusive endings), and
|
|
6
|
+
# unbounded beginnings and endings (which ruby's range does not
|
|
7
|
+
# support).
|
|
8
|
+
#
|
|
9
|
+
# This extension integrates with Sequel's native postgres adapter, so
|
|
10
|
+
# that when range type values are retrieved, they are parsed and returned
|
|
11
|
+
# as instances of Sequel::Postgres::PGRange. PGRange mostly acts
|
|
12
|
+
# like a Range, but it's not a Range as not all PostgreSQL range
|
|
13
|
+
# type values would be valid ruby ranges. If the range type value
|
|
14
|
+
# you are using is a valid ruby range, you can call PGRange#to_range
|
|
15
|
+
# to get a Range. However, if you call PGRange#to_range on a range
|
|
16
|
+
# type value uses features that ruby's Range does not support, an
|
|
17
|
+
# exception will be raised.
|
|
18
|
+
#
|
|
19
|
+
# In addition to the parser, this extension comes with literalizers
|
|
20
|
+
# for both PGRange and Range that use the standard Sequel literalization
|
|
21
|
+
# callbacks, so they work on all adapters.
|
|
22
|
+
#
|
|
23
|
+
# To turn an existing Range into a PGRange, use Sequel.pg_range:
|
|
24
|
+
#
|
|
25
|
+
# Sequel.pg_range(range)
|
|
26
|
+
#
|
|
27
|
+
# If you have loaded the {core_extensions extension}[link:files/doc/core_extensions_rdoc.html]),
|
|
28
|
+
# you can also use Range#pg_range:
|
|
29
|
+
#
|
|
30
|
+
# range.pg_range
|
|
31
|
+
#
|
|
32
|
+
# You may want to specify a specific range type:
|
|
33
|
+
#
|
|
34
|
+
# Sequel.pg_range(range, :daterange)
|
|
35
|
+
# range.pg_range(:daterange)
|
|
36
|
+
#
|
|
37
|
+
# If you specify the range database type, Sequel will automatically cast
|
|
38
|
+
# the value to that type when literalizing.
|
|
39
|
+
#
|
|
40
|
+
# If you would like to use range columns in your model objects, you
|
|
41
|
+
# probably want to modify the schema parsing/typecasting so that it
|
|
42
|
+
# recognizes and correctly handles the range type columns, which you can
|
|
43
|
+
# do by:
|
|
44
|
+
#
|
|
45
|
+
# DB.extension :pg_range
|
|
46
|
+
#
|
|
47
|
+
# If you are not using the native postgres adapter, you probably
|
|
48
|
+
# also want to use the typecast_on_load plugin in the model, and
|
|
49
|
+
# set it to typecast the range type column(s) on load.
|
|
50
|
+
#
|
|
51
|
+
# This extension integrates with the pg_array extension. If you plan
|
|
52
|
+
# to use arrays of range types, load the pg_array extension before the
|
|
53
|
+
# pg_range extension:
|
|
54
|
+
#
|
|
55
|
+
# DB.extension :pg_array, :pg_range
|
|
56
|
+
|
|
57
|
+
Sequel.require 'adapters/utils/pg_types'
|
|
58
|
+
|
|
59
|
+
module Sequel
|
|
60
|
+
module Postgres
|
|
61
|
+
class PGRange
|
|
62
|
+
|
|
63
|
+
# Map of string database type names to type symbols (e.g. 'int4range' => :int4range),
|
|
64
|
+
# used in the schema parsing.
|
|
65
|
+
RANGE_TYPES = {}
|
|
66
|
+
|
|
67
|
+
EMPTY = 'empty'.freeze
|
|
68
|
+
EMPTY_STRING = ''.freeze
|
|
69
|
+
QUOTED_EMPTY_STRING = '""'.freeze
|
|
70
|
+
OPEN_PAREN = "(".freeze
|
|
71
|
+
CLOSE_PAREN = ")".freeze
|
|
72
|
+
OPEN_BRACKET = "[".freeze
|
|
73
|
+
CLOSE_BRACKET = "]".freeze
|
|
74
|
+
ESCAPE_RE = /("|,|\\|\[|\]|\(|\))/.freeze
|
|
75
|
+
ESCAPE_REPLACE = '\\\\\1'.freeze
|
|
76
|
+
CAST = '::'.freeze
|
|
77
|
+
|
|
78
|
+
# Registers a range type that the extension should handle. Makes a Database instance that
|
|
79
|
+
# has been extended with DatabaseMethods recognize the range type given and set up the
|
|
80
|
+
# appropriate typecasting. Also sets up automatic typecasting for the native postgres
|
|
81
|
+
# adapter, so that on retrieval, the values are automatically converted to PGRange instances.
|
|
82
|
+
# The db_type argument should be the name of the range type. Accepts the following options:
|
|
83
|
+
#
|
|
84
|
+
# :converter :: A callable object (e.g. Proc), that is called with the start or end of the range
|
|
85
|
+
# (usually a string), and should return the appropriate typecasted object.
|
|
86
|
+
# :oid :: The PostgreSQL OID for the range type. This is used by the Sequel postgres adapter
|
|
87
|
+
# to set up automatic type conversion on retrieval from the database.
|
|
88
|
+
# :subtype_oid :: Should be the PostgreSQL OID for the range's subtype. If given,
|
|
89
|
+
# automatically sets the :converter option by looking for scalar conversion
|
|
90
|
+
# proc.
|
|
91
|
+
#
|
|
92
|
+
# If a block is given, it is treated as the :converter option.
|
|
93
|
+
def self.register(db_type, opts={}, &block)
|
|
94
|
+
db_type = db_type.to_s.dup.freeze
|
|
95
|
+
|
|
96
|
+
if converter = opts[:converter]
|
|
97
|
+
raise Error, "can't provide both a block and :converter option to register" if block
|
|
98
|
+
else
|
|
99
|
+
converter = block
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
if soid = opts[:subtype_oid]
|
|
103
|
+
raise Error, "can't provide both a converter and :scalar_oid option to register" if converter
|
|
104
|
+
raise Error, "no conversion proc for :scalar_oid=>#{soid.inspect} in PG_TYPES" unless converter = PG_TYPES[soid]
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
parser = Parser.new(db_type, converter)
|
|
108
|
+
|
|
109
|
+
RANGE_TYPES[db_type] = db_type.to_sym
|
|
110
|
+
|
|
111
|
+
DatabaseMethods.define_range_typecast_method(db_type, parser)
|
|
112
|
+
|
|
113
|
+
if oid = opts[:oid]
|
|
114
|
+
Sequel::Postgres::PG_TYPES[oid] = parser
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
nil
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Creates callable objects that convert strings into PGRange instances.
|
|
121
|
+
class Parser
|
|
122
|
+
# Regexp that parses the full range of PostgreSQL range type output,
|
|
123
|
+
# except for empty ranges.
|
|
124
|
+
PARSER = /\A(\[|\()("((?:\\"|[^"])*)"|[^"]*),("((?:\\"|[^"])*)"|[^"]*)(\]|\))\z/o
|
|
125
|
+
|
|
126
|
+
REPLACE_RE = /\\(.)/.freeze
|
|
127
|
+
REPLACE_WITH = '\1'.freeze
|
|
128
|
+
|
|
129
|
+
# The database range type for this parser (e.g. 'int4range'),
|
|
130
|
+
# automatically setting the db_type for the returned PGRange instances.
|
|
131
|
+
attr_reader :db_type
|
|
132
|
+
|
|
133
|
+
# A callable object to convert the beginning and ending of the range into
|
|
134
|
+
# the appropriate ruby type.
|
|
135
|
+
attr_reader :converter
|
|
136
|
+
|
|
137
|
+
# Set the db_type and converter on initialization.
|
|
138
|
+
def initialize(db_type, converter=nil)
|
|
139
|
+
@db_type = db_type.to_s.dup.freeze if db_type
|
|
140
|
+
@converter = converter
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Parse the range type input string into a PGRange value.
|
|
144
|
+
def call(string)
|
|
145
|
+
if string == EMPTY
|
|
146
|
+
return PGRange.empty(db_type)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
raise(InvalidValue, "invalid or unhandled range format: #{string.inspect}") unless matches = PARSER.match(string)
|
|
150
|
+
|
|
151
|
+
exclude_begin = matches[1] == '('
|
|
152
|
+
exclude_end = matches[6] == ')'
|
|
153
|
+
|
|
154
|
+
# If the input is quoted, it needs to be unescaped. Also, quoted input isn't
|
|
155
|
+
# checked for emptiness, since the empty quoted string is considered an
|
|
156
|
+
# element that happens to be the empty string, while an unquoted empty string
|
|
157
|
+
# is considered unbounded.
|
|
158
|
+
#
|
|
159
|
+
# While PostgreSQL allows pure escaping for input (without quoting), it appears
|
|
160
|
+
# to always use the quoted output form when characters need to be escaped, so
|
|
161
|
+
# there isn't a need to unescape unquoted output.
|
|
162
|
+
if beg = matches[3]
|
|
163
|
+
beg.gsub!(REPLACE_RE, REPLACE_WITH)
|
|
164
|
+
else
|
|
165
|
+
beg = matches[2] unless matches[2].empty?
|
|
166
|
+
end
|
|
167
|
+
if en = matches[5]
|
|
168
|
+
en.gsub!(REPLACE_RE, REPLACE_WITH)
|
|
169
|
+
else
|
|
170
|
+
en = matches[4] unless matches[4].empty?
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
if c = converter
|
|
174
|
+
beg = c.call(beg) if beg
|
|
175
|
+
en = c.call(en) if en
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
PGRange.new(beg, en, :exclude_begin=>exclude_begin, :exclude_end=>exclude_end, :db_type=>db_type)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
module DatabaseMethods
|
|
183
|
+
# Reset the conversion procs if using the native postgres adapter,
|
|
184
|
+
# and extend the datasets to correctly literalize ruby Range values.
|
|
185
|
+
def self.extended(db)
|
|
186
|
+
db.extend_datasets(DatasetMethods)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Define a private range typecasting method for the given type that uses
|
|
190
|
+
# the parser argument to do the type conversion.
|
|
191
|
+
def self.define_range_typecast_method(type, parser)
|
|
192
|
+
meth = :"typecast_value_#{type}"
|
|
193
|
+
define_method(meth){|v| typecast_value_pg_range(v, parser)}
|
|
194
|
+
private meth
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Handle Range and PGRange values in bound variables
|
|
198
|
+
def bound_variable_arg(arg, conn)
|
|
199
|
+
case arg
|
|
200
|
+
when PGRange
|
|
201
|
+
arg.unquoted_literal(schema_utility_dataset)
|
|
202
|
+
when Range
|
|
203
|
+
PGRange.from_range(arg).unquoted_literal(schema_utility_dataset)
|
|
204
|
+
else
|
|
205
|
+
super
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
private
|
|
210
|
+
|
|
211
|
+
# Handle arrays of range types in bound variables.
|
|
212
|
+
def bound_variable_array(a)
|
|
213
|
+
case a
|
|
214
|
+
when PGRange, Range
|
|
215
|
+
"\"#{bound_variable_arg(a, nil)}\""
|
|
216
|
+
else
|
|
217
|
+
super
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Manually override the typecasting for tsrange and tstzrange types so that
|
|
222
|
+
# they use the database's timezone instead of the global Sequel
|
|
223
|
+
# timezone.
|
|
224
|
+
def get_conversion_procs
|
|
225
|
+
procs = super
|
|
226
|
+
|
|
227
|
+
procs[3908] = Parser.new("tsrange", procs[1114])
|
|
228
|
+
procs[3910] = Parser.new("tstzrange", procs[1184])
|
|
229
|
+
if defined?(PGArray::Creator)
|
|
230
|
+
procs[3909] = PGArray::Creator.new("tsrange", procs[3908])
|
|
231
|
+
procs[3911] = PGArray::Creator.new("tstzrange", procs[3910])
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
procs
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Recognize the registered database range types.
|
|
238
|
+
def schema_column_type(db_type)
|
|
239
|
+
if type = RANGE_TYPES[db_type]
|
|
240
|
+
type
|
|
241
|
+
else
|
|
242
|
+
super
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Typecast value correctly to a PGRange. If already an
|
|
247
|
+
# PGRange instance with the same db_type, return as is.
|
|
248
|
+
# If a PGRange with a different subtype, return a new
|
|
249
|
+
# PGRange with the same values and the expected subtype.
|
|
250
|
+
# If a Range object, create a PGRange with the given
|
|
251
|
+
# db_type. If a string, assume it is in PostgreSQL
|
|
252
|
+
# output format and parse it using the parser.
|
|
253
|
+
def typecast_value_pg_range(value, parser)
|
|
254
|
+
case value
|
|
255
|
+
when PGRange
|
|
256
|
+
if value.db_type.to_s == parser.db_type
|
|
257
|
+
value
|
|
258
|
+
elsif value.empty?
|
|
259
|
+
PGRange.empty(parser.db_type)
|
|
260
|
+
else
|
|
261
|
+
PGRange.new(value.begin, value.end, :exclude_begin=>value.exclude_begin?, :exclude_end=>value.exclude_end?, :db_type=>parser.db_type)
|
|
262
|
+
end
|
|
263
|
+
when Range
|
|
264
|
+
PGRange.from_range(value, parser.db_type)
|
|
265
|
+
when String
|
|
266
|
+
parser.call(value)
|
|
267
|
+
else
|
|
268
|
+
raise Sequel::InvalidValue, "invalid value for range type: #{value.inspect}"
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
module DatasetMethods
|
|
274
|
+
# Handle literalization of ruby Range objects, treating them as
|
|
275
|
+
# PostgreSQL ranges.
|
|
276
|
+
def literal_other_append(sql, v)
|
|
277
|
+
case v
|
|
278
|
+
when Range
|
|
279
|
+
super(sql, Sequel::Postgres::PGRange.from_range(v))
|
|
280
|
+
else
|
|
281
|
+
super
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
include Enumerable
|
|
287
|
+
|
|
288
|
+
# The beginning of the range. If nil, the range has an unbounded beginning.
|
|
289
|
+
attr_reader :begin
|
|
290
|
+
|
|
291
|
+
# The end of the range. If nil, the range has an unbounded ending.
|
|
292
|
+
attr_reader :end
|
|
293
|
+
|
|
294
|
+
# The PostgreSQL database type for the range (e.g. 'int4range').
|
|
295
|
+
attr_reader :db_type
|
|
296
|
+
|
|
297
|
+
# Create a new PGRange instance using the beginning and ending of the ruby Range,
|
|
298
|
+
# with the given db_type.
|
|
299
|
+
def self.from_range(range, db_type=nil)
|
|
300
|
+
new(range.begin, range.end, :exclude_end=>range.exclude_end?, :db_type=>db_type)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# Create an empty PGRange with the given database type.
|
|
304
|
+
def self.empty(db_type=nil)
|
|
305
|
+
new(nil, nil, :empty=>true, :db_type=>db_type)
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# Initialize a new PGRange instance. Accepts the following options:
|
|
309
|
+
#
|
|
310
|
+
# :db_type :: The PostgreSQL database type for the range.
|
|
311
|
+
# :empty :: Whether the range is empty (has no points)
|
|
312
|
+
# :exclude_begin :: Whether the beginning element is excluded from the range.
|
|
313
|
+
# :exclude_end :: Whether the ending element is excluded from the range.
|
|
314
|
+
def initialize(beg, en, opts={})
|
|
315
|
+
@begin = beg
|
|
316
|
+
@end = en
|
|
317
|
+
@empty = !!opts[:empty]
|
|
318
|
+
@exclude_begin = !!opts[:exclude_begin]
|
|
319
|
+
@exclude_end = !!opts[:exclude_end]
|
|
320
|
+
@db_type = opts[:db_type]
|
|
321
|
+
if @empty
|
|
322
|
+
raise(Error, 'cannot have an empty range with either a beginning or ending') unless @begin.nil? && @end.nil? && opts[:exclude_begin].nil? && opts[:exclude_end].nil?
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Delegate to the ruby range object so that the object mostly acts like a range.
|
|
327
|
+
range_methods = %w'each last first step'
|
|
328
|
+
range_methods << 'cover?' if RUBY_VERSION >= '1.9'
|
|
329
|
+
range_methods.each do |m|
|
|
330
|
+
class_eval("def #{m}(*a, &block) to_range.#{m}(*a, &block) end", __FILE__, __LINE__)
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# Consider the receiver equal to other PGRange instances with the
|
|
334
|
+
# same beginning, ending, exclusions, and database type. Also consider
|
|
335
|
+
# it equal to Range instances if this PGRange can be converted to a
|
|
336
|
+
# a Range and those ranges are equal.
|
|
337
|
+
def eql?(other)
|
|
338
|
+
case other
|
|
339
|
+
when PGRange
|
|
340
|
+
if db_type == other.db_type
|
|
341
|
+
if empty?
|
|
342
|
+
other.empty?
|
|
343
|
+
elsif other.empty?
|
|
344
|
+
false
|
|
345
|
+
else
|
|
346
|
+
[:@begin, :@end, :@exclude_begin, :@exclude_end].all?{|v| instance_variable_get(v) == other.instance_variable_get(v)}
|
|
347
|
+
end
|
|
348
|
+
else
|
|
349
|
+
false
|
|
350
|
+
end
|
|
351
|
+
when Range
|
|
352
|
+
if valid_ruby_range?
|
|
353
|
+
to_range.eql?(other)
|
|
354
|
+
else
|
|
355
|
+
false
|
|
356
|
+
end
|
|
357
|
+
else
|
|
358
|
+
false
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
alias == eql?
|
|
362
|
+
|
|
363
|
+
# Allow PGRange values in case statements, where they return true if they
|
|
364
|
+
# are equal to each other using eql?, or if this PGRange can be converted
|
|
365
|
+
# to a Range, delegating to that range.
|
|
366
|
+
def ===(other)
|
|
367
|
+
if eql?(other)
|
|
368
|
+
true
|
|
369
|
+
else
|
|
370
|
+
if valid_ruby_range?
|
|
371
|
+
to_range === other
|
|
372
|
+
else
|
|
373
|
+
false
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# Whether this range is empty (has no points).
|
|
379
|
+
def empty?
|
|
380
|
+
@empty
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
# Whether the beginning element is excluded from the range.
|
|
384
|
+
def exclude_begin?
|
|
385
|
+
@exclude_begin
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# Whether the ending element is excluded from the range.
|
|
389
|
+
def exclude_end?
|
|
390
|
+
@exclude_end
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
# Append a literalize version of the receiver to the sql.
|
|
394
|
+
def sql_literal_append(ds, sql)
|
|
395
|
+
ds.literal_append(sql, unquoted_literal(ds))
|
|
396
|
+
if s = @db_type
|
|
397
|
+
sql << CAST << s.to_s
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
# Return a ruby Range object for this instance, if one can be created.
|
|
402
|
+
def to_range
|
|
403
|
+
return @range if @range
|
|
404
|
+
raise(Error, "cannot create ruby range for an empty PostgreSQL range") if empty?
|
|
405
|
+
raise(Error, "cannot create ruby range when PostgreSQL range excludes beginning element") if exclude_begin?
|
|
406
|
+
raise(Error, "cannot create ruby range when PostgreSQL range has unbounded beginning") unless self.begin
|
|
407
|
+
raise(Error, "cannot create ruby range when PostgreSQL range has unbounded ending") unless self.end
|
|
408
|
+
@range = Range.new(self.begin, self.end, exclude_end?)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
# Whether or not this PGRange is a valid ruby range. In order to be a valid ruby range,
|
|
412
|
+
# it must have a beginning and an ending (no unbounded ranges), and it cannot exclude
|
|
413
|
+
# the beginning element.
|
|
414
|
+
def valid_ruby_range?
|
|
415
|
+
!(empty? || exclude_begin? || !self.begin || !self.end)
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
# Whether the beginning of the range is unbounded.
|
|
419
|
+
def unbounded_begin?
|
|
420
|
+
self.begin.nil? && !empty?
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
# Whether the end of the range is unbounded.
|
|
424
|
+
def unbounded_end?
|
|
425
|
+
self.end.nil? && !empty?
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
# Return a string containing the unescaped version of the range.
|
|
429
|
+
# Separated out for use by the bound argument code.
|
|
430
|
+
def unquoted_literal(ds)
|
|
431
|
+
if empty?
|
|
432
|
+
EMPTY
|
|
433
|
+
else
|
|
434
|
+
"#{exclude_begin? ? OPEN_PAREN : OPEN_BRACKET}#{escape_value(self.begin, ds)},#{escape_value(self.end, ds)}#{exclude_end? ? CLOSE_PAREN : CLOSE_BRACKET}"
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
private
|
|
439
|
+
|
|
440
|
+
# Escape common range types. Instead of quoting, just backslash escape all
|
|
441
|
+
# special characters.
|
|
442
|
+
def escape_value(k, ds)
|
|
443
|
+
case k
|
|
444
|
+
when nil
|
|
445
|
+
EMPTY_STRING
|
|
446
|
+
when Date, Time
|
|
447
|
+
ds.literal(k)[1...-1]
|
|
448
|
+
when Integer, Float
|
|
449
|
+
k.to_s
|
|
450
|
+
when BigDecimal
|
|
451
|
+
k.to_s('F')
|
|
452
|
+
when LiteralString
|
|
453
|
+
k
|
|
454
|
+
when String
|
|
455
|
+
if k.empty?
|
|
456
|
+
QUOTED_EMPTY_STRING
|
|
457
|
+
else
|
|
458
|
+
k.gsub(ESCAPE_RE, ESCAPE_REPLACE)
|
|
459
|
+
end
|
|
460
|
+
else
|
|
461
|
+
ds.literal(k).gsub(ESCAPE_RE, ESCAPE_REPLACE)
|
|
462
|
+
end
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
PGRange.register('int4range', :oid=>3904, :subtype_oid=>23)
|
|
467
|
+
PGRange.register('numrange', :oid=>3906, :subtype_oid=>1700)
|
|
468
|
+
PGRange.register('tsrange', :oid=>3908, :subtype_oid=>1114)
|
|
469
|
+
PGRange.register('tstzrange', :oid=>3910, :subtype_oid=>1184)
|
|
470
|
+
PGRange.register('daterange', :oid=>3912, :subtype_oid=>1082)
|
|
471
|
+
PGRange.register('int8range', :oid=>3926, :subtype_oid=>20)
|
|
472
|
+
if defined?(PGArray) && PGArray.respond_to?(:register)
|
|
473
|
+
PGArray.register('int4range', :oid=>3905, :scalar_oid=>3904, :scalar_typecast=>:int4range)
|
|
474
|
+
PGArray.register('numrange', :oid=>3907, :scalar_oid=>3906, :scalar_typecast=>:numrange)
|
|
475
|
+
PGArray.register('tsrange', :oid=>3909, :scalar_oid=>3908, :scalar_typecast=>:tsrange)
|
|
476
|
+
PGArray.register('tstzrange', :oid=>3911, :scalar_oid=>3910, :scalar_typecast=>:tstzrange)
|
|
477
|
+
PGArray.register('daterange', :oid=>3913, :scalar_oid=>3912, :scalar_typecast=>:daterange)
|
|
478
|
+
PGArray.register('int8range', :oid=>3927, :scalar_oid=>3926, :scalar_typecast=>:int8range)
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
module SQL::Builders
|
|
483
|
+
# Convert the object to a Postgres::PGRange.
|
|
484
|
+
def pg_range(v, db_type=nil)
|
|
485
|
+
case v
|
|
486
|
+
when Postgres::PGRange
|
|
487
|
+
if db_type.nil? || v.db_type == db_type
|
|
488
|
+
v
|
|
489
|
+
else
|
|
490
|
+
Postgres::PGRange.new(v.begin, v.end, :exclude_begin=>v.exclude_begin?, :exclude_end=>v.exclude_end?, :db_type=>db_type)
|
|
491
|
+
end
|
|
492
|
+
when Range
|
|
493
|
+
Postgres::PGRange.from_range(v, db_type)
|
|
494
|
+
else
|
|
495
|
+
# May not be defined unless the pg_range_ops extension is used
|
|
496
|
+
pg_range_op(v)
|
|
497
|
+
end
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
Database.register_extension(:pg_range, Postgres::PGRange::DatabaseMethods)
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
if Sequel.core_extensions?
|
|
505
|
+
class Range
|
|
506
|
+
# Create a new PGRange using the receiver as the input range,
|
|
507
|
+
# with the given database type.
|
|
508
|
+
def pg_range(db_type=nil)
|
|
509
|
+
Sequel::Postgres::PGRange.from_range(self, db_type)
|
|
510
|
+
end
|
|
511
|
+
end
|
|
512
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# The pg_range_ops extension adds support to Sequel's DSL to make
|
|
2
|
+
# it easier to call PostgreSQL range functions and operators.
|
|
3
|
+
#
|
|
4
|
+
# To load the extension:
|
|
5
|
+
#
|
|
6
|
+
# Sequel.extension :pg_range_ops
|
|
7
|
+
#
|
|
8
|
+
# The most common usage is passing an expression to Sequel.pg_range_op:
|
|
9
|
+
#
|
|
10
|
+
# r = Sequel.pg_range_op(:range)
|
|
11
|
+
#
|
|
12
|
+
# If you have also loaded the pg_range extension, you can use
|
|
13
|
+
# Sequel.pg_range as well:
|
|
14
|
+
#
|
|
15
|
+
# r = Sequel.pg_range(:range)
|
|
16
|
+
#
|
|
17
|
+
# Also, on most Sequel expression objects, you can call the pg_range
|
|
18
|
+
# method:
|
|
19
|
+
#
|
|
20
|
+
# r = Sequel.expr(:range).pg_range
|
|
21
|
+
#
|
|
22
|
+
# If you have loaded the {core_extensions extension}[link:files/doc/core_extensions_rdoc.html]),
|
|
23
|
+
# you can also call Symbol#pg_range:
|
|
24
|
+
#
|
|
25
|
+
# r = :range.pg_range
|
|
26
|
+
#
|
|
27
|
+
# This creates a Sequel::Postgres::RangeOp object that can be used
|
|
28
|
+
# for easier querying:
|
|
29
|
+
#
|
|
30
|
+
# r.contains(:other) # range @> other
|
|
31
|
+
# r.contained_by(:other) # range <@ other
|
|
32
|
+
# r.overlaps(:other) # range && other
|
|
33
|
+
# r.left_of(:other) # range << other
|
|
34
|
+
# r.right_of(:other) # range >> other
|
|
35
|
+
# r.starts_before(:other) # range &< other
|
|
36
|
+
# r.ends_after(:other) # range &> other
|
|
37
|
+
# r.adjacent_to(:other) # range -|- other
|
|
38
|
+
#
|
|
39
|
+
# r.lower # lower(range)
|
|
40
|
+
# r.upper # upper(range)
|
|
41
|
+
# r.isempty # isempty(range)
|
|
42
|
+
# r.lower_inc # lower_inc(range)
|
|
43
|
+
# r.upper_inc # upper_inc(range)
|
|
44
|
+
# r.lower_inf # lower_inf(range)
|
|
45
|
+
# r.upper_inf # upper_inf(range)
|
|
46
|
+
#
|
|
47
|
+
# See the PostgreSQL range function and operator documentation for more
|
|
48
|
+
# details on what these functions and operators do.
|
|
49
|
+
#
|
|
50
|
+
# If you are also using the pg_range extension, you should load it before
|
|
51
|
+
# loading this extension. Doing so will allow you to use PGArray#op to get
|
|
52
|
+
# an RangeOp, allowing you to perform range operations on range literals.
|
|
53
|
+
|
|
54
|
+
module Sequel
|
|
55
|
+
module Postgres
|
|
56
|
+
# The RangeOp class is a simple container for a single object that
|
|
57
|
+
# defines methods that yield Sequel expression objects representing
|
|
58
|
+
# PostgreSQL range operators and functions.
|
|
59
|
+
#
|
|
60
|
+
# Most methods in this class are defined via metaprogramming, see
|
|
61
|
+
# the pg_range_ops extension documentation for details on the API.
|
|
62
|
+
class RangeOp < Sequel::SQL::Wrapper
|
|
63
|
+
OPERATORS = {
|
|
64
|
+
:contains => ["(".freeze, " @> ".freeze, ")".freeze].freeze,
|
|
65
|
+
:contained_by => ["(".freeze, " <@ ".freeze, ")".freeze].freeze,
|
|
66
|
+
:left_of => ["(".freeze, " << ".freeze, ")".freeze].freeze,
|
|
67
|
+
:right_of => ["(".freeze, " >> ".freeze, ")".freeze].freeze,
|
|
68
|
+
:starts_before => ["(".freeze, " &< ".freeze, ")".freeze].freeze,
|
|
69
|
+
:ends_after => ["(".freeze, " &> ".freeze, ")".freeze].freeze,
|
|
70
|
+
:adjacent_to => ["(".freeze, " -|- ".freeze, ")".freeze].freeze,
|
|
71
|
+
:overlaps => ["(".freeze, " && ".freeze, ")".freeze].freeze,
|
|
72
|
+
}
|
|
73
|
+
FUNCTIONS = %w'lower upper isempty lower_inc upper_inc lower_inf upper_inf'
|
|
74
|
+
|
|
75
|
+
FUNCTIONS.each do |f|
|
|
76
|
+
class_eval("def #{f}; function(:#{f}) end", __FILE__, __LINE__)
|
|
77
|
+
end
|
|
78
|
+
OPERATORS.keys.each do |f|
|
|
79
|
+
class_eval("def #{f}(v); operator(:#{f}, v) end", __FILE__, __LINE__)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# These operators are already supported by the wrapper, but for ranges they
|
|
83
|
+
# return ranges, so wrap the results in another RangeOp.
|
|
84
|
+
%w'+ * -'.each do |f|
|
|
85
|
+
class_eval("def #{f}(v); RangeOp.new(super) end", __FILE__, __LINE__)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Return the receiver.
|
|
89
|
+
def pg_range
|
|
90
|
+
self
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
# Create a boolen expression for the given type and argument.
|
|
96
|
+
def operator(type, other)
|
|
97
|
+
Sequel::SQL::BooleanExpression.new(:NOOP, Sequel::SQL::PlaceholderLiteralString.new(OPERATORS[type], [value, other]))
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Return a function called with the receiver.
|
|
101
|
+
def function(name)
|
|
102
|
+
Sequel::SQL::Function.new(name, self)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
module RangeOpMethods
|
|
107
|
+
# Wrap the receiver in an RangeOp so you can easily use the PostgreSQL
|
|
108
|
+
# range functions and operators with it.
|
|
109
|
+
def pg_range
|
|
110
|
+
RangeOp.new(self)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
if defined?(PGRange)
|
|
115
|
+
class PGRange
|
|
116
|
+
# Wrap the PGRange instance in an RangeOp, allowing you to easily use
|
|
117
|
+
# the PostgreSQL range functions and operators with literal ranges.
|
|
118
|
+
def op
|
|
119
|
+
RangeOp.new(self)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
module SQL::Builders
|
|
126
|
+
# Return the expression wrapped in the Postgres::RangeOp.
|
|
127
|
+
def pg_range_op(v)
|
|
128
|
+
case v
|
|
129
|
+
when Postgres::RangeOp
|
|
130
|
+
v
|
|
131
|
+
else
|
|
132
|
+
Postgres::RangeOp.new(v)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
class SQL::GenericExpression
|
|
138
|
+
include Sequel::Postgres::RangeOpMethods
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
class LiteralString
|
|
142
|
+
include Sequel::Postgres::RangeOpMethods
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
if Sequel.core_extensions?
|
|
147
|
+
class Symbol
|
|
148
|
+
include Sequel::Postgres::RangeOpMethods
|
|
149
|
+
end
|
|
150
|
+
end
|