arel_extensions 0.8.7 → 0.9.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.
- 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
|