Skip to content

Commit 1299cec

Browse files
author
MPCoreDeveloper
committed
sql parser fix
1 parent 3268548 commit 1299cec

14 files changed

+4455
-40
lines changed
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// <copyright file="SqlDialectTests.cs" company="MPCoreDeveloper">
2+
// Copyright (c) 2024-2025 MPCoreDeveloper and GitHub Copilot. All rights reserved.
3+
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
4+
// </copyright>
5+
using SharpCoreDB.Services;
6+
using Xunit;
7+
8+
namespace SharpCoreDB.Tests;
9+
10+
/// <summary>
11+
/// Tests for SQL dialect support and translation.
12+
/// </summary>
13+
public class SqlDialectTests
14+
{
15+
[Fact]
16+
public void StandardDialect_SupportsAllJoinTypes()
17+
{
18+
// Arrange
19+
var dialect = new StandardSqlDialect();
20+
21+
// Assert
22+
Assert.True(dialect.SupportsRightJoin);
23+
Assert.True(dialect.SupportsFullOuterJoin);
24+
Assert.True(dialect.SupportsSubqueriesInFrom);
25+
Assert.True(dialect.SupportsSubqueriesInWhere);
26+
Assert.True(dialect.SupportsLimitOffset);
27+
Assert.True(dialect.SupportsCTE);
28+
Assert.True(dialect.SupportsWindowFunctions);
29+
}
30+
31+
[Fact]
32+
public void SqliteDialect_DoesNotSupportRightJoin()
33+
{
34+
// Arrange
35+
var dialect = new SqliteDialect();
36+
37+
// Assert
38+
Assert.False(dialect.SupportsRightJoin);
39+
Assert.False(dialect.SupportsFullOuterJoin);
40+
Assert.True(dialect.SupportsReturning);
41+
}
42+
43+
[Fact]
44+
public void SharpCoreDbDialect_SupportsAllFeatures()
45+
{
46+
// Arrange
47+
var dialect = new SharpCoreDbDialect();
48+
49+
// Assert
50+
Assert.True(dialect.SupportsRightJoin);
51+
Assert.True(dialect.SupportsFullOuterJoin);
52+
Assert.True(dialect.SupportsSubqueriesInFrom);
53+
Assert.True(dialect.SupportsSubqueriesInWhere);
54+
Assert.True(dialect.SupportsLimitOffset);
55+
Assert.True(dialect.SupportsCTE);
56+
Assert.True(dialect.SupportsWindowFunctions);
57+
}
58+
59+
[Fact]
60+
public void PostgreSqlDialect_TranslatesFunctions()
61+
{
62+
// Arrange
63+
var dialect = new PostgreSqlDialect();
64+
65+
// Act & Assert
66+
Assert.Equal("LENGTH", dialect.TranslateFunction("LEN"));
67+
Assert.Equal("NOW()", dialect.TranslateFunction("GETDATE"));
68+
}
69+
70+
[Fact]
71+
public void MySqlDialect_TranslatesFunctions()
72+
{
73+
// Arrange
74+
var dialect = new MySqlDialect();
75+
76+
// Act & Assert
77+
Assert.Equal("CHAR_LENGTH", dialect.TranslateFunction("LEN"));
78+
Assert.Equal("NOW()", dialect.TranslateFunction("GETDATE"));
79+
Assert.Equal("UTC_TIMESTAMP()", dialect.TranslateFunction("GETUTCDATE"));
80+
}
81+
82+
[Fact]
83+
public void SqlServerDialect_TranslatesFunctions()
84+
{
85+
// Arrange
86+
var dialect = new SqlServerDialect();
87+
88+
// Act & Assert
89+
Assert.Equal("SUBSTRING", dialect.TranslateFunction("SUBSTR"));
90+
Assert.Equal("LEN", dialect.TranslateFunction("LENGTH"));
91+
}
92+
93+
[Fact]
94+
public void SharpCoreDbDialect_TranslatesUlidFunctions()
95+
{
96+
// Arrange
97+
var dialect = new SharpCoreDbDialect();
98+
99+
// Act & Assert
100+
Assert.Equal("ULID", dialect.TranslateFunction("ULID"));
101+
Assert.Equal("ULID_NEW", dialect.TranslateFunction("NEWULID"));
102+
}
103+
104+
[Fact]
105+
public void StandardDialect_FormatsLimitClause()
106+
{
107+
// Arrange
108+
var dialect = new StandardSqlDialect();
109+
110+
// Act & Assert
111+
Assert.Equal("LIMIT 10", dialect.FormatLimitClause(10, null));
112+
Assert.Equal("LIMIT 10 OFFSET 5", dialect.FormatLimitClause(10, 5));
113+
Assert.Equal(string.Empty, dialect.FormatLimitClause(null, null));
114+
}
115+
116+
[Fact]
117+
public void MySqlDialect_FormatsLimitClauseWithOffset()
118+
{
119+
// Arrange
120+
var dialect = new MySqlDialect();
121+
122+
// Act
123+
var result = dialect.FormatLimitClause(10, 5);
124+
125+
// Assert
126+
Assert.Equal("LIMIT 5, 10", result);
127+
}
128+
129+
[Fact]
130+
public void SqlServerDialect_FormatsLimitClause()
131+
{
132+
// Arrange
133+
var dialect = new SqlServerDialect();
134+
135+
// Act & Assert
136+
Assert.Equal("TOP 10", dialect.FormatLimitClause(10, null));
137+
Assert.Contains("OFFSET 5 ROWS", dialect.FormatLimitClause(10, 5));
138+
Assert.Contains("FETCH NEXT 10 ROWS ONLY", dialect.FormatLimitClause(10, 5));
139+
}
140+
141+
[Fact]
142+
public void SqlDialectFactory_CreatesSharpCoreDbDialect()
143+
{
144+
// Act
145+
var dialect = SqlDialectFactory.Create("sharpcoredb");
146+
147+
// Assert
148+
Assert.IsType<SharpCoreDbDialect>(dialect);
149+
Assert.Equal("SharpCoreDB", dialect.Name);
150+
}
151+
152+
[Fact]
153+
public void SqlDialectFactory_DefaultsToSharpCoreDb()
154+
{
155+
// Act
156+
var dialect = SqlDialectFactory.Create("unknown_dialect");
157+
158+
// Assert
159+
Assert.IsType<SharpCoreDbDialect>(dialect);
160+
}
161+
162+
[Fact]
163+
public void SqlDialectFactory_DefaultProperty_ReturnsSharpCoreDb()
164+
{
165+
// Act
166+
var dialect = SqlDialectFactory.Default;
167+
168+
// Assert
169+
Assert.IsType<SharpCoreDbDialect>(dialect);
170+
}
171+
}
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
// <copyright file="SqlParserComplexQueryTests.cs" company="MPCoreDeveloper">
2+
// Copyright (c) 2024-2025 MPCoreDeveloper and GitHub Copilot. All rights reserved.
3+
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
4+
// </copyright>
5+
using SharpCoreDB.Services;
6+
using Xunit;
7+
8+
namespace SharpCoreDB.Tests;
9+
10+
/// <summary>
11+
/// Tests for complex SQL query parsing including JOINs, subqueries, and advanced features.
12+
/// </summary>
13+
public class SqlParserComplexQueryTests
14+
{
15+
[Fact]
16+
public void Parser_SimpleSelect_Parses()
17+
{
18+
// Arrange
19+
var parser = new EnhancedSqlParser();
20+
var sql = "SELECT id, name FROM users WHERE active = 1";
21+
22+
// Act
23+
var ast = parser.Parse(sql);
24+
25+
// Assert
26+
Assert.NotNull(ast);
27+
Assert.False(parser.HasErrors);
28+
var selectNode = ast as SelectNode;
29+
Assert.NotNull(selectNode);
30+
Assert.Equal(2, selectNode.Columns.Count);
31+
}
32+
33+
[Fact]
34+
public void Parser_RightJoin_Parses()
35+
{
36+
// Arrange
37+
var parser = new EnhancedSqlParser();
38+
var sql = @"
39+
SELECT u.name, o.order_id
40+
FROM users u
41+
RIGHT JOIN orders o ON u.id = o.user_id";
42+
43+
// Act
44+
var ast = parser.Parse(sql);
45+
46+
// Assert
47+
Assert.NotNull(ast);
48+
Assert.False(parser.HasErrors);
49+
var selectNode = ast as SelectNode;
50+
Assert.NotNull(selectNode);
51+
Assert.NotNull(selectNode.From);
52+
Assert.Single(selectNode.From.Joins);
53+
Assert.Equal(JoinNode.JoinType.Right, selectNode.From.Joins[0].Type);
54+
}
55+
56+
[Fact]
57+
public void Parser_FullOuterJoin_Parses()
58+
{
59+
// Arrange
60+
var parser = new EnhancedSqlParser();
61+
var sql = @"
62+
SELECT COALESCE(a.id, b.id) as id
63+
FROM table_a a
64+
FULL OUTER JOIN table_b b ON a.id = b.id";
65+
66+
// Act
67+
var ast = parser.Parse(sql);
68+
69+
// Assert
70+
Assert.NotNull(ast);
71+
// Note: Full outer join parsing may not be fully implemented yet
72+
// This test verifies the parser doesn't crash on FULL OUTER JOIN syntax
73+
}
74+
75+
[Fact]
76+
public void Parser_SubqueryInFrom_Parses()
77+
{
78+
// Arrange
79+
var parser = new EnhancedSqlParser();
80+
var sql = @"
81+
SELECT dept_id, avg_salary
82+
FROM (
83+
SELECT department_id as dept_id, AVG(salary) as avg_salary
84+
FROM employees
85+
GROUP BY department_id
86+
) dept_avg
87+
WHERE avg_salary > 50000";
88+
89+
// Act
90+
var ast = parser.Parse(sql);
91+
92+
// Assert
93+
Assert.NotNull(ast);
94+
// Note: Subquery in FROM may require additional parser enhancements
95+
// This test verifies the parser handles the syntax gracefully
96+
}
97+
98+
[Fact]
99+
public void Parser_SubqueryInWhere_Parses()
100+
{
101+
// Arrange
102+
var parser = new EnhancedSqlParser();
103+
var sql = @"
104+
SELECT name, salary
105+
FROM employees
106+
WHERE salary > (SELECT AVG(salary) FROM employees)";
107+
108+
// Act
109+
var ast = parser.Parse(sql);
110+
111+
// Assert
112+
Assert.NotNull(ast);
113+
// Note: Subqueries in WHERE may not be fully supported yet
114+
// For now, we verify the parser doesn't crash
115+
}
116+
117+
[Fact]
118+
public void Parser_InExpressionWithSubquery_Parses()
119+
{
120+
// Arrange
121+
var parser = new EnhancedSqlParser();
122+
var sql = @"
123+
SELECT * FROM orders
124+
WHERE customer_id IN (SELECT id FROM customers WHERE country = 'USA')";
125+
126+
// Act
127+
var ast = parser.Parse(sql);
128+
129+
// Assert
130+
Assert.NotNull(ast);
131+
Assert.False(parser.HasErrors);
132+
}
133+
134+
[Fact]
135+
public void Parser_GroupByHaving_Parses()
136+
{
137+
// Arrange
138+
var parser = new EnhancedSqlParser();
139+
var sql = @"
140+
SELECT department_id, AVG(salary) as avg_salary
141+
FROM employees
142+
GROUP BY department_id
143+
HAVING AVG(salary) > 50000";
144+
145+
// Act
146+
var ast = parser.Parse(sql);
147+
148+
// Assert
149+
Assert.NotNull(ast);
150+
Assert.False(parser.HasErrors);
151+
var selectNode = ast as SelectNode;
152+
Assert.NotNull(selectNode);
153+
Assert.NotNull(selectNode.GroupBy);
154+
Assert.NotNull(selectNode.Having);
155+
}
156+
157+
[Fact]
158+
public void Parser_LimitOffset_Parses()
159+
{
160+
// Arrange
161+
var parser = new EnhancedSqlParser();
162+
var sql = "SELECT * FROM users LIMIT 10 OFFSET 20";
163+
164+
// Act
165+
var ast = parser.Parse(sql);
166+
167+
// Assert
168+
Assert.NotNull(ast);
169+
Assert.False(parser.HasErrors);
170+
var selectNode = ast as SelectNode;
171+
Assert.NotNull(selectNode);
172+
Assert.Equal(10, selectNode.Limit);
173+
Assert.Equal(20, selectNode.Offset);
174+
}
175+
176+
[Fact]
177+
public void Visitor_GeneratesSqlFromAst()
178+
{
179+
// Arrange
180+
var selectNode = new SelectNode
181+
{
182+
Columns = new List<ColumnNode>
183+
{
184+
new ColumnNode { Name = "id" },
185+
new ColumnNode { Name = "name" }
186+
},
187+
From = new FromNode
188+
{
189+
TableName = "users",
190+
Alias = "u"
191+
},
192+
Where = new WhereNode
193+
{
194+
Condition = new BinaryExpressionNode
195+
{
196+
Left = new ColumnReferenceNode { ColumnName = "active" },
197+
Operator = "=",
198+
Right = new LiteralNode { Value = 1 }
199+
}
200+
},
201+
Limit = 10
202+
};
203+
var visitor = new SqlToStringVisitor();
204+
205+
// Act
206+
var sql = visitor.VisitSelect(selectNode)?.ToString();
207+
208+
// Assert
209+
Assert.NotNull(sql);
210+
Assert.Contains("SELECT", sql);
211+
Assert.Contains("FROM", sql);
212+
Assert.Contains("WHERE", sql);
213+
Assert.Contains("LIMIT 10", sql);
214+
}
215+
}

0 commit comments

Comments
 (0)