sequel 4.22.0 → 4.23.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +22 -0
- data/README.rdoc +6 -0
- data/Rakefile +59 -81
- data/doc/migration.rdoc +2 -0
- data/doc/release_notes/4.23.0.txt +65 -0
- data/doc/sharding.rdoc +16 -14
- data/doc/testing.rdoc +61 -77
- data/lib/sequel/adapters/jdbc.rb +1 -0
- data/lib/sequel/adapters/mock.rb +0 -1
- data/lib/sequel/adapters/postgres.rb +1 -0
- data/lib/sequel/adapters/postgresql.rb +1 -0
- data/lib/sequel/adapters/shared/postgres.rb +3 -3
- data/lib/sequel/connection_pool/sharded_threaded.rb +5 -0
- data/lib/sequel/connection_pool/threaded.rb +9 -1
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/transactions.rb +2 -1
- data/lib/sequel/dataset/prepared_statements.rb +1 -1
- data/lib/sequel/extensions/constraint_validations.rb +12 -12
- data/lib/sequel/extensions/date_arithmetic.rb +0 -4
- data/lib/sequel/extensions/pagination.rb +14 -2
- data/lib/sequel/extensions/pg_enum.rb +2 -2
- data/lib/sequel/extensions/pg_hstore.rb +1 -1
- data/lib/sequel/extensions/pg_json_ops.rb +2 -2
- data/lib/sequel/plugins/csv_serializer.rb +2 -0
- data/lib/sequel/plugins/delay_add_association.rb +50 -0
- data/lib/sequel/plugins/list.rb +2 -2
- data/lib/sequel/plugins/nested_attributes.rb +8 -28
- data/lib/sequel/plugins/update_refresh.rb +50 -0
- data/lib/sequel/plugins/validate_associated.rb +55 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +29 -29
- data/spec/adapters/firebird_spec.rb +97 -103
- data/spec/adapters/informix_spec.rb +25 -25
- data/spec/adapters/mssql_spec.rb +156 -172
- data/spec/adapters/mysql_spec.rb +334 -359
- data/spec/adapters/oracle_spec.rb +67 -69
- data/spec/adapters/postgres_spec.rb +1298 -1249
- data/spec/adapters/spec_helper.rb +2 -35
- data/spec/adapters/sqlanywhere_spec.rb +39 -39
- data/spec/adapters/sqlite_spec.rb +203 -200
- data/spec/bin_spec.rb +57 -59
- data/spec/core/connection_pool_spec.rb +402 -401
- data/spec/core/database_spec.rb +953 -944
- data/spec/core/dataset_spec.rb +2178 -2168
- data/spec/core/deprecated_spec.rb +19 -19
- data/spec/core/expression_filters_spec.rb +415 -415
- data/spec/core/mock_adapter_spec.rb +212 -212
- data/spec/core/object_graph_spec.rb +73 -73
- data/spec/core/placeholder_literalizer_spec.rb +71 -71
- data/spec/core/schema_generator_spec.rb +44 -44
- data/spec/core/schema_spec.rb +470 -472
- data/spec/core/spec_helper.rb +5 -20
- data/spec/core/version_spec.rb +2 -2
- data/spec/core_extensions_spec.rb +320 -320
- data/spec/extensions/accessed_columns_spec.rb +12 -12
- data/spec/extensions/active_model_spec.rb +3 -3
- data/spec/extensions/after_initialize_spec.rb +2 -2
- data/spec/extensions/arbitrary_servers_spec.rb +23 -23
- data/spec/extensions/association_dependencies_spec.rb +34 -34
- data/spec/extensions/association_pks_spec.rb +98 -98
- data/spec/extensions/association_proxies_spec.rb +33 -33
- data/spec/extensions/auto_validations_spec.rb +46 -46
- data/spec/extensions/blacklist_security_spec.rb +19 -18
- data/spec/extensions/blank_spec.rb +36 -36
- data/spec/extensions/boolean_readers_spec.rb +36 -36
- data/spec/extensions/caching_spec.rb +82 -82
- data/spec/extensions/class_table_inheritance_spec.rb +72 -72
- data/spec/extensions/column_conflicts_spec.rb +19 -14
- data/spec/extensions/column_select_spec.rb +19 -19
- data/spec/extensions/columns_introspection_spec.rb +43 -43
- data/spec/extensions/composition_spec.rb +64 -64
- data/spec/extensions/connection_validator_spec.rb +92 -90
- data/spec/extensions/constraint_validations_plugin_spec.rb +92 -92
- data/spec/extensions/constraint_validations_spec.rb +80 -80
- data/spec/extensions/core_refinements_spec.rb +220 -220
- data/spec/extensions/csv_serializer_spec.rb +44 -44
- data/spec/extensions/current_datetime_timestamp_spec.rb +8 -8
- data/spec/extensions/dataset_associations_spec.rb +65 -65
- data/spec/extensions/dataset_source_alias_spec.rb +16 -16
- data/spec/extensions/date_arithmetic_spec.rb +51 -58
- data/spec/extensions/defaults_setter_spec.rb +19 -19
- data/spec/extensions/delay_add_association_spec.rb +52 -0
- data/spec/extensions/dirty_spec.rb +51 -51
- data/spec/extensions/eager_each_spec.rb +8 -8
- data/spec/extensions/empty_array_ignore_nulls_spec.rb +10 -10
- data/spec/extensions/error_splitter_spec.rb +2 -2
- data/spec/extensions/error_sql_spec.rb +4 -4
- data/spec/extensions/eval_inspect_spec.rb +3 -3
- data/spec/extensions/filter_having_spec.rb +8 -8
- data/spec/extensions/force_encoding_spec.rb +30 -30
- data/spec/extensions/from_block_spec.rb +7 -7
- data/spec/extensions/graph_each_spec.rb +19 -19
- data/spec/extensions/hash_aliases_spec.rb +5 -5
- data/spec/extensions/hook_class_methods_spec.rb +100 -100
- data/spec/extensions/inflector_spec.rb +54 -54
- data/spec/extensions/input_transformer_spec.rb +10 -10
- data/spec/extensions/insert_returning_select_spec.rb +8 -8
- data/spec/extensions/instance_filters_spec.rb +26 -26
- data/spec/extensions/instance_hooks_spec.rb +85 -85
- data/spec/extensions/json_serializer_spec.rb +68 -68
- data/spec/extensions/lazy_attributes_spec.rb +49 -49
- data/spec/extensions/list_spec.rb +77 -75
- data/spec/extensions/looser_typecasting_spec.rb +16 -16
- data/spec/extensions/many_through_many_spec.rb +627 -627
- data/spec/extensions/meta_def_spec.rb +7 -7
- data/spec/extensions/migration_spec.rb +217 -217
- data/spec/extensions/modification_detection_spec.rb +20 -20
- data/spec/extensions/mssql_optimistic_locking_spec.rb +21 -21
- data/spec/extensions/named_timezones_spec.rb +18 -18
- data/spec/extensions/nested_attributes_spec.rb +107 -107
- data/spec/extensions/null_dataset_spec.rb +24 -24
- data/spec/extensions/optimistic_locking_spec.rb +21 -21
- data/spec/extensions/pagination_spec.rb +52 -52
- data/spec/extensions/pg_array_associations_spec.rb +273 -273
- data/spec/extensions/pg_array_ops_spec.rb +52 -52
- data/spec/extensions/pg_array_spec.rb +152 -152
- data/spec/extensions/pg_enum_spec.rb +13 -13
- data/spec/extensions/pg_hstore_ops_spec.rb +63 -63
- data/spec/extensions/pg_hstore_spec.rb +84 -84
- data/spec/extensions/pg_inet_spec.rb +15 -15
- data/spec/extensions/pg_interval_spec.rb +29 -29
- data/spec/extensions/pg_json_ops_spec.rb +86 -84
- data/spec/extensions/pg_json_spec.rb +104 -104
- data/spec/extensions/pg_loose_count_spec.rb +6 -6
- data/spec/extensions/pg_range_ops_spec.rb +24 -24
- data/spec/extensions/pg_range_spec.rb +143 -143
- data/spec/extensions/pg_row_ops_spec.rb +14 -14
- data/spec/extensions/pg_row_plugin_spec.rb +12 -12
- data/spec/extensions/pg_row_spec.rb +118 -118
- data/spec/extensions/pg_static_cache_updater_spec.rb +28 -28
- data/spec/extensions/pg_typecast_on_load_spec.rb +21 -21
- data/spec/extensions/prepared_statements_associations_spec.rb +42 -42
- data/spec/extensions/prepared_statements_safe_spec.rb +18 -18
- data/spec/extensions/prepared_statements_spec.rb +28 -28
- data/spec/extensions/prepared_statements_with_pk_spec.rb +11 -11
- data/spec/extensions/pretty_table_spec.rb +16 -16
- data/spec/extensions/query_literals_spec.rb +37 -37
- data/spec/extensions/query_spec.rb +32 -32
- data/spec/extensions/rcte_tree_spec.rb +141 -141
- data/spec/extensions/round_timestamps_spec.rb +21 -21
- data/spec/extensions/schema_caching_spec.rb +8 -8
- data/spec/extensions/schema_dumper_spec.rb +78 -78
- data/spec/extensions/schema_spec.rb +31 -27
- data/spec/extensions/scissors_spec.rb +3 -3
- data/spec/extensions/select_remove_spec.rb +14 -14
- data/spec/extensions/sequel_3_dataset_methods_spec.rb +28 -28
- data/spec/extensions/serialization_modification_detection_spec.rb +33 -33
- data/spec/extensions/serialization_spec.rb +79 -78
- data/spec/extensions/server_block_spec.rb +17 -17
- data/spec/extensions/set_overrides_spec.rb +30 -30
- data/spec/extensions/sharding_spec.rb +65 -65
- data/spec/extensions/shared_caching_spec.rb +29 -29
- data/spec/extensions/single_table_inheritance_spec.rb +79 -79
- data/spec/extensions/skip_create_refresh_spec.rb +3 -3
- data/spec/extensions/spec_helper.rb +4 -29
- data/spec/extensions/split_array_nil_spec.rb +9 -9
- data/spec/extensions/split_values_spec.rb +7 -7
- data/spec/extensions/sql_expr_spec.rb +32 -32
- data/spec/extensions/static_cache_spec.rb +123 -123
- data/spec/extensions/string_date_time_spec.rb +34 -34
- data/spec/extensions/string_stripper_spec.rb +15 -15
- data/spec/extensions/subclasses_spec.rb +31 -31
- data/spec/extensions/table_select_spec.rb +15 -15
- data/spec/extensions/tactical_eager_loading_spec.rb +23 -23
- data/spec/extensions/thread_local_timezones_spec.rb +13 -13
- data/spec/extensions/timestamps_spec.rb +40 -40
- data/spec/extensions/to_dot_spec.rb +34 -34
- data/spec/extensions/touch_spec.rb +52 -52
- data/spec/extensions/tree_spec.rb +72 -72
- data/spec/extensions/typecast_on_load_spec.rb +25 -25
- data/spec/extensions/unlimited_update_spec.rb +2 -2
- data/spec/extensions/update_or_create_spec.rb +36 -36
- data/spec/extensions/update_primary_key_spec.rb +35 -35
- data/spec/extensions/update_refresh_spec.rb +41 -0
- data/spec/extensions/validate_associated_spec.rb +52 -0
- data/spec/extensions/validation_class_methods_spec.rb +314 -317
- data/spec/extensions/validation_helpers_spec.rb +195 -195
- data/spec/extensions/xml_serializer_spec.rb +48 -48
- data/spec/guards_helper.rb +55 -0
- data/spec/integration/associations_test.rb +1089 -1088
- data/spec/integration/database_test.rb +29 -29
- data/spec/integration/dataset_test.rb +661 -661
- data/spec/integration/eager_loader_test.rb +147 -147
- data/spec/integration/migrator_test.rb +122 -122
- data/spec/integration/model_test.rb +70 -70
- data/spec/integration/plugin_test.rb +682 -640
- data/spec/integration/prepared_statement_test.rb +172 -172
- data/spec/integration/schema_test.rb +245 -245
- data/spec/integration/spec_helper.rb +1 -64
- data/spec/integration/timezone_test.rb +17 -17
- data/spec/integration/transaction_test.rb +87 -87
- data/spec/integration/type_test.rb +33 -33
- data/spec/model/association_reflection_spec.rb +130 -121
- data/spec/model/associations_spec.rb +1112 -1113
- data/spec/model/base_spec.rb +197 -196
- data/spec/model/class_dataset_methods_spec.rb +118 -118
- data/spec/model/dataset_methods_spec.rb +49 -49
- data/spec/model/eager_loading_spec.rb +705 -702
- data/spec/model/hooks_spec.rb +169 -168
- data/spec/model/inflector_spec.rb +5 -5
- data/spec/model/model_spec.rb +287 -297
- data/spec/model/plugins_spec.rb +47 -47
- data/spec/model/record_spec.rb +534 -535
- data/spec/model/spec_helper.rb +3 -21
- data/spec/model/validations_spec.rb +72 -70
- data/spec/spec_config.rb +8 -0
- metadata +41 -9
- data/lib/sequel/adapters/fdbsql.rb +0 -286
- data/lib/sequel/adapters/jdbc/fdbsql.rb +0 -66
- data/lib/sequel/adapters/openbase.rb +0 -54
- data/lib/sequel/adapters/shared/fdbsql.rb +0 -550
- data/spec/adapters/fdbsql_spec.rb +0 -429
- data/spec/rspec_helper.rb +0 -22
@@ -1,66 +0,0 @@
|
|
1
|
-
Sequel::JDBC.load_driver('com.foundationdb.sql.jdbc.Driver')
|
2
|
-
Sequel.require 'adapters/shared/fdbsql'
|
3
|
-
Sequel::Deprecation.deprecate 'The jdbc/fdbsql adapter is deprecated and will be removed in a future version of Sequel.'
|
4
|
-
|
5
|
-
module Sequel
|
6
|
-
Fdbsql::CONVERTED_EXCEPTIONS << NativeException
|
7
|
-
|
8
|
-
module JDBC
|
9
|
-
Sequel.synchronize do
|
10
|
-
DATABASE_SETUP[:fdbsql] = proc do |db|
|
11
|
-
db.extend(Sequel::JDBC::Fdbsql::DatabaseMethods)
|
12
|
-
db.dataset_class = Sequel::JDBC::Fdbsql::Dataset
|
13
|
-
com.foundationdb.sql.jdbc.Driver
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
# Adapter, Database, and Dataset support for accessing the FoundationDB SQL Layer
|
18
|
-
# via JDBC
|
19
|
-
module Fdbsql
|
20
|
-
# Methods to add to Database instances that access Fdbsql via
|
21
|
-
# JDBC.
|
22
|
-
module DatabaseMethods
|
23
|
-
extend Sequel::Database::ResetIdentifierMangling
|
24
|
-
include Sequel::Fdbsql::DatabaseMethods
|
25
|
-
|
26
|
-
# Add the primary_keys and primary_key_sequences instance variables,
|
27
|
-
# so we can get the correct return values for inserted rows.
|
28
|
-
def self.extended(db)
|
29
|
-
super
|
30
|
-
db.send(:adapter_initialize)
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
DISCONNECT_ERROR_RE = /\A(?:This connection has been closed|An I\/O error occurred while sending to the backend)/
|
36
|
-
def disconnect_error?(exception, opts)
|
37
|
-
super || exception.message =~ DISCONNECT_ERROR_RE
|
38
|
-
end
|
39
|
-
|
40
|
-
def database_exception_sqlstate(exception, opts)
|
41
|
-
if exception.respond_to?(:sql_state)
|
42
|
-
exception.sql_state
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
# Methods to add to Dataset instances that access the FoundationDB SQL Layer via
|
48
|
-
# JDBC.
|
49
|
-
class Dataset < JDBC::Dataset
|
50
|
-
include Sequel::Fdbsql::DatasetMethods
|
51
|
-
|
52
|
-
# Add the shared Fdbsql prepared statement methods
|
53
|
-
def prepare(type, name=nil, *values)
|
54
|
-
ps = to_prepared_statement(type, values)
|
55
|
-
ps.extend(JDBC::Dataset::PreparedStatementMethods)
|
56
|
-
ps.extend(::Sequel::Fdbsql::DatasetMethods::PreparedStatementMethods)
|
57
|
-
if name
|
58
|
-
ps.prepared_statement_name = name
|
59
|
-
db.set_prepared_statement(name, ps)
|
60
|
-
end
|
61
|
-
ps
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
require 'openbase'
|
2
|
-
Sequel::Deprecation.deprecate 'The openbase adapter is deprecated and will be removed in a future version of Sequel.'
|
3
|
-
|
4
|
-
module Sequel
|
5
|
-
module OpenBase
|
6
|
-
class Database < Sequel::Database
|
7
|
-
set_adapter_scheme :openbase
|
8
|
-
|
9
|
-
def connect(server)
|
10
|
-
opts = server_opts(server)
|
11
|
-
OpenBase.new(
|
12
|
-
opts[:database],
|
13
|
-
opts[:host] || 'localhost',
|
14
|
-
opts[:user],
|
15
|
-
opts[:password]
|
16
|
-
)
|
17
|
-
end
|
18
|
-
|
19
|
-
def disconnect_connection(c)
|
20
|
-
c.disconnect
|
21
|
-
end
|
22
|
-
|
23
|
-
def execute(sql, opts=OPTS)
|
24
|
-
synchronize(opts[:server]) do |conn|
|
25
|
-
r = log_yield(sql){conn.execute(sql)}
|
26
|
-
yield(r) if block_given?
|
27
|
-
r
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
class Dataset < Sequel::Dataset
|
33
|
-
def_sql_method(self, :select, %w'select distinct columns from join where group having compounds order limit')
|
34
|
-
|
35
|
-
Database::DatasetClass = self
|
36
|
-
|
37
|
-
def fetch_rows(sql)
|
38
|
-
execute(sql) do |result|
|
39
|
-
begin
|
40
|
-
@columns = result.column_infos.map{|c| output_identifier(c.name)}
|
41
|
-
result.each do |r|
|
42
|
-
row = {}
|
43
|
-
r.each_with_index {|v, i| row[@columns[i]] = v}
|
44
|
-
yield row
|
45
|
-
end
|
46
|
-
ensure
|
47
|
-
# result.close
|
48
|
-
end
|
49
|
-
end
|
50
|
-
self
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
@@ -1,550 +0,0 @@
|
|
1
|
-
Sequel.require 'adapters/utils/pg_types'
|
2
|
-
|
3
|
-
module Sequel
|
4
|
-
|
5
|
-
# Top level module for holding all FoundationDB SQL Layer related modules and
|
6
|
-
# classes for Sequel.
|
7
|
-
module Fdbsql
|
8
|
-
|
9
|
-
# Array of exceptions that need to be converted. JDBC
|
10
|
-
# uses NativeExceptions, the native adapter uses PGError.
|
11
|
-
CONVERTED_EXCEPTIONS = []
|
12
|
-
|
13
|
-
# Methods shared by Database instances that connect to
|
14
|
-
# the FoundationDB SQL Layer
|
15
|
-
module DatabaseMethods
|
16
|
-
|
17
|
-
# A hash of conversion procs, keyed by type integer (oid) and
|
18
|
-
# having callable values for the conversion proc for that type.
|
19
|
-
attr_reader :conversion_procs
|
20
|
-
|
21
|
-
# Convert given argument so that it can be used directly by pg. Currently, pg doesn't
|
22
|
-
# handle fractional seconds in Time/DateTime or blobs with "\0", and it won't ever
|
23
|
-
# handle Sequel::SQLTime values correctly. Only public for use by the adapter, shouldn't
|
24
|
-
# be used by external code.
|
25
|
-
def bound_variable_arg(arg, conn)
|
26
|
-
case arg
|
27
|
-
# TODO TDD it:
|
28
|
-
when Sequel::SQL::Blob
|
29
|
-
# the 1 means treat this as a binary blob
|
30
|
-
{:value => arg, :format => 1}
|
31
|
-
when Sequel::SQLTime
|
32
|
-
# the literal methods put quotes around things, but this is a bound variable, so we can't use those
|
33
|
-
arg.strftime(BOUND_VARIABLE_SQLTIME_FORMAT)
|
34
|
-
when DateTime, Time
|
35
|
-
# the literal methods put quotes around things, but this is a bound variable, so we can't use those
|
36
|
-
from_application_timestamp(arg).strftime(BOUND_VARIABLE_TIMESTAMP_FORMAT)
|
37
|
-
else
|
38
|
-
arg
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# Fdbsql uses the :fdbsql database type.
|
43
|
-
def database_type
|
44
|
-
:fdbsql
|
45
|
-
end
|
46
|
-
|
47
|
-
# like PostgreSQL fdbsql uses SERIAL psuedo-type instead of AUTOINCREMENT for
|
48
|
-
# managing incrementing primary keys.
|
49
|
-
def serial_primary_key_options
|
50
|
-
{:primary_key => true, :serial => true, :type=>Integer}
|
51
|
-
end
|
52
|
-
|
53
|
-
# indexes are namespaced per table
|
54
|
-
def global_index_namespace?
|
55
|
-
false
|
56
|
-
end
|
57
|
-
|
58
|
-
# Return primary key for the given table.
|
59
|
-
def primary_key(table_name, opts=OPTS)
|
60
|
-
quoted_table = quote_schema_table(table_name)
|
61
|
-
Sequel.synchronize{return @primary_keys[quoted_table] if @primary_keys.has_key?(quoted_table)}
|
62
|
-
out_identifier, in_identifier = identifier_convertors(opts)
|
63
|
-
schema, table = schema_or_current_and_table(table_name, opts)
|
64
|
-
dataset = metadata_dataset.
|
65
|
-
select(:kc__column_name).
|
66
|
-
from(Sequel.as(:information_schema__key_column_usage, 'kc')).
|
67
|
-
join(Sequel.as(:information_schema__table_constraints, 'tc'),
|
68
|
-
[:table_name, :table_schema, :constraint_name]).
|
69
|
-
where(:kc__table_name => in_identifier.call(table),
|
70
|
-
:kc__table_schema => schema,
|
71
|
-
:tc__constraint_type => 'PRIMARY KEY')
|
72
|
-
value = dataset.map do |row|
|
73
|
-
out_identifier.call(row.delete(:column_name))
|
74
|
-
end
|
75
|
-
value = case value.size
|
76
|
-
when 0 then nil
|
77
|
-
when 1 then value.first
|
78
|
-
else value
|
79
|
-
end
|
80
|
-
Sequel.synchronize{@primary_keys[quoted_table] = value}
|
81
|
-
end
|
82
|
-
|
83
|
-
# the sql layer supports CREATE TABLE IF NOT EXISTS syntax,
|
84
|
-
def supports_create_table_if_not_exists?
|
85
|
-
true
|
86
|
-
end
|
87
|
-
|
88
|
-
# Fdbsql supports deferrable fk constraints
|
89
|
-
def supports_deferrable_foreign_key_constraints?
|
90
|
-
true
|
91
|
-
end
|
92
|
-
|
93
|
-
# the sql layer supports DROP TABLE IF EXISTS
|
94
|
-
def supports_drop_table_if_exists?
|
95
|
-
true
|
96
|
-
end
|
97
|
-
|
98
|
-
# Array of symbols specifying table names in the current database.
|
99
|
-
# The dataset used is yielded to the block if one is provided,
|
100
|
-
# otherwise, an array of symbols of table names is returned.
|
101
|
-
#
|
102
|
-
# Options:
|
103
|
-
# :qualify :: Return the tables as Sequel::SQL::QualifiedIdentifier instances,
|
104
|
-
# using the schema the table is located in as the qualifier.
|
105
|
-
# :schema :: The schema to search
|
106
|
-
# :server :: The server to use
|
107
|
-
def tables(opts=OPTS, &block)
|
108
|
-
tables_or_views('TABLE', opts, &block)
|
109
|
-
end
|
110
|
-
|
111
|
-
# Array of symbols specifying view names in the current database.
|
112
|
-
#
|
113
|
-
# Options:
|
114
|
-
# :qualify :: Return the views as Sequel::SQL::QualifiedIdentifier instances,
|
115
|
-
# using the schema the view is located in as the qualifier.
|
116
|
-
# :schema :: The schema to search
|
117
|
-
# :server :: The server to use
|
118
|
-
def views(opts=OPTS, &block)
|
119
|
-
tables_or_views('VIEW', opts, &block)
|
120
|
-
end
|
121
|
-
|
122
|
-
# Return full foreign key information, including
|
123
|
-
# Postgres returns hash like:
|
124
|
-
# {"b_e_fkey"=> {:name=>:b_e_fkey, :columns=>[:e], :on_update=>:no_action, :on_delete=>:no_action, :deferrable=>false, :table=>:a, :key=>[:c]}}
|
125
|
-
def foreign_key_list(table, opts=OPTS)
|
126
|
-
out_identifier, in_identifier = identifier_convertors(opts)
|
127
|
-
schema, table = schema_or_current_and_table(table, opts)
|
128
|
-
sql_table = in_identifier.call(table)
|
129
|
-
columns_dataset = metadata_dataset.
|
130
|
-
select(:tc__table_name___table_name,
|
131
|
-
:tc__table_schema___table_schema,
|
132
|
-
:tc__is_deferrable___deferrable,
|
133
|
-
:kc__column_name___column_name,
|
134
|
-
:kc__constraint_schema___schema,
|
135
|
-
:kc__constraint_name___name,
|
136
|
-
:rc__update_rule___on_update,
|
137
|
-
:rc__delete_rule___on_delete).
|
138
|
-
from(Sequel.as(:information_schema__table_constraints, 'tc')).
|
139
|
-
join(Sequel.as(:information_schema__key_column_usage, 'kc'),
|
140
|
-
[:constraint_schema, :constraint_name]).
|
141
|
-
join(Sequel.as(:information_schema__referential_constraints, 'rc'),
|
142
|
-
[:constraint_name, :constraint_schema]).
|
143
|
-
where(:tc__table_name => sql_table,
|
144
|
-
:tc__table_schema => schema,
|
145
|
-
:tc__constraint_type => 'FOREIGN KEY')
|
146
|
-
|
147
|
-
keys_dataset = metadata_dataset.
|
148
|
-
select(:rc__constraint_schema___schema,
|
149
|
-
:rc__constraint_name___name,
|
150
|
-
:kc__table_name___key_table,
|
151
|
-
:kc__column_name___key_column).
|
152
|
-
from(Sequel.as(:information_schema__table_constraints, 'tc')).
|
153
|
-
join(Sequel.as(:information_schema__referential_constraints, 'rc'),
|
154
|
-
[:constraint_schema, :constraint_name]).
|
155
|
-
join(Sequel.as(:information_schema__key_column_usage, 'kc'),
|
156
|
-
:kc__constraint_schema => :rc__unique_constraint_schema,
|
157
|
-
:kc__constraint_name => :rc__unique_constraint_name).
|
158
|
-
where(:tc__table_name => sql_table,
|
159
|
-
:tc__table_schema => schema,
|
160
|
-
:tc__constraint_type => 'FOREIGN KEY')
|
161
|
-
foreign_keys = {}
|
162
|
-
columns_dataset.each do |row|
|
163
|
-
foreign_key = foreign_keys.fetch(row[:name]) do |key|
|
164
|
-
foreign_keys[row[:name]] = row
|
165
|
-
row[:name] = out_identifier.call(row[:name])
|
166
|
-
row[:columns] = []
|
167
|
-
row[:key] = []
|
168
|
-
row
|
169
|
-
end
|
170
|
-
foreign_key[:columns] << out_identifier.call(row[:column_name])
|
171
|
-
end
|
172
|
-
keys_dataset.each do |row|
|
173
|
-
foreign_key = foreign_keys[row[:name]]
|
174
|
-
foreign_key[:table] = out_identifier.call(row[:key_table])
|
175
|
-
foreign_key[:key] << out_identifier.call(row[:key_column])
|
176
|
-
end
|
177
|
-
foreign_keys.values
|
178
|
-
end
|
179
|
-
|
180
|
-
# Return indexes for the table
|
181
|
-
# postgres returns:
|
182
|
-
# {:blah_blah_index=>{:columns=>[:n], :unique=>true, :deferrable=>nil},
|
183
|
-
# :items_n_a_index=>{:columns=>[:n, :a], :unique=>false, :deferrable=>nil}}
|
184
|
-
def indexes(table, opts=OPTS)
|
185
|
-
out_identifier, in_identifier = identifier_convertors(opts)
|
186
|
-
schema, table = schema_or_current_and_table(table, opts)
|
187
|
-
dataset = metadata_dataset.
|
188
|
-
select(:is__is_unique,
|
189
|
-
Sequel.as({:is__is_unique => 'YES'}, 'unique'),
|
190
|
-
:is__index_name,
|
191
|
-
:ic__column_name).
|
192
|
-
from(Sequel.as(:information_schema__indexes, 'is')).
|
193
|
-
join(Sequel.as(:information_schema__index_columns, 'ic'),
|
194
|
-
:ic__index_table_schema => :is__table_schema,
|
195
|
-
:ic__index_table_name => :is__table_name,
|
196
|
-
:ic__index_name => :is__index_name).
|
197
|
-
where(:is__table_schema => schema,
|
198
|
-
:is__table_name => in_identifier.call(table)).
|
199
|
-
exclude(:is__index_type => 'PRIMARY')
|
200
|
-
indexes = {}
|
201
|
-
dataset.each do |row|
|
202
|
-
index = indexes.fetch(out_identifier.call(row[:index_name])) do |key|
|
203
|
-
h = { :unique => row[:unique], :columns => [] }
|
204
|
-
indexes[key] = h
|
205
|
-
h
|
206
|
-
end
|
207
|
-
index[:columns] << out_identifier.call(row[:column_name])
|
208
|
-
end
|
209
|
-
indexes
|
210
|
-
end
|
211
|
-
|
212
|
-
private
|
213
|
-
|
214
|
-
# the literal methods put quotes around things, but when we bind a variable there shouldn't be quotes around it
|
215
|
-
# it should just be the timestamp, so we need whole new formats here.
|
216
|
-
BOUND_VARIABLE_TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S".freeze
|
217
|
-
BOUND_VARIABLE_SQLTIME_FORMAT = "%H:%M:%S".freeze
|
218
|
-
|
219
|
-
def adapter_initialize
|
220
|
-
@primary_keys = {}
|
221
|
-
# Postgres supports named types in the db, if we want to support anything that's not built in, this
|
222
|
-
# will have to be changed to not be a constant
|
223
|
-
@conversion_procs = Sequel::Postgres::PG_TYPES.dup
|
224
|
-
@conversion_procs[16] = Proc.new {|s| s == 'true'}
|
225
|
-
@conversion_procs[1184] = @conversion_procs[1114] = method(:to_application_timestamp)
|
226
|
-
@conversion_procs.freeze
|
227
|
-
end
|
228
|
-
|
229
|
-
def alter_table_op_sql(table, op)
|
230
|
-
quoted_name = quote_identifier(op[:name]) if op[:name]
|
231
|
-
case op[:op]
|
232
|
-
when :set_column_type
|
233
|
-
"ALTER COLUMN #{quoted_name} SET DATA TYPE #{type_literal(op)}"
|
234
|
-
when :set_column_null
|
235
|
-
"ALTER COLUMN #{quoted_name} #{op[:null] ? '' : 'NOT'} NULL"
|
236
|
-
else
|
237
|
-
super
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
# Convert exceptions raised from the block into DatabaseErrors.
|
242
|
-
def check_database_errors
|
243
|
-
begin
|
244
|
-
yield
|
245
|
-
rescue => e
|
246
|
-
raise_error(e, :classes=>CONVERTED_EXCEPTIONS)
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
def column_schema_normalize_default(default, type)
|
251
|
-
# the default value returned by schema parsing is not escaped or quoted
|
252
|
-
# in any way, it's just the value of the string
|
253
|
-
# the base implementation assumes it would come back "'my ''default'' value'"
|
254
|
-
# fdbsql returns "my 'default' value" (Not including double quotes for either)
|
255
|
-
return default
|
256
|
-
end
|
257
|
-
|
258
|
-
# FDBSQL requires parens around the SELECT, and the WITH DATA syntax.
|
259
|
-
def create_table_as_sql(name, sql, options)
|
260
|
-
"#{create_table_prefix_sql(name, options)} AS (#{sql}) WITH DATA"
|
261
|
-
end
|
262
|
-
|
263
|
-
def database_error_classes
|
264
|
-
CONVERTED_EXCEPTIONS
|
265
|
-
end
|
266
|
-
|
267
|
-
STALE_STATEMENT_SQLSTATE = '0A50A'.freeze
|
268
|
-
NOT_NULL_CONSTRAINT_SQLSTATES = %w'23502'.freeze.each(&:freeze)
|
269
|
-
FOREIGN_KEY_CONSTRAINT_SQLSTATES = %w'23503 23504'.freeze.each(&:freeze)
|
270
|
-
UNIQUE_CONSTRAINT_SQLSTATES = %w'23501'.freeze.each(&:freeze)
|
271
|
-
|
272
|
-
# Given the SQLState, return the appropriate DatabaseError subclass.
|
273
|
-
def database_specific_error_class_from_sqlstate(sqlstate)
|
274
|
-
# There is also a CheckConstraintViolation in Sequel, but the sql layer doesn't support check constraints
|
275
|
-
case sqlstate
|
276
|
-
when *NOT_NULL_CONSTRAINT_SQLSTATES
|
277
|
-
NotNullConstraintViolation
|
278
|
-
when *FOREIGN_KEY_CONSTRAINT_SQLSTATES
|
279
|
-
ForeignKeyConstraintViolation
|
280
|
-
when *UNIQUE_CONSTRAINT_SQLSTATES
|
281
|
-
UniqueConstraintViolation
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
|
-
# This is a fallback used by the base class if the sqlstate fails to figure out
|
286
|
-
# what error type it is.
|
287
|
-
DATABASE_ERROR_REGEXPS = [
|
288
|
-
# Add this check first, since otherwise it's possible for users to control
|
289
|
-
# which exception class is generated.
|
290
|
-
[/invalid input syntax/, DatabaseError],
|
291
|
-
# the rest of these are backups in case the sqlstate fails
|
292
|
-
[/[dD]uplicate key violates unique constraint/, UniqueConstraintViolation],
|
293
|
-
[/due (?:to|for) foreign key constraint/, ForeignKeyConstraintViolation],
|
294
|
-
[/NULL value not permitted/, NotNullConstraintViolation],
|
295
|
-
].freeze
|
296
|
-
|
297
|
-
def database_error_regexps
|
298
|
-
DATABASE_ERROR_REGEXPS
|
299
|
-
end
|
300
|
-
|
301
|
-
def identifier_convertors(opts=OPTS)
|
302
|
-
[output_identifier_meth(opts[:dataset]), input_identifier_meth(opts[:dataset])]
|
303
|
-
end
|
304
|
-
|
305
|
-
# Like PostgreSQL fdbsql folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
|
306
|
-
def identifier_input_method_default
|
307
|
-
nil
|
308
|
-
end
|
309
|
-
|
310
|
-
# Like PostgreSQL fdbsql folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on output.
|
311
|
-
def identifier_output_method_default
|
312
|
-
nil
|
313
|
-
end
|
314
|
-
|
315
|
-
# If the given type is DECIMAL with scale 0, say that it's an integer
|
316
|
-
def normalize_decimal_to_integer(type, scale)
|
317
|
-
if (type == 'DECIMAL' and scale == 0)
|
318
|
-
'integer'
|
319
|
-
else
|
320
|
-
type
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
|
-
# Remove the cached entries for primary keys and sequences when a table is
|
325
|
-
# changed.
|
326
|
-
def remove_cached_schema(table)
|
327
|
-
tab = quote_schema_table(table)
|
328
|
-
Sequel.synchronize do
|
329
|
-
@primary_keys.delete(tab)
|
330
|
-
end
|
331
|
-
super
|
332
|
-
end
|
333
|
-
|
334
|
-
def schema_or_current_and_table(table, opts=OPTS)
|
335
|
-
schema, table = schema_and_table(table)
|
336
|
-
schema = opts.fetch(:schema, schema || Sequel.lit('CURRENT_SCHEMA'))
|
337
|
-
[schema, table]
|
338
|
-
end
|
339
|
-
|
340
|
-
# returns an array of column information with each column being of the form:
|
341
|
-
# [:column_name, {:db_type=>"integer", :default=>nil, :allow_null=>false, :primary_key=>true, :type=>:integer}]
|
342
|
-
def schema_parse_table(table, opts = {})
|
343
|
-
out_identifier, in_identifier = identifier_convertors(opts)
|
344
|
-
schema, table = schema_or_current_and_table(table, opts)
|
345
|
-
dataset = metadata_dataset.
|
346
|
-
select(:c__column_name,
|
347
|
-
Sequel.as({:c__is_nullable => 'YES'}, 'allow_null'),
|
348
|
-
:c__column_default___default,
|
349
|
-
:c__data_type___db_type,
|
350
|
-
:c__character_maximum_length___max_length,
|
351
|
-
:c__numeric_scale,
|
352
|
-
Sequel.as({:tc__constraint_type => 'PRIMARY KEY'}, 'primary_key')).
|
353
|
-
from(Sequel.as(:information_schema__key_column_usage, 'kc')).
|
354
|
-
join(Sequel.as(:information_schema__table_constraints, 'tc'),
|
355
|
-
:tc__constraint_type => 'PRIMARY KEY',
|
356
|
-
:tc__table_name => :kc__table_name,
|
357
|
-
:tc__table_schema => :kc__table_schema,
|
358
|
-
:tc__constraint_name => :kc__constraint_name).
|
359
|
-
right_outer_join(Sequel.as(:information_schema__columns, 'c'),
|
360
|
-
[:table_name, :table_schema, :column_name]).
|
361
|
-
where(:c__table_name => in_identifier.call(table),
|
362
|
-
:c__table_schema => schema)
|
363
|
-
dataset.map do |row|
|
364
|
-
row[:default] = nil if blank_object?(row[:default])
|
365
|
-
row[:type] = schema_column_type(normalize_decimal_to_integer(row[:db_type], row[:numeric_scale]))
|
366
|
-
[out_identifier.call(row.delete(:column_name)), row]
|
367
|
-
end
|
368
|
-
end
|
369
|
-
|
370
|
-
def tables_or_views(type, opts, &block)
|
371
|
-
schema = opts[:schema] || Sequel.lit('CURRENT_SCHEMA')
|
372
|
-
m = output_identifier_meth
|
373
|
-
dataset = metadata_dataset.server(opts[:server]).select(:table_name).
|
374
|
-
from(Sequel.qualify('information_schema','tables')).
|
375
|
-
where(:table_schema => schema,
|
376
|
-
:table_type => type)
|
377
|
-
if block_given?
|
378
|
-
yield(dataset)
|
379
|
-
elsif opts[:qualify]
|
380
|
-
dataset.select_append(:table_schema).map{|r| Sequel.qualify(m.call(r[:table_schema]), m.call(r[:table_name])) }
|
381
|
-
else
|
382
|
-
dataset.map{|r| m.call(r[:table_name])}
|
383
|
-
end
|
384
|
-
end
|
385
|
-
|
386
|
-
# Handle bigserial type if :serial option is present
|
387
|
-
def type_literal_generic_bignum(column)
|
388
|
-
column[:serial] ? :bigserial : super
|
389
|
-
end
|
390
|
-
|
391
|
-
# Handle serial type if :serial option is present
|
392
|
-
def type_literal_generic_integer(column)
|
393
|
-
column[:serial] ? :serial : super
|
394
|
-
end
|
395
|
-
|
396
|
-
end
|
397
|
-
|
398
|
-
# Instance methods for datasets that connect to the FoundationDB SQL Layer.
|
399
|
-
module DatasetMethods
|
400
|
-
|
401
|
-
Dataset.def_sql_method(self, :delete, %w'with delete from using where returning')
|
402
|
-
Dataset.def_sql_method(self, :insert, %w'with insert into columns values returning')
|
403
|
-
Dataset.def_sql_method(self, :update, %w'with update table set from where returning')
|
404
|
-
|
405
|
-
# Shared methods for prepared statements used with the FoundationDB SQL Layer
|
406
|
-
module PreparedStatementMethods
|
407
|
-
|
408
|
-
def prepared_sql
|
409
|
-
return @prepared_sql if @prepared_sql
|
410
|
-
@opts[:returning] = insert_pk if @prepared_type == :insert
|
411
|
-
super
|
412
|
-
@prepared_sql
|
413
|
-
end
|
414
|
-
|
415
|
-
# Override insert action to use RETURNING if the server supports it.
|
416
|
-
def run
|
417
|
-
if @prepared_type == :insert
|
418
|
-
fetch_rows(prepared_sql){|r| return r.values.first}
|
419
|
-
else
|
420
|
-
super
|
421
|
-
end
|
422
|
-
end
|
423
|
-
end
|
424
|
-
|
425
|
-
# Emulate the bitwise operators.
|
426
|
-
def complex_expression_sql_append(sql, op, args)
|
427
|
-
case op
|
428
|
-
when :&, :|, :^, :<<, :>>, :'B~'
|
429
|
-
complex_expression_emulate_append(sql, op, args)
|
430
|
-
# REGEXP_OPERATORS = [:~, :'!~', :'~*', :'!~*']
|
431
|
-
when :'~'
|
432
|
-
function_sql_append(sql, SQL::Function.new(:REGEX, args.at(0), args.at(1)))
|
433
|
-
when :'!~'
|
434
|
-
sql << Sequel::Dataset::NOT_SPACE
|
435
|
-
function_sql_append(sql, SQL::Function.new(:REGEX, args.at(0), args.at(1)))
|
436
|
-
when :'~*'
|
437
|
-
function_sql_append(sql, SQL::Function.new(:IREGEX, args.at(0), args.at(1)))
|
438
|
-
when :'!~*'
|
439
|
-
sql << Sequel::Dataset::NOT_SPACE
|
440
|
-
function_sql_append(sql, SQL::Function.new(:IREGEX, args.at(0), args.at(1)))
|
441
|
-
else
|
442
|
-
super
|
443
|
-
end
|
444
|
-
end
|
445
|
-
|
446
|
-
# Insert given values into the database.
|
447
|
-
def insert(*values)
|
448
|
-
if @opts[:returning]
|
449
|
-
# Already know which columns to return, let the standard code handle it
|
450
|
-
super
|
451
|
-
elsif @opts[:sql] || @opts[:disable_insert_returning]
|
452
|
-
# Raw SQL used or RETURNING disabled, just use the default behavior
|
453
|
-
# and return nil since sequence is not known.
|
454
|
-
super
|
455
|
-
nil
|
456
|
-
else
|
457
|
-
# Force the use of RETURNING with the primary key value,
|
458
|
-
# unless it has been disabled.
|
459
|
-
returning(*insert_pk).insert(*values){|r| return r.values.first}
|
460
|
-
end
|
461
|
-
end
|
462
|
-
|
463
|
-
# Insert a record returning the record inserted. Always returns nil without
|
464
|
-
# inserting a query if disable_insert_returning is used.
|
465
|
-
def insert_select(*values)
|
466
|
-
unless @opts[:disable_insert_returning]
|
467
|
-
ds = opts[:returning] ? self : returning
|
468
|
-
ds.insert(*values){|r| return r}
|
469
|
-
end
|
470
|
-
end
|
471
|
-
|
472
|
-
# The SQL to use for an insert_select, adds a RETURNING clause to the insert
|
473
|
-
# unless the RETURNING clause is already present.
|
474
|
-
def insert_select_sql(*values)
|
475
|
-
ds = opts[:returning] ? self : returning
|
476
|
-
ds.insert_sql(*values)
|
477
|
-
end
|
478
|
-
|
479
|
-
# FDBSQL has functions to support regular expression pattern matching.
|
480
|
-
def supports_regexp?
|
481
|
-
true
|
482
|
-
end
|
483
|
-
|
484
|
-
# Returning is always supported.
|
485
|
-
def supports_returning?(type)
|
486
|
-
true
|
487
|
-
end
|
488
|
-
|
489
|
-
# FDBSQL truncates all seconds
|
490
|
-
def supports_timestamp_usecs?
|
491
|
-
false
|
492
|
-
end
|
493
|
-
|
494
|
-
# FDBSQL supports quoted function names
|
495
|
-
def supports_quoted_function_names?
|
496
|
-
true
|
497
|
-
end
|
498
|
-
|
499
|
-
private
|
500
|
-
|
501
|
-
# Use USING to specify additional tables in a delete query
|
502
|
-
def delete_using_sql(sql)
|
503
|
-
join_from_sql(:USING, sql)
|
504
|
-
end
|
505
|
-
|
506
|
-
# Return the primary key to use for RETURNING in an INSERT statement
|
507
|
-
def insert_pk
|
508
|
-
if (f = opts[:from]) && !f.empty?
|
509
|
-
case t = f.first
|
510
|
-
when Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier
|
511
|
-
if pk = db.primary_key(t)
|
512
|
-
pk
|
513
|
-
end
|
514
|
-
end
|
515
|
-
end
|
516
|
-
end
|
517
|
-
|
518
|
-
# For multiple table support, PostgreSQL requires at least
|
519
|
-
# two from tables, with joins allowed.
|
520
|
-
def join_from_sql(type, sql)
|
521
|
-
if(from = @opts[:from][1..-1]).empty?
|
522
|
-
raise(Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs') if @opts[:join]
|
523
|
-
else
|
524
|
-
sql << SPACE << type.to_s << SPACE
|
525
|
-
source_list_append(sql, from)
|
526
|
-
select_join_sql(sql)
|
527
|
-
end
|
528
|
-
end
|
529
|
-
|
530
|
-
# FDBSQL uses a preceding x for hex escaping strings
|
531
|
-
def literal_blob_append(sql, v)
|
532
|
-
if v.empty?
|
533
|
-
sql << "''"
|
534
|
-
else
|
535
|
-
sql << "x'#{v.unpack('H*').first}'"
|
536
|
-
end
|
537
|
-
end
|
538
|
-
|
539
|
-
# fdbsql does not support FOR UPDATE, because it's unnecessary with the transaction model
|
540
|
-
def select_lock_sql(sql)
|
541
|
-
@opts[:lock] == :update ? sql : super
|
542
|
-
end
|
543
|
-
|
544
|
-
# Use FROM to specify additional tables in an update query
|
545
|
-
def update_from_sql(sql)
|
546
|
-
join_from_sql(:FROM, sql)
|
547
|
-
end
|
548
|
-
end
|
549
|
-
end
|
550
|
-
end
|