sequel 4.49.0 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|