sequel 4.22.0 → 4.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (214) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +22 -0
  3. data/README.rdoc +6 -0
  4. data/Rakefile +59 -81
  5. data/doc/migration.rdoc +2 -0
  6. data/doc/release_notes/4.23.0.txt +65 -0
  7. data/doc/sharding.rdoc +16 -14
  8. data/doc/testing.rdoc +61 -77
  9. data/lib/sequel/adapters/jdbc.rb +1 -0
  10. data/lib/sequel/adapters/mock.rb +0 -1
  11. data/lib/sequel/adapters/postgres.rb +1 -0
  12. data/lib/sequel/adapters/postgresql.rb +1 -0
  13. data/lib/sequel/adapters/shared/postgres.rb +3 -3
  14. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -0
  15. data/lib/sequel/connection_pool/threaded.rb +9 -1
  16. data/lib/sequel/database/connecting.rb +1 -1
  17. data/lib/sequel/database/transactions.rb +2 -1
  18. data/lib/sequel/dataset/prepared_statements.rb +1 -1
  19. data/lib/sequel/extensions/constraint_validations.rb +12 -12
  20. data/lib/sequel/extensions/date_arithmetic.rb +0 -4
  21. data/lib/sequel/extensions/pagination.rb +14 -2
  22. data/lib/sequel/extensions/pg_enum.rb +2 -2
  23. data/lib/sequel/extensions/pg_hstore.rb +1 -1
  24. data/lib/sequel/extensions/pg_json_ops.rb +2 -2
  25. data/lib/sequel/plugins/csv_serializer.rb +2 -0
  26. data/lib/sequel/plugins/delay_add_association.rb +50 -0
  27. data/lib/sequel/plugins/list.rb +2 -2
  28. data/lib/sequel/plugins/nested_attributes.rb +8 -28
  29. data/lib/sequel/plugins/update_refresh.rb +50 -0
  30. data/lib/sequel/plugins/validate_associated.rb +55 -0
  31. data/lib/sequel/version.rb +1 -1
  32. data/spec/adapters/db2_spec.rb +29 -29
  33. data/spec/adapters/firebird_spec.rb +97 -103
  34. data/spec/adapters/informix_spec.rb +25 -25
  35. data/spec/adapters/mssql_spec.rb +156 -172
  36. data/spec/adapters/mysql_spec.rb +334 -359
  37. data/spec/adapters/oracle_spec.rb +67 -69
  38. data/spec/adapters/postgres_spec.rb +1298 -1249
  39. data/spec/adapters/spec_helper.rb +2 -35
  40. data/spec/adapters/sqlanywhere_spec.rb +39 -39
  41. data/spec/adapters/sqlite_spec.rb +203 -200
  42. data/spec/bin_spec.rb +57 -59
  43. data/spec/core/connection_pool_spec.rb +402 -401
  44. data/spec/core/database_spec.rb +953 -944
  45. data/spec/core/dataset_spec.rb +2178 -2168
  46. data/spec/core/deprecated_spec.rb +19 -19
  47. data/spec/core/expression_filters_spec.rb +415 -415
  48. data/spec/core/mock_adapter_spec.rb +212 -212
  49. data/spec/core/object_graph_spec.rb +73 -73
  50. data/spec/core/placeholder_literalizer_spec.rb +71 -71
  51. data/spec/core/schema_generator_spec.rb +44 -44
  52. data/spec/core/schema_spec.rb +470 -472
  53. data/spec/core/spec_helper.rb +5 -20
  54. data/spec/core/version_spec.rb +2 -2
  55. data/spec/core_extensions_spec.rb +320 -320
  56. data/spec/extensions/accessed_columns_spec.rb +12 -12
  57. data/spec/extensions/active_model_spec.rb +3 -3
  58. data/spec/extensions/after_initialize_spec.rb +2 -2
  59. data/spec/extensions/arbitrary_servers_spec.rb +23 -23
  60. data/spec/extensions/association_dependencies_spec.rb +34 -34
  61. data/spec/extensions/association_pks_spec.rb +98 -98
  62. data/spec/extensions/association_proxies_spec.rb +33 -33
  63. data/spec/extensions/auto_validations_spec.rb +46 -46
  64. data/spec/extensions/blacklist_security_spec.rb +19 -18
  65. data/spec/extensions/blank_spec.rb +36 -36
  66. data/spec/extensions/boolean_readers_spec.rb +36 -36
  67. data/spec/extensions/caching_spec.rb +82 -82
  68. data/spec/extensions/class_table_inheritance_spec.rb +72 -72
  69. data/spec/extensions/column_conflicts_spec.rb +19 -14
  70. data/spec/extensions/column_select_spec.rb +19 -19
  71. data/spec/extensions/columns_introspection_spec.rb +43 -43
  72. data/spec/extensions/composition_spec.rb +64 -64
  73. data/spec/extensions/connection_validator_spec.rb +92 -90
  74. data/spec/extensions/constraint_validations_plugin_spec.rb +92 -92
  75. data/spec/extensions/constraint_validations_spec.rb +80 -80
  76. data/spec/extensions/core_refinements_spec.rb +220 -220
  77. data/spec/extensions/csv_serializer_spec.rb +44 -44
  78. data/spec/extensions/current_datetime_timestamp_spec.rb +8 -8
  79. data/spec/extensions/dataset_associations_spec.rb +65 -65
  80. data/spec/extensions/dataset_source_alias_spec.rb +16 -16
  81. data/spec/extensions/date_arithmetic_spec.rb +51 -58
  82. data/spec/extensions/defaults_setter_spec.rb +19 -19
  83. data/spec/extensions/delay_add_association_spec.rb +52 -0
  84. data/spec/extensions/dirty_spec.rb +51 -51
  85. data/spec/extensions/eager_each_spec.rb +8 -8
  86. data/spec/extensions/empty_array_ignore_nulls_spec.rb +10 -10
  87. data/spec/extensions/error_splitter_spec.rb +2 -2
  88. data/spec/extensions/error_sql_spec.rb +4 -4
  89. data/spec/extensions/eval_inspect_spec.rb +3 -3
  90. data/spec/extensions/filter_having_spec.rb +8 -8
  91. data/spec/extensions/force_encoding_spec.rb +30 -30
  92. data/spec/extensions/from_block_spec.rb +7 -7
  93. data/spec/extensions/graph_each_spec.rb +19 -19
  94. data/spec/extensions/hash_aliases_spec.rb +5 -5
  95. data/spec/extensions/hook_class_methods_spec.rb +100 -100
  96. data/spec/extensions/inflector_spec.rb +54 -54
  97. data/spec/extensions/input_transformer_spec.rb +10 -10
  98. data/spec/extensions/insert_returning_select_spec.rb +8 -8
  99. data/spec/extensions/instance_filters_spec.rb +26 -26
  100. data/spec/extensions/instance_hooks_spec.rb +85 -85
  101. data/spec/extensions/json_serializer_spec.rb +68 -68
  102. data/spec/extensions/lazy_attributes_spec.rb +49 -49
  103. data/spec/extensions/list_spec.rb +77 -75
  104. data/spec/extensions/looser_typecasting_spec.rb +16 -16
  105. data/spec/extensions/many_through_many_spec.rb +627 -627
  106. data/spec/extensions/meta_def_spec.rb +7 -7
  107. data/spec/extensions/migration_spec.rb +217 -217
  108. data/spec/extensions/modification_detection_spec.rb +20 -20
  109. data/spec/extensions/mssql_optimistic_locking_spec.rb +21 -21
  110. data/spec/extensions/named_timezones_spec.rb +18 -18
  111. data/spec/extensions/nested_attributes_spec.rb +107 -107
  112. data/spec/extensions/null_dataset_spec.rb +24 -24
  113. data/spec/extensions/optimistic_locking_spec.rb +21 -21
  114. data/spec/extensions/pagination_spec.rb +52 -52
  115. data/spec/extensions/pg_array_associations_spec.rb +273 -273
  116. data/spec/extensions/pg_array_ops_spec.rb +52 -52
  117. data/spec/extensions/pg_array_spec.rb +152 -152
  118. data/spec/extensions/pg_enum_spec.rb +13 -13
  119. data/spec/extensions/pg_hstore_ops_spec.rb +63 -63
  120. data/spec/extensions/pg_hstore_spec.rb +84 -84
  121. data/spec/extensions/pg_inet_spec.rb +15 -15
  122. data/spec/extensions/pg_interval_spec.rb +29 -29
  123. data/spec/extensions/pg_json_ops_spec.rb +86 -84
  124. data/spec/extensions/pg_json_spec.rb +104 -104
  125. data/spec/extensions/pg_loose_count_spec.rb +6 -6
  126. data/spec/extensions/pg_range_ops_spec.rb +24 -24
  127. data/spec/extensions/pg_range_spec.rb +143 -143
  128. data/spec/extensions/pg_row_ops_spec.rb +14 -14
  129. data/spec/extensions/pg_row_plugin_spec.rb +12 -12
  130. data/spec/extensions/pg_row_spec.rb +118 -118
  131. data/spec/extensions/pg_static_cache_updater_spec.rb +28 -28
  132. data/spec/extensions/pg_typecast_on_load_spec.rb +21 -21
  133. data/spec/extensions/prepared_statements_associations_spec.rb +42 -42
  134. data/spec/extensions/prepared_statements_safe_spec.rb +18 -18
  135. data/spec/extensions/prepared_statements_spec.rb +28 -28
  136. data/spec/extensions/prepared_statements_with_pk_spec.rb +11 -11
  137. data/spec/extensions/pretty_table_spec.rb +16 -16
  138. data/spec/extensions/query_literals_spec.rb +37 -37
  139. data/spec/extensions/query_spec.rb +32 -32
  140. data/spec/extensions/rcte_tree_spec.rb +141 -141
  141. data/spec/extensions/round_timestamps_spec.rb +21 -21
  142. data/spec/extensions/schema_caching_spec.rb +8 -8
  143. data/spec/extensions/schema_dumper_spec.rb +78 -78
  144. data/spec/extensions/schema_spec.rb +31 -27
  145. data/spec/extensions/scissors_spec.rb +3 -3
  146. data/spec/extensions/select_remove_spec.rb +14 -14
  147. data/spec/extensions/sequel_3_dataset_methods_spec.rb +28 -28
  148. data/spec/extensions/serialization_modification_detection_spec.rb +33 -33
  149. data/spec/extensions/serialization_spec.rb +79 -78
  150. data/spec/extensions/server_block_spec.rb +17 -17
  151. data/spec/extensions/set_overrides_spec.rb +30 -30
  152. data/spec/extensions/sharding_spec.rb +65 -65
  153. data/spec/extensions/shared_caching_spec.rb +29 -29
  154. data/spec/extensions/single_table_inheritance_spec.rb +79 -79
  155. data/spec/extensions/skip_create_refresh_spec.rb +3 -3
  156. data/spec/extensions/spec_helper.rb +4 -29
  157. data/spec/extensions/split_array_nil_spec.rb +9 -9
  158. data/spec/extensions/split_values_spec.rb +7 -7
  159. data/spec/extensions/sql_expr_spec.rb +32 -32
  160. data/spec/extensions/static_cache_spec.rb +123 -123
  161. data/spec/extensions/string_date_time_spec.rb +34 -34
  162. data/spec/extensions/string_stripper_spec.rb +15 -15
  163. data/spec/extensions/subclasses_spec.rb +31 -31
  164. data/spec/extensions/table_select_spec.rb +15 -15
  165. data/spec/extensions/tactical_eager_loading_spec.rb +23 -23
  166. data/spec/extensions/thread_local_timezones_spec.rb +13 -13
  167. data/spec/extensions/timestamps_spec.rb +40 -40
  168. data/spec/extensions/to_dot_spec.rb +34 -34
  169. data/spec/extensions/touch_spec.rb +52 -52
  170. data/spec/extensions/tree_spec.rb +72 -72
  171. data/spec/extensions/typecast_on_load_spec.rb +25 -25
  172. data/spec/extensions/unlimited_update_spec.rb +2 -2
  173. data/spec/extensions/update_or_create_spec.rb +36 -36
  174. data/spec/extensions/update_primary_key_spec.rb +35 -35
  175. data/spec/extensions/update_refresh_spec.rb +41 -0
  176. data/spec/extensions/validate_associated_spec.rb +52 -0
  177. data/spec/extensions/validation_class_methods_spec.rb +314 -317
  178. data/spec/extensions/validation_helpers_spec.rb +195 -195
  179. data/spec/extensions/xml_serializer_spec.rb +48 -48
  180. data/spec/guards_helper.rb +55 -0
  181. data/spec/integration/associations_test.rb +1089 -1088
  182. data/spec/integration/database_test.rb +29 -29
  183. data/spec/integration/dataset_test.rb +661 -661
  184. data/spec/integration/eager_loader_test.rb +147 -147
  185. data/spec/integration/migrator_test.rb +122 -122
  186. data/spec/integration/model_test.rb +70 -70
  187. data/spec/integration/plugin_test.rb +682 -640
  188. data/spec/integration/prepared_statement_test.rb +172 -172
  189. data/spec/integration/schema_test.rb +245 -245
  190. data/spec/integration/spec_helper.rb +1 -64
  191. data/spec/integration/timezone_test.rb +17 -17
  192. data/spec/integration/transaction_test.rb +87 -87
  193. data/spec/integration/type_test.rb +33 -33
  194. data/spec/model/association_reflection_spec.rb +130 -121
  195. data/spec/model/associations_spec.rb +1112 -1113
  196. data/spec/model/base_spec.rb +197 -196
  197. data/spec/model/class_dataset_methods_spec.rb +118 -118
  198. data/spec/model/dataset_methods_spec.rb +49 -49
  199. data/spec/model/eager_loading_spec.rb +705 -702
  200. data/spec/model/hooks_spec.rb +169 -168
  201. data/spec/model/inflector_spec.rb +5 -5
  202. data/spec/model/model_spec.rb +287 -297
  203. data/spec/model/plugins_spec.rb +47 -47
  204. data/spec/model/record_spec.rb +534 -535
  205. data/spec/model/spec_helper.rb +3 -21
  206. data/spec/model/validations_spec.rb +72 -70
  207. data/spec/spec_config.rb +8 -0
  208. metadata +41 -9
  209. data/lib/sequel/adapters/fdbsql.rb +0 -286
  210. data/lib/sequel/adapters/jdbc/fdbsql.rb +0 -66
  211. data/lib/sequel/adapters/openbase.rb +0 -54
  212. data/lib/sequel/adapters/shared/fdbsql.rb +0 -550
  213. data/spec/adapters/fdbsql_spec.rb +0 -429
  214. 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