arel_extensions 1.7.0 → 2.0.0.rc3
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/.codeclimate.yml +2 -1
- data/.gitignore +6 -8
- data/.rubocop.yml +4 -194
- data/.travis/oracle/download.js +152 -0
- data/.travis/oracle/download.sh +30 -0
- data/.travis/oracle/download_ojdbc.js +116 -0
- data/.travis/oracle/install.sh +34 -0
- data/.travis/setup_accounts.sh +9 -0
- data/.travis/sqlite3/extension-functions.sh +6 -0
- data/.travis.yml +223 -0
- data/Gemfile +28 -2
- data/README.md +89 -265
- data/Rakefile +30 -50
- data/TODO +1 -0
- data/appveyor.yml +22 -60
- data/arel_extensions.gemspec +17 -14
- data/functions.html +3 -3
- data/gemfiles/rails3.gemfile +20 -0
- data/gemfiles/rails4.gemfile +29 -0
- data/gemfiles/rails5_0.gemfile +29 -0
- data/gemfiles/rails5_1_4.gemfile +17 -19
- data/gemfiles/rails5_2.gemfile +17 -26
- data/init/mssql.sql +4 -4
- data/init/mysql.sql +38 -38
- data/init/oracle.sql +0 -0
- data/init/postgresql.sql +25 -24
- data/init/sqlite.sql +0 -0
- data/lib/arel_extensions/attributes.rb +3 -9
- data/lib/arel_extensions/boolean_functions.rb +16 -57
- data/lib/arel_extensions/common_sql_functions.rb +20 -21
- data/lib/arel_extensions/comparators.rb +28 -31
- data/lib/arel_extensions/date_duration.rb +13 -19
- data/lib/arel_extensions/insert_manager.rb +15 -22
- data/lib/arel_extensions/math.rb +70 -68
- data/lib/arel_extensions/math_functions.rb +52 -57
- data/lib/arel_extensions/nodes/abs.rb +1 -0
- data/lib/arel_extensions/nodes/blank.rb +5 -6
- data/lib/arel_extensions/nodes/case.rb +27 -31
- data/lib/arel_extensions/nodes/cast.rb +12 -12
- data/lib/arel_extensions/nodes/ceil.rb +1 -1
- data/lib/arel_extensions/nodes/coalesce.rb +5 -4
- data/lib/arel_extensions/nodes/collate.rb +10 -9
- data/lib/arel_extensions/nodes/concat.rb +22 -13
- data/lib/arel_extensions/nodes/date_diff.rb +70 -77
- data/lib/arel_extensions/nodes/duration.rb +5 -2
- data/lib/arel_extensions/nodes/find_in_set.rb +1 -0
- data/lib/arel_extensions/nodes/floor.rb +1 -1
- data/lib/arel_extensions/nodes/format.rb +8 -35
- data/lib/arel_extensions/nodes/formatted_number.rb +23 -22
- data/lib/arel_extensions/nodes/function.rb +39 -50
- data/lib/arel_extensions/nodes/is_null.rb +0 -2
- data/lib/arel_extensions/nodes/json.rb +47 -72
- data/lib/arel_extensions/nodes/length.rb +0 -5
- data/lib/arel_extensions/nodes/levenshtein_distance.rb +2 -2
- data/lib/arel_extensions/nodes/locate.rb +3 -2
- data/lib/arel_extensions/nodes/log10.rb +2 -1
- data/lib/arel_extensions/nodes/matches.rb +8 -6
- data/lib/arel_extensions/nodes/md5.rb +1 -0
- data/lib/arel_extensions/nodes/power.rb +5 -5
- data/lib/arel_extensions/nodes/rand.rb +3 -2
- data/lib/arel_extensions/nodes/repeat.rb +6 -4
- data/lib/arel_extensions/nodes/replace.rb +8 -16
- data/lib/arel_extensions/nodes/round.rb +7 -6
- data/lib/arel_extensions/nodes/soundex.rb +15 -15
- data/lib/arel_extensions/nodes/std.rb +21 -18
- data/lib/arel_extensions/nodes/substring.rb +16 -58
- data/lib/arel_extensions/nodes/then.rb +2 -2
- data/lib/arel_extensions/nodes/trim.rb +7 -5
- data/lib/arel_extensions/nodes/union.rb +9 -6
- data/lib/arel_extensions/nodes/union_all.rb +8 -5
- data/lib/arel_extensions/nodes/wday.rb +6 -2
- data/lib/arel_extensions/nodes.rb +1 -1
- data/lib/arel_extensions/null_functions.rb +6 -18
- data/lib/arel_extensions/predications.rb +49 -54
- data/lib/arel_extensions/railtie.rb +5 -5
- data/lib/arel_extensions/set_functions.rb +7 -5
- data/lib/arel_extensions/string_functions.rb +55 -110
- data/lib/arel_extensions/tasks.rb +6 -6
- data/lib/arel_extensions/version.rb +1 -3
- data/lib/arel_extensions/visitors/ibm_db.rb +36 -29
- data/lib/arel_extensions/visitors/mssql.rb +248 -493
- data/lib/arel_extensions/visitors/mysql.rb +244 -420
- data/lib/arel_extensions/visitors/oracle.rb +258 -311
- data/lib/arel_extensions/visitors/oracle12.rb +34 -20
- data/lib/arel_extensions/visitors/postgresql.rb +239 -350
- data/lib/arel_extensions/visitors/sqlite.rb +169 -191
- data/lib/arel_extensions/visitors/to_sql.rb +299 -371
- data/lib/arel_extensions/visitors.rb +62 -83
- data/lib/arel_extensions.rb +32 -243
- data/test/database.yml +10 -20
- data/test/helper.rb +18 -0
- data/test/real_db_test.rb +120 -123
- data/test/support/fake_record.rb +3 -11
- data/test/test_comparators.rb +17 -14
- data/test/visitors/test_bulk_insert_oracle.rb +12 -12
- data/test/visitors/test_bulk_insert_sqlite.rb +14 -13
- data/test/visitors/test_bulk_insert_to_sql.rb +13 -11
- data/test/visitors/test_oracle.rb +55 -55
- data/test/visitors/test_to_sql.rb +226 -419
- data/test/with_ar/all_agnostic_test.rb +375 -912
- data/test/with_ar/insert_agnostic_test.rb +22 -28
- data/test/with_ar/test_bulk_sqlite.rb +17 -18
- data/test/with_ar/test_math_sqlite.rb +27 -27
- data/test/with_ar/test_string_mysql.rb +34 -32
- data/test/with_ar/test_string_sqlite.rb +35 -31
- metadata +64 -50
- data/.github/workflows/publish.yml +0 -30
- data/.github/workflows/release.yml +0 -30
- data/.github/workflows/ruby.yml +0 -452
- data/CONTRIBUTING.md +0 -102
- data/Makefile +0 -48
- data/NEWS.md +0 -131
- data/bin/build +0 -15
- data/bin/publish +0 -8
- data/dev/arelx.dockerfile +0 -41
- data/dev/compose.yaml +0 -69
- data/dev/postgres.dockerfile +0 -5
- data/dev/rbenv +0 -189
- data/gemfiles/rails5.gemfile +0 -36
- data/gemfiles/rails6.gemfile +0 -40
- data/gemfiles/rails6_1.gemfile +0 -48
- data/gemfiles/rails7.gemfile +0 -48
- data/gemfiles/rails7_1.gemfile +0 -48
- data/gemfiles/rails7_2.gemfile +0 -47
- data/gemfiles/rails8.gemfile +0 -40
- data/gemfiles/rails8_1.gemfile +0 -41
- data/gemspecs/arel_extensions-v1.gemspec +0 -25
- data/gemspecs/arel_extensions-v2.gemspec +0 -25
- data/generate_gems.sh +0 -15
- data/lib/arel_extensions/aliases.rb +0 -14
- data/lib/arel_extensions/constants.rb +0 -13
- data/lib/arel_extensions/helpers.rb +0 -61
- data/lib/arel_extensions/nodes/aggregate_function.rb +0 -13
- data/lib/arel_extensions/nodes/byte_size.rb +0 -11
- data/lib/arel_extensions/nodes/char_length.rb +0 -11
- data/lib/arel_extensions/nodes/formatted_date.rb +0 -42
- data/lib/arel_extensions/nodes/rollup.rb +0 -36
- data/lib/arel_extensions/nodes/select.rb +0 -10
- data/lib/arel_extensions/nodes/sum.rb +0 -7
- data/lib/arel_extensions/visitors/convert_format.rb +0 -37
- data/lib/arel_extensions/warning.rb +0 -42
- data/test/arelx_test_helper.rb +0 -94
- data/test/config_loader.rb +0 -9
- data/version_v1.rb +0 -5
- data/version_v2.rb +0 -5
data/README.md
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# Arel Extensions
|
|
2
2
|
|
|
3
|
-
](http://travis-ci.org/Faveod/arel-extensions)
|
|
4
|
+
[](https://ci.appveyor.com/project/yazfav/arel-extensions)
|
|
5
|
+
[](https://hakiri.io/github/Faveod/arel-extensions/master)
|
|
5
6
|

|
|
6
7
|
|
|
7
8
|
Gem: [](https://rubygems.org/gems/arel_extensions)
|
|
8
9
|
[](https://rubygems.org/gems/arel_extensions)
|
|
9
10
|
[](https://rubygems.org/gems/arel_extensions)
|
|
10
11
|
|
|
11
|
-
Arel Extensions adds shortcuts, fixes and new ORM mappings (
|
|
12
|
-
It aims to ensure pure
|
|
12
|
+
Arel Extensions adds shortcuts, fixes and new ORM mappings (ruby to SQL) to Arel.
|
|
13
|
+
It aims to ensure pure ruby syntax for the biggest number of usual cases.
|
|
13
14
|
It allows to use more advanced SQL functions for any supported RDBMS.
|
|
14
15
|
|
|
15
16
|
|
|
@@ -18,11 +19,6 @@ It allows to use more advanced SQL functions for any supported RDBMS.
|
|
|
18
19
|
Arel 6 (Rails 4) or Arel 7+ (Rails 5).
|
|
19
20
|
[Arel Repository](http://github.com/rails/arel)
|
|
20
21
|
|
|
21
|
-
or
|
|
22
|
-
|
|
23
|
-
Rails 6
|
|
24
|
-
[Rails Repository](http://github.com/rails/rails)
|
|
25
|
-
|
|
26
22
|
## Usage
|
|
27
23
|
|
|
28
24
|
Most of the features will work just by adding the gem to your Gemfiles. To make sure to get all the features for any dbms, you should execute the next line as soon as you get your connection to your DB:
|
|
@@ -31,13 +27,12 @@ Most of the features will work just by adding the gem to your Gemfiles. To make
|
|
|
31
27
|
ArelExtensions::CommonSqlFunctions.new(ActiveRecord::Base.connection).add_sql_functions()
|
|
32
28
|
```
|
|
33
29
|
|
|
34
|
-
It will add common SQL features in your DB to align
|
|
30
|
+
It will add common SQL features in your DB to align ti with current routines. Technically, it will execute SQL scripts from init folder.
|
|
35
31
|
|
|
36
32
|
|
|
37
33
|
## Examples
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
`t` is an `Arel::Table` for table `my_table` (i.e., `t = Arel::Table.new('my_table')`).
|
|
35
|
+
t is an Arel::Table for table my_table
|
|
41
36
|
|
|
42
37
|
## Comparators
|
|
43
38
|
|
|
@@ -51,7 +46,7 @@ In the following examples
|
|
|
51
46
|
# => my_table.nb > 42
|
|
52
47
|
```
|
|
53
48
|
|
|
54
|
-
Other operators: <, >=, <=, =~
|
|
49
|
+
Other operators : <, >=, <=, =~
|
|
55
50
|
|
|
56
51
|
|
|
57
52
|
## Maths
|
|
@@ -74,12 +69,12 @@ With Arel Extensions:
|
|
|
74
69
|
# => SUM(my_table.nb) + 42
|
|
75
70
|
```
|
|
76
71
|
|
|
77
|
-
Other functions: ABS, RAND, ROUND, FLOOR, CEIL, FORMAT
|
|
72
|
+
Other functions : ABS, RAND, ROUND, FLOOR, CEIL, FORMAT
|
|
78
73
|
|
|
79
74
|
For Example:
|
|
80
|
-
```ruby
|
|
75
|
+
```ruby
|
|
81
76
|
t[:price].format_number("%07.2f €","fr_FR")
|
|
82
|
-
# equivalent to 'sprintf("%07.2f €",price)' plus locale management
|
|
77
|
+
# equivalent to 'sprintf("%07.2f €",price)' plus locale management
|
|
83
78
|
```
|
|
84
79
|
|
|
85
80
|
## String operations
|
|
@@ -95,29 +90,14 @@ t[:price].format_number("%07.2f €","fr_FR")
|
|
|
95
90
|
# => TRIM(TRIM(TRIM(COALESCE(my_table.name, '')), '\t'), '\n') = ''
|
|
96
91
|
|
|
97
92
|
(t[:name] =~ /\A[a-d_]+/).to_sql
|
|
98
|
-
# => my_table.name REGEXP '
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
The `replace` function supports string and regex patterns.
|
|
102
|
-
For instance
|
|
103
|
-
|
|
104
|
-
```ruby
|
|
105
|
-
t[:email].replace('@', ' at ').replace('.', ' dot ').to_sql
|
|
106
|
-
# => REPLACE(REPLACE(`my_table`.`email`, '@', ' at '), '.', ' dot ')
|
|
93
|
+
# => my_table.name REGEXP '\^[a-d_]+'
|
|
107
94
|
```
|
|
108
95
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
```ruby
|
|
112
|
-
t[:email].replace(/^(.*)@(.*)$/, 'user: \1, host: \2').to_sql
|
|
113
|
-
# => REGEXP_REPLACE(`my_table`.`email`, '(?-mix:^(.*)@(.*)$)', 'user: \\1, host: \\2')
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
Other functions: SOUNDEX, LENGTH, REPLACE, LOCATE, SUBSTRING, TRIM
|
|
96
|
+
Other functions : SOUNDEX, LENGTH, REPLACE, LOCATE, SUBSTRING, TRIM
|
|
117
97
|
|
|
118
98
|
### String Array operations
|
|
119
99
|
|
|
120
|
-
|
|
100
|
+
```t[:list]``` is a classical varchar containing a comma separated list ("1,2,3,4")
|
|
121
101
|
|
|
122
102
|
```ruby
|
|
123
103
|
(t[:list] & 3).to_sql
|
|
@@ -145,77 +125,12 @@ t[:birthdate].month.to_sql
|
|
|
145
125
|
|
|
146
126
|
t[:birthdate].year.to_sql
|
|
147
127
|
# => YEAR(my_table.birthdate)
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### Datetime
|
|
151
|
-
|
|
152
|
-
```ruby
|
|
153
|
-
# datetime difference
|
|
154
|
-
t[:birthdate] - Time.utc(2014, 3, 3, 12, 41, 18)
|
|
155
128
|
|
|
156
|
-
# comparison
|
|
157
|
-
t[:birthdate] >= '2014-03-03 10:10:10'
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### Format and Time Zone Conversion
|
|
161
|
-
|
|
162
|
-
`format` has two forms:
|
|
163
|
-
|
|
164
|
-
```ruby
|
|
165
129
|
t[:birthdate].format('%Y-%m-%d').to_sql
|
|
166
130
|
# => DATE_FORMAT(my_table.birthdate, '%Y-%m-%d')
|
|
167
131
|
```
|
|
168
132
|
|
|
169
|
-
|
|
170
|
-
The second form accepts 2 kinds of values:
|
|
171
|
-
|
|
172
|
-
1. String:
|
|
173
|
-
|
|
174
|
-
```ruby
|
|
175
|
-
t[:birthdate].format('%Y/%m/%d %H:%M:%S', 'posix/Pacific/Tahiti')
|
|
176
|
-
# => DATE_FORMAT(CONVERT_TZ(CAST(my_table.birthdate AS datetime), 'UTC', 'posix/Pacific/Tahiti'), '%Y/%m/%d %H:%i:%S') ## MySQL
|
|
177
|
-
# => TO_CHAR(CAST(my_table.birthdate AS timestamp with time zone) AT TIME ZONE 'UTC' AT TIME ZONE 'posix/Pacific/Tahiti', 'YYYY/MM/DD HH24:MI:SS') ## PostgreSQL
|
|
178
|
-
# => CONVERT(datetime, my_table.birthdate) AT TIME ZONE 'UTC' AT TIME ZONE N'posix/Pacific/Tahiti' ## SQL Server (& truncated for clarity)
|
|
179
|
-
# ^^^^^^^^^^^^^^^^^^^^ 🚨 Invalid timezone for SQL Server. Explanation below.
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
which will convert the datetime field to the supplied time zone. This generally
|
|
183
|
-
means that you're letting the RDBMS decide or infer what is the timezone of the
|
|
184
|
-
column before conversion to the supplied timezone.
|
|
185
|
-
|
|
186
|
-
1. Hash of the form `{ src_time_zone => dst_time_zone }`:
|
|
187
|
-
|
|
188
|
-
```ruby
|
|
189
|
-
t[:birthdate].format('%Y/%m/%d %H:%M:%S', { 'posix/Europe/Paris' => 'posix/Pacific/Tahiti' })
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
which will explicitly indicate the original timestamp that should be considered
|
|
193
|
-
by the RDBMS.
|
|
194
|
-
|
|
195
|
-
Warning:
|
|
196
|
-
|
|
197
|
-
- ⚠️ Time Zone names are specific to each RDBMS. While `PostgreSQL` and `MySQL`
|
|
198
|
-
have overlapping names (the ones prefixed with `posix`), you should always
|
|
199
|
-
read your vendor's documentation. `SQL Server` is a black sheep and has its
|
|
200
|
-
own conventions.
|
|
201
|
-
- ⚠️ Daylight saving is managed by the RDBMS vendor. Choose the approptiate time
|
|
202
|
-
zone name that enforces proper daylight saving conversions.
|
|
203
|
-
- ☣️ Choosing `GMT+offset` will certainly bypass daylight saving computations.
|
|
204
|
-
- ☣️ Choosing abbreviate forms like `CET`, which stands for `Central European
|
|
205
|
-
Time` will behave differently on `PostgreSQL` and `MySQL`. Don't assume
|
|
206
|
-
uniform behavior, or even a _rational_ one.
|
|
207
|
-
- ⚠️ Pay attention to the type of the `datetime` column you're working with. For
|
|
208
|
-
example, in Postgres, a `datetime` can be one of the following types:
|
|
209
|
-
1. `timestamp with time zone`
|
|
210
|
-
2. `timestamp without time zone`
|
|
211
|
-
In the first case, you don't need to supply a conversion hash because postgres
|
|
212
|
-
knows how to convert it to the desired time zone. However, if you do the same
|
|
213
|
-
for the second case, you might get surprises, especially if your Postgres
|
|
214
|
-
installation's default timezone is not `UTC`.
|
|
215
|
-
- ⚠️ SQLite is not supported.
|
|
216
|
-
- 🚨 Always test against your setup 🚨
|
|
217
|
-
|
|
218
|
-
## Unions
|
|
133
|
+
## Unions
|
|
219
134
|
|
|
220
135
|
```ruby
|
|
221
136
|
(t.where(t[:name].eq('str')) + t.where(t[:name].eq('test'))).to_sql
|
|
@@ -228,7 +143,7 @@ Arel-extensions allows to use functions on case clause
|
|
|
228
143
|
|
|
229
144
|
```ruby
|
|
230
145
|
t[:name].when("smith").then(1).when("doe").then(2).else(0).sum.to_sql
|
|
231
|
-
# => SUM(CASE "my_table"."name" WHEN 'smith' THEN 1 WHEN 'doe' THEN 2 ELSE 0 END)
|
|
146
|
+
# => SUM(CASE "my_table"."name" WHEN 'smith' THEN 1 WHEN 'doe' THEN 2 ELSE 0 END)
|
|
232
147
|
```
|
|
233
148
|
|
|
234
149
|
## Cast Function
|
|
@@ -244,15 +159,15 @@ t[:id].cast('char').to_sql
|
|
|
244
159
|
## Stored Procedures and User-defined functions
|
|
245
160
|
|
|
246
161
|
To optimize queries, some classical functions are defined in databases missing any alternative native functions.
|
|
247
|
-
Examples:
|
|
248
|
-
-
|
|
162
|
+
Examples :
|
|
163
|
+
- FIND_IN_SET
|
|
249
164
|
|
|
250
165
|
## BULK INSERT / UPSERT
|
|
251
166
|
|
|
252
167
|
Arel Extensions improves InsertManager by adding bulk_insert method, which allows to insert multiple rows in one insert.
|
|
253
168
|
|
|
254
169
|
|
|
255
|
-
```
|
|
170
|
+
```
|
|
256
171
|
@cols = ['id', 'name', 'comments', 'created_at']
|
|
257
172
|
@data = [
|
|
258
173
|
[23, 'name1', "sdfdsfdsfsdf", '2016-01-01'],
|
|
@@ -294,7 +209,7 @@ User.connection.execute(insert_manager.to_sql)
|
|
|
294
209
|
<td class="tg-yw4l">CEIL<br>column.ceil</td>
|
|
295
210
|
<td class="ok">✔</td>
|
|
296
211
|
<td class="ok">✔</td>
|
|
297
|
-
<td class="tg-j6lv">CASE +
|
|
212
|
+
<td class="tg-j6lv">CASE + ROUND</td>
|
|
298
213
|
<td class="ok">✔</td>
|
|
299
214
|
<td class="tg-j6lv">CEILING()</td>
|
|
300
215
|
<td class="tg-j6lv">CEILING()</td>
|
|
@@ -303,19 +218,10 @@ User.connection.execute(insert_manager.to_sql)
|
|
|
303
218
|
<td class="tg-yw4l">FLOOR<br>column.floor</td>
|
|
304
219
|
<td class="ok">✔</td>
|
|
305
220
|
<td class="ok">✔</td>
|
|
306
|
-
<td class="tg-j6lv">CASE +
|
|
307
|
-
<td class="ok">✔</td>
|
|
308
|
-
<td class="ok">✔</td>
|
|
309
|
-
<td class="ok">✔</td>
|
|
310
|
-
</tr>
|
|
311
|
-
<tr>
|
|
312
|
-
<td class="tg-yw4l">POSIX FORMATTING<br>column.format_number("$ %7.2f","en_US")</td>
|
|
313
|
-
<td class="ok">✔</td>
|
|
314
|
-
<td class="ok">✔</td>
|
|
221
|
+
<td class="tg-j6lv">CASE + ROUND</td>
|
|
315
222
|
<td class="ok">✔</td>
|
|
316
223
|
<td class="ok">✔</td>
|
|
317
224
|
<td class="ok">✔</td>
|
|
318
|
-
<td class="ko">not implemented</td>
|
|
319
225
|
</tr>
|
|
320
226
|
<tr>
|
|
321
227
|
<td class="tg-yw4l">RAND<br>Arel.rand</td>
|
|
@@ -345,53 +251,26 @@ User.connection.execute(insert_manager.to_sql)
|
|
|
345
251
|
<td class="ok">✔</td>
|
|
346
252
|
</tr>
|
|
347
253
|
<tr>
|
|
348
|
-
<
|
|
349
|
-
<td class="tg-yw4l">CONCAT<br>column + "string"</td>
|
|
350
|
-
<td class="ok">✔</td>
|
|
351
|
-
<td class="ok">✔</td>
|
|
352
|
-
<td class="tg-j6lv"> ||</td>
|
|
353
|
-
<td class="ok">✔</td>
|
|
354
|
-
<td class="tg-j6lv">+</td>
|
|
355
|
-
<td class="ok">✔</td>
|
|
356
|
-
</tr>
|
|
357
|
-
<tr>
|
|
358
|
-
<td class="tg-yw4l">FIND_IN_SET<br>column & ("l")</td>
|
|
359
|
-
<td class="ok">✔</td>
|
|
360
|
-
<td class="ok">✔</td>
|
|
361
|
-
<td class="tg-orpl">Ruby function</td>
|
|
362
|
-
<td class="ok">✔</td>
|
|
363
|
-
<td class="ok">✔</td>
|
|
364
|
-
<td class="ok">✔</td>
|
|
365
|
-
</tr>
|
|
366
|
-
<tr>
|
|
367
|
-
<td class="tg-yw4l">ILIKE (in Arel6)<br/>column.imatches('%pattern')</td>
|
|
368
|
-
<td class="tg-j6lv">LOWER() LIKE LOWER()</td>
|
|
369
|
-
<td class="ok">✔</td>
|
|
254
|
+
<td class="tg-yw4l">POSIX FORMATTING<br>column.format_number("$ %7.2f","en_US")</td>
|
|
370
255
|
<td class="ok">✔</td>
|
|
371
|
-
<td class="tg-j6lv">LOWER() LIKE LOWER()</td>
|
|
372
|
-
<td class="tg-j6lv">LOWER() LIKE LOWER()</td>
|
|
373
|
-
<td class="tg-j6lv">LOWER() LIKE LOWER()</td>
|
|
374
|
-
</tr>
|
|
375
|
-
<tr>
|
|
376
|
-
<td class="tg-yw4l">LENGTH<br>column.char_length</td>
|
|
377
256
|
<td class="ok">✔</td>
|
|
378
257
|
<td class="ok">✔</td>
|
|
379
258
|
<td class="ok">✔</td>
|
|
380
259
|
<td class="ok">✔</td>
|
|
381
|
-
<td class="
|
|
382
|
-
<td class="ok">not implemented</td>
|
|
260
|
+
<td class="ko">not implemented</td>
|
|
383
261
|
</tr>
|
|
384
262
|
<tr>
|
|
385
|
-
<
|
|
263
|
+
<th class="tg-ffjm" rowspan="17"><div>String functions</div></th>
|
|
264
|
+
<td class="tg-yw4l">CONCAT<br>column + "string"</td>
|
|
386
265
|
<td class="ok">✔</td>
|
|
387
266
|
<td class="ok">✔</td>
|
|
267
|
+
<td class="tg-j6lv"> ||</td>
|
|
388
268
|
<td class="ok">✔</td>
|
|
269
|
+
<td class="tg-j6lv">+</td>
|
|
389
270
|
<td class="ok">✔</td>
|
|
390
|
-
<td class="tg-j6lv">✔</td>
|
|
391
|
-
<td class="ok">not implemented</td>
|
|
392
271
|
</tr>
|
|
393
272
|
<tr>
|
|
394
|
-
<td class="tg-yw4l">LENGTH<br>column.length
|
|
273
|
+
<td class="tg-yw4l">LENGTH<br>column.length</td>
|
|
395
274
|
<td class="ok">✔</td>
|
|
396
275
|
<td class="ok">✔</td>
|
|
397
276
|
<td class="ok">✔</td>
|
|
@@ -409,70 +288,34 @@ User.connection.execute(insert_manager.to_sql)
|
|
|
409
288
|
<td class="ok">✔</td>
|
|
410
289
|
</tr>
|
|
411
290
|
<tr>
|
|
412
|
-
<td class="tg-yw4l">
|
|
413
|
-
<td class="ok">✔</td>
|
|
414
|
-
<td class="tg-j6lv">unaccent required</td>
|
|
415
|
-
<td class="tg-j6lv">not supported</td>
|
|
416
|
-
<td class="ok">✔</td>
|
|
417
|
-
<td class="tg-j6lv">✔</td>
|
|
418
|
-
<td class="tg-j6lv">?</td>
|
|
419
|
-
</tr>
|
|
420
|
-
<tr>
|
|
421
|
-
<td class="tg-yw4l">Matching Accent Insensitive<br>column.ai_matches('blah')</td>
|
|
422
|
-
<td class="ok">not supported</td>
|
|
423
|
-
<td class="tg-j6lv">not supported</td>
|
|
424
|
-
<td class="tg-j6lv">not supported</td>
|
|
425
|
-
<td class="ok">not supported</td>
|
|
426
|
-
<td class="tg-j6lv">✔</td>
|
|
427
|
-
<td class="tg-j6lv">?</td>
|
|
428
|
-
</tr>
|
|
429
|
-
<tr>
|
|
430
|
-
<td class="tg-yw4l">Matching Case Insensitive<br>column.imatches('blah')</td>
|
|
431
|
-
<td class="ok">not supported</td>
|
|
432
|
-
<td class="tg-j6lv">✔</td>
|
|
433
|
-
<td class="tg-j6lv">✔</td>
|
|
291
|
+
<td class="tg-yw4l">SUBSTRING<br/>column[1..2]<br/>column.substring(1)<br/>column.substring(1, 1)</td>
|
|
434
292
|
<td class="ok">✔</td>
|
|
435
|
-
<td class="tg-j6lv"
|
|
436
|
-
<td class="tg-j6lv"
|
|
437
|
-
|
|
438
|
-
<tr>
|
|
439
|
-
<td class="tg-yw4l">Matching Accent/Case Sensitive<br>column.smatches('blah')</td>
|
|
293
|
+
<td class="tg-j6lv">SUBSTR()</td>
|
|
294
|
+
<td class="tg-j6lv">SUBSTR()</td>
|
|
295
|
+
<td class="tg-j6lv">SUBSTR()</td>
|
|
440
296
|
<td class="ok">✔</td>
|
|
441
|
-
<td class="tg-j6lv">✔</td>
|
|
442
|
-
<td class="tg-j6lv">not supported</td>
|
|
443
297
|
<td class="ok">✔</td>
|
|
444
|
-
<td class="tg-j6lv">✔</td>
|
|
445
|
-
<td class="tg-j6lv">?</td>
|
|
446
298
|
</tr>
|
|
447
299
|
<tr>
|
|
448
|
-
<td class="tg-yw4l">
|
|
300
|
+
<td class="tg-yw4l">FIND_IN_SET<br>column & ("l")</td>
|
|
449
301
|
<td class="ok">✔</td>
|
|
450
|
-
<td class="ok">✔<br></td>
|
|
451
|
-
<td class="tg-3oug">require pcre.so</td>
|
|
452
|
-
<td class="tg-j6lv">NOT REGEXP_LIKE </td>
|
|
453
|
-
<td class="tg-j6lv">NOT LIKE</td>
|
|
454
302
|
<td class="ok">✔</td>
|
|
455
|
-
|
|
456
|
-
<tr>
|
|
457
|
-
<td class="tg-yw4l">REGEXP<br>column =~ "pattern"<br></td>
|
|
303
|
+
<td class="tg-orpl">Ruby function</td>
|
|
458
304
|
<td class="ok">✔</td>
|
|
459
305
|
<td class="ok">✔</td>
|
|
460
|
-
<td class="tg-3oug">require pcre.so</td>
|
|
461
|
-
<td class="tg-j6lv">REGEXP_LIKE</td>
|
|
462
|
-
<td class="tg-j6lv">LIKE</td>
|
|
463
306
|
<td class="ok">✔</td>
|
|
464
307
|
</tr>
|
|
465
308
|
<tr>
|
|
466
|
-
<td class="tg-yw4l">
|
|
467
|
-
<td class="ok">✔</td>
|
|
309
|
+
<td class="tg-yw4l">SOUNDEX<br>column.soundex</td>
|
|
468
310
|
<td class="ok">✔</td>
|
|
311
|
+
<td class="tg-3oug">require fuzzystrmatch</td>
|
|
469
312
|
<td class="ok">✔</td>
|
|
470
313
|
<td class="ok">✔</td>
|
|
471
314
|
<td class="ok">✔</td>
|
|
472
315
|
<td class="ok">✔</td>
|
|
473
316
|
</tr>
|
|
474
317
|
<tr>
|
|
475
|
-
<td class="tg-yw4l">REPLACE<br>column.replace(
|
|
318
|
+
<td class="tg-yw4l">REPLACE<br>column.replace("s","X")</td>
|
|
476
319
|
<td class="ok">✔</td>
|
|
477
320
|
<td class="ok">✔</td>
|
|
478
321
|
<td class="ok">✔</td>
|
|
@@ -481,31 +324,31 @@ User.connection.execute(insert_manager.to_sql)
|
|
|
481
324
|
<td class="ok">✔</td>
|
|
482
325
|
</tr>
|
|
483
326
|
<tr>
|
|
484
|
-
<td class="tg-yw4l">
|
|
485
|
-
<td class="ok">✔</td>
|
|
486
|
-
<td class="tg-3oug">require fuzzystrmatch</td>
|
|
487
|
-
<td class="ok">✔</td>
|
|
327
|
+
<td class="tg-yw4l">REGEXP<br>column =~ "pattern"<br></td>
|
|
488
328
|
<td class="ok">✔</td>
|
|
489
329
|
<td class="ok">✔</td>
|
|
330
|
+
<td class="tg-3oug">require pcre.so</td>
|
|
331
|
+
<td class="tg-j6lv">REGEXP_LIKE</td>
|
|
332
|
+
<td class="tg-j6lv">LIKE</td>
|
|
490
333
|
<td class="ok">✔</td>
|
|
491
334
|
</tr>
|
|
492
335
|
<tr>
|
|
493
|
-
<td class="tg-yw4l">
|
|
494
|
-
<td class="ok">✔</td>
|
|
495
|
-
<td class="tg-j6lv">SUBSTR()</td>
|
|
496
|
-
<td class="tg-j6lv">SUBSTR()</td>
|
|
497
|
-
<td class="tg-j6lv">SUBSTR()</td>
|
|
336
|
+
<td class="tg-yw4l">NOT_REGEXP<br>column != "pattern"</td>
|
|
498
337
|
<td class="ok">✔</td>
|
|
338
|
+
<td class="ok">✔<br></td>
|
|
339
|
+
<td class="tg-3oug">require pcre.so</td>
|
|
340
|
+
<td class="tg-j6lv">NOT REGEXP_LIKE </td>
|
|
341
|
+
<td class="tg-j6lv">NOT LIKE</td>
|
|
499
342
|
<td class="ok">✔</td>
|
|
500
343
|
</tr>
|
|
501
344
|
<tr>
|
|
502
|
-
<td class="tg-yw4l">
|
|
503
|
-
<td class="
|
|
504
|
-
<td class="ok">✔</td>
|
|
505
|
-
<td class="ok">✔</td>
|
|
345
|
+
<td class="tg-yw4l">ILIKE (in Arel6)<br/>column.imatches('%pattern')</td>
|
|
346
|
+
<td class="tg-j6lv">LOWER() LIKE LOWER()</td>
|
|
506
347
|
<td class="ok">✔</td>
|
|
507
348
|
<td class="ok">✔</td>
|
|
508
|
-
<td class="tg-j6lv">
|
|
349
|
+
<td class="tg-j6lv">LOWER() LIKE LOWER()</td>
|
|
350
|
+
<td class="tg-j6lv">LOWER() LIKE LOWER()</td>
|
|
351
|
+
<td class="tg-j6lv">LOWER() LIKE LOWER()</td>
|
|
509
352
|
</tr>
|
|
510
353
|
<tr>
|
|
511
354
|
<td class="tg-yw4l">TRIM (leading)<br>column.trim("LEADING","M")</td>
|
|
@@ -534,6 +377,43 @@ User.connection.execute(insert_manager.to_sql)
|
|
|
534
377
|
<td class="tg-j6lv">LTRIM(RTRIM())</td>
|
|
535
378
|
<td class="tg-j6lv">TRIM()</td>
|
|
536
379
|
</tr>
|
|
380
|
+
<tr>
|
|
381
|
+
<td class="tg-yw4l">Matching Accent/Case Insensitive<br>column.ai_imatches('blah')</td>
|
|
382
|
+
<td class="ok">✔</td>
|
|
383
|
+
<td class="tg-j6lv">unaccent required</td>
|
|
384
|
+
<td class="tg-j6lv">not supported</td>
|
|
385
|
+
<td class="ok">✔</td>
|
|
386
|
+
<td class="tg-j6lv">✔</td>
|
|
387
|
+
<td class="tg-j6lv">?</td>
|
|
388
|
+
</tr>
|
|
389
|
+
<tr>
|
|
390
|
+
<td class="tg-yw4l">Matching Accent Insensitive<br>column.ai_matches('blah')</td>
|
|
391
|
+
<td class="ok">not supported</td>
|
|
392
|
+
<td class="tg-j6lv">not supported</td>
|
|
393
|
+
<td class="tg-j6lv">not supported</td>
|
|
394
|
+
<td class="ok">not supported</td>
|
|
395
|
+
<td class="tg-j6lv">✔</td>
|
|
396
|
+
<td class="tg-j6lv">?</td>
|
|
397
|
+
</tr>
|
|
398
|
+
<tr>
|
|
399
|
+
<td class="tg-yw4l">Matching Case Insensitive<br>column.imatches('blah')</td>
|
|
400
|
+
<td class="ok">not supported</td>
|
|
401
|
+
<td class="tg-j6lv">✔</td>
|
|
402
|
+
<td class="tg-j6lv">✔</td>
|
|
403
|
+
<td class="ok">✔</td>
|
|
404
|
+
<td class="tg-j6lv">✔</td>
|
|
405
|
+
<td class="tg-j6lv">?</td>
|
|
406
|
+
</tr>
|
|
407
|
+
<tr>
|
|
408
|
+
<td class="tg-yw4l">Matching Accent/Case Sensitive<br>column.smatches('blah')</td>
|
|
409
|
+
<td class="ok">✔</td>
|
|
410
|
+
<td class="tg-j6lv">✔</td>
|
|
411
|
+
<td class="tg-j6lv">not supported</td>
|
|
412
|
+
<td class="ok">✔</td>
|
|
413
|
+
<td class="tg-j6lv">✔</td>
|
|
414
|
+
<td class="tg-j6lv">?</td>
|
|
415
|
+
</tr>
|
|
416
|
+
|
|
537
417
|
<tr>
|
|
538
418
|
<th class="tg-4rp9" rowspan="6"><div>Date functions</div></th>
|
|
539
419
|
<td class="tg-yw4l">DATEADD<br>column + 2.year<br></td>
|
|
@@ -590,16 +470,7 @@ User.connection.execute(insert_manager.to_sql)
|
|
|
590
470
|
<td class="ok">✔</td>
|
|
591
471
|
</tr>
|
|
592
472
|
<tr>
|
|
593
|
-
<th class="tg-72dn" rowspan="
|
|
594
|
-
<td class="tg-yw4l">BLANK<br>column.blank<br></td>
|
|
595
|
-
<td class="ok">✔</td>
|
|
596
|
-
<td class="ok">✔</td>
|
|
597
|
-
<td class="ok">✔</td>
|
|
598
|
-
<td class="ok">✔</td>
|
|
599
|
-
<td class="ok">✔</td>
|
|
600
|
-
<td class="ok">✔</td>
|
|
601
|
-
</tr>
|
|
602
|
-
<tr>
|
|
473
|
+
<th class="tg-72dn" rowspan="8"><div>Comparators functions</div></th>
|
|
603
474
|
<td class="tg-yw4l">COALESCE<br>column.coalesce(var)</td>
|
|
604
475
|
<td class="ok">✔</td>
|
|
605
476
|
<td class="ok">✔</td>
|
|
@@ -608,24 +479,6 @@ User.connection.execute(insert_manager.to_sql)
|
|
|
608
479
|
<td class="ok">✔</td>
|
|
609
480
|
<td class="ok">✔</td>
|
|
610
481
|
</tr>
|
|
611
|
-
<tr>
|
|
612
|
-
<td class="tg-yw4l">COALESCE_BLANK<br>column.coalesce_blank(var)</td>
|
|
613
|
-
<td class="ok">✔</td>
|
|
614
|
-
<td class="ok">✔</td>
|
|
615
|
-
<td class="ok">✔</td>
|
|
616
|
-
<td class="ok">✔</td>
|
|
617
|
-
<td class="ok">✔</td>
|
|
618
|
-
<td class="ok">✔</td>
|
|
619
|
-
</tr>
|
|
620
|
-
<tr>
|
|
621
|
-
<td class="tg-yw4l">IF_PRESENT</td>
|
|
622
|
-
<td class="ok">✔</td>
|
|
623
|
-
<td class="ok">✔</td>
|
|
624
|
-
<td class="ok">✔</td>
|
|
625
|
-
<td class="ok">✔</td>
|
|
626
|
-
<td class="ok">✔</td>
|
|
627
|
-
<td class="ok">✔</td>
|
|
628
|
-
</tr>
|
|
629
482
|
<tr>
|
|
630
483
|
<td class="tg-yw4l">ISNULL<br>column.isnull()</td>
|
|
631
484
|
<td class="tg-j6lv">IFNULL()</td>
|
|
@@ -635,24 +488,6 @@ User.connection.execute(insert_manager.to_sql)
|
|
|
635
488
|
<td class="ok">✔</td>
|
|
636
489
|
<td class="ok">✔</td>
|
|
637
490
|
</tr>
|
|
638
|
-
<tr>
|
|
639
|
-
<td class="tg-yw4l">NOT_BLANK<br>column.not_blank<br></td>
|
|
640
|
-
<td class="ok">✔</td>
|
|
641
|
-
<td class="ok">✔</td>
|
|
642
|
-
<td class="ok">✔</td>
|
|
643
|
-
<td class="ok">✔</td>
|
|
644
|
-
<td class="ok">✔</td>
|
|
645
|
-
<td class="ok">✔</td>
|
|
646
|
-
</tr>
|
|
647
|
-
<tr>
|
|
648
|
-
<td class="tg-yw4l">PRESENT<br>column.present<br>alias to NOT_BLANK<br></td>
|
|
649
|
-
<td class="ok">✔</td>
|
|
650
|
-
<td class="ok">✔</td>
|
|
651
|
-
<td class="ok">✔</td>
|
|
652
|
-
<td class="ok">✔</td>
|
|
653
|
-
<td class="ok">✔</td>
|
|
654
|
-
<td class="ok">✔</td>
|
|
655
|
-
</tr>
|
|
656
491
|
<tr>
|
|
657
492
|
<td class="tg-yw4l">==<br>column == integer</td>
|
|
658
493
|
<td class="ok">✔</td>
|
|
@@ -756,14 +591,3 @@ User.connection.execute(insert_manager.to_sql)
|
|
|
756
591
|
</tr>
|
|
757
592
|
</tbody>
|
|
758
593
|
</table>
|
|
759
|
-
|
|
760
|
-
## Version Compatibility
|
|
761
|
-
|
|
762
|
-
<table>
|
|
763
|
-
<tr><th>Ruby</th> <th>Rails</th> <th>Arel Extensions</th></tr>
|
|
764
|
-
<tr><td>3.1</td> <td>6.1</td> <td>2</td></tr>
|
|
765
|
-
<tr><td>3.0</td> <td>6.1</td> <td>2</td></tr>
|
|
766
|
-
<tr><td>2.7</td> <td>6.1, 6.0</td> <td>2</td></tr>
|
|
767
|
-
<tr><td>2.5</td> <td>6.1, 6.0</td> <td>2</td></tr>
|
|
768
|
-
<tr><td>2.5</td> <td>5.2</td> <td>1</td></tr>
|
|
769
|
-
</table>
|
data/Rakefile
CHANGED
|
@@ -1,59 +1,39 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
1
|
require 'bundler'
|
|
4
|
-
Bundler::GemHelper.install_tasks name
|
|
2
|
+
Bundler::GemHelper.install_tasks :name => "arel_extensions"
|
|
5
3
|
|
|
6
4
|
require 'rake/testtask'
|
|
7
5
|
|
|
8
|
-
desc
|
|
9
|
-
task default: [:test]
|
|
6
|
+
desc "Default Task"
|
|
7
|
+
task default: [ :test ]
|
|
10
8
|
|
|
11
9
|
Rake::TestTask.new(:test) do |t|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
namespace :test do
|
|
20
|
-
Rake::TestTask.new('to_sql' => []) { |t|
|
|
21
|
-
t.libs << 'lib'
|
|
22
|
-
t.libs << 'test'
|
|
23
|
-
t.pattern = 'test/visitors/test_to_sql.rb'
|
|
24
|
-
t.warning = true
|
|
25
|
-
t.verbose = true
|
|
26
|
-
t.ruby_opts = ['--dev'] if defined?(JRUBY_VERSION)
|
|
27
|
-
}
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
%w[mysql postgresql sqlite ibm_db oracle mssql].each do |adapter|
|
|
31
|
-
namespace :test do
|
|
32
|
-
Rake::TestTask.new(adapter => "#{adapter}:env") { |t|
|
|
33
|
-
t.libs << 'lib'
|
|
34
|
-
t.libs << 'test'
|
|
35
|
-
t.pattern = 'test/with_ar/*_agnostic_test.rb'
|
|
36
|
-
t.warning = false
|
|
37
|
-
t.verbose = true
|
|
38
|
-
t.ruby_opts = ['--dev'] if defined?(JRUBY_VERSION)
|
|
39
|
-
}
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
namespace adapter do
|
|
43
|
-
task test: "test_#{adapter}"
|
|
44
|
-
task isolated_test: "isolated_test_#{adapter}"
|
|
45
|
-
|
|
46
|
-
# Set the connection environment for the adapter
|
|
47
|
-
task(:env) { ENV['DB'] = adapter }
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Make sure the adapter test evaluates the env setting task
|
|
51
|
-
task "test_#{adapter}" => ["#{adapter}:env", "test:#{adapter}"]
|
|
10
|
+
t.libs << 'lib'
|
|
11
|
+
t.libs << 'test'
|
|
12
|
+
t.pattern = 'test/**/test_*.rb'
|
|
13
|
+
t.warning = true
|
|
14
|
+
t.verbose = true
|
|
52
15
|
end
|
|
53
16
|
|
|
54
|
-
|
|
55
|
-
namespace :test do
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
17
|
+
%w(mysql postgresql sqlite ibm_db oracle mssql).each do |adapter|
|
|
18
|
+
namespace :test do
|
|
19
|
+
Rake::TestTask.new(adapter => "#{adapter}:env") { |t|
|
|
20
|
+
t.libs << 'lib'
|
|
21
|
+
t.libs << 'test'
|
|
22
|
+
t.pattern = 'test/with_ar/*_agnostic_test.rb'
|
|
23
|
+
t.warning = true
|
|
24
|
+
t.verbose = true
|
|
25
|
+
t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
namespace adapter do
|
|
30
|
+
task :test => "test_#{adapter}"
|
|
31
|
+
task :isolated_test => "isolated_test_#{adapter}"
|
|
32
|
+
|
|
33
|
+
# Set the connection environment for the adapter
|
|
34
|
+
task(:env) { ENV['DB'] = adapter }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Make sure the adapter test evaluates the env setting task
|
|
38
|
+
task "test_#{adapter}" => ["#{adapter}:env", "test:#{adapter}"]
|
|
59
39
|
end
|