Skip to content

Commit 7ccf0a3

Browse files
committed
Minor fixes if the user passed Schema.Table.Name tablename notation. Currently, RepoDB is looking at it as Database.Schema.TableName.
1 parent 7cf481e commit 7ccf0a3

7 files changed

Lines changed: 362 additions & 45 deletions

File tree

‎src/Providers/RepoDb.Core/RepoDb.Tests/RepoDb.IntegrationTests/DottedTableTest.cs‎

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,80 @@ public async Task TestQueryAllAsyncDottedTable()
448448

449449
#endregion
450450

451+
#region Table Name
452+
453+
[TestMethod]
454+
[DataRow("[dbo].[Dotted.Table]")]
455+
[DataRow("dbo.[Dotted.Table]")]
456+
[DataRow("[Dotted.Table]")]
457+
public void TestQueryAllByTableNameOfDottedTable(string tableName)
458+
{
459+
// Setup
460+
var entities = Helper.CreateDottedTables(10);
461+
462+
using (var connection = new SqlConnection(Database.ConnectionString))
463+
{
464+
// Act
465+
connection.InsertAll<DottedTable>(entities);
466+
var queryAllResult = connection.QueryAll(tableName);
467+
468+
// Assert
469+
Assert.AreEqual(entities.Count, queryAllResult.Count());
470+
}
471+
}
472+
473+
[TestMethod]
474+
[DataRow("[dbo].[Dotted.Table]")]
475+
[DataRow("dbo.[Dotted.Table]")]
476+
[DataRow("[Dotted.Table]")]
477+
public async Task TestQueryAllAsyncByTableNameOfDottedTable(string tableName)
478+
{
479+
// Setup
480+
var entities = Helper.CreateDottedTables(10);
481+
482+
using (var connection = new SqlConnection(Database.ConnectionString))
483+
{
484+
// Act
485+
await connection.InsertAllAsync<DottedTable>(entities).ConfigureAwait(false);
486+
var queryAllResult = await connection.QueryAllAsync(tableName).ConfigureAwait(false);
487+
488+
// Assert
489+
Assert.AreEqual(entities.Count, queryAllResult.Count());
490+
}
491+
}
492+
493+
[TestMethod]
494+
[DataRow("[dbo].[Dotted.Table]")]
495+
[DataRow("dbo.[Dotted.Table]")]
496+
[DataRow("[Dotted.Table]")]
497+
public void TestCountByTableNameOfDottedTable(string tableName)
498+
{
499+
// Setup
500+
var entities = Helper.CreateDottedTables(10);
501+
502+
using (var connection = new SqlConnection(Database.ConnectionString))
503+
{
504+
// Act
505+
connection.InsertAll<DottedTable>(entities);
506+
var count = connection.Count(tableName, where: (object)null);
507+
508+
// Assert
509+
Assert.AreEqual(entities.Count, count);
510+
}
511+
}
512+
513+
[TestMethod]
514+
public void ThrowExceptionOnQueryAllByTableNameOfDottedTableThatIsNotQuoted()
515+
{
516+
using (var connection = new SqlConnection(Database.ConnectionString))
517+
{
518+
// Act/Assert (the unquoted "dbo.Dotted.Table" is read as Database.Schema.Table)
519+
Assert.ThrowsExactly<SqlException>(() => connection.QueryAll("dbo.Dotted.Table"));
520+
}
521+
}
522+
523+
#endregion
524+
451525
#region Update
452526

453527
[TestMethod]

‎src/Providers/RepoDb.Core/RepoDb.Tests/RepoDb.UnitTests/Others/QuotationTest.cs‎

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,136 @@ public void TestAsQuotedWithDottedTableWithUnquotedDatabaseAndSchema()
121121

122122
#endregion
123123

124+
#region AsQuoted (Dotted Names)
125+
126+
[TestMethod]
127+
public void TestAsQuotedWithDottedTableWithoutSchema()
128+
{
129+
// Setup
130+
var text = "[Dotted.Name]";
131+
132+
// Act
133+
var actual = text.AsQuoted(true, m_dbSetting);
134+
var expected = "[Dotted.Name]";
135+
136+
// Assert
137+
Assert.AreEqual(expected, actual, StringComparer.Ordinal);
138+
}
139+
140+
[TestMethod]
141+
public void TestAsQuotedWithDottedTableWithoutSchemaAndWithManyDots()
142+
{
143+
// Setup
144+
var text = "[Dotted.Table.Name]";
145+
146+
// Act
147+
var actual = text.AsQuoted(true, m_dbSetting);
148+
var expected = "[Dotted.Table.Name]";
149+
150+
// Assert
151+
Assert.AreEqual(expected, actual, StringComparer.Ordinal);
152+
}
153+
154+
[TestMethod]
155+
public void TestAsQuotedWithDottedTableWithManyDots()
156+
{
157+
// Setup
158+
var text = "[Schema].[Dotted.Table.Name]";
159+
160+
// Act
161+
var actual = text.AsQuoted(true, m_dbSetting);
162+
var expected = "[Schema].[Dotted.Table.Name]";
163+
164+
// Assert
165+
Assert.AreEqual(expected, actual, StringComparer.Ordinal);
166+
}
167+
168+
[TestMethod]
169+
public void TestAsQuotedWithDottedTableWithManyDotsAndUnquotedSchema()
170+
{
171+
// Setup
172+
var text = "Schema.[Dotted.Table.Name]";
173+
174+
// Act
175+
var actual = text.AsQuoted(true, m_dbSetting);
176+
var expected = "[Schema].[Dotted.Table.Name]";
177+
178+
// Assert
179+
Assert.AreEqual(expected, actual, StringComparer.Ordinal);
180+
}
181+
182+
[TestMethod]
183+
public void TestAsQuotedWithDottedTableWithManyDotsWithDatabase()
184+
{
185+
// Setup
186+
var text = "[Database].[Schema].[Dotted.Table.Name]";
187+
188+
// Act
189+
var actual = text.AsQuoted(true, m_dbSetting);
190+
var expected = "[Database].[Schema].[Dotted.Table.Name]";
191+
192+
// Assert
193+
Assert.AreEqual(expected, actual, StringComparer.Ordinal);
194+
}
195+
196+
[TestMethod]
197+
public void TestAsQuotedWithDottedSchema()
198+
{
199+
// Setup
200+
var text = "[Dotted.Schema].[Name]";
201+
202+
// Act
203+
var actual = text.AsQuoted(true, m_dbSetting);
204+
var expected = "[Dotted.Schema].[Name]";
205+
206+
// Assert
207+
Assert.AreEqual(expected, actual, StringComparer.Ordinal);
208+
}
209+
210+
[TestMethod]
211+
public void TestAsQuotedWithDottedSchemaAndDottedTable()
212+
{
213+
// Setup
214+
var text = "[Dotted.Schema].[Dotted.Name]";
215+
216+
// Act
217+
var actual = text.AsQuoted(true, m_dbSetting);
218+
var expected = "[Dotted.Schema].[Dotted.Name]";
219+
220+
// Assert
221+
Assert.AreEqual(expected, actual, StringComparer.Ordinal);
222+
}
223+
224+
[TestMethod]
225+
public void TestAsQuotedWithDottedTableAndUnquotedDatabaseAndSchema()
226+
{
227+
// Setup
228+
var text = "Database.Schema.[Dotted.Table.Name]";
229+
230+
// Act
231+
var actual = text.AsQuoted(true, m_dbSetting);
232+
var expected = "[Database].[Schema].[Dotted.Table.Name]";
233+
234+
// Assert
235+
Assert.AreEqual(expected, actual, StringComparer.Ordinal);
236+
}
237+
238+
[TestMethod]
239+
public void TestAsQuotedWithUnquotedDottedNameIsReadAsDatabaseSchemaAndTable()
240+
{
241+
// Setup
242+
var text = "dbo.Dotted.Table";
243+
244+
// Act
245+
var actual = text.AsQuoted(true, m_dbSetting);
246+
var expected = "[dbo].[Dotted].[Table]";
247+
248+
// Assert
249+
Assert.AreEqual(expected, actual, StringComparer.Ordinal);
250+
}
251+
252+
#endregion
253+
124254
#region AsUnQuoted
125255

126256
[TestMethod]

‎src/Providers/RepoDb.Core/RepoDb/Extensions/StringExtension.cs‎

Lines changed: 32 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -230,67 +230,56 @@ public static string AsQuoted(this string value,
230230
}
231231

232232
/// <summary>
233-
/// Quotes a string for database, schema and a table.
233+
/// Quotes a string for database, schema and a table. The parts are separated by the periods that are not inside of a quoted part, so a quoted
234+
/// part can have periods on its own (i.e.: <c>[dbo].[Dotted.Table.Name]</c>). An unquoted value is read as <c>Database.Schema.Table</c>.
234235
/// </summary>
235236
/// <param name="value">The string value to be quoted.</param>
236237
/// <param name="dbSetting">The currently in used <see cref="IDbSetting"/> object.</param>
237238
/// <returns>The quoted string.</returns>
238239
private static string AsQuotedForDatabaseSchemaTableInternal(this string value,
239240
IDbSetting dbSetting)
240241
{
241-
var splitted = value.Split(CharConstant.Period);
242-
if (splitted.Length > 2)
242+
var parts = new List<string>();
243+
var current = new System.Text.StringBuilder(value.Length);
244+
var opening = dbSetting.OpeningQuote;
245+
var closing = dbSetting.ClosingQuote;
246+
var isQuoted = false;
247+
var index = 0;
248+
while (index < value.Length)
243249
{
244-
var list = new List<string>(splitted.Length);
245-
var current = default(string);
246-
foreach (var item in splitted)
250+
if (isQuoted)
247251
{
248-
if (!string.IsNullOrWhiteSpace(current))
252+
if (string.CompareOrdinal(value, index, closing, 0, closing.Length) == 0)
249253
{
250-
if (current.StartsWith(dbSetting.OpeningQuote, StringComparison.OrdinalIgnoreCase)
251-
&& item.EndsWith(dbSetting.ClosingQuote, StringComparison.OrdinalIgnoreCase))
252-
{
253-
list.Add(string.Concat(current, CharConstant.Period, item));
254-
current = null;
255-
}
256-
}
257-
else
258-
{
259-
if (item.StartsWith(dbSetting.OpeningQuote, StringComparison.OrdinalIgnoreCase))
260-
{
261-
if (item.EndsWith(dbSetting.ClosingQuote, StringComparison.OrdinalIgnoreCase))
262-
{
263-
list.Add(item.AsQuotedInternal(dbSetting));
264-
}
265-
else
266-
{
267-
current = item;
268-
}
269-
}
270-
else
271-
{
272-
list.Add(item.AsQuotedInternal(dbSetting));
273-
}
254+
current.Append(closing);
255+
index += closing.Length;
256+
isQuoted = false;
257+
continue;
274258
}
275259
}
276-
if (current != null)
260+
else if (current.Length == 0 && string.CompareOrdinal(value, index, opening, 0, opening.Length) == 0)
277261
{
278-
list.Add(current.AsQuotedInternal(dbSetting));
262+
current.Append(opening);
263+
index += opening.Length;
264+
isQuoted = true;
265+
continue;
279266
}
280-
#if NETSTANDARD2_0
281-
return string.Join(CharConstant.Period.ToString(), list);
282-
#else
283-
return string.Join(CharConstant.Period, list);
284-
#endif
267+
else if (value[index] == CharConstant.Period)
268+
{
269+
parts.Add(current.ToString());
270+
current.Clear();
271+
index++;
272+
continue;
273+
}
274+
current.Append(value[index]);
275+
index++;
285276
}
286-
else
287-
{
277+
parts.Add(current.ToString());
288278
#if NETSTANDARD2_0
289-
return string.Join(CharConstant.Period.ToString(), splitted.Select(item => item.AsQuotedInternal(dbSetting)));
279+
return string.Join(CharConstant.Period.ToString(), parts.Select(part => part.AsQuotedInternal(dbSetting)));
290280
#else
291-
return string.Join(CharConstant.Period, splitted.Select(item => item.AsQuotedInternal(dbSetting)));
281+
return string.Join(CharConstant.Period, parts.Select(part => part.AsQuotedInternal(dbSetting)));
292282
#endif
293-
}
294283
}
295284

296285
/// <summary>

‎src/Providers/RepoDb.Sqlite.Ahtola/RepoDb.Sqlite.Ahtola.IntegrationTests/DbHelperTests.cs‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,5 +254,67 @@ public async Task TestDbHelperGetScopeIdentityAsync()
254254
#endregion
255255

256256
#endregion
257+
258+
#region GetFields (Dotted Table)
259+
260+
[TestMethod]
261+
[DataRow("[Dotted.Table]")]
262+
[DataRow("main.[Dotted.Table]")]
263+
[DataRow("[main].[Dotted.Table]")]
264+
public void TestDbHelperGetFieldsOfDottedTable(string tableName)
265+
{
266+
using (var connection = new SqliteConnection(Database.ConnectionString))
267+
{
268+
// Setup
269+
var helper = connection.GetDbHelper();
270+
connection.ExecuteNonQuery("DROP TABLE IF EXISTS [Dotted.Table]; CREATE TABLE [Dotted.Table] ([Id] INTEGER PRIMARY KEY, [Column.Name] TEXT);");
271+
272+
try
273+
{
274+
// Act
275+
var fields = helper.GetFields(connection, tableName, null).ToList();
276+
277+
// Assert
278+
Assert.AreEqual(2, fields.Count);
279+
Assert.IsTrue(fields.Any(f => string.Equals(f.Name, "Id", StringComparison.OrdinalIgnoreCase) && f.IsPrimary));
280+
Assert.IsTrue(fields.Any(f => string.Equals(f.Name, "Column.Name", StringComparison.OrdinalIgnoreCase)));
281+
}
282+
finally
283+
{
284+
connection.ExecuteNonQuery("DROP TABLE IF EXISTS [Dotted.Table];");
285+
}
286+
}
287+
}
288+
289+
[TestMethod]
290+
[DataRow("[Dotted.Table]")]
291+
[DataRow("main.[Dotted.Table]")]
292+
[DataRow("[main].[Dotted.Table]")]
293+
public async Task TestDbHelperGetFieldsAsyncOfDottedTable(string tableName)
294+
{
295+
using (var connection = new SqliteConnection(Database.ConnectionString))
296+
{
297+
// Setup
298+
var helper = connection.GetDbHelper();
299+
connection.ExecuteNonQuery("DROP TABLE IF EXISTS [Dotted.Table]; CREATE TABLE [Dotted.Table] ([Id] INTEGER PRIMARY KEY, [Column.Name] TEXT);");
300+
301+
try
302+
{
303+
// Act
304+
var fields = (await helper.GetFieldsAsync(connection, tableName, null).ConfigureAwait(false)).ToList();
305+
306+
// Assert
307+
Assert.AreEqual(2, fields.Count);
308+
Assert.IsTrue(fields.Any(f => string.Equals(f.Name, "Id", StringComparison.OrdinalIgnoreCase) && f.IsPrimary));
309+
Assert.IsTrue(fields.Any(f => string.Equals(f.Name, "Column.Name", StringComparison.OrdinalIgnoreCase)));
310+
}
311+
finally
312+
{
313+
connection.ExecuteNonQuery("DROP TABLE IF EXISTS [Dotted.Table];");
314+
}
315+
}
316+
}
317+
318+
#endregion
257319
}
258320
}

‎src/Providers/RepoDb.Sqlite.Ahtola/RepoDb.Sqlite.Ahtola/DbHelpers/AhtolaDbHelper.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public AhtolaDbHelper(IDbSetting dbSetting,
6262
/// <param name="tableName"></param>
6363
/// <returns></returns>
6464
private string GetCommandText(string tableName) =>
65-
$"pragma table_info({DataEntityExtension.GetTableName(tableName, DbSetting).AsUnquoted(DbSetting)});";
65+
$"pragma table_info({DataEntityExtension.GetTableName(tableName, DbSetting).AsQuoted(DbSetting)});";
6666

6767
/// <summary>
6868
///

0 commit comments

Comments
 (0)