diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleDatabaseTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleDatabaseTests.cs index a9ea2f0f..bd1078c9 100644 --- a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleDatabaseTests.cs +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleDatabaseTests.cs @@ -1,14 +1,46 @@ -using PetaPoco.Tests.Integration.Providers; +using System.Reflection; +using PetaPoco.Tests.Integration.Providers; using Xunit; namespace PetaPoco.Tests.Integration.Databases.Oracle { - [Collection("Oracle")] - public class OracleDatabaseTests : DatabaseTests + public abstract partial class OracleDatabaseTests : DatabaseTests { - public OracleDatabaseTests() - : base(new OracleTestProvider()) + protected OracleDatabaseTests(TestProvider provider) + : base(provider) { } + + [Collection("Oracle.Delimited")] + public class Delimited : OracleDatabaseTests + { + public Delimited() + : base(new OracleDelimitedTestProvider()) + { + } + } + + [Collection("Oracle.Ordinary")] + public class Ordinary : OracleDatabaseTests + { + public Ordinary() + : base(new OracleOrdinaryTestProvider()) + { + } + + /// + /// We need to retain the custom provider and default mapper specified in the test provider + /// to ensure correct logic is applied. + /// + protected override void AfterDbCreate(Database db) + { + base.AfterDbCreate(db); + + // ReSharper disable once PossibleNullReferenceException + db.GetType().GetField("_provider", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(db, DB.Provider); + // ReSharper disable once PossibleNullReferenceException + db.GetType().GetField("_defaultMapper", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(db, DB.DefaultMapper); + } + } } } diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleDeleteTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleDeleteTests.cs index 2ff1a9c7..da6ffc68 100644 --- a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleDeleteTests.cs +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleDeleteTests.cs @@ -3,12 +3,29 @@ namespace PetaPoco.Tests.Integration.Databases.Oracle { - [Collection("Oracle")] - public class OracleDeleteTests : DeleteTests + public abstract partial class OracleDeleteTests : DeleteTests { - public OracleDeleteTests() - : base(new OracleTestProvider()) + protected OracleDeleteTests(TestProvider provider) + : base(provider) { } + + [Collection("Oracle.Delimited")] + public class Delimited : OracleDeleteTests + { + public Delimited() + : base(new OracleDelimitedTestProvider()) + { + } + } + + [Collection("Oracle.Ordinary")] + public class Ordinary : OracleDeleteTests + { + public Ordinary() + : base(new OracleOrdinaryTestProvider()) + { + } + } } } diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleExecuteTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleExecuteTests.cs index 8e747d91..ea4de56d 100644 --- a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleExecuteTests.cs +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleExecuteTests.cs @@ -3,12 +3,29 @@ namespace PetaPoco.Tests.Integration.Databases.Oracle { - [Collection("Oracle")] - public class OracleExecuteTests : ExecuteTests + public abstract partial class OracleExecuteTests : ExecuteTests { - public OracleExecuteTests() - : base(new OracleTestProvider()) + protected OracleExecuteTests(TestProvider provider) + : base(provider) { } + + [Collection("Oracle.Delimited")] + public class Delimited : OracleExecuteTests + { + public Delimited() + : base(new OracleDelimitedTestProvider()) + { + } + } + + [Collection("Oracle.Ordinary")] + public class Ordinary : OracleExecuteTests + { + public Ordinary() + : base(new OracleOrdinaryTestProvider()) + { + } + } } } diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleInsertTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleInsertTests.cs index 9b43b6a1..fd96a488 100644 --- a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleInsertTests.cs +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleInsertTests.cs @@ -5,12 +5,29 @@ namespace PetaPoco.Tests.Integration.Databases.Oracle { - [Collection("Oracle")] - public class OracleInsertTests : InsertTests + public abstract partial class OracleInsertTests : InsertTests { - public OracleInsertTests() - : base(new OracleTestProvider()) + protected OracleInsertTests(TestProvider provider) + : base(provider) { } + + [Collection("Oracle.Delimited")] + public class Delimited : OracleInsertTests + { + public Delimited() + : base(new OracleDelimitedTestProvider()) + { + } + } + + [Collection("Oracle.Ordinary")] + public class Ordinary : OracleInsertTests + { + public Ordinary() + : base(new OracleOrdinaryTestProvider()) + { + } + } } } diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleMiscellaneousTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleMiscellaneousTests.cs index 8cc0bb05..20ce859b 100644 --- a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleMiscellaneousTests.cs +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleMiscellaneousTests.cs @@ -3,12 +3,29 @@ namespace PetaPoco.Tests.Integration.Databases.Oracle { - [Collection("Oracle")] - public class OracleMiscellaneousTests : MiscellaneousTests + public abstract partial class OracleMiscellaneousTests : MiscellaneousTests { - public OracleMiscellaneousTests() - : base(new OracleTestProvider()) + protected OracleMiscellaneousTests(TestProvider provider) + : base(provider) { } + + [Collection("Oracle.Delimited")] + public class Delimited : OracleMiscellaneousTests + { + public Delimited() + : base(new OracleDelimitedTestProvider()) + { + } + } + + [Collection("Oracle.Ordinary")] + public class Ordinary : OracleMiscellaneousTests + { + public Ordinary() + : base(new OracleOrdinaryTestProvider()) + { + } + } } } diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OraclePreExecuteTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OraclePreExecuteTests.cs index 976906df..e88f36a1 100644 --- a/PetaPoco.Tests.Integration/Databases/OracleTests/OraclePreExecuteTests.cs +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OraclePreExecuteTests.cs @@ -6,36 +6,79 @@ namespace PetaPoco.Tests.Integration.Databases.Oracle { - [Collection("Oracle")] - public class OraclePreExecuteTests : PreExecuteTests + public abstract partial class OraclePreExecuteTests : PreExecuteTests { - protected override IPreExecuteDatabaseProvider Provider => DB.Provider as PreExecuteDatabaseProvider; - - public OraclePreExecuteTests() - : base(new PreExecuteTestProvider()) + protected OraclePreExecuteTests(TestProvider provider) + : base(provider) { - Provider.ThrowExceptions = true; } - protected class PreExecuteTestProvider : OracleTestProvider + [Collection("Oracle.Delimited")] + public class Delimited : OraclePreExecuteTests { - protected override IDatabase LoadFromConnectionName(string name) - => BuildFromConnectionName(name).UsingProvider().Create(); + protected override IPreExecuteDatabaseProvider Provider => DB.Provider as PreExecuteDatabaseProvider; + + public Delimited() + : base(new PreExecuteTestProvider()) + { + Provider.ThrowExceptions = true; + } + + protected class PreExecuteTestProvider : OracleDelimitedTestProvider + { + protected override IDatabase LoadFromConnectionName(string name) + => BuildFromConnectionName(name).UsingProvider().Create(); + } + + protected class PreExecuteDatabaseProvider : PetaPoco.Providers.OracleDatabaseProvider, IPreExecuteDatabaseProvider + { + public bool ThrowExceptions { get; set; } + public List Parameters { get; set; } = new List(); + + public override void PreExecute(IDbCommand cmd) + { + Parameters.Clear(); + + if (ThrowExceptions) + { + Parameters = cmd.Parameters.Cast().ToList(); + throw new PreExecuteException(); + } + } + } } - protected class PreExecuteDatabaseProvider : PetaPoco.Providers.OracleDatabaseProvider, IPreExecuteDatabaseProvider + [Collection("Oracle.Ordinary")] + public class Ordinary : OraclePreExecuteTests { - public bool ThrowExceptions { get; set; } - public List Parameters { get; set; } = new List(); + protected override IPreExecuteDatabaseProvider Provider => DB.Provider as PreExecuteDatabaseProvider; + + public Ordinary() + : base(new PreExecuteTestProvider()) + { + Provider.ThrowExceptions = true; + } - public override void PreExecute(IDbCommand cmd) + protected class PreExecuteTestProvider : OracleOrdinaryTestProvider { - Parameters.Clear(); + protected override IDatabase LoadFromConnectionName(string name) + => BuildFromConnectionName(name).UsingProvider().Create(); + } - if (ThrowExceptions) + protected class PreExecuteDatabaseProvider : PetaPoco.Providers.OracleDatabaseProvider, IPreExecuteDatabaseProvider + { + public bool ThrowExceptions { get; set; } + public List Parameters { get; set; } = new List(); + + public override void PreExecute(IDbCommand cmd) { - Parameters = cmd.Parameters.Cast().ToList(); - throw new PreExecuteException(); + Parameters.Clear(); + + if (ThrowExceptions) + { + Parameters = cmd.Parameters.Cast().ToList(); + throw new PreExecuteException(); + } } } } diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleQueryLinqTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleQueryLinqTests.cs index 1e7db334..67090119 100644 --- a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleQueryLinqTests.cs +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleQueryLinqTests.cs @@ -3,12 +3,29 @@ namespace PetaPoco.Tests.Integration.Databases.Oracle { - [Collection("Oracle")] - public class OracleQueryLinqTests : QueryLinqTests + public abstract partial class OracleQueryLinqTests : QueryLinqTests { - public OracleQueryLinqTests() - : base(new OracleTestProvider()) + protected OracleQueryLinqTests(TestProvider provider) + : base(provider) { } + + [Collection("Oracle.Delimited")] + public class Delimited : OracleQueryLinqTests + { + public Delimited() + : base(new OracleDelimitedTestProvider()) + { + } + } + + [Collection("Oracle.Ordinary")] + public class Ordinary : OracleQueryLinqTests + { + public Ordinary() + : base(new OracleOrdinaryTestProvider()) + { + } + } } } diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleQueryTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleQueryTests.cs index 18c01da9..2de34e3c 100644 --- a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleQueryTests.cs +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleQueryTests.cs @@ -10,14 +10,31 @@ namespace PetaPoco.Tests.Integration.Databases.Oracle { - [Collection("Oracle")] - public class OracleQueryTests : QueryTests + public abstract partial class OracleQueryTests : QueryTests { - public OracleQueryTests() - : base(new OracleTestProvider()) + protected OracleQueryTests(TestProvider provider) + : base(provider) { } + [Collection("Oracle.Delimited")] + public class Delimited : OracleQueryTests + { + public Delimited() + : base(new OracleDelimitedTestProvider()) + { + } + } + + [Collection("Oracle.Ordinary")] + public class Ordinary : OracleQueryTests + { + public Ordinary() + : base(new OracleOrdinaryTestProvider()) + { + } + } + [Fact] public override async Task FetchAsyncWithPaging_ForDynamicTypeGivenSql_ShouldReturnValidDynamicTypeCollection() { diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleStoredProcTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleStoredProcTests.cs index 1cf298ef..8ee59f9a 100644 --- a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleStoredProcTests.cs +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleStoredProcTests.cs @@ -11,99 +11,138 @@ namespace PetaPoco.Tests.Integration.Databases.Oracle { - [Collection("Oracle")] - public class OracleStoredProcTests : StoredProcTests + public abstract partial class OracleStoredProcTests : StoredProcTests { protected override Type DataParameterType => typeof(OracleParameter); - public OracleStoredProcTests() - : base(new OracleTestProvider()) + + protected OracleStoredProcTests(TestProvider provider) + : base(provider) { } + [Collection("Oracle.Delimited")] + public class Delimited : OracleStoredProcTests + { + public Delimited() + : base(new OracleDelimitedTestProvider()) + { + } + } + + [Collection("Oracle.Ordinary")] + public class Ordinary : OracleStoredProcTests + { + public Ordinary() + : base(new OracleOrdinaryTestProvider()) + { + } + } + private IDataParameter GetOutputParameter() => new OracleParameter("p_out_cursor", OracleDbType.RefCursor, ParameterDirection.Output); [Fact] public override void QueryProc_NoParam_ShouldReturnAll() { - var results = DB.QueryProc("SelectPeople", GetOutputParameter()).ToArray(); + var results = DB.QueryProc(DB.Provider.EscapeTableName("SelectPeople"), GetOutputParameter()).ToArray(); results.Length.ShouldBe(6); } [Fact] public override void QueryProc_WithParam_ShouldReturnSome() { - var results = DB.QueryProc("SelectPeopleWithParam", new { age = 20 }, GetOutputParameter()).ToArray(); + var results = DB.QueryProc(DB.Provider.EscapeTableName("SelectPeopleWithParam"), new { age = 20 }, GetOutputParameter()).ToArray(); results.Length.ShouldBe(3); } [Fact] public override void QueryProc_WithDbParam_ShouldReturnSome() { - var results = DB.QueryProc("SelectPeopleWithParam", GetDataParameter(), GetOutputParameter()).ToArray(); + var results = DB.QueryProc(DB.Provider.EscapeTableName("SelectPeopleWithParam"), GetDataParameter(), GetOutputParameter()).ToArray(); results.Length.ShouldBe(3); } [Fact] public override void FetchProc_NoParam_ShouldReturnAll() { - var results = DB.FetchProc("SelectPeople", GetOutputParameter()); + var results = DB.FetchProc(DB.Provider.EscapeTableName("SelectPeople"), GetOutputParameter()); results.Count.ShouldBe(6); } [Fact] public override void FetchProc_WithParam_ShouldReturnSome() { - var results = DB.FetchProc("SelectPeopleWithParam", new { age = 20 }, GetOutputParameter()); + var results = DB.FetchProc(DB.Provider.EscapeTableName("SelectPeopleWithParam"), new { age = 20 }, GetOutputParameter()); results.Count.ShouldBe(3); } [Fact] public override void FetchProc_WithDbParam_ShouldReturnSome() { - var results = DB.FetchProc("SelectPeopleWithParam", GetDataParameter(), GetOutputParameter()); + var results = DB.FetchProc(DB.Provider.EscapeTableName("SelectPeopleWithParam"), GetDataParameter(), GetOutputParameter()); results.Count.ShouldBe(3); } [Fact] public override void ScalarProc_NoParam_ShouldReturnAll() { - var count = DB.ExecuteScalarProc("CountPeople", GetOutputParameter()); + var count = DB.ExecuteScalarProc(DB.Provider.EscapeTableName("CountPeople"), GetOutputParameter()); count.ShouldBe(6); } [Fact] public override void ScalarProc_WithParam_ShouldReturnSome() { - var count = DB.ExecuteScalarProc("CountPeopleWithParam", new { age = 20 }, GetOutputParameter()); + var count = DB.ExecuteScalarProc(DB.Provider.EscapeTableName("CountPeopleWithParam"), new { age = 20 }, GetOutputParameter()); count.ShouldBe(3); } [Fact] public override void ScalarProc_WithDbParam_ShouldReturnSome() { - var count = DB.ExecuteScalarProc("CountPeopleWithParam", GetDataParameter(), GetOutputParameter()); + var count = DB.ExecuteScalarProc(DB.Provider.EscapeTableName("CountPeopleWithParam"), GetDataParameter(), GetOutputParameter()); count.ShouldBe(3); } [Fact] public override void NonQueryProc_NoParam_ShouldUpdateAll() { - DB.ExecuteNonQueryProc("UpdatePeople"); + DB.ExecuteNonQueryProc(DB.Provider.EscapeTableName("UpdatePeople")); DB.Query($"WHERE {DB.Provider.EscapeSqlIdentifier("FullName")}='Updated'").Count().ShouldBe(6); } [Fact] public override void NonQueryProc_WithParam_ShouldUpdateSome() { - DB.ExecuteNonQueryProc("UpdatePeopleWithParam", new { age = 20 }); + DB.ExecuteNonQueryProc(DB.Provider.EscapeTableName("UpdatePeopleWithParam"), new { age = 20 }); DB.Query($"WHERE {DB.Provider.EscapeSqlIdentifier("FullName")}='Updated'").Count().ShouldBe(3); } [Fact] public override void NonQueryProc_WithDbParam_ShouldUpdateSome() { - DB.ExecuteNonQueryProc("UpdatePeopleWithParam", GetDataParameter()); + DB.ExecuteNonQueryProc(DB.Provider.EscapeTableName("UpdatePeopleWithParam"), GetDataParameter()); + DB.Query($"WHERE {DB.Provider.EscapeSqlIdentifier("FullName")}='Updated'").Count().ShouldBe(3); + } + + [Fact] + public override async Task NonQueryProcAsync_NoParam_ShouldUpdateAll() + { + await DB.ExecuteNonQueryProcAsync(DB.Provider.EscapeSqlIdentifier("UpdatePeople")); + DB.Query($"WHERE {DB.Provider.EscapeSqlIdentifier("FullName")}='Updated'").Count().ShouldBe(6); + } + + [Fact] + public override async Task NonQueryProcAsync_WithParam_ShouldUpdateSome() + { + await DB.ExecuteNonQueryProcAsync(DB.Provider.EscapeTableName("UpdatePeopleWithParam"), new { age = 20 }); + DB.Query($"WHERE {DB.Provider.EscapeSqlIdentifier("FullName")}='Updated'").Count().ShouldBe(3); + } + + [Fact] + public override async Task NonQueryProcAsync_WithDbParam_ShouldUpdateSome() + { + await DB.ExecuteNonQueryProcAsync(DB.Provider.EscapeTableName("UpdatePeopleWithParam"), GetDataParameter()); DB.Query($"WHERE {DB.Provider.EscapeSqlIdentifier("FullName")}='Updated'").Count().ShouldBe(3); } @@ -111,7 +150,7 @@ public override void NonQueryProc_WithDbParam_ShouldUpdateSome() public override async Task QueryProcAsync_NoParam_ShouldReturnAll() { var results = new List(); - await DB.QueryProcAsync(p => results.Add(p), "SelectPeople", GetOutputParameter()); + await DB.QueryProcAsync(p => results.Add(p), DB.Provider.EscapeTableName("SelectPeople"), GetOutputParameter()); results.Count.ShouldBe(6); } @@ -119,7 +158,7 @@ public override async Task QueryProcAsync_NoParam_ShouldReturnAll() public override async Task QueryProcAsync_WithParam_ShouldReturnSome() { var results = new List(); - await DB.QueryProcAsync(p => results.Add(p), "SelectPeopleWithParam", new { age = 20 }, GetOutputParameter()); + await DB.QueryProcAsync(p => results.Add(p), DB.Provider.EscapeTableName("SelectPeopleWithParam"), new { age = 20 }, GetOutputParameter()); results.Count.ShouldBe(3); } @@ -127,7 +166,7 @@ public override async Task QueryProcAsync_WithParam_ShouldReturnSome() public override async Task QueryProcAsync_WithDbParam_ShouldReturnSome() { var results = new List(); - await DB.QueryProcAsync(p => results.Add(p), "SelectPeopleWithParam", GetDataParameter(), GetOutputParameter()); + await DB.QueryProcAsync(p => results.Add(p), DB.Provider.EscapeTableName("SelectPeopleWithParam"), GetDataParameter(), GetOutputParameter()); results.Count.ShouldBe(3); } @@ -135,7 +174,7 @@ public override async Task QueryProcAsync_WithDbParam_ShouldReturnSome() public override async Task QueryProcAsyncReader_NoParam_ShouldReturnAll() { var results = new List(); - using (var reader = await DB.QueryProcAsync("SelectPeople", GetOutputParameter())) + using (var reader = await DB.QueryProcAsync(DB.Provider.EscapeTableName("SelectPeople"), GetOutputParameter())) { while (await reader.ReadAsync()) results.Add(reader.Poco); @@ -147,7 +186,7 @@ public override async Task QueryProcAsyncReader_NoParam_ShouldReturnAll() public override async Task QueryProcAsyncReader_WithParam_ShouldReturnSome() { var results = new List(); - using (var reader = await DB.QueryProcAsync("SelectPeopleWithParam", new { age = 20 }, GetOutputParameter())) + using (var reader = await DB.QueryProcAsync(DB.Provider.EscapeTableName("SelectPeopleWithParam"), new { age = 20 }, GetOutputParameter())) { while (await reader.ReadAsync()) results.Add(reader.Poco); @@ -159,7 +198,7 @@ public override async Task QueryProcAsyncReader_WithParam_ShouldReturnSome() public override async Task QueryProcAsyncReader_WithDbParam_ShouldReturnSome() { var results = new List(); - using (var reader = await DB.QueryProcAsync("SelectPeopleWithParam", GetDataParameter(), GetOutputParameter())) + using (var reader = await DB.QueryProcAsync(DB.Provider.EscapeTableName("SelectPeopleWithParam"), GetDataParameter(), GetOutputParameter())) { while (await reader.ReadAsync()) results.Add(reader.Poco); @@ -170,42 +209,42 @@ public override async Task QueryProcAsyncReader_WithDbParam_ShouldReturnSome() [Fact] public override async Task FetchProcAsync_NoParam_ShouldReturnAll() { - var results = await DB.FetchProcAsync("SelectPeople", GetOutputParameter()); + var results = await DB.FetchProcAsync(DB.Provider.EscapeTableName("SelectPeople"), GetOutputParameter()); results.Count.ShouldBe(6); } [Fact] public override async Task FetchProcAsync_WithParam_ShouldReturnSome() { - var results = await DB.FetchProcAsync("SelectPeopleWithParam", new { age = 20 }, GetOutputParameter()); + var results = await DB.FetchProcAsync(DB.Provider.EscapeTableName("SelectPeopleWithParam"), new { age = 20 }, GetOutputParameter()); results.Count.ShouldBe(3); } [Fact] public override async Task FetchProcAsync_WithDbParam_ShouldReturnSome() { - var results = await DB.FetchProcAsync("SelectPeopleWithParam", GetDataParameter(), GetOutputParameter()); + var results = await DB.FetchProcAsync(DB.Provider.EscapeTableName("SelectPeopleWithParam"), GetDataParameter(), GetOutputParameter()); results.Count.ShouldBe(3); } [Fact] public override async Task ScalarProcAsync_NoParam_ShouldReturnAll() { - var count = await DB.ExecuteScalarProcAsync("CountPeople", GetOutputParameter()); + var count = await DB.ExecuteScalarProcAsync(DB.Provider.EscapeTableName("CountPeople"), GetOutputParameter()); count.ShouldBe(6); } [Fact] public override async Task ScalarProcAsync_WithParam_ShouldReturnSome() { - var count = await DB.ExecuteScalarProcAsync("CountPeopleWithParam", new { age = 20 }, GetOutputParameter()); + var count = await DB.ExecuteScalarProcAsync(DB.Provider.EscapeTableName("CountPeopleWithParam"), new { age = 20 }, GetOutputParameter()); count.ShouldBe(3); } [Fact] public override async Task ScalarProcAsync_WithDbParam_ShouldReturnSome() { - var count = await DB.ExecuteScalarProcAsync("CountPeopleWithParam", GetDataParameter(), GetOutputParameter()); + var count = await DB.ExecuteScalarProcAsync(DB.Provider.EscapeTableName("CountPeopleWithParam"), GetDataParameter(), GetOutputParameter()); count.ShouldBe(3); } } diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleTriageTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleTriageTests.cs index 9d7617ed..747b98a3 100644 --- a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleTriageTests.cs +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleTriageTests.cs @@ -3,12 +3,29 @@ namespace PetaPoco.Tests.Integration.Databases.Oracle { - [Collection("Oracle")] - public class OracleTriageTests : TriageTests + public abstract partial class OracleTriageTests : TriageTests { - public OracleTriageTests() - : base(new OracleTestProvider()) + protected OracleTriageTests(TestProvider provider) + : base(provider) { } + + [Collection("Oracle.Delimited")] + public class Delimited : OracleTriageTests + { + public Delimited() + : base(new OracleDelimitedTestProvider()) + { + } + } + + [Collection("Oracle.Ordinary")] + public class Ordinary : OracleTriageTests + { + public Ordinary() + : base(new OracleOrdinaryTestProvider()) + { + } + } } } diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleUpdateTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleUpdateTests.cs index 914549a0..7e9b4385 100644 --- a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleUpdateTests.cs +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleUpdateTests.cs @@ -1,14 +1,80 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using PetaPoco.Core; +using PetaPoco.Tests.Integration.Models; using PetaPoco.Tests.Integration.Providers; +using Shouldly; using Xunit; namespace PetaPoco.Tests.Integration.Databases.Oracle { - [Collection("Oracle")] - public class OracleUpdateTests : UpdateTests + public abstract partial class OracleUpdateTests : UpdateTests { - public OracleUpdateTests() - : base(new OracleTestProvider()) + protected OracleUpdateTests(TestProvider provider) + : base(provider) { } + + [Collection("Oracle.Delimited")] + public class Delimited : OracleUpdateTests + { + public Delimited() + : base(new OracleDelimitedTestProvider()) + { + } + } + + [Collection("Oracle.Ordinary")] + public class Ordinary : OracleUpdateTests + { + public Ordinary() + : base(new OracleOrdinaryTestProvider()) + { + } + + [Fact] + public override void Update_GivenTablePrimaryKeyNameAndDynamicType_ShouldUpdate() + { + var pd = PocoData.ForType(typeof(Note), DB.DefaultMapper); + var tblNote = DB.Provider.EscapeTableName(pd.TableInfo.TableName); + + CreateNote(); + var entity = DB.Fetch($"SELECT * FROM {tblNote}").First(); + + entity.TEXT += " was updated"; + +#if !NETCOREAPP + DB.Update("Note", "Id", (object)entity); +#else + DB.Update("Note", "Id", entity); +#endif + + var entity2 = DB.Fetch($"SELECT * FROM {tblNote}").First(); + ((string)entity2.TEXT).ShouldContain("updated"); + } + + + [Fact] + public override async Task UpdateAsync_GivenTablePrimaryKeyNameAndDynamicType_ShouldUpdate() + { + var pd = PocoData.ForType(typeof(Note), DB.DefaultMapper); + var tblNote = DB.Provider.EscapeTableName(pd.TableInfo.TableName); + + await CreateNoteAsync(); + var entity = (await DB.FetchAsync($"SELECT * FROM {tblNote}")).First(); + + entity.TEXT += " was updated"; + +#if !NETCOREAPP + await DB.UpdateAsync("Note", "Id", (object)entity); +#else + await DB.UpdateAsync("Note", "Id", entity); +#endif + + var entity2 = (await DB.FetchAsync($"SELECT * FROM {tblNote}")).First(); + ((string)entity2.TEXT).ShouldContain("updated"); + } + } } } diff --git a/PetaPoco.Tests.Integration/Databases/UpdateTests.cs b/PetaPoco.Tests.Integration/Databases/UpdateTests.cs index 93571be2..5539b969 100644 --- a/PetaPoco.Tests.Integration/Databases/UpdateTests.cs +++ b/PetaPoco.Tests.Integration/Databases/UpdateTests.cs @@ -81,6 +81,8 @@ protected static void UpdateProperties(OrderLine orderLine) orderLine.Status = OrderLineStatus.Allocated; } + protected object CreateNote() => DB.Insert(_note); + protected Task CreateNoteAsync() => DB.InsertAsync(_note); #endregion [Fact] @@ -376,7 +378,7 @@ public virtual void Update_GivenTablePrimaryKeyNameAndDynamicType_ShouldNotThrow var pd = PocoData.ForType(typeof(Note), DB.DefaultMapper); var tblNote = DB.Provider.EscapeTableName(pd.TableInfo.TableName); - DB.Insert(_note); + CreateNote(); var entity = DB.Fetch($"SELECT * FROM {tblNote}").First(); #if !NETCOREAPP @@ -392,7 +394,7 @@ public virtual void Update_GivenTablePrimaryKeyNameAndDynamicType_ShouldUpdate() var pd = PocoData.ForType(typeof(Note), DB.DefaultMapper); var tblNote = DB.Provider.EscapeTableName(pd.TableInfo.TableName); - DB.Insert(_note); + CreateNote(); var entity = DB.Fetch($"SELECT * FROM {tblNote}").First(); entity.Text += " was updated"; @@ -699,7 +701,7 @@ public async Task UpdateAsync_GivenTablePrimaryKeyNameAndDynamicType_ShouldNotTh var pd = PocoData.ForType(typeof(Note), DB.DefaultMapper); var tblNote = DB.Provider.EscapeTableName(pd.TableInfo.TableName); - await DB.InsertAsync(_note); + await CreateNoteAsync(); var entity = (await DB.FetchAsync($"SELECT * FROM {tblNote}")).First(); // https://docs.shouldly.org/documentation/exceptions/throw#shouldthrowfuncoftask @@ -711,12 +713,12 @@ public async Task UpdateAsync_GivenTablePrimaryKeyNameAndDynamicType_ShouldNotTh } [Fact] - public async Task UpdateAsync_GivenTablePrimaryKeyNameAndDynamicType_ShouldUpdate() + public virtual async Task UpdateAsync_GivenTablePrimaryKeyNameAndDynamicType_ShouldUpdate() { var pd = PocoData.ForType(typeof(Note), DB.DefaultMapper); var tblNote = DB.Provider.EscapeTableName(pd.TableInfo.TableName); - await DB.InsertAsync(_note); + await CreateNoteAsync(); var entity = (await DB.FetchAsync($"SELECT * FROM {tblNote}")).First(); entity.Text += " was updated"; diff --git a/PetaPoco.Tests.Integration/PetaPoco.Tests.Integration.csproj b/PetaPoco.Tests.Integration/PetaPoco.Tests.Integration.csproj index 4dec1f97..0696e830 100644 --- a/PetaPoco.Tests.Integration/PetaPoco.Tests.Integration.csproj +++ b/PetaPoco.Tests.Integration/PetaPoco.Tests.Integration.csproj @@ -22,8 +22,10 @@ - - + + + + @@ -35,8 +37,10 @@ - - + + + + diff --git a/PetaPoco.Tests.Integration/Providers/OracleTestProvider.cs b/PetaPoco.Tests.Integration/Providers/OracleTestProvider.cs index 0a42aebb..6c7b1e8b 100644 --- a/PetaPoco.Tests.Integration/Providers/OracleTestProvider.cs +++ b/PetaPoco.Tests.Integration/Providers/OracleTestProvider.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Reflection; using Oracle.ManagedDataAccess.Client; /* @@ -9,20 +10,22 @@ namespace PetaPoco.Tests.Integration.Providers { - public class OracleTestProvider : TestProvider + public abstract class OracleTestProvider : TestProvider { private static readonly string[] _splitSemiColon = new[] { ";" }; private static readonly string[] _splitNewLine = new[] { Environment.NewLine }; private static readonly string[] _splitSlash = new[] { Environment.NewLine + "/" }; private static readonly string[] _resources = new[] { - "PetaPoco.Tests.Integration.Scripts.OracleSetupDatabase.sql", - "PetaPoco.Tests.Integration.Scripts.OracleBuildDatabase.sql" + "PetaPoco.Tests.Integration.Scripts.OracleSetupDatabase", + "PetaPoco.Tests.Integration.Scripts.OracleBuildDatabase" }; private static volatile Exception _setupException; private static ExecutionPhase _phase = ExecutionPhase.Setup; - private string _connectionName = "Oracle"; + protected OracleTestProvider(string connectionName) => _connectionName = $"Oracle_{connectionName}"; + + private string _connectionName; protected override string ConnectionName => _connectionName; protected override string ScriptResourceName => _resources[(int)_phase]; @@ -103,4 +106,44 @@ private string StripLineComments(string script) return string.Join(_splitNewLine[0], parts); } } + + public class OracleDelimitedTestProvider : OracleTestProvider + { + public OracleDelimitedTestProvider() : base("Delimited") { } + protected override string ScriptResourceName => $"{base.ScriptResourceName}Delimited.sql"; + } + + public class OracleOrdinaryTestProvider : OracleTestProvider + { + public OracleOrdinaryTestProvider() : base("Ordinary") { } + protected override string ScriptResourceName => $"{base.ScriptResourceName}Ordinary.sql"; + + protected override IDatabaseBuildConfiguration BuildFromConnectionName(string connectionName) + { + return base.BuildFromConnectionName(connectionName) + .UsingProvider() + .UsingDefaultMapper(); + } + + private class CustomOracleDatabaseProvider : PetaPoco.Providers.OracleDatabaseProvider + { + public override string EscapeSqlIdentifier(string sqlIdentifier) => sqlIdentifier.ToUpperInvariant(); + } + + private class CustomConventionMapper : ConventionMapper + { + public CustomConventionMapper() => MapColumn = Customize(MapColumn); + + private Func Customize(Func original) + { + return (ci, t, pi) => + { + if (!original(ci, t, pi)) return false; + + ci.ColumnName = ci.ColumnName.ToUpperInvariant(); + return true; + }; + } + } + } } diff --git a/PetaPoco.Tests.Integration/Scripts/OracleBuildDatabaseDelimited.sql b/PetaPoco.Tests.Integration/Scripts/OracleBuildDatabaseDelimited.sql new file mode 100644 index 00000000..7760a442 --- /dev/null +++ b/PetaPoco.Tests.Integration/Scripts/OracleBuildDatabaseDelimited.sql @@ -0,0 +1,190 @@ +-- Case Sensitive version of OracleBuildDatabaseOrdinary.sql +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_delimited."OrderLines"'); +/ +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_delimited."Orders"'); +/ +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_delimited."People"'); +/ +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_delimited."SpecificOrderLines"'); +/ +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_delimited."SpecificOrders"'); +/ +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_delimited."SpecificPeople"'); +/ +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_delimited."TransactionLogs"'); +/ +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_delimited."Note"'); +/ + +CREATE TABLE "People" ( + "Id" VARCHAR2(36) NOT NULL, + "FullName" VARCHAR2(255), + "Age" NUMBER(19) NOT NULL, + "Height" NUMBER(10) NOT NULL, + "Dob" TIMESTAMP NULL, + PRIMARY KEY("Id") +); + +CREATE TABLE "Orders" ( + "Id" NUMBER(10) GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1), + "PersonId" VARCHAR2(36), + "PoNumber" VARCHAR2(15) NOT NULL, + "OrderStatus" NUMBER(10) NOT NULL, + "CreatedOn" TIMESTAMP NOT NULL, + "CreatedBy" VARCHAR2(255) NOT NULL, + PRIMARY KEY("Id"), + FOREIGN KEY ("PersonId") REFERENCES "People"("Id") +); + +CREATE TABLE "OrderLines" ( + "Id" NUMBER(10) GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1), + "OrderId" NUMBER(10) NOT NULL, + "Qty" NUMBER(5) NOT NULL, + "Status" NUMBER(3) NOT NULL, + "SellPrice" NUMERIC(10, 4) NOT NULL, + PRIMARY KEY("Id"), + FOREIGN KEY ("OrderId") REFERENCES "Orders"("Id") +); + +CREATE TABLE "SpecificPeople" ( + "Id" VARCHAR2(36) NOT NULL, + "FullName" VARCHAR2(255), + "Age" NUMBER(19) NOT NULL, + "Height" NUMBER(10) NOT NULL, + "Dob" TIMESTAMP NULL, + PRIMARY KEY("Id") +); + +CREATE TABLE "SpecificOrders" ( + "Id" NUMBER(10) GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1), + "PersonId" VARCHAR2(36), + "PoNumber" VARCHAR2(15) NOT NULL, + "OrderStatus" NUMBER(10) NOT NULL, + "CreatedOn" TIMESTAMP NOT NULL, + "CreatedBy" VARCHAR2(255) NOT NULL, + PRIMARY KEY("Id"), + FOREIGN KEY("PersonId") REFERENCES "SpecificPeople"("Id") +); + +CREATE TABLE "SpecificOrderLines" ( + "Id" NUMBER(10) GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1), + "OrderId" NUMBER(10) NOT NULL, + "Qty" NUMBER(5) NOT NULL, + "Status" NUMBER(3) NOT NULL, + "SellPrice" NUMBER(10, 4) NOT NULL, + PRIMARY KEY("Id"), + FOREIGN KEY("OrderId") REFERENCES "SpecificOrders"("Id") +); + +CREATE TABLE "TransactionLogs" ( + "Description" LONG, + "CreatedOn" TIMESTAMP NOT NULL +); + +CREATE TABLE "Note" ( + "Id" NUMBER(10) GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1), + "Text" LONG NOT NULL, + "CreatedOn" TIMESTAMP NOT NULL +); +/ + +-- Investigation Tables +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_delimited."BugInvestigation_10R9LZYK"'); +/ +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_delimited."BugInvestigation_3F489XV0"'); +/ +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_delimited."BugInvestigation_64O6LT8U"'); +/ +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_delimited."BugInvestigation_5TN5C4U4"'); +/ + +CREATE TABLE "BugInvestigation_10R9LZYK" ( + "Id" NUMBER(10) GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1), + "TestColumn1" LONG RAW, + PRIMARY KEY("Id") +); +/ + +CREATE TABLE "BugInvestigation_3F489XV0" ( + "Id" NUMBER(10) GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1), + "TC1" NUMBER(10) NOT NULL, + "TC2" NUMBER(10) NOT NULL, + "TC3" NUMBER(10) NOT NULL, + "TC4" NUMBER(10) NOT NULL, + PRIMARY KEY("Id") +); +/ + +CREATE TABLE "BugInvestigation_64O6LT8U" ( + "ColumnA" VARCHAR2(20), + "Column2" VARCHAR2(20) +); +/ + +CREATE TABLE "BugInvestigation_5TN5C4U4" ( + "ColumnA" VARCHAR2(20), + "Column2" VARCHAR2(20) +); +/ + +-- Stored procedures +CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco_delimited."SelectPeople"'); +/ +CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco_delimited."SelectPeopleWithParam"'); +/ +CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco_delimited."CountPeople"'); +/ +CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco_delimited."CountPeopleWithParam"'); +/ +CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco_delimited."UpdatePeople"'); +/ +CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco_delimited."UpdatePeopleWithParam"'); +/ + +CREATE PROCEDURE "SelectPeople" + ("p_out_cursor" OUT SYS_REFCURSOR) AS +BEGIN + OPEN "p_out_cursor" FOR + SELECT * FROM "People"; +END; +/ + +CREATE PROCEDURE "SelectPeopleWithParam" + ("age" IN NUMERIC DEFAULT 0, + "p_out_cursor" OUT SYS_REFCURSOR) AS +BEGIN + OPEN "p_out_cursor" FOR + SELECT * FROM "People" WHERE "Age" > "SelectPeopleWithParam"."age"; +END; +/ + +CREATE PROCEDURE "CountPeople" + ("p_out_cursor" OUT SYS_REFCURSOR) +AS +BEGIN + OPEN "p_out_cursor" FOR + SELECT COUNT(*) FROM "People"; +END; +/ + +CREATE PROCEDURE "CountPeopleWithParam" + ("age" IN NUMERIC DEFAULT 0, + "p_out_cursor" OUT SYS_REFCURSOR) AS +BEGIN + OPEN "p_out_cursor" FOR + SELECT COUNT(*) FROM "People" WHERE "Age" > "CountPeopleWithParam"."age"; +END; +/ + +CREATE PROCEDURE "UpdatePeople" AS +BEGIN + UPDATE "People" SET "FullName" = 'Updated'; +END; +/ + +CREATE PROCEDURE "UpdatePeopleWithParam" + ("age" IN NUMERIC DEFAULT 0) AS +BEGIN + UPDATE "People" SET "FullName" = 'Updated' WHERE "Age" > "UpdatePeopleWithParam"."age"; +END; +/ diff --git a/PetaPoco.Tests.Integration/Scripts/OracleBuildDatabase.sql b/PetaPoco.Tests.Integration/Scripts/OracleBuildDatabaseOrdinary.sql similarity index 71% rename from PetaPoco.Tests.Integration/Scripts/OracleBuildDatabase.sql rename to PetaPoco.Tests.Integration/Scripts/OracleBuildDatabaseOrdinary.sql index 673dfe36..1f015765 100644 --- a/PetaPoco.Tests.Integration/Scripts/OracleBuildDatabase.sql +++ b/PetaPoco.Tests.Integration/Scripts/OracleBuildDatabaseOrdinary.sql @@ -1,18 +1,19 @@ -CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.OrderLines'); +-- Case Insensitive version of OracleBuildDatabaseDelimited.sql +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_ordinary.OrderLines'); / -CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.Orders'); +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_ordinary.Orders'); / -CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.People'); +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_ordinary.People'); / -CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.SpecificOrderLines'); +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_ordinary.SpecificOrderLines'); / -CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.SpecificOrders'); +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_ordinary.SpecificOrders'); / -CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.SpecificPeople'); +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_ordinary.SpecificPeople'); / -CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.TransactionLogs'); +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_ordinary.TransactionLogs'); / -CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.Note'); +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_ordinary.Note'); / CREATE TABLE People ( @@ -88,13 +89,13 @@ CREATE TABLE Note ( / -- Investigation Tables -CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.BugInvestigation_10R9LZYK'); +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_ordinary.BugInvestigation_10R9LZYK'); / -CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.BugInvestigation_3F489XV0'); +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_ordinary.BugInvestigation_3F489XV0'); / -CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.BugInvestigation_64O6LT8U'); +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_ordinary.BugInvestigation_64O6LT8U'); / -CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.BugInvestigation_5TN5C4U4'); +CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco_ordinary.BugInvestigation_5TN5C4U4'); / CREATE TABLE BugInvestigation_10R9LZYK ( @@ -127,17 +128,17 @@ CREATE TABLE BugInvestigation_5TN5C4U4 ( / -- Stored procedures -CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco.SelectPeople'); +CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco_ordinary.SelectPeople'); / -CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco.SelectPeopleWithParam'); +CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco_ordinary.SelectPeopleWithParam'); / -CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco.CountPeople'); +CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco_ordinary.CountPeople'); / -CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco.CountPeopleWithParam'); +CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco_ordinary.CountPeopleWithParam'); / -CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco.UpdatePeople'); +CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco_ordinary.UpdatePeople'); / -CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco.UpdatePeopleWithParam'); +CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco_ordinary.UpdatePeopleWithParam'); / CREATE PROCEDURE SelectPeople diff --git a/PetaPoco.Tests.Integration/Scripts/OracleSetupDatabaseDelimited.sql b/PetaPoco.Tests.Integration/Scripts/OracleSetupDatabaseDelimited.sql new file mode 100644 index 00000000..2be15cb5 --- /dev/null +++ b/PetaPoco.Tests.Integration/Scripts/OracleSetupDatabaseDelimited.sql @@ -0,0 +1,27 @@ +-- Drop PETAPOCO_ORDINARY user COMPLETELY (if it exists) +DECLARE + found number := 0; +BEGIN + SELECT COUNT(*) INTO found + FROM all_users + WHERE username = 'PETAPOCO_DELIMITED'; + + IF found <> 0 THEN + BEGIN + EXECUTE IMMEDIATE 'DROP USER petapoco_delimited CASCADE'; + END; + END IF; +END; +/ + +-- Create fresh user +CREATE USER petapoco_delimited IDENTIFIED BY petapoco; + +-- Ensure that the data tablespace is the default for the user. This tablespace will be used when creating tables for example +ALTER USER petapoco_delimited DEFAULT TABLESPACE data_ts; +-- Give user quota e.g. to perform inserts +ALTER USER petapoco_delimited QUOTA UNLIMITED ON data_ts; + +-- Grant the application developer role +GRANT app_dev_role TO petapoco_delimited; +/ diff --git a/PetaPoco.Tests.Integration/Scripts/OracleSetupDatabaseOrdinary.sql b/PetaPoco.Tests.Integration/Scripts/OracleSetupDatabaseOrdinary.sql new file mode 100644 index 00000000..0a6d9877 --- /dev/null +++ b/PetaPoco.Tests.Integration/Scripts/OracleSetupDatabaseOrdinary.sql @@ -0,0 +1,27 @@ +-- Drop PETAPOCO_ORDINARY user COMPLETELY (if it exists) +DECLARE + found number := 0; +BEGIN + SELECT COUNT(*) INTO found + FROM all_users + WHERE username = 'PETAPOCO_ORDINARY'; + + IF found <> 0 THEN + BEGIN + EXECUTE IMMEDIATE 'DROP USER petapoco_ordinary CASCADE'; + END; + END IF; +END; +/ + +-- Create fresh user +CREATE USER petapoco_ordinary IDENTIFIED BY petapoco; + +-- Ensure that the data tablespace is the default for the user. This tablespace will be used when creating tables for example +ALTER USER petapoco_ordinary DEFAULT TABLESPACE data_ts; +-- Give user quota e.g. to perform inserts +ALTER USER petapoco_ordinary QUOTA UNLIMITED ON data_ts; + +-- Grant the application developer role +GRANT app_dev_role TO petapoco_ordinary; +/ diff --git a/PetaPoco.Tests.Integration/app.config b/PetaPoco.Tests.Integration/app.config index 9e5db441..5f81dd95 100644 --- a/PetaPoco.Tests.Integration/app.config +++ b/PetaPoco.Tests.Integration/app.config @@ -13,7 +13,8 @@ - + + diff --git a/PetaPoco.Tests.Integration/appsettings.json b/PetaPoco.Tests.Integration/appsettings.json index 4f52c081..0142cb12 100644 --- a/PetaPoco.Tests.Integration/appsettings.json +++ b/PetaPoco.Tests.Integration/appsettings.json @@ -52,8 +52,13 @@ "ProviderName": "Oracle.ManagedDataAccess" }, { - "Name": "Oracle", - "ConnectionString": "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=5008))(CONNECT_DATA=(SERVICE_NAME=FREEPDB1)));User Id=petapoco;Password=petapoco;", + "Name": "Oracle_Delimited", + "ConnectionString": "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=5008))(CONNECT_DATA=(SERVICE_NAME=FREEPDB1)));User Id=petapoco_delimited;Password=petapoco;", + "ProviderName": "Oracle.ManagedDataAccess" + }, + { + "Name": "Oracle_Ordinary", + "ConnectionString": "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=5008))(CONNECT_DATA=(SERVICE_NAME=FREEPDB1)));User Id=petapoco_ordinary;Password=petapoco;", "ProviderName": "Oracle.ManagedDataAccess" }, { diff --git a/PetaPoco/Core/PocoData.cs b/PetaPoco/Core/PocoData.cs index 376f66a2..84d92ed7 100644 --- a/PetaPoco/Core/PocoData.cs +++ b/PetaPoco/Core/PocoData.cs @@ -148,8 +148,16 @@ public static PocoData ForObject(object obj, string primaryKeyName, IMapper defa pd.TableInfo.AutoIncrement = true; foreach (var col in (obj as IDictionary).Keys) { - if (col != primaryKeyName) - pd.Columns.Add(col, new ExpandoColumn() { ColumnName = col }); + if (col == primaryKeyName) continue; + if (pd.Columns.TryGetValue(col, out PocoColumn pc)) + { + //When col and primaryKeyName only differ by case, + //we need ColumnName to match case exactly. + pc.ColumnName = col; + continue; + } + + pd.Columns.Add(col, new ExpandoColumn() { ColumnName = col }); } return pd; diff --git a/PetaPoco/Database.cs b/PetaPoco/Database.cs index f2a02776..0a395bce 100644 --- a/PetaPoco/Database.cs +++ b/PetaPoco/Database.cs @@ -3162,9 +3162,13 @@ private void AddParameter(IDbCommand cmd, object value, PocoColumn pc) idbParam.ParameterName = cmd.Parameters.Count.EnsureParamPrefix(_paramPrefix); else if (idbParam.ParameterName?.StartsWith(_paramPrefix) != true) { - // only add param prefix if it's not an Oracle stored procedures - if (!(cmd.CommandType == CommandType.StoredProcedure && _provider is Providers.OracleDatabaseProvider)) - idbParam.ParameterName = idbParam.ParameterName.EnsureParamPrefix(_paramPrefix); + var isOracleStoredProc = cmd.CommandType == CommandType.StoredProcedure && + _provider is Providers.OracleDatabaseProvider; + + // if it's an Oracle stored procedure, only escape the parameter name, don't add param prefix + idbParam.ParameterName = isOracleStoredProc ? + _provider.EscapeSqlIdentifier(idbParam.ParameterName) : + idbParam.ParameterName.EnsureParamPrefix(_paramPrefix); } cmd.Parameters.Add(idbParam); diff --git a/PetaPoco/Providers/OracleDatabaseProvider.cs b/PetaPoco/Providers/OracleDatabaseProvider.cs index 7c16feac..f1e32799 100644 --- a/PetaPoco/Providers/OracleDatabaseProvider.cs +++ b/PetaPoco/Providers/OracleDatabaseProvider.cs @@ -1,10 +1,9 @@ using System; using System.Data; using System.Data.Common; -using System.Text.RegularExpressions; +using System.Linq; using PetaPoco.Core; using PetaPoco.Utilities; -using System.Linq; #if ASYNC using System.Threading; using System.Threading.Tasks; @@ -20,13 +19,6 @@ namespace PetaPoco.Providers /// public class OracleDatabaseProvider : DatabaseProvider { - //An ordinary identifier must begin with a letter and contain only letters, underscore characters (_), and digits. - //The permitted letters and digits include all Unicode letters and digits. - //A delimited identifier is surrounded by double quotation marks and can contain any characters within the double quotation marks. - //Maximum two identifiers can be joined, separated by a dot (.) - private static readonly Regex _ordinaryIdentifierRegex = new Regex(@"^[\p{L}]+[\p{L}\d_]*(?:\.[\p{L}]+[\p{L}\d_]*)?$", RegexOptions.Compiled); - private static readonly Regex _delimitedIdentifierRegex = new Regex(@"^""[^""]*(?:""\.""[^""]*)?""$", RegexOptions.Compiled); - /// public override string GetParameterPrefix(string connectionString) => ":"; @@ -35,6 +27,13 @@ public override void PreExecute(IDbCommand cmd) { cmd.GetType().GetProperty("BindByName")?.SetValue(cmd, true, null); cmd.GetType().GetProperty("InitialLONGFetchSize")?.SetValue(cmd, -1, null); + + //Required for Oracle.DataAccess.Client.OracleCommand + //By default statements are cached, so if the database is modified between two reads (same statement), + //the last one will still access the old statement's definition. + //Setting this property to false prevents caching of statements completely. + //NOTE: Using "Statement Cache Purge=true; Statement Cache Size=0" in the ConnectionString is not a guarantee + cmd.GetType().GetProperty("AddToStatementCache")?.SetValue(cmd, false, null); } /// @@ -49,6 +48,7 @@ public override string BuildPageQuery(long skip, long take, SQLParts parts, ref //args = args.Concat(new object[] { skip, take }).ToArray(); //return sql; + //Older versions of Oracle //Similar to SqlServerProvider with the exception of SELECT NULL FROM DUAL vs SELECT NULL var helper = (PagingHelper)PagingUtility; // when the query does not contain an "order by", it is very slow @@ -81,22 +81,7 @@ public override DbProviderFactory GetFactory() } /// - public override string EscapeSqlIdentifier(string sqlIdentifier) - { - return sqlIdentifier; - - //TODO: Below code determines whether it is required to wrap the identifier in double quotes or not. - // Included for convenience while fixing failing tests until we're sure it's not required. - - //If already quoted, leave as-is - if (_delimitedIdentifierRegex.IsMatch(sqlIdentifier)) return sqlIdentifier; - - //If no quotes required, leave as-is (could also uppercase) - if (_ordinaryIdentifierRegex.IsMatch(sqlIdentifier)) return sqlIdentifier; //.ToUpperInvariant(); - - //If not valid, wrap in quotes, but don't allow use of double quotes in identifier - return "\"" + sqlIdentifier.Replace("\"", "").Replace(".", "\".\"") + "\""; - } + public override string EscapeSqlIdentifier(string sqlIdentifier) => $"\"{sqlIdentifier}\""; /// public override string GetAutoIncrementExpression(TableInfo ti) => !string.IsNullOrEmpty(ti.SequenceName) ? $"{ti.SequenceName}.nextval" : null; diff --git a/PetaPoco/Utilities/ParametersHelper.cs b/PetaPoco/Utilities/ParametersHelper.cs index 2cd0b78a..a7bd810d 100644 --- a/PetaPoco/Utilities/ParametersHelper.cs +++ b/PetaPoco/Utilities/ParametersHelper.cs @@ -18,8 +18,8 @@ namespace PetaPoco.Internal /// internal static class ParametersHelper { - private static Regex ParamPrefixRegex = new Regex(@"(? /// Replaces all parameter prefixes in the provided SQL statement with the specified replacement string. @@ -71,8 +71,7 @@ public static string ProcessQueryParams(string sql, object[] srcArgs, List= srcArgs.Length) @@ -115,7 +114,7 @@ public static string ProcessQueryParams(string sql, object[] srcArgs, List 0 THEN - BEGIN - EXECUTE IMMEDIATE 'DROP USER petapoco CASCADE'; - END; - END IF; -END; -/ - --- Create fresh user -CREATE USER petapoco IDENTIFIED BY petapoco; -/ +ALTER SESSION SET container=FREEPDB1; -- Drop DATA_TS tablespace COMPLETELY (if it exists) DECLARE @@ -74,13 +56,4 @@ CREATE ROLE app_dev_role; -- Grant privileges to the application developer role GRANT CREATE SESSION, CREATE TABLE, CREATE SEQUENCE, CREATE PROCEDURE, CREATE TRIGGER, CREATE VIEW TO app_dev_role; GRANT EXECUTE ON dbms_lock TO app_dev_role; -GRANT EXECUTE ON DROP_IF_EXISTS TO app_dev_role; - --- Ensure that the tablespace created above, is the default for the user. This tablespace will be used when creating tables for example -ALTER USER petapoco DEFAULT TABLESPACE data_ts; --- Give user quota e.g. to perform inserts -ALTER USER petapoco QUOTA UNLIMITED ON data_ts; - --- Grant the application developer role -GRANT app_dev_role TO petapoco; -/ +GRANT EXECUTE ON DROP_IF_EXISTS TO app_dev_role; \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 731aa384..ffd51485 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -84,5 +84,7 @@ services: - "5008:1521" environment: <<: *oracle-environment + volumes: + - ./Scripts/Startup:/opt/oracle/scripts/startup # TODO: teradata