mirror of https://github.com/AMT-Cheif/drift.git
Support flags in regexp (#644)
This commit is contained in:
parent
bccbb3e7a8
commit
d881659db6
|
@ -2,6 +2,9 @@
|
|||
|
||||
- It's now possible to change the name of a class generated for moor-file queries. See
|
||||
[the documentation](https://moor.simonbinder.eu/docs/using-sql/moor_files/#result-class-names) for details.
|
||||
- Added the `multiLine`, `caseSensitive`, `unicode` and `doAll` flags to the `regex` sql extension
|
||||
method.
|
||||
They require the `moor_ffi` version `0.7.0` or later.
|
||||
|
||||
## 3.1.0
|
||||
|
||||
|
|
|
@ -11,9 +11,49 @@ extension StringExpressionOperators on Expression<String> {
|
|||
|
||||
/// Matches this string against the regular expression in [regex].
|
||||
///
|
||||
/// Note that this function is only available when using `moor_ffi`. If
|
||||
/// possible, consider using [like] instead.
|
||||
Expression<bool> regexp(String regex) {
|
||||
/// The [multiLine], [caseSensitive], [unicode] and [dotAll] parameters
|
||||
/// correspond to the parameters on [RegExp].
|
||||
///
|
||||
/// Note that this function is only available when using `moor_ffi`. If you
|
||||
/// need to support the web or `moor_flutter`, consider using [like] instead.
|
||||
Expression<bool> regexp(
|
||||
String regex, {
|
||||
bool multiLine = false,
|
||||
bool caseSensitive = true,
|
||||
bool unicode = false,
|
||||
bool dotAll = false,
|
||||
}) {
|
||||
// moor_ffi has a special regexp sql function that takes a third parameter
|
||||
// to encode flags. If the least significant bit is set, multiLine is
|
||||
// enabled. The next three bits enable case INSENSITIVITY (it's sensitive
|
||||
// by default), unicode and dotAll.
|
||||
var flags = 0;
|
||||
|
||||
if (multiLine) {
|
||||
flags |= 1;
|
||||
}
|
||||
if (!caseSensitive) {
|
||||
flags |= 2;
|
||||
}
|
||||
if (unicode) {
|
||||
flags |= 4;
|
||||
}
|
||||
if (dotAll) {
|
||||
flags |= 8;
|
||||
}
|
||||
|
||||
if (flags != 0) {
|
||||
return FunctionCallExpression<bool>(
|
||||
'regexp_moor_ffi',
|
||||
[
|
||||
Variable.withString(regex),
|
||||
this,
|
||||
Variable.withInt(flags),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// No special flags enabled, use the regular REGEXP operator
|
||||
return _LikeOperator(this, Variable.withString(regex), operator: 'REGEXP');
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@TestOn('vm')
|
||||
import 'package:moor/moor.dart';
|
||||
import 'package:moor/extensions/moor_ffi.dart';
|
||||
import 'package:moor/src/runtime/query_builder/query_builder.dart';
|
||||
import 'package:moor_ffi/moor_ffi.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
@ -56,4 +56,115 @@ void main() {
|
|||
completion(isTrue),
|
||||
);
|
||||
});
|
||||
|
||||
group('regexp flags', () {
|
||||
TodoDb db;
|
||||
|
||||
setUp(() async {
|
||||
db = TodoDb(VmDatabase.memory());
|
||||
// insert exactly one row so that we can evaluate expressions from Dart
|
||||
await db.into(db.pureDefaults).insert(PureDefaultsCompanion.insert());
|
||||
});
|
||||
|
||||
tearDown(() => db.close());
|
||||
|
||||
Future<bool> evaluate(Expression<bool> expr) async {
|
||||
final result = await (db.selectOnly(db.pureDefaults)..addColumns([expr]))
|
||||
.getSingle();
|
||||
|
||||
return result.read(expr);
|
||||
}
|
||||
|
||||
test('multiLine', () {
|
||||
expect(
|
||||
evaluate(
|
||||
Variable.withString('foo\nbar').regexp(
|
||||
'^bar',
|
||||
multiLine: true,
|
||||
),
|
||||
),
|
||||
completion(isTrue),
|
||||
);
|
||||
|
||||
expect(
|
||||
evaluate(
|
||||
Variable.withString('foo\nbar').regexp(
|
||||
'^bar',
|
||||
// multiLine is disabled by default
|
||||
),
|
||||
),
|
||||
completion(isFalse),
|
||||
);
|
||||
});
|
||||
|
||||
test('caseSensitive', () {
|
||||
expect(
|
||||
evaluate(
|
||||
Variable.withString('FOO').regexp(
|
||||
'foo',
|
||||
caseSensitive: false,
|
||||
),
|
||||
),
|
||||
completion(isTrue),
|
||||
);
|
||||
|
||||
expect(
|
||||
evaluate(
|
||||
Variable.withString('FOO').regexp(
|
||||
'foo',
|
||||
// caseSensitive should be true by default
|
||||
),
|
||||
),
|
||||
completion(isFalse),
|
||||
);
|
||||
});
|
||||
|
||||
test('unicode', () {
|
||||
// Note: `𝌆` is U+1D306 TETRAGRAM FOR CENTRE, an astral symbol.
|
||||
// https://mathiasbynens.be/notes/es6-unicode-regex
|
||||
const input = 'a𝌆b';
|
||||
|
||||
expect(
|
||||
evaluate(
|
||||
Variable.withString(input).regexp(
|
||||
'a.b',
|
||||
unicode: true,
|
||||
),
|
||||
),
|
||||
completion(isTrue),
|
||||
);
|
||||
|
||||
expect(
|
||||
evaluate(
|
||||
Variable.withString(input).regexp(
|
||||
'a.b',
|
||||
// Unicode is off by default
|
||||
),
|
||||
),
|
||||
completion(isFalse),
|
||||
);
|
||||
});
|
||||
|
||||
test('dotAll', () {
|
||||
expect(
|
||||
evaluate(
|
||||
Variable.withString('fo\n').regexp(
|
||||
'fo.',
|
||||
dotAll: true,
|
||||
),
|
||||
),
|
||||
completion(isTrue),
|
||||
);
|
||||
|
||||
expect(
|
||||
evaluate(
|
||||
Variable.withString('fo\n').regexp(
|
||||
'fo.',
|
||||
dotAll: false,
|
||||
),
|
||||
),
|
||||
completion(isFalse),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ extension SqliteValuePointer on Pointer<SqliteValue> {
|
|||
/// - a [double]
|
||||
/// - `null`
|
||||
///
|
||||
/// For texts and bytes, the value be copied.
|
||||
/// For texts and bytes, the value will be copied.
|
||||
dynamic get value {
|
||||
final api = bindings;
|
||||
|
||||
|
|
|
@ -69,8 +69,13 @@ void _atanImpl(Pointer<FunctionContext> ctx, int argCount,
|
|||
|
||||
void _regexpImpl(Pointer<FunctionContext> ctx, int argCount,
|
||||
Pointer<Pointer<SqliteValue>> args) {
|
||||
if (argCount != 2) {
|
||||
ctx.resultError('Expected two arguments to regexp');
|
||||
var multiLine = false;
|
||||
var caseSensitive = true;
|
||||
var unicode = false;
|
||||
var dotAll = false;
|
||||
|
||||
if (argCount < 2 || argCount > 3) {
|
||||
ctx.resultError('Expected two or three arguments to regexp');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -81,15 +86,32 @@ void _regexpImpl(Pointer<FunctionContext> ctx, int argCount,
|
|||
ctx.resultNull();
|
||||
return;
|
||||
}
|
||||
|
||||
if (firstParam is! String || secondParam is! String) {
|
||||
ctx.resultError('Expected two strings as parameters to regexp');
|
||||
return;
|
||||
}
|
||||
|
||||
if (argCount == 3) {
|
||||
// In the variant with three arguments, the last (int) arg can be used to
|
||||
// enable regex flags. See the regexp() extension in moor for details.
|
||||
final value = args[2].value;
|
||||
if (value is int) {
|
||||
multiLine = (value & 1) == 1;
|
||||
caseSensitive = (value & 2) != 2;
|
||||
unicode = (value & 4) == 4;
|
||||
dotAll = (value & 8) == 8;
|
||||
}
|
||||
}
|
||||
|
||||
RegExp regex;
|
||||
try {
|
||||
regex = RegExp(firstParam as String);
|
||||
regex = RegExp(
|
||||
firstParam as String,
|
||||
multiLine: multiLine,
|
||||
caseSensitive: caseSensitive,
|
||||
unicode: unicode,
|
||||
dotAll: dotAll,
|
||||
);
|
||||
} on FormatException catch (e) {
|
||||
ctx.resultError('Invalid regex: $e');
|
||||
return;
|
||||
|
@ -149,6 +171,9 @@ void _registerOn(Database db) {
|
|||
|
||||
db.createFunction('regexp', 2, Pointer.fromFunction(_regexpImpl),
|
||||
isDeterministic: true);
|
||||
// Third argument can be used to set flags (like multiline, case sensitivity,
|
||||
// etc.)
|
||||
db.createFunction('regexp_moor_ffi', 3, Pointer.fromFunction(_regexpImpl));
|
||||
|
||||
final containsImplPointer =
|
||||
Pointer.fromFunction<sqlite3_function_handler>(_containsImpl);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name: moor_ffi
|
||||
description: "Provides sqlite bindings using dart:ffi, including a moor executor"
|
||||
version: 0.6.0
|
||||
version: 0.7.0-dev
|
||||
homepage: https://github.com/simolus3/moor/tree/develop/moor_ffi
|
||||
issue_tracker: https://github.com/simolus3/moor/issues
|
||||
|
||||
|
|
|
@ -109,6 +109,13 @@ void main() {
|
|||
expect(result.single['r'], 0);
|
||||
});
|
||||
|
||||
test('supports flags', () {
|
||||
final stmt =
|
||||
db.prepare(r"SELECT regexp_moor_ffi('^bar', 'foo\nbar', 8) AS r;");
|
||||
final result = stmt.select();
|
||||
expect(result.single['r'], 0);
|
||||
});
|
||||
|
||||
test('returns null when either argument is null', () {
|
||||
final stmt = db.prepare('SELECT ? REGEXP ?');
|
||||
|
||||
|
|
Loading…
Reference in New Issue