├── spec ├── files │ ├── empty_migration_folder │ │ └── .gitkeep │ ├── empty_migration │ │ ├── 002_create_nodes.rb │ │ ├── 003_3_create_users.rb │ │ └── 001_create_sessions.rb │ ├── bad_up_migration │ │ ├── 002_create_alt_advanced.rb │ │ └── 001_create_alt_basic.rb │ ├── bad_timestamped_migrations │ │ ├── 1273253853_3_create_users.rb │ │ ├── 1273253851_create_nodes.rb │ │ └── 1273253849_create_sessions.rb │ ├── reversible_migrations │ │ ├── 004_reversible.rb │ │ ├── 002_reversible.rb │ │ ├── 003_reversible.rb │ │ ├── 001_reversible.rb │ │ ├── 006_reversible.rb │ │ ├── 005_reversible.rb │ │ └── 007_reversible.rb │ ├── transaction_unspecified_migrations │ │ ├── 002_create_basic.rb │ │ └── 001_create_alt_basic.rb │ ├── bad_down_migration │ │ ├── 001_create_alt_basic.rb │ │ └── 002_create_alt_advanced.rb │ ├── double_migration │ │ ├── 003_3_create_users.rb │ │ ├── 001_create_sessions.rb │ │ └── 002_create_nodes.rb │ ├── integer_migrations │ │ ├── 003_3_create_users.rb │ │ ├── 002_create_nodes.rb │ │ └── 001_create_sessions.rb │ ├── transaction_specified_migrations │ │ ├── 002_create_basic.rb │ │ └── 001_create_alt_basic.rb │ ├── timestamped_migrations │ │ ├── 1273253853_3_create_users.rb │ │ ├── 1273253851_create_nodes.rb │ │ └── 1273253849_create_sessions.rb │ ├── convert_to_timestamp_migrations │ │ ├── 003_3_create_users.rb │ │ ├── 002_create_nodes.rb │ │ ├── 001_create_sessions.rb │ │ ├── 1273253850_create_artists.rb │ │ └── 1273253852_create_albums.rb │ ├── duplicate_integer_migrations │ │ ├── 001_create_alt_basic.rb │ │ └── 001_create_alt_advanced.rb │ ├── missing_integer_migrations │ │ ├── 001_create_alt_basic.rb │ │ └── 003_create_alt_advanced.rb │ ├── duplicate_timestamped_migrations │ │ ├── 1273253853_create_users.rb │ │ ├── 1273253853_create_nodes.rb │ │ └── 1273253849_create_sessions.rb │ ├── missing_timestamped_migrations │ │ ├── 1273253853_3_create_users.rb │ │ └── 1273253849_create_sessions.rb │ ├── uppercase_timestamped_migrations │ │ ├── 1273253853_3_CREATE_USERS.RB │ │ ├── 1273253851_CREATE_NODES.RB │ │ └── 1273253849_CREATE_SESSIONS.RB │ ├── interleaved_timestamped_migrations │ │ ├── 1273253853_3_create_users.rb │ │ ├── 1273253851_create_nodes.rb │ │ ├── 1273253852_create_albums.rb │ │ ├── 1273253849_create_sessions.rb │ │ └── 1273253850_create_artists.rb │ ├── missing_integer_migrations_missing_last_version │ │ └── 001_create_alt_basic.rb │ └── unused_associations │ │ ├── tua.rb │ │ └── run_tua.rb ├── bin_shim ├── core_spec.rb ├── model_spec.rb ├── plugin_spec.rb ├── core_model_spec.rb ├── model_no_assoc_spec.rb ├── adapter_spec.rb ├── spec_config.rb.example ├── core │ ├── version_spec.rb │ └── spec_helper.rb ├── extensions │ ├── error_splitter_spec.rb │ ├── dataset_run_spec.rb │ ├── symbol_as_refinement_spec.rb │ ├── singular_table_names_spec.rb │ ├── skip_create_refresh_spec.rb │ ├── unlimited_update_spec.rb │ ├── exclude_or_null_spec.rb │ ├── stdio_logger_spec.rb │ ├── integer64_spec.rb │ ├── auto_cast_date_and_time_spec.rb │ ├── after_initialize_spec.rb │ ├── symbol_aref_refinement_spec.rb │ ├── columns_updated_spec.rb │ ├── constant_sql_override_spec.rb │ ├── freeze_datasets_spec.rb │ ├── pg_loose_count_spec.rb │ ├── validation_contexts_spec.rb │ ├── current_datetime_timestamp_spec.rb │ ├── pg_timestamptz_spec.rb │ ├── auto_restrict_eager_graph_spec.rb │ ├── error_sql_spec.rb │ ├── looser_typecasting_spec.rb │ ├── select_remove_spec.rb │ ├── pg_extended_integer_support_spec.rb │ ├── accessed_columns_spec.rb │ ├── async_thread_pool_plugin_spec.rb │ ├── single_statement_dataset_destroy_spec.rb │ ├── split_array_nil_spec.rb │ ├── boolean_subsets_spec.rb │ └── inverted_subsets_spec.rb ├── visibility_checking.rb ├── model │ ├── exceptions_spec.rb │ └── spec_helper.rb ├── deprecation_helper.rb ├── async_spec_helper.rb ├── sequel_coverage.rb └── visibility_checking_after_hook.rb ├── lib ├── sequel.rb └── sequel │ ├── adapters │ ├── postgresql.rb │ ├── odbc │ │ ├── db2.rb │ │ └── oracle.rb │ ├── utils │ │ ├── columns_limit_1.rb │ │ ├── unmodified_identifiers.rb │ │ ├── replace.rb │ │ └── split_alter_table.rb │ └── jdbc │ │ ├── mssql.rb │ │ └── jtds.rb │ ├── extensions │ ├── from_block.rb │ ├── freeze_datasets.rb │ ├── no_auto_literal_strings.rb │ ├── _model_constraint_validations.rb │ ├── symbol_as.rb │ ├── sql_expr.rb │ ├── fiber_concurrency.rb │ ├── _model_pg_row.rb │ ├── symbol_as_refinement.rb │ ├── pretty_table.rb │ ├── integer64.rb │ ├── blank.rb │ ├── pg_loose_count.rb │ ├── symbol_aref_refinement.rb │ ├── set_literalizer.rb │ ├── dataset_run.rb │ ├── any_not_empty.rb │ ├── looser_typecasting.rb │ ├── virtual_row_method_block.rb │ ├── symbol_aref.rb │ ├── datetime_parse_to_time.rb │ ├── empty_array_consider_nulls.rb │ ├── string_date_time.rb │ ├── s.rb │ ├── stdio_logger.rb │ └── round_timestamps.rb │ ├── plugins │ ├── before_after_save.rb │ ├── unlimited_update.rb │ ├── after_initialize.rb │ ├── singular_table_names.rb │ ├── skip_create_refresh.rb │ ├── inspect_pk.rb │ ├── columns_updated.rb │ ├── empty_failure_backtraces.rb │ └── async_thread_pool.rb │ ├── version.rb │ ├── model │ └── dataset_module.rb │ ├── connection_pool │ └── single.rb │ ├── database.rb │ └── dataset │ ├── deprecated_singleton_class_methods.rb │ └── dataset_module.rb ├── www ├── public │ ├── images │ │ ├── favicon.ico │ │ ├── sequel-button.png │ │ └── transparent-background.svg │ └── css │ │ ├── development.css │ │ ├── documentation.css │ │ ├── plugins.css │ │ └── links.css └── make_www.rb ├── .gitignore ├── .github └── ISSUE_TEMPLATE │ └── config.yml ├── doc ├── release_notes │ ├── 5.48.0.txt │ ├── 5.28.0.txt │ ├── 5.30.0.txt │ ├── 5.39.0.txt │ ├── 5.27.0.txt │ ├── 5.60.0.txt │ ├── 5.71.0.txt │ ├── 5.55.0.txt │ ├── 5.65.0.txt │ ├── 5.29.0.txt │ ├── 5.33.0.txt │ ├── 5.69.0.txt │ ├── 5.13.0.txt │ ├── 5.66.0.txt │ ├── 5.19.0.txt │ ├── 5.57.0.txt │ ├── 5.53.0.txt │ ├── 5.41.0.txt │ ├── 5.1.0.txt │ ├── 4.6.0.txt │ ├── 5.92.0.txt │ ├── 5.79.0.txt │ ├── 5.17.0.txt │ ├── 5.38.0.txt │ ├── 5.25.0.txt │ ├── 5.54.0.txt │ ├── 5.97.0.txt │ ├── 5.81.0.txt │ ├── 5.58.0.txt │ ├── 4.5.0.txt │ ├── 5.67.0.txt │ ├── 5.84.0.txt │ ├── 5.37.0.txt │ ├── 5.45.0.txt │ ├── 4.16.0.txt │ ├── 5.6.0.txt │ ├── 5.2.0.txt │ ├── 4.3.0.txt │ ├── 4.30.0.txt │ ├── 5.87.0.txt │ ├── 5.44.0.txt │ ├── 1.0.txt │ ├── 5.74.0.txt │ ├── 4.18.0.txt │ ├── 5.26.0.txt │ ├── 4.29.0.txt │ ├── 5.40.0.txt │ ├── 3.22.0.txt │ ├── 5.63.0.txt │ ├── 3.20.0.txt │ ├── 5.96.0.txt │ ├── 5.72.0.txt │ ├── 4.17.0.txt │ ├── 5.34.0.txt │ ├── 5.70.0.txt │ ├── 5.75.0.txt │ ├── 5.80.0.txt │ └── 5.22.0.txt ├── mssql_stored_procedures.rdoc └── thread_safety.rdoc ├── MIT-LICENSE └── sequel.gemspec /spec/files/empty_migration_folder/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/files/empty_migration/002_create_nodes.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/bin_shim: -------------------------------------------------------------------------------- 1 | load(File.join(Dir.pwd, 'bin/sequel')) 2 | -------------------------------------------------------------------------------- /spec/core_spec.rb: -------------------------------------------------------------------------------- 1 | Dir['./spec/core/*_spec.rb'].each{|f| require f} 2 | -------------------------------------------------------------------------------- /spec/model_spec.rb: -------------------------------------------------------------------------------- 1 | Dir['./spec/model/*_spec.rb'].each{|f| require f} 2 | -------------------------------------------------------------------------------- /spec/plugin_spec.rb: -------------------------------------------------------------------------------- 1 | Dir['./spec/extensions/*_spec.rb'].each{|f| require f} 2 | -------------------------------------------------------------------------------- /lib/sequel.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | require_relative 'sequel/model' 4 | -------------------------------------------------------------------------------- /spec/core_model_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'core_spec' 2 | require_relative 'model_spec' 3 | -------------------------------------------------------------------------------- /lib/sequel/adapters/postgresql.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | require_relative 'postgres' 4 | -------------------------------------------------------------------------------- /spec/files/bad_up_migration/002_create_alt_advanced.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{get(:asdfassfd)} 3 | end 4 | -------------------------------------------------------------------------------- /www/public/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyevans/sequel/HEAD/www/public/images/favicon.ico -------------------------------------------------------------------------------- /spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{get(:asdfsadfas)} 3 | end 4 | -------------------------------------------------------------------------------- /www/public/images/sequel-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyevans/sequel/HEAD/www/public/images/sequel-button.png -------------------------------------------------------------------------------- /lib/sequel/extensions/from_block.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | Sequel::Database.register_extension(:from_block){} 4 | -------------------------------------------------------------------------------- /spec/model_no_assoc_spec.rb: -------------------------------------------------------------------------------- 1 | Dir['./spec/model/*_spec.rb'].delete_if{|f| f =~ /association|eager_loading/}.each{|f| require f} 2 | -------------------------------------------------------------------------------- /spec/files/reversible_migrations/004_reversible.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | change do 3 | rename_table :a, :b 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/sequel/extensions/freeze_datasets.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | Sequel::Database.register_extension(:freeze_datasets){} 4 | -------------------------------------------------------------------------------- /spec/files/reversible_migrations/002_reversible.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | change do 3 | add_column :a, :b, String 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/files/reversible_migrations/003_reversible.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | change do 3 | rename_column :a, :b, :c 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/files/reversible_migrations/001_reversible.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | change do 3 | create_table(:a){Integer :a} 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/files/transaction_unspecified_migrations/002_create_basic.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | change{create_table(:sm){Integer :smc1}} 3 | end 4 | -------------------------------------------------------------------------------- /spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | change{create_table(:sm11111){Integer :smc1}} 3 | end 4 | -------------------------------------------------------------------------------- /spec/files/bad_down_migration/001_create_alt_basic.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{create_table(:sm11111){Integer :smc1}} 3 | down{get(:asdfsadfsa)} 4 | end 5 | -------------------------------------------------------------------------------- /spec/files/double_migration/003_3_create_users.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{create_table(:sm3333){Integer :smc3}} 3 | down{drop_table(:sm3333)} 4 | end 5 | -------------------------------------------------------------------------------- /spec/files/empty_migration/003_3_create_users.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{create_table(:sm3333){Integer :smc3}} 3 | down{drop_table(:sm3333)} 4 | end 5 | -------------------------------------------------------------------------------- /spec/files/integer_migrations/003_3_create_users.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{create_table(:sm3333){Integer :smc3}} 3 | down{drop_table(:sm3333)} 4 | end 5 | -------------------------------------------------------------------------------- /spec/files/transaction_specified_migrations/002_create_basic.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | no_transaction 3 | change{create_table(:sm){Integer :smc1}} 4 | end 5 | -------------------------------------------------------------------------------- /www/public/css/development.css: -------------------------------------------------------------------------------- 1 | section.development { 2 | background-color: #FFF; 3 | border-radius: 12px; 4 | box-shadow: 0 24px 64px rgba(0,0,0,.15); 5 | } 6 | -------------------------------------------------------------------------------- /spec/files/bad_up_migration/001_create_alt_basic.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{create_table(:sm11111){Integer :smc1}} 3 | down{drop_table(:sm11111)} 4 | end 5 | -------------------------------------------------------------------------------- /spec/files/bad_down_migration/002_create_alt_advanced.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{create_table(:sm22222){Integer :smc2}} 3 | down{drop_table(:sm22222)} 4 | end 5 | -------------------------------------------------------------------------------- /spec/files/timestamped_migrations/1273253853_3_create_users.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{create_table(:sm3333){Integer :smc3}} 3 | down{drop_table(:sm3333)} 4 | end 5 | -------------------------------------------------------------------------------- /spec/files/transaction_specified_migrations/001_create_alt_basic.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | transaction 3 | change{create_table(:sm11111){Integer :smc1}} 4 | end 5 | -------------------------------------------------------------------------------- /lib/sequel/plugins/before_after_save.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | module Plugins 5 | module BeforeAfterSave 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/files/convert_to_timestamp_migrations/003_3_create_users.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{create_table(:sm3333){Integer :smc3}} 3 | down{drop_table(:sm3333)} 4 | end 5 | -------------------------------------------------------------------------------- /spec/files/duplicate_integer_migrations/001_create_alt_basic.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{create_table(:sm11111){Integer :smc1}} 3 | down{drop_table(:sm11111)} 4 | end 5 | -------------------------------------------------------------------------------- /spec/files/missing_integer_migrations/001_create_alt_basic.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{create_table(:sm11111){Integer :smc1}} 3 | down{drop_table(:sm11111)} 4 | end 5 | -------------------------------------------------------------------------------- /spec/files/missing_integer_migrations/003_create_alt_advanced.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{create_table(:sm33333){Integer :smc3}} 3 | down{drop_table(:sm33333)} 4 | end 5 | -------------------------------------------------------------------------------- /spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{create_table(:sm33333){Integer :smc3}} 3 | down{drop_table(:sm33333)} 4 | end 5 | -------------------------------------------------------------------------------- /spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{create_table(:sm3333){Integer :smc3}} 3 | down{drop_table(:sm3333)} 4 | end 5 | -------------------------------------------------------------------------------- /spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{create_table(:sm3333){Integer :smc3}} 3 | down{drop_table(:sm3333)} 4 | end 5 | -------------------------------------------------------------------------------- /spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{create_table(:sm3333){Integer :smc3}} 3 | down{drop_table(:sm3333)} 4 | end 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.lock 2 | *.rbc 3 | *.swp 4 | /coverage 5 | /rdoc 6 | /sequel-*.gem 7 | /spec/bin-sequel-* 8 | /spec/spec_config.rb 9 | /www/public/*.html 10 | /www/public/rdoc* 11 | -------------------------------------------------------------------------------- /spec/adapter_spec.rb: -------------------------------------------------------------------------------- 1 | if !ARGV.empty? && ARGV.first != 'none' 2 | require_relative "adapters/#{ARGV.first}_spec" 3 | end 4 | Dir['./spec/integration/*_test.rb'].each{|f| require f} 5 | -------------------------------------------------------------------------------- /spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{create_table(:sm3333){Integer :smc3}} 3 | down{drop_table(:sm3333)} 4 | end 5 | -------------------------------------------------------------------------------- /spec/spec_config.rb.example: -------------------------------------------------------------------------------- 1 | # Custom setup for the adapter/integration specs 2 | # ENV['SEQUEL_INTEGRATION_URL'] = 'sqlite:/' 3 | # ENV['SEQUEL_POSTGRES_URL'] = 'postgres://localhost/test' 4 | -------------------------------------------------------------------------------- /spec/files/missing_integer_migrations_missing_last_version/001_create_alt_basic.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | up{create_table(:sm11111){Integer :smc1}} 3 | down{drop_table(:sm11111)} 4 | end 5 | -------------------------------------------------------------------------------- /lib/sequel/extensions/no_auto_literal_strings.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | Sequel::Database.register_extension(:no_auto_literal_strings){} 4 | Sequel::Dataset.register_extension(:no_auto_literal_strings){} 5 | -------------------------------------------------------------------------------- /spec/files/integer_migrations/002_create_nodes.rb: -------------------------------------------------------------------------------- 1 | Class.new(Sequel::Migration) do 2 | def up 3 | create_table(:sm2222){Integer :smc2} 4 | end 5 | 6 | def down 7 | drop_table(:sm2222) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/double_migration/001_create_sessions.rb: -------------------------------------------------------------------------------- 1 | class CreateSessions < Sequel::Migration 2 | def up 3 | create_table(:sm1111){Integer :smc1} 4 | end 5 | 6 | def down 7 | drop_table(:sm1111) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/empty_migration/001_create_sessions.rb: -------------------------------------------------------------------------------- 1 | class CreateSessions < Sequel::Migration 2 | def up 3 | create_table(:sm1111){Integer :smc1} 4 | end 5 | 6 | def down 7 | drop_table(:sm1111) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/integer_migrations/001_create_sessions.rb: -------------------------------------------------------------------------------- 1 | class CreateSessions < Sequel::Migration 2 | def up 3 | create_table(:sm1111){Integer :smc1} 4 | end 5 | 6 | def down 7 | drop_table(:sm1111) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb: -------------------------------------------------------------------------------- 1 | Class.new(Sequel::Migration) do 2 | def up 3 | create_table(:sm2222){Integer :smc2} 4 | end 5 | 6 | def down 7 | drop_table(:sm2222) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/convert_to_timestamp_migrations/002_create_nodes.rb: -------------------------------------------------------------------------------- 1 | Class.new(Sequel::Migration) do 2 | def up 3 | create_table(:sm2222){Integer :smc2} 4 | end 5 | 6 | def down 7 | drop_table(:sm2222) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/timestamped_migrations/1273253851_create_nodes.rb: -------------------------------------------------------------------------------- 1 | Class.new(Sequel::Migration) do 2 | def up 3 | create_table(:sm2222){Integer :smc2} 4 | end 5 | 6 | def down 7 | drop_table(:sm2222) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb: -------------------------------------------------------------------------------- 1 | class CreateSessions < Sequel::Migration 2 | def up 3 | create_table(:sm1111){Integer :smc1} 4 | end 5 | 6 | def down 7 | get(:asdfsadfas) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/convert_to_timestamp_migrations/001_create_sessions.rb: -------------------------------------------------------------------------------- 1 | class CreateSessions < Sequel::Migration 2 | def up 3 | create_table(:sm1111){Integer :smc1} 4 | end 5 | 6 | def down 7 | drop_table(:sm1111) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb: -------------------------------------------------------------------------------- 1 | Class.new(Sequel::Migration) do 2 | def up 3 | create_table(:sm2222){Integer :smc2} 4 | end 5 | 6 | def down 7 | drop_table(:sm2222) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb: -------------------------------------------------------------------------------- 1 | Class.new(Sequel::Migration) do 2 | def up 3 | create_table(:sm2222){Integer :smc2} 4 | end 5 | 6 | def down 7 | drop_table(:sm2222) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/reversible_migrations/006_reversible.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | change do 3 | create_table(:c) do 4 | primary_key :id 5 | end 6 | alter_table(:b) do 7 | add_foreign_key :f, :c 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/files/timestamped_migrations/1273253849_create_sessions.rb: -------------------------------------------------------------------------------- 1 | class CreateSessions < Sequel::Migration 2 | def up 3 | create_table(:sm1111){Integer :smc1} 4 | end 5 | 6 | def down 7 | drop_table(:sm1111) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB: -------------------------------------------------------------------------------- 1 | Class.new(Sequel::Migration) do 2 | def up 3 | create_table(:sm2222){Integer :smc2} 4 | end 5 | 6 | def down 7 | drop_table(:sm2222) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb: -------------------------------------------------------------------------------- 1 | class CreateArtists < Sequel::Migration 2 | def up 3 | create_table(:sm1122){Integer :smc12} 4 | end 5 | 6 | def down 7 | drop_table(:sm1122) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb: -------------------------------------------------------------------------------- 1 | class CreateAlbums < Sequel::Migration 2 | def up 3 | create_table(:sm2233){Integer :smc23} 4 | end 5 | 6 | def down 7 | drop_table(:sm2233) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb: -------------------------------------------------------------------------------- 1 | class CreateAlbums < Sequel::Migration 2 | def up 3 | create_table(:sm2233){Integer :smc23} 4 | end 5 | 6 | def down 7 | drop_table(:sm2233) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb: -------------------------------------------------------------------------------- 1 | class CreateSessions < Sequel::Migration 2 | def up 3 | create_table(:sm1111){Integer :smc1} 4 | end 5 | 6 | def down 7 | drop_table(:sm1111) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/reversible_migrations/005_reversible.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | change do 3 | alter_table(:b) do 4 | add_column :d, String 5 | end 6 | alter_table(:b) do 7 | rename_column :d, :e 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb: -------------------------------------------------------------------------------- 1 | class CreateSessions < Sequel::Migration 2 | def up 3 | create_table(:sm1111){Integer :smc1} 4 | end 5 | 6 | def down 7 | drop_table(:sm1111) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb: -------------------------------------------------------------------------------- 1 | class CreateSessions < Sequel::Migration 2 | def up 3 | create_table(:sm1111){Integer :smc1} 4 | end 5 | 6 | def down 7 | drop_table(:sm1111) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb: -------------------------------------------------------------------------------- 1 | class CreateArtists < Sequel::Migration 2 | def up 3 | create_table(:sm1122){Integer :smc12} 4 | end 5 | 6 | def down 7 | drop_table(:sm1122) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB: -------------------------------------------------------------------------------- 1 | class CreateSessions < Sequel::Migration 2 | def up 3 | create_table(:sm1111){Integer :smc1} 4 | end 5 | 6 | def down 7 | drop_table(:sm1111) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/files/reversible_migrations/007_reversible.rb: -------------------------------------------------------------------------------- 1 | Sequel.migration do 2 | change do 3 | create_table(:d) do 4 | primary_key :id 5 | end 6 | alter_table(:b) do 7 | add_foreign_key :g, :d, :foreign_key_constraint_name=>:b_f_foo 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /www/public/images/transparent-background.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Questions or Feature Requests 4 | url: https://github.com/jeremyevans/sequel/discussions/new?category=general 5 | about: If you aren't sure what you are reporting is a bug in Sequel, open a discussion post instead of an issue. 6 | -------------------------------------------------------------------------------- /lib/sequel/adapters/odbc/db2.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | require_relative '../shared/db2' 4 | 5 | Sequel.synchronize do 6 | Sequel::ODBC::DATABASE_SETUP[:db2] = proc do |db| 7 | db.extend ::Sequel::DB2::DatabaseMethods 8 | db.extend_datasets ::Sequel::DB2::DatasetMethods 9 | end 10 | end 11 | 12 | -------------------------------------------------------------------------------- /lib/sequel/adapters/odbc/oracle.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | require_relative '../shared/oracle' 4 | 5 | Sequel.synchronize do 6 | Sequel::ODBC::DATABASE_SETUP[:oracle] = proc do |db| 7 | db.extend ::Sequel::Oracle::DatabaseMethods 8 | db.extend_datasets ::Sequel::Oracle::DatasetMethods 9 | end 10 | end 11 | 12 | -------------------------------------------------------------------------------- /spec/files/double_migration/002_create_nodes.rb: -------------------------------------------------------------------------------- 1 | Class.new(Sequel::Migration) do 2 | def up 3 | create_table(:sm2222){Integer :smc2} 4 | end 5 | 6 | def down 7 | drop_table(:sm2222) 8 | end 9 | end 10 | 11 | Class.new(Sequel::Migration) do 12 | def up 13 | create_table(:sm2443){Integer :smc2} 14 | end 15 | 16 | def down 17 | drop_table(:sm2443) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /www/public/css/documentation.css: -------------------------------------------------------------------------------- 1 | section.documentation { 2 | background-color: #FFF; 3 | border-radius: 12px; 4 | box-shadow: 0 24px 64px rgba(0,0,0,.15); 5 | } 6 | 7 | ul.release-notes { 8 | font-family: var(--font-monospace); 9 | display: flex; 10 | flex-direction: column; 11 | gap: 8px; 12 | font-size: 14px; 13 | color: #ccc; 14 | list-style: none; 15 | margin-top: 8px; 16 | margin-bottom: 16px; 17 | padding: 0; 18 | } 19 | 20 | ul.release-notes__li { 21 | list-style: none; 22 | } 23 | -------------------------------------------------------------------------------- /spec/core/version_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "Sequel.version" do 4 | it "should be in the form X.Y.Z with all being numbers" do 5 | Sequel.version.must_match(/\A\d+\.\d+\.\d+\z/) 6 | end 7 | 8 | it "MAJOR/MINOR/TINY/VERSION_NUMBER should be integers" do 9 | Sequel::MAJOR.must_be_kind_of(Integer) 10 | Sequel::MINOR.must_be_kind_of(Integer) 11 | Sequel::TINY.must_be_kind_of(Integer) 12 | Sequel::VERSION_NUMBER.must_be_kind_of(Integer) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/extensions/error_splitter_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "Sequel::Plugins::ErrorSplitter" do 4 | before do 5 | @c = Class.new(Sequel::Model) 6 | @c.plugin :error_splitter 7 | @m = @c.new 8 | def @m.validate 9 | errors.add([:a, :b], 'is bad') 10 | end 11 | end 12 | 13 | it "should split errors for multiple columns and assign them to each column" do 14 | @m.valid?.must_equal false 15 | @m.errors.must_equal(:a=>['is bad'], :b=>['is bad']) 16 | end 17 | end 18 | 19 | -------------------------------------------------------------------------------- /www/make_www.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'erb' 3 | $: << File.join(File.dirname(__FILE__), '..','lib', 'sequel') 4 | require 'version' 5 | Dir.chdir(File.dirname(__FILE__)) 6 | erb = ERB.new(File.read('layout.html.erb')) 7 | Dir['pages/*.html.erb'].each do |page| 8 | public_loc = "#{page.gsub(/\Apages\//, 'public/').sub('.erb', '')}" 9 | content = content = ERB.new(File.read(page)).result(binding) 10 | title = title = File.basename(page.sub('.html.erb', '')) 11 | File.open(public_loc, 'wb'){|f| f.write(erb.result(binding))} 12 | end 13 | -------------------------------------------------------------------------------- /spec/extensions/dataset_run_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "dataset_run extension" do 4 | it "#run should run the SQL on the database" do 5 | db = Sequel.mock 6 | db["SQL with ?", "placeholder"].extension(:dataset_run).run.must_be_nil 7 | db.sqls.must_equal ["SQL with 'placeholder'"] 8 | end 9 | 10 | it "#run should respect current server" do 11 | db = Sequel.mock(:servers=>{:a=>{}}) 12 | db["SQL with ?", "placeholder"].extension(:dataset_run).server(:a).run.must_be_nil 13 | db.sqls.must_equal ["SQL with 'placeholder' -- a"] 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /doc/release_notes/5.48.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A Sequel::Database#like_without_collate accessor has been added on 4 | Microsoft SQL Server, which avoids using the COLLATE clause for 5 | LIKE expressions. This can speed up query performance significantly. 6 | 7 | * A private Sequel::Model::Errors#full_message method has been added 8 | to make it easier to support internationalization for Sequel::Model 9 | error messages. 10 | 11 | = Other Improvements 12 | 13 | * The association reflection tracking in the unused_associations 14 | plugin now works correctly when combining coverage runs. 15 | -------------------------------------------------------------------------------- /lib/sequel/extensions/_model_constraint_validations.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | module Plugins 5 | module ConstraintValidations 6 | module DatabaseMethods 7 | # A hash of validation method call metadata for all tables in the database. 8 | # The hash is keyed by table name string and contains arrays of validation 9 | # method call arrays. 10 | attr_accessor :constraint_validations 11 | end 12 | end 13 | end 14 | 15 | Database.register_extension(:_model_constraint_validations, Plugins::ConstraintValidations::DatabaseMethods) 16 | end 17 | -------------------------------------------------------------------------------- /lib/sequel/extensions/symbol_as.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # The symbol_as extension adds Symbol#as, for creating 4 | # Sequel::SQL::AliasedExpression objects. It's 5 | # designed as a shortcut so that instead of: 6 | # 7 | # Sequel[:column].as(:alias) 8 | # 9 | # you can just write: 10 | # 11 | # :column.as(:alias) 12 | # 13 | # To load the extension: 14 | # 15 | # Sequel.extension :symbol_as 16 | # 17 | # If you are using Ruby 2+, and you would like to use refinements, there 18 | # is a refinement version of this in the symbol_as_refinement extension. 19 | 20 | # 21 | class Symbol 22 | include Sequel::SQL::AliasMethods 23 | end 24 | -------------------------------------------------------------------------------- /spec/extensions/symbol_as_refinement_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | if (RUBY_VERSION >= '2.0.0' && RUBY_ENGINE == 'ruby') || (RUBY_VERSION >= '2.3.0' && RUBY_ENGINE == 'jruby') 4 | Sequel.extension :symbol_as_refinement 5 | using Sequel::SymbolAs 6 | 7 | describe "symbol_as_refinement extension" do 8 | before do 9 | @db = Sequel.mock 10 | end 11 | 12 | it "Symbol#as should create aliased expression" do 13 | @db.literal(:x.as(:y)).must_equal "x AS y" 14 | end 15 | 16 | it "Symbol#as should create aliased expression with columns" do 17 | @db.literal(:x.as(:y, [:c1, :c2])).must_equal "x AS y(c1, c2)" 18 | end 19 | end 20 | end 21 | 22 | -------------------------------------------------------------------------------- /doc/release_notes/5.28.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An any_not_empty extension has been added, for making Dataset#any? 4 | without a block be the same as !empty?. This can result in a 5 | much faster database query. 6 | 7 | * An exclude_or_null extension has been added, adding a 8 | Dataset#exclude_or_null method that returns rows where the given 9 | expression is false or NULL. This extension is supported on 10 | PostgreSQL, SQLite, MySQL, H2, and HSQLDB. 11 | 12 | = Other Improvements 13 | 14 | * When using the jdbc/postgresql adapter, calling with_fetch_size 15 | on a dataset will emit a warning. This is because the driver 16 | will ignore the setting. 17 | -------------------------------------------------------------------------------- /spec/extensions/singular_table_names_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "singular_table_names plugin" do 4 | before do 5 | @c = Class.new(Sequel::Model) 6 | @c.plugin :singular_table_names 7 | end 8 | after do 9 | Object.send(:remove_const, :Foo) 10 | end 11 | 12 | it "should use the singular form of model name for table name" do 13 | class ::Foo < @c; end 14 | Foo.table_name.must_equal :foo 15 | end 16 | 17 | it "should handle namespaced models using single form of last component of model name" do 18 | module ::Foo; end 19 | class Foo::Bar < @c; end 20 | Foo::Bar.table_name.must_equal :bar 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/extensions/skip_create_refresh_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "Sequel::Plugins::SkipCreateRefresh" do 4 | it "should skip the refresh after saving a new object" do 5 | c = Class.new(Sequel::Model(:a)) 6 | c.columns :id, :x 7 | c.dataset = c.dataset.with_autoid(2) 8 | c.db.reset 9 | c.create(:x=>1) 10 | c.db.sqls.must_equal ['INSERT INTO a (x) VALUES (1)', 'SELECT * FROM a WHERE id = 2'] 11 | 12 | c.dataset = c.dataset.with_autoid(2) 13 | c.plugin :skip_create_refresh 14 | c.db.reset 15 | c.create(:x=>3).values.must_equal(:id=>2, :x=>3) 16 | c.db.sqls.must_equal ['INSERT INTO a (x) VALUES (3)'] 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/sequel/adapters/utils/columns_limit_1.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | class Dataset 5 | module ColumnsLimit1 6 | COLUMNS_CLONE_OPTIONS = {:distinct => nil, :limit => 1, :offset=>nil, :where=>nil, :having=>nil, :order=>nil, :row_proc=>nil, :graph=>nil, :eager_graph=>nil}.freeze 7 | 8 | # Use a limit of 1 instead of a limit of 0 when 9 | # getting the columns. 10 | def columns! 11 | ds = clone(COLUMNS_CLONE_OPTIONS) 12 | ds.each{break} 13 | 14 | if cols = ds.cache[:_columns] 15 | self.columns = cols 16 | else 17 | [] 18 | end 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/sequel/extensions/sql_expr.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # The sql_expr extension adds the sql_expr method to every object, which 4 | # returns an wrapped object that works nicely with Sequel's DSL by calling 5 | # Sequel.expr: 6 | # 7 | # 1.sql_expr < :a # 1 < a 8 | # false.sql_expr & :a # FALSE AND a 9 | # true.sql_expr | :a # TRUE OR a 10 | # ~nil.sql_expr # NOT NULL 11 | # "a".sql_expr + "b" # 'a' || 'b' 12 | # 13 | # To load the extension: 14 | # 15 | # Sequel.extension :sql_expr 16 | 17 | # 18 | class Object 19 | # Return the object wrapper in an appropriate Sequel expression object. 20 | def sql_expr 21 | Sequel[self] 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /doc/release_notes/5.30.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * Sequel now supports generated columns on SQLite 3.31+ using the 4 | :generated_always_as and :generated_type options. Example: 5 | 6 | DB.create_table(:table) do 7 | primary_key :id 8 | Numeric :amount, null: false 9 | Numeric :tax, null: false 10 | Numeric :total, generated_always_as: (Sequel[:amount] + :tax) 11 | end 12 | 13 | = Other Improvements 14 | 15 | * The Database#transaction :before_retry option is now called before 16 | retrying the transaction even when the :num_retries option is set 17 | to nil. 18 | 19 | * The gem no longer ships with specs and older release notes, reducing 20 | the gem size by over 40%. 21 | -------------------------------------------------------------------------------- /spec/extensions/unlimited_update_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "Sequel::Plugins::UnlimitedUpdate" do 4 | before do 5 | @db = Sequel.mock(:host=>'mysql', :numrows=>1) 6 | @db.extend_datasets{def quote_identifiers?; false end} 7 | @c = Class.new(Sequel::Model(@db[:test])) 8 | @c.columns :id, :name 9 | @o = @c.load(:id=>1, :name=>'a') 10 | @db.sqls 11 | end 12 | 13 | it "should remove limit from update dataset" do 14 | @o.save 15 | @db.sqls.must_equal ["UPDATE test SET name = 'a' WHERE (id = 1) LIMIT 1"] 16 | 17 | @c.plugin :unlimited_update 18 | @o.save 19 | @db.sqls.must_equal ["UPDATE test SET name = 'a' WHERE (id = 1)"] 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/visibility_checking.rb: -------------------------------------------------------------------------------- 1 | require 'visibility_checker' 2 | changes = VISIBILITY_CHANGES = [] 3 | Minitest.after_run do 4 | if defined?(DB) 5 | [DB.singleton_class, DB.dataset.singleton_class].each do |c| 6 | VISIBILITY_CHANGES.concat(VisibilityChecker.visibility_changes(c).map{|v| [v, c.inspect]}) 7 | end 8 | end 9 | 10 | changes.uniq!{|v,| v} 11 | changes.map! do |v, caller| 12 | "#{caller}: #{v.new_visibility} method #{v.overridden_by}##{v.method} overrides #{v.original_visibility} method in #{v.defined_in}" 13 | end 14 | changes.sort! 15 | if changes.empty? 16 | puts "No visibility changes" 17 | else 18 | puts "Visibility changes:" 19 | puts(*changes) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /doc/release_notes/5.39.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * On Microsoft SQL Server, the :clustered option is now supported 4 | for primary key and unique constraints. You can use a true value 5 | for CLUSTERED and a false value for NONCLUSTERED. 6 | 7 | = Other Improvements 8 | 9 | * Partitioned tables are now included in the result of 10 | Database#tables on PostgreSQL. 11 | 12 | * alter_table set_column_allow_null no longer drops the size of 13 | binary columns on Microsoft SQL Server. 14 | 15 | * In the tree plugin, the roots_dataset method now works correctly 16 | with queries using joins by qualifying the parent column. 17 | 18 | * A fork safety guide has been added, discussing fork safety issues 19 | when using Sequel. 20 | -------------------------------------------------------------------------------- /spec/extensions/exclude_or_null_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "exclude_or_null extension" do 4 | before do 5 | @ds = Sequel.mock[:t].extension(:exclude_or_null) 6 | end 7 | 8 | it "#exclude_or_null should add WHERE condition where a is false or NULL" do 9 | @ds.exclude_or_null(:a).sql.must_equal "SELECT * FROM t WHERE NOT coalesce(a, 'f')" 10 | end 11 | 12 | it "#exclude_or_null_having should add HAVING condition where a is false or NULL" do 13 | @ds.exclude_or_null_having(:a).sql.must_equal "SELECT * FROM t HAVING NOT coalesce(a, 'f')" 14 | end 15 | 16 | it "should not effect normal exclude" do 17 | @ds.exclude(:a).sql.must_equal "SELECT * FROM t WHERE NOT a" 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/extensions/stdio_logger_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | require 'stringio' 4 | 5 | describe "stdio_logger extension: Sequel::StdioLogger" do 6 | before do 7 | Sequel.extension :stdio_logger 8 | @output = StringIO.new 9 | @logger = Sequel::StdioLogger.new(@output) 10 | end 11 | 12 | it "#debug should not log" do 13 | @logger.debug("foo").must_be_nil 14 | @output.rewind 15 | @output.read.must_equal '' 16 | end 17 | 18 | [:info, :warn, :error].each do |level| 19 | it "##{level} should log message with given level" do 20 | @logger.send(level, "foo").must_be_nil 21 | @output.rewind 22 | @output.read.must_match(/ #{level.to_s.upcase}: foo\n\z/) 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/sequel/extensions/fiber_concurrency.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # The fiber_concurrency extension changes the default concurrency 4 | # primitive in Sequel to be Fiber.current instead of Thread.current. 5 | # This is the value used in various hash keys to implement safe 6 | # concurrency (thread-safe concurrency by default, fiber-safe 7 | # concurrency with this extension. It can be enabled via: 8 | # 9 | # Sequel.extension :fiber_concurrency 10 | # 11 | # Related module: Sequel::FiberConcurrency 12 | 13 | require 'fiber' 14 | 15 | module Sequel 16 | module FiberConcurrency 17 | # Make the current concurrency primitive be Fiber.current. 18 | def current 19 | Fiber.current 20 | end 21 | end 22 | 23 | extend FiberConcurrency 24 | end 25 | -------------------------------------------------------------------------------- /lib/sequel/adapters/utils/unmodified_identifiers.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | module UnmodifiedIdentifiers 5 | module DatabaseMethods 6 | private 7 | 8 | # Databases that use this module for unquoted identifiers to lowercase. 9 | def folds_unquoted_identifiers_to_uppercase? 10 | false 11 | end 12 | end 13 | 14 | module DatasetMethods 15 | private 16 | 17 | # Turn the given symbol/string into a symbol, keeping the current case. 18 | def output_identifier(v) 19 | v == '' ? :untitled : v.to_sym 20 | end 21 | 22 | # Turn the given symbol/string into a string, keeping the current case. 23 | def input_identifier(v) 24 | v.to_s 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/model/exceptions_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe Sequel::MassAssignmentRestriction, "#create" do 4 | it "should set model attr" do 5 | model_cls = Class.new 6 | 7 | model = model_cls.new 8 | err = Sequel::MassAssignmentRestriction.create("method foo doesn't exist", model, "foo") 9 | err.message.must_include("method foo doesn't exist for class ") 10 | err.model.must_equal(model) 11 | err.column.must_equal("foo") 12 | 13 | def model_cls.inspect; 'TestModel' end 14 | model = model_cls.new 15 | err = Sequel::MassAssignmentRestriction.create("method foo doesn't exist", model, "foo") 16 | err.message.must_equal("method foo doesn't exist for class TestModel") 17 | err.model.must_equal(model) 18 | err.column.must_equal("foo") 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /doc/release_notes/5.27.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * Sequel::DEFAULT has been added a constant for the DEFAULT expression, 4 | useful in inserts and especially updates: 5 | 6 | DB[:a].where(:id=>1).update(:b=>Sequel::DEFAULT) 7 | # UPDATE "a" SET "b" = DEFAULT WHERE "id" = 1 8 | 9 | * SQL::Function#filter for filtered aggregate functions is now 10 | supported on all databases. On databases not supporting it natively 11 | (all except PostgreSQL 9.4+ and SQLite 3.30+), a CASE statement is 12 | used to emulate the support. 13 | 14 | = Other Improvements 15 | 16 | * NULLS FIRST/LAST is now used without emulation on SQLite 3.30+. 17 | 18 | * The pg_enum extension now works correctly on PostgreSQL 8.3-9.0. 19 | 20 | * Postgres::ArrayOp#join in the pg_array_ops extension now works 21 | correctly on PostgreSQL <9.1. 22 | -------------------------------------------------------------------------------- /lib/sequel/plugins/unlimited_update.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | module Plugins 5 | # The unlimited_update plugin is designed to work around a 6 | # MySQL warning in replicated environments, which occurs if 7 | # you issue an UPDATE with a LIMIT clause. 8 | # 9 | # Usage: 10 | # 11 | # # Make all model subclass not use a limit for update 12 | # Sequel::Model.plugin :unlimited_update 13 | # 14 | # # Make the Album class not use a limit for update 15 | # Album.plugin :unlimited_update 16 | module UnlimitedUpdate 17 | module InstanceMethods 18 | private 19 | 20 | # Use an unlimited dataset for updates. 21 | def _update_dataset 22 | super.unlimited 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/deprecation_helper.rb: -------------------------------------------------------------------------------- 1 | Sequel::Deprecation.backtrace_filter = lambda{|line, lineno| lineno < 4 || line =~ /_spec\.rb/} 2 | 3 | class Minitest::HooksSpec 4 | def self.deprecated(a, &block) 5 | it("#{a} (deprecated)") do 6 | deprecated{instance_exec(&block)} 7 | end 8 | end 9 | 10 | def deprecated 11 | output = Sequel::Deprecation.output 12 | Sequel::Deprecation.output = nil 13 | yield 14 | ensure 15 | Sequel::Deprecation.output = output 16 | end 17 | 18 | def self.with_symbol_splitting(a, &block) 19 | it("#{a}, with symbol splitting enabled") do 20 | with_symbol_splitting{instance_exec(&block)} 21 | end 22 | end 23 | 24 | def with_symbol_splitting 25 | Sequel.split_symbols = true 26 | yield 27 | ensure 28 | Sequel.split_symbols = false 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/extensions/integer64_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "integer64 extension" do 4 | before do 5 | @db = Sequel.mock.extension(:integer64) 6 | end 7 | 8 | it "should use bigint as default integer type" do 9 | @db.create_table(:t){Integer :a; column :b, Integer} 10 | @db.sqls.must_equal ['CREATE TABLE t (a bigint, b bigint)'] 11 | end 12 | 13 | it "should use bigint as default type for primary_key and foreign_key" do 14 | @db.create_table(:t){primary_key :id; foreign_key :t_id, :t} 15 | @db.sqls.must_equal ['CREATE TABLE t (id bigint PRIMARY KEY AUTOINCREMENT, t_id bigint REFERENCES t)'] 16 | end 17 | 18 | it "should use bigint when casting" do 19 | @db.get(Sequel.cast('a', Integer)) 20 | @db.sqls.must_equal ["SELECT CAST('a' AS bigint) AS v LIMIT 1"] 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/sequel/version.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | # The major version of Sequel. Only bumped for major changes. 5 | MAJOR = 5 6 | 7 | # The minor version of Sequel. Bumped for every non-patch level 8 | # release, generally around once a month. 9 | MINOR = 99 10 | 11 | # The tiny version of Sequel. Usually 0, only bumped for bugfix 12 | # releases that fix regressions from previous versions. 13 | TINY = 0 14 | 15 | # The version of Sequel you are using, as a string (e.g. "2.11.0") 16 | VERSION = [MAJOR, MINOR, TINY].join('.').freeze 17 | 18 | # The version of Sequel you are using, as a number (2.11.0 -> 20110) 19 | VERSION_NUMBER = MAJOR*10000 + MINOR*10 + TINY 20 | 21 | # The version of Sequel you are using, as a string (e.g. "2.11.0") 22 | def self.version 23 | VERSION 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/extensions/auto_cast_date_and_time_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "auto_cast_date_and_time extension" do 4 | before do 5 | @db = Sequel.mock.extension :auto_cast_date_and_time 6 | end 7 | 8 | it "should automatically cast Time instances" do 9 | @db.literal(Time.local(2000)).must_equal "TIMESTAMP '2000-01-01 00:00:00.000000'" 10 | end 11 | 12 | it "should automatically cast DateTime instances" do 13 | @db.literal(DateTime.new(2000)).must_equal "TIMESTAMP '2000-01-01 00:00:00.000000'" 14 | end 15 | 16 | it "should automatically cast SQLTime instances" do 17 | @db.literal(Sequel::SQLTime.create(10, 20, 30)).must_equal "TIME '10:20:30.000000'" 18 | end 19 | 20 | it "should automatically cast Date instances" do 21 | @db.literal(Date.new(2000)).must_equal "DATE '2000-01-01'" 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/async_spec_helper.rb: -------------------------------------------------------------------------------- 1 | class Minitest::Spec 2 | if ENV['SEQUEL_ASYNC_THREAD_POOL'] || ENV['SEQUEL_ASYNC_THREAD_POOL_PREEMPT'] || ENV['SEQUEL_EAGER_ASYNC'] 3 | use_async = true 4 | if ENV['SEQUEL_ASYNC_THREAD_POOL_PREEMPT'] 5 | ::DB.opts[:preempt_async_thread] = true 6 | end 7 | ::DB.opts[:num_async_threads] = 12 8 | ::DB.extension :async_thread_pool 9 | 10 | if ENV['SEQUEL_EAGER_ASYNC'] 11 | Sequel::Model.plugin :concurrent_eager_loading, :always=>true 12 | end 13 | end 14 | 15 | if use_async && DB.pool.pool_type == :threaded && (!DB.opts[:max_connections] || DB.opts[:max_connections] >= 4) 16 | def async? 17 | true 18 | end 19 | 20 | def wait 21 | yield.tap{} 22 | end 23 | else 24 | def async? 25 | false 26 | end 27 | 28 | def wait 29 | yield 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/core/spec_helper.rb: -------------------------------------------------------------------------------- 1 | if ENV['COVERAGE'] 2 | require_relative "../sequel_coverage" 3 | SimpleCov.sequel_coverage(:filter=>%r{lib/sequel/(\w+\.rb|(dataset|database|model|connection_pool)/\w+\.rb|adapters/mock\.rb)\z}) 4 | end 5 | 6 | $:.unshift(File.join(File.dirname(File.expand_path(__FILE__)), "../../lib/")) 7 | require_relative "../../lib/sequel/core" 8 | 9 | ENV['MT_NO_PLUGINS'] = '1' # Work around stupid autoloading of plugins 10 | gem 'minitest' 11 | require 'minitest/global_expectations/autorun' 12 | require 'minitest/hooks/default' 13 | 14 | require_relative '../deprecation_helper' 15 | 16 | if ENV['SEQUEL_COLUMNS_INTROSPECTION'] 17 | Sequel.extension :columns_introspection 18 | Sequel::Database.extension :columns_introspection 19 | require_relative '../../lib/sequel/adapters/mock' 20 | Sequel::Mock::Dataset.send(:include, Sequel::ColumnsIntrospection) 21 | end 22 | -------------------------------------------------------------------------------- /lib/sequel/adapters/jdbc/mssql.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | require_relative '../shared/mssql' 4 | 5 | module Sequel 6 | module JDBC 7 | module MSSQL 8 | module DatabaseMethods 9 | include Sequel::MSSQL::DatabaseMethods 10 | 11 | private 12 | 13 | # Get the last inserted id using SCOPE_IDENTITY(). 14 | def last_insert_id(conn, opts=OPTS) 15 | statement(conn) do |stmt| 16 | sql = opts[:prepared] ? 'SELECT @@IDENTITY' : 'SELECT SCOPE_IDENTITY()' 17 | rs = log_connection_yield(sql, conn){stmt.executeQuery(sql)} 18 | rs.next 19 | rs.getLong(1) 20 | end 21 | end 22 | 23 | # Primary key indexes appear to start with pk__ on MSSQL 24 | def primary_key_index_re 25 | /\Apk__/i 26 | end 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/sequel/extensions/_model_pg_row.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | module Plugins 5 | module PgRow 6 | module DatabaseMethods 7 | # Handle Sequel::Model instances in bound variables. 8 | def bound_variable_arg(arg, conn) 9 | case arg 10 | when Sequel::Model 11 | "(#{arg.values.values_at(*arg.columns).map{|v| bound_variable_array(v)}.join(',')})" 12 | else 13 | super 14 | end 15 | end 16 | 17 | # If a Sequel::Model instance is given, return it as-is 18 | # instead of attempting to convert it. 19 | def row_type(db_type, v) 20 | if v.is_a?(Sequel::Model) 21 | v 22 | else 23 | super 24 | end 25 | end 26 | end 27 | end 28 | end 29 | 30 | Database.register_extension(:_model_pg_row, Plugins::PgRow::DatabaseMethods) 31 | end 32 | -------------------------------------------------------------------------------- /spec/extensions/after_initialize_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "Sequel::Plugins::AfterInitialize" do 4 | before do 5 | @db = Sequel.mock(:host=>'mysql', :numrows=>1) 6 | @c = Class.new(Sequel::Model(@db[:test])) 7 | @c.class_eval do 8 | columns :id, :name 9 | plugin :after_initialize 10 | def after_initialize 11 | self.name *= 2 12 | self.id *= 3 if id 13 | end 14 | end 15 | end 16 | 17 | it "should have after_initialize hook be called for new objects" do 18 | @c.new(:name=>'foo').values.must_equal(:name=>'foofoo') 19 | end 20 | 21 | it "should have after_initialize hook be called for objects loaded from the database" do 22 | @c.call(:id=>1, :name=>'foo').values.must_equal(:id=>3, :name=>'foofoo') 23 | end 24 | 25 | it "should not allow .call to be called without arguments" do 26 | proc{@c.call}.must_raise ArgumentError 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/extensions/symbol_aref_refinement_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | if (RUBY_VERSION >= '2.0.0' && RUBY_ENGINE == 'ruby') || (RUBY_VERSION >= '2.3.0' && RUBY_ENGINE == 'jruby') 4 | Sequel.extension :symbol_aref_refinement 5 | using Sequel::SymbolAref 6 | 7 | describe "symbol_aref_refinement extension" do 8 | before do 9 | @db = Sequel.mock 10 | end 11 | 12 | it "Symbol#[] should create qualified identifier if given a symbol" do 13 | @db.literal(:x[:y]).must_equal "x.y" 14 | end 15 | 16 | it "Symbol#[] should create qualified identifier if given an identifier" do 17 | @db.literal(:x[Sequel[:y]]).must_equal "x.y" 18 | end 19 | 20 | it "Symbol#[] should create qualified identifier if given a qualified identifier" do 21 | @db.literal(:x[:y[:z]]).must_equal "x.y.z" 22 | end 23 | 24 | it "should not affect other arguments to Symbol#[]" do 25 | :x[0].must_equal "x" 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /doc/release_notes/5.60.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The date_arithmetic extension now supports arbitrary expressions 4 | as interval values on PostgreSQL 9.4+. Previously, only integers 5 | were supported for the interval values. 6 | 7 | = Other Improvements 8 | 9 | * Most Kernel#respond_to? calls have been converted to equivalent 10 | defined? calls for better performance. defined? is a keyword 11 | and is about 50% faster for the same behavior. 12 | 13 | * The is_distinct_from extension now supports the IS DISTINCT FROM 14 | syntax natively on SQLite 3.39+, instead of emulating it. 15 | 16 | * HAVING without GROUP BY is now supported on SQLite 3.39+. 17 | 18 | * Coverage testing has been significantly expanded. Previously, 19 | the core, model, plugin, and extension code had 100% line/branch 20 | coverage. 100% line/branch coverage has been added for the 21 | core extensions, bin/sequel, and the postgres adapter with the 22 | pg driver. 23 | -------------------------------------------------------------------------------- /doc/release_notes/5.71.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A pg_xmin_optimistic_locking plugin has been added. This plugin 4 | uses PostgreSQL's xmin system column to implement optimistic 5 | locking. The xmin system column is automatically updated whenever 6 | the database row is updated. You can load this plugin into a 7 | base model and have all models that subclass from it use optimistic 8 | locking, without needing any user-defined lock columns. 9 | 10 | = Other Improvements 11 | 12 | * set_column_allow_null is now a reversible migration method inside 13 | alter_table blocks. 14 | 15 | * The use of ILIKE no longer forces the ESCAPE clause on PostgreSQL, 16 | which allows the use of ILIKE ANY and other constructions. There 17 | is no need to use the ESCAPE clause with ILIKE, because the value 18 | Sequel uses is PostgreSQL's default. 19 | 20 | * The xid PostgreSQL type is now recognized as an integer type in the 21 | jdbc/postgresql adapter. 22 | -------------------------------------------------------------------------------- /doc/release_notes/5.55.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An auto_restrict_eager_graph plugin has been added for automatically 4 | disallowing the use of eager_graph with associations using blocks but 5 | lacking graph_* options. This can prevent potentionally invalid usage, 6 | as the restrictions added by the block are not used by eager_graph. 7 | 8 | * The sqlite adapter now supports the :setup_regexp_function 9 | Database option. This option will define a REGEXP function in the 10 | database that will allow regexp support in queries, such as: 11 | 12 | DB[:table].where(column: /(some|pattern)/) 13 | 14 | Note that this creates a Ruby Regexp object per column value tested, 15 | so it isn't the most optimal approach. 16 | 17 | = Other Improvements 18 | 19 | * Calling dataset aggregate methods such as #max on a model dataset now 20 | works correctly. Previously, it could fail if called enough times to 21 | optimize using a placeholder literalizer. 22 | -------------------------------------------------------------------------------- /doc/release_notes/5.65.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * The pg_auto_parameterize extension now uses a modified placeholder 4 | literalizer for speeding up the generation of SQL queries in the same 5 | cases where a standard dataset would use a placeholder literalizer. 6 | This can provide a 4% speedup for simple queries, with greater 7 | speedups for more complex queries. 8 | 9 | * Database#indexes now returns indexes for partitioned tables on 10 | PostgreSQL 11+. 11 | 12 | * MySQL versions not supporting CHECK constraints no longer include 13 | :min_value/:max_value schema entries for decimal/numeric columns. 14 | 15 | = Backwards Compatibility 16 | 17 | * The Dataset::PlaceholderLiterlizer::Record.loader API has changed, 18 | it now accepts the Dataset::PlaceholderLiterlizer class to use as 19 | the first argument. This makes it easier to create 20 | Dataset::PlaceholderLiterlizer subclasses, such as the one now used 21 | by the pg_auto_parameterize extension. 22 | -------------------------------------------------------------------------------- /doc/release_notes/5.29.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An empty_failure_backtraces plugin has been added for using empty 4 | backtraces for ValidationFailed and HookFailed exceptions. In many 5 | cases, these exceptions are automatically handled (e.g. web form 6 | submission handling to display appropriate error pages), and using 7 | empty backtraces is 10-15x faster on JRuby 9.2.7.0+. 8 | 9 | * Dataset#json_serializer_opts has been added to the json_serializer 10 | plugin. This allows setting default options on a per-Dataset basis 11 | for all Dataset#to_json calls. 12 | 13 | = Other Improvements 14 | 15 | * Another disconnect error is now recognized in the tinytds adapter. 16 | 17 | * Using Sequel with the the CRuby master branch (what will be Ruby 3) 18 | now works by supporting a second argument for 19 | Dataset#initialize_clone. 20 | 21 | * Sequel now avoids a warning in verbose mode when using the postgres 22 | adapter, a recent version of ruby-pg, and bound variables. 23 | -------------------------------------------------------------------------------- /doc/release_notes/5.33.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * Custom join types are now supported on a per-association basis when 4 | using eager_graph/association_join. This builds on the previous 5 | support for custom aliases, using Sequel::SQL::AliasedExpression: 6 | 7 | class Artist < Sequel::Model; end 8 | class Album < Sequel::Model; end 9 | class Track < Sequel::Model; end 10 | Artist.one_to_many :albums 11 | Album.one_to_many :tracks 12 | Artist.eager_graph( 13 | Sequel[:albums].as(:a, join_type: :inner) => 14 | Sequel[:tracks].as(:t, join_type: :left) 15 | ) 16 | 17 | * A Database#current_timestamp_utc accessor has been added on SQLite. 18 | Setting this to true will keep CURRENT_TIMESTAMP, CURRENT_TIME, and 19 | CURRENT_DATE in UTC instead of converting them to localtime. 20 | 21 | = Other Improvements 22 | 23 | * The smallserial PostgreSQL type is now recognized and Sequel will 24 | not try to mark smallserial columns as identity columns. 25 | -------------------------------------------------------------------------------- /www/public/css/plugins.css: -------------------------------------------------------------------------------- 1 | section.plugins { 2 | background-color: #FFF; 3 | border-radius: 12px; 4 | box-shadow: 0 24px 64px rgba(0,0,0,.15); 5 | } 6 | 7 | code.plugins__code { 8 | background-color: #eaeaea; 9 | border-radius: 4px; 10 | padding: 2px 4px; 11 | } 12 | 13 | div.plugins__sidebar { 14 | display: grid; 15 | grid-template-columns: 1fr 4fr; 16 | margin-top: var(--gap); 17 | border-top: 1px solid var(--border); 18 | } 19 | @media (max-width: 1200px) { 20 | div.plugins__sidebar { 21 | grid-template-columns: 1fr; 22 | } 23 | } 24 | 25 | aside.plugins__sidebar-aside { 26 | position: sticky; 27 | top: 0; 28 | max-height: 100vh; 29 | border-right: 1px solid var(--border); 30 | padding-bottom: var(--gap); 31 | padding-top: var(--gap); 32 | padding-left: var(--gap); 33 | padding-right: var(--gap); 34 | } 35 | @media (max-width: 1200px) { 36 | aside.plugins__sidebar-aside { 37 | position: inherit; 38 | border-right: none; 39 | border-bottom: 1px solid var(--border); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /spec/extensions/columns_updated_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "Sequel::Plugins::ColumnsUpdated" do 4 | before do 5 | @c = Class.new(Sequel::Model(DB[:items].with_autoid(13))) 6 | @c.columns :id, :x, :y 7 | @c.plugin :columns_updated 8 | end 9 | 10 | it "should make hash used for updating available in columns_updated until after hooks finish running" do 11 | res = nil 12 | @c.send(:define_method, :after_save){res = columns_updated} 13 | o = @c.new(:x => 1, :y => nil) 14 | o[:x] = 2 15 | o.save 16 | res.must_be_nil 17 | o.after_save 18 | res.must_be_nil 19 | 20 | o = @c.load(:id => 23,:x => 1, :y => nil) 21 | o[:x] = 2 22 | o.save 23 | res.must_equal(:x=>2, :y=>nil) 24 | o.after_save 25 | res.must_be_nil 26 | 27 | o = @c.load(:id => 23,:x => 2, :y => nil) 28 | o[:x] = 2 29 | o[:y] = 22 30 | o.save(:columns=>:x) 31 | res.must_equal(:x=>2) 32 | o.after_save 33 | res.must_be_nil 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/sequel/adapters/utils/replace.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | class Dataset 5 | module Replace 6 | # Execute a REPLACE statement on the database (deletes any duplicate 7 | # rows before inserting). 8 | def replace(*values) 9 | execute_insert(replace_sql(*values)) 10 | end 11 | 12 | # SQL statement for REPLACE 13 | def replace_sql(*values) 14 | clone(:replace=>true).insert_sql(*values) 15 | end 16 | 17 | # Replace multiple rows in a single query. 18 | def multi_replace(*values) 19 | clone(:replace=>true).multi_insert(*values) 20 | end 21 | 22 | # Databases using this module support REPLACE. 23 | def supports_replace? 24 | true 25 | end 26 | 27 | private 28 | 29 | # If this is an replace instead of an insert, use replace instead 30 | def insert_insert_sql(sql) 31 | sql << (@opts[:replace] ? 'REPLACE' : 'INSERT') 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /doc/release_notes/5.69.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An adapter has been added for the trilogy MySQL driver. One large 4 | advantage over mysql2 is that trilogy does not require any MySQL 5 | client libraries installed on the machine. The trilogy adapter 6 | has basically the same issues/skipped specs as the mysql2 adapter, 7 | but it also does not support an application_timezone different 8 | than the database_timezone. 9 | 10 | * Model dataset modules now have a model accessor, allowing for 11 | code such as: 12 | 13 | class Foo < Sequel::Model 14 | dataset_module do 15 | where :kept, Sequel[model.table_name][:discarded_at] => nil 16 | end 17 | end 18 | 19 | = Improvements 20 | 21 | * The mysql adapter now works with ruby-mysql 4 (the pure-ruby 22 | MySQL driver). Note that multi-results support does not work 23 | with ruby-mysql 4 (it doesn't work with mysql2, trilogy, or 24 | other Sequel adapters in general). 25 | 26 | * Warnings for unsupported flags are now avoided on ruby-mysql 3. 27 | -------------------------------------------------------------------------------- /spec/extensions/constant_sql_override_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "constant_sql_override extension" do 4 | before do 5 | @db = Sequel.mock.extension(:constant_sql_override) 6 | end 7 | 8 | it 'overrides configured constants' do 9 | @db.set_constant_sql(Sequel::CURRENT_TIMESTAMP, "CURRENT TIMESTAMP AT TIME ZONE 'UTC'") 10 | @db[:tbl].where(foo: Sequel::CURRENT_TIMESTAMP).first 11 | @db.sqls.must_equal ["SELECT * FROM tbl WHERE (foo = CURRENT TIMESTAMP AT TIME ZONE 'UTC') LIMIT 1"] 12 | end 13 | 14 | it 'does not change behavior for unconfigured constants' do 15 | @db[:tbl].where(foo: Sequel::CURRENT_TIMESTAMP).first 16 | @db.sqls.must_equal ["SELECT * FROM tbl WHERE (foo = CURRENT_TIMESTAMP) LIMIT 1"] 17 | end 18 | 19 | it 'freezes the constant_sqls hash when frozen' do 20 | @db.freeze 21 | @db.constant_sqls.frozen?.must_equal true 22 | proc{@db.set_constant_sql(Sequel::CURRENT_TIMESTAMP, "CURRENT TIMESTAMP AT TIME ZONE 'UTC'")}.must_raise RuntimeError 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/extensions/freeze_datasets_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "freeze_datasets extension" do 4 | before do 5 | @db = Sequel.mock.extension(:freeze_datasets) 6 | end 7 | 8 | it "should freeze datasets by default" do 9 | @db.dataset.frozen?.must_equal true 10 | @db.fetch('SQL').frozen?.must_equal true 11 | @db.from(:table).frozen?.must_equal true 12 | @db[:table].frozen?.must_equal true 13 | end 14 | 15 | it "should have dataset#dup return frozen dataset" do 16 | @db.dataset.dup.frozen?.must_equal true 17 | end 18 | 19 | it "should cache Database#from calls with single symbol tables" do 20 | @db.from(:foo).must_be_same_as @db.from(:foo) 21 | @db.from(Sequel[:foo]).wont_be_same_as @db.from(Sequel[:foo]) 22 | end 23 | 24 | it "should clear Database#from cache when modifying the schema" do 25 | ds = @db.from(:foo) 26 | ds.columns(:foo, :bar) 27 | @db[:foo].columns.must_equal [:foo, :bar] 28 | @db.create_table!(:foo){Integer :x} 29 | @db[:foo].columns.wont_equal [:foo, :bar] 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /doc/release_notes/5.13.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A constant_sql_override Database extension has been added, allowing 4 | for overriding the SQL used by constants such as 5 | Sequel::CURRENT_TIMESTAMP. This can be used to force 6 | CURRENT_TIMESTAMP to be literalized at a particular time zone: 7 | 8 | DB.extension :constant_sql_override 9 | DB.set_constant_sql(Sequel::CURRENT_TIMESTAMP, 10 | "CURRENT_TIMESTAMP AT TIME ZONE 'UTC'") 11 | 12 | * Prepared statements now support the :single_value type, which 13 | returns the first column value in the dataset. 14 | 15 | prep_stmt = DB[:table].select(:column).prepare(:single_value, :ps) 16 | prep_stmt.call 17 | # PREPARE ps AS SELECT column FROM table LIMIT 1; 18 | # EXECUTE ps; 19 | # => 42 20 | 21 | = Other Improvements 22 | 23 | * Dataset#from_self will no longer use a cached dataset if any options 24 | are given, as that can result in incorrect behavior. 25 | 26 | * Model.all in the static_cache plugin now accepts a block, mirroring 27 | the API when the static_cache plugin is not used. 28 | -------------------------------------------------------------------------------- /doc/release_notes/5.66.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * Dataset#empty? now correctly handles datasets using custom SQL or 4 | Dataset#values where the first value in the first row is NULL. 5 | 6 | * Dataset#count without an argument or block now works correctly on 7 | Microsoft SQL Server when using custom SQL that uses ORDER BY. 8 | 9 | * Dataset#count now works correctly for datasets using Dataset#values. 10 | 11 | * Sequel now recognizes an additional SQLite constraint violation 12 | error that occurs with recent versions of amalgalite. 13 | 14 | * Dataset#values will now raise an exception when called with an empty 15 | array. Previously, an exception would not be raised until the query 16 | was sent to the database. 17 | 18 | = Backwards Compatibility 19 | 20 | * The changes to make Dataset#empty? and #count work with custom SQL 21 | on Microsoft SQL Server now result in running the custom SQL, which 22 | could result in worse performance than in previous versions. You can 23 | wrap such datasets with Dataset#from_self manually to restore the 24 | previous behavior. 25 | -------------------------------------------------------------------------------- /doc/release_notes/5.19.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A Database#rename_enum_value method has been added to the pg_enum 4 | extension. It is supported on PostgreSQL 10+: 5 | 6 | DB.rename_enum_value(:enum_type, 'old_name', 'new_name') 7 | 8 | = Other Improvements 9 | 10 | * The performance of row fetching and type conversion in the 11 | sqlanywhere adapter has been improved. 12 | 13 | * The performance of row fetching in the sqlite adapter has been 14 | improved. 15 | 16 | * Calling Database#drop_table now drops any constraint validations 17 | metadata for the table if using the constraint_validations 18 | extension. However, modifying the table using Database#alter_table 19 | does not affect the constraint validations metadata. 20 | 21 | * The sqlite adapter when used with ruby-sqlite3 1.4.0+ now uses 22 | SQLite extended result codes for a more accurate determination of 23 | specific database errors types. 24 | 25 | * Performance for typecasting to decimal and floats has been improved 26 | slightly. 27 | 28 | * Performance when merging hashes has been improved slightly. 29 | -------------------------------------------------------------------------------- /doc/release_notes/5.57.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An is_distinct_from extension has been added with support for the 4 | SQL IS DISTINCT FROM operator. This operator is similar to the 5 | not equals operator, except in terms of NULL handling. It returns 6 | true if only one side is NULL, and false if both sides are NULL. 7 | You can call is_distinct_from on Sequel itself or on Sequel objects: 8 | 9 | Sequel.is_distinct_from(:column_a, :column_b) 10 | Sequel[:column_a].is_distinct_from(:column_b) 11 | # (column_a IS DISTINCT FROM column_b) 12 | 13 | On databases not supporting IS DISTINCT FROM, support is emulated 14 | using a CASE statement. 15 | 16 | * Column definitions on MySQL can use the :on_update_current_timestamp 17 | option for ON UPDATE CURRENT_TIMESTAMP, which creates a column that 18 | will automatically have its value set to CURRENT_TIMESTAMP on every 19 | update. 20 | 21 | * Database#create_function on PostgreSQL now supports a :parallel 22 | option to set the thread safety of the funciton. The value should 23 | be :safe, :unsafe, or :restricted. 24 | -------------------------------------------------------------------------------- /lib/sequel/extensions/symbol_as_refinement.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # The symbol_as_refinement extension adds a refinement that makes 4 | # Symbol#as return Sequel::SQL::AliasedExpression instances. It's 5 | # designed as a shortcut so that instead of: 6 | # 7 | # Sequel[:column].as(:alias) # column AS alias 8 | # 9 | # you can just write: 10 | # 11 | # :column.as(:alias) # column AS alias 12 | # 13 | # To load the extension: 14 | # 15 | # Sequel.extension :symbol_as_refinement 16 | # 17 | # To enable the refinement for the current file: 18 | # 19 | # using Sequel::SymbolAs 20 | # 21 | # If you would like this extension to be enabled globally instead 22 | # of as a refinement, use the symbol_as extension. 23 | # 24 | # Related module: Sequel::SymbolAs 25 | 26 | # :nocov: 27 | raise(Sequel::Error, "Refinements require ruby 2.0.0 or greater") unless RUBY_VERSION >= '2.0.0' 28 | # :nocov: 29 | 30 | module Sequel::SymbolAs 31 | refine Symbol do 32 | def as(aliaz, columns=nil) 33 | Sequel::SQL::AliasedExpression.new(self, aliaz, columns) 34 | end 35 | end 36 | end 37 | 38 | -------------------------------------------------------------------------------- /doc/release_notes/5.53.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * The jdbc/h2 subadapter now supports H2 version 2.0. It continues to 4 | support H2 versions 1.3 and 1.4. 5 | 6 | * The mysql2 adapter's prepared statement support now reuses existing 7 | native prepared statements, instead of only binding variables on 8 | newly prepared statements. This was the intended behavior 9 | previously, and should result in increased performance in cases 10 | where preparing a query takes significant time. 11 | 12 | * The subclasses plugin now ignores an existing Class#subclasses 13 | method if it is defined in Ruby. This fixes cases where usage of 14 | ActiveSupport would break the subclasses plugin. 15 | 16 | * Database#call_sproc in the jdbc adapter will now always close the 17 | prepared call it creates. Before, if there was an exception raised 18 | when setting the arguments for the prepared call, the prepared call 19 | would not be closed. 20 | 21 | * A more appropriate error is now issued if you try to use the 22 | column_encryption plugin to encrypt a column without setting up an 23 | encryption key. 24 | -------------------------------------------------------------------------------- /doc/release_notes/5.41.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The validation methods added by the validation_helpers plugin now 4 | support the :skip_invalid option, which will not add a validation 5 | error on a column if it already has a validation error. This can 6 | be useful if you want to avoid having duplicate errors. 7 | 8 | * The auto_validations plugin now supports a :skip_invalid plugin 9 | option, which will pass the :skip_invalid option when calling 10 | validation methods. 11 | 12 | = Other Improvements 13 | 14 | * The :adder, :remover, and :clearer association options now 15 | support keyword arguments in Ruby 2.7+. 16 | 17 | * In the pg_interval extension, Sequel now uses the same number of 18 | seconds per month and seconds per year as active_support. It 19 | originally used the same number, but active_support changed the 20 | values in active_support 5.1. Sequel now uses the active_support 21 | values if they are available. 22 | 23 | * When adding a String column on PostgreSQL, an explicit text: true 24 | option now takes precedence over an explicit :size option, as it 25 | does in Sequel's default behavior. 26 | -------------------------------------------------------------------------------- /spec/extensions/pg_loose_count_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "pg_loose_count extension" do 4 | before do 5 | @db = Sequel.mock(:host=>'postgres', :fetch=>{:v=>1}).extension(:pg_loose_count) 6 | @db.extend_datasets{def quote_identifiers?; false end} 7 | end 8 | 9 | it "should add loose_count method getting fast count for entire table using table statistics" do 10 | @db.loose_count(:a).must_equal 1 11 | @db.sqls.must_equal ["SELECT CAST(reltuples AS integer) AS v FROM pg_class WHERE (oid = CAST(CAST('a' AS regclass) AS oid)) LIMIT 1"] 12 | end 13 | 14 | it "should support schema qualified tables" do 15 | @db.loose_count(Sequel[:a][:b]).must_equal 1 16 | @db.sqls.must_equal ["SELECT CAST(reltuples AS integer) AS v FROM pg_class WHERE (oid = CAST(CAST('a.b' AS regclass) AS oid)) LIMIT 1"] 17 | end 18 | 19 | with_symbol_splitting "should support schema qualified table symbols" do 20 | @db.loose_count(:a__b).must_equal 1 21 | @db.sqls.must_equal ["SELECT CAST(reltuples AS integer) AS v FROM pg_class WHERE (oid = CAST(CAST('a.b' AS regclass) AS oid)) LIMIT 1"] 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /doc/release_notes/5.1.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * Database#copy_into in the jdbc/postgresql adapter now works 4 | correctly when using multibyte characters in strings. 5 | 6 | * The alter_table add_foreign_key method is now reversible when the 7 | :foreign_key_constraint_name option is used. 8 | 9 | * The jdbc/h2 and jdbc/hsqldb adapters now respect the 10 | :foreign_key_constraint_name option. 11 | 12 | * Calling Model.freeze on an already frozen model no longer raises 13 | an error. 14 | 15 | * An unnecessary database query is now avoided when loading the 16 | pg_inet extension when the pg_array extension is already loaded. 17 | 18 | * A better exception message is now used when migrating with an 19 | empty migration directory. 20 | 21 | = Backwards Compatibility 22 | 23 | * Model.allowed_columns has been removed. Use the whitelist_security 24 | plugin if you want to call it. 25 | 26 | * Model use_after_commit_rollback class and instance accessors have 27 | been removed. 28 | 29 | * Support for the Model#_before_validation method has been removed. 30 | 31 | * The private Model.plugin_module_defined? method has been removed. 32 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2007-2008 Sharon Rosner 2 | Copyright (c) 2008-2023 Jeremy Evans 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to 6 | deal in the Software without restriction, including without limitation the 7 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /doc/release_notes/4.6.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * Database#call_mssql_sproc is now available for calling 4 | stored procedures on Microsoft SQL Server, including the use 5 | of output parameters. 6 | 7 | * The Database#{commit,rollback}_prepared_transaction methods now 8 | support a :server option for the server on which to operate. 9 | 10 | = Other Improvements 11 | 12 | * On Microsoft SQL Server 2012, the native OFFSET/FETCH support 13 | is now used for offsets, instead of emulating support via the 14 | ROW_NUMBER window function. 15 | 16 | * Eager loading is now skipped when doing eager(...).naked.all on 17 | a model dataset, instead of raising an error. This can fix issues 18 | when the eager_each plugin is used. 19 | 20 | * A couple additional disconnection errors are now detected in the 21 | jdbc/postgresql adapter. 22 | 23 | * The tinytds adapter now handles returning rows when the fields 24 | are not immediately available. 25 | 26 | * RuntimeErrors raised by oci8 are now handled correctly in the 27 | oracle adapter. 28 | 29 | * Sequel's specs now work with RSpec 3, while still running 30 | correctly on RSpec 1.3 and 2. 31 | -------------------------------------------------------------------------------- /doc/release_notes/5.92.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * Objects that respond to sql_literal_append or sql_literal to 4 | support custom literalization in Sequel datasets can now also 5 | implement the sql_literal_allow_caching? method. If the object 6 | responds to the method and it returns true, then Sequel will 7 | still allow caching of the Dataset's SQL (this is disabled by 8 | default as Sequel cannot be sure that the literalization will 9 | not change between calls). 10 | 11 | * Model.qualified_primary_key has been added, which returns an 12 | SQL::QualifiedIdentifier with the model's primary key, or an 13 | array of SQL::QualifiedIdentifier objects if the model uses a 14 | composite primary key. 15 | 16 | * set_column_not_null is now a reversible method in migrations. 17 | 18 | = Other Improvements 19 | 20 | * The Model dataset paged_each method and the dataset methods in 21 | the paged_operations plugin now automatically use a qualified 22 | primary key instead of an unqualified primary key if the dataset 23 | is joined. 24 | 25 | * Module temporary names when using Ruby 3.5 are now consistent 26 | with Ruby 3.3 and 3.4. 27 | -------------------------------------------------------------------------------- /lib/sequel/extensions/pretty_table.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # The pretty_table extension adds Sequel::Dataset#print and the 4 | # Sequel::PrettyTable class for creating nice-looking plain-text 5 | # tables. Example: 6 | # 7 | # +--+-------+ 8 | # |id|name | 9 | # |--+-------| 10 | # |1 |fasdfas| 11 | # |2 |test | 12 | # +--+-------+ 13 | # 14 | # You can load this extension into specific datasets: 15 | # 16 | # ds = DB[:table] 17 | # ds = ds.extension(:pretty_table) 18 | # 19 | # Or you can load it into all of a database's datasets, which 20 | # is probably the desired behavior if you are using this extension: 21 | # 22 | # DB.extension(:pretty_table) 23 | # 24 | # Related module: Sequel::DatasetPrinter 25 | 26 | # 27 | module Sequel 28 | extension :_pretty_table 29 | 30 | module DatasetPrinter 31 | # Pretty prints the records in the dataset as plain-text table. 32 | def print(*cols) 33 | ds = naked 34 | rows = ds.all 35 | Sequel::PrettyTable.print(rows, cols.empty? ? ds.columns : cols) 36 | end 37 | end 38 | 39 | Dataset.register_extension(:pretty_table, DatasetPrinter) 40 | end 41 | -------------------------------------------------------------------------------- /lib/sequel/model/dataset_module.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | class Model 5 | # This Module subclass is used by Model.dataset_module 6 | # to add dataset methods to classes. In addition to the 7 | # methods offered by Dataset::DatasetModule, it also 8 | # automatically creates class methods for public dataset 9 | # methods. 10 | class DatasetModule < Dataset::DatasetModule 11 | # The model class related to this dataset module. 12 | attr_reader :model 13 | 14 | # Store the model related to this dataset module. 15 | def initialize(model) 16 | @model = model 17 | end 18 | 19 | # Alias for where. 20 | def subset(name, *args, &block) 21 | where(name, *args, &block) 22 | end 23 | 24 | private 25 | 26 | # Add a class method to the related model that 27 | # calls the dataset method of the same name. 28 | def method_added(meth) 29 | @model.send(:def_model_dataset_method, meth) if public_method_defined?(meth) 30 | super 31 | end 32 | end 33 | 34 | @dataset_module_class = DatasetModule 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /doc/release_notes/5.79.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * Dataset#select_prepend has been added for prepending to the 4 | currently selected columns: 5 | 6 | DB[:table].select_prepend(:column) 7 | # SELECT column, table.* 8 | 9 | As not all databases support "SELECT column, *", select_prepend 10 | qualifies wildcard selections to all tables referenced in the 11 | query. 12 | 13 | The only reason to use select_prepend is if you want the hashes 14 | returned by Sequel to be in a specific order. Otherwise, it is 15 | better to use select_append. 16 | 17 | * On PostgreSQL, Sequel now supports an :unlogged_tables_default 18 | Database option, which will default created tables to be UNLOGGED. 19 | This can be useful to speedup testing in some cases, but it should 20 | never be used in cases where data integrity is important. 21 | 22 | = Other Improvements 23 | 24 | * On PostgreSQL, Database#create_or_replace_view now supports the 25 | :materialized option. This allows for dropping an existing 26 | materialized view and creating a new one with the same name 27 | (PostgreSQL does not have native support for replacing materialized 28 | views). 29 | -------------------------------------------------------------------------------- /doc/release_notes/5.17.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An instance-level skip_auto_validations method has been added to 4 | the auto_validations plugin, allowing you to skip all or specific 5 | types of auto validations inside the block: 6 | 7 | model_instance.skip_auto_validations(:unique) do 8 | puts model_instance.valid? 9 | end 10 | 11 | * A Database :preconnect_extensions option has been added. This 12 | option is similar to :extensions, but the extensions are loaded 13 | before the :preconnect option is processed. This allows you to 14 | use the server_logging extension with the :preconnect option. 15 | 16 | * For specifying custom table aliases when using eager_graph and 17 | association_join, you can now use: 18 | 19 | Sequel[:association].as(:table_alias) 20 | 21 | in addition to: 22 | 23 | Sequel.as(:association, :table_alias) 24 | 25 | = Other Improvements 26 | 27 | * The ado/mssql adapter now retrieves the number of deleted or 28 | updated rows for a query without issuing a separate query. 29 | 30 | * Sequel now avoids the use of Proc.new with an implicit block, as 31 | that feature will be deprecated starting in Ruby 2.7. 32 | -------------------------------------------------------------------------------- /spec/sequel_coverage.rb: -------------------------------------------------------------------------------- 1 | require 'simplecov' 2 | 3 | def SimpleCov.sequel_coverage(opts = {}) 4 | start do 5 | enable_coverage :branch 6 | command_name SEQUEL_COVERAGE unless SEQUEL_COVERAGE == "1" 7 | add_filter{|f| f.filename.match(%r{\A#{Regexp.escape(File.dirname(__FILE__))}/})} 8 | 9 | if ENV['SEQUEL_MERGE_COVERAGE'] 10 | filter = %r{bin/sequel\z|lib/sequel/(\w+\.rb|(dataset|database|model|connection_pool|extensions|plugins)/\w+\.rb|adapters/(mock|(shared/)?postgres)\.rb)\z} 11 | add_filter{|src| src.filename !~ filter} 12 | elsif opts[:filter] 13 | add_filter{|src| src.filename !~ opts[:filter]} 14 | end 15 | 16 | if opts[:subprocesses] 17 | enable_for_subprocesses true 18 | ENV['COVERAGE'] = 'subprocess' 19 | ENV['RUBYOPT'] = "#{ENV['RUBYOPT']} -r ./spec/sequel_coverage" 20 | elsif SEQUEL_COVERAGE == 'subprocess' 21 | command_name "bin-#{$$}" 22 | self.print_error_status = false 23 | formatter SimpleCov::Formatter::SimpleFormatter 24 | end 25 | end 26 | end 27 | 28 | SEQUEL_COVERAGE = ENV.delete('COVERAGE') 29 | 30 | if SEQUEL_COVERAGE == 'subprocess' 31 | SimpleCov.sequel_coverage 32 | end 33 | -------------------------------------------------------------------------------- /lib/sequel/extensions/integer64.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # The integer64 extension changes the default type used for Integer 4 | # to be the same type as used for :Bignum. In general, this means that 5 | # instead of Integer resulting in a 32-bit database integer type, it will 6 | # result in a 64-bit database integer type. This affects the default 7 | # type used for primary_key and foreign_key when using the schema 8 | # modification methods. 9 | # 10 | # Note that it doesn't make sense to use this extension on SQLite, since 11 | # the integer type will automatically handle 64-bit integers, and it treats 12 | # the integer type specially when the column is also the primary key. 13 | # 14 | # To load the extension into the database: 15 | # 16 | # DB.extension :integer64 17 | # 18 | # Related module: Sequel::Integer64 19 | 20 | # 21 | module Sequel 22 | module Integer64 23 | private 24 | 25 | # Use same type as used for :Bignum by default for generic integer value. 26 | def type_literal_generic_integer(column) 27 | type_literal_generic_bignum_symbol(column) 28 | end 29 | end 30 | 31 | Database.register_extension(:integer64, Integer64) 32 | end 33 | -------------------------------------------------------------------------------- /lib/sequel/extensions/blank.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # The blank extension adds the blank? method to all objects (e.g. Object#blank?). 4 | # 5 | # To load the extension: 6 | # 7 | # Sequel.extension :blank 8 | 9 | [FalseClass, Object, NilClass, Numeric, String, TrueClass].each do |klass| 10 | # :nocov: 11 | if klass.method_defined?(:blank?) 12 | klass.send(:alias_method, :blank?, :blank?) 13 | end 14 | # :nocov: 15 | end 16 | 17 | class FalseClass 18 | # false is always blank 19 | def blank? 20 | true 21 | end 22 | end 23 | 24 | class Object 25 | # Objects are blank if they respond true to empty? 26 | def blank? 27 | respond_to?(:empty?) && empty? 28 | end 29 | end 30 | 31 | class NilClass 32 | # nil is always blank 33 | def blank? 34 | true 35 | end 36 | end 37 | 38 | class Numeric 39 | # Numerics are never blank (not even 0) 40 | def blank? 41 | false 42 | end 43 | end 44 | 45 | class String 46 | # Strings are blank if they are empty or include only whitespace 47 | def blank? 48 | strip.empty? 49 | end 50 | end 51 | 52 | class TrueClass 53 | # true is never blank 54 | def blank? 55 | false 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /doc/release_notes/5.38.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The jdbc/mysql adapter now supports the newer 4 | com.mysql.cj.jdbc.Driver driver. The adapter will still attempt to 5 | load the older com.mysql.jdbc.Driver if the com.mysql.cj.jdbc.Driver 6 | is not found. 7 | 8 | = Other Improvements 9 | 10 | * When testing a connection after creating a new Database instance 11 | raises an exception, the Database instance is removed from 12 | Sequel::DATABASES. 13 | 14 | * The single_table_inheritance and prepared_statements plugins now 15 | work correctly if loaded into the same class. 16 | 17 | * Database connect and disconnect errors are no longer swallowed when 18 | calling Database#create_or_replace_view, Database#server_version 19 | on PostgreSQL, or Database#create_table* on Oracle. 20 | 21 | = Backwards Compatibility 22 | 23 | * Previously, instantiating a new Database instance directly using 24 | Sequel::Database.new did not test the connection by default. That 25 | was instead handled by Sequel::Database.connect. The test 26 | connection now happens inside Database#initialize. This should only 27 | affect backwards compatibility for code that is calling 28 | Sequel::Database.new directly. 29 | -------------------------------------------------------------------------------- /spec/extensions/validation_contexts_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "Sequel::Plugins::ValidationHelpers" do 4 | before do 5 | @c = Class.new(Sequel::Model(:foo)) 6 | @c.class_eval do 7 | columns :a, :b, :c 8 | plugin :validation_contexts 9 | def validate 10 | errors.add(:a, 'bad') if a == 1 && validation_context == :create 11 | errors.add(:b, 'bad') if b == 2 && validation_context == :update 12 | errors.add(:c, 'bad') if c == 3 && validation_context == :foo 13 | end 14 | end 15 | end 16 | 17 | it "should support :validation_context option to valid?" do 18 | @c.new(:c=>3).valid?.must_equal true 19 | @c.new(:c=>3).valid?(:validation_context=>:foo).must_equal false 20 | end 21 | 22 | it "should support :validation_context option to save?" do 23 | @c.new(:c=>3).save 24 | proc{@c.new(:c=>3).save(:validation_context=>:foo)}.must_raise Sequel::ValidationFailed 25 | end 26 | 27 | it "should raise error if using a validation context on a frozen model instance" do 28 | @c.new(:c=>3).freeze.valid?.must_equal true 29 | proc{@c.new(:c=>3).freeze.valid?(:validation_context=>:foo)}.must_raise RuntimeError, TypeError 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/sequel/plugins/after_initialize.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | module Plugins 5 | # Adds an after_initialize hook to models, called after initializing 6 | # both new objects and ones loaded from the database. 7 | # 8 | # Usage: 9 | # 10 | # # Make all model subclasses support the after_initialize hook 11 | # Sequel::Model.plugin :after_initialize 12 | # 13 | # # Make the Album class support the after_initialize hook 14 | # Album.plugin :after_initialize 15 | module AfterInitialize 16 | module ClassMethods 17 | # Call after_initialize for model objects loaded from the database. 18 | def call(_) 19 | v = super 20 | v.after_initialize 21 | v 22 | end 23 | end 24 | 25 | module InstanceMethods 26 | # Call after_initialize for new model objects. 27 | def initialize(h={}) 28 | super 29 | after_initialize 30 | end 31 | 32 | # An empty after_initialize hook, so that plugins that use this 33 | # can always call super to get the default behavior. 34 | def after_initialize 35 | end 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /spec/files/unused_associations/tua.rb: -------------------------------------------------------------------------------- 1 | class TUA < Sequel::Model 2 | many_to_one :a1, :key=>:t_id, :class=>self, :is_used=>!!ENV['A1_IS_USED'] 3 | one_to_many :a2s, :key=>:t_id, :class=>self 4 | one_to_one :a3, :key=>:t_id, :class=>self 5 | a4s_opts = {:right_key=>:t_id, :left_key=>:t_id, :class=>self} 6 | include(a4s_opts[:methods_module] = Module.new) if ENV['A4S_METHODS_MODULE'] 7 | a4s_opts[:read_only] = true if ENV['A4S_READ_ONLY'] 8 | if ENV['A4S_NO_METHODS'] 9 | a4s_opts[:no_dataset_method] = a4s_opts[:no_association_method] = true 10 | a4s_opts[:adder] = a4s_opts[:remover] = a4s_opts[:clearer] = nil 11 | end 12 | many_to_many :a4s, a4s_opts 13 | one_through_one :a5, :right_key=>:t_id, :left_key=>:t_id, :class=>self, :is_used=>!!ENV['A5_IS_USED'] 14 | one_to_many :a6s, :key=>:t_id, :class=>self, :is_used=>!!ENV['A6S_IS_USED'] 15 | 16 | O = load(:id=>1, :t_id=>2) 17 | 18 | class SC < self 19 | many_to_one :a7, :key=>:t_id, :class=>self 20 | 21 | O = load(:id=>1, :t_id=>2) 22 | end 23 | end 24 | 25 | # Class with no associations 26 | class TUA2 < Sequel::Model 27 | end 28 | 29 | # Anonymous class with associations 30 | Class.new(Sequel::Model(DB[:tuas])) do 31 | many_to_one :a1, :key=>:t_id, :class=>self 32 | end 33 | -------------------------------------------------------------------------------- /lib/sequel/plugins/singular_table_names.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | module Plugins 5 | # The singular_table_names plugin changes the default 6 | # table names for subclasses to not assume a plural version. 7 | # By default, Sequel assumes table names for models use 8 | # the plural versions. 9 | # 10 | # Note that this plugin only affects subclasses of the 11 | # class it is loaded into, it does not affect the 12 | # current class. So it only makes sense to load this 13 | # into Sequel::Model itself, or a subclass of Sequel::Model 14 | # that is created via Class.new. 15 | # 16 | # Usage: 17 | # 18 | # # Don't assume pluralized table names 19 | # Sequel::Model.plugin :singular_table_names 20 | module SingularTableNames 21 | module ClassMethods 22 | # Returns the implicit table name for the model class, which is the demodulized, 23 | # underscored, name of the class. 24 | # 25 | # Artist.implicit_table_name # => :artist 26 | # Foo::ArtistAlias.implicit_table_name # => :artist_alias 27 | def implicit_table_name 28 | underscore(demodulize(name)).to_sym 29 | end 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/extensions/current_datetime_timestamp_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "current_datetime_timestamp extension" do 4 | before do 5 | @ds = Sequel.mock[:table].extension(:current_datetime_timestamp) 6 | end 7 | after do 8 | Sequel.datetime_class = Time 9 | end 10 | 11 | it "should have current_timestamp respect Sequel.datetime_class" do 12 | t = Sequel::Dataset.new(nil).current_datetime 13 | t.must_be_kind_of(Time) 14 | (Time.now - t < 0.1).must_equal true 15 | 16 | Sequel.datetime_class = DateTime 17 | t = Sequel::Dataset.new(nil).current_datetime 18 | t.must_be_kind_of(DateTime) 19 | (DateTime.now - t < (0.1/86400)).must_equal true 20 | end 21 | 22 | it "should have current_timestamp value be literalized as CURRENT_TIMESTAMP" do 23 | @ds.literal(@ds.current_datetime).must_equal 'CURRENT_TIMESTAMP' 24 | Sequel.datetime_class = DateTime 25 | @ds.literal(@ds.current_datetime).must_equal 'CURRENT_TIMESTAMP' 26 | end 27 | 28 | 29 | it "should have other Date/Time values literalized normally" do 30 | t = Time.at(1594239778).getutc 31 | @ds.literal(t).must_equal "'2020-07-08 20:22:58.000000'" 32 | @ds.literal(t.to_datetime).must_equal "'2020-07-08 20:22:58.000000'" 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/sequel/adapters/jdbc/jtds.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | Sequel::JDBC.load_driver('Java::NetSourceforgeJtdsJdbc::Driver', :JTDS) 4 | require_relative 'mssql' 5 | 6 | module Sequel 7 | module JDBC 8 | Sequel.synchronize do 9 | DATABASE_SETUP[:jtds] = proc do |db| 10 | db.extend(Sequel::JDBC::JTDS::DatabaseMethods) 11 | db.extend_datasets Sequel::MSSQL::DatasetMethods 12 | db.send(:set_mssql_unicode_strings) 13 | Java::NetSourceforgeJtdsJdbc::Driver 14 | end 15 | end 16 | 17 | module JTDS 18 | module DatabaseMethods 19 | include Sequel::JDBC::MSSQL::DatabaseMethods 20 | 21 | private 22 | 23 | # JTDS exception handling with SQLState is less accurate than with regexps. 24 | def database_exception_use_sqlstates? 25 | false 26 | end 27 | 28 | def disconnect_error?(exception, opts) 29 | super || exception.message =~ /\AInvalid state, the Connection object is closed\.\z/ 30 | end 31 | 32 | # Handle nil values by using setNull with the correct parameter type. 33 | def set_ps_arg_nil(cps, i) 34 | cps.setNull(i, cps.getParameterMetaData.getParameterType(i)) 35 | end 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /doc/release_notes/5.25.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An association_multi_add_remove plugin has been added. This plugin 4 | adds a shortcut for adding or removing multiple associated objects 5 | in a single method call: 6 | 7 | Artist.plugin :association_multi_add_remove 8 | Artist.many_to_one :albums 9 | Artist[1].add_albums([Album[2], Album[3]]) 10 | Artist[1].remove_albums([Album[4], Album[5]]) 11 | 12 | It also offers a setter method, which will add and remove associated 13 | objects as necessary: 14 | 15 | Artist[1].albums = [Album[3], Album[4]] 16 | 17 | = Other Improvements 18 | 19 | * The sharding plugin now integrates with the server_block extension. 20 | This makes it so if you retrieve a model instance inside a 21 | with_server block, saving the model instance will save it back to 22 | the shard from which it was retrieved. 23 | 24 | * Setting a default for a column on Microsoft SQL Server now works 25 | correctly if the column already has a default. 26 | 27 | * Sequel::SQL::NumericMethods#coerce no longer raises NoMethodError 28 | if the super method is not defined. This fixes some cases when 29 | comparing Date/DateTime instances to Sequel objects. 30 | 31 | * The csv_serializer plugin now avoids keyword argument separation 32 | issues on Ruby 2.7+. 33 | -------------------------------------------------------------------------------- /doc/release_notes/5.54.0.txt: -------------------------------------------------------------------------------- 1 | = New Feature 2 | 3 | * An enum plugin has been added. This plugin allows you to create 4 | model-level enums, giving names to underlying values of a column. 5 | For example: 6 | 7 | Album.plugin :enum 8 | Album.enum :status_id, good: 1, bad: 2 9 | 10 | Adds Album#good! and Album#bad! for changing the status_id to 1 or 11 | 2 respectively. It adds Album#good? and Album#bad? for checking 12 | whether the status_id is 1 or 2 respectively. It overrides 13 | Album#status_id to return :good or :bad instead of 1 or 2, 14 | respectively, and overrides Album#status_id= to accept :good or 15 | :bad instead of 1 or 2 respectively. 16 | 17 | Additionally, it adds good and bad dataset methods for filtering 18 | the model's dataset to records where status_id is 1 or 2 19 | respectively. It also adds not_good and not_bad dataset methods 20 | for filtering the model's dataset to records where status_id is not 21 | 1 or not 2 respectively. 22 | 23 | You can use :prefix and :suffix options when calling enum to 24 | add a prefix or suffix to the method names created. You can 25 | set the :override_accessors option to false to not override 26 | the accessor methods for the column, and set the :dataset_methods 27 | option to false to not add dataset methods. 28 | -------------------------------------------------------------------------------- /doc/release_notes/5.97.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A deprecated_associations plugin has been added, which supports 4 | warning if a deprecated association is used: 5 | 6 | Album.plugin :deprecated_associations 7 | Album.many_to_one :artist, deprecated: true 8 | album = Album[1] 9 | 10 | # Warnings for all of the following calls 11 | album.artist 12 | album.artist_dataset 13 | album.artist = Artist[2] 14 | Album.association_reflection(:artist) 15 | Album.eager(:artist) 16 | Album.eager_graph(:artist) 17 | Album.where(artist: Artist[1]).all 18 | 19 | You can customize the behavior using the :raise, :deduplicate, 20 | and :backtrace plugin options. 21 | 22 | * Database#rename_schema has been added on PostgreSQL to support 23 | renaming schemas. 24 | 25 | = Other Improvements 26 | 27 | * Database#drop_schema on PostgreSQL now clears all cached schema 28 | information. 29 | 30 | * Postgres::PGRange#inspect in the pg_range extension now provides 31 | more friendly output: 32 | 33 | Sequel::Postgres::PGRange.from_range(1...3, :int4).inspect 34 | # => "#" 35 | 36 | * BigDecimal typcasting when using the looser_typecasting extension 37 | now works as expected when using newer bigdecimal gem with 38 | Ruby < 2.4. 39 | -------------------------------------------------------------------------------- /spec/visibility_checking_after_hook.rb: -------------------------------------------------------------------------------- 1 | require_relative "visibility_checking" 2 | model_subclasses = [] 3 | 4 | [Sequel::Database, Sequel::Dataset, Sequel::Model, Sequel::Model.singleton_class].each do |c| 5 | VISIBILITY_CHANGES.concat(VisibilityChecker.visibility_changes(c).map{|v| [v, c.inspect]}) 6 | end 7 | 8 | Sequel::Model.singleton_class.class_eval do 9 | prepend(Module.new do 10 | private 11 | define_method(:inherited) do |sc| 12 | model_subclasses << sc 13 | super(sc) 14 | end 15 | end) 16 | end 17 | 18 | Minitest::HooksSpec.class_eval do 19 | after do 20 | path, lineno = method(@NAME).source_location 21 | check = [] 22 | Sequel::DATABASES.each do |db| 23 | check.push(db.singleton_class) 24 | check.push(db.dataset.singleton_class) 25 | end 26 | Sequel::DATABASES.clear 27 | 28 | subclasses = model_subclasses.dup 29 | model_subclasses.clear 30 | check.concat(subclasses) 31 | check.concat(subclasses.map(&:singleton_class)) 32 | check.concat(subclasses.map{|c| c.dataset.singleton_class if c.instance_variable_get(:@dataset)}) 33 | 34 | check.each do |c| 35 | next unless c 36 | VISIBILITY_CHANGES.concat(VisibilityChecker.visibility_changes(c).map{|v| [v, "#{path}:#{lineno}"]}) 37 | end 38 | end 39 | end 40 | 41 | -------------------------------------------------------------------------------- /spec/extensions/pg_timestamptz_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "pg_timestamptz extension" do 4 | before do 5 | @db = Sequel.mock(:host=>'postgres').extension :pg_timestamptz 6 | end 7 | 8 | it "should use timestamptz as default timestamp type" do 9 | @db.create_table(:t){Time :t; DateTime :tz; Time :ot, :only_time=>true} 10 | @db.sqls.must_equal ['CREATE TABLE "t" ("t" timestamptz, "tz" timestamptz, "ot" time)'] 11 | end 12 | 13 | it "should automatically cast Time and DateTime instances using TIMESTAMP WITH TIME ZONE when using auto_cast_date_and_time " do 14 | t = Time.utc(2000) 15 | dt = DateTime.new(2000) 16 | expected = "TIMESTAMP WITH TIME ZONE '2000-01-01 00:00:00.000000+0000'" 17 | 18 | @db.timezone = :utc 19 | @db.extension :auto_cast_date_and_time 20 | @db.literal(t).must_equal expected 21 | @db.literal(dt).must_equal expected 22 | 23 | db = Sequel.mock(:host=>'postgres').extension :auto_cast_date_and_time 24 | db.extension :pg_timestamptz 25 | db.literal(t).must_equal expected 26 | db.literal(dt).must_equal expected 27 | end 28 | 29 | it "should use timestamptz when casting" do 30 | @db.get(Sequel.cast('a', Time)) 31 | @db.sqls.must_equal ["SELECT CAST('a' AS timestamptz) AS \"v\" LIMIT 1"] 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /doc/release_notes/5.81.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A temporarily_release_connection Database extension has been added, 4 | designed for multithreaded transactional testing. 5 | 6 | This allows one thread to start a transaction, and then release 7 | the connection back for usage by the connection pool, so that 8 | other threads can operate on the connection object safely inside 9 | the transaction. This requires the connection pool be limited 10 | to a single connection, to ensure that the released connection 11 | can be reacquired. It's not perfect, because if the connection 12 | is disconnected and removed from the pool while temporarily 13 | released, there is no way to handle that situation correctly. 14 | Example: 15 | 16 | DB.transaction(rollback: :always, auto_savepoint: true) do |conn| 17 | DB.temporarily_release_connection(conn) do 18 | # Other threads can operate on connection safely inside 19 | # the transaction 20 | yield 21 | end 22 | end 23 | 24 | = Other Improvements 25 | 26 | * In the caller_logging and provenance extensions, Ruby internal 27 | caller locations are skipped when trying to locate the appropriate 28 | caller line to include. 29 | 30 | * A couple ignored block warnings in plugin apply methods have been 31 | fixed on Ruby 3.4. 32 | -------------------------------------------------------------------------------- /lib/sequel/extensions/pg_loose_count.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # The pg_loose_count extension looks at the table statistics 4 | # in the PostgreSQL system tables to get a fast approximate 5 | # count of the number of rows in a given table: 6 | # 7 | # DB.loose_count(:table) # => 123456 8 | # 9 | # It can also support schema qualified tables: 10 | # 11 | # DB.loose_count(Sequel[:schema][:table]) # => 123456 12 | # 13 | # How accurate this count is depends on the number of rows 14 | # added/deleted from the table since the last time it was 15 | # analyzed. If the table has not been vacuumed or analyzed 16 | # yet, this can return 0 or -1 depending on the PostgreSQL 17 | # version in use. 18 | # 19 | # To load the extension into the database: 20 | # 21 | # DB.extension :pg_loose_count 22 | # 23 | # Related module: Sequel::Postgres::LooseCount 24 | 25 | # 26 | module Sequel 27 | module Postgres 28 | module LooseCount 29 | # Look at the table statistics for the given table to get 30 | # an approximate count of the number of rows. 31 | def loose_count(table) 32 | from(:pg_class).where(:oid=>regclass_oid(table)).get(Sequel.cast(:reltuples, Integer)) 33 | end 34 | end 35 | end 36 | 37 | Database.register_extension(:pg_loose_count, Postgres::LooseCount) 38 | end 39 | 40 | -------------------------------------------------------------------------------- /doc/release_notes/5.58.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * Dataset#merge and related #merge_* methods have been added for the 4 | MERGE statement. MERGE is supported on PostgreSQL 15+, Oracle, 5 | Microsoft SQL Server, DB2, H2, HSQLDB, and Derby. You can use MERGE 6 | to insert, update, and/or delete in a single query. You call 7 | the #merge_* methods to setup the MERGE statement, and #merge to 8 | execute it on the database: 9 | 10 | ds = DB[:m1] 11 | merge_using(:m2, i1: :i2). 12 | merge_insert(i1: :i2, a: Sequel[:b]+11). 13 | merge_delete{a > 30}. 14 | merge_update(i1: Sequel[:i1]+:i2+10, a: Sequel[:a]+:b+20) 15 | 16 | ds.merge 17 | # MERGE INTO m1 USING m2 ON (i1 = i2) 18 | # WHEN NOT MATCHED THEN INSERT (i1, a) VALUES (i2, (b + 11)) 19 | # WHEN MATCHED AND (a > 30) THEN DELETE 20 | # WHEN MATCHED THEN UPDATE SET i1 = (i1 + i2 + 10), a = (a + b + 20) 21 | 22 | On PostgreSQL, the following additional MERGE related methods are 23 | available: 24 | 25 | * #merge_do_nothing_when_matched 26 | * #merge_do_nothing_when_not_matched 27 | 28 | * A :disable_split_materialized Database option is now supported on 29 | MySQL. This disables split_materialized support in the optimizer, 30 | working around a bug in MariaDB 10.5+ that causes failures in 31 | Sequel's association tests. 32 | -------------------------------------------------------------------------------- /lib/sequel/extensions/symbol_aref_refinement.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # The symbol_aref_refinement extension adds a refinement that makes 4 | # Symbol#[] support Symbol, #Sequel::SQL::Indentifier, and 5 | # Sequel::SQL::QualifiedIdentifier instances, returning appropriate 6 | # Sequel::SQL::QualifiedIdentifier instances. It's designed as a 7 | # shortcut so that instead of: 8 | # 9 | # Sequel[:table][:column] # table.column 10 | # 11 | # you can just write: 12 | # 13 | # :table[:column] # table.column 14 | # 15 | # To load the extension: 16 | # 17 | # Sequel.extension :symbol_aref_refinement 18 | # 19 | # To enable the refinement for the current file: 20 | # 21 | # using Sequel::SymbolAref 22 | # 23 | # If you would like this extension to be enabled globally instead 24 | # of as a refinement, use the symbol_aref extension. 25 | # 26 | # Related module: Sequel::SymbolAref 27 | 28 | # :nocov: 29 | raise(Sequel::Error, "Refinements require ruby 2.0.0 or greater") unless RUBY_VERSION >= '2.0.0' 30 | # :nocov: 31 | 32 | module Sequel::SymbolAref 33 | refine Symbol do 34 | def [](v) 35 | case v 36 | when Symbol, Sequel::SQL::Identifier, Sequel::SQL::QualifiedIdentifier 37 | Sequel::SQL::QualifiedIdentifier.new(self, v) 38 | else 39 | super 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /doc/release_notes/4.5.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An mssql_optimistic_locking plugin has been added. This is similar 4 | to the regular optimistic_locking plugin, but instead of using an 5 | integer lock column, it uses a timestamp/rowversion lock column. 6 | 7 | * Database#create_table with the :temp=>true option on PostgreSQL now 8 | supports an :on_commit option. This option can be set to :drop or 9 | :delete_rows to either drop or empty the temporary table on 10 | transaction commit. 11 | 12 | = Other Improvements 13 | 14 | * Dataset#insert no longer errors on PostgreSQL if the related table 15 | is a placeholder literal string. 16 | 17 | * Unique constraints are now copied when emulating alter_table 18 | operations on SQLite. 19 | 20 | * Clob column values are no longer returned as SQL::Blob instances 21 | by the db2 and ibmdb adapters unless use_clob_as_blob is true. 22 | 23 | * SQL::Blob objects now work correctly as prepared statement 24 | arguments in the jdbc/db2 adapter if use_clob_as_blob is false. 25 | 26 | = Backwards Compatibility 27 | 28 | * The Model.primary_key array for models with composite keys is now 29 | frozen. 30 | 31 | * On DB2, use_clob_as_blob now defaults to false instead of true. 32 | 33 | * Sequel no longer uses RubyForge. The Sequel website is now located 34 | at http://sequel.jeremyevans.net. 35 | -------------------------------------------------------------------------------- /doc/release_notes/5.67.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A set_literalizer extension has been added, for treating Set 4 | instances in datasets similar to Array instances: 5 | 6 | DB.extension :set_literalizer 7 | DB[:table].where(column: Set.new([1, 2, 3])) 8 | # SELECT FROM table WHERE (column IN (1, 2, 3)) 9 | 10 | = Improvements 11 | 12 | * Sequel now avoids the use of singleton classes for datasets on Ruby 13 | 2.4+, instead creating a regular subclass whenever a dataset would 14 | be extended via #extension or #with_extend. This significantly 15 | improves performance, up to 20-40% for common dataset usage, 16 | because it avoids creating new singleton classes for every dataset 17 | clone, and it allows for cached method lookup. 18 | 19 | * Database#tables and #views now support a :qualify option on Microsoft 20 | SQL Server to returned qualified identifiers. 21 | 22 | * The schema_dumper extension can now dump tables in non-default schemas 23 | when using Microsoft SQL Server. 24 | 25 | * The schema_dumper extension now correctly dumps string column sizes 26 | when using Microsoft SQL Server. 27 | 28 | = Backwards Compatibility 29 | 30 | * Calling Sequel::Dataset.register_extension where the second argument 31 | is not a module now issues a deprecation warning. Support for this 32 | will be removed in Sequel 6. 33 | -------------------------------------------------------------------------------- /doc/release_notes/5.84.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The pg_json_ops extension now supports the json_exists, json_value, 4 | and json_query functions added in PostgreSQL 17: 5 | 6 | Sequel.extension :pg_json_ops 7 | j = Sequel.pg_json_op(:jsonb_column) 8 | j.exists('$.foo') # json_exists(jsonb_column, '$.foo') 9 | j.value('$.foo') # json_value(jsonb_column, '$.foo') 10 | j.query('$.foo') # json_query(jsonb_column, '$.foo') 11 | 12 | j.exists('$.foo', passing: {a: 1}) # json_exists(jsonb_column, '$.foo' PASSING 1 AS a) 13 | j.value('$.foo', returning: Time) # json_value(jsonb_column, '$.foo' RETURNING timestamp) 14 | j.query('$.foo', wrapper: true) # json_query(jsonb_column, '$.foo' WITH WRAPPER) 15 | 16 | All clauses supported by PostgreSQL 17 are supported via options 17 | (supported options differ per method): 18 | 19 | * :on_error : ON ERROR 20 | * :on_empty : ON EMPTY 21 | * :passing : PASSING 22 | * :returning : RETURNING 23 | * :wrapper : WITH WRAPPER | OMIT QUOTES 24 | 25 | * On SQLite, Database#create_table now supports a :using option to 26 | create a virtual table: 27 | 28 | DB.create_table(:t, using: 'fts5(email)') 29 | # CREATE VIRTUAL TABLE t USING fts5(email) 30 | 31 | = Other Improvements 32 | 33 | * The gem size has been reduced 25% by removing documentation. 34 | -------------------------------------------------------------------------------- /www/public/css/links.css: -------------------------------------------------------------------------------- 1 | section.links { 2 | background-color: #FFF; 3 | border-radius: 12px; 4 | box-shadow: 0 24px 64px rgba(0,0,0,.15); 5 | } 6 | 7 | ul.assets { 8 | display: grid; 9 | grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); 10 | gap: calc(var(--gap) / 2); 11 | list-style: none; 12 | padding: 0; 13 | margin: calc(var(--gap) / 2) var(--gap); 14 | } 15 | @media (max-width: 640px) { 16 | ul.assets { 17 | grid-template-columns: 1fr; 18 | } 19 | } 20 | 21 | li.assets__item { 22 | display: grid; 23 | grid-template-rows: auto max-content; 24 | border-radius: 8px; 25 | border: 1px solid var(--border); 26 | } 27 | 28 | a.assets__container { 29 | display: flex; 30 | align-items: center; 31 | justify-content: center; 32 | padding: var(--gap) 32px; 33 | box-sizing: border-box; 34 | background-image: url("../images/transparent-background.svg"); 35 | background-size: 32px; 36 | } 37 | 38 | @media (max-width: 480px) { 39 | img.assets__img { 40 | width: 80%; 41 | } 42 | } 43 | 44 | p.assets__description { 45 | border-top: 1px solid var(--border); 46 | background-color: #f4f4f4; 47 | margin: 0; 48 | padding: 12px 16px; 49 | font-size: 12px; 50 | text-align: center; 51 | border-radius: 0 0 8px 8px; 52 | color: #666; 53 | } 54 | 55 | a.parens:before { 56 | content: "("; 57 | } 58 | a.parens:after { 59 | content: ")"; 60 | } 61 | -------------------------------------------------------------------------------- /doc/release_notes/5.37.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * Model#column_previously_was and #column_previously_changed? have 4 | been added to the dirty plugin, for getting the previous values 5 | of the column before saving and for whether there were changes 6 | before saving. 7 | 8 | Model#column_previously_changed? accepts :from and :to options 9 | to allow you to more easily determine if the value changed from 10 | and/or to specific values. 11 | 12 | This information was previously obtainable via 13 | Model#previous_changes, but these new methods offer a friendlier 14 | interface. 15 | 16 | * Postgres::PGRow::{Array,Hash}Row#op has been added to the 17 | pg_row_ops extension if the pg_row extension is loaded. This 18 | is similar to how the pg_array_ops, pg_hstore_ops, and 19 | pg_json_ops and #op method to their objects. This makes it 20 | easier to perform row operations on literal rows. 21 | 22 | = Other Improvements 23 | 24 | * The schema_dumper extension now supports more unsigned numeric 25 | types, such as "decimal(7,2) unsigned" and "real unsigned". 26 | 27 | * IntegerMigrator now raises an Migrator::Error if attempting to 28 | migrate down when there are migration files missing and needed for 29 | the down migration. Previously, IntegerMigrator would not raise an 30 | exception and would make no database changes in this case. 31 | -------------------------------------------------------------------------------- /lib/sequel/extensions/set_literalizer.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # The set_literalizer extension should no longer be used, as Sequel 4 | # now supports Set values by default. For backwards compatibility 5 | # the set_literalizer extension will treat a set that contains only 6 | # 2 element arrays as a condition specifier (matching the behavior 7 | # for arrays where all elements are 2 element arrays). This is not 8 | # compatible with Sequel's current default behavior. If you are 9 | # relying on this behavior, it is recommended you convert the set 10 | # to an array. 11 | # 12 | # Related module: Sequel::Dataset::SetLiteralizer 13 | 14 | module Sequel 15 | # SEQUEL6: Remove 16 | Sequel::Deprecation.deprecate("The set_literalizer extension", "Sequel now supports set literalization by default") 17 | 18 | class Dataset 19 | module SetLiteralizer 20 | private 21 | 22 | # Allow using sets as condition specifiers. 23 | def filter_expr(expr = nil, &block) 24 | if expr.is_a?(Set) 25 | expr 26 | else 27 | super 28 | end 29 | end 30 | 31 | # Literalize Set instances by converting the set to array. 32 | def literal_set_append(sql, v) 33 | literal_append(sql, v.to_a) 34 | end 35 | end 36 | 37 | register_extension(:set_literalizer, SetLiteralizer) 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /doc/release_notes/5.45.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A auto_validations_constraint_validations_presence_message plugin 4 | has been added that provides integration for the auto_validations 5 | and constraint_validations plugin in the following conditions: 6 | 7 | * The column has a NOT NULL constraint 8 | * The column has a presence constraint validation with both 9 | the :message and :allow_nil options used. 10 | 11 | In this case, when saving a nil value in the column, the plugin 12 | will make it so the more specific message from the presence 13 | constraint validation is used, instead of the generic message 14 | from auto_validations. 15 | 16 | = Other Improvements 17 | 18 | * On SQLite 3.35.0+, Sequel now uses ALTER TABLE DROP COLUMN for 19 | dropping columns, instead of emulating the dropped column by 20 | recreating the table. 21 | 22 | * The Dataset#with :materialized option is now supported on SQLite 23 | 3.35.0+ for specifying whether common table expressions should be 24 | materialized. 25 | 26 | * The odbc adapter now correct handles boolean columns with NULL 27 | values. Previously, such values were returned as false instead 28 | of nil. 29 | 30 | = Backwards Compatibility 31 | 32 | * The change to use ALTER TABLE DROP COLUMN on SQLite 3.35.0+ can 33 | cause backwards compatibility issues if SQLite 3.35.0+ does 34 | not allow dropping the column. 35 | -------------------------------------------------------------------------------- /doc/release_notes/4.16.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * Model#qualified_pk_hash has been added, which is similar to 4 | Model#pk_hash, but uses qualified keys. 5 | 6 | * Dataset#distinct now accepts a virtual row block. 7 | 8 | * Database#drop_table with :foreign=>true option now drops foreign 9 | tables on PostgreSQL. Database#create_table with :foreign option 10 | is now reversible on PostgreSQL. 11 | 12 | = Other Improvements 13 | 14 | * Sequel::Model.cache_associations = false now skips the database's 15 | schema cache when loading the schema for a model. This fixes 16 | some issues in environments that use code reloading. 17 | 18 | * Database#create_table? and #create_join_table? no longer use 19 | IF NOT EXISTS if indexes are being created. 20 | 21 | * Model.primary_key_hash and .qualified_primary_key_hash have been 22 | optimized. 23 | 24 | * validates_unique in the validation_helpers plugin now uses a 25 | qualified primary key if the model's dataset is joined. This fixes 26 | a case when the auto_validations and class_table_inheritance 27 | plugins are used together. 28 | 29 | * Disconnect errors are now recognized in the postgres adapter when 30 | SSL is used for connecting. 31 | 32 | * Empty string default values are no longer converted to nil default 33 | values on MySQL. 34 | 35 | * Database#foreign_key_list now works correctly on Microsoft SQL 36 | Server 2005. 37 | -------------------------------------------------------------------------------- /doc/release_notes/5.6.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * Running migrations using one of the included migrators on separate 4 | Database objects in separate threads simultaneously is now 5 | supported. Previously, the migrators were not thread-safe. 6 | 7 | * On Ruby 2.5+, :db_type entries in the schema hashes are now deduped 8 | for a slight memory savings when using many columns with the same 9 | database type. 10 | 11 | * The schema_caching extension now freezes string values in the 12 | resulting hashes, just as the default schema parsing code started 13 | doing in 5.5.0. 14 | 15 | * The schema_caching extension now supports the :callable_default 16 | schema values used by the pg_json, pg_array, and pg_hstore 17 | extensions, by removing the entry before caching and resetting it 18 | after restoring the cache. 19 | 20 | * Identifier mangling rules are now respected when renaming columns on 21 | Microsoft SQL Server. 22 | 23 | = Backwards Compatibility 24 | 25 | * The migrator internals were modified in order to support 26 | thread-safety. The private Migrator#remove_migration_classes 27 | method has been removed, and #load_migration_file now returns the 28 | migration object/class instead of populating Migration.descendants. 29 | Migration.descendants is now only used for temporary storage, and 30 | will no longer contain all migration objects/classes used by the 31 | migrator. 32 | -------------------------------------------------------------------------------- /lib/sequel/extensions/dataset_run.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # The dataset_run extension is designed for cases where you want 4 | # to use dataset methods to build a query, but want to run that 5 | # query without returning a result. The most common need would 6 | # be to easily use placeholders in an SQL string, which Database#run 7 | # does not support directly. 8 | # 9 | # You can load this extension into specific datasets: 10 | # 11 | # ds = DB["GRANT SELECT ON ? TO ?", :table, :user] 12 | # ds = ds.extension(:dataset_run) 13 | # ds.run 14 | # 15 | # Or you can load it into all of a database's datasets, which 16 | # is probably the desired behavior if you are using this extension: 17 | # 18 | # DB.extension(:dataset_run) 19 | # DB["GRANT SELECT ON ? TO ?", :table, :user].run 20 | # 21 | # Related module: Sequel::DatasetRun 22 | 23 | # 24 | module Sequel 25 | module DatasetRun 26 | # Run the dataset's SQL on the database. Returns NULL. This is 27 | # useful when you want to run SQL without returning a result. 28 | # 29 | # DB["GRANT SELECT ON ? TO ?", :table, :user].run 30 | # # GRANT SELECT ON "table" TO "user" 31 | def run 32 | if server = @opts[:server] 33 | db.run(sql, :server=>server) 34 | else 35 | db.run(sql) 36 | end 37 | end 38 | end 39 | 40 | Dataset.register_extension(:dataset_run, DatasetRun) 41 | end 42 | -------------------------------------------------------------------------------- /doc/mssql_stored_procedures.rdoc: -------------------------------------------------------------------------------- 1 | = Stored Procedures in MSSQL 2 | 3 | This guide documents the workaround implemented to allow executing stored procedures 4 | in MSSQL, as well as getting the value of output variables. 5 | 6 | == Simple Execution 7 | 8 | The following stored procedure is used as an example: 9 | 10 | CREATE PROCEDURE dbo.SequelTest( 11 | @Input varchar(25), 12 | @Output int OUTPUT 13 | ) 14 | AS 15 | SET @Output = LEN(@Input) 16 | RETURN 0 17 | 18 | Execute it as follows: 19 | 20 | DB.call_mssql_sproc(:SequelTest, {args: ['Input String', :output]}) 21 | 22 | Use the +:output+ symbol to denote an output variable. The result will contain a 23 | hash of the output variables, as well as the result code and number of affected rows: 24 | 25 | {:result => 0, :numrows => 1, :var1 => "1"} 26 | 27 | Output variables will be strings by default. To specify their type, include the 28 | SQL type: 29 | 30 | DB.call_mssql_sproc(:SequelTest, {args: ['Input String', [:output, 'int']]}) 31 | 32 | Result: 33 | 34 | {:result => 0, :numrows => 1, :var1 => 1} 35 | 36 | Output variables will be named +var#{n}+ where n is their zero indexed position 37 | in the parameter list. To name the output variable, include their name: 38 | 39 | DB.call_mssql_sproc(:SequelTest, {args: ['Input String', [:output, nil, 'Output']]}) 40 | 41 | Result: 42 | 43 | {:result => 0, :numrows => 1, :output => "1"} 44 | -------------------------------------------------------------------------------- /spec/extensions/auto_restrict_eager_graph_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "auto_restrict_eager_graph plugin" do 4 | before do 5 | @db = Sequel.mock 6 | @c = Class.new(Sequel::Model(@db[:items])) 7 | @c.plugin :auto_restrict_eager_graph 8 | end 9 | 10 | it "should restrict eager_graph for associations with blocks without :graph_* options" do 11 | @c.many_to_one :cs, :class=>@c do |ds| ds.where(:x) end 12 | proc{@c.eager_graph(:cs)}.must_raise Sequel::Error 13 | end 14 | 15 | it "should not restrict eager_graph for associations without blocks" do 16 | @c.many_to_one :cs, :class=>@c 17 | @c.eager_graph(:cs).sql.must_equal "SELECT * FROM items LEFT OUTER JOIN items AS cs ON (cs.id = items.cs_id)" 18 | end 19 | 20 | it "should not restrict eager_graph for associations with :graph_* options" do 21 | @c.many_to_one :cs, :class=>@c, :graph_conditions=>{:x=>true} do |ds| ds.where(:x) end 22 | @c.eager_graph(:cs).sql.must_equal "SELECT * FROM items LEFT OUTER JOIN items AS cs ON ((cs.id = items.cs_id) AND (cs.x IS TRUE))" 23 | end 24 | 25 | it "should not restrict eager_graph for associations with :allow_eager_graph option" do 26 | @c.many_to_one :cs, :class=>@c, :allow_eager_graph=>true do |ds| ds.where(:x) end 27 | @c.eager_graph(:cs).sql.must_equal "SELECT * FROM items LEFT OUTER JOIN items AS cs ON (cs.id = items.cs_id)" 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/sequel/plugins/skip_create_refresh.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | module Plugins 5 | # The skip_create_refresh plugin skips the 6 | # refresh after saving a new model object. Sequel does the 7 | # refresh by default to make sure all columns are populated, which is 8 | # necessary so that database defaults work correctly. 9 | # 10 | # This plugin is mostly for performance reasons where you 11 | # want to save the cost of select statement after the insert, 12 | # but it could also help cases where records are not 13 | # immediately available for selection after insertion. 14 | # 15 | # Note that Sequel by default does not attempt to refresh records when 16 | # updating existing model objects, only when inserting new 17 | # model objects. 18 | # 19 | # Usage: 20 | # 21 | # # Make all model subclass instances skip refreshes when saving 22 | # # (called before loading subclasses) 23 | # Sequel::Model.plugin :skip_create_refresh 24 | # 25 | # # Make the Album class skip refreshes when saving 26 | # Album.plugin :skip_create_refresh 27 | module SkipCreateRefresh 28 | module InstanceMethods 29 | private 30 | # Do nothing instead of refreshing the record inside of save. 31 | def _save_refresh 32 | nil 33 | end 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/sequel/plugins/inspect_pk.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | module Plugins 5 | # The inspect_pk plugin includes the pk right next to the 6 | # model name in inspect, allowing for easily copying and 7 | # pasting to retrieve a copy of the object: 8 | # 9 | # Album.with_pk(1).inspect 10 | # # default: # 11 | # # with inspect_pk: # 12 | # 13 | # Usage: 14 | # 15 | # # Make all model instances include pk in inspect output 16 | # Sequel::Model.plugin :inspect_pk 17 | # 18 | # # Make Album instances include pk in inspect output 19 | # Album.plugin :inspect_pk 20 | module InspectPk 21 | module InstanceMethods 22 | private 23 | 24 | # The primary key value to include in the inspect output, if any. 25 | # For composite primary keys, this only includes a value if all 26 | # fields are present. 27 | def inspect_pk 28 | if primary_key && (pk = self.pk) && (!(Array === pk) || pk.all?) 29 | pk 30 | end 31 | end 32 | 33 | # Include the instance's primary key in the output. 34 | def inspect_prefix 35 | if v = inspect_pk 36 | "#{super}[#{v.inspect}]" 37 | else 38 | super 39 | end 40 | end 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/sequel/plugins/columns_updated.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | module Plugins 5 | # The columns_updated plugin stores the columns hash used in the 6 | # UPDATE query when saving the instance, and makes it available 7 | # in the after_update and after_save hooks via the +columns_updated+ 8 | # accessor. The data is cleared before returning from +save+. 9 | # 10 | # Usage: 11 | # 12 | # # Make all model subclasses store the columns hash used for updating 13 | # Sequel::Model.plugin :columns_updated 14 | # 15 | # # Make the Album class store the columns hash used for updating 16 | # Album.plugin :columns_updated 17 | module ColumnsUpdated 18 | module InstanceMethods 19 | private 20 | 21 | # The hash used for updating records. This should only be called 22 | # in the after_update and after_save hooks. 23 | attr_reader :columns_updated 24 | 25 | # Store the hash used for updating the record, so it can be accessed 26 | # in the after_hooks. 27 | def _update_columns(columns_updated) 28 | @columns_updated = columns_updated 29 | super 30 | end 31 | 32 | # Unset the updated columns hash before returning from save. 33 | def _save(opts) 34 | super 35 | ensure 36 | @columns_updated = nil 37 | end 38 | end 39 | end 40 | end 41 | end 42 | 43 | -------------------------------------------------------------------------------- /doc/release_notes/5.2.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A pg_extended_date_support extension has been added. This 4 | extension adds support for infinite and BC dates/timestamps on 5 | PostgreSQL. 6 | 7 | The postgres adapter already had a convert_infinite_timestamps 8 | setting, but it wasn't supported in the jdbc/postgresql adapter 9 | and it didn't handle BC dates/timestamps. Setting a non-default 10 | convert_infinite_timestamps setting in the postgres adapter will 11 | now automatically load the extension for backwards compatibility. 12 | 13 | The pg_extended_date_support extension by default just fixes the 14 | handling of BC dates/timestamps. To get it to handle infinite 15 | timestamps, you need to choose the appropriate setting for your 16 | application: 17 | 18 | DB.extension :pg_extended_date_support 19 | DB.convert_infinite_timestamps = :string # or :float or :nil 20 | 21 | This extension also enables the handling of timezone offsets 22 | with seconds, which is not natively supported by ruby's Time 23 | class in ruby <2.5. 24 | 25 | = Improvements 26 | 27 | * The jdbc/mysql adapter now handles smallint unsigned and 28 | integer unsigned column types where the value for the column 29 | is outside of the range of a Java short or integer. 30 | 31 | * Sequel::Model.inherited no longer modifies an existing @dataset 32 | instance variable if one has already been set. This fixes a 33 | regression that was introduced in Sequel 5.0.0. 34 | -------------------------------------------------------------------------------- /lib/sequel/plugins/empty_failure_backtraces.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | module Plugins 5 | # The empty_failure_backtraces plugin uses empty backtraces when raising HookFailed and ValidationFailed 6 | # exceptions. This can be significantly faster, and if you are using these exceptions for 7 | # flow control, you do not need the backtraces. This plugin is about 10% faster on CRuby 8 | # and 10-15x faster on JRuby 9.2.7.0+. This does not have an effect on JRuby <9.2.7.0. 9 | # 10 | # Usage: 11 | # 12 | # # Make all model subclass instances use empty backtraces for HookFailed 13 | # # and ValidationFailed exceptions (called before loading subclasses) 14 | # Sequel::Model.plugin :empty_failure_backtraces 15 | # 16 | # # Make the Album class use empty backtraces for HookFailed and ValidationFailed exceptions 17 | # Album.plugin :empty_failure_backtraces 18 | module EmptyFailureBacktraces 19 | module InstanceMethods 20 | private 21 | 22 | # Use empty backtrace for HookFailed exceptions. 23 | def hook_failed_error(msg) 24 | e = super 25 | e.set_backtrace([]) 26 | e 27 | end 28 | 29 | # Use empty backtrace for ValidationFailed exceptions. 30 | def validation_failed_error 31 | e = super 32 | e.set_backtrace([]) 33 | e 34 | end 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /doc/release_notes/4.3.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The tree and rcte_tree plugins now support composite keys. 4 | 5 | * An error_sql Database extension has been added. This extension 6 | adds the DatabaseError#sql method, which should return the 7 | database query that caused the error. This is useful for 8 | drivers that don't include the SQL used as part of the error 9 | message. 10 | 11 | = Other Improvements 12 | 13 | * Empty blobs are now literalized correctly on MySQL. 14 | 15 | * Empty arrays are now literalized correctly on PostgreSQL <8.4. 16 | 17 | * In the pagination extension, Dataset#page_count is now 1 even if 18 | the dataset is empty. This fixes issues with last_page? and 19 | page_range returning bad values for empty datasets. 20 | 21 | * In the pagination extension, calling Dataset#each_page without a 22 | block now returns an Enumerator. 23 | 24 | * Dataset#qualify and Sequel.delay now work together, qualifying 25 | the object returned by the delayed evaluation. 26 | 27 | * Migrator.migrator_class is now a public method. 28 | 29 | * The PostgreSQL citext type is now recognized as a string. 30 | 31 | * Another disconnect error is now recognized in the jdbc/as400 32 | adapter. 33 | 34 | * Guides about using and creating Sequel extensions and model 35 | plugins have been added. 36 | 37 | = Backwards Compatibility 38 | 39 | * If you were expecting Dataset#page_count on a empty paginated 40 | dataset to return 0, you need to update your code. 41 | -------------------------------------------------------------------------------- /lib/sequel/extensions/any_not_empty.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # The any_not_empty extension changes the behavior of Dataset#any? 4 | # if called without a block. By default, this method uses the 5 | # standard Enumerable behavior of enumerating results and seeing 6 | # if any result is not false or nil. With this extension, it 7 | # just checks whether the dataset is empty. This approach can 8 | # be much faster if the dataset is currently large. 9 | # 10 | # DB[:table].any? 11 | # # SELECT * FROM table 12 | # 13 | # DB[:table].extension(:any_not_empty).any? 14 | # # SELECT 1 as one FROM table LIMIT 1 15 | # 16 | # You can load this extension into specific datasets: 17 | # 18 | # ds = DB[:table] 19 | # ds = ds.extension(:any_not_empty) 20 | # 21 | # Or you can load it into all of a database's datasets, which 22 | # is probably the desired behavior if you are using this extension: 23 | # 24 | # DB.extension(:any_not_empty) 25 | # 26 | # Note that this can result in any? returning a different result if 27 | # the dataset has a row_proc that can return false or nil. 28 | # 29 | # Related module: Sequel::AnyNotEmpty 30 | 31 | # 32 | module Sequel 33 | module AnyNotEmpty 34 | # If a block is not given, return whether the dataset is not empty. 35 | def any?(*a) 36 | if !a.empty? || defined?(yield) 37 | super 38 | else 39 | !empty? 40 | end 41 | end 42 | end 43 | 44 | Dataset.register_extension(:any_not_empty, AnyNotEmpty) 45 | end 46 | -------------------------------------------------------------------------------- /lib/sequel/plugins/async_thread_pool.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | extension 'async_thread_pool' 5 | 6 | module Plugins 7 | # The async_thread_pool plugin makes it slightly easier to use the async_thread_pool 8 | # Database extension with models. It makes Model.async return an async dataset for the 9 | # model, and support async behavior for #destroy, #with_pk, and #with_pk! for model 10 | # datasets: 11 | # 12 | # # Will load the artist with primary key 1 asynchronously 13 | # artist = Artist.async.with_pk(1) 14 | # 15 | # You must load the async_thread_pool Database extension into the Database object the 16 | # model class uses in order for async behavior to work. 17 | # 18 | # Usage: 19 | # 20 | # # Make all model subclass datasets support support async class methods and additional 21 | # # async dataset methods 22 | # Sequel::Model.plugin :async_thread_pool 23 | # 24 | # # Make the Album class support async class method and additional async dataset methods 25 | # Album.plugin :async_thread_pool 26 | module AsyncThreadPool 27 | module ClassMethods 28 | Plugins.def_dataset_methods(self, :async) 29 | end 30 | 31 | module DatasetMethods 32 | [:destroy, :with_pk, :with_pk!].each do |meth| 33 | ::Sequel::Database::AsyncThreadPool::DatasetMethods.define_async_method(self, meth) 34 | end 35 | end 36 | end 37 | end 38 | end 39 | 40 | -------------------------------------------------------------------------------- /lib/sequel/extensions/looser_typecasting.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # The LooserTypecasting extension loosens the default database typecasting 4 | # for the following types: 5 | # 6 | # :float :: use to_f instead of Float() 7 | # :integer :: use to_i instead of Integer() 8 | # :decimal :: use 0.0 for unsupported strings 9 | # :string :: silently allow hash and array conversion to string 10 | # 11 | # This also removes bytesize checks for string inputs for float, integer 12 | # and decimal conversions. 13 | # 14 | # To load the extension into the database: 15 | # 16 | # DB.extension :looser_typecasting 17 | # 18 | # Related module: Sequel::LooserTypecasting 19 | 20 | # 21 | module Sequel 22 | module LooserTypecasting 23 | private 24 | 25 | # Typecast the value to a Float using to_f instead of Kernel.Float 26 | def typecast_value_float(value) 27 | value.to_f 28 | end 29 | 30 | # Typecast the value to an Integer using to_i instead of Kernel.Integer 31 | def typecast_value_integer(value) 32 | value.to_i 33 | end 34 | 35 | # Typecast the value to an String using to_s instead of Kernel.String 36 | def typecast_value_string(value) 37 | value.to_s 38 | end 39 | 40 | # Typecast invalid BigDecimal to 0.0. 41 | def _typecast_value_string_to_decimal(value) 42 | BigDecimal(value) 43 | rescue 44 | BigDecimal('0.0') 45 | end 46 | end 47 | 48 | Database.register_extension(:looser_typecasting, LooserTypecasting) 49 | end 50 | 51 | -------------------------------------------------------------------------------- /spec/extensions/error_sql_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "error_sql extension" do 4 | before do 5 | @db = Sequel.mock(:fetch=>proc{|sql| @db.synchronize{|c| @db.log_connection_yield(sql, c){raise StandardError}}}).extension(:error_sql) 6 | end 7 | 8 | it "should have Sequel::DatabaseError#sql give the SQL causing the error" do 9 | @db["SELECT"].all rescue (e = $!) 10 | e.sql.must_equal "SELECT" 11 | end 12 | 13 | it "should include connection information in SQL information if logging connection info" do 14 | @db.log_connection_info = true 15 | @db["SELECT"].all rescue (e = $!) 16 | e.sql.must_match(/\A\(conn: -?\d+\) SELECT\z/) 17 | end 18 | 19 | it "should include arguments in SQL information if given" do 20 | @db["SELECT"].with_fetch(proc{|sql| @db.synchronize{|c| @db.log_connection_yield(sql, c, [1, 2]){raise StandardError}}}).all rescue (e = $!) 21 | e.sql.must_equal "SELECT; [1, 2]" 22 | end 23 | 24 | it "should have Sequel::DatabaseError#sql give the SQL causing the error when using a logger" do 25 | l = Object.new 26 | def l.method_missing(*) end 27 | @db.loggers = [l] 28 | @db["SELECT"].all rescue (e = $!) 29 | e.sql.must_equal "SELECT" 30 | end 31 | 32 | it "should have Sequel::DatabaseError#sql be nil if there is no wrapped exception" do 33 | @db["SELECT"].with_fetch(proc{|sql| @db.log_connection_yield(sql, nil){raise Sequel::DatabaseError}}).all rescue (e = $!) 34 | e.sql.must_be_nil 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/files/unused_associations/run_tua.rb: -------------------------------------------------------------------------------- 1 | $:.unshift(File.expand_path('../../../lib', File.dirname(__FILE__))) 2 | require 'json' 3 | require 'coverage' 4 | 5 | Coverage.start(methods: true) 6 | 7 | require 'sequel' 8 | DB = Sequel.mock(:columns=>[:id, :t_id], :fetch=>{:id=>1, :t_id=>2}, :numrows=>1) 9 | 10 | opts = ENV['PLUGIN_OPTS'] ? Sequel.parse_json(ENV['PLUGIN_OPTS']).transform_keys(&:to_sym) : {} 11 | Sequel::Model.plugin :unused_associations, opts 12 | 13 | require_relative 'tua' 14 | 15 | eval($stdin.read) 16 | 17 | begin 18 | cov_data = if ENV['NO_COVERAGE_RESULT'] 19 | Sequel::Model.update_associations_coverage 20 | else 21 | Sequel::Model.update_associations_coverage(coverage_result: Coverage.result) 22 | end 23 | 24 | data = if ENV['NO_COVERAGE_DATA'] 25 | Sequel::Model.update_unused_associations_data 26 | elsif ENV['KEEP_COVERAGE'] 27 | Sequel::Model.update_unused_associations_data(:keep_coverage=>true) 28 | else 29 | Sequel::Model.update_unused_associations_data(coverage_data: cov_data) 30 | end 31 | 32 | result = if ENV['NO_DATA'] 33 | [Sequel::Model.unused_associations.sort, 34 | Sequel::Model.unused_association_options.sort] 35 | else 36 | [Sequel::Model.unused_associations(unused_associations_data: data).sort, 37 | Sequel::Model.unused_association_options(unused_associations_data: data).sort] 38 | end 39 | rescue => e 40 | result = "#{e.class}: #{e.message}\n#{e.backtrace.join("\n")}" 41 | end 42 | 43 | print Sequel.object_to_json(result) 44 | -------------------------------------------------------------------------------- /lib/sequel/connection_pool/single.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | # This is the fastest connection pool, since it isn't a connection pool at all. 4 | # It is just a wrapper around a single connection that uses the connection pool 5 | # API. 6 | class Sequel::SingleConnectionPool < Sequel::ConnectionPool 7 | def initialize(db, opts=OPTS) 8 | super 9 | @conn = [] 10 | end 11 | 12 | # Yield the connection if one has been made. 13 | def all_connections 14 | yield @conn.first unless @conn.empty? 15 | end 16 | 17 | # Disconnect the connection from the database. 18 | def disconnect(opts=nil) 19 | return unless c = @conn.first 20 | disconnect_connection(c) 21 | @conn.clear 22 | nil 23 | end 24 | 25 | # Yield the connection to the block. 26 | def hold(server=nil) 27 | unless c = @conn.first 28 | @conn.replace([c = make_new(:default)]) 29 | end 30 | yield c 31 | rescue Sequel::DatabaseDisconnectError, *@error_classes => e 32 | disconnect if disconnect_error?(e) 33 | raise 34 | end 35 | 36 | # The SingleConnectionPool always has a maximum size of 1. 37 | def max_size 38 | 1 39 | end 40 | 41 | def pool_type 42 | :single 43 | end 44 | 45 | # The SingleConnectionPool always has a size of 1 if connected 46 | # and 0 if not. 47 | def size 48 | @conn.empty? ? 0 : 1 49 | end 50 | 51 | private 52 | 53 | # Make sure there is a valid connection. 54 | def preconnect(concurrent = nil) 55 | hold{} 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /doc/release_notes/4.30.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * Overriding the :limit and :eager_limit_strategy association options 4 | can now be done on a per-call basis when eager loading, by using an 5 | eager block callback and setting the :eager_limit or 6 | :eager_limit_strategy dataset options. Example: 7 | 8 | Album.eager(:tracks=>proc{|ds| ds.clone(:eager_limit=>5)}).all 9 | 10 | * Dataset#insert_conflict and #insert_ignore have been added on 11 | SQLite, adding support for the INSERT OR ... SQL syntax: 12 | 13 | DB[:table].insert_ignore.insert(:a=>1, :b=>2) 14 | # INSERT OR IGNORE INTO TABLE (a, b) VALUES (1, 2) 15 | 16 | DB[:table].insert_conflict(:replace).insert(:a=>1, :b=>2) 17 | # INSERT OR REPLACE INTO TABLE (a, b) VALUES (1, 2) 18 | 19 | * An identifier_columns plugin has been added, which allows 20 | Sequel::Model#save to work when column names contain double 21 | underscores. 22 | 23 | = Other Improvements 24 | 25 | * IPv6 addresses can now be used in connection URLs when using 26 | ruby 1.9.3+. 27 | 28 | * The :db_type entries in column schema hashes now include sizes 29 | for string and decimal types on DB2 and when using the jdbc 30 | adapter's generic schema parsing. 31 | 32 | * Database#row_type in the pg_row extension now handles different 33 | formats of specifying schema qualified types. So a row type 34 | registered via :schema__type can be found using 35 | Sequel.qualify(:schema, :type). 36 | 37 | * Another disconnect error is recognized in the tinytds adapter. 38 | -------------------------------------------------------------------------------- /lib/sequel/database.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | # Hash of adapters that have been used. The key is the adapter scheme 5 | # symbol, and the value is the Database subclass. 6 | ADAPTER_MAP = {} 7 | 8 | # Hash of shared adapters that have been registered. The key is the 9 | # adapter scheme symbol, and the value is the Sequel module containing 10 | # the shared adapter. 11 | SHARED_ADAPTER_MAP = {} 12 | 13 | # Array of all databases to which Sequel has connected. If you are 14 | # developing an application that can connect to an arbitrary number of 15 | # databases, delete the database objects from this (or use the :keep_reference 16 | # Database option or a block when connecting) or they will not get 17 | # garbage collected. 18 | DATABASES = [] 19 | 20 | # A Database object represents a virtual connection to a database. 21 | # The Database class is meant to be subclassed by database adapters in order 22 | # to provide the functionality needed for executing queries. 23 | class Database 24 | OPTS = Sequel::OPTS 25 | end 26 | 27 | require_relative "database/connecting" 28 | require_relative "database/dataset" 29 | require_relative "database/dataset_defaults" 30 | require_relative "database/logging" 31 | require_relative "database/features" 32 | require_relative "database/misc" 33 | require_relative "database/query" 34 | require_relative "database/transactions" 35 | require_relative "database/schema_generator" 36 | require_relative "database/schema_methods" 37 | end 38 | -------------------------------------------------------------------------------- /lib/sequel/extensions/virtual_row_method_block.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # These modifies virtual row blocks so that you can pass a block 4 | # when calling a method to change the behavior. It only exists 5 | # for backwards compatibility with previous Sequel versions, and 6 | # is not recommended for new applications. 7 | # 8 | # To load the extension: 9 | # 10 | # Sequel.extension :virtual_row_method_block 11 | 12 | # 13 | module Sequel 14 | module SQL 15 | class VirtualRow < BasicObject 16 | include(Module.new do 17 | Sequel.set_temp_name(self){"Sequel::SQL:VirtualRow::_MethodBlockMethodMissing"} 18 | # Handle blocks passed to methods and change the behavior. 19 | def method_missing(m, *args, &block) 20 | if block 21 | if args.empty? 22 | Function.new(m) 23 | else 24 | case args.shift 25 | when :* 26 | Function.new(m, *args).* 27 | when :distinct 28 | Function.new(m, *args).distinct 29 | when :over 30 | opts = args.shift || OPTS 31 | f = Function.new(m, *::Kernel.Array(opts[:args])) 32 | f = f.* if opts[:*] 33 | f.over(opts) 34 | else 35 | Kernel.raise(Error, 'unsupported VirtualRow method argument used with block') 36 | end 37 | end 38 | else 39 | super 40 | end 41 | end 42 | end) 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/sequel/extensions/symbol_aref.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # The symbol_aref extension makes Symbol#[] support Symbol, 4 | # Sequel::SQL::Indentifier, and Sequel::SQL::QualifiedIdentifier instances, 5 | # returning appropriate Sequel::SQL::QualifiedIdentifier instances. It's 6 | # designed as a shortcut so that instead of: 7 | # 8 | # Sequel[:table][:column] # table.column 9 | # 10 | # you can just write: 11 | # 12 | # :table[:column] # table.column 13 | # 14 | # To load the extension: 15 | # 16 | # Sequel.extension :symbol_aref 17 | # 18 | # If you are using Ruby 2+, and you would like to use refinements, there 19 | # is a refinement version of this in the symbol_aref_refinement extension. 20 | # 21 | # Related module: Sequel::SymbolAref 22 | 23 | if RUBY_VERSION >= '2.0' 24 | module Sequel::SymbolAref 25 | def [](v) 26 | case v 27 | when Symbol, Sequel::SQL::Identifier, Sequel::SQL::QualifiedIdentifier 28 | Sequel::SQL::QualifiedIdentifier.new(self, v) 29 | else 30 | super 31 | end 32 | end 33 | end 34 | 35 | class Symbol 36 | prepend Sequel::SymbolAref 37 | end 38 | # :nocov: 39 | else 40 | class Symbol 41 | if method_defined?(:[]) 42 | alias_method :aref_before_sequel, :[] 43 | end 44 | 45 | def [](v) 46 | case v 47 | when Symbol, Sequel::SQL::Identifier, Sequel::SQL::QualifiedIdentifier 48 | Sequel::SQL::QualifiedIdentifier.new(self, v) 49 | else 50 | aref_before_sequel(v) 51 | end 52 | end 53 | end 54 | end 55 | # :nocov: 56 | -------------------------------------------------------------------------------- /doc/release_notes/5.87.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * Column schema hashes on MySQL and PostgreSQL now include the 4 | comment on the column in the :comment entry. 5 | 6 | * An inspect_pk plugin has been added, which makes the inspect output 7 | include the primary key next to the class name, so that you can 8 | copy and paste the inspect output to return the model object: 9 | 10 | Album.with_pk(1).inspect 11 | # default: # 12 | # with inspect_pk: # 13 | 14 | * A pg_schema_caching extension has been added. This builds on top 15 | of the schema_caching extension, and handles OIDs for custom types. 16 | 17 | Type OIDs are included in column schema hashes on PostgreSQL. The 18 | OIDs are static for built-in types, but dynamic for custom types. 19 | The pg_schema_caching extension will perform a query to get the 20 | type OIDs for the custom types after loading the cached schema. 21 | 22 | = Other Improvements 23 | 24 | * Database#extension and Dataset#extension no longer attempt to 25 | require the extension file if the extension has already been 26 | registered. This is how model plugins work, and makes it easier 27 | for applications to use custom extensions without messing with the 28 | load path. 29 | 30 | * When using the pg_auto_parameterize extension, and using 31 | Dataset#no_auto_parameterize, Dataset#import is no longer limited to 32 | inserting 40 rows at a time. 33 | 34 | * The trilogy adapter now treats all database errors with code 1205 as 35 | Sequel::DatabaseLockTimeout. 36 | -------------------------------------------------------------------------------- /lib/sequel/extensions/datetime_parse_to_time.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # This switches the default parsing of strings into Time values 4 | # from using Time.parse to using DateTime.parse.to_time. This 5 | # fixes issues when the times being parsed have no timezone 6 | # information, the implicit timezone for the Database instance 7 | # is set to +:utc+, and the timestamps being used include values 8 | # not valid in the local timezone, such as during a daylight 9 | # savings time switch. 10 | # 11 | # To load the extension: 12 | # 13 | # Sequel.extension :datetime_parse_to_time 14 | 15 | # 16 | module Sequel::DateTimeParseToTime 17 | private 18 | 19 | # Use DateTime.parse.to_time to do the conversion if the input a string and is assumed to 20 | # be in UTC and there is no offset information in the string. 21 | def convert_input_timestamp(v, input_timezone) 22 | if v.is_a?(String) && datetime_class == Time && input_timezone == :utc && !_date_parse(v).has_key?(:offset) 23 | # :nocov: 24 | # Whether this is fully branch covered depends on the order in which the specs are run. 25 | v = handle_date_parse_input(v) if respond_to?(:handle_date_parse_input, true) 26 | # :nocov: 27 | t = DateTime.parse(v).to_time 28 | case application_timezone 29 | when nil, :local 30 | t = t.localtime 31 | end 32 | t 33 | else 34 | super 35 | end 36 | rescue => e 37 | raise convert_exception_class(e, Sequel::InvalidValue) 38 | end 39 | end 40 | 41 | Sequel.extend(Sequel::DateTimeParseToTime) 42 | -------------------------------------------------------------------------------- /spec/extensions/looser_typecasting_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "LooserTypecasting Extension" do 4 | before do 5 | @db = Sequel.mock 6 | def @db.supports_schema_parsing?() true end 7 | def @db.schema(*args) 8 | [[:id, {}], [:z, {:type=>:float}], [:b, {:type=>:integer}], [:d, {:type=>:decimal}], [:s, {:type=>:string}]] 9 | end 10 | @c = Class.new(Sequel::Model(@db[:items])) 11 | @db.extension(:looser_typecasting) 12 | @c.instance_eval do 13 | @columns = [:id, :b, :z, :d, :s] 14 | def columns; @columns; end 15 | end 16 | end 17 | 18 | it "should not raise errors for invalid strings in integer columns" do 19 | @c.new(:b=>'a').b.must_equal 0 20 | @c.new(:b=>'a').b.must_be_kind_of(Integer) 21 | end 22 | 23 | it "should not raise errors for invalid strings in float columns" do 24 | @c.new(:z=>'a').z.must_equal 0.0 25 | @c.new(:z=>'a').z.must_be_kind_of(Float) 26 | end 27 | 28 | it "should not raise errors for hash or array input to string columns" do 29 | @c.new(:s=>'a').s.must_equal 'a' 30 | @c.new(:s=>[]).s.must_be_kind_of(String) 31 | @c.new(:s=>{}).s.must_be_kind_of(String) 32 | end 33 | 34 | it "should not raise errors for invalid strings in decimal columns" do 35 | @c.new(:d=>'a').d.must_equal 0.0 36 | @c.new(:d=>'a').d.must_be_kind_of(BigDecimal) 37 | end 38 | 39 | it "should not affect conversions of other types in decimal columns" do 40 | @c.new(:d=>1).d.must_equal 1 41 | @c.new(:d=>1).d.must_be_kind_of(BigDecimal) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /doc/release_notes/5.44.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A concurrent_eager_loading plugin has been added. This plugin 4 | builds on top of the async_thread_pool Database extension and 5 | allows eager loading multiple associations concurrently in 6 | separate threads. With this plugin, you can mark datasets for 7 | concurrent eager loading using eager_load_concurrently: 8 | 9 | Album.eager_load_concurrently.eager(:artist, :genre, :tracks).all 10 | 11 | Datasets that are marked for concurrent eager loading will use 12 | concurrent eager loading if they are eager loading more than one 13 | association. If you would like to make concurrent eager loading 14 | the default, you can load the plugin with the :always option. 15 | 16 | All of the association types that ship with Sequel now support 17 | concurrent eager loading when using this plugin. For custom eager 18 | loaders using the :eager_loader association option, please see the 19 | documentation for the plugin for how to enable custom eager loading 20 | for them. 21 | 22 | = Other Improvements 23 | 24 | * The date_arithmetic extension now handles ActiveSupport::Duration 25 | values with weeks, as well as :weeks as a key in a hash value. Weeks 26 | are converted into 7 days internally. 27 | 28 | * The shared SQLite adapter now emulates the dropping of non-composite 29 | unique constraints. Non-composite unique constraints are now 30 | treated similarly to composite unique constraints, in that dropping 31 | any unique constraints on a table will drop all unique constraints 32 | on that table. 33 | -------------------------------------------------------------------------------- /doc/release_notes/1.0.txt: -------------------------------------------------------------------------------- 1 | === New code organization 2 | 3 | Sequel is now divided into two parts: sequel_core and sequel_model. 4 | These two parts are distributed as two separate gems. The sequel gem 5 | bundles sequel_core and sequel_model together. If you don't use 6 | Sequel::Model in your code, you can just install and use sequel_core. 7 | 8 | === New model hooks implementation 9 | 10 | The hooks implementation have been rewritten from scratch, is much 11 | more robust and offers a few new features: 12 | 13 | * More ways to define hooks: hooks can now be defined by supplying a 14 | block or a method name, or by overriding the hook instance method. 15 | 16 | * Inheritable hooks: Hooks can now be inherited, which means that you 17 | can define general hooks in a model superclass, and use them in 18 | subclasses. You can also define global hooks on Sequel::Model that 19 | will be invoked for all model classes. 20 | 21 | * Hook chains can be broken by returning false from within the hook. 22 | 23 | * New after_initialize hook, invoked after instance initialization. 24 | 25 | * The hook invocation order can no longer be changed. Hooks are 26 | invoked in order of definition, from the top of the class hierarchy 27 | (that is, from Sequel::Model) down to the specific class. 28 | 29 | === Miscellanea 30 | 31 | * Removed deprecated adapter stubs, and all other deprecations in both 32 | sequel_core and sequel_model. 33 | 34 | * Fixed String#to_time to raise error correctly for invalid time 35 | stamps. 36 | 37 | * Fixed error behavior when parse_tree or ruby2ruby are not available. 38 | 39 | -------------------------------------------------------------------------------- /spec/extensions/select_remove_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "Dataset#select_remove" do 4 | before do 5 | @d = Sequel.mock.from(:test).extension(:select_remove) 6 | @d.columns :a, :b, :c 7 | end 8 | 9 | it "should remove columns from the selected columns" do 10 | @d.sql.must_equal 'SELECT * FROM test' 11 | @d.select_remove(:a).sql.must_equal 'SELECT b, c FROM test' 12 | @d.select_remove(:b).sql.must_equal 'SELECT a, c FROM test' 13 | @d.select_remove(:c).sql.must_equal 'SELECT a, b FROM test' 14 | end 15 | 16 | it "should work correctly if there are already columns selected" do 17 | d = @d.select(:a, :b, :c) 18 | d.columns :a, :b, :c 19 | d.select_remove(:c).sql.must_equal 'SELECT a, b FROM test' 20 | end 21 | 22 | it "should have no effect if the columns given are not currently selected" do 23 | @d.select_remove(:d).sql.must_equal 'SELECT a, b, c FROM test' 24 | end 25 | 26 | it "should handle expressions where Sequel can't determine the alias by itself" do 27 | d = @d.select(:a, Sequel.function(:b), Sequel.as(:c, :b)) 28 | d.columns :a, :"b()", :b 29 | d.select_remove(:"b()").sql.must_equal 'SELECT a, c AS b FROM test' 30 | end 31 | 32 | it "should remove expressions if given exact expressions" do 33 | d = @d.select(:a, Sequel.function(:b), Sequel.as(:c, :b)) 34 | d.columns :a, :"b()", :b 35 | d.select_remove(Sequel.function(:b)).sql.must_equal 'SELECT a, c AS b FROM test' 36 | d.select_remove(Sequel.as(:c, :b)).sql.must_equal 'SELECT a, b() FROM test' 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /doc/release_notes/5.74.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * Sequel.migration blocks now support a revert method, which reverts 4 | the changes in the block on up, and applies them on down. So if 5 | you have a migration such as: 6 | 7 | Sequel.migration do 8 | change do 9 | create_table :table do 10 | # ... 11 | end 12 | end 13 | end 14 | 15 | and you later want to add a migration that drops the table, you 16 | can use: 17 | 18 | Sequel.migration do 19 | revert do 20 | create_table :table do 21 | # ... 22 | end 23 | end 24 | end 25 | 26 | This will drop the table when migrating up, and create a table 27 | with the given schema when migrating down. 28 | 29 | * is_json and is_not_json methods have been added to the pg_json_ops 30 | extension, for the IS [NOT] JSON operator supported in PostgreSQL 31 | 16+. These were previously added in Sequel 5.59.0, and removed 32 | in Sequel 5.61.0 as support was removed in PostgreSQL 15 beta 4. 33 | PostgreSQL 16 shipped with support for them, so support has been 34 | recommitted to Sequel. 35 | 36 | = Other Improvements 37 | 38 | * SQLite generated columns now show up in Database#schema when using 39 | SQLite 3.37+. 40 | 41 | * Sequel now attempts to avoid an infinite loop in pathlogical cases 42 | in the jdbc adapter, where the exception cause chain has a loop. 43 | Additionally, if an exception is already recognized as a disconnect, 44 | or an exception already responds to a getSQLState method, Sequel no 45 | longer looks at the causes of the exception. 46 | -------------------------------------------------------------------------------- /spec/extensions/pg_extended_integer_support_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "pg_extended_integer_support extension" do 4 | before do 5 | @db = Sequel.mock(:host=>'postgres').extension(:pg_extended_integer_support) 6 | end 7 | 8 | it "should literalize integers out of range using single quotes by default" do 9 | @db.literal(2**63).must_equal "'9223372036854775808'" 10 | end 11 | 12 | it "should literalize integers out of range without quotes when configured with :raw strategy" do 13 | @db.dataset.integer_outside_bigint_range_strategy(:raw).literal(2**63).must_equal "9223372036854775808" 14 | end 15 | 16 | it "should raise for integers out of range when configured with :raise strategy" do 17 | ds = @db.dataset.integer_outside_bigint_range_strategy(:raise) 18 | proc{ds.literal(2**63)}.must_raise Sequel::InvalidValue 19 | end 20 | 21 | it "should raise for integers out of range when configured with :quote strategy" do 22 | @db.dataset.integer_outside_bigint_range_strategy(:quote).literal(2**63).must_equal "'9223372036854775808'" 23 | end 24 | 25 | it "should respect :integer_outside_bigint_range_strategy Database option for strategy" do 26 | @db.opts[:integer_outside_bigint_range_strategy] = :raw 27 | @db.literal(2**63).must_equal "9223372036854775808" 28 | 29 | @db.opts[:integer_outside_bigint_range_strategy] = :quote 30 | @db.literal(2**63).must_equal "'9223372036854775808'" 31 | 32 | @db.opts[:integer_outside_bigint_range_strategy] = :raise 33 | proc{@db.literal(2**63)}.must_raise Sequel::InvalidValue 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/sequel/extensions/empty_array_consider_nulls.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # This changes Sequel's literalization of IN/NOT IN with an empty 4 | # array value to consider NULL values if one of the referenced 5 | # columns is NULL: 6 | # 7 | # DB[:test].where(name: []) 8 | # # SELECT * FROM test WHERE (name != name) 9 | # DB[:test].exclude(name: []) 10 | # # SELECT * FROM test WHERE (name = name) 11 | # 12 | # This works for sets in addition to arrays: 13 | # 14 | # DB[:test].where(name: Set[]) 15 | # # SELECT * FROM test WHERE (name != name) 16 | # DB[:test].exclude(name: Set[]) 17 | # # SELECT * FROM test WHERE (name = name) 18 | # 19 | # The default Sequel behavior is to ignore NULLs, as the above 20 | # query is not generally optimized well by databases. 21 | # 22 | # You can load this extension into specific datasets: 23 | # 24 | # ds = DB[:table] 25 | # ds = ds.extension(:empty_array_consider_nulls) 26 | # 27 | # Or you can load it into all of a database's datasets, which 28 | # is probably the desired behavior if you are using this extension: 29 | # 30 | # DB.extension(:empty_array_consider_nulls) 31 | # 32 | # Related module: Sequel::EmptyArrayConsiderNulls 33 | 34 | # 35 | module Sequel 36 | module EmptyArrayConsiderNulls 37 | # Use an expression that returns NULL if the column value is NULL. 38 | def empty_array_value(op, cols) 39 | c = Array(cols) 40 | SQL::BooleanExpression.from_value_pairs(c.zip(c), :AND, op == :IN) 41 | end 42 | 43 | end 44 | 45 | Dataset.register_extension(:empty_array_consider_nulls, EmptyArrayConsiderNulls) 46 | end 47 | -------------------------------------------------------------------------------- /spec/model/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $:.unshift(File.join(File.dirname(File.expand_path(__FILE__)), "../../lib/")) 2 | require_relative "../../lib/sequel" 3 | 4 | Sequel::Deprecation.backtrace_filter = lambda{|line, lineno| lineno < 4 || line =~ /_spec\.rb/} 5 | 6 | ENV['MT_NO_PLUGINS'] = '1' # Work around stupid autoloading of plugins 7 | gem 'minitest' 8 | require 'minitest/global_expectations/autorun' 9 | require 'minitest/hooks/default' 10 | 11 | require_relative '../deprecation_helper' 12 | 13 | class << Sequel::Model 14 | attr_writer :db_schema 15 | alias orig_columns columns 16 | def columns(*cols) 17 | return super if cols.empty? 18 | define_method(:columns){cols} 19 | alias_method(:columns, :columns) 20 | @dataset.send(:columns=, cols) if @dataset 21 | def_column_accessor(*cols) 22 | @columns = cols 23 | @db_schema = {} 24 | cols.each{|c| @db_schema[c] = {}} 25 | end 26 | end 27 | 28 | Sequel::DB = nil 29 | Sequel::Model.use_transactions = false 30 | Sequel::Model.cache_anonymous_models = false 31 | 32 | db = Sequel.mock(:fetch=>{:id => 1, :x => 1}, :numrows=>1, :autoid=>proc{|sql| 10}) 33 | def db.schema(*) [[:id, {:primary_key=>true}]] end 34 | def db.reset() sqls end 35 | def db.supports_schema_parsing?() true end 36 | Sequel::Model.db = DB = db 37 | 38 | if ENV['SEQUEL_COLUMNS_INTROSPECTION'] 39 | Sequel.extension :columns_introspection 40 | Sequel::Database.extension :columns_introspection 41 | Sequel::Mock::Dataset.send(:include, Sequel::ColumnsIntrospection) 42 | end 43 | if ENV['SEQUEL_NO_CACHE_ASSOCIATIONS'] 44 | Sequel::Model.cache_associations = false 45 | end 46 | -------------------------------------------------------------------------------- /doc/release_notes/4.18.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An :auto_increment key has been added to the schema information for 4 | primary key columns on JDBC, PostgreSQL, MySQL, MSSQL, DB2, and 5 | SQLite. This fixes issues in the schema_dumper extension where 6 | non-auto-incrementing integer primary keys are no longer dumped as 7 | auto-incrementing. 8 | 9 | For adapters that don't have specific support for detecting 10 | auto incrementing primary keys, Sequel now assumes a primary key 11 | is auto incrementing only if it is not a composite primary key 12 | and the type contains int (e.g. int, integer, bigint). 13 | 14 | = Other Improvements 15 | 16 | * Dataset#empty? now ignores any order on the dataset. Previously, 17 | calling empty? on a dataset ordered by an alias in the SELECT list 18 | could raise an exception. 19 | 20 | * Schema qualified tables are now handled correctly in 21 | many_through_many associations. 22 | 23 | * Using a hash as the value for the :eager association option now 24 | works correctly. 25 | 26 | * All PG::ConnectionBad exceptions are now treated as disconnect 27 | errors in the postgres adapter. This should be more robust than 28 | the previous method of trying to recognize disconnect errors by 29 | trying to parse the exception message. 30 | 31 | * Sequel now skips a hash allocation when issuing queries through 32 | datasets if sharding is not used. 33 | 34 | * Sequel no longer uses the JDBC schema parsing in the jdbc/sqlserver 35 | adapter. Instead, it uses the MSSQL schema parsing, which should 36 | be more accurate than the generic JDBC schema parsing. 37 | -------------------------------------------------------------------------------- /sequel.gemspec: -------------------------------------------------------------------------------- 1 | require File.expand_path("../lib/sequel/version", __FILE__) 2 | SEQUEL_GEMSPEC = Gem::Specification.new do |s| 3 | s.name = 'sequel' 4 | s.version = Sequel.version 5 | s.platform = Gem::Platform::RUBY 6 | s.extra_rdoc_files = ["MIT-LICENSE"] 7 | s.rdoc_options += ["--quiet", "--line-numbers", "--inline-source", '--title', 'Sequel: The Database Toolkit for Ruby', '--main', 'README.rdoc'] 8 | s.summary = "The Database Toolkit for Ruby" 9 | s.description = s.summary 10 | s.author = "Jeremy Evans" 11 | s.email = "code@jeremyevans.net" 12 | s.homepage = "https://sequel.jeremyevans.net" 13 | s.license = 'MIT' 14 | s.metadata = { 15 | 'bug_tracker_uri' => 'https://github.com/jeremyevans/sequel/issues', 16 | 'changelog_uri' => 'https://sequel.jeremyevans.net/rdoc/files/CHANGELOG.html', 17 | 'documentation_uri' => 'https://sequel.jeremyevans.net/documentation.html', 18 | 'mailing_list_uri' => 'https://github.com/jeremyevans/sequel/discussions', 19 | 'source_code_uri' => 'https://github.com/jeremyevans/sequel', 20 | } 21 | s.required_ruby_version = ">= 1.9.2" 22 | s.files = %w(MIT-LICENSE bin/sequel) + Dir["lib/**/*.rb"] 23 | s.require_path = "lib" 24 | s.bindir = 'bin' 25 | s.executables << 'sequel' 26 | s.add_dependency "bigdecimal" 27 | s.add_development_dependency "minitest", '>=5.7.0' 28 | s.add_development_dependency "minitest-hooks" 29 | s.add_development_dependency "minitest-global_expectations" 30 | s.add_development_dependency "tzinfo" 31 | s.add_development_dependency "activemodel" 32 | s.add_development_dependency "nokogiri" 33 | end 34 | -------------------------------------------------------------------------------- /spec/extensions/accessed_columns_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "accessed_columns plugin" do 4 | before do 5 | @db = Sequel.mock(:fetch=>{:name=>'a', :b=>'c'}, :numrows=>1) 6 | @c = Class.new(Sequel::Model(@db[:test])) 7 | @c.columns :name, :b 8 | @c.plugin :accessed_columns 9 | @o = @c.new 10 | end 11 | 12 | it "should record columns accessed" do 13 | @o.accessed_columns.must_equal [] 14 | @o.name 15 | @o.accessed_columns.must_equal [:name] 16 | @o.name 17 | @o.accessed_columns.must_equal [:name] 18 | @o.b 19 | @o.accessed_columns.sort_by{|s| s.to_s}.must_equal [:b, :name] 20 | end 21 | 22 | it "should clear accessed columns when refreshing" do 23 | @o.name 24 | @o.refresh 25 | @o.accessed_columns.must_equal [] 26 | end 27 | 28 | it "should clear accessed columns when saving" do 29 | @o.name 30 | @o.save 31 | @o.accessed_columns.must_equal [] 32 | end 33 | 34 | it "should work when duping and cloning instances" do 35 | o = @o.dup 36 | o.accessed_columns.must_be_empty 37 | 38 | @o.name 39 | o = @o.dup 40 | @o.accessed_columns.must_equal [:name] 41 | o.accessed_columns.must_equal [:name] 42 | 43 | @o.b 44 | @o.accessed_columns.sort_by{|s| s.to_s}.must_equal [:b, :name] 45 | o.accessed_columns.must_equal [:name] 46 | o2 = o.clone 47 | o2.refresh 48 | o.accessed_columns.must_equal [:name] 49 | o2.accessed_columns.must_equal [] 50 | end 51 | 52 | it "should not raise exceptions when object is frozen" do 53 | @o.freeze 54 | @o.name 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /doc/release_notes/5.26.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * Support for SQL/JSON path expressions has been added to the 4 | pg_json_ops extension. These are supported in PostgreSQL 12+. 5 | Examples: 6 | 7 | j = Sequel.pg_json_op(:json_column) 8 | j.path_exists('$.foo') # (jsonb_column @? '$.foo') 9 | j.path_match('$.foo') # (jsonb_column @@ '$.foo') 10 | j.path_exists!('$.foo') # jsonb_path_exists(jsonb_column, '$.foo') 11 | j.path_match!('$.foo') # jsonb_path_match(jsonb_column, '$.foo') 12 | j.path_query('$.foo') # jsonb_path_query(jsonb_column, '$.foo') 13 | j.path_query_array('$.foo') # jsonb_path_query_array(jsonb_column, '$.foo') 14 | j.path_query_first('$.foo') # jsonb_path_query_first(jsonb_column, '$.foo') 15 | 16 | * The nested_attributes method in the nested_attributes plugin now 17 | supports a :require_modification option, which can override the 18 | default require_modification setting for the nested objects. This 19 | can be useful to avoid errors if multiple requests are submitted 20 | simultaneously to delete the same nested row. 21 | 22 | = Other Improvements 23 | 24 | * The dirty plugin now works correctly with the typecast_on_load 25 | plugin. 26 | 27 | * Sequel::Postgres::PGRange#hash has been added to the pg_range 28 | extension, allowing PGRange instances to be usable as hash keys. 29 | 30 | * Table aliases are now supported for single table INSERT 31 | statements on PostgreSQL 9.5+, which can make some insert_conflict 32 | usage easier. 33 | 34 | * Two more foreign key constraint violation types are now recognized 35 | on MySQL 8.0.13+. 36 | -------------------------------------------------------------------------------- /spec/extensions/async_thread_pool_plugin_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe "async_thread_pool plugin" do 4 | before do 5 | @db = Sequel.mock(:extensions=>'async_thread_pool', :fetch=>{:id=>1}, :keep_reference=>false, :num_async_threads=>1, :numrows=>1) 6 | @Album = Class.new(Sequel::Model) 7 | @Album.set_dataset(@db[:albums]) 8 | @Album.plugin :async_thread_pool 9 | @db.sqls 10 | end 11 | 12 | it 'should support creating async datasets via Model.async' do 13 | t = Thread.current 14 | t2 = nil 15 | 16 | v = @Album.all{|x| t2 = Thread.current} 17 | (Array === v).must_equal true 18 | v.first.must_be_kind_of @Album 19 | t2.must_equal t 20 | 21 | v = @Album.async.all{|x| t2 = Thread.current} 22 | (Array === v).must_equal false 23 | v.first.must_be_kind_of @Album 24 | t2.wont_be_nil 25 | t2.wont_equal t 26 | 27 | @db.sqls.must_equal ["SELECT * FROM albums", "SELECT * FROM albums"] 28 | end 29 | 30 | it 'should support async versions of destroy' do 31 | @Album.dataset.async.destroy.__value.must_equal 1 32 | @db.sqls.must_equal ["SELECT * FROM albums", "DELETE FROM albums WHERE (id = 1)"] 33 | end 34 | 35 | it 'should support async versions of with_pk' do 36 | @Album.dataset.async.with_pk(1).__value.pk.must_equal 1 37 | @db.sqls.must_equal ["SELECT * FROM albums WHERE (albums.id = 1) LIMIT 1"] 38 | end 39 | 40 | it 'should support async versions of with_pk!' do 41 | @Album.dataset.async.with_pk!(1).__value.pk.must_equal 1 42 | @db.sqls.must_equal ["SELECT * FROM albums WHERE (albums.id = 1) LIMIT 1"] 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /spec/extensions/single_statement_dataset_destroy_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "single_statement_dataset_destroy plugin" do 4 | before do 5 | @db = Sequel.mock(:fetch=>[{:id=>1}, {:id=>3}]) 6 | @a = a = [] 7 | @class = Class.new(Sequel::Model(@db[:t])) do 8 | plugin :single_statement_dataset_destroy 9 | define_method(:before_destroy){a << :"b#{pk}"; super()} 10 | define_method(:after_destroy){super(); a << :"a#{pk}"} 11 | end 12 | @db.sqls 13 | end 14 | 15 | it "should delete expected rows, running all before hooks first and all after hooks last" do 16 | @db.numrows = 2 17 | @class.dataset.destroy.must_equal 2 18 | @a.must_equal [:b1, :b3, :a1, :a3] 19 | @db.sqls.must_equal ["BEGIN", "SELECT * FROM t", "DELETE FROM t", "COMMIT"] 20 | end 21 | 22 | it "should use default behavior of multiple queries if a custom around_destroy hook is used" do 23 | @class.send(:define_method, :around_destroy){|&b|b.call} 24 | @db.numrows = 1 25 | @class.dataset.destroy.must_equal 2 26 | @a.must_equal [:b1, :a1, :b3, :a3] 27 | @db.sqls.must_equal [ 28 | "SELECT * FROM t", 29 | "DELETE FROM t WHERE (id = 1)", 30 | "DELETE FROM t WHERE (id = 3)", 31 | ] 32 | end 33 | 34 | it "should raise and rollback if the dataset is modified during the destroy" do 35 | @db.numrows = 4 36 | proc{@class.dataset.destroy}.must_raise(Sequel::Error). 37 | message.must_equal "dataset changed during destroy, expected rows: 2, actual rows: 4" 38 | @db.sqls.must_equal ["BEGIN", "SELECT * FROM t", "DELETE FROM t", "ROLLBACK"] 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/sequel/dataset/deprecated_singleton_class_methods.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | class Dataset 5 | # This module implements methods to support deprecated use of extensions registered 6 | # not using a module. In such cases, for backwards compatibility, Sequel has to use 7 | # a singleton class for the dataset. 8 | module DeprecatedSingletonClassMethods 9 | # Load the extension into a clone of the receiver. 10 | def extension(*a) 11 | c = _clone(:freeze=>false) 12 | c.send(:_extension!, a) 13 | c.freeze 14 | end 15 | 16 | # Extend the cloned of the receiver with the given modules, instead of the default 17 | # approach of creating a subclass of the receiver's class and including the modules 18 | # into that. 19 | def with_extend(*mods, &block) 20 | c = _clone(:freeze=>false) 21 | c.extend(*mods) unless mods.empty? 22 | c.extend(Sequel.set_temp_name(DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location.join(':')})"}) if block 23 | c.freeze 24 | end 25 | 26 | private 27 | 28 | # Load the extensions into the receiver. 29 | def _extension!(exts) 30 | Sequel.extension(*exts) 31 | exts.each do |ext| 32 | if pr = Sequel.synchronize{EXTENSIONS[ext]} 33 | pr.call(self) 34 | else 35 | raise(Error, "Extension #{ext} does not have specific support handling individual datasets (try: Sequel.extension #{ext.inspect})") 36 | end 37 | end 38 | self 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /doc/release_notes/4.29.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A uuid plugin has now been added. This plugin will automatically 4 | create a uuid for newly created model objects. 5 | 6 | Model.plugin :uuid 7 | Model.create.uuid => # some UUID 8 | 9 | * Model#json_serializer_opts has been added to the json_serializer 10 | plugin, allowing you to override the JSON serialization options 11 | on a per instance basis without passing the options directly 12 | to Model#to_json. This is useful if you are including the model 13 | instance inside another datastructure that will be serialized 14 | to JSON. 15 | 16 | obj.json_serializer_opts(:root => true) 17 | [obj].to_json 18 | # => '[{"obj":{"id":1,"name":"Foo"}}]' 19 | 20 | = Other Improvements 21 | 22 | * The Database#transaction :retry_on option now works when using 23 | savepoints. 24 | 25 | * Calling Database#table_exists? inside a transaction will now use 26 | a savepoint if the database supports it, so that if the table 27 | doesn't exist, it will not affect the state of the transaction. 28 | 29 | * Blobs can now be used as bound variables in the oracle adapter. 30 | 31 | * The sqlanywhere adapter now works with database sharding. 32 | 33 | * The Dataset#full_text_search :rank option has been fixed to order 34 | by rank descending instead of ascending. 35 | 36 | * External adapters that do not support INSERT with DEFAULT VALUES 37 | can now override Dataset#insert_empty_columns_values to set 38 | the columns and values to use for an empty INSERT. 39 | 40 | * External adapters can now implement Dataset#date_add_sql_append 41 | to integrate with the date_arithmetic extension. 42 | -------------------------------------------------------------------------------- /doc/release_notes/5.40.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * On SQLite 3.33.0+, the UPDATE FROM syntax is now supported. This 4 | allows you to update one table based on a join to another table. 5 | The SQLite syntax is based on the PostgreSQL syntax, and the 6 | Sequel API is the same for both. You need to pass multiple tables 7 | to Dataset#from. The first table is the table to update, and the 8 | remaining tables are used to construct the UPDATE FROM clause: 9 | 10 | DB[:a, :b].where{{a[:c]=>b[:d]}}.update(:e=>'f') 11 | # UPDATE a SET e = 'f' FROM b WHERE (a.c = b.d) 12 | 13 | Unlike PostgreSQL, SQLite does not support the deletion of joined 14 | datasets. Related to this, the following methods for testing 15 | database support for modifying joined datasets have been added: 16 | 17 | * supports_updating_joins? 18 | * supports_deleting_joins? 19 | 20 | = Other Improvements 21 | 22 | * The pg_interval and date_arithmetic extensions now support 23 | ActiveSupport 6.1. 24 | 25 | * Sequel no longer issues method redefinition warnings in verbose 26 | mode. As Ruby 3 has dropped uninitialized instance variable 27 | warnings, Sequel is now verbose warning free on Ruby 3. 28 | 29 | = Backwards Compatibility 30 | 31 | * Trying to truncate or insert into a joined dataset now correctly 32 | raises an exception even if the joined dataset supports updates. 33 | 34 | * The private Dataset#check_modification_allowed! method is now 35 | deprecated, and users (custom adapters) should now switch to one 36 | of the more specific methods introduced in this version: 37 | 38 | * check_insert_allowed! 39 | * check_update_allowed! 40 | * check_delete_allowed! 41 | -------------------------------------------------------------------------------- /doc/release_notes/3.22.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * Support COLLATE in column definitions. At least MySQL and Microsoft 4 | SQL Server support them, and PostgreSQL 9.1 should as well. 5 | 6 | * When connecting to Microsoft SQL Server, you can use the 7 | mssql_unicode_strings accessor to turn of the default usage 8 | of unicode strings (N'') and use regular strings (''). This 9 | can improve performance, but changes the behavior. It's 10 | set to true by default for backwards compatibility. You can 11 | change it at both the dataset and database level: 12 | 13 | DB.mssql_unicode_strings = false # default for datasets 14 | dataset.mssql_unicode_strings = false # just this dataset 15 | 16 | * In the oracle adapter, if Sequel.application_timezone is :utc, set 17 | the timezone for the connection to use the 00:00 timezone. 18 | 19 | = Other Improvements 20 | 21 | * In the single_table_inheritance plugin, correctly handle a 22 | multi-level class hierarchy so that loading instances from a 23 | middle level of the hierarchy can return instances of subclasses. 24 | 25 | * Don't use a schema when creating a temporary table, even if 26 | default_schema is set. 27 | 28 | * Fix the migrator when a default_schema is used. 29 | 30 | * In the ado adapter, assume a connection to SQL Server if the 31 | :conn_string is given and doesn't indicate Access/Jet. 32 | 33 | * Fix fetching rows in the tinytds adapter when the 34 | identifier_output_method is nil. 35 | 36 | * The tinytds adapter now checks for disconnect errors, but it might 37 | not be reliable until the next release of tiny_tds. 38 | 39 | * The odbc adapter now handles ODBC::Time instances correctly. 40 | -------------------------------------------------------------------------------- /lib/sequel/extensions/string_date_time.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # The string_date_time extension provides String instance methods 4 | # for converting the strings to a date (e.g. String#to_date), allowing 5 | # for backwards compatibility with legacy Sequel code. 6 | # 7 | # These methods calls +parse+ on the related class, and as such, can 8 | # result in denial of service in older versions of Ruby for large 9 | # untrusted input, and raise exceptions in newer versions of Ruby. 10 | # 11 | # To load the extension: 12 | # 13 | # Sequel.extension :string_date_time 14 | 15 | class String 16 | # Converts a string into a Date object. 17 | def to_date 18 | Date.parse(self, Sequel.convert_two_digit_years) 19 | rescue => e 20 | raise Sequel.convert_exception_class(e, Sequel::InvalidValue) 21 | end 22 | 23 | # Converts a string into a DateTime object. 24 | def to_datetime 25 | DateTime.parse(self, Sequel.convert_two_digit_years) 26 | rescue => e 27 | raise Sequel.convert_exception_class(e, Sequel::InvalidValue) 28 | end 29 | 30 | # Converts a string into a Time or DateTime object, depending on the 31 | # value of Sequel.datetime_class 32 | def to_sequel_time 33 | if Sequel.datetime_class == DateTime 34 | DateTime.parse(self, Sequel.convert_two_digit_years) 35 | else 36 | Sequel.datetime_class.parse(self) 37 | end 38 | rescue => e 39 | raise Sequel.convert_exception_class(e, Sequel::InvalidValue) 40 | end 41 | 42 | # Converts a string into a Time object. 43 | def to_time 44 | Time.parse(self) 45 | rescue => e 46 | raise Sequel.convert_exception_class(e, Sequel::InvalidValue) 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/sequel/extensions/s.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # The s extension adds Sequel::S, a module containing a private #S 4 | # method that calls Sequel.expr. It's designed as a shortcut so 5 | # that instead of: 6 | # 7 | # Sequel.expr(:column) + 1 8 | # # or 9 | # Sequel.expr{column + 1} 10 | # 11 | # you can just write: 12 | # 13 | # S(:column) + 1 14 | # # or 15 | # S{column + 1} 16 | # 17 | # To load the extension: 18 | # 19 | # Sequel.extension :s 20 | # 21 | # Then you can include the Sequel::S module into whatever classes or 22 | # objects you care about: 23 | # 24 | # Sequel::Model.send(:include, Sequel::S) # available in model instance methods 25 | # Sequel::Model.extend(Sequel::S) # available in model class methods 26 | # Sequel::Dataset.send(:include, Sequel::S) # available in dataset methods 27 | # 28 | # or just into Object if you want it available everywhere: 29 | # 30 | # Object.send(:include, Sequel::S) 31 | # 32 | # If you are using Ruby 2+, and you would like to use refinements, you 33 | # can use Sequel::S as a refinement, in which case the private #S method 34 | # will be available on all objects while the refinement is active. 35 | # 36 | # using Sequel::S 37 | # 38 | # S(:column) + 1 39 | # 40 | # Related module: Sequel::S 41 | 42 | 43 | # 44 | module Sequel::S 45 | private 46 | 47 | # Delegate to Sequel.expr 48 | def S(*a, &block) 49 | Sequel.expr(*a, &block) 50 | end 51 | 52 | # :nocov: 53 | if RUBY_VERSION >= '2.0.0' 54 | include_meth = RUBY_VERSION >= '3.1' ? :import_methods : :include 55 | # :nocov: 56 | refine Object do 57 | send include_meth, Sequel::S 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /doc/release_notes/5.63.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * On Ruby 3.2, the pool_class: :timed_queue Database option can now 4 | be used to use an alternative connection pool that stores 5 | connections in a queue, and uses the new Queue#pop :timeout option 6 | in Ruby 3.2 to implement the pool timeout. This new connection 7 | pool is simpler than the default connection pool. It is not yet 8 | the default connection pool on Ruby 3.2, but it may become the 9 | default in a later version. Users of Ruby 3.2 are encouraged to 10 | try out the pool_class: :timed_queue Database option and provide 11 | feedback on how it works in their application. 12 | 13 | = Other Improvements 14 | 15 | * The tactical_eager_loading plugin now works in combination with the 16 | single_table_inheritance and class_table_inheritance plugins, when 17 | loading an association only defined in a specific subclass. 18 | Previously, eager loading would be skipped in such a case. Now, 19 | an eager load will be attempted for all instances supporting the 20 | association. 21 | 22 | * The validate_associated plugin now avoids database type errors for 23 | non-integer association keys. In cases where the associated object 24 | doesn't have a value for the associated key, and the current object 25 | does not have a key value that can be set in the associated object, 26 | validation errors in the associated object related to the associated 27 | key will be ignored. 28 | 29 | * Thread-keyed connection pool hashes now use compare_by_identity for 30 | better performance. 31 | 32 | * The JRuby workaround in the named_timezones extension is no longer 33 | used on JRuby 9.3.9.0+, as JRuby fixed the related bug. 34 | -------------------------------------------------------------------------------- /lib/sequel/extensions/stdio_logger.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # The stdio_logger extension exposes a Sequel::StdioLogger class that 4 | # can be used for logging with Sequel, as a minimal alternative to 5 | # the logger library. It exposes debug/info/warn/error methods for the 6 | # different warning levels. The debug method is a no-op, so that setting 7 | # the Database sql_log_level to debug will result in no output for normal 8 | # queries. The info/warn/error methods log the current time, log level, 9 | # and the given message. 10 | # 11 | # To use this extension: 12 | # 13 | # Sequel.extension :stdio_logger 14 | # 15 | # Then you you can use Sequel::StdioLogger to wrap IO objects that you 16 | # would like Sequel to log to: 17 | # 18 | # DB.loggers << Sequel::StdioLogger.new($stdout) 19 | # 20 | # log_file = File.open("db_queries.log", 'a') 21 | # log_file.sync = true 22 | # DB.loggers << Sequel::StdioLogger.new(log_file) 23 | # 24 | # This is implemented as a global extension instead of a Database extension 25 | # because Database loggers must be set before Database extensions are loaded. 26 | # 27 | # Related module: Sequel::StdioLogger 28 | 29 | # 30 | module Sequel 31 | class StdioLogger 32 | def initialize(device) 33 | @device = device 34 | end 35 | 36 | # Do not log debug messages. This is so setting the Database 37 | # sql_log_level to debug will result in no output. 38 | def debug(msg) 39 | end 40 | 41 | [:info, :warn, :error].each do |meth| 42 | define_method(meth) do |msg| 43 | @device.write("#{Time.now.strftime('%F %T')} #{meth.to_s.upcase}: #{msg}\n") 44 | nil 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /doc/release_notes/3.20.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The swift adapter now supports an SQLite subadapter. Use the 4 | :db_type => 'sqlite' option when connecting. You can use an 5 | in memory database with the following connection string: 6 | 7 | swift:///?database=:memory:&db_type=sqlite 8 | 9 | * Arbitrary JDBC properties can now be set in the JDBC adapter 10 | using the :jdbc_properties option when connecting. The value 11 | of this option should be a hash where keys and values are JDBC 12 | property keys and values. 13 | 14 | * Basic Microsoft Access support was added to the ado adapter. 15 | The creation of autoincrementing primary key integers now works, 16 | and identifiers are now quoted with []. 17 | 18 | * The Database#indexes method now supports a :partial option when 19 | connecting to MySQL, which makes it include partial indexes (which 20 | are usually excluded). 21 | 22 | = Other Improvements 23 | 24 | * The class_table_inheritance plugin now handles subclass 25 | associations better. Previously, the implicit eager loading code 26 | had issues when you called an association method that only existed 27 | in the subclass. 28 | 29 | * The error message used when a validates_max_length validation is 30 | applied to a nil column value has been improved. You can override 31 | the message yourself using the :nil_message option. 32 | 33 | * The read_timeout and connect_timeout options now work correctly in 34 | the mysql adapter. 35 | 36 | * Another MySQL disconnect error message is now recognized. 37 | 38 | = Backwards Compatibility 39 | 40 | * The swift adapter was upgraded to support swift 0.8.1. Older 41 | versions of swift are no longer supported. 42 | -------------------------------------------------------------------------------- /doc/release_notes/5.96.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * On PostgreSQL 9.2+, alter_table now supports a rename_constraint 4 | method, to rename an existing constraint: 5 | 6 | DB.alter_table(:table) do 7 | rename_constraint(:old_name, :new_name) 8 | end 9 | 10 | * Virtual generated columns are now supported on PostgreSQL 18+, 11 | using the :generated_always_as and :virtual options. For backwards 12 | compatibility, the default without the :virtual option still 13 | creates a stored generated column. This is unfortunately different 14 | from the PostgreSQL 18+ default, where VIRTUAL is assumed if neither 15 | VIRTUAL nor STORED is specified. 16 | 17 | * The strip_nulls method in the pg_json_ops extension now supports an 18 | :in_arrays option on PostgreSQL 18+, for stripping nulls in json 19 | arrays. 20 | 21 | * The pg_array_ops extension now supports sort and reverse methods, 22 | for the array_sort and array_reverse database functions added in 23 | PostgreSQL 18. 24 | 25 | = Other Improvements 26 | 27 | * When using the pg_range and/or pg_multirange extensions in 28 | conjunction with the the pg_auto_parameterize extension, PostgreSQL 29 | range and multirange types containing non-value expressions will no 30 | longer be automatically parameterized. Note that in order for Sequel 31 | to generate appropriate SQL for range types with a non-value 32 | expressions, the range must have an explicit type, as PostgreSQL 33 | does not offer a function to create an untyped range. 34 | 35 | * A regression in 5.95.0 where the :deferred option would be ignored 36 | for unique and primary key constraints on PostgreSQL has been 37 | fixed (fix originally included in 5.95.1). 38 | -------------------------------------------------------------------------------- /lib/sequel/dataset/dataset_module.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel 4 | class Dataset 5 | # This Module subclass is used by Database#extend_datasets 6 | # and Dataset#with_extend to add dataset methods to classes. 7 | # It adds some helper methods inside the module that can define 8 | # named methods on the dataset instances which do specific actions. 9 | # For example: 10 | # 11 | # DB.extend_datasets do 12 | # order :by_id, :id 13 | # select :with_id_and_name, :id, :name 14 | # where :active, :active 15 | # end 16 | # 17 | # DB[:table].active.with_id_and_name.by_id 18 | # # SELECT id, name FROM table WHERE active ORDER BY id 19 | class DatasetModule < ::Module 20 | meths = (<<-METHS).split.map(&:to_sym) 21 | where exclude exclude_having having 22 | distinct grep group group_and_count group_append 23 | limit offset order order_append order_prepend reverse 24 | select select_all select_append select_group select_prepend server 25 | METHS 26 | 27 | # Define a method in the module 28 | def self.def_dataset_caching_method(mod, meth) 29 | mod.send(:define_method, meth) do |name, *args, &block| 30 | if block 31 | define_method(name){public_send(meth, *args, &block)} 32 | else 33 | key = :"_#{meth}_#{name}_ds" 34 | define_method(name) do 35 | cached_dataset(key){public_send(meth, *args)} 36 | end 37 | end 38 | end 39 | end 40 | 41 | meths.each do |meth| 42 | def_dataset_caching_method(self, meth) 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/sequel/adapters/utils/split_alter_table.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | module Sequel::Database::SplitAlterTable 4 | private 5 | 6 | # Preprocess the array of operations. If it looks like some operations depend 7 | # on results of earlier operations and may require reloading the schema to 8 | # work correctly, split those operations into separate lists, and between each 9 | # list, remove the cached schema so that the later operations deal with the 10 | # then current table schema. 11 | def apply_alter_table(name, ops) 12 | modified_columns = [] 13 | op_groups = [[]] 14 | ops.each do |op| 15 | case op[:op] 16 | when :add_column, :set_column_type, :set_column_null, :set_column_default 17 | if modified_columns.include?(op[:name]) 18 | op_groups << [] 19 | else 20 | modified_columns << op[:name] 21 | end 22 | when :rename_column 23 | if modified_columns.include?(op[:name]) || modified_columns.include?(op[:new_name]) 24 | op_groups << [] 25 | end 26 | modified_columns << op[:name] unless modified_columns.include?(op[:name]) 27 | modified_columns << op[:new_name] unless modified_columns.include?(op[:new_name]) 28 | end 29 | if split_alter_table_op?(op) 30 | op_groups << [] 31 | end 32 | op_groups.last << op 33 | end 34 | 35 | op_groups.each do |opgs| 36 | next if opgs.empty? 37 | alter_table_sql_list(name, opgs).each{|sql| execute_ddl(sql)} 38 | remove_cached_schema(name) 39 | end 40 | end 41 | 42 | # Whether the given alter table op should start a new group. 43 | def split_alter_table_op?(op) 44 | false 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /doc/release_notes/5.72.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A pg_auto_parameterize_in_array extension has been added, which 4 | handles conversion of IN/NOT IN to = ANY or != ALL for more types. 5 | The pg_auto_parameterize extension only handles integer types by 6 | default, because other types require the pg_array extension. This 7 | new extension adds handling for Float, BigDecimal, Date, Time, 8 | DateTime, Sequel::SQLTime, and Sequel::SQL::Blob types. It can 9 | also handle String types if the :treat_string_list_as_text_array 10 | Database option is present, using the text type for that. Handling 11 | String values as text is not the default because that may cause 12 | issues for some queries. 13 | 14 | = Other Improvements 15 | 16 | * The defaults_setter plugin now does a deep copy of database 17 | default values that are hash/array or delegates to hash/array. 18 | This fixes cases where the database default values are mutated. 19 | 20 | * Sequel now correctly handles infinite and NaN float values used 21 | inside PostgreSQL array bound variables. 22 | 23 | * The data in the cache files used by the schema_caching and 24 | index_caching extensions and static_cache_cache and 25 | pg_auto_constraint_validations plugins are now sorted before the 26 | cache file is saved, increasing consistency between runs. 27 | 28 | * bigdecimal has been added as a dependency. bigdecimal is currently 29 | a default gem in Ruby from 1.9 to 3.2, but it will move to a 30 | bundled gem in Ruby 3.4, and there will be warnings in Ruby 3.3 31 | for cases that will break in Ruby 3.4. Adding bigdecimal as a 32 | dependency should avoid warnings when using bundler in Ruby 3.3, 33 | and should avoid errors in Ruby 3.4. 34 | -------------------------------------------------------------------------------- /spec/extensions/split_array_nil_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "split_array_nil extension" do 4 | before do 5 | @ds = Sequel.mock[:table].extension(:split_array_nil) 6 | end 7 | 8 | it "should split IN with nil in array into separate OR IS NULL clause" do 9 | @ds.filter(:a=>[1, nil]).sql.must_equal "SELECT * FROM table WHERE ((a IN (1)) OR (a IS NULL))" 10 | end 11 | 12 | it "should split NOT IN with nil in array into separate AND IS NOT NULL clause" do 13 | @ds.exclude(:a=>[1, nil]).sql.must_equal "SELECT * FROM table WHERE ((a NOT IN (1)) AND (a IS NOT NULL))" 14 | end 15 | 16 | it "should not affect other IN/NOT in clauses with arrays" do 17 | @ds.filter(:a=>[1, 2]).sql.must_equal "SELECT * FROM table WHERE (a IN (1, 2))" 18 | @ds.exclude(:a=>[1, 2]).sql.must_equal "SELECT * FROM table WHERE (a NOT IN (1, 2))" 19 | end 20 | 21 | it "should split IN with nil in set into separate OR IS NULL clause" do 22 | @ds.filter(:a=>Set[1, nil]).sql.must_equal "SELECT * FROM table WHERE ((a IN (1)) OR (a IS NULL))" 23 | end 24 | 25 | it "should split NOT IN with nil in set into separate AND IS NOT NULL clause" do 26 | @ds.exclude(:a=>Set[1, nil]).sql.must_equal "SELECT * FROM table WHERE ((a NOT IN (1)) AND (a IS NOT NULL))" 27 | end 28 | 29 | it "should not affect other IN/NOT in clauses with sets" do 30 | @ds.filter(:a=>Set[1, 2]).sql.must_equal "SELECT * FROM table WHERE (a IN (1, 2))" 31 | @ds.exclude(:a=>Set[1, 2]).sql.must_equal "SELECT * FROM table WHERE (a NOT IN (1, 2))" 32 | end 33 | 34 | it "should not affect other types of filters clauses" do 35 | @ds.filter(:a=>1).sql.must_equal "SELECT * FROM table WHERE (a = 1)" 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /doc/release_notes/4.17.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A :preconnect Database option has been added, for automatically 4 | creating the maximum number of connections to the database on 5 | instantiation. This is useful when there is high latency for 6 | initial connection setup, where Sequel's usual approach of 7 | connecting as needed can cause pauses at runtime. 8 | 9 | * Database#sharded? has been added for checking whether the Database 10 | object uses multiple servers. 11 | 12 | * Dataset#server? has been added, for returning a cloned dataset 13 | associated with the given server/shard if the dataset does not 14 | already have a server set. This returns the receiver if the 15 | server has already been set or the Database is not sharded. 16 | 17 | = Other Improvements 18 | 19 | * Sequel now uses the correct shard when deleting model instances. 20 | Previously, the correct shard was only used in the unoptimized 21 | case, not in the optimized case. 22 | 23 | * Sequel now uses the correct shard when using Dataset#insert_select 24 | on PostgreSQL. This was first broken in the 4.13.0 release. 25 | 26 | * Sequel now correctly handles Sequel::SQL::Blob instances used in 27 | bound variables in the postgres adapter. Previously this resulted 28 | in duplicate apostrophes being used. 29 | 30 | * When using the jdbc/sqlite3 adapter with jdbc-sqlite3 3.8.7, Sequel 31 | now handles date objects and empty blobs correctly, working around 32 | bugs in the driver. 33 | 34 | = Backwards Compatibility 35 | 36 | * In the update_or_create plugin, Model.update_or_create now always 37 | returns the object. Previously it would not return the object if 38 | the object already existed but no updates were necessary. 39 | -------------------------------------------------------------------------------- /spec/extensions/boolean_subsets_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "boolean_subsets plugin" do 4 | before do 5 | @db = Sequel.mock 6 | def @db.supports_schema_parsing?() true end 7 | def @db.schema(*args) 8 | [[:asdaf9898as, {}], [:active, {:type=>:boolean}]] 9 | end 10 | 11 | @c = Class.new(Sequel::Model(@db[:items])) 12 | @p = proc do 13 | @columns = [:asdaf9898as, :active] 14 | def columns; @columns; end 15 | singleton_class.send(:alias_method, :columns, :columns) 16 | end 17 | @c.instance_eval(&@p) 18 | end 19 | 20 | it "should create subsets only for boolean attributes" do 21 | @c.plugin(:boolean_subsets) 22 | @c.active.sql.must_equal "SELECT * FROM items WHERE (active IS TRUE)" 23 | @c.respond_to?(:asdaf9898as).must_equal false 24 | end 25 | 26 | it "should handle a block passed to the plugin" do 27 | @c.plugin(:boolean_subsets){|c| ["where_#{c}", c]} 28 | @c.where_active.sql.must_equal "SELECT * FROM items WHERE active" 29 | @c.respond_to?(:active).must_equal false 30 | end 31 | 32 | it "should create boolean subsets when set_dataset is called" do 33 | c = Class.new(Sequel::Model(@db)) 34 | c.instance_eval(&@p) 35 | c.plugin(:boolean_subsets) 36 | c.respond_to?(:active).must_equal false 37 | 38 | c.set_dataset(@db[:items]) 39 | c.active.sql.must_equal "SELECT * FROM items WHERE (active IS TRUE)" 40 | c.respond_to?(:asdaf9898as).must_equal false 41 | end 42 | 43 | it "should handle cases where getting the columns raises an error" do 44 | def @c.columns; raise Sequel::Error end 45 | @c.plugin(:boolean_subsets) 46 | @c.respond_to?(:active).must_equal false 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /doc/release_notes/5.34.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The association_pks plugin now creates *_pks_dataset methods for 4 | each association. These are similar to the existing *_pks getter 5 | methods, but they return a dataset of the keys instead of the keys 6 | themselves. 7 | 8 | * The association_pks plugin now supports a :cache_pks association 9 | option, which will cache calls to the *_pks getter method. The 10 | default behavior remains that the *_pks getter method only returns 11 | cached values if the *_pks= setter method has been used to set the 12 | values. 13 | 14 | * The *_pks getter methods supported by the association_pks plugin 15 | now support a :refresh option to ignore any cached values, similar 16 | to how the association getter methods work. 17 | 18 | = Other Improvements 19 | 20 | * If trying to disconnect a server that doesn't exist when using a 21 | sharded connection pool, a Sequel::Error is now raised. Previously, 22 | the sharded threaded pool raised a NoMethodError and the sharded 23 | single connection pool did not raise an error. 24 | 25 | * If using the :savepoint option when savepoints are not supported, 26 | a Sequel::InvalidOperation exception is now raised, instead of a 27 | NoMethodError. 28 | 29 | * Calling Dataset#eager_graph with no arguments now returns the 30 | dataset. 31 | 32 | * If not connected to the database, the single connection pool will 33 | not yield any connections to Database#pool.all_connections. 34 | 35 | * Forcing a :ruby eager limit strategy for an association without a 36 | limit or offset now works correctly. 37 | 38 | * Multiple unnecessary conditionals have been removed. 39 | 40 | * Sequel core and model code now have 100% branch coverage. 41 | -------------------------------------------------------------------------------- /lib/sequel/extensions/round_timestamps.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | # 3 | # The round_timestamps extension will automatically round timestamp 4 | # values to the database's supported level of precision before literalizing 5 | # them. 6 | # 7 | # For example, if the database supports millisecond precision, and you give 8 | # it a Time value with microsecond precision, it will round it appropriately: 9 | # 10 | # Time.at(1405341161.917999982833862) 11 | # # default: 2014-07-14 14:32:41.917999 12 | # # with extension: 2014-07-14 14:32:41.918000 13 | # 14 | # The round_timestamps extension correctly deals with databases that support 15 | # millisecond or second precision. In addition to handling Time values, it 16 | # also handles DateTime values and Sequel::SQLTime values (for the TIME type). 17 | # 18 | # To round timestamps for a single dataset: 19 | # 20 | # ds = ds.extension(:round_timestamps) 21 | # 22 | # To round timestamps for all datasets on a single database: 23 | # 24 | # DB.extension(:round_timestamps) 25 | # 26 | # Related module: Sequel::Dataset::RoundTimestamps 27 | 28 | module Sequel 29 | class Dataset 30 | module RoundTimestamps 31 | # Round DateTime values before literalizing 32 | def literal_datetime(v) 33 | super(v + Rational(5, 10**timestamp_precision)/864000) 34 | end 35 | 36 | # Round Sequel::SQLTime values before literalizing 37 | def literal_sqltime(v) 38 | super(v.round(sqltime_precision)) 39 | end 40 | 41 | # Round Time values before literalizing 42 | def literal_time(v) 43 | super(v.round(timestamp_precision)) 44 | end 45 | end 46 | 47 | register_extension(:round_timestamps, RoundTimestamps) 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /doc/release_notes/5.70.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A sharded_timed_queue connection pool has been added. This offers 4 | most of the same features as the sharded_threaded connection pool, 5 | but uses the new Queue#pop :timeout features added in Ruby 3.2 to 6 | allow for a simpler and possibly faster and more robust 7 | implementation. 8 | 9 | * If a :pool_class option is not specified when creating a Database, 10 | Sequel will now look at the SEQUEL_DEFAULT_CONNECTION_POOL 11 | environment variable to determine the connection pool class to use. 12 | This allows you to set SEQUEL_DEFAULT_CONNECTION_POOL=timed_queue 13 | on Ruby 3.2 to test with the timed_queue connection pool without 14 | making any code changes. If the :servers Database option is given, 15 | Sequel will automatically use the sharded version of the connection 16 | pool specified by SEQUEL_DEFAULT_CONNECTION_POOL. 17 | 18 | = Other Improvements 19 | 20 | * The connection_validator, connection_expiration, and 21 | async_thread_pool extensions now work with the timed_queue and 22 | sharded_timed_queue connection pools. 23 | 24 | * The sharded_threaded connection pool now disconnects connections 25 | for all specified servers instead of just the last specified server 26 | when using remove_server. 27 | 28 | * The static_cache plugin now recognizes when the forbid_lazy_load 29 | plugin is already loaded, and does not return instances that 30 | forbid lazy load for methods that return a single object, such as 31 | Database.{[],cache_get_pk,first}. 32 | 33 | * Sequel now displays an informative error message if attempting to 34 | load the connection_validator or connection_expiration extensions 35 | when using the single threaded connection pool. 36 | -------------------------------------------------------------------------------- /doc/thread_safety.rdoc: -------------------------------------------------------------------------------- 1 | = Thread Safety 2 | 3 | Most Sequel usage (and all common Sequel usage) is thread safe by default. Specifically, multiple threads can operate on Database instances, Dataset instances, and Model classes concurrently without problems. In general, Database instance and Model classes are not modified after application startup, and Dataset instances are always frozen. 4 | 5 | == Connection Pool 6 | 7 | In order to allow multiple threads to operate on the same database at the same time, Sequel uses a connection pool. The connection pool is designed so that a thread uses a connection for the minimum amount of time, returning the connection to the pool as soon as it is done using the connection. If a thread requests a connection and the pool does not have an available connection, a new connection will be created. If the maximum number of connections in the pool has already been reached, the thread will block until a connection is available or the connection pool timeout has elapsed (in which case a Sequel::PoolTimeout error will be raised). 8 | 9 | == Exceptions 10 | 11 | This is a small list of things that are specifically non thread-safe. This is not an exhaustive list, there may be cases not mentioned here. 12 | 13 | 1) Model instances: Model instances are not thread-safe unless they are frozen first. Multiple threads should not operate on an unfrozen model instance concurrently. 14 | 15 | 2) Model class modifications: Model class modifications, such as adding associations and loading plugins, are not designed to be thread safe. You should not modify a class in one thread if any other thread can concurrently access it. Model subclassing is designed to be thread-safe, so you create a model subclass in a thread and modify it safely. 16 | -------------------------------------------------------------------------------- /doc/release_notes/5.75.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * Database#{defer,immediate}_constraints methods have been added on 4 | PostgreSQL for changing handling of deferrable constraints inside 5 | a transaction. defer_constraints sets deferrable constraints to 6 | be deferred (not checked until transaction commit), and 7 | immediate_constraints sets deferrable constraints to be checked 8 | as part of the related query, and any already deferred constraint 9 | checks to be applied immediately. You can pass the :constraints 10 | option to only apply the changes to specific constraints. 11 | 12 | * TimestampMigrator.run_single has been added, to migrate a single 13 | migration up or down. 14 | 15 | = Other Improvements 16 | 17 | * INSERT RETURNING is now supported on MariaDB 10.5+, and used 18 | automatically when saving new model objects. Note that this 19 | is not supported when using the jdbc adapter, because the 20 | jdbc-mysql driver doesn't support it. A jdbc/mariadb adapter 21 | could be added, as it's likely recent versions of the 22 | jdbc-mariadb driver would support it, but the jdbc-mariadb gem 23 | hasn't been updated in over 4 years. Talk to the jdbc-mariadb 24 | gem maintainers if you want to use this feature with the jdbc 25 | adapter. 26 | 27 | * The Dataset#paged_each optimization in the postgres adapter 28 | now respects the :skip_transaction option, making it the 29 | same as the :hold option. Note that this has effects beyond 30 | just skipping the transaction, but non-HOLD cursors are only 31 | supported inside transactions. 32 | 33 | * The any_not_empty? extension's Dataset#any? method now supports 34 | an argument, passing it to Enumerable#any? (which has supported 35 | an argument since Ruby 2.5). 36 | -------------------------------------------------------------------------------- /doc/release_notes/5.80.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A provenance dataset extension has been added. This extension makes 4 | SQL queries include a comment describing how the dataset was built. 5 | This can make debugging complex cases significantly easier. Here's 6 | a simple example: 7 | 8 | DB.extension :provenance 9 | 10 | DB[:table]. 11 | select(:a). 12 | where{b > 10}. 13 | order(:c). 14 | limit(10) 15 | # SQL: 16 | # SELECT a FROM table WHERE (b > 10) ORDER BY c LIMIT 10 -- 17 | # -- Dataset Provenance 18 | # -- Keys:[:from] Source:(eval at bin/sequel:257):2:in `
' 19 | # -- Keys:[:select] Source:(eval at bin/sequel:257):3:in `
' 20 | # -- Keys:[:where] Source:(eval at bin/sequel:257):4:in `
' 21 | # -- Keys:[:order] Source:(eval at bin/sequel:257):5:in `
' 22 | # -- Keys:[:limit] Source:(eval at bin/sequel:257):6:in `
' 23 | 24 | With the above example, it's obvious how the dataset is created, but 25 | but in real applications, where datasets can be built from multiple 26 | files, seeing where each dataset clone was made can be helpful. 27 | 28 | The source listed will skip locations in the Ruby standard library 29 | as well as Sequel itself. Other locations can be skipped by 30 | providing a Database :provenance_caller_ignore Regexp option: 31 | 32 | DB.opts[:provenance_caller_ignore] = /\/gems\/library_name-/ 33 | 34 | = Other Improvements 35 | 36 | * For dataset methods where Sequel can determine that the return 37 | value would be equivalent to the receiver, Sequel now returns the 38 | receiver. This reduces the number of dataset allocations. 39 | 40 | * Sequel now supports Dataset#skip_locked on MariaDB 10.6+. 41 | -------------------------------------------------------------------------------- /spec/extensions/inverted_subsets_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative "spec_helper" 2 | 3 | describe "Sequel::Plugins::InvertedSubsets" do 4 | it "should name generated dataset module class" do 5 | c = Sequel::Model(:items) 6 | c.plugin :inverted_subsets 7 | c.dataset_module_class.name.must_equal "Sequel::_Model(:items)::@dataset_module_class(InvertedSubsets)" 8 | end if RUBY_VERSION >= '3.3' 9 | 10 | it "should add an inverted subset method which inverts the condition" do 11 | c = Class.new(Sequel::Model(:a)) 12 | c.plugin :inverted_subsets 13 | c.dataset_module{subset(:published, :published => true)} 14 | c.not_published.sql.must_equal 'SELECT * FROM a WHERE (published IS NOT TRUE)' 15 | end 16 | 17 | it "should support a configuration block to customise the inverted method name" do 18 | c = Class.new(Sequel::Model(:a)) 19 | c.plugin(:inverted_subsets){|name| "exclude_#{name}"} 20 | c.dataset_module{where(:published, :published => true)} 21 | c.exclude_published.sql.must_equal 'SELECT * FROM a WHERE (published IS NOT TRUE)' 22 | end 23 | 24 | it "should chain to existing dataset" do 25 | c = Class.new(Sequel::Model(:a)) 26 | c.plugin :inverted_subsets 27 | c.dataset_module{where(:published, :published => true)} 28 | c.where(1=>0).not_published.sql.must_equal 'SELECT * FROM a WHERE ((1 = 0) AND (published IS NOT TRUE))' 29 | end 30 | 31 | it "should work in subclasses" do 32 | c = Class.new(Sequel::Model) 33 | c.plugin(:inverted_subsets){|name| "exclude_#{name}"} 34 | c = Class.new(c) 35 | c.dataset = :a 36 | c.dataset_module{subset(:published, :published => true)} 37 | c.exclude_published.sql.must_equal 'SELECT * FROM a WHERE (published IS NOT TRUE)' 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /doc/release_notes/5.22.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * Sequel now supports Ruby 2.7+ startless ranges in filters: 4 | 5 | DB[:table].where(:column=>(..10)) 6 | # SELECT * FROM table WHERE (column <= 10) 7 | 8 | DB[:table].where(:column=>(...10)) 9 | # SELECT * FROM table WHERE (column < 10) 10 | 11 | It also supports startless, endless ranges in filters, using a 12 | condition that is always true: 13 | 14 | DB[:table].where(:column=>(nil..nil)) 15 | # SELECT * FROM table WHERE (1 = 1) 16 | 17 | * Sequel now supports startless ranges in the pg_range extension: 18 | 19 | DB.extension :pg_range 20 | 21 | DB[:table].insert(:column=>(..10)) 22 | # INSERT INTO "table" ("column") VALUES ('[,10]') RETURNING "id" 23 | 24 | DB[:table].insert(:column=>(...10)) 25 | # INSERT INTO "table" ("column") VALUES ('[,10)') RETURNING "id" 26 | 27 | DB[:table].insert(:column=>(nil..nil)) 28 | # INSERT INTO "table" ("column") VALUES ('[,]') RETURNING "id" 29 | 30 | * Sequel now supports a :materialized option in Dataset#with on 31 | PostgreSQL 12+, to control the inlining of common table expressions: 32 | 33 | DB[:t].with(:t, DB[:t2], :materialized=>false) 34 | # WITH "t" AS NOT MATERIALIZED (SELECT * FROM "t2") 35 | # SELECT * FROM "t" 36 | 37 | DB[:t].with(:t, DB[:t2], :materialized=>true) 38 | # WITH "t" AS MATERIALIZED (SELECT * FROM "t2") 39 | # SELECT * FROM "t" 40 | 41 | = Other Improvements 42 | 43 | * Database#primary_key_sequence now works for tables without serial 44 | sequences on PostgreSQL 12+. 45 | 46 | * Dataset#multi_insert and #import with return: :primary_key option 47 | on Microsoft SQL Server now work correctly if the dataset uses 48 | a row_proc (e.g. for model datasets). 49 | --------------------------------------------------------------------------------