sequel 3.21.0 → 3.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +1061 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +103 -63
- data/Rakefile +59 -27
- data/bin/sequel +50 -26
- data/doc/active_record.rdoc +67 -51
- data/doc/advanced_associations.rdoc +189 -75
- data/doc/association_basics.rdoc +327 -79
- data/doc/cheat_sheet.rdoc +21 -21
- data/doc/core_extensions.rdoc +374 -0
- data/doc/dataset_basics.rdoc +10 -10
- data/doc/dataset_filtering.rdoc +54 -42
- data/doc/mass_assignment.rdoc +56 -0
- data/doc/migration.rdoc +89 -557
- data/doc/model_hooks.rdoc +80 -29
- data/doc/object_model.rdoc +547 -0
- data/doc/opening_databases.rdoc +114 -53
- data/doc/prepared_statements.rdoc +41 -15
- data/doc/querying.rdoc +137 -56
- data/doc/reflection.rdoc +36 -10
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/release_notes/3.31.0.txt +146 -0
- data/doc/release_notes/3.32.0.txt +202 -0
- data/doc/release_notes/3.33.0.txt +157 -0
- data/doc/release_notes/3.34.0.txt +671 -0
- data/doc/release_notes/3.35.0.txt +144 -0
- data/doc/release_notes/3.36.0.txt +245 -0
- data/doc/release_notes/3.37.0.txt +338 -0
- data/doc/release_notes/3.38.0.txt +234 -0
- data/doc/release_notes/3.39.0.txt +237 -0
- data/doc/schema_modification.rdoc +585 -0
- data/doc/sharding.rdoc +99 -8
- data/doc/sql.rdoc +154 -112
- data/doc/testing.rdoc +169 -0
- data/doc/thread_safety.rdoc +17 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/validations.rdoc +1 -1
- data/doc/virtual_rows.rdoc +78 -43
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +17 -9
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +17 -11
- data/lib/sequel/adapters/do/postgres.rb +2 -61
- data/lib/sequel/adapters/do/sqlite.rb +0 -10
- data/lib/sequel/adapters/do.rb +18 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +453 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/db2.rb +56 -0
- data/lib/sequel/adapters/jdbc/derby.rb +325 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
- data/lib/sequel/adapters/jdbc/h2.rb +73 -31
- data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
- data/lib/sequel/adapters/jdbc/informix.rb +21 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
- data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
- data/lib/sequel/adapters/jdbc/progress.rb +21 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
- data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +279 -97
- data/lib/sequel/adapters/mock.rb +372 -0
- data/lib/sequel/adapters/mysql.rb +125 -222
- data/lib/sequel/adapters/mysql2.rb +65 -56
- data/lib/sequel/adapters/odbc/mssql.rb +17 -9
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +349 -53
- data/lib/sequel/adapters/postgres.rb +368 -138
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +343 -0
- data/lib/sequel/adapters/shared/firebird.rb +221 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +401 -116
- data/lib/sequel/adapters/shared/mysql.rb +424 -89
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
- data/lib/sequel/adapters/shared/oracle.rb +257 -45
- data/lib/sequel/adapters/shared/postgres.rb +675 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +285 -96
- data/lib/sequel/adapters/sqlite.rb +101 -39
- data/lib/sequel/adapters/swift/mysql.rb +10 -12
- data/lib/sequel/adapters/swift/postgres.rb +13 -79
- data/lib/sequel/adapters/swift/sqlite.rb +9 -6
- data/lib/sequel/adapters/swift.rb +28 -19
- data/lib/sequel/adapters/tinytds.rb +174 -22
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
- data/lib/sequel/adapters/utils/pg_types.rb +81 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
- data/lib/sequel/ast_transformer.rb +194 -0
- data/lib/sequel/connection_pool/sharded_single.rb +11 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +33 -2
- data/lib/sequel/connection_pool/single.rb +5 -0
- data/lib/sequel/connection_pool/threaded.rb +43 -14
- data/lib/sequel/connection_pool.rb +10 -2
- data/lib/sequel/core.rb +143 -11
- data/lib/sequel/database/connecting.rb +38 -28
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +63 -2
- data/lib/sequel/database/logging.rb +7 -2
- data/lib/sequel/database/misc.rb +190 -46
- data/lib/sequel/database/query.rb +272 -110
- data/lib/sequel/database/schema_generator.rb +103 -36
- data/lib/sequel/database/schema_methods.rb +300 -67
- data/lib/sequel/dataset/actions.rb +344 -90
- data/lib/sequel/dataset/features.rb +101 -8
- data/lib/sequel/dataset/graph.rb +42 -15
- data/lib/sequel/dataset/misc.rb +52 -25
- data/lib/sequel/dataset/mutation.rb +12 -7
- data/lib/sequel/dataset/prepared_statements.rb +68 -30
- data/lib/sequel/dataset/query.rb +305 -54
- data/lib/sequel/dataset/sql.rb +720 -292
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/_pretty_table.rb +83 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
- data/lib/sequel/extensions/blank.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +72 -0
- data/lib/sequel/extensions/constraint_validations.rb +451 -0
- data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
- data/lib/sequel/extensions/eval_inspect.rb +173 -0
- data/lib/sequel/extensions/inflector.rb +4 -0
- data/lib/sequel/extensions/looser_typecasting.rb +5 -4
- data/lib/sequel/extensions/migration.rb +100 -11
- data/lib/sequel/extensions/named_timezones.rb +9 -0
- data/lib/sequel/extensions/null_dataset.rb +94 -0
- data/lib/sequel/extensions/pagination.rb +4 -0
- data/lib/sequel/extensions/pg_array.rb +537 -0
- data/lib/sequel/extensions/pg_array_ops.rb +263 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
- data/lib/sequel/extensions/pg_hstore.rb +313 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
- data/lib/sequel/extensions/pg_inet.rb +113 -0
- data/lib/sequel/extensions/pg_interval.rb +191 -0
- data/lib/sequel/extensions/pg_json.rb +208 -0
- data/lib/sequel/extensions/pg_range.rb +512 -0
- data/lib/sequel/extensions/pg_range_ops.rb +150 -0
- data/lib/sequel/extensions/pg_row.rb +572 -0
- data/lib/sequel/extensions/pg_row_ops.rb +182 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
- data/lib/sequel/extensions/pretty_table.rb +17 -72
- data/lib/sequel/extensions/query.rb +8 -4
- data/lib/sequel/extensions/query_literals.rb +79 -0
- data/lib/sequel/extensions/schema_caching.rb +76 -0
- data/lib/sequel/extensions/schema_dumper.rb +282 -76
- data/lib/sequel/extensions/select_remove.rb +39 -0
- data/lib/sequel/extensions/server_block.rb +140 -0
- data/lib/sequel/extensions/split_array_nil.rb +65 -0
- data/lib/sequel/extensions/sql_expr.rb +8 -110
- data/lib/sequel/extensions/string_date_time.rb +4 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
- data/lib/sequel/extensions/to_dot.rb +99 -83
- data/lib/sequel/model/associations.rb +1263 -409
- data/lib/sequel/model/base.rb +624 -171
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +24 -2
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model.rb +30 -11
- data/lib/sequel/no_core_ext.rb +2 -0
- data/lib/sequel/plugins/active_model.rb +13 -1
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/caching.rb +25 -18
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/composition.rb +44 -12
- data/lib/sequel/plugins/constraint_validations.rb +198 -0
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +72 -0
- data/lib/sequel/plugins/dirty.rb +214 -0
- data/lib/sequel/plugins/eager_each.rb +59 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/plugins/identity_map.rb +134 -15
- data/lib/sequel/plugins/instance_filters.rb +10 -0
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +58 -6
- data/lib/sequel/plugins/list.rb +13 -2
- data/lib/sequel/plugins/many_through_many.rb +103 -51
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
- data/lib/sequel/plugins/nested_attributes.rb +150 -66
- data/lib/sequel/plugins/optimistic_locking.rb +8 -0
- data/lib/sequel/plugins/pg_row.rb +121 -0
- data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
- data/lib/sequel/plugins/prepared_statements.rb +167 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +31 -17
- data/lib/sequel/plugins/schema.rb +8 -3
- data/lib/sequel/plugins/serialization.rb +98 -49
- data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
- data/lib/sequel/plugins/static_cache.rb +99 -0
- data/lib/sequel/plugins/subclasses.rb +29 -3
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/tree.rb +3 -3
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/plugins/validation_helpers.rb +55 -4
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +649 -122
- data/lib/sequel/timezones.rb +67 -40
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/firebird_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +194 -66
- data/spec/adapters/mysql_spec.rb +475 -306
- data/spec/adapters/oracle_spec.rb +92 -117
- data/spec/adapters/postgres_spec.rb +1982 -325
- data/spec/adapters/spec_helper.rb +9 -6
- data/spec/adapters/sqlite_spec.rb +203 -75
- data/spec/core/connection_pool_spec.rb +218 -93
- data/spec/core/database_spec.rb +956 -522
- data/spec/core/dataset_spec.rb +1631 -1242
- data/spec/core/expression_filters_spec.rb +690 -334
- data/spec/core/mock_adapter_spec.rb +453 -0
- data/spec/core/object_graph_spec.rb +88 -129
- data/spec/core/schema_generator_spec.rb +4 -4
- data/spec/core/schema_spec.rb +556 -53
- data/spec/core/spec_helper.rb +7 -48
- data/spec/core_extensions_spec.rb +626 -0
- data/spec/extensions/active_model_spec.rb +13 -0
- data/spec/extensions/arbitrary_servers_spec.rb +110 -0
- data/spec/extensions/association_autoreloading_spec.rb +18 -10
- data/spec/extensions/association_dependencies_spec.rb +15 -25
- data/spec/extensions/association_pks_spec.rb +66 -32
- data/spec/extensions/association_proxies_spec.rb +4 -4
- data/spec/extensions/boolean_readers_spec.rb +25 -25
- data/spec/extensions/caching_spec.rb +47 -51
- data/spec/extensions/class_table_inheritance_spec.rb +31 -83
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
- data/spec/extensions/constraint_validations_spec.rb +316 -0
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +88 -0
- data/spec/extensions/dirty_spec.rb +155 -0
- data/spec/extensions/eager_each_spec.rb +34 -0
- data/spec/extensions/eval_inspect_spec.rb +67 -0
- data/spec/extensions/force_encoding_spec.rb +4 -2
- data/spec/extensions/hook_class_methods_spec.rb +97 -128
- data/spec/extensions/identity_map_spec.rb +142 -24
- data/spec/extensions/inflector_spec.rb +0 -4
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +72 -0
- data/spec/extensions/json_serializer_spec.rb +49 -12
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +49 -40
- data/spec/extensions/looser_typecasting_spec.rb +7 -7
- data/spec/extensions/many_through_many_spec.rb +368 -254
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
- data/spec/extensions/migration_spec.rb +140 -35
- data/spec/extensions/named_timezones_spec.rb +29 -11
- data/spec/extensions/nested_attributes_spec.rb +268 -89
- data/spec/extensions/null_dataset_spec.rb +85 -0
- data/spec/extensions/optimistic_locking_spec.rb +24 -21
- data/spec/extensions/pg_array_ops_spec.rb +112 -0
- data/spec/extensions/pg_array_spec.rb +320 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
- data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
- data/spec/extensions/pg_hstore_spec.rb +194 -0
- data/spec/extensions/pg_inet_spec.rb +47 -0
- data/spec/extensions/pg_interval_spec.rb +72 -0
- data/spec/extensions/pg_json_spec.rb +99 -0
- data/spec/extensions/pg_range_ops_spec.rb +56 -0
- data/spec/extensions/pg_range_spec.rb +395 -0
- data/spec/extensions/pg_row_ops_spec.rb +58 -0
- data/spec/extensions/pg_row_plugin_spec.rb +49 -0
- data/spec/extensions/pg_row_spec.rb +323 -0
- data/spec/extensions/pg_statement_cache_spec.rb +208 -0
- data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
- data/spec/extensions/prepared_statements_spec.rb +87 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +7 -6
- data/spec/extensions/query_literals_spec.rb +167 -0
- data/spec/extensions/query_spec.rb +9 -3
- data/spec/extensions/rcte_tree_spec.rb +50 -43
- data/spec/extensions/schema_caching_spec.rb +41 -0
- data/spec/extensions/schema_dumper_spec.rb +434 -49
- data/spec/extensions/schema_spec.rb +30 -49
- data/spec/extensions/select_remove_spec.rb +38 -0
- data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
- data/spec/extensions/serialization_spec.rb +86 -48
- data/spec/extensions/server_block_spec.rb +90 -0
- data/spec/extensions/sharding_spec.rb +69 -143
- data/spec/extensions/single_table_inheritance_spec.rb +23 -29
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +34 -67
- data/spec/extensions/split_array_nil_spec.rb +24 -0
- data/spec/extensions/sql_expr_spec.rb +29 -60
- data/spec/extensions/static_cache_spec.rb +145 -0
- data/spec/extensions/subclasses_spec.rb +14 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +8 -11
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/typecast_on_load_spec.rb +9 -6
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +58 -67
- data/spec/extensions/validation_helpers_spec.rb +31 -37
- data/spec/extensions/xml_serializer_spec.rb +29 -4
- data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
- data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
- data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
- data/spec/integration/associations_test.rb +659 -21
- data/spec/integration/database_test.rb +11 -5
- data/spec/integration/dataset_test.rb +717 -129
- data/spec/integration/eager_loader_test.rb +38 -54
- data/spec/integration/migrator_test.rb +2 -3
- data/spec/integration/model_test.rb +81 -2
- data/spec/integration/plugin_test.rb +604 -147
- data/spec/integration/prepared_statement_test.rb +272 -128
- data/spec/integration/schema_test.rb +367 -57
- data/spec/integration/spec_helper.rb +42 -32
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +31 -8
- data/spec/model/association_reflection_spec.rb +207 -8
- data/spec/model/associations_spec.rb +1206 -693
- data/spec/model/base_spec.rb +334 -100
- data/spec/model/dataset_methods_spec.rb +45 -27
- data/spec/model/eager_loading_spec.rb +828 -692
- data/spec/model/hooks_spec.rb +355 -67
- data/spec/model/model_spec.rb +251 -179
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +506 -232
- data/spec/model/spec_helper.rb +16 -77
- data/spec/model/validations_spec.rb +38 -2
- data/spec/rcov.opts +2 -0
- metadata +209 -80
- data/spec/core/core_sql_spec.rb +0 -451
|
@@ -6,16 +6,10 @@ module Sequel
|
|
|
6
6
|
def database_type
|
|
7
7
|
:progress
|
|
8
8
|
end
|
|
9
|
-
|
|
10
|
-
def dataset(opts = nil)
|
|
11
|
-
ds = super
|
|
12
|
-
ds.extend(DatasetMethods)
|
|
13
|
-
ds
|
|
14
|
-
end
|
|
15
9
|
end
|
|
16
10
|
|
|
17
11
|
module DatasetMethods
|
|
18
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'limit distinct columns from join where group order having compounds')
|
|
12
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select limit distinct columns from join where group order having compounds')
|
|
19
13
|
|
|
20
14
|
# Progress requires SQL standard datetimes
|
|
21
15
|
def requires_sql_standard_datetimes?
|
|
@@ -37,7 +31,10 @@ module Sequel
|
|
|
37
31
|
# The Progress adapter targets Progress 9, so it silently ignores the option.
|
|
38
32
|
def select_limit_sql(sql)
|
|
39
33
|
raise(Error, "OFFSET not supported") if @opts[:offset]
|
|
40
|
-
#
|
|
34
|
+
# if l = @opts[:limit]
|
|
35
|
+
# sql << " TOP "
|
|
36
|
+
# literal_append(sql, l)
|
|
37
|
+
# end
|
|
41
38
|
end
|
|
42
39
|
end
|
|
43
40
|
end
|
|
@@ -7,16 +7,13 @@ module Sequel
|
|
|
7
7
|
AUTO_VACUUM = [:none, :full, :incremental].freeze
|
|
8
8
|
PRIMARY_KEY_INDEX_RE = /\Asqlite_autoindex_/.freeze
|
|
9
9
|
SYNCHRONOUS = [:off, :normal, :full].freeze
|
|
10
|
-
TABLES_FILTER = "type = 'table' AND NOT name = 'sqlite_sequence'"
|
|
10
|
+
TABLES_FILTER = "type = 'table' AND NOT name = 'sqlite_sequence'".freeze
|
|
11
11
|
TEMP_STORE = [:default, :file, :memory].freeze
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
generator ||= Schema::AlterTableGenerator.new(self, &block)
|
|
18
|
-
transaction{generator.operations.each{|op| alter_table_sql_list(name, [op]).flatten.each{|sql| execute_ddl(sql)}}}
|
|
19
|
-
end
|
|
12
|
+
VIEWS_FILTER = "type = 'view'".freeze
|
|
13
|
+
|
|
14
|
+
# Whether to use integers for booleans in the database. SQLite recommends
|
|
15
|
+
# booleans be stored as integers, but historically Sequel has used 't'/'f'.
|
|
16
|
+
attr_accessor :integer_booleans
|
|
20
17
|
|
|
21
18
|
# A symbol signifying the value of the auto_vacuum PRAGMA.
|
|
22
19
|
def auto_vacuum
|
|
@@ -30,7 +27,14 @@ module Sequel
|
|
|
30
27
|
value = AUTO_VACUUM.index(value) || (raise Error, "Invalid value for auto_vacuum option. Please specify one of :none, :full, :incremental.")
|
|
31
28
|
pragma_set(:auto_vacuum, value)
|
|
32
29
|
end
|
|
33
|
-
|
|
30
|
+
|
|
31
|
+
# Set the case_sensitive_like PRAGMA using the given boolean value, if using
|
|
32
|
+
# SQLite 3.2.3+. If not using 3.2.3+, no error is raised. See pragma_set.
|
|
33
|
+
# Consider using the :case_sensitive_like Database option instead.
|
|
34
|
+
def case_sensitive_like=(value)
|
|
35
|
+
pragma_set(:case_sensitive_like, !!value ? 'on' : 'off') if sqlite_version >= 30203
|
|
36
|
+
end
|
|
37
|
+
|
|
34
38
|
# SQLite uses the :sqlite database type.
|
|
35
39
|
def database_type
|
|
36
40
|
:sqlite
|
|
@@ -49,22 +53,33 @@ module Sequel
|
|
|
49
53
|
pragma_set(:foreign_keys, !!value ? 'on' : 'off') if sqlite_version >= 30619
|
|
50
54
|
end
|
|
51
55
|
|
|
56
|
+
# Return the array of foreign key info hashes using the foreign_key_list PRAGMA,
|
|
57
|
+
# including information for the :on_update and :on_delete entries.
|
|
58
|
+
def foreign_key_list(table, opts={})
|
|
59
|
+
m = output_identifier_meth
|
|
60
|
+
h = {}
|
|
61
|
+
metadata_dataset.with_sql("PRAGMA foreign_key_list(?)", input_identifier_meth.call(table)).each do |row|
|
|
62
|
+
if r = h[row[:id]]
|
|
63
|
+
r[:columns] << m.call(row[:from])
|
|
64
|
+
r[:key] << m.call(row[:to]) if r[:key]
|
|
65
|
+
else
|
|
66
|
+
h[row[:id]] = {:columns=>[m.call(row[:from])], :table=>m.call(row[:table]), :key=>([m.call(row[:to])] if row[:to]), :on_update=>on_delete_sql_to_sym(row[:on_update]), :on_delete=>on_delete_sql_to_sym(row[:on_delete])}
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
h.values
|
|
70
|
+
end
|
|
71
|
+
|
|
52
72
|
# Use the index_list and index_info PRAGMAs to determine the indexes on the table.
|
|
53
73
|
def indexes(table, opts={})
|
|
54
74
|
m = output_identifier_meth
|
|
55
75
|
im = input_identifier_meth
|
|
56
76
|
indexes = {}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
nil
|
|
64
|
-
else
|
|
65
|
-
indexes.each do |k, v|
|
|
66
|
-
v[:columns] = metadata_dataset.with_sql("PRAGMA index_info(?)", im.call(k)).map(:name).map{|x| m.call(x)}
|
|
67
|
-
end
|
|
77
|
+
metadata_dataset.with_sql("PRAGMA index_list(?)", im.call(table)).each do |r|
|
|
78
|
+
next if r[:name] =~ PRIMARY_KEY_INDEX_RE
|
|
79
|
+
indexes[m.call(r[:name])] = {:unique=>r[:unique].to_i==1}
|
|
80
|
+
end
|
|
81
|
+
indexes.each do |k, v|
|
|
82
|
+
v[:columns] = metadata_dataset.with_sql("PRAGMA index_info(?)", im.call(k)).map(:name).map{|x| m.call(x)}
|
|
68
83
|
end
|
|
69
84
|
indexes
|
|
70
85
|
end
|
|
@@ -83,6 +98,11 @@ module Sequel
|
|
|
83
98
|
def pragma_set(name, value)
|
|
84
99
|
execute_ddl("PRAGMA #{name} = #{value}")
|
|
85
100
|
end
|
|
101
|
+
|
|
102
|
+
# Set the integer_booleans option using the passed in :integer_boolean option.
|
|
103
|
+
def set_integer_booleans
|
|
104
|
+
@integer_booleans = typecast_value_boolean(@opts[:integer_booleans])
|
|
105
|
+
end
|
|
86
106
|
|
|
87
107
|
# The version of the server as an integer, where 3.6.19 = 30619.
|
|
88
108
|
# If the server version can't be determined, 0 is used.
|
|
@@ -96,11 +116,29 @@ module Sequel
|
|
|
96
116
|
end
|
|
97
117
|
end
|
|
98
118
|
|
|
119
|
+
# SQLite supports CREATE TABLE IF NOT EXISTS syntax since 3.3.0.
|
|
120
|
+
def supports_create_table_if_not_exists?
|
|
121
|
+
sqlite_version >= 30300
|
|
122
|
+
end
|
|
123
|
+
|
|
99
124
|
# SQLite 3.6.8+ supports savepoints.
|
|
100
125
|
def supports_savepoints?
|
|
101
126
|
sqlite_version >= 30608
|
|
102
127
|
end
|
|
103
128
|
|
|
129
|
+
# Override the default setting for whether to use timezones in timestamps.
|
|
130
|
+
# For backwards compatibility, it is set to +true+ by default.
|
|
131
|
+
# Anyone wanting to use SQLite's datetime functions should set it to +false+
|
|
132
|
+
# using this method. It's possible that the default will change in a future version,
|
|
133
|
+
# so anyone relying on timezones in timestamps should set this to +true+.
|
|
134
|
+
attr_writer :use_timestamp_timezones
|
|
135
|
+
|
|
136
|
+
# SQLite supports timezones in timestamps, since it just stores them as strings,
|
|
137
|
+
# but it breaks the usage of SQLite's datetime functions.
|
|
138
|
+
def use_timestamp_timezones?
|
|
139
|
+
defined?(@use_timestamp_timezones) ? @use_timestamp_timezones : (@use_timestamp_timezones = true)
|
|
140
|
+
end
|
|
141
|
+
|
|
104
142
|
# A symbol signifying the value of the synchronous PRAGMA.
|
|
105
143
|
def synchronous
|
|
106
144
|
SYNCHRONOUS[pragma_get(:synchronous).to_i]
|
|
@@ -118,8 +156,7 @@ module Sequel
|
|
|
118
156
|
# Options:
|
|
119
157
|
# * :server - Set the server to use.
|
|
120
158
|
def tables(opts={})
|
|
121
|
-
|
|
122
|
-
metadata_dataset.from(:sqlite_master).server(opts[:server]).filter(TABLES_FILTER).map{|r| m.call(r[:name])}
|
|
159
|
+
tables_and_views(TABLES_FILTER, opts)
|
|
123
160
|
end
|
|
124
161
|
|
|
125
162
|
# A symbol signifying the value of the temp_store PRAGMA.
|
|
@@ -134,8 +171,35 @@ module Sequel
|
|
|
134
171
|
pragma_set(:temp_store, value)
|
|
135
172
|
end
|
|
136
173
|
|
|
174
|
+
# Array of symbols specifying the view names in the current database.
|
|
175
|
+
#
|
|
176
|
+
# Options:
|
|
177
|
+
# * :server - Set the server to use.
|
|
178
|
+
def views(opts={})
|
|
179
|
+
tables_and_views(VIEWS_FILTER, opts)
|
|
180
|
+
end
|
|
181
|
+
|
|
137
182
|
private
|
|
138
183
|
|
|
184
|
+
# Run all alter_table commands in a transaction. This is technically only
|
|
185
|
+
# needed for drop column.
|
|
186
|
+
def apply_alter_table(table, ops)
|
|
187
|
+
fks = foreign_keys
|
|
188
|
+
self.foreign_keys = false if fks
|
|
189
|
+
transaction do
|
|
190
|
+
if ops.length > 1 && ops.all?{|op| op[:op] == :add_constraint}
|
|
191
|
+
# If you are just doing constraints, apply all of them at the same time,
|
|
192
|
+
# as otherwise all but the last one get lost.
|
|
193
|
+
alter_table_sql_list(table, [{:op=>:add_constraints, :ops=>ops}]).flatten.each{|sql| execute_ddl(sql)}
|
|
194
|
+
else
|
|
195
|
+
# Run each operation separately, as later operations may depend on the
|
|
196
|
+
# results of earlier operations.
|
|
197
|
+
ops.each{|op| alter_table_sql_list(table, [op]).flatten.each{|sql| execute_ddl(sql)}}
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
self.foreign_keys = true if fks
|
|
201
|
+
end
|
|
202
|
+
|
|
139
203
|
# SQLite supports limited table modification. You can add a column
|
|
140
204
|
# or an index. Dropping columns is supported by copying the table into
|
|
141
205
|
# a temporary table, dropping the table, and creating a new table without
|
|
@@ -161,13 +225,26 @@ module Sequel
|
|
|
161
225
|
when :set_column_null
|
|
162
226
|
duplicate_table(table){|columns| columns.each{|s| s[:null] = op[:null] if s[:name].to_s == op[:name].to_s}}
|
|
163
227
|
when :set_column_type
|
|
164
|
-
duplicate_table(table){|columns| columns.each{|s| s
|
|
228
|
+
duplicate_table(table){|columns| columns.each{|s| s.merge!(op) if s[:name].to_s == op[:name].to_s}}
|
|
229
|
+
when :drop_constraint
|
|
230
|
+
case op[:type]
|
|
231
|
+
when :primary_key
|
|
232
|
+
duplicate_table(table){|columns| columns.each{|s| s[:primary_key] = nil}}
|
|
233
|
+
when :foreign_key
|
|
234
|
+
duplicate_table(table, :no_foreign_keys=>true)
|
|
235
|
+
else
|
|
236
|
+
duplicate_table(table)
|
|
237
|
+
end
|
|
238
|
+
when :add_constraint
|
|
239
|
+
duplicate_table(table, :constraints=>[op])
|
|
240
|
+
when :add_constraints
|
|
241
|
+
duplicate_table(table, :constraints=>op[:ops])
|
|
165
242
|
else
|
|
166
|
-
raise Error, "Unsupported ALTER TABLE operation"
|
|
243
|
+
raise Error, "Unsupported ALTER TABLE operation: #{op[:op].inspect}"
|
|
167
244
|
end
|
|
168
245
|
end
|
|
169
|
-
|
|
170
|
-
#
|
|
246
|
+
|
|
247
|
+
# A name to use for the backup table
|
|
171
248
|
def backup_table_name(table, opts={})
|
|
172
249
|
table = table.gsub('`', '')
|
|
173
250
|
(opts[:times]||1000).times do |i|
|
|
@@ -176,17 +253,19 @@ module Sequel
|
|
|
176
253
|
end
|
|
177
254
|
end
|
|
178
255
|
|
|
179
|
-
#
|
|
180
|
-
def
|
|
181
|
-
|
|
256
|
+
# Surround default with parens to appease SQLite
|
|
257
|
+
def column_definition_default_sql(sql, column)
|
|
258
|
+
sql << " DEFAULT (#{literal(column[:default])})" if column.include?(:default)
|
|
182
259
|
end
|
|
183
|
-
|
|
260
|
+
|
|
184
261
|
# Array of PRAGMA SQL statements based on the Database options that should be applied to
|
|
185
262
|
# new connections.
|
|
186
263
|
def connection_pragmas
|
|
187
264
|
ps = []
|
|
188
265
|
v = typecast_value_boolean(opts.fetch(:foreign_keys, 1))
|
|
189
266
|
ps << "PRAGMA foreign_keys = #{v ? 1 : 0}"
|
|
267
|
+
v = typecast_value_boolean(opts.fetch(:case_sensitive_like, 1))
|
|
268
|
+
ps << "PRAGMA case_sensitive_like = #{v ? 1 : 0}"
|
|
190
269
|
[[:auto_vacuum, AUTO_VACUUM], [:synchronous, SYNCHRONOUS], [:temp_store, TEMP_STORE]].each do |prag, con|
|
|
191
270
|
if v = opts[prag]
|
|
192
271
|
raise(Error, "Value for PRAGMA #{prag} not supported, should be one of #{con.join(', ')}") unless v = con.index(v.to_sym)
|
|
@@ -196,28 +275,13 @@ module Sequel
|
|
|
196
275
|
ps
|
|
197
276
|
end
|
|
198
277
|
|
|
199
|
-
# The array of column schema hashes
|
|
200
|
-
def defined_columns_for(table
|
|
278
|
+
# The array of column schema hashes for the current columns in the table
|
|
279
|
+
def defined_columns_for(table)
|
|
201
280
|
cols = parse_pragma(table, {})
|
|
202
281
|
cols.each do |c|
|
|
203
282
|
c[:default] = LiteralString.new(c[:default]) if c[:default]
|
|
204
283
|
c[:type] = c[:db_type]
|
|
205
284
|
end
|
|
206
|
-
if opts[:except]
|
|
207
|
-
nono= Array(opts[:except]).compact.map{|n| n.to_s}
|
|
208
|
-
cols.reject!{|c| nono.include? c[:name] }
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
if foreign_keys
|
|
212
|
-
metadata_dataset.with_sql("PRAGMA foreign_key_list(?)", input_identifier_meth.call(table)).each do |row|
|
|
213
|
-
c = cols.find {|co| co[:name] == row[:from] } or next
|
|
214
|
-
c[:table] = row[:table]
|
|
215
|
-
c[:key] = row[:to]
|
|
216
|
-
c[:on_update] = on_delete_sql_to_sym(row[:on_update])
|
|
217
|
-
c[:on_delete] = on_delete_sql_to_sym(row[:on_delete])
|
|
218
|
-
# is there any way to get deferrable status?
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
285
|
cols
|
|
222
286
|
end
|
|
223
287
|
|
|
@@ -231,17 +295,40 @@ module Sequel
|
|
|
231
295
|
opts[:old_columns_proc].call(old_columns) if opts[:old_columns_proc]
|
|
232
296
|
|
|
233
297
|
yield def_columns if block_given?
|
|
234
|
-
|
|
298
|
+
|
|
299
|
+
constraints = (opts[:constraints] || []).dup
|
|
300
|
+
pks = []
|
|
301
|
+
def_columns.each{|c| pks << c[:name] if c[:primary_key]}
|
|
302
|
+
if pks.length > 1
|
|
303
|
+
constraints << {:type=>:primary_key, :columns=>pks}
|
|
304
|
+
def_columns.each{|c| c[:primary_key] = false if c[:primary_key]}
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# If dropping a foreign key constraint, drop all foreign key constraints,
|
|
308
|
+
# as there is no way to determine which one to drop.
|
|
309
|
+
unless opts[:no_foreign_keys]
|
|
310
|
+
fks = foreign_key_list(table)
|
|
311
|
+
|
|
312
|
+
# If dropping a column, if there is a foreign key with that
|
|
313
|
+
# column, don't include it when building a copy of the table.
|
|
314
|
+
if ocp = opts[:old_columns_proc]
|
|
315
|
+
fks.delete_if{|c| ocp.call(c[:columns].dup) != c[:columns]}
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
constraints.concat(fks.each{|h| h[:type] = :foreign_key})
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def_columns_str = (def_columns.map{|c| column_definition_sql(c)} + constraints.map{|c| constraint_definition_sql(c)}).join(', ')
|
|
235
322
|
new_columns = old_columns.dup
|
|
236
323
|
opts[:new_columns_proc].call(new_columns) if opts[:new_columns_proc]
|
|
237
324
|
|
|
238
325
|
qt = quote_schema_table(table)
|
|
239
326
|
bt = quote_identifier(backup_table_name(qt))
|
|
240
327
|
a = [
|
|
241
|
-
"
|
|
242
|
-
"
|
|
243
|
-
"
|
|
244
|
-
"
|
|
328
|
+
"ALTER TABLE #{qt} RENAME TO #{bt}",
|
|
329
|
+
"CREATE TABLE #{qt}(#{def_columns_str})",
|
|
330
|
+
"INSERT INTO #{qt}(#{dataset.send(:identifier_list, new_columns)}) SELECT #{dataset.send(:identifier_list, old_columns)} FROM #{bt}",
|
|
331
|
+
"DROP TABLE #{bt}"
|
|
245
332
|
]
|
|
246
333
|
indexes(table).each do |name, h|
|
|
247
334
|
if (h[:columns].map{|x| x.to_s} - new_columns).empty?
|
|
@@ -280,7 +367,7 @@ module Sequel
|
|
|
280
367
|
|
|
281
368
|
# Parse the output of the table_info pragma
|
|
282
369
|
def parse_pragma(table_name, opts)
|
|
283
|
-
metadata_dataset.with_sql("PRAGMA table_info(?)", input_identifier_meth.call(table_name)).map do |row|
|
|
370
|
+
metadata_dataset.with_sql("PRAGMA table_info(?)", input_identifier_meth(opts[:dataset]).call(table_name)).map do |row|
|
|
284
371
|
row.delete(:cid)
|
|
285
372
|
row[:allow_null] = row.delete(:notnull).to_i == 0
|
|
286
373
|
row[:default] = row.delete(:dflt_value)
|
|
@@ -294,65 +381,123 @@ module Sequel
|
|
|
294
381
|
|
|
295
382
|
# SQLite treats integer primary keys as autoincrementing (alias of rowid).
|
|
296
383
|
def schema_autoincrementing_primary_key?(schema)
|
|
297
|
-
super
|
|
384
|
+
super && schema[:db_type].downcase == 'integer'
|
|
298
385
|
end
|
|
299
386
|
|
|
300
387
|
# SQLite supports schema parsing using the table_info PRAGMA, so
|
|
301
388
|
# parse the output of that into the format Sequel expects.
|
|
302
389
|
def schema_parse_table(table_name, opts)
|
|
303
|
-
m = output_identifier_meth
|
|
390
|
+
m = output_identifier_meth(opts[:dataset])
|
|
304
391
|
parse_pragma(table_name, opts).map do |row|
|
|
305
392
|
[m.call(row.delete(:name)), row]
|
|
306
393
|
end
|
|
307
394
|
end
|
|
308
395
|
|
|
309
|
-
#
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
396
|
+
# Backbone of the tables and views support.
|
|
397
|
+
def tables_and_views(filter, opts)
|
|
398
|
+
m = output_identifier_meth
|
|
399
|
+
metadata_dataset.from(:sqlite_master).server(opts[:server]).filter(filter).map{|r| m.call(r[:name])}
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
# SQLite only supports AUTOINCREMENT on integer columns, not
|
|
403
|
+
# bigint columns, so use integer instead of bigint for those
|
|
404
|
+
# columns.
|
|
313
405
|
def type_literal_generic_bignum(column)
|
|
314
|
-
:integer
|
|
406
|
+
column[:auto_increment] ? :integer : super
|
|
315
407
|
end
|
|
316
408
|
end
|
|
317
409
|
|
|
318
410
|
# Instance methods for datasets that connect to an SQLite database
|
|
319
411
|
module DatasetMethods
|
|
320
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'distinct columns from join where group having compounds order limit')
|
|
321
|
-
COMMA_SEPARATOR = ', '.freeze
|
|
412
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct columns from join where group having compounds order limit')
|
|
322
413
|
CONSTANT_MAP = {:CURRENT_DATE=>"date(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIMESTAMP=>"datetime(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIME=>"time(CURRENT_TIMESTAMP, 'localtime')".freeze}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
414
|
+
EMULATED_FUNCTION_MAP = {:char_length=>'length'.freeze}
|
|
415
|
+
EXTRACT_MAP = {:year=>"'%Y'", :month=>"'%m'", :day=>"'%d'", :hour=>"'%H'", :minute=>"'%M'", :second=>"'%f'"}
|
|
416
|
+
NOT_SPACE = Dataset::NOT_SPACE
|
|
417
|
+
COMMA = Dataset::COMMA
|
|
418
|
+
PAREN_CLOSE = Dataset::PAREN_CLOSE
|
|
419
|
+
AS = Dataset::AS
|
|
420
|
+
APOS = Dataset::APOS
|
|
421
|
+
EXTRACT_OPEN = "CAST(strftime(".freeze
|
|
422
|
+
EXTRACT_CLOSE = ') AS '.freeze
|
|
423
|
+
NUMERIC = 'NUMERIC'.freeze
|
|
424
|
+
INTEGER = 'INTEGER'.freeze
|
|
425
|
+
BACKTICK = '`'.freeze
|
|
426
|
+
BACKTICK_RE = /`/.freeze
|
|
427
|
+
DOUBLE_BACKTICK = '``'.freeze
|
|
428
|
+
BLOB_START = "X'".freeze
|
|
429
|
+
HSTAR = "H*".freeze
|
|
430
|
+
DATE_OPEN = "date(".freeze
|
|
431
|
+
DATETIME_OPEN = "datetime(".freeze
|
|
432
|
+
|
|
433
|
+
def cast_sql_append(sql, expr, type)
|
|
434
|
+
if type == Time or type == DateTime
|
|
435
|
+
sql << DATETIME_OPEN
|
|
436
|
+
literal_append(sql, expr)
|
|
437
|
+
sql << PAREN_CLOSE
|
|
438
|
+
elsif type == Date
|
|
439
|
+
sql << DATE_OPEN
|
|
440
|
+
literal_append(sql, expr)
|
|
441
|
+
sql << PAREN_CLOSE
|
|
442
|
+
else
|
|
443
|
+
super
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
# SQLite is case insensitive (depending on pragma), so use LIKE for ILIKE.
|
|
448
|
+
# It also doesn't support a NOT LIKE b, you need to use NOT (a LIKE b).
|
|
449
|
+
# It doesn't support xor or the extract function natively, so those have to be emulated.
|
|
450
|
+
def complex_expression_sql_append(sql, op, args)
|
|
328
451
|
case op
|
|
329
|
-
when
|
|
330
|
-
|
|
331
|
-
when :
|
|
332
|
-
|
|
333
|
-
|
|
452
|
+
when :ILIKE
|
|
453
|
+
super(sql, :LIKE, args.map{|a| SQL::Function.new(:upper, a)})
|
|
454
|
+
when :"NOT LIKE", :"NOT ILIKE"
|
|
455
|
+
sql << NOT_SPACE
|
|
456
|
+
complex_expression_sql_append(sql, (op == :"NOT ILIKE" ? :ILIKE : :LIKE), args)
|
|
457
|
+
when :^
|
|
458
|
+
sql << complex_expression_arg_pairs(args) do |a, b|
|
|
459
|
+
a = literal(a)
|
|
460
|
+
b = literal(b)
|
|
461
|
+
"((~(#{a} & #{b})) & (#{a} | #{b}))"
|
|
462
|
+
end
|
|
463
|
+
when :extract
|
|
464
|
+
part = args.at(0)
|
|
465
|
+
raise(Sequel::Error, "unsupported extract argument: #{part.inspect}") unless format = EXTRACT_MAP[part]
|
|
466
|
+
sql << EXTRACT_OPEN << format << COMMA
|
|
467
|
+
literal_append(sql, args.at(1))
|
|
468
|
+
sql << EXTRACT_CLOSE << (part == :second ? NUMERIC : INTEGER) << PAREN_CLOSE
|
|
334
469
|
else
|
|
335
|
-
super
|
|
470
|
+
super
|
|
336
471
|
end
|
|
337
472
|
end
|
|
338
473
|
|
|
339
|
-
#
|
|
340
|
-
|
|
341
|
-
|
|
474
|
+
# SQLite has CURRENT_TIMESTAMP and related constants in UTC instead
|
|
475
|
+
# of in localtime, so convert those constants to local time.
|
|
476
|
+
def constant_sql_append(sql, constant)
|
|
477
|
+
if c = CONSTANT_MAP[constant]
|
|
478
|
+
sql << c
|
|
479
|
+
else
|
|
480
|
+
super
|
|
481
|
+
end
|
|
342
482
|
end
|
|
343
483
|
|
|
344
484
|
# SQLite performs a TRUNCATE style DELETE if no filter is specified.
|
|
345
485
|
# Since we want to always return the count of records, add a condition
|
|
346
486
|
# that is always true and then delete.
|
|
347
487
|
def delete
|
|
348
|
-
@opts[:where] ? super :
|
|
488
|
+
@opts[:where] ? super : where(1=>1).delete
|
|
349
489
|
end
|
|
350
490
|
|
|
351
491
|
# Return an array of strings specifying a query explanation for a SELECT of the
|
|
352
|
-
# current dataset.
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
492
|
+
# current dataset. Currently, the options are ignore, but it accepts options
|
|
493
|
+
# to be compatible with other adapters.
|
|
494
|
+
def explain(opts=nil)
|
|
495
|
+
# Load the PrettyTable class, needed for explain output
|
|
496
|
+
Sequel.extension(:_pretty_table) unless defined?(Sequel::PrettyTable)
|
|
497
|
+
|
|
498
|
+
ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}")
|
|
499
|
+
rows = ds.all
|
|
500
|
+
Sequel::PrettyTable.string(rows, ds.columns)
|
|
356
501
|
end
|
|
357
502
|
|
|
358
503
|
# HAVING requires GROUP BY on SQLite
|
|
@@ -362,8 +507,20 @@ module Sequel
|
|
|
362
507
|
end
|
|
363
508
|
|
|
364
509
|
# SQLite uses the nonstandard ` (backtick) for quoting identifiers.
|
|
365
|
-
def
|
|
366
|
-
|
|
510
|
+
def quoted_identifier_append(sql, c)
|
|
511
|
+
sql << BACKTICK << c.to_s.gsub(BACKTICK_RE, DOUBLE_BACKTICK) << BACKTICK
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
# When a qualified column is selected on SQLite and the qualifier
|
|
515
|
+
# is a subselect, the column name used is the full qualified name
|
|
516
|
+
# (including the qualifier) instead of just the column name. To
|
|
517
|
+
# get correct column names, you must use an alias.
|
|
518
|
+
def select(*cols)
|
|
519
|
+
if ((f = @opts[:from]) && f.any?{|t| t.is_a?(Dataset) || (t.is_a?(SQL::AliasedExpression) && t.expression.is_a?(Dataset))}) || ((j = @opts[:join]) && j.any?{|t| t.table.is_a?(Dataset)})
|
|
520
|
+
super(*cols.map{|c| alias_qualified_column(c)})
|
|
521
|
+
else
|
|
522
|
+
super
|
|
523
|
+
end
|
|
367
524
|
end
|
|
368
525
|
|
|
369
526
|
# SQLite does not support INTERSECT ALL or EXCEPT ALL
|
|
@@ -382,31 +539,63 @@ module Sequel
|
|
|
382
539
|
end
|
|
383
540
|
|
|
384
541
|
# SQLite supports timezones in literal timestamps, since it stores them
|
|
385
|
-
# as text.
|
|
542
|
+
# as text. But using timezones in timestamps breaks SQLite datetime
|
|
543
|
+
# functions, so we allow the user to override the default per database.
|
|
386
544
|
def supports_timestamp_timezones?
|
|
387
|
-
|
|
545
|
+
db.use_timestamp_timezones?
|
|
388
546
|
end
|
|
389
547
|
|
|
548
|
+
# SQLite cannot use WHERE 't'.
|
|
549
|
+
def supports_where_true?
|
|
550
|
+
false
|
|
551
|
+
end
|
|
552
|
+
|
|
390
553
|
private
|
|
391
554
|
|
|
392
555
|
# SQLite uses string literals instead of identifiers in AS clauses.
|
|
393
|
-
def
|
|
556
|
+
def as_sql_append(sql, aliaz)
|
|
394
557
|
aliaz = aliaz.value if aliaz.is_a?(SQL::Identifier)
|
|
395
|
-
|
|
558
|
+
sql << AS
|
|
559
|
+
literal_append(sql, aliaz.to_s)
|
|
396
560
|
end
|
|
397
|
-
|
|
561
|
+
|
|
562
|
+
# If col is a qualified column, alias it to the same as the column name
|
|
563
|
+
def alias_qualified_column(col)
|
|
564
|
+
case col
|
|
565
|
+
when Symbol
|
|
566
|
+
t, c, a = split_symbol(col)
|
|
567
|
+
if t && !a
|
|
568
|
+
alias_qualified_column(SQL::QualifiedIdentifier.new(t, c))
|
|
569
|
+
else
|
|
570
|
+
col
|
|
571
|
+
end
|
|
572
|
+
when SQL::QualifiedIdentifier
|
|
573
|
+
SQL::AliasedExpression.new(col, col.column)
|
|
574
|
+
else
|
|
575
|
+
col
|
|
576
|
+
end
|
|
577
|
+
end
|
|
578
|
+
|
|
398
579
|
# SQL fragment specifying a list of identifiers
|
|
399
580
|
def identifier_list(columns)
|
|
400
|
-
columns.map{|i| quote_identifier(i)}.join(
|
|
581
|
+
columns.map{|i| quote_identifier(i)}.join(COMMA)
|
|
401
582
|
end
|
|
402
583
|
|
|
403
584
|
# SQLite uses a preceding X for hex escaping strings
|
|
404
|
-
def
|
|
405
|
-
|
|
406
|
-
v.each_byte{|x| blob << sprintf('%02x', x)}
|
|
407
|
-
"X'#{blob}'"
|
|
585
|
+
def literal_blob_append(sql, v)
|
|
586
|
+
sql << BLOB_START << v.unpack(HSTAR).first << APOS
|
|
408
587
|
end
|
|
409
|
-
|
|
588
|
+
|
|
589
|
+
# Respect the database integer_booleans setting, using 0 or 'f'.
|
|
590
|
+
def literal_false
|
|
591
|
+
@db.integer_booleans ? '0' : "'f'"
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
# Respect the database integer_booleans setting, using 1 or 't'.
|
|
595
|
+
def literal_true
|
|
596
|
+
@db.integer_booleans ? '1' : "'t'"
|
|
597
|
+
end
|
|
598
|
+
|
|
410
599
|
# SQLite does not support the SQL WITH clause
|
|
411
600
|
def select_clause_methods
|
|
412
601
|
SELECT_CLAUSE_METHODS
|
|
@@ -416,7 +605,7 @@ module Sequel
|
|
|
416
605
|
def select_lock_sql(sql)
|
|
417
606
|
super unless @opts[:lock] == :update
|
|
418
607
|
end
|
|
419
|
-
|
|
608
|
+
|
|
420
609
|
# SQLite treats a DELETE with no WHERE clause as a TRUNCATE
|
|
421
610
|
def _truncate_sql(table)
|
|
422
611
|
"DELETE FROM #{table}"
|