Skip to content

Commit 45e7d8a

Browse files
authored
Fix CREATE/DROP DATABASE related issues. (#88)
1 parent 00178eb commit 45e7d8a

File tree

6 files changed

+127
-24
lines changed

6 files changed

+127
-24
lines changed

src/EFCore.Jet.Data/JetConnection.cs

+1-3
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,7 @@ public override int ConnectionTimeout
210210
/// </returns>
211211
protected override DbCommand CreateDbCommand()
212212
{
213-
var command = JetFactory.CreateCommand();
214-
command.Connection = this;
215-
return command;
213+
return CreateCommand(null);
216214
}
217215

218216
/// <summary>

src/EFCore.Jet.Data/JetStoreSchemaDefinition/JetStoreDatabaseHandling.cs

+17-8
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,10 @@ static JetStoreDatabaseHandling()
5353
RegexOptions.IgnoreCase);
5454

5555
// Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Joe's Database.accdb;
56+
// Provider=Microsoft.ACE.OLEDB.12.0;Data Source="Joe's Database.accdb";
57+
// Provider=Microsoft.ACE.OLEDB.12.0;Data Source='Joe''s Database.accdb';
5658
_regExExtractFilenameFromConnectionString = new Regex(
57-
@"^(?:.*;)?\s*(?:data source|dbq)\s*=\s*(?<filename>.*?)\s*(?:;|$)",
59+
@"^(?:.*;)?\s*(?:data source|dbq)\s*=\s*(?:(?<quote>[""'])\s*(?<filename>.*?)\s*\k<quote>|(?<filename>.*?))\s*(?:;|$)",
5860
RegexOptions.IgnoreCase);
5961

6062
// CREATE DATABASE Joe's Database.accdb;
@@ -171,14 +173,21 @@ public static bool ProcessDatabaseOperation(JetCommand command)
171173

172174
public static string ExtractFileNameFromConnectionString(string connectionString)
173175
{
174-
string fileName;
175-
Match match = _regExExtractFilenameFromConnectionString.Match(connectionString);
176+
var match = _regExExtractFilenameFromConnectionString.Match(connectionString);
176177
if (match.Success)
177-
fileName = match.Groups["filename"]
178-
.Value;
179-
else
180-
fileName = connectionString;
181-
return fileName;
178+
{
179+
var fileName = match.Groups["filename"].Value;
180+
181+
if (match.Groups["quote"].Success)
182+
{
183+
var quoteChar = match.Groups["quote"].Value;
184+
fileName = fileName.Replace(quoteChar + quoteChar, quoteChar);
185+
}
186+
187+
return fileName;
188+
}
189+
190+
return connectionString;
182191
}
183192

184193
public static bool IsConnectionString(string connectionString)

src/EFCore.Jet/Storage/Internal/JetDatabaseCreator.cs

+12-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.Generic;
55
using System.Threading;
66
using System.Threading.Tasks;
7+
using EntityFrameworkCore.Jet.Data;
78
using EntityFrameworkCore.Jet.Internal;
89
using EntityFrameworkCore.Jet.Migrations.Operations;
910
using JetBrains.Annotations;
@@ -101,7 +102,17 @@ private IRelationalCommand CreateHasTablesCommand()
101102
=> _rawSqlCommandBuilder.Build(@"SELECT * FROM `INFORMATION_SCHEMA.TABLES` WHERE TABLE_TYPE IN ('BASE TABLE', 'VIEW')");
102103

103104
private IReadOnlyList<MigrationCommand> CreateCreateOperations()
104-
=> Dependencies.MigrationsSqlGenerator.Generate(new[] {new JetCreateDatabaseOperation {Name = _relationalConnection.DbConnection.DataSource}});
105+
{
106+
var dataSource = _relationalConnection.DbConnection.DataSource;
107+
108+
// Alternative:
109+
// var connection = (JetConnection) _relationalConnection.DbConnection;
110+
// var csb = connection.JetFactory.CreateConnectionStringBuilder();
111+
// csb.ConnectionString = connection.ConnectionString;
112+
// var dataSource = csb.GetDataSource();
113+
114+
return Dependencies.MigrationsSqlGenerator.Generate(new[] {new JetCreateDatabaseOperation {Name = dataSource}});
115+
}
105116

106117
/// <summary>
107118
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System.Data.Odbc;
2+
using Microsoft.VisualStudio.TestTools.UnitTesting;
3+
4+
namespace EntityFrameworkCore.Jet.Data.Tests
5+
{
6+
[TestClass]
7+
public class ConnectionStringTest
8+
{
9+
[TestMethod]
10+
public void Escape_double_quoted_connection_string()
11+
{
12+
var expectedDatabaseName = "Joe's \"Recipes\" Database.accdb";
13+
var escapedDatabaseName = expectedDatabaseName.Replace("\"", "\"\"");
14+
15+
var connectionString = Helpers.DataAccessProviderFactory is OdbcFactory
16+
? $"DBQ=\"{escapedDatabaseName}\""
17+
: $"Data Source=\"{escapedDatabaseName}\"";
18+
19+
var csb = Helpers.DataAccessProviderFactory.CreateConnectionStringBuilder();
20+
csb.ConnectionString = connectionString;
21+
22+
var actualDatabaseName = csb.GetDataSource();
23+
24+
Assert.AreEqual(expectedDatabaseName, actualDatabaseName);
25+
}
26+
27+
[TestMethod]
28+
public void Escape_single_quoted_connection_string()
29+
{
30+
var expectedDatabaseName = "Joe's \"Recipes\" Database.accdb";
31+
var escapedDatabaseName = expectedDatabaseName.Replace("'", "''");
32+
33+
var connectionString = Helpers.DataAccessProviderFactory is OdbcFactory
34+
? $"DBQ='{escapedDatabaseName}'"
35+
: $"Data Source='{escapedDatabaseName}'";
36+
37+
using var connection = new JetConnection(connectionString, Helpers.DataAccessProviderFactory);
38+
39+
var csb = Helpers.DataAccessProviderFactory.CreateConnectionStringBuilder();
40+
csb.ConnectionString = connectionString;
41+
42+
var actualDatabaseName = csb.GetDataSource();
43+
44+
Assert.AreEqual(expectedDatabaseName, actualDatabaseName);
45+
}
46+
}
47+
}

test/EFCore.Jet.Data.Tests/CreateDatabaseTest.cs

+48-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Diagnostics;
1+
using System;
2+
using System.Data.Odbc;
23
using System.IO;
34
using Microsoft.VisualStudio.TestTools.UnitTesting;
45

@@ -25,31 +26,67 @@ public void TearDown()
2526
public void CreateAndDropDatabaseFromConnection()
2627
{
2728
using var connection = new JetConnection(StoreName, Helpers.DataAccessProviderFactory);
29+
2830
connection.CreateDatabase();
29-
3031
Assert.IsTrue(File.Exists(StoreName));
31-
32-
using var command = connection.CreateCommand();
33-
command.CommandText = "DROP DATABASE " + StoreName;
34-
command.ExecuteNonQuery();
35-
32+
33+
connection.DropDatabase();
3634
Assert.IsFalse(File.Exists(StoreName));
3735
}
3836

3937
[TestMethod]
4038
public void CreateAndDropDatabaseWithUnsetConnection()
4139
{
42-
using var connection = new JetConnection();
40+
using var connection = new JetConnection(Helpers.DataAccessProviderFactory);
4341

4442
var command = connection.CreateCommand();
45-
command.CommandText = "CREATE DATABASE " + StoreName;
43+
command.CommandText = $"CREATE DATABASE '{StoreName}'";
4644
command.ExecuteNonQuery();
47-
4845
Assert.IsTrue(File.Exists(StoreName));
4946

50-
command.CommandText = "DROP DATABASE " + StoreName;
47+
command.CommandText = $"DROP DATABASE '{StoreName}'";
5148
command.ExecuteNonQuery();
49+
Assert.IsFalse(File.Exists(StoreName));
50+
}
51+
52+
[TestMethod]
53+
public void CreateAndDropDatabaseWithUnsetConnectionWithoutDataAccessProviderFactoryThrows()
54+
{
55+
using var connection = new JetConnection();
56+
57+
Assert.ThrowsException<InvalidOperationException>(
58+
() => { using var command = connection.CreateCommand(); });
59+
}
60+
61+
[TestMethod]
62+
public void CreateAndDropDatabaseWithSingleQuotedConnectionString()
63+
{
64+
var connectionString = Helpers.DataAccessProviderFactory is OdbcFactory
65+
? $"DBQ='{StoreName}'"
66+
: $"Data Source='{StoreName}'";
67+
68+
using var connection = new JetConnection(connectionString, Helpers.DataAccessProviderFactory);
69+
70+
connection.CreateDatabase();
71+
Assert.IsTrue(File.Exists(StoreName));
72+
73+
connection.DropDatabase();
74+
Assert.IsFalse(File.Exists(StoreName));
75+
}
76+
77+
[TestMethod]
78+
public void CreateAndDropDatabaseWithDoubleQuotedConnectionString()
79+
{
80+
var connectionString = Helpers.DataAccessProviderFactory is OdbcFactory
81+
? $"DBQ=\"{StoreName}\""
82+
: $"Data Source=\"{StoreName}\"";
5283

84+
using var connection = new JetConnection(connectionString, Helpers.DataAccessProviderFactory);
85+
86+
connection.CreateDatabase();
87+
Assert.IsTrue(File.Exists(StoreName));
88+
89+
connection.DropDatabase();
5390
Assert.IsFalse(File.Exists(StoreName));
5491
}
5592
}

test/EFCore.Jet.Tests/TestBase.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ public class TestBase<TContext> : IDisposable
1111
{
1212
public TestBase()
1313
{
14-
TestStore = JetTestStore.CreateInitialized(nameof(JetMigrationTest));
14+
TestStore = JetTestStore.CreateInitialized(StoreName);
1515
}
1616

1717
public virtual void Dispose() => TestStore.Dispose();
1818

19+
public virtual string StoreName => GetType().Name;
1920
public virtual JetTestStore TestStore { get; }
2021
public virtual List<string> SqlCommands { get; } = new List<string>();
2122
public virtual string Sql => string.Join("\n\n", SqlCommands);

0 commit comments

Comments
 (0)