sequel-impala 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG +3 -0
- data/LICENSE +462 -0
- data/README.rdoc +39 -0
- data/Rakefile +39 -0
- data/lib/driver/commons-logging-1.2.jar +0 -0
- data/lib/driver/hadoop-common-2.6.0.jar +0 -0
- data/lib/driver/hadoop-core-2.6.0.jar +0 -0
- data/lib/driver/hive-exec-1.1.0.jar +0 -0
- data/lib/driver/hive-jdbc-1.1.0.jar +0 -0
- data/lib/driver/hive-metastore-1.1.0.jar +0 -0
- data/lib/driver/hive-service-1.1.0.jar +0 -0
- data/lib/driver/httpclient-4.3.jar +0 -0
- data/lib/driver/httpcore-4.3.jar +0 -0
- data/lib/driver/libfb303-0.9.0.jar +0 -0
- data/lib/driver/slf4j-api-1.7.5.jar +0 -0
- data/lib/impala.rb +47 -0
- data/lib/impala/connection.rb +117 -0
- data/lib/impala/cursor.rb +157 -0
- data/lib/impala/protocol.rb +8 -0
- data/lib/impala/protocol/beeswax_constants.rb +15 -0
- data/lib/impala/protocol/beeswax_service.rb +766 -0
- data/lib/impala/protocol/beeswax_types.rb +193 -0
- data/lib/impala/protocol/cli_service_constants.rb +60 -0
- data/lib/impala/protocol/cli_service_types.rb +1452 -0
- data/lib/impala/protocol/facebook_service.rb +706 -0
- data/lib/impala/protocol/fb303_constants.rb +15 -0
- data/lib/impala/protocol/fb303_types.rb +25 -0
- data/lib/impala/protocol/hive_metastore_constants.rb +53 -0
- data/lib/impala/protocol/hive_metastore_types.rb +698 -0
- data/lib/impala/protocol/impala_hive_server2_service.rb +29 -0
- data/lib/impala/protocol/impala_service.rb +377 -0
- data/lib/impala/protocol/impala_service_constants.rb +13 -0
- data/lib/impala/protocol/impala_service_types.rb +90 -0
- data/lib/impala/protocol/status_constants.rb +13 -0
- data/lib/impala/protocol/status_types.rb +46 -0
- data/lib/impala/protocol/t_c_l_i_service.rb +948 -0
- data/lib/impala/protocol/thrift_hive_metastore.rb +4707 -0
- data/lib/impala/version.rb +3 -0
- data/lib/jdbc/hive2.rb +46 -0
- data/lib/sequel/adapters/impala.rb +123 -0
- data/lib/sequel/adapters/jdbc/hive2.rb +26 -0
- data/lib/sequel/adapters/shared/impala.rb +635 -0
- data/lib/sequel/extensions/csv_to_parquet.rb +112 -0
- data/spec/database_test.rb +56 -0
- data/spec/dataset_test.rb +1268 -0
- data/spec/files/bad_down_migration/001_create_alt_basic.rb +4 -0
- data/spec/files/bad_down_migration/002_create_alt_advanced.rb +4 -0
- data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +9 -0
- data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +3 -0
- data/spec/files/bad_up_migration/001_create_alt_basic.rb +4 -0
- data/spec/files/bad_up_migration/002_create_alt_advanced.rb +3 -0
- data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +9 -0
- data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +9 -0
- data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +4 -0
- data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +9 -0
- data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +9 -0
- data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +9 -0
- data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +4 -0
- data/spec/files/integer_migrations/001_create_sessions.rb +9 -0
- data/spec/files/integer_migrations/002_create_nodes.rb +9 -0
- data/spec/files/integer_migrations/003_3_create_users.rb +4 -0
- data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +4 -0
- data/spec/files/reversible_migrations/001_reversible.rb +5 -0
- data/spec/files/reversible_migrations/002_reversible.rb +5 -0
- data/spec/files/reversible_migrations/003_reversible.rb +5 -0
- data/spec/files/reversible_migrations/004_reversible.rb +5 -0
- data/spec/files/reversible_migrations/005_reversible.rb +10 -0
- data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +9 -0
- data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +4 -0
- data/spec/impala_test.rb +285 -0
- data/spec/migrator_test.rb +240 -0
- data/spec/plugin_test.rb +91 -0
- data/spec/prepared_statement_test.rb +327 -0
- data/spec/schema_test.rb +356 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/timezone_test.rb +86 -0
- data/spec/type_test.rb +99 -0
- metadata +239 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
require 'shellwords'
|
3
|
+
|
4
|
+
module Sequel::CsvToParquet
|
5
|
+
# Load a CSV file into an existing parquet table. By default,
|
6
|
+
# assumes the CSV file has headers that match the column names
|
7
|
+
# in the parquet table. If this isn't true, the :headers or
|
8
|
+
# :mapping option should be specified.
|
9
|
+
#
|
10
|
+
# This works by adding the CSV file to HDFS via hdfs -put, then
|
11
|
+
# creating an external CSV table in Impala, then inserting into
|
12
|
+
# parquet table from the CSV table.
|
13
|
+
#
|
14
|
+
# Options:
|
15
|
+
# :empty_null :: Convert empty CSV cells to \N when adding to HDFS,
|
16
|
+
# so Impala will treat them as NULL instead of the
|
17
|
+
# empty string.
|
18
|
+
# :headers :: Specify the headers to use in the CSV file, assuming the
|
19
|
+
# csv file does not contain headers. If :skip_headers is set
|
20
|
+
# to true, this will ignore the existing headers in the file.
|
21
|
+
# :hdfs_tmp_dir :: The temporary HDFS directory to use when uploading.
|
22
|
+
# :mapping :: Override the mapping of the CSV columns to the parquet table
|
23
|
+
# columns. By default, assumes the CSV header names are the
|
24
|
+
# same as the parquet table columns, and uses both. If specified
|
25
|
+
# this should be a hash with parquet column symbol keys, with the
|
26
|
+
# value being the value to insert into the parquet table. This
|
27
|
+
# can be used to transform the data from the CSV table when loading
|
28
|
+
# it into the parquet table.
|
29
|
+
# :overwrite :: Set to true to overwrite existing data in the parquet table
|
30
|
+
# with the information from the CSV file. The default is to
|
31
|
+
# append the data to the existing parquet table.
|
32
|
+
# :skip_header :: Specifies that the first row contains headers and should
|
33
|
+
# be skipped when copying the CSV file to HDFS. If not
|
34
|
+
# specified, headers are skipped unless the :headers option
|
35
|
+
# is given.
|
36
|
+
# :tmp_table :: The temporary table name to use for the CSV table.
|
37
|
+
# :types :: Specify the types to use for the temporary CSV table. By default,
|
38
|
+
# it introspects the parquet table to get the type information, and
|
39
|
+
# uses the type for the matching column name.
|
40
|
+
def load_csv(local_csv_path, into_table, opts={})
|
41
|
+
tmp_num = SecureRandom.hex(8)
|
42
|
+
hdfs_tmp_dir = opts[:hdfs_tmp_dir] || "/tmp/cvs-#{tmp_num}"
|
43
|
+
hdfs_tmp_file = "#{hdfs_tmp_dir}/#{File.basename(local_csv_path)}"
|
44
|
+
tmp_table = opts[:tmp_table] || "csv_#{tmp_num}"
|
45
|
+
|
46
|
+
skip_header = opts.fetch(:skip_header, !opts.has_key?(:headers))
|
47
|
+
mapping = opts[:mapping]
|
48
|
+
overwrite = opts[:overwrite]
|
49
|
+
|
50
|
+
if columns = opts[:headers]
|
51
|
+
columns = columns.split(',') if columns.is_a?(String)
|
52
|
+
else
|
53
|
+
columns = File.open(local_csv_path).readline.chomp.split(',').map(&:downcase).map(&:to_sym)
|
54
|
+
end
|
55
|
+
|
56
|
+
into_table_columns = describe(into_table) rescue nil
|
57
|
+
|
58
|
+
if types = opts[:types]
|
59
|
+
types = types.split(',') if types.is_a?(String)
|
60
|
+
elsif (into_table_columns)
|
61
|
+
sch = Hash[into_table_columns.map { |h| [h[:name].downcase.to_sym, h[:type]]}]
|
62
|
+
types = columns.map { |col| sch[col] || "string" }
|
63
|
+
else
|
64
|
+
types = ["string"] * columns.length
|
65
|
+
end
|
66
|
+
|
67
|
+
unless types.length == columns.length
|
68
|
+
raise ArgumentError, "number of types doesn't match number of columns"
|
69
|
+
end
|
70
|
+
|
71
|
+
system("hdfs", "dfs", "-mkdir", hdfs_tmp_dir)
|
72
|
+
|
73
|
+
pipeline = if skip_header
|
74
|
+
"tail -n +2 #{Shellwords.shellescape(local_csv_path)}"
|
75
|
+
else
|
76
|
+
"cat #{Shellwords.shellescape(local_csv_path)}"
|
77
|
+
end
|
78
|
+
|
79
|
+
case opts[:empty_null]
|
80
|
+
when nil, false
|
81
|
+
when :perl
|
82
|
+
pipeline << ' | perl -p -e \'s/(^|,)(?=,|$)/\\1\\\\N/g\''
|
83
|
+
else
|
84
|
+
pipeline << (' | sed -r \'s/(^|,)(,|$)/\\1\\\\N\\2/g\'' * 2 )
|
85
|
+
end
|
86
|
+
|
87
|
+
system("#{pipeline} | hdfs dfs -put - #{Shellwords.shellescape(hdfs_tmp_file)}")
|
88
|
+
|
89
|
+
create_table(tmp_table, :external=>true, :field_term=>',', :location=>hdfs_tmp_dir) do
|
90
|
+
columns.zip(types) do |c, t|
|
91
|
+
column c, t
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
ds = from(into_table)
|
96
|
+
ds = ds.insert_overwrite if overwrite
|
97
|
+
|
98
|
+
if mapping
|
99
|
+
table_columns, csv_columns = mapping.to_a.transpose
|
100
|
+
else
|
101
|
+
table_columns = csv_columns = into_table_columns.map { |h| h[:name].to_sym }
|
102
|
+
end
|
103
|
+
ds.insert(table_columns, from(tmp_table).select(*csv_columns))
|
104
|
+
|
105
|
+
ensure
|
106
|
+
system("hdfs", "dfs", "-rm", hdfs_tmp_file)
|
107
|
+
system("hdfs", "dfs", "-rmdir", hdfs_tmp_dir)
|
108
|
+
drop_table?(tmp_table)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
Sequel::Database.register_extension(:csv_to_parquet, Sequel::CsvToParquet)
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
|
2
|
+
|
3
|
+
describe Sequel::Database do
|
4
|
+
before do
|
5
|
+
@db = DB
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should provide disconnect functionality" do
|
9
|
+
@db.disconnect
|
10
|
+
@db.pool.size.must_equal 0
|
11
|
+
@db.test_connection
|
12
|
+
@db.pool.size.must_equal 1
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should provide disconnect functionality after preparing a statement" do
|
16
|
+
@db.create_table!(:items){Integer :i}
|
17
|
+
@db[:items].prepare(:first, :a).call
|
18
|
+
@db.disconnect
|
19
|
+
@db.pool.size.must_equal 0
|
20
|
+
@db.drop_table?(:items)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should raise Sequel::DatabaseError on invalid SQL" do
|
24
|
+
proc{@db << "S"}.must_raise(Sequel::DatabaseError)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should store underlying wrapped exception in Sequel::DatabaseError" do
|
28
|
+
begin
|
29
|
+
@db << "SELECT"
|
30
|
+
rescue Sequel::DatabaseError=>e
|
31
|
+
if defined?(Java::JavaLang::Exception)
|
32
|
+
(e.wrapped_exception.is_a?(Exception) || e.wrapped_exception.is_a?(Java::JavaLang::Exception)).must_equal true
|
33
|
+
else
|
34
|
+
e.wrapped_exception.must_be_kind_of(Exception)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should not have the connection pool swallow non-StandardError based exceptions" do
|
40
|
+
proc{@db.pool.hold{raise Interrupt, "test"}}.must_raise(Interrupt)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should be able to disconnect connections more than once without exceptions" do
|
44
|
+
conn = @db.synchronize{|c| c}
|
45
|
+
@db.disconnect
|
46
|
+
@db.disconnect_connection(conn)
|
47
|
+
@db.disconnect_connection(conn)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should provide ability to check connections for validity" do
|
51
|
+
conn = @db.synchronize{|c| c}
|
52
|
+
@db.valid_connection?(conn).must_equal true
|
53
|
+
@db.disconnect
|
54
|
+
@db.valid_connection?(conn).must_equal false
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,1268 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
|
2
|
+
|
3
|
+
describe "Simple Dataset operations" do
|
4
|
+
before(:all) do
|
5
|
+
@db = DB
|
6
|
+
@ds = @db[:items]
|
7
|
+
end
|
8
|
+
after do
|
9
|
+
@db.drop_table?(:items)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should support sequential primary keys with a Bignum" do
|
13
|
+
@db.create_table!(:items) do
|
14
|
+
primary_key :id, :type=>Bignum
|
15
|
+
Integer :number
|
16
|
+
end
|
17
|
+
@ds << {:id=>1, :number=>20}
|
18
|
+
@ds << {:id=>2, :number=>30}
|
19
|
+
@ds.order(:number).all.must_equal [{:id => 1, :number=>20}, {:id => 2, :number=>30}]
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should have insert work correctly with static SQL" do
|
23
|
+
@db.create_table!(:items) do
|
24
|
+
primary_key :id
|
25
|
+
Integer :number
|
26
|
+
end
|
27
|
+
@db["INSERT INTO #{@ds.literal(:items)} (id, number) VALUES (2, 30)"].insert
|
28
|
+
@ds.all.must_equal [{:id => 2, :number=>30}]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should have insert work correctly when inserting a row with all NULL values" do
|
32
|
+
@db.create_table!(:items) do
|
33
|
+
Integer :id
|
34
|
+
Integer :number
|
35
|
+
end
|
36
|
+
@ds.insert
|
37
|
+
@ds.all.must_equal [{:id=>nil, :number=>nil}]
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should support iterating over large numbers of records with paged_each" do
|
41
|
+
@db.create_table!(:items) do
|
42
|
+
Integer :id
|
43
|
+
Integer :number
|
44
|
+
end
|
45
|
+
@ds.import([:id, :number], (1..10).map{|i| [i, i*10]})
|
46
|
+
|
47
|
+
[:offset, :filter].each do |strategy|
|
48
|
+
rows = []
|
49
|
+
@ds.order(:number).paged_each(:rows_per_fetch=>5, :strategy=>strategy){|row| rows << row}
|
50
|
+
rows.must_equal((1..10).map{|i| {:id=>i, :number=>i*10}})
|
51
|
+
|
52
|
+
rows = []
|
53
|
+
@ds.order(:number).paged_each(:rows_per_fetch=>3, :strategy=>strategy){|row| rows << row}
|
54
|
+
rows.must_equal((1..10).map{|i| {:id=>i, :number=>i*10}})
|
55
|
+
|
56
|
+
rows = []
|
57
|
+
@ds.order(:number, :id).paged_each(:rows_per_fetch=>5, :strategy=>strategy){|row| rows << row}
|
58
|
+
rows.must_equal((1..10).map{|i| {:id=>i, :number=>i*10}})
|
59
|
+
|
60
|
+
rows = []
|
61
|
+
@ds.reverse_order(:number).paged_each(:rows_per_fetch=>5, :strategy=>strategy){|row| rows << row}
|
62
|
+
rows.must_equal((1..10).map{|i| {:id=>i, :number=>i*10}}.reverse)
|
63
|
+
|
64
|
+
rows = []
|
65
|
+
@ds.order(Sequel.desc(:number), :id).paged_each(:rows_per_fetch=>5, :strategy=>strategy){|row| rows << row}
|
66
|
+
rows.must_equal((1..10).map{|i| {:id=>i, :number=>i*10}}.reverse)
|
67
|
+
end
|
68
|
+
|
69
|
+
rows = []
|
70
|
+
@ds.order(:number).limit(5, 2).paged_each(:rows_per_fetch=>3){|row| rows << row}
|
71
|
+
rows.must_equal((3..7).map{|i| {:id=>i, :number=>i*10}})
|
72
|
+
|
73
|
+
rows = []
|
74
|
+
@ds.order(Sequel.*(:number, 2)).paged_each(:rows_per_fetch=>5){|row| rows << row}
|
75
|
+
rows.must_equal((1..10).map{|i| {:id=>i, :number=>i*10}})
|
76
|
+
|
77
|
+
rows = []
|
78
|
+
@ds.order(Sequel.*(:number, 2)).paged_each(:rows_per_fetch=>5, :strategy=>:filter, :filter_values=>proc{|row, _| [row[:number] * 2]}){|row| rows << row}
|
79
|
+
rows.must_equal((1..10).map{|i| {:id=>i, :number=>i*10}})
|
80
|
+
|
81
|
+
if RUBY_ENGINE == 'jruby'
|
82
|
+
# check retrival with varying fetch sizes
|
83
|
+
array = (1..10).to_a
|
84
|
+
[1, 2, 5, 10, 20].each do |i|
|
85
|
+
@ds.with_fetch_size(i).select_order_map(:id).must_equal array
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should fetch correctly with a limit and offset for different combinations of from and join tables" do
|
91
|
+
@db.create_table!(:items) do
|
92
|
+
Integer :id
|
93
|
+
Integer :number
|
94
|
+
end
|
95
|
+
@ds.insert(:id=>1, :number=>10)
|
96
|
+
@db.create_table!(:items2){primary_key :id2; Integer :number2}
|
97
|
+
@db[:items2].insert(:id2=>1, :number2=>10)
|
98
|
+
@ds.from(:items, :items2).order(:id).limit(2, 0).all.must_equal [{:id=>1, :number=>10, :id2=>1, :number2=>10}]
|
99
|
+
@ds.from(:items___i, :items2___i2).order(:id).limit(2, 0).all.must_equal [{:id=>1, :number=>10, :id2=>1, :number2=>10}]
|
100
|
+
@ds.cross_join(:items2).order(:id).limit(2, 0).all.must_equal [{:id=>1, :number=>10, :id2=>1, :number2=>10}]
|
101
|
+
@ds.from(:items___i).cross_join(:items2___i2).order(:id).limit(2, 0).all.must_equal [{:id=>1, :number=>10, :id2=>1, :number2=>10}]
|
102
|
+
@ds.cross_join(:items2___i).cross_join(@db[:items2].select(:id2___id3, :number2___number3)).order(:id).limit(2, 0).all.must_equal [{:id=>1, :number=>10, :id2=>1, :number2=>10, :id3=>1, :number3=>10}]
|
103
|
+
|
104
|
+
@ds.from(:items, :items2).order(:id).limit(2, 1).all.must_equal []
|
105
|
+
@ds.from(:items___i, :items2___i2).order(:id).limit(2, 1).all.must_equal []
|
106
|
+
@ds.cross_join(:items2).order(:id).limit(2, 1).all.must_equal []
|
107
|
+
@ds.from(:items___i).cross_join(:items2___i2).order(:id).limit(2, 1).all.must_equal []
|
108
|
+
@ds.cross_join(:items2___i).cross_join(@db[:items2].select(:id2___id3, :number2___number3)).order(:id).limit(2, 1).all.must_equal []
|
109
|
+
@db.drop_table(:items2)
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "Simple Dataset operations" do
|
115
|
+
before(:all) do
|
116
|
+
@db = DB
|
117
|
+
@db.create_table!(:items) do
|
118
|
+
primary_key :id
|
119
|
+
Integer :number
|
120
|
+
end
|
121
|
+
@ds = @db[:items]
|
122
|
+
@ds.insert(:id=>1, :number=>10)
|
123
|
+
end
|
124
|
+
after(:all) do
|
125
|
+
@db.drop_table?(:items)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should join correctly" do
|
129
|
+
@ds.join(:items___b, :id=>:id).select_all(:items).all.must_equal [{:id=>1, :number=>10}]
|
130
|
+
@ds.join(:items___b, [:id]).select_all(:items).all.must_equal [{:id=>1, :number=>10}]
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should correctly handle subqueries" do
|
134
|
+
@ds.from_self(:alias=>:a).all.must_equal [{:id=>1, :number=>10}]
|
135
|
+
@ds.join(@ds.as(:a), :id=>:id).select_all(:a).all.must_equal [{:id=>1, :number=>10}]
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should graph correctly" do
|
139
|
+
a = [{:items=>{:id=>1, :number=>10}, :b=>{:id=>1, :number=>10}}]
|
140
|
+
pr = proc{|t| @ds.graph(t, {:id=>:id}, :table_alias=>:b).extension(:graph_each).all.must_equal a}
|
141
|
+
pr[:items]
|
142
|
+
pr[:items___foo]
|
143
|
+
pr[Sequel.identifier(:items)]
|
144
|
+
pr[Sequel.identifier('items')]
|
145
|
+
pr[Sequel.as(:items, :foo)]
|
146
|
+
pr[Sequel.as(Sequel.identifier('items'), 'foo')]
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should graph correctly with a subselect" do
|
150
|
+
@ds.from_self(:alias=>:items).graph(@ds.from_self, {:id=>:id}, :table_alias=>:b).extension(:graph_each).all.must_equal [{:items=>{:id=>1, :number=>10}, :b=>{:id=>1, :number=>10}}]
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should iterate over records as they come in" do
|
154
|
+
called = false
|
155
|
+
@ds.each{|row| called = true; row.must_equal(:id=>1, :number=>10)}
|
156
|
+
called.must_equal true
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should fetch all results correctly" do
|
160
|
+
@ds.all.must_equal [{:id=>1, :number=>10}]
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should fetch a single row correctly" do
|
164
|
+
@ds.first.must_equal(:id=>1, :number=>10)
|
165
|
+
@ds.single_record.must_equal(:id=>1, :number=>10)
|
166
|
+
@ds.single_record!.must_equal(:id=>1, :number=>10)
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should work correctly when returning from each without iterating over the whole result set" do
|
170
|
+
@ds.order(:id).each{|v| break v}.must_equal(:id=>1, :number=>10)
|
171
|
+
@ds.reverse(:id).each{|v| break v}.must_equal(:id=>1, :number=>10)
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should fetch a single value correctly" do
|
175
|
+
@ds.get(:id).must_equal 1
|
176
|
+
@ds.select(:id).single_value.must_equal 1
|
177
|
+
@ds.select(:id).single_value!.must_equal 1
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should have distinct work with limit" do
|
181
|
+
@ds.limit(1).distinct.all.must_equal [{:id=>1, :number=>10}]
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should fetch correctly with a limit" do
|
185
|
+
@ds.order(:id).limit(1).all.must_equal [{:id=>1, :number=>10}]
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should fetch correctly with a limit and offset" do
|
189
|
+
@ds.order(:id).limit(1, 0).all.must_equal [{:id=>1, :number=>10}]
|
190
|
+
@ds.order(:id).limit(1, 1).all.must_equal []
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should fetch correctly with just offset" do
|
194
|
+
@ds.order(:id).offset(0).all.must_equal [{:id=>1, :number=>10}]
|
195
|
+
@ds.order(:id).offset(1).all.must_equal []
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should fetch correctly with a limit and offset using seperate methods" do
|
199
|
+
@ds.order(:id).limit(1).offset(0).all.must_equal [{:id=>1, :number=>10}]
|
200
|
+
@ds.order(:id).limit(1).offset(1).all.must_equal []
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should provide correct columns when using a limit and offset" do
|
204
|
+
ds = @ds.order(:id).limit(1, 1)
|
205
|
+
ds.all
|
206
|
+
ds.columns.must_equal [:id, :number]
|
207
|
+
@ds.order(:id).limit(1, 1).columns.must_equal [:id, :number]
|
208
|
+
end
|
209
|
+
|
210
|
+
it "should fetch correctly with a limit and offset without an order" do
|
211
|
+
ds = @ds.order(1)
|
212
|
+
ds.limit(2, 1).all.must_equal []
|
213
|
+
ds.join(:items___i, :id=>:id).select(:items__id___s, :i__id___id2).limit(2, 1).all.must_equal []
|
214
|
+
ds.join(:items___i, :id=>:id).select(:items__id).limit(2, 1).all.must_equal []
|
215
|
+
ds.join(:items___i, :id=>:id).select(Sequel.qualify(:items, :id)).limit(2, 1).all.must_equal []
|
216
|
+
ds.join(:items___i, :id=>:id).select(Sequel.qualify(:items, :id).as(:s)).limit(2, 1).all.must_equal []
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should be orderable by column number" do
|
220
|
+
@ds.order(2, 1).select_map([:id, :number]).must_equal [[1, 10]]
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should fetch correctly with a limit in an IN subselect" do
|
224
|
+
@ds.where(:id=>@ds.select(:id).order(:id).limit(1)).all.must_equal [{:id=>1, :number=>10}]
|
225
|
+
end
|
226
|
+
|
227
|
+
it "should fetch correctly with a limit and offset in an IN subselect" do
|
228
|
+
@ds.where(:id=>@ds.select(:id).order(:id).limit(2, 0)).all.must_equal [{:id=>1, :number=>10}]
|
229
|
+
@ds.where(:id=>@ds.select(:id).order(:id).limit(2, 1)).all.must_equal []
|
230
|
+
end
|
231
|
+
|
232
|
+
it "should fetch correctly when using limit and offset in a from_self" do
|
233
|
+
ds = @ds.order(:id).limit(1, 1).from_self
|
234
|
+
ds.all.must_equal []
|
235
|
+
ds.columns.must_equal [:id, :number]
|
236
|
+
@ds.order(:id).limit(1, 1).columns.must_equal [:id, :number]
|
237
|
+
end
|
238
|
+
|
239
|
+
it "should fetch correctly when using nested limit and offset in a from_self" do
|
240
|
+
ds = @ds.order(:id).limit(1, 0).from_self.reverse_order(:number).limit(1, 0)
|
241
|
+
ds.all.must_equal [{:number=>10, :id=>1}]
|
242
|
+
ds.columns.must_equal [:id, :number]
|
243
|
+
@ds.order(:id).limit(1, 0).from_self.reverse_order(:number).limit(1, 0).columns.must_equal [:id, :number]
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should alias columns correctly" do
|
247
|
+
@ds.select(:id___x, :number___n).first.must_equal(:x=>1, :n=>10)
|
248
|
+
end
|
249
|
+
|
250
|
+
it "should handle true/false properly" do
|
251
|
+
@ds.filter(Sequel::TRUE).select_map(:number).must_equal [10]
|
252
|
+
@ds.filter(Sequel::FALSE).select_map(:number).must_equal []
|
253
|
+
@ds.filter(true).select_map(:number).must_equal [10]
|
254
|
+
@ds.filter(false).select_map(:number).must_equal []
|
255
|
+
@ds.filter({:id=>1}=>true).select_map(:number).must_equal [10]
|
256
|
+
@ds.filter({:id=>1}=>false).select_map(:number).must_equal []
|
257
|
+
@ds.literal(true)
|
258
|
+
@ds.literal(false)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
describe Sequel::Dataset do
|
263
|
+
before(:all) do
|
264
|
+
DB.create_table!(:test) do
|
265
|
+
String :name
|
266
|
+
Integer :value
|
267
|
+
end
|
268
|
+
@d = DB[:test]
|
269
|
+
@d.multi_insert([{:name => 'abc', :value => 123}, {:name => 'abc', :value => 456}, {:name => 'def', :value => 789}])
|
270
|
+
end
|
271
|
+
after(:all) do
|
272
|
+
DB.drop_table?(:test)
|
273
|
+
end
|
274
|
+
|
275
|
+
it "should correctly return avg" do
|
276
|
+
@d.avg(:value).to_i.must_equal 456
|
277
|
+
end
|
278
|
+
|
279
|
+
it "should correctly return sum" do
|
280
|
+
@d.sum(:value).to_i.must_equal 1368
|
281
|
+
end
|
282
|
+
|
283
|
+
it "should correctly return max" do
|
284
|
+
@d.max(:value).to_i.must_equal 789
|
285
|
+
end
|
286
|
+
|
287
|
+
it "should correctly return min" do
|
288
|
+
@d.min(:value).to_i.must_equal 123
|
289
|
+
end
|
290
|
+
|
291
|
+
it "should return the correct record count" do
|
292
|
+
@d.count.must_equal 3
|
293
|
+
end
|
294
|
+
|
295
|
+
it "should handle functions with identifier names correctly" do
|
296
|
+
@d.get{sum.function(:value)}.must_equal 1368
|
297
|
+
end
|
298
|
+
|
299
|
+
it "should handle aggregate methods on limited datasets correctly" do
|
300
|
+
@d = @d.order(:name).limit(2)
|
301
|
+
@d.count.must_equal 2
|
302
|
+
@d.avg(:value).to_i.must_equal 289
|
303
|
+
@d.min(:value).to_i.must_equal 123
|
304
|
+
@d.reverse(:value).min(:value).to_i.must_equal 456
|
305
|
+
@d.max(:value).to_i.must_equal 456
|
306
|
+
@d.sum(:value).to_i.must_equal 579
|
307
|
+
@d.interval(:value).to_i.must_equal 333
|
308
|
+
end
|
309
|
+
|
310
|
+
it "should return the correct records" do
|
311
|
+
@d.order(:value).to_a.must_equal [{:name => 'abc', :value => 123}, {:name => 'abc', :value => 456}, {:name => 'def', :value => 789}]
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
describe Sequel::Database do
|
316
|
+
it "should correctly escape strings" do
|
317
|
+
["\\\n",
|
318
|
+
"\\\\\n",
|
319
|
+
"\\\r\n",
|
320
|
+
"\\\\\r\n",
|
321
|
+
"\\\\\n\n",
|
322
|
+
"\\\\\r\n\r\n",
|
323
|
+
"\b\a'\0\3",
|
324
|
+
#"\t\b\a'\0\3",
|
325
|
+
"\\dingo",
|
326
|
+
"\\'dingo",
|
327
|
+
"\\\\''dingo",
|
328
|
+
].each do |str|
|
329
|
+
DB.get(Sequel.cast(str, String)).must_equal str
|
330
|
+
str = "1#{str}1"
|
331
|
+
DB.get(Sequel.cast(str, String)).must_equal str
|
332
|
+
str = "#{str}#{str}"
|
333
|
+
DB.get(Sequel.cast(str, String)).must_equal str
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
it "should have a working table_exists?" do
|
338
|
+
t = :basdfdsafsaddsaf
|
339
|
+
DB.drop_table?(t)
|
340
|
+
DB.table_exists?(t).must_equal false
|
341
|
+
DB.create_table(t){Integer :a}
|
342
|
+
begin
|
343
|
+
DB.table_exists?(t).must_equal true
|
344
|
+
ensure
|
345
|
+
DB.drop_table(t)
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
describe "Simple Dataset operations" do
|
351
|
+
before do
|
352
|
+
DB.create_table!(:items) do
|
353
|
+
Integer :number
|
354
|
+
TrueClass :flag
|
355
|
+
end
|
356
|
+
@ds = DB[:items]
|
357
|
+
end
|
358
|
+
after do
|
359
|
+
DB.drop_table?(:items)
|
360
|
+
end
|
361
|
+
|
362
|
+
it "should deal with boolean conditions correctly" do
|
363
|
+
@ds.insert(:number=>1, :flag=>true)
|
364
|
+
@ds.insert(:number=>2, :flag=>false)
|
365
|
+
@ds.insert(:number=>3, :flag=>nil)
|
366
|
+
@ds.order!(:number)
|
367
|
+
@ds.filter(:flag=>true).map(:number).must_equal [1]
|
368
|
+
@ds.filter(:flag=>false).map(:number).must_equal [2]
|
369
|
+
@ds.filter(:flag=>nil).map(:number).must_equal [3]
|
370
|
+
@ds.exclude(:flag=>true).map(:number).must_equal [2, 3]
|
371
|
+
@ds.exclude(:flag=>false).map(:number).must_equal [1, 3]
|
372
|
+
@ds.exclude(:flag=>nil).map(:number).must_equal [1, 2]
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
describe "Dataset UNION, EXCEPT, and INTERSECT" do
|
377
|
+
before(:all) do
|
378
|
+
DB.create_table!(:i1){integer :number}
|
379
|
+
DB.create_table!(:i2){integer :number}
|
380
|
+
DB.create_table!(:i3){integer :number}
|
381
|
+
@ds1 = DB[:i1]
|
382
|
+
@ds1.insert(:number=>8)
|
383
|
+
@ds1.insert(:number=>10)
|
384
|
+
@ds1.insert(:number=>20)
|
385
|
+
@ds1.insert(:number=>38)
|
386
|
+
@ds2 = DB[:i2]
|
387
|
+
@ds2.insert(:number=>9)
|
388
|
+
@ds2.insert(:number=>10)
|
389
|
+
@ds2.insert(:number=>30)
|
390
|
+
@ds2.insert(:number=>39)
|
391
|
+
@ds3 = DB[:i3]
|
392
|
+
@ds3.insert(:number=>10)
|
393
|
+
@ds3.insert(:number=>40)
|
394
|
+
end
|
395
|
+
after(:all) do
|
396
|
+
DB.drop_table?(:i1, :i2, :i3)
|
397
|
+
end
|
398
|
+
|
399
|
+
it "should give the correct results for simple UNION" do
|
400
|
+
@ds1.union(@ds2).order(:number).map{|x| x[:number].to_s}.must_equal %w'8 9 10 20 30 38 39'
|
401
|
+
end
|
402
|
+
|
403
|
+
it "should give the correct results for UNION when used with ordering and limits" do
|
404
|
+
|
405
|
+
@ds1.reverse_order(:number).union(@ds2).order(:number).map{|x| x[:number].to_s}.must_equal %w'8 9 10 20 30 38 39'
|
406
|
+
@ds1.union(@ds2.reverse_order(:number)).order(:number).map{|x| x[:number].to_s}.must_equal %w'8 9 10 20 30 38 39'
|
407
|
+
|
408
|
+
@ds1.reverse_order(:number).limit(1).union(@ds2).order(:number).map{|x| x[:number].to_s}.must_equal %w'9 10 30 38 39'
|
409
|
+
@ds2.reverse_order(:number).limit(1).union(@ds1).order(:number).map{|x| x[:number].to_s}.must_equal %w'8 10 20 38 39'
|
410
|
+
|
411
|
+
@ds1.union(@ds2.order(:number).limit(1)).order(:number).map{|x| x[:number].to_s}.must_equal %w'8 9 10 20 38'
|
412
|
+
@ds2.union(@ds1.order(:number).limit(1)).order(:number).map{|x| x[:number].to_s}.must_equal %w'8 9 10 30 39'
|
413
|
+
|
414
|
+
@ds1.union(@ds2).limit(2).order(:number).map{|x| x[:number].to_s}.must_equal %w'8 9'
|
415
|
+
@ds2.union(@ds1).reverse_order(:number).limit(2).map{|x| x[:number].to_s}.must_equal %w'39 38'
|
416
|
+
|
417
|
+
@ds1.reverse_order(:number).limit(2).union(@ds2.reverse_order(:number).limit(2)).order(:number).limit(3).map{|x| x[:number].to_s}.must_equal %w'20 30 38'
|
418
|
+
@ds2.order(:number).limit(2).union(@ds1.order(:number).limit(2)).reverse_order(:number).limit(3).map{|x| x[:number].to_s}.must_equal %w'10 9 8'
|
419
|
+
end
|
420
|
+
|
421
|
+
it "should give the correct results for compound UNION" do
|
422
|
+
@ds1.union(@ds2).union(@ds3).order(:number).map{|x| x[:number].to_s}.must_equal %w'8 9 10 20 30 38 39 40'
|
423
|
+
@ds1.union(@ds2.union(@ds3)).order(:number).map{|x| x[:number].to_s}.must_equal %w'8 9 10 20 30 38 39 40'
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
describe "Common Table Expressions" do
|
428
|
+
before(:all) do
|
429
|
+
@db = DB
|
430
|
+
@db.create_table!(:i1){Integer :id; Integer :parent_id}
|
431
|
+
@ds = @db[:i1]
|
432
|
+
@ds.insert(:id=>1)
|
433
|
+
@ds.insert(:id=>2)
|
434
|
+
@ds.insert(:id=>3, :parent_id=>1)
|
435
|
+
@ds.insert(:id=>4, :parent_id=>1)
|
436
|
+
@ds.insert(:id=>5, :parent_id=>3)
|
437
|
+
@ds.insert(:id=>6, :parent_id=>5)
|
438
|
+
end
|
439
|
+
after(:all) do
|
440
|
+
@db.drop_table?(:i1)
|
441
|
+
end
|
442
|
+
|
443
|
+
it "should give correct results for WITH" do
|
444
|
+
@db[:t].with(:t, @ds.filter(:parent_id=>nil).select(:id)).order(:id).map(:id).must_equal [1, 2]
|
445
|
+
end
|
446
|
+
|
447
|
+
it "should support joining a dataset with a CTE" do
|
448
|
+
@ds.inner_join(@db[:t].with(:t, @ds.filter(:parent_id=>nil)), :id => :id).select(:i1__id).order(:i1__id).map(:id).must_equal [1,2]
|
449
|
+
@db[:t].with(:t, @ds).inner_join(@db[:s].with(:s, @ds.filter(:parent_id=>nil)), :id => :id).select(:t__id).order(:t__id).map(:id).must_equal [1,2]
|
450
|
+
end
|
451
|
+
|
452
|
+
it "should support a subselect in the FROM clause with a CTE" do
|
453
|
+
@ds.from(@db[:t].with(:t, @ds)).select_order_map(:id).must_equal [1,2,3,4,5,6]
|
454
|
+
@db[:t].with(:t, @ds).from_self.select_order_map(:id).must_equal [1,2,3,4,5,6]
|
455
|
+
end
|
456
|
+
|
457
|
+
it "should support using a CTE inside a CTE" do
|
458
|
+
@db[:s].with(:s, @db[:t].with(:t, @ds)).select_order_map(:id).must_equal [1,2,3,4,5,6]
|
459
|
+
@db[:s].with_recursive(:s, @db[:t].with(:t, @ds), @db[:t2].with(:t2, @ds)).select_order_map(:id).must_equal [1,1,2,2,3,3,4,4,5,5,6,6]
|
460
|
+
end
|
461
|
+
|
462
|
+
it "should support using a CTE inside UNION/EXCEPT/INTERSECT" do
|
463
|
+
@ds.union(@db[:t].with(:t, @ds)).select_order_map(:id).must_equal [1,2,3,4,5,6]
|
464
|
+
if @ds.supports_intersect_except?
|
465
|
+
@ds.intersect(@db[:t].with(:t, @ds)).select_order_map(:id).must_equal [1,2,3,4,5,6]
|
466
|
+
@ds.except(@db[:t].with(:t, @ds)).select_order_map(:id).must_equal []
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
describe "Window Functions" do
|
472
|
+
before(:all) do
|
473
|
+
@db = DB
|
474
|
+
@db.create_table!(:i1){Integer :id; Integer :group_id; Integer :amount}
|
475
|
+
@ds = @db[:i1].order(:id)
|
476
|
+
@ds.insert(:id=>1, :group_id=>1, :amount=>1)
|
477
|
+
@ds.insert(:id=>2, :group_id=>1, :amount=>10)
|
478
|
+
@ds.insert(:id=>3, :group_id=>1, :amount=>100)
|
479
|
+
@ds.insert(:id=>4, :group_id=>2, :amount=>1000)
|
480
|
+
@ds.insert(:id=>5, :group_id=>2, :amount=>10000)
|
481
|
+
@ds.insert(:id=>6, :group_id=>2, :amount=>100000)
|
482
|
+
end
|
483
|
+
after(:all) do
|
484
|
+
@db.drop_table?(:i1)
|
485
|
+
end
|
486
|
+
|
487
|
+
it "should give correct results for aggregate window functions" do
|
488
|
+
@ds.select(:id){sum(:amount).over(:partition=>:group_id).as(:sum)}.all.
|
489
|
+
must_equal [{:sum=>111, :id=>1}, {:sum=>111, :id=>2}, {:sum=>111, :id=>3}, {:sum=>111000, :id=>4}, {:sum=>111000, :id=>5}, {:sum=>111000, :id=>6}]
|
490
|
+
@ds.select(:id){sum(:amount).over.as(:sum)}.all.
|
491
|
+
must_equal [{:sum=>111111, :id=>1}, {:sum=>111111, :id=>2}, {:sum=>111111, :id=>3}, {:sum=>111111, :id=>4}, {:sum=>111111, :id=>5}, {:sum=>111111, :id=>6}]
|
492
|
+
end
|
493
|
+
|
494
|
+
it "should give correct results for ranking window functions with orders" do
|
495
|
+
@ds.select(:id){rank{}.over(:partition=>:group_id, :order=>:id).as(:rank)}.all.
|
496
|
+
must_equal [{:rank=>1, :id=>1}, {:rank=>2, :id=>2}, {:rank=>3, :id=>3}, {:rank=>1, :id=>4}, {:rank=>2, :id=>5}, {:rank=>3, :id=>6}]
|
497
|
+
@ds.select(:id){rank{}.over(:order=>id).as(:rank)}.all.
|
498
|
+
must_equal [{:rank=>1, :id=>1}, {:rank=>2, :id=>2}, {:rank=>3, :id=>3}, {:rank=>4, :id=>4}, {:rank=>5, :id=>5}, {:rank=>6, :id=>6}]
|
499
|
+
end
|
500
|
+
|
501
|
+
it "should give correct results for aggregate window functions with orders" do
|
502
|
+
@ds.select(:id){sum(:amount).over(:partition=>:group_id, :order=>:id).as(:sum)}.all.
|
503
|
+
must_equal [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1000, :id=>4}, {:sum=>11000, :id=>5}, {:sum=>111000, :id=>6}]
|
504
|
+
@ds.select(:id){sum(:amount).over(:order=>:id).as(:sum)}.all.
|
505
|
+
must_equal [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1111, :id=>4}, {:sum=>11111, :id=>5}, {:sum=>111111, :id=>6}]
|
506
|
+
end
|
507
|
+
|
508
|
+
it "should give correct results for aggregate window functions with frames" do
|
509
|
+
@ds.select(:id){sum(:amount).over(:partition=>:group_id, :order=>:id, :frame=>:all).as(:sum)}.all.
|
510
|
+
must_equal [{:sum=>111, :id=>1}, {:sum=>111, :id=>2}, {:sum=>111, :id=>3}, {:sum=>111000, :id=>4}, {:sum=>111000, :id=>5}, {:sum=>111000, :id=>6}]
|
511
|
+
@ds.select(:id){sum(:amount).over(:order=>:id, :frame=>:all).as(:sum)}.all.
|
512
|
+
must_equal [{:sum=>111111, :id=>1}, {:sum=>111111, :id=>2}, {:sum=>111111, :id=>3}, {:sum=>111111, :id=>4}, {:sum=>111111, :id=>5}, {:sum=>111111, :id=>6}]
|
513
|
+
|
514
|
+
@ds.select(:id){sum(:amount).over(:partition=>:group_id, :order=>:id, :frame=>:rows).as(:sum)}.all.
|
515
|
+
must_equal [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1000, :id=>4}, {:sum=>11000, :id=>5}, {:sum=>111000, :id=>6}]
|
516
|
+
@ds.select(:id){sum(:amount).over(:order=>:id, :frame=>:rows).as(:sum)}.all.
|
517
|
+
must_equal [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1111, :id=>4}, {:sum=>11111, :id=>5}, {:sum=>111111, :id=>6}]
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
describe Sequel::SQL::Constants do
|
522
|
+
before do
|
523
|
+
@db = DB
|
524
|
+
@ds = @db[:constants]
|
525
|
+
@c = proc do |v|
|
526
|
+
case v
|
527
|
+
when Time
|
528
|
+
v
|
529
|
+
when DateTime, String
|
530
|
+
Time.parse(v.to_s)
|
531
|
+
else
|
532
|
+
v
|
533
|
+
end
|
534
|
+
end
|
535
|
+
@c2 = proc{|v| v.is_a?(Date) ? v : Date.parse(v) }
|
536
|
+
end
|
537
|
+
after do
|
538
|
+
@db.drop_table?(:constants)
|
539
|
+
end
|
540
|
+
|
541
|
+
it "should have working CURRENT_TIMESTAMP" do
|
542
|
+
@db.create_table!(:constants){DateTime :ts}
|
543
|
+
@ds.insert(:ts=>Sequel::CURRENT_TIMESTAMP)
|
544
|
+
(Time.now - @c[@ds.get(:ts)]).must_be_close_to 0, 60
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
describe "Sequel::Dataset#import and #multi_insert" do
|
549
|
+
before do
|
550
|
+
@db = DB
|
551
|
+
@db.create_table!(:imp){Integer :i}
|
552
|
+
@ids = @db[:imp].order(:i)
|
553
|
+
end
|
554
|
+
after do
|
555
|
+
@db.drop_table?(:imp)
|
556
|
+
end
|
557
|
+
|
558
|
+
it "should import with multi_insert and an array of hashes" do
|
559
|
+
@ids.multi_insert([{:i=>10}, {:i=>20}])
|
560
|
+
@ids.all.must_equal [{:i=>10}, {:i=>20}]
|
561
|
+
end
|
562
|
+
|
563
|
+
it "should import with an array of arrays of values" do
|
564
|
+
@ids.import([:i], [[10], [20]])
|
565
|
+
@ids.all.must_equal [{:i=>10}, {:i=>20}]
|
566
|
+
end
|
567
|
+
|
568
|
+
it "should import with a dataset" do
|
569
|
+
@db.create_table!(:exp2){Integer :i}
|
570
|
+
@db[:exp2].import([:i], [[10], [20]])
|
571
|
+
@ids.import([:i], @db[:exp2])
|
572
|
+
@ids.all.must_equal [{:i=>10}, {:i=>20}]
|
573
|
+
@db.drop_table(:exp2)
|
574
|
+
end
|
575
|
+
|
576
|
+
it "should have import work with the :slice_size option" do
|
577
|
+
@ids.import([:i], [[10], [20], [30]], :slice_size=>1)
|
578
|
+
@ids.all.must_equal [{:i=>10}, {:i=>20}, {:i=>30}]
|
579
|
+
end
|
580
|
+
|
581
|
+
it "should import many rows at once" do
|
582
|
+
@ids.import([:i], (1..20).to_a.map{|x| [x]})
|
583
|
+
@ids.select_order_map(:i).must_equal((1..20).to_a)
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
describe "Sequel::Dataset convenience methods" do
|
588
|
+
before do
|
589
|
+
@db = DB
|
590
|
+
@db.create_table!(:a){Integer :a; Integer :b}
|
591
|
+
@ds = @db[:a].order(:a)
|
592
|
+
end
|
593
|
+
after do
|
594
|
+
@db.drop_table?(:a)
|
595
|
+
end
|
596
|
+
|
597
|
+
it "#empty? should return whether the dataset returns no rows" do
|
598
|
+
@ds.empty?.must_equal true
|
599
|
+
@ds.insert(20, 10)
|
600
|
+
@ds.empty?.must_equal false
|
601
|
+
end
|
602
|
+
|
603
|
+
it "#empty? should work correctly for datasets with limits" do
|
604
|
+
ds = @ds.limit(1)
|
605
|
+
ds.empty?.must_equal true
|
606
|
+
ds.insert(20, 10)
|
607
|
+
ds.empty?.must_equal false
|
608
|
+
end
|
609
|
+
|
610
|
+
it "#empty? should work correctly for datasets with limits and offsets" do
|
611
|
+
ds = @ds.order(:a).limit(1, 1)
|
612
|
+
ds.empty?.must_equal true
|
613
|
+
ds.insert(20, 10)
|
614
|
+
ds.empty?.must_equal true
|
615
|
+
ds.insert(20, 10)
|
616
|
+
ds.empty?.must_equal false
|
617
|
+
end
|
618
|
+
|
619
|
+
it "#group_and_count should return a grouping by count" do
|
620
|
+
@ds.group_and_count(:a).order{count(:a)}.all.must_equal []
|
621
|
+
@ds.insert(20, 10)
|
622
|
+
@ds.group_and_count(:a).order{count(:a)}.all.each{|h| h[:count] = h[:count].to_i}.must_equal [{:a=>20, :count=>1}]
|
623
|
+
@ds.insert(20, 30)
|
624
|
+
@ds.group_and_count(:a).order{count(:a)}.all.each{|h| h[:count] = h[:count].to_i}.must_equal [{:a=>20, :count=>2}]
|
625
|
+
@ds.insert(30, 30)
|
626
|
+
@ds.group_and_count(:a).order{count(:a)}.all.each{|h| h[:count] = h[:count].to_i}.must_equal [{:a=>30, :count=>1}, {:a=>20, :count=>2}]
|
627
|
+
end
|
628
|
+
|
629
|
+
it "#group_and_count should support column aliases" do
|
630
|
+
@ds.group_and_count(:a___c).order{count(:a)}.all.must_equal []
|
631
|
+
@ds.insert(20, 10)
|
632
|
+
@ds.group_and_count(:a___c).order{count(:a)}.all.each{|h| h[:count] = h[:count].to_i}.must_equal [{:c=>20, :count=>1}]
|
633
|
+
@ds.insert(20, 30)
|
634
|
+
@ds.group_and_count(:a___c).order{count(:a)}.all.each{|h| h[:count] = h[:count].to_i}.must_equal [{:c=>20, :count=>2}]
|
635
|
+
@ds.insert(30, 30)
|
636
|
+
@ds.group_and_count(:a___c).order{count(:a)}.all.each{|h| h[:count] = h[:count].to_i}.must_equal [{:c=>30, :count=>1}, {:c=>20, :count=>2}]
|
637
|
+
end
|
638
|
+
|
639
|
+
it "#range should return the range between the maximum and minimum values" do
|
640
|
+
@ds = @ds.unordered
|
641
|
+
@ds.insert(20, 10)
|
642
|
+
@ds.insert(30, 10)
|
643
|
+
@ds.range(:a).must_equal(20..30)
|
644
|
+
@ds.range(:b).must_equal(10..10)
|
645
|
+
end
|
646
|
+
|
647
|
+
it "#interval should return the different between the maximum and minimum values" do
|
648
|
+
@ds = @ds.unordered
|
649
|
+
@ds.insert(20, 10)
|
650
|
+
@ds.insert(30, 10)
|
651
|
+
@ds.interval(:a).to_i.must_equal 10
|
652
|
+
@ds.interval(:b).to_i.must_equal 0
|
653
|
+
end
|
654
|
+
end
|
655
|
+
|
656
|
+
describe "Sequel::Dataset main SQL methods" do
|
657
|
+
before do
|
658
|
+
@db = DB
|
659
|
+
@db.create_table!(:d){Integer :a; Integer :b}
|
660
|
+
@ds = @db[:d].order(:a)
|
661
|
+
end
|
662
|
+
after do
|
663
|
+
@db.drop_table?(:d)
|
664
|
+
end
|
665
|
+
|
666
|
+
it "#exists should return a usable exists clause" do
|
667
|
+
@ds.filter(@db[:d___c].filter(:c__a=>:d__b).exists).all.must_equal []
|
668
|
+
@ds.insert(20, 30)
|
669
|
+
@ds.insert(10, 20)
|
670
|
+
@ds.filter(@db[:d___c].filter(:c__a=>:d__b).exists).all.must_equal [{:a=>10, :b=>20}]
|
671
|
+
end
|
672
|
+
|
673
|
+
it "#filter and #exclude should work with placeholder strings" do
|
674
|
+
@ds.insert(20, 30)
|
675
|
+
@ds.filter("a > ?", 15).all.must_equal [{:a=>20, :b=>30}]
|
676
|
+
@ds.exclude("b < ?", 15).all.must_equal [{:a=>20, :b=>30}]
|
677
|
+
@ds.filter("b < ?", 15).invert.all.must_equal [{:a=>20, :b=>30}]
|
678
|
+
end
|
679
|
+
|
680
|
+
it "#and and #or should work correctly" do
|
681
|
+
@ds.insert(20, 30)
|
682
|
+
@ds.filter(:a=>20).and(:b=>30).all.must_equal [{:a=>20, :b=>30}]
|
683
|
+
@ds.filter(:a=>20).and(:b=>15).all.must_equal []
|
684
|
+
@ds.filter(:a=>20).or(:b=>15).all.must_equal [{:a=>20, :b=>30}]
|
685
|
+
@ds.filter(:a=>10).or(:b=>15).all.must_equal []
|
686
|
+
end
|
687
|
+
|
688
|
+
it "#select_group should work correctly" do
|
689
|
+
@ds.unordered!
|
690
|
+
@ds.select_group(:a).all.must_equal []
|
691
|
+
@ds.insert(20, 30)
|
692
|
+
@ds.select_group(:a).all.must_equal [{:a=>20}]
|
693
|
+
@ds.select_group(:b).all.must_equal [{:b=>30}]
|
694
|
+
@ds.insert(20, 40)
|
695
|
+
@ds.select_group(:a).all.must_equal [{:a=>20}]
|
696
|
+
@ds.order(:b).select_group(:b).all.must_equal [{:b=>30}, {:b=>40}]
|
697
|
+
end
|
698
|
+
|
699
|
+
it "#select_group should work correctly when aliasing" do
|
700
|
+
@ds.unordered!
|
701
|
+
@ds.insert(20, 30)
|
702
|
+
@ds.select_group(:b___c).all.must_equal [{:c=>30}]
|
703
|
+
end
|
704
|
+
|
705
|
+
it "#having should work correctly" do
|
706
|
+
@ds.unordered!
|
707
|
+
@ds.select{[b, max(a).as(c)]}.group(:b).having{max(a) > 30}.all.must_equal []
|
708
|
+
@ds.insert(20, 30)
|
709
|
+
@ds.select{[b, max(a).as(c)]}.group(:b).having{max(a) > 30}.all.must_equal []
|
710
|
+
@ds.insert(40, 20)
|
711
|
+
@ds.select{[b, max(a).as(c)]}.group(:b).having{max(a) > 30}.all.each{|h| h[:c] = h[:c].to_i}.must_equal [{:b=>20, :c=>40}]
|
712
|
+
end
|
713
|
+
|
714
|
+
it "#having should work without a previous group" do
|
715
|
+
@ds.unordered!
|
716
|
+
@ds.select{max(a).as(c)}.having{max(a) > 30}.all.must_equal []
|
717
|
+
@ds.insert(20, 30)
|
718
|
+
@ds.select{max(a).as(c)}.having{max(a) > 30}.all.must_equal []
|
719
|
+
@ds.insert(40, 20)
|
720
|
+
@ds.select{max(a).as(c)}.having{max(a) > 30}.all.each{|h| h[:c] = h[:c].to_i}.must_equal [{:c=>40}]
|
721
|
+
end
|
722
|
+
end
|
723
|
+
|
724
|
+
describe "Sequel::Dataset convenience methods" do
|
725
|
+
before do
|
726
|
+
@db = DB
|
727
|
+
@db.create_table!(:a){Integer :a; Integer :b; Integer :c; Integer :d}
|
728
|
+
@ds = @db[:a].order(:a)
|
729
|
+
@ds.insert(1, 2, 3, 4)
|
730
|
+
@ds.insert(5, 6, 7, 8)
|
731
|
+
end
|
732
|
+
after do
|
733
|
+
@db.drop_table?(:a)
|
734
|
+
end
|
735
|
+
|
736
|
+
it "should have working #map" do
|
737
|
+
@ds.map(:a).must_equal [1, 5]
|
738
|
+
@ds.map(:b).must_equal [2, 6]
|
739
|
+
@ds.map([:a, :b]).must_equal [[1, 2], [5, 6]]
|
740
|
+
end
|
741
|
+
|
742
|
+
it "should have working #to_hash" do
|
743
|
+
@ds.to_hash(:a).must_equal(1=>{:a=>1, :b=>2, :c=>3, :d=>4}, 5=>{:a=>5, :b=>6, :c=>7, :d=>8})
|
744
|
+
@ds.to_hash(:b).must_equal(2=>{:a=>1, :b=>2, :c=>3, :d=>4}, 6=>{:a=>5, :b=>6, :c=>7, :d=>8})
|
745
|
+
@ds.to_hash([:a, :b]).must_equal([1, 2]=>{:a=>1, :b=>2, :c=>3, :d=>4}, [5, 6]=>{:a=>5, :b=>6, :c=>7, :d=>8})
|
746
|
+
|
747
|
+
@ds.to_hash(:a, :b).must_equal(1=>2, 5=>6)
|
748
|
+
@ds.to_hash([:a, :c], :b).must_equal([1, 3]=>2, [5, 7]=>6)
|
749
|
+
@ds.to_hash(:a, [:b, :c]).must_equal(1=>[2, 3], 5=>[6, 7])
|
750
|
+
@ds.to_hash([:a, :c], [:b, :d]).must_equal([1, 3]=>[2, 4], [5, 7]=>[6, 8])
|
751
|
+
end
|
752
|
+
|
753
|
+
it "should have working #to_hash_groups" do
|
754
|
+
ds = @ds.order(*@ds.columns)
|
755
|
+
ds.insert(1, 2, 3, 9)
|
756
|
+
ds.to_hash_groups(:a).must_equal(1=>[{:a=>1, :b=>2, :c=>3, :d=>4}, {:a=>1, :b=>2, :c=>3, :d=>9}], 5=>[{:a=>5, :b=>6, :c=>7, :d=>8}])
|
757
|
+
ds.to_hash_groups(:b).must_equal(2=>[{:a=>1, :b=>2, :c=>3, :d=>4}, {:a=>1, :b=>2, :c=>3, :d=>9}], 6=>[{:a=>5, :b=>6, :c=>7, :d=>8}])
|
758
|
+
ds.to_hash_groups([:a, :b]).must_equal([1, 2]=>[{:a=>1, :b=>2, :c=>3, :d=>4}, {:a=>1, :b=>2, :c=>3, :d=>9}], [5, 6]=>[{:a=>5, :b=>6, :c=>7, :d=>8}])
|
759
|
+
|
760
|
+
ds.to_hash_groups(:a, :d).must_equal(1=>[4, 9], 5=>[8])
|
761
|
+
ds.to_hash_groups([:a, :c], :d).must_equal([1, 3]=>[4, 9], [5, 7]=>[8])
|
762
|
+
ds.to_hash_groups(:a, [:b, :d]).must_equal(1=>[[2, 4], [2, 9]], 5=>[[6, 8]])
|
763
|
+
ds.to_hash_groups([:a, :c], [:b, :d]).must_equal([1, 3]=>[[2, 4], [2, 9]], [5, 7]=>[[6, 8]])
|
764
|
+
end
|
765
|
+
|
766
|
+
it "should have working #select_map" do
|
767
|
+
@ds.select_map(:a).must_equal [1, 5]
|
768
|
+
@ds.select_map(:b).must_equal [2, 6]
|
769
|
+
@ds.select_map([:a]).must_equal [[1], [5]]
|
770
|
+
@ds.select_map([:a, :b]).must_equal [[1, 2], [5, 6]]
|
771
|
+
|
772
|
+
@ds.select_map(:a___e).must_equal [1, 5]
|
773
|
+
@ds.select_map(:b___e).must_equal [2, 6]
|
774
|
+
@ds.select_map([:a___e, :b___f]).must_equal [[1, 2], [5, 6]]
|
775
|
+
@ds.select_map([:a__a___e, :a__b___f]).must_equal [[1, 2], [5, 6]]
|
776
|
+
@ds.select_map([Sequel.expr(:a__a).as(:e), Sequel.expr(:a__b).as(:f)]).must_equal [[1, 2], [5, 6]]
|
777
|
+
@ds.select_map([Sequel.qualify(:a, :a).as(:e), Sequel.qualify(:a, :b).as(:f)]).must_equal [[1, 2], [5, 6]]
|
778
|
+
@ds.select_map([Sequel.identifier(:a).qualify(:a).as(:e), Sequel.qualify(:a, :b).as(:f)]).must_equal [[1, 2], [5, 6]]
|
779
|
+
end
|
780
|
+
|
781
|
+
it "should have working #select_order_map" do
|
782
|
+
@ds.select_order_map(:a).must_equal [1, 5]
|
783
|
+
@ds.select_order_map(Sequel.desc(:a__b)).must_equal [6, 2]
|
784
|
+
@ds.select_order_map(Sequel.desc(:a__b___e)).must_equal [6, 2]
|
785
|
+
@ds.select_order_map(Sequel.qualify(:a, :b).as(:e)).must_equal [2, 6]
|
786
|
+
@ds.select_order_map([:a]).must_equal [[1], [5]]
|
787
|
+
@ds.select_order_map([Sequel.desc(:a), :b]).must_equal [[5, 6], [1, 2]]
|
788
|
+
|
789
|
+
@ds.select_order_map(:a___e).must_equal [1, 5]
|
790
|
+
@ds.select_order_map(:b___e).must_equal [2, 6]
|
791
|
+
@ds.select_order_map([Sequel.desc(:a___e), :b___f]).must_equal [[5, 6], [1, 2]]
|
792
|
+
@ds.select_order_map([Sequel.desc(:a__a___e), :a__b___f]).must_equal [[5, 6], [1, 2]]
|
793
|
+
@ds.select_order_map([Sequel.desc(:a__a), Sequel.expr(:a__b).as(:f)]).must_equal [[5, 6], [1, 2]]
|
794
|
+
@ds.select_order_map([Sequel.qualify(:a, :a).desc, Sequel.qualify(:a, :b).as(:f)]).must_equal [[5, 6], [1, 2]]
|
795
|
+
@ds.select_order_map([Sequel.identifier(:a).qualify(:a).desc, Sequel.qualify(:a, :b).as(:f)]).must_equal [[5, 6], [1, 2]]
|
796
|
+
end
|
797
|
+
|
798
|
+
it "should have working #select_hash" do
|
799
|
+
@ds.select_hash(:a, :b).must_equal(1=>2, 5=>6)
|
800
|
+
@ds.select_hash(:a__a___e, :b).must_equal(1=>2, 5=>6)
|
801
|
+
@ds.select_hash(Sequel.expr(:a__a).as(:e), :b).must_equal(1=>2, 5=>6)
|
802
|
+
@ds.select_hash(Sequel.qualify(:a, :a).as(:e), :b).must_equal(1=>2, 5=>6)
|
803
|
+
@ds.select_hash(Sequel.identifier(:a).qualify(:a).as(:e), :b).must_equal(1=>2, 5=>6)
|
804
|
+
@ds.select_hash([:a, :c], :b).must_equal([1, 3]=>2, [5, 7]=>6)
|
805
|
+
@ds.select_hash(:a, [:b, :c]).must_equal(1=>[2, 3], 5=>[6, 7])
|
806
|
+
@ds.select_hash([:a, :c], [:b, :d]).must_equal([1, 3]=>[2, 4], [5, 7]=>[6, 8])
|
807
|
+
end
|
808
|
+
|
809
|
+
it "should have working #select_hash_groups" do
|
810
|
+
ds = @ds.order(*@ds.columns)
|
811
|
+
ds.insert(1, 2, 3, 9)
|
812
|
+
ds.select_hash_groups(:a, :d).must_equal(1=>[4, 9], 5=>[8])
|
813
|
+
ds.select_hash_groups(:a__a___e, :d).must_equal(1=>[4, 9], 5=>[8])
|
814
|
+
ds.select_hash_groups(Sequel.expr(:a__a).as(:e), :d).must_equal(1=>[4, 9], 5=>[8])
|
815
|
+
ds.select_hash_groups(Sequel.qualify(:a, :a).as(:e), :d).must_equal(1=>[4, 9], 5=>[8])
|
816
|
+
ds.select_hash_groups(Sequel.identifier(:a).qualify(:a).as(:e), :d).must_equal(1=>[4, 9], 5=>[8])
|
817
|
+
ds.select_hash_groups([:a, :c], :d).must_equal([1, 3]=>[4, 9], [5, 7]=>[8])
|
818
|
+
ds.select_hash_groups(:a, [:b, :d]).must_equal(1=>[[2, 4], [2, 9]], 5=>[[6, 8]])
|
819
|
+
ds.select_hash_groups([:a, :c], [:b, :d]).must_equal([1, 3]=>[[2, 4], [2, 9]], [5, 7]=>[[6, 8]])
|
820
|
+
end
|
821
|
+
end
|
822
|
+
|
823
|
+
describe "Sequel::Dataset DSL support" do
|
824
|
+
before do
|
825
|
+
@db = DB
|
826
|
+
@db.create_table!(:a){Integer :a; Integer :b}
|
827
|
+
@ds = @db[:a].order(:a)
|
828
|
+
end
|
829
|
+
after do
|
830
|
+
@db.drop_table?(:a)
|
831
|
+
end
|
832
|
+
|
833
|
+
it "should work with standard mathematical operators" do
|
834
|
+
@ds.insert(20, 10)
|
835
|
+
@ds.get{a + b}.to_i.must_equal 30
|
836
|
+
@ds.get{a - b}.to_i.must_equal 10
|
837
|
+
@ds.get{a * b}.to_i.must_equal 200
|
838
|
+
@ds.get{a / b}.to_i.must_equal 2
|
839
|
+
end
|
840
|
+
|
841
|
+
it "should work with bitwise AND and OR operators" do
|
842
|
+
@ds.insert(3, 5)
|
843
|
+
@ds.get{a.sql_number | b}.to_i.must_equal 7
|
844
|
+
@ds.get{a.sql_number & b}.to_i.must_equal 1
|
845
|
+
@ds.get{a.sql_number | b | 8}.to_i.must_equal 15
|
846
|
+
@ds.get{a.sql_number & b & 8}.to_i.must_equal 0
|
847
|
+
end
|
848
|
+
|
849
|
+
it "should work with the bitwise compliment operator" do
|
850
|
+
@ds.insert(-3, 3)
|
851
|
+
@ds.get{~a.sql_number}.to_i.must_equal 2
|
852
|
+
@ds.get{~b.sql_number}.to_i.must_equal(-4)
|
853
|
+
end
|
854
|
+
|
855
|
+
it "should work with the bitwise xor operator" do
|
856
|
+
@ds.insert(3, 5)
|
857
|
+
@ds.get{a.sql_number ^ b}.to_i.must_equal 6
|
858
|
+
@ds.get{a.sql_number ^ b ^ 1}.to_i.must_equal 7
|
859
|
+
end
|
860
|
+
|
861
|
+
it "should work with the modulus operator" do
|
862
|
+
@ds.insert(3, 5)
|
863
|
+
@ds.get{a.sql_number % 4}.to_i.must_equal 3
|
864
|
+
@ds.get{b.sql_number % 4}.to_i.must_equal 1
|
865
|
+
@ds.get{a.sql_number % 4 % 2}.to_i.must_equal 1
|
866
|
+
end
|
867
|
+
|
868
|
+
it "should work with inequality operators" do
|
869
|
+
@ds.insert(10, 11)
|
870
|
+
@ds.insert(11, 11)
|
871
|
+
@ds.insert(20, 19)
|
872
|
+
@ds.insert(20, 20)
|
873
|
+
@ds.filter{a > b}.select_order_map(:a).must_equal [20]
|
874
|
+
@ds.filter{a >= b}.select_order_map(:a).must_equal [11, 20, 20]
|
875
|
+
@ds.filter{a < b}.select_order_map(:a).must_equal [10]
|
876
|
+
@ds.filter{a <= b}.select_order_map(:a).must_equal [10, 11, 20]
|
877
|
+
end
|
878
|
+
|
879
|
+
it "should work with casting and string concatentation" do
|
880
|
+
@ds.insert(20, 20)
|
881
|
+
@ds.get{Sequel.cast(a, String).sql_string + Sequel.cast(b, String)}.must_equal '2020'
|
882
|
+
end
|
883
|
+
|
884
|
+
it "should work with ordering" do
|
885
|
+
@ds.insert(10, 20)
|
886
|
+
@ds.insert(20, 10)
|
887
|
+
@ds.order(:a, :b).all.must_equal [{:a=>10, :b=>20}, {:a=>20, :b=>10}]
|
888
|
+
@ds.order(Sequel.asc(:a), Sequel.asc(:b)).all.must_equal [{:a=>10, :b=>20}, {:a=>20, :b=>10}]
|
889
|
+
@ds.order(Sequel.desc(:a), Sequel.desc(:b)).all.must_equal [{:a=>20, :b=>10}, {:a=>10, :b=>20}]
|
890
|
+
end
|
891
|
+
|
892
|
+
it "should work with qualifying" do
|
893
|
+
@ds.insert(10, 20)
|
894
|
+
@ds.get(:a__b).must_equal 20
|
895
|
+
@ds.get{a__b}.must_equal 20
|
896
|
+
@ds.get(Sequel.qualify(:a, :b)).must_equal 20
|
897
|
+
end
|
898
|
+
|
899
|
+
it "should work with aliasing" do
|
900
|
+
@ds.insert(10, 20)
|
901
|
+
@ds.get(:a__b___c).must_equal 20
|
902
|
+
@ds.get{a__b.as(c)}.must_equal 20
|
903
|
+
@ds.get(Sequel.qualify(:a, :b).as(:c)).must_equal 20
|
904
|
+
@ds.get(Sequel.as(:b, :c)).must_equal 20
|
905
|
+
end
|
906
|
+
|
907
|
+
it "should work with selecting all columns of a table" do
|
908
|
+
@ds.insert(20, 10)
|
909
|
+
@ds.select_all(:a).all.must_equal [{:a=>20, :b=>10}]
|
910
|
+
end
|
911
|
+
|
912
|
+
it "should work with ranges as hash values" do
|
913
|
+
@ds.insert(20, 10)
|
914
|
+
@ds.filter(:a=>(10..30)).all.must_equal [{:a=>20, :b=>10}]
|
915
|
+
@ds.filter(:a=>(25..30)).all.must_equal []
|
916
|
+
@ds.filter(:a=>(10..15)).all.must_equal []
|
917
|
+
@ds.exclude(:a=>(10..30)).all.must_equal []
|
918
|
+
@ds.exclude(:a=>(25..30)).all.must_equal [{:a=>20, :b=>10}]
|
919
|
+
@ds.exclude(:a=>(10..15)).all.must_equal [{:a=>20, :b=>10}]
|
920
|
+
end
|
921
|
+
|
922
|
+
it "should work with nil as hash value" do
|
923
|
+
@ds.insert(20, nil)
|
924
|
+
@ds.filter(:a=>nil).all.must_equal []
|
925
|
+
@ds.filter(:b=>nil).all.must_equal [{:a=>20, :b=>nil}]
|
926
|
+
@ds.exclude(:b=>nil).all.must_equal []
|
927
|
+
@ds.exclude(:a=>nil).all.must_equal [{:a=>20, :b=>nil}]
|
928
|
+
end
|
929
|
+
|
930
|
+
it "should work with arrays as hash values" do
|
931
|
+
@ds.insert(20, 10)
|
932
|
+
@ds.filter(:a=>[10]).all.must_equal []
|
933
|
+
@ds.filter(:a=>[20, 10]).all.must_equal [{:a=>20, :b=>10}]
|
934
|
+
@ds.exclude(:a=>[10]).all.must_equal [{:a=>20, :b=>10}]
|
935
|
+
@ds.exclude(:a=>[20, 10]).all.must_equal []
|
936
|
+
end
|
937
|
+
|
938
|
+
it "should work with ranges as hash values" do
|
939
|
+
@ds.insert(20, 10)
|
940
|
+
@ds.filter(:a=>(10..30)).all.must_equal [{:a=>20, :b=>10}]
|
941
|
+
@ds.filter(:a=>(25..30)).all.must_equal []
|
942
|
+
@ds.filter(:a=>(10..15)).all.must_equal []
|
943
|
+
@ds.exclude(:a=>(10..30)).all.must_equal []
|
944
|
+
@ds.exclude(:a=>(25..30)).all.must_equal [{:a=>20, :b=>10}]
|
945
|
+
@ds.exclude(:a=>(10..15)).all.must_equal [{:a=>20, :b=>10}]
|
946
|
+
end
|
947
|
+
|
948
|
+
it "should work with CASE statements" do
|
949
|
+
@ds.insert(20, 10)
|
950
|
+
@ds.filter(Sequel.case({{:a=>20}=>20}, 0) > 0).all.must_equal [{:a=>20, :b=>10}]
|
951
|
+
@ds.filter(Sequel.case({{:a=>15}=>20}, 0) > 0).all.must_equal []
|
952
|
+
@ds.filter(Sequel.case({20=>20}, 0, :a) > 0).all.must_equal [{:a=>20, :b=>10}]
|
953
|
+
@ds.filter(Sequel.case({15=>20}, 0, :a) > 0).all.must_equal []
|
954
|
+
end
|
955
|
+
|
956
|
+
it "should work with multiple value arrays" do
|
957
|
+
@ds.insert(20, 10)
|
958
|
+
@ds.quote_identifiers = false
|
959
|
+
@ds.filter([:a, :b]=>[[20, 10]]).all.must_equal [{:a=>20, :b=>10}]
|
960
|
+
@ds.filter([:a, :b]=>[[10, 20]]).all.must_equal []
|
961
|
+
@ds.filter([:a, :b]=>[[20, 10], [1, 2]]).all.must_equal [{:a=>20, :b=>10}]
|
962
|
+
@ds.filter([:a, :b]=>[[10, 10], [20, 20]]).all.must_equal []
|
963
|
+
|
964
|
+
@ds.exclude([:a, :b]=>[[20, 10]]).all.must_equal []
|
965
|
+
@ds.exclude([:a, :b]=>[[10, 20]]).all.must_equal [{:a=>20, :b=>10}]
|
966
|
+
@ds.exclude([:a, :b]=>[[20, 10], [1, 2]]).all.must_equal []
|
967
|
+
@ds.exclude([:a, :b]=>[[10, 10], [20, 20]]).all.must_equal [{:a=>20, :b=>10}]
|
968
|
+
end
|
969
|
+
|
970
|
+
it "should work with IN/NOT in with datasets" do
|
971
|
+
@ds.insert(20, 10)
|
972
|
+
ds = @ds.unordered
|
973
|
+
@ds.quote_identifiers = false
|
974
|
+
|
975
|
+
@ds.filter(:a=>ds.select(:a)).all.must_equal [{:a=>20, :b=>10}]
|
976
|
+
@ds.filter(:a=>ds.select(:a).where(:a=>15)).all.must_equal []
|
977
|
+
@ds.exclude(:a=>ds.select(:a)).all.must_equal []
|
978
|
+
@ds.exclude(:a=>ds.select(:a).where(:a=>15)).all.must_equal [{:a=>20, :b=>10}]
|
979
|
+
|
980
|
+
@ds.filter([:a, :b]=>ds.select(:a, :b)).all.must_equal [{:a=>20, :b=>10}]
|
981
|
+
@ds.filter([:a, :b]=>ds.select(:b, :a)).all.must_equal []
|
982
|
+
@ds.exclude([:a, :b]=>ds.select(:a, :b)).all.must_equal []
|
983
|
+
@ds.exclude([:a, :b]=>ds.select(:b, :a)).all.must_equal [{:a=>20, :b=>10}]
|
984
|
+
|
985
|
+
@ds.filter([:a, :b]=>ds.select(:a, :b).where(:a=>15)).all.must_equal []
|
986
|
+
@ds.exclude([:a, :b]=>ds.select(:a, :b).where(:a=>15)).all.must_equal [{:a=>20, :b=>10}]
|
987
|
+
end
|
988
|
+
|
989
|
+
it "should work empty arrays" do
|
990
|
+
@ds.insert(20, 10)
|
991
|
+
@ds.filter(:a=>[]).all.must_equal []
|
992
|
+
@ds.exclude(:a=>[]).all.must_equal [{:a=>20, :b=>10}]
|
993
|
+
@ds.filter([:a, :b]=>[]).all.must_equal []
|
994
|
+
@ds.exclude([:a, :b]=>[]).all.must_equal [{:a=>20, :b=>10}]
|
995
|
+
end
|
996
|
+
|
997
|
+
it "should work empty arrays with nulls" do
|
998
|
+
@ds = @ds.extension(:empty_array_consider_nulls)
|
999
|
+
@ds.insert(nil, nil)
|
1000
|
+
@ds.filter(:a=>[]).all.must_equal []
|
1001
|
+
@ds.exclude(:a=>[]).all.must_equal []
|
1002
|
+
@ds.filter([:a, :b]=>[]).all.must_equal []
|
1003
|
+
@ds.exclude([:a, :b]=>[]).all.must_equal []
|
1004
|
+
|
1005
|
+
pr = proc{|r| r.is_a?(Integer) ? (r != 0) : r}
|
1006
|
+
pr[@ds.get(Sequel.expr(:a=>[]))].must_equal nil
|
1007
|
+
pr[@ds.get(~Sequel.expr(:a=>[]))].must_equal nil
|
1008
|
+
pr[@ds.get(Sequel.expr([:a, :b]=>[]))].must_equal nil
|
1009
|
+
pr[@ds.get(~Sequel.expr([:a, :b]=>[]))].must_equal nil
|
1010
|
+
end
|
1011
|
+
|
1012
|
+
it "should work empty arrays with nulls and the empty_array_ignore_nulls extension" do
|
1013
|
+
ds = @ds
|
1014
|
+
ds.insert(nil, nil)
|
1015
|
+
ds.filter(:a=>[]).all.must_equal []
|
1016
|
+
ds.exclude(:a=>[]).all.must_equal [{:a=>nil, :b=>nil}]
|
1017
|
+
ds.filter([:a, :b]=>[]).all.must_equal []
|
1018
|
+
ds.exclude([:a, :b]=>[]).all.must_equal [{:a=>nil, :b=>nil}]
|
1019
|
+
|
1020
|
+
pr = proc{|r| r.is_a?(Integer) ? (r != 0) : r}
|
1021
|
+
pr[ds.get(Sequel.expr(:a=>[]))].must_equal false
|
1022
|
+
pr[ds.get(~Sequel.expr(:a=>[]))].must_equal true
|
1023
|
+
pr[ds.get(Sequel.expr([:a, :b]=>[]))].must_equal false
|
1024
|
+
pr[ds.get(~Sequel.expr([:a, :b]=>[]))].must_equal true
|
1025
|
+
end
|
1026
|
+
|
1027
|
+
it "should work multiple conditions" do
|
1028
|
+
@ds.insert(20, 10)
|
1029
|
+
@ds.filter(:a=>20, :b=>10).all.must_equal [{:a=>20, :b=>10}]
|
1030
|
+
@ds.filter([[:a, 20], [:b, 10]]).all.must_equal [{:a=>20, :b=>10}]
|
1031
|
+
@ds.filter({:a=>20}, {:b=>10}).all.must_equal [{:a=>20, :b=>10}]
|
1032
|
+
@ds.filter(Sequel.|({:a=>20}, {:b=>5})).all.must_equal [{:a=>20, :b=>10}]
|
1033
|
+
@ds.filter(Sequel.~(:a=>10)).all.must_equal [{:a=>20, :b=>10}]
|
1034
|
+
end
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
describe "SQL Extract Function" do
|
1038
|
+
before do
|
1039
|
+
@db = DB
|
1040
|
+
@db.create_table!(:a){DateTime :a}
|
1041
|
+
@ds = @db[:a].order(:a)
|
1042
|
+
end
|
1043
|
+
after do
|
1044
|
+
@db.drop_table?(:a)
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
it "should return the part of the datetime asked for" do
|
1048
|
+
t = Time.now
|
1049
|
+
def @ds.supports_timestamp_timezones?() false end
|
1050
|
+
@ds.insert(t)
|
1051
|
+
@ds.get{a.extract(:year)}.must_equal t.year
|
1052
|
+
@ds.get{a.extract(:month)}.must_equal t.month
|
1053
|
+
@ds.get{a.extract(:day)}.must_equal t.day
|
1054
|
+
@ds.get{a.extract(:hour)}.must_equal t.hour
|
1055
|
+
@ds.get{a.extract(:minute)}.must_equal t.min
|
1056
|
+
@ds.get{a.extract(:second)}.to_i.must_equal t.sec
|
1057
|
+
end
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
describe "Dataset string methods" do
|
1061
|
+
before do
|
1062
|
+
@db = DB
|
1063
|
+
@db.create_table!(:a) do
|
1064
|
+
String :a
|
1065
|
+
String :b
|
1066
|
+
end
|
1067
|
+
@ds = @db[:a].order(:a)
|
1068
|
+
end
|
1069
|
+
after do
|
1070
|
+
@db.drop_table?(:a)
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
it "#grep should return matching rows" do
|
1074
|
+
@ds.insert('foo', 'bar')
|
1075
|
+
@ds.grep(:a, 'foo').all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1076
|
+
@ds.grep(:b, 'foo').all.must_equal []
|
1077
|
+
@ds.grep(:b, 'bar').all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1078
|
+
@ds.grep(:a, 'bar').all.must_equal []
|
1079
|
+
@ds.grep([:a, :b], %w'foo bar').all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1080
|
+
@ds.grep([:a, :b], %w'boo far').all.must_equal []
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
it "#grep should work with :all_patterns and :all_columns options" do
|
1084
|
+
@ds.insert('foo bar', ' ')
|
1085
|
+
@ds.insert('foo d', 'bar')
|
1086
|
+
@ds.insert('foo e', ' ')
|
1087
|
+
@ds.insert(' ', 'bar')
|
1088
|
+
@ds.insert('foo f', 'baz')
|
1089
|
+
@ds.insert('foo baz', 'bar baz')
|
1090
|
+
@ds.insert('foo boo', 'boo foo')
|
1091
|
+
|
1092
|
+
@ds.grep([:a, :b], %w'%foo% %bar%', :all_patterns=>true).all.must_equal [{:a=>'foo bar', :b=>' '}, {:a=>'foo baz', :b=>'bar baz'}, {:a=>'foo d', :b=>'bar'}]
|
1093
|
+
@ds.grep([:a, :b], %w'%foo% %bar% %blob%', :all_patterns=>true).all.must_equal []
|
1094
|
+
|
1095
|
+
@ds.grep([:a, :b], %w'%bar% %foo%', :all_columns=>true).all.must_equal [{:a=>"foo baz", :b=>"bar baz"}, {:a=>"foo boo", :b=>"boo foo"}, {:a=>"foo d", :b=>"bar"}]
|
1096
|
+
@ds.grep([:a, :b], %w'%baz%', :all_columns=>true).all.must_equal [{:a=>'foo baz', :b=>'bar baz'}]
|
1097
|
+
|
1098
|
+
@ds.grep([:a, :b], %w'%baz% %foo%', :all_columns=>true, :all_patterns=>true).all.must_equal []
|
1099
|
+
@ds.grep([:a, :b], %w'%boo% %foo%', :all_columns=>true, :all_patterns=>true).all.must_equal [{:a=>'foo boo', :b=>'boo foo'}]
|
1100
|
+
end
|
1101
|
+
|
1102
|
+
it "#like should return matching rows" do
|
1103
|
+
@ds.insert('foo', 'bar')
|
1104
|
+
@ds.filter(Sequel.expr(:a).like('foo')).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1105
|
+
@ds.filter(Sequel.expr(:a).like('bar')).all.must_equal []
|
1106
|
+
@ds.filter(Sequel.expr(:a).like('foo', 'bar')).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1107
|
+
@ds.exclude(Sequel.expr(:a).like('foo')).all.must_equal []
|
1108
|
+
@ds.exclude(Sequel.expr(:a).like('bar')).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1109
|
+
@ds.exclude(Sequel.expr(:a).like('foo', 'bar')).all.must_equal []
|
1110
|
+
end
|
1111
|
+
|
1112
|
+
it "#like should be case sensitive" do
|
1113
|
+
@ds.insert('foo', 'bar')
|
1114
|
+
@ds.filter(Sequel.expr(:a).like('Foo')).all.must_equal []
|
1115
|
+
@ds.filter(Sequel.expr(:b).like('baR')).all.must_equal []
|
1116
|
+
@ds.filter(Sequel.expr(:a).like('FOO', 'BAR')).all.must_equal []
|
1117
|
+
@ds.exclude(Sequel.expr(:a).like('Foo')).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1118
|
+
@ds.exclude(Sequel.expr(:b).like('baR')).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1119
|
+
@ds.exclude(Sequel.expr(:a).like('FOO', 'BAR')).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1120
|
+
end
|
1121
|
+
|
1122
|
+
it "#ilike should return matching rows, in a case insensitive manner" do
|
1123
|
+
@ds.insert('foo', 'bar')
|
1124
|
+
@ds.filter(Sequel.expr(:a).ilike('Foo')).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1125
|
+
@ds.filter(Sequel.expr(:a).ilike('baR')).all.must_equal []
|
1126
|
+
@ds.filter(Sequel.expr(:a).ilike('FOO', 'BAR')).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1127
|
+
@ds.exclude(Sequel.expr(:a).ilike('Foo')).all.must_equal []
|
1128
|
+
@ds.exclude(Sequel.expr(:a).ilike('baR')).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1129
|
+
@ds.exclude(Sequel.expr(:a).ilike('FOO', 'BAR')).all.must_equal []
|
1130
|
+
end
|
1131
|
+
|
1132
|
+
it "#like with regexp return matching rows" do
|
1133
|
+
@ds.insert('foo', 'bar')
|
1134
|
+
@ds.filter(Sequel.expr(:a).like(/fo/)).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1135
|
+
@ds.filter(Sequel.expr(:a).like(/fo$/)).all.must_equal []
|
1136
|
+
@ds.filter(Sequel.expr(:a).like(/fo/, /ar/)).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1137
|
+
@ds.exclude(Sequel.expr(:a).like(/fo/)).all.must_equal []
|
1138
|
+
@ds.exclude(Sequel.expr(:a).like(/fo$/)).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1139
|
+
@ds.exclude(Sequel.expr(:a).like(/fo/, /ar/)).all.must_equal []
|
1140
|
+
end
|
1141
|
+
|
1142
|
+
it "#like with regexp should be case sensitive if regexp is case sensitive" do
|
1143
|
+
@ds.insert('foo', 'bar')
|
1144
|
+
@ds.filter(Sequel.expr(:a).like(/Fo/)).all.must_equal []
|
1145
|
+
@ds.filter(Sequel.expr(:b).like(/baR/)).all.must_equal []
|
1146
|
+
@ds.filter(Sequel.expr(:a).like(/FOO/, /BAR/)).all.must_equal []
|
1147
|
+
@ds.exclude(Sequel.expr(:a).like(/Fo/)).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1148
|
+
@ds.exclude(Sequel.expr(:b).like(/baR/)).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1149
|
+
@ds.exclude(Sequel.expr(:a).like(/FOO/, /BAR/)).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1150
|
+
|
1151
|
+
@ds.filter(Sequel.expr(:a).like(/Fo/i)).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1152
|
+
@ds.filter(Sequel.expr(:b).like(/baR/i)).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1153
|
+
@ds.filter(Sequel.expr(:a).like(/FOO/i, /BAR/i)).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1154
|
+
@ds.exclude(Sequel.expr(:a).like(/Fo/i)).all.must_equal []
|
1155
|
+
@ds.exclude(Sequel.expr(:b).like(/baR/i)).all.must_equal []
|
1156
|
+
@ds.exclude(Sequel.expr(:a).like(/FOO/i, /BAR/i)).all.must_equal []
|
1157
|
+
end
|
1158
|
+
|
1159
|
+
it "#ilike with regexp should return matching rows, in a case insensitive manner" do
|
1160
|
+
@ds.insert('foo', 'bar')
|
1161
|
+
@ds.filter(Sequel.expr(:a).ilike(/Fo/)).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1162
|
+
@ds.filter(Sequel.expr(:b).ilike(/baR/)).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1163
|
+
@ds.filter(Sequel.expr(:a).ilike(/FOO/, /BAR/)).all.must_equal [{:a=>'foo', :b=>'bar'}]
|
1164
|
+
@ds.exclude(Sequel.expr(:a).ilike(/Fo/)).all.must_equal []
|
1165
|
+
@ds.exclude(Sequel.expr(:b).ilike(/baR/)).all.must_equal []
|
1166
|
+
@ds.exclude(Sequel.expr(:a).ilike(/FOO/, /BAR/)).all.must_equal []
|
1167
|
+
end
|
1168
|
+
|
1169
|
+
it "should work with strings created with Sequel.join" do
|
1170
|
+
@ds.insert('foo', 'bar')
|
1171
|
+
@ds.get(Sequel.join([:a, "bar"])).must_equal 'foobar'
|
1172
|
+
@ds.get(Sequel.join(["foo", :b], ' ')).must_equal 'foo bar'
|
1173
|
+
end
|
1174
|
+
end
|
1175
|
+
|
1176
|
+
describe "Dataset identifier methods" do
|
1177
|
+
before(:all) do
|
1178
|
+
class ::String
|
1179
|
+
def uprev
|
1180
|
+
upcase.reverse
|
1181
|
+
end
|
1182
|
+
end
|
1183
|
+
@db = DB
|
1184
|
+
@db.create_table!(:a){Integer :ab}
|
1185
|
+
@db[:a].insert(1)
|
1186
|
+
end
|
1187
|
+
before do
|
1188
|
+
@ds = @db[:a].order(:ab)
|
1189
|
+
end
|
1190
|
+
after(:all) do
|
1191
|
+
@db.drop_table?(:a)
|
1192
|
+
end
|
1193
|
+
|
1194
|
+
it "#identifier_output_method should change how identifiers are output" do
|
1195
|
+
@ds.identifier_output_method = :upcase
|
1196
|
+
@ds.first.must_equal(:AB=>1)
|
1197
|
+
@ds.identifier_output_method = :uprev
|
1198
|
+
@ds.first.must_equal(:BA=>1)
|
1199
|
+
end
|
1200
|
+
|
1201
|
+
it "should work with a nil identifier_output_method" do
|
1202
|
+
@ds.identifier_output_method = nil
|
1203
|
+
[{:ab=>1}, {:AB=>1}].must_include(@ds.first)
|
1204
|
+
end
|
1205
|
+
|
1206
|
+
it "should work when not quoting identifiers" do
|
1207
|
+
@ds.quote_identifiers = false
|
1208
|
+
@ds.first.must_equal(:ab=>1)
|
1209
|
+
end
|
1210
|
+
end
|
1211
|
+
|
1212
|
+
describe "Dataset defaults and overrides" do
|
1213
|
+
before do
|
1214
|
+
@db = DB
|
1215
|
+
@db.create_table!(:a){Integer :a}
|
1216
|
+
@ds = @db[:a].order(:a).extension(:set_overrides)
|
1217
|
+
end
|
1218
|
+
after do
|
1219
|
+
@db.drop_table?(:a)
|
1220
|
+
end
|
1221
|
+
|
1222
|
+
it "#set_defaults should set defaults that can be overridden" do
|
1223
|
+
@ds = @ds.set_defaults(:a=>10)
|
1224
|
+
@ds.insert
|
1225
|
+
@ds.insert(:a=>20)
|
1226
|
+
@ds.all.must_equal [{:a=>10}, {:a=>20}]
|
1227
|
+
end
|
1228
|
+
|
1229
|
+
it "#set_overrides should set defaults that cannot be overridden" do
|
1230
|
+
@ds = @ds.set_overrides(:a=>10)
|
1231
|
+
@ds.insert
|
1232
|
+
@ds.insert(:a=>20)
|
1233
|
+
@ds.all.must_equal [{:a=>10}, {:a=>10}]
|
1234
|
+
end
|
1235
|
+
end
|
1236
|
+
|
1237
|
+
describe "Emulated functions" do
|
1238
|
+
before do
|
1239
|
+
@db = DB
|
1240
|
+
@db.create_table!(:a){String :a}
|
1241
|
+
@ds = @db[:a]
|
1242
|
+
end
|
1243
|
+
after do
|
1244
|
+
@db.drop_table?(:a)
|
1245
|
+
end
|
1246
|
+
|
1247
|
+
it "Sequel.char_length should return the length of characters in the string" do
|
1248
|
+
@ds.get(Sequel.char_length(:a)).must_equal nil
|
1249
|
+
@ds.insert(:a=>'foo')
|
1250
|
+
@ds.get(Sequel.char_length(:a)).must_equal 3
|
1251
|
+
end
|
1252
|
+
|
1253
|
+
it "Sequel.char_length should return the length of characters in the string including trailing blanks" do
|
1254
|
+
@ds.insert(:a=>' foo22 ')
|
1255
|
+
@ds.get(Sequel.char_length(:a)).must_equal 7
|
1256
|
+
end
|
1257
|
+
|
1258
|
+
it "Sequel.trim should return the string with spaces trimmed from both sides" do
|
1259
|
+
@ds.get(Sequel.trim(:a)).must_equal nil
|
1260
|
+
@ds.insert(:a=>'foo')
|
1261
|
+
@ds.get(Sequel.trim(:a)).must_equal 'foo'
|
1262
|
+
end
|
1263
|
+
|
1264
|
+
it "Sequel.trim should return the string with spaces trimmed from both sides" do
|
1265
|
+
@ds.insert(:a=>' foo22 ')
|
1266
|
+
@ds.get(Sequel.trim(:a)).must_equal 'foo22'
|
1267
|
+
end
|
1268
|
+
end
|