sequel 3.1.0 → 3.2.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 +76 -0
- data/Rakefile +2 -2
- data/bin/sequel +9 -4
- data/doc/opening_databases.rdoc +279 -0
- data/doc/release_notes/3.2.0.txt +268 -0
- data/doc/virtual_rows.rdoc +42 -51
- data/lib/sequel/adapters/ado.rb +2 -5
- data/lib/sequel/adapters/db2.rb +5 -0
- data/lib/sequel/adapters/do.rb +3 -0
- data/lib/sequel/adapters/firebird.rb +6 -4
- data/lib/sequel/adapters/informix.rb +5 -3
- data/lib/sequel/adapters/jdbc.rb +10 -8
- data/lib/sequel/adapters/jdbc/h2.rb +17 -4
- data/lib/sequel/adapters/mysql.rb +6 -19
- data/lib/sequel/adapters/odbc.rb +14 -18
- data/lib/sequel/adapters/openbase.rb +8 -0
- data/lib/sequel/adapters/shared/mssql.rb +14 -8
- data/lib/sequel/adapters/shared/mysql.rb +53 -28
- data/lib/sequel/adapters/shared/oracle.rb +21 -12
- data/lib/sequel/adapters/shared/postgres.rb +46 -26
- data/lib/sequel/adapters/shared/progress.rb +10 -5
- data/lib/sequel/adapters/shared/sqlite.rb +28 -12
- data/lib/sequel/adapters/sqlite.rb +4 -3
- data/lib/sequel/adapters/utils/stored_procedures.rb +18 -9
- data/lib/sequel/connection_pool.rb +4 -3
- data/lib/sequel/database.rb +110 -10
- data/lib/sequel/database/schema_sql.rb +12 -3
- data/lib/sequel/dataset.rb +40 -3
- data/lib/sequel/dataset/convenience.rb +0 -11
- data/lib/sequel/dataset/graph.rb +25 -11
- data/lib/sequel/dataset/sql.rb +176 -68
- data/lib/sequel/extensions/migration.rb +37 -21
- data/lib/sequel/extensions/schema_dumper.rb +8 -61
- data/lib/sequel/model.rb +3 -3
- data/lib/sequel/model/associations.rb +9 -1
- data/lib/sequel/model/base.rb +8 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/sql.rb +125 -18
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/ado_spec.rb +1 -0
- data/spec/adapters/firebird_spec.rb +1 -0
- data/spec/adapters/informix_spec.rb +1 -0
- data/spec/adapters/mysql_spec.rb +23 -8
- data/spec/adapters/oracle_spec.rb +1 -0
- data/spec/adapters/postgres_spec.rb +52 -4
- data/spec/adapters/spec_helper.rb +2 -2
- data/spec/adapters/sqlite_spec.rb +2 -1
- data/spec/core/connection_pool_spec.rb +16 -0
- data/spec/core/database_spec.rb +174 -0
- data/spec/core/dataset_spec.rb +121 -26
- data/spec/core/expression_filters_spec.rb +156 -0
- data/spec/core/object_graph_spec.rb +20 -1
- data/spec/core/schema_spec.rb +5 -5
- data/spec/extensions/migration_spec.rb +140 -74
- data/spec/extensions/schema_dumper_spec.rb +3 -69
- data/spec/extensions/single_table_inheritance_spec.rb +6 -0
- data/spec/integration/dataset_test.rb +84 -2
- data/spec/integration/schema_test.rb +24 -5
- data/spec/integration/spec_helper.rb +8 -6
- data/spec/model/eager_loading_spec.rb +9 -0
- data/spec/model/record_spec.rb +35 -8
- metadata +8 -7
- data/lib/sequel/adapters/utils/date_format.rb +0 -21
- data/lib/sequel/adapters/utils/savepoint_transactions.rb +0 -80
- data/lib/sequel/adapters/utils/unsupported.rb +0 -50
@@ -6,10 +6,6 @@
|
|
6
6
|
|
7
7
|
module Sequel
|
8
8
|
class Database
|
9
|
-
POSTGRES_DEFAULT_RE = /\A(?:B?('.*')::[^']+|\((-?\d+(?:\.\d+)?)\))\z/
|
10
|
-
MYSQL_TIMESTAMP_RE = /\ACURRENT_(?:DATE|TIMESTAMP)?\z/
|
11
|
-
STRING_DEFAULT_RE = /\A'(.*)'\z/
|
12
|
-
|
13
9
|
# Dump indexes for all tables as a migration. This complements
|
14
10
|
# the :indexes=>false option to dump_schema_migration. Options:
|
15
11
|
# * :same_db - Create a dump for the same database type, so
|
@@ -72,65 +68,12 @@ END_MIG
|
|
72
68
|
end
|
73
69
|
|
74
70
|
private
|
75
|
-
|
76
|
-
# Convert the given default, which should be a database specific string, into
|
77
|
-
# a ruby object.
|
78
|
-
def column_schema_to_ruby_default(default, type, options)
|
79
|
-
return if default.nil?
|
80
|
-
orig_default = default
|
81
|
-
if database_type == :postgres and m = POSTGRES_DEFAULT_RE.match(default)
|
82
|
-
default = m[1] || m[2]
|
83
|
-
end
|
84
|
-
if [:string, :blob, :date, :datetime, :time].include?(type)
|
85
|
-
if database_type == :mysql
|
86
|
-
if [:date, :datetime, :time].include?(type) && MYSQL_TIMESTAMP_RE.match(default)
|
87
|
-
return column_schema_to_ruby_default_fallback(default, options)
|
88
|
-
end
|
89
|
-
orig_default = default = "'#{default.gsub("'", "''").gsub('\\', '\\\\')}'"
|
90
|
-
end
|
91
|
-
if m = STRING_DEFAULT_RE.match(default)
|
92
|
-
default = m[1].gsub("''", "'")
|
93
|
-
else
|
94
|
-
return column_schema_to_ruby_default_fallback(default, options)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
res = begin
|
98
|
-
case type
|
99
|
-
when :boolean
|
100
|
-
case default
|
101
|
-
when /[f0]/i
|
102
|
-
false
|
103
|
-
when /[t1]/i
|
104
|
-
true
|
105
|
-
end
|
106
|
-
when :string
|
107
|
-
default
|
108
|
-
when :blob
|
109
|
-
Sequel::SQL::Blob.new(default)
|
110
|
-
when :integer
|
111
|
-
Integer(default)
|
112
|
-
when :float
|
113
|
-
Float(default)
|
114
|
-
when :date
|
115
|
-
Sequel.string_to_date(default)
|
116
|
-
when :datetime
|
117
|
-
DateTime.parse(default)
|
118
|
-
when :time
|
119
|
-
Sequel.string_to_time(default)
|
120
|
-
when :decimal
|
121
|
-
BigDecimal.new(default)
|
122
|
-
end
|
123
|
-
rescue
|
124
|
-
nil
|
125
|
-
end
|
126
|
-
res.nil? ? column_schema_to_ruby_default_fallback(orig_default, options) : res
|
127
|
-
end
|
128
71
|
|
129
|
-
# If
|
72
|
+
# If a database default exists and can't be converted, return the string with the inspect
|
130
73
|
# method modified so that .lit is always appended after it, only if the
|
131
74
|
# :same_db option is used.
|
132
75
|
def column_schema_to_ruby_default_fallback(default, options)
|
133
|
-
if options[:same_db]
|
76
|
+
if options[:same_db] && default.is_a?(String)
|
134
77
|
default = default.to_s
|
135
78
|
def default.inspect
|
136
79
|
"#{super}.lit"
|
@@ -148,8 +91,12 @@ END_MIG
|
|
148
91
|
col_opts = options[:same_db] ? {:type=>schema[:db_type]} : column_schema_to_ruby_type(schema)
|
149
92
|
type = col_opts.delete(:type)
|
150
93
|
col_opts.delete(:size) if col_opts[:size].nil?
|
151
|
-
|
152
|
-
|
94
|
+
col_opts[:default] = if schema[:ruby_default].nil?
|
95
|
+
column_schema_to_ruby_default_fallback(schema[:default], options)
|
96
|
+
else
|
97
|
+
schema[:ruby_default]
|
98
|
+
end
|
99
|
+
col_opts.delete(:default) if col_opts[:default].nil?
|
153
100
|
col_opts[:null] = false if schema[:allow_null] == false
|
154
101
|
[:column, name, type, col_opts]
|
155
102
|
end
|
data/lib/sequel/model.rb
CHANGED
@@ -40,13 +40,13 @@ module Sequel
|
|
40
40
|
# Class methods added to model that call the method of the same name on the dataset
|
41
41
|
DATASET_METHODS = %w'<< all avg count delete distinct eager eager_graph
|
42
42
|
each each_page empty? except exclude filter first from from_self
|
43
|
-
full_outer_join get graph group group_and_count group_by having import
|
43
|
+
full_outer_join get graph grep group group_and_count group_by having import
|
44
44
|
inner_join insert insert_multiple intersect interval join join_table
|
45
45
|
last left_outer_join limit map multi_insert naked order order_by
|
46
|
-
order_more paginate print query range reverse_order right_outer_join
|
46
|
+
order_more paginate print qualify query range reverse_order right_outer_join
|
47
47
|
select select_all select_more server set set_graph_aliases
|
48
48
|
single_value to_csv to_hash union unfiltered unordered
|
49
|
-
update where with_sql'.map{|x| x.to_sym}
|
49
|
+
update where with with_sql'.map{|x| x.to_sym}
|
50
50
|
|
51
51
|
# Class instance variables to set to nil when a subclass is created, for -w compliance
|
52
52
|
EMPTY_INSTANCE_VARIABLES = [:@overridable_methods_module, :@db]
|
@@ -1104,6 +1104,14 @@ module Sequel
|
|
1104
1104
|
ds.eager_graph_associations(ds, model, table_name, [], *associations)
|
1105
1105
|
end
|
1106
1106
|
|
1107
|
+
# Do not attempt to split the result set into associations,
|
1108
|
+
# just return results as simple objects. This is useful if you
|
1109
|
+
# want to use eager_graph as a shortcut to have all of the joins
|
1110
|
+
# and aliasing set up, but want to do something else with the dataset.
|
1111
|
+
def ungraphed
|
1112
|
+
super.clone(:eager_graph=>nil)
|
1113
|
+
end
|
1114
|
+
|
1107
1115
|
protected
|
1108
1116
|
|
1109
1117
|
# Call graph on the association with the correct arguments,
|
@@ -1240,7 +1248,7 @@ module Sequel
|
|
1240
1248
|
end
|
1241
1249
|
table_alias
|
1242
1250
|
end
|
1243
|
-
|
1251
|
+
|
1244
1252
|
private
|
1245
1253
|
|
1246
1254
|
# Make sure the association is valid for this model, and return the related AssociationReflection.
|
data/lib/sequel/model/base.rb
CHANGED
@@ -563,6 +563,13 @@ module Sequel
|
|
563
563
|
def associations
|
564
564
|
@associations ||= {}
|
565
565
|
end
|
566
|
+
|
567
|
+
# The autoincrementing primary key for this model object. Should be
|
568
|
+
# overridden if you have a composite primary key with one part of it
|
569
|
+
# being autoincrementing.
|
570
|
+
def autoincrementing_primary_key
|
571
|
+
primary_key
|
572
|
+
end
|
566
573
|
|
567
574
|
# The columns that have been updated. This isn't completely accurate,
|
568
575
|
# see Model#[]=.
|
@@ -794,7 +801,7 @@ module Sequel
|
|
794
801
|
iid = ds.insert(@values)
|
795
802
|
# if we have a regular primary key and it's not set in @values,
|
796
803
|
# we assume it's the last inserted id
|
797
|
-
if (pk =
|
804
|
+
if (pk = autoincrementing_primary_key) && pk.is_a?(Symbol) && !@values[pk]
|
798
805
|
@values[pk] = iid
|
799
806
|
end
|
800
807
|
@this = nil if pk
|
@@ -55,7 +55,7 @@ module Sequel
|
|
55
55
|
# Set the sti_key column to the name of the model.
|
56
56
|
def before_create
|
57
57
|
return false if super == false
|
58
|
-
send("#{model.sti_key}=", model.name.to_s)
|
58
|
+
send("#{model.sti_key}=", model.name.to_s) unless send(model.sti_key)
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
data/lib/sequel/sql.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
module Sequel
|
2
|
+
class LiteralString < ::String
|
3
|
+
end
|
4
|
+
|
2
5
|
# The SQL module holds classes whose instances represent SQL fragments.
|
3
6
|
# It also holds modules that are included in core ruby classes that
|
4
7
|
# make Sequel a friendly DSL.
|
5
8
|
module SQL
|
9
|
+
|
6
10
|
### Parent Classes ###
|
7
11
|
|
8
12
|
# Classes/Modules aren't an alphabetical order due to the fact that
|
@@ -693,6 +697,9 @@ module Sequel
|
|
693
697
|
include StringConcatenationMethods
|
694
698
|
include InequalityMethods
|
695
699
|
include NoBooleanInputMethods
|
700
|
+
|
701
|
+
# Map of [regexp, case_insenstive] to ComplexExpression operator
|
702
|
+
LIKE_MAP = {[true, true]=>:'~*', [true, false]=>:~, [false, true]=>:ILIKE, [false, false]=>:LIKE}
|
696
703
|
|
697
704
|
# Creates a SQL pattern match exprssion. left (l) is the SQL string we
|
698
705
|
# are matching against, and ces are the patterns we are matching.
|
@@ -709,13 +716,27 @@ module Sequel
|
|
709
716
|
# if a case insensitive regular expression is used (//i), that particular
|
710
717
|
# pattern which will always be case insensitive.
|
711
718
|
def self.like(l, *ces)
|
712
|
-
|
719
|
+
l, lre, lci = like_element(l)
|
720
|
+
lci = (ces.last.is_a?(Hash) ? ces.pop : {})[:case_insensitive] ? true : lci
|
713
721
|
ces.collect! do |ce|
|
714
|
-
|
715
|
-
BooleanExpression.new(
|
722
|
+
r, rre, rci = like_element(ce)
|
723
|
+
BooleanExpression.new(LIKE_MAP[[lre||rre, lci||rci]], l, r)
|
716
724
|
end
|
717
725
|
ces.length == 1 ? ces.at(0) : BooleanExpression.new(:OR, *ces)
|
718
726
|
end
|
727
|
+
|
728
|
+
# An array of three parts:
|
729
|
+
# * The object to use
|
730
|
+
# * Whether it is a regular expression
|
731
|
+
# * Whether it is case insensitive
|
732
|
+
def self.like_element(re) # :nodoc:
|
733
|
+
if re.is_a?(Regexp)
|
734
|
+
[re.source, true, re.casefold?]
|
735
|
+
else
|
736
|
+
[re, false, false]
|
737
|
+
end
|
738
|
+
end
|
739
|
+
private_class_method :like_element
|
719
740
|
end
|
720
741
|
|
721
742
|
# Represents an SQL array. Added so it is possible to deal with a
|
@@ -769,38 +790,124 @@ module Sequel
|
|
769
790
|
# the methods defined by Sequel, or if you are running on ruby 1.9.
|
770
791
|
#
|
771
792
|
# An instance of this class is yielded to the block supplied to filter, order, and select.
|
772
|
-
# If
|
773
|
-
#
|
793
|
+
# If the block doesn't take an argument, the block is instance_evaled in the context of
|
794
|
+
# a new instance of this class.
|
795
|
+
#
|
796
|
+
# VirtualRow uses method_missing to return Identifiers, QualifiedIdentifiers, Functions, or WindowFunctions,
|
797
|
+
# depending on how it is called. If a block is not given, creates one of the following objects:
|
798
|
+
# * Function - returned if any arguments are supplied, using the method name
|
799
|
+
# as the function name, and the arguments as the function arguments.
|
800
|
+
# * QualifiedIdentifier - returned if the method name contains __, with the
|
801
|
+
# table being the part before __, and the column being the part after.
|
802
|
+
# * Identifier - returned otherwise, using the method name.
|
803
|
+
# If a block is given, it returns either a Function or WindowFunction, depending on the first
|
804
|
+
# argument to the method. Note that the block is currently not called by the code, though
|
805
|
+
# this may change in a future version. If the first argument is:
|
806
|
+
# * no arguments given - uses a Function with no arguments.
|
807
|
+
# * :* - uses a Function with a literal wildcard argument (*), mostly useful for COUNT.
|
808
|
+
# * :distinct - uses a Function that prepends DISTINCT to the rest of the arguments, mostly
|
809
|
+
# useful for aggregate functions.
|
810
|
+
# * :over - uses a WindowFunction. If a second argument is provided, it should be a hash
|
811
|
+
# of options which are passed to Window (e.g. :window, :partition, :order, :frame). The
|
812
|
+
# arguments to the function itself should be specified as :*=>true for a wildcard, or via
|
813
|
+
# the :args option.
|
774
814
|
#
|
775
815
|
# Examples:
|
776
816
|
#
|
777
817
|
# ds = DB[:t]
|
818
|
+
# # Argument yielded to block
|
778
819
|
# ds.filter{|r| r.name < 2} # SELECT * FROM t WHERE (name < 2)
|
779
|
-
#
|
780
|
-
# ds.filter{
|
820
|
+
# # Block without argument (instance_eval)
|
821
|
+
# ds.filter{name < 2} # SELECT * FROM t WHERE (name < 2)
|
822
|
+
# # Qualified identifiers
|
823
|
+
# ds.filter{table__column + 1 < 2} # SELECT * FROM t WHERE ((table.column + 1) < 2)
|
824
|
+
# # Functions
|
825
|
+
# ds.filter{is_active(1, 'arg2')} # SELECT * FROM t WHERE is_active(1, 'arg2')
|
826
|
+
# ds.select{version{}} # SELECT version() FROM t
|
827
|
+
# ds.select{count(:*){}} # SELECT count(*) FROM t
|
828
|
+
# ds.select{count(:distinct, col1){}} # SELECT count(DISTINCT col1) FROM t
|
829
|
+
# # Window Functions
|
830
|
+
# ds.select{rank(:over){}} # SELECT rank() OVER () FROM t
|
831
|
+
# ds.select{count(:over, :*=>true){}} # SELECT count(*) OVER () FROM t
|
832
|
+
# ds.select{sum(:over, :args=>col1, :partition=>col2, :order=>col3){}} # SELECT sum(col1) OVER (PARTITION BY col2 ORDER BY col3) FROM t
|
781
833
|
class VirtualRow
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
#
|
788
|
-
#
|
789
|
-
|
790
|
-
|
791
|
-
|
834
|
+
WILDCARD = LiteralString.new('*').freeze
|
835
|
+
QUESTION_MARK = LiteralString.new('?').freeze
|
836
|
+
COMMA_SEPARATOR = LiteralString.new(', ').freeze
|
837
|
+
DOUBLE_UNDERSCORE = '__'.freeze
|
838
|
+
|
839
|
+
# Return Identifiers, QualifiedIdentifiers, Functions, or WindowFunctions, depending
|
840
|
+
# on arguments and whether a block is provided. Does not currently call the block.
|
841
|
+
# See the class level documentation.
|
842
|
+
def method_missing(m, *args, &block)
|
843
|
+
if block
|
844
|
+
if args.empty?
|
845
|
+
Function.new(m)
|
846
|
+
else
|
847
|
+
case arg = args.shift
|
848
|
+
when :*
|
849
|
+
Function.new(m, WILDCARD)
|
850
|
+
when :distinct
|
851
|
+
Function.new(m, PlaceholderLiteralString.new("DISTINCT #{args.map{QUESTION_MARK}.join(COMMA_SEPARATOR)}", args))
|
852
|
+
when :over
|
853
|
+
opts = args.shift || {}
|
854
|
+
fun_args = ::Kernel.Array(opts[:*] ? WILDCARD : opts[:args])
|
855
|
+
WindowFunction.new(Function.new(m, *fun_args), Window.new(opts))
|
856
|
+
else
|
857
|
+
raise Error, 'unsupported VirtualRow method argument used with block'
|
858
|
+
end
|
859
|
+
end
|
860
|
+
elsif args.empty?
|
861
|
+
table, column = m.to_s.split(DOUBLE_UNDERSCORE, 2)
|
792
862
|
column ? QualifiedIdentifier.new(table, column) : Identifier.new(m)
|
793
863
|
else
|
794
864
|
Function.new(m, *args)
|
795
865
|
end
|
796
866
|
end
|
797
867
|
end
|
868
|
+
|
869
|
+
# A window is part of a window function specifying the window over which the function operates.
|
870
|
+
# It is separated from the WindowFunction class because it also can be used separately on
|
871
|
+
# some databases.
|
872
|
+
class Window < Expression
|
873
|
+
# The options for this window. Options currently used are:
|
874
|
+
# * :frame - if specified, should be :all or :rows. :all always operates over all rows in the
|
875
|
+
# partition, while :rows excludes the current row's later peers. The default is to include
|
876
|
+
# all previous rows in the partition up to the current row's last peer.
|
877
|
+
# * :order - order on the column(s) given
|
878
|
+
# * :partition - partition/group on the column(s) given
|
879
|
+
# * :window - base results on a previously specified named window
|
880
|
+
attr_reader :opts
|
881
|
+
|
882
|
+
# Set the options to the options given
|
883
|
+
def initialize(opts={})
|
884
|
+
@opts = opts
|
885
|
+
end
|
886
|
+
|
887
|
+
to_s_method :window_sql, '@opts'
|
888
|
+
end
|
889
|
+
|
890
|
+
# A WindowFunction is a grouping of a function with a window over which it operates.
|
891
|
+
class WindowFunction < Expression
|
892
|
+
# The function to use, should be an SQL::Function.
|
893
|
+
attr_reader :function
|
894
|
+
|
895
|
+
# The window to use, should be an SQL::Window.
|
896
|
+
attr_reader :window
|
897
|
+
|
898
|
+
# Set the function and window.
|
899
|
+
def initialize(function, window)
|
900
|
+
@function, @window = function, window
|
901
|
+
end
|
902
|
+
|
903
|
+
to_s_method :window_function_sql, '@function, @window'
|
904
|
+
end
|
798
905
|
end
|
799
906
|
|
800
907
|
# LiteralString is used to represent literal SQL expressions. A
|
801
908
|
# LiteralString is copied verbatim into an SQL statement. Instances of
|
802
909
|
# LiteralString can be created by calling String#lit.
|
803
|
-
class LiteralString
|
910
|
+
class LiteralString
|
804
911
|
include SQL::OrderMethods
|
805
912
|
include SQL::ComplexExpressionMethods
|
806
913
|
include SQL::BooleanMethods
|
data/lib/sequel/version.rb
CHANGED
data/spec/adapters/ado_spec.rb
CHANGED
@@ -3,6 +3,7 @@ require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
|
3
3
|
unless defined?(ADO_DB)
|
4
4
|
ADO_DB = Sequel.ado(:host => 'MY_SQL_SERVER', :database => 'MyDB', :user => 'my_usr', :password => 'my_pwd')
|
5
5
|
end
|
6
|
+
INTEGRATION_DB = ADO_DB unless defined?(INTEGRATION_DB)
|
6
7
|
|
7
8
|
context "An ADO dataset" do
|
8
9
|
before(:each) do
|
@@ -4,6 +4,7 @@ unless defined?(FIREBIRD_DB)
|
|
4
4
|
FIREBIRD_URL = 'firebird://sysdba:masterkey@localhost/reality_spec' unless defined? FIREBIRD_URL
|
5
5
|
FIREBIRD_DB = Sequel.connect(ENV['SEQUEL_FB_SPEC_DB']||FIREBIRD_URL)
|
6
6
|
end
|
7
|
+
INTEGRATION_DB = FIREBIRD_DB unless defined?(INTEGRATION_DB)
|
7
8
|
|
8
9
|
def FIREBIRD_DB.sqls
|
9
10
|
(@sqls ||= [])
|
@@ -3,6 +3,7 @@ require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
|
3
3
|
unless defined?(INFORMIX_DB)
|
4
4
|
INFORMIX_DB = Sequel.connect('informix://localhost/mydb')
|
5
5
|
end
|
6
|
+
INTEGRATION_DB = INFORMIX_DB unless defined?(INTEGRATION_DB)
|
6
7
|
|
7
8
|
if INFORMIX_DB.table_exists?(:test)
|
8
9
|
INFORMIX_DB.drop_table :test
|
data/spec/adapters/mysql_spec.rb
CHANGED
@@ -10,6 +10,7 @@ end
|
|
10
10
|
unless defined?(MYSQL_SOCKET_FILE)
|
11
11
|
MYSQL_SOCKET_FILE = '/tmp/mysql.sock'
|
12
12
|
end
|
13
|
+
INTEGRATION_DB = MYSQL_DB unless defined?(INTEGRATION_DB)
|
13
14
|
|
14
15
|
MYSQL_URI = URI.parse(MYSQL_DB.uri)
|
15
16
|
|
@@ -78,10 +79,10 @@ context "A MySQL database" do
|
|
78
79
|
end
|
79
80
|
|
80
81
|
specify "should correctly parse the schema" do
|
81
|
-
@db.schema(:booltest, :reload=>true).should == [[:value, {:type=>:boolean, :allow_null=>true, :primary_key=>false, :default=>nil, :db_type=>"tinyint(4)"}]]
|
82
|
+
@db.schema(:booltest, :reload=>true).should == [[:value, {:type=>:boolean, :allow_null=>true, :primary_key=>false, :default=>nil, :ruby_default=>nil, :db_type=>"tinyint(4)"}]]
|
82
83
|
|
83
84
|
Sequel.convert_tinyint_to_bool = false
|
84
|
-
@db.schema(:booltest, :reload=>true).should == [[:value, {:type=>:integer, :allow_null=>true, :primary_key=>false, :default=>nil, :db_type=>"tinyint(4)"}]]
|
85
|
+
@db.schema(:booltest, :reload=>true).should == [[:value, {:type=>:integer, :allow_null=>true, :primary_key=>false, :default=>nil, :ruby_default=>nil, :db_type=>"tinyint(4)"}]]
|
85
86
|
end
|
86
87
|
|
87
88
|
specify "should accept and return tinyints as bools or integers when configured to do so" do
|
@@ -425,11 +426,11 @@ context "A MySQL database" do
|
|
425
426
|
end
|
426
427
|
|
427
428
|
specify "should have rename_column support keep existing options" do
|
428
|
-
@db.create_table(:items){
|
429
|
+
@db.create_table(:items){String :id, :null=>false, :default=>'blah'}
|
429
430
|
@db.alter_table(:items){rename_column :id, :nid}
|
430
|
-
@db.sqls.should == ["CREATE TABLE items (id
|
431
|
+
@db.sqls.should == ["CREATE TABLE items (id varchar(255) NOT NULL DEFAULT 'blah')", "DESCRIBE items", "ALTER TABLE items CHANGE COLUMN id nid varchar(255) NOT NULL DEFAULT 'blah'"]
|
431
432
|
@db[:items].insert
|
432
|
-
@db[:items].all.should == [{:nid=>
|
433
|
+
@db[:items].all.should == [{:nid=>'blah'}]
|
433
434
|
proc{@db[:items].insert(:nid=>nil)}.should raise_error(Sequel::DatabaseError)
|
434
435
|
end
|
435
436
|
|
@@ -635,6 +636,21 @@ context "MySQL::Dataset#insert and related methods" do
|
|
635
636
|
]
|
636
637
|
end
|
637
638
|
|
639
|
+
specify "#on_duplicate_key_update should work with regular inserts" do
|
640
|
+
MYSQL_DB.add_index :items, :name, :unique=>true
|
641
|
+
MYSQL_DB.sqls.clear
|
642
|
+
@d.insert(:name => 'abc', :value => 1)
|
643
|
+
@d.on_duplicate_key_update(:name, :value => 6).insert(:name => 'abc', :value => 1)
|
644
|
+
@d.on_duplicate_key_update(:name, :value => 6).insert(:name => 'def', :value => 2)
|
645
|
+
|
646
|
+
MYSQL_DB.sqls.length.should == 3
|
647
|
+
MYSQL_DB.sqls[0].should =~ /\AINSERT INTO items \((name|value), (name|value)\) VALUES \(('abc'|1), (1|'abc')\)\z/
|
648
|
+
MYSQL_DB.sqls[1].should =~ /\AINSERT INTO items \((name|value), (name|value)\) VALUES \(('abc'|1), (1|'abc')\) ON DUPLICATE KEY UPDATE name=VALUES\(name\), value=6\z/
|
649
|
+
MYSQL_DB.sqls[2].should =~ /\AINSERT INTO items \((name|value), (name|value)\) VALUES \(('def'|2), (2|'def')\) ON DUPLICATE KEY UPDATE name=VALUES\(name\), value=6\z/
|
650
|
+
|
651
|
+
@d.all.should == [{:name => 'abc', :value => 6}, {:name => 'def', :value => 2}]
|
652
|
+
end
|
653
|
+
|
638
654
|
specify "#multi_insert should insert multiple records in a single statement" do
|
639
655
|
@d.multi_insert([{:name => 'abc'}, {:name => 'def'}])
|
640
656
|
|
@@ -727,11 +743,10 @@ context "MySQL::Dataset#insert and related methods" do
|
|
727
743
|
end
|
728
744
|
|
729
745
|
specify "#on_duplicate_key_update should add the ON DUPLICATE KEY UPDATE and ALL columns when no args given" do
|
730
|
-
@d.on_duplicate_key_update.import([:name,:value],
|
731
|
-
[['abc', 1], ['def',2]]
|
732
|
-
)
|
746
|
+
@d.on_duplicate_key_update.import([:name,:value], [['abc', 1], ['def',2]])
|
733
747
|
|
734
748
|
MYSQL_DB.sqls.should == [
|
749
|
+
"SELECT * FROM items LIMIT 1",
|
735
750
|
SQL_BEGIN,
|
736
751
|
"INSERT INTO items (name, value) VALUES ('abc', 1), ('def', 2) ON DUPLICATE KEY UPDATE name=VALUES(name), value=VALUES(value)",
|
737
752
|
SQL_COMMIT
|