sequel 4.26.0 → 5.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG +405 -5656
- data/MIT-LICENSE +1 -1
- data/README.rdoc +232 -157
- data/bin/sequel +32 -9
- data/doc/advanced_associations.rdoc +252 -188
- data/doc/association_basics.rdoc +231 -273
- data/doc/bin_sequel.rdoc +5 -3
- data/doc/cheat_sheet.rdoc +75 -48
- data/doc/code_order.rdoc +28 -10
- data/doc/core_extensions.rdoc +104 -63
- data/doc/dataset_basics.rdoc +12 -21
- data/doc/dataset_filtering.rdoc +99 -86
- data/doc/extensions.rdoc +3 -10
- data/doc/mass_assignment.rdoc +74 -31
- data/doc/migration.rdoc +72 -46
- data/doc/model_dataset_method_design.rdoc +129 -0
- data/doc/model_hooks.rdoc +15 -25
- data/doc/model_plugins.rdoc +12 -12
- data/doc/mssql_stored_procedures.rdoc +3 -3
- data/doc/object_model.rdoc +59 -69
- data/doc/opening_databases.rdoc +84 -94
- data/doc/postgresql.rdoc +268 -38
- data/doc/prepared_statements.rdoc +29 -24
- data/doc/querying.rdoc +184 -164
- data/doc/reflection.rdoc +5 -6
- data/doc/release_notes/5.0.0.txt +159 -0
- data/doc/release_notes/5.1.0.txt +31 -0
- data/doc/release_notes/5.10.0.txt +84 -0
- data/doc/release_notes/5.11.0.txt +83 -0
- data/doc/release_notes/5.12.0.txt +141 -0
- data/doc/release_notes/5.13.0.txt +27 -0
- data/doc/release_notes/5.14.0.txt +63 -0
- data/doc/release_notes/5.15.0.txt +39 -0
- data/doc/release_notes/5.16.0.txt +110 -0
- data/doc/release_notes/5.17.0.txt +31 -0
- data/doc/release_notes/5.18.0.txt +69 -0
- data/doc/release_notes/5.19.0.txt +28 -0
- data/doc/release_notes/5.2.0.txt +33 -0
- data/doc/release_notes/5.20.0.txt +89 -0
- data/doc/release_notes/5.21.0.txt +87 -0
- data/doc/release_notes/5.22.0.txt +48 -0
- data/doc/release_notes/5.23.0.txt +56 -0
- data/doc/release_notes/5.24.0.txt +56 -0
- data/doc/release_notes/5.25.0.txt +32 -0
- data/doc/release_notes/5.26.0.txt +35 -0
- data/doc/release_notes/5.27.0.txt +21 -0
- data/doc/release_notes/5.28.0.txt +16 -0
- data/doc/release_notes/5.29.0.txt +22 -0
- data/doc/release_notes/5.3.0.txt +121 -0
- data/doc/release_notes/5.30.0.txt +20 -0
- data/doc/release_notes/5.31.0.txt +148 -0
- data/doc/release_notes/5.32.0.txt +46 -0
- data/doc/release_notes/5.33.0.txt +24 -0
- data/doc/release_notes/5.34.0.txt +40 -0
- data/doc/release_notes/5.35.0.txt +56 -0
- data/doc/release_notes/5.36.0.txt +60 -0
- data/doc/release_notes/5.37.0.txt +30 -0
- data/doc/release_notes/5.4.0.txt +80 -0
- data/doc/release_notes/5.5.0.txt +61 -0
- data/doc/release_notes/5.6.0.txt +31 -0
- data/doc/release_notes/5.7.0.txt +108 -0
- data/doc/release_notes/5.8.0.txt +170 -0
- data/doc/release_notes/5.9.0.txt +99 -0
- data/doc/schema_modification.rdoc +102 -77
- data/doc/security.rdoc +160 -87
- data/doc/sharding.rdoc +74 -47
- data/doc/sql.rdoc +135 -122
- data/doc/testing.rdoc +34 -18
- data/doc/thread_safety.rdoc +2 -4
- data/doc/transactions.rdoc +101 -19
- data/doc/validations.rdoc +64 -51
- data/doc/virtual_rows.rdoc +90 -109
- data/lib/sequel.rb +3 -1
- data/lib/sequel/adapters/ado.rb +154 -22
- data/lib/sequel/adapters/ado/access.rb +21 -21
- data/lib/sequel/adapters/ado/mssql.rb +8 -15
- data/lib/sequel/adapters/amalgalite.rb +17 -25
- data/lib/sequel/adapters/ibmdb.rb +52 -58
- data/lib/sequel/adapters/jdbc.rb +149 -127
- data/lib/sequel/adapters/jdbc/db2.rb +32 -40
- data/lib/sequel/adapters/jdbc/derby.rb +56 -58
- data/lib/sequel/adapters/jdbc/h2.rb +40 -30
- data/lib/sequel/adapters/jdbc/hsqldb.rb +22 -33
- data/lib/sequel/adapters/jdbc/jtds.rb +4 -10
- data/lib/sequel/adapters/jdbc/mssql.rb +6 -12
- data/lib/sequel/adapters/jdbc/mysql.rb +17 -18
- data/lib/sequel/adapters/jdbc/oracle.rb +25 -19
- data/lib/sequel/adapters/jdbc/postgresql.rb +90 -69
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +14 -24
- data/lib/sequel/adapters/jdbc/sqlite.rb +50 -12
- data/lib/sequel/adapters/jdbc/sqlserver.rb +36 -9
- data/lib/sequel/adapters/jdbc/transactions.rb +25 -39
- data/lib/sequel/adapters/mock.rb +104 -113
- data/lib/sequel/adapters/mysql.rb +42 -61
- data/lib/sequel/adapters/mysql2.rb +126 -35
- data/lib/sequel/adapters/odbc.rb +21 -28
- data/lib/sequel/adapters/odbc/db2.rb +3 -1
- data/lib/sequel/adapters/odbc/mssql.rb +11 -15
- data/lib/sequel/adapters/odbc/oracle.rb +11 -0
- data/lib/sequel/adapters/oracle.rb +62 -68
- data/lib/sequel/adapters/postgres.rb +257 -311
- data/lib/sequel/adapters/postgresql.rb +3 -1
- data/lib/sequel/adapters/shared/access.rb +75 -79
- data/lib/sequel/adapters/shared/db2.rb +96 -74
- data/lib/sequel/adapters/shared/mssql.rb +258 -213
- data/lib/sequel/adapters/shared/mysql.rb +284 -216
- data/lib/sequel/adapters/shared/oracle.rb +175 -60
- data/lib/sequel/adapters/shared/postgres.rb +829 -383
- data/lib/sequel/adapters/shared/sqlanywhere.rb +105 -127
- data/lib/sequel/adapters/shared/sqlite.rb +382 -159
- data/lib/sequel/adapters/sqlanywhere.rb +53 -38
- data/lib/sequel/adapters/sqlite.rb +111 -105
- data/lib/sequel/adapters/tinytds.rb +38 -46
- data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +8 -9
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +7 -5
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +87 -0
- data/lib/sequel/adapters/utils/mysql_prepared_statements.rb +56 -0
- data/lib/sequel/adapters/utils/replace.rb +3 -4
- data/lib/sequel/adapters/utils/split_alter_table.rb +2 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +9 -22
- data/lib/sequel/adapters/utils/unmodified_identifiers.rb +28 -0
- data/lib/sequel/ast_transformer.rb +13 -89
- data/lib/sequel/connection_pool.rb +54 -26
- data/lib/sequel/connection_pool/sharded_single.rb +19 -12
- data/lib/sequel/connection_pool/sharded_threaded.rb +160 -111
- data/lib/sequel/connection_pool/single.rb +21 -12
- data/lib/sequel/connection_pool/threaded.rb +137 -119
- data/lib/sequel/core.rb +352 -320
- data/lib/sequel/database.rb +19 -2
- data/lib/sequel/database/connecting.rb +70 -55
- data/lib/sequel/database/dataset.rb +15 -5
- data/lib/sequel/database/dataset_defaults.rb +20 -102
- data/lib/sequel/database/features.rb +20 -4
- data/lib/sequel/database/logging.rb +25 -7
- data/lib/sequel/database/misc.rb +132 -118
- data/lib/sequel/database/query.rb +51 -28
- data/lib/sequel/database/schema_generator.rb +188 -75
- data/lib/sequel/database/schema_methods.rb +161 -92
- data/lib/sequel/database/transactions.rb +260 -58
- data/lib/sequel/dataset.rb +28 -12
- data/lib/sequel/dataset/actions.rb +354 -170
- data/lib/sequel/dataset/dataset_module.rb +46 -0
- data/lib/sequel/dataset/features.rb +81 -34
- data/lib/sequel/dataset/graph.rb +82 -58
- data/lib/sequel/dataset/misc.rb +139 -47
- data/lib/sequel/dataset/placeholder_literalizer.rb +66 -26
- data/lib/sequel/dataset/prepared_statements.rb +188 -85
- data/lib/sequel/dataset/query.rb +428 -214
- data/lib/sequel/dataset/sql.rb +446 -339
- data/lib/sequel/deprecated.rb +14 -2
- data/lib/sequel/exceptions.rb +48 -16
- data/lib/sequel/extensions/_model_constraint_validations.rb +16 -0
- data/lib/sequel/extensions/_model_pg_row.rb +43 -0
- data/lib/sequel/extensions/_pretty_table.rb +10 -9
- data/lib/sequel/extensions/any_not_empty.rb +45 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +15 -11
- data/lib/sequel/extensions/auto_literal_strings.rb +74 -0
- data/lib/sequel/extensions/blank.rb +2 -0
- data/lib/sequel/extensions/caller_logging.rb +79 -0
- data/lib/sequel/extensions/columns_introspection.rb +9 -4
- data/lib/sequel/extensions/connection_expiration.rb +99 -0
- data/lib/sequel/extensions/connection_validator.rb +26 -13
- data/lib/sequel/extensions/constant_sql_override.rb +65 -0
- data/lib/sequel/extensions/constraint_validations.rb +93 -38
- data/lib/sequel/extensions/core_extensions.rb +45 -53
- data/lib/sequel/extensions/core_refinements.rb +44 -46
- data/lib/sequel/extensions/current_datetime_timestamp.rb +5 -4
- data/lib/sequel/extensions/dataset_source_alias.rb +4 -0
- data/lib/sequel/extensions/date_arithmetic.rb +42 -16
- data/lib/sequel/extensions/datetime_parse_to_time.rb +37 -0
- data/lib/sequel/extensions/duplicate_columns_handler.rb +94 -0
- data/lib/sequel/extensions/empty_array_consider_nulls.rb +7 -3
- data/lib/sequel/extensions/error_sql.rb +7 -3
- data/lib/sequel/extensions/escaped_like.rb +100 -0
- data/lib/sequel/extensions/eval_inspect.rb +14 -15
- data/lib/sequel/extensions/exclude_or_null.rb +68 -0
- data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
- data/lib/sequel/extensions/freeze_datasets.rb +3 -0
- data/lib/sequel/extensions/from_block.rb +2 -31
- data/lib/sequel/extensions/graph_each.rb +19 -6
- data/lib/sequel/extensions/identifier_mangling.rb +180 -0
- data/lib/sequel/extensions/implicit_subquery.rb +48 -0
- data/lib/sequel/extensions/index_caching.rb +109 -0
- data/lib/sequel/extensions/inflector.rb +8 -4
- data/lib/sequel/extensions/integer64.rb +32 -0
- data/lib/sequel/extensions/looser_typecasting.rb +19 -9
- data/lib/sequel/extensions/migration.rb +132 -80
- data/lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb +4 -0
- data/lib/sequel/extensions/named_timezones.rb +88 -23
- data/lib/sequel/extensions/no_auto_literal_strings.rb +4 -0
- data/lib/sequel/extensions/null_dataset.rb +12 -8
- data/lib/sequel/extensions/pagination.rb +35 -28
- data/lib/sequel/extensions/pg_array.rb +227 -316
- data/lib/sequel/extensions/pg_array_ops.rb +19 -7
- data/lib/sequel/extensions/pg_enum.rb +69 -24
- data/lib/sequel/extensions/pg_extended_date_support.rb +250 -0
- data/lib/sequel/extensions/pg_hstore.rb +50 -59
- data/lib/sequel/extensions/pg_hstore_ops.rb +9 -3
- data/lib/sequel/extensions/pg_inet.rb +34 -15
- data/lib/sequel/extensions/pg_inet_ops.rb +5 -1
- data/lib/sequel/extensions/pg_interval.rb +26 -26
- data/lib/sequel/extensions/pg_json.rb +422 -141
- data/lib/sequel/extensions/pg_json_ops.rb +248 -9
- data/lib/sequel/extensions/pg_loose_count.rb +5 -1
- data/lib/sequel/extensions/pg_range.rb +162 -146
- data/lib/sequel/extensions/pg_range_ops.rb +10 -5
- data/lib/sequel/extensions/pg_row.rb +53 -87
- data/lib/sequel/extensions/pg_row_ops.rb +36 -13
- data/lib/sequel/extensions/pg_static_cache_updater.rb +6 -2
- data/lib/sequel/extensions/pg_timestamptz.rb +28 -0
- data/lib/sequel/extensions/pretty_table.rb +4 -0
- data/lib/sequel/extensions/query.rb +12 -7
- data/lib/sequel/extensions/round_timestamps.rb +6 -9
- data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
- data/lib/sequel/extensions/s.rb +59 -0
- data/lib/sequel/extensions/schema_caching.rb +14 -1
- data/lib/sequel/extensions/schema_dumper.rb +83 -55
- data/lib/sequel/extensions/select_remove.rb +8 -4
- data/lib/sequel/extensions/sequel_4_dataset_methods.rb +85 -0
- data/lib/sequel/extensions/server_block.rb +50 -17
- data/lib/sequel/extensions/server_logging.rb +61 -0
- data/lib/sequel/extensions/split_array_nil.rb +8 -4
- data/lib/sequel/extensions/sql_comments.rb +96 -0
- data/lib/sequel/extensions/sql_expr.rb +4 -1
- data/lib/sequel/extensions/string_agg.rb +181 -0
- data/lib/sequel/extensions/string_date_time.rb +2 -0
- data/lib/sequel/extensions/symbol_aref.rb +53 -0
- data/lib/sequel/extensions/symbol_aref_refinement.rb +43 -0
- data/lib/sequel/extensions/symbol_as.rb +23 -0
- data/lib/sequel/extensions/symbol_as_refinement.rb +37 -0
- data/lib/sequel/extensions/synchronize_sql.rb +45 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +4 -0
- data/lib/sequel/extensions/to_dot.rb +15 -5
- data/lib/sequel/extensions/virtual_row_method_block.rb +44 -0
- data/lib/sequel/model.rb +36 -126
- data/lib/sequel/model/associations.rb +850 -257
- data/lib/sequel/model/base.rb +652 -764
- data/lib/sequel/model/dataset_module.rb +13 -10
- data/lib/sequel/model/default_inflections.rb +3 -1
- data/lib/sequel/model/errors.rb +3 -3
- data/lib/sequel/model/exceptions.rb +12 -12
- data/lib/sequel/model/inflections.rb +8 -19
- data/lib/sequel/model/plugins.rb +111 -0
- data/lib/sequel/plugins/accessed_columns.rb +2 -0
- data/lib/sequel/plugins/active_model.rb +32 -7
- data/lib/sequel/plugins/after_initialize.rb +3 -1
- data/lib/sequel/plugins/association_dependencies.rb +27 -18
- data/lib/sequel/plugins/association_lazy_eager_option.rb +66 -0
- data/lib/sequel/plugins/association_multi_add_remove.rb +85 -0
- data/lib/sequel/plugins/association_pks.rb +181 -83
- data/lib/sequel/plugins/association_proxies.rb +33 -9
- data/lib/sequel/plugins/auto_validations.rb +58 -23
- data/lib/sequel/plugins/before_after_save.rb +8 -0
- data/lib/sequel/plugins/blacklist_security.rb +23 -12
- data/lib/sequel/plugins/boolean_readers.rb +9 -6
- data/lib/sequel/plugins/boolean_subsets.rb +64 -0
- data/lib/sequel/plugins/caching.rb +27 -16
- data/lib/sequel/plugins/class_table_inheritance.rb +192 -94
- data/lib/sequel/plugins/column_conflicts.rb +18 -3
- data/lib/sequel/plugins/column_select.rb +9 -5
- data/lib/sequel/plugins/columns_updated.rb +42 -0
- data/lib/sequel/plugins/composition.rb +36 -24
- data/lib/sequel/plugins/constraint_validations.rb +37 -16
- data/lib/sequel/plugins/csv_serializer.rb +58 -35
- data/lib/sequel/plugins/dataset_associations.rb +60 -18
- data/lib/sequel/plugins/def_dataset_method.rb +90 -0
- data/lib/sequel/plugins/defaults_setter.rb +74 -13
- data/lib/sequel/plugins/delay_add_association.rb +4 -1
- data/lib/sequel/plugins/dirty.rb +65 -24
- data/lib/sequel/plugins/eager_each.rb +27 -3
- data/lib/sequel/plugins/eager_graph_eager.rb +139 -0
- data/lib/sequel/plugins/empty_failure_backtraces.rb +38 -0
- data/lib/sequel/plugins/error_splitter.rb +19 -12
- data/lib/sequel/plugins/finder.rb +246 -0
- data/lib/sequel/plugins/forbid_lazy_load.rb +216 -0
- data/lib/sequel/plugins/force_encoding.rb +9 -12
- data/lib/sequel/plugins/hook_class_methods.rb +39 -54
- data/lib/sequel/plugins/input_transformer.rb +20 -10
- data/lib/sequel/plugins/insert_conflict.rb +72 -0
- data/lib/sequel/plugins/insert_returning_select.rb +4 -2
- data/lib/sequel/plugins/instance_filters.rb +12 -8
- data/lib/sequel/plugins/instance_hooks.rb +36 -17
- data/lib/sequel/plugins/instance_specific_default.rb +113 -0
- data/lib/sequel/plugins/inverted_subsets.rb +24 -13
- data/lib/sequel/plugins/json_serializer.rb +123 -47
- data/lib/sequel/plugins/lazy_attributes.rb +20 -14
- data/lib/sequel/plugins/list.rb +40 -26
- data/lib/sequel/plugins/many_through_many.rb +28 -12
- data/lib/sequel/plugins/modification_detection.rb +17 -5
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -5
- data/lib/sequel/plugins/nested_attributes.rb +55 -28
- data/lib/sequel/plugins/optimistic_locking.rb +5 -3
- data/lib/sequel/plugins/pg_array_associations.rb +52 -18
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +348 -0
- data/lib/sequel/plugins/pg_row.rb +7 -51
- data/lib/sequel/plugins/prepared_statements.rb +53 -72
- data/lib/sequel/plugins/prepared_statements_safe.rb +13 -5
- data/lib/sequel/plugins/rcte_tree.rb +43 -63
- data/lib/sequel/plugins/serialization.rb +37 -44
- data/lib/sequel/plugins/serialization_modification_detection.rb +3 -1
- data/lib/sequel/plugins/sharding.rb +17 -10
- data/lib/sequel/plugins/single_table_inheritance.rb +62 -28
- data/lib/sequel/plugins/singular_table_names.rb +2 -0
- data/lib/sequel/plugins/skip_create_refresh.rb +5 -3
- data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
- data/lib/sequel/plugins/split_values.rb +13 -6
- data/lib/sequel/plugins/static_cache.rb +79 -53
- data/lib/sequel/plugins/static_cache_cache.rb +53 -0
- data/lib/sequel/plugins/string_stripper.rb +5 -3
- data/lib/sequel/plugins/subclasses.rb +20 -2
- data/lib/sequel/plugins/subset_conditions.rb +48 -0
- data/lib/sequel/plugins/table_select.rb +4 -2
- data/lib/sequel/plugins/tactical_eager_loading.rb +120 -6
- data/lib/sequel/plugins/throw_failures.rb +110 -0
- data/lib/sequel/plugins/timestamps.rb +22 -8
- data/lib/sequel/plugins/touch.rb +21 -8
- data/lib/sequel/plugins/tree.rb +57 -30
- data/lib/sequel/plugins/typecast_on_load.rb +14 -4
- data/lib/sequel/plugins/unlimited_update.rb +3 -7
- data/lib/sequel/plugins/update_or_create.rb +6 -4
- data/lib/sequel/plugins/update_primary_key.rb +3 -1
- data/lib/sequel/plugins/update_refresh.rb +28 -15
- data/lib/sequel/plugins/uuid.rb +70 -0
- data/lib/sequel/plugins/validate_associated.rb +20 -0
- data/lib/sequel/plugins/validation_class_methods.rb +40 -19
- data/lib/sequel/plugins/validation_contexts.rb +49 -0
- data/lib/sequel/plugins/validation_helpers.rb +49 -31
- data/lib/sequel/plugins/whitelist_security.rb +122 -0
- data/lib/sequel/plugins/xml_serializer.rb +31 -30
- data/lib/sequel/sql.rb +479 -329
- data/lib/sequel/timezones.rb +62 -32
- data/lib/sequel/version.rb +10 -3
- metadata +177 -477
- data/Rakefile +0 -165
- data/doc/active_record.rdoc +0 -912
- data/doc/release_notes/1.0.txt +0 -38
- data/doc/release_notes/1.1.txt +0 -143
- data/doc/release_notes/1.3.txt +0 -101
- data/doc/release_notes/1.4.0.txt +0 -53
- data/doc/release_notes/1.5.0.txt +0 -155
- data/doc/release_notes/2.0.0.txt +0 -298
- data/doc/release_notes/2.1.0.txt +0 -271
- data/doc/release_notes/2.10.0.txt +0 -328
- data/doc/release_notes/2.11.0.txt +0 -215
- data/doc/release_notes/2.12.0.txt +0 -534
- data/doc/release_notes/2.2.0.txt +0 -253
- data/doc/release_notes/2.3.0.txt +0 -88
- data/doc/release_notes/2.4.0.txt +0 -106
- data/doc/release_notes/2.5.0.txt +0 -137
- data/doc/release_notes/2.6.0.txt +0 -157
- data/doc/release_notes/2.7.0.txt +0 -166
- data/doc/release_notes/2.8.0.txt +0 -171
- data/doc/release_notes/2.9.0.txt +0 -97
- data/doc/release_notes/3.0.0.txt +0 -221
- data/doc/release_notes/3.1.0.txt +0 -406
- data/doc/release_notes/3.10.0.txt +0 -286
- data/doc/release_notes/3.11.0.txt +0 -254
- data/doc/release_notes/3.12.0.txt +0 -304
- data/doc/release_notes/3.13.0.txt +0 -210
- data/doc/release_notes/3.14.0.txt +0 -118
- data/doc/release_notes/3.15.0.txt +0 -78
- data/doc/release_notes/3.16.0.txt +0 -45
- data/doc/release_notes/3.17.0.txt +0 -58
- data/doc/release_notes/3.18.0.txt +0 -120
- data/doc/release_notes/3.19.0.txt +0 -67
- data/doc/release_notes/3.2.0.txt +0 -268
- data/doc/release_notes/3.20.0.txt +0 -41
- data/doc/release_notes/3.21.0.txt +0 -87
- data/doc/release_notes/3.22.0.txt +0 -39
- data/doc/release_notes/3.23.0.txt +0 -172
- data/doc/release_notes/3.24.0.txt +0 -420
- data/doc/release_notes/3.25.0.txt +0 -88
- data/doc/release_notes/3.26.0.txt +0 -88
- data/doc/release_notes/3.27.0.txt +0 -82
- data/doc/release_notes/3.28.0.txt +0 -304
- data/doc/release_notes/3.29.0.txt +0 -459
- data/doc/release_notes/3.3.0.txt +0 -192
- data/doc/release_notes/3.30.0.txt +0 -135
- data/doc/release_notes/3.31.0.txt +0 -146
- data/doc/release_notes/3.32.0.txt +0 -202
- data/doc/release_notes/3.33.0.txt +0 -157
- data/doc/release_notes/3.34.0.txt +0 -671
- data/doc/release_notes/3.35.0.txt +0 -144
- data/doc/release_notes/3.36.0.txt +0 -245
- data/doc/release_notes/3.37.0.txt +0 -338
- data/doc/release_notes/3.38.0.txt +0 -234
- data/doc/release_notes/3.39.0.txt +0 -237
- data/doc/release_notes/3.4.0.txt +0 -325
- data/doc/release_notes/3.40.0.txt +0 -73
- data/doc/release_notes/3.41.0.txt +0 -155
- data/doc/release_notes/3.42.0.txt +0 -74
- data/doc/release_notes/3.43.0.txt +0 -105
- data/doc/release_notes/3.44.0.txt +0 -152
- data/doc/release_notes/3.45.0.txt +0 -179
- data/doc/release_notes/3.46.0.txt +0 -122
- data/doc/release_notes/3.47.0.txt +0 -270
- data/doc/release_notes/3.48.0.txt +0 -477
- data/doc/release_notes/3.5.0.txt +0 -510
- data/doc/release_notes/3.6.0.txt +0 -366
- data/doc/release_notes/3.7.0.txt +0 -179
- data/doc/release_notes/3.8.0.txt +0 -151
- data/doc/release_notes/3.9.0.txt +0 -233
- data/doc/release_notes/4.0.0.txt +0 -262
- data/doc/release_notes/4.1.0.txt +0 -85
- data/doc/release_notes/4.10.0.txt +0 -226
- data/doc/release_notes/4.11.0.txt +0 -147
- data/doc/release_notes/4.12.0.txt +0 -105
- data/doc/release_notes/4.13.0.txt +0 -169
- data/doc/release_notes/4.14.0.txt +0 -68
- data/doc/release_notes/4.15.0.txt +0 -56
- data/doc/release_notes/4.16.0.txt +0 -36
- data/doc/release_notes/4.17.0.txt +0 -38
- data/doc/release_notes/4.18.0.txt +0 -36
- data/doc/release_notes/4.19.0.txt +0 -45
- data/doc/release_notes/4.2.0.txt +0 -129
- data/doc/release_notes/4.20.0.txt +0 -79
- data/doc/release_notes/4.21.0.txt +0 -94
- data/doc/release_notes/4.22.0.txt +0 -72
- data/doc/release_notes/4.23.0.txt +0 -65
- data/doc/release_notes/4.24.0.txt +0 -99
- data/doc/release_notes/4.25.0.txt +0 -181
- data/doc/release_notes/4.26.0.txt +0 -44
- data/doc/release_notes/4.3.0.txt +0 -40
- data/doc/release_notes/4.4.0.txt +0 -92
- data/doc/release_notes/4.5.0.txt +0 -34
- data/doc/release_notes/4.6.0.txt +0 -30
- data/doc/release_notes/4.7.0.txt +0 -103
- data/doc/release_notes/4.8.0.txt +0 -175
- data/doc/release_notes/4.9.0.txt +0 -190
- data/lib/sequel/adapters/cubrid.rb +0 -142
- data/lib/sequel/adapters/do.rb +0 -156
- data/lib/sequel/adapters/do/mysql.rb +0 -64
- data/lib/sequel/adapters/do/postgres.rb +0 -42
- data/lib/sequel/adapters/do/sqlite3.rb +0 -40
- data/lib/sequel/adapters/jdbc/as400.rb +0 -82
- data/lib/sequel/adapters/jdbc/cubrid.rb +0 -62
- data/lib/sequel/adapters/jdbc/firebirdsql.rb +0 -34
- data/lib/sequel/adapters/jdbc/informix-sqli.rb +0 -31
- data/lib/sequel/adapters/jdbc/jdbcprogress.rb +0 -31
- data/lib/sequel/adapters/odbc/progress.rb +0 -8
- data/lib/sequel/adapters/shared/cubrid.rb +0 -243
- data/lib/sequel/adapters/shared/firebird.rb +0 -245
- data/lib/sequel/adapters/shared/informix.rb +0 -52
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +0 -150
- data/lib/sequel/adapters/shared/progress.rb +0 -38
- data/lib/sequel/adapters/swift.rb +0 -158
- data/lib/sequel/adapters/swift/mysql.rb +0 -47
- data/lib/sequel/adapters/swift/postgres.rb +0 -45
- data/lib/sequel/adapters/swift/sqlite.rb +0 -47
- data/lib/sequel/adapters/utils/pg_types.rb +0 -68
- data/lib/sequel/dataset/mutation.rb +0 -109
- data/lib/sequel/extensions/empty_array_ignore_nulls.rb +0 -3
- data/lib/sequel/extensions/filter_having.rb +0 -59
- data/lib/sequel/extensions/hash_aliases.rb +0 -45
- data/lib/sequel/extensions/meta_def.rb +0 -31
- data/lib/sequel/extensions/query_literals.rb +0 -80
- data/lib/sequel/extensions/ruby18_symbol_extensions.rb +0 -22
- data/lib/sequel/extensions/sequel_3_dataset_methods.rb +0 -118
- data/lib/sequel/extensions/set_overrides.rb +0 -72
- data/lib/sequel/no_core_ext.rb +0 -1
- data/lib/sequel/plugins/association_autoreloading.rb +0 -7
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +0 -7
- data/lib/sequel/plugins/pg_typecast_on_load.rb +0 -78
- data/lib/sequel/plugins/prepared_statements_associations.rb +0 -117
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +0 -59
- data/lib/sequel/plugins/schema.rb +0 -80
- data/lib/sequel/plugins/scissors.rb +0 -33
- data/spec/adapters/db2_spec.rb +0 -160
- data/spec/adapters/firebird_spec.rb +0 -411
- data/spec/adapters/informix_spec.rb +0 -100
- data/spec/adapters/mssql_spec.rb +0 -706
- data/spec/adapters/mysql_spec.rb +0 -1287
- data/spec/adapters/oracle_spec.rb +0 -313
- data/spec/adapters/postgres_spec.rb +0 -3725
- data/spec/adapters/spec_helper.rb +0 -43
- data/spec/adapters/sqlanywhere_spec.rb +0 -170
- data/spec/adapters/sqlite_spec.rb +0 -653
- data/spec/bin_spec.rb +0 -254
- data/spec/core/connection_pool_spec.rb +0 -1016
- data/spec/core/database_spec.rb +0 -2531
- data/spec/core/dataset_spec.rb +0 -5098
- data/spec/core/deprecated_spec.rb +0 -70
- data/spec/core/expression_filters_spec.rb +0 -1243
- data/spec/core/mock_adapter_spec.rb +0 -462
- data/spec/core/object_graph_spec.rb +0 -303
- data/spec/core/placeholder_literalizer_spec.rb +0 -163
- data/spec/core/schema_generator_spec.rb +0 -179
- data/spec/core/schema_spec.rb +0 -1659
- data/spec/core/spec_helper.rb +0 -34
- data/spec/core/version_spec.rb +0 -7
- data/spec/core_extensions_spec.rb +0 -699
- data/spec/extensions/accessed_columns_spec.rb +0 -51
- data/spec/extensions/active_model_spec.rb +0 -123
- data/spec/extensions/after_initialize_spec.rb +0 -24
- data/spec/extensions/arbitrary_servers_spec.rb +0 -109
- data/spec/extensions/association_dependencies_spec.rb +0 -117
- data/spec/extensions/association_pks_spec.rb +0 -365
- data/spec/extensions/association_proxies_spec.rb +0 -86
- data/spec/extensions/auto_validations_spec.rb +0 -192
- data/spec/extensions/blacklist_security_spec.rb +0 -88
- data/spec/extensions/blank_spec.rb +0 -69
- data/spec/extensions/boolean_readers_spec.rb +0 -93
- data/spec/extensions/caching_spec.rb +0 -270
- data/spec/extensions/class_table_inheritance_spec.rb +0 -420
- data/spec/extensions/column_conflicts_spec.rb +0 -60
- data/spec/extensions/column_select_spec.rb +0 -108
- data/spec/extensions/columns_introspection_spec.rb +0 -91
- data/spec/extensions/composition_spec.rb +0 -242
- data/spec/extensions/connection_validator_spec.rb +0 -120
- data/spec/extensions/constraint_validations_plugin_spec.rb +0 -274
- data/spec/extensions/constraint_validations_spec.rb +0 -325
- data/spec/extensions/core_refinements_spec.rb +0 -519
- data/spec/extensions/csv_serializer_spec.rb +0 -173
- data/spec/extensions/current_datetime_timestamp_spec.rb +0 -27
- data/spec/extensions/dataset_associations_spec.rb +0 -311
- data/spec/extensions/dataset_source_alias_spec.rb +0 -51
- data/spec/extensions/date_arithmetic_spec.rb +0 -150
- data/spec/extensions/defaults_setter_spec.rb +0 -101
- data/spec/extensions/delay_add_association_spec.rb +0 -52
- data/spec/extensions/dirty_spec.rb +0 -180
- data/spec/extensions/eager_each_spec.rb +0 -42
- data/spec/extensions/empty_array_consider_nulls_spec.rb +0 -24
- data/spec/extensions/error_splitter_spec.rb +0 -18
- data/spec/extensions/error_sql_spec.rb +0 -20
- data/spec/extensions/eval_inspect_spec.rb +0 -73
- data/spec/extensions/filter_having_spec.rb +0 -40
- data/spec/extensions/force_encoding_spec.rb +0 -114
- data/spec/extensions/from_block_spec.rb +0 -21
- data/spec/extensions/graph_each_spec.rb +0 -109
- data/spec/extensions/hash_aliases_spec.rb +0 -24
- data/spec/extensions/hook_class_methods_spec.rb +0 -429
- data/spec/extensions/inflector_spec.rb +0 -183
- data/spec/extensions/input_transformer_spec.rb +0 -54
- data/spec/extensions/insert_returning_select_spec.rb +0 -46
- data/spec/extensions/instance_filters_spec.rb +0 -79
- data/spec/extensions/instance_hooks_spec.rb +0 -276
- data/spec/extensions/inverted_subsets_spec.rb +0 -33
- data/spec/extensions/json_serializer_spec.rb +0 -291
- data/spec/extensions/lazy_attributes_spec.rb +0 -170
- data/spec/extensions/list_spec.rb +0 -267
- data/spec/extensions/looser_typecasting_spec.rb +0 -43
- data/spec/extensions/many_through_many_spec.rb +0 -2172
- data/spec/extensions/meta_def_spec.rb +0 -21
- data/spec/extensions/migration_spec.rb +0 -712
- data/spec/extensions/modification_detection_spec.rb +0 -80
- data/spec/extensions/mssql_optimistic_locking_spec.rb +0 -91
- data/spec/extensions/named_timezones_spec.rb +0 -108
- data/spec/extensions/nested_attributes_spec.rb +0 -697
- data/spec/extensions/null_dataset_spec.rb +0 -85
- data/spec/extensions/optimistic_locking_spec.rb +0 -128
- data/spec/extensions/pagination_spec.rb +0 -118
- data/spec/extensions/pg_array_associations_spec.rb +0 -736
- data/spec/extensions/pg_array_ops_spec.rb +0 -143
- data/spec/extensions/pg_array_spec.rb +0 -395
- data/spec/extensions/pg_enum_spec.rb +0 -92
- data/spec/extensions/pg_hstore_ops_spec.rb +0 -236
- data/spec/extensions/pg_hstore_spec.rb +0 -206
- data/spec/extensions/pg_inet_ops_spec.rb +0 -101
- data/spec/extensions/pg_inet_spec.rb +0 -52
- data/spec/extensions/pg_interval_spec.rb +0 -76
- data/spec/extensions/pg_json_ops_spec.rb +0 -229
- data/spec/extensions/pg_json_spec.rb +0 -218
- data/spec/extensions/pg_loose_count_spec.rb +0 -17
- data/spec/extensions/pg_range_ops_spec.rb +0 -58
- data/spec/extensions/pg_range_spec.rb +0 -404
- data/spec/extensions/pg_row_ops_spec.rb +0 -60
- data/spec/extensions/pg_row_plugin_spec.rb +0 -62
- data/spec/extensions/pg_row_spec.rb +0 -360
- data/spec/extensions/pg_static_cache_updater_spec.rb +0 -92
- data/spec/extensions/pg_typecast_on_load_spec.rb +0 -63
- data/spec/extensions/prepared_statements_associations_spec.rb +0 -159
- data/spec/extensions/prepared_statements_safe_spec.rb +0 -61
- data/spec/extensions/prepared_statements_spec.rb +0 -103
- data/spec/extensions/prepared_statements_with_pk_spec.rb +0 -31
- data/spec/extensions/pretty_table_spec.rb +0 -92
- data/spec/extensions/query_literals_spec.rb +0 -183
- data/spec/extensions/query_spec.rb +0 -102
- data/spec/extensions/rcte_tree_spec.rb +0 -392
- data/spec/extensions/round_timestamps_spec.rb +0 -43
- data/spec/extensions/schema_caching_spec.rb +0 -41
- data/spec/extensions/schema_dumper_spec.rb +0 -789
- data/spec/extensions/schema_spec.rb +0 -117
- data/spec/extensions/scissors_spec.rb +0 -26
- data/spec/extensions/select_remove_spec.rb +0 -38
- data/spec/extensions/sequel_3_dataset_methods_spec.rb +0 -101
- data/spec/extensions/serialization_modification_detection_spec.rb +0 -98
- data/spec/extensions/serialization_spec.rb +0 -362
- data/spec/extensions/server_block_spec.rb +0 -90
- data/spec/extensions/set_overrides_spec.rb +0 -61
- data/spec/extensions/sharding_spec.rb +0 -198
- data/spec/extensions/shared_caching_spec.rb +0 -175
- data/spec/extensions/single_table_inheritance_spec.rb +0 -297
- data/spec/extensions/singular_table_names_spec.rb +0 -22
- data/spec/extensions/skip_create_refresh_spec.rb +0 -17
- data/spec/extensions/spec_helper.rb +0 -71
- data/spec/extensions/split_array_nil_spec.rb +0 -24
- data/spec/extensions/split_values_spec.rb +0 -22
- data/spec/extensions/sql_expr_spec.rb +0 -60
- data/spec/extensions/static_cache_spec.rb +0 -361
- data/spec/extensions/string_date_time_spec.rb +0 -95
- data/spec/extensions/string_stripper_spec.rb +0 -68
- data/spec/extensions/subclasses_spec.rb +0 -66
- data/spec/extensions/table_select_spec.rb +0 -71
- data/spec/extensions/tactical_eager_loading_spec.rb +0 -82
- data/spec/extensions/thread_local_timezones_spec.rb +0 -67
- data/spec/extensions/timestamps_spec.rb +0 -175
- data/spec/extensions/to_dot_spec.rb +0 -154
- data/spec/extensions/touch_spec.rb +0 -203
- data/spec/extensions/tree_spec.rb +0 -274
- data/spec/extensions/typecast_on_load_spec.rb +0 -80
- data/spec/extensions/unlimited_update_spec.rb +0 -20
- data/spec/extensions/update_or_create_spec.rb +0 -87
- data/spec/extensions/update_primary_key_spec.rb +0 -100
- data/spec/extensions/update_refresh_spec.rb +0 -53
- data/spec/extensions/validate_associated_spec.rb +0 -52
- data/spec/extensions/validation_class_methods_spec.rb +0 -1027
- data/spec/extensions/validation_helpers_spec.rb +0 -541
- data/spec/extensions/xml_serializer_spec.rb +0 -207
- data/spec/files/bad_down_migration/001_create_alt_basic.rb +0 -4
- data/spec/files/bad_down_migration/002_create_alt_advanced.rb +0 -4
- data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +0 -9
- data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +0 -9
- data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +0 -3
- data/spec/files/bad_up_migration/001_create_alt_basic.rb +0 -4
- data/spec/files/bad_up_migration/002_create_alt_advanced.rb +0 -3
- data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +0 -9
- data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +0 -9
- data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +0 -4
- data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +0 -9
- data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +0 -9
- data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +0 -4
- data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +0 -4
- data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +0 -9
- data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +0 -9
- data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +0 -4
- data/spec/files/integer_migrations/001_create_sessions.rb +0 -9
- data/spec/files/integer_migrations/002_create_nodes.rb +0 -9
- data/spec/files/integer_migrations/003_3_create_users.rb +0 -4
- data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +0 -9
- data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +0 -9
- data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +0 -9
- data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +0 -9
- data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +0 -4
- data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +0 -4
- data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +0 -4
- data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +0 -9
- data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +0 -4
- data/spec/files/reversible_migrations/001_reversible.rb +0 -5
- data/spec/files/reversible_migrations/002_reversible.rb +0 -5
- data/spec/files/reversible_migrations/003_reversible.rb +0 -5
- data/spec/files/reversible_migrations/004_reversible.rb +0 -5
- data/spec/files/reversible_migrations/005_reversible.rb +0 -10
- data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +0 -9
- data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +0 -9
- data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +0 -4
- data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +0 -4
- data/spec/files/transaction_specified_migrations/002_create_basic.rb +0 -4
- data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +0 -3
- data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +0 -3
- data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +0 -9
- data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +0 -9
- data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +0 -4
- data/spec/guards_helper.rb +0 -55
- data/spec/integration/associations_test.rb +0 -2454
- data/spec/integration/database_test.rb +0 -113
- data/spec/integration/dataset_test.rb +0 -1808
- data/spec/integration/eager_loader_test.rb +0 -687
- data/spec/integration/migrator_test.rb +0 -240
- data/spec/integration/model_test.rb +0 -226
- data/spec/integration/plugin_test.rb +0 -2240
- data/spec/integration/prepared_statement_test.rb +0 -467
- data/spec/integration/schema_test.rb +0 -817
- data/spec/integration/spec_helper.rb +0 -48
- data/spec/integration/timezone_test.rb +0 -86
- data/spec/integration/transaction_test.rb +0 -374
- data/spec/integration/type_test.rb +0 -133
- data/spec/model/association_reflection_spec.rb +0 -525
- data/spec/model/associations_spec.rb +0 -4426
- data/spec/model/base_spec.rb +0 -759
- data/spec/model/class_dataset_methods_spec.rb +0 -146
- data/spec/model/dataset_methods_spec.rb +0 -149
- data/spec/model/eager_loading_spec.rb +0 -2137
- data/spec/model/hooks_spec.rb +0 -604
- data/spec/model/inflector_spec.rb +0 -26
- data/spec/model/model_spec.rb +0 -982
- data/spec/model/plugins_spec.rb +0 -299
- data/spec/model/record_spec.rb +0 -2147
- data/spec/model/spec_helper.rb +0 -46
- data/spec/model/validations_spec.rb +0 -193
- data/spec/sequel_coverage.rb +0 -15
- data/spec/spec_config.rb +0 -10
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../utils/emulate_offset_with_row_number'
|
|
2
4
|
|
|
3
5
|
module Sequel
|
|
4
6
|
module Oracle
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
Sequel::Database.set_shared_adapter_scheme(:oracle, self)
|
|
8
|
+
|
|
9
|
+
def self.mock_adapter_setup(db)
|
|
10
|
+
db.instance_exec do
|
|
11
|
+
@server_version = 11000000
|
|
12
|
+
@primary_key_sequences = {}
|
|
13
|
+
end
|
|
14
|
+
end
|
|
10
15
|
|
|
16
|
+
module DatabaseMethods
|
|
11
17
|
attr_accessor :autosequence
|
|
12
18
|
|
|
13
19
|
def create_sequence(name, opts=OPTS)
|
|
@@ -26,7 +32,6 @@ module Sequel
|
|
|
26
32
|
self << drop_sequence_sql(name)
|
|
27
33
|
end
|
|
28
34
|
|
|
29
|
-
# Oracle uses the :oracle database type
|
|
30
35
|
def database_type
|
|
31
36
|
:oracle
|
|
32
37
|
end
|
|
@@ -36,11 +41,22 @@ module Sequel
|
|
|
36
41
|
im = input_identifier_meth
|
|
37
42
|
schema, table = schema_and_table(table)
|
|
38
43
|
ds = metadata_dataset.
|
|
39
|
-
from(:
|
|
40
|
-
where
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
from{[all_cons_columns.as(:pc), all_constraints.as(:p), all_cons_columns.as(:fc), all_constraints.as(:f)]}.
|
|
45
|
+
where{{
|
|
46
|
+
f[:table_name]=>im.call(table),
|
|
47
|
+
f[:constraint_type]=>'R',
|
|
48
|
+
p[:owner]=>f[:r_owner],
|
|
49
|
+
p[:constraint_name]=>f[:r_constraint_name],
|
|
50
|
+
pc[:owner]=>p[:owner],
|
|
51
|
+
pc[:constraint_name]=>p[:constraint_name],
|
|
52
|
+
pc[:table_name]=>p[:table_name],
|
|
53
|
+
fc[:owner]=>f[:owner],
|
|
54
|
+
fc[:constraint_name]=>f[:constraint_name],
|
|
55
|
+
fc[:table_name]=>f[:table_name],
|
|
56
|
+
fc[:position]=>pc[:position]}}.
|
|
57
|
+
select{[p[:table_name].as(:table), pc[:column_name].as(:key), fc[:column_name].as(:column), f[:constraint_name].as(:name)]}.
|
|
58
|
+
order{[:table, fc[:position]]}
|
|
59
|
+
ds = ds.where{{f[:schema_name]=>im.call(schema)}} if schema
|
|
44
60
|
|
|
45
61
|
fks = {}
|
|
46
62
|
ds.each do |r|
|
|
@@ -54,12 +70,19 @@ module Sequel
|
|
|
54
70
|
fks.values
|
|
55
71
|
end
|
|
56
72
|
|
|
73
|
+
def freeze
|
|
74
|
+
current_user
|
|
75
|
+
server_version
|
|
76
|
+
@conversion_procs.freeze
|
|
77
|
+
super
|
|
78
|
+
end
|
|
79
|
+
|
|
57
80
|
# Oracle namespaces indexes per table.
|
|
58
81
|
def global_index_namespace?
|
|
59
82
|
false
|
|
60
83
|
end
|
|
61
84
|
|
|
62
|
-
IGNORE_OWNERS = %w'APEX_040000 CTXSYS EXFSYS MDSYS OLAPSYS ORDDATA ORDSYS SYS SYSTEM XDB XDBMETADATA XDBPM XFILES WMSYS'
|
|
85
|
+
IGNORE_OWNERS = %w'APEX_040000 CTXSYS EXFSYS MDSYS OLAPSYS ORDDATA ORDSYS SYS SYSTEM XDB XDBMETADATA XDBPM XFILES WMSYS'.freeze
|
|
63
86
|
|
|
64
87
|
def tables(opts=OPTS)
|
|
65
88
|
m = output_identifier_meth
|
|
@@ -80,14 +103,37 @@ module Sequel
|
|
|
80
103
|
map{|r| m.call(r[:view_name])}
|
|
81
104
|
end
|
|
82
105
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
106
|
+
# Whether a view with a given name exists. By default, looks in all schemas other than system
|
|
107
|
+
# schemas. If the :current_schema option is given, looks in the schema for the current user.
|
|
108
|
+
def view_exists?(name, opts=OPTS)
|
|
109
|
+
ds = metadata_dataset.from(:all_views).where(:view_name=>input_identifier_meth.call(name))
|
|
110
|
+
|
|
111
|
+
if opts[:current_schema]
|
|
112
|
+
ds = ds.where(:owner=>Sequel.function(:SYS_CONTEXT, 'userenv', 'current_schema'))
|
|
113
|
+
else
|
|
114
|
+
ds = ds.exclude(:owner=>IGNORE_OWNERS)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
ds.count > 0
|
|
89
118
|
end
|
|
90
119
|
|
|
120
|
+
# The version of the Oracle server, used for determining capability.
|
|
121
|
+
def server_version(server=nil)
|
|
122
|
+
return @server_version if @server_version
|
|
123
|
+
@server_version = synchronize(server) do |conn|
|
|
124
|
+
(conn.server_version rescue nil) if conn.respond_to?(:server_version)
|
|
125
|
+
end
|
|
126
|
+
unless @server_version
|
|
127
|
+
@server_version = if m = /(\d+)\.(\d+)\.?(\d+)?\.?(\d+)?/.match(fetch("select version from PRODUCT_COMPONENT_VERSION where lower(product) like 'oracle%'").single_value)
|
|
128
|
+
(m[1].to_i*1000000) + (m[2].to_i*10000) + (m[3].to_i*100) + m[4].to_i
|
|
129
|
+
else
|
|
130
|
+
0
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
@server_version
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
|
|
91
137
|
# Oracle supports deferrable constraints.
|
|
92
138
|
def supports_deferrable_constraints?
|
|
93
139
|
true
|
|
@@ -100,7 +146,6 @@ module Sequel
|
|
|
100
146
|
|
|
101
147
|
private
|
|
102
148
|
|
|
103
|
-
# Handle Oracle specific ALTER TABLE SQL
|
|
104
149
|
def alter_table_sql(table, op)
|
|
105
150
|
case op[:op]
|
|
106
151
|
when :add_column
|
|
@@ -130,7 +175,7 @@ module Sequel
|
|
|
130
175
|
end
|
|
131
176
|
|
|
132
177
|
def auto_increment_sql
|
|
133
|
-
|
|
178
|
+
''
|
|
134
179
|
end
|
|
135
180
|
|
|
136
181
|
def create_sequence_sql(name, opts=OPTS)
|
|
@@ -186,6 +231,7 @@ module Sequel
|
|
|
186
231
|
/check constraint .+ violated/ => CheckConstraintViolation,
|
|
187
232
|
/cannot insert NULL into|cannot update .+ to NULL/ => NotNullConstraintViolation,
|
|
188
233
|
/can't serialize access for this transaction/ => SerializationFailure,
|
|
234
|
+
/resource busy and acquire with NOWAIT specified or timeout/ => DatabaseLockTimeout,
|
|
189
235
|
}.freeze
|
|
190
236
|
def database_error_regexps
|
|
191
237
|
DATABASE_ERROR_REGEXPS
|
|
@@ -200,14 +246,14 @@ module Sequel
|
|
|
200
246
|
end
|
|
201
247
|
|
|
202
248
|
def remove_cached_schema(table)
|
|
203
|
-
@primary_key_sequences.delete(table)
|
|
249
|
+
Sequel.synchronize{@primary_key_sequences.delete(table)}
|
|
204
250
|
super
|
|
205
251
|
end
|
|
206
252
|
|
|
207
253
|
TRANSACTION_ISOLATION_LEVELS = {:uncommitted=>'READ COMMITTED'.freeze,
|
|
208
254
|
:committed=>'READ COMMITTED'.freeze,
|
|
209
255
|
:repeatable=>'SERIALIZABLE'.freeze,
|
|
210
|
-
:serializable=>'SERIALIZABLE'.freeze}
|
|
256
|
+
:serializable=>'SERIALIZABLE'.freeze}.freeze
|
|
211
257
|
# Oracle doesn't support READ UNCOMMITTED OR REPEATABLE READ transaction
|
|
212
258
|
# isolation levels, so upgrade to the next highest level in those cases.
|
|
213
259
|
def set_transaction_isolation_sql(level)
|
|
@@ -216,13 +262,20 @@ module Sequel
|
|
|
216
262
|
|
|
217
263
|
def sequence_for_table(table)
|
|
218
264
|
return nil unless autosequence
|
|
219
|
-
@primary_key_sequences.
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
265
|
+
Sequel.synchronize{return @primary_key_sequences[table] if @primary_key_sequences.has_key?(table)}
|
|
266
|
+
|
|
267
|
+
begin
|
|
268
|
+
sch = schema(table)
|
|
269
|
+
rescue Sequel::Error
|
|
270
|
+
return nil
|
|
225
271
|
end
|
|
272
|
+
|
|
273
|
+
pk = sch.select{|k, v| v[:primary_key]}
|
|
274
|
+
pks = if pk.length == 1
|
|
275
|
+
seq = "seq_#{table}_#{pk.first.first}"
|
|
276
|
+
seq.to_sym unless from(:user_sequences).where(:sequence_name=>input_identifier_meth.call(seq)).empty?
|
|
277
|
+
end
|
|
278
|
+
Sequel.synchronize{@primary_key_sequences[table] = pks}
|
|
226
279
|
end
|
|
227
280
|
|
|
228
281
|
# Oracle supports CREATE OR REPLACE VIEW.
|
|
@@ -233,13 +286,13 @@ module Sequel
|
|
|
233
286
|
# Oracle's integer/:number type handles larger values than
|
|
234
287
|
# most other databases's bigint types, so it should be
|
|
235
288
|
# safe to use for Bignum.
|
|
236
|
-
def
|
|
289
|
+
def type_literal_generic_bignum_symbol(column)
|
|
237
290
|
:integer
|
|
238
291
|
end
|
|
239
292
|
|
|
240
293
|
# Oracle doesn't have a time type, so use timestamp for all
|
|
241
294
|
# time columns.
|
|
242
|
-
def
|
|
295
|
+
def type_literal_generic_only_time(column)
|
|
243
296
|
:timestamp
|
|
244
297
|
end
|
|
245
298
|
|
|
@@ -251,7 +304,7 @@ module Sequel
|
|
|
251
304
|
|
|
252
305
|
# SQL fragment for showing a table is temporary
|
|
253
306
|
def temporary_table_sql
|
|
254
|
-
TEMPORARY
|
|
307
|
+
'GLOBAL TEMPORARY '
|
|
255
308
|
end
|
|
256
309
|
|
|
257
310
|
# Oracle uses clob for text types.
|
|
@@ -267,21 +320,10 @@ module Sequel
|
|
|
267
320
|
|
|
268
321
|
module DatasetMethods
|
|
269
322
|
ROW_NUMBER_EXPRESSION = LiteralString.new('ROWNUM').freeze
|
|
270
|
-
SPACE = Dataset::SPACE
|
|
271
|
-
APOS = Dataset::APOS
|
|
272
|
-
APOS_RE = Dataset::APOS_RE
|
|
273
|
-
DOUBLE_APOS = Dataset::DOUBLE_APOS
|
|
274
|
-
FROM = Dataset::FROM
|
|
275
|
-
TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S%N %z'".freeze
|
|
276
|
-
TIMESTAMP_OFFSET_FORMAT = "%+03i:%02i".freeze
|
|
277
|
-
BOOL_FALSE = "'N'".freeze
|
|
278
|
-
BOOL_TRUE = "'Y'".freeze
|
|
279
|
-
HSTAR = "H*".freeze
|
|
280
|
-
DUAL = ' FROM DUAL'.freeze
|
|
281
323
|
BITAND_PROC = lambda{|a, b| Sequel.lit(["CAST(BITAND(", ", ", ") AS INTEGER)"], a, b)}
|
|
282
324
|
|
|
283
325
|
include(Module.new do
|
|
284
|
-
Dataset.def_sql_method(self, :select, %w'with select distinct columns from join where group having compounds order lock')
|
|
326
|
+
Dataset.def_sql_method(self, :select, %w'with select distinct columns from join where group having compounds order limit lock')
|
|
285
327
|
end)
|
|
286
328
|
|
|
287
329
|
def complex_expression_sql_append(sql, op, args)
|
|
@@ -296,6 +338,19 @@ module Sequel
|
|
|
296
338
|
s2 = complex_expression_arg_pairs(x, &BITAND_PROC)
|
|
297
339
|
Sequel.lit(["(", " - ", ")"], s1, s2)
|
|
298
340
|
end
|
|
341
|
+
when :~, :'!~', :'~*', :'!~*'
|
|
342
|
+
raise InvalidOperation, "Pattern matching via regular expressions is not supported in this Oracle version" unless supports_regexp?
|
|
343
|
+
if op == :'!~' || op == :'!~*'
|
|
344
|
+
sql << 'NOT '
|
|
345
|
+
end
|
|
346
|
+
sql << 'REGEXP_LIKE('
|
|
347
|
+
literal_append(sql, args[0])
|
|
348
|
+
sql << ','
|
|
349
|
+
literal_append(sql, args[1])
|
|
350
|
+
if op == :'~*' || op == :'!~*'
|
|
351
|
+
sql << ", 'i'"
|
|
352
|
+
end
|
|
353
|
+
sql << ')'
|
|
299
354
|
when :%, :<<, :>>, :'B~'
|
|
300
355
|
complex_expression_emulate_append(sql, op, args)
|
|
301
356
|
else
|
|
@@ -338,11 +393,15 @@ module Sequel
|
|
|
338
393
|
clone(:sequence=>s)
|
|
339
394
|
end
|
|
340
395
|
|
|
341
|
-
# Handle LIMIT by using a unlimited subselect filtered with ROWNUM
|
|
396
|
+
# Handle LIMIT by using a unlimited subselect filtered with ROWNUM,
|
|
397
|
+
# unless Oracle 12 is used.
|
|
342
398
|
def select_sql
|
|
343
399
|
return super if @opts[:sql]
|
|
344
|
-
|
|
345
|
-
|
|
400
|
+
return super if supports_fetch_next_rows?
|
|
401
|
+
|
|
402
|
+
o = @opts[:offset]
|
|
403
|
+
if o && o != 0
|
|
404
|
+
columns = clone(:append_sql=>String.new, :placeholder_literal_null=>true).columns
|
|
346
405
|
dsa1 = dataset_alias(1)
|
|
347
406
|
rn = row_number_column
|
|
348
407
|
limit = @opts[:limit]
|
|
@@ -353,15 +412,15 @@ module Sequel
|
|
|
353
412
|
select(*columns).
|
|
354
413
|
where(SQL::Identifier.new(rn) > o)
|
|
355
414
|
ds = ds.where(SQL::Identifier.new(rn) <= Sequel.+(o, limit)) if limit
|
|
356
|
-
sql = @opts[:append_sql] ||
|
|
415
|
+
sql = @opts[:append_sql] || String.new
|
|
357
416
|
subselect_sql_append(sql, ds)
|
|
358
417
|
sql
|
|
359
418
|
elsif limit = @opts[:limit]
|
|
360
|
-
ds =
|
|
419
|
+
ds = unlimited
|
|
361
420
|
# Lock doesn't work in subselects, so don't use a subselect when locking.
|
|
362
421
|
# Don't use a subselect if custom SQL is used, as it breaks somethings.
|
|
363
422
|
ds = ds.from_self unless @opts[:lock]
|
|
364
|
-
sql = @opts[:append_sql] ||
|
|
423
|
+
sql = @opts[:append_sql] || String.new
|
|
365
424
|
subselect_sql_append(sql, ds.where(SQL::ComplexExpression.new(:<=, ROW_NUMBER_EXPRESSION, limit)))
|
|
366
425
|
sql
|
|
367
426
|
else
|
|
@@ -383,6 +442,12 @@ module Sequel
|
|
|
383
442
|
false
|
|
384
443
|
end
|
|
385
444
|
|
|
445
|
+
# Oracle supports FETCH NEXT ROWS since 12c, but it doesn't work when
|
|
446
|
+
# locking or when skipping locked rows.
|
|
447
|
+
def supports_fetch_next_rows?
|
|
448
|
+
server_version >= 12000000 && !(@opts[:lock] || @opts[:skip_locked])
|
|
449
|
+
end
|
|
450
|
+
|
|
386
451
|
# Oracle supports GROUP BY CUBE
|
|
387
452
|
def supports_group_cube?
|
|
388
453
|
true
|
|
@@ -413,6 +478,11 @@ module Sequel
|
|
|
413
478
|
false
|
|
414
479
|
end
|
|
415
480
|
|
|
481
|
+
# Oracle supports NOWAIT.
|
|
482
|
+
def supports_nowait?
|
|
483
|
+
true
|
|
484
|
+
end
|
|
485
|
+
|
|
416
486
|
# Oracle does not support offsets in correlated subqueries.
|
|
417
487
|
def supports_offsets_in_correlated_subqueries?
|
|
418
488
|
false
|
|
@@ -423,6 +493,11 @@ module Sequel
|
|
|
423
493
|
false
|
|
424
494
|
end
|
|
425
495
|
|
|
496
|
+
# Oracle supports SKIP LOCKED.
|
|
497
|
+
def supports_skip_locked?
|
|
498
|
+
true
|
|
499
|
+
end
|
|
500
|
+
|
|
426
501
|
# Oracle supports timezones in literal timestamps.
|
|
427
502
|
def supports_timestamp_timezones?
|
|
428
503
|
true
|
|
@@ -438,6 +513,16 @@ module Sequel
|
|
|
438
513
|
true
|
|
439
514
|
end
|
|
440
515
|
|
|
516
|
+
# The version of the database server
|
|
517
|
+
def server_version
|
|
518
|
+
db.server_version(@opts[:server])
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
# Oracle 10+ supports pattern matching via regular expressions
|
|
522
|
+
def supports_regexp?
|
|
523
|
+
server_version >= 10010002
|
|
524
|
+
end
|
|
525
|
+
|
|
441
526
|
private
|
|
442
527
|
|
|
443
528
|
# Allow preparing prepared statements, since determining the prepared sql to use for
|
|
@@ -447,23 +532,24 @@ module Sequel
|
|
|
447
532
|
end
|
|
448
533
|
|
|
449
534
|
# Oracle doesn't support the use of AS when aliasing a dataset. It doesn't require
|
|
450
|
-
# the use of AS anywhere, so this disables it in all cases.
|
|
535
|
+
# the use of AS anywhere, so this disables it in all cases. Oracle also does not support
|
|
536
|
+
# derived column lists in aliases.
|
|
451
537
|
def as_sql_append(sql, aliaz, column_aliases=nil)
|
|
452
538
|
raise Error, "oracle does not support derived column lists" if column_aliases
|
|
453
|
-
sql <<
|
|
539
|
+
sql << ' '
|
|
454
540
|
quote_identifier_append(sql, aliaz)
|
|
455
541
|
end
|
|
456
542
|
|
|
457
543
|
# The strftime format to use when literalizing the time.
|
|
458
544
|
def default_timestamp_format
|
|
459
|
-
|
|
545
|
+
"TIMESTAMP '%Y-%m-%d %H:%M:%S%N %z'"
|
|
460
546
|
end
|
|
461
547
|
|
|
462
548
|
def empty_from_sql
|
|
463
|
-
DUAL
|
|
549
|
+
' FROM DUAL'
|
|
464
550
|
end
|
|
465
551
|
|
|
466
|
-
# There is no function on
|
|
552
|
+
# There is no function on Oracle that does character length
|
|
467
553
|
# and respects trailing spaces (datalength respects trailing spaces, but
|
|
468
554
|
# counts bytes instead of characters). Use a hack to work around the
|
|
469
555
|
# trailing spaces issue.
|
|
@@ -495,7 +581,7 @@ module Sequel
|
|
|
495
581
|
|
|
496
582
|
# Use a colon for the timestamp offset, since Oracle appears to require it.
|
|
497
583
|
def format_timestamp_offset(hour, minute)
|
|
498
|
-
sprintf(
|
|
584
|
+
sprintf("%+03i:%02i", hour, minute)
|
|
499
585
|
end
|
|
500
586
|
|
|
501
587
|
# Oracle doesn't support empty values when inserting.
|
|
@@ -505,22 +591,22 @@ module Sequel
|
|
|
505
591
|
|
|
506
592
|
# Use string in hex format for blob data.
|
|
507
593
|
def literal_blob_append(sql, v)
|
|
508
|
-
sql <<
|
|
594
|
+
sql << "'" << v.unpack("H*").first << "'"
|
|
509
595
|
end
|
|
510
596
|
|
|
511
597
|
# Oracle uses 'N' for false values.
|
|
512
598
|
def literal_false
|
|
513
|
-
|
|
599
|
+
"'N'"
|
|
514
600
|
end
|
|
515
601
|
|
|
516
602
|
# Oracle uses the SQL standard of only doubling ' inside strings.
|
|
517
603
|
def literal_string_append(sql, v)
|
|
518
|
-
sql <<
|
|
604
|
+
sql << "'" << v.gsub("'", "''") << "'"
|
|
519
605
|
end
|
|
520
606
|
|
|
521
607
|
# Oracle uses 'Y' for true values.
|
|
522
608
|
def literal_true
|
|
523
|
-
|
|
609
|
+
"'Y'"
|
|
524
610
|
end
|
|
525
611
|
|
|
526
612
|
# Oracle can insert multiple rows using a UNION
|
|
@@ -528,6 +614,35 @@ module Sequel
|
|
|
528
614
|
:union
|
|
529
615
|
end
|
|
530
616
|
|
|
617
|
+
def select_limit_sql(sql)
|
|
618
|
+
return unless supports_fetch_next_rows?
|
|
619
|
+
|
|
620
|
+
if offset = @opts[:offset]
|
|
621
|
+
sql << " OFFSET "
|
|
622
|
+
literal_append(sql, offset)
|
|
623
|
+
sql << " ROWS"
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
if limit = @opts[:limit]
|
|
627
|
+
sql << " FETCH NEXT "
|
|
628
|
+
literal_append(sql, limit)
|
|
629
|
+
sql << " ROWS ONLY"
|
|
630
|
+
end
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
# Use SKIP LOCKED if skipping locked rows.
|
|
634
|
+
def select_lock_sql(sql)
|
|
635
|
+
super
|
|
636
|
+
|
|
637
|
+
if @opts[:lock]
|
|
638
|
+
if @opts[:skip_locked]
|
|
639
|
+
sql << " SKIP LOCKED"
|
|
640
|
+
elsif @opts[:nowait]
|
|
641
|
+
sql << " NOWAIT"
|
|
642
|
+
end
|
|
643
|
+
end
|
|
644
|
+
end
|
|
645
|
+
|
|
531
646
|
# Oracle supports quoted function names.
|
|
532
647
|
def supports_quoted_function_names?
|
|
533
648
|
true
|
|
@@ -1,61 +1,113 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../utils/unmodified_identifiers'
|
|
2
4
|
|
|
3
5
|
module Sequel
|
|
4
6
|
# Top level module for holding all PostgreSQL-related modules and classes
|
|
5
|
-
# for Sequel.
|
|
6
|
-
# metaprogramming. These are:
|
|
7
|
-
#
|
|
8
|
-
# client_min_messages :: Change the minimum level of messages that PostgreSQL will send to the
|
|
9
|
-
# the client. The PostgreSQL default is NOTICE, the Sequel default is
|
|
10
|
-
# WARNING. Set to nil to not change the server default. Overridable on
|
|
11
|
-
# a per instance basis via the :client_min_messages option.
|
|
12
|
-
# force_standard_strings :: Set to false to not force the use of standard strings. Overridable
|
|
13
|
-
# on a per instance basis via the :force_standard_strings option.
|
|
14
|
-
#
|
|
15
|
-
# It is not recommened you use these module-level accessors. Instead,
|
|
16
|
-
# use the database option to make the setting per-Database.
|
|
17
|
-
#
|
|
18
|
-
# All adapters that connect to PostgreSQL support the following option in
|
|
19
|
-
# addition to those mentioned above:
|
|
7
|
+
# for Sequel. All adapters that connect to PostgreSQL support the following options:
|
|
20
8
|
#
|
|
9
|
+
# :client_min_messages :: Change the minimum level of messages that PostgreSQL will send to the
|
|
10
|
+
# the client. The PostgreSQL default is NOTICE, the Sequel default is
|
|
11
|
+
# WARNING. Set to nil to not change the server default. Overridable on
|
|
12
|
+
# a per instance basis via the :client_min_messages option.
|
|
13
|
+
# :force_standard_strings :: Set to false to not force the use of standard strings. Overridable
|
|
14
|
+
# on a per instance basis via the :force_standard_strings option.
|
|
21
15
|
# :search_path :: Set the schema search_path for this Database's connections.
|
|
22
16
|
# Allows to to set which schemas do not need explicit
|
|
23
17
|
# qualification, and in which order to check the schemas when
|
|
24
18
|
# an unqualified object is referenced.
|
|
25
19
|
module Postgres
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
20
|
+
Sequel::Database.set_shared_adapter_scheme(:postgres, self)
|
|
21
|
+
|
|
22
|
+
NAN = 0.0/0.0
|
|
23
|
+
PLUS_INFINITY = 1.0/0.0
|
|
24
|
+
MINUS_INFINITY = -1.0/0.0
|
|
25
|
+
|
|
26
|
+
boolean = Object.new
|
|
27
|
+
def boolean.call(s) s == 't' end
|
|
28
|
+
integer = Object.new
|
|
29
|
+
def integer.call(s) s.to_i end
|
|
30
|
+
float = Object.new
|
|
31
|
+
def float.call(s)
|
|
32
|
+
case s
|
|
33
|
+
when 'NaN'
|
|
34
|
+
NAN
|
|
35
|
+
when 'Infinity'
|
|
36
|
+
PLUS_INFINITY
|
|
37
|
+
when '-Infinity'
|
|
38
|
+
MINUS_INFINITY
|
|
39
|
+
else
|
|
40
|
+
s.to_f
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
date = Object.new
|
|
44
|
+
def date.call(s) ::Date.new(*s.split('-').map(&:to_i)) end
|
|
45
|
+
TYPE_TRANSLATOR_DATE = date.freeze
|
|
46
|
+
bytea = Object.new
|
|
47
|
+
def bytea.call(str)
|
|
48
|
+
str = if str =~ /\A\\x/
|
|
49
|
+
# PostgreSQL 9.0+ bytea hex format
|
|
50
|
+
str[2..-1].gsub(/(..)/){|s| s.to_i(16).chr}
|
|
51
|
+
else
|
|
52
|
+
# Historical PostgreSQL bytea escape format
|
|
53
|
+
str.gsub(/\\(\\|'|[0-3][0-7][0-7])/) {|s|
|
|
54
|
+
if s.size == 2 then s[1,1] else s[1,3].oct.chr end
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
::Sequel::SQL::Blob.new(str)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
CONVERSION_PROCS = {}
|
|
61
|
+
|
|
62
|
+
{
|
|
63
|
+
[16] => boolean,
|
|
64
|
+
[17] => bytea,
|
|
65
|
+
[20, 21, 23, 26] => integer,
|
|
66
|
+
[700, 701] => float,
|
|
67
|
+
[1700] => ::Kernel.method(:BigDecimal),
|
|
68
|
+
[1083, 1266] => ::Sequel.method(:string_to_time),
|
|
69
|
+
[1082] => ::Sequel.method(:string_to_date),
|
|
70
|
+
[1184, 1114] => ::Sequel.method(:database_to_application_timestamp),
|
|
71
|
+
}.each do |k,v|
|
|
72
|
+
k.each do |n|
|
|
73
|
+
CONVERSION_PROCS[n] = v
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
CONVERSION_PROCS.freeze
|
|
77
|
+
|
|
78
|
+
module MockAdapterDatabaseMethods
|
|
79
|
+
def bound_variable_arg(arg, conn)
|
|
80
|
+
arg
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def primary_key(table)
|
|
84
|
+
:id
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def self.mock_adapter_setup(db)
|
|
89
|
+
db.instance_exec do
|
|
90
|
+
@server_version = 90500
|
|
91
|
+
initialize_postgres_adapter
|
|
92
|
+
extend(MockAdapterDatabaseMethods)
|
|
93
|
+
end
|
|
46
94
|
end
|
|
47
95
|
|
|
48
|
-
class CreateTableGenerator < Sequel::Schema::
|
|
96
|
+
class CreateTableGenerator < Sequel::Schema::CreateTableGenerator
|
|
49
97
|
# Add an exclusion constraint when creating the table. Elements should be
|
|
50
98
|
# an array of 2 element arrays, with the first element being the column or
|
|
51
99
|
# expression the exclusion constraint is applied to, and the second element
|
|
52
|
-
# being the operator to use for the column/expression to check for exclusion
|
|
53
|
-
#
|
|
54
|
-
# Example:
|
|
100
|
+
# being the operator to use for the column/expression to check for exclusion:
|
|
55
101
|
#
|
|
56
102
|
# exclude([[:col1, '&&'], [:col2, '=']])
|
|
57
103
|
# # EXCLUDE USING gist (col1 WITH &&, col2 WITH =)
|
|
58
104
|
#
|
|
105
|
+
# To use a custom operator class, you need to use Sequel.lit with the expression
|
|
106
|
+
# and operator class:
|
|
107
|
+
#
|
|
108
|
+
# exclude([[Sequel.lit('col1 inet_ops'), '&&'], [:col2, '=']])
|
|
109
|
+
# # EXCLUDE USING gist (col1 inet_ops WITH &&, col2 WITH =)
|
|
110
|
+
#
|
|
59
111
|
# Options supported:
|
|
60
112
|
#
|
|
61
113
|
# :name :: Name the constraint with the given name (useful if you may
|
|
@@ -82,21 +134,106 @@ module Sequel
|
|
|
82
134
|
end
|
|
83
135
|
end
|
|
84
136
|
|
|
137
|
+
# Generator used for creating tables that are partitions of other tables.
|
|
138
|
+
class CreatePartitionOfTableGenerator
|
|
139
|
+
MINVALUE = Sequel.lit('MINVALUE').freeze
|
|
140
|
+
MAXVALUE = Sequel.lit('MAXVALUE').freeze
|
|
141
|
+
|
|
142
|
+
def initialize(&block)
|
|
143
|
+
instance_exec(&block)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# The minimum value of the data type used in range partitions, useful
|
|
147
|
+
# as an argument to #from.
|
|
148
|
+
def minvalue
|
|
149
|
+
MINVALUE
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# The minimum value of the data type used in range partitions, useful
|
|
153
|
+
# as an argument to #to.
|
|
154
|
+
def maxvalue
|
|
155
|
+
MAXVALUE
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Assumes range partitioning, sets the inclusive minimum value of the range for
|
|
159
|
+
# this partition.
|
|
160
|
+
def from(*v)
|
|
161
|
+
@from = v
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Assumes range partitioning, sets the exclusive maximum value of the range for
|
|
165
|
+
# this partition.
|
|
166
|
+
def to(*v)
|
|
167
|
+
@to = v
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Assumes list partitioning, sets the values to be included in this partition.
|
|
171
|
+
def values_in(*v)
|
|
172
|
+
@in = v
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Assumes hash partitioning, sets the modulus for this parition.
|
|
176
|
+
def modulus(v)
|
|
177
|
+
@modulus = v
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Assumes hash partitioning, sets the remainder for this parition.
|
|
181
|
+
def remainder(v)
|
|
182
|
+
@remainder = v
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Sets that this is a default partition, where values not in other partitions
|
|
186
|
+
# are stored.
|
|
187
|
+
def default
|
|
188
|
+
@default = true
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# The from and to values of this partition for a range partition.
|
|
192
|
+
def range
|
|
193
|
+
[@from, @to]
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# The values to include in this partition for a list partition.
|
|
197
|
+
def list
|
|
198
|
+
@in
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# The modulus and remainder to use for this partition for a hash partition.
|
|
202
|
+
def hash_values
|
|
203
|
+
[@modulus, @remainder]
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Determine the appropriate partition type for this partition by which methods
|
|
207
|
+
# were called on it.
|
|
208
|
+
def partition_type
|
|
209
|
+
raise Error, "Unable to determine partition type, multiple different partitioning methods called" if [@from || @to, @list, @modulus || @remainder, @default].compact.length > 1
|
|
210
|
+
|
|
211
|
+
if @from || @to
|
|
212
|
+
raise Error, "must call both from and to when creating a partition of a table if calling either" unless @from && @to
|
|
213
|
+
:range
|
|
214
|
+
elsif @in
|
|
215
|
+
:list
|
|
216
|
+
elsif @modulus || @remainder
|
|
217
|
+
raise Error, "must call both modulus and remainder when creating a partition of a table if calling either" unless @modulus && @remainder
|
|
218
|
+
:hash
|
|
219
|
+
elsif @default
|
|
220
|
+
:default
|
|
221
|
+
else
|
|
222
|
+
raise Error, "unable to determine partition type, no partitioning methods called"
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
85
227
|
# Error raised when Sequel determines a PostgreSQL exclusion constraint has been violated.
|
|
86
228
|
class ExclusionConstraintViolation < Sequel::ConstraintViolation; end
|
|
87
229
|
|
|
88
|
-
# Methods shared by Database instances that connect to PostgreSQL.
|
|
89
230
|
module DatabaseMethods
|
|
90
|
-
|
|
231
|
+
include UnmodifiedIdentifiers::DatabaseMethods
|
|
91
232
|
|
|
92
233
|
PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
UNLOGGED = 'UNLOGGED '.freeze
|
|
97
|
-
ON_COMMIT = {
|
|
98
|
-
:drop => 'DROP', :delete_rows => 'DELETE ROWS', :preserve_rows => 'PRESERVE ROWS',
|
|
99
|
-
}.freeze
|
|
234
|
+
FOREIGN_KEY_LIST_ON_DELETE_MAP = {'a'=>:no_action, 'r'=>:restrict, 'c'=>:cascade, 'n'=>:set_null, 'd'=>:set_default}.freeze
|
|
235
|
+
ON_COMMIT = {:drop => 'DROP', :delete_rows => 'DELETE ROWS', :preserve_rows => 'PRESERVE ROWS'}.freeze
|
|
236
|
+
ON_COMMIT.each_value(&:freeze)
|
|
100
237
|
|
|
101
238
|
# SQL fragment for custom sequences (ones not created by serial primary key),
|
|
102
239
|
# Returning the schema and literal form of the sequence name, by parsing
|
|
@@ -104,10 +241,10 @@ module Sequel
|
|
|
104
241
|
SELECT_CUSTOM_SEQUENCE_SQL = (<<-end_sql
|
|
105
242
|
SELECT name.nspname AS "schema",
|
|
106
243
|
CASE
|
|
107
|
-
WHEN split_part(def.
|
|
108
|
-
substr(split_part(def.
|
|
109
|
-
strpos(split_part(def.
|
|
110
|
-
ELSE split_part(def.
|
|
244
|
+
WHEN split_part(pg_get_expr(def.adbin, attr.attrelid), '''', 2) ~ '.' THEN
|
|
245
|
+
substr(split_part(pg_get_expr(def.adbin, attr.attrelid), '''', 2),
|
|
246
|
+
strpos(split_part(pg_get_expr(def.adbin, attr.attrelid), '''', 2), '.')+1)
|
|
247
|
+
ELSE split_part(pg_get_expr(def.adbin, attr.attrelid), '''', 2)
|
|
111
248
|
END AS "sequence"
|
|
112
249
|
FROM pg_class t
|
|
113
250
|
JOIN pg_namespace name ON (t.relnamespace = name.oid)
|
|
@@ -115,7 +252,7 @@ module Sequel
|
|
|
115
252
|
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
|
116
253
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
|
117
254
|
WHERE cons.contype = 'p'
|
|
118
|
-
AND def.
|
|
255
|
+
AND pg_get_expr(def.adbin, attr.attrelid) ~* 'nextval'
|
|
119
256
|
end_sql
|
|
120
257
|
).strip.gsub(/\s+/, ' ').freeze
|
|
121
258
|
|
|
@@ -154,21 +291,120 @@ module Sequel
|
|
|
154
291
|
# having callable values for the conversion proc for that type.
|
|
155
292
|
attr_reader :conversion_procs
|
|
156
293
|
|
|
157
|
-
#
|
|
158
|
-
#
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
294
|
+
# Set a conversion proc for the given oid. The callable can
|
|
295
|
+
# be passed either as a argument or a block.
|
|
296
|
+
def add_conversion_proc(oid, callable=nil, &block)
|
|
297
|
+
conversion_procs[oid] = callable || block
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# Add a conversion proc for a named type, using the given block.
|
|
301
|
+
# This should be used for types without fixed OIDs, which includes all types that
|
|
302
|
+
# are not included in a default PostgreSQL installation.
|
|
162
303
|
def add_named_conversion_proc(name, &block)
|
|
163
|
-
|
|
304
|
+
unless oid = from(:pg_type).where(:typtype=>['b', 'e'], :typname=>name.to_s).get(:oid)
|
|
305
|
+
raise Error, "No matching type in pg_type for #{name.inspect}"
|
|
306
|
+
end
|
|
307
|
+
add_conversion_proc(oid, block)
|
|
164
308
|
end
|
|
165
309
|
|
|
166
|
-
# Commit an existing prepared transaction with the given transaction
|
|
167
|
-
# identifier string.
|
|
168
310
|
def commit_prepared_transaction(transaction_id, opts=OPTS)
|
|
169
311
|
run("COMMIT PREPARED #{literal(transaction_id)}", opts)
|
|
170
312
|
end
|
|
171
313
|
|
|
314
|
+
# A hash of metadata for CHECK constraints on the table.
|
|
315
|
+
# Keys are CHECK constraint name symbols. Values are hashes with the following keys:
|
|
316
|
+
# :definition :: An SQL fragment for the definition of the constraint
|
|
317
|
+
# :columns :: An array of column symbols for the columns referenced in the constraint,
|
|
318
|
+
# can be an empty array if the database cannot deteremine the column symbols.
|
|
319
|
+
def check_constraints(table)
|
|
320
|
+
m = output_identifier_meth
|
|
321
|
+
|
|
322
|
+
rows = metadata_dataset.
|
|
323
|
+
from{pg_constraint.as(:co)}.
|
|
324
|
+
left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
|
325
|
+
where(:conrelid=>regclass_oid(table), :contype=>'c').
|
|
326
|
+
select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
|
|
327
|
+
|
|
328
|
+
hash = {}
|
|
329
|
+
rows.each do |row|
|
|
330
|
+
constraint = m.call(row[:constraint])
|
|
331
|
+
entry = hash[constraint] ||= {:definition=>row[:definition], :columns=>[]}
|
|
332
|
+
entry[:columns] << m.call(row[:column]) if row[:column]
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
hash
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Convert the first primary key column in the +table+ from being a serial column to being an identity column.
|
|
339
|
+
# If the column is already an identity column, assume it was already converted and make no changes.
|
|
340
|
+
#
|
|
341
|
+
# Only supported on PostgreSQL 10.2+, since on those versions Sequel will use identity columns
|
|
342
|
+
# instead of serial columns for auto incrementing primary keys. Only supported when running as
|
|
343
|
+
# a superuser, since regular users cannot modify system tables, and there is no way to keep an
|
|
344
|
+
# existing sequence when changing an existing column to be an identity column.
|
|
345
|
+
#
|
|
346
|
+
# This method can raise an exception in at least the following cases where it may otherwise succeed
|
|
347
|
+
# (there may be additional cases not listed here):
|
|
348
|
+
#
|
|
349
|
+
# * The serial column was added after table creation using PostgreSQL <7.3
|
|
350
|
+
# * A regular index also exists on the column (such an index can probably be dropped as the
|
|
351
|
+
# primary key index should suffice)
|
|
352
|
+
#
|
|
353
|
+
# Options:
|
|
354
|
+
# :column :: Specify the column to convert instead of using the first primary key column
|
|
355
|
+
# :server :: Run the SQL on the given server
|
|
356
|
+
def convert_serial_to_identity(table, opts=OPTS)
|
|
357
|
+
raise Error, "convert_serial_to_identity is only supported on PostgreSQL 10.2+" unless server_version >= 100002
|
|
358
|
+
|
|
359
|
+
server = opts[:server]
|
|
360
|
+
server_hash = server ? {:server=>server} : OPTS
|
|
361
|
+
ds = dataset
|
|
362
|
+
ds = ds.server(server) if server
|
|
363
|
+
|
|
364
|
+
raise Error, "convert_serial_to_identity requires superuser permissions" unless ds.get{current_setting('is_superuser')} == 'on'
|
|
365
|
+
|
|
366
|
+
table_oid = regclass_oid(table)
|
|
367
|
+
im = input_identifier_meth
|
|
368
|
+
unless column = im.call(opts[:column] || ((sch = schema(table).find{|_, sc| sc[:primary_key] && sc[:auto_increment]}) && sch[0]))
|
|
369
|
+
raise Error, "could not determine column to convert from serial to identity automatically"
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
column_num = ds.from(:pg_attribute).
|
|
373
|
+
where(:attrelid=>table_oid, :attname=>column).
|
|
374
|
+
get(:attnum)
|
|
375
|
+
|
|
376
|
+
pg_class = Sequel.cast('pg_class', :regclass)
|
|
377
|
+
res = ds.from(:pg_depend).
|
|
378
|
+
where(:refclassid=>pg_class, :refobjid=>table_oid, :refobjsubid=>column_num, :classid=>pg_class, :objsubid=>0, :deptype=>%w'a i').
|
|
379
|
+
select_map([:objid, Sequel.as({:deptype=>'i'}, :v)])
|
|
380
|
+
|
|
381
|
+
case res.length
|
|
382
|
+
when 0
|
|
383
|
+
raise Error, "unable to find related sequence when converting serial to identity"
|
|
384
|
+
when 1
|
|
385
|
+
seq_oid, already_identity = res.first
|
|
386
|
+
else
|
|
387
|
+
raise Error, "more than one linked sequence found when converting serial to identity"
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
return if already_identity
|
|
391
|
+
|
|
392
|
+
transaction(server_hash) do
|
|
393
|
+
run("ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(column)} DROP DEFAULT", server_hash)
|
|
394
|
+
|
|
395
|
+
ds.from(:pg_depend).
|
|
396
|
+
where(:classid=>pg_class, :objid=>seq_oid, :objsubid=>0, :deptype=>'a').
|
|
397
|
+
update(:deptype=>'i')
|
|
398
|
+
|
|
399
|
+
ds.from(:pg_attribute).
|
|
400
|
+
where(:attrelid=>table_oid, :attname=>column).
|
|
401
|
+
update(:attidentity=>'d')
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
remove_cached_schema(table)
|
|
405
|
+
nil
|
|
406
|
+
end
|
|
407
|
+
|
|
172
408
|
# Creates the function in the database. Arguments:
|
|
173
409
|
# name :: name of the function to create
|
|
174
410
|
# definition :: string definition of the function, or object file for a dynamically loaded C function.
|
|
@@ -213,6 +449,26 @@ module Sequel
|
|
|
213
449
|
self << create_schema_sql(name, opts)
|
|
214
450
|
end
|
|
215
451
|
|
|
452
|
+
# Support partitions of tables using the :partition_of option.
|
|
453
|
+
def create_table(name, options=OPTS, &block)
|
|
454
|
+
if options[:partition_of]
|
|
455
|
+
create_partition_of_table_from_generator(name, CreatePartitionOfTableGenerator.new(&block), options)
|
|
456
|
+
return
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
super
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
# Support partitions of tables using the :partition_of option.
|
|
463
|
+
def create_table?(name, options=OPTS, &block)
|
|
464
|
+
if options[:partition_of]
|
|
465
|
+
create_table(name, options.merge!(:if_not_exists=>true), &block)
|
|
466
|
+
return
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
super
|
|
470
|
+
end
|
|
471
|
+
|
|
216
472
|
# Create a trigger in the database. Arguments:
|
|
217
473
|
# table :: the table on which this trigger operates
|
|
218
474
|
# name :: the name of this trigger
|
|
@@ -228,7 +484,6 @@ module Sequel
|
|
|
228
484
|
self << create_trigger_sql(table, name, function, opts)
|
|
229
485
|
end
|
|
230
486
|
|
|
231
|
-
# PostgreSQL uses the :postgres database type.
|
|
232
487
|
def database_type
|
|
233
488
|
:postgres
|
|
234
489
|
end
|
|
@@ -283,75 +538,144 @@ module Sequel
|
|
|
283
538
|
|
|
284
539
|
# Return full foreign key information using the pg system tables, including
|
|
285
540
|
# :name, :on_delete, :on_update, and :deferrable entries in the hashes.
|
|
541
|
+
#
|
|
542
|
+
# Supports additional options:
|
|
543
|
+
# :reverse :: Instead of returning foreign keys in the current table, return
|
|
544
|
+
# foreign keys in other tables that reference the current table.
|
|
545
|
+
# :schema :: Set to true to have the :table value in the hashes be a qualified
|
|
546
|
+
# identifier. Set to false to use a separate :schema value with
|
|
547
|
+
# the related schema. Defaults to whether the given table argument
|
|
548
|
+
# is a qualified identifier.
|
|
286
549
|
def foreign_key_list(table, opts=OPTS)
|
|
287
550
|
m = output_identifier_meth
|
|
288
551
|
schema, _ = opts.fetch(:schema, schema_and_table(table))
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
552
|
+
oid = regclass_oid(table)
|
|
553
|
+
reverse = opts[:reverse]
|
|
554
|
+
|
|
555
|
+
if reverse
|
|
556
|
+
ctable = Sequel[:att2]
|
|
557
|
+
cclass = Sequel[:cl2]
|
|
558
|
+
rtable = Sequel[:att]
|
|
559
|
+
rclass = Sequel[:cl]
|
|
560
|
+
else
|
|
561
|
+
ctable = Sequel[:att]
|
|
562
|
+
cclass = Sequel[:cl]
|
|
563
|
+
rtable = Sequel[:att2]
|
|
564
|
+
rclass = Sequel[:cl2]
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
if server_version >= 90500
|
|
568
|
+
cpos = Sequel.expr{array_position(co[:conkey], ctable[:attnum])}
|
|
569
|
+
rpos = Sequel.expr{array_position(co[:confkey], rtable[:attnum])}
|
|
570
|
+
else
|
|
571
|
+
range = 0...32
|
|
572
|
+
cpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, ctable[:attnum])}
|
|
573
|
+
rpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, rtable[:attnum])}
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
ds = metadata_dataset.
|
|
577
|
+
from{pg_constraint.as(:co)}.
|
|
578
|
+
join(Sequel[:pg_class].as(cclass), :oid=>:conrelid).
|
|
579
|
+
join(Sequel[:pg_attribute].as(ctable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
|
580
|
+
join(Sequel[:pg_class].as(rclass), :oid=>Sequel[:co][:confrelid]).
|
|
581
|
+
join(Sequel[:pg_attribute].as(rtable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:confkey])).
|
|
582
|
+
join(Sequel[:pg_namespace].as(:nsp), :oid=>Sequel[:cl2][:relnamespace]).
|
|
583
|
+
order{[co[:conname], cpos]}.
|
|
584
|
+
where{{
|
|
585
|
+
cl[:relkind]=>'r',
|
|
586
|
+
co[:contype]=>'f',
|
|
587
|
+
cl[:oid]=>oid,
|
|
588
|
+
cpos=>rpos
|
|
589
|
+
}}.
|
|
590
|
+
select{[
|
|
591
|
+
co[:conname].as(:name),
|
|
592
|
+
ctable[:attname].as(:column),
|
|
593
|
+
co[:confupdtype].as(:on_update),
|
|
594
|
+
co[:confdeltype].as(:on_delete),
|
|
595
|
+
cl2[:relname].as(:table),
|
|
596
|
+
rtable[:attname].as(:refcolumn),
|
|
597
|
+
SQL::BooleanExpression.new(:AND, co[:condeferrable], co[:condeferred]).as(:deferrable),
|
|
598
|
+
nsp[:nspname].as(:schema)
|
|
599
|
+
]}
|
|
600
|
+
|
|
601
|
+
if reverse
|
|
602
|
+
ds = ds.order_append(Sequel[:nsp][:nspname], Sequel[:cl2][:relname])
|
|
319
603
|
end
|
|
320
604
|
|
|
321
605
|
h = {}
|
|
322
606
|
fklod_map = FOREIGN_KEY_LIST_ON_DELETE_MAP
|
|
607
|
+
|
|
323
608
|
ds.each do |row|
|
|
324
|
-
if
|
|
609
|
+
if reverse
|
|
610
|
+
key = [row[:schema], row[:table], row[:name]]
|
|
611
|
+
else
|
|
612
|
+
key = row[:name]
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
if r = h[key]
|
|
325
616
|
r[:columns] << m.call(row[:column])
|
|
617
|
+
r[:key] << m.call(row[:refcolumn])
|
|
326
618
|
else
|
|
327
|
-
h[
|
|
619
|
+
entry = h[key] = {
|
|
620
|
+
:name=>m.call(row[:name]),
|
|
621
|
+
:columns=>[m.call(row[:column])],
|
|
622
|
+
:key=>[m.call(row[:refcolumn])],
|
|
623
|
+
:on_update=>fklod_map[row[:on_update]],
|
|
624
|
+
:on_delete=>fklod_map[row[:on_delete]],
|
|
625
|
+
:deferrable=>row[:deferrable],
|
|
626
|
+
:table=>schema ? SQL::QualifiedIdentifier.new(m.call(row[:schema]), m.call(row[:table])) : m.call(row[:table]),
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
unless schema
|
|
630
|
+
# If not combining schema information into the :table entry
|
|
631
|
+
# include it as a separate entry.
|
|
632
|
+
entry[:schema] = m.call(row[:schema])
|
|
633
|
+
end
|
|
328
634
|
end
|
|
329
635
|
end
|
|
330
|
-
|
|
331
|
-
r = h[row[:name]]
|
|
332
|
-
r[:table] ||= schema ? SQL::QualifiedIdentifier.new(m.call(row[:schema]), m.call(row[:table])) : m.call(row[:table])
|
|
333
|
-
r[:key] ||= []
|
|
334
|
-
r[:key] << m.call(row[:refcolumn])
|
|
335
|
-
end
|
|
636
|
+
|
|
336
637
|
h.values
|
|
337
638
|
end
|
|
338
639
|
|
|
640
|
+
def freeze
|
|
641
|
+
server_version
|
|
642
|
+
supports_prepared_transactions?
|
|
643
|
+
@conversion_procs.freeze
|
|
644
|
+
super
|
|
645
|
+
end
|
|
646
|
+
|
|
339
647
|
# Use the pg_* system tables to determine indexes on a table
|
|
340
648
|
def indexes(table, opts=OPTS)
|
|
341
649
|
m = output_identifier_meth
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
select(:indc__relname___name, :ind__indisunique___unique, :att__attname___column, :con__condeferrable___deferrable)
|
|
650
|
+
oid = regclass_oid(table, opts)
|
|
651
|
+
|
|
652
|
+
if server_version >= 90500
|
|
653
|
+
order = [Sequel[:indc][:relname], Sequel.function(:array_position, Sequel[:ind][:indkey], Sequel[:att][:attnum])]
|
|
654
|
+
else
|
|
655
|
+
range = 0...32
|
|
656
|
+
order = [Sequel[:indc][:relname], SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(Sequel[:ind][:indkey], [x]), x]}, 32, Sequel[:att][:attnum])]
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
attnums = SQL::Function.new(:ANY, Sequel[:ind][:indkey])
|
|
353
660
|
|
|
354
|
-
ds
|
|
661
|
+
ds = metadata_dataset.
|
|
662
|
+
from{pg_class.as(:tab)}.
|
|
663
|
+
join(Sequel[:pg_index].as(:ind), :indrelid=>:oid).
|
|
664
|
+
join(Sequel[:pg_class].as(:indc), :oid=>:indexrelid).
|
|
665
|
+
join(Sequel[:pg_attribute].as(:att), :attrelid=>Sequel[:tab][:oid], :attnum=>attnums).
|
|
666
|
+
left_join(Sequel[:pg_constraint].as(:con), :conname=>Sequel[:indc][:relname]).
|
|
667
|
+
where{{
|
|
668
|
+
indc[:relkind]=>'i',
|
|
669
|
+
ind[:indisprimary]=>false,
|
|
670
|
+
:indexprs=>nil,
|
|
671
|
+
:indisvalid=>true,
|
|
672
|
+
tab[:oid]=>oid}}.
|
|
673
|
+
order(*order).
|
|
674
|
+
select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
|
|
675
|
+
|
|
676
|
+
ds = ds.where(:indpred=>nil) unless opts[:include_partial]
|
|
677
|
+
ds = ds.where(:indisready=>true) if server_version >= 80300
|
|
678
|
+
ds = ds.where(:indislive=>true) if server_version >= 90300
|
|
355
679
|
|
|
356
680
|
indexes = {}
|
|
357
681
|
ds.each do |r|
|
|
@@ -363,7 +687,7 @@ module Sequel
|
|
|
363
687
|
|
|
364
688
|
# Dataset containing all current database locks
|
|
365
689
|
def locks
|
|
366
|
-
dataset.from(:pg_class).join(:pg_locks, :relation=>:relfilenode).select
|
|
690
|
+
dataset.from(:pg_class).join(:pg_locks, :relation=>:relfilenode).select{[pg_class[:relname], Sequel::SQL::ColumnAll.new(:pg_locks)]}
|
|
367
691
|
end
|
|
368
692
|
|
|
369
693
|
# Notifies the given channel. See the PostgreSQL NOTIFY documentation. Options:
|
|
@@ -373,7 +697,8 @@ module Sequel
|
|
|
373
697
|
# :server :: The server to which to send the NOTIFY statement, if the sharding support
|
|
374
698
|
# is being used.
|
|
375
699
|
def notify(channel, opts=OPTS)
|
|
376
|
-
sql =
|
|
700
|
+
sql = String.new
|
|
701
|
+
sql << "NOTIFY "
|
|
377
702
|
dataset.send(:identifier_append, sql, channel)
|
|
378
703
|
if payload = opts[:payload]
|
|
379
704
|
sql << ", "
|
|
@@ -418,28 +743,28 @@ module Sequel
|
|
|
418
743
|
run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}"
|
|
419
744
|
end
|
|
420
745
|
|
|
421
|
-
# Reset the database's conversion procs, requires a server query if there
|
|
422
|
-
# any named types.
|
|
423
|
-
def reset_conversion_procs
|
|
424
|
-
@conversion_procs = get_conversion_procs
|
|
425
|
-
conversion_procs_updated
|
|
426
|
-
@conversion_procs
|
|
427
|
-
end
|
|
428
|
-
|
|
429
746
|
# Reset the primary key sequence for the given table, basing it on the
|
|
430
747
|
# maximum current value of the table's primary key.
|
|
431
748
|
def reset_primary_key_sequence(table)
|
|
432
749
|
return unless seq = primary_key_sequence(table)
|
|
433
750
|
pk = SQL::Identifier.new(primary_key(table))
|
|
434
751
|
db = self
|
|
435
|
-
seq_ds = db.from(LiteralString.new(seq))
|
|
436
752
|
s, t = schema_and_table(table)
|
|
437
753
|
table = Sequel.qualify(s, t) if s
|
|
438
|
-
|
|
754
|
+
|
|
755
|
+
if server_version >= 100000
|
|
756
|
+
seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq)))
|
|
757
|
+
increment_by = :seqincrement
|
|
758
|
+
min_value = :seqmin
|
|
759
|
+
else
|
|
760
|
+
seq_ds = metadata_dataset.from(LiteralString.new(seq))
|
|
761
|
+
increment_by = :increment_by
|
|
762
|
+
min_value = :min_value
|
|
763
|
+
end
|
|
764
|
+
|
|
765
|
+
get{setval(seq, db[table].select(coalesce(max(pk)+seq_ds.select(increment_by), seq_ds.select(min_value))), false)}
|
|
439
766
|
end
|
|
440
767
|
|
|
441
|
-
# Rollback an existing prepared transaction with the given transaction
|
|
442
|
-
# identifier string.
|
|
443
768
|
def rollback_prepared_transaction(transaction_id, opts=OPTS)
|
|
444
769
|
run("ROLLBACK PREPARED #{literal(transaction_id)}", opts)
|
|
445
770
|
end
|
|
@@ -447,24 +772,16 @@ module Sequel
|
|
|
447
772
|
# PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
|
|
448
773
|
# managing incrementing primary keys.
|
|
449
774
|
def serial_primary_key_options
|
|
450
|
-
|
|
775
|
+
auto_increment_key = server_version >= 100002 ? :identity : :serial
|
|
776
|
+
{:primary_key => true, auto_increment_key => true, :type=>Integer}
|
|
451
777
|
end
|
|
452
778
|
|
|
453
779
|
# The version of the PostgreSQL server, used for determining capability.
|
|
454
780
|
def server_version(server=nil)
|
|
455
781
|
return @server_version if @server_version
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
unless @server_version
|
|
460
|
-
@server_version = if m = /PostgreSQL (\d+)\.(\d+)(?:(?:rc\d+)|\.(\d+))?/.match(fetch('SELECT version()').single_value)
|
|
461
|
-
(m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
|
|
462
|
-
else
|
|
463
|
-
0
|
|
464
|
-
end
|
|
465
|
-
end
|
|
466
|
-
warn 'Sequel no longer supports PostgreSQL <8.2, some things may not work' if @server_version < 80200
|
|
467
|
-
@server_version
|
|
782
|
+
ds = dataset
|
|
783
|
+
ds = ds.server(server) if server
|
|
784
|
+
@server_version ||= ds.with_sql("SELECT CAST(current_setting('server_version_num') AS integer) AS v").single_value rescue 0
|
|
468
785
|
end
|
|
469
786
|
|
|
470
787
|
# PostgreSQL supports CREATE TABLE IF NOT EXISTS on 9.1+
|
|
@@ -535,17 +852,18 @@ module Sequel
|
|
|
535
852
|
# Check whether the given type name string/symbol (e.g. :hstore) is supported by
|
|
536
853
|
# the database.
|
|
537
854
|
def type_supported?(type)
|
|
538
|
-
@supported_types
|
|
539
|
-
|
|
855
|
+
Sequel.synchronize{return @supported_types[type] if @supported_types.has_key?(type)}
|
|
856
|
+
supported = from(:pg_type).where(:typtype=>'b', :typname=>type.to_s).count > 0
|
|
857
|
+
Sequel.synchronize{return @supported_types[type] = supported}
|
|
540
858
|
end
|
|
541
859
|
|
|
542
860
|
# Creates a dataset that uses the VALUES clause:
|
|
543
861
|
#
|
|
544
862
|
# DB.values([[1, 2], [3, 4]])
|
|
545
|
-
# VALUES ((1, 2), (3, 4))
|
|
863
|
+
# # VALUES ((1, 2), (3, 4))
|
|
546
864
|
#
|
|
547
865
|
# DB.values([[1, 2], [3, 4]]).order(:column2).limit(1, 1)
|
|
548
|
-
# VALUES ((1, 2), (3, 4)) ORDER BY column2 LIMIT 1 OFFSET 1
|
|
866
|
+
# # VALUES ((1, 2), (3, 4)) ORDER BY column2 LIMIT 1 OFFSET 1
|
|
549
867
|
def values(v)
|
|
550
868
|
@default_dataset.clone(:values=>v)
|
|
551
869
|
end
|
|
@@ -553,28 +871,22 @@ module Sequel
|
|
|
553
871
|
# Array of symbols specifying view names in the current database.
|
|
554
872
|
#
|
|
555
873
|
# Options:
|
|
874
|
+
# :materialized :: Return materialized views
|
|
556
875
|
# :qualify :: Return the views as Sequel::SQL::QualifiedIdentifier instances,
|
|
557
876
|
# using the schema the view is located in as the qualifier.
|
|
558
877
|
# :schema :: The schema to search
|
|
559
878
|
# :server :: The server to use
|
|
560
879
|
def views(opts=OPTS)
|
|
561
|
-
|
|
880
|
+
relkind = opts[:materialized] ? 'm' : 'v'
|
|
881
|
+
pg_class_relname(relkind, opts)
|
|
562
882
|
end
|
|
563
883
|
|
|
564
884
|
private
|
|
565
885
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
def add_named_conversion_procs(procs, named_procs)
|
|
569
|
-
unless (named_procs).empty?
|
|
570
|
-
convert_named_procs_to_procs(named_procs).each do |oid, pr|
|
|
571
|
-
procs[oid] ||= pr
|
|
572
|
-
end
|
|
573
|
-
conversion_procs_updated
|
|
574
|
-
end
|
|
886
|
+
def alter_table_add_column_sql(table, op)
|
|
887
|
+
"ADD COLUMN#{' IF NOT EXISTS' if op[:if_not_exists]} #{column_definition_sql(op)}"
|
|
575
888
|
end
|
|
576
889
|
|
|
577
|
-
# Use a PostgreSQL-specific alter table generator
|
|
578
890
|
def alter_table_generator_class
|
|
579
891
|
Postgres::AlterTableGenerator
|
|
580
892
|
end
|
|
@@ -583,7 +895,7 @@ module Sequel
|
|
|
583
895
|
s = super
|
|
584
896
|
if using = op[:using]
|
|
585
897
|
using = Sequel::LiteralString.new(using) if using.is_a?(String)
|
|
586
|
-
s
|
|
898
|
+
s += ' USING '
|
|
587
899
|
s << literal(using)
|
|
588
900
|
end
|
|
589
901
|
s
|
|
@@ -634,9 +946,24 @@ module Sequel
|
|
|
634
946
|
end
|
|
635
947
|
end
|
|
636
948
|
|
|
949
|
+
# Support identity columns, but only use the identity SQL syntax if no
|
|
950
|
+
# default value is given.
|
|
951
|
+
def column_definition_default_sql(sql, column)
|
|
952
|
+
super
|
|
953
|
+
if !column[:serial] && !['smallserial', 'serial', 'bigserial'].include?(column[:type].to_s) && !column[:default]
|
|
954
|
+
if (identity = column[:identity])
|
|
955
|
+
sql << " GENERATED "
|
|
956
|
+
sql << (identity == :always ? "ALWAYS" : "BY DEFAULT")
|
|
957
|
+
sql << " AS IDENTITY"
|
|
958
|
+
elsif (generated = column[:generated_always_as])
|
|
959
|
+
sql << " GENERATED ALWAYS AS (#{literal(generated)}) STORED"
|
|
960
|
+
end
|
|
961
|
+
end
|
|
962
|
+
end
|
|
963
|
+
|
|
637
964
|
# Handle PostgreSQL specific default format.
|
|
638
965
|
def column_schema_normalize_default(default, type)
|
|
639
|
-
if m =
|
|
966
|
+
if m = /\A(?:B?('.*')::[^']+|\((-?\d+(?:\.\d+)?)\))\z/.match(default)
|
|
640
967
|
default = m[1] || m[2]
|
|
641
968
|
end
|
|
642
969
|
super(default, type)
|
|
@@ -658,14 +985,15 @@ module Sequel
|
|
|
658
985
|
(super || op[:op] == :validate_constraint) && op[:op] != :rename_column
|
|
659
986
|
end
|
|
660
987
|
|
|
661
|
-
VALID_CLIENT_MIN_MESSAGES = %w'DEBUG5 DEBUG4 DEBUG3 DEBUG2 DEBUG1 LOG NOTICE WARNING ERROR FATAL PANIC'.freeze
|
|
988
|
+
VALID_CLIENT_MIN_MESSAGES = %w'DEBUG5 DEBUG4 DEBUG3 DEBUG2 DEBUG1 LOG NOTICE WARNING ERROR FATAL PANIC'.freeze.each(&:freeze)
|
|
662
989
|
# The SQL queries to execute when starting a new connection.
|
|
663
|
-
def connection_configuration_sqls
|
|
990
|
+
def connection_configuration_sqls(opts=@opts)
|
|
664
991
|
sqls = []
|
|
665
992
|
|
|
666
|
-
sqls << "SET standard_conforming_strings = ON" if typecast_value_boolean(
|
|
993
|
+
sqls << "SET standard_conforming_strings = ON" if typecast_value_boolean(opts.fetch(:force_standard_strings, true))
|
|
667
994
|
|
|
668
|
-
|
|
995
|
+
cmm = opts.fetch(:client_min_messages, :warning)
|
|
996
|
+
if cmm && !cmm.to_s.empty?
|
|
669
997
|
cmm = cmm.to_s.upcase.strip
|
|
670
998
|
unless VALID_CLIENT_MIN_MESSAGES.include?(cmm)
|
|
671
999
|
raise Error, "Unsupported client_min_messages setting: #{cmm}"
|
|
@@ -673,7 +1001,7 @@ module Sequel
|
|
|
673
1001
|
sqls << "SET client_min_messages = '#{cmm.to_s.upcase}'"
|
|
674
1002
|
end
|
|
675
1003
|
|
|
676
|
-
if search_path =
|
|
1004
|
+
if search_path = opts[:search_path]
|
|
677
1005
|
case search_path
|
|
678
1006
|
when String
|
|
679
1007
|
search_path = search_path.split(",").map(&:strip)
|
|
@@ -693,7 +1021,8 @@ module Sequel
|
|
|
693
1021
|
case constraint[:type]
|
|
694
1022
|
when :exclude
|
|
695
1023
|
elements = constraint[:elements].map{|c, op| "#{literal(c)} WITH #{op}"}.join(', ')
|
|
696
|
-
sql =
|
|
1024
|
+
sql = String.new
|
|
1025
|
+
sql << "#{"CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]}EXCLUDE USING #{constraint[:using]||'gist'} (#{elements})#{" WHERE #{filter_expr(constraint[:where])}" if constraint[:where]}"
|
|
697
1026
|
constraint_deferrable_sql_append(sql, constraint[:deferrable])
|
|
698
1027
|
sql
|
|
699
1028
|
when :foreign_key, :check
|
|
@@ -707,37 +1036,13 @@ module Sequel
|
|
|
707
1036
|
end
|
|
708
1037
|
end
|
|
709
1038
|
|
|
710
|
-
# Callback used when conversion procs are updated.
|
|
711
|
-
def conversion_procs_updated
|
|
712
|
-
nil
|
|
713
|
-
end
|
|
714
|
-
|
|
715
|
-
# Convert the hash of named conversion procs into a hash a oid conversion procs.
|
|
716
|
-
def convert_named_procs_to_procs(named_procs)
|
|
717
|
-
h = {}
|
|
718
|
-
from(:pg_type).where(:typtype=>['b', 'e'], :typname=>named_procs.keys.map(&:to_s)).select_map([:oid, :typname]).each do |oid, name|
|
|
719
|
-
h[oid.to_i] = named_procs[name.untaint.to_sym]
|
|
720
|
-
end
|
|
721
|
-
h
|
|
722
|
-
end
|
|
723
|
-
|
|
724
|
-
# Copy the conversion procs related to the given oids from PG_TYPES into
|
|
725
|
-
# the conversion procs for this instance.
|
|
726
|
-
def copy_conversion_procs(oids)
|
|
727
|
-
procs = conversion_procs
|
|
728
|
-
oids.each do |oid|
|
|
729
|
-
procs[oid] = PG_TYPES[oid]
|
|
730
|
-
end
|
|
731
|
-
conversion_procs_updated
|
|
732
|
-
end
|
|
733
|
-
|
|
734
|
-
EXCLUSION_CONSTRAINT_SQL_STATE = '23P01'.freeze
|
|
735
|
-
DEADLOCK_SQL_STATE = '40P01'.freeze
|
|
736
1039
|
def database_specific_error_class_from_sqlstate(sqlstate)
|
|
737
|
-
if sqlstate ==
|
|
1040
|
+
if sqlstate == '23P01'
|
|
738
1041
|
ExclusionConstraintViolation
|
|
739
|
-
elsif sqlstate ==
|
|
1042
|
+
elsif sqlstate == '40P01'
|
|
740
1043
|
SerializationFailure
|
|
1044
|
+
elsif sqlstate == '55P03'
|
|
1045
|
+
DatabaseLockTimeout
|
|
741
1046
|
else
|
|
742
1047
|
super
|
|
743
1048
|
end
|
|
@@ -753,6 +1058,7 @@ module Sequel
|
|
|
753
1058
|
[/violates not-null constraint/, NotNullConstraintViolation],
|
|
754
1059
|
[/conflicting key value violates exclusion constraint/, ExclusionConstraintViolation],
|
|
755
1060
|
[/could not serialize access/, SerializationFailure],
|
|
1061
|
+
[/could not obtain lock on row in relation/, DatabaseLockTimeout],
|
|
756
1062
|
].freeze
|
|
757
1063
|
def database_error_regexps
|
|
758
1064
|
DATABASE_ERROR_REGEXPS
|
|
@@ -760,7 +1066,8 @@ module Sequel
|
|
|
760
1066
|
|
|
761
1067
|
# SQL for doing fast table insert from stdin.
|
|
762
1068
|
def copy_into_sql(table, opts)
|
|
763
|
-
sql =
|
|
1069
|
+
sql = String.new
|
|
1070
|
+
sql << "COPY #{literal(table)}"
|
|
764
1071
|
if cols = opts[:columns]
|
|
765
1072
|
sql << literal(Array(cols))
|
|
766
1073
|
end
|
|
@@ -780,7 +1087,8 @@ module Sequel
|
|
|
780
1087
|
table
|
|
781
1088
|
else
|
|
782
1089
|
if opts[:options] || opts[:format]
|
|
783
|
-
options =
|
|
1090
|
+
options = String.new
|
|
1091
|
+
options << " ("
|
|
784
1092
|
options << "FORMAT #{opts[:format]}" if opts[:format]
|
|
785
1093
|
options << "#{', ' if opts[:format]}#{opts[:options]}" if opts[:options]
|
|
786
1094
|
options << ')'
|
|
@@ -820,6 +1128,36 @@ module Sequel
|
|
|
820
1128
|
"CREATE#{' OR REPLACE' if opts[:replace] && server_version >= 90000}#{' TRUSTED' if opts[:trusted]} LANGUAGE #{name}#{" HANDLER #{opts[:handler]}" if opts[:handler]}#{" VALIDATOR #{opts[:validator]}" if opts[:validator]}"
|
|
821
1129
|
end
|
|
822
1130
|
|
|
1131
|
+
# Create a partition of another table, used when the create_table with
|
|
1132
|
+
# the :partition_of option is given.
|
|
1133
|
+
def create_partition_of_table_from_generator(name, generator, options)
|
|
1134
|
+
execute_ddl(create_partition_of_table_sql(name, generator, options))
|
|
1135
|
+
end
|
|
1136
|
+
|
|
1137
|
+
# SQL for creating a partition of another table.
|
|
1138
|
+
def create_partition_of_table_sql(name, generator, options)
|
|
1139
|
+
sql = create_table_prefix_sql(name, options).dup
|
|
1140
|
+
|
|
1141
|
+
sql << " PARTITION OF #{quote_schema_table(options[:partition_of])}"
|
|
1142
|
+
|
|
1143
|
+
case generator.partition_type
|
|
1144
|
+
when :range
|
|
1145
|
+
from, to = generator.range
|
|
1146
|
+
sql << " FOR VALUES FROM #{literal(from)} TO #{literal(to)}"
|
|
1147
|
+
when :list
|
|
1148
|
+
sql << " FOR VALUES IN #{literal(generator.list)}"
|
|
1149
|
+
when :hash
|
|
1150
|
+
mod, remainder = generator.hash_values
|
|
1151
|
+
sql << " FOR VALUES WITH (MODULUS #{literal(mod)}, REMAINDER #{literal(remainder)})"
|
|
1152
|
+
when :default
|
|
1153
|
+
sql << " DEFAULT"
|
|
1154
|
+
end
|
|
1155
|
+
|
|
1156
|
+
sql << create_table_suffix_sql(name, options)
|
|
1157
|
+
|
|
1158
|
+
sql
|
|
1159
|
+
end
|
|
1160
|
+
|
|
823
1161
|
# SQL for creating a schema.
|
|
824
1162
|
def create_schema_sql(name, opts=OPTS)
|
|
825
1163
|
"CREATE SCHEMA #{'IF NOT EXISTS ' if opts[:if_not_exists]}#{quote_identifier(name)}#{" AUTHORIZATION #{literal(opts[:owner])}" if opts[:owner]}"
|
|
@@ -835,25 +1173,40 @@ module Sequel
|
|
|
835
1173
|
raise(Error, "can't provide both :foreign and :unlogged to create_table") if options[:unlogged]
|
|
836
1174
|
'FOREIGN '
|
|
837
1175
|
elsif options[:unlogged]
|
|
838
|
-
UNLOGGED
|
|
1176
|
+
'UNLOGGED '
|
|
839
1177
|
end
|
|
840
1178
|
|
|
841
1179
|
"CREATE #{prefix_sql}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{options[:temp] ? quote_identifier(name) : quote_schema_table(name)}"
|
|
842
1180
|
end
|
|
843
1181
|
|
|
1182
|
+
# SQL for creating a table with PostgreSQL specific options
|
|
844
1183
|
def create_table_sql(name, generator, options)
|
|
845
|
-
|
|
1184
|
+
"#{super}#{create_table_suffix_sql(name, options)}"
|
|
1185
|
+
end
|
|
1186
|
+
|
|
1187
|
+
# Handle various PostgreSQl specific table extensions such as inheritance,
|
|
1188
|
+
# partitioning, tablespaces, and foreign tables.
|
|
1189
|
+
def create_table_suffix_sql(name, options)
|
|
1190
|
+
sql = String.new
|
|
846
1191
|
|
|
847
1192
|
if inherits = options[:inherits]
|
|
848
1193
|
sql << " INHERITS (#{Array(inherits).map{|t| quote_schema_table(t)}.join(', ')})"
|
|
849
1194
|
end
|
|
850
1195
|
|
|
1196
|
+
if partition_by = options[:partition_by]
|
|
1197
|
+
sql << " PARTITION BY #{options[:partition_type]||'RANGE'} #{literal(Array(partition_by))}"
|
|
1198
|
+
end
|
|
1199
|
+
|
|
851
1200
|
if on_commit = options[:on_commit]
|
|
852
1201
|
raise(Error, "can't provide :on_commit without :temp to create_table") unless options[:temp]
|
|
853
1202
|
raise(Error, "unsupported on_commit option: #{on_commit.inspect}") unless ON_COMMIT.has_key?(on_commit)
|
|
854
1203
|
sql << " ON COMMIT #{ON_COMMIT[on_commit]}"
|
|
855
1204
|
end
|
|
856
1205
|
|
|
1206
|
+
if tablespace = options[:tablespace]
|
|
1207
|
+
sql << " TABLESPACE #{quote_identifier(tablespace)}"
|
|
1208
|
+
end
|
|
1209
|
+
|
|
857
1210
|
if server = options[:foreign]
|
|
858
1211
|
sql << " SERVER #{quote_identifier(server)}"
|
|
859
1212
|
if foreign_opts = options[:options]
|
|
@@ -867,12 +1220,11 @@ module Sequel
|
|
|
867
1220
|
def create_table_as_sql(name, sql, options)
|
|
868
1221
|
result = create_table_prefix_sql name, options
|
|
869
1222
|
if on_commit = options[:on_commit]
|
|
870
|
-
result
|
|
1223
|
+
result += " ON COMMIT #{ON_COMMIT[on_commit]}"
|
|
871
1224
|
end
|
|
872
|
-
result
|
|
1225
|
+
result += " AS #{sql}"
|
|
873
1226
|
end
|
|
874
1227
|
|
|
875
|
-
# Use a PostgreSQL-specific create table generator
|
|
876
1228
|
def create_table_generator_class
|
|
877
1229
|
Postgres::CreateTableGenerator
|
|
878
1230
|
end
|
|
@@ -890,12 +1242,13 @@ module Sequel
|
|
|
890
1242
|
|
|
891
1243
|
# DDL fragment for initial part of CREATE VIEW statement
|
|
892
1244
|
def create_view_prefix_sql(name, options)
|
|
893
|
-
create_view_sql_append_columns("CREATE #{'OR REPLACE 'if options[:replace]}#{'TEMPORARY 'if options[:temp]}#{'RECURSIVE ' if options[:recursive]}#{'MATERIALIZED ' if options[:materialized]}VIEW #{quote_schema_table(name)}", options[:columns] || options[:recursive])
|
|
894
|
-
|
|
1245
|
+
sql = create_view_sql_append_columns("CREATE #{'OR REPLACE 'if options[:replace]}#{'TEMPORARY 'if options[:temp]}#{'RECURSIVE ' if options[:recursive]}#{'MATERIALIZED ' if options[:materialized]}VIEW #{quote_schema_table(name)}", options[:columns] || options[:recursive])
|
|
1246
|
+
|
|
1247
|
+
if tablespace = options[:tablespace]
|
|
1248
|
+
sql += " TABLESPACE #{quote_identifier(tablespace)}"
|
|
1249
|
+
end
|
|
895
1250
|
|
|
896
|
-
|
|
897
|
-
def database_error_classes
|
|
898
|
-
CONVERTED_EXCEPTIONS
|
|
1251
|
+
sql
|
|
899
1252
|
end
|
|
900
1253
|
|
|
901
1254
|
# SQL for dropping a function from the database.
|
|
@@ -934,36 +1287,17 @@ module Sequel
|
|
|
934
1287
|
"DROP #{'MATERIALIZED ' if opts[:materialized]}VIEW#{' IF EXISTS' if opts[:if_exists]} #{quote_schema_table(name)}#{' CASCADE' if opts[:cascade]}"
|
|
935
1288
|
end
|
|
936
1289
|
|
|
937
|
-
# If opts includes a :schema option,
|
|
938
|
-
#
|
|
1290
|
+
# If opts includes a :schema option, use it, otherwise restrict the filter to only the
|
|
1291
|
+
# currently visible schemas.
|
|
939
1292
|
def filter_schema(ds, opts)
|
|
940
1293
|
expr = if schema = opts[:schema]
|
|
941
1294
|
schema.to_s
|
|
942
1295
|
else
|
|
943
1296
|
Sequel.function(:any, Sequel.function(:current_schemas, false))
|
|
944
1297
|
end
|
|
945
|
-
ds.where
|
|
946
|
-
end
|
|
947
|
-
|
|
948
|
-
# Return a hash with oid keys and callable values, used for converting types.
|
|
949
|
-
def get_conversion_procs
|
|
950
|
-
procs = PG_TYPES.dup
|
|
951
|
-
procs[1184] = procs[1114] = method(:to_application_timestamp)
|
|
952
|
-
add_named_conversion_procs(procs, PG_NAMED_TYPES)
|
|
953
|
-
procs
|
|
1298
|
+
ds.where{{pg_namespace[:nspname]=>expr}}
|
|
954
1299
|
end
|
|
955
1300
|
|
|
956
|
-
# PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
|
|
957
|
-
def identifier_input_method_default
|
|
958
|
-
nil
|
|
959
|
-
end
|
|
960
|
-
|
|
961
|
-
# PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on output.
|
|
962
|
-
def identifier_output_method_default
|
|
963
|
-
nil
|
|
964
|
-
end
|
|
965
|
-
|
|
966
|
-
# PostgreSQL specific index SQL.
|
|
967
1301
|
def index_definition_sql(table_name, index)
|
|
968
1302
|
cols = index[:columns]
|
|
969
1303
|
index_name = index[:name] || default_index_name(table_name, cols)
|
|
@@ -972,6 +1306,7 @@ module Sequel
|
|
|
972
1306
|
else
|
|
973
1307
|
literal(Array(cols))
|
|
974
1308
|
end
|
|
1309
|
+
if_not_exists = " IF NOT EXISTS" if index[:if_not_exists]
|
|
975
1310
|
unique = "UNIQUE " if index[:unique]
|
|
976
1311
|
index_type = index[:type]
|
|
977
1312
|
filter = index[:where] || index[:filter]
|
|
@@ -983,33 +1318,33 @@ module Sequel
|
|
|
983
1318
|
when :spatial
|
|
984
1319
|
index_type = :gist
|
|
985
1320
|
end
|
|
986
|
-
"CREATE #{unique}INDEX#{' CONCURRENTLY' if index[:concurrently]} #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{filter}"
|
|
1321
|
+
"CREATE #{unique}INDEX#{' CONCURRENTLY' if index[:concurrently]}#{if_not_exists} #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{" INCLUDE #{literal(Array(index[:include]))}" if index[:include]}#{" TABLESPACE #{quote_identifier(index[:tablespace])}" if index[:tablespace]}#{filter}"
|
|
987
1322
|
end
|
|
988
1323
|
|
|
989
1324
|
# Setup datastructures shared by all postgres adapters.
|
|
990
1325
|
def initialize_postgres_adapter
|
|
991
1326
|
@primary_keys = {}
|
|
992
1327
|
@primary_key_sequences = {}
|
|
993
|
-
@
|
|
994
|
-
|
|
1328
|
+
@supported_types = {}
|
|
1329
|
+
procs = @conversion_procs = CONVERSION_PROCS.dup
|
|
1330
|
+
procs[1184] = procs[1114] = method(:to_application_timestamp)
|
|
995
1331
|
end
|
|
996
1332
|
|
|
997
1333
|
# Backbone of the tables and views support.
|
|
998
1334
|
def pg_class_relname(type, opts)
|
|
999
|
-
ds = metadata_dataset.from(:pg_class).
|
|
1335
|
+
ds = metadata_dataset.from(:pg_class).where(:relkind=>type).select(:relname).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
|
|
1000
1336
|
ds = filter_schema(ds, opts)
|
|
1001
1337
|
m = output_identifier_meth
|
|
1002
1338
|
if block_given?
|
|
1003
1339
|
yield(ds)
|
|
1004
1340
|
elsif opts[:qualify]
|
|
1005
|
-
ds.select_append
|
|
1341
|
+
ds.select_append{pg_namespace[:nspname]}.map{|r| Sequel.qualify(m.call(r[:nspname]).to_s, m.call(r[:relname]).to_s)}
|
|
1006
1342
|
else
|
|
1007
1343
|
ds.map{|r| m.call(r[:relname])}
|
|
1008
1344
|
end
|
|
1009
1345
|
end
|
|
1010
1346
|
|
|
1011
|
-
# Use a dollar sign instead of question mark for the argument
|
|
1012
|
-
# placeholder.
|
|
1347
|
+
# Use a dollar sign instead of question mark for the argument placeholder.
|
|
1013
1348
|
def prepared_arg_placeholder
|
|
1014
1349
|
PREPARED_ARG_PLACEHOLDER
|
|
1015
1350
|
end
|
|
@@ -1036,8 +1371,7 @@ module Sequel
|
|
|
1036
1371
|
Sequel.cast(expr.to_s,:regclass).cast(:oid)
|
|
1037
1372
|
end
|
|
1038
1373
|
|
|
1039
|
-
# Remove the cached entries for primary keys and sequences when a table is
|
|
1040
|
-
# changed.
|
|
1374
|
+
# Remove the cached entries for primary keys and sequences when a table is changed.
|
|
1041
1375
|
def remove_cached_schema(table)
|
|
1042
1376
|
tab = quote_schema_table(table)
|
|
1043
1377
|
Sequel.synchronize do
|
|
@@ -1053,7 +1387,6 @@ module Sequel
|
|
|
1053
1387
|
"ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
|
|
1054
1388
|
end
|
|
1055
1389
|
|
|
1056
|
-
# Recognize PostgreSQL interval type.
|
|
1057
1390
|
def schema_column_type(db_type)
|
|
1058
1391
|
case db_type
|
|
1059
1392
|
when /\Ainterval\z/io
|
|
@@ -1068,24 +1401,35 @@ module Sequel
|
|
|
1068
1401
|
# The dataset used for parsing table schemas, using the pg_* system catalogs.
|
|
1069
1402
|
def schema_parse_table(table_name, opts)
|
|
1070
1403
|
m = output_identifier_meth(opts[:dataset])
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
SQL::
|
|
1075
|
-
SQL::
|
|
1076
|
-
SQL::Function.new(:
|
|
1077
|
-
SQL::
|
|
1078
|
-
SQL::Function.new(:
|
|
1404
|
+
oid = regclass_oid(table_name, opts)
|
|
1405
|
+
ds = metadata_dataset.select{[
|
|
1406
|
+
pg_attribute[:attname].as(:name),
|
|
1407
|
+
SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
|
|
1408
|
+
SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
|
|
1409
|
+
SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
|
|
1410
|
+
SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
|
|
1411
|
+
SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
|
|
1412
|
+
SQL::BooleanExpression.new(:NOT, pg_attribute[:attnotnull]).as(:allow_null),
|
|
1413
|
+
SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(pg_attribute[:attnum] => SQL::Function.new(:ANY, pg_index[:indkey])), false).as(:primary_key)]}.
|
|
1079
1414
|
from(:pg_class).
|
|
1080
1415
|
join(:pg_attribute, :attrelid=>:oid).
|
|
1081
1416
|
join(:pg_type, :oid=>:atttypid).
|
|
1082
|
-
left_outer_join(:
|
|
1083
|
-
left_outer_join(:pg_attrdef, :adrelid
|
|
1084
|
-
left_outer_join(:pg_index, :indrelid
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
order
|
|
1417
|
+
left_outer_join(Sequel[:pg_type].as(:basetype), :oid=>:typbasetype).
|
|
1418
|
+
left_outer_join(:pg_attrdef, :adrelid=>Sequel[:pg_class][:oid], :adnum=>Sequel[:pg_attribute][:attnum]).
|
|
1419
|
+
left_outer_join(:pg_index, :indrelid=>Sequel[:pg_class][:oid], :indisprimary=>true).
|
|
1420
|
+
where{{pg_attribute[:attisdropped]=>false}}.
|
|
1421
|
+
where{pg_attribute[:attnum] > 0}.
|
|
1422
|
+
where{{pg_class[:oid]=>oid}}.
|
|
1423
|
+
order{pg_attribute[:attnum]}
|
|
1424
|
+
|
|
1425
|
+
if server_version > 100000
|
|
1426
|
+
ds = ds.select_append{pg_attribute[:attidentity]}
|
|
1427
|
+
|
|
1428
|
+
if server_version > 120000
|
|
1429
|
+
ds = ds.select_append{Sequel.~(pg_attribute[:attgenerated]=>'').as(:generated)}
|
|
1430
|
+
end
|
|
1431
|
+
end
|
|
1432
|
+
|
|
1089
1433
|
ds.map do |row|
|
|
1090
1434
|
row[:default] = nil if blank_object?(row[:default])
|
|
1091
1435
|
if row[:base_oid]
|
|
@@ -1098,8 +1442,9 @@ module Sequel
|
|
|
1098
1442
|
row.delete(:db_base_type)
|
|
1099
1443
|
end
|
|
1100
1444
|
row[:type] = schema_column_type(row[:db_type])
|
|
1445
|
+
identity = row.delete(:attidentity)
|
|
1101
1446
|
if row[:primary_key]
|
|
1102
|
-
row[:auto_increment] = !!(row[:default] =~ /\
|
|
1447
|
+
row[:auto_increment] = !!(row[:default] =~ /\A(?:nextval)/i) || identity == 'a' || identity == 'd'
|
|
1103
1448
|
end
|
|
1104
1449
|
[m.call(row.delete(:name)), row]
|
|
1105
1450
|
end
|
|
@@ -1111,7 +1456,8 @@ module Sequel
|
|
|
1111
1456
|
read_only = opts[:read_only]
|
|
1112
1457
|
deferrable = opts[:deferrable]
|
|
1113
1458
|
if level || !read_only.nil? || !deferrable.nil?
|
|
1114
|
-
sql =
|
|
1459
|
+
sql = String.new
|
|
1460
|
+
sql << "SET TRANSACTION"
|
|
1115
1461
|
sql << " ISOLATION LEVEL #{Sequel::Database::TRANSACTION_ISOLATION_LEVELS[level]}" if level
|
|
1116
1462
|
sql << " READ #{read_only ? 'ONLY' : 'WRITE'}" unless read_only.nil?
|
|
1117
1463
|
sql << " #{'NOT ' unless deferrable}DEFERRABLE" unless deferrable.nil?
|
|
@@ -1135,7 +1481,7 @@ module Sequel
|
|
|
1135
1481
|
end
|
|
1136
1482
|
|
|
1137
1483
|
# Handle bigserial type if :serial option is present
|
|
1138
|
-
def
|
|
1484
|
+
def type_literal_generic_bignum_symbol(column)
|
|
1139
1485
|
column[:serial] ? :bigserial : super
|
|
1140
1486
|
end
|
|
1141
1487
|
|
|
@@ -1152,7 +1498,7 @@ module Sequel
|
|
|
1152
1498
|
# PostgreSQL prefers the text datatype. If a fixed size is requested,
|
|
1153
1499
|
# the char type is used. If the text type is specifically
|
|
1154
1500
|
# disallowed or there is a size specified, use the varchar type.
|
|
1155
|
-
# Otherwise use the
|
|
1501
|
+
# Otherwise use the text type.
|
|
1156
1502
|
def type_literal_generic_string(column)
|
|
1157
1503
|
if column[:fixed]
|
|
1158
1504
|
"char(#{column[:size]||255})"
|
|
@@ -1169,72 +1515,17 @@ module Sequel
|
|
|
1169
1515
|
end
|
|
1170
1516
|
end
|
|
1171
1517
|
|
|
1172
|
-
# Instance methods for datasets that connect to a PostgreSQL database.
|
|
1173
1518
|
module DatasetMethods
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
BOOL_FALSE = 'false'.freeze
|
|
1177
|
-
BOOL_TRUE = 'true'.freeze
|
|
1178
|
-
COMMA_SEPARATOR = ', '.freeze
|
|
1179
|
-
EXCLUSIVE = 'EXCLUSIVE'.freeze
|
|
1180
|
-
EXPLAIN = 'EXPLAIN '.freeze
|
|
1181
|
-
EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
|
|
1182
|
-
FOR_SHARE = ' FOR SHARE'.freeze
|
|
1519
|
+
include UnmodifiedIdentifiers::DatasetMethods
|
|
1520
|
+
|
|
1183
1521
|
NULL = LiteralString.new('NULL').freeze
|
|
1184
|
-
|
|
1185
|
-
QUERY_PLAN = 'QUERY PLAN'.to_sym
|
|
1186
|
-
ROW_EXCLUSIVE = 'ROW EXCLUSIVE'.freeze
|
|
1187
|
-
ROW_SHARE = 'ROW SHARE'.freeze
|
|
1188
|
-
SHARE = 'SHARE'.freeze
|
|
1189
|
-
SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
|
|
1190
|
-
SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
|
|
1191
|
-
SQL_WITH_RECURSIVE = "WITH RECURSIVE ".freeze
|
|
1192
|
-
SPACE = Dataset::SPACE
|
|
1193
|
-
FROM = Dataset::FROM
|
|
1194
|
-
APOS = Dataset::APOS
|
|
1195
|
-
APOS_RE = Dataset::APOS_RE
|
|
1196
|
-
DOUBLE_APOS = Dataset::DOUBLE_APOS
|
|
1197
|
-
PAREN_OPEN = Dataset::PAREN_OPEN
|
|
1198
|
-
PAREN_CLOSE = Dataset::PAREN_CLOSE
|
|
1199
|
-
COMMA = Dataset::COMMA
|
|
1200
|
-
ESCAPE = Dataset::ESCAPE
|
|
1201
|
-
BACKSLASH = Dataset::BACKSLASH
|
|
1202
|
-
AS = Dataset::AS
|
|
1203
|
-
XOR_OP = ' # '.freeze
|
|
1204
|
-
CRLF = "\r\n".freeze
|
|
1205
|
-
BLOB_RE = /[\000-\037\047\134\177-\377]/n.freeze
|
|
1206
|
-
WINDOW = " WINDOW ".freeze
|
|
1207
|
-
SELECT_VALUES = "VALUES ".freeze
|
|
1208
|
-
EMPTY_STRING = ''.freeze
|
|
1209
|
-
LOCK_MODES = ['ACCESS SHARE', 'ROW SHARE', 'ROW EXCLUSIVE', 'SHARE UPDATE EXCLUSIVE', 'SHARE', 'SHARE ROW EXCLUSIVE', 'EXCLUSIVE', 'ACCESS EXCLUSIVE'].each(&:freeze)
|
|
1522
|
+
LOCK_MODES = ['ACCESS SHARE', 'ROW SHARE', 'ROW EXCLUSIVE', 'SHARE UPDATE EXCLUSIVE', 'SHARE', 'SHARE ROW EXCLUSIVE', 'EXCLUSIVE', 'ACCESS EXCLUSIVE'].each(&:freeze).freeze
|
|
1210
1523
|
|
|
1211
1524
|
Dataset.def_sql_method(self, :delete, [['if server_version >= 90100', %w'with delete from using where returning'], ['else', %w'delete from using where returning']])
|
|
1212
1525
|
Dataset.def_sql_method(self, :insert, [['if server_version >= 90500', %w'with insert into columns values conflict returning'], ['elsif server_version >= 90100', %w'with insert into columns values returning'], ['else', %w'insert into columns values returning']])
|
|
1213
1526
|
Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'values order limit'], ['elsif server_version >= 80400', %w'with select distinct columns from join where group having window compounds order limit lock'], ['else', %w'select distinct columns from join where group having compounds order limit lock']])
|
|
1214
1527
|
Dataset.def_sql_method(self, :update, [['if server_version >= 90100', %w'with update table set from where returning'], ['else', %w'update table set from where returning']])
|
|
1215
1528
|
|
|
1216
|
-
# Shared methods for prepared statements when used with PostgreSQL databases.
|
|
1217
|
-
module PreparedStatementMethods
|
|
1218
|
-
# Override insert action to use RETURNING if the server supports it.
|
|
1219
|
-
def run
|
|
1220
|
-
if @prepared_type == :insert && (opts[:returning_pk] || !opts[:returning])
|
|
1221
|
-
fetch_rows(prepared_sql){|r| return r.values.first}
|
|
1222
|
-
else
|
|
1223
|
-
super
|
|
1224
|
-
end
|
|
1225
|
-
end
|
|
1226
|
-
|
|
1227
|
-
def prepared_sql
|
|
1228
|
-
return @prepared_sql if @prepared_sql
|
|
1229
|
-
if @prepared_type == :insert && !opts[:returning]
|
|
1230
|
-
@opts[:returning] = insert_pk
|
|
1231
|
-
@opts[:returning_pk] = true
|
|
1232
|
-
end
|
|
1233
|
-
super
|
|
1234
|
-
@prepared_sql
|
|
1235
|
-
end
|
|
1236
|
-
end
|
|
1237
|
-
|
|
1238
1529
|
# Return the results of an EXPLAIN ANALYZE query as a string
|
|
1239
1530
|
def analyze
|
|
1240
1531
|
explain(:analyze=>true)
|
|
@@ -1246,7 +1537,7 @@ module Sequel
|
|
|
1246
1537
|
def complex_expression_sql_append(sql, op, args)
|
|
1247
1538
|
case op
|
|
1248
1539
|
when :^
|
|
1249
|
-
j =
|
|
1540
|
+
j = ' # '
|
|
1250
1541
|
c = false
|
|
1251
1542
|
args.each do |a|
|
|
1252
1543
|
sql << j if c
|
|
@@ -1254,13 +1545,13 @@ module Sequel
|
|
|
1254
1545
|
c ||= true
|
|
1255
1546
|
end
|
|
1256
1547
|
when :ILIKE, :'NOT ILIKE'
|
|
1257
|
-
sql <<
|
|
1258
|
-
literal_append(sql, args
|
|
1259
|
-
sql <<
|
|
1260
|
-
literal_append(sql, args
|
|
1261
|
-
sql << ESCAPE
|
|
1262
|
-
literal_append(sql,
|
|
1263
|
-
sql <<
|
|
1548
|
+
sql << '('
|
|
1549
|
+
literal_append(sql, args[0])
|
|
1550
|
+
sql << ' ' << op.to_s << ' '
|
|
1551
|
+
literal_append(sql, args[1])
|
|
1552
|
+
sql << " ESCAPE "
|
|
1553
|
+
literal_append(sql, "\\")
|
|
1554
|
+
sql << ')'
|
|
1264
1555
|
else
|
|
1265
1556
|
super
|
|
1266
1557
|
end
|
|
@@ -1286,7 +1577,7 @@ module Sequel
|
|
|
1286
1577
|
|
|
1287
1578
|
# Return the results of an EXPLAIN query as a string
|
|
1288
1579
|
def explain(opts=OPTS)
|
|
1289
|
-
with_sql((opts[:analyze] ?
|
|
1580
|
+
with_sql((opts[:analyze] ? 'EXPLAIN ANALYZE ' : 'EXPLAIN ') + select_sql).map(:'QUERY PLAN').join("\r\n")
|
|
1290
1581
|
end
|
|
1291
1582
|
|
|
1292
1583
|
# Return a cloned dataset which will use FOR SHARE to lock returned rows.
|
|
@@ -1298,6 +1589,8 @@ module Sequel
|
|
|
1298
1589
|
# of any of the terms in any of the cols.
|
|
1299
1590
|
#
|
|
1300
1591
|
# Options:
|
|
1592
|
+
# :headline :: Append a expression to the selected columns aliased to headline that
|
|
1593
|
+
# contains an extract of the matched text.
|
|
1301
1594
|
# :language :: The language to use for the search (default: 'simple')
|
|
1302
1595
|
# :plain :: Whether a plain search should be used (default: false). In this case,
|
|
1303
1596
|
# terms should be a single string, and it will do a search where cols
|
|
@@ -1305,6 +1598,8 @@ module Sequel
|
|
|
1305
1598
|
# :phrase :: Similar to :plain, but also adding an ILIKE filter to ensure that
|
|
1306
1599
|
# returned rows also include the exact phrase used.
|
|
1307
1600
|
# :rank :: Set to true to order by the rank, so that closer matches are returned first.
|
|
1601
|
+
# :to_tsquery :: Can be set to :plain or :phrase to specify the function to use to
|
|
1602
|
+
# convert the terms to a ts_query.
|
|
1308
1603
|
# :tsquery :: Specifies the terms argument is already a valid SQL expression returning a
|
|
1309
1604
|
# tsquery, and can be used directly in the query.
|
|
1310
1605
|
# :tsvector :: Specifies the cols argument is already a valid SQL expression returning a
|
|
@@ -1319,11 +1614,18 @@ module Sequel
|
|
|
1319
1614
|
|
|
1320
1615
|
unless opts[:tsquery]
|
|
1321
1616
|
phrase_terms = terms.is_a?(Array) ? terms.join(' | ') : terms
|
|
1322
|
-
|
|
1617
|
+
|
|
1618
|
+
query_func = case to_tsquery = opts[:to_tsquery]
|
|
1619
|
+
when :phrase, :plain
|
|
1620
|
+
:"#{to_tsquery}to_tsquery"
|
|
1621
|
+
else
|
|
1622
|
+
(opts[:phrase] || opts[:plain]) ? :plainto_tsquery : :to_tsquery
|
|
1623
|
+
end
|
|
1624
|
+
|
|
1323
1625
|
terms = Sequel.function(query_func, lang, phrase_terms)
|
|
1324
1626
|
end
|
|
1325
1627
|
|
|
1326
|
-
ds = where(Sequel.lit(["
|
|
1628
|
+
ds = where(Sequel.lit(["", " @@ ", ""], cols, terms))
|
|
1327
1629
|
|
|
1328
1630
|
if opts[:phrase]
|
|
1329
1631
|
raise Error, "can't use :phrase with either :tsvector or :tsquery arguments to full_text_search together" if opts[:tsvector] || opts[:tsquery]
|
|
@@ -1331,7 +1633,11 @@ module Sequel
|
|
|
1331
1633
|
end
|
|
1332
1634
|
|
|
1333
1635
|
if opts[:rank]
|
|
1334
|
-
ds = ds.
|
|
1636
|
+
ds = ds.reverse{ts_rank_cd(cols, terms)}
|
|
1637
|
+
end
|
|
1638
|
+
|
|
1639
|
+
if opts[:headline]
|
|
1640
|
+
ds = ds.select_append{ts_headline(lang, phrase_cols, terms).as(:headline)}
|
|
1335
1641
|
end
|
|
1336
1642
|
|
|
1337
1643
|
ds
|
|
@@ -1356,6 +1662,7 @@ module Sequel
|
|
|
1356
1662
|
|
|
1357
1663
|
# Handle uniqueness violations when inserting, by updating the conflicting row, using
|
|
1358
1664
|
# ON CONFLICT. With no options, uses ON CONFLICT DO NOTHING. Options:
|
|
1665
|
+
# :conflict_where :: The index filter, when using a partial index to determine uniqueness.
|
|
1359
1666
|
# :constraint :: An explicit constraint name, has precendence over :target.
|
|
1360
1667
|
# :target :: The column name or expression to handle uniqueness violations on.
|
|
1361
1668
|
# :update :: A hash of columns and values to set. Uses ON CONFLICT DO UPDATE.
|
|
@@ -1363,24 +1670,28 @@ module Sequel
|
|
|
1363
1670
|
#
|
|
1364
1671
|
# Examples:
|
|
1365
1672
|
#
|
|
1366
|
-
# DB[:table].insert_conflict.insert(:
|
|
1673
|
+
# DB[:table].insert_conflict.insert(a: 1, b: 2)
|
|
1367
1674
|
# # INSERT INTO TABLE (a, b) VALUES (1, 2)
|
|
1368
1675
|
# # ON CONFLICT DO NOTHING
|
|
1369
1676
|
#
|
|
1370
|
-
# DB[:table].insert_conflict(:
|
|
1677
|
+
# DB[:table].insert_conflict(constraint: :table_a_uidx).insert(a: 1, b: 2)
|
|
1371
1678
|
# # INSERT INTO TABLE (a, b) VALUES (1, 2)
|
|
1372
1679
|
# # ON CONFLICT ON CONSTRAINT table_a_uidx DO NOTHING
|
|
1373
1680
|
#
|
|
1374
|
-
# DB[:table].insert_conflict(:
|
|
1681
|
+
# DB[:table].insert_conflict(target: :a).insert(a: 1, b: 2)
|
|
1375
1682
|
# # INSERT INTO TABLE (a, b) VALUES (1, 2)
|
|
1376
1683
|
# # ON CONFLICT (a) DO NOTHING
|
|
1684
|
+
#
|
|
1685
|
+
# DB[:table].insert_conflict(target: :a, conflict_where: {c: true}).insert(a: 1, b: 2)
|
|
1686
|
+
# # INSERT INTO TABLE (a, b) VALUES (1, 2)
|
|
1687
|
+
# # ON CONFLICT (a) WHERE (c IS TRUE) DO NOTHING
|
|
1377
1688
|
#
|
|
1378
|
-
# DB[:table].insert_conflict(:
|
|
1689
|
+
# DB[:table].insert_conflict(target: :a, update: {b: Sequel[:excluded][:b]}).insert(a: 1, b: 2)
|
|
1379
1690
|
# # INSERT INTO TABLE (a, b) VALUES (1, 2)
|
|
1380
1691
|
# # ON CONFLICT (a) DO UPDATE SET b = excluded.b
|
|
1381
1692
|
#
|
|
1382
|
-
# DB[:table].insert_conflict(:
|
|
1383
|
-
# :
|
|
1693
|
+
# DB[:table].insert_conflict(constraint: :table_a_uidx,
|
|
1694
|
+
# update: {b: Sequel[:excluded][:b]}, update_where: {Sequel[:table][:status_id] => 1}).insert(a: 1, b: 2)
|
|
1384
1695
|
# # INSERT INTO TABLE (a, b) VALUES (1, 2)
|
|
1385
1696
|
# # ON CONFLICT ON CONSTRAINT table_a_uidx
|
|
1386
1697
|
# # DO UPDATE SET b = excluded.b WHERE (table.status_id = 1)
|
|
@@ -1391,18 +1702,20 @@ module Sequel
|
|
|
1391
1702
|
# Ignore uniqueness/exclusion violations when inserting, using ON CONFLICT DO NOTHING.
|
|
1392
1703
|
# Exists mostly for compatibility to MySQL's insert_ignore. Example:
|
|
1393
1704
|
#
|
|
1394
|
-
# DB[:table].insert_ignore.insert(:
|
|
1705
|
+
# DB[:table].insert_ignore.insert(a: 1, b: 2)
|
|
1395
1706
|
# # INSERT INTO TABLE (a, b) VALUES (1, 2)
|
|
1396
1707
|
# # ON CONFLICT DO NOTHING
|
|
1397
1708
|
def insert_ignore
|
|
1398
1709
|
insert_conflict
|
|
1399
1710
|
end
|
|
1400
1711
|
|
|
1401
|
-
# Insert a record returning the record inserted. Always returns nil without
|
|
1402
|
-
#
|
|
1712
|
+
# Insert a record, returning the record inserted, using RETURNING. Always returns nil without
|
|
1713
|
+
# running an INSERT statement if disable_insert_returning is used. If the query runs
|
|
1714
|
+
# but returns no values, returns false.
|
|
1403
1715
|
def insert_select(*values)
|
|
1404
1716
|
return unless supports_insert_select?
|
|
1405
|
-
|
|
1717
|
+
# Handle case where query does not return a row
|
|
1718
|
+
server?(:default).with_sql_first(insert_select_sql(*values)) || false
|
|
1406
1719
|
end
|
|
1407
1720
|
|
|
1408
1721
|
# The SQL to use for an insert_select, adds a RETURNING clause to the insert
|
|
@@ -1414,14 +1727,14 @@ module Sequel
|
|
|
1414
1727
|
|
|
1415
1728
|
# Locks all tables in the dataset's FROM clause (but not in JOINs) with
|
|
1416
1729
|
# the specified mode (e.g. 'EXCLUSIVE'). If a block is given, starts
|
|
1417
|
-
# a new transaction, locks the table, and yields. If a block is not given
|
|
1730
|
+
# a new transaction, locks the table, and yields. If a block is not given,
|
|
1418
1731
|
# just locks the tables. Note that PostgreSQL will probably raise an error
|
|
1419
1732
|
# if you lock the table outside of an existing transaction. Returns nil.
|
|
1420
1733
|
def lock(mode, opts=OPTS)
|
|
1421
1734
|
if block_given? # perform locking inside a transaction and yield to block
|
|
1422
1735
|
@db.transaction(opts){lock(mode, opts); yield}
|
|
1423
1736
|
else
|
|
1424
|
-
sql = 'LOCK TABLE '
|
|
1737
|
+
sql = 'LOCK TABLE '.dup
|
|
1425
1738
|
source_list_append(sql, @opts[:from])
|
|
1426
1739
|
mode = mode.to_s.upcase.strip
|
|
1427
1740
|
unless LOCK_MODES.include?(mode)
|
|
@@ -1433,6 +1746,19 @@ module Sequel
|
|
|
1433
1746
|
nil
|
|
1434
1747
|
end
|
|
1435
1748
|
|
|
1749
|
+
# Use OVERRIDING USER VALUE for INSERT statements, so that identity columns
|
|
1750
|
+
# always use the user supplied value, and an error is not raised for identity
|
|
1751
|
+
# columns that are GENERATED ALWAYS.
|
|
1752
|
+
def overriding_system_value
|
|
1753
|
+
clone(:override=>:system)
|
|
1754
|
+
end
|
|
1755
|
+
|
|
1756
|
+
# Use OVERRIDING USER VALUE for INSERT statements, so that identity columns
|
|
1757
|
+
# always use the sequence value instead of the user supplied value.
|
|
1758
|
+
def overriding_user_value
|
|
1759
|
+
clone(:override=>:user)
|
|
1760
|
+
end
|
|
1761
|
+
|
|
1436
1762
|
def supports_cte?(type=:select)
|
|
1437
1763
|
if type == :select
|
|
1438
1764
|
server_version >= 80400
|
|
@@ -1477,7 +1803,7 @@ module Sequel
|
|
|
1477
1803
|
server_version >= 90500
|
|
1478
1804
|
end
|
|
1479
1805
|
|
|
1480
|
-
# PostgreSQL 9.
|
|
1806
|
+
# PostgreSQL 9.3+ supports lateral subqueries
|
|
1481
1807
|
def supports_lateral_subqueries?
|
|
1482
1808
|
server_version >= 90300
|
|
1483
1809
|
end
|
|
@@ -1487,6 +1813,11 @@ module Sequel
|
|
|
1487
1813
|
true
|
|
1488
1814
|
end
|
|
1489
1815
|
|
|
1816
|
+
# PostgreSQL supports NOWAIT.
|
|
1817
|
+
def supports_nowait?
|
|
1818
|
+
true
|
|
1819
|
+
end
|
|
1820
|
+
|
|
1490
1821
|
# Returning is always supported.
|
|
1491
1822
|
def supports_returning?(type)
|
|
1492
1823
|
true
|
|
@@ -1497,16 +1828,39 @@ module Sequel
|
|
|
1497
1828
|
true
|
|
1498
1829
|
end
|
|
1499
1830
|
|
|
1831
|
+
# PostgreSQL 9.5+ supports SKIP LOCKED.
|
|
1832
|
+
def supports_skip_locked?
|
|
1833
|
+
server_version >= 90500
|
|
1834
|
+
end
|
|
1835
|
+
|
|
1500
1836
|
# PostgreSQL supports timezones in literal timestamps
|
|
1501
1837
|
def supports_timestamp_timezones?
|
|
1502
1838
|
true
|
|
1503
1839
|
end
|
|
1504
1840
|
|
|
1841
|
+
# PostgreSQL 8.4+ supports WINDOW clause.
|
|
1842
|
+
def supports_window_clause?
|
|
1843
|
+
server_version >= 80400
|
|
1844
|
+
end
|
|
1845
|
+
|
|
1505
1846
|
# PostgreSQL 8.4+ supports window functions
|
|
1506
1847
|
def supports_window_functions?
|
|
1507
1848
|
server_version >= 80400
|
|
1508
1849
|
end
|
|
1509
1850
|
|
|
1851
|
+
# Base support added in 8.4, offset supported added in 9.0,
|
|
1852
|
+
# GROUPS and EXCLUDE support added in 11.0.
|
|
1853
|
+
def supports_window_function_frame_option?(option)
|
|
1854
|
+
case option
|
|
1855
|
+
when :rows, :range
|
|
1856
|
+
true
|
|
1857
|
+
when :offset
|
|
1858
|
+
server_version >= 90000
|
|
1859
|
+
when :groups, :exclude
|
|
1860
|
+
server_version >= 110000
|
|
1861
|
+
end
|
|
1862
|
+
end
|
|
1863
|
+
|
|
1510
1864
|
# Truncates the dataset. Returns nil.
|
|
1511
1865
|
#
|
|
1512
1866
|
# Options:
|
|
@@ -1518,10 +1872,11 @@ module Sequel
|
|
|
1518
1872
|
# :only and :restart only work correctly on PostgreSQL 8.4+.
|
|
1519
1873
|
#
|
|
1520
1874
|
# Usage:
|
|
1521
|
-
# DB[:table].truncate
|
|
1522
|
-
# #
|
|
1523
|
-
#
|
|
1524
|
-
#
|
|
1875
|
+
# DB[:table].truncate
|
|
1876
|
+
# # TRUNCATE TABLE "table"
|
|
1877
|
+
#
|
|
1878
|
+
# DB[:table].truncate(cascade: true, only: true, restart: true)
|
|
1879
|
+
# # TRUNCATE TABLE ONLY "table" RESTART IDENTITY CASCADE
|
|
1525
1880
|
def truncate(opts = OPTS)
|
|
1526
1881
|
if opts.empty?
|
|
1527
1882
|
super()
|
|
@@ -1530,9 +1885,11 @@ module Sequel
|
|
|
1530
1885
|
end
|
|
1531
1886
|
end
|
|
1532
1887
|
|
|
1533
|
-
#
|
|
1534
|
-
|
|
1535
|
-
|
|
1888
|
+
# Use WITH TIES when limiting the result set to also include additional
|
|
1889
|
+
# rules that have the same results for the order column as the final row.
|
|
1890
|
+
# Requires PostgreSQL 13.
|
|
1891
|
+
def with_ties
|
|
1892
|
+
clone(:limit_with_ties=>true)
|
|
1536
1893
|
end
|
|
1537
1894
|
|
|
1538
1895
|
protected
|
|
@@ -1544,7 +1901,9 @@ module Sequel
|
|
|
1544
1901
|
def _import(columns, values, opts=OPTS)
|
|
1545
1902
|
if @opts[:returning]
|
|
1546
1903
|
statements = multi_insert_sql(columns, values)
|
|
1547
|
-
|
|
1904
|
+
trans_opts = Hash[opts]
|
|
1905
|
+
trans_opts[:server] = @opts[:server]
|
|
1906
|
+
@db.transaction(trans_opts) do
|
|
1548
1907
|
statements.map{|st| returning_fetch_rows(st)}
|
|
1549
1908
|
end.first.map{|v| v.length == 1 ? v.values.first : v}
|
|
1550
1909
|
elsif opts[:return] == :primary_key
|
|
@@ -1554,11 +1913,19 @@ module Sequel
|
|
|
1554
1913
|
end
|
|
1555
1914
|
end
|
|
1556
1915
|
|
|
1916
|
+
def to_prepared_statement(type, *a)
|
|
1917
|
+
if type == :insert && !@opts.has_key?(:returning)
|
|
1918
|
+
returning(insert_pk).send(:to_prepared_statement, :insert_pk, *a)
|
|
1919
|
+
else
|
|
1920
|
+
super
|
|
1921
|
+
end
|
|
1922
|
+
end
|
|
1923
|
+
|
|
1557
1924
|
private
|
|
1558
1925
|
|
|
1559
1926
|
# Format TRUNCATE statement with PostgreSQL specific options.
|
|
1560
1927
|
def _truncate_sql(table)
|
|
1561
|
-
to = @opts[:truncate_opts] ||
|
|
1928
|
+
to = @opts[:truncate_opts] || OPTS
|
|
1562
1929
|
"TRUNCATE TABLE#{' ONLY' if to[:only]} #{table}#{' RESTART IDENTITY' if to[:restart]}#{' CASCADE' if to[:cascade]}"
|
|
1563
1930
|
end
|
|
1564
1931
|
|
|
@@ -1570,7 +1937,7 @@ module Sequel
|
|
|
1570
1937
|
|
|
1571
1938
|
# Only include the primary table in the main delete clause
|
|
1572
1939
|
def delete_from_sql(sql)
|
|
1573
|
-
sql << FROM
|
|
1940
|
+
sql << ' FROM '
|
|
1574
1941
|
source_list_append(sql, @opts[:from][0..0])
|
|
1575
1942
|
end
|
|
1576
1943
|
|
|
@@ -1588,22 +1955,34 @@ module Sequel
|
|
|
1588
1955
|
sql << " ON CONSTRAINT "
|
|
1589
1956
|
identifier_append(sql, target)
|
|
1590
1957
|
elsif target = opts[:target]
|
|
1591
|
-
sql << '
|
|
1592
|
-
identifier_append(sql, target)
|
|
1593
|
-
|
|
1958
|
+
sql << ' '
|
|
1959
|
+
identifier_append(sql, Array(target))
|
|
1960
|
+
if conflict_where = opts[:conflict_where]
|
|
1961
|
+
sql << " WHERE "
|
|
1962
|
+
literal_append(sql, conflict_where)
|
|
1963
|
+
end
|
|
1594
1964
|
end
|
|
1595
1965
|
|
|
1596
1966
|
if values = opts[:update]
|
|
1597
1967
|
sql << " DO UPDATE SET "
|
|
1598
1968
|
update_sql_values_hash(sql, values)
|
|
1599
|
-
if
|
|
1969
|
+
if update_where = opts[:update_where]
|
|
1600
1970
|
sql << " WHERE "
|
|
1601
|
-
literal_append(sql,
|
|
1971
|
+
literal_append(sql, update_where)
|
|
1602
1972
|
end
|
|
1603
1973
|
else
|
|
1604
1974
|
sql << " DO NOTHING"
|
|
1605
1975
|
end
|
|
1976
|
+
end
|
|
1977
|
+
end
|
|
1606
1978
|
|
|
1979
|
+
# Include aliases when inserting into a single table on PostgreSQL 9.5+.
|
|
1980
|
+
def insert_into_sql(sql)
|
|
1981
|
+
sql << " INTO "
|
|
1982
|
+
if (f = @opts[:from]) && f.length == 1
|
|
1983
|
+
identifier_append(sql, server_version >= 90500 ? f.first : unaliased_identifier(f.first))
|
|
1984
|
+
else
|
|
1985
|
+
source_list_append(sql, f)
|
|
1607
1986
|
end
|
|
1608
1987
|
end
|
|
1609
1988
|
|
|
@@ -1619,13 +1998,24 @@ module Sequel
|
|
|
1619
1998
|
end
|
|
1620
1999
|
end
|
|
1621
2000
|
|
|
2001
|
+
# Support OVERRIDING SYSTEM|USER VALUE in insert statements
|
|
2002
|
+
def insert_values_sql(sql)
|
|
2003
|
+
case opts[:override]
|
|
2004
|
+
when :system
|
|
2005
|
+
sql << " OVERRIDING SYSTEM VALUE"
|
|
2006
|
+
when :user
|
|
2007
|
+
sql << " OVERRIDING USER VALUE"
|
|
2008
|
+
end
|
|
2009
|
+
super
|
|
2010
|
+
end
|
|
2011
|
+
|
|
1622
2012
|
# For multiple table support, PostgreSQL requires at least
|
|
1623
2013
|
# two from tables, with joins allowed.
|
|
1624
2014
|
def join_from_sql(type, sql)
|
|
1625
2015
|
if(from = @opts[:from][1..-1]).empty?
|
|
1626
2016
|
raise(Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs') if @opts[:join]
|
|
1627
2017
|
else
|
|
1628
|
-
sql <<
|
|
2018
|
+
sql << ' ' << type.to_s << ' '
|
|
1629
2019
|
source_list_append(sql, from)
|
|
1630
2020
|
select_join_sql(sql)
|
|
1631
2021
|
end
|
|
@@ -1633,12 +2023,12 @@ module Sequel
|
|
|
1633
2023
|
|
|
1634
2024
|
# Use a generic blob quoting method, hopefully overridden in one of the subadapter methods
|
|
1635
2025
|
def literal_blob_append(sql, v)
|
|
1636
|
-
sql <<
|
|
2026
|
+
sql << "'" << v.gsub(/[\000-\037\047\134\177-\377]/n){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"} << "'"
|
|
1637
2027
|
end
|
|
1638
2028
|
|
|
1639
2029
|
# PostgreSQL uses FALSE for false values
|
|
1640
2030
|
def literal_false
|
|
1641
|
-
|
|
2031
|
+
'false'
|
|
1642
2032
|
end
|
|
1643
2033
|
|
|
1644
2034
|
# PostgreSQL quotes NaN and Infinity.
|
|
@@ -1656,12 +2046,12 @@ module Sequel
|
|
|
1656
2046
|
|
|
1657
2047
|
# Assume that SQL standard quoting is on, per Sequel's defaults
|
|
1658
2048
|
def literal_string_append(sql, v)
|
|
1659
|
-
sql <<
|
|
2049
|
+
sql << "'" << v.gsub("'", "''") << "'"
|
|
1660
2050
|
end
|
|
1661
2051
|
|
|
1662
|
-
# PostgreSQL uses
|
|
2052
|
+
# PostgreSQL uses true for true values
|
|
1663
2053
|
def literal_true
|
|
1664
|
-
|
|
2054
|
+
'true'
|
|
1665
2055
|
end
|
|
1666
2056
|
|
|
1667
2057
|
# PostgreSQL supports multiple rows in INSERT.
|
|
@@ -1669,45 +2059,96 @@ module Sequel
|
|
|
1669
2059
|
:values
|
|
1670
2060
|
end
|
|
1671
2061
|
|
|
2062
|
+
# Dataset options that do not affect the generated SQL.
|
|
2063
|
+
def non_sql_option?(key)
|
|
2064
|
+
super || key == :cursor || key == :insert_conflict
|
|
2065
|
+
end
|
|
2066
|
+
|
|
1672
2067
|
# PostgreSQL requires parentheses around compound datasets if they use
|
|
1673
2068
|
# CTEs, and using them in other places doesn't hurt.
|
|
1674
2069
|
def compound_dataset_sql_append(sql, ds)
|
|
1675
|
-
sql <<
|
|
2070
|
+
sql << '('
|
|
1676
2071
|
super
|
|
1677
|
-
sql <<
|
|
2072
|
+
sql << ')'
|
|
2073
|
+
end
|
|
2074
|
+
|
|
2075
|
+
# Backslash is supported by default as the escape character on PostgreSQL,
|
|
2076
|
+
# and using ESCAPE can break LIKE ANY() usage.
|
|
2077
|
+
def requires_like_escape?
|
|
2078
|
+
false
|
|
2079
|
+
end
|
|
2080
|
+
|
|
2081
|
+
# Support FETCH FIRST WITH TIES on PostgreSQL 13+.
|
|
2082
|
+
def select_limit_sql(sql)
|
|
2083
|
+
l = @opts[:limit]
|
|
2084
|
+
o = @opts[:offset]
|
|
2085
|
+
|
|
2086
|
+
return unless l || o
|
|
2087
|
+
|
|
2088
|
+
if @opts[:limit_with_ties]
|
|
2089
|
+
if o
|
|
2090
|
+
sql << " OFFSET "
|
|
2091
|
+
literal_append(sql, o)
|
|
2092
|
+
end
|
|
2093
|
+
|
|
2094
|
+
if l
|
|
2095
|
+
sql << " FETCH FIRST "
|
|
2096
|
+
literal_append(sql, l)
|
|
2097
|
+
sql << " ROWS WITH TIES"
|
|
2098
|
+
end
|
|
2099
|
+
else
|
|
2100
|
+
if l
|
|
2101
|
+
sql << " LIMIT "
|
|
2102
|
+
literal_append(sql, l)
|
|
2103
|
+
end
|
|
2104
|
+
|
|
2105
|
+
if o
|
|
2106
|
+
sql << " OFFSET "
|
|
2107
|
+
literal_append(sql, o)
|
|
2108
|
+
end
|
|
2109
|
+
end
|
|
1678
2110
|
end
|
|
1679
2111
|
|
|
1680
2112
|
# Support FOR SHARE locking when using the :share lock style.
|
|
2113
|
+
# Use SKIP LOCKED if skipping locked rows.
|
|
1681
2114
|
def select_lock_sql(sql)
|
|
1682
|
-
@opts[:lock]
|
|
2115
|
+
lock = @opts[:lock]
|
|
2116
|
+
if lock == :share
|
|
2117
|
+
sql << ' FOR SHARE'
|
|
2118
|
+
else
|
|
2119
|
+
super
|
|
2120
|
+
end
|
|
2121
|
+
|
|
2122
|
+
if lock
|
|
2123
|
+
if @opts[:skip_locked]
|
|
2124
|
+
sql << " SKIP LOCKED"
|
|
2125
|
+
elsif @opts[:nowait]
|
|
2126
|
+
sql << " NOWAIT"
|
|
2127
|
+
end
|
|
2128
|
+
end
|
|
1683
2129
|
end
|
|
1684
2130
|
|
|
1685
2131
|
# Support VALUES clause instead of the SELECT clause to return rows.
|
|
1686
2132
|
def select_values_sql(sql)
|
|
1687
|
-
sql <<
|
|
2133
|
+
sql << "VALUES "
|
|
1688
2134
|
expression_list_append(sql, opts[:values])
|
|
1689
2135
|
end
|
|
1690
2136
|
|
|
1691
|
-
# SQL fragment for named window specifications
|
|
1692
|
-
def select_window_sql(sql)
|
|
1693
|
-
if ws = @opts[:window]
|
|
1694
|
-
sql << WINDOW
|
|
1695
|
-
c = false
|
|
1696
|
-
co = COMMA
|
|
1697
|
-
as = AS
|
|
1698
|
-
ws.map do |name, window|
|
|
1699
|
-
sql << co if c
|
|
1700
|
-
literal_append(sql, name)
|
|
1701
|
-
sql << as
|
|
1702
|
-
literal_append(sql, window)
|
|
1703
|
-
c ||= true
|
|
1704
|
-
end
|
|
1705
|
-
end
|
|
1706
|
-
end
|
|
1707
|
-
|
|
1708
2137
|
# Use WITH RECURSIVE instead of WITH if any of the CTEs is recursive
|
|
1709
2138
|
def select_with_sql_base
|
|
1710
|
-
opts[:with].any?{|w| w[:recursive]} ?
|
|
2139
|
+
opts[:with].any?{|w| w[:recursive]} ? "WITH RECURSIVE " : super
|
|
2140
|
+
end
|
|
2141
|
+
|
|
2142
|
+
# Support WITH AS [NOT] MATERIALIZED if :materialized option is used.
|
|
2143
|
+
def select_with_sql_prefix(sql, w)
|
|
2144
|
+
super
|
|
2145
|
+
|
|
2146
|
+
case w[:materialized]
|
|
2147
|
+
when true
|
|
2148
|
+
sql << "MATERIALIZED "
|
|
2149
|
+
when false
|
|
2150
|
+
sql << "NOT MATERIALIZED "
|
|
2151
|
+
end
|
|
1711
2152
|
end
|
|
1712
2153
|
|
|
1713
2154
|
# The version of the database server
|
|
@@ -1715,6 +2156,11 @@ module Sequel
|
|
|
1715
2156
|
db.server_version(@opts[:server])
|
|
1716
2157
|
end
|
|
1717
2158
|
|
|
2159
|
+
# PostgreSQL 9.4+ supports the FILTER clause for aggregate functions.
|
|
2160
|
+
def supports_filtered_aggregates?
|
|
2161
|
+
server_version >= 90400
|
|
2162
|
+
end
|
|
2163
|
+
|
|
1718
2164
|
# PostgreSQL supports quoted function names.
|
|
1719
2165
|
def supports_quoted_function_names?
|
|
1720
2166
|
true
|
|
@@ -1722,8 +2168,8 @@ module Sequel
|
|
|
1722
2168
|
|
|
1723
2169
|
# Concatenate the expressions with a space in between
|
|
1724
2170
|
def full_text_string_join(cols)
|
|
1725
|
-
cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x,
|
|
1726
|
-
cols = cols.zip([
|
|
2171
|
+
cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x, '')}
|
|
2172
|
+
cols = cols.zip([' '] * cols.length).flatten
|
|
1727
2173
|
cols.pop
|
|
1728
2174
|
SQL::StringExpression.new(:'||', *cols)
|
|
1729
2175
|
end
|
|
@@ -1735,7 +2181,7 @@ module Sequel
|
|
|
1735
2181
|
|
|
1736
2182
|
# Only include the primary table in the main update clause
|
|
1737
2183
|
def update_table_sql(sql)
|
|
1738
|
-
sql <<
|
|
2184
|
+
sql << ' '
|
|
1739
2185
|
source_list_append(sql, @opts[:from][0..0])
|
|
1740
2186
|
end
|
|
1741
2187
|
end
|