Skip to content

Commit

Permalink
fix: parse WITH command as SELECT
Browse files Browse the repository at this point in the history
Parses WITH statements as queries and allows other whitespace
characters than only ' '.

Fixes #5857
  • Loading branch information
olavloite authored and amanda-tarafa committed Jan 21, 2021
1 parent 91fa8df commit 0343ff8
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,92 @@ public void DropDatabaseUnrecognized(string ddlString)
var builder = SpannerCommandTextBuilder.FromCommandText(ddlString);
Assert.False(builder.IsDropDatabaseCommand);
}

// https://cloud.google.com/spanner/docs/query-syntax
[Theory]
[InlineData("SELECT * FROM Albums")]
[InlineData(" SELECT * FROM Albums")]
[InlineData("\t\nSELECT * FROM Albums")]
[InlineData("SELECT * FROM Albums")]
[InlineData("SELECT\t\n* FROM Albums")]
[InlineData("WITH AlbumsView AS (SELECT * FROM Albums) select Title from AlbumsView")]
[InlineData(" WITH AlbumsView AS (SELECT * FROM Albums) select Title from AlbumsView")]
[InlineData("\t\nWITH AlbumsView AS (SELECT * FROM Albums) select Title from AlbumsView")]
[InlineData(" WITH AlbumsView AS (SELECT * FROM Albums) select Title from AlbumsView")]
[InlineData("WITH\t\nAlbumsView AS (SELECT * FROM Albums) select Title from AlbumsView")]
public void SelectCommand(string commandText)
{
var builder = SpannerCommandTextBuilder.FromCommandText(commandText);
Assert.Equal(SpannerCommandType.Select, builder.SpannerCommandType);
}

// https://cloud.google.com/spanner/docs/dml-syntax#insert-statement
[Theory]
[InlineData("INSERT INTO Albums (AlbumId) VALUES (@id)")]
[InlineData(" INSERT INTO Albums (AlbumId) VALUES (@id)")]
[InlineData("\t\nINSERT\t\nINTO\t\nAlbums\t\n(AlbumId)t\nVALUESt\n(@id)")]
[InlineData("INSERT INTO Albums (AlbumId) VALUES (@id)")]
[InlineData("INSERT\t\nINTO\t\nAlbums\t\n(AlbumId)t\nVALUESt\n(@id)")]
[InlineData("INSERT Albums (AlbumId) VALUES (@id)")]
[InlineData(" INSERT Albums (AlbumId) VALUES (@id)")]
[InlineData("\t\nINSERT\t\nAlbums\t\n(AlbumId)t\nVALUESt\n(@id)")]
[InlineData("INSERT Albums (AlbumId) VALUES (@id)")]
[InlineData("INSERT\t\nAlbums\t\n(AlbumId)t\nVALUESt\n(@id)")]
public void DmlInsertCommand(string commandText)
{
var builder = SpannerCommandTextBuilder.FromCommandText(commandText);
Assert.Equal(SpannerCommandType.Dml, builder.SpannerCommandType);
}

// https://cloud.google.com/spanner/docs/dml-syntax#update-statement
[Theory]
[InlineData("UPDATE Albums SET Title=@title WHERE TRUE")]
[InlineData(" UPDATE Albums SET Title=@title WHERE TRUE")]
[InlineData("\t\nUPDATE\t\nAlbumst\nSETt\nTitle=@titlet\nWHEREt\nTRUE")]
[InlineData("UPDATE Albums SET Title=@title WHERE TRUE")]
[InlineData("UPDATE\t\nAlbums\t\nSETt\nTitle=@titlet\nWHEREt\nTRUE")]
public void DmlUpdateCommand(string commandText)
{
var builder = SpannerCommandTextBuilder.FromCommandText(commandText);
Assert.Equal(SpannerCommandType.Dml, builder.SpannerCommandType);
}

// https://cloud.google.com/spanner/docs/dml-syntax#delete-statement
[Theory]
[InlineData("DELETE FROM Albums WHERE TRUE")]
[InlineData(" DELETE FROM Albums WHERE TRUE")]
[InlineData("\t\nDELETE FROM Albums WHERE TRUE")]
[InlineData("DELETE FROM Albums WHERE TRUE")]
[InlineData("DELETE\t\nFROM\t\nAlbumst\nWHEREt\nTRUE")]
[InlineData("DELETE Albums WHERE TRUE")]
[InlineData(" DELETE Albums WHERE TRUE")]
[InlineData("\t\nDELETE\t\nAlbumst\nWHEREt\nTRUE")]
[InlineData("DELETE Albums WHERE TRUE")]
[InlineData("DELETE\t\nAlbumst\nWHEREt\nTRUE")]
public void DmlDeleteCommand(string commandText)
{
var builder = SpannerCommandTextBuilder.FromCommandText(commandText);
Assert.Equal(SpannerCommandType.Dml, builder.SpannerCommandType);
}

// https://cloud.google.com/spanner/docs/data-definition-language
[Theory]
[InlineData("CREATE TABLE FOO")]
[InlineData(" CREATE TABLE FOO")]
[InlineData("CREATE\t\nTABLE\t\nFOO")]
[InlineData("\t\nCREATE\t\nTABLE\t\nFOO")]
[InlineData("DROP TABLE FOO")]
[InlineData(" DROP TABLE FOO")]
[InlineData("DROP\t\nTABLE\t\nFOO")]
[InlineData("\t\nDROP\t\nTABLE\t\nFOO")]
[InlineData("ALTER TABLE FOO")]
[InlineData(" ALTER TABLE FOO")]
[InlineData("ALTER\t\nTABLE\t\nFOO")]
[InlineData("\t\nALTER\t\nTABLE\t\nFOO")]
public void DdlCommand(string commandText)
{
var builder = SpannerCommandTextBuilder.FromCommandText(commandText);
Assert.Equal(SpannerCommandType.Ddl, builder.SpannerCommandType);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public sealed class SpannerCommandTextBuilder
private const string UpdateCommand = "UPDATE";
private const string DeleteCommand = "DELETE";
private const string SelectCommand = "SELECT";
private const string WithCommand = "WITH"; // Queries may also start with a WITH clause.
private const string AlterCommand = "ALTER";
private const string CreateCommand = "CREATE";
private const string DropCommand = "DROP";
Expand All @@ -45,6 +46,7 @@ public sealed class SpannerCommandTextBuilder
{ UpdateCommand, SpannerCommandType.Update },
{ DeleteCommand, SpannerCommandType.Delete },
{ SelectCommand, SpannerCommandType.Select },
{ WithCommand, SpannerCommandType.Select },
// These three form the ddl for spanner.
// For reference: https://cloud.google.com/spanner/docs/data-definition-language
{ AlterCommand, SpannerCommandType.Ddl },
Expand Down Expand Up @@ -217,7 +219,8 @@ public static SpannerCommandTextBuilder FromCommandText(string commandText)
{
GaxPreconditions.CheckNotNullOrEmpty(commandText, nameof(commandText));
commandText = commandText.Trim();
var commandSections = commandText.Split(' ');
// Split(new char[0]) splits the string using all whitespace characters.
var commandSections = commandText.Split(new char[0], StringSplitOptions.RemoveEmptyEntries);
if (commandSections.Length < 2)
{
throw new ArgumentException($"'{commandText}' is not a recognized Spanner command.", nameof(commandText));
Expand Down

0 comments on commit 0343ff8

Please sign in to comment.