From 1032f9a1d4a0275fbfe1b12b2ca8875b51fa2ca2 Mon Sep 17 00:00:00 2001 From: Curlack Date: Sat, 21 Oct 2023 23:41:28 +0200 Subject: [PATCH 1/2] Leave sqlIdentifier untouched when escaping in OracleDatabaseProvider. Add remaining Oracle tests. Rather use VARCHAR2(36) for Guid instead of RAW(16). --- .../Databases/ExecuteTests.cs | 24 +-- .../OracleTests/OracleDeleteTests.cs | 14 ++ .../OracleTests/OracleExecuteTests.cs | 14 ++ .../OracleTests/OracleInsertTests.cs | 16 ++ .../OracleTests/OracleMiscellaneousTests.cs | 14 ++ .../OracleTests/OraclePreExecuteTests.cs | 43 ++++ .../OracleTests/OracleQueryLinqTests.cs | 14 ++ .../Databases/OracleTests/OracleQueryTests.cs | 183 ++++++++++++++++++ .../OracleTests/OracleStoredProcTests.cs | 18 ++ .../OracleTests/OracleTriageTests.cs | 14 ++ .../OracleTests/OracleUpdateTests.cs | 14 ++ .../Scripts/OracleBuildDatabase.sql | 8 +- PetaPoco/Providers/OracleDatabaseProvider.cs | 26 ++- 13 files changed, 385 insertions(+), 17 deletions(-) create mode 100644 PetaPoco.Tests.Integration/Databases/OracleTests/OracleDeleteTests.cs create mode 100644 PetaPoco.Tests.Integration/Databases/OracleTests/OracleExecuteTests.cs create mode 100644 PetaPoco.Tests.Integration/Databases/OracleTests/OracleInsertTests.cs create mode 100644 PetaPoco.Tests.Integration/Databases/OracleTests/OracleMiscellaneousTests.cs create mode 100644 PetaPoco.Tests.Integration/Databases/OracleTests/OraclePreExecuteTests.cs create mode 100644 PetaPoco.Tests.Integration/Databases/OracleTests/OracleQueryLinqTests.cs create mode 100644 PetaPoco.Tests.Integration/Databases/OracleTests/OracleQueryTests.cs create mode 100644 PetaPoco.Tests.Integration/Databases/OracleTests/OracleStoredProcTests.cs create mode 100644 PetaPoco.Tests.Integration/Databases/OracleTests/OracleTriageTests.cs create mode 100644 PetaPoco.Tests.Integration/Databases/OracleTests/OracleUpdateTests.cs diff --git a/PetaPoco.Tests.Integration/Databases/ExecuteTests.cs b/PetaPoco.Tests.Integration/Databases/ExecuteTests.cs index 3d668e12..e3954ed7 100644 --- a/PetaPoco.Tests.Integration/Databases/ExecuteTests.cs +++ b/PetaPoco.Tests.Integration/Databases/ExecuteTests.cs @@ -45,7 +45,7 @@ public virtual void Execute_GivenSqlAndParameterAffectsOneRow_ShouldReturnOne() InsertNotes(5); var beforeCount = CountNotes(); - var result = DB.Execute($"DELETE FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)}" + + var result = DB.Execute($"DELETE FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)} " + $"WHERE {DB.Provider.EscapeSqlIdentifier(_pd.TableInfo.PrimaryKey)} = @0", 1); var afterCount = CountNotes(); @@ -60,7 +60,7 @@ public virtual void Execute_GivenSqlAndParametersAffectsTwoRows_ShouldReturnTwo( InsertNotes(5); var beforeCount = CountNotes(); - var result = DB.Execute($"DELETE FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)}" + + var result = DB.Execute($"DELETE FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)} " + $"WHERE {DB.Provider.EscapeSqlIdentifier(_pd.TableInfo.PrimaryKey)} IN(@0,@1)", 1, 2); var afterCount = CountNotes(); @@ -75,7 +75,7 @@ public virtual void Execute_GivenSqlAffectsOneRow_ShouldReturnOne() InsertNotes(5); var beforeCount = CountNotes(); - var result = DB.Execute($"DELETE FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)}" + + var result = DB.Execute($"DELETE FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)} " + $"WHERE {DB.Provider.EscapeSqlIdentifier(_pd.TableInfo.PrimaryKey)} = 1"); var afterCount = CountNotes(); @@ -90,7 +90,7 @@ public virtual void Execute_GivenSqlAffectsTwoRows_ShouldReturnTwo() InsertNotes(5); var beforeCount = CountNotes(); - var result = DB.Execute($"DELETE FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)}" + + var result = DB.Execute($"DELETE FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)} " + $"WHERE {DB.Provider.EscapeSqlIdentifier(_pd.TableInfo.PrimaryKey)} IN(1,2)"); var afterCount = CountNotes(); @@ -105,7 +105,7 @@ public virtual async Task ExecuteAsync_GivenSqlAndParameterAffectsOneRow_ShouldR InsertNotes(5); var beforeCount = CountNotes(); - var result = await DB.ExecuteAsync($"DELETE FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)}" + + var result = await DB.ExecuteAsync($"DELETE FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)} " + $"WHERE {DB.Provider.EscapeSqlIdentifier(_pd.TableInfo.PrimaryKey)} = @0", 1); var afterCount = CountNotes(); @@ -120,7 +120,7 @@ public virtual async Task ExecuteAsync_GivenSqlAndParametersAffectsTwoRows_Shoul InsertNotes(5); var beforeCount = CountNotes(); - var result = await DB.ExecuteAsync($"DELETE FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)}" + + var result = await DB.ExecuteAsync($"DELETE FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)} " + $"WHERE {DB.Provider.EscapeSqlIdentifier(_pd.TableInfo.PrimaryKey)} IN(@0,@1)", 1, 2); var afterCount = CountNotes(); @@ -135,7 +135,7 @@ public virtual async Task ExecuteAsync_GivenSqlAffectsOneRow_ShouldReturnOne() InsertNotes(5); var beforeCount = CountNotes(); - var result = await DB.ExecuteAsync($"DELETE FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)}" + + var result = await DB.ExecuteAsync($"DELETE FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)} " + $"WHERE {DB.Provider.EscapeSqlIdentifier(_pd.TableInfo.PrimaryKey)} = 1"); var afterCount = CountNotes(); @@ -150,7 +150,7 @@ public virtual async Task ExecuteAsync_GivenSqlAffectsTwoRows_ShouldReturnTwo() InsertNotes(5); var beforeCount = CountNotes(); - var result = await DB.ExecuteAsync($"DELETE FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)}" + + var result = await DB.ExecuteAsync($"DELETE FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)} " + $"WHERE {DB.Provider.EscapeSqlIdentifier(_pd.TableInfo.PrimaryKey)} IN(1,2)"); var afterCount = CountNotes(); @@ -172,7 +172,7 @@ public virtual void ExecuteScalar_GivenSqlAndParameter_ReturnShouldBeValid() { InsertNotes(4); - DB.ExecuteScalar($"SELECT COUNT(*) FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)}" + + DB.ExecuteScalar($"SELECT COUNT(*) FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)} " + $"WHERE {DB.Provider.EscapeSqlIdentifier(_pd.TableInfo.PrimaryKey)} <= @0", 2) .ShouldBe(2); } @@ -182,7 +182,7 @@ public virtual void ExecuteScalar_GivenSqlAndParameters_ReturnShouldBeValid() { InsertNotes(5); - DB.ExecuteScalar($"SELECT COUNT(*) FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)}" + + DB.ExecuteScalar($"SELECT COUNT(*) FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)} " + $"WHERE {DB.Provider.EscapeSqlIdentifier(_pd.TableInfo.PrimaryKey)} IN(@0, @1)", 1, 2) .ShouldBe(2); } @@ -200,7 +200,7 @@ public virtual async Task ExecuteScalarAsync_GivenSqlAndParameter_ReturnShouldBe { InsertNotes(4); - (await DB.ExecuteScalarAsync($"SELECT COUNT(*) FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)}" + + (await DB.ExecuteScalarAsync($"SELECT COUNT(*) FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)} " + $"WHERE {DB.Provider.EscapeSqlIdentifier(_pd.TableInfo.PrimaryKey)} <= @0", 2)) .ShouldBe(2); } @@ -210,7 +210,7 @@ public virtual async Task ExecuteScalarAsync_GivenSqlAndParameters_ReturnShouldB { InsertNotes(5); - (await DB.ExecuteScalarAsync($"SELECT COUNT(*) FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)}" + + (await DB.ExecuteScalarAsync($"SELECT COUNT(*) FROM {DB.Provider.EscapeTableName(_pd.TableInfo.TableName)} " + $"WHERE {DB.Provider.EscapeSqlIdentifier(_pd.TableInfo.PrimaryKey)} IN(@0, @1)", 1, 2)) .ShouldBe(2); } diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleDeleteTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleDeleteTests.cs new file mode 100644 index 00000000..2ff1a9c7 --- /dev/null +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleDeleteTests.cs @@ -0,0 +1,14 @@ +using PetaPoco.Tests.Integration.Providers; +using Xunit; + +namespace PetaPoco.Tests.Integration.Databases.Oracle +{ + [Collection("Oracle")] + public class OracleDeleteTests : DeleteTests + { + public OracleDeleteTests() + : base(new OracleTestProvider()) + { + } + } +} diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleExecuteTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleExecuteTests.cs new file mode 100644 index 00000000..8e747d91 --- /dev/null +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleExecuteTests.cs @@ -0,0 +1,14 @@ +using PetaPoco.Tests.Integration.Providers; +using Xunit; + +namespace PetaPoco.Tests.Integration.Databases.Oracle +{ + [Collection("Oracle")] + public class OracleExecuteTests : ExecuteTests + { + public OracleExecuteTests() + : base(new OracleTestProvider()) + { + } + } +} diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleInsertTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleInsertTests.cs new file mode 100644 index 00000000..9b43b6a1 --- /dev/null +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleInsertTests.cs @@ -0,0 +1,16 @@ +using PetaPoco.Tests.Integration.Models.Postgres; +using PetaPoco.Tests.Integration.Providers; +using Shouldly; +using Xunit; + +namespace PetaPoco.Tests.Integration.Databases.Oracle +{ + [Collection("Oracle")] + public class OracleInsertTests : InsertTests + { + public OracleInsertTests() + : base(new OracleTestProvider()) + { + } + } +} diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleMiscellaneousTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleMiscellaneousTests.cs new file mode 100644 index 00000000..8cc0bb05 --- /dev/null +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleMiscellaneousTests.cs @@ -0,0 +1,14 @@ +using PetaPoco.Tests.Integration.Providers; +using Xunit; + +namespace PetaPoco.Tests.Integration.Databases.Oracle +{ + [Collection("Oracle")] + public class OracleMiscellaneousTests : MiscellaneousTests + { + public OracleMiscellaneousTests() + : base(new OracleTestProvider()) + { + } + } +} diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OraclePreExecuteTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OraclePreExecuteTests.cs new file mode 100644 index 00000000..976906df --- /dev/null +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OraclePreExecuteTests.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Data; +using System.Linq; +using PetaPoco.Tests.Integration.Providers; +using Xunit; + +namespace PetaPoco.Tests.Integration.Databases.Oracle +{ + [Collection("Oracle")] + public class OraclePreExecuteTests : PreExecuteTests + { + protected override IPreExecuteDatabaseProvider Provider => DB.Provider as PreExecuteDatabaseProvider; + + public OraclePreExecuteTests() + : base(new PreExecuteTestProvider()) + { + Provider.ThrowExceptions = true; + } + + protected class PreExecuteTestProvider : OracleTestProvider + { + 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(); + } + } + } + } +} diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleQueryLinqTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleQueryLinqTests.cs new file mode 100644 index 00000000..1e7db334 --- /dev/null +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleQueryLinqTests.cs @@ -0,0 +1,14 @@ +using PetaPoco.Tests.Integration.Providers; +using Xunit; + +namespace PetaPoco.Tests.Integration.Databases.Oracle +{ + [Collection("Oracle")] + public class OracleQueryLinqTests : QueryLinqTests + { + public OracleQueryLinqTests() + : base(new OracleTestProvider()) + { + } + } +} diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleQueryTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleQueryTests.cs new file mode 100644 index 00000000..781bdd87 --- /dev/null +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleQueryTests.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections.Generic; +using System.Linq; +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 OracleQueryTests : QueryTests + { + public OracleQueryTests() + : base(new OracleTestProvider()) + { + } + + [Fact] + public override void QueryMultiple_ForSingleResultsSetWithSinglePoco_ShouldReturnValidPocoCollection() + { + AddPeople(1, 0); + + var pd = PocoData.ForType(typeof(Person), DB.DefaultMapper); + var pdName = pd.Columns.Values.First(c => c.PropertyInfo.Name == "Name").ColumnName; + + var sql = $@"SELECT * + FROM {DB.Provider.EscapeTableName(pd.TableInfo.TableName)} + WHERE {DB.Provider.EscapeSqlIdentifier(pdName)} LIKE @0 || '%';"; + + List result; + using (var multi = DB.QueryMultiple(sql, "Peta")) + { + result = multi.Read().ToList(); + } + + result.Count.ShouldBe(1); + + var person = result.First(); + person.Id.ShouldNotBe(Guid.Empty); + person.Name.ShouldStartWith("Peta"); + person.Age.ShouldBe(18); + } + + [Fact] + public override void QueryMultiple_ForSingleResultsSetWithMultiPoco_ShouldReturnValidPocoCollection() + { + AddOrders(1); + + var pd = PocoData.ForType(typeof(Person), DB.DefaultMapper); + var od = PocoData.ForType(typeof(Order), DB.DefaultMapper); + var pdId = pd.Columns.Values.First(c => c.PropertyInfo.Name == "Id").ColumnName; + var pdName = pd.Columns.Values.First(c => c.PropertyInfo.Name == "Name").ColumnName; + var odPersonId = od.Columns.Values.First(c => c.PropertyInfo.Name == "PersonId").ColumnName; + + var sql = $@"SELECT * FROM {DB.Provider.EscapeTableName(od.TableInfo.TableName)} o + INNER JOIN {DB.Provider.EscapeTableName(pd.TableInfo.TableName)} p ON p.{DB.Provider.EscapeSqlIdentifier(pdId)} = o.{DB.Provider.EscapeSqlIdentifier(odPersonId)} + WHERE p.{DB.Provider.EscapeSqlIdentifier(pdName)} = @0 + ORDER BY 1 DESC + LIMIT 1;"; + + List result; + using (var multi = DB.QueryMultiple(sql, "Peta0")) + { + result = multi.Read((o, p) => + { + o.Person = p; + return o; + }).ToList(); + } + + result.Count.ShouldBe(1); + + var order = result.First(); + + order.PoNumber.ShouldStartWith("PO"); + order.Status.ShouldBeOneOf(Enum.GetValues(typeof(OrderStatus)).Cast().ToArray()); + order.PersonId.ShouldNotBe(Guid.Empty); + order.CreatedOn.ShouldBeLessThanOrEqualTo(new DateTime(1990, 1, 1, 0, 0, 0, DateTimeKind.Utc)); + order.CreatedBy.ShouldStartWith("Harry"); + + order.Person.ShouldNotBeNull(); + order.Person.Id.ShouldNotBe(Guid.Empty); + order.Person.Name.ShouldStartWith("Peta"); + order.Person.Age.ShouldBe(18); + } + + [Fact] + public override void QueryMultiple_ForMultiResultsSetWithSinglePoco_ShouldReturnValidPocoCollection() + { + AddOrders(1); + + var pd = PocoData.ForType(typeof(Person), DB.DefaultMapper); + var od = PocoData.ForType(typeof(Order), DB.DefaultMapper); + var pdName = pd.Columns.Values.First(c => c.PropertyInfo.Name == "Name").ColumnName; + var odId = od.Columns.Values.First(c => c.PropertyInfo.Name == "Id").ColumnName; + + var sql = $@"SELECT * FROM {DB.Provider.EscapeTableName(od.TableInfo.TableName)} o + WHERE o.{DB.Provider.EscapeSqlIdentifier(odId)} = @0; + SELECT * FROM {DB.Provider.EscapeTableName(pd.TableInfo.TableName)} p + WHERE p.{DB.Provider.EscapeSqlIdentifier(pdName)} = @1;"; + + Order order; + using (var multi = DB.QueryMultiple(sql, 1, "Peta0")) + { + order = multi.Read().First(); + order.Person = multi.Read().First(); + } + + order.PoNumber.ShouldStartWith("PO"); + order.Status.ShouldBeOneOf(Enum.GetValues(typeof(OrderStatus)).Cast().ToArray()); + order.PersonId.ShouldNotBe(Guid.Empty); + order.CreatedOn.ShouldBeLessThanOrEqualTo(new DateTime(1990, 1, 1, 0, 0, 0, DateTimeKind.Utc)); + order.CreatedBy.ShouldStartWith("Harry"); + + order.Person.ShouldNotBeNull(); + order.Person.Id.ShouldNotBe(Guid.Empty); + order.Person.Name.ShouldStartWith("Peta"); + order.Person.Age.ShouldBe(18); + } + + [Fact] + public override void QueryMultiple_ForMultiResultsSetWithMultiPoco_ShouldReturnValidPocoCollection() + { + AddOrders(12); + + var pd = PocoData.ForType(typeof(Person), DB.DefaultMapper); + var od = PocoData.ForType(typeof(Order), DB.DefaultMapper); + var old = PocoData.ForType(typeof(OrderLine), DB.DefaultMapper); + var pdId = pd.Columns.Values.First(c => c.PropertyInfo.Name == "Id").ColumnName; + var odId = od.Columns.Values.First(c => c.PropertyInfo.Name == "Id").ColumnName; + var odPersonId = od.Columns.Values.First(c => c.PropertyInfo.Name == "PersonId").ColumnName; + var oldOrderId = old.Columns.Values.First(c => c.PropertyInfo.Name == "OrderId").ColumnName; + + var sql = $@"SELECT * FROM {DB.Provider.EscapeTableName(od.TableInfo.TableName)} o + INNER JOIN {DB.Provider.EscapeTableName(pd.TableInfo.TableName)} p ON p.{DB.Provider.EscapeSqlIdentifier(pdId)} = o.{DB.Provider.EscapeSqlIdentifier(odPersonId)} + ORDER BY o.{DB.Provider.EscapeSqlIdentifier(odId)} ASC; + SELECT * FROM {DB.Provider.EscapeTableName(old.TableInfo.TableName)} ol + ORDER BY ol.{DB.Provider.EscapeSqlIdentifier(oldOrderId)} ASC;"; + + List results; + using (var multi = DB.QueryMultiple(sql)) + { + results = multi.Read((o, p) => + { + o.Person = p; + return o; + }).ToList(); + + var orderLines = multi.Read().ToList(); + foreach (var order in results) + order.OrderLines = orderLines.Where(ol => ol.OrderId == order.Id).ToList(); + } + + results.Count.ShouldBe(12); + + results.ForEach(o => + { + o.PoNumber.ShouldStartWith("PO"); + o.Status.ShouldBeOneOf(Enum.GetValues(typeof(OrderStatus)).Cast().ToArray()); + o.PersonId.ShouldNotBe(Guid.Empty); + o.CreatedOn.ShouldBeLessThanOrEqualTo(new DateTime(1990, 1, 1, 0, 0, 0, DateTimeKind.Utc)); + o.CreatedBy.ShouldStartWith("Harry"); + + o.Person.ShouldNotBeNull(); + o.Person.Id.ShouldNotBe(Guid.Empty); + o.Person.Name.ShouldStartWith("Peta"); + o.Person.Age.ShouldBeGreaterThanOrEqualTo(18); + + o.OrderLines.Count.ShouldBe(2); + + var firstOrderLine = o.OrderLines.First(); + firstOrderLine.Quantity.ToString().ShouldBe("1"); + firstOrderLine.SellPrice.ShouldBe(9.99m); + + var secondOrderLine = o.OrderLines.Skip(1).First(); + secondOrderLine.Quantity.ToString().ShouldBe("2"); + secondOrderLine.SellPrice.ShouldBe(19.98m); + }); + } + } +} diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleStoredProcTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleStoredProcTests.cs new file mode 100644 index 00000000..2dcebf19 --- /dev/null +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleStoredProcTests.cs @@ -0,0 +1,18 @@ +using System; +using Oracle.ManagedDataAccess.Client; +using PetaPoco.Tests.Integration.Providers; +using Xunit; + +namespace PetaPoco.Tests.Integration.Databases.Oracle +{ + [Collection("Oracle")] + public class OracleStoredProcTests : StoredProcTests + { + protected override Type DataParameterType => typeof(OracleParameter); + + public OracleStoredProcTests() + : base(new OracleTestProvider()) + { + } + } +} diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleTriageTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleTriageTests.cs new file mode 100644 index 00000000..9d7617ed --- /dev/null +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleTriageTests.cs @@ -0,0 +1,14 @@ +using PetaPoco.Tests.Integration.Providers; +using Xunit; + +namespace PetaPoco.Tests.Integration.Databases.Oracle +{ + [Collection("Oracle")] + public class OracleTriageTests : TriageTests + { + public OracleTriageTests() + : base(new OracleTestProvider()) + { + } + } +} diff --git a/PetaPoco.Tests.Integration/Databases/OracleTests/OracleUpdateTests.cs b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleUpdateTests.cs new file mode 100644 index 00000000..914549a0 --- /dev/null +++ b/PetaPoco.Tests.Integration/Databases/OracleTests/OracleUpdateTests.cs @@ -0,0 +1,14 @@ +using PetaPoco.Tests.Integration.Providers; +using Xunit; + +namespace PetaPoco.Tests.Integration.Databases.Oracle +{ + [Collection("Oracle")] + public class OracleUpdateTests : UpdateTests + { + public OracleUpdateTests() + : base(new OracleTestProvider()) + { + } + } +} diff --git a/PetaPoco.Tests.Integration/Scripts/OracleBuildDatabase.sql b/PetaPoco.Tests.Integration/Scripts/OracleBuildDatabase.sql index 71e282a2..ccf3ffa8 100644 --- a/PetaPoco.Tests.Integration/Scripts/OracleBuildDatabase.sql +++ b/PetaPoco.Tests.Integration/Scripts/OracleBuildDatabase.sql @@ -16,7 +16,7 @@ CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.Note'); / CREATE TABLE People ( - Id RAW(16) NOT NULL, + Id VARCHAR2(36) NOT NULL, FullName VARCHAR2(255), Age NUMBER(19) NOT NULL, Height NUMBER(10) NOT NULL, @@ -26,7 +26,7 @@ CREATE TABLE People ( CREATE TABLE Orders ( Id NUMBER(10) GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1), - PersonId RAW(16), + PersonId VARCHAR2(36), PoNumber VARCHAR2(15) NOT NULL, OrderStatus NUMBER(10) NOT NULL, CreatedOn TIMESTAMP NOT NULL, @@ -46,7 +46,7 @@ CREATE TABLE OrderLines ( ); CREATE TABLE SpecificPeople ( - Id RAW(16) NOT NULL, + Id VARCHAR2(36) NOT NULL, FullName VARCHAR2(255), Age NUMBER(19) NOT NULL, Height NUMBER(10) NOT NULL, @@ -56,7 +56,7 @@ CREATE TABLE SpecificPeople ( CREATE TABLE SpecificOrders ( Id NUMBER(10) GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1), - PersonId RAW(16), + PersonId VARCHAR2(36), PoNumber VARCHAR2(15) NOT NULL, OrderStatus NUMBER(10) NOT NULL, CreatedOn TIMESTAMP NOT NULL, diff --git a/PetaPoco/Providers/OracleDatabaseProvider.cs b/PetaPoco/Providers/OracleDatabaseProvider.cs index 7a06a2b6..750f4b29 100644 --- a/PetaPoco/Providers/OracleDatabaseProvider.cs +++ b/PetaPoco/Providers/OracleDatabaseProvider.cs @@ -1,6 +1,8 @@ using System; using System.Data; using System.Data.Common; +using System.Linq; +using System.Text.RegularExpressions; using PetaPoco.Core; using PetaPoco.Internal; using PetaPoco.Utilities; @@ -19,6 +21,13 @@ 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) => ":"; @@ -50,7 +59,22 @@ public override DbProviderFactory GetFactory() } /// - public override string EscapeSqlIdentifier(string sqlIdentifier) => $"\"{sqlIdentifier.ToUpperInvariant()}\""; + 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 GetAutoIncrementExpression(TableInfo ti) => !string.IsNullOrEmpty(ti.SequenceName) ? $"{ti.SequenceName}.nextval" : null; From 67f0dc33f28b743057484e263bfbc2e55dee219f Mon Sep 17 00:00:00 2001 From: Curlack Date: Sat, 21 Oct 2023 23:48:32 +0200 Subject: [PATCH 2/2] Remove unused using --- PetaPoco/Providers/OracleDatabaseProvider.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/PetaPoco/Providers/OracleDatabaseProvider.cs b/PetaPoco/Providers/OracleDatabaseProvider.cs index 750f4b29..f4769750 100644 --- a/PetaPoco/Providers/OracleDatabaseProvider.cs +++ b/PetaPoco/Providers/OracleDatabaseProvider.cs @@ -1,7 +1,6 @@ using System; using System.Data; using System.Data.Common; -using System.Linq; using System.Text.RegularExpressions; using PetaPoco.Core; using PetaPoco.Internal;