sequel 3.21.0 → 3.41.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 +1131 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +106 -65
- data/Rakefile +59 -27
- data/bin/sequel +50 -26
- data/doc/active_record.rdoc +68 -52
- data/doc/advanced_associations.rdoc +201 -75
- data/doc/association_basics.rdoc +327 -79
- data/doc/bin_sequel.rdoc +144 -0
- 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 +576 -0
- data/doc/opening_databases.rdoc +121 -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/release_notes/3.40.0.txt +73 -0
- data/doc/release_notes/3.41.0.txt +155 -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/access.rb +334 -0
- data/lib/sequel/adapters/ado/mssql.rb +18 -12
- data/lib/sequel/adapters/ado.rb +69 -21
- data/lib/sequel/adapters/amalgalite.rb +17 -14
- data/lib/sequel/adapters/cubrid.rb +143 -0
- data/lib/sequel/adapters/db2.rb +178 -85
- data/lib/sequel/adapters/dbi.rb +19 -21
- 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 +20 -39
- data/lib/sequel/adapters/firebird.rb +33 -210
- data/lib/sequel/adapters/ibmdb.rb +452 -0
- data/lib/sequel/adapters/informix.rb +6 -29
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/cubrid.rb +52 -0
- data/lib/sequel/adapters/jdbc/db2.rb +78 -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 +68 -31
- data/lib/sequel/adapters/jdbc/hsqldb.rb +189 -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 +16 -28
- 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 +72 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +308 -114
- data/lib/sequel/adapters/mock.rb +372 -0
- data/lib/sequel/adapters/mysql.rb +130 -230
- data/lib/sequel/adapters/mysql2.rb +64 -63
- data/lib/sequel/adapters/odbc/mssql.rb +17 -9
- data/lib/sequel/adapters/odbc.rb +29 -10
- data/lib/sequel/adapters/openbase.rb +7 -11
- data/lib/sequel/adapters/oracle.rb +364 -57
- data/lib/sequel/adapters/postgres.rb +379 -145
- data/lib/sequel/adapters/shared/access.rb +236 -11
- data/lib/sequel/adapters/shared/cubrid.rb +216 -0
- data/lib/sequel/adapters/shared/db2.rb +353 -0
- data/lib/sequel/adapters/shared/firebird.rb +231 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +412 -116
- data/lib/sequel/adapters/shared/mysql.rb +396 -89
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +166 -0
- data/lib/sequel/adapters/shared/oracle.rb +262 -45
- data/lib/sequel/adapters/shared/postgres.rb +688 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +317 -96
- data/lib/sequel/adapters/sqlite.rb +109 -46
- 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 +31 -27
- data/lib/sequel/adapters/tinytds.rb +177 -25
- 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/split_alter_table.rb +36 -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 +23 -13
- data/lib/sequel/connection_pool/sharded_threaded.rb +64 -13
- data/lib/sequel/connection_pool/single.rb +11 -3
- data/lib/sequel/connection_pool/threaded.rb +69 -20
- data/lib/sequel/connection_pool.rb +20 -18
- data/lib/sequel/core.rb +143 -11
- data/lib/sequel/database/connecting.rb +66 -29
- 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 +192 -51
- data/lib/sequel/database/query.rb +299 -114
- data/lib/sequel/database/schema_generator.rb +103 -36
- data/lib/sequel/database/schema_methods.rb +310 -69
- 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 +322 -68
- data/lib/sequel/dataset/sql.rb +716 -293
- 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/connection_validator.rb +109 -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 +539 -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 +334 -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 +212 -0
- data/lib/sequel/extensions/pg_range.rb +513 -0
- data/lib/sequel/extensions/pg_range_ops.rb +150 -0
- data/lib/sequel/extensions/pg_row.rb +576 -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 +137 -16
- 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 +77 -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 +168 -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 +57 -12
- 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/touch.rb +18 -6
- 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 +2 -1
- data/lib/sequel/plugins/validation_helpers.rb +57 -4
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +721 -141
- 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 +53 -39
- data/spec/adapters/mssql_spec.rb +261 -66
- data/spec/adapters/mysql_spec.rb +597 -352
- data/spec/adapters/oracle_spec.rb +92 -117
- data/spec/adapters/postgres_spec.rb +2103 -341
- data/spec/adapters/spec_helper.rb +16 -6
- data/spec/adapters/sqlite_spec.rb +237 -76
- data/spec/core/connection_pool_spec.rb +270 -141
- data/spec/core/database_spec.rb +980 -524
- data/spec/core/dataset_spec.rb +1658 -1243
- data/spec/core/expression_filters_spec.rb +741 -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 +227 -33
- 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/connection_validator_spec.rb +118 -0
- 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 +324 -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 +200 -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 +340 -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 +65 -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 +53 -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 +77 -68
- data/spec/extensions/validation_helpers_spec.rb +48 -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 +25 -5
- data/spec/integration/dataset_test.rb +729 -141
- 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 +871 -160
- data/spec/integration/prepared_statement_test.rb +272 -128
- data/spec/integration/schema_test.rb +377 -57
- data/spec/integration/spec_helper.rb +49 -32
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +40 -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 +223 -81
- 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,19 @@ 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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
VIEWS_FILTER = "type = 'view'".freeze
|
|
13
|
+
TRANSACTION_MODE = {
|
|
14
|
+
:deferred => "BEGIN DEFERRED TRANSACTION".freeze,
|
|
15
|
+
:immediate => "BEGIN IMMEDIATE TRANSACTION".freeze,
|
|
16
|
+
:exclusive => "BEGIN EXCLUSIVE TRANSACTION".freeze,
|
|
17
|
+
nil => Sequel::Database::SQL_BEGIN,
|
|
18
|
+
}.freeze
|
|
19
|
+
|
|
20
|
+
# Whether to use integers for booleans in the database. SQLite recommends
|
|
21
|
+
# booleans be stored as integers, but historically Sequel has used 't'/'f'.
|
|
22
|
+
attr_accessor :integer_booleans
|
|
20
23
|
|
|
21
24
|
# A symbol signifying the value of the auto_vacuum PRAGMA.
|
|
22
25
|
def auto_vacuum
|
|
@@ -30,7 +33,27 @@ module Sequel
|
|
|
30
33
|
value = AUTO_VACUUM.index(value) || (raise Error, "Invalid value for auto_vacuum option. Please specify one of :none, :full, :incremental.")
|
|
31
34
|
pragma_set(:auto_vacuum, value)
|
|
32
35
|
end
|
|
33
|
-
|
|
36
|
+
|
|
37
|
+
# Set the case_sensitive_like PRAGMA using the given boolean value, if using
|
|
38
|
+
# SQLite 3.2.3+. If not using 3.2.3+, no error is raised. See pragma_set.
|
|
39
|
+
# Consider using the :case_sensitive_like Database option instead.
|
|
40
|
+
def case_sensitive_like=(value)
|
|
41
|
+
pragma_set(:case_sensitive_like, !!value ? 'on' : 'off') if sqlite_version >= 30203
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# A symbol signifying the value of the default transaction mode
|
|
45
|
+
def transaction_mode
|
|
46
|
+
defined?(@transaction_mode) ? @transaction_mode : (@transaction_mode = nil)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def transaction_mode=(value)
|
|
50
|
+
if TRANSACTION_MODE.include?(value)
|
|
51
|
+
@transaction_mode = value
|
|
52
|
+
else
|
|
53
|
+
raise Error, "Invalid value for transaction_mode. Please specify one of :deferred, :immediate, :exclusive, nil"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
34
57
|
# SQLite uses the :sqlite database type.
|
|
35
58
|
def database_type
|
|
36
59
|
:sqlite
|
|
@@ -49,22 +72,33 @@ module Sequel
|
|
|
49
72
|
pragma_set(:foreign_keys, !!value ? 'on' : 'off') if sqlite_version >= 30619
|
|
50
73
|
end
|
|
51
74
|
|
|
75
|
+
# Return the array of foreign key info hashes using the foreign_key_list PRAGMA,
|
|
76
|
+
# including information for the :on_update and :on_delete entries.
|
|
77
|
+
def foreign_key_list(table, opts={})
|
|
78
|
+
m = output_identifier_meth
|
|
79
|
+
h = {}
|
|
80
|
+
metadata_dataset.with_sql("PRAGMA foreign_key_list(?)", input_identifier_meth.call(table)).each do |row|
|
|
81
|
+
if r = h[row[:id]]
|
|
82
|
+
r[:columns] << m.call(row[:from])
|
|
83
|
+
r[:key] << m.call(row[:to]) if r[:key]
|
|
84
|
+
else
|
|
85
|
+
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])}
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
h.values
|
|
89
|
+
end
|
|
90
|
+
|
|
52
91
|
# Use the index_list and index_info PRAGMAs to determine the indexes on the table.
|
|
53
92
|
def indexes(table, opts={})
|
|
54
93
|
m = output_identifier_meth
|
|
55
94
|
im = input_identifier_meth
|
|
56
95
|
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
|
|
96
|
+
metadata_dataset.with_sql("PRAGMA index_list(?)", im.call(table)).each do |r|
|
|
97
|
+
next if r[:name] =~ PRIMARY_KEY_INDEX_RE
|
|
98
|
+
indexes[m.call(r[:name])] = {:unique=>r[:unique].to_i==1}
|
|
99
|
+
end
|
|
100
|
+
indexes.each do |k, v|
|
|
101
|
+
v[:columns] = metadata_dataset.with_sql("PRAGMA index_info(?)", im.call(k)).map(:name).map{|x| m.call(x)}
|
|
68
102
|
end
|
|
69
103
|
indexes
|
|
70
104
|
end
|
|
@@ -83,6 +117,11 @@ module Sequel
|
|
|
83
117
|
def pragma_set(name, value)
|
|
84
118
|
execute_ddl("PRAGMA #{name} = #{value}")
|
|
85
119
|
end
|
|
120
|
+
|
|
121
|
+
# Set the integer_booleans option using the passed in :integer_boolean option.
|
|
122
|
+
def set_integer_booleans
|
|
123
|
+
@integer_booleans = typecast_value_boolean(@opts[:integer_booleans])
|
|
124
|
+
end
|
|
86
125
|
|
|
87
126
|
# The version of the server as an integer, where 3.6.19 = 30619.
|
|
88
127
|
# If the server version can't be determined, 0 is used.
|
|
@@ -96,11 +135,29 @@ module Sequel
|
|
|
96
135
|
end
|
|
97
136
|
end
|
|
98
137
|
|
|
138
|
+
# SQLite supports CREATE TABLE IF NOT EXISTS syntax since 3.3.0.
|
|
139
|
+
def supports_create_table_if_not_exists?
|
|
140
|
+
sqlite_version >= 30300
|
|
141
|
+
end
|
|
142
|
+
|
|
99
143
|
# SQLite 3.6.8+ supports savepoints.
|
|
100
144
|
def supports_savepoints?
|
|
101
145
|
sqlite_version >= 30608
|
|
102
146
|
end
|
|
103
147
|
|
|
148
|
+
# Override the default setting for whether to use timezones in timestamps.
|
|
149
|
+
# For backwards compatibility, it is set to +true+ by default.
|
|
150
|
+
# Anyone wanting to use SQLite's datetime functions should set it to +false+
|
|
151
|
+
# using this method. It's possible that the default will change in a future version,
|
|
152
|
+
# so anyone relying on timezones in timestamps should set this to +true+.
|
|
153
|
+
attr_writer :use_timestamp_timezones
|
|
154
|
+
|
|
155
|
+
# SQLite supports timezones in timestamps, since it just stores them as strings,
|
|
156
|
+
# but it breaks the usage of SQLite's datetime functions.
|
|
157
|
+
def use_timestamp_timezones?
|
|
158
|
+
defined?(@use_timestamp_timezones) ? @use_timestamp_timezones : (@use_timestamp_timezones = true)
|
|
159
|
+
end
|
|
160
|
+
|
|
104
161
|
# A symbol signifying the value of the synchronous PRAGMA.
|
|
105
162
|
def synchronous
|
|
106
163
|
SYNCHRONOUS[pragma_get(:synchronous).to_i]
|
|
@@ -118,8 +175,7 @@ module Sequel
|
|
|
118
175
|
# Options:
|
|
119
176
|
# * :server - Set the server to use.
|
|
120
177
|
def tables(opts={})
|
|
121
|
-
|
|
122
|
-
metadata_dataset.from(:sqlite_master).server(opts[:server]).filter(TABLES_FILTER).map{|r| m.call(r[:name])}
|
|
178
|
+
tables_and_views(TABLES_FILTER, opts)
|
|
123
179
|
end
|
|
124
180
|
|
|
125
181
|
# A symbol signifying the value of the temp_store PRAGMA.
|
|
@@ -134,8 +190,35 @@ module Sequel
|
|
|
134
190
|
pragma_set(:temp_store, value)
|
|
135
191
|
end
|
|
136
192
|
|
|
193
|
+
# Array of symbols specifying the view names in the current database.
|
|
194
|
+
#
|
|
195
|
+
# Options:
|
|
196
|
+
# * :server - Set the server to use.
|
|
197
|
+
def views(opts={})
|
|
198
|
+
tables_and_views(VIEWS_FILTER, opts)
|
|
199
|
+
end
|
|
200
|
+
|
|
137
201
|
private
|
|
138
202
|
|
|
203
|
+
# Run all alter_table commands in a transaction. This is technically only
|
|
204
|
+
# needed for drop column.
|
|
205
|
+
def apply_alter_table(table, ops)
|
|
206
|
+
fks = foreign_keys
|
|
207
|
+
self.foreign_keys = false if fks
|
|
208
|
+
transaction do
|
|
209
|
+
if ops.length > 1 && ops.all?{|op| op[:op] == :add_constraint}
|
|
210
|
+
# If you are just doing constraints, apply all of them at the same time,
|
|
211
|
+
# as otherwise all but the last one get lost.
|
|
212
|
+
alter_table_sql_list(table, [{:op=>:add_constraints, :ops=>ops}]).flatten.each{|sql| execute_ddl(sql)}
|
|
213
|
+
else
|
|
214
|
+
# Run each operation separately, as later operations may depend on the
|
|
215
|
+
# results of earlier operations.
|
|
216
|
+
ops.each{|op| alter_table_sql_list(table, [op]).flatten.each{|sql| execute_ddl(sql)}}
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
self.foreign_keys = true if fks
|
|
220
|
+
end
|
|
221
|
+
|
|
139
222
|
# SQLite supports limited table modification. You can add a column
|
|
140
223
|
# or an index. Dropping columns is supported by copying the table into
|
|
141
224
|
# a temporary table, dropping the table, and creating a new table without
|
|
@@ -161,13 +244,33 @@ module Sequel
|
|
|
161
244
|
when :set_column_null
|
|
162
245
|
duplicate_table(table){|columns| columns.each{|s| s[:null] = op[:null] if s[:name].to_s == op[:name].to_s}}
|
|
163
246
|
when :set_column_type
|
|
164
|
-
duplicate_table(table){|columns| columns.each{|s| s
|
|
247
|
+
duplicate_table(table){|columns| columns.each{|s| s.merge!(op) if s[:name].to_s == op[:name].to_s}}
|
|
248
|
+
when :drop_constraint
|
|
249
|
+
case op[:type]
|
|
250
|
+
when :primary_key
|
|
251
|
+
duplicate_table(table){|columns| columns.each{|s| s[:primary_key] = nil}}
|
|
252
|
+
when :foreign_key
|
|
253
|
+
duplicate_table(table, :no_foreign_keys=>true)
|
|
254
|
+
else
|
|
255
|
+
duplicate_table(table)
|
|
256
|
+
end
|
|
257
|
+
when :add_constraint
|
|
258
|
+
duplicate_table(table, :constraints=>[op])
|
|
259
|
+
when :add_constraints
|
|
260
|
+
duplicate_table(table, :constraints=>op[:ops])
|
|
165
261
|
else
|
|
166
|
-
raise Error, "Unsupported ALTER TABLE operation"
|
|
262
|
+
raise Error, "Unsupported ALTER TABLE operation: #{op[:op].inspect}"
|
|
167
263
|
end
|
|
168
264
|
end
|
|
169
|
-
|
|
170
|
-
|
|
265
|
+
|
|
266
|
+
def begin_new_transaction(conn, opts)
|
|
267
|
+
mode = opts[:mode] || @transaction_mode
|
|
268
|
+
sql = TRANSACTION_MODE[mode] or raise Error, "transaction :mode must be one of: :deferred, :immediate, :exclusive, nil"
|
|
269
|
+
log_connection_execute(conn, sql)
|
|
270
|
+
set_transaction_isolation(conn, opts)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# A name to use for the backup table
|
|
171
274
|
def backup_table_name(table, opts={})
|
|
172
275
|
table = table.gsub('`', '')
|
|
173
276
|
(opts[:times]||1000).times do |i|
|
|
@@ -176,17 +279,25 @@ module Sequel
|
|
|
176
279
|
end
|
|
177
280
|
end
|
|
178
281
|
|
|
179
|
-
#
|
|
180
|
-
def
|
|
181
|
-
|
|
282
|
+
# Surround default with parens to appease SQLite
|
|
283
|
+
def column_definition_default_sql(sql, column)
|
|
284
|
+
sql << " DEFAULT (#{literal(column[:default])})" if column.include?(:default)
|
|
182
285
|
end
|
|
183
|
-
|
|
286
|
+
|
|
287
|
+
# Add null/not null SQL fragment to column creation SQL.
|
|
288
|
+
def column_definition_null_sql(sql, column)
|
|
289
|
+
column = column.merge(:null=>false) if column[:primary_key]
|
|
290
|
+
super(sql, column)
|
|
291
|
+
end
|
|
292
|
+
|
|
184
293
|
# Array of PRAGMA SQL statements based on the Database options that should be applied to
|
|
185
294
|
# new connections.
|
|
186
295
|
def connection_pragmas
|
|
187
296
|
ps = []
|
|
188
297
|
v = typecast_value_boolean(opts.fetch(:foreign_keys, 1))
|
|
189
298
|
ps << "PRAGMA foreign_keys = #{v ? 1 : 0}"
|
|
299
|
+
v = typecast_value_boolean(opts.fetch(:case_sensitive_like, 1))
|
|
300
|
+
ps << "PRAGMA case_sensitive_like = #{v ? 1 : 0}"
|
|
190
301
|
[[:auto_vacuum, AUTO_VACUUM], [:synchronous, SYNCHRONOUS], [:temp_store, TEMP_STORE]].each do |prag, con|
|
|
191
302
|
if v = opts[prag]
|
|
192
303
|
raise(Error, "Value for PRAGMA #{prag} not supported, should be one of #{con.join(', ')}") unless v = con.index(v.to_sym)
|
|
@@ -196,28 +307,13 @@ module Sequel
|
|
|
196
307
|
ps
|
|
197
308
|
end
|
|
198
309
|
|
|
199
|
-
# The array of column schema hashes
|
|
200
|
-
def defined_columns_for(table
|
|
310
|
+
# The array of column schema hashes for the current columns in the table
|
|
311
|
+
def defined_columns_for(table)
|
|
201
312
|
cols = parse_pragma(table, {})
|
|
202
313
|
cols.each do |c|
|
|
203
314
|
c[:default] = LiteralString.new(c[:default]) if c[:default]
|
|
204
315
|
c[:type] = c[:db_type]
|
|
205
316
|
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
317
|
cols
|
|
222
318
|
end
|
|
223
319
|
|
|
@@ -231,17 +327,40 @@ module Sequel
|
|
|
231
327
|
opts[:old_columns_proc].call(old_columns) if opts[:old_columns_proc]
|
|
232
328
|
|
|
233
329
|
yield def_columns if block_given?
|
|
234
|
-
|
|
330
|
+
|
|
331
|
+
constraints = (opts[:constraints] || []).dup
|
|
332
|
+
pks = []
|
|
333
|
+
def_columns.each{|c| pks << c[:name] if c[:primary_key]}
|
|
334
|
+
if pks.length > 1
|
|
335
|
+
constraints << {:type=>:primary_key, :columns=>pks}
|
|
336
|
+
def_columns.each{|c| c[:primary_key] = false if c[:primary_key]}
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
# If dropping a foreign key constraint, drop all foreign key constraints,
|
|
340
|
+
# as there is no way to determine which one to drop.
|
|
341
|
+
unless opts[:no_foreign_keys]
|
|
342
|
+
fks = foreign_key_list(table)
|
|
343
|
+
|
|
344
|
+
# If dropping a column, if there is a foreign key with that
|
|
345
|
+
# column, don't include it when building a copy of the table.
|
|
346
|
+
if ocp = opts[:old_columns_proc]
|
|
347
|
+
fks.delete_if{|c| ocp.call(c[:columns].dup) != c[:columns]}
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
constraints.concat(fks.each{|h| h[:type] = :foreign_key})
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def_columns_str = (def_columns.map{|c| column_definition_sql(c)} + constraints.map{|c| constraint_definition_sql(c)}).join(', ')
|
|
235
354
|
new_columns = old_columns.dup
|
|
236
355
|
opts[:new_columns_proc].call(new_columns) if opts[:new_columns_proc]
|
|
237
356
|
|
|
238
357
|
qt = quote_schema_table(table)
|
|
239
358
|
bt = quote_identifier(backup_table_name(qt))
|
|
240
359
|
a = [
|
|
241
|
-
"
|
|
242
|
-
"
|
|
243
|
-
"
|
|
244
|
-
"
|
|
360
|
+
"ALTER TABLE #{qt} RENAME TO #{bt}",
|
|
361
|
+
"CREATE TABLE #{qt}(#{def_columns_str})",
|
|
362
|
+
"INSERT INTO #{qt}(#{dataset.send(:identifier_list, new_columns)}) SELECT #{dataset.send(:identifier_list, old_columns)} FROM #{bt}",
|
|
363
|
+
"DROP TABLE #{bt}"
|
|
245
364
|
]
|
|
246
365
|
indexes(table).each do |name, h|
|
|
247
366
|
if (h[:columns].map{|x| x.to_s} - new_columns).empty?
|
|
@@ -280,7 +399,7 @@ module Sequel
|
|
|
280
399
|
|
|
281
400
|
# Parse the output of the table_info pragma
|
|
282
401
|
def parse_pragma(table_name, opts)
|
|
283
|
-
metadata_dataset.with_sql("PRAGMA table_info(?)", input_identifier_meth.call(table_name)).map do |row|
|
|
402
|
+
metadata_dataset.with_sql("PRAGMA table_info(?)", input_identifier_meth(opts[:dataset]).call(table_name)).map do |row|
|
|
284
403
|
row.delete(:cid)
|
|
285
404
|
row[:allow_null] = row.delete(:notnull).to_i == 0
|
|
286
405
|
row[:default] = row.delete(:dflt_value)
|
|
@@ -294,65 +413,123 @@ module Sequel
|
|
|
294
413
|
|
|
295
414
|
# SQLite treats integer primary keys as autoincrementing (alias of rowid).
|
|
296
415
|
def schema_autoincrementing_primary_key?(schema)
|
|
297
|
-
super
|
|
416
|
+
super && schema[:db_type].downcase == 'integer'
|
|
298
417
|
end
|
|
299
418
|
|
|
300
419
|
# SQLite supports schema parsing using the table_info PRAGMA, so
|
|
301
420
|
# parse the output of that into the format Sequel expects.
|
|
302
421
|
def schema_parse_table(table_name, opts)
|
|
303
|
-
m = output_identifier_meth
|
|
422
|
+
m = output_identifier_meth(opts[:dataset])
|
|
304
423
|
parse_pragma(table_name, opts).map do |row|
|
|
305
424
|
[m.call(row.delete(:name)), row]
|
|
306
425
|
end
|
|
307
426
|
end
|
|
308
427
|
|
|
309
|
-
#
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
428
|
+
# Backbone of the tables and views support.
|
|
429
|
+
def tables_and_views(filter, opts)
|
|
430
|
+
m = output_identifier_meth
|
|
431
|
+
metadata_dataset.from(:sqlite_master).server(opts[:server]).filter(filter).map{|r| m.call(r[:name])}
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
# SQLite only supports AUTOINCREMENT on integer columns, not
|
|
435
|
+
# bigint columns, so use integer instead of bigint for those
|
|
436
|
+
# columns.
|
|
313
437
|
def type_literal_generic_bignum(column)
|
|
314
|
-
:integer
|
|
438
|
+
column[:auto_increment] ? :integer : super
|
|
315
439
|
end
|
|
316
440
|
end
|
|
317
441
|
|
|
318
442
|
# Instance methods for datasets that connect to an SQLite database
|
|
319
443
|
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
|
|
444
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct columns from join where group having compounds order limit')
|
|
322
445
|
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
|
-
|
|
446
|
+
EMULATED_FUNCTION_MAP = {:char_length=>'length'.freeze}
|
|
447
|
+
EXTRACT_MAP = {:year=>"'%Y'", :month=>"'%m'", :day=>"'%d'", :hour=>"'%H'", :minute=>"'%M'", :second=>"'%f'"}
|
|
448
|
+
NOT_SPACE = Dataset::NOT_SPACE
|
|
449
|
+
COMMA = Dataset::COMMA
|
|
450
|
+
PAREN_CLOSE = Dataset::PAREN_CLOSE
|
|
451
|
+
AS = Dataset::AS
|
|
452
|
+
APOS = Dataset::APOS
|
|
453
|
+
EXTRACT_OPEN = "CAST(strftime(".freeze
|
|
454
|
+
EXTRACT_CLOSE = ') AS '.freeze
|
|
455
|
+
NUMERIC = 'NUMERIC'.freeze
|
|
456
|
+
INTEGER = 'INTEGER'.freeze
|
|
457
|
+
BACKTICK = '`'.freeze
|
|
458
|
+
BACKTICK_RE = /`/.freeze
|
|
459
|
+
DOUBLE_BACKTICK = '``'.freeze
|
|
460
|
+
BLOB_START = "X'".freeze
|
|
461
|
+
HSTAR = "H*".freeze
|
|
462
|
+
DATE_OPEN = "date(".freeze
|
|
463
|
+
DATETIME_OPEN = "datetime(".freeze
|
|
464
|
+
|
|
465
|
+
def cast_sql_append(sql, expr, type)
|
|
466
|
+
if type == Time or type == DateTime
|
|
467
|
+
sql << DATETIME_OPEN
|
|
468
|
+
literal_append(sql, expr)
|
|
469
|
+
sql << PAREN_CLOSE
|
|
470
|
+
elsif type == Date
|
|
471
|
+
sql << DATE_OPEN
|
|
472
|
+
literal_append(sql, expr)
|
|
473
|
+
sql << PAREN_CLOSE
|
|
474
|
+
else
|
|
475
|
+
super
|
|
476
|
+
end
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
# SQLite is case insensitive (depending on pragma), so use LIKE for ILIKE.
|
|
480
|
+
# It also doesn't support a NOT LIKE b, you need to use NOT (a LIKE b).
|
|
481
|
+
# It doesn't support xor or the extract function natively, so those have to be emulated.
|
|
482
|
+
def complex_expression_sql_append(sql, op, args)
|
|
328
483
|
case op
|
|
329
|
-
when
|
|
330
|
-
|
|
331
|
-
when :
|
|
332
|
-
|
|
333
|
-
|
|
484
|
+
when :ILIKE
|
|
485
|
+
super(sql, :LIKE, args.map{|a| SQL::Function.new(:upper, a)})
|
|
486
|
+
when :"NOT LIKE", :"NOT ILIKE"
|
|
487
|
+
sql << NOT_SPACE
|
|
488
|
+
complex_expression_sql_append(sql, (op == :"NOT ILIKE" ? :ILIKE : :LIKE), args)
|
|
489
|
+
when :^
|
|
490
|
+
sql << complex_expression_arg_pairs(args) do |a, b|
|
|
491
|
+
a = literal(a)
|
|
492
|
+
b = literal(b)
|
|
493
|
+
"((~(#{a} & #{b})) & (#{a} | #{b}))"
|
|
494
|
+
end
|
|
495
|
+
when :extract
|
|
496
|
+
part = args.at(0)
|
|
497
|
+
raise(Sequel::Error, "unsupported extract argument: #{part.inspect}") unless format = EXTRACT_MAP[part]
|
|
498
|
+
sql << EXTRACT_OPEN << format << COMMA
|
|
499
|
+
literal_append(sql, args.at(1))
|
|
500
|
+
sql << EXTRACT_CLOSE << (part == :second ? NUMERIC : INTEGER) << PAREN_CLOSE
|
|
334
501
|
else
|
|
335
|
-
super
|
|
502
|
+
super
|
|
336
503
|
end
|
|
337
504
|
end
|
|
338
505
|
|
|
339
|
-
#
|
|
340
|
-
|
|
341
|
-
|
|
506
|
+
# SQLite has CURRENT_TIMESTAMP and related constants in UTC instead
|
|
507
|
+
# of in localtime, so convert those constants to local time.
|
|
508
|
+
def constant_sql_append(sql, constant)
|
|
509
|
+
if c = CONSTANT_MAP[constant]
|
|
510
|
+
sql << c
|
|
511
|
+
else
|
|
512
|
+
super
|
|
513
|
+
end
|
|
342
514
|
end
|
|
343
515
|
|
|
344
516
|
# SQLite performs a TRUNCATE style DELETE if no filter is specified.
|
|
345
517
|
# Since we want to always return the count of records, add a condition
|
|
346
518
|
# that is always true and then delete.
|
|
347
519
|
def delete
|
|
348
|
-
@opts[:where] ? super :
|
|
520
|
+
@opts[:where] ? super : where(1=>1).delete
|
|
349
521
|
end
|
|
350
522
|
|
|
351
523
|
# Return an array of strings specifying a query explanation for a SELECT of the
|
|
352
|
-
# current dataset.
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
524
|
+
# current dataset. Currently, the options are ignore, but it accepts options
|
|
525
|
+
# to be compatible with other adapters.
|
|
526
|
+
def explain(opts=nil)
|
|
527
|
+
# Load the PrettyTable class, needed for explain output
|
|
528
|
+
Sequel.extension(:_pretty_table) unless defined?(Sequel::PrettyTable)
|
|
529
|
+
|
|
530
|
+
ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}")
|
|
531
|
+
rows = ds.all
|
|
532
|
+
Sequel::PrettyTable.string(rows, ds.columns)
|
|
356
533
|
end
|
|
357
534
|
|
|
358
535
|
# HAVING requires GROUP BY on SQLite
|
|
@@ -362,8 +539,20 @@ module Sequel
|
|
|
362
539
|
end
|
|
363
540
|
|
|
364
541
|
# SQLite uses the nonstandard ` (backtick) for quoting identifiers.
|
|
365
|
-
def
|
|
366
|
-
|
|
542
|
+
def quoted_identifier_append(sql, c)
|
|
543
|
+
sql << BACKTICK << c.to_s.gsub(BACKTICK_RE, DOUBLE_BACKTICK) << BACKTICK
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
# When a qualified column is selected on SQLite and the qualifier
|
|
547
|
+
# is a subselect, the column name used is the full qualified name
|
|
548
|
+
# (including the qualifier) instead of just the column name. To
|
|
549
|
+
# get correct column names, you must use an alias.
|
|
550
|
+
def select(*cols)
|
|
551
|
+
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)})
|
|
552
|
+
super(*cols.map{|c| alias_qualified_column(c)})
|
|
553
|
+
else
|
|
554
|
+
super
|
|
555
|
+
end
|
|
367
556
|
end
|
|
368
557
|
|
|
369
558
|
# SQLite does not support INTERSECT ALL or EXCEPT ALL
|
|
@@ -382,31 +571,63 @@ module Sequel
|
|
|
382
571
|
end
|
|
383
572
|
|
|
384
573
|
# SQLite supports timezones in literal timestamps, since it stores them
|
|
385
|
-
# as text.
|
|
574
|
+
# as text. But using timezones in timestamps breaks SQLite datetime
|
|
575
|
+
# functions, so we allow the user to override the default per database.
|
|
386
576
|
def supports_timestamp_timezones?
|
|
387
|
-
|
|
577
|
+
db.use_timestamp_timezones?
|
|
388
578
|
end
|
|
389
579
|
|
|
580
|
+
# SQLite cannot use WHERE 't'.
|
|
581
|
+
def supports_where_true?
|
|
582
|
+
false
|
|
583
|
+
end
|
|
584
|
+
|
|
390
585
|
private
|
|
391
586
|
|
|
392
587
|
# SQLite uses string literals instead of identifiers in AS clauses.
|
|
393
|
-
def
|
|
588
|
+
def as_sql_append(sql, aliaz)
|
|
394
589
|
aliaz = aliaz.value if aliaz.is_a?(SQL::Identifier)
|
|
395
|
-
|
|
590
|
+
sql << AS
|
|
591
|
+
literal_append(sql, aliaz.to_s)
|
|
396
592
|
end
|
|
397
|
-
|
|
593
|
+
|
|
594
|
+
# If col is a qualified column, alias it to the same as the column name
|
|
595
|
+
def alias_qualified_column(col)
|
|
596
|
+
case col
|
|
597
|
+
when Symbol
|
|
598
|
+
t, c, a = split_symbol(col)
|
|
599
|
+
if t && !a
|
|
600
|
+
alias_qualified_column(SQL::QualifiedIdentifier.new(t, c))
|
|
601
|
+
else
|
|
602
|
+
col
|
|
603
|
+
end
|
|
604
|
+
when SQL::QualifiedIdentifier
|
|
605
|
+
SQL::AliasedExpression.new(col, col.column)
|
|
606
|
+
else
|
|
607
|
+
col
|
|
608
|
+
end
|
|
609
|
+
end
|
|
610
|
+
|
|
398
611
|
# SQL fragment specifying a list of identifiers
|
|
399
612
|
def identifier_list(columns)
|
|
400
|
-
columns.map{|i| quote_identifier(i)}.join(
|
|
613
|
+
columns.map{|i| quote_identifier(i)}.join(COMMA)
|
|
401
614
|
end
|
|
402
615
|
|
|
403
616
|
# SQLite uses a preceding X for hex escaping strings
|
|
404
|
-
def
|
|
405
|
-
|
|
406
|
-
v.each_byte{|x| blob << sprintf('%02x', x)}
|
|
407
|
-
"X'#{blob}'"
|
|
617
|
+
def literal_blob_append(sql, v)
|
|
618
|
+
sql << BLOB_START << v.unpack(HSTAR).first << APOS
|
|
408
619
|
end
|
|
409
|
-
|
|
620
|
+
|
|
621
|
+
# Respect the database integer_booleans setting, using 0 or 'f'.
|
|
622
|
+
def literal_false
|
|
623
|
+
@db.integer_booleans ? '0' : "'f'"
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
# Respect the database integer_booleans setting, using 1 or 't'.
|
|
627
|
+
def literal_true
|
|
628
|
+
@db.integer_booleans ? '1' : "'t'"
|
|
629
|
+
end
|
|
630
|
+
|
|
410
631
|
# SQLite does not support the SQL WITH clause
|
|
411
632
|
def select_clause_methods
|
|
412
633
|
SELECT_CLAUSE_METHODS
|
|
@@ -416,7 +637,7 @@ module Sequel
|
|
|
416
637
|
def select_lock_sql(sql)
|
|
417
638
|
super unless @opts[:lock] == :update
|
|
418
639
|
end
|
|
419
|
-
|
|
640
|
+
|
|
420
641
|
# SQLite treats a DELETE with no WHERE clause as a TRUNCATE
|
|
421
642
|
def _truncate_sql(table)
|
|
422
643
|
"DELETE FROM #{table}"
|