Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ SQL-Server-Regex gives you the power to run regular expressions inside SQL Serve

# Examples

### IsMatch

The [RegexIsMatch()](/examples/is-match.md) scalar function evaluates a regular expression against a string and returns 1 when a match is found, or 0 otherwise.

### Match

The [RegexMatch()](/examples/match.md) scalar function lets you call a regular expression against a string, and returns the first result if there is a match.
Expand Down
96 changes: 96 additions & 0 deletions examples/is-match.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# IsMatch

![SQL Regex Logo](/images/sql-regex-logo.png)

RegexIsMatch() is a scalar function that lets you test whether a regular expression matches a string. It is a SQL CLR function that exposes the [System.Text.RegularExpressions](https://msdn.microsoft.com/en-us/library/system.text.regularexpressions(v=vs.110).aspx)' [IsMatch()](https://learn.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.regex.ismatch?view=net-10.0) method.

It returns:

- **1** if the pattern matches
- **0** otherwise

The function is intended for **validation**, not extraction.

Let's look at a few examples, inspired by a handy [Regular Expressions tutorial](http://www.regular-expressions.info/examples.html)

---

## Examples

### Digits

Check whether a string contains at least one digit.

```sql
select dbo.RegexIsMatch('ABC123', '\d+'); -- 1
select dbo.RegexIsMatch('ABC', '\d+'); -- 0
```

---

### Letters only (whole string)

Validate that the entire string contains only letters.

```sql
select dbo.RegexIsMatch('Hello', '^[A-Za-z]+$'); -- 1
select dbo.RegexIsMatch('Hi!', '^[A-Za-z]+$'); -- 0
```

---

### Empty or exactly two characters

```sql
select dbo.RegexIsMatch('', '^(.{2})?$'); -- 1
select dbo.RegexIsMatch('AB', '^(.{2})?$'); -- 1
select dbo.RegexIsMatch('A', '^(.{2})?$'); -- 0
```

---

### No underscore allowed

Validate that a string does not contain `_`.

```sql
select dbo.RegexIsMatch('abc', '^[^_]+$'); -- 1
select dbo.RegexIsMatch('abc_', '^[^_]+$'); -- 0
```

---

### Whitespace only

Check if a string is empty or contains only whitespace.

```sql
select dbo.RegexIsMatch('', '^\s*$'); -- 1
select dbo.RegexIsMatch(' ', '^\s*$'); -- 1
select dbo.RegexIsMatch('abc', '^\s*$'); -- 0
```

---

### Partial match vs full-string match

`RegexIsMatch()` succeeds if the pattern matches **anywhere** in the string.

```sql
select dbo.RegexIsMatch('abc_', '[^_]+'); -- 1 (matches "abc")
```

To validate the **entire** string, use anchors:

```sql
select dbo.RegexIsMatch('abc_', '^[^_]+$'); -- 0
select dbo.RegexIsMatch('abc', '^[^_]+$'); -- 1
```

---

## Notes

- Use `^` and `$` when validating full values.
- The function uses the .NET regular expression engine.
- For extracting matched text, use `RegexMatch()` instead.
8 changes: 8 additions & 0 deletions sql/scalar_functions.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use Scratch
go

drop function dbo.RegexIsMatch
go

drop function dbo.RegexMatch
go

Expand All @@ -12,6 +15,11 @@ go

-- see https://msdn.microsoft.com/en-us/library/ms186755.aspx for details

CREATE FUNCTION dbo.RegexIsMatch (@input nvarchar(max), @pattern nvarchar(max))
RETURNS bit
AS EXTERNAL NAME [RegexAssembly].[UDF].[IsMatch]
go

CREATE FUNCTION dbo.RegexMatch (@input nvarchar(max), @pattern nvarchar(max))
RETURNS nvarchar(max)
AS EXTERNAL NAME [RegexAssembly].[UDF].[Match]
Expand Down
57 changes: 32 additions & 25 deletions src/Regex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,54 +8,61 @@
public partial class UDF
{
[Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true)]
public static SqlString Match(String input, String pattern)
public static SqlBoolean IsMatch(string input, string pattern)
{
if (String.IsNullOrEmpty(input) || String.IsNullOrEmpty(pattern))
if (input is null || string.IsNullOrEmpty(pattern))
{
return new SqlString(null);
return new SqlBoolean(false);
}
else
{
Match m = Regex.Match(input, pattern);

return new SqlString(m.Success ? m.Value : null);
var success = Regex.IsMatch(input, pattern);

return new SqlBoolean(success);
}

[Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true)]
public static SqlString Match(string input, string pattern)
{
if (input is null || string.IsNullOrEmpty(pattern))
{
return new SqlString(null);
}

Match m = Regex.Match(input, pattern);

return new SqlString(m.Success ? m.Value : null);
}

[Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true, IsPrecise = true)]
public static SqlString GroupMatch(String input, String pattern, String group)
public static SqlString GroupMatch(string input, string pattern, string group)
{
if (String.IsNullOrEmpty(input) || String.IsNullOrEmpty(pattern) || String.IsNullOrEmpty(group))
if (input is null || string.IsNullOrEmpty(pattern) || string.IsNullOrEmpty(group))
{
return new SqlString(null);
}
else
{
Group g = Regex.Match(input, pattern).Groups[group];

return new SqlString(g.Success ? g.Value : null);
}
Group g = Regex.Match(input, pattern).Groups[group];

return new SqlString(g.Success ? g.Value : null);
}

[Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true, IsPrecise = true)]
public static SqlString Replace(String input, String pattern, String replacement)
public static SqlString Replace(string input, string pattern, string replacement)
{
// the replacement string is not checked for an empty string because that is a valid replacement pattern
if (String.IsNullOrEmpty(input) || String.IsNullOrEmpty(pattern) || replacement == null)
if (input is null || string.IsNullOrEmpty(pattern) || replacement == null)
{
return new SqlString(null);
}
else
{
return new SqlString(Regex.Replace(input, pattern, replacement));
}

return new SqlString(Regex.Replace(input, pattern, replacement));
}

[SqlFunction(DataAccess = DataAccessKind.None, FillRowMethodName = "FillMatches", TableDefinition = "Position int, MatchText nvarchar(max)")]
public static IEnumerable Matches(String input, String pattern)
public static IEnumerable Matches(string input, string pattern)
{
List<RegexMatch> MatchCollection = new List<RegexMatch>();
if (!String.IsNullOrEmpty(input) && !String.IsNullOrEmpty(pattern))
if (input != null && !string.IsNullOrEmpty(pattern))
{
//only run through the matches if the inputs have non-empty, non-null strings
foreach (Match m in Regex.Matches(input, pattern))
Expand All @@ -67,13 +74,13 @@ public static IEnumerable Matches(String input, String pattern)
}

[SqlFunction(DataAccess = DataAccessKind.None, FillRowMethodName = "FillMatches", TableDefinition = "Position int, MatchText nvarchar(max)")]
public static IEnumerable Split(String input, String pattern)
public static IEnumerable Split(string input, string pattern)
{
List<RegexMatch> MatchCollection = new List<RegexMatch>();
if (!String.IsNullOrEmpty(input) && !String.IsNullOrEmpty(pattern))
if (input != null && !string.IsNullOrEmpty(pattern))
{
//only run through the splits if the inputs have non-empty, non-null strings
String[] splits = Regex.Split(input, pattern);
string[] splits = Regex.Split(input, pattern);
for (int i = 0; i < splits.Length; i++)
{
MatchCollection.Add(new RegexMatch(i, splits[i]));
Expand Down
2 changes: 1 addition & 1 deletion src/sqlregex.sqlproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<OutputType>Database</OutputType>
<NoStandardLibraries>false</NoStandardLibraries>
<AssemblyName>sql-server-regex</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<ConnectionString>
</ConnectionString>
<TargetFrameworkProfile />
Expand Down