Skip to content

Commit

Permalink
Merge pull request #702 from Curlack/feature/oracle-support
Browse files Browse the repository at this point in the history
Adds base support for Oracle Database.
  • Loading branch information
Ste1io authored Oct 20, 2023
2 parents 32319b6 + 9b5b929 commit eaf8e5f
Show file tree
Hide file tree
Showing 12 changed files with 432 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using PetaPoco.Tests.Integration.Providers;
using Xunit;

namespace PetaPoco.Tests.Integration.Databases.Oracle
{
[Collection("Oracle")]
public class OracleDatabaseTests : DatabaseTests
{
public OracleDatabaseTests()
: base(new OracleTestProvider())
{
}
}
}
8 changes: 7 additions & 1 deletion PetaPoco.Tests.Integration/PetaPoco.Tests.Integration.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net472;netcoreapp3.1</TargetFrameworks>
Expand All @@ -22,6 +22,8 @@
<None Remove="Scripts\MariaDBBuildDatabase.sql" />
<None Remove="Scripts\MSAccessBuildDatabase.sql" />
<None Remove="Scripts\MySqlBuildDatabase.sql" />
<None Remove="Scripts\OracleBuildDatabase.sql" />
<None Remove="Scripts\OracleSetupDatabase.sql" />
<None Remove="Scripts\PostgresBuildDatabase.sql" />
<None Remove="Scripts\SQLiteBuildDatabase.sql" />
<None Remove="Scripts\SqlServerBuildDatabase.sql" />
Expand All @@ -33,6 +35,8 @@
<EmbeddedResource Include="Scripts\MariaDBBuildDatabase.sql" />
<EmbeddedResource Include="Scripts\MSAccessBuildDatabase.sql" />
<EmbeddedResource Include="Scripts\MySqlBuildDatabase.sql" />
<EmbeddedResource Include="Scripts\OracleBuildDatabase.sql" />
<EmbeddedResource Include="Scripts\OracleSetupDatabase.sql" />
<EmbeddedResource Include="Scripts\PostgresBuildDatabase.sql" />
<EmbeddedResource Include="Scripts\SQLiteBuildDatabase.sql" />
<EmbeddedResource Include="Scripts\SqlServerBuildDatabase.sql" />
Expand Down Expand Up @@ -70,6 +74,8 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.0" Condition="'$(TargetFramework)' == 'netcoreapp3.1'" />
<PackageReference Include="Moq" Version="[4.8.2,4.20)" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Oracle.ManagedDataAccess" Version="21.12.0" Condition="'$(TargetFramework)' != 'netcoreapp3.1'" />
<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="3.21.120" Condition="'$(TargetFramework)' == 'netcoreapp3.1'" />
<PackageReference Include="Shouldly" Version="4.0.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="xunit" Version="2.5.3" />
Expand Down
41 changes: 26 additions & 15 deletions PetaPoco.Tests.Integration/Providers/MSAccessTestProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ namespace PetaPoco.Tests.Integration.Providers
{
public class MSAccessTestProvider : TestProvider
{
private static readonly string[] _splitSemiColon = new[] { ";" };
private static readonly string[] _splitNewLine = new[] { Environment.NewLine };

protected override string ConnectionName => "MSAccess";

protected override string ScriptResourceName => "PetaPoco.Tests.Integration.Scripts.MSAccessBuildDatabase.sql";
Expand All @@ -33,26 +36,34 @@ public override IDatabase Execute()

public override void ExecuteBuildScript(IDatabase database, string script)
{
script.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToList().ForEach(s =>
{
if (string.IsNullOrEmpty(s) || s.StartsWith("--"))
return;

if (s.StartsWith("DROP"))
script.Split(_splitSemiColon, StringSplitOptions.RemoveEmptyEntries)
.Select(s => StripLineComments(s).Trim()).ToList()
.ForEach(s =>
{
try
{
base.ExecuteBuildScript(database, s);
}
catch
if (string.IsNullOrEmpty(s)) return;

if (s.StartsWith("DROP"))
{
try
{
base.ExecuteBuildScript(database, s);
}
catch
{
}

return;
}

return;
}
base.ExecuteBuildScript(database, s);
});
}

base.ExecuteBuildScript(database, s);
});
private string StripLineComments(string script)
{
var parts = script.Split(_splitNewLine, StringSplitOptions.RemoveEmptyEntries)
.Where(s => !s.Trim().StartsWith("--"));
return string.Join(_splitNewLine[0], parts);
}
}
}
79 changes: 79 additions & 0 deletions PetaPoco.Tests.Integration/Providers/OracleTestProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System;
using System.Linq;

/*
* Converted build scripts from SqlServerBuildDatabase.sql using MSSQLTips.com for datatype conversions
* Link: https://www.mssqltips.com/sqlservertip/2944/comparing-sql-server-and-oracle-datatypes
*/

namespace PetaPoco.Tests.Integration.Providers
{
public 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"
};
private static ExecutionPhase _phase = ExecutionPhase.Setup;

private string _connectionName = "Oracle";
protected override string ConnectionName => _connectionName;

protected override string ScriptResourceName => _resources[(int)_phase];

public override IDatabase Execute()
{
EnsureDatabaseSetup();
return base.Execute();
}

public override void ExecuteBuildScript(IDatabase database, string script)
{
//The script file can contain multiple script blocks, separated by a line containing a single forward slash ("\r\n/").
//Script blocks end with "END;" and can execute as a whole.
//Statements are separated by a semi colon and have to be executed separately.
//This "one statement at a time" limitation is due to the database provider.

script.Split(_splitSlash, StringSplitOptions.RemoveEmptyEntries)
.Select(s => StripLineComments(s).Trim()).ToList()
.ForEach(s =>
{
if (string.IsNullOrEmpty(s)) return;

if (s.EndsWith("END;", StringComparison.OrdinalIgnoreCase))
{
base.ExecuteBuildScript(database, s);
return;
}

s.Split(_splitSemiColon, StringSplitOptions.RemoveEmptyEntries).ToList()
.ForEach(x => base.ExecuteBuildScript(database, x));
});
}

private void EnsureDatabaseSetup()
{
//No need to run database setup scripts for every test
if (_phase != ExecutionPhase.Setup) return;

var previousName = _connectionName;
_connectionName = "Oracle_Builder";

_ = base.Execute();

_connectionName = previousName;
_phase = ExecutionPhase.Build;
}

private string StripLineComments(string script)
{
var parts = script.Split(_splitNewLine, StringSplitOptions.RemoveEmptyEntries)
.Where(s => !s.Trim().StartsWith("--"));
return string.Join(_splitNewLine[0], parts);
}
}
}
6 changes: 6 additions & 0 deletions PetaPoco.Tests.Integration/Providers/TestProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,11 @@ protected string GetProviderName(string connectionName)
return ConfigurationManager.ConnectionStrings[connectionName].ProviderName;
#endif
}

public enum ExecutionPhase
{
Setup = 0,
Build = 1
}
}
}
189 changes: 189 additions & 0 deletions PetaPoco.Tests.Integration/Scripts/OracleBuildDatabase.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.OrderLines');
/
CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.Orders');
/
CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.People');
/
CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.SpecificOrderLines');
/
CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.SpecificOrders');
/
CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.SpecificPeople');
/
CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.TransactionLogs');
/
CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.Note');
/

CREATE TABLE People (
Id RAW(16) 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 RAW(16),
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 RAW(16) 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 RAW(16),
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.BugInvestigation_10R9LZYK');
/
CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.BugInvestigation_3F489XV0');
/
CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.BugInvestigation_64O6LT8U');
/
CALL SYS.DROP_IF_EXISTS('TABLE', 'petapoco.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.SelectPeople');
/
CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco.SelectPeopleWithParam');
/
CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco.CountPeople');
/
CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco.CountPeopleWithParam');
/
CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco.UpdatePeople');
/
CALL SYS.DROP_IF_EXISTS('PROCEDURE', 'petapoco.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 > 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 > 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 > age;
END;
/
Loading

0 comments on commit eaf8e5f

Please sign in to comment.