diff --git a/src/SqlParser.Tests/CoverageTests.cs b/src/SqlParser.Tests/CoverageTests.cs index ae0476b..fe6f9a0 100644 --- a/src/SqlParser.Tests/CoverageTests.cs +++ b/src/SqlParser.Tests/CoverageTests.cs @@ -63,6 +63,7 @@ public void Top_Renders_Sql() { //ar top = new Top(new Expression.LiteralValue(new Value.Number("1")), false, false); var top = new Top(new TopQuantity.TopExpression(new Expression.LiteralValue(new Value.Number("1"))), false, false); + Assert.Equal("TOP (1)", top.ToSql()); } diff --git a/src/SqlParser.Tests/Dialects/BigQueryDialectTests.cs b/src/SqlParser.Tests/Dialects/BigQueryDialectTests.cs index 647812f..2de3c1d 100644 --- a/src/SqlParser.Tests/Dialects/BigQueryDialectTests.cs +++ b/src/SqlParser.Tests/Dialects/BigQueryDialectTests.cs @@ -195,7 +195,7 @@ public void Parse_Join_Constraint_Unnest_Alias() { new (new TableFactor.UnNest([new CompoundIdentifier(new Ident[] { "t1", "a" })]) { - Alias = new TableAlias("f"), + Alias = new TableAlias("f", true), }, new JoinOperator.Inner(new JoinConstraint.On( new BinaryOp( @@ -442,12 +442,12 @@ public void Parse_Typeless_Struct_Syntax() Assert.Equal(expected, select.Projection.Skip(2).First().AsExpr()); expected = new Struct([ - new Named(new LiteralValue(new Value.Number("1")), "a"), - new Named(new LiteralValue(new Value.SingleQuotedString("abc")), "b") + new Named(new LiteralValue(new Value.Number("1")), "a", true), + new Named(new LiteralValue(new Value.SingleQuotedString("abc")), "b", true) ], []); Assert.Equal(expected, select.Projection.Skip(3).First().AsExpr()); - expected = new Struct([new Named(new Identifier("str_col"), "abc")], []); + expected = new Struct([new Named(new Identifier("str_col"), "abc", true)], []); Assert.Equal(expected, select.Projection.Skip(4).First().AsExpr()); } @@ -769,7 +769,7 @@ CREATE VIEW myproject.mydataset.newview var create = VerifiedStatement(sql); var columns = new Sequence { - new("name"), + new("name"), new("age", Options: [new ColumnOption.Options([new SqlOption.KeyValue("description", new LiteralValue(new Value.DoubleQuotedString("field age")))])]) }; Assert.Equal(new ObjectName(["myproject", "mydataset", "newview"]), create.Name); diff --git a/src/SqlParser.Tests/Dialects/HiveDialectTests.cs b/src/SqlParser.Tests/Dialects/HiveDialectTests.cs index 45fc544..3df8d2f 100644 --- a/src/SqlParser.Tests/Dialects/HiveDialectTests.cs +++ b/src/SqlParser.Tests/Dialects/HiveDialectTests.cs @@ -244,7 +244,7 @@ public void Parse_Create_Function() [Fact] public void Filter_As_Alias() { - OneStatementParsesTo("SELECT name filter FROM region", "SELECT name AS filter FROM region"); + OneStatementParsesTo("SELECT name filter FROM region", "SELECT name filter FROM region"); } [Fact] diff --git a/src/SqlParser.Tests/Dialects/MsSqlDialectTests.cs b/src/SqlParser.Tests/Dialects/MsSqlDialectTests.cs index 4e94e23..1461da8 100644 --- a/src/SqlParser.Tests/Dialects/MsSqlDialectTests.cs +++ b/src/SqlParser.Tests/Dialects/MsSqlDialectTests.cs @@ -30,7 +30,7 @@ public void Parse_MsSql_Single_Quoted_Identifiers() { DefaultDialects = [new MsSqlDialect(), new GenericDialect()]; - OneStatementParsesTo("SELECT foo 'alias'", "SELECT foo AS 'alias'"); + OneStatementParsesTo("SELECT foo 'alias'", "SELECT foo 'alias'"); } [Fact] @@ -38,7 +38,7 @@ public void Parse_MsSql_Delimited_Identifiers() { OneStatementParsesTo( "SELECT [a.b!] [FROM] FROM foo [WHERE]", - "SELECT [a.b!] AS [FROM] FROM foo AS [WHERE]"); + "SELECT [a.b!] [FROM] FROM foo [WHERE]"); } [Fact] diff --git a/src/SqlParser.Tests/Dialects/MySqlDialectTests.cs b/src/SqlParser.Tests/Dialects/MySqlDialectTests.cs index d723780..3c02731 100644 --- a/src/SqlParser.Tests/Dialects/MySqlDialectTests.cs +++ b/src/SqlParser.Tests/Dialects/MySqlDialectTests.cs @@ -496,14 +496,14 @@ public void Parse_Update_With_Joins() var table = new TableWithJoins(new TableFactor.Table("orders") { - Alias = new TableAlias("o") + Alias = new TableAlias("o", true) }) { Joins = new Join[] { new(new TableFactor.Table("customers") { - Alias = new TableAlias("c") + Alias = new TableAlias("c", true) }) { JoinOperator = new JoinOperator.Inner(new JoinConstraint.On(new BinaryOp( @@ -1090,7 +1090,7 @@ public void Parse_Json_Table() new JsonTableColumnErrorHandling.Null())) ]) { - Alias = new TableAlias("t") + Alias = new TableAlias("t", true) }; Assert.Equal(expected, joinTable.From![0].Relation); @@ -1114,7 +1114,7 @@ public void Parse_Create_Table_With_Column_Collate() [Fact] public void Parse_Lock_Tables() { - OneStatementParsesTo("LOCK TABLES trans t READ, customer WRITE", "LOCK TABLES trans AS t READ, customer WRITE"); + OneStatementParsesTo("LOCK TABLES trans t READ, customer WRITE", "LOCK TABLES trans t READ, customer WRITE"); //VerifiedStatement("LOCK TABLES trans AS t READ, customer WRITE"); //VerifiedStatement("LOCK TABLES trans AS t READ LOCAL, customer WRITE"); diff --git a/src/SqlParser.Tests/Dialects/PostgresDialectTests.cs b/src/SqlParser.Tests/Dialects/PostgresDialectTests.cs index 19b1eb3..c0a140f 100644 --- a/src/SqlParser.Tests/Dialects/PostgresDialectTests.cs +++ b/src/SqlParser.Tests/Dialects/PostgresDialectTests.cs @@ -1086,8 +1086,8 @@ public void Parse_Pg_Returning() var expected = new SelectItem[] { - new SelectItem.ExpressionWithAlias(new Identifier("temp_lo"), "lo"), - new SelectItem.ExpressionWithAlias(new Identifier("temp_hi"), "hi"), + new SelectItem.ExpressionWithAlias(new Identifier("temp_lo"), "lo", true), + new SelectItem.ExpressionWithAlias(new Identifier("temp_hi"), "hi", true), new SelectItem.UnnamedExpression(new Identifier("prcp")) }; @@ -1792,7 +1792,7 @@ public void Parse_Delimited_Identifiers() var table = new TableFactor.Table(new ObjectName(new Ident("a table", Symbols.DoubleQuote))) { - Alias = new TableAlias(new Ident("alias", Symbols.DoubleQuote)) + Alias = new TableAlias(new Ident("alias", Symbols.DoubleQuote), true) }; Assert.Equal(table, select.From!.Single().Relation); @@ -1809,7 +1809,7 @@ public void Parse_Delimited_Identifiers() Assert.Equal(new SelectItem.ExpressionWithAlias(new Identifier( new Ident("simple id", Symbols.DoubleQuote)), - new Ident("column alias", Symbols.DoubleQuote)), + new Ident("column alias", Symbols.DoubleQuote), true), select.Projection[2]); VerifiedStatement("CREATE TABLE \"foo\" (\"bar\" \"int\")"); @@ -1921,7 +1921,7 @@ public void Parse_Dollar_Quoted_String() Assert.Equal(new LiteralValue(new Value.DollarQuotedString(new DollarQuotedStringValue("hello"))), projection[0].AsExpr()); Assert.Equal(new LiteralValue(new Value.DollarQuotedString(new DollarQuotedStringValue("world", "tag_name"))), projection[1].AsExpr()); Assert.Equal(new LiteralValue(new Value.DollarQuotedString(new DollarQuotedStringValue("Foo$Bar"))), projection[2].AsExpr()); - var expr = new SelectItem.ExpressionWithAlias(new LiteralValue(new Value.DollarQuotedString(new DollarQuotedStringValue("Foo$Bar"))), "col_name"); + var expr = new SelectItem.ExpressionWithAlias(new LiteralValue(new Value.DollarQuotedString(new DollarQuotedStringValue("Foo$Bar"))), "col_name", false); Assert.Equal(expr, projection[3]); Assert.Equal(new LiteralValue(new Value.DollarQuotedString(new DollarQuotedStringValue(""))), projection[4].AsExpr()); Assert.Equal(new LiteralValue(new Value.DollarQuotedString(new DollarQuotedStringValue("", "tag_name"))), projection[5].AsExpr()); @@ -2085,7 +2085,7 @@ public void Parse_Join_Constraint_Unnest_Alias() { new (new TableFactor.UnNest([new CompoundIdentifier(new Ident[] { "t1", "a" })]) { - Alias = new TableAlias("f"), + Alias = new TableAlias("f", true), }, new JoinOperator.Inner(new JoinConstraint.On( new BinaryOp( diff --git a/src/SqlParser.Tests/Dialects/RedshiftDialectTests.cs b/src/SqlParser.Tests/Dialects/RedshiftDialectTests.cs index 023e359..70b34a4 100644 --- a/src/SqlParser.Tests/Dialects/RedshiftDialectTests.cs +++ b/src/SqlParser.Tests/Dialects/RedshiftDialectTests.cs @@ -56,7 +56,7 @@ public void Parse_Delimited_Identifiers() var table = new TableFactor.Table(new ObjectName(new Ident("a table", Symbols.DoubleQuote))) { - Alias = new TableAlias(new Ident("alias", Symbols.DoubleQuote)) + Alias = new TableAlias(new Ident("alias", Symbols.DoubleQuote), true) }; Assert.Equal(table, select.From!.Single().Relation); diff --git a/src/SqlParser.Tests/Dialects/SnowflakeDialectTests.cs b/src/SqlParser.Tests/Dialects/SnowflakeDialectTests.cs index b543bac..da97bb4 100644 --- a/src/SqlParser.Tests/Dialects/SnowflakeDialectTests.cs +++ b/src/SqlParser.Tests/Dialects/SnowflakeDialectTests.cs @@ -104,25 +104,25 @@ public void Test_Single_Table_In_Parenthesis_With_Alias() OneStatementParsesTo( "SELECT * FROM (a NATURAL JOIN (b) c )", - "SELECT * FROM (a NATURAL JOIN b AS c)"); + "SELECT * FROM (a NATURAL JOIN b c)"); OneStatementParsesTo( "SELECT * FROM (a NATURAL JOIN ((b)) c )", - "SELECT * FROM (a NATURAL JOIN b AS c)"); + "SELECT * FROM (a NATURAL JOIN b c)"); OneStatementParsesTo( "SELECT * FROM (a NATURAL JOIN ( (b) c ) )", - "SELECT * FROM (a NATURAL JOIN b AS c)"); + "SELECT * FROM (a NATURAL JOIN b c)"); OneStatementParsesTo( "SELECT * FROM (a NATURAL JOIN ( (b) as c ) )", "SELECT * FROM (a NATURAL JOIN b AS c)"); OneStatementParsesTo( "SELECT * FROM (a alias1 NATURAL JOIN ( (b) c ) )", - "SELECT * FROM (a AS alias1 NATURAL JOIN b AS c)"); + "SELECT * FROM (a alias1 NATURAL JOIN b c)"); OneStatementParsesTo( "SELECT * FROM (a as alias1 NATURAL JOIN ( (b) as c ) )", "SELECT * FROM (a AS alias1 NATURAL JOIN b AS c)"); OneStatementParsesTo( "SELECT * FROM (a NATURAL JOIN b) c", - "SELECT * FROM (a NATURAL JOIN b) AS c"); + "SELECT * FROM (a NATURAL JOIN b) c"); DefaultDialects = [new SnowflakeDialect()]; var ex = Assert.Throws(() => ParseSqlStatements("SELECT * FROM (a b) c")); @@ -259,7 +259,7 @@ public void Parse_Delimited_Identifiers() var table = new TableFactor.Table(new ObjectName(new Ident("a table", Symbols.DoubleQuote))) { - Alias = new TableAlias(new Ident("alias", Symbols.DoubleQuote)) + Alias = new TableAlias(new Ident("alias", Symbols.DoubleQuote), true) }; Assert.Equal(table, select.From!.Single().Relation); @@ -1083,14 +1083,14 @@ ASOF JOIN quotes_unixtime AS qu var expected = new TableWithJoins(new TableFactor.Table("trades_unixtime") { - Alias = new TableAlias("tu") + Alias = new TableAlias("tu", true) }) { Joins = [ new Join(new TableFactor.Table("quotes_unixtime") { - Alias = new TableAlias("qu") + Alias = new TableAlias("qu", true) }, new JoinOperator.AsOf(new BinaryOp( new CompoundIdentifier(["tu", "trade_time"]), diff --git a/src/SqlParser.Tests/ParserCommonTests.cs b/src/SqlParser.Tests/ParserCommonTests.cs index 3b39e4b..89c0012 100644 --- a/src/SqlParser.Tests/ParserCommonTests.cs +++ b/src/SqlParser.Tests/ParserCommonTests.cs @@ -171,7 +171,7 @@ public void Parse_Update_Set_From() var subQuery = new Query(body); var derived = new TableFactor.Derived(subQuery) { - Alias = new TableAlias("t2") + Alias = new TableAlias("t2", true) }; var from = new TableWithJoins(derived); @@ -192,7 +192,7 @@ public void Parse_Update_With_Table_Alias() var statement = VerifiedStatement(sql); var table = new TableWithJoins(new TableFactor.Table("users") { - Alias = new TableAlias("u") + Alias = new TableAlias("u", true) }); var assignment = new Statement.Assignment( new AssignmentTarget.ColumnName(new ObjectName(["u", "username"])), @@ -259,8 +259,8 @@ public void Parse_Where_Delete_With_Alias_Statement() { var delete = VerifiedStatement("DELETE FROM basket AS a USING basket AS b WHERE a.id < b.id"); - var table = new TableFactor.Table("basket") { Alias = new TableAlias("a") }; - var @using = new TableFactor.Table("basket") { Alias = new TableAlias("b") }; + var table = new TableFactor.Table("basket") { Alias = new TableAlias("a", true) }; + var @using = new TableFactor.Table("basket") { Alias = new TableAlias("b", true) }; var binaryOp = new BinaryOp( new CompoundIdentifier(new Ident[] { "a", "id" }), BinaryOperator.Lt, @@ -539,8 +539,8 @@ public void Parse_Exponent_In_Select() Assert.Equal(new SelectItem.UnnamedExpression(new LiteralValue(Number("10e-20"))), select.Projection[0]); Assert.Equal(new SelectItem.UnnamedExpression(new LiteralValue(Number("1e3"))), select.Projection[1]); Assert.Equal(new SelectItem.UnnamedExpression(new LiteralValue(Number("1e+3"))), select.Projection[2]); - Assert.Equal(new SelectItem.ExpressionWithAlias(new LiteralValue(Number("1e3")), "a"), select.Projection[3]); - Assert.Equal(new SelectItem.ExpressionWithAlias(new LiteralValue(Number("1")), "e"), select.Projection[4]); + Assert.Equal(new SelectItem.ExpressionWithAlias(new LiteralValue(Number("1e3")), "a", false), select.Projection[3]); + Assert.Equal(new SelectItem.ExpressionWithAlias(new LiteralValue(Number("1")), "e", false), select.Projection[4]); Assert.Equal(new SelectItem.UnnamedExpression(new LiteralValue(Number("0.5e2"))), select.Projection[5]); } @@ -1319,7 +1319,7 @@ public void Parse_Cast() select = VerifiedOnlySelect("SELECT CAST(id AS VARBINARY(50)) FROM customer"); expected = new Cast(new Identifier("id"), new Varbinary(new BinaryLength.IntegerLength(50)), CastKind.Cast); Assert.Equal(expected, select.Projection.Single().AsExpr()); - + select = VerifiedOnlySelect("SELECT CAST(id AS VARBINARY(MAX)) FROM customer"); expected = new Cast(new Identifier("id"), new Varbinary(new BinaryLength.Max()), CastKind.Cast); Assert.Equal(expected, select.Projection.Single().AsExpr()); @@ -3084,7 +3084,7 @@ public void Parse_Unnest() [ new TableWithJoins(new TableFactor.UnNest([new Identifier("expr")]) { - Alias = new TableAlias("numbers"), + Alias = new TableAlias("numbers", true), WithOffset = true }) ]); @@ -3109,7 +3109,7 @@ public void Parse_Unnest() [ new TableWithJoins(new TableFactor.UnNest([new Identifier("expr")]) { - Alias = new TableAlias("numbers"), + Alias = new TableAlias("numbers", true), }) ]); return; @@ -3346,13 +3346,13 @@ static Join Test(string relation, TableAlias? alias, Func new JoinOperator.Inner(jc)); + var expected = Test("t2", new TableAlias("foo", true), jc => new JoinOperator.Inner(jc)); var actual = VerifiedOnlySelect("SELECT * FROM t1 JOIN t2 AS foo USING(c1)").From!.Single().Joins; Assert.Equal(new[] { expected }, actual!); OneStatementParsesTo( "SELECT * FROM t1 JOIN t2 foo USING(c1)", - "SELECT * FROM t1 JOIN t2 AS foo USING(c1)" + "SELECT * FROM t1 JOIN t2 foo USING(c1)" ); // Test parsing of different join operators @@ -3422,7 +3422,7 @@ public void Parse_Natural_Join() // natural join another table with alias - expected = Test(jc => new JoinOperator.Inner(jc), new TableAlias("t3")); + expected = Test(jc => new JoinOperator.Inner(jc), new TableAlias("t3", true)); actual = VerifiedOnlySelect("SELECT * FROM t1 NATURAL JOIN t2 AS t3").From!.Single().Joins; Assert.Equal(new[] { expected }, actual!); @@ -3613,7 +3613,7 @@ public void Parse_Recursive_Cte() var cteQuery = VerifiedQuery(cteSql); var query = VerifiedQuery(sql); - var expected = new CommonTableExpression(new TableAlias("nums") + var expected = new CommonTableExpression(new TableAlias("nums", true) { Columns = new Ident[] { @@ -3640,7 +3640,7 @@ public void Parse_Derived_Tables() { TableWithJoins = new TableWithJoins(new TableFactor.Derived(VerifiedQuery("(SELECT 1) UNION (SELECT 2)")) { - Alias = new TableAlias("t1") + Alias = new TableAlias("t1", true) }) { Joins = new Join[] @@ -4540,7 +4540,7 @@ WHEN MATCHED THEN DELETE Assert.False(mergeNoInto.Into); Assert.Equal( - new TableFactor.Table(new ObjectName(["s", "bar"])) { Alias = new TableAlias("dest") }, + new TableFactor.Table(new ObjectName(["s", "bar"])) { Alias = new TableAlias("dest", true) }, mergeInto.Table); Assert.Equal(mergeInto.Table, mergeNoInto.Table); var body = new SetExpression.SelectExpression(new Select(new[] @@ -4556,7 +4556,7 @@ WHEN MATCHED THEN DELETE Assert.Equal( new TableFactor.Derived(new Query(body)) { - Alias = new TableAlias("stg") + Alias = new TableAlias("stg", true) }, mergeInto.Source); @@ -5216,16 +5216,16 @@ ORDER BY EMPID }; var table = new TableFactor.Table("monthly_sales") { - Alias = new TableAlias("a") + Alias = new TableAlias("a", true) }; var expected = new TableFactor.Pivot(table, functions, new Ident[] { "a", "MONTH" }, new PivotValueSource.List([ - new ExpressionWithAlias(new LiteralValue(new Value.Number("1")), "x"), - new ExpressionWithAlias(new LiteralValue(new Value.SingleQuotedString("two")), null), - new ExpressionWithAlias(new Identifier("three"), "y"), + new ExpressionWithAlias(new LiteralValue(new Value.Number("1")), "x", true), + new ExpressionWithAlias(new LiteralValue(new Value.SingleQuotedString("two")), null, true), + new ExpressionWithAlias(new Identifier("three"), "y", true), ]), null, - new TableAlias("p", new Ident[] { "c", "d" })); + new TableAlias("p", true, new Ident[] { "c", "d" })); Assert.Equal(expected, relation); Assert.Equal(sql.Replace("\r", "").Replace("\n", ""), VerifiedStatement(sql).ToSql()); @@ -5239,7 +5239,7 @@ ExpressionWithAlias ExpectedFn(string t, string? alias) new CompoundIdentifier([new Ident(t), new Ident("amount")]))) ])) }; - return new ExpressionWithAlias(expr, alias != null ? new Ident(alias) : null); + return new ExpressionWithAlias(expr, alias != null ? new Ident(alias) : null, true); } } @@ -5257,11 +5257,11 @@ public void Parse_Unpivot_Table() var table = new TableFactor.Table("sales") { - Alias = new TableAlias("s") + Alias = new TableAlias("s", true) }; var expected = new TableFactor.Unpivot(table, "quantity", "quarter", ["Q1", "Q2", "Q3", "Q4"]) { - PivotAlias = new TableAlias("u", ["product", "quarter", "quantity"]) + PivotAlias = new TableAlias("u", true, ["product", "quarter", "quantity"]) }; @@ -6716,7 +6716,7 @@ public void Parse_Drop_Type() { var drop = VerifiedStatement("DROP TYPE abc"); Assert.Equal(["abc"], drop.Names); - Assert.Equal(ObjectType.Type , drop.ObjectType); + Assert.Equal(ObjectType.Type, drop.ObjectType); drop = VerifiedStatement("DROP TYPE IF EXISTS def, magician, quaternion"); Assert.Equal(["def", "magician", "quaternion"], drop.Names); @@ -6853,7 +6853,7 @@ public void Parse_Execute_Stored_Procedure() "my_schema", "my_stored_procedure" ]), [ - + new LiteralValue(new Value.NationalStringLiteral("param1")), new LiteralValue(new Value.NationalStringLiteral("param2")) ], false, null); @@ -6864,7 +6864,7 @@ public void Parse_Execute_Stored_Procedure() execute = (Statement.Execute)OneStatementParsesTo( "EXEC my_schema.my_stored_procedure N'param1', N'param2';", "EXECUTE my_schema.my_stored_procedure N'param1', N'param2'"); - + Assert.Equal(expected, execute); } @@ -6887,7 +6887,7 @@ public void Parse_Bang_Not() Assert.Equal(expectedIdent, select.Projection[0]); Assert.Equal(expectedNested, select.Projection[1]); - var statements = new []{"SELECT a!", "SELECT a ! b", "SELECT a ! as b"}; + var statements = new[] { "SELECT a!", "SELECT a ! b", "SELECT a ! as b" }; foreach (var statement in statements) { @@ -6940,7 +6940,8 @@ public void Parse_Factorial_Operator() // and the `factorial` operator, additional filtering supports // `bang not` operator is required here. dialects = AllDialects.Where(d => d is { SupportsFactorialOperator: false, SupportsBangNotOperator: true }).ToList(); - foreach (var statement in statements){ + foreach (var statement in statements) + { Assert.Throws(() => ParseSqlStatements(statement, dialects)); } diff --git a/src/SqlParser/Ast/CommonTableExpression.cs b/src/SqlParser/Ast/CommonTableExpression.cs index e9803c8..9a9ea9c 100644 --- a/src/SqlParser/Ast/CommonTableExpression.cs +++ b/src/SqlParser/Ast/CommonTableExpression.cs @@ -17,13 +17,19 @@ public void ToSql(SqlTextWriter writer) { if (Materialized == null) { - writer.WriteSql($"{Alias} AS ({Query})"); + if (Alias.AsKeyword) + writer.WriteSql($"{Alias} AS ({Query})"); + else + writer.WriteSql($"{Alias} ({Query})"); } else { - writer.WriteSql($"{Alias} AS {Materialized} ({Query})"); + if (Alias.AsKeyword) + writer.WriteSql($"{Alias} AS {Materialized} ({Query})"); + else + writer.WriteSql($"{Alias} {Materialized} ({Query})"); } - + if (From != null) { writer.WriteSql($" FROM {From}"); diff --git a/src/SqlParser/Ast/Expression.cs b/src/SqlParser/Ast/Expression.cs index 5276b6c..29d137a 100644 --- a/src/SqlParser/Ast/Expression.cs +++ b/src/SqlParser/Ast/Expression.cs @@ -144,13 +144,13 @@ public void SetCustomOperator(string customOperator) public virtual bool Equals(BinaryOp? other) { - if (ReferenceEquals(null, other)){ return false; } - if (ReferenceEquals(this, other)){ return true; } + if (ReferenceEquals(null, other)) { return false; } + if (ReferenceEquals(this, other)) { return true; } return base.Equals(other) && - Equals(PgOptions, other.PgOptions) && - Left.Equals(other.Left) && - Op == other.Op && + Equals(PgOptions, other.PgOptions) && + Left.Equals(other.Left) && + Op == other.Op && Right.Equals(other.Right); } @@ -217,7 +217,7 @@ public override void ToSql(SqlTextWriter writer) CastKind.TryCast => "TRY_CAST", CastKind.SafeCast => "SAFE_CAST" }; - + if (Format != null) { writer.WriteSql($"{kind}({Expression} as {DataType} FORMAT {Format})"); @@ -243,11 +243,11 @@ public override void ToSql(SqlTextWriter writer) case CeilFloorKind.DateTimeFieldKind { Field: DateTimeField.NoDateTime }: writer.WriteSql($"CEIL({Expression})"); break; - + case CeilFloorKind.DateTimeFieldKind dt: writer.WriteSql($"CEIL({Expression} TO {dt.Field})"); break; - + case CeilFloorKind.Scale s: writer.WriteSql($"CEIL({Expression}, {s.Field})"); break; @@ -309,8 +309,8 @@ public override void ToSql(SqlTextWriter writer) /// public record Convert( Expression Expression, - DataType? DataType, - ObjectName? CharacterSet, + DataType? DataType, + ObjectName? CharacterSet, bool TargetBeforeValue, Sequence Styles, bool IsTry = false) : Expression @@ -480,7 +480,7 @@ public record Function(ObjectName Name) : Expression /// /// Sequence function call /// - public FunctionArguments Args { get; internal set; } + public FunctionArguments Args { get; init; } public FunctionArguments Parameters { get; init; } @@ -572,11 +572,11 @@ public record ILike(Expression Expression, bool Negated, Expression Pattern, str { public ILike(Expression expression, bool negated, Expression pattern, bool Any) : this(expression, negated, pattern, null, Any) { } - + public override void ToSql(SqlTextWriter writer) { var any = Any ? "ANY " : string.Empty; - + // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression if (EscapeChar != null) { @@ -644,7 +644,7 @@ public Interval(Expression value, DateTimeField? leadingField = null, DateTimeFi LeadingField = leadingField ?? new DateTimeField.None(); LastField = lastField ?? new DateTimeField.None(); } - + public Expression Value { get; } public DateTimeField LeadingField { get; } public DateTimeField LastField { get; } @@ -899,7 +899,7 @@ public record Map(Ast.Map MapExpression) : Expression { public override void ToSql(SqlTextWriter writer) { - writer.WriteSql($"{MapExpression}"); + writer.WriteSql($"{MapExpression}"); } } /// Access a map-like object by field @@ -974,11 +974,14 @@ public override void ToSql(SqlTextWriter writer) /// /// BigQuery specific: A named expression in a typeless struct /// - public record Named(Expression Expression, Ident Name) : Expression + public record Named(Expression Expression, Ident Name, bool AsKeyword = true) : Expression { public override void ToSql(SqlTextWriter writer) { - writer.WriteSql($"{Expression} AS {Name}"); + if (AsKeyword) + writer.WriteSql($"{Expression} AS {Name}"); + else + writer.WriteSql($"{Expression} {Name}"); } } /// @@ -1140,10 +1143,10 @@ public override void ToSql(SqlTextWriter writer) public record SimilarTo(Expression Expression, bool Negated, Expression Pattern, string? EscapeChar = null) : Expression, INegated { public SimilarTo(Expression expression, bool negated, Expression pattern, char? escapeChar = null) - : this(expression, negated, pattern, escapeChar?.ToString()){ } + : this(expression, negated, pattern, escapeChar?.ToString()) { } public SimilarTo(Expression expression, bool negated, Expression pattern) - : this(expression, negated, pattern, (string?) null) { } + : this(expression, negated, pattern, (string?)null) { } public override void ToSql(SqlTextWriter writer) { @@ -1372,7 +1375,7 @@ public LiteralValue AsLiteral() } // ReSharper restore CommentTypo -public record ExpressionWithAlias(Expression Expression, Ident? Alias) : IWriteSql, IElement +public record ExpressionWithAlias(Expression Expression, Ident? Alias, bool AsKeyword = true) : IWriteSql, IElement { public void ToSql(SqlTextWriter writer) { @@ -1380,7 +1383,10 @@ public void ToSql(SqlTextWriter writer) if (Alias != null) { - writer.WriteSql($" AS {Alias}"); + if (AsKeyword) + writer.WriteSql($" AS {Alias}"); + else + writer.WriteSql($" {Alias}"); } } } \ No newline at end of file diff --git a/src/SqlParser/Ast/Ident.cs b/src/SqlParser/Ast/Ident.cs index 638ed4e..e6d305c 100644 --- a/src/SqlParser/Ast/Ident.cs +++ b/src/SqlParser/Ast/Ident.cs @@ -36,7 +36,8 @@ public override string ToString() /// Sql writer instance public void ToSql(SqlTextWriter writer) { - switch( QuoteStyle){ + switch (QuoteStyle) + { case Symbols.DoubleQuote: case Symbols.SingleQuote: case Symbols.Backtick: @@ -59,10 +60,14 @@ public void ToSql(SqlTextWriter writer) /// /// Name identifier /// Alias identifier -public record IdentWithAlias(Ident Name, Ident Alias):IWriteSql, IElement +/// +public record IdentWithAlias(Ident Name, Ident Alias, bool AsKeyword = true) : IWriteSql, IElement { public void ToSql(SqlTextWriter writer) { - writer.WriteSql($"{Name} AS {Alias}"); + if (AsKeyword) + writer.WriteSql($"{Name} AS {Alias}"); + else + writer.WriteSql($"{Name} {Alias}"); } } \ No newline at end of file diff --git a/src/SqlParser/Ast/LockTable.cs b/src/SqlParser/Ast/LockTable.cs index 3e169c2..ff77744 100644 --- a/src/SqlParser/Ast/LockTable.cs +++ b/src/SqlParser/Ast/LockTable.cs @@ -1,6 +1,6 @@ namespace SqlParser.Ast; -public record LockTable(Ident Table, Ident? Alias, LockTableType LockTableType) : IWriteSql, IElement +public record LockTable(Ident Table, Ident? Alias, LockTableType LockTableType, bool AsKeyword = true) : IWriteSql, IElement { public void ToSql(SqlTextWriter writer) { @@ -8,7 +8,10 @@ public void ToSql(SqlTextWriter writer) if (Alias != null) { - writer.WriteSql($"AS {Alias} "); + if (AsKeyword) + writer.WriteSql($"AS {Alias} "); + else + writer.WriteSql($"{Alias} "); } writer.WriteSql($"{LockTableType}"); diff --git a/src/SqlParser/Ast/SelectItem.cs b/src/SqlParser/Ast/SelectItem.cs index 5bbe92e..2c20e6c 100644 --- a/src/SqlParser/Ast/SelectItem.cs +++ b/src/SqlParser/Ast/SelectItem.cs @@ -33,11 +33,14 @@ public override void ToSql(SqlTextWriter writer) /// /// Select expression /// Select alias - public record ExpressionWithAlias(Expression Expression, Ident Alias) : SelectItem + public record ExpressionWithAlias(Expression Expression, Ident Alias, bool AsKeyword = true) : SelectItem { public override void ToSql(SqlTextWriter writer) { - writer.WriteSql($"{Expression} AS {Alias}"); + if (AsKeyword) + writer.WriteSql($"{Expression} AS {Alias}"); + else + writer.WriteSql($"{Expression} {Alias}"); } } /// @@ -56,7 +59,7 @@ public override void ToSql(SqlTextWriter writer) public T As() where T : SelectItem { - return (T) this; + return (T)this; } public UnnamedExpression AsUnnamed() diff --git a/src/SqlParser/Ast/Table.cs b/src/SqlParser/Ast/Table.cs index 99e447a..41a38f0 100644 --- a/src/SqlParser/Ast/Table.cs +++ b/src/SqlParser/Ast/Table.cs @@ -1,4 +1,6 @@ -namespace SqlParser.Ast; +using System.Reflection.Metadata.Ecma335; + +namespace SqlParser.Ast; /// /// Table object @@ -24,7 +26,7 @@ public void ToSql(SqlTextWriter writer) /// Table alias /// /// Name identifier -public record TableAlias(Ident Name, Sequence? Columns = null) : IWriteSql, IElement +public record TableAlias(Ident Name, bool AsKeyword = true, Sequence? Columns = null) : IWriteSql, IElement { public void ToSql(SqlTextWriter writer) { diff --git a/src/SqlParser/Ast/TableFactor.cs b/src/SqlParser/Ast/TableFactor.cs index b0cfbd1..f235ce0 100644 --- a/src/SqlParser/Ast/TableFactor.cs +++ b/src/SqlParser/Ast/TableFactor.cs @@ -8,7 +8,7 @@ public abstract record TableFactor : IWriteSql, IElement /// /// Common table alias across all implementations /// - [Visit(1)] public TableAlias? Alias { get; internal set; } + [Visit(1)] public TableAlias? Alias { get; set; } /// /// Derived table factor /// @@ -27,7 +27,10 @@ public override void ToSql(SqlTextWriter writer) if (Alias != null) { - writer.WriteSql($" AS {Alias}"); + if (Alias.AsKeyword) + writer.WriteSql($" AS {Alias}"); + else + writer.WriteSql($" {Alias}"); } } } @@ -45,7 +48,10 @@ public override void ToSql(SqlTextWriter writer) if (Alias != null) { - writer.WriteSql($" AS {Alias}"); + if (Alias.AsKeyword) + writer.WriteSql($" AS {Alias}"); + else + writer.WriteSql($" {Alias}"); } } } @@ -67,7 +73,10 @@ public override void ToSql(SqlTextWriter writer) if (Alias != null) { - writer.WriteSql($" AS {Alias}"); + if (Alias.AsKeyword) + writer.WriteSql($" AS {Alias}"); + else + writer.WriteSql($" {Alias}"); } } } @@ -172,7 +181,10 @@ public override void ToSql(SqlTextWriter writer) if (Alias != null) { - writer.WriteSql($" AS {Alias}"); + if (Alias.AsKeyword) + writer.WriteSql($" AS {Alias}"); + else + writer.WriteSql($" {Alias}"); } if (WithHints.SafeAny()) @@ -203,7 +215,10 @@ public override void ToSql(SqlTextWriter writer) if (Alias != null) { - writer.WriteSql($" AS {Alias}"); + if (Alias.AsKeyword) + writer.WriteSql($" AS {Alias}"); + else + writer.WriteSql($" {Alias}"); } } } @@ -230,7 +245,7 @@ public record UnNest(Sequence ArrayExpressions) : TableFactor public override void ToSql(SqlTextWriter writer) { writer.Write($"UNNEST({ArrayExpressions.ToSqlDelimited()})"); - + if (WithOrdinality) { writer.Write(" WITH ORDINALITY"); @@ -238,7 +253,10 @@ public override void ToSql(SqlTextWriter writer) if (Alias != null) { - writer.WriteSql($" AS {Alias}"); + if (Alias.AsKeyword) + writer.WriteSql($" AS {Alias}"); + else + writer.WriteSql($" {Alias}"); } if (WithOffset) @@ -275,7 +293,10 @@ public override void ToSql(SqlTextWriter writer) if (Alias != null) { - writer.WriteSql($" AS {Alias}"); + if (Alias.AsKeyword) + writer.WriteSql($" AS {Alias}"); + else + writer.WriteSql($" {Alias}"); } } } @@ -336,7 +357,10 @@ public override void ToSql(SqlTextWriter writer) if (Alias != null) { - writer.WriteSql($" AS {Alias}"); + if (Alias.AsKeyword) + writer.WriteSql($" AS {Alias}"); + else + writer.WriteSql($" {Alias}"); } } } diff --git a/src/SqlParser/Ast/WildcardAdditionalOptions.cs b/src/SqlParser/Ast/WildcardAdditionalOptions.cs index 2e81e7a..7c299ad 100644 --- a/src/SqlParser/Ast/WildcardAdditionalOptions.cs +++ b/src/SqlParser/Ast/WildcardAdditionalOptions.cs @@ -15,7 +15,7 @@ public record WildcardAdditionalOptions : IWriteSql, IElement // [REPLACE] // BigQuery syntax: public ReplaceSelectItem? ReplaceOption { get; init; } - + public void ToSql(SqlTextWriter writer) { if (ILikeOption != null) @@ -53,18 +53,14 @@ public void ToSql(SqlTextWriter writer) } } -public record ReplaceSelectElement(Expression Expr, Ident Name, bool AsKeyword) : IWriteSql, IElement +public record ReplaceSelectElement(Expression Expr, Ident Name, bool AsKeyword = true) : IWriteSql, IElement { public void ToSql(SqlTextWriter writer) { if (AsKeyword) - { writer.WriteSql($"{Expr} AS {Name}"); - } else - { writer.WriteSql($"{Expr} {Name}"); - } } } diff --git a/src/SqlParser/Parser.Expressions.cs b/src/SqlParser/Parser.Expressions.cs index ba09b84..103e111 100644 --- a/src/SqlParser/Parser.Expressions.cs +++ b/src/SqlParser/Parser.Expressions.cs @@ -777,7 +777,7 @@ _dialect is not ClickHouseDialect and not DatabricksDialect LeftBracket => ParseArrayExpr(false), Minus or Plus => ParseUnary(), - ExclamationMark when _dialect.SupportsBangNotOperator => + ExclamationMark when _dialect.SupportsBangNotOperator => new UnaryOp(ParseSubExpression((short)Precedence.UnaryNot), UnaryOperator.BangNot), DoubleExclamationMark @@ -869,7 +869,7 @@ public Expression ParseStructFieldExpression(bool typedSyntax) } var fieldName = ParseIdentifier(); - return new Named(expression, fieldName); + return new Named(expression, fieldName, true); } return expression; @@ -901,13 +901,13 @@ public Expression ParseFunction(ObjectName name) } var withinGroup = ParseInit(ParseKeywordSequence(Keyword.WITHIN, Keyword.GROUP), () => + { + return ExpectParens(() => { - return ExpectParens(() => - { - ExpectKeywords(Keyword.ORDER, Keyword.BY); - return ParseCommaSeparated(ParseOrderByExpr); - }); + ExpectKeywords(Keyword.ORDER, Keyword.BY); + return ParseCommaSeparated(ParseOrderByExpr); }); + }); var filter = ParseInit( _dialect.SupportsFilterDuringAggregation && @@ -1251,7 +1251,6 @@ private SetExpression ParseRemainingSetExpressions(SetExpression expr, int prece return expr; - SetOperator ParseSetOperator() { return PeekToken() switch diff --git a/src/SqlParser/Parser.cs b/src/SqlParser/Parser.cs index b7aecf0..9894f4f 100644 --- a/src/SqlParser/Parser.cs +++ b/src/SqlParser/Parser.cs @@ -722,7 +722,7 @@ public JsonPathElement ParseJsonPathObjectKey() public LockTable ParseLockTable() { var table = ParseIdentifier(); - var alias = ParseOptionalAlias(new[] { Keyword.READ, Keyword.WRITE, Keyword.LOW_PRIORITY }); + var alias = ParseOptionalAlias(new[] { Keyword.READ, Keyword.WRITE, Keyword.LOW_PRIORITY }, out var asKeyword); LockTableType lockType; if (ParseKeyword(Keyword.READ)) @@ -742,7 +742,7 @@ public LockTable ParseLockTable() throw Expected("an lock type in LOCK TABLES", PeekToken()); } - return new LockTable(table, alias, lockType); + return new LockTable(table, alias, lockType, asKeyword); } /// /// Parse either `ALL` or `DISTINCT`. Returns `true` if `DISTINCT` is parsed and results in a @@ -1712,16 +1712,16 @@ public Ast.CreateIndex ParseCreateIndex(bool unique) var columns = ExpectParens(() => ParseCommaSeparated(ParseOrderByExpr)); var include = ParseInit(ParseKeyword(Keyword.INCLUDE), () => - { - return ExpectParens(() => ParseCommaSeparated(ParseIdentifier)); - }); + { + return ExpectParens(() => ParseCommaSeparated(ParseIdentifier)); + }); var nullsDistinct = ParseInit(ParseKeyword(Keyword.NULLS), () => - { - var not = ParseKeyword(Keyword.NOT); - ExpectKeyword(Keyword.DISTINCT); - return !not; - }); + { + var not = ParseKeyword(Keyword.NOT); + ExpectKeyword(Keyword.DISTINCT); + return !not; + }); var withExpressions = ParseInit( _dialect.SupportsCreateIndexWithClause && ParseKeyword(Keyword.WITH), () => @@ -2233,7 +2233,7 @@ or Keyword.GENERATED public ColumnOption? ParseOptionalColumnOption() { var option = _dialect.ParseColumnOption(this); - if (option!=null) + if (option != null) { return option; } @@ -2357,13 +2357,13 @@ or Keyword.GENERATED if (_dialect is MySqlDialect or GenericDialect && ParseKeyword(Keyword.AUTO_INCREMENT)) { // Support AUTO_INCREMENT for MySQL - return new ColumnOption.DialectSpecific([new Word("AUTO_INCREMENT") ]); + return new ColumnOption.DialectSpecific([new Word("AUTO_INCREMENT")]); } if (_dialect is SQLiteDialect or GenericDialect && ParseKeyword(Keyword.AUTOINCREMENT)) { // Support AUTOINCREMENT for SQLite - return new ColumnOption.DialectSpecific([new Word("AUTOINCREMENT") ]); + return new ColumnOption.DialectSpecific([new Word("AUTOINCREMENT")]); } if (_dialect.SupportsAscDescInColumnDefinition && ParseKeyword(Keyword.ASC)) @@ -2885,7 +2885,7 @@ public ViewColumnDef ParseViewColumn() { PrevToken(); var option = ParseOptionalColumnOption(); - return new Sequence{ option! }; + return new Sequence { option! }; }); var dataType = ParseInit(_dialect is ClickHouseDialect, ParseDataType); @@ -3768,7 +3768,7 @@ public Value ParseIntroducedStringValue() _ => ParseUnmatched() }; - if(parserException != null || _currentException != null) + if (parserException != null || _currentException != null) { return (data, trailingBracket, parserException ?? _currentException); } @@ -3946,7 +3946,7 @@ DataType ParseUnion() PrevToken(); return new DataType.Union(ParseUnionTypeDef()); } - + DataType ParseUnmatched() { PrevToken(); @@ -4022,7 +4022,7 @@ public IdentWithAlias ParseIdentifierWithAlias() var ident = ParseIdentifier(); ExpectKeyword(Keyword.AS); var alias = ParseIdentifier(); - return new IdentWithAlias(ident, alias); + return new IdentWithAlias(ident, alias, true); } /// /// Parse `AS identifier` (or simply `identifier` if it's not a reserved keyword) @@ -4030,9 +4030,9 @@ public IdentWithAlias ParseIdentifierWithAlias() /// /// /// - public Ident? ParseOptionalAlias(IEnumerable reservedKeywords) + public Ident? ParseOptionalAlias(IEnumerable reservedKeywords, out bool asKeyword) { - var afterAs = ParseKeyword(Keyword.AS); + asKeyword = ParseKeyword(Keyword.AS); var token = NextToken(); return token switch @@ -4042,7 +4042,7 @@ public IdentWithAlias ParseIdentifierWithAlias() // which may start a construct allowed in this position, to be parsed as aliases. // (For example, in `FROM t1 JOIN` the `JOIN` will always be parsed as a keyword, // not an alias.) - Word word when afterAs || !reservedKeywords.Contains(word.Keyword) => word.ToIdent(), + Word word when asKeyword || !reservedKeywords.Contains(word.Keyword) => word.ToIdent(), // MSSQL supports single-quoted strings as aliases for columns // We accept them as table aliases too, although MSSQL does not. @@ -4058,7 +4058,7 @@ public IdentWithAlias ParseIdentifierWithAlias() SingleQuotedString s => new Ident(s.Value, Symbols.SingleQuote), // Support for MySql dialect double-quoted string, `AS "HOUR"` for example DoubleQuotedString s => new Ident(s.Value, Symbols.DoubleQuote), - _ when afterAs => throw Expected("an identifier after AS", token), + _ when asKeyword => throw Expected("an identifier after AS", token), _ => None() }; @@ -4079,18 +4079,18 @@ public IdentWithAlias ParseIdentifierWithAlias() /// public TableAlias? ParseOptionalTableAlias(IEnumerable keywords) { - var alias = ParseOptionalAlias(keywords); + var alias = ParseOptionalAlias(keywords, out var asKeyword); if (alias != null) { - var columns = ParseParenthesizedColumnList(IsOptional.Optional, false); + var columns = ParseParenthesizedColumnList(IsOptional.Optional, asKeyword); if (!columns.Any()) { columns = null; } - return new TableAlias(alias, columns); + return new TableAlias(alias, asKeyword, columns); } return null; @@ -4477,7 +4477,7 @@ public CommonTableExpression ParseCommonTableExpression() }); var query = ExpectParens(() => ParseQuery()); - var alias = new TableAlias(name); + var alias = new TableAlias(name, true); cte = new CommonTableExpression(alias, query.Query, Materialized: isMaterialized); } else @@ -4504,7 +4504,7 @@ public CommonTableExpression ParseCommonTableExpression() }); var query = ExpectParens(() => ParseQuery()); - var alias = new TableAlias(name, columns); + var alias = new TableAlias(name, true, columns); cte = new CommonTableExpression(alias, query.Query, Materialized: isMaterialized); } @@ -4707,7 +4707,7 @@ public Select ParseSelect() var lateralView = ParseExpr(); var lateralViewName = ParseObjectName(); var lateralColAlias = ParseCommaSeparated(() => - ParseOptionalAlias(new[] { Keyword.WHERE, Keyword.GROUP, Keyword.CLUSTER, Keyword.HAVING, Keyword.LATERAL })); + ParseOptionalAlias(new[] { Keyword.WHERE, Keyword.GROUP, Keyword.CLUSTER, Keyword.HAVING, Keyword.LATERAL }, out var asKeyword)); lateralViews ??= new Sequence(); lateralViews.Add(new LateralView(lateralView) @@ -4787,7 +4787,7 @@ public Select ParseSelect() return (windows, null, true); } - + if (ParseKeyword(Keyword.QUALIFY)) { var qualifyExpr = ParseExpr(); @@ -5297,7 +5297,7 @@ or TableFactor.MatchRecognize var alias = ParseOptionalTableAlias(Keywords.ReservedForColumnAlias); var withOffset = ParseKeywordSequence(Keyword.WITH, Keyword.OFFSET); - var withOffsetAlias = withOffset ? ParseOptionalAlias(Keywords.ReservedForColumnAlias) : null; + var withOffsetAlias = withOffset ? ParseOptionalAlias(Keywords.ReservedForColumnAlias, out var asKeyword) : null; return new TableFactor.UnNest(expressions) { Alias = alias, @@ -6077,17 +6077,17 @@ public SelectItem ParseSelectItem() throw Expected($"Expected an expression, found: {v}"); } - if (wildcardExpr is BinaryOp { Op: BinaryOperator.Eq } b && + if (wildcardExpr is BinaryOp { Op: BinaryOperator.Eq } b && _dialect.SupportsEqualAliasAssignment && b.Left is Identifier leftIdent) { - return new SelectItem.ExpressionWithAlias(b.Right, leftIdent.Ident.Value); + return new SelectItem.ExpressionWithAlias(b.Right, leftIdent.Ident.Value, true); } - var alias = ParseOptionalAlias(Keywords.ReservedForColumnAlias); + var alias = ParseOptionalAlias(Keywords.ReservedForColumnAlias, out var asKeyword); if (alias != null) { - return new SelectItem.ExpressionWithAlias(wildcardExpr, alias); + return new SelectItem.ExpressionWithAlias(wildcardExpr, alias, asKeyword); } return new SelectItem.UnnamedExpression(wildcardExpr); @@ -6122,6 +6122,7 @@ or DuckDbDialect ReplaceOption = optReplace }; } + /// /// Parse an [`Ilike`](IlikeSelectItem) information for wildcard select items. /// @@ -6440,7 +6441,7 @@ public Execute ParseExecute() (true, _) => typeof(RightParen), (false, EOF) => typeof(EOF), (false, Word { Keyword: Keyword.USING }) => typeof(Word), - (false, _ ) => typeof(SemiColon) + (false, _) => typeof(SemiColon) }; var parameters = ParseCommaSeparated0(ParseExpr, endToken);