Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QP-7117] OracleScriptParser가 Oracle 규격에 맞게 Identifier를 파싱하도록 수정 #70

Merged
merged 1 commit into from
Feb 29, 2024
Merged
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
34 changes: 34 additions & 0 deletions Qsi.Oracle/OracleLookbehindUnknownKeywordRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Qsi.Parsing.Common;
using Qsi.Parsing.Common.Rules;

namespace Qsi.Oracle;

public class OracleLookbehindUnknownKeywordRule : ITokenRule
{
public bool Run(CommonScriptCursor cursor)
{
for (int i = cursor.Index; i < cursor.Length; i++)
{
var c = cursor.Value[i];

if (IsOracleIdentifier(c))
continue;

cursor.Index = i - 1;
return true;
}

cursor.Index = cursor.Length - 1;
return true;
}

public static bool IsOracleIdentifier(char ch)
{
return ch is >= 'a' and <= 'z' || // a-z
ch is >= 'A' and <= 'Z' || // A-Z
ch == '_' || // _
ch == '$' || // $
ch == '#' || // #
ch >= '\u0080'; // utf8
}
}
33 changes: 30 additions & 3 deletions Qsi.Oracle/OracleScriptParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using Qsi.Data;
using Qsi.Parsing.Common;
using Qsi.Parsing.Common.Rules;
using Qsi.Shared.Extensions;

namespace Qsi.Oracle;
Expand Down Expand Up @@ -36,6 +37,8 @@ public sealed class OracleScriptParser : CommonScriptParser

private const string BlockKey = "Oracle::Type";

private readonly ITokenRule _oracleKeyword = new OracleLookbehindUnknownKeywordRule();

public OracleScriptParser()
{
EnablePoundComment = false;
Expand All @@ -53,9 +56,33 @@ protected override QsiScriptType GetSuitableType(CommonScriptCursor cursor, IEnu

protected override bool TryParseToken(CommonScriptCursor cursor, out Token token)
{
if (cursor.Current == ';')
int offset;
TokenType tokenType;
ITokenRule rule;

switch (cursor.Current)
{
case ';':
token = new Token(TokenType.Fragment, new Range(cursor.Index, cursor.Index + 1));
return true;

case var c when OracleLookbehindUnknownKeywordRule.IsOracleIdentifier(c):
offset = 1;
rule = _oracleKeyword;
tokenType = TokenType.Keyword;
break;

default:
return base.TryParseToken(cursor, out token);
}

int start = cursor.Index;
cursor.Index += offset;

if (rule.Run(cursor))
{
token = new Token(TokenType.Fragment, new Range(cursor.Index, cursor.Index + 1));
token = new Token(tokenType, start..(cursor.Index + 1));

return true;
}

Expand Down Expand Up @@ -358,4 +385,4 @@ public void Dispose()
_context = null;
}
}
}
}
81 changes: 81 additions & 0 deletions Qsi.Tests/Vendor/Oracle/OracleScriptParserTest.Data.ParseTokens.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using NUnit.Framework;

namespace Qsi.Tests.Oracle;

public partial class OracleScriptParserTest
{
public static TestCaseData[] ParseTokens_TestDatas()
{
return new TestCaseData[]
{
new("SELECT * FROM C##TEST.TEST_TABLE")
{
ExpectedResult = new[] { "SELECT", "*", "FROM", "C##TEST", ".", "TEST_TABLE" }
},
new("SELECT * FROM 한글테이블")
{
ExpectedResult = new[] { "SELECT", "*", "FROM", "한글테이블" }
},
new("SELECT * FROM 한글_테_이블")
{
ExpectedResult = new[] { "SELECT", "*", "FROM", "한글_테_이블" }
},
new("SELECT * FROM C##TEST.TEST_TABLE WHERE ID = 1")
{
ExpectedResult = new[] { "SELECT", "*", "FROM", "C##TEST", ".", "TEST_TABLE", "WHERE", "ID", "=", "1" }
},
new("SELECT * FROM C##TEST.TEST_TABLE WHERE ID = 1 AND NAME = 'TEST'")
{
ExpectedResult = new[] { "SELECT", "*", "FROM", "C##TEST", ".", "TEST_TABLE", "WHERE", "ID", "=", "1", "AND", "NAME", "=", "'TEST'" }
},
new("SELECT * FROM C##TEST.TEST_TABLE WHERE ID = 1 AND NAME = 'TEST' AND AGE = 20")
{
ExpectedResult = new[] { "SELECT", "*", "FROM", "C##TEST", ".", "TEST_TABLE", "WHERE", "ID", "=", "1", "AND", "NAME", "=", "'TEST'", "AND", "AGE", "=", "20" }
},
new("ALTER SESSION SET CURRENT_SCHEMA = C##TEST")
{
ExpectedResult = new[] { "ALTER", "SESSION", "SET", "CURRENT_SCHEMA", "=", "C##TEST" }
},
new("ALTER SESSION SET CURRENT_SCHEMA = \"C##TEST\"")
{
ExpectedResult = new[] { "ALTER", "SESSION", "SET", "CURRENT_SCHEMA", "=", "\"C##TEST\"" }
},
new("ALTER SESSION SET CURRENT_SCHEMA = \"C##\"\"TEST\"")
{
ExpectedResult = new[] { "ALTER", "SESSION", "SET", "CURRENT_SCHEMA", "=", "\"C##\"\"TEST\"" }
},
new("ALTER SESSION SET CURRENT_SCHEMA = C###")
{
ExpectedResult = new[] { "ALTER", "SESSION", "SET", "CURRENT_SCHEMA", "=", "C###" }
},
new("ALTER SESSION SET CURRENT_SCHEMA = C##$")
{
ExpectedResult = new[] { "ALTER", "SESSION", "SET", "CURRENT_SCHEMA", "=", "C##$" }
},
new("ALTER SESSION SET CURRENT_SCHEMA = C####")
{
ExpectedResult = new[] { "ALTER", "SESSION", "SET", "CURRENT_SCHEMA", "=", "C####" }
},
new("ALTER SESSION SET CURRENT_SCHEMA = C##__")
{
ExpectedResult = new[] { "ALTER", "SESSION", "SET", "CURRENT_SCHEMA", "=", "C##__" }
},
new("CREATE USER c## IDENTIFIED BY querypie")
{
ExpectedResult = new[] { "CREATE", "USER", "c##", "IDENTIFIED", "BY", "querypie" }
},
new("CREATE USER c##$ IDENTIFIED BY querypie")
{
ExpectedResult = new[] { "CREATE", "USER", "c##$", "IDENTIFIED", "BY", "querypie" }
},
new("CREATE USER c###$ IDENTIFIED BY querypie")
{
ExpectedResult = new[] { "CREATE", "USER", "c###$", "IDENTIFIED", "BY", "querypie" }
},
new("CREATE USER c##test IDENTIFIED BY querypie")
{
ExpectedResult = new[] { "CREATE", "USER", "c##test", "IDENTIFIED", "BY", "querypie" }
}
};
}
}
13 changes: 13 additions & 0 deletions Qsi.Tests/Vendor/Oracle/OracleScriptParserTest.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Linq;
using NUnit.Framework;
using Qsi.Oracle;
using Qsi.Parsing.Common;
using static Qsi.Parsing.Common.CommonScriptParser;

namespace Qsi.Tests.Oracle;

Expand All @@ -15,4 +17,15 @@ public string[] Test_Parse(string sql)
.Select(x => x.Script)
.ToArray();
}

[TestCaseSource(nameof(ParseTokens_TestDatas))]
public string[] Test_ParseTokens(string sql)
{
var parser = new OracleScriptParser();

return parser.ParseTokens(new CommonScriptCursor(sql))
.Where(t => t.Type is not (TokenType.WhiteSpace or TokenType.SingeLineComment or TokenType.MultiLineComment))
.Select(x => sql[x.Span])
.ToArray();
}
}
Loading