arel_extensions 2.0.21 → 2.2.2
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 +1 -2
- data/.github/workflows/publish.yml +29 -0
- data/.github/workflows/release.yml +30 -0
- data/.github/workflows/ruby.yml +377 -80
- data/.gitignore +7 -6
- data/.rubocop.yml +62 -1
- data/CONTRIBUTING.md +102 -0
- data/Gemfile +2 -23
- data/NEWS.md +89 -0
- data/README.md +228 -84
- data/Rakefile +11 -4
- data/TODO +0 -1
- data/appveyor.yml +60 -22
- data/arel_extensions.gemspec +11 -12
- data/bin/build +15 -0
- data/bin/compose +6 -0
- data/bin/publish +8 -0
- data/dev/arelx.dockerfile +44 -0
- data/dev/compose.yaml +71 -0
- data/dev/postgres.dockerfile +5 -0
- data/dev/rbenv +189 -0
- data/gemfiles/rails3.gemfile +10 -10
- data/gemfiles/rails4_2.gemfile +38 -0
- data/gemfiles/rails5.gemfile +29 -0
- data/gemfiles/rails5_1_4.gemfile +13 -13
- data/gemfiles/rails5_2.gemfile +16 -14
- data/gemfiles/rails6.gemfile +18 -15
- data/gemfiles/rails6_1.gemfile +18 -15
- data/gemfiles/rails7.gemfile +33 -0
- data/gemfiles/rails7_1.gemfile +33 -0
- data/gemfiles/rails7_2.gemfile +33 -0
- data/gemspecs/arel_extensions-v1.gemspec +12 -13
- data/gemspecs/arel_extensions-v2.gemspec +11 -12
- data/init/mssql.sql +0 -0
- data/init/mysql.sql +0 -0
- data/init/oracle.sql +0 -0
- data/init/postgresql.sql +0 -0
- data/init/sqlite.sql +0 -0
- data/lib/arel_extensions/aliases.rb +14 -0
- data/lib/arel_extensions/attributes.rb +10 -2
- data/lib/arel_extensions/boolean_functions.rb +2 -4
- data/lib/arel_extensions/common_sql_functions.rb +12 -12
- data/lib/arel_extensions/comparators.rb +14 -14
- data/lib/arel_extensions/date_duration.rb +14 -9
- data/lib/arel_extensions/helpers.rb +62 -0
- data/lib/arel_extensions/insert_manager.rb +19 -17
- data/lib/arel_extensions/math.rb +48 -45
- data/lib/arel_extensions/math_functions.rb +18 -18
- data/lib/arel_extensions/nodes/abs.rb +0 -0
- data/lib/arel_extensions/nodes/aggregate_function.rb +0 -0
- data/lib/arel_extensions/nodes/blank.rb +1 -1
- data/lib/arel_extensions/nodes/case.rb +10 -12
- data/lib/arel_extensions/nodes/cast.rb +6 -6
- data/lib/arel_extensions/nodes/ceil.rb +0 -0
- data/lib/arel_extensions/nodes/change_case.rb +0 -0
- data/lib/arel_extensions/nodes/coalesce.rb +1 -1
- data/lib/arel_extensions/nodes/collate.rb +9 -9
- data/lib/arel_extensions/nodes/concat.rb +2 -2
- data/lib/arel_extensions/nodes/date_diff.rb +33 -14
- data/lib/arel_extensions/nodes/duration.rb +0 -0
- data/lib/arel_extensions/nodes/find_in_set.rb +0 -0
- data/lib/arel_extensions/nodes/floor.rb +0 -0
- data/lib/arel_extensions/nodes/format.rb +3 -2
- data/lib/arel_extensions/nodes/formatted_date.rb +42 -0
- data/lib/arel_extensions/nodes/formatted_number.rb +2 -2
- data/lib/arel_extensions/nodes/function.rb +22 -26
- data/lib/arel_extensions/nodes/is_null.rb +0 -0
- data/lib/arel_extensions/nodes/json.rb +15 -9
- data/lib/arel_extensions/nodes/length.rb +6 -0
- data/lib/arel_extensions/nodes/levenshtein_distance.rb +1 -1
- data/lib/arel_extensions/nodes/locate.rb +1 -1
- data/lib/arel_extensions/nodes/log10.rb +0 -0
- data/lib/arel_extensions/nodes/matches.rb +1 -1
- data/lib/arel_extensions/nodes/md5.rb +0 -0
- data/lib/arel_extensions/nodes/power.rb +0 -0
- data/lib/arel_extensions/nodes/rand.rb +0 -0
- data/lib/arel_extensions/nodes/repeat.rb +2 -2
- data/lib/arel_extensions/nodes/replace.rb +2 -10
- data/lib/arel_extensions/nodes/rollup.rb +36 -0
- data/lib/arel_extensions/nodes/round.rb +0 -0
- data/lib/arel_extensions/nodes/select.rb +10 -0
- data/lib/arel_extensions/nodes/soundex.rb +2 -2
- data/lib/arel_extensions/nodes/std.rb +0 -0
- data/lib/arel_extensions/nodes/substring.rb +1 -1
- data/lib/arel_extensions/nodes/sum.rb +0 -0
- data/lib/arel_extensions/nodes/then.rb +1 -1
- data/lib/arel_extensions/nodes/trim.rb +2 -2
- data/lib/arel_extensions/nodes/union.rb +5 -5
- data/lib/arel_extensions/nodes/union_all.rb +4 -4
- data/lib/arel_extensions/nodes/wday.rb +0 -0
- data/lib/arel_extensions/nodes.rb +0 -0
- data/lib/arel_extensions/null_functions.rb +16 -0
- data/lib/arel_extensions/predications.rb +10 -10
- data/lib/arel_extensions/railtie.rb +1 -1
- data/lib/arel_extensions/set_functions.rb +3 -3
- data/lib/arel_extensions/string_functions.rb +19 -10
- data/lib/arel_extensions/tasks.rb +2 -2
- data/lib/arel_extensions/version.rb +1 -1
- data/lib/arel_extensions/visitors/convert_format.rb +0 -0
- data/lib/arel_extensions/visitors/ibm_db.rb +20 -20
- data/lib/arel_extensions/visitors/mssql.rb +394 -169
- data/lib/arel_extensions/visitors/mysql.rb +238 -151
- data/lib/arel_extensions/visitors/oracle.rb +170 -131
- data/lib/arel_extensions/visitors/oracle12.rb +16 -16
- data/lib/arel_extensions/visitors/postgresql.rb +170 -140
- data/lib/arel_extensions/visitors/sqlite.rb +88 -87
- data/lib/arel_extensions/visitors/to_sql.rb +185 -156
- data/lib/arel_extensions/visitors.rb +73 -60
- data/lib/arel_extensions.rb +173 -36
- data/test/arelx_test_helper.rb +49 -1
- data/test/database.yml +13 -7
- data/test/real_db_test.rb +101 -83
- data/test/support/fake_record.rb +8 -2
- data/test/test_comparators.rb +5 -5
- data/test/visitors/test_bulk_insert_oracle.rb +5 -5
- data/test/visitors/test_bulk_insert_sqlite.rb +5 -5
- data/test/visitors/test_bulk_insert_to_sql.rb +5 -5
- data/test/visitors/test_oracle.rb +14 -14
- data/test/visitors/test_to_sql.rb +121 -93
- data/test/with_ar/all_agnostic_test.rb +630 -320
- data/test/with_ar/insert_agnostic_test.rb +25 -18
- data/test/with_ar/test_bulk_sqlite.rb +11 -7
- data/test/with_ar/test_math_sqlite.rb +18 -14
- data/test/with_ar/test_string_mysql.rb +26 -22
- data/test/with_ar/test_string_sqlite.rb +26 -22
- data/version_v1.rb +1 -1
- data/version_v2.rb +1 -1
- metadata +24 -26
- data/.travis/oracle/download.js +0 -152
- data/.travis/oracle/download.sh +0 -30
- data/.travis/oracle/download_ojdbc.js +0 -116
- data/.travis/oracle/install.sh +0 -34
- data/.travis/setup_accounts.sh +0 -9
- data/.travis/sqlite3/extension-functions.sh +0 -6
- data/.travis.yml +0 -193
- data/gemfiles/rails4.gemfile +0 -29
- data/gemfiles/rails5_0.gemfile +0 -29
data/README.md
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
# Arel Extensions
|
2
2
|
|
3
|
-
|
4
|
-
[](https://hakiri.io/github/Faveod/arel-extensions/master)
|
3
|
+

|
4
|
+
[](https://ci.appveyor.com/project/jdelporte/arel-extensions)
|
6
5
|

|
7
6
|
|
8
7
|
Gem: [](https://rubygems.org/gems/arel_extensions)
|
9
8
|
[](https://rubygems.org/gems/arel_extensions)
|
10
9
|
[](https://rubygems.org/gems/arel_extensions)
|
11
10
|
|
12
|
-
Arel Extensions adds shortcuts, fixes and new ORM mappings (
|
13
|
-
It aims to ensure pure
|
11
|
+
Arel Extensions adds shortcuts, fixes and new ORM mappings (Ruby to SQL) to Arel.
|
12
|
+
It aims to ensure pure Ruby syntax for most usual cases.
|
14
13
|
It allows to use more advanced SQL functions for any supported RDBMS.
|
15
14
|
|
16
15
|
|
@@ -37,7 +36,8 @@ It will add common SQL features in your DB to align ti with current routines. Te
|
|
37
36
|
|
38
37
|
## Examples
|
39
38
|
|
40
|
-
|
39
|
+
In the following examples
|
40
|
+
`t` is an `Arel::Table` for table `my_table` (i.e., `t = Arel::Table.new('my_table')`).
|
41
41
|
|
42
42
|
## Comparators
|
43
43
|
|
@@ -51,7 +51,7 @@ t is an Arel::Table for table my_table
|
|
51
51
|
# => my_table.nb > 42
|
52
52
|
```
|
53
53
|
|
54
|
-
Other operators
|
54
|
+
Other operators: <, >=, <=, =~
|
55
55
|
|
56
56
|
|
57
57
|
## Maths
|
@@ -74,7 +74,7 @@ With Arel Extensions:
|
|
74
74
|
# => SUM(my_table.nb) + 42
|
75
75
|
```
|
76
76
|
|
77
|
-
Other functions
|
77
|
+
Other functions: ABS, RAND, ROUND, FLOOR, CEIL, FORMAT
|
78
78
|
|
79
79
|
For Example:
|
80
80
|
```ruby
|
@@ -95,14 +95,29 @@ t[:price].format_number("%07.2f €","fr_FR")
|
|
95
95
|
# => TRIM(TRIM(TRIM(COALESCE(my_table.name, '')), '\t'), '\n') = ''
|
96
96
|
|
97
97
|
(t[:name] =~ /\A[a-d_]+/).to_sql
|
98
|
-
# => my_table.name REGEXP '
|
98
|
+
# => my_table.name REGEXP '^[a-d_]+'
|
99
99
|
```
|
100
100
|
|
101
|
-
|
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 ')
|
107
|
+
```
|
108
|
+
|
109
|
+
Captures are supported when using regex patterns. The replace string may then reference the capture groups using `\1`, `\2`, etc. For instance
|
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
|
102
117
|
|
103
118
|
### String Array operations
|
104
119
|
|
105
|
-
|
120
|
+
`t[:list]` is a classical varchar containing a comma separated list (`"1,2,3,4"`).
|
106
121
|
|
107
122
|
```ruby
|
108
123
|
(t[:list] & 3).to_sql
|
@@ -130,11 +145,76 @@ t[:birthdate].month.to_sql
|
|
130
145
|
|
131
146
|
t[:birthdate].year.to_sql
|
132
147
|
# => 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
|
+
|
156
|
+
# comparison
|
157
|
+
t[:birthdate] >= '2014-03-03 10:10:10'
|
158
|
+
```
|
133
159
|
|
160
|
+
### Format and Time Zone Conversion
|
161
|
+
|
162
|
+
`format` has two forms:
|
163
|
+
|
164
|
+
```ruby
|
134
165
|
t[:birthdate].format('%Y-%m-%d').to_sql
|
135
166
|
# => DATE_FORMAT(my_table.birthdate, '%Y-%m-%d')
|
136
167
|
```
|
137
168
|
|
169
|
+
Which formats the datetime without any time zone conversion.
|
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 overlaping 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
|
+
|
138
218
|
## Unions
|
139
219
|
|
140
220
|
```ruby
|
@@ -164,15 +244,15 @@ t[:id].cast('char').to_sql
|
|
164
244
|
## Stored Procedures and User-defined functions
|
165
245
|
|
166
246
|
To optimize queries, some classical functions are defined in databases missing any alternative native functions.
|
167
|
-
Examples
|
168
|
-
- FIND_IN_SET
|
247
|
+
Examples:
|
248
|
+
- `FIND_IN_SET`
|
169
249
|
|
170
250
|
## BULK INSERT / UPSERT
|
171
251
|
|
172
252
|
Arel Extensions improves InsertManager by adding bulk_insert method, which allows to insert multiple rows in one insert.
|
173
253
|
|
174
254
|
|
175
|
-
```
|
255
|
+
```ruby
|
176
256
|
@cols = ['id', 'name', 'comments', 'created_at']
|
177
257
|
@data = [
|
178
258
|
[23, 'name1', "sdfdsfdsfsdf", '2016-01-01'],
|
@@ -228,6 +308,15 @@ User.connection.execute(insert_manager.to_sql)
|
|
228
308
|
<td class="ok">✔</td>
|
229
309
|
<td class="ok">✔</td>
|
230
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>
|
315
|
+
<td class="ok">✔</td>
|
316
|
+
<td class="ok">✔</td>
|
317
|
+
<td class="ok">✔</td>
|
318
|
+
<td class="ko">not implemented</td>
|
319
|
+
</tr>
|
231
320
|
<tr>
|
232
321
|
<td class="tg-yw4l">RAND<br>Arel.rand</td>
|
233
322
|
<td class="ok">✔</td>
|
@@ -256,24 +345,33 @@ User.connection.execute(insert_manager.to_sql)
|
|
256
345
|
<td class="ok">✔</td>
|
257
346
|
</tr>
|
258
347
|
<tr>
|
259
|
-
<
|
260
|
-
<td class="
|
348
|
+
<th class="tg-ffjm" rowspan="17"><div>String functions</div></th>
|
349
|
+
<td class="tg-yw4l">CONCAT<br>column + "string"</td>
|
261
350
|
<td class="ok">✔</td>
|
262
351
|
<td class="ok">✔</td>
|
352
|
+
<td class="tg-j6lv"> ||</td>
|
263
353
|
<td class="ok">✔</td>
|
354
|
+
<td class="tg-j6lv">+</td>
|
264
355
|
<td class="ok">✔</td>
|
265
|
-
<td class="ko">not implemented</td>
|
266
356
|
</tr>
|
267
357
|
<tr>
|
268
|
-
<
|
269
|
-
<td class="tg-yw4l">CONCAT<br>column + "string"</td>
|
358
|
+
<td class="tg-yw4l">FIND_IN_SET<br>column & ("l")</td>
|
270
359
|
<td class="ok">✔</td>
|
271
360
|
<td class="ok">✔</td>
|
272
|
-
<td class="tg-
|
361
|
+
<td class="tg-orpl">Ruby function</td>
|
362
|
+
<td class="ok">✔</td>
|
273
363
|
<td class="ok">✔</td>
|
274
|
-
<td class="tg-j6lv">+</td>
|
275
364
|
<td class="ok">✔</td>
|
276
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>
|
370
|
+
<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>
|
277
375
|
<tr>
|
278
376
|
<td class="tg-yw4l">LENGTH<br>column.length</td>
|
279
377
|
<td class="ok">✔</td>
|
@@ -293,30 +391,57 @@ User.connection.execute(insert_manager.to_sql)
|
|
293
391
|
<td class="ok">✔</td>
|
294
392
|
</tr>
|
295
393
|
<tr>
|
296
|
-
<td class="tg-yw4l">
|
297
|
-
<td class="ok">✔</td>
|
298
|
-
<td class="tg-j6lv">SUBSTR()</td>
|
299
|
-
<td class="tg-j6lv">SUBSTR()</td>
|
300
|
-
<td class="tg-j6lv">SUBSTR()</td>
|
394
|
+
<td class="tg-yw4l">Matching Accent/Case Insensitive<br>column.ai_imatches('blah')</td>
|
301
395
|
<td class="ok">✔</td>
|
396
|
+
<td class="tg-j6lv">unaccent required</td>
|
397
|
+
<td class="tg-j6lv">not supported</td>
|
302
398
|
<td class="ok">✔</td>
|
399
|
+
<td class="tg-j6lv">✔</td>
|
400
|
+
<td class="tg-j6lv">?</td>
|
303
401
|
</tr>
|
304
402
|
<tr>
|
305
|
-
<td class="tg-yw4l">
|
306
|
-
<td class="ok"
|
307
|
-
<td class="
|
308
|
-
<td class="tg-
|
403
|
+
<td class="tg-yw4l">Matching Accent Insensitive<br>column.ai_matches('blah')</td>
|
404
|
+
<td class="ok">not supported</td>
|
405
|
+
<td class="tg-j6lv">not supported</td>
|
406
|
+
<td class="tg-j6lv">not supported</td>
|
407
|
+
<td class="ok">not supported</td>
|
408
|
+
<td class="tg-j6lv">✔</td>
|
409
|
+
<td class="tg-j6lv">?</td>
|
410
|
+
</tr>
|
411
|
+
<tr>
|
412
|
+
<td class="tg-yw4l">Matching Case Insensitive<br>column.imatches('blah')</td>
|
413
|
+
<td class="ok">not supported</td>
|
414
|
+
<td class="tg-j6lv">✔</td>
|
415
|
+
<td class="tg-j6lv">✔</td>
|
309
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/Case Sensitive<br>column.smatches('blah')</td>
|
310
422
|
<td class="ok">✔</td>
|
423
|
+
<td class="tg-j6lv">✔</td>
|
424
|
+
<td class="tg-j6lv">not supported</td>
|
311
425
|
<td class="ok">✔</td>
|
426
|
+
<td class="tg-j6lv">✔</td>
|
427
|
+
<td class="tg-j6lv">?</td>
|
312
428
|
</tr>
|
313
429
|
<tr>
|
314
|
-
<td class="tg-yw4l">
|
430
|
+
<td class="tg-yw4l">NOT_REGEXP<br>column != "pattern"</td>
|
315
431
|
<td class="ok">✔</td>
|
316
|
-
<td class="
|
432
|
+
<td class="ok">✔<br></td>
|
433
|
+
<td class="tg-3oug">require pcre.so</td>
|
434
|
+
<td class="tg-j6lv">NOT REGEXP_LIKE </td>
|
435
|
+
<td class="tg-j6lv">NOT LIKE</td>
|
317
436
|
<td class="ok">✔</td>
|
437
|
+
</tr>
|
438
|
+
<tr>
|
439
|
+
<td class="tg-yw4l">REGEXP<br>column =~ "pattern"<br></td>
|
318
440
|
<td class="ok">✔</td>
|
319
441
|
<td class="ok">✔</td>
|
442
|
+
<td class="tg-3oug">require pcre.so</td>
|
443
|
+
<td class="tg-j6lv">REGEXP_LIKE</td>
|
444
|
+
<td class="tg-j6lv">LIKE</td>
|
320
445
|
<td class="ok">✔</td>
|
321
446
|
</tr>
|
322
447
|
<tr>
|
@@ -329,31 +454,31 @@ User.connection.execute(insert_manager.to_sql)
|
|
329
454
|
<td class="ok">✔</td>
|
330
455
|
</tr>
|
331
456
|
<tr>
|
332
|
-
<td class="tg-yw4l">
|
457
|
+
<td class="tg-yw4l">REPLACE<br>column.replace(/re/,"X")</td>
|
458
|
+
<td class="ok">✔</td>
|
459
|
+
<td class="ok">✔</td>
|
460
|
+
<td class="ok">✔</td>
|
333
461
|
<td class="ok">✔</td>
|
334
462
|
<td class="ok">✔</td>
|
335
|
-
<td class="tg-3oug">require pcre.so</td>
|
336
|
-
<td class="tg-j6lv">REGEXP_LIKE</td>
|
337
|
-
<td class="tg-j6lv">LIKE</td>
|
338
463
|
<td class="ok">✔</td>
|
339
464
|
</tr>
|
340
465
|
<tr>
|
341
|
-
<td class="tg-yw4l">
|
466
|
+
<td class="tg-yw4l">SOUNDEX<br>column.soundex</td>
|
467
|
+
<td class="ok">✔</td>
|
468
|
+
<td class="tg-3oug">require fuzzystrmatch</td>
|
469
|
+
<td class="ok">✔</td>
|
470
|
+
<td class="ok">✔</td>
|
342
471
|
<td class="ok">✔</td>
|
343
|
-
<td class="ok">✔<br></td>
|
344
|
-
<td class="tg-3oug">require pcre.so</td>
|
345
|
-
<td class="tg-j6lv">NOT REGEXP_LIKE </td>
|
346
|
-
<td class="tg-j6lv">NOT LIKE</td>
|
347
472
|
<td class="ok">✔</td>
|
348
473
|
</tr>
|
349
474
|
<tr>
|
350
|
-
<td class="tg-yw4l">
|
351
|
-
<td class="
|
475
|
+
<td class="tg-yw4l">SUBSTRING<br/>column[1..2]<br/>column.substring(1)<br/>column.substring(1, 1)</td>
|
476
|
+
<td class="ok">✔</td>
|
477
|
+
<td class="tg-j6lv">SUBSTR()</td>
|
478
|
+
<td class="tg-j6lv">SUBSTR()</td>
|
479
|
+
<td class="tg-j6lv">SUBSTR()</td>
|
352
480
|
<td class="ok">✔</td>
|
353
481
|
<td class="ok">✔</td>
|
354
|
-
<td class="tg-j6lv">LOWER() LIKE LOWER()</td>
|
355
|
-
<td class="tg-j6lv">LOWER() LIKE LOWER()</td>
|
356
|
-
<td class="tg-j6lv">LOWER() LIKE LOWER()</td>
|
357
482
|
</tr>
|
358
483
|
<tr>
|
359
484
|
<td class="tg-yw4l">TRIM (leading)<br>column.trim("LEADING","M")</td>
|
@@ -382,43 +507,6 @@ User.connection.execute(insert_manager.to_sql)
|
|
382
507
|
<td class="tg-j6lv">LTRIM(RTRIM())</td>
|
383
508
|
<td class="tg-j6lv">TRIM()</td>
|
384
509
|
</tr>
|
385
|
-
<tr>
|
386
|
-
<td class="tg-yw4l">Matching Accent/Case Insensitive<br>column.ai_imatches('blah')</td>
|
387
|
-
<td class="ok">✔</td>
|
388
|
-
<td class="tg-j6lv">unaccent required</td>
|
389
|
-
<td class="tg-j6lv">not supported</td>
|
390
|
-
<td class="ok">✔</td>
|
391
|
-
<td class="tg-j6lv">✔</td>
|
392
|
-
<td class="tg-j6lv">?</td>
|
393
|
-
</tr>
|
394
|
-
<tr>
|
395
|
-
<td class="tg-yw4l">Matching Accent Insensitive<br>column.ai_matches('blah')</td>
|
396
|
-
<td class="ok">not supported</td>
|
397
|
-
<td class="tg-j6lv">not supported</td>
|
398
|
-
<td class="tg-j6lv">not supported</td>
|
399
|
-
<td class="ok">not supported</td>
|
400
|
-
<td class="tg-j6lv">✔</td>
|
401
|
-
<td class="tg-j6lv">?</td>
|
402
|
-
</tr>
|
403
|
-
<tr>
|
404
|
-
<td class="tg-yw4l">Matching Case Insensitive<br>column.imatches('blah')</td>
|
405
|
-
<td class="ok">not supported</td>
|
406
|
-
<td class="tg-j6lv">✔</td>
|
407
|
-
<td class="tg-j6lv">✔</td>
|
408
|
-
<td class="ok">✔</td>
|
409
|
-
<td class="tg-j6lv">✔</td>
|
410
|
-
<td class="tg-j6lv">?</td>
|
411
|
-
</tr>
|
412
|
-
<tr>
|
413
|
-
<td class="tg-yw4l">Matching Accent/Case Sensitive<br>column.smatches('blah')</td>
|
414
|
-
<td class="ok">✔</td>
|
415
|
-
<td class="tg-j6lv">✔</td>
|
416
|
-
<td class="tg-j6lv">not supported</td>
|
417
|
-
<td class="ok">✔</td>
|
418
|
-
<td class="tg-j6lv">✔</td>
|
419
|
-
<td class="tg-j6lv">?</td>
|
420
|
-
</tr>
|
421
|
-
|
422
510
|
<tr>
|
423
511
|
<th class="tg-4rp9" rowspan="6"><div>Date functions</div></th>
|
424
512
|
<td class="tg-yw4l">DATEADD<br>column + 2.year<br></td>
|
@@ -475,7 +563,16 @@ User.connection.execute(insert_manager.to_sql)
|
|
475
563
|
<td class="ok">✔</td>
|
476
564
|
</tr>
|
477
565
|
<tr>
|
478
|
-
<th class="tg-72dn" rowspan="
|
566
|
+
<th class="tg-72dn" rowspan="13"><div>Comparators functions</div></th>
|
567
|
+
<td class="tg-yw4l">BLANK<br>column.blank<br></td>
|
568
|
+
<td class="ok">✔</td>
|
569
|
+
<td class="ok">✔</td>
|
570
|
+
<td class="ok">✔</td>
|
571
|
+
<td class="ok">✔</td>
|
572
|
+
<td class="ok">✔</td>
|
573
|
+
<td class="ok">✔</td>
|
574
|
+
</tr>
|
575
|
+
<tr>
|
479
576
|
<td class="tg-yw4l">COALESCE<br>column.coalesce(var)</td>
|
480
577
|
<td class="ok">✔</td>
|
481
578
|
<td class="ok">✔</td>
|
@@ -484,6 +581,24 @@ User.connection.execute(insert_manager.to_sql)
|
|
484
581
|
<td class="ok">✔</td>
|
485
582
|
<td class="ok">✔</td>
|
486
583
|
</tr>
|
584
|
+
<tr>
|
585
|
+
<td class="tg-yw4l">COALESCE_BLANK<br>column.coalesce_blank(var)</td>
|
586
|
+
<td class="ok">✔</td>
|
587
|
+
<td class="ok">✔</td>
|
588
|
+
<td class="ok">✔</td>
|
589
|
+
<td class="ok">✔</td>
|
590
|
+
<td class="ok">✔</td>
|
591
|
+
<td class="ok">✔</td>
|
592
|
+
</tr>
|
593
|
+
<tr>
|
594
|
+
<td class="tg-yw4l">IF_PRESENT</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>
|
487
602
|
<tr>
|
488
603
|
<td class="tg-yw4l">ISNULL<br>column.isnull()</td>
|
489
604
|
<td class="tg-j6lv">IFNULL()</td>
|
@@ -493,6 +608,24 @@ User.connection.execute(insert_manager.to_sql)
|
|
493
608
|
<td class="ok">✔</td>
|
494
609
|
<td class="ok">✔</td>
|
495
610
|
</tr>
|
611
|
+
<tr>
|
612
|
+
<td class="tg-yw4l">NOT_BLANK<br>column.not_blank<br></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">PRESENT<br>column.present<br>alias to NOT_BLANK<br></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>
|
496
629
|
<tr>
|
497
630
|
<td class="tg-yw4l">==<br>column == integer</td>
|
498
631
|
<td class="ok">✔</td>
|
@@ -596,3 +729,14 @@ User.connection.execute(insert_manager.to_sql)
|
|
596
729
|
</tr>
|
597
730
|
</tbody>
|
598
731
|
</table>
|
732
|
+
|
733
|
+
## Version Compatibility
|
734
|
+
|
735
|
+
<table>
|
736
|
+
<tr><th>Ruby</th> <th>Rails</th> <th>Arel Extensions</th></tr>
|
737
|
+
<tr><td>3.1</td> <td>6.1</td> <td>2</td></tr>
|
738
|
+
<tr><td>3.0</td> <td>6.1</td> <td>2</td></tr>
|
739
|
+
<tr><td>2.7</td> <td>6.1, 6.0</td> <td>2</td></tr>
|
740
|
+
<tr><td>2.5</td> <td>6.1, 6.0</td> <td>2</td></tr>
|
741
|
+
<tr><td>2.5</td> <td>5.2</td> <td>1</td></tr>
|
742
|
+
</table>
|
data/Rakefile
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'bundler'
|
2
|
-
Bundler::GemHelper.install_tasks name:
|
2
|
+
Bundler::GemHelper.install_tasks name: 'arel_extensions'
|
3
3
|
|
4
4
|
require 'rake/testtask'
|
5
5
|
|
6
|
-
desc
|
6
|
+
desc 'Default Task'
|
7
7
|
task default: [:test]
|
8
8
|
|
9
9
|
Rake::TestTask.new(:test) do |t|
|
@@ -21,7 +21,7 @@ namespace :test do
|
|
21
21
|
t.pattern = 'test/visitors/test_to_sql.rb'
|
22
22
|
t.warning = true
|
23
23
|
t.verbose = true
|
24
|
-
t.ruby_opts = [
|
24
|
+
t.ruby_opts = ['--dev'] if defined?(JRUBY_VERSION)
|
25
25
|
}
|
26
26
|
end
|
27
27
|
|
@@ -33,7 +33,7 @@ end
|
|
33
33
|
t.pattern = 'test/with_ar/*_agnostic_test.rb'
|
34
34
|
t.warning = false
|
35
35
|
t.verbose = true
|
36
|
-
t.ruby_opts = [
|
36
|
+
t.ruby_opts = ['--dev'] if defined?(JRUBY_VERSION)
|
37
37
|
}
|
38
38
|
end
|
39
39
|
|
@@ -48,3 +48,10 @@ end
|
|
48
48
|
# Make sure the adapter test evaluates the env setting task
|
49
49
|
task "test_#{adapter}" => ["#{adapter}:env", "test:#{adapter}"]
|
50
50
|
end
|
51
|
+
|
52
|
+
# Useful shorthands.
|
53
|
+
namespace :test do
|
54
|
+
task :sql => :to_sql
|
55
|
+
task :pg => :postgresql
|
56
|
+
task :postgres => :postgresql
|
57
|
+
end
|
data/TODO
CHANGED
data/appveyor.yml
CHANGED
@@ -1,44 +1,82 @@
|
|
1
|
+
image: Visual Studio 2015
|
2
|
+
|
1
3
|
version: "{build}"
|
2
4
|
|
3
5
|
cache:
|
4
6
|
- vendor/bundle
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
environment:
|
9
|
+
matrix:
|
10
|
+
- RUBY_VERSION: 25-x64
|
11
|
+
RAILS: 4_2
|
12
|
+
SQL: MSSQL$SQL2012SP1
|
13
|
+
- RUBY_VERSION: 25-x64
|
14
|
+
RAILS: 4_2
|
15
|
+
SQL: MSSQL$SQL2014
|
16
|
+
- RUBY_VERSION: 25-x64
|
17
|
+
RAILS: 4_2
|
18
|
+
SQL: MSSQL$SQL2016
|
19
|
+
- RUBY_VERSION: 25-x64
|
20
|
+
RAILS: 5_2
|
21
|
+
SQL: MSSQL$SQL2012SP1
|
22
|
+
- RUBY_VERSION: 25-x64
|
23
|
+
RAILS: 5_2
|
24
|
+
SQL: MSSQL$SQL2014
|
25
|
+
- RUBY_VERSION: 25-x64
|
26
|
+
RAILS: 5_2
|
27
|
+
SQL: MSSQL$SQL2016
|
28
|
+
- RUBY_VERSION: 25-x64
|
29
|
+
RAILS: 6
|
30
|
+
SQL: MSSQL$SQL2012SP1
|
31
|
+
- RUBY_VERSION: 25-x64
|
32
|
+
RAILS: 6
|
33
|
+
SQL: MSSQL$SQL2014
|
34
|
+
- RUBY_VERSION: 25-x64
|
35
|
+
RAILS: 6
|
36
|
+
SQL: MSSQL$SQL2016
|
37
|
+
- RUBY_VERSION: 25-x64
|
38
|
+
RAILS: 6_1
|
39
|
+
SQL: MSSQL$SQL2012SP1
|
40
|
+
- RUBY_VERSION: 25-x64
|
41
|
+
RAILS: 6_1
|
42
|
+
SQL: MSSQL$SQL2014
|
43
|
+
- RUBY_VERSION: 25-x64
|
44
|
+
RAILS: 6_1
|
45
|
+
SQL: MSSQL$SQL2016
|
13
46
|
|
14
47
|
install:
|
15
48
|
- set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH%
|
49
|
+
- gem update --system
|
50
|
+
- gem install rubygems-update && update_rubygems
|
16
51
|
- bundle config --local path vendor/bundle
|
52
|
+
- cp ./gemfiles/rails%RAILS%.gemfile ./Gemfile
|
53
|
+
- bundle config set gemfile ./gemfiles/rails%RAILS%.gemfile
|
17
54
|
- bundle install
|
18
55
|
|
19
|
-
# Disable normal Windows builds in favor of our test script.
|
20
56
|
build: off
|
21
57
|
|
22
|
-
|
23
58
|
before_test:
|
24
59
|
- ruby -v
|
25
60
|
- gem -v
|
26
61
|
- bundle -v
|
27
62
|
|
28
63
|
test_script:
|
29
|
-
-
|
30
|
-
-
|
31
|
-
- bundler --version
|
32
|
-
- bundle exec rake test
|
33
|
-
- ps: Start-Service 'MSSQL$SQL2014'
|
34
|
-
- timeout /t 4 /nobreak > NUL
|
64
|
+
- ps: Get-Service '*SQL*'
|
65
|
+
- net start %SQL%
|
35
66
|
- bundle exec rake test:mssql
|
36
67
|
|
37
|
-
|
68
|
+
for:
|
69
|
+
-
|
38
70
|
matrix:
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
- RUBY_VERSION
|
43
|
-
-
|
44
|
-
|
71
|
+
except:
|
72
|
+
- RAILS: 5_2
|
73
|
+
install:
|
74
|
+
- set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH%
|
75
|
+
- cp ./gemspecs/arel_extensions-v2.gemspec ./arel_extensions.gemspec
|
76
|
+
- cp ./version_v2.rb lib/arel_extensions/version.rb
|
77
|
+
- cp ./gemfiles/rails%RAILS%.gemfile ./Gemfile
|
78
|
+
- gem update --system
|
79
|
+
- gem install rubygems-update && update_rubygems
|
80
|
+
- bundle config --local path vendor/bundle
|
81
|
+
- bundle config set gemfile ./gemfiles/rails%RAILS%.gemfile
|
82
|
+
- bundle install
|
data/arel_extensions.gemspec
CHANGED
@@ -1,28 +1,27 @@
|
|
1
|
-
$:.push File.expand_path(
|
2
|
-
require
|
1
|
+
$:.push File.expand_path('../lib', __FILE__)
|
2
|
+
require 'arel_extensions/version'
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
|
-
s.name =
|
5
|
+
s.name = 'arel_extensions'
|
6
6
|
s.version = ArelExtensions::VERSION
|
7
7
|
s.platform = Gem::Platform::RUBY
|
8
|
-
s.authors = [
|
9
|
-
s.email = [
|
10
|
-
s.homepage =
|
11
|
-
s.description =
|
12
|
-
s.summary =
|
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
|
+
s.homepage = 'https://github.com/Faveod/arel-extensions'
|
11
|
+
s.description = 'Adds new features to Arel'
|
12
|
+
s.summary = 'Extending Arel'
|
13
13
|
s.license = 'MIT'
|
14
14
|
|
15
|
-
s.rdoc_options = [
|
16
|
-
s.extra_rdoc_files = [
|
15
|
+
s.rdoc_options = ['--main', 'README.md']
|
16
|
+
s.extra_rdoc_files = ['MIT-LICENSE.txt', 'README.md', 'functions.html']
|
17
17
|
|
18
18
|
# Manifest
|
19
19
|
s.files = `git ls-files`.split("\n")
|
20
20
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
-
s.require_paths = [
|
21
|
+
s.require_paths = ['lib']
|
22
22
|
|
23
23
|
s.add_dependency('activerecord', '>= 6.0')
|
24
24
|
|
25
25
|
s.add_development_dependency('minitest', '~> 5.9')
|
26
|
-
s.add_development_dependency('rdoc', '~> 4.0')
|
27
26
|
s.add_development_dependency('rake', '~> 12.3.3')
|
28
27
|
end
|