arel_extensions 0.8.7 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -0
- data/Gemfile +2 -2
- data/SQL_Challenges.md +29 -0
- data/arel_extensions.gemspec +5 -5
- data/functions.html +1 -1
- data/gemfiles/rails4.gemfile +1 -1
- data/init/mssql.sql +41 -3
- data/lib/arel_extensions/nodes/blank.rb +2 -1
- data/lib/arel_extensions/nodes/date_diff.rb +26 -2
- data/lib/arel_extensions/version.rb +1 -1
- data/lib/arel_extensions/visitors.rb +18 -1
- data/lib/arel_extensions/visitors/mssql.rb +184 -52
- data/lib/arel_extensions/visitors/to_sql.rb +5 -3
- data/test/database.yml +1 -2
- data/test/support/fake_record.rb +8 -0
- data/test/with_ar/all_agnostic_test.rb +29 -8
- data/test/with_ar/insert_agnostic_test.rb +3 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d3be3f3b4a82f0721265a42e8ae75fe66da12ba3
|
4
|
+
data.tar.gz: a1660c2b64388e6e56bf442de63504da32c2c5ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4465b08c98214889ca8be08d437a49deeb59df7de899095bcb4c4a7e7ff5d11c8c1cf50a7f8961d0f73b39b5ba0da2db36cfadd3fda4a4e8ee2eaacdc5251978
|
7
|
+
data.tar.gz: 5781edd20996ca251937563e1fa15a6fa453a250654aa8191fade92b56970edcb205fe1f5cb37659cbd80460451d0cac858a32f8733919aa6b29bf26640eb519
|
data/.travis.yml
CHANGED
@@ -89,6 +89,10 @@ matrix:
|
|
89
89
|
- rvm: ruby-head
|
90
90
|
gemfile: gemfiles/rails4.gemfile
|
91
91
|
allow_failures:
|
92
|
+
- rvm: rbx-2
|
93
|
+
gemfile: gemfiles/rails4.gemfile
|
94
|
+
- rvm: rbx-2
|
95
|
+
gemfile: gemfiles/rails5.gemfile
|
92
96
|
- rvm: jruby-9.0.5.0
|
93
97
|
gemfile: gemfiles/rails5.gemfile
|
94
98
|
- rvm: jruby-head
|
data/Gemfile
CHANGED
@@ -2,7 +2,7 @@ source "https://rubygems.org"
|
|
2
2
|
|
3
3
|
gemspec
|
4
4
|
|
5
|
-
group :test do
|
5
|
+
group :development, :test do
|
6
6
|
gem "sqlite3", :platforms => [:mri, :mswin, :mingw, :x64_mingw]
|
7
7
|
gem "mysql2", :platforms => [:mri, :mswin, :mingw, :x64_mingw]
|
8
8
|
gem "pg", :platforms => [:mri, :mingw, :x64_mingw]
|
@@ -13,7 +13,7 @@ group :test do
|
|
13
13
|
gem "activerecord-jdbcpostgresql-adapter", :platforms => :jruby
|
14
14
|
|
15
15
|
gem "tiny_tds", :platforms => [:mri, :mingw, :x64_mingw]
|
16
|
-
gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw, :x64_mingw]
|
16
|
+
gem "activerecord-sqlserver-adapter", '~> 4.2.0', :platforms => [:mri, :mingw, :x64_mingw]
|
17
17
|
|
18
18
|
gem 'activesupport', '~> 4.0'
|
19
19
|
gem 'activemodel', '~> 4.0'
|
data/SQL_Challenges.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# SQL Challenges
|
2
|
+
|
3
|
+
It should be possible to do any advanced SQL functions of any RDBMS in any other using classical SQL routines. We hope so.
|
4
|
+
But here are some of the features we did not succeed to create in some cases. Any help would be very useful.
|
5
|
+
A good way could be to create user defined functions to ensure a total transparent compatibility and to encapsulate these improvements.
|
6
|
+
|
7
|
+
|
8
|
+
## SQLite (without extension)
|
9
|
+
- CEIL and FLOOR without pcre
|
10
|
+
- FIND_IN_SET
|
11
|
+
- LOCATE
|
12
|
+
- REGEXP
|
13
|
+
- SOUNDEX
|
14
|
+
|
15
|
+
## PostgreSQL (without extension)
|
16
|
+
- SOUNDEX
|
17
|
+
|
18
|
+
|
19
|
+
## Oracle
|
20
|
+
- Maths Operators on string ('a' <= 'b')
|
21
|
+
|
22
|
+
|
23
|
+
## SQL Server (any version > 2005, without extension)
|
24
|
+
- REGEXP
|
25
|
+
- (L/R)TRIM(str, 'x') -> TRIM with arguments
|
26
|
+
- Maths Operators on string ('a' <= 'b')
|
27
|
+
- GROUP_CONCAT
|
28
|
+
- FIND_IN_SET
|
29
|
+
- any date format (YYYY/MM/DD HH:MM:SS:MMM for example)
|
data/arel_extensions.gemspec
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
# # -*- encoding: utf-8 -*-
|
2
2
|
$:.push File.expand_path("../lib", __FILE__)
|
3
|
-
|
3
|
+
require "arel_extensions/version"
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "arel_extensions"
|
7
|
-
s.version =
|
7
|
+
s.version = ArelExtensions::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.authors = ["Yann Azoury", "Mathilde Pechdimaldjian", "Félix Bellanger"]
|
10
10
|
s.email = ["yann.azoury@faveod.com", "mathilde.pechdimaldjian@gmail.com", "felix.bellanger@faveod.com"]
|
11
11
|
s.homepage = "https://github.com/Faveod/arel-extensions"
|
12
12
|
s.description = "Adds new features to Arel"
|
13
13
|
s.summary = "Extending Arel"
|
14
|
-
s.license =
|
14
|
+
s.license = 'MIT'
|
15
15
|
|
16
16
|
s.rdoc_options = ["--main", "README.md"]
|
17
|
-
s.extra_rdoc_files = ["MIT-LICENSE.txt", "README.md"]
|
17
|
+
s.extra_rdoc_files = ["MIT-LICENSE.txt", "README.md", 'functions.html']
|
18
18
|
|
19
19
|
# Manifest
|
20
20
|
s.files = `git ls-files`.split("\n")
|
21
21
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
22
|
s.require_paths = ["lib"]
|
23
23
|
|
24
|
-
s.add_dependency('arel', '
|
24
|
+
s.add_dependency('arel', '>= 6.0')
|
25
25
|
|
26
26
|
s.add_development_dependency('minitest', '~> 5.9')
|
27
27
|
s.add_development_dependency('rdoc', '~> 4.0')
|
data/functions.html
CHANGED
data/gemfiles/rails4.gemfile
CHANGED
@@ -12,7 +12,7 @@ group :development, :test do
|
|
12
12
|
gem "pg", :platforms => [:mri, :mingw, :x64_mingw]
|
13
13
|
|
14
14
|
gem "tiny_tds", :platforms => [:mri, :mingw, :x64_mingw]
|
15
|
-
gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw, :x64_mingw]
|
15
|
+
gem "activerecord-sqlserver-adapter", '~> 4.2.0', :platforms => [:mri, :mingw, :x64_mingw]
|
16
16
|
|
17
17
|
gem 'ruby-oci8', :platforms => [:mri, :mswin, :mingw] if ENV.has_key? 'ORACLE_HOME'
|
18
18
|
gem 'activerecord-oracle_enhanced-adapter', '~> 1.6.0' if ENV.has_key? 'ORACLE_HOME'
|
data/init/mssql.sql
CHANGED
@@ -1,6 +1,44 @@
|
|
1
|
-
|
1
|
+
IF OBJECT_ID (N'dbo.TRIM', N'FN') IS NOT NULL
|
2
|
+
DROP FUNCTION dbo.TRIM;
|
3
|
+
GO
|
4
|
+
CREATE FUNCTION dbo.TRIM (@string VARCHAR(MAX))
|
2
5
|
RETURNS VARCHAR(MAX)
|
6
|
+
AS
|
3
7
|
BEGIN
|
4
|
-
RETURN LTRIM(RTRIM(@string))
|
5
|
-
END
|
8
|
+
RETURN LTRIM(RTRIM(@string));
|
9
|
+
END;
|
10
|
+
GO
|
11
|
+
|
12
|
+
-----------------------------
|
13
|
+
-- GO
|
14
|
+
|
15
|
+
IF OBJECT_ID (N'dbo.FIND_IN_SET', N'FN') IS NOT NULL
|
16
|
+
DROP FUNCTION dbo.FIND_IN_SET;
|
6
17
|
GO
|
18
|
+
-- CREATE FUNCTION dbo.FIND_IN_SET(@value VARCHAR(MAX), @list VARCHAR(MAX), @delim VARCHAR(MAX))
|
19
|
+
-- RETURNS VARCHAR(MAX)
|
20
|
+
-- AS BEGIN
|
21
|
+
-- RETURN LTRIM(RTRIM(@value));
|
22
|
+
-- END;
|
23
|
+
|
24
|
+
-- GO
|
25
|
+
|
26
|
+
--IF OBJECT_ID (N'dbo.SplitString', N'FN') IS NOT NULL
|
27
|
+
-- DROP FUNCTION dbo.SplitString;
|
28
|
+
--GO
|
29
|
+
--CREATE FUNCTION dbo.SplitString (@List NVARCHAR(MAX), @Delim VARCHAR(255))
|
30
|
+
--RETURNS TABLE
|
31
|
+
--AS
|
32
|
+
--BEGIN
|
33
|
+
-- RETURN ( SELECT [Value] FROM
|
34
|
+
-- (
|
35
|
+
-- SELECT
|
36
|
+
-- [Value] = LTRIM(RTRIM(SUBSTRING(@List, [Number],
|
37
|
+
-- CHARINDEX(@Delim, @List + @Delim, [Number]) - [Number])))
|
38
|
+
-- FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
|
39
|
+
-- FROM sys.all_objects) AS x
|
40
|
+
-- WHERE Number <= LEN(@List)
|
41
|
+
-- AND SUBSTRING(@Delim + @List, [Number], LEN(@Delim)) = @Delim
|
42
|
+
-- ) AS y
|
43
|
+
-- );
|
44
|
+
--END;
|
@@ -90,8 +90,32 @@ module ArelExtensions
|
|
90
90
|
elsif @date_type == :datetime
|
91
91
|
Arel.sql("INTERVAL '%s' SECOND" % v.to_i)
|
92
92
|
end
|
93
|
-
|
94
|
-
|
93
|
+
else
|
94
|
+
v
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def mssql_value(v = nil)
|
99
|
+
v ||= self.expressions.last
|
100
|
+
if defined?(ActiveSupport::Duration) && ActiveSupport::Duration === v
|
101
|
+
if @date_type == :date
|
102
|
+
v.inspect.to_i
|
103
|
+
elsif @date_type == :datetime
|
104
|
+
v.to_i
|
105
|
+
end
|
106
|
+
else
|
107
|
+
v
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def mssql_datepart(v = nil)
|
112
|
+
v ||= self.expressions.last
|
113
|
+
if defined?(ActiveSupport::Duration) && ActiveSupport::Duration === v
|
114
|
+
if @date_type == :date
|
115
|
+
Arel.sql('day')
|
116
|
+
elsif @date_type == :datetime
|
117
|
+
Arel.sql('second')
|
118
|
+
end
|
95
119
|
else
|
96
120
|
v
|
97
121
|
end
|
@@ -3,4 +3,21 @@ require 'arel_extensions/visitors/mysql'
|
|
3
3
|
require 'arel_extensions/visitors/oracle'
|
4
4
|
require 'arel_extensions/visitors/postgresql'
|
5
5
|
require 'arel_extensions/visitors/sqlite'
|
6
|
-
require 'arel_extensions/visitors/mssql'
|
6
|
+
require 'arel_extensions/visitors/mssql'
|
7
|
+
|
8
|
+
Arel::Visitors::MSSQL.class_eval do
|
9
|
+
include ArelExtensions::Visitors::MSSQL
|
10
|
+
end
|
11
|
+
|
12
|
+
begin
|
13
|
+
require 'arel_sqlserver'
|
14
|
+
if Arel::VERSION.to_i == 6
|
15
|
+
if Arel::Visitors::VISITORS['sqlserver'] && Arel::Visitors::VISITORS['sqlserver'] != Arel::Visitors::MSSQL
|
16
|
+
Arel::Visitors::VISITORS['sqlserver'].class_eval do
|
17
|
+
include ArelExtensions::Visitors::MSSQL
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
rescue LoadError
|
22
|
+
rescue => e
|
23
|
+
end
|
@@ -1,12 +1,25 @@
|
|
1
1
|
module ArelExtensions
|
2
2
|
module Visitors
|
3
|
-
|
3
|
+
module MSSQL
|
4
|
+
Arel::Visitors::MSSQL::DATE_MAPPING = {'d' => 'day', 'm' => 'month', 'y' => 'year', 'wd' => 'weekday', 'w' => 'week'}
|
4
5
|
Arel::Visitors::MSSQL::DATE_FORMAT_DIRECTIVES = {
|
5
|
-
'%Y' => '
|
6
|
-
'%d' => '
|
6
|
+
'%Y' => 'YYYY', '%C' => '', '%y' => 'YY', '%m' => 'MM', '%B' => '', '%b' => '', '%^b' => '', # year, month
|
7
|
+
'%d' => 'DD', '%e' => '', '%j' => '', '%w' => 'dw', '%A' => '', # day, weekday
|
7
8
|
'%H' => 'hh', '%k' => '', '%I' => '', '%l' => '', '%P' => '', '%p' => '', # hours
|
8
9
|
'%M' => 'mi', '%S' => 'ss', '%L' => 'ms', '%N' => 'ns', '%z' => 'tz'
|
9
10
|
}
|
11
|
+
# TODO all others... http://www.sql-server-helper.com/tips/date-formats.aspx
|
12
|
+
Arel::Visitors::MSSQL::DATE_CONVERT_FORMATS = {
|
13
|
+
'YYYY-MM-DD' => 120,
|
14
|
+
'YY-MM-DD' => 120,
|
15
|
+
'MM/DD/YYYY' => 101,
|
16
|
+
'MM-DD-YYYY' => 110,
|
17
|
+
'YYYY/MM/DD' => 111,
|
18
|
+
'DD-MM-YYYY' => 105,
|
19
|
+
'DD-MM-YY' => 5,
|
20
|
+
'DD.MM.YYYY' => 104,
|
21
|
+
'YYYY-MM-DDTHH:MM:SS:MMM' => 126
|
22
|
+
}
|
10
23
|
|
11
24
|
# Math Functions
|
12
25
|
def visit_ArelExtensions_Nodes_Ceil o, collector
|
@@ -16,7 +29,16 @@ module ArelExtensions
|
|
16
29
|
collector
|
17
30
|
end
|
18
31
|
|
19
|
-
def
|
32
|
+
def visit_ArelExtensions_Nodes_IsNull o, collector
|
33
|
+
collector << "("
|
34
|
+
collector = visit o.left, collector
|
35
|
+
# collector << Arel::Visitors::MSSQL::COMMA
|
36
|
+
collector << " IS NULL)"
|
37
|
+
collector
|
38
|
+
end
|
39
|
+
|
40
|
+
# Deprecated
|
41
|
+
def visit_ArelExtensions_Nodes_ConcatOld o, collector
|
20
42
|
arg = o.left.relation.engine.columns.find{|c| c.name == o.left.name.to_s}.type
|
21
43
|
if(o.right.is_a?(Arel::Attributes::Attribute))
|
22
44
|
collector = visit o.left, collector
|
@@ -24,7 +46,10 @@ module ArelExtensions
|
|
24
46
|
collector = visit o.right, collector
|
25
47
|
collector
|
26
48
|
elsif ( arg == :date || arg == :datetime)
|
27
|
-
collector << "DATEADD(day
|
49
|
+
collector << "DATEADD(day"
|
50
|
+
collector << Arel::Visitors::MSSQL::COMMA
|
51
|
+
collector = visit o.right, collector
|
52
|
+
collector << Arel::Visitors::MSSQL::COMMA
|
28
53
|
collector = visit o.left, collector
|
29
54
|
collector
|
30
55
|
else
|
@@ -35,8 +60,19 @@ module ArelExtensions
|
|
35
60
|
end
|
36
61
|
end
|
37
62
|
|
63
|
+
def visit_ArelExtensions_Nodes_Concat o, collector
|
64
|
+
collector << "CONCAT("
|
65
|
+
o.expressions.each_with_index { |arg, i|
|
66
|
+
collector << Arel::Visitors::MSSQL::COMMA unless i == 0
|
67
|
+
collector = visit arg, collector
|
68
|
+
}
|
69
|
+
collector << ")"
|
70
|
+
collector
|
71
|
+
end
|
72
|
+
|
38
73
|
def visit_ArelExtensions_Nodes_DateDiff o, collector
|
39
|
-
collector << "DATEDIFF(day
|
74
|
+
collector << "DATEDIFF(day"
|
75
|
+
collector << Arel::Visitors::MSSQL::COMMA
|
40
76
|
collector = visit o.left, collector
|
41
77
|
collector << Arel::Visitors::MSSQL::COMMA
|
42
78
|
collector = visit o.right, collector
|
@@ -44,94 +80,190 @@ module ArelExtensions
|
|
44
80
|
collector
|
45
81
|
end
|
46
82
|
|
83
|
+
def visit_ArelExtensions_Nodes_DateAdd o, collector
|
84
|
+
collector << "DATEADD("
|
85
|
+
collector << o.mssql_datepart(o.right)
|
86
|
+
collector << Arel::Visitors::MSSQL::COMMA
|
87
|
+
collector = visit o.mssql_value(o.right), collector
|
88
|
+
collector << Arel::Visitors::MSSQL::COMMA
|
89
|
+
collector = visit o.left, collector
|
90
|
+
collector << ")"
|
91
|
+
collector
|
92
|
+
end
|
47
93
|
|
48
94
|
def visit_ArelExtensions_Nodes_Duration o, collector
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
elsif(o.left == "m")
|
53
|
-
collector << "MONTH("
|
54
|
-
elsif (o.left == "w")
|
55
|
-
collector << "WEEK"
|
56
|
-
elsif (o.left == "y")
|
57
|
-
collector << "YEAR("
|
58
|
-
end
|
59
|
-
#visit right
|
95
|
+
collector << 'DATEPART('
|
96
|
+
collector << Arel::Visitors::MSSQL::DATE_MAPPING[o.left]
|
97
|
+
collector << Arel::Visitors::MSSQL::COMMA
|
60
98
|
collector = visit o.right, collector
|
61
99
|
collector << ")"
|
62
100
|
collector
|
63
101
|
end
|
64
102
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
103
|
+
def visit_ArelExtensions_Nodes_Length o, collector
|
104
|
+
collector << "LEN("
|
105
|
+
collector = visit o.expr, collector
|
106
|
+
collector << ")"
|
107
|
+
collector
|
108
|
+
end
|
71
109
|
|
110
|
+
def visit_ArelExtensions_Nodes_Round o, collector
|
111
|
+
collector << "ROUND("
|
112
|
+
o.expressions.each_with_index { |arg, i|
|
113
|
+
collector << Arel::Visitors::MSSQL::COMMA unless i == 0
|
114
|
+
collector = visit arg, collector
|
115
|
+
}
|
116
|
+
if o.expressions.length == 1
|
117
|
+
collector << Arel::Visitors::MSSQL::COMMA
|
118
|
+
collector << "0"
|
119
|
+
end
|
120
|
+
collector << ")"
|
121
|
+
collector
|
122
|
+
end
|
72
123
|
|
73
124
|
def visit_ArelExtensions_Nodes_Locate o, collector
|
74
125
|
collector << "CHARINDEX("
|
75
|
-
collector = visit o.
|
126
|
+
collector = visit o.right, collector
|
76
127
|
collector << Arel::Visitors::MSSQL::COMMA
|
77
|
-
collector = visit o.
|
128
|
+
collector = visit o.left, collector
|
78
129
|
collector << ")"
|
79
130
|
collector
|
80
131
|
end
|
81
132
|
|
82
|
-
|
83
|
-
|
133
|
+
# TODO manage 2nd argument
|
134
|
+
def visit_ArelExtensions_Nodes_Trim o, collector
|
135
|
+
collector << "LTRIM(RTRIM("
|
136
|
+
collector = visit o.left, collector
|
137
|
+
collector << "))"
|
138
|
+
collector
|
139
|
+
end
|
140
|
+
|
141
|
+
# TODO manage 2nd argument
|
142
|
+
def visit_ArelExtensions_Nodes_Ltrim o, collector
|
143
|
+
collector << "LTRIM("
|
144
|
+
collector = visit o.left, collector
|
145
|
+
collector << ")"
|
146
|
+
collector
|
147
|
+
end
|
148
|
+
|
149
|
+
# TODO manage 2nd argument
|
150
|
+
def visit_ArelExtensions_Nodes_Rtrim o, collector
|
151
|
+
collector << "RTRIM("
|
152
|
+
collector = visit o.left, collector
|
153
|
+
collector << ")"
|
154
|
+
collector
|
155
|
+
end
|
84
156
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
157
|
+
def visit_ArelExtensions_Nodes_Blank o, collector
|
158
|
+
collector << 'CASE WHEN LEN(LTRIM(RTRIM(ISNULL('
|
159
|
+
collector = visit o.left, collector
|
160
|
+
collector << ", '')))) = 0 THEN 1 ELSE 0 END"
|
161
|
+
collector
|
162
|
+
end
|
163
|
+
|
164
|
+
def visit_ArelExtensions_Nodes_Format o, collector
|
165
|
+
f = o.iso_format.dup
|
166
|
+
Arel::Visitors::MSSQL::DATE_FORMAT_DIRECTIVES.each { |d, r| f.gsub!(d, r) }
|
167
|
+
if Arel::Visitors::MSSQL::DATE_CONVERT_FORMATS[f]
|
168
|
+
collector << "CONVERT(VARCHAR(#{f.length})"
|
169
|
+
collector << Arel::Visitors::MSSQL::COMMA
|
170
|
+
collector = visit o.left, collector
|
171
|
+
collector << Arel::Visitors::MSSQL::COMMA
|
172
|
+
collector << Arel::Visitors::MSSQL::DATE_CONVERT_FORMATS[f].to_s
|
173
|
+
collector << ')'
|
174
|
+
collector
|
175
|
+
else
|
176
|
+
collector << "("
|
177
|
+
t = o.iso_format.split('%')
|
178
|
+
t.each_with_index {|str, i|
|
179
|
+
if i == 0 && t[0] != '%'
|
180
|
+
collector = visit Arel::Nodes.build_quoted(str), collector
|
96
181
|
if str.length > 1
|
97
182
|
collector << Arel::Visitors::MSSQL::COMMA
|
98
183
|
collector = visit Arel::Nodes.build_quoted(str.sub(/\A./, '')), collector
|
99
184
|
end
|
185
|
+
elsif str.length > 0
|
186
|
+
if !Arel::Visitors::MSSQL::DATE_FORMAT_DIRECTIVES['%' + str[0]].blank?
|
187
|
+
collector << 'LTRIM(STR(DATEPART('
|
188
|
+
collector << Arel::Visitors::MSSQL::DATE_FORMAT_DIRECTIVES['%' + str[0]]
|
189
|
+
collector << Arel::Visitors::MSSQL::COMMA
|
190
|
+
collector = visit o.left, collector
|
191
|
+
collector << ')))'
|
192
|
+
if str.length > 1
|
193
|
+
collector << ' + '
|
194
|
+
collector = visit Arel::Nodes.build_quoted(str.sub(/\A./, '')), collector
|
195
|
+
end
|
196
|
+
end
|
100
197
|
end
|
101
|
-
|
102
|
-
|
103
|
-
}
|
198
|
+
collector << ' + ' if t[i + 1]
|
199
|
+
}
|
104
200
|
|
105
|
-
|
106
|
-
|
201
|
+
collector << ')'
|
202
|
+
collector
|
203
|
+
end
|
107
204
|
end
|
108
205
|
|
109
206
|
def visit_ArelExtensions_Nodes_Replace o, collector
|
110
207
|
collector << "REPLACE("
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
collector << Arel::Visitors::MSSQL::COMMA
|
116
|
-
collector = visit o.right, collector
|
208
|
+
o.expressions.each_with_index { |arg, i|
|
209
|
+
collector << Arel::Visitors::MSSQL::COMMA unless i == 0
|
210
|
+
collector = visit arg, collector
|
211
|
+
}
|
117
212
|
collector << ")"
|
118
213
|
collector
|
119
214
|
end
|
120
215
|
|
121
216
|
|
217
|
+
# TODO manage case insensitivity
|
218
|
+
def visit_ArelExtensions_Nodes_IMatches o, collector
|
219
|
+
collector = infix_value o, collector, ' LIKE '
|
220
|
+
if o.escape
|
221
|
+
collector << ' ESCAPE '
|
222
|
+
visit o.escape, collector
|
223
|
+
else
|
224
|
+
collector
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# TODO manage case insensitivity
|
229
|
+
def visit_ArelExtensions_Nodes_IDoesNotMatch o, collector
|
230
|
+
collector = infix_value o, collector, ' NOT LIKE '
|
231
|
+
if o.escape
|
232
|
+
collector << ' ESCAPE '
|
233
|
+
collector = visit o.escape, collector
|
234
|
+
end
|
235
|
+
collector
|
236
|
+
end
|
237
|
+
|
122
238
|
# SQL Server does not know about REGEXP
|
123
239
|
def visit_Arel_Nodes_Regexp o, collector
|
124
240
|
collector = visit o.left, collector
|
125
|
-
collector << "LIKE '
|
241
|
+
collector << "LIKE '%#{o.right}%'"
|
126
242
|
collector
|
127
243
|
end
|
128
244
|
|
129
245
|
def visit_Arel_Nodes_NotRegexp o, collector
|
130
246
|
collector = visit o.left, collector
|
131
|
-
collector << "NOT LIKE '
|
247
|
+
collector << "NOT LIKE '%#{o.right}%'"
|
132
248
|
collector
|
133
249
|
end
|
134
250
|
|
251
|
+
# TODO
|
252
|
+
def visit_ArelExtensions_Nodes_GroupConcat o, collector
|
253
|
+
collector << "(LISTAGG("
|
254
|
+
collector = visit o.left, collector
|
255
|
+
if o.right
|
256
|
+
collector << Arel::Visitors::Oracle::COMMA
|
257
|
+
collector = visit o.right, collector
|
258
|
+
end
|
259
|
+
collector << ") WITHIN GROUP (ORDER BY "
|
260
|
+
collector = visit o.left, collector
|
261
|
+
collector << "))"
|
262
|
+
collector
|
263
|
+
end
|
264
|
+
|
265
|
+
|
266
|
+
|
135
267
|
end
|
136
268
|
end
|
137
|
-
end
|
269
|
+
end
|
@@ -153,9 +153,11 @@ module ArelExtensions
|
|
153
153
|
end
|
154
154
|
|
155
155
|
def visit_ArelExtensions_Nodes_Blank o, collector
|
156
|
-
collector << '('
|
156
|
+
collector << 'LENGTH(TRIM(COALESCE('
|
157
157
|
collector = visit o.left, collector
|
158
|
-
collector <<
|
158
|
+
collector << Arel::Visitors::ToSql::COMMA
|
159
|
+
collector = visit Arel::Nodes.build_quoted(''), collector
|
160
|
+
collector << "))) = 0"
|
159
161
|
collector
|
160
162
|
end
|
161
163
|
|
@@ -271,7 +273,7 @@ module ArelExtensions
|
|
271
273
|
collector << "DATE_ADD("
|
272
274
|
collector = visit o.left, collector
|
273
275
|
collector << Arel::Visitors::ToSql::COMMA
|
274
|
-
collector = visit o.right, collector
|
276
|
+
collector = visit o.sqlite_value(o.right), collector
|
275
277
|
collector << ")"
|
276
278
|
collector
|
277
279
|
end
|
data/test/database.yml
CHANGED
@@ -3,7 +3,7 @@ sqlite:
|
|
3
3
|
database: ":memory:"
|
4
4
|
timeout: 500
|
5
5
|
jdbc-sqlite:
|
6
|
-
adapter: jdbcsqlite3
|
6
|
+
adapter: jdbcsqlite3
|
7
7
|
database: ":memory:"
|
8
8
|
timeout: 500
|
9
9
|
mysql:
|
@@ -41,7 +41,6 @@ ibm_db:
|
|
41
41
|
mssql:
|
42
42
|
adapter: sqlserver
|
43
43
|
host: localhost
|
44
|
-
dataserver: localhost\SQL2014
|
45
44
|
database: master
|
46
45
|
username: sa
|
47
46
|
password: Password12!
|
data/test/support/fake_record.rb
CHANGED
@@ -45,6 +45,10 @@ module FakeRecord
|
|
45
45
|
@tables.include? name.to_s
|
46
46
|
end
|
47
47
|
|
48
|
+
def data_source_exists? name
|
49
|
+
@tables.include? name.to_s
|
50
|
+
end
|
51
|
+
|
48
52
|
def columns name, message = nil
|
49
53
|
@columns[name.to_s]
|
50
54
|
end
|
@@ -110,6 +114,10 @@ module FakeRecord
|
|
110
114
|
connection.tables.include? name.to_s
|
111
115
|
end
|
112
116
|
|
117
|
+
def data_source_exists? name
|
118
|
+
connection.tables.include? name.to_s
|
119
|
+
end
|
120
|
+
|
113
121
|
def columns_hash
|
114
122
|
connection.columns_hash
|
115
123
|
end
|
@@ -5,6 +5,7 @@ module ArelExtensions
|
|
5
5
|
module WthAr
|
6
6
|
|
7
7
|
class ListTest < Minitest::Test
|
8
|
+
require 'minitest/pride'
|
8
9
|
def setup_db
|
9
10
|
ActiveRecord::Base.configurations = YAML.load_file('test/database.yml')
|
10
11
|
if ENV['DB'] == 'oracle' && ((defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx") || (RUBY_PLATFORM == 'java')) # not supported
|
@@ -39,7 +40,13 @@ module ArelExtensions
|
|
39
40
|
end
|
40
41
|
if File.exist?("init/#{@env_db}.sql")
|
41
42
|
sql = File.read("init/#{@env_db}.sql")
|
42
|
-
@
|
43
|
+
if @env_db == 'mssql'
|
44
|
+
sql.split(/^GO\s*$/).each {|str|
|
45
|
+
@cnx.execute(str.strip) unless str.blank?
|
46
|
+
}
|
47
|
+
else
|
48
|
+
@cnx.execute(sql) unless sql.blank?
|
49
|
+
end
|
43
50
|
end
|
44
51
|
@cnx.drop_table(:user_tests) rescue nil
|
45
52
|
@cnx.create_table :user_tests do |t|
|
@@ -138,16 +145,25 @@ module ArelExtensions
|
|
138
145
|
end
|
139
146
|
|
140
147
|
def test_sum
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
148
|
+
if @env_db == 'mssql'
|
149
|
+
skip "SQL Server forces order?" # TODO
|
150
|
+
assert_equal 68, User.select((@age.sum + 1).as("res"), User.arel_table[:id].sum).take(50).reorder(@age).first.res
|
151
|
+
assert_equal 134, User.reorder(nil).select((@age.sum + @age.sum).as("res"), User.arel_table[:id].sum).take(50).first.res
|
152
|
+
assert_equal 201, User.reorder(nil).select(((@age * 3).sum).as("res"), User.arel_table[:id].sum).take(50).first.res
|
153
|
+
assert_equal 4009, User.reorder(nil).select(((@age * @age).sum).as("res"), User.arel_table[:id].sum).take(50).first.res
|
154
|
+
else
|
155
|
+
assert_equal 68, User.select((@age.sum + 1).as("res")).take(50).first.res
|
156
|
+
assert_equal 134, User.select((@age.sum + @age.sum).as("res")).take(50).first.res
|
157
|
+
assert_equal 201, User.select(((@age * 3).sum).as("res")).take(50).first.res
|
158
|
+
assert_equal 4009, User.select(((@age * @age).sum).as("res")).take(50).first.res
|
159
|
+
end
|
145
160
|
end
|
146
161
|
|
147
162
|
# String Functions
|
148
163
|
def test_concat
|
149
164
|
assert_equal 'Camille Camille', t(@camille, @name + ' ' + @name)
|
150
165
|
assert_equal 'Laure 2', t(@laure, @name + ' ' + 2)
|
166
|
+
skip "TODO: find a way... to do group_concat/listagg in SQL Server" if @env_db == 'mssql'
|
151
167
|
if @env_db == 'postgresql'
|
152
168
|
assert_equal "Lucas Sophie", t(User.reorder(nil).from(User.select(:name).where(:name => ['Lucas', 'Sophie']).reorder(:name).as('user_tests')), @name.group_concat(' '))
|
153
169
|
else
|
@@ -170,12 +186,14 @@ module ArelExtensions
|
|
170
186
|
|
171
187
|
def test_find_in_set
|
172
188
|
skip "Sqlite version can't load extension for find_in_set" if $sqlite && $load_extension_disabled
|
189
|
+
skip "SQL Server does not know about FIND_IN_SET" if @env_db == 'mssql'
|
173
190
|
assert_equal 5, t(@neg, @comments & 2)
|
174
191
|
assert_equal 0, t(@neg, @comments & 6) # not found
|
175
192
|
end
|
176
193
|
|
177
194
|
def test_string_comparators
|
178
195
|
skip "Oracle can't use math operators to compare strings" if @env_db == 'oracle' # use GREATEST ?
|
196
|
+
skip "SQL Server can't use math operators to compare strings" if @env_db == 'mssql' # use GREATEST ?
|
179
197
|
if @env_db == 'postgresql' # may return real boolean
|
180
198
|
assert t(@neg, @name >= 'Mest') == true || t(@neg, @name >= 'Mest') == 't' # depends of ar version
|
181
199
|
assert t(@neg, @name <= (@name + 'Z')) == true || t(@neg, @name <= (@name + 'Z')) == 't'
|
@@ -187,6 +205,7 @@ module ArelExtensions
|
|
187
205
|
|
188
206
|
def test_regexp_not_regexp
|
189
207
|
skip "Sqlite version can't load extension for regexp" if $sqlite && $load_extension_disabled
|
208
|
+
skip "SQL Server does not know about REGEXP without extensions" if @env_db == 'mssql'
|
190
209
|
assert_equal 1, User.where(@name =~ '^M').count
|
191
210
|
assert_equal 6, User.where(@name !~ '^L').count
|
192
211
|
assert_equal 1, User.where(@name =~ /^M/).count
|
@@ -213,9 +232,10 @@ module ArelExtensions
|
|
213
232
|
|
214
233
|
def test_trim
|
215
234
|
assert_equal "Myung", t(@myung, @name.trim)
|
235
|
+
assert_equal "Myung", t(@myung, @name.trim.ltrim.rtrim)
|
236
|
+
skip "SQL Server does not manage argument for (L/R)TRIM" if @env_db == 'mssql'
|
216
237
|
assert_equal "Myun", t(@myung, @name.rtrim("g"))
|
217
238
|
assert_equal "yung", t(@myung, @name.ltrim("M"))
|
218
|
-
assert_equal "Myung", t(@myung, @name.trim.ltrim.rtrim)
|
219
239
|
assert_equal "yung", t(@myung, (@name + "M").trim("M"))
|
220
240
|
skip "Oracle does not accept multi char trim" if @env_db == 'oracle'
|
221
241
|
assert_equal "", t(@myung, @name.rtrim(@name))
|
@@ -237,6 +257,7 @@ module ArelExtensions
|
|
237
257
|
|
238
258
|
def test_format
|
239
259
|
assert_equal '2016-05-23', t(@lucas, @created_at.format('%Y-%m-%d'))
|
260
|
+
skip "SQL Server does not accept any format" if @env_db == 'mssql'
|
240
261
|
assert_equal '2014/03/03 12:42:00', t(@lucas, @updated_at.format('%Y/%m/%d %H:%M:%S'))
|
241
262
|
end
|
242
263
|
|
@@ -276,7 +297,7 @@ module ArelExtensions
|
|
276
297
|
assert_equal 5, t(@camille, @created_at.month).to_i
|
277
298
|
assert_equal 8, User.where(@created_at.month.eq("05")).count
|
278
299
|
#Week
|
279
|
-
assert_equal 21, t(@arthur, @created_at.week).to_i
|
300
|
+
assert_equal (@env_db == 'mssql' ? 22 : 21), t(@arthur, @created_at.week).to_i
|
280
301
|
assert_equal 8, User.where(@created_at.month.eq("05")).count
|
281
302
|
#Day
|
282
303
|
assert_equal 23, t(@laure, @created_at.day).to_i
|
@@ -327,7 +348,7 @@ module ArelExtensions
|
|
327
348
|
|
328
349
|
def test_wday
|
329
350
|
d = Date.new(2016, 6, 26)
|
330
|
-
assert_equal(@env_db == 'oracle' ? 2 : 1, t(@myung, @created_at.wday).to_i) # monday
|
351
|
+
assert_equal(@env_db == 'oracle' || @env_db == 'mssql' ? 2 : 1, t(@myung, @created_at.wday).to_i) # monday
|
331
352
|
assert_equal 0, User.select(d.wday).as("res").first.to_i
|
332
353
|
end
|
333
354
|
|
@@ -19,7 +19,9 @@ module ArelExtensions
|
|
19
19
|
Arel::Table.engine = ActiveRecord::Base
|
20
20
|
if File.exist?("init/#{@env_db}.sql")
|
21
21
|
sql = File.read("init/#{@env_db}.sql")
|
22
|
-
|
22
|
+
unless sql.blank?
|
23
|
+
@cnx.execute(sql) rescue $stderr << "can't create functions"
|
24
|
+
end
|
23
25
|
end
|
24
26
|
@cnx.drop_table(:user_tests) rescue nil
|
25
27
|
@cnx.create_table :user_tests do |t|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arel_extensions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yann Azoury
|
@@ -10,20 +10,20 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2016-
|
13
|
+
date: 2016-11-06 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: arel
|
17
17
|
requirement: !ruby/object:Gem::Requirement
|
18
18
|
requirements:
|
19
|
-
- - "
|
19
|
+
- - ">="
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: '6.0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
|
-
- - "
|
26
|
+
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
version: '6.0'
|
29
29
|
- !ruby/object:Gem::Dependency
|
@@ -78,6 +78,7 @@ extensions: []
|
|
78
78
|
extra_rdoc_files:
|
79
79
|
- MIT-LICENSE.txt
|
80
80
|
- README.md
|
81
|
+
- functions.html
|
81
82
|
files:
|
82
83
|
- ".gitignore"
|
83
84
|
- ".travis.yml"
|
@@ -90,6 +91,7 @@ files:
|
|
90
91
|
- MIT-LICENSE.txt
|
91
92
|
- README.md
|
92
93
|
- Rakefile
|
94
|
+
- SQL_Challenges.md
|
93
95
|
- TODO
|
94
96
|
- appveyor.yml
|
95
97
|
- arel_extensions.gemspec
|