sequel 3.21.0 → 3.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +855 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +23 -7
- data/Rakefile +42 -24
- data/bin/sequel +26 -8
- data/doc/active_record.rdoc +1 -0
- data/doc/advanced_associations.rdoc +171 -11
- data/doc/association_basics.rdoc +293 -43
- data/doc/dataset_basics.rdoc +5 -5
- data/doc/dataset_filtering.rdoc +8 -0
- data/doc/mass_assignment.rdoc +56 -0
- data/doc/migration.rdoc +85 -552
- data/doc/model_hooks.rdoc +78 -27
- data/doc/object_model.rdoc +541 -0
- data/doc/opening_databases.rdoc +97 -50
- data/doc/prepared_statements.rdoc +40 -14
- data/doc/querying.rdoc +28 -4
- data/doc/reflection.rdoc +30 -4
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/release_notes/3.31.0.txt +146 -0
- data/doc/release_notes/3.32.0.txt +202 -0
- data/doc/release_notes/3.33.0.txt +157 -0
- data/doc/release_notes/3.34.0.txt +671 -0
- data/doc/release_notes/3.35.0.txt +144 -0
- data/doc/release_notes/3.36.0.txt +245 -0
- data/doc/schema_modification.rdoc +572 -0
- data/doc/sharding.rdoc +95 -3
- data/doc/testing.rdoc +129 -0
- data/doc/thread_safety.rdoc +17 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/virtual_rows.rdoc +51 -2
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +17 -9
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +17 -11
- data/lib/sequel/adapters/do/postgres.rb +1 -57
- data/lib/sequel/adapters/do/sqlite.rb +0 -10
- data/lib/sequel/adapters/do.rb +18 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +453 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/db2.rb +47 -0
- data/lib/sequel/adapters/jdbc/derby.rb +287 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
- data/lib/sequel/adapters/jdbc/h2.rb +73 -31
- data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
- data/lib/sequel/adapters/jdbc/informix.rb +21 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
- data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
- data/lib/sequel/adapters/jdbc/progress.rb +21 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
- data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +265 -94
- data/lib/sequel/adapters/mock.rb +361 -0
- data/lib/sequel/adapters/mysql.rb +118 -222
- data/lib/sequel/adapters/mysql2.rb +58 -57
- data/lib/sequel/adapters/odbc/mssql.rb +15 -7
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +349 -53
- data/lib/sequel/adapters/postgres.rb +356 -107
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +342 -0
- data/lib/sequel/adapters/shared/firebird.rb +221 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +335 -118
- data/lib/sequel/adapters/shared/mysql.rb +340 -76
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
- data/lib/sequel/adapters/shared/oracle.rb +243 -45
- data/lib/sequel/adapters/shared/postgres.rb +524 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +265 -89
- data/lib/sequel/adapters/sqlite.rb +101 -39
- data/lib/sequel/adapters/swift/mysql.rb +9 -12
- data/lib/sequel/adapters/swift/postgres.rb +9 -74
- data/lib/sequel/adapters/swift/sqlite.rb +8 -6
- data/lib/sequel/adapters/swift.rb +8 -7
- data/lib/sequel/adapters/tinytds.rb +161 -21
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
- data/lib/sequel/ast_transformer.rb +194 -0
- data/lib/sequel/connection_pool/sharded_single.rb +11 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
- data/lib/sequel/connection_pool/single.rb +5 -0
- data/lib/sequel/connection_pool/threaded.rb +38 -14
- data/lib/sequel/connection_pool.rb +10 -2
- data/lib/sequel/core.rb +118 -11
- data/lib/sequel/database/connecting.rb +38 -28
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +63 -2
- data/lib/sequel/database/logging.rb +7 -2
- data/lib/sequel/database/misc.rb +150 -46
- data/lib/sequel/database/query.rb +215 -65
- data/lib/sequel/database/schema_generator.rb +64 -20
- data/lib/sequel/database/schema_methods.rb +212 -54
- data/lib/sequel/dataset/actions.rb +322 -85
- data/lib/sequel/dataset/features.rb +96 -8
- data/lib/sequel/dataset/graph.rb +38 -11
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +12 -7
- data/lib/sequel/dataset/prepared_statements.rb +68 -30
- data/lib/sequel/dataset/query.rb +237 -44
- data/lib/sequel/dataset/sql.rb +698 -278
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/_pretty_table.rb +79 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
- data/lib/sequel/extensions/migration.rb +97 -11
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/null_dataset.rb +90 -0
- data/lib/sequel/extensions/pg_array.rb +460 -0
- data/lib/sequel/extensions/pg_array_ops.rb +220 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
- data/lib/sequel/extensions/pg_hstore.rb +296 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
- data/lib/sequel/extensions/pg_inet.rb +89 -0
- data/lib/sequel/extensions/pg_json.rb +178 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
- data/lib/sequel/extensions/pretty_table.rb +5 -71
- data/lib/sequel/extensions/query.rb +1 -1
- data/lib/sequel/extensions/query_literals.rb +79 -0
- data/lib/sequel/extensions/schema_caching.rb +76 -0
- data/lib/sequel/extensions/schema_dumper.rb +251 -37
- data/lib/sequel/extensions/select_remove.rb +35 -0
- data/lib/sequel/extensions/server_block.rb +139 -0
- data/lib/sequel/extensions/sql_expr.rb +4 -110
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +1187 -393
- data/lib/sequel/model/base.rb +578 -170
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model.rb +30 -11
- data/lib/sequel/no_core_ext.rb +2 -0
- data/lib/sequel/plugins/active_model.rb +13 -1
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/caching.rb +25 -18
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/composition.rb +44 -12
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/dirty.rb +214 -0
- data/lib/sequel/plugins/eager_each.rb +59 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/plugins/identity_map.rb +123 -8
- data/lib/sequel/plugins/instance_filters.rb +10 -0
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +58 -6
- data/lib/sequel/plugins/list.rb +12 -2
- data/lib/sequel/plugins/many_through_many.rb +79 -28
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
- data/lib/sequel/plugins/nested_attributes.rb +139 -63
- data/lib/sequel/plugins/optimistic_locking.rb +8 -0
- data/lib/sequel/plugins/prepared_statements.rb +167 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/schema.rb +7 -2
- data/lib/sequel/plugins/serialization.rb +98 -49
- data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
- data/lib/sequel/plugins/static_cache.rb +99 -0
- data/lib/sequel/plugins/subclasses.rb +10 -2
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +526 -59
- data/lib/sequel/timezones.rb +57 -38
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/firebird_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +142 -40
- data/spec/adapters/mysql_spec.rb +380 -234
- data/spec/adapters/oracle_spec.rb +67 -77
- data/spec/adapters/postgres_spec.rb +1050 -212
- data/spec/adapters/spec_helper.rb +8 -5
- data/spec/adapters/sqlite_spec.rb +139 -27
- data/spec/core/connection_pool_spec.rb +196 -93
- data/spec/core/database_spec.rb +879 -510
- data/spec/core/dataset_spec.rb +1415 -1086
- data/spec/core/expression_filters_spec.rb +554 -109
- data/spec/core/mock_adapter_spec.rb +449 -0
- data/spec/core/object_graph_spec.rb +86 -114
- data/spec/core/schema_generator_spec.rb +4 -4
- data/spec/core/schema_spec.rb +482 -38
- data/spec/core/spec_helper.rb +6 -48
- data/spec/extensions/active_model_spec.rb +13 -0
- data/spec/extensions/arbitrary_servers_spec.rb +110 -0
- data/spec/extensions/association_autoreloading_spec.rb +18 -10
- data/spec/extensions/association_dependencies_spec.rb +15 -25
- data/spec/extensions/association_pks_spec.rb +66 -32
- data/spec/extensions/association_proxies_spec.rb +4 -4
- data/spec/extensions/caching_spec.rb +47 -51
- data/spec/extensions/class_table_inheritance_spec.rb +31 -83
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/dirty_spec.rb +155 -0
- data/spec/extensions/eager_each_spec.rb +34 -0
- data/spec/extensions/force_encoding_spec.rb +4 -2
- data/spec/extensions/hook_class_methods_spec.rb +69 -92
- data/spec/extensions/identity_map_spec.rb +131 -22
- data/spec/extensions/inflector_spec.rb +0 -4
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +72 -0
- data/spec/extensions/json_serializer_spec.rb +49 -6
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +48 -39
- data/spec/extensions/many_through_many_spec.rb +283 -250
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
- data/spec/extensions/migration_spec.rb +140 -35
- data/spec/extensions/named_timezones_spec.rb +29 -11
- data/spec/extensions/nested_attributes_spec.rb +251 -89
- data/spec/extensions/null_dataset_spec.rb +85 -0
- data/spec/extensions/optimistic_locking_spec.rb +24 -21
- data/spec/extensions/pg_array_ops_spec.rb +105 -0
- data/spec/extensions/pg_array_spec.rb +196 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
- data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
- data/spec/extensions/pg_hstore_spec.rb +195 -0
- data/spec/extensions/pg_inet_spec.rb +44 -0
- data/spec/extensions/pg_json_spec.rb +101 -0
- data/spec/extensions/pg_statement_cache_spec.rb +209 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
- data/spec/extensions/prepared_statements_spec.rb +87 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +7 -6
- data/spec/extensions/query_literals_spec.rb +168 -0
- data/spec/extensions/query_spec.rb +6 -0
- data/spec/extensions/rcte_tree_spec.rb +50 -43
- data/spec/extensions/schema_caching_spec.rb +41 -0
- data/spec/extensions/schema_dumper_spec.rb +429 -22
- data/spec/extensions/schema_spec.rb +25 -21
- data/spec/extensions/select_remove_spec.rb +38 -0
- data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
- data/spec/extensions/serialization_spec.rb +85 -43
- data/spec/extensions/server_block_spec.rb +90 -0
- data/spec/extensions/sharding_spec.rb +69 -143
- data/spec/extensions/single_table_inheritance_spec.rb +23 -29
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +22 -61
- data/spec/extensions/sql_expr_spec.rb +29 -60
- data/spec/extensions/static_cache_spec.rb +145 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +4 -7
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/typecast_on_load_spec.rb +9 -6
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +30 -51
- data/spec/extensions/validation_helpers_spec.rb +16 -35
- data/spec/extensions/xml_serializer_spec.rb +29 -4
- data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
- data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
- data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
- data/spec/integration/associations_test.rb +659 -21
- data/spec/integration/database_test.rb +9 -3
- data/spec/integration/dataset_test.rb +605 -91
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/migrator_test.rb +2 -3
- data/spec/integration/model_test.rb +81 -2
- data/spec/integration/plugin_test.rb +454 -143
- data/spec/integration/prepared_statement_test.rb +263 -128
- data/spec/integration/schema_test.rb +309 -43
- data/spec/integration/spec_helper.rb +40 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +18 -4
- data/spec/model/association_reflection_spec.rb +201 -7
- data/spec/model/associations_spec.rb +1067 -661
- data/spec/model/base_spec.rb +329 -95
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +677 -676
- data/spec/model/hooks_spec.rb +349 -67
- data/spec/model/model_spec.rb +240 -168
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +490 -232
- data/spec/model/spec_helper.rb +14 -71
- data/spec/model/validations_spec.rb +11 -0
- data/spec/rcov.opts +2 -0
- metadata +178 -80
|
@@ -12,7 +12,7 @@ rescue LoadError => e
|
|
|
12
12
|
class PGconn
|
|
13
13
|
unless method_defined?(:escape_string)
|
|
14
14
|
if self.respond_to?(:escape)
|
|
15
|
-
# If there is no escape_string
|
|
15
|
+
# If there is no escape_string instance method, but there is an
|
|
16
16
|
# escape class method, use that instead.
|
|
17
17
|
def escape_string(str)
|
|
18
18
|
Sequel::Postgres.force_standard_strings ? str.gsub("'", "''") : self.class.escape(str)
|
|
@@ -88,28 +88,49 @@ module Sequel
|
|
|
88
88
|
module Postgres
|
|
89
89
|
CONVERTED_EXCEPTIONS << PGError
|
|
90
90
|
|
|
91
|
+
NAN = 0.0/0.0
|
|
92
|
+
PLUS_INFINITY = 1.0/0.0
|
|
93
|
+
MINUS_INFINITY = -1.0/0.0
|
|
94
|
+
NAN_STR = 'NaN'.freeze
|
|
95
|
+
PLUS_INFINITY_STR = 'Infinity'.freeze
|
|
96
|
+
MINUS_INFINITY_STR = '-Infinity'.freeze
|
|
97
|
+
TRUE_STR = 't'.freeze
|
|
98
|
+
DASH_STR = '-'.freeze
|
|
99
|
+
|
|
91
100
|
TYPE_TRANSLATOR = tt = Class.new do
|
|
92
|
-
def boolean(s) s ==
|
|
101
|
+
def boolean(s) s == TRUE_STR end
|
|
93
102
|
def bytea(s) ::Sequel::SQL::Blob.new(Adapter.unescape_bytea(s)) end
|
|
94
103
|
def integer(s) s.to_i end
|
|
95
|
-
def float(s)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
104
|
+
def float(s)
|
|
105
|
+
case s
|
|
106
|
+
when NAN_STR
|
|
107
|
+
NAN
|
|
108
|
+
when PLUS_INFINITY_STR
|
|
109
|
+
PLUS_INFINITY
|
|
110
|
+
when MINUS_INFINITY_STR
|
|
111
|
+
MINUS_INFINITY
|
|
112
|
+
else
|
|
113
|
+
s.to_f
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
def date(s) ::Date.new(*s.split(DASH_STR).map{|x| x.to_i}) end
|
|
101
117
|
end.new
|
|
102
118
|
|
|
119
|
+
# Hash with type name strings/symbols and callable values for converting PostgreSQL types.
|
|
120
|
+
# Non-builtin types that don't have fixed numbers should use this to register
|
|
121
|
+
# conversion procs.
|
|
122
|
+
PG_NAMED_TYPES = {} unless defined?(PG_NAMED_TYPES)
|
|
123
|
+
|
|
103
124
|
# Hash with integer keys and callable values for converting PostgreSQL types.
|
|
104
|
-
PG_TYPES = {}
|
|
125
|
+
PG_TYPES = {} unless defined?(PG_TYPES)
|
|
126
|
+
|
|
105
127
|
{
|
|
106
128
|
[16] => tt.method(:boolean),
|
|
107
129
|
[17] => tt.method(:bytea),
|
|
108
130
|
[20, 21, 22, 23, 26] => tt.method(:integer),
|
|
109
131
|
[700, 701] => tt.method(:float),
|
|
110
|
-
[790, 1700] =>
|
|
111
|
-
[1083, 1266] =>
|
|
112
|
-
[1114, 1184] => tt.method(:timestamp)
|
|
132
|
+
[790, 1700] => ::BigDecimal.method(:new),
|
|
133
|
+
[1083, 1266] => ::Sequel.method(:string_to_time),
|
|
113
134
|
}.each do |k,v|
|
|
114
135
|
k.each{|n| PG_TYPES[n] = v}
|
|
115
136
|
end
|
|
@@ -123,7 +144,7 @@ module Sequel
|
|
|
123
144
|
|
|
124
145
|
# Modify the type translator for the date type depending on the value given.
|
|
125
146
|
def self.use_iso_date_format=(v)
|
|
126
|
-
PG_TYPES[1082] = TYPE_TRANSLATOR.method(
|
|
147
|
+
PG_TYPES[1082] = v ? TYPE_TRANSLATOR.method(:date) : Sequel.method(:string_to_date)
|
|
127
148
|
@use_iso_date_format = v
|
|
128
149
|
end
|
|
129
150
|
self.use_iso_date_format = true
|
|
@@ -131,7 +152,8 @@ module Sequel
|
|
|
131
152
|
# PGconn subclass for connection specific methods used with the
|
|
132
153
|
# pg, postgres, or postgres-pr driver.
|
|
133
154
|
class Adapter < ::PGconn
|
|
134
|
-
|
|
155
|
+
DISCONNECT_ERROR_RE = /\Acould not receive data from server: Software caused connection abort/
|
|
156
|
+
|
|
135
157
|
self.translate_results = false if respond_to?(:translate_results=)
|
|
136
158
|
|
|
137
159
|
# Hash of prepared statements for this connection. Keys are
|
|
@@ -139,31 +161,22 @@ module Sequel
|
|
|
139
161
|
# are SQL strings.
|
|
140
162
|
attr_reader(:prepared_statements) if SEQUEL_POSTGRES_USES_PG
|
|
141
163
|
|
|
142
|
-
# Apply connection settings for this connection. Current sets
|
|
143
|
-
# the date style to ISO in order make Date object creation in ruby faster,
|
|
144
|
-
# if Postgres.use_iso_date_format is true.
|
|
145
|
-
def apply_connection_settings
|
|
146
|
-
super
|
|
147
|
-
if Postgres.use_iso_date_format
|
|
148
|
-
sql = "SET DateStyle = 'ISO'"
|
|
149
|
-
execute(sql)
|
|
150
|
-
end
|
|
151
|
-
@prepared_statements = {} if SEQUEL_POSTGRES_USES_PG
|
|
152
|
-
end
|
|
153
|
-
|
|
154
164
|
# Raise a Sequel::DatabaseDisconnectError if a PGError is raised and
|
|
155
165
|
# the connection status cannot be determined or it is not OK.
|
|
156
166
|
def check_disconnect_errors
|
|
157
167
|
begin
|
|
158
168
|
yield
|
|
159
169
|
rescue PGError =>e
|
|
170
|
+
disconnect = false
|
|
160
171
|
begin
|
|
161
172
|
s = status
|
|
162
173
|
rescue PGError
|
|
163
|
-
|
|
174
|
+
disconnect = true
|
|
164
175
|
end
|
|
165
176
|
status_ok = (s == Adapter::CONNECTION_OK)
|
|
166
|
-
|
|
177
|
+
disconnect ||= !status_ok
|
|
178
|
+
disconnect ||= e.message =~ DISCONNECT_ERROR_RE
|
|
179
|
+
disconnect ? raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError)) : raise
|
|
167
180
|
ensure
|
|
168
181
|
block if status_ok
|
|
169
182
|
end
|
|
@@ -172,19 +185,21 @@ module Sequel
|
|
|
172
185
|
# Execute the given SQL with this connection. If a block is given,
|
|
173
186
|
# yield the results, otherwise, return the number of changed rows.
|
|
174
187
|
def execute(sql, args=nil)
|
|
175
|
-
|
|
188
|
+
args = args.map{|v| @db.bound_variable_arg(v, self)} if args
|
|
189
|
+
q = check_disconnect_errors{execute_query(sql, args)}
|
|
176
190
|
begin
|
|
177
191
|
block_given? ? yield(q) : q.cmd_tuples
|
|
178
192
|
ensure
|
|
179
|
-
q.clear
|
|
193
|
+
q.clear if q
|
|
180
194
|
end
|
|
181
195
|
end
|
|
182
196
|
|
|
183
197
|
private
|
|
184
|
-
|
|
185
|
-
# Return the
|
|
186
|
-
|
|
187
|
-
|
|
198
|
+
|
|
199
|
+
# Return the PGResult object that is returned by executing the given
|
|
200
|
+
# sql and args.
|
|
201
|
+
def execute_query(sql, args)
|
|
202
|
+
@db.log_yield(sql, args){args ? async_exec(sql, args) : async_exec(sql)}
|
|
188
203
|
end
|
|
189
204
|
end
|
|
190
205
|
|
|
@@ -192,30 +207,77 @@ module Sequel
|
|
|
192
207
|
# pg, postgres, or postgres-pr driver.
|
|
193
208
|
class Database < Sequel::Database
|
|
194
209
|
include Sequel::Postgres::DatabaseMethods
|
|
210
|
+
|
|
211
|
+
INFINITE_TIMESTAMP_STRINGS = ['infinity'.freeze, '-infinity'.freeze].freeze
|
|
212
|
+
INFINITE_DATETIME_VALUES = ([PLUS_INFINITY, MINUS_INFINITY] + INFINITE_TIMESTAMP_STRINGS).freeze
|
|
195
213
|
|
|
196
214
|
set_adapter_scheme :postgres
|
|
215
|
+
|
|
216
|
+
# A hash of conversion procs, keyed by type integer (oid) and
|
|
217
|
+
# having callable values for the conversion proc for that type.
|
|
218
|
+
attr_reader :conversion_procs
|
|
219
|
+
|
|
220
|
+
# Whether infinite timestamps should be converted on retrieval. By default, no
|
|
221
|
+
# conversion is done, so an error is raised if you attempt to retrieve an infinite
|
|
222
|
+
# timestamp. You can set this to :nil to convert to nil, :string to leave
|
|
223
|
+
# as a string, or :float to convert to an infinite float.
|
|
224
|
+
attr_accessor :convert_infinite_timestamps
|
|
197
225
|
|
|
198
226
|
# Add the primary_keys and primary_key_sequences instance variables,
|
|
199
227
|
# so we can get the correct return values for inserted rows.
|
|
200
228
|
def initialize(*args)
|
|
201
229
|
super
|
|
230
|
+
@convert_infinite_timestamps = false
|
|
202
231
|
@primary_keys = {}
|
|
203
232
|
@primary_key_sequences = {}
|
|
204
233
|
end
|
|
205
234
|
|
|
235
|
+
# Convert given argument so that it can be used directly by pg. Currently, pg doesn't
|
|
236
|
+
# handle fractional seconds in Time/DateTime or blobs with "\0", and it won't ever
|
|
237
|
+
# handle Sequel::SQLTime values correctly. Only public for use by the adapter, shouldn't
|
|
238
|
+
# be used by external code.
|
|
239
|
+
def bound_variable_arg(arg, conn)
|
|
240
|
+
case arg
|
|
241
|
+
when Sequel::SQL::Blob
|
|
242
|
+
conn.escape_bytea(arg)
|
|
243
|
+
when Sequel::SQLTime
|
|
244
|
+
literal(arg)
|
|
245
|
+
when DateTime, Time
|
|
246
|
+
literal(arg)
|
|
247
|
+
else
|
|
248
|
+
arg
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
206
252
|
# Connects to the database. In addition to the standard database
|
|
207
253
|
# options, using the :encoding or :charset option changes the
|
|
208
|
-
# client encoding for the connection
|
|
254
|
+
# client encoding for the connection, :connect_timeout is a
|
|
255
|
+
# connection timeout in seconds, and :sslmode sets whether postgres's
|
|
256
|
+
# sslmode. :connect_timeout and :ssl_mode are only supported if the pg
|
|
257
|
+
# driver is used.
|
|
209
258
|
def connect(server)
|
|
210
259
|
opts = server_opts(server)
|
|
211
|
-
conn =
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
260
|
+
conn = if SEQUEL_POSTGRES_USES_PG
|
|
261
|
+
connection_params = {
|
|
262
|
+
:host => opts[:host],
|
|
263
|
+
:port => opts[:port] || 5432,
|
|
264
|
+
:dbname => opts[:database],
|
|
265
|
+
:user => opts[:user],
|
|
266
|
+
:password => opts[:password],
|
|
267
|
+
:connect_timeout => opts[:connect_timeout] || 20,
|
|
268
|
+
:sslmode => opts[:sslmode]
|
|
269
|
+
}.delete_if { |key, value| blank_object?(value) }
|
|
270
|
+
Adapter.connect(connection_params)
|
|
271
|
+
else
|
|
272
|
+
Adapter.connect(
|
|
273
|
+
(opts[:host] unless blank_object?(opts[:host])),
|
|
274
|
+
opts[:port] || 5432,
|
|
275
|
+
nil, '',
|
|
276
|
+
opts[:database],
|
|
277
|
+
opts[:user],
|
|
278
|
+
opts[:password]
|
|
279
|
+
)
|
|
280
|
+
end
|
|
219
281
|
if encoding = opts[:encoding] || opts[:charset]
|
|
220
282
|
if conn.respond_to?(:set_client_encoding)
|
|
221
283
|
conn.set_client_encoding(encoding)
|
|
@@ -223,38 +285,163 @@ module Sequel
|
|
|
223
285
|
conn.async_exec("set client_encoding to '#{encoding}'")
|
|
224
286
|
end
|
|
225
287
|
end
|
|
226
|
-
conn.db
|
|
227
|
-
conn.
|
|
288
|
+
conn.instance_variable_set(:@db, self)
|
|
289
|
+
conn.instance_variable_set(:@prepared_statements, {}) if SEQUEL_POSTGRES_USES_PG
|
|
290
|
+
connection_configuration_sqls.each{|sql| conn.execute(sql)}
|
|
291
|
+
@conversion_procs ||= get_conversion_procs(conn)
|
|
228
292
|
conn
|
|
229
293
|
end
|
|
230
294
|
|
|
231
|
-
# Return instance of Sequel::Postgres::Dataset with the given options.
|
|
232
|
-
def dataset(opts = nil)
|
|
233
|
-
Postgres::Dataset.new(self, opts)
|
|
234
|
-
end
|
|
235
|
-
|
|
236
295
|
# Execute the given SQL with the given args on an available connection.
|
|
237
296
|
def execute(sql, opts={}, &block)
|
|
238
|
-
check_database_errors
|
|
239
|
-
|
|
240
|
-
|
|
297
|
+
synchronize(opts[:server]){|conn| check_database_errors{_execute(conn, sql, opts, &block)}}
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
if SEQUEL_POSTGRES_USES_PG
|
|
301
|
+
# +copy_table+ uses PostgreSQL's +COPY+ SQL statement to return formatted
|
|
302
|
+
# results directly to the caller. This method is only supported if pg is the
|
|
303
|
+
# underlying ruby driver. This method should only be called if you want
|
|
304
|
+
# results returned to the client. If you are using +COPY FROM+ or +COPY TO+
|
|
305
|
+
# with a filename, you should just use +run+ instead of this method. This
|
|
306
|
+
# method does not currently support +COPY FROM STDIN+, but that may be supported
|
|
307
|
+
# in the future.
|
|
308
|
+
#
|
|
309
|
+
# The table argument supports the following types:
|
|
310
|
+
#
|
|
311
|
+
# String :: Uses the first argument directly as literal SQL. If you are using
|
|
312
|
+
# a version of PostgreSQL before 9.0, you will probably want to
|
|
313
|
+
# use a string if you are using any options at all, as the syntax
|
|
314
|
+
# Sequel uses for options is only compatible with PostgreSQL 9.0+.
|
|
315
|
+
# Dataset :: Uses a query instead of a table name when copying.
|
|
316
|
+
# other :: Uses a table name (usually a symbol) when copying.
|
|
317
|
+
#
|
|
318
|
+
# The following options are respected:
|
|
319
|
+
#
|
|
320
|
+
# :format :: The format to use. text is the default, so this should be :csv or :binary.
|
|
321
|
+
# :options :: An options SQL string to use, which should contain comma separated options.
|
|
322
|
+
# :server :: The server on which to run the query.
|
|
323
|
+
#
|
|
324
|
+
# If a block is provided, the method continually yields to the block, one yield
|
|
325
|
+
# per row. If a block is not provided, a single string is returned with all
|
|
326
|
+
# of the data.
|
|
327
|
+
def copy_table(table, opts={})
|
|
328
|
+
sql = if table.is_a?(String)
|
|
329
|
+
sql = table
|
|
330
|
+
else
|
|
331
|
+
if opts[:options] || opts[:format]
|
|
332
|
+
options = " ("
|
|
333
|
+
options << "FORMAT #{opts[:format]}" if opts[:format]
|
|
334
|
+
options << "#{', ' if opts[:format]}#{opts[:options]}" if opts[:options]
|
|
335
|
+
options << ')'
|
|
336
|
+
end
|
|
337
|
+
table = if table.is_a?(::Sequel::Dataset)
|
|
338
|
+
"(#{table.sql})"
|
|
339
|
+
else
|
|
340
|
+
literal(table)
|
|
341
|
+
end
|
|
342
|
+
sql = "COPY #{table} TO STDOUT#{options}"
|
|
343
|
+
end
|
|
344
|
+
synchronize(opts[:server]) do |conn|
|
|
345
|
+
conn.execute(sql)
|
|
346
|
+
begin
|
|
347
|
+
if block_given?
|
|
348
|
+
while buf = conn.get_copy_data
|
|
349
|
+
yield buf
|
|
350
|
+
end
|
|
351
|
+
nil
|
|
352
|
+
else
|
|
353
|
+
b = ''
|
|
354
|
+
b << buf while buf = conn.get_copy_data
|
|
355
|
+
b
|
|
356
|
+
end
|
|
357
|
+
ensure
|
|
358
|
+
raise DatabaseDisconnectError, "disconnecting as a partial COPY may leave the connection in an unusable state" if buf
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# Listens on the given channel (or multiple channels if channel is an array), waiting for notifications.
|
|
364
|
+
# After a notification is received, or the timeout has passed, stops listening to the channel. Options:
|
|
365
|
+
#
|
|
366
|
+
# :after_listen :: An object that responds to +call+ that is called with the underlying connection after the LISTEN
|
|
367
|
+
# statement is sent, but before the connection starts waiting for notifications.
|
|
368
|
+
# :loop :: Whether to continually wait for notifications, instead of just waiting for a single
|
|
369
|
+
# notification. If this option is given, a block must be provided. If this object responds to call, it is
|
|
370
|
+
# called with the underlying connection after each notification is received (after the block is called).
|
|
371
|
+
# If a :timeout option is used, and a callable object is given, the object will also be called if the
|
|
372
|
+
# timeout expires. If :loop is used and you want to stop listening, you can either break from inside the
|
|
373
|
+
# block given to #listen, or you can throw :stop from inside the :loop object's call method or the block.
|
|
374
|
+
# :server :: The server on which to listen, if the sharding support is being used.
|
|
375
|
+
# :timeout :: How long to wait for a notification, in seconds (can provide a float value for
|
|
376
|
+
# fractional seconds). If not given or nil, waits indefinitely.
|
|
377
|
+
#
|
|
378
|
+
# This method is only supported if pg is used as the underlying ruby driver. It returns the
|
|
379
|
+
# channel the notification was sent to (as a string), unless :loop was used, in which case it returns nil.
|
|
380
|
+
# If a block is given, it is yielded 3 arguments:
|
|
381
|
+
# * the channel the notification was sent to (as a string)
|
|
382
|
+
# * the backend pid of the notifier (as an integer),
|
|
383
|
+
# * and the payload of the notification (as a string or nil).
|
|
384
|
+
def listen(channels, opts={}, &block)
|
|
385
|
+
check_database_errors do
|
|
386
|
+
synchronize(opts[:server]) do |conn|
|
|
387
|
+
begin
|
|
388
|
+
channels = Array(channels)
|
|
389
|
+
channels.each{|channel| conn.execute("LISTEN #{channel}")}
|
|
390
|
+
opts[:after_listen].call(conn) if opts[:after_listen]
|
|
391
|
+
timeout = opts[:timeout] ? [opts[:timeout]] : []
|
|
392
|
+
if l = opts[:loop]
|
|
393
|
+
raise Error, 'calling #listen with :loop requires a block' unless block
|
|
394
|
+
loop_call = l.respond_to?(:call)
|
|
395
|
+
catch(:stop) do
|
|
396
|
+
loop do
|
|
397
|
+
conn.wait_for_notify(*timeout, &block)
|
|
398
|
+
l.call(conn) if loop_call
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
nil
|
|
402
|
+
else
|
|
403
|
+
conn.wait_for_notify(*timeout, &block)
|
|
404
|
+
end
|
|
405
|
+
ensure
|
|
406
|
+
conn.execute("UNLISTEN *")
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
end
|
|
241
410
|
end
|
|
242
411
|
end
|
|
243
|
-
|
|
244
|
-
#
|
|
245
|
-
#
|
|
246
|
-
def
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
412
|
+
|
|
413
|
+
# Reset the database's conversion procs, requires a server query if there
|
|
414
|
+
# any named types.
|
|
415
|
+
def reset_conversion_procs
|
|
416
|
+
synchronize{|conn| @conversion_procs = get_conversion_procs(conn)}
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
# If convert_infinite_timestamps is true and the value is infinite, return an appropriate
|
|
420
|
+
# value based on the convert_infinite_timestamps setting.
|
|
421
|
+
def to_application_timestamp(value)
|
|
422
|
+
if c = convert_infinite_timestamps
|
|
423
|
+
case value
|
|
424
|
+
when *INFINITE_TIMESTAMP_STRINGS
|
|
425
|
+
infinite_timestamp_value(value)
|
|
426
|
+
else
|
|
427
|
+
super
|
|
252
428
|
end
|
|
429
|
+
else
|
|
430
|
+
super
|
|
253
431
|
end
|
|
254
432
|
end
|
|
255
|
-
|
|
433
|
+
|
|
256
434
|
private
|
|
257
|
-
|
|
435
|
+
|
|
436
|
+
# Execute the given SQL string or prepared statement on the connection object.
|
|
437
|
+
def _execute(conn, sql, opts, &block)
|
|
438
|
+
if sql.is_a?(Symbol)
|
|
439
|
+
execute_prepared_statement(conn, sql, opts, &block)
|
|
440
|
+
else
|
|
441
|
+
conn.execute(sql, opts[:arguments], &block)
|
|
442
|
+
end
|
|
443
|
+
end
|
|
444
|
+
|
|
258
445
|
# Convert exceptions raised from the block into DatabaseErrors.
|
|
259
446
|
def check_database_errors
|
|
260
447
|
begin
|
|
@@ -264,6 +451,13 @@ module Sequel
|
|
|
264
451
|
end
|
|
265
452
|
end
|
|
266
453
|
|
|
454
|
+
# Set the DateStyle to ISO if configured, for faster date parsing.
|
|
455
|
+
def connection_configuration_sqls
|
|
456
|
+
sqls = super
|
|
457
|
+
sqls << "SET DateStyle = 'ISO'" if Postgres.use_iso_date_format
|
|
458
|
+
sqls
|
|
459
|
+
end
|
|
460
|
+
|
|
267
461
|
# Disconnect given connection
|
|
268
462
|
def disconnect_connection(conn)
|
|
269
463
|
begin
|
|
@@ -278,31 +472,81 @@ module Sequel
|
|
|
278
472
|
# has prepared a statement with the same name and different SQL,
|
|
279
473
|
# deallocate that statement first and then prepare this statement.
|
|
280
474
|
# If a block is given, yield the result, otherwise, return the number
|
|
281
|
-
# of rows changed.
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
ps = prepared_statements[name]
|
|
475
|
+
# of rows changed.
|
|
476
|
+
def execute_prepared_statement(conn, name, opts={}, &block)
|
|
477
|
+
ps = prepared_statement(name)
|
|
285
478
|
sql = ps.prepared_sql
|
|
286
479
|
ps_name = name.to_s
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
480
|
+
|
|
481
|
+
if args = opts[:arguments]
|
|
482
|
+
args = args.map{|arg| bound_variable_arg(arg, conn)}
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
unless conn.prepared_statements[ps_name] == sql
|
|
486
|
+
conn.execute("DEALLOCATE #{ps_name}") if conn.prepared_statements.include?(ps_name)
|
|
487
|
+
conn.prepared_statements[ps_name] = sql
|
|
488
|
+
conn.check_disconnect_errors{log_yield("PREPARE #{ps_name} AS #{sql}"){conn.prepare(ps_name, sql)}}
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
log_sql = "EXECUTE #{ps_name}"
|
|
492
|
+
if ps.log_sql
|
|
493
|
+
log_sql << " ("
|
|
494
|
+
log_sql << sql
|
|
495
|
+
log_sql << ")"
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
q = conn.check_disconnect_errors{log_yield(log_sql, args){conn.exec_prepared(ps_name, args)}}
|
|
499
|
+
begin
|
|
500
|
+
block_given? ? yield(q) : q.cmd_tuples
|
|
501
|
+
ensure
|
|
502
|
+
q.clear
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
# Return the conversion procs hash to use for this database.
|
|
507
|
+
def get_conversion_procs(conn)
|
|
508
|
+
procs = PG_TYPES.dup
|
|
509
|
+
procs[1184] = procs[1114] = method(:to_application_timestamp)
|
|
510
|
+
unless (pgnt = PG_NAMED_TYPES).empty?
|
|
511
|
+
conn.execute("SELECT oid, typname FROM pg_type where typtype = 'b' AND typname IN ('#{pgnt.keys.map{|type| conn.escape_string(type.to_s)}.join("', '")}')") do |res|
|
|
512
|
+
res.ntuples.times do |i|
|
|
513
|
+
procs[res.getvalue(i, 0).to_i] ||= pgnt[res.getvalue(i, 1).untaint.to_sym]
|
|
292
514
|
end
|
|
293
|
-
conn.prepared_statements[ps_name] = sql
|
|
294
|
-
conn.check_disconnect_errors{log_yield("PREPARE #{ps_name} AS #{sql}"){conn.prepare(ps_name, sql)}}
|
|
295
515
|
end
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
516
|
+
end
|
|
517
|
+
procs
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
# Return an appropriate value for the given infinite timestamp string.
|
|
521
|
+
def infinite_timestamp_value(value)
|
|
522
|
+
case convert_infinite_timestamps
|
|
523
|
+
when :nil
|
|
524
|
+
nil
|
|
525
|
+
when :string
|
|
526
|
+
value
|
|
527
|
+
else
|
|
528
|
+
value == 'infinity' ? PLUS_INFINITY : MINUS_INFINITY
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
# Don't log, since logging is done by the underlying connection.
|
|
533
|
+
def log_connection_execute(conn, sql)
|
|
534
|
+
conn.execute(sql)
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
# If the value is an infinite value (either an infinite float or a string returned by
|
|
538
|
+
# by PostgreSQL for an infinite timestamp), return it without converting it if
|
|
539
|
+
# convert_infinite_timestamps is set.
|
|
540
|
+
def typecast_value_datetime(value)
|
|
541
|
+
if convert_infinite_timestamps
|
|
542
|
+
case value
|
|
543
|
+
when *INFINITE_DATETIME_VALUES
|
|
544
|
+
value
|
|
299
545
|
else
|
|
300
|
-
|
|
301
|
-
block_given? ? yield(q) : q.cmd_tuples
|
|
302
|
-
ensure
|
|
303
|
-
q.clear
|
|
304
|
-
end
|
|
546
|
+
super
|
|
305
547
|
end
|
|
548
|
+
else
|
|
549
|
+
super
|
|
306
550
|
end
|
|
307
551
|
end
|
|
308
552
|
end
|
|
@@ -311,12 +555,15 @@ module Sequel
|
|
|
311
555
|
# postgres-pr driver.
|
|
312
556
|
class Dataset < Sequel::Dataset
|
|
313
557
|
include Sequel::Postgres::DatasetMethods
|
|
558
|
+
|
|
559
|
+
Database::DatasetClass = self
|
|
560
|
+
APOS = Sequel::Dataset::APOS
|
|
314
561
|
|
|
315
562
|
# Yield all rows returned by executing the given SQL and converting
|
|
316
563
|
# the types.
|
|
317
|
-
def fetch_rows(sql
|
|
318
|
-
return cursor_fetch_rows(sql
|
|
319
|
-
execute(sql){|res| yield_hash_rows(res, fetch_rows_set_cols(res)
|
|
564
|
+
def fetch_rows(sql)
|
|
565
|
+
return cursor_fetch_rows(sql){|h| yield h} if @opts[:cursor]
|
|
566
|
+
execute(sql){|res| yield_hash_rows(res, fetch_rows_set_cols(res)){|h| yield h}}
|
|
320
567
|
end
|
|
321
568
|
|
|
322
569
|
# Uses a cursor for fetching records, instead of fetching the entire result
|
|
@@ -372,11 +619,17 @@ module Sequel
|
|
|
372
619
|
end
|
|
373
620
|
LiteralString.new("#{prepared_arg_placeholder}#{i}#{"::#{type}" if type}")
|
|
374
621
|
end
|
|
622
|
+
|
|
623
|
+
# Always assume a prepared argument.
|
|
624
|
+
def prepared_arg?(k)
|
|
625
|
+
true
|
|
626
|
+
end
|
|
375
627
|
end
|
|
376
628
|
|
|
377
629
|
# Allow use of bind arguments for PostgreSQL using the pg driver.
|
|
378
630
|
module BindArgumentMethods
|
|
379
631
|
include ArgumentMapper
|
|
632
|
+
include ::Sequel::Postgres::DatasetMethods::PreparedStatementMethods
|
|
380
633
|
|
|
381
634
|
private
|
|
382
635
|
|
|
@@ -389,18 +642,18 @@ module Sequel
|
|
|
389
642
|
def execute_dui(sql, opts={}, &block)
|
|
390
643
|
super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
391
644
|
end
|
|
392
|
-
|
|
393
|
-
# Same as execute, explicit due to intricacies of alias and super.
|
|
394
|
-
def execute_insert(sql, opts={}, &block)
|
|
395
|
-
super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
396
|
-
end
|
|
397
645
|
end
|
|
398
646
|
|
|
399
647
|
# Allow use of server side prepared statements for PostgreSQL using the
|
|
400
648
|
# pg driver.
|
|
401
649
|
module PreparedStatementMethods
|
|
402
650
|
include BindArgumentMethods
|
|
403
|
-
|
|
651
|
+
|
|
652
|
+
# Raise a more obvious error if you attempt to call a unnamed prepared statement.
|
|
653
|
+
def call(*)
|
|
654
|
+
raise Error, "Cannot call prepared statement without a name" if prepared_statement_name.nil?
|
|
655
|
+
super
|
|
656
|
+
end
|
|
404
657
|
|
|
405
658
|
private
|
|
406
659
|
|
|
@@ -414,11 +667,6 @@ module Sequel
|
|
|
414
667
|
def execute_dui(sql, opts={}, &block)
|
|
415
668
|
super(prepared_statement_name, opts, &block)
|
|
416
669
|
end
|
|
417
|
-
|
|
418
|
-
# Same as execute, explicit due to intricacies of alias and super.
|
|
419
|
-
def execute_insert(sql, opts={}, &block)
|
|
420
|
-
super(prepared_statement_name, opts, &block)
|
|
421
|
-
end
|
|
422
670
|
end
|
|
423
671
|
|
|
424
672
|
# Execute the given type of statement with the hash of values.
|
|
@@ -427,7 +675,7 @@ module Sequel
|
|
|
427
675
|
ps.extend(BindArgumentMethods)
|
|
428
676
|
ps.call(bind_vars, &block)
|
|
429
677
|
end
|
|
430
|
-
|
|
678
|
+
|
|
431
679
|
# Prepare the given type of statement with the given name, and store
|
|
432
680
|
# it in the database to be called later.
|
|
433
681
|
def prepare(type, name=nil, *values)
|
|
@@ -435,7 +683,7 @@ module Sequel
|
|
|
435
683
|
ps.extend(PreparedStatementMethods)
|
|
436
684
|
if name
|
|
437
685
|
ps.prepared_statement_name = name
|
|
438
|
-
db.
|
|
686
|
+
db.set_prepared_statement(name, ps)
|
|
439
687
|
end
|
|
440
688
|
ps
|
|
441
689
|
end
|
|
@@ -452,7 +700,7 @@ module Sequel
|
|
|
452
700
|
private
|
|
453
701
|
|
|
454
702
|
# Use a cursor to fetch groups of records at a time, yielding them to the block.
|
|
455
|
-
def cursor_fetch_rows(sql
|
|
703
|
+
def cursor_fetch_rows(sql)
|
|
456
704
|
server_opts = {:server=>@opts[:server] || :read_only}
|
|
457
705
|
db.transaction(server_opts) do
|
|
458
706
|
begin
|
|
@@ -464,12 +712,12 @@ module Sequel
|
|
|
464
712
|
# Load columns only in the first fetch, so subsequent fetches are faster
|
|
465
713
|
execute(fetch_sql) do |res|
|
|
466
714
|
cols = fetch_rows_set_cols(res)
|
|
467
|
-
yield_hash_rows(res, cols
|
|
715
|
+
yield_hash_rows(res, cols){|h| yield h}
|
|
468
716
|
return if res.ntuples < rows_per_fetch
|
|
469
717
|
end
|
|
470
718
|
loop do
|
|
471
719
|
execute(fetch_sql) do |res|
|
|
472
|
-
yield_hash_rows(res, cols
|
|
720
|
+
yield_hash_rows(res, cols){|h| yield h}
|
|
473
721
|
return if res.ntuples < rows_per_fetch
|
|
474
722
|
end
|
|
475
723
|
end
|
|
@@ -483,21 +731,22 @@ module Sequel
|
|
|
483
731
|
# field numers, type conversion procs, and name symbol arrays.
|
|
484
732
|
def fetch_rows_set_cols(res)
|
|
485
733
|
cols = []
|
|
734
|
+
procs = db.conversion_procs
|
|
486
735
|
res.nfields.times do |fieldnum|
|
|
487
|
-
cols << [fieldnum,
|
|
736
|
+
cols << [fieldnum, procs[res.ftype(fieldnum)], output_identifier(res.fname(fieldnum))]
|
|
488
737
|
end
|
|
489
738
|
@columns = cols.map{|c| c.at(2)}
|
|
490
739
|
cols
|
|
491
740
|
end
|
|
492
741
|
|
|
493
742
|
# Use the driver's escape_bytea
|
|
494
|
-
def
|
|
495
|
-
db.synchronize{|c|
|
|
743
|
+
def literal_blob_append(sql, v)
|
|
744
|
+
sql << APOS << db.synchronize{|c| c.escape_bytea(v)} << APOS
|
|
496
745
|
end
|
|
497
746
|
|
|
498
747
|
# Use the driver's escape_string
|
|
499
|
-
def
|
|
500
|
-
db.synchronize{|c|
|
|
748
|
+
def literal_string_append(sql, v)
|
|
749
|
+
sql << APOS << db.synchronize{|c| c.escape_string(v)} << APOS
|
|
501
750
|
end
|
|
502
751
|
|
|
503
752
|
# For each row in the result set, yield a hash with column name symbol
|