arel_extensions 0.9.8 → 1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +25 -0
- data/.rubocop.yml +6 -0
- data/.travis.yml +23 -15
- data/.travis/oracle/download.js +94 -58
- data/.travis/oracle/download.sh +1 -0
- data/Gemfile +1 -1
- data/README.md +30 -7
- data/TODO +8 -15
- data/arel_extensions.gemspec +2 -2
- data/functions.html +13 -2
- data/gemfiles/rails4.gemfile +2 -2
- data/gemfiles/rails5.gemfile +2 -2
- data/gemfiles/rails5_0.gemfile +2 -2
- data/lib/arel_extensions.rb +27 -0
- data/lib/arel_extensions/math.rb +72 -38
- data/lib/arel_extensions/nodes/as.rb +12 -0
- data/lib/arel_extensions/nodes/function.rb +1 -1
- data/lib/arel_extensions/nodes/union.rb +24 -0
- data/lib/arel_extensions/nodes/union_all.rb +20 -0
- data/lib/arel_extensions/version.rb +1 -1
- data/lib/arel_extensions/visitors/ibm_db.rb +2 -0
- data/lib/arel_extensions/visitors/mssql.rb +1 -0
- data/lib/arel_extensions/visitors/mysql.rb +1 -0
- data/lib/arel_extensions/visitors/oracle.rb +16 -7
- data/lib/arel_extensions/visitors/postgresql.rb +6 -5
- data/lib/arel_extensions/visitors/sqlite.rb +32 -6
- data/lib/arel_extensions/visitors/to_sql.rb +36 -14
- data/test/visitors/test_to_sql.rb +48 -2
- data/test/with_ar/all_agnostic_test.rb +22 -2
- data/test/with_ar/insert_agnostic_test.rb +26 -2
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5142c37a719ede88896bbdcfa06b0690ddc4b75
|
4
|
+
data.tar.gz: 9da10d459cb7e1fa8a52305d12f1fb0ca945a537
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61d6268f8785ffd105d29b1fead496f0e7e35bfd1f660174e6fd922df197341449c2cb4379d4de37d91c62de24f48bfcb3be5aba83178f139c98d15189302e69
|
7
|
+
data.tar.gz: cd7c81aeedf284bcb67501ea045d82dce9cd2ff8d52f622262b7f3f0633f0c3681613de4977ef3c62aeca0f43d60d201a5fbb1f7f5f8ca5a4df36e17f7ca0073
|
data/.codeclimate.yml
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
---
|
2
|
+
engines:
|
3
|
+
rubocop:
|
4
|
+
enabled: true
|
5
|
+
#checks:
|
6
|
+
# Rubocop/Metrics/ClassLength:
|
7
|
+
# enabled: false
|
8
|
+
duplication:
|
9
|
+
enabled: false
|
10
|
+
brakeman:
|
11
|
+
enabled: false
|
12
|
+
reek:
|
13
|
+
enabled: true
|
14
|
+
"bundler-audit":
|
15
|
+
enabled: false
|
16
|
+
markdownlint:
|
17
|
+
enabled: false
|
18
|
+
ratings:
|
19
|
+
paths:
|
20
|
+
- lib/**
|
21
|
+
- "**.rb"
|
22
|
+
- "**.md"
|
23
|
+
exclude_paths:
|
24
|
+
- .travis/**/*
|
25
|
+
- test/**/*
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
@@ -59,39 +59,41 @@ rvm:
|
|
59
59
|
- ruby-head
|
60
60
|
jdk:
|
61
61
|
- openjdk7
|
62
|
-
- oraclejdk7
|
63
62
|
- oraclejdk8
|
63
|
+
- oraclejdk9
|
64
64
|
matrix:
|
65
65
|
fast_finish: true
|
66
66
|
exclude:
|
67
67
|
- rvm: rbx-2
|
68
68
|
jdk: openjdk7
|
69
|
-
- rvm: rbx-2
|
70
|
-
jdk: oraclejdk7
|
71
69
|
- rvm: 2.0.0
|
72
70
|
jdk: openjdk7
|
73
|
-
- rvm: 2.0.0
|
74
|
-
jdk: oraclejdk7
|
75
71
|
- rvm: 2.1
|
76
72
|
jdk: openjdk7
|
77
|
-
- rvm: 2.1
|
78
|
-
jdk: oraclejdk7
|
79
73
|
- rvm: 2.2.5
|
80
74
|
jdk: openjdk7
|
81
|
-
- rvm: 2.2.5
|
82
|
-
jdk: oraclejdk7
|
83
75
|
- rvm: 2.3.1
|
84
76
|
jdk: openjdk7
|
85
|
-
- rvm: 2.3.1
|
86
|
-
jdk: oraclejdk7
|
87
77
|
- rvm: 2.4.0
|
88
78
|
jdk: openjdk7
|
89
|
-
- rvm: 2.4.0
|
90
|
-
jdk: oraclejdk7
|
91
79
|
- rvm: ruby-head
|
80
|
+
jdk: openjdk7
|
81
|
+
- rvm: jruby-head
|
92
82
|
jdk: openjdk7
|
83
|
+
- rvm: rbx-2
|
84
|
+
jdk: oraclejdk8
|
85
|
+
- rvm: 2.0.0
|
86
|
+
jdk: oraclejdk8
|
87
|
+
- rvm: 2.1
|
88
|
+
jdk: oraclejdk8
|
89
|
+
- rvm: 2.2.5
|
90
|
+
jdk: oraclejdk8
|
91
|
+
- rvm: 2.3.1
|
92
|
+
jdk: oraclejdk8
|
93
|
+
- rvm: 2.4.0
|
94
|
+
jdk: oraclejdk8
|
93
95
|
- rvm: ruby-head
|
94
|
-
jdk:
|
96
|
+
jdk: oraclejdk8
|
95
97
|
- rvm: 2.0.0
|
96
98
|
gemfile: gemfiles/rails5.gemfile
|
97
99
|
- rvm: 2.1
|
@@ -121,6 +123,12 @@ matrix:
|
|
121
123
|
gemfile: gemfiles/rails5.gemfile
|
122
124
|
- rvm: jruby-head
|
123
125
|
gemfile: gemfiles/rails5.gemfile
|
126
|
+
- rvm: jruby-head
|
127
|
+
gemfile: gemfiles/rails4.gemfile
|
128
|
+
jdk: oraclejdk9
|
124
129
|
bundler_args: "--jobs 3 --retry 2"
|
125
130
|
notifications:
|
126
|
-
email:
|
131
|
+
email:
|
132
|
+
notifications:
|
133
|
+
- julien.delporte@faveod.com
|
134
|
+
- yann.azoury@faveod.com
|
data/.travis/oracle/download.js
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
// vim: set et sw=2 ts=2:
|
2
2
|
"use strict";
|
3
3
|
var env = process.env;
|
4
|
+
var url = require('url');
|
4
5
|
var Promise = require('bluebird');
|
5
6
|
var Phantom = Promise.promisifyAll(require('node-phantom-simple'));
|
6
|
-
|
7
|
+
|
8
|
+
var login = {
|
9
|
+
begin: url.parse(env['ORACLE_LOGIN_BEGIN'] || "https://www.oracle.com/webapps/redirect/signon?nexturl=https://www.oracle.com/favicon.ico"),
|
10
|
+
end: url.parse(env['ORCALE_LOGIN_END'] || "https://www.oracle.com/favicon.ico"),
|
11
|
+
};
|
12
|
+
delete env['ORACLE_LOGIN_BEGIN'];
|
13
|
+
delete env['ORACLE_LOGIN_END'];
|
7
14
|
|
8
15
|
var credentials = Object.keys(env)
|
9
16
|
.filter(function (key) { return key.indexOf('ORACLE_LOGIN_') == 0 })
|
@@ -16,38 +23,92 @@ if (credentials.length <= 0) {
|
|
16
23
|
|
17
24
|
Phantom.createAsync({ parameters: { 'ssl-protocol': 'tlsv1' } }).then(function (browser) {
|
18
25
|
browser = Promise.promisifyAll(browser, { suffix: 'Promise' });
|
26
|
+
browser.addCookie({'name': 'oraclelicense', 'value': "accept-" + env['ORACLE_COOKIE'] + "-cookie", 'domain': '.oracle.com' });
|
19
27
|
|
20
|
-
//
|
21
|
-
return browser
|
22
|
-
.addCookiePromise({'name': 'oraclelicense', 'value': "accept-" + env['ORACLE_COOKIE'] + "-cookie", 'domain': '.oracle.com' })
|
23
|
-
.then(function () {
|
24
|
-
return browser.createPagePromise();
|
25
|
-
})
|
26
|
-
.then(function (page) {
|
28
|
+
// Open a tab, configure it
|
29
|
+
return browser.createPagePromise().then(function (page) {
|
27
30
|
page = Promise.promisifyAll(page, { suffix: 'Promise' });
|
28
31
|
|
29
|
-
|
32
|
+
var received = "";
|
33
|
+
page.onNavigationRequested = function () { console.info("%s %j", (new Date()).toISOString(), arguments["0"]); };
|
30
34
|
page.onResourceError = console.error.bind(console);
|
35
|
+
page.onResourceReceived = function (response) { if (response.stage == "end") received = response.url; };
|
36
|
+
page.set('settings.loadImages', false);
|
37
|
+
|
31
38
|
return page
|
32
39
|
.setPromise('settings.userAgent', env['USER_AGENT']) // PhantomJS configures the UA per tab
|
33
40
|
|
34
|
-
//
|
41
|
+
// Begin login, wait for the login page
|
35
42
|
.then(function () {
|
36
|
-
return page.openPromise(
|
37
|
-
if (status != 'success') throw "Unable to connect to
|
38
|
-
|
43
|
+
return page.openPromise(login.begin.href).then(function (status) {
|
44
|
+
if (status != 'success') throw "Unable to connect to " + login.begin.host;
|
45
|
+
|
46
|
+
return new Promise(function (resolve, reject) {
|
47
|
+
var deadline = Date.now() + 6000;
|
48
|
+
var interval = 100;
|
49
|
+
|
50
|
+
var check = function () {
|
51
|
+
if (deadline < Date.now()) return reject("Timeout waiting for form");
|
52
|
+
|
53
|
+
page.evaluate(function () {
|
54
|
+
return window['jQuery'] && document.querySelectorAll('input[type=password]').length;
|
55
|
+
}, function (err, result) {
|
56
|
+
if (result) { resolve(); } else { setTimeout(check, interval); }
|
57
|
+
});
|
58
|
+
};
|
59
|
+
|
60
|
+
check();
|
61
|
+
});
|
39
62
|
})
|
40
|
-
.
|
63
|
+
.tapCatch(function (err) {
|
41
64
|
return page.getPromise('plainText').then(function (text) {
|
42
65
|
console.error("Unable to load login page. Last response was:\n" + text);
|
43
|
-
|
66
|
+
});
|
67
|
+
});
|
68
|
+
})
|
69
|
+
|
70
|
+
// Submit the login form
|
71
|
+
.then(function () {
|
72
|
+
return page.evaluatePromise(function (credentials) {
|
73
|
+
var $form = jQuery(document.forms[0]);
|
74
|
+
return credentials.filter(function (tuple) {
|
75
|
+
return $form.find("[name='"+tuple[0]+"']").val(tuple[1]).length == 0;
|
76
|
+
})
|
77
|
+
.map(function (tuple) { return tuple[0]; });
|
78
|
+
}, credentials)
|
79
|
+
.then(function (unapplied) {
|
80
|
+
if (unapplied.length > 0) {
|
81
|
+
console.warn("Unable to use all ORACLE_LOGIN environment variables: %j", unapplied);
|
82
|
+
}
|
83
|
+
return page.evaluatePromise(function () {
|
84
|
+
jQuery(function () { document.forms[0].submit(); });
|
85
|
+
});
|
86
|
+
});
|
87
|
+
})
|
88
|
+
|
89
|
+
// Wait for login result
|
90
|
+
.then(function () {
|
91
|
+
return new Promise(function (resolve, reject) {
|
92
|
+
var deadline = Date.now() + 6000;
|
93
|
+
var interval = 100;
|
94
|
+
|
95
|
+
var check = function () {
|
96
|
+
if (deadline < Date.now()) return reject("Timeout waiting for " + login.end.href);
|
97
|
+
if (received == login.end.href) { resolve(); } else { setTimeout(check, interval); }
|
98
|
+
};
|
99
|
+
|
100
|
+
check();
|
101
|
+
})
|
102
|
+
.tapCatch(function (err) {
|
103
|
+
return page.getPromise('plainText').then(function (text) {
|
104
|
+
console.error("Unable to load login result. Last response was:\n" + text);
|
44
105
|
});
|
45
106
|
});
|
46
107
|
})
|
47
108
|
|
48
109
|
// Export cookies for cURL
|
49
110
|
.then(function () {
|
50
|
-
return
|
111
|
+
return browser.getPromise('cookies').then(function (cookies) {
|
51
112
|
var data = "";
|
52
113
|
for (var i = 0; i < cookies.length; ++i) {
|
53
114
|
var cookie = cookies[i];
|
@@ -55,52 +116,27 @@ Phantom.createAsync({ parameters: { 'ssl-protocol': 'tlsv1' } }).then(function (
|
|
55
116
|
+ (cookie.secure ? "TRUE" : "FALSE") + "\t0\t"
|
56
117
|
+ cookie.name + "\t" + cookie.value + "\n";
|
57
118
|
}
|
58
|
-
return Promise.
|
119
|
+
return Promise.promisify(require('fs').writeFile)(env['COOKIES'], data);
|
59
120
|
});
|
60
121
|
})
|
61
122
|
|
62
|
-
//
|
123
|
+
// Download file using cURL
|
63
124
|
.then(function () {
|
64
|
-
return
|
65
|
-
var
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
return !applied;
|
80
|
-
})
|
81
|
-
.map(function (tuple) { return tuple[0] });
|
82
|
-
|
83
|
-
if (unapplied.length > 0) {
|
84
|
-
console.warn("Unable to use all ORACLE_LOGIN environment variables: %j", unapplied);
|
85
|
-
}
|
86
|
-
|
87
|
-
var cmd = ['curl', [
|
88
|
-
'--cookie', env['COOKIES'],
|
89
|
-
'--cookie-jar', env['COOKIES'],
|
90
|
-
'--data', '@-',
|
91
|
-
'--location',
|
92
|
-
'--output', env['ORACLE_DOWNLOAD_FILE'],
|
93
|
-
'--user-agent', env['USER_AGENT'],
|
94
|
-
form.action
|
95
|
-
]];
|
96
|
-
|
97
|
-
console.info("Executing %j", cmd);
|
98
|
-
|
99
|
-
var child_process = require('child_process');
|
100
|
-
var child = child_process.spawn.apply(child_process, cmd.concat({ stdio: ['pipe', 1, 2] }));
|
101
|
-
child.on('exit', process.exit);
|
102
|
-
child.stdin.end(form.data);
|
103
|
-
});
|
125
|
+
return browser.exitPromise().then(function () {
|
126
|
+
var cmd = ['curl', [
|
127
|
+
'--cookie', env['COOKIES'],
|
128
|
+
'--cookie-jar', env['COOKIES'],
|
129
|
+
'--location',
|
130
|
+
'--output', env['ORACLE_DOWNLOAD_FILE'],
|
131
|
+
'--user-agent', env['USER_AGENT'],
|
132
|
+
"https://edelivery.oracle.com/akam/otn/linux/" + env['ORACLE_FILE']
|
133
|
+
]];
|
134
|
+
|
135
|
+
console.info("Executing %j", cmd);
|
136
|
+
|
137
|
+
var child_process = require('child_process');
|
138
|
+
var child = child_process.spawn.apply(child_process, cmd.concat({ stdio: [0, 1, 2] }));
|
139
|
+
child.on('exit', process.exit);
|
104
140
|
});
|
105
141
|
})
|
106
142
|
.catch(function (err) {
|
data/.travis/oracle/download.sh
CHANGED
data/Gemfile
CHANGED
@@ -13,7 +13,7 @@ group :development, :test do
|
|
13
13
|
gem "activerecord-jdbcpostgresql-adapter", :platforms => :jruby
|
14
14
|
|
15
15
|
|
16
|
-
gem "tiny_tds",
|
16
|
+
gem "tiny_tds", '~> 1.3.0' ,:require => false, :platforms => [:mingw, :x64_mingw, :mswin]
|
17
17
|
gem "activerecord-sqlserver-adapter", '~> 4.2.0', :platforms => [:mingw, :x64_mingw, :mswin]
|
18
18
|
|
19
19
|
gem 'activesupport', '~> 4.0'
|
data/README.md
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
# Arel Extensions
|
2
2
|
|
3
|
-
[![Build Status](https://
|
4
|
-
[![
|
3
|
+
[![Travis Build Status](https://img.shields.io/travis/Faveod/arel-extensions.svg?label=Travis%20build)](http://travis-ci.org/Faveod/arel-extensions)
|
4
|
+
[![AppVeyor Build Status](https://img.shields.io/appveyor/ci/yazfav/arel-extensions.svg?label=AppVeyor%20build)](https://ci.appveyor.com/project/yazfav/arel-extensions)
|
5
|
+
[![Code Climate](https://img.shields.io/codeclimate/github/Faveod/arel-extensions.svg)](https://codeclimate.com/github/Faveod/arel-extensions)
|
6
|
+
[![Gemnasium](https://img.shields.io/gemnasium/Faveod/arel-extensions.svg)](https://gemnasium.com/github.com/Faveod/arel-extensions)
|
7
|
+
[![Security](https://hakiri.io/github/Faveod/arel-extensions/master.svg)](https://hakiri.io/github/Faveod/arel-extensions/master)
|
5
8
|
![](http://img.shields.io/badge/license-MIT-brightgreen.svg)
|
6
|
-
|
7
|
-
![](https://
|
9
|
+
|
10
|
+
Gem: [![Latest Release](https://img.shields.io/gem/v/arel_extensions.svg)](https://rubygems.org/gems/arel_extensions)
|
11
|
+
[![Gem](https://ruby-gem-downloads-badge.herokuapp.com/arel_extensions?type=total)](https://rubygems.org/gems/arel_extensions)
|
12
|
+
[![Gem](https://ruby-gem-downloads-badge.herokuapp.com/arel_extensions?label=downloads-current-version)](https://rubygems.org/gems/arel_extensions)
|
8
13
|
|
9
14
|
Arel Extensions adds shortcuts, fixes and new ORM mappings (ruby to SQL) to Arel.
|
10
15
|
It aims to ensure pure ruby syntax for the biggest number of usual cases.
|
@@ -121,11 +126,11 @@ t[:birthdate].format('%Y-%m-%d').to_sql
|
|
121
126
|
# => DATE_FORMAT(my_table.birthdate, '%Y-%m-%d')
|
122
127
|
```
|
123
128
|
|
124
|
-
## Unions
|
129
|
+
## Unions
|
125
130
|
|
126
131
|
```ruby
|
127
132
|
(t.where(t[:name].eq('str')) + t.where(t[:name].eq('test'))).to_sql
|
128
|
-
# => SELECT * FROM my_table WHERE
|
133
|
+
# => (SELECT * FROM my_table WHERE name='str') UNION (SELECT * FROM my_table WHERE name='test')
|
129
134
|
```
|
130
135
|
|
131
136
|
## Stored Procedures and User-defined functions
|
@@ -496,6 +501,24 @@ User.connection.execute(insert_manager.to_sql)
|
|
496
501
|
<td class="ok">✔</td>
|
497
502
|
<td class="ok">✔</td>
|
498
503
|
<td class="ok">✔</td>
|
504
|
+
</tr> <tr>
|
505
|
+
<th class="set_operators" rowspan="1"><div>Set <br/> Operators</div></th>
|
506
|
+
<td class="tg-yw4l">UNION (+)<br/>query + query</td>
|
507
|
+
<td class="ok">✔</td>
|
508
|
+
<td class="ok">✔</td>
|
509
|
+
<td class="ok">✔</td>
|
510
|
+
<td class="ok">✔</td>
|
511
|
+
<td class="ok">✔</td>
|
512
|
+
<td class="ok">✔</td>
|
513
|
+
</tr> <tr>
|
514
|
+
<th class="set_operators" rowspan="1"><div>Set <br/> Operators</div></th>
|
515
|
+
<td class="tg-yw4l">UNION ALL<br/>query.union_all(query)</td>
|
516
|
+
<td class="ok">✔</td>
|
517
|
+
<td class="ok">✔</td>
|
518
|
+
<td class="ok">✔</td>
|
519
|
+
<td class="ok">✔</td>
|
520
|
+
<td class="ok">✔</td>
|
521
|
+
<td class="ok">✔</td>
|
499
522
|
</tr>
|
500
523
|
</tbody>
|
501
|
-
</table>
|
524
|
+
</table>
|
data/TODO
CHANGED
@@ -1,30 +1,23 @@
|
|
1
1
|
Code quality:
|
2
|
-
|
2
|
+
+ codeclimate.com
|
3
|
+
+ gemnasium
|
4
|
+
+ appveyor.com (windows)
|
5
|
+
+ hakiri.io
|
3
6
|
- coveralls.io
|
4
|
-
- gemnasium
|
5
7
|
- inch-ci.org
|
6
|
-
-
|
7
|
-
- hakiri.io
|
8
|
-
|
9
|
-
[![Gem Version](https://img.shields.io/gem/v/arel_extensions.svg)][gem]
|
10
|
-
[![Build Status](https://img.shields.io/travis/faveod/arel_extensions.svg)][travis]
|
11
|
-
[![Dependency Status](https://img.shields.io/gemnasium/faveod/arel_extensions.svg)][https://gemnasium.com/faveod/arel_extensions]
|
12
|
-
[![Code Climate](https://img.shields.io/codeclimate/github/faveod/arel_extensions.svg)][https://codeclimate.com/github/faveod/arel_extensions]
|
13
|
-
[![Coverage Status](https://img.shields.io/coveralls/faveod/arel_extensions.svg)][https://coveralls.io/r/faveod/arel_extensions]
|
14
|
-
[![Inline docs](http://inch-ci.org/github/faveod/arel_extensions.svg)][https://inch-ci.org/github/faveod/https://gemnasium.com/faveod/arel_extensions]
|
15
|
-
[![security](https://hakiri.io/github/faveod/arel_extensions/master.svg)](https://hakiri.io/github/faveod/arel_extensions/master)
|
16
|
-
[![codebeat](https://codebeat.co/badges/<change>)](https://codebeat.co/projects/github-com-faveod-arel_extensions)
|
8
|
+
- codebeat.co
|
17
9
|
|
18
10
|
TODO:
|
19
11
|
- all skips in tests
|
12
|
+
- all SQL Server date formats
|
20
13
|
|
21
14
|
New features:
|
22
15
|
- POSIX format numbers (%.2f), dates (man 3 printf)
|
23
|
-
- easy unions
|
24
16
|
- cast, to_char
|
17
|
+
- auto-remove order by in sub queries
|
25
18
|
- avoid incompatible queries in PgSQL/Oracle/SQLServer when columns are used outside of group by columns (in select or order by)
|
26
19
|
|
27
20
|
Tests improvements:
|
28
21
|
- SQL Server
|
29
22
|
- DB2
|
30
|
-
- Informix
|
23
|
+
- Informix
|
data/arel_extensions.gemspec
CHANGED
@@ -5,8 +5,8 @@ Gem::Specification.new do |s|
|
|
5
5
|
s.name = "arel_extensions"
|
6
6
|
s.version = ArelExtensions::VERSION
|
7
7
|
s.platform = Gem::Platform::RUBY
|
8
|
-
s.authors = ["Yann Azoury", "
|
9
|
-
s.email = ["yann.azoury@faveod.com", "
|
8
|
+
s.authors = ["Yann Azoury", "Félix Bellanger", "Julien Delporte"]
|
9
|
+
s.email = ["yann.azoury@faveod.com", "felix.bellanger@faveod.com", "julien.delporte@faveod.com"]
|
10
10
|
s.homepage = "https://github.com/Faveod/arel-extensions"
|
11
11
|
s.description = "Adds new features to Arel"
|
12
12
|
s.summary = "Extending Arel"
|
data/functions.html
CHANGED
@@ -23,6 +23,7 @@
|
|
23
23
|
.arel-functions th[rowspan] { vertical-align: middle; width:3em;}
|
24
24
|
.arel-functions th[rowspan] > div { transform:rotate(-90deg); transform-origin: center center; white-space: nowrap; }
|
25
25
|
.arel-functions td.ok { color:green; text-align:center; }
|
26
|
+
.arel-functions td.ko { color:red; text-align:center; }
|
26
27
|
</style>
|
27
28
|
</head>
|
28
29
|
<body>
|
@@ -96,7 +97,7 @@
|
|
96
97
|
<td class="ok">✔</td>
|
97
98
|
</tr>
|
98
99
|
<tr>
|
99
|
-
<th class="tg-ffjm" rowspan="
|
100
|
+
<th class="tg-ffjm" rowspan="14"><div>String functions</div></th>
|
100
101
|
<td class="tg-yw4l">CONCAT<br>column + "string"</td>
|
101
102
|
<td class="ok">✔</td>
|
102
103
|
<td class="ok">✔</td>
|
@@ -378,9 +379,19 @@
|
|
378
379
|
<td class="ok">✔</td>
|
379
380
|
<td class="ok">✔</td>
|
380
381
|
<td class="ok">✔</td>
|
382
|
+
</tr>
|
383
|
+
<tr>
|
384
|
+
<th class="bulk_insert" rowspan="1"><div>Set<br/> Operators</div></th>
|
385
|
+
<td class="tg-yw4l">UNION ( + )<br/>query + query</td>
|
386
|
+
<td class="ko">to be tested</td>
|
387
|
+
<td class="ko">to be tested</td>
|
388
|
+
<td class="ko">to be tested</td>
|
389
|
+
<td class="ko">to be tested</td>
|
390
|
+
<td class="ko">to be tested</td>
|
391
|
+
<td class="ko">to be tested</td>
|
381
392
|
</tr>
|
382
393
|
</tbody>
|
383
394
|
</table>
|
384
395
|
|
385
396
|
</body>
|
386
|
-
</html>
|
397
|
+
</html>
|
data/gemfiles/rails4.gemfile
CHANGED
@@ -11,8 +11,8 @@ group :development, :test do
|
|
11
11
|
gem "mysql2", :platforms => [:mri, :mswin, :mingw]
|
12
12
|
gem "pg", :platforms => [:mri, :mingw]
|
13
13
|
|
14
|
-
gem "tiny_tds", :platforms => [:mri, :mingw, :mswin]
|
15
|
-
gem "activerecord-sqlserver-adapter", '~> 4.2.0', :platforms => [:mri, :mingw, :mswin]
|
14
|
+
gem "tiny_tds", :platforms => [:mri, :mingw, :mswin] if RUBY_PLATFORM =~ /windows/
|
15
|
+
gem "activerecord-sqlserver-adapter", '~> 4.2.0', :platforms => [:mri, :mingw, :mswin] if RUBY_PLATFORM =~ /windows/
|
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/gemfiles/rails5.gemfile
CHANGED
@@ -11,7 +11,7 @@ group :development, :test do
|
|
11
11
|
gem "mysql2", :platforms => [:mri, :mswin, :mingw]
|
12
12
|
gem "pg", :platforms => [:mri, :mingw]
|
13
13
|
|
14
|
-
gem "tiny_tds", :platforms => [:mri, :mingw]
|
14
|
+
gem "tiny_tds", :platforms => [:mri, :mingw] if RUBY_PLATFORM =~ /windows/
|
15
15
|
# gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw]
|
16
16
|
|
17
17
|
gem 'ruby-oci8', :platforms => [:mri, :mswin, :mingw] if ENV.has_key? 'ORACLE_HOME'
|
@@ -26,4 +26,4 @@ group :development, :test do
|
|
26
26
|
gem "activerecord-jdbcmssql-adapter", :platforms => :jruby
|
27
27
|
end
|
28
28
|
|
29
|
-
gemspec :path => "../"
|
29
|
+
gemspec :path => "../"
|
data/gemfiles/rails5_0.gemfile
CHANGED
@@ -11,7 +11,7 @@ group :development, :test do
|
|
11
11
|
gem "mysql2", :platforms => [:mri, :mswin, :mingw]
|
12
12
|
gem "pg", :platforms => [:mri, :mingw]
|
13
13
|
|
14
|
-
gem "tiny_tds", :platforms => [:mri, :mingw]
|
14
|
+
gem "tiny_tds", :platforms => [:mri, :mingw] if RUBY_PLATFORM =~ /windows/
|
15
15
|
# gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw]
|
16
16
|
|
17
17
|
gem 'ruby-oci8', :platforms => [:mri, :mswin, :mingw] if ENV.has_key? 'ORACLE_HOME'
|
@@ -26,4 +26,4 @@ group :development, :test do
|
|
26
26
|
gem "activerecord-jdbcmssql-adapter", :platforms => :jruby
|
27
27
|
end
|
28
28
|
|
29
|
-
gemspec :path => "../"
|
29
|
+
gemspec :path => "../"
|
data/lib/arel_extensions.rb
CHANGED
@@ -44,6 +44,12 @@ require 'arel_extensions/insert_manager'
|
|
44
44
|
|
45
45
|
require 'arel_extensions/common_sql_functions'
|
46
46
|
|
47
|
+
require 'arel_extensions/nodes/union'
|
48
|
+
require 'arel_extensions/nodes/union_all'
|
49
|
+
require 'arel_extensions/nodes/as'
|
50
|
+
|
51
|
+
|
52
|
+
|
47
53
|
module Arel
|
48
54
|
def self.rand
|
49
55
|
ArelExtensions::Nodes::Rand.new
|
@@ -90,3 +96,24 @@ end
|
|
90
96
|
Arel::InsertManager.class_eval do
|
91
97
|
include ArelExtensions::InsertManager
|
92
98
|
end
|
99
|
+
|
100
|
+
Arel::SelectManager.class_eval do
|
101
|
+
include ArelExtensions::Math
|
102
|
+
include ArelExtensions::Nodes
|
103
|
+
end
|
104
|
+
|
105
|
+
Arel::Nodes::Union.class_eval do
|
106
|
+
include ArelExtensions::Math
|
107
|
+
include ArelExtensions::Nodes
|
108
|
+
end
|
109
|
+
|
110
|
+
Arel::Nodes::UnionAll.class_eval do
|
111
|
+
include ArelExtensions::Math
|
112
|
+
include ArelExtensions::Nodes
|
113
|
+
end
|
114
|
+
|
115
|
+
Arel::Nodes::As.class_eval do
|
116
|
+
include ArelExtensions::Nodes
|
117
|
+
end
|
118
|
+
|
119
|
+
|
data/lib/arel_extensions/math.rb
CHANGED
@@ -4,6 +4,8 @@ require 'arel_extensions/nodes/concat'
|
|
4
4
|
require 'arel_extensions/nodes/date_diff'
|
5
5
|
require 'arel_extensions/nodes/duration'
|
6
6
|
require 'arel_extensions/nodes/wday'
|
7
|
+
require 'arel_extensions/nodes/union'
|
8
|
+
require 'arel_extensions/nodes/union_all'
|
7
9
|
|
8
10
|
module ArelExtensions
|
9
11
|
module Math
|
@@ -11,6 +13,7 @@ module ArelExtensions
|
|
11
13
|
#String and others (convert in string) allows you to concatenate 2 or more strings together.
|
12
14
|
#Date and integer adds or subtracts a specified time interval from a date.
|
13
15
|
def +(other)
|
16
|
+
return ArelExtensions::Nodes::Union.new self, other if self.is_a?(Arel::SelectManager) || self.is_a?(Arel::Nodes::Union)
|
14
17
|
return ArelExtensions::Nodes::Concat.new [self, other] if self.is_a?(Arel::Nodes::Quoted)
|
15
18
|
if self.is_a?(Arel::Nodes::Grouping)
|
16
19
|
if self.expr.left.is_a?(String) || self.expr.right.is_a?(String)
|
@@ -29,18 +32,23 @@ module ArelExtensions
|
|
29
32
|
else
|
30
33
|
ArelExtensions::Nodes::Concat.new [self, other]
|
31
34
|
end if self.is_a?(ArelExtensions::Nodes::Function)
|
32
|
-
|
33
|
-
if
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
35
|
+
col = Arel::Table.engine.connection.schema_cache.columns_hash(self.relation.table_name)[self.name.to_s]
|
36
|
+
if (!col) #if the column doesn't exist in the database
|
37
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new(self, other))
|
38
|
+
else
|
39
|
+
arg = col.type
|
40
|
+
if arg == :integer || (!arg)
|
41
|
+
other = other.to_i if other.is_a?(String)
|
42
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new self, other)
|
43
|
+
elsif arg == :decimal || arg == :float
|
44
|
+
other = Arel.sql(other) if other.is_a?(String) # Arel should accept Float & BigDecimal!
|
45
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new self, other)
|
46
|
+
elsif arg == :datetime || arg == :date
|
47
|
+
ArelExtensions::Nodes::DateAdd.new [self, other]
|
48
|
+
elsif arg == :string || arg == :text
|
49
|
+
ArelExtensions::Nodes::Concat.new [self, other]
|
50
|
+
end
|
51
|
+
end
|
44
52
|
end
|
45
53
|
|
46
54
|
#function returns the time between two dates
|
@@ -56,32 +64,58 @@ module ArelExtensions
|
|
56
64
|
else
|
57
65
|
Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
|
58
66
|
end if self.is_a?(ArelExtensions::Nodes::Function)
|
59
|
-
|
60
|
-
if (
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
67
|
+
col = Arel::Table.engine.connection.schema_cache.columns_hash(self.relation.table_name)[self.name.to_s]
|
68
|
+
if (!col) #if the column doesn't exist in the database
|
69
|
+
return Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
|
70
|
+
else
|
71
|
+
arg = col.type
|
72
|
+
if (arg == :date || arg == :datetime)
|
73
|
+
case other
|
74
|
+
when Arel::Attributes::Attribute
|
75
|
+
col2 = Arel::Table.engine.connection.schema_cache.columns_hash(other.relation.table_name)[other.name.to_s]
|
76
|
+
if (!col2) #if the column doesn't exist in the database
|
77
|
+
ArelExtensions::Nodes::DateSub.new [self, other]
|
78
|
+
else
|
79
|
+
arg2 = col2.type
|
80
|
+
if arg2 == :date || arg2 == :datetime
|
81
|
+
ArelExtensions::Nodes::DateDiff.new [self, other]
|
82
|
+
else
|
83
|
+
ArelExtensions::Nodes::DateSub.new [self, other]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
when Arel::Nodes::Node, DateTime, Time, String, Date
|
87
|
+
ArelExtensions::Nodes::DateDiff.new [self, other]
|
88
|
+
when Integer
|
89
|
+
ArelExtensions::Nodes::DateSub.new [self, other]
|
90
|
+
end
|
91
|
+
else
|
92
|
+
case other
|
93
|
+
when Integer, Float, BigDecimal
|
94
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, Arel.sql(other.to_s)))
|
95
|
+
when String
|
96
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, Arel.sql(other)))
|
97
|
+
else
|
98
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
84
102
|
end
|
85
|
-
|
103
|
+
|
104
|
+
#def *(other)
|
105
|
+
#if self.is_a?(Arel::SelectManager) || self.is_a?(Arel::Nodes::UnionAll) || self.is_a?(Arel::Nodes::Union)
|
106
|
+
#return ArelExtensions::Nodes::UnionAll.new self, other
|
107
|
+
#else
|
108
|
+
#return super(other)
|
109
|
+
#end
|
110
|
+
#end
|
111
|
+
|
112
|
+
def union(other)
|
113
|
+
return ArelExtensions::Nodes::Union.new(self,other)
|
114
|
+
end
|
115
|
+
|
116
|
+
def union_all(other)
|
117
|
+
return ArelExtensions::Nodes::UnionAll.new(self,other)
|
118
|
+
end
|
119
|
+
|
86
120
|
end
|
87
121
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ArelExtensions
|
2
|
+
module Nodes
|
3
|
+
class Union < Arel::Nodes::Union
|
4
|
+
|
5
|
+
def initialize left,right
|
6
|
+
return super(left,right)
|
7
|
+
end
|
8
|
+
|
9
|
+
def +(other)
|
10
|
+
return ArelExtensions::Nodes::Union.new(self,other)
|
11
|
+
end
|
12
|
+
|
13
|
+
def union(other)
|
14
|
+
return ArelExtensions::Nodes::UnionAll.new(self,other)
|
15
|
+
end
|
16
|
+
|
17
|
+
def as other
|
18
|
+
ArelExtensions::Nodes::As.new self, Arel::Nodes::SqlLiteral.new(other.to_s)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ArelExtensions
|
2
|
+
module Nodes
|
3
|
+
class UnionAll < Arel::Nodes::UnionAll
|
4
|
+
|
5
|
+
def initialize left,right
|
6
|
+
return super(left,right)
|
7
|
+
end
|
8
|
+
|
9
|
+
def union_all(other)
|
10
|
+
return ArelExtensions::Nodes::UnionAll.new(self,other)
|
11
|
+
end
|
12
|
+
|
13
|
+
def as other
|
14
|
+
ArelExtensions::Nodes::As.new self, Arel::Nodes::SqlLiteral.new(other.to_s)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -266,21 +266,32 @@ module ArelExtensions
|
|
266
266
|
collector
|
267
267
|
end
|
268
268
|
|
269
|
+
# add primary_key if not present, avoid zip
|
269
270
|
if Arel::VERSION.to_i < 7
|
270
271
|
def visit_ArelExtensions_InsertManager_BulkValues o, collector
|
272
|
+
raise ArgumentError, "missing columns" if o.cols.empty?
|
271
273
|
table = collector.value.sub(/\AINSERT INTO/, '')
|
272
274
|
into = " INTO#{table}"
|
273
275
|
collector = Arel::Collectors::SQLString.new
|
274
276
|
collector << "INSERT ALL\n"
|
275
|
-
o.
|
277
|
+
pk_name = @connection.primary_key(o.cols.first.relation.name)
|
278
|
+
if pk_name
|
279
|
+
pk_missing = !o.cols.detect{|c| c.name == pk_name }
|
280
|
+
into.sub!(/\(/, %Q[("#{pk_name.upcase}", ]) if pk_missing
|
281
|
+
else
|
282
|
+
pk_missing = false
|
283
|
+
end
|
284
|
+
o.left.each_with_index do |row, idx| # values
|
276
285
|
collector << "#{into} VALUES ("
|
286
|
+
collector << "NULL, " if pk_missing # expects to have a trigger to set the value before insert
|
277
287
|
v = Arel::Nodes::Values.new(row, o.cols)
|
278
288
|
len = v.expressions.length - 1
|
279
|
-
v.expressions.
|
289
|
+
v.expressions.each_with_index { |value, i|
|
280
290
|
case value
|
281
291
|
when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
|
282
292
|
collector = visit value, collector
|
283
293
|
else
|
294
|
+
attr = v.columns[i]
|
284
295
|
collector << quote(value, attr && column_for(attr)).to_s
|
285
296
|
end
|
286
297
|
collector << Arel::Visitors::Oracle::COMMA unless i == len
|
@@ -305,11 +316,7 @@ module ArelExtensions
|
|
305
316
|
when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
|
306
317
|
collector = visit value, collector
|
307
318
|
else
|
308
|
-
|
309
|
-
collector << quote(attr.type_cast_for_database(value))
|
310
|
-
else
|
311
|
-
collector << quote(value).to_s
|
312
|
-
end
|
319
|
+
collector << (attr && attr.able_to_type_cast? ? quote(attr.type_cast_for_database(value)) : quote(value).to_s)
|
313
320
|
end
|
314
321
|
collector << Arel::Visitors::Oracle::COMMA unless i == len
|
315
322
|
}
|
@@ -320,6 +327,8 @@ module ArelExtensions
|
|
320
327
|
end
|
321
328
|
end
|
322
329
|
|
330
|
+
|
331
|
+
|
323
332
|
end
|
324
333
|
end
|
325
334
|
end
|
@@ -99,11 +99,11 @@ module ArelExtensions
|
|
99
99
|
end
|
100
100
|
|
101
101
|
def visit_ArelExtensions_Nodes_DateDiff o, collector
|
102
|
-
if o.left_node_type == :ruby_time || o.left_node_type == :datetime || o.left_node_type == :time
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
102
|
+
collector << if o.left_node_type == :ruby_time || o.left_node_type == :datetime || o.left_node_type == :time
|
103
|
+
"DATEDIFF('second', "
|
104
|
+
else
|
105
|
+
"DATEDIFF('day', "
|
106
|
+
end
|
107
107
|
collector = visit o.right, collector
|
108
108
|
collector << (o.right_node_type == :date ? '::date' : '::timestamp')
|
109
109
|
collector << Arel::Visitors::PostgreSQL::COMMA
|
@@ -159,6 +159,7 @@ module ArelExtensions
|
|
159
159
|
collector
|
160
160
|
end
|
161
161
|
|
162
|
+
|
162
163
|
end
|
163
164
|
end
|
164
165
|
end
|
@@ -187,12 +187,7 @@ module ArelExtensions
|
|
187
187
|
when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
|
188
188
|
collector = visit value.as(attr.name), collector
|
189
189
|
else
|
190
|
-
|
191
|
-
collector << quote(attr.type_cast_for_database(value))
|
192
|
-
else
|
193
|
-
# collector << quote(value, column_for(attr))
|
194
|
-
collector << quote(value).to_s
|
195
|
-
end
|
190
|
+
collector << (attr && attr.able_to_type_cast? ? quote(attr.type_cast_for_database(value)) : quote(value).to_s)
|
196
191
|
if idx == 0
|
197
192
|
collector << " AS "
|
198
193
|
collector << quote(attr.name)
|
@@ -205,6 +200,37 @@ module ArelExtensions
|
|
205
200
|
collector
|
206
201
|
end
|
207
202
|
end
|
203
|
+
|
204
|
+
|
205
|
+
def visit_ArelExtensions_Nodes_Union o, collector
|
206
|
+
if o.left.is_a?(Arel::SelectManager)
|
207
|
+
collector = visit o.left.ast, collector
|
208
|
+
else
|
209
|
+
collector = visit o.left, collector
|
210
|
+
end
|
211
|
+
collector << " UNION "
|
212
|
+
if o.right.is_a?(Arel::SelectManager)
|
213
|
+
collector = visit o.right.ast, collector
|
214
|
+
else
|
215
|
+
collector = visit o.right, collector
|
216
|
+
end
|
217
|
+
collector
|
218
|
+
end
|
219
|
+
|
220
|
+
def visit_ArelExtensions_Nodes_UnionAll o, collector
|
221
|
+
if o.left.is_a?(Arel::SelectManager)
|
222
|
+
collector = visit o.left.ast, collector
|
223
|
+
else
|
224
|
+
collector = visit o.left, collector
|
225
|
+
end
|
226
|
+
collector << " UNION ALL "
|
227
|
+
if o.right.is_a?(Arel::SelectManager)
|
228
|
+
collector = visit o.right.ast, collector
|
229
|
+
else
|
230
|
+
collector = visit o.right, collector
|
231
|
+
end
|
232
|
+
collector
|
233
|
+
end
|
208
234
|
|
209
235
|
end
|
210
236
|
end
|
@@ -234,11 +234,11 @@ module ArelExtensions
|
|
234
234
|
end
|
235
235
|
|
236
236
|
def visit_ArelExtensions_Nodes_DateDiff o, collector
|
237
|
-
if o.left_node_type == :ruby_time || o.left_node_type == :datetime || o.left_node_type == :time
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
237
|
+
collector << if o.left_node_type == :ruby_time || o.left_node_type == :datetime || o.left_node_type == :time
|
238
|
+
'TIMEDIFF('
|
239
|
+
else
|
240
|
+
'DATEDIFF('
|
241
|
+
end
|
242
242
|
collector = visit o.left, collector
|
243
243
|
collector << Arel::Visitors::ToSql::COMMA
|
244
244
|
collector = visit o.right, collector
|
@@ -331,13 +331,13 @@ module ArelExtensions
|
|
331
331
|
collector = visit o.left, collector
|
332
332
|
collector << Arel::Visitors::ToSql::COMMA
|
333
333
|
collector = visit o.sqlite_value(o.right), collector
|
334
|
-
collector <<
|
334
|
+
collector << ')'
|
335
335
|
collector
|
336
336
|
end
|
337
337
|
|
338
338
|
if Arel::VERSION.to_i < 7
|
339
339
|
def visit_ArelExtensions_InsertManager_BulkValues o, collector
|
340
|
-
collector <<
|
340
|
+
collector << 'VALUES '
|
341
341
|
row_nb = o.left.length
|
342
342
|
o.left.each_with_index do |row, idx|
|
343
343
|
collector << '('
|
@@ -358,7 +358,7 @@ module ArelExtensions
|
|
358
358
|
end
|
359
359
|
else
|
360
360
|
def visit_ArelExtensions_InsertManager_BulkValues o, collector
|
361
|
-
collector <<
|
361
|
+
collector << 'VALUES '
|
362
362
|
row_nb = o.left.length
|
363
363
|
o.left.each_with_index do |row, idx|
|
364
364
|
collector << '('
|
@@ -369,11 +369,7 @@ module ArelExtensions
|
|
369
369
|
when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
|
370
370
|
collector = visit value, collector
|
371
371
|
else
|
372
|
-
|
373
|
-
collector << quote(attr.type_cast_for_database(value))
|
374
|
-
else
|
375
|
-
collector << quote(value).to_s
|
376
|
-
end
|
372
|
+
collector << (attr && attr.able_to_type_cast? ? quote(attr.type_cast_for_database(value)) : quote(value).to_s)
|
377
373
|
end
|
378
374
|
collector << Arel::Visitors::ToSql::COMMA unless i == len
|
379
375
|
}
|
@@ -383,7 +379,33 @@ module ArelExtensions
|
|
383
379
|
end
|
384
380
|
end
|
385
381
|
|
386
|
-
|
382
|
+
def visit_ArelExtensions_Nodes_Union o, collector
|
383
|
+
collector = visit o.left, collector
|
384
|
+
collector << " UNION "
|
385
|
+
collector = visit o.right, collector
|
386
|
+
collector
|
387
|
+
end
|
388
|
+
|
389
|
+
def visit_ArelExtensions_Nodes_UnionAll o, collector
|
390
|
+
collector = visit o.left, collector
|
391
|
+
collector << " UNION ALL "
|
392
|
+
collector = visit o.right, collector
|
393
|
+
collector
|
394
|
+
end
|
395
|
+
|
396
|
+
def visit_ArelExtensions_Nodes_As o, collector
|
397
|
+
if o.left.is_a?(ArelExtensions::Nodes::Union) || o.left.is_a?(ArelExtensions::Nodes::UnionAll)
|
398
|
+
collector << "("
|
399
|
+
collector = visit o.left, collector
|
400
|
+
collector << ") "
|
401
|
+
visit o.right, collector
|
402
|
+
else
|
403
|
+
collector = visit o.left, collector
|
404
|
+
collector << " AS "
|
405
|
+
visit o.right, collector
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
387
409
|
end
|
388
410
|
|
389
411
|
end
|
@@ -25,6 +25,18 @@ module ArelExtensions
|
|
25
25
|
# Math Functions
|
26
26
|
it "should not break Arel functions" do
|
27
27
|
compile(@price + 42).must_be_like %{("products"."price" + 42)}
|
28
|
+
compile(@table[:id] + @table[:pas_en_base])
|
29
|
+
.must_be_like %{("users"."id" + "users"."pas_en_base")}
|
30
|
+
compile(@table[:pas_en_base] + @table[:id])
|
31
|
+
.must_be_like %{("users"."pas_en_base" + "users"."id")}
|
32
|
+
compile(@table[:id] - @table[:pas_en_base])
|
33
|
+
.must_be_like %{("users"."id" - "users"."pas_en_base")}
|
34
|
+
compile(@table[:pas_en_base] - @table[:id])
|
35
|
+
.must_be_like %{("users"."pas_en_base" - "users"."id")}
|
36
|
+
compile(@table[:id] * @table[:pas_en_base])
|
37
|
+
.must_be_like %{"users"."id" * "users"."pas_en_base"}
|
38
|
+
compile(@table[:pas_en_base] * @table[:id])
|
39
|
+
.must_be_like %{"users"."pas_en_base" * "users"."id"}
|
28
40
|
end
|
29
41
|
|
30
42
|
it "should return right calculations on numbers" do
|
@@ -152,7 +164,41 @@ module ArelExtensions
|
|
152
164
|
(c.length / 42).round(2).floor > (@table[:updated_at] - Date.new(2000, 3, 31)).abs.ceil
|
153
165
|
).must_be_like %{FLOOR(ROUND(LENGTH("users"."name") / 42, 2)) > CEIL(ABS(TIMEDIFF("users"."updated_at", '2000-03-31 00:00:00 UTC')))}
|
154
166
|
end
|
155
|
-
|
167
|
+
|
168
|
+
# Unions
|
169
|
+
it "should accept union operators on queries and union nodes" do
|
170
|
+
c = @table.project(@table[:name])
|
171
|
+
compile(c + c)
|
172
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION (SELECT "users"."name" FROM "users")}
|
173
|
+
(c + c).to_sql
|
174
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION (SELECT "users"."name" FROM "users")}
|
175
|
+
(c + (c + c)).to_sql
|
176
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION (SELECT "users"."name" FROM "users") UNION (SELECT "users"."name" FROM "users")}
|
177
|
+
((c + c) + c).to_sql
|
178
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION (SELECT "users"."name" FROM "users") UNION (SELECT "users"."name" FROM "users")}
|
179
|
+
(c + c + c).to_sql
|
180
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION (SELECT "users"."name" FROM "users") UNION (SELECT "users"."name" FROM "users")}
|
181
|
+
|
182
|
+
(c + c).as('union_table').to_sql
|
183
|
+
.must_be_like %{((SELECT "users"."name" FROM "users") UNION (SELECT "users"."name" FROM "users")) union_table}
|
184
|
+
|
185
|
+
|
186
|
+
c = @table.project(@table[:name])
|
187
|
+
compile(c.union_all(c))
|
188
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users")}
|
189
|
+
(c.union_all(c)).to_sql
|
190
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users")}
|
191
|
+
(c.union_all(c.union_all(c))).to_sql
|
192
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users")}
|
193
|
+
((c.union_all(c)).union_all(c)).to_sql
|
194
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users")}
|
195
|
+
(c.union_all(c).union_all(c)).to_sql
|
196
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users")}
|
197
|
+
(c.union_all(c)).as('union_table').to_sql
|
198
|
+
.must_be_like %{((SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users")) union_table}
|
199
|
+
|
200
|
+
end
|
201
|
+
|
156
202
|
end
|
157
203
|
end
|
158
|
-
end
|
204
|
+
end
|
@@ -74,6 +74,10 @@ module ArelExtensions
|
|
74
74
|
@updated_at = User.arel_table[:updated_at]
|
75
75
|
@comments = User.arel_table[:comments]
|
76
76
|
@price = Product.arel_table[:price]
|
77
|
+
@not_in_table = User.arel_table[:not_in_table]
|
78
|
+
|
79
|
+
@ut = User.arel_table
|
80
|
+
@pt = Product.arel_table
|
77
81
|
end
|
78
82
|
|
79
83
|
def teardown
|
@@ -352,7 +356,7 @@ module ArelExtensions
|
|
352
356
|
else
|
353
357
|
assert_equal 42, t(@lucas, @updated_at - Time.utc(2014, 3, 3, 12, 41, 18)).to_i
|
354
358
|
assert_equal(-3600, t(@lucas, @updated_at - Time.utc(2014, 3, 3, 13, 42)).to_i)
|
355
|
-
if @env_db == 'mssql'
|
359
|
+
if @env_db == 'mssql' || @env_db == 'oracle' # can't select booleans
|
356
360
|
assert_equal 0, @lucas.where((@updated_at - Time.utc(2014, 3, 3, 12, 41, 18)) < -1).count
|
357
361
|
else
|
358
362
|
assert_includes [nil, 0, 'f', false], t(@lucas, (@updated_at - Time.utc(2014, 3, 3, 12, 41, 18)) < -1)
|
@@ -360,6 +364,7 @@ module ArelExtensions
|
|
360
364
|
end
|
361
365
|
end
|
362
366
|
|
367
|
+
# TODO; cast types
|
363
368
|
def test_cast_types
|
364
369
|
skip "not implemented yet"
|
365
370
|
assert_equal true, t(@arthur, @score =~ /22/)
|
@@ -414,8 +419,23 @@ module ArelExtensions
|
|
414
419
|
(@score.round > 19).⋀(@score.round < 21).⋁(@score.round(1) >= 20.1)
|
415
420
|
).count
|
416
421
|
end
|
422
|
+
|
423
|
+
# Union operator
|
424
|
+
def test_union_operator
|
425
|
+
assert_equal 3, User.find_by_sql((@ut.project(@age).where(@age.gt(22)) + @ut.project(@age).where(@age.lt(0))).to_sql).length
|
426
|
+
assert_equal 2, User.find_by_sql((@ut.project(@age).where(@age.eq(20)) + @ut.project(@age).where(@age.eq(20)) + @ut.project(@age).where(@age.eq(21))).to_sql).length
|
427
|
+
assert_equal 3, User.select('*').from((@ut.project(@age).where(@age.gt(22)) + @ut.project(@age).where(@age.lt(0))).as('my_union')).length
|
428
|
+
assert_equal 3, User.select('*').from((@ut.project(@age).where(@age.eq(20)) + @ut.project(@age).where(@age.eq(23)) + @ut.project(@age).where(@age.eq(21))).as('my_union')).length
|
429
|
+
assert_equal 2, User.select('*').from((@ut.project(@age).where(@age.eq(20)) + @ut.project(@age).where(@age.eq(20)) + @ut.project(@age).where(@age.eq(21))).as('my_union')).length
|
430
|
+
|
431
|
+
assert_equal 3, User.find_by_sql((@ut.project(@age).where(@age.gt(22)).union_all(@ut.project(@age).where(@age.lt(0)))).to_sql).length
|
432
|
+
assert_equal 3, User.find_by_sql((@ut.project(@age).where(@age.eq(20)).union_all(@ut.project(@age).where(@age.eq(20))).union_all(@ut.project(@age).where(@age.eq(21)))).to_sql).length
|
433
|
+
assert_equal 3, User.select('*').from((@ut.project(@age).where(@age.gt(22)).union_all(@ut.project(@age).where(@age.lt(0)))).as('my_union')).length
|
434
|
+
assert_equal 3, User.select('*').from((@ut.project(@age).where(@age.eq(20)).union_all(@ut.project(@age).where(@age.eq(23))).union_all(@ut.project(@age).where(@age.eq(21)))).as('my_union')).length
|
435
|
+
assert_equal 3, User.select('*').from((@ut.project(@age).where(@age.eq(20)).union_all(@ut.project(@age).where(@age.eq(20))).union_all(@ut.project(@age).where(@age.eq(21)))).as('my_union')).length
|
436
|
+
end
|
417
437
|
|
418
438
|
|
419
439
|
end
|
420
440
|
end
|
421
|
-
end
|
441
|
+
end
|
@@ -32,6 +32,20 @@ module ArelExtensions
|
|
32
32
|
t.column :updated_at, :datetime
|
33
33
|
t.column :score, :decimal, :precision => 20, :scale => 10
|
34
34
|
end
|
35
|
+
if @env_db == 'oracle'
|
36
|
+
@cnx.execute(%q[CREATE OR REPLACE trigger user_tests_trg
|
37
|
+
before insert on user_tests
|
38
|
+
for each row
|
39
|
+
BEGIN
|
40
|
+
IF :new.id IS NULL THEN
|
41
|
+
:new.id := user_tests_seq.nextval;
|
42
|
+
END IF;
|
43
|
+
END;])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class User < ActiveRecord::Base
|
48
|
+
self.table_name = 'user_tests'
|
35
49
|
end
|
36
50
|
|
37
51
|
def setup
|
@@ -39,8 +53,13 @@ module ArelExtensions
|
|
39
53
|
@table = Arel::Table.new(:user_tests)
|
40
54
|
@cols = ['id', 'name', 'comments', 'created_at']
|
41
55
|
@data = [
|
42
|
-
[23, 'nom1', "
|
43
|
-
[25, 'nom2', "sdfdsfdsfsdf", '2016-01-
|
56
|
+
[23, 'nom1', "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor.", '2016-01-01'],
|
57
|
+
[25, 'nom2', "sdfdsfdsfsdf", '2016-01-02']
|
58
|
+
]
|
59
|
+
@cols2 = ['name', 'comments', 'created_at']
|
60
|
+
@data2 = [
|
61
|
+
['nom3', "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor.", '2016-01-01'],
|
62
|
+
['nom4', "sdfdsfdsfsdf", '2016-01-04']
|
44
63
|
]
|
45
64
|
end
|
46
65
|
|
@@ -53,6 +72,11 @@ module ArelExtensions
|
|
53
72
|
insert_manager = Arel::VERSION.to_i > 6 ? Arel::InsertManager.new().into(@table) : Arel::InsertManager.new(Arel::Table.engine).into(@table)
|
54
73
|
insert_manager.bulk_insert(@cols, @data)
|
55
74
|
@cnx.execute(insert_manager.to_sql)
|
75
|
+
assert_equal 2, User.count, "insertions failed"
|
76
|
+
insert_manager = Arel::VERSION.to_i > 6 ? Arel::InsertManager.new().into(@table) : Arel::InsertManager.new(Arel::Table.engine).into(@table)
|
77
|
+
insert_manager.bulk_insert(@cols2, @data2)
|
78
|
+
@cnx.execute(insert_manager.to_sql)
|
79
|
+
assert_equal 4, User.count, "insertions failed"
|
56
80
|
end
|
57
81
|
|
58
82
|
end
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arel_extensions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0
|
4
|
+
version: '1.0'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yann Azoury
|
8
|
-
- Mathilde Pechdimaldjian
|
9
8
|
- Félix Bellanger
|
9
|
+
- Julien Delporte
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2017-
|
13
|
+
date: 2017-12-13 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: arel
|
@@ -71,8 +71,8 @@ dependencies:
|
|
71
71
|
description: Adds new features to Arel
|
72
72
|
email:
|
73
73
|
- yann.azoury@faveod.com
|
74
|
-
- mathilde.pechdimaldjian@gmail.com
|
75
74
|
- felix.bellanger@faveod.com
|
75
|
+
- julien.delporte@faveod.com
|
76
76
|
executables: []
|
77
77
|
extensions: []
|
78
78
|
extra_rdoc_files:
|
@@ -80,6 +80,7 @@ extra_rdoc_files:
|
|
80
80
|
- README.md
|
81
81
|
- functions.html
|
82
82
|
files:
|
83
|
+
- ".codeclimate.yml"
|
83
84
|
- ".gitignore"
|
84
85
|
- ".rubocop.yml"
|
85
86
|
- ".travis.yml"
|
@@ -118,6 +119,7 @@ files:
|
|
118
119
|
- lib/arel_extensions/math_functions.rb
|
119
120
|
- lib/arel_extensions/nodes.rb
|
120
121
|
- lib/arel_extensions/nodes/abs.rb
|
122
|
+
- lib/arel_extensions/nodes/as.rb
|
121
123
|
- lib/arel_extensions/nodes/blank.rb
|
122
124
|
- lib/arel_extensions/nodes/ceil.rb
|
123
125
|
- lib/arel_extensions/nodes/change_case.rb
|
@@ -140,6 +142,8 @@ files:
|
|
140
142
|
- lib/arel_extensions/nodes/substring.rb
|
141
143
|
- lib/arel_extensions/nodes/then.rb
|
142
144
|
- lib/arel_extensions/nodes/trim.rb
|
145
|
+
- lib/arel_extensions/nodes/union.rb
|
146
|
+
- lib/arel_extensions/nodes/union_all.rb
|
143
147
|
- lib/arel_extensions/nodes/wday.rb
|
144
148
|
- lib/arel_extensions/null_functions.rb
|
145
149
|
- lib/arel_extensions/railtie.rb
|