Database System 0.1.0
Advanced C++20 Database System with Multi-Backend Support
Loading...
Searching...
No Matches
sql_dialect_test.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
17#include <gtest/gtest.h>
19#include <memory>
20
22{
23
24//=============================================================================
25// PostgreSQL Dialect Tests
26//=============================================================================
27
28class PostgreSQLDialectTest : public ::testing::Test
29{
30protected:
35
36 std::unique_ptr<sql_dialect> dialect_;
37};
38
39TEST_F(PostgreSQLDialectTest, PlaceholderStyle)
40{
41 EXPECT_EQ(dialect_->placeholder(1), "$1");
42 EXPECT_EQ(dialect_->placeholder(2), "$2");
43 EXPECT_EQ(dialect_->placeholder(10), "$10");
44 EXPECT_EQ(dialect_->placeholder(100), "$100");
45}
46
48{
49 EXPECT_EQ(dialect_->quote_identifier("users"), "\"users\"");
50 EXPECT_EQ(dialect_->quote_identifier("user_name"), "\"user_name\"");
51 EXPECT_EQ(dialect_->quote_identifier("SELECT"), "\"SELECT\""); // Reserved word
52}
53
54TEST_F(PostgreSQLDialectTest, QuoteIdentifierWithSpecialChars)
55{
56 // Double quotes should be escaped by doubling
57 EXPECT_EQ(dialect_->quote_identifier("user\"name"), "\"user\"\"name\"");
58}
59
61{
62 EXPECT_EQ(dialect_->escape_string("hello"), "hello");
63 EXPECT_EQ(dialect_->escape_string("it's"), "it''s"); // Single quote doubled
64 EXPECT_EQ(dialect_->escape_string("back\\slash"), "back\\\\slash"); // Backslash escaped
65 EXPECT_EQ(dialect_->escape_string("O'Brien's"), "O''Brien''s");
66}
67
69{
70 EXPECT_EQ(dialect_->returning_clause("id"), " RETURNING \"id\"");
71 EXPECT_EQ(dialect_->returning_clause(""), " RETURNING *");
72}
73
75{
76 std::vector<std::string> conflict_cols = {"id"};
77 std::vector<std::string> update_cols = {"name", "email"};
78
79 std::string result = dialect_->upsert_clause(conflict_cols, update_cols);
80
81 EXPECT_TRUE(result.find("ON CONFLICT") != std::string::npos);
82 EXPECT_TRUE(result.find("\"id\"") != std::string::npos);
83 EXPECT_TRUE(result.find("DO UPDATE SET") != std::string::npos);
84 EXPECT_TRUE(result.find("EXCLUDED") != std::string::npos);
85}
86
87TEST_F(PostgreSQLDialectTest, UpsertClauseDoNothing)
88{
89 std::vector<std::string> conflict_cols = {"id"};
90 std::vector<std::string> update_cols; // Empty = DO NOTHING
91
92 std::string result = dialect_->upsert_clause(conflict_cols, update_cols);
93
94 EXPECT_TRUE(result.find("DO NOTHING") != std::string::npos);
95}
96
97TEST_F(PostgreSQLDialectTest, UpsertClauseMultipleConflictColumns)
98{
99 std::vector<std::string> conflict_cols = {"tenant_id", "user_id"};
100 std::vector<std::string> update_cols = {"updated_at"};
101
102 std::string result = dialect_->upsert_clause(conflict_cols, update_cols);
103
104 EXPECT_TRUE(result.find("\"tenant_id\"") != std::string::npos);
105 EXPECT_TRUE(result.find("\"user_id\"") != std::string::npos);
106}
107
108TEST_F(PostgreSQLDialectTest, SupportsFeatureReturning)
109{
110 EXPECT_TRUE(dialect_->supports_feature("returning"));
111 EXPECT_TRUE(dialect_->supports_feature("upsert"));
112 EXPECT_TRUE(dialect_->supports_feature("full_outer_join"));
113}
114
115//=============================================================================
116// SQLite Dialect Tests
117//=============================================================================
118
119class SQLiteDialectTest : public ::testing::Test
120{
121protected:
122 void SetUp() override
123 {
125 }
126
127 std::unique_ptr<sql_dialect> dialect_;
128};
129
130TEST_F(SQLiteDialectTest, PlaceholderStyle)
131{
132 // SQLite uses ?N numbered placeholders
133 EXPECT_EQ(dialect_->placeholder(1), "?1");
134 EXPECT_EQ(dialect_->placeholder(2), "?2");
135 EXPECT_EQ(dialect_->placeholder(10), "?10");
136}
137
138TEST_F(SQLiteDialectTest, QuoteIdentifier)
139{
140 EXPECT_EQ(dialect_->quote_identifier("users"), "\"users\"");
141 EXPECT_EQ(dialect_->quote_identifier("user_name"), "\"user_name\"");
142 EXPECT_EQ(dialect_->quote_identifier("SELECT"), "\"SELECT\"");
143}
144
145TEST_F(SQLiteDialectTest, QuoteIdentifierWithQuotes)
146{
147 // Double quotes should be escaped by doubling
148 EXPECT_EQ(dialect_->quote_identifier("user\"name"), "\"user\"\"name\"");
149}
150
152{
153 EXPECT_EQ(dialect_->escape_string("hello"), "hello");
154 EXPECT_EQ(dialect_->escape_string("it's"), "it''s"); // Single quote doubled
155 EXPECT_EQ(dialect_->escape_string("O'Brien's"), "O''Brien''s");
156}
157
158TEST_F(SQLiteDialectTest, ReturningClause)
159{
160 // SQLite 3.35+ supports RETURNING
161 EXPECT_EQ(dialect_->returning_clause("id"), " RETURNING \"id\"");
162 EXPECT_EQ(dialect_->returning_clause(""), " RETURNING *");
163}
164
166{
167 std::vector<std::string> conflict_cols = {"id"};
168 std::vector<std::string> update_cols = {"name", "email"};
169
170 std::string result = dialect_->upsert_clause(conflict_cols, update_cols);
171
172 EXPECT_TRUE(result.find("ON CONFLICT") != std::string::npos);
173 EXPECT_TRUE(result.find("\"id\"") != std::string::npos);
174 EXPECT_TRUE(result.find("DO UPDATE SET") != std::string::npos);
175 EXPECT_TRUE(result.find("excluded") != std::string::npos); // SQLite uses lowercase
176}
177
178TEST_F(SQLiteDialectTest, UpsertClauseDoNothing)
179{
180 std::vector<std::string> conflict_cols = {"id"};
181 std::vector<std::string> update_cols;
182
183 std::string result = dialect_->upsert_clause(conflict_cols, update_cols);
184
185 EXPECT_TRUE(result.find("DO NOTHING") != std::string::npos);
186}
187
188TEST_F(SQLiteDialectTest, SupportsFeatureReturning)
189{
190 EXPECT_TRUE(dialect_->supports_feature("returning"));
191 EXPECT_TRUE(dialect_->supports_feature("upsert"));
192 EXPECT_FALSE(dialect_->supports_feature("full_outer_join"));
193}
194
195TEST_F(SQLiteDialectTest, LimitClauseSyntax)
196{
197 EXPECT_EQ(dialect_->limit_clause(10, 0), "LIMIT 10");
198 EXPECT_EQ(dialect_->limit_clause(10, 20), "LIMIT 10 OFFSET 20");
199}
200
201//=============================================================================
202// Factory Method Tests
203//=============================================================================
204
205TEST(SqlDialectFactoryTest, CreatePostgreSQLDialect)
206{
208 ASSERT_NE(dialect, nullptr);
209 EXPECT_EQ(dialect->placeholder(1), "$1");
210}
211
212TEST(SqlDialectFactoryTest, CreateSQLiteDialect)
213{
215 ASSERT_NE(dialect, nullptr);
216 EXPECT_EQ(dialect->placeholder(1), "?1");
217}
218
219TEST(SqlDialectFactoryTest, CreateUnsupportedDialectThrows)
220{
221 EXPECT_THROW(
223 std::invalid_argument
224 );
225 EXPECT_THROW(
227 std::invalid_argument
228 );
229}
230
231//=============================================================================
232// Cross-Dialect Comparison Tests
233//=============================================================================
234
235class CrossDialectTest : public ::testing::Test
236{
237protected:
243
244 std::unique_ptr<sql_dialect> postgres_;
245 std::unique_ptr<sql_dialect> sqlite_;
246};
247
248TEST_F(CrossDialectTest, PlaceholderStylesDiffer)
249{
250 // Both dialects should produce different placeholders
251 EXPECT_NE(postgres_->placeholder(1), sqlite_->placeholder(1));
252}
253
254TEST_F(CrossDialectTest, QuoteIdentifierStyles)
255{
256 std::string col = "user_id";
257
258 // PostgreSQL and SQLite use double quotes
259 EXPECT_EQ(postgres_->quote_identifier(col), "\"user_id\"");
260 EXPECT_EQ(sqlite_->quote_identifier(col), "\"user_id\"");
261}
262
263TEST_F(CrossDialectTest, ConcatOperatorDiffers)
264{
265 // PostgreSQL and SQLite use ||
266 EXPECT_EQ(postgres_->concat_operator(), "||");
267 EXPECT_EQ(sqlite_->concat_operator(), "||");
268}
269
270TEST_F(CrossDialectTest, AutoIncrementSyntax)
271{
272 EXPECT_EQ(postgres_->auto_increment(), "SERIAL");
273 EXPECT_EQ(sqlite_->auto_increment(), "AUTOINCREMENT");
274}
275
276TEST_F(CrossDialectTest, CurrentTimestampFunction)
277{
278 EXPECT_EQ(postgres_->current_timestamp(), "CURRENT_TIMESTAMP");
279 EXPECT_EQ(sqlite_->current_timestamp(), "CURRENT_TIMESTAMP");
280}
281
282} // namespace database::query::tests
static std::unique_ptr< sql_dialect > create(database_types type)
Factory method to create appropriate dialect.
std::unique_ptr< sql_dialect > postgres_
std::unique_ptr< sql_dialect > sqlite_
std::unique_ptr< sql_dialect > dialect_
TEST_F(PostgreSQLDialectTest, PlaceholderStyle)
@ mongodb
Indicates a MongoDB database (future implementation).
@ sqlite
Indicates a SQLite database.
@ redis
Indicates a Redis database (future implementation).
@ postgres
Indicates a PostgreSQL database.
#define TEST(name)