sequel 3.21.0 → 3.41.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 +1131 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +106 -65
- data/Rakefile +59 -27
- data/bin/sequel +50 -26
- data/doc/active_record.rdoc +68 -52
- data/doc/advanced_associations.rdoc +201 -75
- data/doc/association_basics.rdoc +327 -79
- data/doc/bin_sequel.rdoc +144 -0
- 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 +576 -0
- data/doc/opening_databases.rdoc +121 -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/release_notes/3.40.0.txt +73 -0
- data/doc/release_notes/3.41.0.txt +155 -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/access.rb +334 -0
- data/lib/sequel/adapters/ado/mssql.rb +18 -12
- data/lib/sequel/adapters/ado.rb +69 -21
- data/lib/sequel/adapters/amalgalite.rb +17 -14
- data/lib/sequel/adapters/cubrid.rb +143 -0
- data/lib/sequel/adapters/db2.rb +178 -85
- data/lib/sequel/adapters/dbi.rb +19 -21
- 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 +20 -39
- data/lib/sequel/adapters/firebird.rb +33 -210
- data/lib/sequel/adapters/ibmdb.rb +452 -0
- data/lib/sequel/adapters/informix.rb +6 -29
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/cubrid.rb +52 -0
- data/lib/sequel/adapters/jdbc/db2.rb +78 -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 +68 -31
- data/lib/sequel/adapters/jdbc/hsqldb.rb +189 -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 +16 -28
- 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 +72 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +308 -114
- data/lib/sequel/adapters/mock.rb +372 -0
- data/lib/sequel/adapters/mysql.rb +130 -230
- data/lib/sequel/adapters/mysql2.rb +64 -63
- data/lib/sequel/adapters/odbc/mssql.rb +17 -9
- data/lib/sequel/adapters/odbc.rb +29 -10
- data/lib/sequel/adapters/openbase.rb +7 -11
- data/lib/sequel/adapters/oracle.rb +364 -57
- data/lib/sequel/adapters/postgres.rb +379 -145
- data/lib/sequel/adapters/shared/access.rb +236 -11
- data/lib/sequel/adapters/shared/cubrid.rb +216 -0
- data/lib/sequel/adapters/shared/db2.rb +353 -0
- data/lib/sequel/adapters/shared/firebird.rb +231 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +412 -116
- data/lib/sequel/adapters/shared/mysql.rb +396 -89
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +166 -0
- data/lib/sequel/adapters/shared/oracle.rb +262 -45
- data/lib/sequel/adapters/shared/postgres.rb +688 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +317 -96
- data/lib/sequel/adapters/sqlite.rb +109 -46
- 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 +31 -27
- data/lib/sequel/adapters/tinytds.rb +177 -25
- 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/split_alter_table.rb +36 -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 +23 -13
- data/lib/sequel/connection_pool/sharded_threaded.rb +64 -13
- data/lib/sequel/connection_pool/single.rb +11 -3
- data/lib/sequel/connection_pool/threaded.rb +69 -20
- data/lib/sequel/connection_pool.rb +20 -18
- data/lib/sequel/core.rb +143 -11
- data/lib/sequel/database/connecting.rb +66 -29
- 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 +192 -51
- data/lib/sequel/database/query.rb +299 -114
- data/lib/sequel/database/schema_generator.rb +103 -36
- data/lib/sequel/database/schema_methods.rb +310 -69
- 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 +322 -68
- data/lib/sequel/dataset/sql.rb +716 -293
- 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/connection_validator.rb +109 -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 +539 -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 +334 -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 +212 -0
- data/lib/sequel/extensions/pg_range.rb +513 -0
- data/lib/sequel/extensions/pg_range_ops.rb +150 -0
- data/lib/sequel/extensions/pg_row.rb +576 -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 +137 -16
- 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 +77 -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 +168 -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 +57 -12
- 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/touch.rb +18 -6
- 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 +2 -1
- data/lib/sequel/plugins/validation_helpers.rb +57 -4
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +721 -141
- 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 +53 -39
- data/spec/adapters/mssql_spec.rb +261 -66
- data/spec/adapters/mysql_spec.rb +597 -352
- data/spec/adapters/oracle_spec.rb +92 -117
- data/spec/adapters/postgres_spec.rb +2103 -341
- data/spec/adapters/spec_helper.rb +16 -6
- data/spec/adapters/sqlite_spec.rb +237 -76
- data/spec/core/connection_pool_spec.rb +270 -141
- data/spec/core/database_spec.rb +980 -524
- data/spec/core/dataset_spec.rb +1658 -1243
- data/spec/core/expression_filters_spec.rb +741 -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 +227 -33
- 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/connection_validator_spec.rb +118 -0
- 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 +324 -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 +200 -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 +340 -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 +65 -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 +53 -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 +77 -68
- data/spec/extensions/validation_helpers_spec.rb +48 -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 +25 -5
- data/spec/integration/dataset_test.rb +729 -141
- 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 +871 -160
- data/spec/integration/prepared_statement_test.rb +272 -128
- data/spec/integration/schema_test.rb +377 -57
- data/spec/integration/spec_helper.rb +49 -32
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +40 -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 +223 -81
- 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/
|
|
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,52 +104,45 @@ 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
|
-
rescue PGError =>e
|
|
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
|
-
block if status_ok
|
|
124
|
+
block if status_ok && !disconnect
|
|
169
125
|
end
|
|
170
126
|
end
|
|
171
127
|
|
|
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,228 @@ 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
|
-
#
|
|
232
|
-
def
|
|
233
|
-
|
|
232
|
+
# Disconnect given connection
|
|
233
|
+
def disconnect_connection(conn)
|
|
234
|
+
begin
|
|
235
|
+
conn.finish
|
|
236
|
+
rescue PGError
|
|
237
|
+
end
|
|
234
238
|
end
|
|
235
239
|
|
|
236
240
|
# Execute the given SQL with the given args on an available connection.
|
|
237
241
|
def execute(sql, opts={}, &block)
|
|
238
|
-
check_database_errors
|
|
239
|
-
|
|
240
|
-
|
|
242
|
+
synchronize(opts[:server]){|conn| check_database_errors{_execute(conn, sql, opts, &block)}}
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
if SEQUEL_POSTGRES_USES_PG
|
|
246
|
+
# +copy_table+ uses PostgreSQL's +COPY TO STDOUT+ SQL statement to return formatted
|
|
247
|
+
# results directly to the caller. This method is only supported if pg is the
|
|
248
|
+
# underlying ruby driver. This method should only be called if you want
|
|
249
|
+
# results returned to the client. If you are using +COPY TO+
|
|
250
|
+
# with a filename, you should just use +run+ instead of this method.
|
|
251
|
+
#
|
|
252
|
+
# The table argument supports the following types:
|
|
253
|
+
#
|
|
254
|
+
# String :: Uses the first argument directly as literal SQL. If you are using
|
|
255
|
+
# a version of PostgreSQL before 9.0, you will probably want to
|
|
256
|
+
# use a string if you are using any options at all, as the syntax
|
|
257
|
+
# Sequel uses for options is only compatible with PostgreSQL 9.0+.
|
|
258
|
+
# Dataset :: Uses a query instead of a table name when copying.
|
|
259
|
+
# other :: Uses a table name (usually a symbol) when copying.
|
|
260
|
+
#
|
|
261
|
+
# The following options are respected:
|
|
262
|
+
#
|
|
263
|
+
# :format :: The format to use. text is the default, so this should be :csv or :binary.
|
|
264
|
+
# :options :: An options SQL string to use, which should contain comma separated options.
|
|
265
|
+
# :server :: The server on which to run the query.
|
|
266
|
+
#
|
|
267
|
+
# If a block is provided, the method continually yields to the block, one yield
|
|
268
|
+
# per row. If a block is not provided, a single string is returned with all
|
|
269
|
+
# of the data.
|
|
270
|
+
def copy_table(table, opts={})
|
|
271
|
+
sql = if table.is_a?(String)
|
|
272
|
+
sql = table
|
|
273
|
+
else
|
|
274
|
+
if opts[:options] || opts[:format]
|
|
275
|
+
options = " ("
|
|
276
|
+
options << "FORMAT #{opts[:format]}" if opts[:format]
|
|
277
|
+
options << "#{', ' if opts[:format]}#{opts[:options]}" if opts[:options]
|
|
278
|
+
options << ')'
|
|
279
|
+
end
|
|
280
|
+
table = if table.is_a?(::Sequel::Dataset)
|
|
281
|
+
"(#{table.sql})"
|
|
282
|
+
else
|
|
283
|
+
literal(table)
|
|
284
|
+
end
|
|
285
|
+
sql = "COPY #{table} TO STDOUT#{options}"
|
|
286
|
+
end
|
|
287
|
+
synchronize(opts[:server]) do |conn|
|
|
288
|
+
conn.execute(sql)
|
|
289
|
+
begin
|
|
290
|
+
if block_given?
|
|
291
|
+
while buf = conn.get_copy_data
|
|
292
|
+
yield buf
|
|
293
|
+
end
|
|
294
|
+
nil
|
|
295
|
+
else
|
|
296
|
+
b = ''
|
|
297
|
+
b << buf while buf = conn.get_copy_data
|
|
298
|
+
b
|
|
299
|
+
end
|
|
300
|
+
ensure
|
|
301
|
+
raise DatabaseDisconnectError, "disconnecting as a partial COPY may leave the connection in an unusable state" if buf
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# +copy_into+ uses PostgreSQL's +COPY FROM STDIN+ SQL statement to do very fast inserts
|
|
307
|
+
# into a table using input preformatting in either CSV or PostgreSQL text format.
|
|
308
|
+
# This method is only supported if pg is the underlying ruby driver. This method should only be called if you want
|
|
309
|
+
# results returned to the client. If you are using +COPY FROM+
|
|
310
|
+
# with a filename, you should just use +run+ instead of this method.
|
|
311
|
+
#
|
|
312
|
+
# The following options are respected:
|
|
313
|
+
#
|
|
314
|
+
# :columns :: The columns to insert into, with the same order as the columns in the
|
|
315
|
+
# input data. If this isn't given, uses all columns in the table.
|
|
316
|
+
# :data :: The data to copy to PostgreSQL, which should already be in CSV or PostgreSQL
|
|
317
|
+
# text format. This can be either a string, or any object that responds to
|
|
318
|
+
# each and yields string.
|
|
319
|
+
# :format :: The format to use. text is the default, so this should be :csv or :binary.
|
|
320
|
+
# :options :: An options SQL string to use, which should contain comma separated options.
|
|
321
|
+
# :server :: The server on which to run the query.
|
|
322
|
+
#
|
|
323
|
+
# If a block is provided and :data option is not, this will yield to the block repeatedly.
|
|
324
|
+
# The block should return a string, or nil to signal that it is finished.
|
|
325
|
+
def copy_into(table, opts={})
|
|
326
|
+
sql = "COPY #{literal(table)}"
|
|
327
|
+
if cols = opts[:columns]
|
|
328
|
+
sql << literal(Array(cols))
|
|
329
|
+
end
|
|
330
|
+
sql << " FROM STDIN"
|
|
331
|
+
if opts[:options] || opts[:format]
|
|
332
|
+
sql << " ("
|
|
333
|
+
sql << "FORMAT #{opts[:format]}" if opts[:format]
|
|
334
|
+
sql << "#{', ' if opts[:format]}#{opts[:options]}" if opts[:options]
|
|
335
|
+
sql << ')'
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
data = opts[:data]
|
|
339
|
+
data = Array(data) if data.is_a?(String)
|
|
340
|
+
|
|
341
|
+
if block_given? && data
|
|
342
|
+
raise Error, "Cannot provide both a :data option and a block to copy_into"
|
|
343
|
+
elsif !block_given? && !data
|
|
344
|
+
raise Error, "Must provide either a :data option or a block to copy_into"
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
synchronize(opts[:server]) do |conn|
|
|
348
|
+
conn.execute(sql)
|
|
349
|
+
begin
|
|
350
|
+
if block_given?
|
|
351
|
+
while buf = yield
|
|
352
|
+
conn.put_copy_data(buf)
|
|
353
|
+
end
|
|
354
|
+
else
|
|
355
|
+
data.each{|buf| conn.put_copy_data(buf)}
|
|
356
|
+
end
|
|
357
|
+
rescue Exception => e
|
|
358
|
+
conn.put_copy_end("ruby exception occurred while copying data into PostgreSQL")
|
|
359
|
+
raise
|
|
360
|
+
ensure
|
|
361
|
+
conn.put_copy_end unless e
|
|
362
|
+
conn.get_result
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# Listens on the given channel (or multiple channels if channel is an array), waiting for notifications.
|
|
368
|
+
# After a notification is received, or the timeout has passed, stops listening to the channel. Options:
|
|
369
|
+
#
|
|
370
|
+
# :after_listen :: An object that responds to +call+ that is called with the underlying connection after the LISTEN
|
|
371
|
+
# statement is sent, but before the connection starts waiting for notifications.
|
|
372
|
+
# :loop :: Whether to continually wait for notifications, instead of just waiting for a single
|
|
373
|
+
# notification. If this option is given, a block must be provided. If this object responds to call, it is
|
|
374
|
+
# called with the underlying connection after each notification is received (after the block is called).
|
|
375
|
+
# If a :timeout option is used, and a callable object is given, the object will also be called if the
|
|
376
|
+
# timeout expires. If :loop is used and you want to stop listening, you can either break from inside the
|
|
377
|
+
# block given to #listen, or you can throw :stop from inside the :loop object's call method or the block.
|
|
378
|
+
# :server :: The server on which to listen, if the sharding support is being used.
|
|
379
|
+
# :timeout :: How long to wait for a notification, in seconds (can provide a float value for
|
|
380
|
+
# fractional seconds). If not given or nil, waits indefinitely.
|
|
381
|
+
#
|
|
382
|
+
# This method is only supported if pg is used as the underlying ruby driver. It returns the
|
|
383
|
+
# channel the notification was sent to (as a string), unless :loop was used, in which case it returns nil.
|
|
384
|
+
# If a block is given, it is yielded 3 arguments:
|
|
385
|
+
# * the channel the notification was sent to (as a string)
|
|
386
|
+
# * the backend pid of the notifier (as an integer),
|
|
387
|
+
# * and the payload of the notification (as a string or nil).
|
|
388
|
+
def listen(channels, opts={}, &block)
|
|
389
|
+
check_database_errors do
|
|
390
|
+
synchronize(opts[:server]) do |conn|
|
|
391
|
+
begin
|
|
392
|
+
channels = Array(channels)
|
|
393
|
+
channels.each{|channel| conn.execute("LISTEN #{dataset.send(:table_ref, channel)}")}
|
|
394
|
+
opts[:after_listen].call(conn) if opts[:after_listen]
|
|
395
|
+
timeout = opts[:timeout] ? [opts[:timeout]] : []
|
|
396
|
+
if l = opts[:loop]
|
|
397
|
+
raise Error, 'calling #listen with :loop requires a block' unless block
|
|
398
|
+
loop_call = l.respond_to?(:call)
|
|
399
|
+
catch(:stop) do
|
|
400
|
+
loop do
|
|
401
|
+
conn.wait_for_notify(*timeout, &block)
|
|
402
|
+
l.call(conn) if loop_call
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
nil
|
|
406
|
+
else
|
|
407
|
+
conn.wait_for_notify(*timeout, &block)
|
|
408
|
+
end
|
|
409
|
+
ensure
|
|
410
|
+
conn.execute("UNLISTEN *")
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
end
|
|
241
414
|
end
|
|
242
415
|
end
|
|
243
|
-
|
|
244
|
-
#
|
|
245
|
-
#
|
|
246
|
-
def
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
416
|
+
|
|
417
|
+
# If convert_infinite_timestamps is true and the value is infinite, return an appropriate
|
|
418
|
+
# value based on the convert_infinite_timestamps setting.
|
|
419
|
+
def to_application_timestamp(value)
|
|
420
|
+
if c = convert_infinite_timestamps
|
|
421
|
+
case value
|
|
422
|
+
when *INFINITE_TIMESTAMP_STRINGS
|
|
423
|
+
infinite_timestamp_value(value)
|
|
424
|
+
else
|
|
425
|
+
super
|
|
252
426
|
end
|
|
427
|
+
else
|
|
428
|
+
super
|
|
253
429
|
end
|
|
254
430
|
end
|
|
255
|
-
|
|
431
|
+
|
|
256
432
|
private
|
|
257
|
-
|
|
433
|
+
|
|
434
|
+
# Execute the given SQL string or prepared statement on the connection object.
|
|
435
|
+
def _execute(conn, sql, opts, &block)
|
|
436
|
+
if sql.is_a?(Symbol)
|
|
437
|
+
execute_prepared_statement(conn, sql, opts, &block)
|
|
438
|
+
else
|
|
439
|
+
conn.execute(sql, opts[:arguments], &block)
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
# Execute the prepared statement name with the given arguments on the connection.
|
|
444
|
+
def _execute_prepared_statement(conn, ps_name, args, opts)
|
|
445
|
+
conn.exec_prepared(ps_name, args)
|
|
446
|
+
end
|
|
447
|
+
|
|
258
448
|
# Convert exceptions raised from the block into DatabaseErrors.
|
|
259
449
|
def check_database_errors
|
|
260
450
|
begin
|
|
@@ -264,45 +454,84 @@ module Sequel
|
|
|
264
454
|
end
|
|
265
455
|
end
|
|
266
456
|
|
|
267
|
-
#
|
|
268
|
-
def
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
end
|
|
457
|
+
# Set the DateStyle to ISO if configured, for faster date parsing.
|
|
458
|
+
def connection_configuration_sqls
|
|
459
|
+
sqls = super
|
|
460
|
+
sqls << "SET DateStyle = 'ISO'" if Postgres.use_iso_date_format
|
|
461
|
+
sqls
|
|
273
462
|
end
|
|
274
|
-
|
|
463
|
+
|
|
464
|
+
def database_error_classes
|
|
465
|
+
[PGError]
|
|
466
|
+
end
|
|
467
|
+
|
|
275
468
|
# Execute the prepared statement with the given name on an available
|
|
276
469
|
# connection, using the given args. If the connection has not prepared
|
|
277
470
|
# a statement with the given name yet, prepare it. If the connection
|
|
278
471
|
# has prepared a statement with the same name and different SQL,
|
|
279
472
|
# deallocate that statement first and then prepare this statement.
|
|
280
473
|
# If a block is given, yield the result, otherwise, return the number
|
|
281
|
-
# of rows changed.
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
ps = prepared_statements[name]
|
|
474
|
+
# of rows changed.
|
|
475
|
+
def execute_prepared_statement(conn, name, opts={}, &block)
|
|
476
|
+
ps = prepared_statement(name)
|
|
285
477
|
sql = ps.prepared_sql
|
|
286
478
|
ps_name = name.to_s
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
479
|
+
|
|
480
|
+
if args = opts[:arguments]
|
|
481
|
+
args = args.map{|arg| bound_variable_arg(arg, conn)}
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
unless conn.prepared_statements[ps_name] == sql
|
|
485
|
+
conn.execute("DEALLOCATE #{ps_name}") if conn.prepared_statements.include?(ps_name)
|
|
486
|
+
conn.check_disconnect_errors{log_yield("PREPARE #{ps_name} AS #{sql}"){conn.prepare(ps_name, sql)}}
|
|
487
|
+
conn.prepared_statements[ps_name] = sql
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
log_sql = "EXECUTE #{ps_name}"
|
|
491
|
+
if ps.log_sql
|
|
492
|
+
log_sql << " ("
|
|
493
|
+
log_sql << sql
|
|
494
|
+
log_sql << ")"
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
q = conn.check_disconnect_errors{log_yield(log_sql, args){_execute_prepared_statement(conn, ps_name, args, opts)}}
|
|
498
|
+
begin
|
|
499
|
+
block_given? ? yield(q) : q.cmd_tuples
|
|
500
|
+
ensure
|
|
501
|
+
q.clear if q && q.respond_to?(:clear)
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
# Return an appropriate value for the given infinite timestamp string.
|
|
506
|
+
def infinite_timestamp_value(value)
|
|
507
|
+
case convert_infinite_timestamps
|
|
508
|
+
when :nil
|
|
509
|
+
nil
|
|
510
|
+
when :string
|
|
511
|
+
value
|
|
512
|
+
else
|
|
513
|
+
value == 'infinity' ? PLUS_INFINITY : MINUS_INFINITY
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
# Don't log, since logging is done by the underlying connection.
|
|
518
|
+
def log_connection_execute(conn, sql)
|
|
519
|
+
conn.execute(sql)
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
# If the value is an infinite value (either an infinite float or a string returned by
|
|
523
|
+
# by PostgreSQL for an infinite timestamp), return it without converting it if
|
|
524
|
+
# convert_infinite_timestamps is set.
|
|
525
|
+
def typecast_value_datetime(value)
|
|
526
|
+
if convert_infinite_timestamps
|
|
527
|
+
case value
|
|
528
|
+
when *INFINITE_DATETIME_VALUES
|
|
529
|
+
value
|
|
299
530
|
else
|
|
300
|
-
|
|
301
|
-
block_given? ? yield(q) : q.cmd_tuples
|
|
302
|
-
ensure
|
|
303
|
-
q.clear
|
|
304
|
-
end
|
|
531
|
+
super
|
|
305
532
|
end
|
|
533
|
+
else
|
|
534
|
+
super
|
|
306
535
|
end
|
|
307
536
|
end
|
|
308
537
|
end
|
|
@@ -311,12 +540,15 @@ module Sequel
|
|
|
311
540
|
# postgres-pr driver.
|
|
312
541
|
class Dataset < Sequel::Dataset
|
|
313
542
|
include Sequel::Postgres::DatasetMethods
|
|
543
|
+
|
|
544
|
+
Database::DatasetClass = self
|
|
545
|
+
APOS = Sequel::Dataset::APOS
|
|
314
546
|
|
|
315
547
|
# Yield all rows returned by executing the given SQL and converting
|
|
316
548
|
# 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)
|
|
549
|
+
def fetch_rows(sql)
|
|
550
|
+
return cursor_fetch_rows(sql){|h| yield h} if @opts[:cursor]
|
|
551
|
+
execute(sql){|res| yield_hash_rows(res, fetch_rows_set_cols(res)){|h| yield h}}
|
|
320
552
|
end
|
|
321
553
|
|
|
322
554
|
# Uses a cursor for fetching records, instead of fetching the entire result
|
|
@@ -372,11 +604,17 @@ module Sequel
|
|
|
372
604
|
end
|
|
373
605
|
LiteralString.new("#{prepared_arg_placeholder}#{i}#{"::#{type}" if type}")
|
|
374
606
|
end
|
|
607
|
+
|
|
608
|
+
# Always assume a prepared argument.
|
|
609
|
+
def prepared_arg?(k)
|
|
610
|
+
true
|
|
611
|
+
end
|
|
375
612
|
end
|
|
376
613
|
|
|
377
614
|
# Allow use of bind arguments for PostgreSQL using the pg driver.
|
|
378
615
|
module BindArgumentMethods
|
|
379
616
|
include ArgumentMapper
|
|
617
|
+
include ::Sequel::Postgres::DatasetMethods::PreparedStatementMethods
|
|
380
618
|
|
|
381
619
|
private
|
|
382
620
|
|
|
@@ -389,18 +627,18 @@ module Sequel
|
|
|
389
627
|
def execute_dui(sql, opts={}, &block)
|
|
390
628
|
super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
391
629
|
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
630
|
end
|
|
398
631
|
|
|
399
632
|
# Allow use of server side prepared statements for PostgreSQL using the
|
|
400
633
|
# pg driver.
|
|
401
634
|
module PreparedStatementMethods
|
|
402
635
|
include BindArgumentMethods
|
|
403
|
-
|
|
636
|
+
|
|
637
|
+
# Raise a more obvious error if you attempt to call a unnamed prepared statement.
|
|
638
|
+
def call(*)
|
|
639
|
+
raise Error, "Cannot call prepared statement without a name" if prepared_statement_name.nil?
|
|
640
|
+
super
|
|
641
|
+
end
|
|
404
642
|
|
|
405
643
|
private
|
|
406
644
|
|
|
@@ -414,11 +652,6 @@ module Sequel
|
|
|
414
652
|
def execute_dui(sql, opts={}, &block)
|
|
415
653
|
super(prepared_statement_name, opts, &block)
|
|
416
654
|
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
655
|
end
|
|
423
656
|
|
|
424
657
|
# Execute the given type of statement with the hash of values.
|
|
@@ -427,7 +660,7 @@ module Sequel
|
|
|
427
660
|
ps.extend(BindArgumentMethods)
|
|
428
661
|
ps.call(bind_vars, &block)
|
|
429
662
|
end
|
|
430
|
-
|
|
663
|
+
|
|
431
664
|
# Prepare the given type of statement with the given name, and store
|
|
432
665
|
# it in the database to be called later.
|
|
433
666
|
def prepare(type, name=nil, *values)
|
|
@@ -435,7 +668,7 @@ module Sequel
|
|
|
435
668
|
ps.extend(PreparedStatementMethods)
|
|
436
669
|
if name
|
|
437
670
|
ps.prepared_statement_name = name
|
|
438
|
-
db.
|
|
671
|
+
db.set_prepared_statement(name, ps)
|
|
439
672
|
end
|
|
440
673
|
ps
|
|
441
674
|
end
|
|
@@ -452,7 +685,7 @@ module Sequel
|
|
|
452
685
|
private
|
|
453
686
|
|
|
454
687
|
# Use a cursor to fetch groups of records at a time, yielding them to the block.
|
|
455
|
-
def cursor_fetch_rows(sql
|
|
688
|
+
def cursor_fetch_rows(sql)
|
|
456
689
|
server_opts = {:server=>@opts[:server] || :read_only}
|
|
457
690
|
db.transaction(server_opts) do
|
|
458
691
|
begin
|
|
@@ -464,12 +697,12 @@ module Sequel
|
|
|
464
697
|
# Load columns only in the first fetch, so subsequent fetches are faster
|
|
465
698
|
execute(fetch_sql) do |res|
|
|
466
699
|
cols = fetch_rows_set_cols(res)
|
|
467
|
-
yield_hash_rows(res, cols
|
|
700
|
+
yield_hash_rows(res, cols){|h| yield h}
|
|
468
701
|
return if res.ntuples < rows_per_fetch
|
|
469
702
|
end
|
|
470
703
|
loop do
|
|
471
704
|
execute(fetch_sql) do |res|
|
|
472
|
-
yield_hash_rows(res, cols
|
|
705
|
+
yield_hash_rows(res, cols){|h| yield h}
|
|
473
706
|
return if res.ntuples < rows_per_fetch
|
|
474
707
|
end
|
|
475
708
|
end
|
|
@@ -483,21 +716,22 @@ module Sequel
|
|
|
483
716
|
# field numers, type conversion procs, and name symbol arrays.
|
|
484
717
|
def fetch_rows_set_cols(res)
|
|
485
718
|
cols = []
|
|
719
|
+
procs = db.conversion_procs
|
|
486
720
|
res.nfields.times do |fieldnum|
|
|
487
|
-
cols << [fieldnum,
|
|
721
|
+
cols << [fieldnum, procs[res.ftype(fieldnum)], output_identifier(res.fname(fieldnum))]
|
|
488
722
|
end
|
|
489
723
|
@columns = cols.map{|c| c.at(2)}
|
|
490
724
|
cols
|
|
491
725
|
end
|
|
492
726
|
|
|
493
727
|
# Use the driver's escape_bytea
|
|
494
|
-
def
|
|
495
|
-
db.synchronize{|c|
|
|
728
|
+
def literal_blob_append(sql, v)
|
|
729
|
+
sql << APOS << db.synchronize{|c| c.escape_bytea(v)} << APOS
|
|
496
730
|
end
|
|
497
731
|
|
|
498
732
|
# Use the driver's escape_string
|
|
499
|
-
def
|
|
500
|
-
db.synchronize{|c|
|
|
733
|
+
def literal_string_append(sql, v)
|
|
734
|
+
sql << APOS << db.synchronize{|c| c.escape_string(v)} << APOS
|
|
501
735
|
end
|
|
502
736
|
|
|
503
737
|
# For each row in the result set, yield a hash with column name symbol
|