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
|
@@ -6,6 +6,14 @@ unless defined?(POSTGRES_DB)
|
|
|
6
6
|
end
|
|
7
7
|
INTEGRATION_DB = POSTGRES_DB unless defined?(INTEGRATION_DB)
|
|
8
8
|
|
|
9
|
+
# Automatic parameterization changes the SQL used, so don't check
|
|
10
|
+
# for expected SQL if it is being used.
|
|
11
|
+
if defined?(Sequel::Postgres::AutoParameterize)
|
|
12
|
+
check_sqls = false
|
|
13
|
+
else
|
|
14
|
+
check_sqls = true
|
|
15
|
+
end
|
|
16
|
+
|
|
9
17
|
def POSTGRES_DB.sqls
|
|
10
18
|
(@sqls ||= [])
|
|
11
19
|
end
|
|
@@ -13,147 +21,249 @@ logger = Object.new
|
|
|
13
21
|
def logger.method_missing(m, msg)
|
|
14
22
|
POSTGRES_DB.sqls << msg
|
|
15
23
|
end
|
|
16
|
-
POSTGRES_DB.loggers
|
|
24
|
+
POSTGRES_DB.loggers << logger
|
|
17
25
|
|
|
18
|
-
#POSTGRES_DB.instance_variable_set(:@server_version,
|
|
19
|
-
POSTGRES_DB.create_table! :test do
|
|
20
|
-
text :name
|
|
21
|
-
integer :value, :index => true
|
|
22
|
-
end
|
|
23
|
-
POSTGRES_DB.create_table! :test2 do
|
|
24
|
-
text :name
|
|
25
|
-
integer :value
|
|
26
|
-
end
|
|
27
|
-
POSTGRES_DB.create_table! :test3 do
|
|
28
|
-
integer :value
|
|
29
|
-
timestamp :time
|
|
30
|
-
end
|
|
31
|
-
POSTGRES_DB.create_table! :test4 do
|
|
32
|
-
varchar :name, :size => 20
|
|
33
|
-
bytea :value
|
|
34
|
-
end
|
|
26
|
+
#POSTGRES_DB.instance_variable_set(:@server_version, 80200)
|
|
35
27
|
|
|
36
28
|
describe "A PostgreSQL database" do
|
|
37
|
-
before do
|
|
29
|
+
before(:all) do
|
|
38
30
|
@db = POSTGRES_DB
|
|
31
|
+
@db.create_table!(:public__testfk){primary_key :id; foreign_key :i, :public__testfk}
|
|
39
32
|
end
|
|
40
|
-
|
|
33
|
+
after(:all) do
|
|
34
|
+
@db.drop_table?(:public__testfk)
|
|
35
|
+
end
|
|
36
|
+
|
|
41
37
|
specify "should provide the server version" do
|
|
42
38
|
@db.server_version.should > 70000
|
|
43
39
|
end
|
|
44
40
|
|
|
45
41
|
specify "should correctly parse the schema" do
|
|
46
|
-
@db.schema(:
|
|
47
|
-
[:
|
|
48
|
-
[:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
42
|
+
@db.schema(:public__testfk, :reload=>true).should == [
|
|
43
|
+
[:id, {:type=>:integer, :ruby_default=>nil, :db_type=>"integer", :default=>"nextval('testfk_id_seq'::regclass)", :oid=>23, :primary_key=>true, :allow_null=>false}],
|
|
44
|
+
[:i, {:type=>:integer, :ruby_default=>nil, :db_type=>"integer", :default=>nil, :oid=>23, :primary_key=>false, :allow_null=>true}]]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
specify "should parse foreign keys for tables in a schema" do
|
|
48
|
+
@db.foreign_key_list(:public__testfk).should == [{:on_delete=>:no_action, :on_update=>:no_action, :columns=>[:i], :key=>[:id], :deferrable=>false, :table=>Sequel.qualify(:public, :testfk), :name=>:testfk_i_fkey}]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
specify "should return uuid fields as strings" do
|
|
52
|
+
@db.get(Sequel.cast('550e8400-e29b-41d4-a716-446655440000', :uuid)).should == '550e8400-e29b-41d4-a716-446655440000'
|
|
54
53
|
end
|
|
55
54
|
end
|
|
56
55
|
|
|
57
56
|
describe "A PostgreSQL dataset" do
|
|
57
|
+
before(:all) do
|
|
58
|
+
@db = POSTGRES_DB
|
|
59
|
+
@d = @db[:test]
|
|
60
|
+
@db.create_table! :test do
|
|
61
|
+
text :name
|
|
62
|
+
integer :value, :index => true
|
|
63
|
+
end
|
|
64
|
+
end
|
|
58
65
|
before do
|
|
59
|
-
@d
|
|
60
|
-
@
|
|
66
|
+
@d.delete
|
|
67
|
+
@db.sqls.clear
|
|
68
|
+
end
|
|
69
|
+
after do
|
|
70
|
+
@db.drop_table?(:atest)
|
|
71
|
+
end
|
|
72
|
+
after(:all) do
|
|
73
|
+
@db.drop_table?(:test)
|
|
61
74
|
end
|
|
62
|
-
|
|
75
|
+
|
|
63
76
|
specify "should quote columns and tables using double quotes if quoting identifiers" do
|
|
64
|
-
@d.
|
|
65
|
-
@d.select(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
@d.select(
|
|
69
|
-
|
|
77
|
+
@d.select(:name).sql.should == 'SELECT "name" FROM "test"'
|
|
78
|
+
@d.select(Sequel.lit('COUNT(*)')).sql.should == 'SELECT COUNT(*) FROM "test"'
|
|
79
|
+
@d.select(Sequel.function(:max, :value)).sql.should == 'SELECT max("value") FROM "test"'
|
|
80
|
+
@d.select(Sequel.function(:NOW)).sql.should == 'SELECT NOW() FROM "test"'
|
|
81
|
+
@d.select(Sequel.function(:max, :items__value)).sql.should == 'SELECT max("items"."value") FROM "test"'
|
|
82
|
+
@d.order(Sequel.desc(:name)).sql.should == 'SELECT * FROM "test" ORDER BY "name" DESC'
|
|
83
|
+
@d.select(Sequel.lit('test.name AS item_name')).sql.should == 'SELECT test.name AS item_name FROM "test"'
|
|
84
|
+
@d.select(Sequel.lit('"name"')).sql.should == 'SELECT "name" FROM "test"'
|
|
85
|
+
@d.select(Sequel.lit('max(test."name") AS "max_name"')).sql.should == 'SELECT max(test."name") AS "max_name" FROM "test"'
|
|
86
|
+
@d.insert_sql(:x => :y).should =~ /\AINSERT INTO "test" \("x"\) VALUES \("y"\)( RETURNING NULL)?\z/
|
|
87
|
+
|
|
88
|
+
if check_sqls
|
|
89
|
+
@d.select(Sequel.function(:test, :abc, 'hello')).sql.should == "SELECT test(\"abc\", 'hello') FROM \"test\""
|
|
90
|
+
@d.select(Sequel.function(:test, :abc__def, 'hello')).sql.should == "SELECT test(\"abc\".\"def\", 'hello') FROM \"test\""
|
|
91
|
+
@d.select(Sequel.function(:test, :abc__def, 'hello').as(:x2)).sql.should == "SELECT test(\"abc\".\"def\", 'hello') AS \"x2\" FROM \"test\""
|
|
92
|
+
@d.insert_sql(:value => 333).should =~ /\AINSERT INTO "test" \("value"\) VALUES \(333\)( RETURNING NULL)?\z/
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
specify "should quote fields correctly when reversing the order if quoting identifiers" do
|
|
97
|
+
@d.reverse_order(:name).sql.should == 'SELECT * FROM "test" ORDER BY "name" DESC'
|
|
98
|
+
@d.reverse_order(Sequel.desc(:name)).sql.should == 'SELECT * FROM "test" ORDER BY "name" ASC'
|
|
99
|
+
@d.reverse_order(:name, Sequel.desc(:test)).sql.should == 'SELECT * FROM "test" ORDER BY "name" DESC, "test" ASC'
|
|
100
|
+
@d.reverse_order(Sequel.desc(:name), :test).sql.should == 'SELECT * FROM "test" ORDER BY "name" ASC, "test" DESC'
|
|
101
|
+
end
|
|
70
102
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
@d.
|
|
75
|
-
|
|
103
|
+
specify "should support regexps" do
|
|
104
|
+
@d << {:name => 'abc', :value => 1}
|
|
105
|
+
@d << {:name => 'bcd', :value => 2}
|
|
106
|
+
@d.filter(:name => /bc/).count.should == 2
|
|
107
|
+
@d.filter(:name => /^bc/).count.should == 1
|
|
108
|
+
end
|
|
76
109
|
|
|
77
|
-
|
|
78
|
-
|
|
110
|
+
specify "should support NULLS FIRST and NULLS LAST" do
|
|
111
|
+
@d << {:name => 'abc'}
|
|
112
|
+
@d << {:name => 'bcd'}
|
|
113
|
+
@d << {:name => 'bcd', :value => 2}
|
|
114
|
+
@d.order(Sequel.asc(:value, :nulls=>:first), :name).select_map(:name).should == %w[abc bcd bcd]
|
|
115
|
+
@d.order(Sequel.asc(:value, :nulls=>:last), :name).select_map(:name).should == %w[bcd abc bcd]
|
|
116
|
+
@d.order(Sequel.asc(:value, :nulls=>:first), :name).reverse.select_map(:name).should == %w[bcd bcd abc]
|
|
117
|
+
end
|
|
79
118
|
|
|
80
|
-
|
|
81
|
-
|
|
119
|
+
specify "#lock should lock tables and yield if a block is given" do
|
|
120
|
+
@d.lock('EXCLUSIVE'){@d.insert(:name=>'a')}
|
|
121
|
+
end
|
|
82
122
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
@
|
|
87
|
-
|
|
123
|
+
specify "should support exclusion constraints when creating or altering tables" do
|
|
124
|
+
@db.create_table!(:atest){Integer :t; exclude [[Sequel.desc(:t, :nulls=>:last), '=']], :using=>:btree, :where=>proc{t > 0}}
|
|
125
|
+
@db[:atest].insert(1)
|
|
126
|
+
@db[:atest].insert(2)
|
|
127
|
+
proc{@db[:atest].insert(2)}.should raise_error(Sequel::DatabaseError)
|
|
88
128
|
|
|
89
|
-
@
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
@
|
|
93
|
-
|
|
129
|
+
@db.create_table!(:atest){Integer :t}
|
|
130
|
+
@db.alter_table(:atest){add_exclusion_constraint [[:t, '=']], :using=>:btree, :name=>'atest_ex'}
|
|
131
|
+
@db[:atest].insert(1)
|
|
132
|
+
@db[:atest].insert(2)
|
|
133
|
+
proc{@db[:atest].insert(2)}.should raise_error(Sequel::DatabaseError)
|
|
134
|
+
@db.alter_table(:atest){drop_constraint 'atest_ex'}
|
|
135
|
+
end if POSTGRES_DB.server_version >= 90000
|
|
94
136
|
|
|
95
|
-
|
|
96
|
-
|
|
137
|
+
specify "should support adding foreign key constarints that are not yet valid, and validating them later" do
|
|
138
|
+
@db.create_table!(:atest){primary_key :id; Integer :fk}
|
|
139
|
+
@db[:atest].insert(1, 5)
|
|
140
|
+
@db.alter_table(:atest){add_foreign_key [:fk], :atest, :not_valid=>true, :name=>:atest_fk}
|
|
141
|
+
@db[:atest].insert(2, 1)
|
|
142
|
+
proc{@db[:atest].insert(3, 4)}.should raise_error(Sequel::DatabaseError)
|
|
97
143
|
|
|
98
|
-
@
|
|
99
|
-
|
|
144
|
+
proc{@db.alter_table(:atest){validate_constraint :atest_fk}}.should raise_error(Sequel::DatabaseError)
|
|
145
|
+
@db[:atest].where(:id=>1).update(:fk=>2)
|
|
146
|
+
@db.alter_table(:atest){validate_constraint :atest_fk}
|
|
147
|
+
proc{@db.alter_table(:atest){validate_constraint :atest_fk}}.should_not raise_error
|
|
148
|
+
end if POSTGRES_DB.server_version >= 90200
|
|
100
149
|
|
|
101
|
-
|
|
102
|
-
|
|
150
|
+
specify "should support :using when altering a column's type" do
|
|
151
|
+
@db.create_table!(:atest){Integer :t}
|
|
152
|
+
@db[:atest].insert(1262304000)
|
|
153
|
+
@db.alter_table(:atest){set_column_type :t, Time, :using=>Sequel.cast('epoch', Time) + Sequel.cast('1 second', :interval) * :t}
|
|
154
|
+
@db[:atest].get(Sequel.extract(:year, :t)).should == 2010
|
|
155
|
+
end
|
|
103
156
|
|
|
104
|
-
|
|
105
|
-
|
|
157
|
+
specify "should support :using with a string when altering a column's type" do
|
|
158
|
+
@db.create_table!(:atest){Integer :t}
|
|
159
|
+
@db[:atest].insert(1262304000)
|
|
160
|
+
@db.alter_table(:atest){set_column_type :t, Time, :using=>"'epoch'::timestamp + '1 second'::interval * t"}
|
|
161
|
+
@db[:atest].get(Sequel.extract(:year, :t)).should == 2010
|
|
162
|
+
end
|
|
106
163
|
|
|
107
|
-
|
|
108
|
-
|
|
164
|
+
specify "should be able to parse the default value for an interval type" do
|
|
165
|
+
@db.create_table!(:atest){interval :t, :default=>'1 week'}
|
|
166
|
+
@db.schema(:atest).first.last[:ruby_default].should == '7 days'
|
|
109
167
|
end
|
|
110
|
-
|
|
111
|
-
specify "should quote fields correctly when reversing the order if quoting identifiers" do
|
|
112
|
-
@d.quote_identifiers = true
|
|
113
|
-
@d.reverse_order(:name).sql.should == \
|
|
114
|
-
'SELECT * FROM "test" ORDER BY "name" DESC'
|
|
115
168
|
|
|
116
|
-
|
|
117
|
-
|
|
169
|
+
specify "should have #transaction support various types of synchronous options" do
|
|
170
|
+
@db.transaction(:synchronous=>:on){}
|
|
171
|
+
@db.transaction(:synchronous=>true){}
|
|
172
|
+
@db.transaction(:synchronous=>:off){}
|
|
173
|
+
@db.transaction(:synchronous=>false){}
|
|
174
|
+
@db.sqls.grep(/synchronous/).should == ["SET LOCAL synchronous_commit = on", "SET LOCAL synchronous_commit = on", "SET LOCAL synchronous_commit = off", "SET LOCAL synchronous_commit = off"]
|
|
175
|
+
|
|
176
|
+
@db.sqls.clear
|
|
177
|
+
@db.transaction(:synchronous=>nil){}
|
|
178
|
+
@db.sqls.should == ['BEGIN', 'COMMIT']
|
|
118
179
|
|
|
119
|
-
@
|
|
120
|
-
|
|
180
|
+
if @db.server_version >= 90100
|
|
181
|
+
@db.sqls.clear
|
|
182
|
+
@db.transaction(:synchronous=>:local){}
|
|
183
|
+
@db.sqls.grep(/synchronous/).should == ["SET LOCAL synchronous_commit = local"]
|
|
121
184
|
|
|
122
|
-
|
|
123
|
-
|
|
185
|
+
if @db.server_version >= 90200
|
|
186
|
+
@db.sqls.clear
|
|
187
|
+
@db.transaction(:synchronous=>:remote_write){}
|
|
188
|
+
@db.sqls.grep(/synchronous/).should == ["SET LOCAL synchronous_commit = remote_write"]
|
|
189
|
+
end
|
|
190
|
+
end
|
|
124
191
|
end
|
|
125
192
|
|
|
126
|
-
specify "should support
|
|
127
|
-
@
|
|
128
|
-
@
|
|
129
|
-
@
|
|
130
|
-
@
|
|
193
|
+
specify "should have #transaction support read only transactions" do
|
|
194
|
+
@db.transaction(:read_only=>true){}
|
|
195
|
+
@db.transaction(:read_only=>false){}
|
|
196
|
+
@db.transaction(:isolation=>:serializable, :read_only=>true){}
|
|
197
|
+
@db.transaction(:isolation=>:serializable, :read_only=>false){}
|
|
198
|
+
@db.sqls.grep(/READ/).should == ["SET TRANSACTION READ ONLY", "SET TRANSACTION READ WRITE", "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY", "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ WRITE"]
|
|
131
199
|
end
|
|
132
|
-
|
|
133
|
-
specify "should
|
|
134
|
-
@
|
|
135
|
-
@
|
|
136
|
-
@
|
|
137
|
-
@
|
|
138
|
-
@
|
|
139
|
-
@
|
|
200
|
+
|
|
201
|
+
specify "should have #transaction support deferrable transactions" do
|
|
202
|
+
@db.transaction(:deferrable=>true){}
|
|
203
|
+
@db.transaction(:deferrable=>false){}
|
|
204
|
+
@db.transaction(:deferrable=>true, :read_only=>true){}
|
|
205
|
+
@db.transaction(:deferrable=>false, :read_only=>false){}
|
|
206
|
+
@db.transaction(:isolation=>:serializable, :deferrable=>true, :read_only=>true){}
|
|
207
|
+
@db.transaction(:isolation=>:serializable, :deferrable=>false, :read_only=>false){}
|
|
208
|
+
@db.sqls.grep(/DEF/).should == ["SET TRANSACTION DEFERRABLE", "SET TRANSACTION NOT DEFERRABLE", "SET TRANSACTION READ ONLY DEFERRABLE", "SET TRANSACTION READ WRITE NOT DEFERRABLE", "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY DEFERRABLE", "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ WRITE NOT DEFERRABLE"]
|
|
209
|
+
end if POSTGRES_DB.server_version >= 90100
|
|
210
|
+
|
|
211
|
+
specify "should support creating indexes concurrently" do
|
|
212
|
+
@db.add_index :test, [:name, :value], :concurrently=>true
|
|
213
|
+
@db.sqls.should == ['CREATE INDEX CONCURRENTLY "test_name_value_index" ON "test" ("name", "value")'] if check_sqls
|
|
140
214
|
end
|
|
141
|
-
|
|
142
|
-
specify "
|
|
143
|
-
@
|
|
215
|
+
|
|
216
|
+
specify "should support dropping indexes only if they already exist" do
|
|
217
|
+
@db.add_index :test, [:name, :value], :name=>'tnv1'
|
|
218
|
+
@db.sqls.clear
|
|
219
|
+
@db.drop_index :test, [:name, :value], :if_exists=>true, :name=>'tnv1'
|
|
220
|
+
@db.sqls.should == ['DROP INDEX IF EXISTS "tnv1"']
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
specify "should support CASCADE when dropping indexes" do
|
|
224
|
+
@db.add_index :test, [:name, :value], :name=>'tnv2'
|
|
225
|
+
@db.sqls.clear
|
|
226
|
+
@db.drop_index :test, [:name, :value], :cascade=>true, :name=>'tnv2'
|
|
227
|
+
@db.sqls.should == ['DROP INDEX "tnv2" CASCADE']
|
|
144
228
|
end
|
|
145
|
-
|
|
229
|
+
|
|
230
|
+
specify "should support dropping indexes concurrently" do
|
|
231
|
+
@db.add_index :test, [:name, :value], :name=>'tnv2'
|
|
232
|
+
@db.sqls.clear
|
|
233
|
+
@db.drop_index :test, [:name, :value], :concurrently=>true, :name=>'tnv2'
|
|
234
|
+
@db.sqls.should == ['DROP INDEX CONCURRENTLY "tnv2"']
|
|
235
|
+
end if POSTGRES_DB.server_version >= 90200
|
|
236
|
+
|
|
146
237
|
specify "#lock should lock table if inside a transaction" do
|
|
147
|
-
|
|
238
|
+
@db.transaction{@d.lock('EXCLUSIVE'); @d.insert(:name=>'a')}
|
|
148
239
|
end
|
|
149
|
-
|
|
240
|
+
|
|
150
241
|
specify "#lock should return nil" do
|
|
151
242
|
@d.lock('EXCLUSIVE'){@d.insert(:name=>'a')}.should == nil
|
|
152
|
-
|
|
243
|
+
@db.transaction{@d.lock('EXCLUSIVE').should == nil; @d.insert(:name=>'a')}
|
|
153
244
|
end
|
|
154
|
-
|
|
245
|
+
|
|
155
246
|
specify "should raise an error if attempting to update a joined dataset with a single FROM table" do
|
|
156
|
-
proc{
|
|
247
|
+
proc{@db[:test].join(:test, [:name]).update(:name=>'a')}.should raise_error(Sequel::Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs')
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
specify "should truncate with options" do
|
|
251
|
+
@d << { :name => 'abc', :value => 1}
|
|
252
|
+
@d.count.should == 1
|
|
253
|
+
@d.truncate(:cascade => true)
|
|
254
|
+
@d.count.should == 0
|
|
255
|
+
if @d.db.server_version > 80400
|
|
256
|
+
@d << { :name => 'abc', :value => 1}
|
|
257
|
+
@d.truncate(:cascade => true, :only=>true, :restart=>true)
|
|
258
|
+
@d.count.should == 0
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
specify "should truncate multiple tables at once" do
|
|
263
|
+
tables = [:test, :test]
|
|
264
|
+
tables.each{|t| @d.from(t).insert}
|
|
265
|
+
@d.from(:test, :test).truncate
|
|
266
|
+
tables.each{|t| @d.from(t).count.should == 0}
|
|
157
267
|
end
|
|
158
268
|
end
|
|
159
269
|
|
|
@@ -167,16 +277,16 @@ describe "Dataset#distinct" do
|
|
|
167
277
|
@ds = @db[:a]
|
|
168
278
|
end
|
|
169
279
|
after do
|
|
170
|
-
@db.drop_table(:a)
|
|
280
|
+
@db.drop_table?(:a)
|
|
171
281
|
end
|
|
172
|
-
|
|
282
|
+
|
|
173
283
|
it "#distinct with arguments should return results distinct on those arguments" do
|
|
174
284
|
@ds.insert(20, 10)
|
|
175
285
|
@ds.insert(30, 10)
|
|
176
286
|
@ds.order(:b, :a).distinct.map(:a).should == [20, 30]
|
|
177
|
-
@ds.order(:b, :a
|
|
287
|
+
@ds.order(:b, Sequel.desc(:a)).distinct.map(:a).should == [30, 20]
|
|
178
288
|
@ds.order(:b, :a).distinct(:b).map(:a).should == [20]
|
|
179
|
-
@ds.order(:b, :a
|
|
289
|
+
@ds.order(:b, Sequel.desc(:a)).distinct(:b).map(:a).should == [30]
|
|
180
290
|
end
|
|
181
291
|
end
|
|
182
292
|
|
|
@@ -191,41 +301,45 @@ if POSTGRES_DB.pool.respond_to?(:max_size) and POSTGRES_DB.pool.max_size > 1
|
|
|
191
301
|
@ds = POSTGRES_DB[:items]
|
|
192
302
|
end
|
|
193
303
|
after do
|
|
194
|
-
POSTGRES_DB.drop_table(:items)
|
|
304
|
+
POSTGRES_DB.drop_table?(:items)
|
|
195
305
|
POSTGRES_DB.disconnect
|
|
196
306
|
end
|
|
197
|
-
|
|
307
|
+
|
|
198
308
|
specify "should handle FOR UPDATE" do
|
|
199
309
|
@ds.insert(:number=>20)
|
|
200
|
-
c = nil
|
|
201
|
-
|
|
310
|
+
c, t = nil, nil
|
|
311
|
+
q = Queue.new
|
|
202
312
|
POSTGRES_DB.transaction do
|
|
203
313
|
@ds.for_update.first(:id=>1)
|
|
204
314
|
t = Thread.new do
|
|
205
315
|
POSTGRES_DB.transaction do
|
|
316
|
+
q.push nil
|
|
206
317
|
@ds.filter(:id=>1).update(:name=>'Jim')
|
|
207
318
|
c = @ds.first(:id=>1)
|
|
319
|
+
q.push nil
|
|
208
320
|
end
|
|
209
321
|
end
|
|
210
|
-
|
|
322
|
+
q.pop
|
|
211
323
|
@ds.filter(:id=>1).update(:number=>30)
|
|
212
324
|
end
|
|
325
|
+
q.pop
|
|
213
326
|
t.join
|
|
214
327
|
c.should == {:id=>1, :number=>30, :name=>'Jim'}
|
|
215
328
|
end
|
|
216
|
-
|
|
329
|
+
|
|
217
330
|
specify "should handle FOR SHARE" do
|
|
218
331
|
@ds.insert(:number=>20)
|
|
219
|
-
c = nil
|
|
220
|
-
|
|
332
|
+
c, t = nil
|
|
333
|
+
q = Queue.new
|
|
221
334
|
POSTGRES_DB.transaction do
|
|
222
335
|
@ds.for_share.first(:id=>1)
|
|
223
336
|
t = Thread.new do
|
|
224
337
|
POSTGRES_DB.transaction do
|
|
225
338
|
c = @ds.for_share.filter(:id=>1).first
|
|
339
|
+
q.push nil
|
|
226
340
|
end
|
|
227
341
|
end
|
|
228
|
-
|
|
342
|
+
q.pop
|
|
229
343
|
@ds.filter(:id=>1).update(:name=>'Jim')
|
|
230
344
|
c.should == {:id=>1, :number=>20, :name=>nil}
|
|
231
345
|
end
|
|
@@ -235,41 +349,93 @@ if POSTGRES_DB.pool.respond_to?(:max_size) and POSTGRES_DB.pool.max_size > 1
|
|
|
235
349
|
end
|
|
236
350
|
|
|
237
351
|
describe "A PostgreSQL dataset with a timestamp field" do
|
|
352
|
+
before(:all) do
|
|
353
|
+
@db = POSTGRES_DB
|
|
354
|
+
@db.create_table! :test3 do
|
|
355
|
+
integer :value
|
|
356
|
+
timestamp :time
|
|
357
|
+
end
|
|
358
|
+
@d = @db[:test3]
|
|
359
|
+
end
|
|
238
360
|
before do
|
|
239
|
-
@d = POSTGRES_DB[:test3]
|
|
240
361
|
@d.delete
|
|
241
362
|
end
|
|
363
|
+
after do
|
|
364
|
+
@db.convert_infinite_timestamps = false if @db.adapter_scheme == :postgres
|
|
365
|
+
end
|
|
366
|
+
after(:all) do
|
|
367
|
+
@db.drop_table?(:test3)
|
|
368
|
+
end
|
|
242
369
|
|
|
243
|
-
cspecify "should store milliseconds in time fields for Time objects", :do do
|
|
370
|
+
cspecify "should store milliseconds in time fields for Time objects", :do, :swift do
|
|
244
371
|
t = Time.now
|
|
245
372
|
@d << {:value=>1, :time=>t}
|
|
246
|
-
t2 = @d[:value =>
|
|
373
|
+
t2 = @d[:value =>1][:time]
|
|
247
374
|
@d.literal(t2).should == @d.literal(t)
|
|
248
375
|
t2.strftime('%Y-%m-%d %H:%M:%S').should == t.strftime('%Y-%m-%d %H:%M:%S')
|
|
249
|
-
t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000 == t.usec
|
|
376
|
+
(t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000).should == t.usec
|
|
250
377
|
end
|
|
251
378
|
|
|
252
|
-
cspecify "should store milliseconds in time fields for DateTime objects", :do do
|
|
379
|
+
cspecify "should store milliseconds in time fields for DateTime objects", :do, :swift do
|
|
253
380
|
t = DateTime.now
|
|
254
381
|
@d << {:value=>1, :time=>t}
|
|
255
|
-
t2 = @d[:value =>
|
|
382
|
+
t2 = @d[:value =>1][:time]
|
|
256
383
|
@d.literal(t2).should == @d.literal(t)
|
|
257
384
|
t2.strftime('%Y-%m-%d %H:%M:%S').should == t.strftime('%Y-%m-%d %H:%M:%S')
|
|
258
|
-
t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000 == t.strftime('%N').to_i/1000
|
|
385
|
+
(t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000).should == t.strftime('%N').to_i/1000
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
if POSTGRES_DB.adapter_scheme == :postgres
|
|
389
|
+
specify "should handle infinite timestamps if convert_infinite_timestamps is set" do
|
|
390
|
+
@d << {:time=>Sequel.cast('infinity', :timestamp)}
|
|
391
|
+
@db.convert_infinite_timestamps = :nil
|
|
392
|
+
@db[:test3].get(:time).should == nil
|
|
393
|
+
@db.convert_infinite_timestamps = :string
|
|
394
|
+
@db[:test3].get(:time).should == 'infinity'
|
|
395
|
+
@db.convert_infinite_timestamps = :float
|
|
396
|
+
@db[:test3].get(:time).should == 1.0/0.0
|
|
397
|
+
|
|
398
|
+
@d.update(:time=>Sequel.cast('-infinity', :timestamp))
|
|
399
|
+
@db.convert_infinite_timestamps = :nil
|
|
400
|
+
@db[:test3].get(:time).should == nil
|
|
401
|
+
@db.convert_infinite_timestamps = :string
|
|
402
|
+
@db[:test3].get(:time).should == '-infinity'
|
|
403
|
+
@db.convert_infinite_timestamps = :float
|
|
404
|
+
@db[:test3].get(:time).should == -1.0/0.0
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
specify "should handle conversions from infinite strings/floats in models" do
|
|
408
|
+
c = Class.new(Sequel::Model(:test3))
|
|
409
|
+
@db.convert_infinite_timestamps = :float
|
|
410
|
+
c.new(:time=>'infinity').time.should == 'infinity'
|
|
411
|
+
c.new(:time=>'-infinity').time.should == '-infinity'
|
|
412
|
+
c.new(:time=>1.0/0.0).time.should == 1.0/0.0
|
|
413
|
+
c.new(:time=>-1.0/0.0).time.should == -1.0/0.0
|
|
414
|
+
end
|
|
259
415
|
end
|
|
260
|
-
end
|
|
261
416
|
|
|
262
|
-
|
|
263
|
-
specify "should not raise errors" do
|
|
417
|
+
specify "explain and analyze should not raise errors" do
|
|
264
418
|
@d = POSTGRES_DB[:test3]
|
|
265
419
|
proc{@d.explain}.should_not raise_error
|
|
266
420
|
proc{@d.analyze}.should_not raise_error
|
|
267
421
|
end
|
|
422
|
+
|
|
423
|
+
specify "#locks should be a dataset returning database locks " do
|
|
424
|
+
@db.locks.should be_a_kind_of(Sequel::Dataset)
|
|
425
|
+
@db.locks.all.should be_a_kind_of(Array)
|
|
426
|
+
end
|
|
268
427
|
end
|
|
269
428
|
|
|
270
429
|
describe "A PostgreSQL database" do
|
|
271
430
|
before do
|
|
272
431
|
@db = POSTGRES_DB
|
|
432
|
+
@db.create_table! :test2 do
|
|
433
|
+
text :name
|
|
434
|
+
integer :value
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
after do
|
|
438
|
+
@db.drop_table?(:test2)
|
|
273
439
|
end
|
|
274
440
|
|
|
275
441
|
specify "should support column operations" do
|
|
@@ -281,12 +447,12 @@ describe "A PostgreSQL database" do
|
|
|
281
447
|
@db[:test2].columns.should == [:name, :value, :xyz]
|
|
282
448
|
@db[:test2] << {:name => 'mmm', :value => 111}
|
|
283
449
|
@db[:test2].first[:xyz].should == '000'
|
|
284
|
-
|
|
450
|
+
|
|
285
451
|
@db[:test2].columns.should == [:name, :value, :xyz]
|
|
286
452
|
@db.drop_column :test2, :xyz
|
|
287
|
-
|
|
453
|
+
|
|
288
454
|
@db[:test2].columns.should == [:name, :value]
|
|
289
|
-
|
|
455
|
+
|
|
290
456
|
@db[:test2].delete
|
|
291
457
|
@db.add_column :test2, :xyz, :text, :default => '000'
|
|
292
458
|
@db[:test2] << {:name => 'mmm', :value => 111, :xyz => 'qqqq'}
|
|
@@ -295,31 +461,26 @@ describe "A PostgreSQL database" do
|
|
|
295
461
|
@db.rename_column :test2, :xyz, :zyx
|
|
296
462
|
@db[:test2].columns.should == [:name, :value, :zyx]
|
|
297
463
|
@db[:test2].first[:zyx].should == 'qqqq'
|
|
298
|
-
|
|
464
|
+
|
|
299
465
|
@db.add_column :test2, :xyz, :float
|
|
300
466
|
@db[:test2].delete
|
|
301
467
|
@db[:test2] << {:name => 'mmm', :value => 111, :xyz => 56.78}
|
|
302
468
|
@db.set_column_type :test2, :xyz, :integer
|
|
303
|
-
|
|
469
|
+
|
|
304
470
|
@db[:test2].first[:xyz].should == 57
|
|
305
471
|
end
|
|
306
|
-
|
|
307
|
-
specify "#locks should be a dataset returning database locks " do
|
|
308
|
-
@db.locks.should be_a_kind_of(Sequel::Dataset)
|
|
309
|
-
@db.locks.all.should be_a_kind_of(Array)
|
|
310
|
-
end
|
|
311
|
-
end
|
|
472
|
+
end
|
|
312
473
|
|
|
313
474
|
describe "A PostgreSQL database" do
|
|
314
475
|
before do
|
|
315
476
|
@db = POSTGRES_DB
|
|
316
|
-
@db.drop_table(:posts)
|
|
477
|
+
@db.drop_table?(:posts)
|
|
317
478
|
@db.sqls.clear
|
|
318
479
|
end
|
|
319
480
|
after do
|
|
320
|
-
@db.drop_table(:posts)
|
|
481
|
+
@db.drop_table?(:posts)
|
|
321
482
|
end
|
|
322
|
-
|
|
483
|
+
|
|
323
484
|
specify "should support resetting the primary key sequence" do
|
|
324
485
|
@db.create_table(:posts){primary_key :a}
|
|
325
486
|
@db[:posts].insert(:a=>20).should == 20
|
|
@@ -330,7 +491,7 @@ describe "A PostgreSQL database" do
|
|
|
330
491
|
@db[:posts].insert.should == 21
|
|
331
492
|
@db[:posts].order(:a).map(:a).should == [1, 2, 10, 20, 21]
|
|
332
493
|
end
|
|
333
|
-
|
|
494
|
+
|
|
334
495
|
specify "should support specifying Integer/Bignum/Fixnum types in primary keys and have them be auto incrementing" do
|
|
335
496
|
@db.create_table(:posts){primary_key :a, :type=>Integer}
|
|
336
497
|
@db[:posts].insert.should == 1
|
|
@@ -347,22 +508,22 @@ describe "A PostgreSQL database" do
|
|
|
347
508
|
@db.create_table(:posts){Integer :a}
|
|
348
509
|
@db.reset_primary_key_sequence(:posts).should == nil
|
|
349
510
|
end
|
|
350
|
-
|
|
511
|
+
|
|
351
512
|
specify "should support opclass specification" do
|
|
352
513
|
@db.create_table(:posts){text :title; text :body; integer :user_id; index(:user_id, :opclass => :int4_ops, :type => :btree)}
|
|
353
514
|
@db.sqls.should == [
|
|
354
|
-
|
|
355
|
-
|
|
515
|
+
'CREATE TABLE "posts" ("title" text, "body" text, "user_id" integer)',
|
|
516
|
+
'CREATE INDEX "posts_user_id_index" ON "posts" USING btree ("user_id" int4_ops)'
|
|
356
517
|
]
|
|
357
518
|
end
|
|
358
519
|
|
|
359
520
|
specify "should support fulltext indexes and searching" do
|
|
360
521
|
@db.create_table(:posts){text :title; text :body; full_text_index [:title, :body]; full_text_index :title, :language => 'french'}
|
|
361
522
|
@db.sqls.should == [
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
]
|
|
523
|
+
%{CREATE TABLE "posts" ("title" text, "body" text)},
|
|
524
|
+
%{CREATE INDEX "posts_title_body_index" ON "posts" USING gin (to_tsvector('simple'::regconfig, (COALESCE("title", '') || ' ' || COALESCE("body", ''))))},
|
|
525
|
+
%{CREATE INDEX "posts_title_index" ON "posts" USING gin (to_tsvector('french'::regconfig, (COALESCE("title", ''))))}
|
|
526
|
+
] if check_sqls
|
|
366
527
|
|
|
367
528
|
@db[:posts].insert(:title=>'ruby rails', :body=>'yowsa')
|
|
368
529
|
@db[:posts].insert(:title=>'sequel', :body=>'ruby')
|
|
@@ -373,51 +534,54 @@ describe "A PostgreSQL database" do
|
|
|
373
534
|
@db[:posts].full_text_search([:title, :body], ['yowsa', 'rails']).all.should == [:title=>'ruby rails', :body=>'yowsa']
|
|
374
535
|
@db[:posts].full_text_search(:title, 'scooby', :language => 'french').all.should == [{:title=>'ruby scooby', :body=>'x'}]
|
|
375
536
|
@db.sqls.should == [
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
537
|
+
%{SELECT * FROM "posts" WHERE (to_tsvector('simple'::regconfig, (COALESCE("title", ''))) @@ to_tsquery('simple'::regconfig, 'rails'))},
|
|
538
|
+
%{SELECT * FROM "posts" WHERE (to_tsvector('simple'::regconfig, (COALESCE("title", '') || ' ' || COALESCE("body", ''))) @@ to_tsquery('simple'::regconfig, 'yowsa | rails'))},
|
|
539
|
+
%{SELECT * FROM "posts" WHERE (to_tsvector('french'::regconfig, (COALESCE("title", ''))) @@ to_tsquery('french'::regconfig, 'scooby'))}] if check_sqls
|
|
540
|
+
|
|
541
|
+
@db[:posts].full_text_search(:title, :$n).call(:select, :n=>'rails').should == [{:title=>'ruby rails', :body=>'yowsa'}]
|
|
542
|
+
@db[:posts].full_text_search(:title, :$n).prepare(:select, :fts_select).call(:n=>'rails').should == [{:title=>'ruby rails', :body=>'yowsa'}]
|
|
379
543
|
end
|
|
380
544
|
|
|
381
545
|
specify "should support spatial indexes" do
|
|
382
546
|
@db.create_table(:posts){box :geom; spatial_index [:geom]}
|
|
383
547
|
@db.sqls.should == [
|
|
384
|
-
|
|
385
|
-
|
|
548
|
+
'CREATE TABLE "posts" ("geom" box)',
|
|
549
|
+
'CREATE INDEX "posts_geom_index" ON "posts" USING gist ("geom")'
|
|
386
550
|
]
|
|
387
551
|
end
|
|
388
|
-
|
|
552
|
+
|
|
389
553
|
specify "should support indexes with index type" do
|
|
390
554
|
@db.create_table(:posts){varchar :title, :size => 5; index :title, :type => 'hash'}
|
|
391
555
|
@db.sqls.should == [
|
|
392
|
-
|
|
393
|
-
|
|
556
|
+
'CREATE TABLE "posts" ("title" varchar(5))',
|
|
557
|
+
'CREATE INDEX "posts_title_index" ON "posts" USING hash ("title")'
|
|
394
558
|
]
|
|
395
559
|
end
|
|
396
|
-
|
|
560
|
+
|
|
397
561
|
specify "should support unique indexes with index type" do
|
|
398
562
|
@db.create_table(:posts){varchar :title, :size => 5; index :title, :type => 'btree', :unique => true}
|
|
399
563
|
@db.sqls.should == [
|
|
400
|
-
|
|
401
|
-
|
|
564
|
+
'CREATE TABLE "posts" ("title" varchar(5))',
|
|
565
|
+
'CREATE UNIQUE INDEX "posts_title_index" ON "posts" USING btree ("title")'
|
|
402
566
|
]
|
|
403
567
|
end
|
|
404
|
-
|
|
568
|
+
|
|
405
569
|
specify "should support partial indexes" do
|
|
406
570
|
@db.create_table(:posts){varchar :title, :size => 5; index :title, :where => {:title => '5'}}
|
|
407
571
|
@db.sqls.should == [
|
|
408
|
-
|
|
409
|
-
|
|
572
|
+
'CREATE TABLE "posts" ("title" varchar(5))',
|
|
573
|
+
'CREATE INDEX "posts_title_index" ON "posts" ("title") WHERE ("title" = \'5\')'
|
|
410
574
|
]
|
|
411
575
|
end
|
|
412
|
-
|
|
576
|
+
|
|
413
577
|
specify "should support identifiers for table names in indicies" do
|
|
414
578
|
@db.create_table(Sequel::SQL::Identifier.new(:posts)){varchar :title, :size => 5; index :title, :where => {:title => '5'}}
|
|
415
579
|
@db.sqls.should == [
|
|
416
|
-
|
|
417
|
-
|
|
580
|
+
'CREATE TABLE "posts" ("title" varchar(5))',
|
|
581
|
+
'CREATE INDEX "posts_title_index" ON "posts" ("title") WHERE ("title" = \'5\')'
|
|
418
582
|
]
|
|
419
583
|
end
|
|
420
|
-
|
|
584
|
+
|
|
421
585
|
specify "should support renaming tables" do
|
|
422
586
|
@db.create_table!(:posts1){primary_key :a}
|
|
423
587
|
@db.rename_table(:posts1, :posts)
|
|
@@ -427,38 +591,33 @@ end
|
|
|
427
591
|
describe "Postgres::Dataset#import" do
|
|
428
592
|
before do
|
|
429
593
|
@db = POSTGRES_DB
|
|
430
|
-
@db.create_table!(:test){
|
|
594
|
+
@db.create_table!(:test){primary_key :x; Integer :y}
|
|
431
595
|
@db.sqls.clear
|
|
432
596
|
@ds = @db[:test]
|
|
433
597
|
end
|
|
434
598
|
after do
|
|
435
|
-
@db.drop_table(:test)
|
|
599
|
+
@db.drop_table?(:test)
|
|
436
600
|
end
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
specify "#import should a single insert statement" do
|
|
441
604
|
@ds.import([:x, :y], [[1, 2], [3, 4]])
|
|
442
|
-
|
|
443
|
-
@db.sqls.should == [
|
|
444
|
-
'BEGIN',
|
|
445
|
-
'INSERT INTO test (x, y) VALUES (1, 2)',
|
|
446
|
-
'INSERT INTO test (x, y) VALUES (3, 4)',
|
|
447
|
-
'COMMIT'
|
|
448
|
-
]
|
|
605
|
+
@db.sqls.should == ['BEGIN', 'INSERT INTO "test" ("x", "y") VALUES (1, 2), (3, 4)', 'COMMIT']
|
|
449
606
|
@ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
|
|
450
607
|
end
|
|
451
|
-
|
|
452
|
-
specify "#import should
|
|
453
|
-
@ds.
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
608
|
+
|
|
609
|
+
specify "#import should work correctly when returning primary keys" do
|
|
610
|
+
@ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key).should == [1, 3]
|
|
611
|
+
@ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
specify "#import should work correctly when returning primary keys with :slice option" do
|
|
615
|
+
@ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key, :slice=>1).should == [1, 3]
|
|
616
|
+
@ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
specify "#import should work correctly with an arbitrary returning value" do
|
|
620
|
+
@ds.returning(:y, :x).import([:x, :y], [[1, 2], [3, 4]]).should == [{:y=>2, :x=>1}, {:y=>4, :x=>3}]
|
|
462
621
|
@ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
|
|
463
622
|
end
|
|
464
623
|
end
|
|
@@ -471,7 +630,7 @@ describe "Postgres::Dataset#insert" do
|
|
|
471
630
|
@ds = @db[:test5]
|
|
472
631
|
end
|
|
473
632
|
after do
|
|
474
|
-
@db.drop_table(:test5)
|
|
633
|
+
@db.drop_table?(:test5)
|
|
475
634
|
end
|
|
476
635
|
|
|
477
636
|
specify "should work with static SQL" do
|
|
@@ -480,56 +639,17 @@ describe "Postgres::Dataset#insert" do
|
|
|
480
639
|
@ds.all.should == [{:xid=>1, :value=>10}, {:xid=>2, :value=>20}]
|
|
481
640
|
end
|
|
482
641
|
|
|
483
|
-
specify "should
|
|
484
|
-
@ds.insert(:value
|
|
485
|
-
@ds.
|
|
486
|
-
@ds.meta_def(:server_version){80100}
|
|
487
|
-
@ds.insert(:value=>13).should == 3
|
|
488
|
-
|
|
489
|
-
@db.sqls.reject{|x| x =~ /pg_class/}.should == [
|
|
490
|
-
'INSERT INTO test5 (value) VALUES (10) RETURNING xid',
|
|
491
|
-
'INSERT INTO test5 (value) VALUES (20)',
|
|
492
|
-
"SELECT currval('\"public\".test5_xid_seq')",
|
|
493
|
-
'INSERT INTO test5 (value) VALUES (13)',
|
|
494
|
-
"SELECT currval('\"public\".test5_xid_seq')"
|
|
495
|
-
]
|
|
496
|
-
@ds.all.should == [{:xid=>1, :value=>10}, {:xid=>2, :value=>20}, {:xid=>3, :value=>13}]
|
|
497
|
-
end
|
|
498
|
-
|
|
499
|
-
specify "should call execute_insert if server_version < 80200" do
|
|
500
|
-
@ds.meta_def(:server_version){80100}
|
|
501
|
-
@ds.should_receive(:execute_insert).once.with('INSERT INTO test5 (value) VALUES (10)', :table=>:test5, :values=>{:value=>10})
|
|
502
|
-
@ds.insert(:value=>10)
|
|
503
|
-
end
|
|
504
|
-
|
|
505
|
-
specify "should call execute_insert if disabling insert returning" do
|
|
506
|
-
@ds.disable_insert_returning!
|
|
507
|
-
@ds.should_receive(:execute_insert).once.with('INSERT INTO test5 (value) VALUES (10)', :table=>:test5, :values=>{:value=>10})
|
|
508
|
-
@ds.insert(:value=>10)
|
|
642
|
+
specify "should insert correctly if using a column array and a value array" do
|
|
643
|
+
@ds.insert([:value], [10]).should == 1
|
|
644
|
+
@ds.all.should == [{:xid=>1, :value=>10}]
|
|
509
645
|
end
|
|
510
646
|
|
|
511
|
-
specify "should use INSERT RETURNING
|
|
512
|
-
@ds.
|
|
513
|
-
@
|
|
514
|
-
@db.sqls.last.should == 'INSERT INTO test5 (value) VALUES (10) RETURNING xid'
|
|
515
|
-
end
|
|
516
|
-
|
|
517
|
-
specify "should have insert_returning_sql use the RETURNING keyword" do
|
|
518
|
-
@ds.insert_returning_sql(:xid, :value=>10).should == "INSERT INTO test5 (value) VALUES (10) RETURNING xid"
|
|
519
|
-
@ds.insert_returning_sql('*'.lit, :value=>10).should == "INSERT INTO test5 (value) VALUES (10) RETURNING *"
|
|
520
|
-
end
|
|
521
|
-
|
|
522
|
-
specify "should have insert_select return nil if server_version < 80200" do
|
|
523
|
-
@ds.meta_def(:server_version){80100}
|
|
524
|
-
@ds.insert_select(:value=>10).should == nil
|
|
525
|
-
end
|
|
526
|
-
|
|
527
|
-
specify "should have insert_select return nil if disable_insert_returning is used" do
|
|
528
|
-
@ds.disable_insert_returning.insert_select(:value=>10).should == nil
|
|
647
|
+
specify "should use INSERT RETURNING" do
|
|
648
|
+
@ds.insert(:value=>10).should == 1
|
|
649
|
+
@db.sqls.last.should == 'INSERT INTO "test5" ("value") VALUES (10) RETURNING "xid"' if check_sqls
|
|
529
650
|
end
|
|
530
651
|
|
|
531
|
-
specify "should have insert_select insert the record and return the inserted record
|
|
532
|
-
@ds.meta_def(:server_version){80201}
|
|
652
|
+
specify "should have insert_select insert the record and return the inserted record" do
|
|
533
653
|
h = @ds.insert_select(:value=>10)
|
|
534
654
|
h[:value].should == 10
|
|
535
655
|
@ds.first(:xid=>h[:xid])[:value].should == 10
|
|
@@ -545,9 +665,9 @@ describe "Postgres::Dataset#insert" do
|
|
|
545
665
|
end
|
|
546
666
|
|
|
547
667
|
specify "should return nil if the table has no primary key" do
|
|
548
|
-
|
|
549
|
-
ds.delete
|
|
550
|
-
ds.insert(:name=>'a').should == nil
|
|
668
|
+
@db.create_table!(:test5){String :name; Integer :value}
|
|
669
|
+
@ds.delete
|
|
670
|
+
@ds.insert(:name=>'a').should == nil
|
|
551
671
|
end
|
|
552
672
|
end
|
|
553
673
|
|
|
@@ -558,36 +678,35 @@ describe "Postgres::Database schema qualified tables" do
|
|
|
558
678
|
POSTGRES_DB.instance_variable_set(:@primary_key_sequences, {})
|
|
559
679
|
end
|
|
560
680
|
after do
|
|
561
|
-
POSTGRES_DB.quote_identifiers = false
|
|
562
681
|
POSTGRES_DB << "DROP SCHEMA schema_test CASCADE"
|
|
563
682
|
POSTGRES_DB.default_schema = nil
|
|
564
683
|
end
|
|
565
|
-
|
|
684
|
+
|
|
566
685
|
specify "should be able to create, drop, select and insert into tables in a given schema" do
|
|
567
686
|
POSTGRES_DB.create_table(:schema_test__schema_test){primary_key :i}
|
|
568
687
|
POSTGRES_DB[:schema_test__schema_test].first.should == nil
|
|
569
688
|
POSTGRES_DB[:schema_test__schema_test].insert(:i=>1).should == 1
|
|
570
689
|
POSTGRES_DB[:schema_test__schema_test].first.should == {:i=>1}
|
|
571
|
-
POSTGRES_DB.from('schema_test.schema_test'
|
|
690
|
+
POSTGRES_DB.from(Sequel.lit('schema_test.schema_test')).first.should == {:i=>1}
|
|
572
691
|
POSTGRES_DB.drop_table(:schema_test__schema_test)
|
|
573
|
-
POSTGRES_DB.create_table(
|
|
692
|
+
POSTGRES_DB.create_table(Sequel.qualify(:schema_test, :schema_test)){integer :i}
|
|
574
693
|
POSTGRES_DB[:schema_test__schema_test].first.should == nil
|
|
575
|
-
POSTGRES_DB.from('schema_test.schema_test'
|
|
576
|
-
POSTGRES_DB.drop_table(
|
|
694
|
+
POSTGRES_DB.from(Sequel.lit('schema_test.schema_test')).first.should == nil
|
|
695
|
+
POSTGRES_DB.drop_table(Sequel.qualify(:schema_test, :schema_test))
|
|
577
696
|
end
|
|
578
|
-
|
|
697
|
+
|
|
579
698
|
specify "#tables should not include tables in a default non-public schema" do
|
|
580
699
|
POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
|
|
581
700
|
POSTGRES_DB.tables.should include(:schema_test)
|
|
582
701
|
POSTGRES_DB.tables.should_not include(:pg_am)
|
|
583
702
|
POSTGRES_DB.tables.should_not include(:domain_udt_usage)
|
|
584
703
|
end
|
|
585
|
-
|
|
704
|
+
|
|
586
705
|
specify "#tables should return tables in the schema provided by the :schema argument" do
|
|
587
706
|
POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
|
|
588
707
|
POSTGRES_DB.tables(:schema=>:schema_test).should == [:schema_test]
|
|
589
708
|
end
|
|
590
|
-
|
|
709
|
+
|
|
591
710
|
specify "#schema should not include columns from tables in a default non-public schema" do
|
|
592
711
|
POSTGRES_DB.create_table(:schema_test__domains){integer :i}
|
|
593
712
|
sch = POSTGRES_DB.schema(:domains)
|
|
@@ -595,7 +714,7 @@ describe "Postgres::Database schema qualified tables" do
|
|
|
595
714
|
cs.should include(:i)
|
|
596
715
|
cs.should_not include(:data_type)
|
|
597
716
|
end
|
|
598
|
-
|
|
717
|
+
|
|
599
718
|
specify "#schema should only include columns from the table in the given :schema argument" do
|
|
600
719
|
POSTGRES_DB.create_table!(:domains){integer :d}
|
|
601
720
|
POSTGRES_DB.create_table(:schema_test__domains){integer :i}
|
|
@@ -605,54 +724,58 @@ describe "Postgres::Database schema qualified tables" do
|
|
|
605
724
|
cs.should_not include(:d)
|
|
606
725
|
POSTGRES_DB.drop_table(:domains)
|
|
607
726
|
end
|
|
608
|
-
|
|
609
|
-
specify "#
|
|
610
|
-
POSTGRES_DB.create_table(:
|
|
611
|
-
POSTGRES_DB.
|
|
612
|
-
|
|
727
|
+
|
|
728
|
+
specify "#schema should raise an exception if columns from tables in two separate schema are returned" do
|
|
729
|
+
POSTGRES_DB.create_table!(:public__domains){integer :d}
|
|
730
|
+
POSTGRES_DB.create_table(:schema_test__domains){integer :i}
|
|
731
|
+
begin
|
|
732
|
+
proc{POSTGRES_DB.schema(:domains)}.should raise_error(Sequel::Error)
|
|
733
|
+
POSTGRES_DB.schema(:public__domains).map{|x| x.first}.should == [:d]
|
|
734
|
+
POSTGRES_DB.schema(:schema_test__domains).map{|x| x.first}.should == [:i]
|
|
735
|
+
ensure
|
|
736
|
+
POSTGRES_DB.drop_table?(:public__domains)
|
|
737
|
+
end
|
|
613
738
|
end
|
|
614
|
-
|
|
739
|
+
|
|
615
740
|
specify "#table_exists? should see if the table is in a given schema" do
|
|
616
741
|
POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
|
|
617
742
|
POSTGRES_DB.table_exists?(:schema_test__schema_test).should == true
|
|
618
743
|
end
|
|
619
|
-
|
|
744
|
+
|
|
620
745
|
specify "should be able to get primary keys for tables in a given schema" do
|
|
621
746
|
POSTGRES_DB.create_table(:schema_test__schema_test){primary_key :i}
|
|
622
747
|
POSTGRES_DB.primary_key(:schema_test__schema_test).should == 'i'
|
|
623
748
|
end
|
|
624
|
-
|
|
749
|
+
|
|
625
750
|
specify "should be able to get serial sequences for tables in a given schema" do
|
|
626
751
|
POSTGRES_DB.create_table(:schema_test__schema_test){primary_key :i}
|
|
627
|
-
POSTGRES_DB.primary_key_sequence(:schema_test__schema_test).should == '"schema_test".schema_test_i_seq'
|
|
752
|
+
POSTGRES_DB.primary_key_sequence(:schema_test__schema_test).should == '"schema_test"."schema_test_i_seq"'
|
|
628
753
|
end
|
|
629
|
-
|
|
754
|
+
|
|
630
755
|
specify "should be able to get serial sequences for tables that have spaces in the name in a given schema" do
|
|
631
|
-
POSTGRES_DB.quote_identifiers = true
|
|
632
756
|
POSTGRES_DB.create_table(:"schema_test__schema test"){primary_key :i}
|
|
633
757
|
POSTGRES_DB.primary_key_sequence(:"schema_test__schema test").should == '"schema_test"."schema test_i_seq"'
|
|
634
758
|
end
|
|
635
|
-
|
|
759
|
+
|
|
636
760
|
specify "should be able to get custom sequences for tables in a given schema" do
|
|
637
761
|
POSTGRES_DB << "CREATE SEQUENCE schema_test.kseq"
|
|
638
|
-
POSTGRES_DB.create_table(:schema_test__schema_test){integer :j; primary_key :k, :type=>:integer, :default=>"nextval('schema_test.kseq'::regclass)"
|
|
762
|
+
POSTGRES_DB.create_table(:schema_test__schema_test){integer :j; primary_key :k, :type=>:integer, :default=>Sequel.lit("nextval('schema_test.kseq'::regclass)")}
|
|
639
763
|
POSTGRES_DB.primary_key_sequence(:schema_test__schema_test).should == '"schema_test".kseq'
|
|
640
764
|
end
|
|
641
|
-
|
|
765
|
+
|
|
642
766
|
specify "should be able to get custom sequences for tables that have spaces in the name in a given schema" do
|
|
643
|
-
POSTGRES_DB.quote_identifiers = true
|
|
644
767
|
POSTGRES_DB << "CREATE SEQUENCE schema_test.\"ks eq\""
|
|
645
|
-
POSTGRES_DB.create_table(:"schema_test__schema test"){integer :j; primary_key :k, :type=>:integer, :default=>"nextval('schema_test.\"ks eq\"'::regclass)"
|
|
768
|
+
POSTGRES_DB.create_table(:"schema_test__schema test"){integer :j; primary_key :k, :type=>:integer, :default=>Sequel.lit("nextval('schema_test.\"ks eq\"'::regclass)")}
|
|
646
769
|
POSTGRES_DB.primary_key_sequence(:"schema_test__schema test").should == '"schema_test"."ks eq"'
|
|
647
770
|
end
|
|
648
|
-
|
|
771
|
+
|
|
649
772
|
specify "#default_schema= should change the default schema used from public" do
|
|
650
773
|
POSTGRES_DB.create_table(:schema_test__schema_test){primary_key :i}
|
|
651
774
|
POSTGRES_DB.default_schema = :schema_test
|
|
652
775
|
POSTGRES_DB.table_exists?(:schema_test).should == true
|
|
653
776
|
POSTGRES_DB.tables.should == [:schema_test]
|
|
654
777
|
POSTGRES_DB.primary_key(:schema_test__schema_test).should == 'i'
|
|
655
|
-
POSTGRES_DB.primary_key_sequence(:schema_test__schema_test).should == '"schema_test".schema_test_i_seq'
|
|
778
|
+
POSTGRES_DB.primary_key_sequence(:schema_test__schema_test).should == '"schema_test"."schema_test_i_seq"'
|
|
656
779
|
end
|
|
657
780
|
end
|
|
658
781
|
|
|
@@ -661,13 +784,12 @@ describe "Postgres::Database schema qualified tables and eager graphing" do
|
|
|
661
784
|
@db = POSTGRES_DB
|
|
662
785
|
@db.run "DROP SCHEMA s CASCADE" rescue nil
|
|
663
786
|
@db.run "CREATE SCHEMA s"
|
|
664
|
-
|
|
665
|
-
|
|
787
|
+
|
|
666
788
|
@db.create_table(:s__bands){primary_key :id; String :name}
|
|
667
789
|
@db.create_table(:s__albums){primary_key :id; String :name; foreign_key :band_id, :s__bands}
|
|
668
790
|
@db.create_table(:s__tracks){primary_key :id; String :name; foreign_key :album_id, :s__albums}
|
|
669
791
|
@db.create_table(:s__members){primary_key :id; String :name; foreign_key :band_id, :s__bands}
|
|
670
|
-
|
|
792
|
+
|
|
671
793
|
@Band = Class.new(Sequel::Model(:s__bands))
|
|
672
794
|
@Album = Class.new(Sequel::Model(:s__albums))
|
|
673
795
|
@Track = Class.new(Sequel::Model(:s__tracks))
|
|
@@ -676,17 +798,17 @@ describe "Postgres::Database schema qualified tables and eager graphing" do
|
|
|
676
798
|
def @Album.name; :Album; end
|
|
677
799
|
def @Track.name; :Track; end
|
|
678
800
|
def @Member.name; :Member; end
|
|
679
|
-
|
|
801
|
+
|
|
680
802
|
@Band.one_to_many :albums, :class=>@Album, :order=>:name
|
|
681
803
|
@Band.one_to_many :members, :class=>@Member, :order=>:name
|
|
682
804
|
@Album.many_to_one :band, :class=>@Band, :order=>:name
|
|
683
805
|
@Album.one_to_many :tracks, :class=>@Track, :order=>:name
|
|
684
806
|
@Track.many_to_one :album, :class=>@Album, :order=>:name
|
|
685
807
|
@Member.many_to_one :band, :class=>@Band, :order=>:name
|
|
686
|
-
|
|
808
|
+
|
|
687
809
|
@Member.many_to_many :members, :class=>@Member, :join_table=>:s__bands, :right_key=>:id, :left_key=>:id, :left_primary_key=>:band_id, :right_primary_key=>:band_id, :order=>:name
|
|
688
810
|
@Band.many_to_many :tracks, :class=>@Track, :join_table=>:s__albums, :right_key=>:id, :right_primary_key=>:album_id, :order=>:name
|
|
689
|
-
|
|
811
|
+
|
|
690
812
|
@b1 = @Band.create(:name=>"BM")
|
|
691
813
|
@b2 = @Band.create(:name=>"J")
|
|
692
814
|
@a1 = @Album.create(:name=>"BM1", :band=>@b1)
|
|
@@ -703,10 +825,9 @@ describe "Postgres::Database schema qualified tables and eager graphing" do
|
|
|
703
825
|
@m4 = @Member.create(:name=>"JC", :band=>@b2)
|
|
704
826
|
end
|
|
705
827
|
after(:all) do
|
|
706
|
-
@db.quote_identifiers = false
|
|
707
828
|
@db.run "DROP SCHEMA s CASCADE"
|
|
708
829
|
end
|
|
709
|
-
|
|
830
|
+
|
|
710
831
|
specify "should return all eager graphs correctly" do
|
|
711
832
|
bands = @Band.order(:bands__name).eager_graph(:albums).all
|
|
712
833
|
bands.should == [@b1, @b2]
|
|
@@ -725,14 +846,14 @@ describe "Postgres::Database schema qualified tables and eager graphing" do
|
|
|
725
846
|
end
|
|
726
847
|
|
|
727
848
|
specify "should have eager graphs work with previous joins" do
|
|
728
|
-
bands = @Band.order(:bands__name).
|
|
849
|
+
bands = @Band.order(:bands__name).select_all(:s__bands).join(:s__members, :band_id=>:id).from_self(:alias=>:bands0).eager_graph(:albums=>:tracks).all
|
|
729
850
|
bands.should == [@b1, @b2]
|
|
730
851
|
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
|
731
852
|
bands.map{|x| x.albums.map{|y| y.tracks}}.should == [[[@t1, @t2], [@t3, @t4]], [[], []]]
|
|
732
853
|
end
|
|
733
854
|
|
|
734
855
|
specify "should have eager graphs work with joins with the same tables" do
|
|
735
|
-
bands = @Band.order(:bands__name).
|
|
856
|
+
bands = @Band.order(:bands__name).select_all(:s__bands).join(:s__members, :band_id=>:id).eager_graph({:albums=>:tracks}, :members).all
|
|
736
857
|
bands.should == [@b1, @b2]
|
|
737
858
|
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
|
738
859
|
bands.map{|x| x.albums.map{|y| y.tracks}}.should == [[[@t1, @t2], [@t3, @t4]], [[], []]]
|
|
@@ -770,62 +891,62 @@ describe "Postgres::Database schema qualified tables and eager graphing" do
|
|
|
770
891
|
bands.should == [@b1, @b2]
|
|
771
892
|
bands.map{|x| x.tracks}.should == [[@t1, @t2, @t3, @t4], []]
|
|
772
893
|
|
|
773
|
-
bands = @Band.order(:tracks__name).from(:s__bands.as(:tracks)).eager_graph(:tracks).all
|
|
894
|
+
bands = @Band.order(:tracks__name).from(Sequel.expr(:s__bands).as(:tracks)).eager_graph(:tracks).all
|
|
774
895
|
bands.should == [@b1, @b2]
|
|
775
896
|
bands.map{|x| x.tracks}.should == [[@t1, @t2, @t3, @t4], []]
|
|
776
897
|
|
|
777
|
-
bands = @Band.order(:tracks__name).from(:s__bands.as(:tracks
|
|
898
|
+
bands = @Band.order(:tracks__name).from(Sequel.expr(:s__bands).as(Sequel.identifier(:tracks))).eager_graph(:tracks).all
|
|
778
899
|
bands.should == [@b1, @b2]
|
|
779
900
|
bands.map{|x| x.tracks}.should == [[@t1, @t2, @t3, @t4], []]
|
|
780
901
|
|
|
781
|
-
bands = @Band.order(:tracks__name).from(:s__bands.as('tracks')).eager_graph(:tracks).all
|
|
902
|
+
bands = @Band.order(:tracks__name).from(Sequel.expr(:s__bands).as('tracks')).eager_graph(:tracks).all
|
|
782
903
|
bands.should == [@b1, @b2]
|
|
783
904
|
bands.map{|x| x.tracks}.should == [[@t1, @t2, @t3, @t4], []]
|
|
784
905
|
end
|
|
785
906
|
|
|
786
907
|
specify "should have eager graphs work with join tables with aliases" do
|
|
787
|
-
bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums___tracks, :band_id
|
|
908
|
+
bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums___tracks, :band_id=>Sequel.qualify(:s__bands, :id)).eager_graph(:albums=>:tracks).all
|
|
788
909
|
bands.should == [@b1, @b2]
|
|
789
910
|
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
|
790
911
|
bands.map{|x| x.members}.should == [[@m1, @m2], [@m4, @m3]]
|
|
791
912
|
|
|
792
|
-
bands = @Band.order(:bands__name).eager_graph(:members).join(
|
|
913
|
+
bands = @Band.order(:bands__name).eager_graph(:members).join(Sequel.as(:s__albums, :tracks), :band_id=>Sequel.qualify(:s__bands, :id)).eager_graph(:albums=>:tracks).all
|
|
793
914
|
bands.should == [@b1, @b2]
|
|
794
915
|
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
|
795
916
|
bands.map{|x| x.members}.should == [[@m1, @m2], [@m4, @m3]]
|
|
796
917
|
|
|
797
|
-
bands = @Band.order(:bands__name).eager_graph(:members).join(
|
|
918
|
+
bands = @Band.order(:bands__name).eager_graph(:members).join(Sequel.as(:s__albums, 'tracks'), :band_id=>Sequel.qualify(:s__bands, :id)).eager_graph(:albums=>:tracks).all
|
|
798
919
|
bands.should == [@b1, @b2]
|
|
799
920
|
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
|
800
921
|
bands.map{|x| x.members}.should == [[@m1, @m2], [@m4, @m3]]
|
|
801
922
|
|
|
802
|
-
bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums.
|
|
923
|
+
bands = @Band.order(:bands__name).eager_graph(:members).join(Sequel.as(:s__albums, Sequel.identifier(:tracks)), :band_id=>Sequel.qualify(:s__bands, :id)).eager_graph(:albums=>:tracks).all
|
|
803
924
|
bands.should == [@b1, @b2]
|
|
804
925
|
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
|
805
926
|
bands.map{|x| x.members}.should == [[@m1, @m2], [@m4, @m3]]
|
|
806
927
|
|
|
807
|
-
bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums, {:band_id
|
|
928
|
+
bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums, {:band_id=>Sequel.qualify(:s__bands, :id)}, :table_alias=>:tracks).eager_graph(:albums=>:tracks).all
|
|
808
929
|
bands.should == [@b1, @b2]
|
|
809
930
|
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
|
810
931
|
bands.map{|x| x.members}.should == [[@m1, @m2], [@m4, @m3]]
|
|
811
932
|
|
|
812
|
-
bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums, {:band_id
|
|
933
|
+
bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums, {:band_id=>Sequel.qualify(:s__bands, :id)}, :table_alias=>'tracks').eager_graph(:albums=>:tracks).all
|
|
813
934
|
bands.should == [@b1, @b2]
|
|
814
935
|
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
|
815
936
|
bands.map{|x| x.members}.should == [[@m1, @m2], [@m4, @m3]]
|
|
816
937
|
|
|
817
|
-
bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums, {:band_id
|
|
938
|
+
bands = @Band.order(:bands__name).eager_graph(:members).join(:s__albums, {:band_id=>Sequel.qualify(:s__bands, :id)}, :table_alias=>Sequel.identifier(:tracks)).eager_graph(:albums=>:tracks).all
|
|
818
939
|
bands.should == [@b1, @b2]
|
|
819
940
|
bands.map{|x| x.albums}.should == [[@a1, @a2], [@a3, @a4]]
|
|
820
941
|
bands.map{|x| x.members}.should == [[@m1, @m2], [@m4, @m3]]
|
|
821
942
|
end
|
|
822
943
|
|
|
823
944
|
specify "should have eager graphs work with different types of qualified from tables" do
|
|
824
|
-
bands = @Band.order(:bands__name).from(
|
|
945
|
+
bands = @Band.order(:bands__name).from(Sequel.qualify(:s, :bands)).eager_graph(:tracks).all
|
|
825
946
|
bands.should == [@b1, @b2]
|
|
826
947
|
bands.map{|x| x.tracks}.should == [[@t1, @t2, @t3, @t4], []]
|
|
827
948
|
|
|
828
|
-
bands = @Band.order(:bands__name).from(:bands.
|
|
949
|
+
bands = @Band.order(:bands__name).from(Sequel.identifier(:bands).qualify(:s)).eager_graph(:tracks).all
|
|
829
950
|
bands.should == [@b1, @b2]
|
|
830
951
|
bands.map{|x| x.tracks}.should == [[@t1, @t2, @t3, @t4], []]
|
|
831
952
|
|
|
@@ -837,20 +958,21 @@ describe "Postgres::Database schema qualified tables and eager graphing" do
|
|
|
837
958
|
end
|
|
838
959
|
|
|
839
960
|
if POSTGRES_DB.server_version >= 80300
|
|
840
|
-
|
|
841
|
-
POSTGRES_DB.create_table! :test6 do
|
|
842
|
-
text :title
|
|
843
|
-
text :body
|
|
844
|
-
full_text_index [:title, :body]
|
|
845
|
-
end
|
|
846
|
-
|
|
847
961
|
describe "PostgreSQL tsearch2" do
|
|
848
|
-
before do
|
|
962
|
+
before(:all) do
|
|
963
|
+
POSTGRES_DB.create_table! :test6 do
|
|
964
|
+
text :title
|
|
965
|
+
text :body
|
|
966
|
+
full_text_index [:title, :body]
|
|
967
|
+
end
|
|
849
968
|
@ds = POSTGRES_DB[:test6]
|
|
850
969
|
end
|
|
851
970
|
after do
|
|
852
971
|
POSTGRES_DB[:test6].delete
|
|
853
972
|
end
|
|
973
|
+
after(:all) do
|
|
974
|
+
POSTGRES_DB.drop_table?(:test6)
|
|
975
|
+
end
|
|
854
976
|
|
|
855
977
|
specify "should search by indexed column" do
|
|
856
978
|
record = {:title => "oopsla conference", :body => "test"}
|
|
@@ -886,9 +1008,9 @@ if POSTGRES_DB.dataset.supports_window_functions?
|
|
|
886
1008
|
@ds.insert(:id=>6, :group_id=>2, :amount=>100000)
|
|
887
1009
|
end
|
|
888
1010
|
after do
|
|
889
|
-
@db.drop_table(:i1)
|
|
1011
|
+
@db.drop_table?(:i1)
|
|
890
1012
|
end
|
|
891
|
-
|
|
1013
|
+
|
|
892
1014
|
specify "should give correct results for window functions" do
|
|
893
1015
|
@ds.window(:win, :partition=>:group_id, :order=>:id).select(:id){sum(:over, :args=>amount, :window=>win){}}.all.should ==
|
|
894
1016
|
[{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1000, :id=>4}, {:sum=>11000, :id=>5}, {:sum=>111000, :id=>6}]
|
|
@@ -902,15 +1024,16 @@ if POSTGRES_DB.dataset.supports_window_functions?
|
|
|
902
1024
|
end
|
|
903
1025
|
end
|
|
904
1026
|
|
|
905
|
-
describe "Postgres::Database functions, languages, and triggers" do
|
|
1027
|
+
describe "Postgres::Database functions, languages, schemas, and triggers" do
|
|
906
1028
|
before do
|
|
907
1029
|
@d = POSTGRES_DB
|
|
908
1030
|
end
|
|
909
1031
|
after do
|
|
910
1032
|
@d.drop_function('tf', :if_exists=>true, :cascade=>true)
|
|
911
1033
|
@d.drop_function('tf', :if_exists=>true, :cascade=>true, :args=>%w'integer integer')
|
|
912
|
-
@d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true)
|
|
913
|
-
@d.
|
|
1034
|
+
@d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true) if @d.server_version < 90000
|
|
1035
|
+
@d.drop_schema(:sequel, :if_exists=>true, :cascade=>true)
|
|
1036
|
+
@d.drop_table?(:test)
|
|
914
1037
|
end
|
|
915
1038
|
|
|
916
1039
|
specify "#create_function and #drop_function should create and drop functions" do
|
|
@@ -923,7 +1046,7 @@ describe "Postgres::Database functions, languages, and triggers" do
|
|
|
923
1046
|
@d.drop_function('tf')
|
|
924
1047
|
proc{@d['SELECT tf()'].all}.should raise_error(Sequel::DatabaseError)
|
|
925
1048
|
end
|
|
926
|
-
|
|
1049
|
+
|
|
927
1050
|
specify "#create_function and #drop_function should support options" do
|
|
928
1051
|
args = ['tf', 'SELECT $1 + $2', {:args=>[[:integer, :a], :integer], :replace=>true, :returns=>:integer, :language=>'SQL', :behavior=>:immutable, :strict=>true, :security_definer=>true, :cost=>2, :set=>{:search_path => 'public'}}]
|
|
929
1052
|
@d.send(:create_function_sql,*args).should =~ /\A\s*CREATE OR REPLACE FUNCTION tf\(a integer, integer\)\s+RETURNS integer\s+LANGUAGE SQL\s+IMMUTABLE\s+STRICT\s+SECURITY DEFINER\s+COST 2\s+SET search_path = public\s+AS 'SELECT \$1 \+ \$2'\s*\z/
|
|
@@ -937,24 +1060,33 @@ describe "Postgres::Database functions, languages, and triggers" do
|
|
|
937
1060
|
# Make sure if exists works
|
|
938
1061
|
@d.drop_function(*args)
|
|
939
1062
|
end
|
|
940
|
-
|
|
1063
|
+
|
|
941
1064
|
specify "#create_language and #drop_language should create and drop languages" do
|
|
942
1065
|
@d.send(:create_language_sql, :plpgsql).should == 'CREATE LANGUAGE plpgsql'
|
|
943
|
-
@d.create_language(:plpgsql, :replace=>true)
|
|
1066
|
+
@d.create_language(:plpgsql, :replace=>true) if @d.server_version < 90000
|
|
944
1067
|
proc{@d.create_language(:plpgsql)}.should raise_error(Sequel::DatabaseError)
|
|
945
1068
|
@d.send(:drop_language_sql, :plpgsql).should == 'DROP LANGUAGE plpgsql'
|
|
946
|
-
@d.drop_language(:plpgsql)
|
|
947
|
-
proc{@d.drop_language(:plpgsql)}.should raise_error(Sequel::DatabaseError)
|
|
1069
|
+
@d.drop_language(:plpgsql) if @d.server_version < 90000
|
|
1070
|
+
proc{@d.drop_language(:plpgsql)}.should raise_error(Sequel::DatabaseError) if @d.server_version < 90000
|
|
948
1071
|
@d.send(:create_language_sql, :plpgsql, :replace=>true, :trusted=>true, :handler=>:a, :validator=>:b).should == (@d.server_version >= 90000 ? 'CREATE OR REPLACE TRUSTED LANGUAGE plpgsql HANDLER a VALIDATOR b' : 'CREATE TRUSTED LANGUAGE plpgsql HANDLER a VALIDATOR b')
|
|
949
1072
|
@d.send(:drop_language_sql, :plpgsql, :if_exists=>true, :cascade=>true).should == 'DROP LANGUAGE IF EXISTS plpgsql CASCADE'
|
|
950
1073
|
# Make sure if exists works
|
|
951
|
-
@d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true)
|
|
1074
|
+
@d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true) if @d.server_version < 90000
|
|
1075
|
+
end
|
|
1076
|
+
|
|
1077
|
+
specify "#create_schema and #drop_schema should create and drop schemas" do
|
|
1078
|
+
@d.send(:create_schema_sql, :sequel).should == 'CREATE SCHEMA "sequel"'
|
|
1079
|
+
@d.send(:drop_schema_sql, :sequel).should == 'DROP SCHEMA "sequel"'
|
|
1080
|
+
@d.send(:drop_schema_sql, :sequel, :if_exists=>true, :cascade=>true).should == 'DROP SCHEMA IF EXISTS "sequel" CASCADE'
|
|
1081
|
+
@d.create_schema(:sequel)
|
|
1082
|
+
@d.create_table(:sequel__test){Integer :a}
|
|
1083
|
+
@d.drop_schema(:sequel, :if_exists=>true, :cascade=>true)
|
|
952
1084
|
end
|
|
953
|
-
|
|
1085
|
+
|
|
954
1086
|
specify "#create_trigger and #drop_trigger should create and drop triggers" do
|
|
955
|
-
@d.create_language(:plpgsql)
|
|
1087
|
+
@d.create_language(:plpgsql) if @d.server_version < 90000
|
|
956
1088
|
@d.create_function(:tf, 'BEGIN IF NEW.value IS NULL THEN RAISE EXCEPTION \'Blah\'; END IF; RETURN NEW; END;', :language=>:plpgsql, :returns=>:trigger)
|
|
957
|
-
@d.send(:create_trigger_sql, :test, :identity, :tf, :each_row=>true).should == 'CREATE TRIGGER identity BEFORE INSERT OR UPDATE OR DELETE ON test FOR EACH ROW EXECUTE PROCEDURE tf()'
|
|
1089
|
+
@d.send(:create_trigger_sql, :test, :identity, :tf, :each_row=>true).should == 'CREATE TRIGGER identity BEFORE INSERT OR UPDATE OR DELETE ON "test" FOR EACH ROW EXECUTE PROCEDURE tf()'
|
|
958
1090
|
@d.create_table(:test){String :name; Integer :value}
|
|
959
1091
|
@d.create_trigger(:test, :identity, :tf, :each_row=>true)
|
|
960
1092
|
@d[:test].insert(:name=>'a', :value=>1)
|
|
@@ -963,32 +1095,32 @@ describe "Postgres::Database functions, languages, and triggers" do
|
|
|
963
1095
|
@d[:test].filter(:name=>'a').all.should == [{:name=>'a', :value=>1}]
|
|
964
1096
|
@d[:test].filter(:name=>'a').update(:value=>3)
|
|
965
1097
|
@d[:test].filter(:name=>'a').all.should == [{:name=>'a', :value=>3}]
|
|
966
|
-
@d.send(:drop_trigger_sql, :test, :identity).should == 'DROP TRIGGER identity ON test'
|
|
1098
|
+
@d.send(:drop_trigger_sql, :test, :identity).should == 'DROP TRIGGER identity ON "test"'
|
|
967
1099
|
@d.drop_trigger(:test, :identity)
|
|
968
|
-
@d.send(:create_trigger_sql, :test, :identity, :tf, :after=>true, :events=>:insert, :args=>[1, 'a']).should == 'CREATE TRIGGER identity AFTER INSERT ON test EXECUTE PROCEDURE tf(1, \'a\')'
|
|
969
|
-
@d.send(:drop_trigger_sql, :test, :identity, :if_exists=>true, :cascade=>true).should == 'DROP TRIGGER IF EXISTS identity ON test CASCADE'
|
|
1100
|
+
@d.send(:create_trigger_sql, :test, :identity, :tf, :after=>true, :events=>:insert, :args=>[1, 'a']).should == 'CREATE TRIGGER identity AFTER INSERT ON "test" EXECUTE PROCEDURE tf(1, \'a\')'
|
|
1101
|
+
@d.send(:drop_trigger_sql, :test, :identity, :if_exists=>true, :cascade=>true).should == 'DROP TRIGGER IF EXISTS identity ON "test" CASCADE'
|
|
970
1102
|
# Make sure if exists works
|
|
971
1103
|
@d.drop_trigger(:test, :identity, :if_exists=>true, :cascade=>true)
|
|
972
1104
|
end
|
|
973
1105
|
end
|
|
974
1106
|
|
|
975
1107
|
if POSTGRES_DB.adapter_scheme == :postgres
|
|
976
|
-
describe "Postgres::Dataset #use_cursor" do
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1108
|
+
describe "Postgres::Dataset #use_cursor" do
|
|
1109
|
+
before(:all) do
|
|
1110
|
+
@db = POSTGRES_DB
|
|
1111
|
+
@db.create_table!(:test_cursor){Integer :x}
|
|
1112
|
+
@db.sqls.clear
|
|
1113
|
+
@ds = @db[:test_cursor]
|
|
1114
|
+
@db.transaction{1001.times{|i| @ds.insert(i)}}
|
|
1115
|
+
end
|
|
1116
|
+
after(:all) do
|
|
1117
|
+
@db.drop_table?(:test_cursor)
|
|
1118
|
+
end
|
|
1119
|
+
|
|
988
1120
|
specify "should return the same results as the non-cursor use" do
|
|
989
1121
|
@ds.all.should == @ds.use_cursor.all
|
|
990
1122
|
end
|
|
991
|
-
|
|
1123
|
+
|
|
992
1124
|
specify "should respect the :rows_per_fetch option" do
|
|
993
1125
|
@db.sqls.clear
|
|
994
1126
|
@ds.use_cursor.all
|
|
@@ -997,7 +1129,7 @@ describe "Postgres::Dataset #use_cursor" do
|
|
|
997
1129
|
@ds.use_cursor(:rows_per_fetch=>100).all
|
|
998
1130
|
@db.sqls.length.should == 15
|
|
999
1131
|
end
|
|
1000
|
-
|
|
1132
|
+
|
|
1001
1133
|
specify "should handle returning inside block" do
|
|
1002
1134
|
def @ds.check_return
|
|
1003
1135
|
use_cursor.each{|r| return}
|
|
@@ -1005,5 +1137,1530 @@ describe "Postgres::Dataset #use_cursor" do
|
|
|
1005
1137
|
@ds.check_return
|
|
1006
1138
|
@ds.all.should == @ds.use_cursor.all
|
|
1007
1139
|
end
|
|
1140
|
+
end
|
|
1141
|
+
|
|
1142
|
+
describe "Postgres::PG_NAMED_TYPES" do
|
|
1143
|
+
before do
|
|
1144
|
+
@db = POSTGRES_DB
|
|
1145
|
+
Sequel::Postgres::PG_NAMED_TYPES[:interval] = lambda{|v| v.reverse}
|
|
1146
|
+
@db.reset_conversion_procs
|
|
1147
|
+
end
|
|
1148
|
+
after do
|
|
1149
|
+
Sequel::Postgres::PG_NAMED_TYPES.delete(:interval)
|
|
1150
|
+
@db.reset_conversion_procs
|
|
1151
|
+
@db.drop_table?(:foo)
|
|
1152
|
+
end
|
|
1153
|
+
|
|
1154
|
+
specify "should look up conversion procs by name" do
|
|
1155
|
+
@db.create_table!(:foo){interval :bar}
|
|
1156
|
+
@db[:foo].insert(Sequel.cast('21 days', :interval))
|
|
1157
|
+
@db[:foo].get(:bar).should == 'syad 12'
|
|
1158
|
+
end
|
|
1159
|
+
end
|
|
1008
1160
|
end
|
|
1161
|
+
|
|
1162
|
+
if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && POSTGRES_DB.server_version >= 90000
|
|
1163
|
+
describe "Postgres::Database#copy_table" do
|
|
1164
|
+
before(:all) do
|
|
1165
|
+
@db = POSTGRES_DB
|
|
1166
|
+
@db.create_table!(:test_copy){Integer :x; Integer :y}
|
|
1167
|
+
ds = @db[:test_copy]
|
|
1168
|
+
ds.insert(1, 2)
|
|
1169
|
+
ds.insert(3, 4)
|
|
1170
|
+
end
|
|
1171
|
+
after(:all) do
|
|
1172
|
+
@db.drop_table?(:test_copy)
|
|
1173
|
+
end
|
|
1174
|
+
|
|
1175
|
+
specify "without a block or options should return a text version of the table as a single string" do
|
|
1176
|
+
@db.copy_table(:test_copy).should == "1\t2\n3\t4\n"
|
|
1177
|
+
end
|
|
1178
|
+
|
|
1179
|
+
specify "without a block and with :format=>:csv should return a csv version of the table as a single string" do
|
|
1180
|
+
@db.copy_table(:test_copy, :format=>:csv).should == "1,2\n3,4\n"
|
|
1181
|
+
end
|
|
1182
|
+
|
|
1183
|
+
specify "should treat string as SQL code" do
|
|
1184
|
+
@db.copy_table('COPY "test_copy" TO STDOUT').should == "1\t2\n3\t4\n"
|
|
1185
|
+
end
|
|
1186
|
+
|
|
1187
|
+
specify "should respect given :options options" do
|
|
1188
|
+
@db.copy_table(:test_copy, :options=>"FORMAT csv, HEADER TRUE").should == "x,y\n1,2\n3,4\n"
|
|
1189
|
+
end
|
|
1190
|
+
|
|
1191
|
+
specify "should respect given :options options when :format is used" do
|
|
1192
|
+
@db.copy_table(:test_copy, :format=>:csv, :options=>"QUOTE '''', FORCE_QUOTE *").should == "'1','2'\n'3','4'\n"
|
|
1193
|
+
end
|
|
1194
|
+
|
|
1195
|
+
specify "should accept dataset as first argument" do
|
|
1196
|
+
@db.copy_table(@db[:test_copy].cross_join(:test_copy___tc).order(:test_copy__x, :test_copy__y, :tc__x, :tc__y)).should == "1\t2\t1\t2\n1\t2\t3\t4\n3\t4\t1\t2\n3\t4\t3\t4\n"
|
|
1197
|
+
end
|
|
1198
|
+
|
|
1199
|
+
specify "with a block and no options should yield each row as a string in text format" do
|
|
1200
|
+
buf = []
|
|
1201
|
+
@db.copy_table(:test_copy){|b| buf << b}
|
|
1202
|
+
buf.should == ["1\t2\n", "3\t4\n"]
|
|
1203
|
+
end
|
|
1204
|
+
|
|
1205
|
+
specify "with a block and :format=>:csv should yield each row as a string in csv format" do
|
|
1206
|
+
buf = []
|
|
1207
|
+
@db.copy_table(:test_copy, :format=>:csv){|b| buf << b}
|
|
1208
|
+
buf.should == ["1,2\n", "3,4\n"]
|
|
1209
|
+
end
|
|
1210
|
+
|
|
1211
|
+
specify "should work fine when using a block that is terminated early with a following copy_table" do
|
|
1212
|
+
buf = []
|
|
1213
|
+
proc{@db.copy_table(:test_copy, :format=>:csv){|b| buf << b; break}}.should raise_error(Sequel::DatabaseDisconnectError)
|
|
1214
|
+
buf.should == ["1,2\n"]
|
|
1215
|
+
buf.clear
|
|
1216
|
+
proc{@db.copy_table(:test_copy, :format=>:csv){|b| buf << b; raise ArgumentError}}.should raise_error(Sequel::DatabaseDisconnectError)
|
|
1217
|
+
buf.should == ["1,2\n"]
|
|
1218
|
+
buf.clear
|
|
1219
|
+
@db.copy_table(:test_copy){|b| buf << b}
|
|
1220
|
+
buf.should == ["1\t2\n", "3\t4\n"]
|
|
1221
|
+
end
|
|
1222
|
+
|
|
1223
|
+
specify "should work fine when using a block that is terminated early with a following regular query" do
|
|
1224
|
+
buf = []
|
|
1225
|
+
proc{@db.copy_table(:test_copy, :format=>:csv){|b| buf << b; break}}.should raise_error(Sequel::DatabaseDisconnectError)
|
|
1226
|
+
buf.should == ["1,2\n"]
|
|
1227
|
+
buf.clear
|
|
1228
|
+
proc{@db.copy_table(:test_copy, :format=>:csv){|b| buf << b; raise ArgumentError}}.should raise_error(Sequel::DatabaseDisconnectError)
|
|
1229
|
+
buf.should == ["1,2\n"]
|
|
1230
|
+
@db[:test_copy].select_order_map(:x).should == [1, 3]
|
|
1231
|
+
end
|
|
1232
|
+
end
|
|
1233
|
+
|
|
1234
|
+
describe "Postgres::Database#copy_table_from" do
|
|
1235
|
+
before(:all) do
|
|
1236
|
+
@db = POSTGRES_DB
|
|
1237
|
+
@db.create_table!(:test_copy){Integer :x; Integer :y}
|
|
1238
|
+
@ds = @db[:test_copy].order(:x, :y)
|
|
1239
|
+
end
|
|
1240
|
+
before do
|
|
1241
|
+
@db[:test_copy].delete
|
|
1242
|
+
end
|
|
1243
|
+
after(:all) do
|
|
1244
|
+
@db.drop_table?(:test_copy)
|
|
1245
|
+
end
|
|
1246
|
+
|
|
1247
|
+
specify "should work with a :data option containing data in PostgreSQL text format" do
|
|
1248
|
+
@db.copy_into(:test_copy, :data=>"1\t2\n3\t4\n")
|
|
1249
|
+
@ds.select_map([:x, :y]).should == [[1, 2], [3, 4]]
|
|
1250
|
+
end
|
|
1251
|
+
|
|
1252
|
+
specify "should work with :format=>:csv option and :data option containing data in CSV format" do
|
|
1253
|
+
@db.copy_into(:test_copy, :format=>:csv, :data=>"1,2\n3,4\n")
|
|
1254
|
+
@ds.select_map([:x, :y]).should == [[1, 2], [3, 4]]
|
|
1255
|
+
end
|
|
1256
|
+
|
|
1257
|
+
specify "should respect given :options" do
|
|
1258
|
+
@db.copy_into(:test_copy, :options=>"FORMAT csv, HEADER TRUE", :data=>"x,y\n1,2\n3,4\n")
|
|
1259
|
+
@ds.select_map([:x, :y]).should == [[1, 2], [3, 4]]
|
|
1260
|
+
end
|
|
1261
|
+
|
|
1262
|
+
specify "should respect given :options options when :format is used" do
|
|
1263
|
+
@db.copy_into(:test_copy, :options=>"QUOTE '''', DELIMITER '|'", :format=>:csv, :data=>"'1'|'2'\n'3'|'4'\n")
|
|
1264
|
+
@ds.select_map([:x, :y]).should == [[1, 2], [3, 4]]
|
|
1265
|
+
end
|
|
1266
|
+
|
|
1267
|
+
specify "should accept :columns option to online copy the given columns" do
|
|
1268
|
+
@db.copy_into(:test_copy, :data=>"1\t2\n3\t4\n", :columns=>[:y, :x])
|
|
1269
|
+
@ds.select_map([:x, :y]).should == [[2, 1], [4, 3]]
|
|
1270
|
+
end
|
|
1271
|
+
|
|
1272
|
+
specify "should accept a block and use returned values for the copy in data stream" do
|
|
1273
|
+
buf = ["1\t2\n", "3\t4\n"]
|
|
1274
|
+
@db.copy_into(:test_copy){buf.shift}
|
|
1275
|
+
@ds.select_map([:x, :y]).should == [[1, 2], [3, 4]]
|
|
1276
|
+
end
|
|
1277
|
+
|
|
1278
|
+
specify "should work correctly with a block and :format=>:csv" do
|
|
1279
|
+
buf = ["1,2\n", "3,4\n"]
|
|
1280
|
+
@db.copy_into(:test_copy, :format=>:csv){buf.shift}
|
|
1281
|
+
@ds.select_map([:x, :y]).should == [[1, 2], [3, 4]]
|
|
1282
|
+
end
|
|
1283
|
+
|
|
1284
|
+
specify "should accept an enumerable as the :data option" do
|
|
1285
|
+
@db.copy_into(:test_copy, :data=>["1\t2\n", "3\t4\n"])
|
|
1286
|
+
@ds.select_map([:x, :y]).should == [[1, 2], [3, 4]]
|
|
1287
|
+
end
|
|
1288
|
+
|
|
1289
|
+
specify "should have an exception should cause a rollback of copied data and still have a usable connection" do
|
|
1290
|
+
2.times do
|
|
1291
|
+
sent = false
|
|
1292
|
+
proc{@db.copy_into(:test_copy){raise ArgumentError if sent; sent = true; "1\t2\n"}}.should raise_error(ArgumentError)
|
|
1293
|
+
@ds.select_map([:x, :y]).should == []
|
|
1294
|
+
end
|
|
1295
|
+
end
|
|
1296
|
+
|
|
1297
|
+
specify "should raise an Error if both :data and a block are provided" do
|
|
1298
|
+
proc{@db.copy_into(:test_copy, :data=>["1\t2\n", "3\t4\n"]){}}.should raise_error(Sequel::Error)
|
|
1299
|
+
end
|
|
1300
|
+
|
|
1301
|
+
specify "should raise an Error if neither :data or a block are provided" do
|
|
1302
|
+
proc{@db.copy_into(:test_copy)}.should raise_error(Sequel::Error)
|
|
1303
|
+
end
|
|
1304
|
+
end
|
|
1305
|
+
|
|
1306
|
+
describe "Postgres::Database LISTEN/NOTIFY" do
|
|
1307
|
+
before(:all) do
|
|
1308
|
+
@db = POSTGRES_DB
|
|
1309
|
+
end
|
|
1310
|
+
|
|
1311
|
+
specify "should support listen and notify" do
|
|
1312
|
+
notify_pid = @db.synchronize{|conn| conn.backend_pid}
|
|
1313
|
+
|
|
1314
|
+
called = false
|
|
1315
|
+
@db.listen('foo', :after_listen=>proc{@db.notify('foo')}) do |ev, pid, payload|
|
|
1316
|
+
ev.should == 'foo'
|
|
1317
|
+
pid.should == notify_pid
|
|
1318
|
+
['', nil].should include(payload)
|
|
1319
|
+
called = true
|
|
1320
|
+
end.should == 'foo'
|
|
1321
|
+
called.should be_true
|
|
1322
|
+
|
|
1323
|
+
called = false
|
|
1324
|
+
@db.listen('foo', :after_listen=>proc{@db.notify('foo', :payload=>'bar')}) do |ev, pid, payload|
|
|
1325
|
+
ev.should == 'foo'
|
|
1326
|
+
pid.should == notify_pid
|
|
1327
|
+
payload.should == 'bar'
|
|
1328
|
+
called = true
|
|
1329
|
+
end.should == 'foo'
|
|
1330
|
+
called.should be_true
|
|
1331
|
+
|
|
1332
|
+
@db.listen('foo', :after_listen=>proc{@db.notify('foo')}).should == 'foo'
|
|
1333
|
+
|
|
1334
|
+
called = false
|
|
1335
|
+
called2 = false
|
|
1336
|
+
i = 0
|
|
1337
|
+
@db.listen(['foo', 'bar'], :after_listen=>proc{@db.notify('foo', :payload=>'bar'); @db.notify('bar', :payload=>'foo')}, :loop=>proc{i+=1}) do |ev, pid, payload|
|
|
1338
|
+
if !called
|
|
1339
|
+
ev.should == 'foo'
|
|
1340
|
+
pid.should == notify_pid
|
|
1341
|
+
payload.should == 'bar'
|
|
1342
|
+
called = true
|
|
1343
|
+
else
|
|
1344
|
+
ev.should == 'bar'
|
|
1345
|
+
pid.should == notify_pid
|
|
1346
|
+
payload.should == 'foo'
|
|
1347
|
+
called2 = true
|
|
1348
|
+
break
|
|
1349
|
+
end
|
|
1350
|
+
end.should be_nil
|
|
1351
|
+
called.should be_true
|
|
1352
|
+
called2.should be_true
|
|
1353
|
+
i.should == 1
|
|
1354
|
+
end
|
|
1355
|
+
|
|
1356
|
+
specify "should accept a :timeout option in listen" do
|
|
1357
|
+
@db.listen('foo2', :timeout=>0.001).should == nil
|
|
1358
|
+
called = false
|
|
1359
|
+
@db.listen('foo2', :timeout=>0.001){|ev, pid, payload| called = true}.should == nil
|
|
1360
|
+
called.should be_false
|
|
1361
|
+
i = 0
|
|
1362
|
+
@db.listen('foo2', :timeout=>0.001, :loop=>proc{i+=1; throw :stop if i > 3}){|ev, pid, payload| called = true}.should == nil
|
|
1363
|
+
i.should == 4
|
|
1364
|
+
end unless RUBY_PLATFORM =~ /mingw/ # Ruby freezes on this spec on this platform/version
|
|
1365
|
+
end
|
|
1366
|
+
end
|
|
1367
|
+
|
|
1368
|
+
describe 'PostgreSQL special float handling' do
|
|
1369
|
+
before do
|
|
1370
|
+
@db = POSTGRES_DB
|
|
1371
|
+
@db.create_table!(:test5){Float :value}
|
|
1372
|
+
@db.sqls.clear
|
|
1373
|
+
@ds = @db[:test5]
|
|
1374
|
+
end
|
|
1375
|
+
after do
|
|
1376
|
+
@db.drop_table?(:test5)
|
|
1377
|
+
end
|
|
1378
|
+
|
|
1379
|
+
if check_sqls
|
|
1380
|
+
specify 'should quote NaN' do
|
|
1381
|
+
nan = 0.0/0.0
|
|
1382
|
+
@ds.insert_sql(:value => nan).should == %q{INSERT INTO "test5" ("value") VALUES ('NaN')}
|
|
1383
|
+
end
|
|
1384
|
+
|
|
1385
|
+
specify 'should quote +Infinity' do
|
|
1386
|
+
inf = 1.0/0.0
|
|
1387
|
+
@ds.insert_sql(:value => inf).should == %q{INSERT INTO "test5" ("value") VALUES ('Infinity')}
|
|
1388
|
+
end
|
|
1389
|
+
|
|
1390
|
+
specify 'should quote -Infinity' do
|
|
1391
|
+
inf = -1.0/0.0
|
|
1392
|
+
@ds.insert_sql(:value => inf).should == %q{INSERT INTO "test5" ("value") VALUES ('-Infinity')}
|
|
1393
|
+
end
|
|
1394
|
+
end
|
|
1395
|
+
|
|
1396
|
+
if POSTGRES_DB.adapter_scheme == :postgres
|
|
1397
|
+
specify 'inserts NaN' do
|
|
1398
|
+
nan = 0.0/0.0
|
|
1399
|
+
@ds.insert(:value=>nan)
|
|
1400
|
+
@ds.all[0][:value].nan?.should be_true
|
|
1401
|
+
end
|
|
1402
|
+
|
|
1403
|
+
specify 'inserts +Infinity' do
|
|
1404
|
+
inf = 1.0/0.0
|
|
1405
|
+
@ds.insert(:value=>inf)
|
|
1406
|
+
@ds.all[0][:value].infinite?.should > 0
|
|
1407
|
+
end
|
|
1408
|
+
|
|
1409
|
+
specify 'inserts -Infinity' do
|
|
1410
|
+
inf = -1.0/0.0
|
|
1411
|
+
@ds.insert(:value=>inf)
|
|
1412
|
+
@ds.all[0][:value].infinite?.should < 0
|
|
1413
|
+
end
|
|
1414
|
+
end
|
|
1415
|
+
end
|
|
1416
|
+
|
|
1417
|
+
describe 'PostgreSQL array handling' do
|
|
1418
|
+
before(:all) do
|
|
1419
|
+
@db = POSTGRES_DB
|
|
1420
|
+
@db.extension :pg_array
|
|
1421
|
+
@ds = @db[:items]
|
|
1422
|
+
@native = POSTGRES_DB.adapter_scheme == :postgres
|
|
1423
|
+
@jdbc = POSTGRES_DB.adapter_scheme == :jdbc
|
|
1424
|
+
@tp = lambda{@db.schema(:items).map{|a| a.last[:type]}}
|
|
1425
|
+
end
|
|
1426
|
+
after do
|
|
1427
|
+
@db.drop_table?(:items)
|
|
1428
|
+
end
|
|
1429
|
+
|
|
1430
|
+
specify 'insert and retrieve integer and float arrays of various sizes' do
|
|
1431
|
+
@db.create_table!(:items) do
|
|
1432
|
+
column :i2, 'int2[]'
|
|
1433
|
+
column :i4, 'int4[]'
|
|
1434
|
+
column :i8, 'int8[]'
|
|
1435
|
+
column :r, 'real[]'
|
|
1436
|
+
column :dp, 'double precision[]'
|
|
1437
|
+
end
|
|
1438
|
+
@tp.call.should == [:integer_array, :integer_array, :bigint_array, :float_array, :float_array]
|
|
1439
|
+
@ds.insert(Sequel.pg_array([1], :int2), Sequel.pg_array([nil, 2], :int4), Sequel.pg_array([3, nil], :int8), Sequel.pg_array([4, nil, 4.5], :real), Sequel.pg_array([5, nil, 5.5], "double precision"))
|
|
1440
|
+
@ds.count.should == 1
|
|
1441
|
+
rs = @ds.all
|
|
1442
|
+
if @jdbc || @native
|
|
1443
|
+
rs.should == [{:i2=>[1], :i4=>[nil, 2], :i8=>[3, nil], :r=>[4.0, nil, 4.5], :dp=>[5.0, nil, 5.5]}]
|
|
1444
|
+
end
|
|
1445
|
+
if @native
|
|
1446
|
+
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
|
|
1447
|
+
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
|
|
1448
|
+
@ds.delete
|
|
1449
|
+
@ds.insert(rs.first)
|
|
1450
|
+
@ds.all.should == rs
|
|
1451
|
+
end
|
|
1452
|
+
|
|
1453
|
+
@ds.delete
|
|
1454
|
+
@ds.insert(Sequel.pg_array([[1], [2]], :int2), Sequel.pg_array([[nil, 2], [3, 4]], :int4), Sequel.pg_array([[3, nil], [nil, nil]], :int8), Sequel.pg_array([[4, nil], [nil, 4.5]], :real), Sequel.pg_array([[5, nil], [nil, 5.5]], "double precision"))
|
|
1455
|
+
|
|
1456
|
+
rs = @ds.all
|
|
1457
|
+
if @jdbc || @native
|
|
1458
|
+
rs.should == [{:i2=>[[1], [2]], :i4=>[[nil, 2], [3, 4]], :i8=>[[3, nil], [nil, nil]], :r=>[[4, nil], [nil, 4.5]], :dp=>[[5, nil], [nil, 5.5]]}]
|
|
1459
|
+
end
|
|
1460
|
+
if @native
|
|
1461
|
+
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
|
|
1462
|
+
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
|
|
1463
|
+
@ds.delete
|
|
1464
|
+
@ds.insert(rs.first)
|
|
1465
|
+
@ds.all.should == rs
|
|
1466
|
+
end
|
|
1467
|
+
end
|
|
1468
|
+
|
|
1469
|
+
specify 'insert and retrieve decimal arrays' do
|
|
1470
|
+
@db.create_table!(:items) do
|
|
1471
|
+
column :n, 'numeric[]'
|
|
1472
|
+
end
|
|
1473
|
+
@tp.call.should == [:decimal_array]
|
|
1474
|
+
@ds.insert(Sequel.pg_array([BigDecimal.new('1.000000000000000000001'), nil, BigDecimal.new('1')], :numeric))
|
|
1475
|
+
@ds.count.should == 1
|
|
1476
|
+
rs = @ds.all
|
|
1477
|
+
if @jdbc || @native
|
|
1478
|
+
rs.should == [{:n=>[BigDecimal.new('1.000000000000000000001'), nil, BigDecimal.new('1')]}]
|
|
1479
|
+
end
|
|
1480
|
+
if @native
|
|
1481
|
+
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
|
|
1482
|
+
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
|
|
1483
|
+
@ds.delete
|
|
1484
|
+
@ds.insert(rs.first)
|
|
1485
|
+
@ds.all.should == rs
|
|
1486
|
+
end
|
|
1487
|
+
|
|
1488
|
+
@ds.delete
|
|
1489
|
+
@ds.insert(Sequel.pg_array([[BigDecimal.new('1.0000000000000000000000000000001'), nil], [nil, BigDecimal.new('1')]], :numeric))
|
|
1490
|
+
rs = @ds.all
|
|
1491
|
+
if @jdbc || @native
|
|
1492
|
+
rs.should == [{:n=>[[BigDecimal.new('1.0000000000000000000000000000001'), nil], [nil, BigDecimal.new('1')]]}]
|
|
1493
|
+
end
|
|
1494
|
+
if @native
|
|
1495
|
+
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
|
|
1496
|
+
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
|
|
1497
|
+
@ds.delete
|
|
1498
|
+
@ds.insert(rs.first)
|
|
1499
|
+
@ds.all.should == rs
|
|
1500
|
+
end
|
|
1501
|
+
end
|
|
1502
|
+
|
|
1503
|
+
specify 'insert and retrieve string arrays' do
|
|
1504
|
+
@db.create_table!(:items) do
|
|
1505
|
+
column :c, 'char(4)[]'
|
|
1506
|
+
column :vc, 'varchar[]'
|
|
1507
|
+
column :t, 'text[]'
|
|
1508
|
+
end
|
|
1509
|
+
@tp.call.should == [:string_array, :string_array, :string_array]
|
|
1510
|
+
@ds.insert(Sequel.pg_array(['a', nil, 'NULL', 'b"\'c'], 'char(4)'), Sequel.pg_array(['a', nil, 'NULL', 'b"\'c'], :varchar), Sequel.pg_array(['a', nil, 'NULL', 'b"\'c'], :text))
|
|
1511
|
+
@ds.count.should == 1
|
|
1512
|
+
rs = @ds.all
|
|
1513
|
+
if @jdbc || @native
|
|
1514
|
+
rs.should == [{:c=>['a ', nil, 'NULL', 'b"\'c'], :vc=>['a', nil, 'NULL', 'b"\'c'], :t=>['a', nil, 'NULL', 'b"\'c']}]
|
|
1515
|
+
end
|
|
1516
|
+
if @native
|
|
1517
|
+
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
|
|
1518
|
+
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
|
|
1519
|
+
@ds.delete
|
|
1520
|
+
@ds.insert(rs.first)
|
|
1521
|
+
@ds.all.should == rs
|
|
1522
|
+
end
|
|
1523
|
+
|
|
1524
|
+
@ds.delete
|
|
1525
|
+
@ds.insert(Sequel.pg_array([[['a'], [nil]], [['NULL'], ['b"\'c']]], 'char(4)'), Sequel.pg_array([[['a'], ['']], [['NULL'], ['b"\'c']]], :varchar), Sequel.pg_array([[['a'], [nil]], [['NULL'], ['b"\'c']]], :text))
|
|
1526
|
+
rs = @ds.all
|
|
1527
|
+
if @jdbc || @native
|
|
1528
|
+
rs.should == [{:c=>[[['a '], [nil]], [['NULL'], ['b"\'c']]], :vc=>[[['a'], ['']], [['NULL'], ['b"\'c']]], :t=>[[['a'], [nil]], [['NULL'], ['b"\'c']]]}]
|
|
1529
|
+
end
|
|
1530
|
+
if @native
|
|
1531
|
+
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
|
|
1532
|
+
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
|
|
1533
|
+
@ds.delete
|
|
1534
|
+
@ds.insert(rs.first)
|
|
1535
|
+
@ds.all.should == rs
|
|
1536
|
+
end
|
|
1537
|
+
end
|
|
1538
|
+
|
|
1539
|
+
specify 'insert and retrieve arrays of other types' do
|
|
1540
|
+
@db.create_table!(:items) do
|
|
1541
|
+
column :b, 'bool[]'
|
|
1542
|
+
column :d, 'date[]'
|
|
1543
|
+
column :t, 'time[]'
|
|
1544
|
+
column :ts, 'timestamp[]'
|
|
1545
|
+
column :tstz, 'timestamptz[]'
|
|
1546
|
+
end
|
|
1547
|
+
@tp.call.should == [:boolean_array, :date_array, :time_array, :datetime_array, :datetime_timezone_array]
|
|
1548
|
+
|
|
1549
|
+
d = Date.today
|
|
1550
|
+
t = Sequel::SQLTime.create(10, 20, 30)
|
|
1551
|
+
ts = Time.local(2011, 1, 2, 3, 4, 5)
|
|
1552
|
+
|
|
1553
|
+
@ds.insert(Sequel.pg_array([true, false], :bool), Sequel.pg_array([d, nil], :date), Sequel.pg_array([t, nil], :time), Sequel.pg_array([ts, nil], :timestamp), Sequel.pg_array([ts, nil], :timestamptz))
|
|
1554
|
+
@ds.count.should == 1
|
|
1555
|
+
rs = @ds.all
|
|
1556
|
+
if @jdbc || @native
|
|
1557
|
+
rs.should == [{:b=>[true, false], :d=>[d, nil], :t=>[t, nil], :ts=>[ts, nil], :tstz=>[ts, nil]}]
|
|
1558
|
+
end
|
|
1559
|
+
if @native
|
|
1560
|
+
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
|
|
1561
|
+
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
|
|
1562
|
+
@ds.delete
|
|
1563
|
+
@ds.insert(rs.first)
|
|
1564
|
+
@ds.all.should == rs
|
|
1565
|
+
end
|
|
1566
|
+
|
|
1567
|
+
@db.create_table!(:items) do
|
|
1568
|
+
column :ba, 'bytea[]'
|
|
1569
|
+
column :tz, 'timetz[]'
|
|
1570
|
+
column :o, 'oid[]'
|
|
1571
|
+
end
|
|
1572
|
+
@tp.call.should == [:blob_array, :time_timezone_array, :integer_array]
|
|
1573
|
+
@ds.insert(Sequel.pg_array([Sequel.blob("a\0"), nil], :bytea), Sequel.pg_array([t, nil], :timetz), Sequel.pg_array([1, 2, 3], :oid))
|
|
1574
|
+
@ds.count.should == 1
|
|
1575
|
+
if @native
|
|
1576
|
+
rs = @ds.all
|
|
1577
|
+
rs.should == [{:ba=>[Sequel.blob("a\0"), nil], :tz=>[t, nil], :o=>[1, 2, 3]}]
|
|
1578
|
+
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
|
|
1579
|
+
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
|
|
1580
|
+
@ds.delete
|
|
1581
|
+
@ds.insert(rs.first)
|
|
1582
|
+
@ds.all.should == rs
|
|
1583
|
+
end
|
|
1584
|
+
end
|
|
1585
|
+
|
|
1586
|
+
specify 'use arrays in bound variables' do
|
|
1587
|
+
@db.create_table!(:items) do
|
|
1588
|
+
column :i, 'int4[]'
|
|
1589
|
+
end
|
|
1590
|
+
@ds.call(:insert, {:i=>[1,2]}, {:i=>:$i})
|
|
1591
|
+
@ds.get(:i).should == [1, 2]
|
|
1592
|
+
@ds.filter(:i=>:$i).call(:first, :i=>[1,2]).should == {:i=>[1,2]}
|
|
1593
|
+
@ds.filter(:i=>:$i).call(:first, :i=>[1,3]).should == nil
|
|
1594
|
+
|
|
1595
|
+
@db.create_table!(:items) do
|
|
1596
|
+
column :i, 'text[]'
|
|
1597
|
+
end
|
|
1598
|
+
a = ["\"\\\\\"{}\n\t\r \v\b123afP", 'NULL', nil, '']
|
|
1599
|
+
@ds.call(:insert, {:i=>:$i}, :i=>Sequel.pg_array(a))
|
|
1600
|
+
@ds.get(:i).should == a
|
|
1601
|
+
@ds.filter(:i=>:$i).call(:first, :i=>a).should == {:i=>a}
|
|
1602
|
+
@ds.filter(:i=>:$i).call(:first, :i=>['', nil, nil, 'a']).should == nil
|
|
1603
|
+
|
|
1604
|
+
@db.create_table!(:items) do
|
|
1605
|
+
column :i, 'date[]'
|
|
1606
|
+
end
|
|
1607
|
+
a = [Date.today]
|
|
1608
|
+
@ds.call(:insert, {:i=>:$i}, :i=>Sequel.pg_array(a, 'date'))
|
|
1609
|
+
@ds.get(:i).should == a
|
|
1610
|
+
@ds.filter(:i=>:$i).call(:first, :i=>a).should == {:i=>a}
|
|
1611
|
+
@ds.filter(:i=>:$i).call(:first, :i=>Sequel.pg_array([Date.today-1], 'date')).should == nil
|
|
1612
|
+
|
|
1613
|
+
@db.create_table!(:items) do
|
|
1614
|
+
column :i, 'timestamp[]'
|
|
1615
|
+
end
|
|
1616
|
+
a = [Time.local(2011, 1, 2, 3, 4, 5)]
|
|
1617
|
+
@ds.call(:insert, {:i=>:$i}, :i=>Sequel.pg_array(a, 'timestamp'))
|
|
1618
|
+
@ds.get(:i).should == a
|
|
1619
|
+
@ds.filter(:i=>:$i).call(:first, :i=>a).should == {:i=>a}
|
|
1620
|
+
@ds.filter(:i=>:$i).call(:first, :i=>Sequel.pg_array([a.first-1], 'timestamp')).should == nil
|
|
1621
|
+
|
|
1622
|
+
@db.create_table!(:items) do
|
|
1623
|
+
column :i, 'boolean[]'
|
|
1624
|
+
end
|
|
1625
|
+
a = [true, false]
|
|
1626
|
+
@ds.call(:insert, {:i=>:$i}, :i=>Sequel.pg_array(a, 'boolean'))
|
|
1627
|
+
@ds.get(:i).should == a
|
|
1628
|
+
@ds.filter(:i=>:$i).call(:first, :i=>a).should == {:i=>a}
|
|
1629
|
+
@ds.filter(:i=>:$i).call(:first, :i=>Sequel.pg_array([false, true], 'boolean')).should == nil
|
|
1630
|
+
|
|
1631
|
+
@db.create_table!(:items) do
|
|
1632
|
+
column :i, 'bytea[]'
|
|
1633
|
+
end
|
|
1634
|
+
a = [Sequel.blob("a\0'\"")]
|
|
1635
|
+
@ds.call(:insert, {:i=>:$i}, :i=>Sequel.pg_array(a, 'bytea'))
|
|
1636
|
+
@ds.get(:i).should == a
|
|
1637
|
+
@ds.filter(:i=>:$i).call(:first, :i=>a).should == {:i=>a}
|
|
1638
|
+
@ds.filter(:i=>:$i).call(:first, :i=>Sequel.pg_array([Sequel.blob("b\0")], 'bytea')).should == nil
|
|
1639
|
+
end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
|
|
1640
|
+
|
|
1641
|
+
specify 'with models' do
|
|
1642
|
+
@db.create_table!(:items) do
|
|
1643
|
+
primary_key :id
|
|
1644
|
+
column :i, 'integer[]'
|
|
1645
|
+
column :f, 'double precision[]'
|
|
1646
|
+
column :d, 'numeric[]'
|
|
1647
|
+
column :t, 'text[]'
|
|
1648
|
+
end
|
|
1649
|
+
c = Class.new(Sequel::Model(@db[:items]))
|
|
1650
|
+
c.plugin :pg_typecast_on_load, :i, :f, :d, :t unless @native
|
|
1651
|
+
o = c.create(:i=>[1,2, nil], :f=>[[1, 2.5], [3, 4.5]], :d=>[1, BigDecimal.new('1.000000000000000000001')], :t=>[%w'a b c', ['NULL', nil, '1']])
|
|
1652
|
+
o.i.should == [1, 2, nil]
|
|
1653
|
+
o.f.should == [[1, 2.5], [3, 4.5]]
|
|
1654
|
+
o.d.should == [BigDecimal.new('1'), BigDecimal.new('1.000000000000000000001')]
|
|
1655
|
+
o.t.should == [%w'a b c', ['NULL', nil, '1']]
|
|
1656
|
+
end
|
|
1657
|
+
|
|
1658
|
+
specify 'operations/functions with pg_array_ops' do
|
|
1659
|
+
Sequel.extension :pg_array_ops
|
|
1660
|
+
@db.create_table!(:items){column :i, 'integer[]'; column :i2, 'integer[]'; column :i3, 'integer[]'; column :i4, 'integer[]'; column :i5, 'integer[]'}
|
|
1661
|
+
@ds.insert(Sequel.pg_array([1, 2, 3]), Sequel.pg_array([2, 1]), Sequel.pg_array([4, 4]), Sequel.pg_array([[5, 5], [4, 3]]), Sequel.pg_array([1, nil, 5]))
|
|
1662
|
+
|
|
1663
|
+
@ds.get(Sequel.pg_array(:i) > :i3).should be_false
|
|
1664
|
+
@ds.get(Sequel.pg_array(:i3) > :i).should be_true
|
|
1665
|
+
|
|
1666
|
+
@ds.get(Sequel.pg_array(:i) >= :i3).should be_false
|
|
1667
|
+
@ds.get(Sequel.pg_array(:i) >= :i).should be_true
|
|
1668
|
+
|
|
1669
|
+
@ds.get(Sequel.pg_array(:i3) < :i).should be_false
|
|
1670
|
+
@ds.get(Sequel.pg_array(:i) < :i3).should be_true
|
|
1671
|
+
|
|
1672
|
+
@ds.get(Sequel.pg_array(:i3) <= :i).should be_false
|
|
1673
|
+
@ds.get(Sequel.pg_array(:i) <= :i).should be_true
|
|
1674
|
+
|
|
1675
|
+
@ds.get(Sequel.expr(5=>Sequel.pg_array(:i).any)).should be_false
|
|
1676
|
+
@ds.get(Sequel.expr(1=>Sequel.pg_array(:i).any)).should be_true
|
|
1677
|
+
|
|
1678
|
+
@ds.get(Sequel.expr(1=>Sequel.pg_array(:i3).all)).should be_false
|
|
1679
|
+
@ds.get(Sequel.expr(4=>Sequel.pg_array(:i3).all)).should be_true
|
|
1680
|
+
|
|
1681
|
+
@ds.get(Sequel.pg_array(:i2)[1]).should == 2
|
|
1682
|
+
@ds.get(Sequel.pg_array(:i2)[2]).should == 1
|
|
1683
|
+
|
|
1684
|
+
@ds.get(Sequel.pg_array(:i4)[2][1]).should == 4
|
|
1685
|
+
@ds.get(Sequel.pg_array(:i4)[2][2]).should == 3
|
|
1686
|
+
|
|
1687
|
+
@ds.get(Sequel.pg_array(:i).contains(:i2)).should be_true
|
|
1688
|
+
@ds.get(Sequel.pg_array(:i).contains(:i3)).should be_false
|
|
1689
|
+
|
|
1690
|
+
@ds.get(Sequel.pg_array(:i2).contained_by(:i)).should be_true
|
|
1691
|
+
@ds.get(Sequel.pg_array(:i).contained_by(:i2)).should be_false
|
|
1692
|
+
|
|
1693
|
+
@ds.get(Sequel.pg_array(:i).overlaps(:i2)).should be_true
|
|
1694
|
+
@ds.get(Sequel.pg_array(:i2).overlaps(:i3)).should be_false
|
|
1695
|
+
|
|
1696
|
+
@ds.get(Sequel.pg_array(:i).dims).should == '[1:3]'
|
|
1697
|
+
@ds.get(Sequel.pg_array(:i).length).should == 3
|
|
1698
|
+
@ds.get(Sequel.pg_array(:i).lower).should == 1
|
|
1699
|
+
|
|
1700
|
+
if @db.server_version >= 90000
|
|
1701
|
+
@ds.get(Sequel.pg_array(:i5).join).should == '15'
|
|
1702
|
+
@ds.get(Sequel.pg_array(:i5).join(':')).should == '1:5'
|
|
1703
|
+
@ds.get(Sequel.pg_array(:i5).join(':', '*')).should == '1:*:5'
|
|
1704
|
+
end
|
|
1705
|
+
@ds.select(Sequel.pg_array(:i).unnest).from_self.count.should == 3 if @db.server_version >= 80400
|
|
1706
|
+
|
|
1707
|
+
if @native
|
|
1708
|
+
@ds.get(Sequel.pg_array(:i).push(4)).should == [1, 2, 3, 4]
|
|
1709
|
+
@ds.get(Sequel.pg_array(:i).unshift(4)).should == [4, 1, 2, 3]
|
|
1710
|
+
@ds.get(Sequel.pg_array(:i).concat(:i2)).should == [1, 2, 3, 2, 1]
|
|
1711
|
+
end
|
|
1712
|
+
end
|
|
1713
|
+
end
|
|
1714
|
+
|
|
1715
|
+
describe 'PostgreSQL hstore handling' do
|
|
1716
|
+
before(:all) do
|
|
1717
|
+
@db = POSTGRES_DB
|
|
1718
|
+
@db.extension :pg_hstore
|
|
1719
|
+
@ds = @db[:items]
|
|
1720
|
+
@h = {'a'=>'b', 'c'=>nil, 'd'=>'NULL', 'e'=>'\\\\" \\\' ,=>'}
|
|
1721
|
+
@native = POSTGRES_DB.adapter_scheme == :postgres
|
|
1722
|
+
end
|
|
1723
|
+
after do
|
|
1724
|
+
@db.drop_table?(:items)
|
|
1725
|
+
end
|
|
1726
|
+
|
|
1727
|
+
specify 'insert and retrieve hstore values' do
|
|
1728
|
+
@db.create_table!(:items) do
|
|
1729
|
+
column :h, :hstore
|
|
1730
|
+
end
|
|
1731
|
+
@ds.insert(Sequel.hstore(@h))
|
|
1732
|
+
@ds.count.should == 1
|
|
1733
|
+
if @native
|
|
1734
|
+
rs = @ds.all
|
|
1735
|
+
v = rs.first[:h]
|
|
1736
|
+
v.should_not be_a_kind_of(Hash)
|
|
1737
|
+
v.to_hash.should be_a_kind_of(Hash)
|
|
1738
|
+
v.to_hash.should == @h
|
|
1739
|
+
@ds.delete
|
|
1740
|
+
@ds.insert(rs.first)
|
|
1741
|
+
@ds.all.should == rs
|
|
1742
|
+
end
|
|
1743
|
+
end
|
|
1744
|
+
|
|
1745
|
+
specify 'use hstore in bound variables' do
|
|
1746
|
+
@db.create_table!(:items) do
|
|
1747
|
+
column :i, :hstore
|
|
1748
|
+
end
|
|
1749
|
+
@ds.call(:insert, {:i=>Sequel.hstore(@h)}, {:i=>:$i})
|
|
1750
|
+
@ds.get(:i).should == @h
|
|
1751
|
+
@ds.filter(:i=>:$i).call(:first, :i=>Sequel.hstore(@h)).should == {:i=>@h}
|
|
1752
|
+
@ds.filter(:i=>:$i).call(:first, :i=>Sequel.hstore({})).should == nil
|
|
1753
|
+
|
|
1754
|
+
@ds.delete
|
|
1755
|
+
@ds.call(:insert, {:i=>@h}, {:i=>:$i})
|
|
1756
|
+
@ds.get(:i).should == @h
|
|
1757
|
+
@ds.filter(:i=>:$i).call(:first, :i=>@h).should == {:i=>@h}
|
|
1758
|
+
@ds.filter(:i=>:$i).call(:first, :i=>{}).should == nil
|
|
1759
|
+
end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
|
|
1760
|
+
|
|
1761
|
+
specify 'with models and associations' do
|
|
1762
|
+
@db.create_table!(:items) do
|
|
1763
|
+
primary_key :id
|
|
1764
|
+
column :h, :hstore
|
|
1765
|
+
end
|
|
1766
|
+
c = Class.new(Sequel::Model(@db[:items])) do
|
|
1767
|
+
def self.name
|
|
1768
|
+
'Item'
|
|
1769
|
+
end
|
|
1770
|
+
unrestrict_primary_key
|
|
1771
|
+
def item_id
|
|
1772
|
+
h['item_id'].to_i if h
|
|
1773
|
+
end
|
|
1774
|
+
def left_item_id
|
|
1775
|
+
h['left_item_id'].to_i if h
|
|
1776
|
+
end
|
|
1777
|
+
end
|
|
1778
|
+
Sequel.extension :pg_hstore_ops
|
|
1779
|
+
c.plugin :many_through_many
|
|
1780
|
+
c.plugin :pg_typecast_on_load, :h unless @native
|
|
1781
|
+
|
|
1782
|
+
h = {'item_id'=>"2", 'left_item_id'=>"1"}
|
|
1783
|
+
o2 = c.create(:id=>2)
|
|
1784
|
+
o = c.create(:id=>1, :h=>h)
|
|
1785
|
+
o.h.should == h
|
|
1786
|
+
|
|
1787
|
+
c.many_to_one :item, :class=>c, :key_column=>Sequel.cast(Sequel.hstore(:h)['item_id'], Integer)
|
|
1788
|
+
c.one_to_many :items, :class=>c, :key=>Sequel.cast(Sequel.hstore(:h)['item_id'], Integer), :key_method=>:item_id
|
|
1789
|
+
c.many_to_many :related_items, :class=>c, :join_table=>:items___i, :left_key=>Sequel.cast(Sequel.hstore(:h)['left_item_id'], Integer), :right_key=>Sequel.cast(Sequel.hstore(:h)['item_id'], Integer)
|
|
1790
|
+
|
|
1791
|
+
c.many_to_one :other_item, :class=>c, :key=>:id, :primary_key_method=>:item_id, :primary_key=>Sequel.cast(Sequel.hstore(:h)['item_id'], Integer)
|
|
1792
|
+
c.one_to_many :other_items, :class=>c, :primary_key=>:item_id, :key=>:id, :primary_key_column=>Sequel.cast(Sequel.hstore(:h)['item_id'], Integer)
|
|
1793
|
+
c.many_to_many :other_related_items, :class=>c, :join_table=>:items___i, :left_key=>:id, :right_key=>:id,
|
|
1794
|
+
:left_primary_key_column=>Sequel.cast(Sequel.hstore(:h)['left_item_id'], Integer),
|
|
1795
|
+
:left_primary_key=>:left_item_id,
|
|
1796
|
+
:right_primary_key=>Sequel.cast(Sequel.hstore(:h)['left_item_id'], Integer),
|
|
1797
|
+
:right_primary_key_method=>:left_item_id
|
|
1798
|
+
|
|
1799
|
+
c.many_through_many :mtm_items, [
|
|
1800
|
+
[:items, Sequel.cast(Sequel.hstore(:h)['item_id'], Integer), Sequel.cast(Sequel.hstore(:h)['left_item_id'], Integer)],
|
|
1801
|
+
[:items, Sequel.cast(Sequel.hstore(:h)['left_item_id'], Integer), Sequel.cast(Sequel.hstore(:h)['left_item_id'], Integer)]
|
|
1802
|
+
],
|
|
1803
|
+
:class=>c,
|
|
1804
|
+
:left_primary_key_column=>Sequel.cast(Sequel.hstore(:h)['item_id'], Integer),
|
|
1805
|
+
:left_primary_key=>:item_id,
|
|
1806
|
+
:right_primary_key=>Sequel.cast(Sequel.hstore(:h)['left_item_id'], Integer),
|
|
1807
|
+
:right_primary_key_method=>:left_item_id
|
|
1808
|
+
|
|
1809
|
+
# Lazily Loading
|
|
1810
|
+
o.item.should == o2
|
|
1811
|
+
o2.items.should == [o]
|
|
1812
|
+
o.related_items.should == [o2]
|
|
1813
|
+
o2.other_item.should == o
|
|
1814
|
+
o.other_items.should == [o2]
|
|
1815
|
+
o.other_related_items.should == [o]
|
|
1816
|
+
o.mtm_items.should == [o]
|
|
1817
|
+
|
|
1818
|
+
# Eager Loading via eager
|
|
1819
|
+
os = c.eager(:item, :related_items, :other_items, :other_related_items, :mtm_items).where(:id=>1).all.first
|
|
1820
|
+
os.item.should == o2
|
|
1821
|
+
os.related_items.should == [o2]
|
|
1822
|
+
os.other_items.should == [o2]
|
|
1823
|
+
os.other_related_items.should == [o]
|
|
1824
|
+
os.mtm_items.should == [o]
|
|
1825
|
+
os = c.eager(:items, :other_item).where(:id=>2).all.first
|
|
1826
|
+
os.items.should == [o]
|
|
1827
|
+
os.other_item.should == o
|
|
1828
|
+
|
|
1829
|
+
# Eager Loading via eager_graph
|
|
1830
|
+
c.eager_graph(:item).where(:items__id=>1).all.first.item.should == o2
|
|
1831
|
+
c.eager_graph(:items).where(:items__id=>2).all.first.items.should == [o]
|
|
1832
|
+
c.eager_graph(:related_items).where(:items__id=>1).all.first.related_items.should == [o2]
|
|
1833
|
+
c.eager_graph(:other_item).where(:items__id=>2).all.first.other_item.should == o
|
|
1834
|
+
c.eager_graph(:other_items).where(:items__id=>1).all.first.other_items.should == [o2]
|
|
1835
|
+
c.eager_graph(:other_related_items).where(:items__id=>1).all.first.other_related_items.should == [o]
|
|
1836
|
+
c.eager_graph(:mtm_items).where(:items__id=>1).all.first.mtm_items.should == [o]
|
|
1837
|
+
|
|
1838
|
+
# Filter By Associations - Model Instances
|
|
1839
|
+
c.filter(:item=>o2).all.should == [o]
|
|
1840
|
+
c.filter(:items=>o).all.should == [o2]
|
|
1841
|
+
c.filter(:related_items=>o2).all.should == [o]
|
|
1842
|
+
c.filter(:other_item=>o).all.should == [o2]
|
|
1843
|
+
c.filter(:other_items=>o2).all.should == [o]
|
|
1844
|
+
c.filter(:other_related_items=>o).all.should == [o]
|
|
1845
|
+
c.filter(:mtm_items=>o).all.should == [o]
|
|
1846
|
+
|
|
1847
|
+
# Filter By Associations - Model Datasets
|
|
1848
|
+
c.filter(:item=>c.filter(:id=>o2.id)).all.should == [o]
|
|
1849
|
+
c.filter(:items=>c.filter(:id=>o.id)).all.should == [o2]
|
|
1850
|
+
c.filter(:related_items=>c.filter(:id=>o2.id)).all.should == [o]
|
|
1851
|
+
c.filter(:other_item=>c.filter(:id=>o.id)).all.should == [o2]
|
|
1852
|
+
c.filter(:other_items=>c.filter(:id=>o2.id)).all.should == [o]
|
|
1853
|
+
c.filter(:other_related_items=>c.filter(:id=>o.id)).all.should == [o]
|
|
1854
|
+
c.filter(:mtm_items=>c.filter(:id=>o.id)).all.should == [o]
|
|
1855
|
+
end
|
|
1856
|
+
|
|
1857
|
+
specify 'operations/functions with pg_hstore_ops' do
|
|
1858
|
+
Sequel.extension :pg_hstore_ops, :pg_array, :pg_array_ops
|
|
1859
|
+
@db.create_table!(:items){hstore :h1; hstore :h2; hstore :h3; String :t}
|
|
1860
|
+
@ds.insert(Sequel.hstore('a'=>'b', 'c'=>nil), Sequel.hstore('a'=>'b'), Sequel.hstore('d'=>'e'))
|
|
1861
|
+
h1 = Sequel.hstore(:h1)
|
|
1862
|
+
h2 = Sequel.hstore(:h2)
|
|
1863
|
+
h3 = Sequel.hstore(:h3)
|
|
1864
|
+
|
|
1865
|
+
@ds.get(h1['a']).should == 'b'
|
|
1866
|
+
@ds.get(h1['d']).should == nil
|
|
1867
|
+
|
|
1868
|
+
@ds.get(h2.concat(h3).keys.pg_array.length).should == 2
|
|
1869
|
+
@ds.get(h1.concat(h3).keys.pg_array.length).should == 3
|
|
1870
|
+
@ds.get(h2.merge(h3).keys.pg_array.length).should == 2
|
|
1871
|
+
@ds.get(h1.merge(h3).keys.pg_array.length).should == 3
|
|
1872
|
+
|
|
1873
|
+
unless [:do].include?(@db.adapter_scheme)
|
|
1874
|
+
# Broken DataObjects thinks operators with ? represent placeholders
|
|
1875
|
+
@ds.get(h1.contain_all(Sequel.pg_array(%w'a c'))).should == true
|
|
1876
|
+
@ds.get(h1.contain_all(Sequel.pg_array(%w'a d'))).should == false
|
|
1877
|
+
|
|
1878
|
+
@ds.get(h1.contain_any(Sequel.pg_array(%w'a d'))).should == true
|
|
1879
|
+
@ds.get(h1.contain_any(Sequel.pg_array(%w'e d'))).should == false
|
|
1880
|
+
end
|
|
1881
|
+
|
|
1882
|
+
@ds.get(h1.contains(h2)).should == true
|
|
1883
|
+
@ds.get(h1.contains(h3)).should == false
|
|
1884
|
+
|
|
1885
|
+
@ds.get(h2.contained_by(h1)).should == true
|
|
1886
|
+
@ds.get(h2.contained_by(h3)).should == false
|
|
1887
|
+
|
|
1888
|
+
@ds.get(h1.defined('a')).should == true
|
|
1889
|
+
@ds.get(h1.defined('c')).should == false
|
|
1890
|
+
@ds.get(h1.defined('d')).should == false
|
|
1891
|
+
|
|
1892
|
+
@ds.get(h1.delete('a')['c']).should == nil
|
|
1893
|
+
@ds.get(h1.delete(Sequel.pg_array(%w'a d'))['c']).should == nil
|
|
1894
|
+
@ds.get(h1.delete(h2)['c']).should == nil
|
|
1895
|
+
|
|
1896
|
+
@ds.from(Sequel.hstore('a'=>'b', 'c'=>nil).op.each).order(:key).all.should == [{:key=>'a', :value=>'b'}, {:key=>'c', :value=>nil}]
|
|
1897
|
+
|
|
1898
|
+
unless [:do].include?(@db.adapter_scheme)
|
|
1899
|
+
@ds.get(h1.has_key?('c')).should == true
|
|
1900
|
+
@ds.get(h1.include?('c')).should == true
|
|
1901
|
+
@ds.get(h1.key?('c')).should == true
|
|
1902
|
+
@ds.get(h1.member?('c')).should == true
|
|
1903
|
+
@ds.get(h1.exist?('c')).should == true
|
|
1904
|
+
@ds.get(h1.has_key?('d')).should == false
|
|
1905
|
+
@ds.get(h1.include?('d')).should == false
|
|
1906
|
+
@ds.get(h1.key?('d')).should == false
|
|
1907
|
+
@ds.get(h1.member?('d')).should == false
|
|
1908
|
+
@ds.get(h1.exist?('d')).should == false
|
|
1909
|
+
end
|
|
1910
|
+
|
|
1911
|
+
@ds.get(h1.hstore.hstore.hstore.keys.pg_array.length).should == 2
|
|
1912
|
+
@ds.get(h1.keys.pg_array.length).should == 2
|
|
1913
|
+
@ds.get(h2.keys.pg_array.length).should == 1
|
|
1914
|
+
@ds.get(h1.akeys.pg_array.length).should == 2
|
|
1915
|
+
@ds.get(h2.akeys.pg_array.length).should == 1
|
|
1916
|
+
|
|
1917
|
+
@ds.from(Sequel.hstore('t'=>'s').op.populate(Sequel::SQL::Cast.new(nil, :items))).select_map(:t).should == ['s']
|
|
1918
|
+
@ds.from(:items___i).select(Sequel.hstore('t'=>'s').op.record_set(:i).as(:r)).from_self(:alias=>:s).select(Sequel.lit('(r).*')).from_self.select_map(:t).should == ['s']
|
|
1919
|
+
|
|
1920
|
+
@ds.from(Sequel.hstore('t'=>'s', 'a'=>'b').op.skeys.as(:s)).select_order_map(:s).should == %w'a t'
|
|
1921
|
+
|
|
1922
|
+
@ds.get(h1.slice(Sequel.pg_array(%w'a c')).keys.pg_array.length).should == 2
|
|
1923
|
+
@ds.get(h1.slice(Sequel.pg_array(%w'd c')).keys.pg_array.length).should == 1
|
|
1924
|
+
@ds.get(h1.slice(Sequel.pg_array(%w'd e')).keys.pg_array.length).should == nil
|
|
1925
|
+
|
|
1926
|
+
@ds.from(Sequel.hstore('t'=>'s', 'a'=>'b').op.svals.as(:s)).select_order_map(:s).should == %w'b s'
|
|
1927
|
+
|
|
1928
|
+
@ds.get(h1.to_array.pg_array.length).should == 4
|
|
1929
|
+
@ds.get(h2.to_array.pg_array.length).should == 2
|
|
1930
|
+
|
|
1931
|
+
@ds.get(h1.to_matrix.pg_array.length).should == 2
|
|
1932
|
+
@ds.get(h2.to_matrix.pg_array.length).should == 1
|
|
1933
|
+
|
|
1934
|
+
@ds.get(h1.values.pg_array.length).should == 2
|
|
1935
|
+
@ds.get(h2.values.pg_array.length).should == 1
|
|
1936
|
+
@ds.get(h1.avals.pg_array.length).should == 2
|
|
1937
|
+
@ds.get(h2.avals.pg_array.length).should == 1
|
|
1938
|
+
end
|
|
1939
|
+
end if POSTGRES_DB.type_supported?(:hstore)
|
|
1940
|
+
|
|
1941
|
+
describe 'PostgreSQL json type' do
|
|
1942
|
+
before(:all) do
|
|
1943
|
+
@db = POSTGRES_DB
|
|
1944
|
+
@db.extension :pg_array, :pg_json
|
|
1945
|
+
@ds = @db[:items]
|
|
1946
|
+
@a = [1, 2, {'a'=>'b'}, 3.0]
|
|
1947
|
+
@h = {'a'=>'b', '1'=>[3, 4, 5]}
|
|
1948
|
+
@native = POSTGRES_DB.adapter_scheme == :postgres
|
|
1949
|
+
end
|
|
1950
|
+
after do
|
|
1951
|
+
@db.drop_table?(:items)
|
|
1952
|
+
end
|
|
1953
|
+
|
|
1954
|
+
specify 'insert and retrieve json values' do
|
|
1955
|
+
@db.create_table!(:items){json :j}
|
|
1956
|
+
@ds.insert(Sequel.pg_json(@h))
|
|
1957
|
+
@ds.count.should == 1
|
|
1958
|
+
if @native
|
|
1959
|
+
rs = @ds.all
|
|
1960
|
+
v = rs.first[:j]
|
|
1961
|
+
v.should_not be_a_kind_of(Hash)
|
|
1962
|
+
v.to_hash.should be_a_kind_of(Hash)
|
|
1963
|
+
v.should == @h
|
|
1964
|
+
v.to_hash.should == @h
|
|
1965
|
+
@ds.delete
|
|
1966
|
+
@ds.insert(rs.first)
|
|
1967
|
+
@ds.all.should == rs
|
|
1968
|
+
end
|
|
1969
|
+
|
|
1970
|
+
@ds.delete
|
|
1971
|
+
@ds.insert(Sequel.pg_json(@a))
|
|
1972
|
+
@ds.count.should == 1
|
|
1973
|
+
if @native
|
|
1974
|
+
rs = @ds.all
|
|
1975
|
+
v = rs.first[:j]
|
|
1976
|
+
v.should_not be_a_kind_of(Array)
|
|
1977
|
+
v.to_a.should be_a_kind_of(Array)
|
|
1978
|
+
v.should == @a
|
|
1979
|
+
v.to_a.should == @a
|
|
1980
|
+
@ds.delete
|
|
1981
|
+
@ds.insert(rs.first)
|
|
1982
|
+
@ds.all.should == rs
|
|
1983
|
+
end
|
|
1984
|
+
end
|
|
1985
|
+
|
|
1986
|
+
specify 'insert and retrieve json[] values' do
|
|
1987
|
+
@db.create_table!(:items){column :j, 'json[]'}
|
|
1988
|
+
j = Sequel.pg_array([Sequel.pg_json('a'=>1), Sequel.pg_json(['b', 2])])
|
|
1989
|
+
@ds.insert(j)
|
|
1990
|
+
@ds.count.should == 1
|
|
1991
|
+
if @native
|
|
1992
|
+
rs = @ds.all
|
|
1993
|
+
v = rs.first[:j]
|
|
1994
|
+
v.should_not be_a_kind_of(Array)
|
|
1995
|
+
v.to_a.should be_a_kind_of(Array)
|
|
1996
|
+
v.should == j
|
|
1997
|
+
v.to_a.should == j
|
|
1998
|
+
@ds.delete
|
|
1999
|
+
@ds.insert(rs.first)
|
|
2000
|
+
@ds.all.should == rs
|
|
2001
|
+
end
|
|
2002
|
+
end
|
|
2003
|
+
|
|
2004
|
+
specify 'use json in bound variables' do
|
|
2005
|
+
@db.create_table!(:items){json :i}
|
|
2006
|
+
@ds.call(:insert, {:i=>Sequel.pg_json(@h)}, {:i=>:$i})
|
|
2007
|
+
@ds.get(:i).should == @h
|
|
2008
|
+
@ds.filter(Sequel.cast(:i, String)=>:$i).call(:first, :i=>Sequel.pg_json(@h)).should == {:i=>@h}
|
|
2009
|
+
@ds.filter(Sequel.cast(:i, String)=>:$i).call(:first, :i=>Sequel.pg_json({})).should == nil
|
|
2010
|
+
@ds.filter(Sequel.cast(:i, String)=>:$i).call(:delete, :i=>Sequel.pg_json(@h)).should == 1
|
|
2011
|
+
|
|
2012
|
+
@ds.call(:insert, {:i=>Sequel.pg_json(@a)}, {:i=>:$i})
|
|
2013
|
+
@ds.get(:i).should == @a
|
|
2014
|
+
@ds.filter(Sequel.cast(:i, String)=>:$i).call(:first, :i=>Sequel.pg_json(@a)).should == {:i=>@a}
|
|
2015
|
+
@ds.filter(Sequel.cast(:i, String)=>:$i).call(:first, :i=>Sequel.pg_json([])).should == nil
|
|
2016
|
+
|
|
2017
|
+
@db.create_table!(:items){column :i, 'json[]'}
|
|
2018
|
+
j = Sequel.pg_array([Sequel.pg_json('a'=>1), Sequel.pg_json(['b', 2])], :text)
|
|
2019
|
+
@ds.call(:insert, {:i=>j}, {:i=>:$i})
|
|
2020
|
+
@ds.get(:i).should == j
|
|
2021
|
+
@ds.filter(Sequel.cast(:i, 'text[]')=>:$i).call(:first, :i=>j).should == {:i=>j}
|
|
2022
|
+
@ds.filter(Sequel.cast(:i, 'text[]')=>:$i).call(:first, :i=>Sequel.pg_array([])).should == nil
|
|
2023
|
+
end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
|
|
2024
|
+
|
|
2025
|
+
specify 'with models' do
|
|
2026
|
+
@db.create_table!(:items) do
|
|
2027
|
+
primary_key :id
|
|
2028
|
+
json :h
|
|
2029
|
+
end
|
|
2030
|
+
c = Class.new(Sequel::Model(@db[:items]))
|
|
2031
|
+
c.plugin :pg_typecast_on_load, :h unless @native
|
|
2032
|
+
c.create(:h=>Sequel.pg_json(@h)).h.should == @h
|
|
2033
|
+
c.create(:h=>Sequel.pg_json(@a)).h.should == @a
|
|
2034
|
+
end
|
|
2035
|
+
end if POSTGRES_DB.server_version >= 90200
|
|
2036
|
+
|
|
2037
|
+
describe 'PostgreSQL inet/cidr types' do
|
|
2038
|
+
ipv6_broken = (IPAddr.new('::1'); false) rescue true
|
|
2039
|
+
|
|
2040
|
+
before(:all) do
|
|
2041
|
+
@db = POSTGRES_DB
|
|
2042
|
+
@db.extension :pg_array, :pg_inet
|
|
2043
|
+
@ds = @db[:items]
|
|
2044
|
+
@v4 = '127.0.0.1'
|
|
2045
|
+
@v4nm = '127.0.0.0/8'
|
|
2046
|
+
@v6 = '2001:4f8:3:ba:2e0:81ff:fe22:d1f1'
|
|
2047
|
+
@v6nm = '2001:4f8:3:ba::/64'
|
|
2048
|
+
@ipv4 = IPAddr.new(@v4)
|
|
2049
|
+
@ipv4nm = IPAddr.new(@v4nm)
|
|
2050
|
+
unless ipv6_broken
|
|
2051
|
+
@ipv6 = IPAddr.new(@v6)
|
|
2052
|
+
@ipv6nm = IPAddr.new(@v6nm)
|
|
2053
|
+
end
|
|
2054
|
+
@native = POSTGRES_DB.adapter_scheme == :postgres
|
|
2055
|
+
end
|
|
2056
|
+
after do
|
|
2057
|
+
@db.drop_table?(:items)
|
|
2058
|
+
end
|
|
2059
|
+
|
|
2060
|
+
specify 'insert and retrieve inet/cidr values' do
|
|
2061
|
+
@db.create_table!(:items){inet :i; cidr :c}
|
|
2062
|
+
@ds.insert(@ipv4, @ipv4nm)
|
|
2063
|
+
@ds.count.should == 1
|
|
2064
|
+
if @native
|
|
2065
|
+
rs = @ds.all
|
|
2066
|
+
rs.first[:i].should == @ipv4
|
|
2067
|
+
rs.first[:c].should == @ipv4nm
|
|
2068
|
+
rs.first[:i].should be_a_kind_of(IPAddr)
|
|
2069
|
+
rs.first[:c].should be_a_kind_of(IPAddr)
|
|
2070
|
+
@ds.delete
|
|
2071
|
+
@ds.insert(rs.first)
|
|
2072
|
+
@ds.all.should == rs
|
|
2073
|
+
end
|
|
2074
|
+
|
|
2075
|
+
unless ipv6_broken
|
|
2076
|
+
@ds.delete
|
|
2077
|
+
@ds.insert(@ipv6, @ipv6nm)
|
|
2078
|
+
@ds.count.should == 1
|
|
2079
|
+
if @native
|
|
2080
|
+
rs = @ds.all
|
|
2081
|
+
v = rs.first[:j]
|
|
2082
|
+
rs.first[:i].should == @ipv6
|
|
2083
|
+
rs.first[:c].should == @ipv6nm
|
|
2084
|
+
rs.first[:i].should be_a_kind_of(IPAddr)
|
|
2085
|
+
rs.first[:c].should be_a_kind_of(IPAddr)
|
|
2086
|
+
@ds.delete
|
|
2087
|
+
@ds.insert(rs.first)
|
|
2088
|
+
@ds.all.should == rs
|
|
2089
|
+
end
|
|
2090
|
+
end
|
|
2091
|
+
end
|
|
2092
|
+
|
|
2093
|
+
specify 'insert and retrieve inet/cidr/macaddr array values' do
|
|
2094
|
+
@db.create_table!(:items){column :i, 'inet[]'; column :c, 'cidr[]'; column :m, 'macaddr[]'}
|
|
2095
|
+
@ds.insert(Sequel.pg_array([@ipv4], 'inet'), Sequel.pg_array([@ipv4nm], 'cidr'), Sequel.pg_array(['12:34:56:78:90:ab'], 'macaddr'))
|
|
2096
|
+
@ds.count.should == 1
|
|
2097
|
+
if @native
|
|
2098
|
+
rs = @ds.all
|
|
2099
|
+
rs.first.values.all?{|c| c.is_a?(Sequel::Postgres::PGArray)}.should be_true
|
|
2100
|
+
rs.first[:i].first.should == @ipv4
|
|
2101
|
+
rs.first[:c].first.should == @ipv4nm
|
|
2102
|
+
rs.first[:m].first.should == '12:34:56:78:90:ab'
|
|
2103
|
+
rs.first[:i].first.should be_a_kind_of(IPAddr)
|
|
2104
|
+
rs.first[:c].first.should be_a_kind_of(IPAddr)
|
|
2105
|
+
@ds.delete
|
|
2106
|
+
@ds.insert(rs.first)
|
|
2107
|
+
@ds.all.should == rs
|
|
2108
|
+
end
|
|
2109
|
+
end
|
|
2110
|
+
|
|
2111
|
+
specify 'use ipaddr in bound variables' do
|
|
2112
|
+
@db.create_table!(:items){inet :i; cidr :c}
|
|
2113
|
+
|
|
2114
|
+
@ds.call(:insert, {:i=>@ipv4, :c=>@ipv4nm}, {:i=>:$i, :c=>:$c})
|
|
2115
|
+
@ds.get(:i).should == @ipv4
|
|
2116
|
+
@ds.get(:c).should == @ipv4nm
|
|
2117
|
+
@ds.filter(:i=>:$i, :c=>:$c).call(:first, :i=>@ipv4, :c=>@ipv4nm).should == {:i=>@ipv4, :c=>@ipv4nm}
|
|
2118
|
+
@ds.filter(:i=>:$i, :c=>:$c).call(:first, :i=>@ipv6, :c=>@ipv6nm).should == nil
|
|
2119
|
+
@ds.filter(:i=>:$i, :c=>:$c).call(:delete, :i=>@ipv4, :c=>@ipv4nm).should == 1
|
|
2120
|
+
|
|
2121
|
+
unless ipv6_broken
|
|
2122
|
+
@ds.call(:insert, {:i=>@ipv6, :c=>@ipv6nm}, {:i=>:$i, :c=>:$c})
|
|
2123
|
+
@ds.get(:i).should == @ipv6
|
|
2124
|
+
@ds.get(:c).should == @ipv6nm
|
|
2125
|
+
@ds.filter(:i=>:$i, :c=>:$c).call(:first, :i=>@ipv6, :c=>@ipv6nm).should == {:i=>@ipv6, :c=>@ipv6nm}
|
|
2126
|
+
@ds.filter(:i=>:$i, :c=>:$c).call(:first, :i=>@ipv4, :c=>@ipv4nm).should == nil
|
|
2127
|
+
@ds.filter(:i=>:$i, :c=>:$c).call(:delete, :i=>@ipv6, :c=>@ipv6nm).should == 1
|
|
2128
|
+
end
|
|
2129
|
+
|
|
2130
|
+
@db.create_table!(:items){column :i, 'inet[]'; column :c, 'cidr[]'; column :m, 'macaddr[]'}
|
|
2131
|
+
@ds.call(:insert, {:i=>[@ipv4], :c=>[@ipv4nm], :m=>['12:34:56:78:90:ab']}, {:i=>:$i, :c=>:$c, :m=>:$m})
|
|
2132
|
+
@ds.filter(:i=>:$i, :c=>:$c, :m=>:$m).call(:first, :i=>[@ipv4], :c=>[@ipv4nm], :m=>['12:34:56:78:90:ab']).should == {:i=>[@ipv4], :c=>[@ipv4nm], :m=>['12:34:56:78:90:ab']}
|
|
2133
|
+
@ds.filter(:i=>:$i, :c=>:$c, :m=>:$m).call(:first, :i=>[], :c=>[], :m=>[]).should == nil
|
|
2134
|
+
@ds.filter(:i=>:$i, :c=>:$c, :m=>:$m).call(:delete, :i=>[@ipv4], :c=>[@ipv4nm], :m=>['12:34:56:78:90:ab']).should == 1
|
|
2135
|
+
end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
|
|
2136
|
+
|
|
2137
|
+
specify 'with models' do
|
|
2138
|
+
@db.create_table!(:items) do
|
|
2139
|
+
primary_key :id
|
|
2140
|
+
inet :i
|
|
2141
|
+
cidr :c
|
|
2142
|
+
end
|
|
2143
|
+
c = Class.new(Sequel::Model(@db[:items]))
|
|
2144
|
+
c.plugin :pg_typecast_on_load, :i, :c unless @native
|
|
2145
|
+
c.create(:i=>@v4, :c=>@v4nm).values.values_at(:i, :c).should == [@ipv4, @ipv4nm]
|
|
2146
|
+
unless ipv6_broken
|
|
2147
|
+
c.create(:i=>@ipv6, :c=>@ipv6nm).values.values_at(:i, :c).should == [@ipv6, @ipv6nm]
|
|
2148
|
+
end
|
|
2149
|
+
end
|
|
2150
|
+
end
|
|
2151
|
+
|
|
2152
|
+
describe 'PostgreSQL range types' do
|
|
2153
|
+
before(:all) do
|
|
2154
|
+
@db = POSTGRES_DB
|
|
2155
|
+
@db.extension :pg_array, :pg_range
|
|
2156
|
+
@ds = @db[:items]
|
|
2157
|
+
@map = {:i4=>'int4range', :i8=>'int8range', :n=>'numrange', :d=>'daterange', :t=>'tsrange', :tz=>'tstzrange'}
|
|
2158
|
+
@r = {:i4=>1...2, :i8=>2...3, :n=>BigDecimal.new('1.0')..BigDecimal.new('2.0'), :d=>Date.today...(Date.today+1), :t=>Time.local(2011, 1)..Time.local(2011, 2), :tz=>Time.local(2011, 1)..Time.local(2011, 2)}
|
|
2159
|
+
@ra = {}
|
|
2160
|
+
@pgr = {}
|
|
2161
|
+
@pgra = {}
|
|
2162
|
+
@r.each{|k, v| @ra[k] = Sequel.pg_array([v], @map[k])}
|
|
2163
|
+
@r.each{|k, v| @pgr[k] = Sequel.pg_range(v)}
|
|
2164
|
+
@r.each{|k, v| @pgra[k] = Sequel.pg_array([Sequel.pg_range(v)], @map[k])}
|
|
2165
|
+
@native = POSTGRES_DB.adapter_scheme == :postgres
|
|
2166
|
+
end
|
|
2167
|
+
after do
|
|
2168
|
+
@db.drop_table?(:items)
|
|
2169
|
+
end
|
|
2170
|
+
|
|
2171
|
+
specify 'insert and retrieve range type values' do
|
|
2172
|
+
@db.create_table!(:items){int4range :i4; int8range :i8; numrange :n; daterange :d; tsrange :t; tstzrange :tz}
|
|
2173
|
+
[@r, @pgr].each do |input|
|
|
2174
|
+
h = {}
|
|
2175
|
+
input.each{|k, v| h[k] = Sequel.cast(v, @map[k])}
|
|
2176
|
+
@ds.insert(h)
|
|
2177
|
+
@ds.count.should == 1
|
|
2178
|
+
if @native
|
|
2179
|
+
rs = @ds.all
|
|
2180
|
+
rs.first.each do |k, v|
|
|
2181
|
+
v.should_not be_a_kind_of(Range)
|
|
2182
|
+
v.to_range.should be_a_kind_of(Range)
|
|
2183
|
+
v.should == @r[k]
|
|
2184
|
+
v.to_range.should == @r[k]
|
|
2185
|
+
end
|
|
2186
|
+
@ds.delete
|
|
2187
|
+
@ds.insert(rs.first)
|
|
2188
|
+
@ds.all.should == rs
|
|
2189
|
+
end
|
|
2190
|
+
@ds.delete
|
|
2191
|
+
end
|
|
2192
|
+
end
|
|
2193
|
+
|
|
2194
|
+
specify 'insert and retrieve arrays of range type values' do
|
|
2195
|
+
@db.create_table!(:items){column :i4, 'int4range[]'; column :i8, 'int8range[]'; column :n, 'numrange[]'; column :d, 'daterange[]'; column :t, 'tsrange[]'; column :tz, 'tstzrange[]'}
|
|
2196
|
+
[@ra, @pgra].each do |input|
|
|
2197
|
+
@ds.insert(input)
|
|
2198
|
+
@ds.count.should == 1
|
|
2199
|
+
if @native
|
|
2200
|
+
rs = @ds.all
|
|
2201
|
+
rs.first.each do |k, v|
|
|
2202
|
+
v.should_not be_a_kind_of(Array)
|
|
2203
|
+
v.to_a.should be_a_kind_of(Array)
|
|
2204
|
+
v.first.should_not be_a_kind_of(Range)
|
|
2205
|
+
v.first.to_range.should be_a_kind_of(Range)
|
|
2206
|
+
v.should == @ra[k].to_a
|
|
2207
|
+
v.first.should == @r[k]
|
|
2208
|
+
end
|
|
2209
|
+
@ds.delete
|
|
2210
|
+
@ds.insert(rs.first)
|
|
2211
|
+
@ds.all.should == rs
|
|
2212
|
+
end
|
|
2213
|
+
@ds.delete
|
|
2214
|
+
end
|
|
2215
|
+
end
|
|
2216
|
+
|
|
2217
|
+
specify 'use range types in bound variables' do
|
|
2218
|
+
@db.create_table!(:items){int4range :i4; int8range :i8; numrange :n; daterange :d; tsrange :t; tstzrange :tz}
|
|
2219
|
+
h = {}
|
|
2220
|
+
@r.keys.each{|k| h[k] = :"$#{k}"}
|
|
2221
|
+
r2 = {}
|
|
2222
|
+
@r.each{|k, v| r2[k] = Range.new(v.begin, v.end+2)}
|
|
2223
|
+
@ds.call(:insert, @r, h)
|
|
2224
|
+
@ds.first.should == @r
|
|
2225
|
+
@ds.filter(h).call(:first, @r).should == @r
|
|
2226
|
+
@ds.filter(h).call(:first, @pgr).should == @r
|
|
2227
|
+
@ds.filter(h).call(:first, r2).should == nil
|
|
2228
|
+
@ds.filter(h).call(:delete, @r).should == 1
|
|
2229
|
+
|
|
2230
|
+
@db.create_table!(:items){column :i4, 'int4range[]'; column :i8, 'int8range[]'; column :n, 'numrange[]'; column :d, 'daterange[]'; column :t, 'tsrange[]'; column :tz, 'tstzrange[]'}
|
|
2231
|
+
@r.each{|k, v| r2[k] = [Range.new(v.begin, v.end+2)]}
|
|
2232
|
+
@ds.call(:insert, @ra, h)
|
|
2233
|
+
@ds.filter(h).call(:first, @ra).each{|k, v| v.should == @ra[k].to_a}
|
|
2234
|
+
@ds.filter(h).call(:first, @pgra).each{|k, v| v.should == @ra[k].to_a}
|
|
2235
|
+
@ds.filter(h).call(:first, r2).should == nil
|
|
2236
|
+
@ds.filter(h).call(:delete, @ra).should == 1
|
|
2237
|
+
end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
|
|
2238
|
+
|
|
2239
|
+
specify 'with models' do
|
|
2240
|
+
@db.create_table!(:items){primary_key :id; int4range :i4; int8range :i8; numrange :n; daterange :d; tsrange :t; tstzrange :tz}
|
|
2241
|
+
c = Class.new(Sequel::Model(@db[:items]))
|
|
2242
|
+
c.plugin :pg_typecast_on_load, :i4, :i8, :n, :d, :t, :tz unless @native
|
|
2243
|
+
v = c.create(@r).values
|
|
2244
|
+
v.delete(:id)
|
|
2245
|
+
v.should == @r
|
|
2246
|
+
|
|
2247
|
+
unless @db.adapter_scheme == :jdbc
|
|
2248
|
+
@db.create_table!(:items){primary_key :id; column :i4, 'int4range[]'; column :i8, 'int8range[]'; column :n, 'numrange[]'; column :d, 'daterange[]'; column :t, 'tsrange[]'; column :tz, 'tstzrange[]'}
|
|
2249
|
+
c = Class.new(Sequel::Model(@db[:items]))
|
|
2250
|
+
c.plugin :pg_typecast_on_load, :i4, :i8, :n, :d, :t, :tz unless @native
|
|
2251
|
+
v = c.create(@ra).values
|
|
2252
|
+
v.delete(:id)
|
|
2253
|
+
v.each{|k,v| v.should == @ra[k].to_a}
|
|
2254
|
+
end
|
|
2255
|
+
end
|
|
2256
|
+
|
|
2257
|
+
specify 'operations/functions with pg_range_ops' do
|
|
2258
|
+
Sequel.extension :pg_range_ops
|
|
2259
|
+
|
|
2260
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.contains(2..4)).should be_true
|
|
2261
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.contains(3..6)).should be_false
|
|
2262
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.contains(0..6)).should be_false
|
|
2263
|
+
|
|
2264
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.contained_by(0..6)).should be_true
|
|
2265
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.contained_by(3..6)).should be_false
|
|
2266
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.contained_by(2..4)).should be_false
|
|
2267
|
+
|
|
2268
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.overlaps(5..6)).should be_true
|
|
2269
|
+
@db.get(Sequel.pg_range(1...5, :int4range).op.overlaps(5..6)).should be_false
|
|
2270
|
+
|
|
2271
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.left_of(6..10)).should be_true
|
|
2272
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.left_of(5..10)).should be_false
|
|
2273
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.left_of(-1..0)).should be_false
|
|
2274
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.left_of(-1..3)).should be_false
|
|
2275
|
+
|
|
2276
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.right_of(6..10)).should be_false
|
|
2277
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.right_of(5..10)).should be_false
|
|
2278
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.right_of(-1..0)).should be_true
|
|
2279
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.right_of(-1..3)).should be_false
|
|
2280
|
+
|
|
2281
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.starts_before(6..10)).should be_true
|
|
2282
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.starts_before(5..10)).should be_true
|
|
2283
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.starts_before(-1..0)).should be_false
|
|
2284
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.starts_before(-1..3)).should be_false
|
|
2285
|
+
|
|
2286
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.ends_after(6..10)).should be_false
|
|
2287
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.ends_after(5..10)).should be_false
|
|
2288
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.ends_after(-1..0)).should be_true
|
|
2289
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.ends_after(-1..3)).should be_true
|
|
2290
|
+
|
|
2291
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.adjacent_to(6..10)).should be_true
|
|
2292
|
+
@db.get(Sequel.pg_range(1...5, :int4range).op.adjacent_to(6..10)).should be_false
|
|
2293
|
+
|
|
2294
|
+
@db.get((Sequel.pg_range(1..5, :int4range).op + (6..10)).adjacent_to(6..10)).should be_false
|
|
2295
|
+
@db.get((Sequel.pg_range(1..5, :int4range).op + (6..10)).adjacent_to(11..20)).should be_true
|
|
2296
|
+
|
|
2297
|
+
@db.get((Sequel.pg_range(1..5, :int4range).op * (2..6)).adjacent_to(6..10)).should be_true
|
|
2298
|
+
@db.get((Sequel.pg_range(1..4, :int4range).op * (2..6)).adjacent_to(6..10)).should be_false
|
|
2299
|
+
|
|
2300
|
+
@db.get((Sequel.pg_range(1..5, :int4range).op - (2..6)).adjacent_to(2..10)).should be_true
|
|
2301
|
+
@db.get((Sequel.pg_range(0..4, :int4range).op - (3..6)).adjacent_to(4..10)).should be_false
|
|
2302
|
+
|
|
2303
|
+
@db.get(Sequel.pg_range(0..4, :int4range).op.lower).should == 0
|
|
2304
|
+
@db.get(Sequel.pg_range(0..4, :int4range).op.upper).should == 5
|
|
2305
|
+
|
|
2306
|
+
@db.get(Sequel.pg_range(0..4, :int4range).op.isempty).should be_false
|
|
2307
|
+
@db.get(Sequel::Postgres::PGRange.empty(:int4range).op.isempty).should be_true
|
|
2308
|
+
|
|
2309
|
+
@db.get(Sequel.pg_range(1..5, :numrange).op.lower_inc).should be_true
|
|
2310
|
+
@db.get(Sequel::Postgres::PGRange.new(1, 5, :exclude_begin=>true, :db_type=>:numrange).op.lower_inc).should be_false
|
|
2311
|
+
|
|
2312
|
+
@db.get(Sequel.pg_range(1..5, :numrange).op.upper_inc).should be_true
|
|
2313
|
+
@db.get(Sequel.pg_range(1...5, :numrange).op.upper_inc).should be_false
|
|
2314
|
+
|
|
2315
|
+
@db.get(Sequel::Postgres::PGRange.new(1, 5, :db_type=>:int4range).op.lower_inf).should be_false
|
|
2316
|
+
@db.get(Sequel::Postgres::PGRange.new(nil, 5, :db_type=>:int4range).op.lower_inf).should be_true
|
|
2317
|
+
|
|
2318
|
+
@db.get(Sequel::Postgres::PGRange.new(1, 5, :db_type=>:int4range).op.upper_inf).should be_false
|
|
2319
|
+
@db.get(Sequel::Postgres::PGRange.new(1, nil, :db_type=>:int4range).op.upper_inf).should be_true
|
|
2320
|
+
end
|
|
2321
|
+
end if POSTGRES_DB.server_version >= 90200
|
|
2322
|
+
|
|
2323
|
+
describe 'PostgreSQL interval types' do
|
|
2324
|
+
before(:all) do
|
|
2325
|
+
@db = POSTGRES_DB
|
|
2326
|
+
@db.extension :pg_array, :pg_interval
|
|
2327
|
+
@ds = @db[:items]
|
|
2328
|
+
@native = POSTGRES_DB.adapter_scheme == :postgres
|
|
2329
|
+
end
|
|
2330
|
+
after(:all) do
|
|
2331
|
+
Sequel::Postgres::PG_TYPES.delete(1186)
|
|
2332
|
+
end
|
|
2333
|
+
after do
|
|
2334
|
+
@db.drop_table?(:items)
|
|
2335
|
+
end
|
|
2336
|
+
|
|
2337
|
+
specify 'insert and retrieve interval values' do
|
|
2338
|
+
@db.create_table!(:items){interval :i}
|
|
2339
|
+
[
|
|
2340
|
+
['0', '00:00:00', 0, [[:seconds, 0]]],
|
|
2341
|
+
['1 microsecond', '00:00:00.000001', 0.000001, [[:seconds, 0.000001]]],
|
|
2342
|
+
['1 millisecond', '00:00:00.001', 0.001, [[:seconds, 0.001]]],
|
|
2343
|
+
['1 second', '00:00:01', 1, [[:seconds, 1]]],
|
|
2344
|
+
['1 minute', '00:01:00', 60, [[:seconds, 60]]],
|
|
2345
|
+
['1 hour', '01:00:00', 3600, [[:seconds, 3600]]],
|
|
2346
|
+
['1 day', '1 day', 86400, [[:days, 1]]],
|
|
2347
|
+
['1 week', '7 days', 86400*7, [[:days, 7]]],
|
|
2348
|
+
['1 month', '1 mon', 86400*30, [[:months, 1]]],
|
|
2349
|
+
['1 year', '1 year', 31557600, [[:years, 1]]],
|
|
2350
|
+
['1 decade', '10 years', 31557600*10, [[:years, 10]]],
|
|
2351
|
+
['1 century', '100 years', 31557600*100, [[:years, 100]]],
|
|
2352
|
+
['1 millennium', '1000 years', 31557600*1000, [[:years, 1000]]],
|
|
2353
|
+
['1 year 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds', '1 year 2 mons 25 days 05:06:07', 31557600 + 2*86400*30 + 3*86400*7 + 4*86400 + 5*3600 + 6*60 + 7, [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]]],
|
|
2354
|
+
['-1 year +2 months -3 weeks +4 days -5 hours +6 minutes -7 seconds', '-10 mons -17 days -04:54:07', -10*86400*30 - 3*86400*7 + 4*86400 - 5*3600 + 6*60 - 7, [[:months, -10], [:days, -17], [:seconds, -17647]]],
|
|
2355
|
+
['+2 years -1 months +3 weeks -4 days +5 hours -6 minutes +7 seconds', '1 year 11 mons 17 days 04:54:07', 31557600 + 11*86400*30 + 3*86400*7 - 4*86400 + 5*3600 - 6*60 + 7, [[:years, 1], [:months, 11], [:days, 17], [:seconds, 17647]]],
|
|
2356
|
+
].each do |instr, outstr, value, parts|
|
|
2357
|
+
@ds.insert(instr)
|
|
2358
|
+
@ds.count.should == 1
|
|
2359
|
+
if @native
|
|
2360
|
+
@ds.get(Sequel.cast(:i, String)).should == outstr
|
|
2361
|
+
rs = @ds.all
|
|
2362
|
+
rs.first[:i].is_a?(ActiveSupport::Duration).should be_true
|
|
2363
|
+
rs.first[:i].should == ActiveSupport::Duration.new(value, parts)
|
|
2364
|
+
rs.first[:i].parts.sort_by{|k,v| k.to_s}.should == parts.sort_by{|k,v| k.to_s}
|
|
2365
|
+
@ds.delete
|
|
2366
|
+
@ds.insert(rs.first)
|
|
2367
|
+
@ds.all.should == rs
|
|
2368
|
+
end
|
|
2369
|
+
@ds.delete
|
|
2370
|
+
end
|
|
2371
|
+
end
|
|
2372
|
+
|
|
2373
|
+
specify 'insert and retrieve interval array values' do
|
|
2374
|
+
@db.create_table!(:items){column :i, 'interval[]'}
|
|
2375
|
+
@ds.insert(Sequel.pg_array(['1 year 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds'], 'interval'))
|
|
2376
|
+
@ds.count.should == 1
|
|
2377
|
+
if @native
|
|
2378
|
+
rs = @ds.all
|
|
2379
|
+
rs.first[:i].is_a?(Sequel::Postgres::PGArray).should be_true
|
|
2380
|
+
rs.first[:i].first.is_a?(ActiveSupport::Duration).should be_true
|
|
2381
|
+
rs.first[:i].first.should == ActiveSupport::Duration.new(31557600 + 2*86400*30 + 3*86400*7 + 4*86400 + 5*3600 + 6*60 + 7, [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]])
|
|
2382
|
+
rs.first[:i].first.parts.sort_by{|k,v| k.to_s}.should == [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]].sort_by{|k,v| k.to_s}
|
|
2383
|
+
@ds.delete
|
|
2384
|
+
@ds.insert(rs.first)
|
|
2385
|
+
@ds.all.should == rs
|
|
2386
|
+
end
|
|
2387
|
+
end
|
|
2388
|
+
|
|
2389
|
+
specify 'use intervals in bound variables' do
|
|
2390
|
+
@db.create_table!(:items){interval :i}
|
|
2391
|
+
@ds.insert('1 year 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds')
|
|
2392
|
+
d = @ds.get(:i)
|
|
2393
|
+
@ds.delete
|
|
2394
|
+
|
|
2395
|
+
@ds.call(:insert, {:i=>d}, {:i=>:$i})
|
|
2396
|
+
@ds.get(:i).should == d
|
|
2397
|
+
@ds.filter(:i=>:$i).call(:first, :i=>d).should == {:i=>d}
|
|
2398
|
+
@ds.filter(:i=>:$i).call(:first, :i=>'0').should == nil
|
|
2399
|
+
@ds.filter(:i=>:$i).call(:delete, :i=>d).should == 1
|
|
2400
|
+
|
|
2401
|
+
@db.create_table!(:items){column :i, 'interval[]'}
|
|
2402
|
+
@ds.call(:insert, {:i=>[d]}, {:i=>:$i})
|
|
2403
|
+
@ds.filter(:i=>:$i).call(:first, :i=>[d]).should == {:i=>[d]}
|
|
2404
|
+
@ds.filter(:i=>:$i).call(:first, :i=>[]).should == nil
|
|
2405
|
+
@ds.filter(:i=>:$i).call(:delete, :i=>[d]).should == 1
|
|
2406
|
+
end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
|
|
2407
|
+
|
|
2408
|
+
specify 'with models' do
|
|
2409
|
+
@db.create_table!(:items) do
|
|
2410
|
+
primary_key :id
|
|
2411
|
+
interval :i
|
|
2412
|
+
end
|
|
2413
|
+
c = Class.new(Sequel::Model(@db[:items]))
|
|
2414
|
+
c.plugin :pg_typecast_on_load, :i, :c unless @native
|
|
2415
|
+
v = c.create(:i=>'1 year 2 mons 25 days 05:06:07').i
|
|
2416
|
+
v.is_a?(ActiveSupport::Duration).should be_true
|
|
2417
|
+
v.should == ActiveSupport::Duration.new(31557600 + 2*86400*30 + 3*86400*7 + 4*86400 + 5*3600 + 6*60 + 7, [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]])
|
|
2418
|
+
v.parts.sort_by{|k,v| k.to_s}.should == [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]].sort_by{|k,v| k.to_s}
|
|
2419
|
+
end
|
|
2420
|
+
end if ((require 'active_support/duration'; require 'active_support/inflector'; require 'active_support/core_ext/string/inflections'; true) rescue false)
|
|
2421
|
+
|
|
2422
|
+
describe 'PostgreSQL row-valued/composite types' do
|
|
2423
|
+
before(:all) do
|
|
2424
|
+
@db = POSTGRES_DB
|
|
2425
|
+
Sequel.extension :pg_array_ops, :pg_row_ops
|
|
2426
|
+
@db.extension :pg_array, :pg_row
|
|
2427
|
+
@ds = @db[:person]
|
|
2428
|
+
|
|
2429
|
+
@db.create_table!(:address) do
|
|
2430
|
+
String :street
|
|
2431
|
+
String :city
|
|
2432
|
+
String :zip
|
|
2433
|
+
end
|
|
2434
|
+
@db.create_table!(:person) do
|
|
2435
|
+
Integer :id
|
|
2436
|
+
address :address
|
|
2437
|
+
end
|
|
2438
|
+
@db.create_table!(:company) do
|
|
2439
|
+
Integer :id
|
|
2440
|
+
column :employees, 'person[]'
|
|
2441
|
+
end
|
|
2442
|
+
@db.register_row_type(:address)
|
|
2443
|
+
@db.register_row_type(:person)
|
|
2444
|
+
@db.register_row_type(:company)
|
|
2445
|
+
|
|
2446
|
+
@native = POSTGRES_DB.adapter_scheme == :postgres
|
|
2447
|
+
end
|
|
2448
|
+
after(:all) do
|
|
2449
|
+
@db.drop_table?(:company, :person, :address)
|
|
2450
|
+
@db.row_types.clear
|
|
2451
|
+
@db.reset_conversion_procs if @native
|
|
2452
|
+
end
|
|
2453
|
+
after do
|
|
2454
|
+
[:company, :person, :address].each{|t| @db[t].delete}
|
|
2455
|
+
end
|
|
2456
|
+
|
|
2457
|
+
specify 'insert and retrieve row types' do
|
|
2458
|
+
@ds.insert(:id=>1, :address=>Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345']))
|
|
2459
|
+
@ds.count.should == 1
|
|
2460
|
+
if @native
|
|
2461
|
+
# Single row valued type
|
|
2462
|
+
rs = @ds.all
|
|
2463
|
+
v = rs.first[:address]
|
|
2464
|
+
v.should_not be_a_kind_of(Hash)
|
|
2465
|
+
v.to_hash.should be_a_kind_of(Hash)
|
|
2466
|
+
v.to_hash.should == {:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345'}
|
|
2467
|
+
@ds.delete
|
|
2468
|
+
@ds.insert(rs.first)
|
|
2469
|
+
@ds.all.should == rs
|
|
2470
|
+
|
|
2471
|
+
# Nested row value type
|
|
2472
|
+
p = @ds.get(:person)
|
|
2473
|
+
p[:id].should == 1
|
|
2474
|
+
p[:address].should == v
|
|
2475
|
+
end
|
|
2476
|
+
end
|
|
2477
|
+
|
|
2478
|
+
specify 'insert and retrieve arrays of row types' do
|
|
2479
|
+
@ds = @db[:company]
|
|
2480
|
+
@ds.insert(:id=>1, :employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345'])])]))
|
|
2481
|
+
@ds.count.should == 1
|
|
2482
|
+
if @native
|
|
2483
|
+
v = @ds.get(:company)
|
|
2484
|
+
v.should_not be_a_kind_of(Hash)
|
|
2485
|
+
v.to_hash.should be_a_kind_of(Hash)
|
|
2486
|
+
v[:id].should == 1
|
|
2487
|
+
employees = v[:employees]
|
|
2488
|
+
employees.should_not be_a_kind_of(Array)
|
|
2489
|
+
employees.to_a.should be_a_kind_of(Array)
|
|
2490
|
+
employees.should == [{:id=>1, :address=>{:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345'}}]
|
|
2491
|
+
@ds.delete
|
|
2492
|
+
@ds.insert(v[:id], v[:employees])
|
|
2493
|
+
@ds.get(:company).should == v
|
|
2494
|
+
end
|
|
2495
|
+
end
|
|
2496
|
+
|
|
2497
|
+
specify 'use row types in bound variables' do
|
|
2498
|
+
@ds.call(:insert, {:address=>Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345'])}, {:address=>:$address, :id=>1})
|
|
2499
|
+
@ds.get(:address).should == {:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345'}
|
|
2500
|
+
@ds.filter(:address=>Sequel.cast(:$address, :address)).call(:first, :address=>Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345']))[:id].should == 1
|
|
2501
|
+
@ds.filter(:address=>Sequel.cast(:$address, :address)).call(:first, :address=>Sequel.pg_row(['123 Sesame St', 'Somewhere', '12356'])).should == nil
|
|
2502
|
+
end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
|
|
2503
|
+
|
|
2504
|
+
specify 'use arrays of row types in bound variables' do
|
|
2505
|
+
@ds = @db[:company]
|
|
2506
|
+
@ds.call(:insert, {:employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345'])])])}, {:employees=>:$employees, :id=>1})
|
|
2507
|
+
@ds.get(:company).should == {:id=>1, :employees=>[{:id=>1, :address=>{:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345'}}]}
|
|
2508
|
+
@ds.filter(:employees=>Sequel.cast(:$employees, 'person[]')).call(:first, :employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345'])])]))[:id].should == 1
|
|
2509
|
+
@ds.filter(:employees=>Sequel.cast(:$employees, 'person[]')).call(:first, :employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row(['123 Sesame St', 'Somewhere', '12356'])])])).should == nil
|
|
2510
|
+
end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
|
|
2511
|
+
|
|
2512
|
+
specify 'operations/functions with pg_row_ops' do
|
|
2513
|
+
@ds.insert(:id=>1, :address=>Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345']))
|
|
2514
|
+
@ds.get(Sequel.pg_row(:address)[:street]).should == '123 Sesame St'
|
|
2515
|
+
@ds.get(Sequel.pg_row(:address)[:city]).should == 'Somewhere'
|
|
2516
|
+
@ds.get(Sequel.pg_row(:address)[:zip]).should == '12345'
|
|
2517
|
+
|
|
2518
|
+
@ds = @db[:company]
|
|
2519
|
+
@ds.insert(:id=>1, :employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345'])])]))
|
|
2520
|
+
@ds.get(Sequel.pg_row(:company)[:id]).should == 1
|
|
2521
|
+
if @native
|
|
2522
|
+
@ds.get(Sequel.pg_row(:company)[:employees]).should == [{:id=>1, :address=>{:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345'}}]
|
|
2523
|
+
@ds.get(Sequel.pg_row(:company)[:employees][1]).should == {:id=>1, :address=>{:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345'}}
|
|
2524
|
+
@ds.get(Sequel.pg_row(:company)[:employees][1][:address]).should == {:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345'}
|
|
2525
|
+
end
|
|
2526
|
+
@ds.get(Sequel.pg_row(:company)[:employees][1][:id]).should == 1
|
|
2527
|
+
@ds.get(Sequel.pg_row(:company)[:employees][1][:address][:street]).should == '123 Sesame St'
|
|
2528
|
+
@ds.get(Sequel.pg_row(:company)[:employees][1][:address][:city]).should == 'Somewhere'
|
|
2529
|
+
@ds.get(Sequel.pg_row(:company)[:employees][1][:address][:zip]).should == '12345'
|
|
2530
|
+
end
|
|
2531
|
+
|
|
2532
|
+
context "#splat and #*" do
|
|
2533
|
+
before(:all) do
|
|
2534
|
+
@db.create_table!(:a){Integer :a}
|
|
2535
|
+
@db.create_table!(:b){a :b; Integer :a}
|
|
2536
|
+
@db.register_row_type(:a)
|
|
2537
|
+
@db.register_row_type(:b)
|
|
2538
|
+
@db[:b].insert(:a=>1, :b=>@db.row_type(:a, [2]))
|
|
2539
|
+
end
|
|
2540
|
+
after(:all) do
|
|
2541
|
+
@db.drop_table?(:b, :a)
|
|
2542
|
+
end
|
|
2543
|
+
|
|
2544
|
+
specify "splat should reference the table type" do
|
|
2545
|
+
@db[:b].select(:a).first.should == {:a=>1}
|
|
2546
|
+
@db[:b].select(:b__a).first.should == {:a=>1}
|
|
2547
|
+
@db[:b].select(Sequel.pg_row(:b)[:a]).first.should == {:a=>2}
|
|
2548
|
+
@db[:b].select(Sequel.pg_row(:b).splat[:a]).first.should == {:a=>1}
|
|
2549
|
+
|
|
2550
|
+
if @native
|
|
2551
|
+
@db[:b].select(:b).first.should == {:b=>{:a=>2}}
|
|
2552
|
+
@db[:b].select(Sequel.pg_row(:b).splat).first.should == {:a=>1, :b=>{:a=>2}}
|
|
2553
|
+
@db[:b].select(Sequel.pg_row(:b).splat(:b)).first.should == {:b=>{:a=>1, :b=>{:a=>2}}}
|
|
2554
|
+
end
|
|
2555
|
+
end
|
|
2556
|
+
|
|
2557
|
+
specify "* should expand the table type into separate columns" do
|
|
2558
|
+
ds = @db[:b].select(Sequel.pg_row(:b).splat(:b)).from_self(:alias=>:t)
|
|
2559
|
+
if @native
|
|
2560
|
+
ds.first.should == {:b=>{:a=>1, :b=>{:a=>2}}}
|
|
2561
|
+
ds.select(Sequel.pg_row(:b).*).first.should == {:a=>1, :b=>{:a=>2}}
|
|
2562
|
+
ds.select(Sequel.pg_row(:b)[:b]).first.should == {:b=>{:a=>2}}
|
|
2563
|
+
ds.select(Sequel.pg_row(:t__b).*).first.should == {:a=>1, :b=>{:a=>2}}
|
|
2564
|
+
ds.select(Sequel.pg_row(:t__b)[:b]).first.should == {:b=>{:a=>2}}
|
|
2565
|
+
end
|
|
2566
|
+
ds.select(Sequel.pg_row(:b)[:a]).first.should == {:a=>1}
|
|
2567
|
+
ds.select(Sequel.pg_row(:t__b)[:a]).first.should == {:a=>1}
|
|
2568
|
+
end
|
|
2569
|
+
end
|
|
2570
|
+
|
|
2571
|
+
context "with models" do
|
|
2572
|
+
before(:all) do
|
|
2573
|
+
class Address < Sequel::Model(:address)
|
|
2574
|
+
plugin :pg_row
|
|
2575
|
+
end
|
|
2576
|
+
class Person < Sequel::Model(:person)
|
|
2577
|
+
plugin :pg_row
|
|
2578
|
+
end
|
|
2579
|
+
class Company < Sequel::Model(:company)
|
|
2580
|
+
plugin :pg_row
|
|
2581
|
+
end
|
|
2582
|
+
@a = Address.new(:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345')
|
|
2583
|
+
@es = Sequel.pg_array([Person.new(:id=>1, :address=>@a)])
|
|
2584
|
+
end
|
|
2585
|
+
after(:all) do
|
|
2586
|
+
Object.send(:remove_const, :Address) rescue nil
|
|
2587
|
+
Object.send(:remove_const, :Person) rescue nil
|
|
2588
|
+
Object.send(:remove_const, :Company) rescue nil
|
|
2589
|
+
end
|
|
2590
|
+
|
|
2591
|
+
specify 'insert and retrieve row types as model objects' do
|
|
2592
|
+
@ds.insert(:id=>1, :address=>@a)
|
|
2593
|
+
@ds.count.should == 1
|
|
2594
|
+
if @native
|
|
2595
|
+
# Single row valued type
|
|
2596
|
+
rs = @ds.all
|
|
2597
|
+
v = rs.first[:address]
|
|
2598
|
+
v.should be_a_kind_of(Address)
|
|
2599
|
+
v.should == @a
|
|
2600
|
+
@ds.delete
|
|
2601
|
+
@ds.insert(rs.first)
|
|
2602
|
+
@ds.all.should == rs
|
|
2603
|
+
|
|
2604
|
+
# Nested row value type
|
|
2605
|
+
p = @ds.get(:person)
|
|
2606
|
+
p.should be_a_kind_of(Person)
|
|
2607
|
+
p.id.should == 1
|
|
2608
|
+
p.address.should be_a_kind_of(Address)
|
|
2609
|
+
p.address.should == @a
|
|
2610
|
+
end
|
|
2611
|
+
end
|
|
2612
|
+
|
|
2613
|
+
specify 'insert and retrieve arrays of row types as model objects' do
|
|
2614
|
+
@ds = @db[:company]
|
|
2615
|
+
@ds.insert(:id=>1, :employees=>@es)
|
|
2616
|
+
@ds.count.should == 1
|
|
2617
|
+
if @native
|
|
2618
|
+
v = @ds.get(:company)
|
|
2619
|
+
v.should be_a_kind_of(Company)
|
|
2620
|
+
v.id.should == 1
|
|
2621
|
+
employees = v[:employees]
|
|
2622
|
+
employees.should_not be_a_kind_of(Array)
|
|
2623
|
+
employees.to_a.should be_a_kind_of(Array)
|
|
2624
|
+
employees.should == @es
|
|
2625
|
+
@ds.delete
|
|
2626
|
+
@ds.insert(v.id, v.employees)
|
|
2627
|
+
@ds.get(:company).should == v
|
|
2628
|
+
end
|
|
2629
|
+
end
|
|
2630
|
+
|
|
2631
|
+
specify 'use model objects in bound variables' do
|
|
2632
|
+
@ds.call(:insert, {:address=>@a}, {:address=>:$address, :id=>1})
|
|
2633
|
+
@ds.get(:address).should == @a
|
|
2634
|
+
@ds.filter(:address=>Sequel.cast(:$address, :address)).call(:first, :address=>@a)[:id].should == 1
|
|
2635
|
+
@ds.filter(:address=>Sequel.cast(:$address, :address)).call(:first, :address=>Address.new(:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12356')).should == nil
|
|
2636
|
+
end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
|
|
2637
|
+
|
|
2638
|
+
specify 'use arrays of model objects in bound variables' do
|
|
2639
|
+
@ds = @db[:company]
|
|
2640
|
+
@ds.call(:insert, {:employees=>@es}, {:employees=>:$employees, :id=>1})
|
|
2641
|
+
@ds.get(:company).should == Company.new(:id=>1, :employees=>@es)
|
|
2642
|
+
@ds.filter(:employees=>Sequel.cast(:$employees, 'person[]')).call(:first, :employees=>@es)[:id].should == 1
|
|
2643
|
+
@ds.filter(:employees=>Sequel.cast(:$employees, 'person[]')).call(:first, :employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row(['123 Sesame St', 'Somewhere', '12356'])])])).should == nil
|
|
2644
|
+
end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
|
|
2645
|
+
|
|
2646
|
+
specify 'model typecasting' do
|
|
2647
|
+
Person.plugin :pg_typecast_on_load, :address unless @native
|
|
2648
|
+
a = Address.new(:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345')
|
|
2649
|
+
o = Person.create(:id=>1, :address=>['123 Sesame St', 'Somewhere', '12345'])
|
|
2650
|
+
o.address.should == a
|
|
2651
|
+
o = Person.create(:id=>1, :address=>{:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345'})
|
|
2652
|
+
o.address.should == a
|
|
2653
|
+
o = Person.create(:id=>1, :address=>a)
|
|
2654
|
+
o.address.should == a
|
|
2655
|
+
|
|
2656
|
+
Company.plugin :pg_typecast_on_load, :employees unless @native
|
|
2657
|
+
e = Person.new(:id=>1, :address=>a)
|
|
2658
|
+
unless @db.adapter_scheme == :jdbc
|
|
2659
|
+
o = Company.create(:id=>1, :employees=>[{:id=>1, :address=>{:street=>'123 Sesame St', :city=>'Somewhere', :zip=>'12345'}}])
|
|
2660
|
+
o.employees.should == [e]
|
|
2661
|
+
o = Company.create(:id=>1, :employees=>[e])
|
|
2662
|
+
o.employees.should == [e]
|
|
2663
|
+
end
|
|
2664
|
+
end
|
|
2665
|
+
end
|
|
1009
2666
|
end
|