sequel 4.49.0 → 5.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG +70 -0
- data/README.rdoc +195 -136
- data/Rakefile +26 -42
- data/bin/sequel +3 -5
- data/doc/advanced_associations.rdoc +86 -163
- data/doc/association_basics.rdoc +197 -274
- data/doc/bin_sequel.rdoc +5 -3
- data/doc/cheat_sheet.rdoc +66 -43
- data/doc/code_order.rdoc +1 -8
- data/doc/core_extensions.rdoc +81 -56
- data/doc/dataset_basics.rdoc +8 -17
- data/doc/dataset_filtering.rdoc +81 -86
- data/doc/extensions.rdoc +3 -10
- data/doc/mass_assignment.rdoc +73 -30
- data/doc/migration.rdoc +19 -36
- data/doc/model_dataset_method_design.rdoc +14 -17
- data/doc/model_hooks.rdoc +15 -25
- data/doc/model_plugins.rdoc +10 -10
- data/doc/mssql_stored_procedures.rdoc +3 -3
- data/doc/object_model.rdoc +52 -70
- data/doc/opening_databases.rdoc +39 -32
- data/doc/postgresql.rdoc +48 -38
- data/doc/prepared_statements.rdoc +27 -22
- data/doc/querying.rdoc +173 -150
- data/doc/reflection.rdoc +5 -6
- data/doc/release_notes/5.0.0.txt +159 -0
- data/doc/schema_modification.rdoc +63 -60
- data/doc/security.rdoc +97 -88
- data/doc/sharding.rdoc +43 -30
- data/doc/sql.rdoc +53 -65
- data/doc/testing.rdoc +3 -5
- data/doc/thread_safety.rdoc +2 -4
- data/doc/transactions.rdoc +18 -17
- data/doc/validations.rdoc +48 -45
- data/doc/virtual_rows.rdoc +87 -115
- data/lib/sequel.rb +1 -1
- data/lib/sequel/adapters/ado.rb +9 -25
- data/lib/sequel/adapters/ado/access.rb +7 -13
- data/lib/sequel/adapters/ado/mssql.rb +2 -9
- data/lib/sequel/adapters/amalgalite.rb +3 -18
- data/lib/sequel/adapters/ibmdb.rb +9 -45
- data/lib/sequel/adapters/jdbc.rb +13 -73
- data/lib/sequel/adapters/jdbc/db2.rb +8 -37
- data/lib/sequel/adapters/jdbc/derby.rb +4 -50
- data/lib/sequel/adapters/jdbc/h2.rb +4 -25
- data/lib/sequel/adapters/jdbc/hsqldb.rb +1 -26
- data/lib/sequel/adapters/jdbc/jtds.rb +2 -9
- data/lib/sequel/adapters/jdbc/mssql.rb +1 -11
- data/lib/sequel/adapters/jdbc/mysql.rb +1 -15
- data/lib/sequel/adapters/jdbc/oracle.rb +4 -26
- data/lib/sequel/adapters/jdbc/postgresql.rb +2 -31
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +4 -17
- data/lib/sequel/adapters/jdbc/sqlite.rb +1 -7
- data/lib/sequel/adapters/jdbc/sqlserver.rb +1 -13
- data/lib/sequel/adapters/jdbc/transactions.rb +1 -14
- data/lib/sequel/adapters/mock.rb +4 -30
- data/lib/sequel/adapters/mysql.rb +7 -44
- data/lib/sequel/adapters/mysql2.rb +5 -23
- data/lib/sequel/adapters/odbc.rb +0 -19
- data/lib/sequel/adapters/odbc/db2.rb +1 -1
- data/lib/sequel/adapters/odbc/mssql.rb +4 -12
- data/lib/sequel/adapters/odbc/oracle.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +7 -13
- data/lib/sequel/adapters/postgres.rb +13 -57
- data/lib/sequel/adapters/postgresql.rb +1 -1
- data/lib/sequel/adapters/shared/access.rb +11 -51
- data/lib/sequel/adapters/shared/db2.rb +3 -61
- data/lib/sequel/adapters/shared/mssql.rb +21 -157
- data/lib/sequel/adapters/shared/mysql.rb +23 -224
- data/lib/sequel/adapters/shared/oracle.rb +13 -41
- data/lib/sequel/adapters/shared/postgres.rb +44 -259
- data/lib/sequel/adapters/shared/sqlanywhere.rb +4 -96
- data/lib/sequel/adapters/shared/sqlite.rb +12 -101
- data/lib/sequel/adapters/sqlanywhere.rb +4 -23
- data/lib/sequel/adapters/sqlite.rb +2 -19
- data/lib/sequel/adapters/tinytds.rb +5 -15
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +1 -1
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -4
- data/lib/sequel/adapters/utils/mysql_prepared_statements.rb +3 -6
- data/lib/sequel/adapters/utils/replace.rb +0 -5
- data/lib/sequel/adapters/utils/stored_procedures.rb +0 -2
- data/lib/sequel/adapters/utils/unmodified_identifiers.rb +2 -0
- data/lib/sequel/ast_transformer.rb +3 -94
- data/lib/sequel/connection_pool.rb +26 -28
- data/lib/sequel/connection_pool/sharded_single.rb +1 -4
- data/lib/sequel/connection_pool/sharded_threaded.rb +97 -95
- data/lib/sequel/connection_pool/single.rb +0 -2
- data/lib/sequel/connection_pool/threaded.rb +94 -110
- data/lib/sequel/core.rb +42 -101
- data/lib/sequel/database.rb +12 -2
- data/lib/sequel/database/connecting.rb +23 -60
- data/lib/sequel/database/dataset.rb +6 -9
- data/lib/sequel/database/dataset_defaults.rb +4 -48
- data/lib/sequel/database/features.rb +5 -4
- data/lib/sequel/database/logging.rb +2 -9
- data/lib/sequel/database/misc.rb +23 -55
- data/lib/sequel/database/query.rb +8 -13
- data/lib/sequel/database/schema_generator.rb +89 -64
- data/lib/sequel/database/schema_methods.rb +61 -79
- data/lib/sequel/database/transactions.rb +4 -24
- data/lib/sequel/dataset.rb +18 -10
- data/lib/sequel/dataset/actions.rb +53 -107
- data/lib/sequel/dataset/dataset_module.rb +3 -15
- data/lib/sequel/dataset/features.rb +30 -30
- data/lib/sequel/dataset/graph.rb +40 -49
- data/lib/sequel/dataset/misc.rb +12 -37
- data/lib/sequel/dataset/placeholder_literalizer.rb +4 -4
- data/lib/sequel/dataset/prepared_statements.rb +23 -51
- data/lib/sequel/dataset/query.rb +71 -155
- data/lib/sequel/dataset/sql.rb +30 -225
- data/lib/sequel/deprecated.rb +18 -27
- data/lib/sequel/exceptions.rb +1 -17
- data/lib/sequel/extensions/_model_pg_row.rb +0 -7
- data/lib/sequel/extensions/_pretty_table.rb +1 -3
- data/lib/sequel/extensions/arbitrary_servers.rb +10 -10
- data/lib/sequel/extensions/connection_expiration.rb +1 -1
- data/lib/sequel/extensions/connection_validator.rb +1 -1
- data/lib/sequel/extensions/constraint_validations.rb +11 -11
- data/lib/sequel/extensions/core_extensions.rb +39 -49
- data/lib/sequel/extensions/core_refinements.rb +39 -45
- data/lib/sequel/extensions/current_datetime_timestamp.rb +0 -4
- data/lib/sequel/extensions/date_arithmetic.rb +7 -7
- data/lib/sequel/extensions/duplicate_columns_handler.rb +12 -9
- data/lib/sequel/extensions/empty_array_consider_nulls.rb +2 -2
- data/lib/sequel/extensions/eval_inspect.rb +4 -11
- data/lib/sequel/extensions/freeze_datasets.rb +1 -69
- data/lib/sequel/extensions/from_block.rb +1 -35
- data/lib/sequel/extensions/graph_each.rb +2 -2
- data/lib/sequel/extensions/identifier_mangling.rb +9 -19
- data/lib/sequel/extensions/implicit_subquery.rb +2 -2
- data/lib/sequel/extensions/inflector.rb +4 -4
- data/lib/sequel/extensions/migration.rb +23 -40
- data/lib/sequel/extensions/no_auto_literal_strings.rb +2 -84
- data/lib/sequel/extensions/null_dataset.rb +2 -8
- data/lib/sequel/extensions/pagination.rb +1 -17
- data/lib/sequel/extensions/pg_array.rb +20 -189
- data/lib/sequel/extensions/pg_hstore.rb +11 -50
- data/lib/sequel/extensions/pg_hstore_ops.rb +2 -2
- data/lib/sequel/extensions/pg_inet.rb +2 -15
- data/lib/sequel/extensions/pg_interval.rb +1 -20
- data/lib/sequel/extensions/pg_json.rb +7 -27
- data/lib/sequel/extensions/pg_loose_count.rb +1 -1
- data/lib/sequel/extensions/pg_range.rb +6 -121
- data/lib/sequel/extensions/pg_range_ops.rb +1 -3
- data/lib/sequel/extensions/pg_row.rb +5 -77
- data/lib/sequel/extensions/pg_row_ops.rb +2 -13
- data/lib/sequel/extensions/query.rb +3 -4
- data/lib/sequel/extensions/round_timestamps.rb +0 -6
- data/lib/sequel/extensions/schema_dumper.rb +13 -13
- data/lib/sequel/extensions/select_remove.rb +3 -3
- data/lib/sequel/extensions/split_array_nil.rb +2 -2
- data/lib/sequel/extensions/sql_comments.rb +2 -2
- data/lib/sequel/extensions/string_agg.rb +11 -8
- data/lib/sequel/extensions/symbol_aref.rb +6 -20
- data/lib/sequel/model.rb +27 -62
- data/lib/sequel/model/associations.rb +128 -131
- data/lib/sequel/model/base.rb +171 -711
- data/lib/sequel/model/default_inflections.rb +1 -1
- data/lib/sequel/model/errors.rb +0 -3
- data/lib/sequel/model/exceptions.rb +2 -6
- data/lib/sequel/model/inflections.rb +1 -26
- data/lib/sequel/model/plugins.rb +1 -0
- data/lib/sequel/plugins/active_model.rb +2 -5
- data/lib/sequel/plugins/association_dependencies.rb +15 -15
- data/lib/sequel/plugins/association_pks.rb +14 -28
- data/lib/sequel/plugins/association_proxies.rb +6 -7
- data/lib/sequel/plugins/auto_validations.rb +4 -4
- data/lib/sequel/plugins/before_after_save.rb +0 -43
- data/lib/sequel/plugins/blacklist_security.rb +9 -8
- data/lib/sequel/plugins/boolean_readers.rb +3 -3
- data/lib/sequel/plugins/boolean_subsets.rb +2 -2
- data/lib/sequel/plugins/caching.rb +5 -5
- data/lib/sequel/plugins/class_table_inheritance.rb +71 -102
- data/lib/sequel/plugins/column_conflicts.rb +2 -2
- data/lib/sequel/plugins/column_select.rb +2 -2
- data/lib/sequel/plugins/composition.rb +15 -24
- data/lib/sequel/plugins/constraint_validations.rb +4 -3
- data/lib/sequel/plugins/csv_serializer.rb +13 -20
- data/lib/sequel/plugins/dataset_associations.rb +2 -2
- data/lib/sequel/plugins/def_dataset_method.rb +5 -5
- data/lib/sequel/plugins/defaults_setter.rb +1 -1
- data/lib/sequel/plugins/delay_add_association.rb +1 -1
- data/lib/sequel/plugins/finder.rb +16 -10
- data/lib/sequel/plugins/force_encoding.rb +1 -7
- data/lib/sequel/plugins/hook_class_methods.rb +4 -106
- data/lib/sequel/plugins/input_transformer.rb +10 -11
- data/lib/sequel/plugins/insert_returning_select.rb +1 -9
- data/lib/sequel/plugins/instance_filters.rb +5 -5
- data/lib/sequel/plugins/instance_hooks.rb +7 -52
- data/lib/sequel/plugins/inverted_subsets.rb +3 -1
- data/lib/sequel/plugins/json_serializer.rb +19 -19
- data/lib/sequel/plugins/lazy_attributes.rb +1 -10
- data/lib/sequel/plugins/list.rb +6 -6
- data/lib/sequel/plugins/many_through_many.rb +11 -8
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +3 -3
- data/lib/sequel/plugins/nested_attributes.rb +18 -31
- data/lib/sequel/plugins/optimistic_locking.rb +3 -3
- data/lib/sequel/plugins/pg_array_associations.rb +8 -2
- data/lib/sequel/plugins/pg_row.rb +2 -11
- data/lib/sequel/plugins/prepared_statements.rb +13 -66
- data/lib/sequel/plugins/prepared_statements_safe.rb +1 -1
- data/lib/sequel/plugins/rcte_tree.rb +7 -7
- data/lib/sequel/plugins/serialization.rb +15 -33
- data/lib/sequel/plugins/serialization_modification_detection.rb +1 -1
- data/lib/sequel/plugins/sharding.rb +2 -8
- data/lib/sequel/plugins/single_table_inheritance.rb +10 -13
- data/lib/sequel/plugins/skip_create_refresh.rb +3 -3
- data/lib/sequel/plugins/static_cache.rb +8 -9
- data/lib/sequel/plugins/string_stripper.rb +3 -3
- data/lib/sequel/plugins/subclasses.rb +1 -1
- data/lib/sequel/plugins/subset_conditions.rb +2 -2
- data/lib/sequel/plugins/table_select.rb +2 -2
- data/lib/sequel/plugins/tactical_eager_loading.rb +4 -4
- data/lib/sequel/plugins/timestamps.rb +6 -7
- data/lib/sequel/plugins/touch.rb +4 -8
- data/lib/sequel/plugins/tree.rb +3 -3
- data/lib/sequel/plugins/typecast_on_load.rb +2 -2
- data/lib/sequel/plugins/unlimited_update.rb +1 -7
- data/lib/sequel/plugins/update_or_create.rb +3 -3
- data/lib/sequel/plugins/update_refresh.rb +3 -3
- data/lib/sequel/plugins/uuid.rb +7 -11
- data/lib/sequel/plugins/validation_class_methods.rb +10 -9
- data/lib/sequel/plugins/validation_contexts.rb +4 -4
- data/lib/sequel/plugins/validation_helpers.rb +26 -25
- data/lib/sequel/plugins/whitelist_security.rb +13 -9
- data/lib/sequel/plugins/xml_serializer.rb +24 -25
- data/lib/sequel/sql.rb +145 -276
- data/lib/sequel/timezones.rb +8 -22
- data/lib/sequel/version.rb +2 -2
- data/spec/adapter_spec.rb +1 -1
- data/spec/adapters/db2_spec.rb +2 -103
- data/spec/adapters/mssql_spec.rb +89 -68
- data/spec/adapters/mysql_spec.rb +101 -480
- data/spec/adapters/oracle_spec.rb +1 -9
- data/spec/adapters/postgres_spec.rb +312 -565
- data/spec/adapters/spec_helper.rb +12 -31
- data/spec/adapters/sqlanywhere_spec.rb +2 -77
- data/spec/adapters/sqlite_spec.rb +8 -146
- data/spec/bin_spec.rb +11 -16
- data/spec/core/connection_pool_spec.rb +173 -74
- data/spec/core/database_spec.rb +64 -244
- data/spec/core/dataset_spec.rb +81 -415
- data/spec/core/deprecated_spec.rb +3 -3
- data/spec/core/expression_filters_spec.rb +37 -144
- data/spec/core/mock_adapter_spec.rb +176 -4
- data/spec/core/object_graph_spec.rb +11 -60
- data/spec/core/placeholder_literalizer_spec.rb +1 -14
- data/spec/core/schema_generator_spec.rb +51 -40
- data/spec/core/schema_spec.rb +74 -77
- data/spec/core/spec_helper.rb +6 -24
- data/spec/core/version_spec.rb +1 -1
- data/spec/core_extensions_spec.rb +7 -83
- data/spec/core_model_spec.rb +2 -2
- data/spec/deprecation_helper.rb +2 -14
- data/spec/extensions/accessed_columns_spec.rb +1 -1
- data/spec/extensions/active_model_spec.rb +3 -3
- data/spec/extensions/after_initialize_spec.rb +1 -1
- data/spec/extensions/arbitrary_servers_spec.rb +2 -2
- data/spec/extensions/association_dependencies_spec.rb +1 -1
- data/spec/extensions/association_pks_spec.rb +4 -59
- data/spec/extensions/association_proxies_spec.rb +1 -1
- data/spec/extensions/auto_literal_strings_spec.rb +1 -12
- data/spec/extensions/auto_validations_spec.rb +1 -1
- data/spec/extensions/blacklist_security_spec.rb +1 -1
- data/spec/extensions/blank_spec.rb +1 -1
- data/spec/extensions/boolean_readers_spec.rb +1 -1
- data/spec/extensions/boolean_subsets_spec.rb +1 -1
- data/spec/extensions/caching_spec.rb +1 -1
- data/spec/extensions/class_table_inheritance_spec.rb +35 -1086
- data/spec/extensions/column_conflicts_spec.rb +1 -1
- data/spec/extensions/column_select_spec.rb +4 -4
- data/spec/extensions/columns_introspection_spec.rb +1 -1
- data/spec/extensions/columns_updated_spec.rb +1 -1
- data/spec/extensions/composition_spec.rb +1 -7
- data/spec/extensions/connection_expiration_spec.rb +3 -3
- data/spec/extensions/connection_validator_spec.rb +3 -3
- data/spec/extensions/constraint_validations_plugin_spec.rb +1 -1
- data/spec/extensions/constraint_validations_spec.rb +1 -1
- data/spec/extensions/core_refinements_spec.rb +1 -3
- data/spec/extensions/csv_serializer_spec.rb +4 -9
- data/spec/extensions/current_datetime_timestamp_spec.rb +1 -1
- data/spec/extensions/dataset_associations_spec.rb +2 -1
- data/spec/extensions/dataset_source_alias_spec.rb +1 -1
- data/spec/extensions/date_arithmetic_spec.rb +3 -3
- data/spec/extensions/def_dataset_method_spec.rb +1 -1
- data/spec/extensions/defaults_setter_spec.rb +2 -2
- data/spec/extensions/delay_add_association_spec.rb +8 -9
- data/spec/extensions/dirty_spec.rb +1 -1
- data/spec/extensions/duplicate_columns_handler_spec.rb +1 -1
- data/spec/extensions/eager_each_spec.rb +2 -2
- data/spec/extensions/empty_array_consider_nulls_spec.rb +1 -1
- data/spec/extensions/error_splitter_spec.rb +1 -1
- data/spec/extensions/error_sql_spec.rb +1 -1
- data/spec/extensions/eval_inspect_spec.rb +1 -1
- data/spec/extensions/finder_spec.rb +1 -1
- data/spec/extensions/force_encoding_spec.rb +2 -5
- data/spec/extensions/freeze_datasets_spec.rb +1 -1
- data/spec/extensions/graph_each_spec.rb +5 -5
- data/spec/extensions/hook_class_methods_spec.rb +1 -194
- data/spec/extensions/identifier_mangling_spec.rb +17 -170
- data/spec/extensions/implicit_subquery_spec.rb +1 -5
- data/spec/extensions/inflector_spec.rb +1 -1
- data/spec/extensions/input_transformer_spec.rb +7 -2
- data/spec/extensions/insert_returning_select_spec.rb +1 -1
- data/spec/extensions/instance_filters_spec.rb +1 -1
- data/spec/extensions/instance_hooks_spec.rb +1 -95
- data/spec/extensions/inverted_subsets_spec.rb +1 -1
- data/spec/extensions/json_serializer_spec.rb +1 -1
- data/spec/extensions/lazy_attributes_spec.rb +1 -7
- data/spec/extensions/list_spec.rb +1 -1
- data/spec/extensions/looser_typecasting_spec.rb +1 -1
- data/spec/extensions/many_through_many_spec.rb +1 -1
- data/spec/extensions/migration_spec.rb +2 -2
- data/spec/extensions/modification_detection_spec.rb +1 -1
- data/spec/extensions/mssql_optimistic_locking_spec.rb +1 -1
- data/spec/extensions/named_timezones_spec.rb +3 -3
- data/spec/extensions/nested_attributes_spec.rb +1 -29
- data/spec/extensions/null_dataset_spec.rb +1 -11
- data/spec/extensions/optimistic_locking_spec.rb +1 -1
- data/spec/extensions/pagination_spec.rb +1 -1
- data/spec/extensions/pg_array_associations_spec.rb +4 -1
- data/spec/extensions/pg_array_ops_spec.rb +1 -1
- data/spec/extensions/pg_array_spec.rb +3 -48
- data/spec/extensions/pg_enum_spec.rb +1 -1
- data/spec/extensions/pg_hstore_ops_spec.rb +1 -1
- data/spec/extensions/pg_hstore_spec.rb +23 -32
- data/spec/extensions/pg_inet_ops_spec.rb +1 -1
- data/spec/extensions/pg_inet_spec.rb +1 -14
- data/spec/extensions/pg_interval_spec.rb +3 -13
- data/spec/extensions/pg_json_ops_spec.rb +1 -1
- data/spec/extensions/pg_json_spec.rb +1 -13
- data/spec/extensions/pg_loose_count_spec.rb +1 -1
- data/spec/extensions/pg_range_ops_spec.rb +1 -1
- data/spec/extensions/pg_range_spec.rb +3 -88
- data/spec/extensions/pg_row_ops_spec.rb +1 -1
- data/spec/extensions/pg_row_plugin_spec.rb +1 -1
- data/spec/extensions/pg_row_spec.rb +1 -44
- data/spec/extensions/pg_static_cache_updater_spec.rb +1 -1
- data/spec/extensions/prepared_statements_safe_spec.rb +1 -1
- data/spec/extensions/prepared_statements_spec.rb +13 -48
- data/spec/extensions/pretty_table_spec.rb +1 -1
- data/spec/extensions/query_spec.rb +1 -12
- data/spec/extensions/rcte_tree_spec.rb +1 -1
- data/spec/extensions/round_timestamps_spec.rb +1 -5
- data/spec/extensions/s_spec.rb +1 -1
- data/spec/extensions/schema_caching_spec.rb +1 -1
- data/spec/extensions/schema_dumper_spec.rb +1 -1
- data/spec/extensions/select_remove_spec.rb +1 -1
- data/spec/extensions/sequel_4_dataset_methods_spec.rb +1 -1
- data/spec/extensions/serialization_modification_detection_spec.rb +1 -1
- data/spec/extensions/serialization_spec.rb +2 -14
- data/spec/extensions/server_block_spec.rb +1 -1
- data/spec/extensions/server_logging_spec.rb +2 -2
- data/spec/extensions/sharding_spec.rb +1 -1
- data/spec/extensions/shared_caching_spec.rb +1 -28
- data/spec/extensions/single_table_inheritance_spec.rb +2 -5
- data/spec/extensions/singular_table_names_spec.rb +1 -1
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +5 -27
- data/spec/extensions/split_array_nil_spec.rb +1 -1
- data/spec/extensions/split_values_spec.rb +1 -1
- data/spec/extensions/sql_comments_spec.rb +1 -1
- data/spec/extensions/sql_expr_spec.rb +1 -1
- data/spec/extensions/static_cache_spec.rb +1 -1
- data/spec/extensions/string_agg_spec.rb +2 -2
- data/spec/extensions/string_date_time_spec.rb +1 -1
- data/spec/extensions/string_stripper_spec.rb +1 -1
- data/spec/extensions/subclasses_spec.rb +1 -1
- data/spec/extensions/subset_conditions_spec.rb +1 -1
- data/spec/extensions/symbol_aref_refinement_spec.rb +1 -1
- data/spec/extensions/symbol_as_refinement_spec.rb +1 -1
- data/spec/extensions/table_select_spec.rb +4 -4
- data/spec/extensions/tactical_eager_loading_spec.rb +1 -6
- data/spec/extensions/thread_local_timezones_spec.rb +1 -1
- data/spec/extensions/timestamps_spec.rb +3 -3
- data/spec/extensions/to_dot_spec.rb +1 -1
- data/spec/extensions/touch_spec.rb +1 -1
- data/spec/extensions/tree_spec.rb +1 -1
- data/spec/extensions/typecast_on_load_spec.rb +1 -1
- data/spec/extensions/unlimited_update_spec.rb +1 -1
- data/spec/extensions/update_or_create_spec.rb +1 -1
- data/spec/extensions/update_primary_key_spec.rb +4 -3
- data/spec/extensions/update_refresh_spec.rb +1 -1
- data/spec/extensions/uuid_spec.rb +10 -12
- data/spec/extensions/validate_associated_spec.rb +1 -1
- data/spec/extensions/validation_class_methods_spec.rb +3 -3
- data/spec/extensions/validation_contexts_spec.rb +1 -1
- data/spec/extensions/validation_helpers_spec.rb +10 -44
- data/spec/extensions/whitelist_security_spec.rb +5 -5
- data/spec/extensions/xml_serializer_spec.rb +3 -3
- data/spec/guards_helper.rb +2 -1
- data/spec/integration/associations_test.rb +1 -23
- data/spec/integration/database_test.rb +7 -7
- data/spec/integration/dataset_test.rb +5 -47
- data/spec/integration/eager_loader_test.rb +1 -1
- data/spec/integration/migrator_test.rb +1 -1
- data/spec/integration/model_test.rb +4 -82
- data/spec/integration/plugin_test.rb +6 -22
- data/spec/integration/prepared_statement_test.rb +8 -88
- data/spec/integration/schema_test.rb +6 -6
- data/spec/integration/spec_helper.rb +13 -21
- data/spec/integration/timezone_test.rb +5 -5
- data/spec/integration/transaction_test.rb +3 -55
- data/spec/integration/type_test.rb +9 -9
- data/spec/model/association_reflection_spec.rb +24 -9
- data/spec/model/associations_spec.rb +124 -303
- data/spec/model/base_spec.rb +18 -137
- data/spec/model/class_dataset_methods_spec.rb +2 -20
- data/spec/model/dataset_methods_spec.rb +1 -20
- data/spec/model/eager_loading_spec.rb +17 -11
- data/spec/model/hooks_spec.rb +5 -300
- data/spec/model/inflector_spec.rb +1 -1
- data/spec/model/model_spec.rb +15 -320
- data/spec/model/plugins_spec.rb +2 -16
- data/spec/model/record_spec.rb +29 -121
- data/spec/model/spec_helper.rb +5 -15
- data/spec/model/validations_spec.rb +1 -1
- data/spec/sequel_warning.rb +1 -12
- metadata +8 -64
- data/doc/active_record.rdoc +0 -927
- data/lib/sequel/adapters/cubrid.rb +0 -160
- data/lib/sequel/adapters/do.rb +0 -166
- data/lib/sequel/adapters/do/mysql.rb +0 -69
- data/lib/sequel/adapters/do/postgres.rb +0 -46
- data/lib/sequel/adapters/do/sqlite3.rb +0 -41
- data/lib/sequel/adapters/jdbc/as400.rb +0 -92
- data/lib/sequel/adapters/jdbc/cubrid.rb +0 -65
- data/lib/sequel/adapters/jdbc/firebirdsql.rb +0 -37
- data/lib/sequel/adapters/jdbc/informix-sqli.rb +0 -34
- data/lib/sequel/adapters/jdbc/jdbcprogress.rb +0 -34
- data/lib/sequel/adapters/odbc/progress.rb +0 -12
- data/lib/sequel/adapters/shared/cubrid.rb +0 -245
- data/lib/sequel/adapters/shared/firebird.rb +0 -261
- data/lib/sequel/adapters/shared/informix.rb +0 -63
- data/lib/sequel/adapters/shared/progress.rb +0 -40
- data/lib/sequel/adapters/swift.rb +0 -169
- data/lib/sequel/adapters/swift/mysql.rb +0 -50
- data/lib/sequel/adapters/swift/postgres.rb +0 -49
- data/lib/sequel/adapters/swift/sqlite.rb +0 -48
- data/lib/sequel/adapters/utils/pg_types.rb +0 -4
- data/lib/sequel/dataset/mutation.rb +0 -98
- data/lib/sequel/extensions/_deprecated_identifier_mangling.rb +0 -117
- data/lib/sequel/extensions/empty_array_ignore_nulls.rb +0 -8
- data/lib/sequel/extensions/filter_having.rb +0 -65
- data/lib/sequel/extensions/hash_aliases.rb +0 -51
- data/lib/sequel/extensions/meta_def.rb +0 -37
- data/lib/sequel/extensions/query_literals.rb +0 -86
- data/lib/sequel/extensions/ruby18_symbol_extensions.rb +0 -26
- data/lib/sequel/extensions/sequel_3_dataset_methods.rb +0 -133
- data/lib/sequel/extensions/set_overrides.rb +0 -82
- data/lib/sequel/no_core_ext.rb +0 -4
- data/lib/sequel/plugins/association_autoreloading.rb +0 -11
- data/lib/sequel/plugins/identifier_columns.rb +0 -49
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +0 -11
- data/lib/sequel/plugins/pg_typecast_on_load.rb +0 -90
- data/lib/sequel/plugins/prepared_statements_associations.rb +0 -137
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +0 -71
- data/lib/sequel/plugins/schema.rb +0 -84
- data/lib/sequel/plugins/scissors.rb +0 -37
- data/spec/core/dataset_mutation_spec.rb +0 -253
- data/spec/extensions/_deprecated_identifier_mangling_spec.rb +0 -314
- data/spec/extensions/before_after_save_spec.rb +0 -40
- data/spec/extensions/filter_having_spec.rb +0 -42
- data/spec/extensions/from_block_spec.rb +0 -21
- data/spec/extensions/hash_aliases_spec.rb +0 -26
- data/spec/extensions/identifier_columns_spec.rb +0 -19
- data/spec/extensions/meta_def_spec.rb +0 -35
- data/spec/extensions/no_auto_literal_strings_spec.rb +0 -69
- data/spec/extensions/pg_typecast_on_load_spec.rb +0 -70
- data/spec/extensions/prepared_statements_associations_spec.rb +0 -212
- data/spec/extensions/prepared_statements_with_pk_spec.rb +0 -40
- data/spec/extensions/query_literals_spec.rb +0 -185
- data/spec/extensions/schema_spec.rb +0 -123
- data/spec/extensions/scissors_spec.rb +0 -27
- data/spec/extensions/sequel_3_dataset_methods_spec.rb +0 -118
- data/spec/extensions/set_overrides_spec.rb +0 -75
data/doc/postgresql.rdoc
CHANGED
@@ -19,10 +19,10 @@ rarely used PostgreSQL features that Sequel supports which are not mentioned her
|
|
19
19
|
|
20
20
|
Some of this this support depends on the specific adapter or underlying driver in use.
|
21
21
|
<tt>postgres only</tt> will denote support specific to the postgres adapter (i.e.
|
22
|
-
not available when connecting to PostgreSQL via the jdbc
|
22
|
+
not available when connecting to PostgreSQL via the jdbc adapter).
|
23
23
|
<tt>postgres/pg only</tt> will denote support specific to the postgres adapter when
|
24
|
-
pg is used as the underlying driver (i.e. not available when using the postgres-pr
|
25
|
-
|
24
|
+
pg is used as the underlying driver (i.e. not available when using the postgres-pr
|
25
|
+
driver).
|
26
26
|
|
27
27
|
== PostgreSQL-specific Database Type Support
|
28
28
|
|
@@ -44,7 +44,7 @@ pg_range :: ranges (for any scalar type), as a ruby Range-like object
|
|
44
44
|
pg_row :: row-valued/composite types, as a ruby Hash-like or Sequel::Model object
|
45
45
|
|
46
46
|
In general, these extensions just add support for Database objects to return retrieved
|
47
|
-
column values as the appropriate type
|
47
|
+
column values as the appropriate type and support for literalizing
|
48
48
|
the objects correctly for use in an SQL string, or using them as bound variable values (<tt>postgres/pg and jdbc/postgres only</tt>).
|
49
49
|
|
50
50
|
There are also type-specific extensions that make it easy to use database functions
|
@@ -56,6 +56,13 @@ pg_json_ops :: json-related functions and operators
|
|
56
56
|
pg_range_ops :: range-related functions and operators
|
57
57
|
pg_row_ops :: row-valued/composite type syntax support
|
58
58
|
|
59
|
+
These extensions aren't Database specific, they are global extensions, so you should
|
60
|
+
load them via <tt>Sequel.extension</tt>, after loading support for the specific types
|
61
|
+
into the Database instance:
|
62
|
+
|
63
|
+
DB.extension :pg_array
|
64
|
+
Sequel.extension :pg_array_ops
|
65
|
+
|
59
66
|
== PostgreSQL-specific DDL Support
|
60
67
|
|
61
68
|
=== Exclusion Constraints
|
@@ -64,7 +71,7 @@ In +create_table+ blocks, you can use the +exclude+ method to set up exclusion c
|
|
64
71
|
|
65
72
|
DB.create_table(:table) do
|
66
73
|
daterange :during
|
67
|
-
exclude([[:during, '&&']], :
|
74
|
+
exclude([[:during, '&&']], name: :table_during_excl)
|
68
75
|
end
|
69
76
|
# CREATE TABLE "table" ("during" daterange,
|
70
77
|
# CONSTRAINT "table_during_excl" EXCLUDE USING gist ("during" WITH &&))
|
@@ -72,20 +79,20 @@ In +create_table+ blocks, you can use the +exclude+ method to set up exclusion c
|
|
72
79
|
You can also add exclusion constraints in +alter_table+ blocks using add_exclusion_constraint:
|
73
80
|
|
74
81
|
DB.alter_table(:table) do
|
75
|
-
add_exclusion_constraint([[:during, '&&']], :
|
82
|
+
add_exclusion_constraint([[:during, '&&']], name: :table_during_excl)
|
76
83
|
end
|
77
84
|
# ALTER TABLE "table" ADD CONSTRAINT "table_during_excl" EXCLUDE USING gist ("during" WITH &&)
|
78
85
|
|
79
86
|
=== Adding Foreign Key and Check Constraints Without Initial Validation
|
80
87
|
|
81
|
-
You can add a <tt
|
88
|
+
You can add a <tt>not_valid: true</tt> option when adding constraints to existing tables so
|
82
89
|
that it doesn't check if all current rows are valid:
|
83
90
|
|
84
91
|
DB.alter_table(:table) do
|
85
92
|
# Assumes t_id column already exists
|
86
|
-
add_foreign_key([:t_id], :table, :
|
93
|
+
add_foreign_key([:t_id], :table, not_valid: true, name: :table_fk)
|
87
94
|
|
88
|
-
constraint({:
|
95
|
+
constraint({name: :col_123, not_valid: true}, col: [1,2,3])
|
89
96
|
end
|
90
97
|
# ALTER TABLE "table" ADD CONSTRAINT "table_fk" FOREIGN KEY ("t_id") REFERENCES "table" NOT VALID
|
91
98
|
# ALTER TABLE "table" ADD CONSTRAINT "col_123" CHECK (col IN (1, 2, 3)) NOT VALID
|
@@ -102,14 +109,14 @@ all existing rows have been fixed, you can validate the constraint:
|
|
102
109
|
|
103
110
|
=== Creating Indexes Concurrently
|
104
111
|
|
105
|
-
You can create indexes concurrently using the <tt
|
112
|
+
You can create indexes concurrently using the <tt>concurrently: true</tt> option:
|
106
113
|
|
107
|
-
DB.add_index(:table, :t_id, :
|
114
|
+
DB.add_index(:table, :t_id, concurrently: true)
|
108
115
|
# CREATE INDEX CONCURRENTLY "table_t_id_index" ON "table" ("t_id")
|
109
116
|
|
110
117
|
Similarly, you can drop indexes concurrently as well:
|
111
118
|
|
112
|
-
DB.drop_index(:table, :t_id, :
|
119
|
+
DB.drop_index(:table, :t_id, concurrently: true)
|
113
120
|
# DROP INDEX CONCURRENTLY "table_t_id_index"
|
114
121
|
|
115
122
|
=== Specific Conversions When Altering Column Types
|
@@ -119,7 +126,7 @@ conversion via a USING clause, and Sequel supports this using the <tt>:using</tt
|
|
119
126
|
|
120
127
|
DB.alter_table(:table) do
|
121
128
|
# Assume unix_time column is stored as an integer, and you want to change it to timestamp
|
122
|
-
set_column_type :unix_time, Time, :
|
129
|
+
set_column_type :unix_time, Time, using: (Sequel.cast('epoch', Time) + Sequel.cast('1 second', :interval) * :unix_time)
|
123
130
|
end
|
124
131
|
# ALTER TABLE "table" ALTER COLUMN "unix_time" TYPE timestamp
|
125
132
|
# USING (CAST('epoch' AS timestamp) + (CAST('1 second' AS interval) * "unix_time"))
|
@@ -127,9 +134,9 @@ conversion via a USING clause, and Sequel supports this using the <tt>:using</tt
|
|
127
134
|
=== Creating Unlogged Tables
|
128
135
|
|
129
136
|
PostgreSQL allows users to create unlogged tables, which are faster but not crash safe. Sequel
|
130
|
-
allows you do create an unlogged table by specifying the <tt
|
137
|
+
allows you do create an unlogged table by specifying the <tt>unlogged: true</tt> option to +create_table+:
|
131
138
|
|
132
|
-
DB.create_table(:table, :
|
139
|
+
DB.create_table(:table, unlogged: true){Integer :i}
|
133
140
|
# CREATE UNLOGGED TABLE "table" ("i" integer)
|
134
141
|
|
135
142
|
=== Creating/Dropping Schemas, Languages, Functions, and Triggers
|
@@ -146,7 +153,7 @@ Sequel has built in support for creating and dropping PostgreSQL schemas, proced
|
|
146
153
|
DB.drop_language(:plperl)
|
147
154
|
# DROP LANGUAGE plperl
|
148
155
|
|
149
|
-
DB.create_function(:set_updated_at, <<-SQL, :
|
156
|
+
DB.create_function(:set_updated_at, <<-SQL, language: :plpgsql, returns: :trigger)
|
150
157
|
BEGIN
|
151
158
|
NEW.updated_at := CURRENT_TIMESTAMP;
|
152
159
|
RETURN NEW;
|
@@ -160,11 +167,13 @@ Sequel has built in support for creating and dropping PostgreSQL schemas, proced
|
|
160
167
|
DB.drop_function(:set_updated_at)
|
161
168
|
# DROP FUNCTION set_updated_at()
|
162
169
|
|
163
|
-
DB.create_trigger(:table, :trg_updated_at, :set_updated_at, :
|
170
|
+
DB.create_trigger(:table, :trg_updated_at, :set_updated_at, events: :update, each_row: true, when: {Sequel[:new][:updated_at] => Sequel[:old][:updated_at]})
|
164
171
|
# CREATE TRIGGER trg_updated_at BEFORE UPDATE ON "table" FOR EACH ROW WHEN ("new"."updated_at" = "old"."updated_at") EXECUTE PROCEDURE set_updated_at()
|
165
172
|
DB.drop_trigger(:table, :trg_updated_at)
|
166
173
|
# DROP TRIGGER trg_updated_at ON "table"
|
167
174
|
|
175
|
+
However, you may want to consider just use <tt>Database#run</tt> with the necessary SQL code, at least for functions and triggers.
|
176
|
+
|
168
177
|
== PostgreSQL-specific DML Support
|
169
178
|
|
170
179
|
=== Returning Rows From Insert, Update, and Delete Statements
|
@@ -178,12 +187,12 @@ Sequel supports the ability to return rows from insert, update, and delete state
|
|
178
187
|
DB[:table].returning(:id).delete
|
179
188
|
# DELETE FROM "table" RETURNING "id"
|
180
189
|
|
181
|
-
DB[:table].returning(:id, Sequel.*(:id, :id).as(:idsq)).update(:
|
190
|
+
DB[:table].returning(:id, Sequel.*(:id, :id).as(:idsq)).update(id: 2)
|
182
191
|
# UPDATE "table" SET "id" = 2 RETURNING "id", ("id" * "id") AS "idsq"
|
183
192
|
|
184
193
|
When returning is used, instead of returning the number of rows affected (for updated/delete)
|
185
194
|
or the serial primary key value (for insert), it will return an array of hashes with the
|
186
|
-
|
195
|
+
returning results.
|
187
196
|
|
188
197
|
=== VALUES Support
|
189
198
|
|
@@ -203,27 +212,27 @@ Sequel offers support for the +VALUES+ statement using <tt>Database#values</tt>:
|
|
203
212
|
Starting with PostgreSQL 9.5, you can do an upsert or ignore unique or exclusion constraint
|
204
213
|
violations when inserting using <tt>Dataset#insert_conflict</tt>:
|
205
214
|
|
206
|
-
DB[:table].insert_conflict.insert(:
|
215
|
+
DB[:table].insert_conflict.insert(a: 1, b: 2)
|
207
216
|
# INSERT INTO TABLE (a, b) VALUES (1, 2)
|
208
217
|
# ON CONFLICT DO NOTHING
|
209
218
|
|
210
219
|
For compatibility with Sequel's MySQL support, you can also use +insert_ignore+:
|
211
220
|
|
212
|
-
DB[:table].insert_ignore.insert(:
|
221
|
+
DB[:table].insert_ignore.insert(a: 1, b: 2)
|
213
222
|
# INSERT INTO TABLE (a, b) VALUES (1, 2)
|
214
223
|
# ON CONFLICT DO NOTHING
|
215
224
|
|
216
225
|
You can pass a specific constraint name using +:constraint+, to only ignore a specific
|
217
226
|
constraint violation:
|
218
227
|
|
219
|
-
DB[:table].insert_conflict(:
|
228
|
+
DB[:table].insert_conflict(constraint: :table_a_uidx).insert(a: 1, b: 2)
|
220
229
|
# INSERT INTO TABLE (a, b) VALUES (1, 2)
|
221
230
|
# ON CONFLICT ON CONSTRAINT table_a_uidx DO NOTHING
|
222
231
|
|
223
232
|
If the unique or exclusion constraint covers the whole table (e.g. it isn't a partial unique
|
224
233
|
index), then you can just specify the column using the +:target+ option:
|
225
234
|
|
226
|
-
DB[:table].insert_conflict(:
|
235
|
+
DB[:table].insert_conflict(target: :a).insert(a: 1, b: 2)
|
227
236
|
# INSERT INTO TABLE (a, b) VALUES (1, 2)
|
228
237
|
# ON CONFLICT (a) DO NOTHING
|
229
238
|
|
@@ -231,7 +240,7 @@ If you want to update the existing row instead of ignoring the constraint violat
|
|
231
240
|
can pass an +:update+ option with a hash of values to update. You must pass either the
|
232
241
|
+:target+ or +:constraint+ options when passing the +:update+ option:
|
233
242
|
|
234
|
-
DB[:table].insert_conflict(:
|
243
|
+
DB[:table].insert_conflict(target: :a, update: {b: Sequel[:excluded][:b]}).insert(a: 1, b: 2)
|
235
244
|
# INSERT INTO TABLE (a, b) VALUES (1, 2)
|
236
245
|
# ON CONFLICT (a) DO UPDATE SET b = excluded.b
|
237
246
|
|
@@ -239,8 +248,9 @@ Additionally, if you only want to do the update in certain cases, you can specif
|
|
239
248
|
+:update_where+ option, which will be used as a filter. If the row doesn't match the
|
240
249
|
conditions, the constraint violation will be ignored, but the row will not be updated:
|
241
250
|
|
242
|
-
DB[:table].insert_conflict(
|
243
|
-
:
|
251
|
+
DB[:table].insert_conflict(constraint::table_a_uidx,
|
252
|
+
update: {b: Sequel[:excluded][:b]},
|
253
|
+
update_where: {Sequel[:table][:status_id]=>1}).insert(a: 1, b: 2)
|
244
254
|
# INSERT INTO TABLE (a, b) VALUES (1, 2)
|
245
255
|
# ON CONFLICT ON CONSTRAINT table_a_uidx
|
246
256
|
# DO UPDATE SET b = excluded.b WHERE (table.status_id = 1)
|
@@ -271,19 +281,19 @@ without keeping all rows in memory:
|
|
271
281
|
This support is used by default when using <tt>Dataset#paged_each</tt>.
|
272
282
|
|
273
283
|
Using cursors, it is possible to update individual rows of a large dataset
|
274
|
-
easily using the <tt
|
284
|
+
easily using the <tt>rows_per_fetch: 1</tt> option in conjunction with
|
275
285
|
<tt>Dataset#where_current_of</tt>. This is useful if the logic needed to
|
276
286
|
update the rows exists in the application and not in the database:
|
277
287
|
|
278
|
-
ds.use_cursor(:
|
279
|
-
ds.where_current_of.update(:
|
288
|
+
ds.use_cursor(rows_per_fetch: 1).each do |row|
|
289
|
+
ds.where_current_of.update(col: new_col_value(row))
|
280
290
|
end
|
281
291
|
|
282
292
|
=== Truncate Modifiers
|
283
293
|
|
284
294
|
Sequel supports PostgreSQL-specific truncate options:
|
285
295
|
|
286
|
-
DB[:table].truncate(:
|
296
|
+
DB[:table].truncate(cascade: true, only: true, restart: true)
|
287
297
|
# TRUNCATE TABLE ONLY "table" RESTART IDENTITY CASCADE
|
288
298
|
|
289
299
|
=== COPY Support <tt>postgres/pg and jdbc/postgres only</tt>
|
@@ -292,14 +302,14 @@ PostgreSQL's COPY feature is pretty much the fastest way to get data in or out o
|
|
292
302
|
Sequel supports getting data out of the database via <tt>Database#copy_table</tt>, either for
|
293
303
|
a specific table or for an arbitrary dataset:
|
294
304
|
|
295
|
-
DB.copy_table(:table, :
|
305
|
+
DB.copy_table(:table, format: :csv)
|
296
306
|
# COPY "table" TO STDOUT (FORMAT csv)
|
297
|
-
DB.copy_table(DB[:table], :
|
307
|
+
DB.copy_table(DB[:table], format: :csv)
|
298
308
|
# COPY (SELECT * FROM "table") TO STDOUT (FORMAT csv)
|
299
309
|
|
300
310
|
It supports putting data into the database via <tt>Database#copy_into</tt>:
|
301
311
|
|
302
|
-
DB.copy_into(:table, :
|
312
|
+
DB.copy_into(:table, format: :csv, columns: [:column1, :column2], data: "1,2\n2,3\n")
|
303
313
|
# COPY "table"("column1", "column2") FROM STDIN (FORMAT csv)
|
304
314
|
|
305
315
|
=== Anonymous Function Execution
|
@@ -336,7 +346,7 @@ this blocks until the listening thread is notified:
|
|
336
346
|
Note that +listen+ by default only listens for a single notification. If you want to loop and process
|
337
347
|
notifications:
|
338
348
|
|
339
|
-
DB.listen(:channel, :
|
349
|
+
DB.listen(:channel, loop: true){|channel| p channel}
|
340
350
|
|
341
351
|
The +pg_static_cache_updater+ extension uses this support to automatically update
|
342
352
|
the caches for models using the +static_cache+ plugin. Look at the documentation of that
|
@@ -348,7 +358,7 @@ Sequel makes it easy to lock tables, though it is generally better to let the da
|
|
348
358
|
handle locking:
|
349
359
|
|
350
360
|
DB[:table].lock('EXCLUSIVE') do
|
351
|
-
DB[:table].insert(:
|
361
|
+
DB[:table].insert(id: DB[:table].max(:id)+1)
|
352
362
|
end
|
353
363
|
# BEGIN;
|
354
364
|
# LOCK TABLE "table" IN EXCLUSIVE MODE;
|
@@ -360,14 +370,14 @@ handle locking:
|
|
360
370
|
|
361
371
|
When the postgres adapter is used with the pg driver, Sequel automatically checks for sequel_pg, and
|
362
372
|
loads it if it is available. sequel_pg is a C extension that optimizes the fetching of rows, generally
|
363
|
-
resulting in a
|
373
|
+
resulting in a ~2x speedup. It is highly recommended to install sequel_pg if you are using the
|
364
374
|
postgres adapter with pg.
|
365
375
|
|
366
|
-
sequel_pg has additional optimizations when using the Dataset +map+, +
|
376
|
+
sequel_pg has additional optimizations when using the Dataset +map+, +as_hash+,
|
367
377
|
+to_hash_groups+, +select_hash+, +select_hash_groups+, +select_map+, and +select_order_map+ methods,
|
368
378
|
which avoids creating intermediate hashes and can add further speedups.
|
369
379
|
|
370
|
-
In addition to optimization, sequel_pg also adds streaming support if used on PostgreSQL 9.2
|
380
|
+
In addition to optimization, sequel_pg also adds streaming support if used on PostgreSQL 9.2+. Streaming
|
371
381
|
support is similar to using a cursor, but it is faster and more transparent.
|
372
382
|
|
373
383
|
You can enable the streaming support:
|
@@ -14,7 +14,7 @@ the following adapters:
|
|
14
14
|
* sqlite
|
15
15
|
* tinytds
|
16
16
|
|
17
|
-
Support on other adapters is emulated
|
17
|
+
Support on other adapters is emulated.
|
18
18
|
|
19
19
|
You can use the prepared_statements model plugin to automatically use prepared
|
20
20
|
statements for some common model actions such as saving or deleting a model
|
@@ -29,7 +29,7 @@ significantly for placeholders (e.g. :name, $1, ?). Sequel abstracts all of
|
|
29
29
|
that and allows you to specify placeholders by using the :$name format for
|
30
30
|
placeholders, e.g.:
|
31
31
|
|
32
|
-
ds = DB[:items].where(:
|
32
|
+
ds = DB[:items].where(name: :$n)
|
33
33
|
|
34
34
|
You can use these placeholders in most places where you can use the value
|
35
35
|
directly. For example, if you want to use placeholders while also using
|
@@ -41,45 +41,49 @@ raw SQL, you can do:
|
|
41
41
|
|
42
42
|
Using bound variables for this query is simple:
|
43
43
|
|
44
|
-
ds.call(:select, :
|
44
|
+
ds.call(:select, n: 'Jim')
|
45
45
|
|
46
46
|
This will do the equivalent of selecting records that have the name 'Jim'. It
|
47
47
|
returns all records, and can take a block that is passed to <tt>Dataset#all</tt>.
|
48
48
|
|
49
49
|
Deleting or returning the first record works similarly:
|
50
50
|
|
51
|
-
ds.call(:first, :
|
52
|
-
ds.call(:delete, :
|
51
|
+
ds.call(:first, n: 'Jim') # First record with name 'Jim'
|
52
|
+
ds.call(:delete, n: 'Jim') # Delete records with name 'Jim'
|
53
53
|
|
54
54
|
For inserting/updating records, you should also specify a value hash, which
|
55
55
|
may itself contain placeholders:
|
56
56
|
|
57
57
|
# Insert record with 'Jim', note that the previous filter is ignored
|
58
|
-
ds.call(:insert, {:
|
58
|
+
ds.call(:insert, {n: 'Jim'}, name: :$n)
|
59
59
|
# Change name to 'Bob' for all records with name of 'Jim'
|
60
|
-
ds.call(:update, {:
|
60
|
+
ds.call(:update, {n: 'Jim', new_n: 'Bob'}, name: :$new_n)
|
61
61
|
|
62
62
|
== Prepared Statements
|
63
63
|
|
64
64
|
Prepared statement support is similar to bound variable support, but you
|
65
65
|
use <tt>Dataset#prepare</tt> with a name, and <tt>Dataset#call</tt> or <tt>Database#call</tt> later with the values:
|
66
66
|
|
67
|
-
ds = DB[:items].where(:
|
67
|
+
ds = DB[:items].where(name: :$n)
|
68
68
|
ps = ds.prepare(:select, :select_by_name)
|
69
|
-
|
70
|
-
|
69
|
+
|
70
|
+
ps.call(n: 'Jim')
|
71
|
+
DB.call(:select_by_name, n: 'Jim') # same
|
71
72
|
|
72
73
|
The <tt>Dataset#prepare</tt> method returns a prepared statement, and also stores a
|
73
74
|
copy of the prepared statement in the database for later use. For insert
|
74
75
|
and update queries, the hash to insert/update is passed to +prepare+:
|
75
76
|
|
76
|
-
ps1 = DB[:items].prepare(:insert, :insert_with_name, :
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
77
|
+
ps1 = DB[:items].prepare(:insert, :insert_with_name, name: :$n)
|
78
|
+
|
79
|
+
ps1.call(n: 'Jim')
|
80
|
+
DB.call(:insert_with_name, n: 'Jim') # same
|
81
|
+
|
82
|
+
ds = DB[:items].where(name: :$n)
|
83
|
+
ps2 = ds.prepare(:update, :update_name, name: :$new_n)
|
84
|
+
|
85
|
+
ps2.call(n: 'Jim', new_n: 'Bob')
|
86
|
+
DB.call(:update_name, n: 'Jim', new_n: 'Bob') # same
|
83
87
|
|
84
88
|
== Implementation Issues
|
85
89
|
|
@@ -92,7 +96,7 @@ to create prepared statements dynamically at runtime.
|
|
92
96
|
|
93
97
|
=== PostgreSQL
|
94
98
|
|
95
|
-
If you are using the
|
99
|
+
If you are using the postgres-pr driver, PostgreSQL uses the
|
96
100
|
default emulated support. If you are using ruby-pg, there is native support
|
97
101
|
for both prepared statements and bound variables. Prepared statements are
|
98
102
|
always server side.
|
@@ -103,9 +107,10 @@ SQLite supports both prepared statements and bound variables.
|
|
103
107
|
|
104
108
|
=== MySQL/Mysql2
|
105
109
|
|
106
|
-
The MySQL
|
107
|
-
variable methods
|
108
|
-
|
110
|
+
The MySQL and Mysql2 <0.4 ruby drivers do not support bound variables, so the bound
|
111
|
+
variable methods are emulated. It uses server side prepared statements.
|
112
|
+
|
113
|
+
Mysql2 0.4+ supports both prepared statements and bound variables.
|
109
114
|
|
110
115
|
=== JDBC
|
111
116
|
|
@@ -136,4 +141,4 @@ not currently supported.
|
|
136
141
|
|
137
142
|
=== All Others
|
138
143
|
|
139
|
-
Support is emulated
|
144
|
+
Support is emulated.
|
data/doc/querying.rdoc
CHANGED
@@ -4,15 +4,15 @@ This guide is based on http://guides.rubyonrails.org/active_record_querying.html
|
|
4
4
|
|
5
5
|
== Purpose of this Guide
|
6
6
|
|
7
|
-
Sequel is a
|
7
|
+
Sequel is a flexible and powerful database library
|
8
8
|
that supports a wide variety of different querying methods. This guide
|
9
|
-
aims to be a
|
9
|
+
aims to be a introduction to Sequel's querying support.
|
10
10
|
|
11
|
-
While you can
|
11
|
+
While you can use raw SQL with Sequel, a large part of the
|
12
12
|
advantage you get from using Sequel is Sequel's ability to abstract
|
13
|
-
SQL from you and give you a
|
13
|
+
SQL from you and give you a pure-ruby interface. Sequel also ships with
|
14
14
|
a {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc],
|
15
|
-
which
|
15
|
+
which adds methods to core ruby types to work with Sequel.
|
16
16
|
|
17
17
|
== Retrieving Objects
|
18
18
|
|
@@ -39,7 +39,7 @@ by its primary key value:
|
|
39
39
|
|
40
40
|
# Find artist with primary key (id) 1
|
41
41
|
artist = Artist[1]
|
42
|
-
# SELECT * FROM artists WHERE id = 1
|
42
|
+
# SELECT * FROM artists WHERE (id = 1)
|
43
43
|
# => #<Artist @values={:name=>"YJM", :id=>1}>
|
44
44
|
|
45
45
|
If there is no record with the given primary key, nil will be returned. If you want
|
@@ -58,7 +58,7 @@ If you just want the first record in the dataset,
|
|
58
58
|
|
59
59
|
Any options you pass to +first+ will be used as a filter:
|
60
60
|
|
61
|
-
artist = Artist.first(:
|
61
|
+
artist = Artist.first(name: 'YJM')
|
62
62
|
# SELECT * FROM artists WHERE (name = 'YJM') LIMIT 1
|
63
63
|
# => #<Artist @values={:name=>"YJM", :id=>1}>
|
64
64
|
|
@@ -72,7 +72,7 @@ raise an exception instead, use <tt>first!</tt>.
|
|
72
72
|
<tt>Sequel::Dataset#[]</tt> is basically an alias for +first+, except it
|
73
73
|
requires an argument:
|
74
74
|
|
75
|
-
DB[:artists][:
|
75
|
+
DB[:artists][{name: 'YJM'}]
|
76
76
|
# SELECT * FROM artists WHERE (name = 'YJM') LIMIT 1
|
77
77
|
# => {:name=>"YJM", :id=>1}
|
78
78
|
|
@@ -109,6 +109,15 @@ you want:
|
|
109
109
|
# SELECT name FROM artists LIMIT 1
|
110
110
|
# => "YJM"
|
111
111
|
|
112
|
+
==== Retrieving a Multiple Column Values
|
113
|
+
|
114
|
+
If you want the value for multiple columns, you can pass an array to
|
115
|
+
<tt>Sequel::Dataset#get</tt>:
|
116
|
+
|
117
|
+
artist_id, artist_name, = Artist.get([:id, :name])
|
118
|
+
# SELECT name FROM artists LIMIT 1
|
119
|
+
# => [1, "YJM"]
|
120
|
+
|
112
121
|
=== Retrieving Multiple Objects
|
113
122
|
|
114
123
|
==== As an Array of Hashes or Model Objects
|
@@ -181,21 +190,21 @@ an array of arrays of values will be returned:
|
|
181
190
|
==== As a Hash
|
182
191
|
|
183
192
|
Sequel makes it easy to take an SQL query and return it as a ruby hash,
|
184
|
-
using the +
|
193
|
+
using the +as_hash+ method:
|
185
194
|
|
186
|
-
artist_names = Artist.
|
195
|
+
artist_names = Artist.as_hash(:id, :name)
|
187
196
|
# SELECT * FROM artists
|
188
197
|
# => {1=>"YJM", 2=>"AS"}
|
189
198
|
|
190
|
-
As you can see, the +
|
199
|
+
As you can see, the +as_hash+ method uses the first symbol as the key
|
191
200
|
and the second symbol as the value. So if you swap the two arguments the hash
|
192
201
|
will have its keys and values transposed:
|
193
202
|
|
194
|
-
artist_names = Artist.
|
203
|
+
artist_names = Artist.as_hash(:name, :id)
|
195
204
|
# SELECT * FROM artists
|
196
205
|
# => {"YJM"=>1, "AS"=>2}
|
197
206
|
|
198
|
-
Now what if you have multiple values for the same key? By default, +
|
207
|
+
Now what if you have multiple values for the same key? By default, +as_hash+
|
199
208
|
will just have the last matching value. If you care about all matching values,
|
200
209
|
use +to_hash_groups+, which makes the values of the array an array of matching
|
201
210
|
values, in the order they were received:
|
@@ -204,10 +213,10 @@ values, in the order they were received:
|
|
204
213
|
# SELECT * FROM artists
|
205
214
|
# => {"YJM"=>[1, 10, ...], "AS"=>[2, 20, ...]}
|
206
215
|
|
207
|
-
If you only provide one argument to +
|
216
|
+
If you only provide one argument to +as_hash+, it uses the entire hash
|
208
217
|
or model object as the value:
|
209
218
|
|
210
|
-
artist_names = DB[:artists].
|
219
|
+
artist_names = DB[:artists].as_hash(:name)
|
211
220
|
# SELECT * FROM artists
|
212
221
|
# => {"YJM"=>{:id=>1, :name=>"YJM"}, "AS"=>{:id=>2, :name=>"AS"}}
|
213
222
|
|
@@ -217,12 +226,12 @@ and +to_hash_groups+ works similarly:
|
|
217
226
|
# SELECT * FROM artists
|
218
227
|
# => {"YJM"=>[{:id=>1, :name=>"YJM"}, {:id=>10, :name=>"YJM"}], ...}
|
219
228
|
|
220
|
-
Model datasets have a +
|
229
|
+
Model datasets have a +as_hash+ method that can be called without any
|
221
230
|
arguments, in which case it will use the primary key as the key and
|
222
231
|
the model object as the value. This can be used to easily create an
|
223
232
|
identity map:
|
224
233
|
|
225
|
-
artist_names = Artist.
|
234
|
+
artist_names = Artist.as_hash
|
226
235
|
# SELECT * FROM artists
|
227
236
|
# => {1=>#<Artist @values={:id=>1, :name=>"YGM"}>,
|
228
237
|
# 2=>#<Artist @values={:id=>2, :name=>"AS"}>}
|
@@ -230,7 +239,7 @@ identity map:
|
|
230
239
|
There is no equivalent handling to +to_hash_groups+, since there would
|
231
240
|
only be one matching record, as the primary key must be unique.
|
232
241
|
|
233
|
-
Note that +
|
242
|
+
Note that +as_hash+ never modifies the columns selected. However, just
|
234
243
|
like Sequel has a +select_map+ method to modify the columns selected and
|
235
244
|
return an array, Sequel also has a +select_hash+ method to modify the
|
236
245
|
columns selected and return a hash:
|
@@ -260,8 +269,8 @@ be using:
|
|
260
269
|
1. Methods that return row(s), discussed above
|
261
270
|
2. Methods that return modified datasets, discussed below
|
262
271
|
|
263
|
-
Sequel
|
264
|
-
|
272
|
+
Sequel datasets are frozen and use a method chaining, functional style API
|
273
|
+
that returns modified datasets. Let's start with a simple example.
|
265
274
|
|
266
275
|
This is a basic dataset that includes all records in the
|
267
276
|
table +artists+:
|
@@ -273,7 +282,7 @@ Let's say we are only interested in the artists whose names
|
|
273
282
|
start with "A":
|
274
283
|
|
275
284
|
ds2 = ds1.where(Sequel.like(:name, 'A%'))
|
276
|
-
# SELECT * FROM artists WHERE name LIKE 'A%' ESCAPE '\'
|
285
|
+
# SELECT * FROM artists WHERE (name LIKE 'A%' ESCAPE '\')
|
277
286
|
|
278
287
|
Here we see that +where+ returns a dataset that adds a +WHERE+
|
279
288
|
clause to the query. It's important to note that +where+ does
|
@@ -282,10 +291,9 @@ not modify the receiver:
|
|
282
291
|
ds1
|
283
292
|
# SELECT * FROM artists
|
284
293
|
ds2
|
285
|
-
# SELECT * FROM artists WHERE name LIKE 'A%' ESCAPE '\'
|
294
|
+
# SELECT * FROM artists WHERE (name LIKE 'A%' ESCAPE '\')
|
286
295
|
|
287
|
-
In Sequel,
|
288
|
-
not modify the dataset itself, so you can freely use the dataset in multiple
|
296
|
+
In Sequel, dataset methods do not modify the dataset itself, so you can freely use the dataset in multiple
|
289
297
|
places without worrying that its usage in one place will affect its usage
|
290
298
|
in another place. This is what is meant by a functional style API.
|
291
299
|
|
@@ -293,7 +301,7 @@ Let's say we only want to select the id and name columns, and that
|
|
293
301
|
we want to order by name:
|
294
302
|
|
295
303
|
ds3 = ds2.order(:name).select(:id, :name)
|
296
|
-
# SELECT id, name FROM artists WHERE name LIKE 'A%' ESCAPE '\' ORDER BY name
|
304
|
+
# SELECT id, name FROM artists WHERE (name LIKE 'A%' ESCAPE '\') ORDER BY name
|
297
305
|
|
298
306
|
Note how you don't need to assign the returned value of order to a variable,
|
299
307
|
and then call select on that. Because order just returns a dataset, you can
|
@@ -315,54 +323,57 @@ below.
|
|
315
323
|
=== Hashes
|
316
324
|
|
317
325
|
The most common format for providing filters is via a hash. In general, Sequel
|
318
|
-
treats conditions specified with a hash as equality or
|
326
|
+
treats conditions specified with a hash as equality, inclusion, or identity. What type
|
319
327
|
of condition is used depends on the values in the hash.
|
320
328
|
|
321
329
|
Unless Sequel has special support for the value's class, it uses a simple
|
322
330
|
equality statement:
|
323
331
|
|
324
|
-
Artist.where(:
|
325
|
-
# SELECT * FROM artists WHERE id = 1
|
332
|
+
Artist.where(id: 1)
|
333
|
+
# SELECT * FROM artists WHERE (id = 1)
|
326
334
|
|
327
|
-
Artist.where(:
|
328
|
-
# SELECT * FROM artists WHERE name = 'YJM'
|
335
|
+
Artist.where(name: 'YJM')
|
336
|
+
# SELECT * FROM artists WHERE (name = 'YJM')
|
329
337
|
|
330
|
-
For arrays, Sequel uses the IN operator
|
338
|
+
For arrays, Sequel uses the IN operator with a value list:
|
331
339
|
|
332
|
-
Artist.where(:
|
333
|
-
# SELECT * FROM artists WHERE id IN (1, 2)
|
340
|
+
Artist.where(id: [1, 2])
|
341
|
+
# SELECT * FROM artists WHERE (id IN (1, 2))
|
334
342
|
|
335
343
|
For datasets, Sequel uses the IN operator with a subselect:
|
336
344
|
|
337
|
-
Artist.where(:
|
338
|
-
# SELECT * FROM artists WHERE id IN (
|
339
|
-
# SELECT artist_id FROM albums)
|
345
|
+
Artist.where(id: Album.select(:artist_id))
|
346
|
+
# SELECT * FROM artists WHERE (id IN (
|
347
|
+
# SELECT artist_id FROM albums))
|
340
348
|
|
341
349
|
For boolean values such as nil, true, and false, Sequel uses the IS operator:
|
342
350
|
|
343
|
-
Artist.where(:
|
344
|
-
# SELECT * FROM artists WHERE id IS NULL
|
351
|
+
Artist.where(id: nil)
|
352
|
+
# SELECT * FROM artists WHERE (id IS NULL)
|
345
353
|
|
346
354
|
For ranges, Sequel uses a pair of inequality statements:
|
347
355
|
|
348
|
-
Artist.where(:
|
349
|
-
# SELECT * FROM artists WHERE id >= 1 AND id <= 5
|
356
|
+
Artist.where(id: 1..5)
|
357
|
+
# SELECT * FROM artists WHERE ((id >= 1) AND (id <= 5))
|
358
|
+
|
359
|
+
Artist.where(id: 1...5)
|
360
|
+
# SELECT * FROM artists WHERE ((id >= 1) AND (id < 5))
|
350
361
|
|
351
362
|
Finally, for regexps, Sequel uses an SQL regular expression. Note that this
|
352
363
|
is probably only supported on PostgreSQL and MySQL.
|
353
364
|
|
354
|
-
Artist.where(:
|
355
|
-
# SELECT * FROM artists WHERE name ~ 'JM$'
|
365
|
+
Artist.where(name: /JM$/)
|
366
|
+
# SELECT * FROM artists WHERE (name ~ 'JM$')
|
356
367
|
|
357
368
|
If there are multiple arguments in the hash, the filters are ANDed together:
|
358
369
|
|
359
|
-
Artist.where(:
|
360
|
-
# SELECT * FROM artists WHERE id = 1 AND name ~ 'JM$'
|
370
|
+
Artist.where(id: 1, name: /JM$/)
|
371
|
+
# SELECT * FROM artists WHERE ((id = 1) AND (name ~ 'JM$'))
|
361
372
|
|
362
373
|
This works the same as if you used two separate +where+ calls:
|
363
374
|
|
364
|
-
Artist.where(:
|
365
|
-
# SELECT * FROM artists WHERE id = 1 AND name ~ 'JM$'
|
375
|
+
Artist.where(id: 1).where(name: /JM$/)
|
376
|
+
# SELECT * FROM artists WHERE ((id = 1) AND (name ~ 'JM$'))
|
366
377
|
|
367
378
|
=== Array of Two Element Arrays
|
368
379
|
|
@@ -371,28 +382,28 @@ advantage to using an array of two element arrays is that it allows you to
|
|
371
382
|
duplicate keys, so you can do:
|
372
383
|
|
373
384
|
Artist.where([[:name, /JM$/], [:name, /^YJ/]])
|
374
|
-
# SELECT * FROM artists WHERE name ~ 'JM$' AND name ~ '^YJ'
|
385
|
+
# SELECT * FROM artists WHERE ((name ~ 'JM$')) AND ((name ~ '^YJ'))
|
375
386
|
|
376
387
|
=== Virtual Row Blocks
|
377
388
|
|
378
389
|
If a block is passed to a filter, it is treated as a virtual row block:
|
379
390
|
|
380
391
|
Artist.where{id > 5}
|
381
|
-
# SELECT * FROM artists WHERE id > 5
|
392
|
+
# SELECT * FROM artists WHERE (id > 5)
|
382
393
|
|
383
394
|
You can learn more about virtual row blocks in the {"Virtual Rows" guide}[rdoc-ref:doc/virtual_rows.rdoc].
|
384
395
|
|
385
396
|
You can provide both regular arguments and a block, in which case the results
|
386
397
|
will be ANDed together:
|
387
398
|
|
388
|
-
Artist.where(:
|
389
|
-
# SELECT * FROM artists WHERE name >= 'A' AND name < 'M' AND id > 5
|
399
|
+
Artist.where(name: 'A'...'M'){id > 5}
|
400
|
+
# SELECT * FROM artists WHERE ((name >= 'A') AND (name < 'M') AND (id > 5))
|
390
401
|
|
391
402
|
Using virtual row blocks, what you can do with single entry hash or an array with
|
392
403
|
a single two element array can also be done using the =~ method:
|
393
404
|
|
394
405
|
Artist.where{id =~ 5}
|
395
|
-
# SELECT * FROM artists WHERE id = 5
|
406
|
+
# SELECT * FROM artists WHERE (id = 5)
|
396
407
|
|
397
408
|
=== Symbols
|
398
409
|
|
@@ -420,43 +431,44 @@ method is Sequel.[], which takes any object and wraps it in a SQL::Expression
|
|
420
431
|
object. In most cases, the SQL::Expression returned supports the & operator for
|
421
432
|
+AND+, the | operator for +OR+, and the ~ operator for inversion:
|
422
433
|
|
423
|
-
Artist.where(Sequel.like(:name, 'Y%') & (Sequel[:
|
424
|
-
# SELECT * FROM artists WHERE name LIKE 'Y%' ESCAPE '\' AND (b = 1 OR c != 3)
|
434
|
+
Artist.where(Sequel.like(:name, 'Y%') & (Sequel[{b: 1}] | Sequel.~(c: 3)))
|
435
|
+
# SELECT * FROM artists WHERE ((name LIKE 'Y%' ESCAPE '\') AND ((b = 1) OR (c != 3)))
|
425
436
|
|
426
437
|
You can combine these expression operators with the virtual row support:
|
427
438
|
|
428
439
|
Artist.where{(a > 1) & ~((b(c) < 1) | d)}
|
429
|
-
# SELECT * FROM artists WHERE a > 1 AND b(c) >= 1 AND NOT d
|
440
|
+
# SELECT * FROM artists WHERE ((a > 1) AND (b(c) >= 1) AND NOT d)
|
430
441
|
|
431
442
|
Note the use of parentheses when using the & and | operators, as they have lower
|
432
443
|
precedence than other operators. The following will not work:
|
433
444
|
|
434
445
|
Artist.where{a > 1 & ~(b(c) < 1 | d)}
|
435
|
-
# Raises a TypeError
|
446
|
+
# Raises a TypeError
|
436
447
|
|
437
448
|
=== Strings with Placeholders
|
438
449
|
|
439
|
-
Assuming you want to get your hands dirty and
|
440
|
-
to
|
450
|
+
Assuming you want to get your hands dirty and use SQL fragments in filters, Sequel allows you
|
451
|
+
to do so if you explicitly mark the strings as literal strings using +Sequel.lit+. You can
|
452
|
+
use placeholders in the string and pass arguments for the placeholders:
|
441
453
|
|
442
|
-
Artist.where("name LIKE ?", 'Y%')
|
443
|
-
# SELECT * FROM artists WHERE name LIKE 'Y%'
|
454
|
+
Artist.where(Sequel.lit("name LIKE ?", 'Y%'))
|
455
|
+
# SELECT * FROM artists WHERE (name LIKE 'Y%')
|
444
456
|
|
445
457
|
This is the most common type of placeholder, where each question mark is substituted
|
446
458
|
with the next argument:
|
447
459
|
|
448
|
-
Artist.where("name LIKE ? AND id = ?", 'Y%', 5)
|
449
|
-
# SELECT * FROM artists WHERE name LIKE 'Y%' AND id = 5
|
460
|
+
Artist.where(Sequel.lit("name LIKE ? AND id = ?", 'Y%', 5))
|
461
|
+
# SELECT * FROM artists WHERE (name LIKE 'Y%' AND id = 5)
|
450
462
|
|
451
463
|
You can also use named placeholders with a hash, where the named placeholders use
|
452
464
|
colons before the placeholder names:
|
453
465
|
|
454
|
-
Artist.where("name LIKE :name AND id = :id", :
|
455
|
-
# SELECT * FROM artists WHERE name LIKE 'Y%' AND id = 5
|
466
|
+
Artist.where(Sequel.lit("name LIKE :name AND id = :id", name: 'Y%', id: 5))
|
467
|
+
# SELECT * FROM artists WHERE (name LIKE 'Y%' AND id = 5)
|
456
468
|
|
457
469
|
You don't have to provide any placeholders if you don't want to:
|
458
470
|
|
459
|
-
Artist.where("id = 2")
|
471
|
+
Artist.where(Sequel.lit("id = 2"))
|
460
472
|
# SELECT * FROM artists WHERE id = 2
|
461
473
|
|
462
474
|
However, if you are using any untrusted input, you should definitely be using placeholders.
|
@@ -464,9 +476,9 @@ In general, unless you are hardcoding values in the strings, you should use plac
|
|
464
476
|
You should never pass a string that has been built using interpolation, unless you are
|
465
477
|
sure of what you are doing.
|
466
478
|
|
467
|
-
Artist.where("id = #{params[:id]}") # Don't do this!
|
468
|
-
Artist.where("id = ?", params[:id]) # Do this instead
|
469
|
-
Artist.where(:
|
479
|
+
Artist.where(Sequel.lit("id = #{params[:id]}")) # Don't do this!
|
480
|
+
Artist.where(Sequel.lit("id = ?", params[:id])) # Do this instead
|
481
|
+
Artist.where(id: params[:id].to_i) # Even better
|
470
482
|
|
471
483
|
=== Inverting
|
472
484
|
|
@@ -474,53 +486,53 @@ You may be wondering how to specify a not equals condition in Sequel, or the NOT
|
|
474
486
|
operator. Sequel has generic support for inverting conditions, so to write a not
|
475
487
|
equals condition, you write an equals condition, and invert it:
|
476
488
|
|
477
|
-
Artist.where(:
|
478
|
-
# SELECT * FROM artists WHERE id != 5
|
489
|
+
Artist.where(id: 5).invert
|
490
|
+
# SELECT * FROM artists WHERE (id != 5)
|
479
491
|
|
480
492
|
Note that +invert+ inverts the entire filter:
|
481
493
|
|
482
|
-
Artist.where(:
|
483
|
-
# SELECT * FROM artists WHERE id != 5 OR name <= 'A'
|
494
|
+
Artist.where(id: 5).where{name > 'A'}.invert
|
495
|
+
# SELECT * FROM artists WHERE ((id != 5) OR (name <= 'A'))
|
484
496
|
|
485
497
|
In general, +invert+ is used rarely, since +exclude+ allows you to invert only specific
|
486
498
|
filters:
|
487
499
|
|
488
|
-
Artist.exclude(:
|
489
|
-
# SELECT * FROM artists WHERE id != 5
|
500
|
+
Artist.exclude(id: 5)
|
501
|
+
# SELECT * FROM artists WHERE (id != 5)
|
490
502
|
|
491
|
-
Artist.where(:
|
492
|
-
# SELECT * FROM artists WHERE id = 5 OR name <= 'A'
|
503
|
+
Artist.where(id: 5).exclude{name > 'A'}
|
504
|
+
# SELECT * FROM artists WHERE ((id = 5) OR (name <= 'A')
|
493
505
|
|
494
506
|
So to do a NOT IN with an array:
|
495
507
|
|
496
|
-
Artist.exclude(:
|
497
|
-
# SELECT * FROM artists WHERE id NOT IN (1, 2)
|
508
|
+
Artist.exclude(id: [1, 2])
|
509
|
+
# SELECT * FROM artists WHERE (id NOT IN (1, 2))
|
498
510
|
|
499
511
|
Or to use the NOT LIKE operator:
|
500
512
|
|
501
513
|
Artist.exclude(Sequel.like(:name, '%J%'))
|
502
|
-
# SELECT * FROM artists WHERE name NOT LIKE '%J%' ESCAPE '\'
|
514
|
+
# SELECT * FROM artists WHERE (name NOT LIKE '%J%' ESCAPE '\')
|
503
515
|
|
504
516
|
You can use Sequel.~ to negate expressions:
|
505
517
|
|
506
|
-
Artist.where(Sequel.~(:
|
518
|
+
Artist.where(Sequel.~(id: 5))
|
507
519
|
# SELECT * FROM artists WHERE id != 5
|
508
520
|
|
509
521
|
On Sequel expression objects, you can use ~ to negate them:
|
510
522
|
|
511
523
|
Artist.where(~Sequel.like(:name, '%J%'))
|
512
|
-
# SELECT * FROM artists WHERE name NOT LIKE '%J%' ESCAPE '\'
|
524
|
+
# SELECT * FROM artists WHERE (name NOT LIKE '%J%' ESCAPE '\')
|
513
525
|
|
514
|
-
|
526
|
+
You can use !~ on Sequel expressions to create negated expressions:
|
515
527
|
|
516
|
-
Artist.where{
|
517
|
-
# SELECT * FROM artists WHERE id != 5
|
528
|
+
Artist.where{id !~ 5}
|
529
|
+
# SELECT * FROM artists WHERE (id != 5)
|
518
530
|
|
519
531
|
=== Removing
|
520
532
|
|
521
533
|
To remove all existing filters, use +unfiltered+:
|
522
534
|
|
523
|
-
Artist.where(:
|
535
|
+
Artist.where(id: 1).unfiltered
|
524
536
|
# SELECT * FROM artists
|
525
537
|
|
526
538
|
== Ordering
|
@@ -555,14 +567,17 @@ If you want to add a column to the beginning of the existing order:
|
|
555
567
|
=== Reversing
|
556
568
|
|
557
569
|
Just like you can invert an existing filter, you can reverse an existing
|
558
|
-
order, using +reverse
|
570
|
+
order, using +reverse+ without an order:
|
559
571
|
|
560
572
|
Artist.order(:id).reverse
|
561
573
|
# SELECT FROM artists ORDER BY id DESC
|
562
574
|
|
563
|
-
|
564
|
-
|
565
|
-
|
575
|
+
Alternatively, you can provide reverse with the order:
|
576
|
+
|
577
|
+
Artist.reverse(:id)
|
578
|
+
# SELECT FROM artists ORDER BY id DESC
|
579
|
+
|
580
|
+
To specify a single entry be reversed, <tt>Sequel.desc</tt> can be used:
|
566
581
|
|
567
582
|
Artist.order(Sequel.desc(:id))
|
568
583
|
# SELECT FROM artists ORDER BY id DESC
|
@@ -658,7 +673,7 @@ You can also call the +offset+ method separately:
|
|
658
673
|
Either of these would return the 11th through 15th records in the original
|
659
674
|
dataset.
|
660
675
|
|
661
|
-
To remove a limit from a dataset, use +unlimited+:
|
676
|
+
To remove a limit and offset from a dataset, use +unlimited+:
|
662
677
|
|
663
678
|
Artist.limit(5, 10).unlimited
|
664
679
|
# SELECT * FROM artists
|
@@ -709,50 +724,46 @@ filters the results after the grouping has been applied, instead of
|
|
709
724
|
before. One possible use is if you only wanted to return artists
|
710
725
|
who had at least 10 albums:
|
711
726
|
|
712
|
-
Album.group_and_count(:artist_id).having{count
|
727
|
+
Album.group_and_count(:artist_id).having{count.function.* >= 10}
|
713
728
|
# SELECT artist_id, count(*) AS count FROM albums
|
714
|
-
# GROUP BY artist_id HAVING count(*) >= 10
|
729
|
+
# GROUP BY artist_id HAVING (count(*) >= 10)
|
715
730
|
|
716
731
|
Both the WHERE clause and the HAVING clause are removed by +unfiltered+:
|
717
732
|
|
718
|
-
Album.group_and_count(:artist_id).having{count
|
733
|
+
Album.group_and_count(:artist_id).having{count.function.* >= 10}.
|
719
734
|
where(:name.like('A%')).unfiltered
|
720
735
|
# SELECT artist_id, count(*) AS count FROM albums GROUP BY artist_id
|
721
736
|
|
722
737
|
== Joins
|
723
738
|
|
724
|
-
Sequel
|
739
|
+
Sequel has support for many different SQL join types.
|
725
740
|
The underlying method used is +join_table+:
|
726
741
|
|
727
|
-
Album.join_table(:inner, :artists, :
|
742
|
+
Album.join_table(:inner, :artists, id: :artist_id)
|
728
743
|
# SELECT * FROM albums
|
729
|
-
# INNER JOIN artists ON artists.id = albums.artist_id
|
744
|
+
# INNER JOIN artists ON (artists.id = albums.artist_id)
|
730
745
|
|
731
746
|
In most cases, you won't call +join_table+ directly, as Sequel provides
|
732
747
|
shortcuts for all common (and most uncommon) join types. For example
|
733
748
|
+join+ does an inner join:
|
734
749
|
|
735
|
-
Album.join(:artists, :
|
750
|
+
Album.join(:artists, id: :artist_id)
|
736
751
|
# SELECT * FROM albums
|
737
|
-
# INNER JOIN artists ON artists.id = albums.artist_id
|
752
|
+
# INNER JOIN artists ON (artists.id = albums.artist_id))))
|
738
753
|
|
739
754
|
And +left_join+ does a LEFT JOIN:
|
740
755
|
|
741
|
-
Album.left_join(:artists, :
|
756
|
+
Album.left_join(:artists, id: :artist_id)
|
742
757
|
# SELECT * FROM albums
|
743
|
-
# LEFT JOIN artists ON artists.id = albums.artist_id
|
758
|
+
# LEFT JOIN artists ON (artists.id = albums.artist_id)
|
744
759
|
|
745
760
|
=== Table/Dataset to Join
|
746
761
|
|
747
762
|
For all of these specialized join methods, the first argument is
|
748
763
|
generally the name of the table to which you are joining. However, you
|
749
|
-
can also provide a
|
750
|
-
|
751
|
-
Album.join(Artist, :id=>:artist_id)
|
752
|
-
|
753
|
-
Or a dataset, in which case a subselect is used:
|
764
|
+
can also provide a dataset, in which case a subselect is used:
|
754
765
|
|
755
|
-
Album.join(Artist.where{name < 'A'}, :
|
766
|
+
Album.join(Artist.where{name < 'A'}, id: :artist_id)
|
756
767
|
# SELECT * FROM albums
|
757
768
|
# INNER JOIN (SELECT * FROM artists WHERE (name < 'A')) AS t1
|
758
769
|
# ON (t1.id = albums.artist_id)
|
@@ -768,80 +779,78 @@ a few minor exceptions.
|
|
768
779
|
A hash used as the join conditions operates similarly to a filter,
|
769
780
|
except that unqualified symbol keys are automatically qualified
|
770
781
|
with the table from the first argument, and unqualified symbol values
|
771
|
-
are automatically qualified with the
|
772
|
-
|
773
|
-
in Sequel are easy to specify:
|
782
|
+
are automatically qualified with the last table joined (or the first
|
783
|
+
table in the dataset if there hasn't been a previous join):
|
774
784
|
|
775
|
-
Album.join(:artists, :
|
785
|
+
Album.join(:artists, id: :artist_id)
|
776
786
|
# SELECT * FROM albums
|
777
|
-
# INNER JOIN artists ON artists.id = albums.artist_id
|
787
|
+
# INNER JOIN artists ON (artists.id = albums.artist_id)
|
778
788
|
|
779
|
-
Note how the
|
789
|
+
Note how the +id+ symbol is automatically qualified with +artists+,
|
780
790
|
while the +artist_id+ symbol is automatically qualified with +albums+.
|
781
791
|
|
782
792
|
Because Sequel uses the last joined table for implicit qualifications
|
783
793
|
of values, you can do things like:
|
784
794
|
|
785
|
-
Album.join(:artists, :
|
786
|
-
join(:members, :
|
795
|
+
Album.join(:artists, id: :artist_id).
|
796
|
+
join(:members, artist_id: :id)
|
787
797
|
# SELECT * FROM albums
|
788
|
-
# INNER JOIN artists ON artists.id = albums.artist_id
|
789
|
-
# INNER JOIN members ON members.artist_id = artists.id
|
798
|
+
# INNER JOIN artists ON (artists.id = albums.artist_id)
|
799
|
+
# INNER JOIN members ON (members.artist_id = artists.id)
|
790
800
|
|
791
801
|
Note that when joining to the +members+ table, +artist_id+ is qualified
|
792
802
|
with +members+ and +id+ is qualified with +artists+.
|
793
803
|
|
794
804
|
While a good default, implicit qualification is not always correct:
|
795
805
|
|
796
|
-
Album.join(:artists, :
|
797
|
-
join(:tracks, :
|
806
|
+
Album.join(:artists, id: :artist_id).
|
807
|
+
join(:tracks, album_id: :id)
|
798
808
|
# SELECT * FROM albums
|
799
|
-
# INNER JOIN artists ON artists.id = albums.artist_id
|
800
|
-
# INNER JOIN tracks ON tracks.album_id = artists.id
|
809
|
+
# INNER JOIN artists ON (artists.id = albums.artist_id)
|
810
|
+
# INNER JOIN tracks ON (tracks.album_id = artists.id)
|
801
811
|
|
802
812
|
Note here how +id+ is qualified with +artists+ instead of +albums+. This
|
803
813
|
is wrong as the foreign key <tt>tracks.album_id</tt> refers to <tt>albums.id</tt>, not
|
804
814
|
<tt>artists.id</tt>. To fix this, you need to explicitly qualify when joining:
|
805
815
|
|
806
|
-
Album.join(:artists, :
|
807
|
-
join(:tracks, :
|
816
|
+
Album.join(:artists, id: :artist_id).
|
817
|
+
join(:tracks, album_id: Sequel[:albums][:id])
|
808
818
|
# SELECT * FROM albums
|
809
|
-
# INNER JOIN artists ON artists.id = albums.artist_id
|
810
|
-
# INNER JOIN tracks ON tracks.album_id = albums.id
|
819
|
+
# INNER JOIN artists ON (artists.id = albums.artist_id)
|
820
|
+
# INNER JOIN tracks ON (tracks.album_id = albums.id)
|
811
821
|
|
812
822
|
Just like in filters, an array of two element arrays is treated the same
|
813
823
|
as a hash, but allows for duplicate keys:
|
814
824
|
|
815
825
|
Album.join(:artists, [[:id, :artist_id], [:id, 1..5]])
|
816
826
|
# SELECT * FROM albums INNER JOIN artists
|
817
|
-
# ON artists.id = albums.artist_id
|
818
|
-
# AND artists.id >= 1 AND artists.id <= 5
|
827
|
+
# ON ((artists.id = albums.artist_id)
|
828
|
+
# AND (artists.id >= 1) AND (artists.id <= 5))
|
819
829
|
|
820
830
|
And just like in the hash case, unqualified symbol elements in the
|
821
831
|
array are implicitly qualified.
|
822
832
|
|
823
833
|
By default, Sequel only qualifies unqualified symbols in the conditions. However,
|
824
|
-
You can provide an options hash with a <tt
|
834
|
+
You can provide an options hash with a <tt>qualify: :deep</tt> option to do a deep
|
825
835
|
qualification, which can qualify subexpressions. For example, let's say you are doing
|
826
836
|
a JOIN using case insensitive string comparison:
|
827
837
|
|
828
838
|
Album.join(:artists, {Sequel.function(:lower, :name) =>
|
829
839
|
Sequel.function(:lower, :artist_name)},
|
830
|
-
:
|
840
|
+
qualify: :deep)
|
831
841
|
# SELECT * FROM albums INNER JOIN artists
|
832
842
|
# ON (lower(artists.name) = lower(albums.artist_name))
|
833
843
|
|
834
|
-
Note how the arguments to lower were qualified correctly in both cases.
|
835
|
-
the <tt>:qualify=>:deep</tt> option is going to become the default.
|
844
|
+
Note how the arguments to lower were qualified correctly in both cases.
|
836
845
|
|
837
846
|
==== USING Joins
|
838
847
|
|
839
848
|
The most common type of join conditions is a JOIN ON, as displayed
|
840
849
|
above. However, the SQL standard allows for join conditions to be
|
841
|
-
specified with JOIN USING,
|
850
|
+
specified with JOIN USING, assuming the column name is the same in
|
851
|
+
both tables.
|
842
852
|
|
843
|
-
|
844
|
-
names in both tables. For example, if instead of having a primary
|
853
|
+
For example, if instead of having a primary
|
845
854
|
column named +id+ in all of your tables, you use +artist_id+ in your
|
846
855
|
+artists+ table and +album_id+ in your +albums+ table, you could do:
|
847
856
|
|
@@ -852,7 +861,7 @@ See here how you specify the USING columns as an array of symbols.
|
|
852
861
|
|
853
862
|
==== NATURAL Joins
|
854
863
|
|
855
|
-
NATURAL
|
864
|
+
NATURAL joins take it one step further than USING joins, by assuming
|
856
865
|
that all columns with the same names in both tables should be
|
857
866
|
used for joining:
|
858
867
|
|
@@ -875,12 +884,12 @@ being used. For example, lets say you wanted to join the albums
|
|
875
884
|
and artists tables, but only want albums where the artist's name
|
876
885
|
comes before the album's name.
|
877
886
|
|
878
|
-
Album.join(:artists, :
|
887
|
+
Album.join(:artists, id: :artist_id) do |j, lj, js|
|
879
888
|
Sequel[j][:name] < Sequel[lj][:name]
|
880
889
|
end
|
881
890
|
# SELECT * FROM albums INNER JOIN artists
|
882
|
-
# ON artists.id = albums.artist_id
|
883
|
-
# AND artists.name < albums.name
|
891
|
+
# ON ((artists.id = albums.artist_id)
|
892
|
+
# AND (artists.name < albums.name))
|
884
893
|
|
885
894
|
Because greater than can't be expressed with a hash in Sequel, you
|
886
895
|
need to use a block and qualify the tables manually.
|
@@ -905,7 +914,7 @@ Using multiple FROM tables and setting conditions in the WHERE clause is
|
|
905
914
|
an old-school way of joining tables:
|
906
915
|
|
907
916
|
DB.from(:albums, :artists).where{{artists[:id]=>albums[:artist_id]}}
|
908
|
-
# SELECT * FROM albums, artists WHERE artists.id = albums.artist_id
|
917
|
+
# SELECT * FROM albums, artists WHERE (artists.id = albums.artist_id)
|
909
918
|
|
910
919
|
=== Using the current dataset in a subselect
|
911
920
|
|
@@ -916,7 +925,7 @@ Here's an example using +from_self+:
|
|
916
925
|
# SELECT * FROM (SELECT * FROM albums ORDER BY artist_id LIMIT 100)
|
917
926
|
# AS t1 GROUP BY artist_id
|
918
927
|
|
919
|
-
This is
|
928
|
+
This is different than without +from_self+:
|
920
929
|
|
921
930
|
Album.order(:artist_id).limit(100).group(:artist_id)
|
922
931
|
# SELECT * FROM albums GROUP BY artist_id ORDER BY name LIMIT 100
|
@@ -937,8 +946,8 @@ current transaction commits. You just use the +for_update+ dataset
|
|
937
946
|
method when returning the rows:
|
938
947
|
|
939
948
|
DB.transaction do
|
940
|
-
album = Album.for_update.first(:
|
941
|
-
# SELECT * FROM albums WHERE id = 1 FOR UPDATE
|
949
|
+
album = Album.for_update.first(id: 1)
|
950
|
+
# SELECT * FROM albums WHERE (id = 1) FOR UPDATE
|
942
951
|
album.num_tracks += 1
|
943
952
|
album.save
|
944
953
|
end
|
@@ -956,7 +965,7 @@ stateless nature.
|
|
956
965
|
|
957
966
|
== Custom SQL
|
958
967
|
|
959
|
-
Sequel makes it easy to use custom SQL by providing it to the <tt>Database#[]</tt>
|
968
|
+
Sequel makes it easy to use custom SQL for the query by providing it to the <tt>Database#[]</tt>
|
960
969
|
method as a string:
|
961
970
|
|
962
971
|
DB["SELECT * FROM artists"]
|
@@ -973,7 +982,7 @@ With either of these methods, you can use placeholders:
|
|
973
982
|
DB["SELECT * FROM artists WHERE id = ?", 5]
|
974
983
|
# SELECT * FROM artists WHERE id = 5
|
975
984
|
|
976
|
-
DB[:albums].with_sql("SELECT * FROM artists WHERE id = :id", :
|
985
|
+
DB[:albums].with_sql("SELECT * FROM artists WHERE id = :id", id: 5)
|
977
986
|
# SELECT * FROM artists WHERE id = 5
|
978
987
|
|
979
988
|
Note that if you specify the dataset using custom SQL, you can still call the dataset
|
@@ -982,6 +991,13 @@ modification methods, but in many cases they will appear to have no affect:
|
|
982
991
|
DB["SELECT * FROM artists"].select(:name).order(:id)
|
983
992
|
# SELECT * FROM artists
|
984
993
|
|
994
|
+
You can use the implicit_subquery extension to automatically wrap queries that use
|
995
|
+
custom SQL in subqueries if a method is called that would modify the SQL:
|
996
|
+
|
997
|
+
DB.extension :implicit_subquery
|
998
|
+
DB["SELECT * FROM artists"].select(:name).order(:id)
|
999
|
+
# SELECT name FROM (SELECT * FROM artists) AS t1 ORDER BY id"
|
1000
|
+
|
985
1001
|
If you must drop down to using custom SQL, it's recommended that you only do so for
|
986
1002
|
specific parts of a query. For example, if the reason you are using custom SQL is
|
987
1003
|
to use a custom operator in the database in the SELECT clause:
|
@@ -1003,12 +1019,12 @@ If you just want to know whether the current dataset would return any rows, use
|
|
1003
1019
|
# SELECT 1 FROM albums LIMIT 1
|
1004
1020
|
# => false
|
1005
1021
|
|
1006
|
-
Album.where(:
|
1007
|
-
# SELECT 1 FROM albums WHERE id = 0 LIMIT 1
|
1022
|
+
Album.where(id: 0).empty?
|
1023
|
+
# SELECT 1 FROM albums WHERE (id = 0) LIMIT 1
|
1008
1024
|
# => true
|
1009
1025
|
|
1010
1026
|
Album.where(Sequel.like(:name, 'R%')).empty?
|
1011
|
-
# SELECT 1 FROM albums WHERE name LIKE 'R%' ESCAPE '\' LIMIT 1
|
1027
|
+
# SELECT 1 FROM albums WHERE (name LIKE 'R%' ESCAPE '\') LIMIT 1
|
1012
1028
|
# => false
|
1013
1029
|
|
1014
1030
|
== Aggregate Calculations
|
@@ -1022,22 +1038,29 @@ for each of these aggregate functions.
|
|
1022
1038
|
Album.count
|
1023
1039
|
# SELECT count(*) AS count FROM albums LIMIT 1
|
1024
1040
|
# => 2
|
1025
|
-
|
1041
|
+
|
1042
|
+
If you pass an expression to count, it will return the number of records where
|
1043
|
+
that expression in not NULL:
|
1044
|
+
|
1045
|
+
Album.count(:artist_id)
|
1046
|
+
# SELECT count(artist_id) AS count FROM albums LIMIT 1
|
1047
|
+
# => 1
|
1048
|
+
|
1026
1049
|
The other methods take a column argument and call the aggregate function with
|
1027
1050
|
the argument:
|
1028
1051
|
|
1029
1052
|
Album.sum(:id)
|
1030
|
-
# SELECT sum(id) FROM albums LIMIT 1
|
1053
|
+
# SELECT sum(id) AS sum FROM albums LIMIT 1
|
1031
1054
|
# => 3
|
1032
1055
|
|
1033
1056
|
Album.avg(:id)
|
1034
|
-
# SELECT avg(id) FROM albums LIMIT 1
|
1057
|
+
# SELECT avg(id) AS avg FROM albums LIMIT 1
|
1035
1058
|
# => 1.5
|
1036
1059
|
|
1037
1060
|
Album.min(:id)
|
1038
|
-
# SELECT min(id) FROM albums LIMIT 1
|
1061
|
+
# SELECT min(id) AS min FROM albums LIMIT 1
|
1039
1062
|
# => 1
|
1040
1063
|
|
1041
1064
|
Album.max(:id)
|
1042
|
-
# SELECT max(id) FROM albums LIMIT 1
|
1065
|
+
# SELECT max(id) AS max FROM albums LIMIT 1
|
1043
1066
|
# => 2
|