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