You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Lidarr/src/NzbDrone.Core/Datastore/Migration/Framework/SqliteSyntaxReader.cs

272 lines
7.1 KiB

using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
namespace NzbDrone.Core.Datastore.Migration.Framework
{
public class SqliteSyntaxReader
{
public string Buffer { get; private set; }
public int Index { get; private set; }
private int _previousIndex;
public TokenType Type { get; private set; }
public string Value { get; private set; }
public string ValueToUpper => Value.ToUpperInvariant();
public bool IsEndOfFile => Index >= Buffer.Length;
public enum TokenType
{
Start,
Whitespace,
End,
ListStart,
ListSeparator,
ListEnd,
Identifier,
StringToken,
StringLiteral,
UnknownSymbol
}
public SqliteSyntaxReader(string sql)
{
Buffer = sql;
}
public void TrimBuffer()
{
Buffer = Buffer.Substring(Index);
Index = 0;
_previousIndex = 0;
}
public void SkipWhitespace()
{
while (!IsEndOfFile && char.IsWhiteSpace(Buffer[Index]))
{
Index++;
}
}
public void SkipTillToken(TokenType tokenType)
{
if (IsEndOfFile)
{
return;
}
while (Read() != tokenType)
{
if (Type == TokenType.ListStart)
{
SkipTillToken(TokenType.ListEnd);
}
}
}
public void Rollback()
{
Index = _previousIndex;
Type = TokenType.Whitespace;
}
public TokenType Read()
{
if (!IsEndOfFile && char.IsWhiteSpace(Buffer[Index]))
{
Type = TokenType.Whitespace;
SkipWhitespace();
}
_previousIndex = Index;
if (IsEndOfFile)
{
Type = TokenType.End;
return Type;
}
if (Buffer[Index] == '(')
{
Type = TokenType.ListStart;
Value = null;
Index++;
return Type;
}
if (Buffer[Index] == ',')
{
Type = TokenType.ListSeparator;
Value = null;
Index++;
return Type;
}
if (Buffer[Index] == ')')
{
Type = TokenType.ListEnd;
Value = null;
Index++;
return Type;
}
if (Buffer[Index] == '\'')
{
Type = TokenType.StringLiteral;
Value = ReadEscapedString('\'');
return Type;
}
if (Buffer[Index] == '\"')
{
Type = TokenType.Identifier;
Value = ReadEscapedString('\"');
return Type;
}
if (Buffer[Index] == '`')
{
Type = TokenType.Identifier;
Value = ReadEscapedString('`');
return Type;
}
if (Buffer[Index] == '[')
{
Type = TokenType.Identifier;
Value = ReadTerminatedString(']');
return Type;
}
if (Type == TokenType.UnknownSymbol)
{
Value = Buffer[Index].ToString();
Index++;
return Type;
}
if (char.IsLetter(Buffer[Index]))
{
var start = Index;
var end = start + 1;
while (end < Buffer.Length && (char.IsLetter(Buffer[end]) || char.IsNumber(Buffer[end]) || Buffer[end] == '_'))
{
end++;
}
if (end >= Buffer.Length || Buffer[end] == ',' || Buffer[end] == '(' || Buffer[end] == ')' || char.IsWhiteSpace(Buffer[end]))
{
Index = end;
}
else if (Type == TokenType.UnknownSymbol)
{
Value = Buffer[Index].ToString();
Index++;
return Type;
}
else
{
throw CreateSyntaxException("Unexpected sequence.");
}
Type = TokenType.StringToken;
Value = Buffer.Substring(start, end - start);
return Type;
}
Type = TokenType.UnknownSymbol;
Value = Buffer[Index].ToString();
Index++;
return Type;
}
public List<SqliteSyntaxReader> ReadList()
{
if (Type != TokenType.ListStart)
{
throw new InvalidOperationException();
}
var result = new List<SqliteSyntaxReader>();
var start = Index;
while (Read() != TokenType.ListEnd)
{
if (Type == TokenType.End)
{
throw CreateSyntaxException("Expected ListEnd first");
}
if (Type == TokenType.ListStart)
{
SkipTillToken(TokenType.ListEnd);
}
else if (Type == TokenType.ListSeparator)
{
result.Add(new SqliteSyntaxReader(Buffer.Substring(start, Index - start - 1)));
start = Index;
}
}
if (Index >= start + 1)
{
result.Add(new SqliteSyntaxReader(Buffer.Substring(start, Index - start - 1)));
}
return result;
}
protected string ReadTerminatedString(char terminator)
{
var start = Index + 1;
var end = Buffer.IndexOf(terminator, Index);
if (end == -1)
{
throw new SyntaxErrorException();
}
Index = end + 1;
return Buffer.Substring(start, end - start);
}
protected string ReadEscapedString(char escape)
{
var identifier = new StringBuilder();
while (true)
{
var start = Index + 1;
var end = Buffer.IndexOf(escape, start);
if (end == -1)
{
throw new SyntaxErrorException();
}
Index = end + 1;
identifier.Append(Buffer.AsSpan(start, end - start));
if (Buffer[Index] != escape)
{
break;
}
identifier.Append(escape);
}
return identifier.ToString();
}
public SyntaxErrorException CreateSyntaxException(string message, params object[] args)
{
return new SyntaxErrorException(string.Format("{0}. Syntax Error near: {1}", string.Format(message, args), Buffer.Substring(_previousIndex)));
}
}
}