sequel 3.21.0 → 3.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +855 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +23 -7
- data/Rakefile +42 -24
- data/bin/sequel +26 -8
- data/doc/active_record.rdoc +1 -0
- data/doc/advanced_associations.rdoc +171 -11
- data/doc/association_basics.rdoc +293 -43
- data/doc/dataset_basics.rdoc +5 -5
- data/doc/dataset_filtering.rdoc +8 -0
- data/doc/mass_assignment.rdoc +56 -0
- data/doc/migration.rdoc +85 -552
- data/doc/model_hooks.rdoc +78 -27
- data/doc/object_model.rdoc +541 -0
- data/doc/opening_databases.rdoc +97 -50
- data/doc/prepared_statements.rdoc +40 -14
- data/doc/querying.rdoc +28 -4
- data/doc/reflection.rdoc +30 -4
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/release_notes/3.31.0.txt +146 -0
- data/doc/release_notes/3.32.0.txt +202 -0
- data/doc/release_notes/3.33.0.txt +157 -0
- data/doc/release_notes/3.34.0.txt +671 -0
- data/doc/release_notes/3.35.0.txt +144 -0
- data/doc/release_notes/3.36.0.txt +245 -0
- data/doc/schema_modification.rdoc +572 -0
- data/doc/sharding.rdoc +95 -3
- data/doc/testing.rdoc +129 -0
- data/doc/thread_safety.rdoc +17 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/virtual_rows.rdoc +51 -2
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +17 -9
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +17 -11
- data/lib/sequel/adapters/do/postgres.rb +1 -57
- data/lib/sequel/adapters/do/sqlite.rb +0 -10
- data/lib/sequel/adapters/do.rb +18 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +453 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/db2.rb +47 -0
- data/lib/sequel/adapters/jdbc/derby.rb +287 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
- data/lib/sequel/adapters/jdbc/h2.rb +73 -31
- data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
- data/lib/sequel/adapters/jdbc/informix.rb +21 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
- data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
- data/lib/sequel/adapters/jdbc/progress.rb +21 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
- data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +265 -94
- data/lib/sequel/adapters/mock.rb +361 -0
- data/lib/sequel/adapters/mysql.rb +118 -222
- data/lib/sequel/adapters/mysql2.rb +58 -57
- data/lib/sequel/adapters/odbc/mssql.rb +15 -7
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +349 -53
- data/lib/sequel/adapters/postgres.rb +356 -107
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +342 -0
- data/lib/sequel/adapters/shared/firebird.rb +221 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +335 -118
- data/lib/sequel/adapters/shared/mysql.rb +340 -76
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
- data/lib/sequel/adapters/shared/oracle.rb +243 -45
- data/lib/sequel/adapters/shared/postgres.rb +524 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +265 -89
- data/lib/sequel/adapters/sqlite.rb +101 -39
- data/lib/sequel/adapters/swift/mysql.rb +9 -12
- data/lib/sequel/adapters/swift/postgres.rb +9 -74
- data/lib/sequel/adapters/swift/sqlite.rb +8 -6
- data/lib/sequel/adapters/swift.rb +8 -7
- data/lib/sequel/adapters/tinytds.rb +161 -21
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
- data/lib/sequel/ast_transformer.rb +194 -0
- data/lib/sequel/connection_pool/sharded_single.rb +11 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
- data/lib/sequel/connection_pool/single.rb +5 -0
- data/lib/sequel/connection_pool/threaded.rb +38 -14
- data/lib/sequel/connection_pool.rb +10 -2
- data/lib/sequel/core.rb +118 -11
- data/lib/sequel/database/connecting.rb +38 -28
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +63 -2
- data/lib/sequel/database/logging.rb +7 -2
- data/lib/sequel/database/misc.rb +150 -46
- data/lib/sequel/database/query.rb +215 -65
- data/lib/sequel/database/schema_generator.rb +64 -20
- data/lib/sequel/database/schema_methods.rb +212 -54
- data/lib/sequel/dataset/actions.rb +322 -85
- data/lib/sequel/dataset/features.rb +96 -8
- data/lib/sequel/dataset/graph.rb +38 -11
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +12 -7
- data/lib/sequel/dataset/prepared_statements.rb +68 -30
- data/lib/sequel/dataset/query.rb +237 -44
- data/lib/sequel/dataset/sql.rb +698 -278
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/_pretty_table.rb +79 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
- data/lib/sequel/extensions/migration.rb +97 -11
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/null_dataset.rb +90 -0
- data/lib/sequel/extensions/pg_array.rb +460 -0
- data/lib/sequel/extensions/pg_array_ops.rb +220 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
- data/lib/sequel/extensions/pg_hstore.rb +296 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
- data/lib/sequel/extensions/pg_inet.rb +89 -0
- data/lib/sequel/extensions/pg_json.rb +178 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
- data/lib/sequel/extensions/pretty_table.rb +5 -71
- data/lib/sequel/extensions/query.rb +1 -1
- data/lib/sequel/extensions/query_literals.rb +79 -0
- data/lib/sequel/extensions/schema_caching.rb +76 -0
- data/lib/sequel/extensions/schema_dumper.rb +251 -37
- data/lib/sequel/extensions/select_remove.rb +35 -0
- data/lib/sequel/extensions/server_block.rb +139 -0
- data/lib/sequel/extensions/sql_expr.rb +4 -110
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +1187 -393
- data/lib/sequel/model/base.rb +578 -170
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model.rb +30 -11
- data/lib/sequel/no_core_ext.rb +2 -0
- data/lib/sequel/plugins/active_model.rb +13 -1
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/caching.rb +25 -18
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/composition.rb +44 -12
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/dirty.rb +214 -0
- data/lib/sequel/plugins/eager_each.rb +59 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/plugins/identity_map.rb +123 -8
- data/lib/sequel/plugins/instance_filters.rb +10 -0
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +58 -6
- data/lib/sequel/plugins/list.rb +12 -2
- data/lib/sequel/plugins/many_through_many.rb +79 -28
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
- data/lib/sequel/plugins/nested_attributes.rb +139 -63
- data/lib/sequel/plugins/optimistic_locking.rb +8 -0
- data/lib/sequel/plugins/prepared_statements.rb +167 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/schema.rb +7 -2
- data/lib/sequel/plugins/serialization.rb +98 -49
- data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
- data/lib/sequel/plugins/static_cache.rb +99 -0
- data/lib/sequel/plugins/subclasses.rb +10 -2
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +526 -59
- data/lib/sequel/timezones.rb +57 -38
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/firebird_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +142 -40
- data/spec/adapters/mysql_spec.rb +380 -234
- data/spec/adapters/oracle_spec.rb +67 -77
- data/spec/adapters/postgres_spec.rb +1050 -212
- data/spec/adapters/spec_helper.rb +8 -5
- data/spec/adapters/sqlite_spec.rb +139 -27
- data/spec/core/connection_pool_spec.rb +196 -93
- data/spec/core/database_spec.rb +879 -510
- data/spec/core/dataset_spec.rb +1415 -1086
- data/spec/core/expression_filters_spec.rb +554 -109
- data/spec/core/mock_adapter_spec.rb +449 -0
- data/spec/core/object_graph_spec.rb +86 -114
- data/spec/core/schema_generator_spec.rb +4 -4
- data/spec/core/schema_spec.rb +482 -38
- data/spec/core/spec_helper.rb +6 -48
- data/spec/extensions/active_model_spec.rb +13 -0
- data/spec/extensions/arbitrary_servers_spec.rb +110 -0
- data/spec/extensions/association_autoreloading_spec.rb +18 -10
- data/spec/extensions/association_dependencies_spec.rb +15 -25
- data/spec/extensions/association_pks_spec.rb +66 -32
- data/spec/extensions/association_proxies_spec.rb +4 -4
- data/spec/extensions/caching_spec.rb +47 -51
- data/spec/extensions/class_table_inheritance_spec.rb +31 -83
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/dirty_spec.rb +155 -0
- data/spec/extensions/eager_each_spec.rb +34 -0
- data/spec/extensions/force_encoding_spec.rb +4 -2
- data/spec/extensions/hook_class_methods_spec.rb +69 -92
- data/spec/extensions/identity_map_spec.rb +131 -22
- data/spec/extensions/inflector_spec.rb +0 -4
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +72 -0
- data/spec/extensions/json_serializer_spec.rb +49 -6
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +48 -39
- data/spec/extensions/many_through_many_spec.rb +283 -250
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
- data/spec/extensions/migration_spec.rb +140 -35
- data/spec/extensions/named_timezones_spec.rb +29 -11
- data/spec/extensions/nested_attributes_spec.rb +251 -89
- data/spec/extensions/null_dataset_spec.rb +85 -0
- data/spec/extensions/optimistic_locking_spec.rb +24 -21
- data/spec/extensions/pg_array_ops_spec.rb +105 -0
- data/spec/extensions/pg_array_spec.rb +196 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
- data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
- data/spec/extensions/pg_hstore_spec.rb +195 -0
- data/spec/extensions/pg_inet_spec.rb +44 -0
- data/spec/extensions/pg_json_spec.rb +101 -0
- data/spec/extensions/pg_statement_cache_spec.rb +209 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
- data/spec/extensions/prepared_statements_spec.rb +87 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +7 -6
- data/spec/extensions/query_literals_spec.rb +168 -0
- data/spec/extensions/query_spec.rb +6 -0
- data/spec/extensions/rcte_tree_spec.rb +50 -43
- data/spec/extensions/schema_caching_spec.rb +41 -0
- data/spec/extensions/schema_dumper_spec.rb +429 -22
- data/spec/extensions/schema_spec.rb +25 -21
- data/spec/extensions/select_remove_spec.rb +38 -0
- data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
- data/spec/extensions/serialization_spec.rb +85 -43
- data/spec/extensions/server_block_spec.rb +90 -0
- data/spec/extensions/sharding_spec.rb +69 -143
- data/spec/extensions/single_table_inheritance_spec.rb +23 -29
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +22 -61
- data/spec/extensions/sql_expr_spec.rb +29 -60
- data/spec/extensions/static_cache_spec.rb +145 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +4 -7
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/typecast_on_load_spec.rb +9 -6
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +30 -51
- data/spec/extensions/validation_helpers_spec.rb +16 -35
- data/spec/extensions/xml_serializer_spec.rb +29 -4
- data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
- data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
- data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
- data/spec/integration/associations_test.rb +659 -21
- data/spec/integration/database_test.rb +9 -3
- data/spec/integration/dataset_test.rb +605 -91
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/migrator_test.rb +2 -3
- data/spec/integration/model_test.rb +81 -2
- data/spec/integration/plugin_test.rb +454 -143
- data/spec/integration/prepared_statement_test.rb +263 -128
- data/spec/integration/schema_test.rb +309 -43
- data/spec/integration/spec_helper.rb +40 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +18 -4
- data/spec/model/association_reflection_spec.rb +201 -7
- data/spec/model/associations_spec.rb +1067 -661
- data/spec/model/base_spec.rb +329 -95
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +677 -676
- data/spec/model/hooks_spec.rb +349 -67
- data/spec/model/model_spec.rb +240 -168
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +490 -232
- data/spec/model/spec_helper.rb +14 -71
- data/spec/model/validations_spec.rb +11 -0
- data/spec/rcov.opts +2 -0
- metadata +178 -80
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe "null_dataset extension" do
|
|
4
|
+
before do
|
|
5
|
+
@db = Sequel::mock(:fetch=>{:id=>1}, :autoid=>1, :numrows=>1, :columns=>[:id])
|
|
6
|
+
@ds = @db[:table].nullify
|
|
7
|
+
@i = 0
|
|
8
|
+
@pr = proc{|*a| @i += 1}
|
|
9
|
+
end
|
|
10
|
+
after do
|
|
11
|
+
@db.sqls.should == [] unless @skip_check
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "should make each be a noop" do
|
|
15
|
+
@ds.each(&@pr).should equal(@ds)
|
|
16
|
+
@i.should == 0
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "should make fetch_rows be a noop" do
|
|
20
|
+
@ds.fetch_rows("SELECT 1", &@pr).should == nil
|
|
21
|
+
@i.should == 0
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "should make insert be a noop" do
|
|
25
|
+
@ds.insert(1).should == nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "should make update be a noop" do
|
|
29
|
+
@ds.update(:a=>1).should == 0
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should make delete be a noop" do
|
|
33
|
+
@ds.delete.should == 0
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "should make truncate be a noop" do
|
|
37
|
+
@ds.truncate.should == nil
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "should make execute_* be a noop" do
|
|
41
|
+
@ds.send(:execute_ddl,'FOO').should == nil
|
|
42
|
+
@ds.send(:execute_insert,'FOO').should == nil
|
|
43
|
+
@ds.send(:execute_dui,'FOO').should == nil
|
|
44
|
+
@ds.send(:execute,'FOO').should == nil
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should have working columns" do
|
|
48
|
+
@skip_check = true
|
|
49
|
+
@ds.columns.should == [:id]
|
|
50
|
+
@db.sqls.should == ['SELECT * FROM table LIMIT 1']
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should have count return 0" do
|
|
54
|
+
@ds.count.should == 0
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "should have empty return true" do
|
|
58
|
+
@ds.empty?.should == true
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "should make import a noop" do
|
|
62
|
+
@ds.import([:id], [[1], [2], [3]]).should == nil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "should have nullify method returned modified receiver" do
|
|
66
|
+
@skip_check = true
|
|
67
|
+
ds = @db[:table]
|
|
68
|
+
ds.nullify.should_not equal(ds)
|
|
69
|
+
ds.each(&@pr)
|
|
70
|
+
@db.sqls.should == ['SELECT * FROM table']
|
|
71
|
+
@i.should == 1
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "should have null! method modify receiver" do
|
|
75
|
+
ds = @db[:table]
|
|
76
|
+
ds.nullify!.should equal(ds)
|
|
77
|
+
ds.each(&@pr)
|
|
78
|
+
@i.should == 0
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "should work with method chaining" do
|
|
82
|
+
@ds.where(:a=>1).select(:b).each(&@pr)
|
|
83
|
+
@i.should == 0
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -6,11 +6,8 @@ describe "optimistic_locking plugin" do
|
|
|
6
6
|
end
|
|
7
7
|
h = {1=>{:id=>1, :name=>'John', :lock_version=>2}}
|
|
8
8
|
lv = @lv = "lock_version"
|
|
9
|
-
@c.dataset.
|
|
10
|
-
|
|
11
|
-
@c.dataset.meta_def(:lv){lv}
|
|
12
|
-
@c.dataset.meta_def(:update) do |opts|
|
|
13
|
-
case update_sql(opts)
|
|
9
|
+
@c.instance_dataset.numrows = @c.dataset.numrows = proc do |sql|
|
|
10
|
+
case sql
|
|
14
11
|
when /UPDATE people SET (name|#{lv}) = ('Jim'|'Bob'|\d+), (?:name|#{lv}) = ('Jim'|'Bob'|\d+) WHERE \(\(id = (\d+)\) AND \(#{lv} = (\d+)\)\)/
|
|
15
12
|
name, nlv = $1 == 'name' ? [$2, $3] : [$3, $2]
|
|
16
13
|
m = h[$4.to_i]
|
|
@@ -28,20 +25,6 @@ describe "optimistic_locking plugin" do
|
|
|
28
25
|
else
|
|
29
26
|
0
|
|
30
27
|
end
|
|
31
|
-
else
|
|
32
|
-
puts update_sql(opts)
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
@c.dataset.instance_eval do
|
|
36
|
-
def fetch_rows(sql)
|
|
37
|
-
m = h[1].dup
|
|
38
|
-
v = m.delete(:lock_version)
|
|
39
|
-
m[lv.to_sym] = v
|
|
40
|
-
yield(m)
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
@c.dataset.meta_def(:delete) do
|
|
44
|
-
case delete_sql
|
|
45
28
|
when /DELETE FROM people WHERE \(\(id = (\d+)\) AND \(#{lv} = (\d+)\)\)/
|
|
46
29
|
m = h[$1.to_i]
|
|
47
30
|
if m && m[lv.to_sym] == $2.to_i
|
|
@@ -51,9 +34,15 @@ describe "optimistic_locking plugin" do
|
|
|
51
34
|
0
|
|
52
35
|
end
|
|
53
36
|
else
|
|
54
|
-
puts
|
|
37
|
+
puts sql
|
|
55
38
|
end
|
|
56
39
|
end
|
|
40
|
+
@c.instance_dataset._fetch = @c.dataset._fetch = proc do |sql|
|
|
41
|
+
m = h[1].dup
|
|
42
|
+
v = m.delete(:lock_version)
|
|
43
|
+
m[lv.to_sym] = v
|
|
44
|
+
m
|
|
45
|
+
end
|
|
57
46
|
@c.columns :id, :name, :lock_version
|
|
58
47
|
@c.plugin :optimistic_locking
|
|
59
48
|
end
|
|
@@ -106,6 +95,20 @@ describe "optimistic_locking plugin" do
|
|
|
106
95
|
proc{p2.update(:name=>'Bob')}.should raise_error(Sequel::Plugins::OptimisticLocking::Error)
|
|
107
96
|
end
|
|
108
97
|
|
|
98
|
+
specify "should work correctly if attempting to refresh and save again after a failed save" do
|
|
99
|
+
p1 = @c[1]
|
|
100
|
+
p2 = @c[1]
|
|
101
|
+
p1.update(:name=>'Jim')
|
|
102
|
+
begin
|
|
103
|
+
p2.update(:name=>'Bob')
|
|
104
|
+
rescue Sequel::Plugins::OptimisticLocking::Error
|
|
105
|
+
p2.refresh
|
|
106
|
+
@c.db.sqls
|
|
107
|
+
proc{p2.update(:name=>'Bob')}.should_not raise_error
|
|
108
|
+
end
|
|
109
|
+
@c.db.sqls.first.should =~ /UPDATE people SET (name = 'Bob', lock_version = 4|lock_version = 4, name = 'Bob') WHERE \(\(id = 1\) AND \(lock_version = 3\)\)/
|
|
110
|
+
end
|
|
111
|
+
|
|
109
112
|
specify "should increment the lock column when #modified! even if no columns are changed" do
|
|
110
113
|
p1 = @c[1]
|
|
111
114
|
p1.modified!
|
|
@@ -115,7 +118,7 @@ describe "optimistic_locking plugin" do
|
|
|
115
118
|
end
|
|
116
119
|
|
|
117
120
|
specify "should not increment the lock column when the update fails" do
|
|
118
|
-
@c.
|
|
121
|
+
@c.instance_dataset.meta_def(:update) { raise Exception }
|
|
119
122
|
p1 = @c[1]
|
|
120
123
|
p1.modified!
|
|
121
124
|
lv = p1.lock_version
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe "Sequel::Postgres::ArrayOp" do
|
|
4
|
+
# Attempt to load pg_array first to test PGArray -> ArrayOp code
|
|
5
|
+
pg_array_loaded = begin
|
|
6
|
+
Sequel.extension :pg_array
|
|
7
|
+
true
|
|
8
|
+
rescue LoadError
|
|
9
|
+
false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
Sequel.extension :pg_array_ops
|
|
13
|
+
|
|
14
|
+
before do
|
|
15
|
+
@db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
|
|
16
|
+
@a = :a.pg_array
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "should support the standard mathematical operators" do
|
|
20
|
+
@db.literal(@a < @a).should == "(a < a)"
|
|
21
|
+
@db.literal(@a <= @a).should == "(a <= a)"
|
|
22
|
+
@db.literal(@a > @a).should == "(a > a)"
|
|
23
|
+
@db.literal(@a >= @a).should == "(a >= a)"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "#[] should support subscript access" do
|
|
27
|
+
@db.literal(@a[1]).should == "a[1]"
|
|
28
|
+
@db.literal(@a[1][2]).should == "a[1][2]"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "#any should use the ANY method" do
|
|
32
|
+
@db.literal(1=>@a.any).should == "(1 = ANY(a))"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "#all should use the ALL method" do
|
|
36
|
+
@db.literal(1=>@a.all).should == "(1 = ALL(a))"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "#contains should use the @> operator" do
|
|
40
|
+
@db.literal(@a.contains(:b)).should == "(a @> b)"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "#contained_by should use the <@ operator" do
|
|
44
|
+
@db.literal(@a.contained_by(:b)).should == "(a <@ b)"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "#overlaps should use the && operator" do
|
|
48
|
+
@db.literal(@a.overlaps(:b)).should == "(a && b)"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "#push/concat should use the || operator in append mode" do
|
|
52
|
+
@db.literal(@a.push(:b)).should == "(a || b)"
|
|
53
|
+
@db.literal(@a.concat(:b)).should == "(a || b)"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "#unshift should use the || operator in prepend mode" do
|
|
57
|
+
@db.literal(@a.unshift(:b)).should == "(b || a)"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "#dims should use the array_dims function" do
|
|
61
|
+
@db.literal(@a.dims).should == "array_dims(a)"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "#length should use the array_length function" do
|
|
65
|
+
@db.literal(@a.length).should == "array_length(a, 1)"
|
|
66
|
+
@db.literal(@a.length(2)).should == "array_length(a, 2)"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "#length should use the array_lower function" do
|
|
70
|
+
@db.literal(@a.lower).should == "array_lower(a, 1)"
|
|
71
|
+
@db.literal(@a.lower(2)).should == "array_lower(a, 2)"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "#to_string/join should use the array_to_string function" do
|
|
75
|
+
@db.literal(@a.to_string).should == "array_to_string(a, '', NULL)"
|
|
76
|
+
@db.literal(@a.join).should == "array_to_string(a, '', NULL)"
|
|
77
|
+
@db.literal(@a.join(':')).should == "array_to_string(a, ':', NULL)"
|
|
78
|
+
@db.literal(@a.join(':', '*')).should == "array_to_string(a, ':', '*')"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "#unnest should use the unnest function" do
|
|
82
|
+
@db.literal(@a.unnest).should == "unnest(a)"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "#pg_array should return self" do
|
|
86
|
+
@a.pg_array.should equal(@a)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "should be able to turn expressions into array ops using pg_array" do
|
|
90
|
+
@db.literal(:a.qualify(:b).pg_array.push(3)).should == "(b.a || 3)"
|
|
91
|
+
@db.literal(:a.sql_function(:b).pg_array.push(3)).should == "(a(b) || 3)"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "should be able to turn literal strings into array ops using pg_array" do
|
|
95
|
+
@db.literal('a'.lit.pg_array.unnest).should == "unnest(a)"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "should be able to turn symbols into array ops using pg_array" do
|
|
99
|
+
@db.literal(:a.pg_array.unnest).should == "unnest(a)"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "should allow transforming PGArray instances into ArrayOp instances" do
|
|
103
|
+
@db.literal([1,2].pg_array.op.push(3)).should == "(ARRAY[1,2] || 3)"
|
|
104
|
+
end if pg_array_loaded
|
|
105
|
+
end
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
Sequel.extension :pg_array
|
|
5
|
+
rescue LoadError => e
|
|
6
|
+
skip_warn "can't load pg_array extension (#{e.class}: #{e})"
|
|
7
|
+
else
|
|
8
|
+
describe "pg_array extension" do
|
|
9
|
+
before do
|
|
10
|
+
@db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
|
|
11
|
+
@db.extend(Module.new{def bound_variable_arg(arg, conn) arg end})
|
|
12
|
+
@m = Sequel::Postgres
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should parse single dimensional text arrays" do
|
|
16
|
+
@m::PGStringArray.parse("{a}").to_a.first.should be_a_kind_of(String)
|
|
17
|
+
@m::PGStringArray.parse("{}").to_a.should == []
|
|
18
|
+
@m::PGStringArray.parse("{a}").to_a.should == ['a']
|
|
19
|
+
@m::PGStringArray.parse('{"a b"}').to_a.should == ['a b']
|
|
20
|
+
@m::PGStringArray.parse('{a,b}').to_a.should == ['a', 'b']
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "should parse multi-dimensional text arrays" do
|
|
24
|
+
@m::PGStringArray.parse("{{}}").to_a.should == [[]]
|
|
25
|
+
@m::PGStringArray.parse("{{a},{b}}").to_a.should == [['a'], ['b']]
|
|
26
|
+
@m::PGStringArray.parse('{{"a b"},{c}}').to_a.should == [['a b'], ['c']]
|
|
27
|
+
@m::PGStringArray.parse('{{{a},{b}},{{c},{d}}}').to_a.should == [[['a'], ['b']], [['c'], ['d']]]
|
|
28
|
+
@m::PGStringArray.parse('{{{a,e},{b,f}},{{c,g},{d,h}}}').to_a.should == [[['a', 'e'], ['b', 'f']], [['c', 'g'], ['d', 'h']]]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "should parse text arrays with embedded deliminaters" do
|
|
32
|
+
@m::PGStringArray.parse('{{"{},","\\",\\,\\\\\\"\\""}}').to_a.should == [['{},', '",,\\""']]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "should parse single dimensional integer arrays" do
|
|
36
|
+
@m::PGIntegerArray.parse("{1}").to_a.first.should be_a_kind_of(Integer)
|
|
37
|
+
@m::PGIntegerArray.parse("{}").to_a.should == []
|
|
38
|
+
@m::PGIntegerArray.parse("{1}").to_a.should == [1]
|
|
39
|
+
@m::PGIntegerArray.parse('{2,3}').to_a.should == [2, 3]
|
|
40
|
+
@m::PGIntegerArray.parse('{3,4,5}').to_a.should == [3, 4, 5]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should parse multiple dimensional integer arrays" do
|
|
44
|
+
@m::PGIntegerArray.parse("{{}}").to_a.should == [[]]
|
|
45
|
+
@m::PGIntegerArray.parse("{{1}}").to_a.should == [[1]]
|
|
46
|
+
@m::PGIntegerArray.parse('{{2},{3}}').to_a.should == [[2], [3]]
|
|
47
|
+
@m::PGIntegerArray.parse('{{{1,2},{3,4}},{{5,6},{7,8}}}').to_a.should == [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "should parse single dimensional float arrays" do
|
|
51
|
+
@m::PGFloatArray.parse("{}").to_a.should == []
|
|
52
|
+
@m::PGFloatArray.parse("{1.5}").to_a.should == [1.5]
|
|
53
|
+
@m::PGFloatArray.parse('{2.5,3.5}').to_a.should == [2.5, 3.5]
|
|
54
|
+
@m::PGFloatArray.parse('{3.5,4.5,5.5}').to_a.should == [3.5, 4.5, 5.5]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "should parse multiple dimensional float arrays" do
|
|
58
|
+
@m::PGFloatArray.parse("{{}}").to_a.should == [[]]
|
|
59
|
+
@m::PGFloatArray.parse("{{1.5}}").to_a.should == [[1.5]]
|
|
60
|
+
@m::PGFloatArray.parse('{{2.5},{3.5}}').to_a.should == [[2.5], [3.5]]
|
|
61
|
+
@m::PGFloatArray.parse('{{{1.5,2.5},{3.5,4.5}},{{5.5,6.5},{7.5,8.5}}}').to_a.should == [[[1.5, 2.5], [3.5, 4.5]], [[5.5, 6.5], [7.5, 8.5]]]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "should parse integers in float arrays as floats" do
|
|
65
|
+
@m::PGFloatArray.parse("{1}").to_a.first.should be_a_kind_of(Float)
|
|
66
|
+
@m::PGFloatArray.parse("{1}").to_a.should == [1.0]
|
|
67
|
+
@m::PGFloatArray.parse('{{{1,2},{3,4}},{{5,6},{7,8}}}').to_a.should == [[[1.0, 2.0], [3.0, 4.0]], [[5.0, 6.0], [7.0, 8.0]]]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "should parse single dimensional decimal arrays" do
|
|
71
|
+
@m::PGDecimalArray.parse("{}").to_a.should == []
|
|
72
|
+
@m::PGDecimalArray.parse("{1.5}").to_a.should == [BigDecimal.new('1.5')]
|
|
73
|
+
@m::PGDecimalArray.parse('{2.5,3.5}').to_a.should == [BigDecimal.new('2.5'), BigDecimal.new('3.5')]
|
|
74
|
+
@m::PGDecimalArray.parse('{3.5,4.5,5.5}').to_a.should == [BigDecimal.new('3.5'), BigDecimal.new('4.5'), BigDecimal.new('5.5')]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "should parse multiple dimensional decimal arrays" do
|
|
78
|
+
@m::PGDecimalArray.parse("{{}}").to_a.should == [[]]
|
|
79
|
+
@m::PGDecimalArray.parse("{{1.5}}").to_a.should == [[BigDecimal.new('1.5')]]
|
|
80
|
+
@m::PGDecimalArray.parse('{{2.5},{3.5}}').to_a.should == [[BigDecimal.new('2.5')], [BigDecimal.new('3.5')]]
|
|
81
|
+
@m::PGDecimalArray.parse('{{{1.5,2.5},{3.5,4.5}},{{5.5,6.5},{7.5,8.5}}}').to_a.should == [[[BigDecimal.new('1.5'), BigDecimal.new('2.5')], [BigDecimal.new('3.5'), BigDecimal.new('4.5')]], [[BigDecimal.new('5.5'), BigDecimal.new('6.5')], [BigDecimal.new('7.5'), BigDecimal.new('8.5')]]]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "should parse decimal values with arbitrary precision" do
|
|
85
|
+
@m::PGDecimalArray.parse("{1.000000000000000000005}").to_a.should == [BigDecimal.new('1.000000000000000000005')]
|
|
86
|
+
@m::PGDecimalArray.parse("{{1.000000000000000000005,2.000000000000000000005},{3.000000000000000000005,4.000000000000000000005}}").to_a.should == [[BigDecimal.new('1.000000000000000000005'), BigDecimal.new('2.000000000000000000005')], [BigDecimal.new('3.000000000000000000005'), BigDecimal.new('4.000000000000000000005')]]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "should parse integers in decimal arrays as BigDecimals" do
|
|
90
|
+
@m::PGDecimalArray.parse("{1}").to_a.first.should be_a_kind_of(BigDecimal)
|
|
91
|
+
@m::PGDecimalArray.parse("{1}").to_a.should == [BigDecimal.new('1')]
|
|
92
|
+
@m::PGDecimalArray.parse('{{{1,2},{3,4}},{{5,6},{7,8}}}').to_a.should == [[[BigDecimal.new('1'), BigDecimal.new('2')], [BigDecimal.new('3'), BigDecimal.new('4')]], [[BigDecimal.new('5'), BigDecimal.new('6')], [BigDecimal.new('7'), BigDecimal.new('8')]]]
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "should parse arrays with NULL values" do
|
|
96
|
+
[@m::PGStringArray, @m::PGIntegerArray, @m::PGFloatArray, @m::PGDecimalArray].each do |c|
|
|
97
|
+
c.parse("{NULL}").should == [nil]
|
|
98
|
+
c.parse("{NULL,NULL}").should == [nil,nil]
|
|
99
|
+
c.parse("{{NULL,NULL},{NULL,NULL}}").should == [[nil,nil],[nil,nil]]
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'should parse arrays with "NULL" values' do
|
|
104
|
+
@m::PGStringArray.parse('{NULL,"NULL",NULL}').to_a.should == [nil, "NULL", nil]
|
|
105
|
+
@m::PGStringArray.parse('{NULLA,"NULL",NULL}').to_a.should == ["NULLA", "NULL", nil]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it "should literalize arrays without types correctly" do
|
|
109
|
+
@db.literal(@m::PGArray.new([])).should == 'ARRAY[]'
|
|
110
|
+
@db.literal(@m::PGArray.new([1])).should == 'ARRAY[1]'
|
|
111
|
+
@db.literal(@m::PGArray.new([nil])).should == 'ARRAY[NULL]'
|
|
112
|
+
@db.literal(@m::PGArray.new([nil, 1])).should == 'ARRAY[NULL,1]'
|
|
113
|
+
@db.literal(@m::PGArray.new([1.0, 2.5])).should == 'ARRAY[1.0,2.5]'
|
|
114
|
+
@db.literal(@m::PGArray.new([BigDecimal.new('1'), BigDecimal.new('2.000000000000000000005')])).should == 'ARRAY[1.0,2.000000000000000000005]'
|
|
115
|
+
@db.literal(@m::PGArray.new([nil, "NULL"])).should == "ARRAY[NULL,'NULL']"
|
|
116
|
+
@db.literal(@m::PGArray.new([nil, "{},[]'\""])).should == "ARRAY[NULL,'{},[]''\"']"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "should literalize multidimensional arrays correctly" do
|
|
120
|
+
@db.literal(@m::PGArray.new([[]])).should == 'ARRAY[[]]'
|
|
121
|
+
@db.literal(@m::PGArray.new([[1, 2]])).should == 'ARRAY[[1,2]]'
|
|
122
|
+
@db.literal(@m::PGArray.new([[3], [5]])).should == 'ARRAY[[3],[5]]'
|
|
123
|
+
@db.literal(@m::PGArray.new([[[1.0]], [[2.5]]])).should == 'ARRAY[[[1.0]],[[2.5]]]'
|
|
124
|
+
@db.literal(@m::PGArray.new([[[["NULL"]]]])).should == "ARRAY[[[['NULL']]]]"
|
|
125
|
+
@db.literal(@m::PGArray.new([["a", "b"], ["{},[]'\"", nil]])).should == "ARRAY[['a','b'],['{},[]''\"',NULL]]"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it "should literalize with types correctly" do
|
|
129
|
+
@db.literal(@m::PGArray.new([1], :int4)).should == 'ARRAY[1]::int4[]'
|
|
130
|
+
@db.literal(@m::PGArray.new([nil], :text)).should == 'ARRAY[NULL]::text[]'
|
|
131
|
+
@db.literal(@m::PGArray.new([nil, 1], :int8)).should == 'ARRAY[NULL,1]::int8[]'
|
|
132
|
+
@db.literal(@m::PGArray.new([1.0, 2.5], :real)).should == 'ARRAY[1.0,2.5]::real[]'
|
|
133
|
+
@db.literal(@m::PGArray.new([BigDecimal.new('1'), BigDecimal.new('2.000000000000000000005')], :decimal)).should == 'ARRAY[1.0,2.000000000000000000005]::decimal[]'
|
|
134
|
+
@db.literal(@m::PGArray.new([nil, "NULL"], :varchar)).should == "ARRAY[NULL,'NULL']::varchar[]"
|
|
135
|
+
@db.literal(@m::PGArray.new([nil, "{},[]'\""], :"varchar(255)")).should == "ARRAY[NULL,'{},[]''\"']::varchar(255)[]"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it "should have reasonable default types" do
|
|
139
|
+
@db.literal(@m::PGArray.new([])).should == 'ARRAY[]'
|
|
140
|
+
@db.literal(@m::PGIntegerArray.new([])).should == 'ARRAY[]::int4[]'
|
|
141
|
+
@db.literal(@m::PGFloatArray.new([])).should == 'ARRAY[]::double precision[]'
|
|
142
|
+
@db.literal(@m::PGStringArray.new([])).should == 'ARRAY[]::text[]'
|
|
143
|
+
@db.literal(@m::PGDecimalArray.new([])).should == 'ARRAY[]::decimal[]'
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "should use varchar type for char arrays without length" do
|
|
147
|
+
@db.literal(@m::PGStringArray.new([], :char)).should == 'ARRAY[]::varchar[]'
|
|
148
|
+
@db.literal(@m::PGStringArray.new([], 'char')).should == 'ARRAY[]::varchar[]'
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it "should use given type for char arrays with length" do
|
|
152
|
+
@db.literal(@m::PGStringArray.new([], :'char(2)')).should == 'ARRAY[]::char(2)[]'
|
|
153
|
+
@db.literal(@m::PGStringArray.new([], 'char(1)')).should == 'ARRAY[]::char(1)[]'
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it "should have Array#pg_array method for easy PGArray creation" do
|
|
157
|
+
@db.literal([1].pg_array).should == 'ARRAY[1]'
|
|
158
|
+
@db.literal([1, 2].pg_array(:int4)).should == 'ARRAY[1,2]::int4[]'
|
|
159
|
+
@db.literal([[[1], [2]], [[3], [4]]].pg_array(:real)).should == 'ARRAY[[[1],[2]],[[3],[4]]]::real[]'
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it "should support using arrays as bound variables" do
|
|
163
|
+
@db.extend Sequel::Postgres::PGArray::DatabaseMethods
|
|
164
|
+
@db.bound_variable_arg(1, nil).should == 1
|
|
165
|
+
@db.bound_variable_arg([1,2].pg_array, nil).should == '{1,2}'
|
|
166
|
+
@db.bound_variable_arg([1,2], nil).should == '{1,2}'
|
|
167
|
+
@db.bound_variable_arg([[1,2]], nil).should == '{{1,2}}'
|
|
168
|
+
@db.bound_variable_arg([1.0,2.0], nil).should == '{1.0,2.0}'
|
|
169
|
+
@db.bound_variable_arg(["\\ \"", 'NULL', nil], nil).should == '{"\\\\ \\"","NULL",NULL}'
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it "should parse array types from the schema correctly" do
|
|
173
|
+
@db.extend Sequel::Postgres::PGArray::DatabaseMethods
|
|
174
|
+
@db.fetch = [{:name=>'id', :db_type=>'integer'}, {:name=>'i', :db_type=>'integer[]'}, {:name=>'f', :db_type=>'real[]'}, {:name=>'d', :db_type=>'numeric[]'}, {:name=>'t', :db_type=>'text[]'}]
|
|
175
|
+
@db.schema(:items).map{|e| e[1][:type]}.should == [:integer, :integer_array, :float_array, :decimal_array, :string_array]
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
it "should support typecasting of the various array types" do
|
|
179
|
+
@db.extend Sequel::Postgres::PGArray::DatabaseMethods
|
|
180
|
+
a = [1, 2]
|
|
181
|
+
o = a.pg_array
|
|
182
|
+
%w[integer float decimal string].each do |x|
|
|
183
|
+
@db.typecast_value(:"#{x}_array", o).should equal(o)
|
|
184
|
+
@db.typecast_value(:"#{x}_array", a).should == a
|
|
185
|
+
@db.typecast_value(:"#{x}_array", a).should be_a_kind_of(eval("Sequel::Postgres::PG#{x.capitalize}Array"))
|
|
186
|
+
@db.typecast_value(:"#{x}_array", '{}').should == []
|
|
187
|
+
@db.typecast_value(:"#{x}_array", '{}').should be_a_kind_of(eval("Sequel::Postgres::PG#{x.capitalize}Array"))
|
|
188
|
+
end
|
|
189
|
+
@db.typecast_value(:integer_array, '{1}').should == [1]
|
|
190
|
+
@db.typecast_value(:float_array, '{1}').should == [1.0]
|
|
191
|
+
@db.typecast_value(:decimal_array, '{1}').should == [BigDecimal.new('1')]
|
|
192
|
+
@db.typecast_value(:string_array, '{1}').should == ['1']
|
|
193
|
+
proc{@db.typecast_value(:integer_array, {})}.should raise_error(Sequel::InvalidValue)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe "pg_auto_parameterize extension" do
|
|
4
|
+
before do
|
|
5
|
+
@db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
|
|
6
|
+
@db.synchronize{|c| def c.escape_bytea(v) v*2 end}
|
|
7
|
+
@db.extend_datasets{def use_cursor(*) clone end}
|
|
8
|
+
@db.extend Sequel::Postgres::AutoParameterize::DatabaseMethods
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "should automatically parameterize queries strings, blobs, numerics, dates, and times" do
|
|
12
|
+
pr = proc do |ds, sql, *args|
|
|
13
|
+
arg = args[0]
|
|
14
|
+
parg = args[1] || arg
|
|
15
|
+
s = ds.filter(:a=>arg).sql
|
|
16
|
+
s.should == sql
|
|
17
|
+
s.args.should == (parg == :nil ? nil : [parg])
|
|
18
|
+
end
|
|
19
|
+
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::int4)', 1)
|
|
20
|
+
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::int8)', 18446744073709551616)
|
|
21
|
+
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::double precision)', 1.1)
|
|
22
|
+
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::numeric)', BigDecimal.new('1.01'))
|
|
23
|
+
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::text)', "a")
|
|
24
|
+
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::bytea)', "a\0b".to_sequel_blob)
|
|
25
|
+
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = 1)', '1'.lit, :nil)
|
|
26
|
+
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::time)', Sequel::SQLTime.create(1, 2, 3, 500000))
|
|
27
|
+
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::date)', Date.today)
|
|
28
|
+
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::timestamp)', DateTime.new(2012, 1, 2, 3, 4, 5))
|
|
29
|
+
pr.call(@db[:table], 'SELECT * FROM table WHERE (a = $1::timestamp)', Time.utc(2012, 1, 2, 3, 4, 5))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should extract parameters from query string when executing" do
|
|
33
|
+
@db.fetch = {:a=>1}
|
|
34
|
+
@db.numrows = 1
|
|
35
|
+
@db.autoid = 1
|
|
36
|
+
|
|
37
|
+
@db[:table].filter(:a=>1).all.should == [{:a=>1}]
|
|
38
|
+
@db.sqls.should == ['SELECT * FROM table WHERE (a = $1::int4) -- args: [1]']
|
|
39
|
+
|
|
40
|
+
@db[:table].filter(:a=>1).update(:b=>'a').should == 1
|
|
41
|
+
@db.sqls.should == ['UPDATE table SET b = $1::text WHERE (a = $2::int4) -- args: ["a", 1]']
|
|
42
|
+
|
|
43
|
+
@db[:table].filter(:a=>1).delete.should == 1
|
|
44
|
+
@db.sqls.should == ['DELETE FROM table WHERE (a = $1::int4) -- args: [1]']
|
|
45
|
+
|
|
46
|
+
@db[:table].insert(:a=>1).should == 1
|
|
47
|
+
@db.sqls.should == ['INSERT INTO table (a) VALUES ($1::int4) RETURNING id -- args: [1]']
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "should not automatically paramiterize if no_auto_parameterize is used" do
|
|
51
|
+
@db[:table].no_auto_parameterize.filter(:a=>1).sql.should == 'SELECT * FROM table WHERE (a = 1)'
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "should not automatically parameterize prepared statements" do
|
|
55
|
+
@db[:table].filter(:a=>1, :b=>:$b).prepare(:select).sql.should =~ /SELECT \* FROM table WHERE \(\((a = 1|b = \$b)\) AND \((a = 1|b = \$b)\)\)/
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "should show args with string when inspecting SQL " do
|
|
59
|
+
@db[:table].filter(:a=>1).sql.inspect.should == '"SELECT * FROM table WHERE (a = $1::int4); [1]"'
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "should not auto parameterize when using cursors" do
|
|
63
|
+
@db[:table].filter(:a=>1).use_cursor.opts[:no_auto_parameterize].should be_true
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe "Sequel::Postgres::HStoreOp" do
|
|
4
|
+
before do
|
|
5
|
+
@ds = Sequel.connect('mock://postgres', :quote_identifiers=>false).dataset
|
|
6
|
+
@h = :h.hstore
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "#- should use the - operator" do
|
|
10
|
+
@ds.literal(@h - 'a').should == "(h - 'a')"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "#- should return an HStoreOp" do
|
|
14
|
+
@ds.literal((@h - 'a')['a']).should == "((h - 'a') -> 'a')"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "#[] should use the -> operator" do
|
|
18
|
+
@ds.literal(@h['a']).should == "(h -> 'a')"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "#[] should return a string expression" do
|
|
22
|
+
@ds.literal(@h['a'] + 'b').should == "((h -> 'a') || 'b')"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "#concat and #merge should use the || operator" do
|
|
26
|
+
@ds.literal(@h.concat(:h1)).should == "(h || h1)"
|
|
27
|
+
@ds.literal(@h.merge(:h1)).should == "(h || h1)"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "#concat should return an HStoreOp" do
|
|
31
|
+
@ds.literal(@h.concat(:h1)['a']).should == "((h || h1) -> 'a')"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "#contain_all should use the ?& operator" do
|
|
35
|
+
@ds.literal(@h.contain_all(:h1)).should == "(h ?& h1)"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "#contain_any should use the ?| operator" do
|
|
39
|
+
@ds.literal(@h.contain_any(:h1)).should == "(h ?| h1)"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "#contains should use the @> operator" do
|
|
43
|
+
@ds.literal(@h.contains(:h1)).should == "(h @> h1)"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "#contained_by should use the <@ operator" do
|
|
47
|
+
@ds.literal(@h.contained_by(:h1)).should == "(h <@ h1)"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "#defined should use the defined function" do
|
|
51
|
+
@ds.literal(@h.defined('a')).should == "defined(h, 'a')"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "#delete should use the delete function" do
|
|
55
|
+
@ds.literal(@h.delete('a')).should == "delete(h, 'a')"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "#delete should return an HStoreOp" do
|
|
59
|
+
@ds.literal(@h.delete('a')['a']).should == "(delete(h, 'a') -> 'a')"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "#each should use the each function" do
|
|
63
|
+
@ds.literal(@h.each).should == "each(h)"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "#has_key? and aliases should use the ? operator" do
|
|
67
|
+
@ds.literal(@h.has_key?('a')).should == "(h ? 'a')"
|
|
68
|
+
@ds.literal(@h.key?('a')).should == "(h ? 'a')"
|
|
69
|
+
@ds.literal(@h.member?('a')).should == "(h ? 'a')"
|
|
70
|
+
@ds.literal(@h.include?('a')).should == "(h ? 'a')"
|
|
71
|
+
@ds.literal(@h.exist?('a')).should == "(h ? 'a')"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "#hstore should return the receiver" do
|
|
75
|
+
@h.hstore.should equal(@h)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "#keys and #akeys should use the akeys function" do
|
|
79
|
+
@ds.literal(@h.keys).should == "akeys(h)"
|
|
80
|
+
@ds.literal(@h.akeys).should == "akeys(h)"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "#populate should use the populate_record function" do
|
|
84
|
+
@ds.literal(@h.populate(:a)).should == "populate_record(a, h)"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "#record_set should use the #= operator" do
|
|
88
|
+
@ds.literal(@h.record_set(:a)).should == "(a #= h)"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "#skeys should use the skeys function" do
|
|
92
|
+
@ds.literal(@h.skeys).should == "skeys(h)"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "#slice should should use the slice function" do
|
|
96
|
+
@ds.literal(@h.slice(:a)).should == "slice(h, a)"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "#slice should return an HStoreOp" do
|
|
100
|
+
@ds.literal(@h.slice(:a)['a']).should == "(slice(h, a) -> 'a')"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "#svals should use the svals function" do
|
|
104
|
+
@ds.literal(@h.svals).should == "svals(h)"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "#to_array should use the hstore_to_array function" do
|
|
108
|
+
@ds.literal(@h.to_array).should == "hstore_to_array(h)"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it "#to_matrix should use the hstore_to_matrix function" do
|
|
112
|
+
@ds.literal(@h.to_matrix).should == "hstore_to_matrix(h)"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "#values and #avals should use the avals function" do
|
|
116
|
+
@ds.literal(@h.values).should == "avals(h)"
|
|
117
|
+
@ds.literal(@h.avals).should == "avals(h)"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "should be able to turn expressions into hstore ops using hstore" do
|
|
121
|
+
@ds.literal(:a.qualify(:b).hstore['a']).should == "(b.a -> 'a')"
|
|
122
|
+
@ds.literal(:a.sql_function(:b).hstore['a']).should == "(a(b) -> 'a')"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "should be able to turn literal strings into hstore ops using hstore" do
|
|
126
|
+
@ds.literal('a'.lit.hstore['a']).should == "(a -> 'a')"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "should be able to turn symbols into hstore ops using hstore" do
|
|
130
|
+
@ds.literal(:a.hstore['a']).should == "(a -> 'a')"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "should allow transforming HStore instances into HStoreOp instances" do
|
|
134
|
+
@ds.literal({'a'=>'b'}.hstore.op['a']).should == "('\"a\"=>\"b\"'::hstore -> 'a')"
|
|
135
|
+
end
|
|
136
|
+
end
|