diff --git a/QueryDB.Core.Tests/Docker/docker-compose-mssql.yml b/QueryDB.Core.Tests/Docker/docker-compose-mssql.yml index 2664ee5..19f5097 100644 --- a/QueryDB.Core.Tests/Docker/docker-compose-mssql.yml +++ b/QueryDB.Core.Tests/Docker/docker-compose-mssql.yml @@ -8,7 +8,7 @@ services: ports: - "1433:1433" volumes: - - ./QueryDB.Core.Tests/SeedData:/home + - ../SeedData:/home healthcheck: test: ["CMD-SHELL", "/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P $$SA_PASSWORD -Q \"SELECT 'mssql' AS current_database;\""] interval: 5s diff --git a/QueryDB.Core.Tests/Docker/docker-compose-mysql.yml b/QueryDB.Core.Tests/Docker/docker-compose-mysql.yml index f2fb660..907a237 100644 --- a/QueryDB.Core.Tests/Docker/docker-compose-mysql.yml +++ b/QueryDB.Core.Tests/Docker/docker-compose-mysql.yml @@ -7,7 +7,7 @@ services: ports: - "3306:3306" volumes: - - ./QueryDB.Core.Tests/SeedData:/home + - ../SeedData:/home healthcheck: test: ["CMD-SHELL", "mysql -uroot -p$$MYSQL_ROOT_PASSWORD -e 'SELECT DATABASE() AS current_database;'"] interval: 5s diff --git a/QueryDB.Core.Tests/Docker/docker-compose-oracle.yml b/QueryDB.Core.Tests/Docker/docker-compose-oracle.yml index 6c0690f..7befd74 100644 --- a/QueryDB.Core.Tests/Docker/docker-compose-oracle.yml +++ b/QueryDB.Core.Tests/Docker/docker-compose-oracle.yml @@ -7,7 +7,7 @@ services: ports: - "1521:1521" volumes: - - ./QueryDB.Core.Tests/SeedData:/home + - ../SeedData:/home shm_size: 4gb healthcheck: test: ["CMD-SHELL", "echo \"SELECT 'oracle' AS current_database FROM dual;\" | sqlplus -s sys/$$ORACLE_PWD@localhost:1521/XE as sysdba"] diff --git a/QueryDB.Core.Tests/Docker/docker-compose-postgres.yml b/QueryDB.Core.Tests/Docker/docker-compose-postgres.yml index 859c76d..3f82cf8 100644 --- a/QueryDB.Core.Tests/Docker/docker-compose-postgres.yml +++ b/QueryDB.Core.Tests/Docker/docker-compose-postgres.yml @@ -9,7 +9,7 @@ services: ports: - "5432:5432" volumes: - - ./QueryDB.Core.Tests/SeedData:/home + - ../SeedData:/home healthcheck: test: ["CMD-SHELL", "pg_isready -U sys"] interval: 5s diff --git a/QueryDB.Core.Tests/MSSQLTests.cs b/QueryDB.Core.Tests/MSSQLTests.cs index 553271f..92f072e 100644 --- a/QueryDB.Core.Tests/MSSQLTests.cs +++ b/QueryDB.Core.Tests/MSSQLTests.cs @@ -1,5 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using QueryDB.Exceptions; using System; +using System.Collections.Generic; using System.Linq; namespace QueryDB.Core.Tests @@ -308,6 +310,310 @@ public void Test_MSSQL_FetchData_Entity_Strict_Error_Check() #endregion + #region Execute Command Tests - << int ExecuteCommand(string sqlStatement) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public void Test_MSSQL_ExecuteCommand_DDL_Queries() + { + var createTableSql = Queries.MSSQLQueries.TestDB.DDL.Create_Table; + var alterTableSql = Queries.MSSQLQueries.TestDB.DDL.Alter_Table; + var commentTableSql = Queries.MSSQLQueries.TestDB.DDL.Comment_Table; + var commentTableColumnSql = Queries.MSSQLQueries.TestDB.DDL.Comment_Table_Column; + var truncateTableSql = Queries.MSSQLQueries.TestDB.DDL.Truncate_Table; + var renameTableSql = Queries.MSSQLQueries.TestDB.DDL.Rename_Table; + var dropTableSql = Queries.MSSQLQueries.TestDB.DDL.Drop_Table; + var dDLExecutionCheckSql = Queries.MSSQLQueries.TestDB.DDL.DDL_Execute_check; + var dDLTableCommentCheckSql = Queries.MSSQLQueries.TestDB.DDL.DDL_Table_Comment_check; + var dDLTableColumnCommentCheckSql = Queries.MSSQLQueries.TestDB.DDL.DDL_Table_Column_Comment_check; + + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + dbContext.ExecuteCommand(createTableSql); + dbContext.ExecuteCommand(alterTableSql); + dbContext.ExecuteCommand(commentTableSql); + dbContext.ExecuteCommand(commentTableColumnSql); + dbContext.ExecuteCommand(truncateTableSql); + + var tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "dbo", "Employee")); + Assert.AreEqual("1", tableCount[0].ReferenceData["Table_Count"]); + var tableComment = dbContext + .FetchData(string.Format(dDLTableCommentCheckSql, "dbo", "Employee")); + Assert.AreEqual("This table stores employee records", tableComment[0].ReferenceData["Table_Comment"]); + var tableColumnComment = dbContext + .FetchData(string.Format(dDLTableColumnCommentCheckSql, "dbo", "Employee")); + Assert.AreEqual("This column stores employee middle name", tableColumnComment[0].ReferenceData["Table_Column_Comment"]); + + dbContext.ExecuteCommand(renameTableSql); + + tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "dbo", "Employee")); + Assert.AreEqual("0", tableCount[0].ReferenceData["Table_Count"]); + tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "dbo", "Employees")); + Assert.AreEqual("1", tableCount[0].ReferenceData["Table_Count"]); + + dbContext.ExecuteCommand(dropTableSql); + + tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "dbo", "Employees")); + Assert.AreEqual("0", tableCount[0].ReferenceData["Table_Count"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public void Test_MSSQL_ExecuteCommand_DML_Queries() + { + var insertSql = Queries.MSSQLQueries.TestDB.DML.InsertSql; + var updateSql = Queries.MSSQLQueries.TestDB.DML.UpdateSql; + var deleteSql = Queries.MSSQLQueries.TestDB.DML.DeleteSql; + var verifyDMLExecution = Queries.MSSQLQueries.TestDB.DML.VerifyDMLExecution; + + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + + // Insert + var rows = dbContext.ExecuteCommand(insertSql); + Assert.AreEqual(1, rows); + var data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 1); + var agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["Agent_Code"]); + Assert.AreEqual("John", agent.ReferenceData["Agent_Name"]); + Assert.AreEqual("Wick", agent.ReferenceData["Working_Area"]); + Assert.AreEqual("0.11", agent.ReferenceData["Commission"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["Phone_No"]); + Assert.AreEqual("", agent.ReferenceData["Country"]); + + // Update + rows = dbContext.ExecuteCommand(updateSql); + Assert.AreEqual(1, rows); + data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 1); + agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["Agent_Code"]); + Assert.AreEqual("John", agent.ReferenceData["Agent_Name"]); + Assert.AreEqual("Wick", agent.ReferenceData["Working_Area"]); + Assert.AreEqual("0.15", agent.ReferenceData["Commission"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["Phone_No"]); + Assert.AreEqual("", agent.ReferenceData["Country"]); + + // Delete + rows = dbContext.ExecuteCommand(deleteSql); + Assert.AreEqual(1, rows); + data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 0); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public void Test_MSSQL_ExecuteCommand_DML_Unsupported_SELECT_Queries() + { + var selectSql = Queries.MSSQLQueries.TestDB.DML.SelectSql; + + // Select + try + { + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + var rows = dbContext.ExecuteCommand(selectSql); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("SELECT queries are not supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteCommand' doesn't support SELECT queries.", ex.AdditionalInfo); + } + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public void Test_MSSQL_ExecuteCommand_DCL_Queries() + { + var login = "test_user"; + var user = "test_user"; + var password = "Test@1234"; + var table = "agents"; + var commands = "SELECT, UPDATE"; + var checkCommand = "SELECT"; + + var createLogin = string.Format(Queries.MSSQLQueries.TestDB.DCL.CreateLoginSql_Login_Password, login, password); + var createUser = string.Format(Queries.MSSQLQueries.TestDB.DCL.CreateUserSql_User_Login, user, login); + var grantSql = string.Format(Queries.MSSQLQueries.TestDB.DCL.GrantSql_Command_Table_User, commands, table, user); + var revokeSql = string.Format(Queries.MSSQLQueries.TestDB.DCL.RevokeSql_Command_Table_User, commands, table, user); + var verifyPermissions = string.Format(Queries.MSSQLQueries.TestDB.DCL.VerifyPermission_User_Table_Command, user, table, checkCommand); + var removeUser = string.Format(Queries.MSSQLQueries.TestDB.DCL.RemoveUserSql_User, user); + var removeLogin = string.Format(Queries.MSSQLQueries.TestDB.DCL.RemoveLoginSql_Login, login); + + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + + // Create Login + var result = dbContext.ExecuteCommand(createLogin); + Assert.AreEqual(-1, result); + + // Create User + result = dbContext.ExecuteCommand(createUser); + Assert.AreEqual(-1, result); + + // Existing Permissions + var data = dbContext.FetchData(verifyPermissions).FirstOrDefault(); + Assert.AreEqual("0", data.ReferenceData["HasPermission"]); + + // Grant + result = dbContext.ExecuteCommand(grantSql); + Assert.AreEqual(-1, result); + data = dbContext.FetchData(verifyPermissions).FirstOrDefault(); + Assert.AreEqual("1", data.ReferenceData["HasPermission"]); + + // Revoke + result = dbContext.ExecuteCommand(revokeSql); + Assert.AreEqual(-1, result); + data = dbContext.FetchData(verifyPermissions).FirstOrDefault(); + Assert.AreEqual("0", data.ReferenceData["HasPermission"]); + + // Remove User + result = dbContext.ExecuteCommand(removeUser); + Assert.AreEqual(-1, result); + + // Remove Login + result = dbContext.ExecuteCommand(removeLogin); + Assert.AreEqual(-1, result); + } + + #endregion + + #region Execute Transaction Tests - << bool ExecuteTransaction(List sqlStatements) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public void Test_MSSQL_ExecuteTransaction_DDL_Multiple_Queries() + { + var createTableSql = Queries.MSSQLQueries.TestDB.DDL.Create_Table; + var alterTableSql = Queries.MSSQLQueries.TestDB.DDL.Alter_Table; + var truncateTableSql = Queries.MSSQLQueries.TestDB.DDL.Truncate_Table; + var renameTableSql = Queries.MSSQLQueries.TestDB.DDL.Rename_Table; + var dropTableSql = Queries.MSSQLQueries.TestDB.DDL.Drop_Table; + var dDLExecutionCheckSql = Queries.MSSQLQueries.TestDB.DDL.DDL_Execute_check; + + // Create, Alter & Truncate + var statements = new List + { + createTableSql, + alterTableSql, + truncateTableSql + }; + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + var result = dbContext.ExecuteTransaction(statements); + Assert.IsTrue(result); + + var tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "dbo", "Employee")); + Assert.AreEqual("1", tableCount[0].ReferenceData["Table_Count"]); + + // Rename & Drop + statements = new List + { + renameTableSql, + dropTableSql + }; + result = dbContext.ExecuteTransaction(statements); + Assert.IsTrue(result); + + tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "dbo", "Employees")); + Assert.AreEqual("0", tableCount[0].ReferenceData["Table_Count"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public void Test_MSSQL_ExecuteTransaction_DML_Multiple_Queries() + { + var insertSql = Queries.MSSQLQueries.TestDB.DML.InsertSql; + var updateSql = Queries.MSSQLQueries.TestDB.DML.UpdateSql; + var deleteSql = Queries.MSSQLQueries.TestDB.DML.DeleteSql; + var verifyDMLExecution = Queries.MSSQLQueries.TestDB.DML.VerifyDMLExecution; + + var statements = new List + { + insertSql, + updateSql + }; + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + + // Insert & Update + var result = dbContext.ExecuteTransaction(statements); + Assert.IsTrue(result); + var data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 1); + var agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["Agent_Code"]); + Assert.AreEqual("John", agent.ReferenceData["Agent_Name"]); + Assert.AreEqual("Wick", agent.ReferenceData["Working_Area"]); + Assert.AreEqual("0.15", agent.ReferenceData["Commission"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["Phone_No"]); + Assert.AreEqual("", agent.ReferenceData["Country"]); + + // Delete + statements = new List + { + deleteSql + }; + result = dbContext.ExecuteTransaction(statements); + Assert.IsTrue(result); + data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 0); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public void Test_MSSQL_ExecuteTransaction_Incomplete_Transaction_Rollback_On_Error() + { + var insertSql = Queries.MSSQLQueries.TestDB.DML.InsertSql; + var updateSql = Queries.MSSQLQueries.TestDB.DML.UpdateSql; + var updateErrorSql = "UPDATE"; + var verifyDMLExecution = Queries.MSSQLQueries.TestDB.DML.VerifyDMLExecution; + + var statements = new List + { + insertSql, + updateSql, + updateErrorSql + }; + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + + // Insert & Update + var result = dbContext.ExecuteTransaction(statements); + Assert.IsFalse(result); + var data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 0); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public void Test_MSSQL_ExecuteTransaction_DML_Unsupported_SELECT_Queries() + { + var selectSql = Queries.MSSQLQueries.TestDB.DML.SelectSql; + + // Select + try + { + var statements = new List + { + selectSql + }; + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + var result = dbContext.ExecuteTransaction(statements); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("SELECT queries are not supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteTransaction' doesn't support SELECT queries.", ex.AdditionalInfo); + } + } + + #endregion + #endregion } diff --git a/QueryDB.Core.Tests/MySQLTests.cs b/QueryDB.Core.Tests/MySQLTests.cs index 6335bb1..c8812cc 100644 --- a/QueryDB.Core.Tests/MySQLTests.cs +++ b/QueryDB.Core.Tests/MySQLTests.cs @@ -1,5 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using QueryDB.Exceptions; using System; +using System.Collections.Generic; using System.Linq; namespace QueryDB.Core.Tests @@ -288,6 +290,302 @@ public void Test_MySQL_FetchData_Entity_Strict_Error_Check() #endregion + #region Execute Command Tests - << int ExecuteCommand(string sqlStatement) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public void Test_MySQL_ExecuteCommand_DDL_Queries() + { + var createTableSql = Queries.MySQLQueries.TestDB.DDL.Create_Table; + var alterTableSql = Queries.MySQLQueries.TestDB.DDL.Alter_Table; + var commentTableSql = Queries.MySQLQueries.TestDB.DDL.Comment_Table; + var commentTableColumnSql = Queries.MySQLQueries.TestDB.DDL.Comment_Table_Column; + var truncateTableSql = Queries.MySQLQueries.TestDB.DDL.Truncate_Table; + var renameTableSql = Queries.MySQLQueries.TestDB.DDL.Rename_Table; + var dropTableSql = Queries.MySQLQueries.TestDB.DDL.Drop_Table; + var dDLExecutionCheckSql = Queries.MySQLQueries.TestDB.DDL.DDL_Execute_check; + var dDLTableCommentCheckSql = Queries.MySQLQueries.TestDB.DDL.DDL_Table_Comment_check; + var dDLTableColumnCommentCheckSql = Queries.MySQLQueries.TestDB.DDL.DDL_Table_Column_Comment_check; + + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + dbContext.ExecuteCommand(createTableSql); + dbContext.ExecuteCommand(alterTableSql); + dbContext.ExecuteCommand(commentTableSql); + dbContext.ExecuteCommand(commentTableColumnSql); + dbContext.ExecuteCommand(truncateTableSql); + + var tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "mysql", "Employee")); + Assert.AreEqual("1", tableCount[0].ReferenceData["Table_Count"]); + var tableComment = dbContext + .FetchData(string.Format(dDLTableCommentCheckSql, "mysql", "Employee")); + Assert.AreEqual("This table stores employee records", tableComment[0].ReferenceData["Table_Comment"]); + var tableColumnComment = dbContext + .FetchData(string.Format(dDLTableColumnCommentCheckSql, "mysql", "Employee")); + Assert.AreEqual("This column stores employee middle name", tableColumnComment[3].ReferenceData["Table_Column_Comment"]); + + dbContext.ExecuteCommand(renameTableSql); + + tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "mysql", "Employee")); + Assert.AreEqual("0", tableCount[0].ReferenceData["Table_Count"]); + tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "mysql", "Employees")); + Assert.AreEqual("1", tableCount[0].ReferenceData["Table_Count"]); + + dbContext.ExecuteCommand(dropTableSql); + + tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "mysql", "Employees")); + Assert.AreEqual("0", tableCount[0].ReferenceData["Table_Count"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public void Test_MySQL_ExecuteCommand_DML_Queries() + { + var insertSql = Queries.MySQLQueries.TestDB.DML.InsertSql; + var updateSql = Queries.MySQLQueries.TestDB.DML.UpdateSql; + var deleteSql = Queries.MySQLQueries.TestDB.DML.DeleteSql; + var verifyDMLExecution = Queries.MSSQLQueries.TestDB.DML.VerifyDMLExecution; + + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + + // Insert + var rows = dbContext.ExecuteCommand(insertSql); + Assert.AreEqual(1, rows); + var data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 1); + var agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["Agent_Code"]); + Assert.AreEqual("John", agent.ReferenceData["Agent_Name"]); + Assert.AreEqual("Wick", agent.ReferenceData["Working_Area"]); + Assert.AreEqual("0.11", agent.ReferenceData["Commission"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["Phone_No"]); + Assert.AreEqual("", agent.ReferenceData["Country"]); + + // Update + rows = dbContext.ExecuteCommand(updateSql); + Assert.AreEqual(1, rows); + data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 1); + agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["Agent_Code"]); + Assert.AreEqual("John", agent.ReferenceData["Agent_Name"]); + Assert.AreEqual("Wick", agent.ReferenceData["Working_Area"]); + Assert.AreEqual("0.15", agent.ReferenceData["Commission"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["Phone_No"]); + Assert.AreEqual("", agent.ReferenceData["Country"]); + + // Delete + rows = dbContext.ExecuteCommand(deleteSql); + Assert.AreEqual(1, rows); + data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 0); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public void Test_MySQL_ExecuteCommand_DML_Unsupported_SELECT_Queries() + { + var selectSql = Queries.MySQLQueries.TestDB.DML.SelectSql; + + // Select + try + { + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + var rows = dbContext.ExecuteCommand(selectSql); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("SELECT queries are not supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteCommand' doesn't support SELECT queries.", ex.AdditionalInfo); + } + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public void Test_MySQL_ExecuteCommand_DCL_Queries() + { + var user = "test_user"; + var password = "Test@1234"; + var table = "Agents"; + var commands = "SELECT, UPDATE"; + var checkCommand = "SELECT"; + + var createUser = string.Format(Queries.MySQLQueries.TestDB.DCL.CreateUserSql_User_Password, user, password); + var grantSql = string.Format(Queries.MySQLQueries.TestDB.DCL.GrantSql_Command_Table_User, commands, table, user); + var revokeSql = string.Format(Queries.MySQLQueries.TestDB.DCL.RevokeSql_Command_Table_User, commands, table, user); + var verifyPermissions = string.Format(Queries.MySQLQueries.TestDB.DCL.VerifyPermission_User, user); + var removeUser = string.Format(Queries.MySQLQueries.TestDB.DCL.RemoveUserSql_User, user); + + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + + // Create User + var result = dbContext.ExecuteCommand(createUser); + Assert.AreEqual(0, result); + + // Existing Permissions + var data = dbContext.FetchData(verifyPermissions); + Assert.AreEqual(1, data.Count); + Assert.IsFalse(data.Any(data => data.ReferenceData.Values.Any(value => value.Contains(checkCommand)))); + + // Grant + result = dbContext.ExecuteCommand(grantSql); + Assert.AreEqual(0, result); + data = dbContext.FetchData(verifyPermissions); + Assert.AreEqual(2, data.Count); + Assert.IsTrue(data.Any(data => data.ReferenceData.Values.Any(value => value.Contains(checkCommand)))); + + // Revoke + result = dbContext.ExecuteCommand(revokeSql); + Assert.AreEqual(0, result); + data = dbContext.FetchData(verifyPermissions); + Assert.AreEqual(1, data.Count); + Assert.IsFalse(data.Any(data => data.ReferenceData.Values.Any(value => value.Contains(checkCommand)))); + + //Remove User + result = dbContext.ExecuteCommand(removeUser); + Assert.AreEqual(0, result); + } + + #endregion + + #region Execute Transaction Tests - << bool ExecuteTransaction(List sqlStatements) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public void Test_MySQL_ExecuteTransaction_DDL_Multiple_Queries() + { + var createTableSql = Queries.MySQLQueries.TestDB.DDL.Create_Table; + var alterTableSql = Queries.MySQLQueries.TestDB.DDL.Alter_Table; + var truncateTableSql = Queries.MySQLQueries.TestDB.DDL.Truncate_Table; + var renameTableSql = Queries.MySQLQueries.TestDB.DDL.Rename_Table; + var dropTableSql = Queries.MySQLQueries.TestDB.DDL.Drop_Table; + var dDLExecutionCheckSql = Queries.MySQLQueries.TestDB.DDL.DDL_Execute_check; + + // Create, Alter & Truncate + var statements = new List + { + createTableSql, + alterTableSql, + truncateTableSql + }; + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + var result = dbContext.ExecuteTransaction(statements); + Assert.IsTrue(result); + + var tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "mysql", "Employee")); + Assert.AreEqual("1", tableCount[0].ReferenceData["Table_Count"]); + + // Rename & Drop + statements = new List + { + renameTableSql, + dropTableSql + }; + result = dbContext.ExecuteTransaction(statements); + Assert.IsTrue(result); + + tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "mysql", "Employees")); + Assert.AreEqual("0", tableCount[0].ReferenceData["Table_Count"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public void Test_MySQL_ExecuteTransaction_DML_Multiple_Queries() + { + var insertSql = Queries.MySQLQueries.TestDB.DML.InsertSql; + var updateSql = Queries.MySQLQueries.TestDB.DML.UpdateSql; + var deleteSql = Queries.MySQLQueries.TestDB.DML.DeleteSql; + var verifyDMLExecution = Queries.MySQLQueries.TestDB.DML.VerifyDMLExecution; + + var statements = new List + { + insertSql, + updateSql + }; + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + + // Insert & Update + var result = dbContext.ExecuteTransaction(statements); + Assert.IsTrue(result); + var data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 1); + var agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["Agent_Code"]); + Assert.AreEqual("John", agent.ReferenceData["Agent_Name"]); + Assert.AreEqual("Wick", agent.ReferenceData["Working_Area"]); + Assert.AreEqual("0.15", agent.ReferenceData["Commission"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["Phone_No"]); + Assert.AreEqual("", agent.ReferenceData["Country"]); + + // Delete + statements = new List + { + deleteSql + }; + result = dbContext.ExecuteTransaction(statements); + Assert.IsTrue(result); + data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 0); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public void Test_MySQL_ExecuteTransaction_Incomplete_Transaction_Rollback_On_Error() + { + var insertSql = Queries.MySQLQueries.TestDB.DML.InsertSql; + var updateSql = Queries.MySQLQueries.TestDB.DML.UpdateSql; + var updateErrorSql = "UPDATE"; + var verifyDMLExecution = Queries.MySQLQueries.TestDB.DML.VerifyDMLExecution; + + var statements = new List + { + insertSql, + updateSql, + updateErrorSql + }; + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + + // Insert & Update + var result = dbContext.ExecuteTransaction(statements); + Assert.IsFalse(result); + var data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 0); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public void Test_MySQL_ExecuteTransaction_DML_Unsupported_SELECT_Queries() + { + var selectSql = Queries.MySQLQueries.TestDB.DML.SelectSql; + + // Select + try + { + var statements = new List + { + selectSql + }; + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + var result = dbContext.ExecuteTransaction(statements); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("SELECT queries are not supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteTransaction' doesn't support SELECT queries.", ex.AdditionalInfo); + } + } + + #endregion + #endregion } diff --git a/QueryDB.Core.Tests/OracleTests.cs b/QueryDB.Core.Tests/OracleTests.cs index 3541380..c8a5e4f 100644 --- a/QueryDB.Core.Tests/OracleTests.cs +++ b/QueryDB.Core.Tests/OracleTests.cs @@ -1,5 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using QueryDB.Exceptions; using System; +using System.Collections.Generic; using System.Linq; namespace QueryDB.Core.Tests @@ -290,6 +292,311 @@ public void Test_Oracle_FetchData_Entity_Strict_Error_Check() #endregion + #region Execute Command Tests - << int ExecuteCommand(string sqlStatement) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public void Test_Oracle_ExecuteCommand_DDL_Queries() + { + var createTableSql = Queries.OracleQueries.TestDB.DDL.Create_Table; + var alterTableSql = Queries.OracleQueries.TestDB.DDL.Alter_Table; + var commentTableSql = Queries.OracleQueries.TestDB.DDL.Comment_Table; + var commentTableColumnSql = Queries.OracleQueries.TestDB.DDL.Comment_Table_Column; + var truncateTableSql = Queries.OracleQueries.TestDB.DDL.Truncate_Table; + var renameTableSql = Queries.OracleQueries.TestDB.DDL.Rename_Table; + var dropTableSql = Queries.OracleQueries.TestDB.DDL.Drop_Table; + var dDLExecutionCheckSql = Queries.OracleQueries.TestDB.DDL.DDL_Execute_check; + var dDLTableCommentCheckSql = Queries.OracleQueries.TestDB.DDL.DDL_Table_Comment_check; + var dDLTableColumnCommentCheckSql = Queries.OracleQueries.TestDB.DDL.DDL_Table_Column_Comment_check; + + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + dbContext.ExecuteCommand(createTableSql); + dbContext.ExecuteCommand(alterTableSql); + dbContext.ExecuteCommand(commentTableSql); + dbContext.ExecuteCommand(commentTableColumnSql); + dbContext.ExecuteCommand(truncateTableSql); + + var tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "Employee")); + Assert.AreEqual("1", tableCount[0].ReferenceData["TABLE_COUNT"]); + var tableComment = dbContext + .FetchData(string.Format(dDLTableCommentCheckSql, "Employee")); + Assert.AreEqual("This table stores employee records", tableComment[0].ReferenceData["TABLE_COMMENT"]); + var tableColumnComment = dbContext + .FetchData(string.Format(dDLTableColumnCommentCheckSql, "Employee")); + Assert.AreEqual("This column stores employee middle name", tableColumnComment[3].ReferenceData["TABLE_COLUMN_COMMENT"]); + + dbContext.ExecuteCommand(renameTableSql); + + tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "Employee")); + Assert.AreEqual("0", tableCount[0].ReferenceData["TABLE_COUNT"]); + tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "Employees")); + Assert.AreEqual("1", tableCount[0].ReferenceData["TABLE_COUNT"]); + + dbContext.ExecuteCommand(dropTableSql); + + tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "Employees")); + Assert.AreEqual("0", tableCount[0].ReferenceData["TABLE_COUNT"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public void Test_Oracle_ExecuteCommand_DML_Queries() + { + var insertSql = Queries.OracleQueries.TestDB.DML.InsertSql; + var updateSql = Queries.OracleQueries.TestDB.DML.UpdateSql; + var deleteSql = Queries.OracleQueries.TestDB.DML.DeleteSql; + var verifyDMLExecution = Queries.OracleQueries.TestDB.DML.VerifyDMLExecution; + + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + + // Insert + var rows = dbContext.ExecuteCommand(insertSql); + Assert.AreEqual(1, rows); + var data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 1); + var agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("John", agent.ReferenceData["AGENT_NAME"]); + Assert.AreEqual("Wick", agent.ReferenceData["WORKING_AREA"]); + Assert.AreEqual("0.11", agent.ReferenceData["COMMISSION"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["PHONE_NO"]); + Assert.AreEqual("", agent.ReferenceData["COUNTRY"]); + + // Update + rows = dbContext.ExecuteCommand(updateSql); + Assert.AreEqual(1, rows); + data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 1); + agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("John", agent.ReferenceData["AGENT_NAME"]); + Assert.AreEqual("Wick", agent.ReferenceData["WORKING_AREA"]); + Assert.AreEqual("0.15", agent.ReferenceData["COMMISSION"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["PHONE_NO"]); + Assert.AreEqual("", agent.ReferenceData["COUNTRY"]); + + // Delete + rows = dbContext.ExecuteCommand(deleteSql); + Assert.AreEqual(1, rows); + data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 0); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public void Test_Oracle_ExecuteCommand_DML_Unsupported_SELECT_Queries() + { + var selectSql = Queries.OracleQueries.TestDB.DML.SelectSql; + + // Select + try + { + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + var rows = dbContext.ExecuteCommand(selectSql); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("SELECT queries are not supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteCommand' doesn't support SELECT queries.", ex.AdditionalInfo); + } + } + + [TestMethod] + [TestCategory(ORACLE_TESTS), TestCategory(ORACLE_TESTS)] + public void Test_Oracle_ExecuteCommand_DCL_Queries() + { + var user = "C##TEST_USER"; + var password = "Test123456"; + var table = "AGENTS"; + var commands = "SELECT, UPDATE"; + var checkCommand = "SELECT"; + + var createUser = string.Format(Queries.OracleQueries.TestDB.DCL.CreateUserSql_User_Password, user, password); + var grantConnect = string.Format(Queries.OracleQueries.TestDB.DCL.GrantConnectSql_User, user); + var grantSql = string.Format(Queries.OracleQueries.TestDB.DCL.GrantSql_Command_Table_User, commands, table, user); + var revokeSql = string.Format(Queries.OracleQueries.TestDB.DCL.RevokeSql_Command_Table_User, commands, table, user); + var verifyPermissions = string.Format(Queries.OracleQueries.TestDB.DCL.VerifyPermission_User, user); + var removeUser = string.Format(Queries.OracleQueries.TestDB.DCL.RemoveUserSql_User, user); + + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + + // Create User + var result = dbContext.ExecuteCommand(createUser); + Assert.AreEqual(0, result); + + // Grant CONNECT to User + result = dbContext.ExecuteCommand("CREATE ROLE CONNECT"); + result = dbContext.ExecuteCommand($"GRANT CONNECT, RESOURCE TO {user}"); + //result = dbContext.ExecuteCommand($"GRANT CREATE SEQUENCE TO {user}"); + //result = dbContext.ExecuteCommand($"GRANT CREATE SYNONYM TO {user}"); + //result = dbContext.ExecuteCommand($"GRANT UNLIMITED TABLESPACE TO {user}"); + //Assert.AreEqual(0, result); + + // Existing Permissions + var data = dbContext.FetchData(verifyPermissions); + Assert.AreEqual(1, data.Count); + Assert.IsFalse(data.Any(data => data.ReferenceData.Values.Any(value => value.Contains(checkCommand)))); + + // Grant + result = dbContext.ExecuteCommand(grantSql); + Assert.AreEqual(0, result); + data = dbContext.FetchData(verifyPermissions); + Assert.AreEqual(2, data.Count); + Assert.IsTrue(data.Any(data => data.ReferenceData.Values.Any(value => value.Contains(checkCommand)))); + + // Revoke + result = dbContext.ExecuteCommand(revokeSql); + Assert.AreEqual(0, result); + data = dbContext.FetchData(verifyPermissions); + Assert.AreEqual(1, data.Count); + Assert.IsFalse(data.Any(data => data.ReferenceData.Values.Any(value => value.Contains(checkCommand)))); + + // Remove User + result = dbContext.ExecuteCommand(removeUser); + Assert.AreEqual(0, result); + } + + #endregion + + #region Execute Transaction Tests - << bool ExecuteTransaction(List sqlStatements) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public void Test_Oracle_ExecuteTransaction_DDL_Multiple_Queries() + { + var createTableSql = Queries.OracleQueries.TestDB.DDL.Create_Table; + var alterTableSql = Queries.OracleQueries.TestDB.DDL.Alter_Table; + var truncateTableSql = Queries.OracleQueries.TestDB.DDL.Truncate_Table; + var renameTableSql = Queries.OracleQueries.TestDB.DDL.Rename_Table; + var dropTableSql = Queries.OracleQueries.TestDB.DDL.Drop_Table; + var dDLExecutionCheckSql = Queries.OracleQueries.TestDB.DDL.DDL_Execute_check; + + // Create, Alter & Truncate + var statements = new List + { + createTableSql, + alterTableSql, + truncateTableSql + }; + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + var result = dbContext.ExecuteTransaction(statements); + Assert.IsTrue(result); + + var tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "Employee")); + Assert.AreEqual("1", tableCount[0].ReferenceData["TABLE_COUNT"]); + + // Rename & Drop + statements = new List + { + renameTableSql, + dropTableSql + }; + result = dbContext.ExecuteTransaction(statements); + Assert.IsTrue(result); + + tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "Employees")); + Assert.AreEqual("0", tableCount[0].ReferenceData["TABLE_COUNT"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public void Test_Oracle_ExecuteTransaction_DML_Multiple_Queries() + { + var insertSql = Queries.OracleQueries.TestDB.DML.InsertSql; + var updateSql = Queries.OracleQueries.TestDB.DML.UpdateSql; + var deleteSql = Queries.OracleQueries.TestDB.DML.DeleteSql; + var verifyDMLExecution = Queries.OracleQueries.TestDB.DML.VerifyDMLExecution; + + var statements = new List + { + insertSql, + updateSql + }; + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + + // Insert & Update + var result = dbContext.ExecuteTransaction(statements); + Assert.IsTrue(result); + var data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 1); + var agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("John", agent.ReferenceData["AGENT_NAME"]); + Assert.AreEqual("Wick", agent.ReferenceData["WORKING_AREA"]); + Assert.AreEqual("0.15", agent.ReferenceData["COMMISSION"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["PHONE_NO"]); + Assert.AreEqual("", agent.ReferenceData["COUNTRY"]); + + // Delete + statements = new List + { + deleteSql + }; + result = dbContext.ExecuteTransaction(statements); + Assert.IsTrue(result); + data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 0); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public void Test_Oracle_ExecuteTransaction_Incomplete_Transaction_Rollback_On_Error() + { + var insertSql = Queries.OracleQueries.TestDB.DML.InsertSql; + var updateSql = Queries.OracleQueries.TestDB.DML.UpdateSql; + var updateErrorSql = "UPDATE"; + var verifyDMLExecution = Queries.OracleQueries.TestDB.DML.VerifyDMLExecution; + + var statements = new List + { + insertSql, + updateSql, + updateErrorSql + }; + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + + // Insert & Update + var result = dbContext.ExecuteTransaction(statements); + Assert.IsFalse(result); + var data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 0); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public void Test_Oracle_ExecuteTransaction_DML_Unsupported_SELECT_Queries() + { + var selectSql = Queries.OracleQueries.TestDB.DML.SelectSql; + + // Select + try + { + var statements = new List + { + selectSql + }; + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + var result = dbContext.ExecuteTransaction(statements); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("SELECT queries are not supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteTransaction' doesn't support SELECT queries.", ex.AdditionalInfo); + } + } + + #endregion + #endregion } diff --git a/QueryDB.Core.Tests/PostgreSQLTests.cs b/QueryDB.Core.Tests/PostgreSQLTests.cs index 6ceb29b..67cd8df 100644 --- a/QueryDB.Core.Tests/PostgreSQLTests.cs +++ b/QueryDB.Core.Tests/PostgreSQLTests.cs @@ -1,5 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using QueryDB.Exceptions; using System; +using System.Collections.Generic; using System.Linq; namespace QueryDB.Core.Tests @@ -282,6 +284,302 @@ public void Test_PostgreSQL_FetchData_Entity_Strict_Error_Check() #endregion + #region Execute Command Tests - << int ExecuteCommand(string sqlStatement) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public void Test_PostgreSQL_ExecuteCommand_DDL_Queries() + { + var createTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Create_Table; + var alterTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Alter_Table; + var commentTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Comment_Table; + var commentTableColumnSql = Queries.PostgreSQLQueries.TestDB.DDL.Comment_Table_Column; + var truncateTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Truncate_Table; + var renameTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Rename_Table; + var dropTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Drop_Table; + var dDLExecutionCheckSql = Queries.PostgreSQLQueries.TestDB.DDL.DDL_Execute_check; + var dDLTableCommentCheckSql = Queries.PostgreSQLQueries.TestDB.DDL.DDL_Table_Comment_check; + var dDLTableColumnCommentCheckSql = Queries.PostgreSQLQueries.TestDB.DDL.DDL_Table_Column_Comment_check; + + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + dbContext.ExecuteCommand(createTableSql); + dbContext.ExecuteCommand(alterTableSql); + dbContext.ExecuteCommand(commentTableSql); + dbContext.ExecuteCommand(commentTableColumnSql); + dbContext.ExecuteCommand(truncateTableSql); + + var tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "public", "Employee")); + Assert.AreEqual("1", tableCount[0].ReferenceData["table_count"]); + var tableComment = dbContext + .FetchData(string.Format(dDLTableCommentCheckSql, "public", "Employee")); + Assert.AreEqual("This table stores employee records", tableComment[0].ReferenceData["table_comment"]); + var tableColumnComment = dbContext + .FetchData(string.Format(dDLTableColumnCommentCheckSql, "public", "Employee")); + Assert.AreEqual("This column stores employee middle name", tableColumnComment[3].ReferenceData["table_column_comment"]); + + dbContext.ExecuteCommand(renameTableSql); + + tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "public", "Employee")); + Assert.AreEqual("0", tableCount[0].ReferenceData["table_count"]); + tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "public", "Employees")); + Assert.AreEqual("1", tableCount[0].ReferenceData["table_count"]); + + dbContext.ExecuteCommand(dropTableSql); + + tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "public", "Employees")); + Assert.AreEqual("0", tableCount[0].ReferenceData["table_count"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public void Test_PostgreSQL_ExecuteCommand_DML_Queries() + { + var insertSql = Queries.PostgreSQLQueries.TestDB.DML.InsertSql; + var updateSql = Queries.PostgreSQLQueries.TestDB.DML.UpdateSql; + var deleteSql = Queries.PostgreSQLQueries.TestDB.DML.DeleteSql; + var verifyDMLExecution = Queries.MSSQLQueries.TestDB.DML.VerifyDMLExecution; + + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + + // Insert + var rows = dbContext.ExecuteCommand(insertSql); + Assert.AreEqual(1, rows); + var data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 1); + var agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["agent_code"]); + Assert.AreEqual("John", agent.ReferenceData["agent_name"]); + Assert.AreEqual("Wick", agent.ReferenceData["working_area"]); + Assert.AreEqual("0.11", agent.ReferenceData["commission"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["phone_no"]); + Assert.AreEqual("", agent.ReferenceData["country"]); + + // Update + rows = dbContext.ExecuteCommand(updateSql); + Assert.AreEqual(1, rows); + data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 1); + agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["agent_code"]); + Assert.AreEqual("John", agent.ReferenceData["agent_name"]); + Assert.AreEqual("Wick", agent.ReferenceData["working_area"]); + Assert.AreEqual("0.15", agent.ReferenceData["commission"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["phone_no"]); + Assert.AreEqual("", agent.ReferenceData["country"]); + + // Delete + rows = dbContext.ExecuteCommand(deleteSql); + Assert.AreEqual(1, rows); + data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 0); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public void Test_PostgreSQL_ExecuteCommand_DML_Unsupported_SELECT_Queries() + { + var selectSql = Queries.PostgreSQLQueries.TestDB.DML.SelectSql; + + // Select + try + { + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + var rows = dbContext.ExecuteCommand(selectSql); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("SELECT queries are not supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteCommand' doesn't support SELECT queries.", ex.AdditionalInfo); + } + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public void Test_PostgreSQL_ExecuteCommand_DCL_Queries() + { + var user = "test_user"; + var password = "Test@1234"; + var table = "Agents"; + var commands = "SELECT, UPDATE"; + var checkCommand = "SELECT"; + + var createUser = string.Format(Queries.PostgreSQLQueries.TestDB.DCL.CreateUserSql_User_Password, user, password); + var grantSql = string.Format(Queries.PostgreSQLQueries.TestDB.DCL.GrantSql_Command_Table_User, commands, table, user); + var revokeSql = string.Format(Queries.PostgreSQLQueries.TestDB.DCL.RevokeSql_Command_Table_User, commands, table, user); + var verifyPermissions = string.Format(Queries.PostgreSQLQueries.TestDB.DCL.VerifyPermission_User, user); + var removeUser = string.Format(Queries.PostgreSQLQueries.TestDB.DCL.RemoveUserSql_User, user); + + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + + // Create User + var result = dbContext.ExecuteCommand(createUser); + Assert.AreEqual(-1, result); + + // Existing Permissions + var data = dbContext.FetchData(verifyPermissions); + Assert.AreEqual(0, data.Count); + Assert.IsFalse(data.Any(data => data.ReferenceData.Values.Any(value => value.Contains(checkCommand)))); + + // Grant + result = dbContext.ExecuteCommand(grantSql); + Assert.AreEqual(-1, result); + data = dbContext.FetchData(verifyPermissions); + Assert.AreEqual(2, data.Count); + Assert.IsTrue(data.Any(data => data.ReferenceData.Values.Any(value => value.Contains(checkCommand)))); + + // Revoke + result = dbContext.ExecuteCommand(revokeSql); + Assert.AreEqual(-1, result); + data = dbContext.FetchData(verifyPermissions); + Assert.AreEqual(0, data.Count); + Assert.IsFalse(data.Any(data => data.ReferenceData.Values.Any(value => value.Contains(checkCommand)))); + + // Remove User + result = dbContext.ExecuteCommand(removeUser); + Assert.AreEqual(-1, result); + } + + #endregion + + #region Execute Transaction Tests - << bool ExecuteTransaction(List sqlStatements) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public void Test_PostgreSQL_ExecuteTransaction_DDL_Multiple_Queries() + { + var createTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Create_Table; + var alterTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Alter_Table; + var truncateTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Truncate_Table; + var renameTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Rename_Table; + var dropTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Drop_Table; + var dDLExecutionCheckSql = Queries.PostgreSQLQueries.TestDB.DDL.DDL_Execute_check; + + // Create, Alter & Truncate + var statements = new List + { + createTableSql, + alterTableSql, + truncateTableSql + }; + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + var result = dbContext.ExecuteTransaction(statements); + Assert.IsTrue(result); + + var tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "public", "Employee")); + Assert.AreEqual("1", tableCount[0].ReferenceData["table_count"]); + + // Rename & Drop + statements = new List + { + renameTableSql, + dropTableSql + }; + result = dbContext.ExecuteTransaction(statements); + Assert.IsTrue(result); + + tableCount = dbContext + .FetchData(string.Format(dDLExecutionCheckSql, "public", "Employees")); + Assert.AreEqual("0", tableCount[0].ReferenceData["table_count"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public void Test_PostgreSQL_ExecuteTransaction_DML_Multiple_Queries() + { + var insertSql = Queries.PostgreSQLQueries.TestDB.DML.InsertSql; + var updateSql = Queries.PostgreSQLQueries.TestDB.DML.UpdateSql; + var deleteSql = Queries.PostgreSQLQueries.TestDB.DML.DeleteSql; + var verifyDMLExecution = Queries.PostgreSQLQueries.TestDB.DML.VerifyDMLExecution; + + var statements = new List + { + insertSql, + updateSql + }; + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + + // Insert & Update + var result = dbContext.ExecuteTransaction(statements); + Assert.IsTrue(result); + var data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 1); + var agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["agent_code"]); + Assert.AreEqual("John", agent.ReferenceData["agent_name"]); + Assert.AreEqual("Wick", agent.ReferenceData["working_area"]); + Assert.AreEqual("0.15", agent.ReferenceData["commission"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["phone_no"]); + Assert.AreEqual("", agent.ReferenceData["country"]); + + // Delete + statements = new List + { + deleteSql + }; + result = dbContext.ExecuteTransaction(statements); + Assert.IsTrue(result); + data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 0); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public void Test_PostgreSQL_ExecuteTransaction_Incomplete_Transaction_Rollback_On_Error() + { + var insertSql = Queries.PostgreSQLQueries.TestDB.DML.InsertSql; + var updateSql = Queries.PostgreSQLQueries.TestDB.DML.UpdateSql; + var updateErrorSql = "UPDATE"; + var verifyDMLExecution = Queries.PostgreSQLQueries.TestDB.DML.VerifyDMLExecution; + + var statements = new List + { + insertSql, + updateSql, + updateErrorSql + }; + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + + // Insert & Update + var result = dbContext.ExecuteTransaction(statements); + Assert.IsFalse(result); + var data = dbContext.FetchData(verifyDMLExecution); + Assert.IsTrue(data.Count == 0); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public void Test_PostgreSQL_ExecuteTransaction_DML_Unsupported_SELECT_Queries() + { + var selectSql = Queries.PostgreSQLQueries.TestDB.DML.SelectSql; + + // Select + try + { + var statements = new List + { + selectSql + }; + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + var result = dbContext.ExecuteTransaction(statements); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("SELECT queries are not supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteTransaction' doesn't support SELECT queries.", ex.AdditionalInfo); + } + } + + #endregion + #endregion } diff --git a/QueryDB.Core.Tests/Queries.cs b/QueryDB.Core.Tests/Queries.cs index a148186..4dfa599 100644 --- a/QueryDB.Core.Tests/Queries.cs +++ b/QueryDB.Core.Tests/Queries.cs @@ -6,43 +6,106 @@ internal static class MSSQLQueries { internal static class Smoke { - public static string SelectSql = @"SELECT 'mssql' AS current_database"; + internal static string SelectSql = @"SELECT 'mssql' AS current_database"; } internal static class TestDB { - public static string SelectSql = @"SELECT * FROM Agents"; - public static string SelectSql_Join = @"SELECT A.Agent_Code, A.Agent_Name, C.Cust_Code, C.Cust_Name, O.Ord_Num, O.Ord_Amount, O.Advance_Amount, O.Ord_Date, O.Ord_Description FROM Agents A INNER JOIN + internal static string SelectSql = @"SELECT * FROM Agents"; + internal static string SelectSql_Join = @"SELECT A.Agent_Code, A.Agent_Name, C.Cust_Code, C.Cust_Name, O.Ord_Num, O.Ord_Amount, O.Advance_Amount, O.Ord_Date, O.Ord_Description FROM Agents A INNER JOIN Customer C ON C.Agent_Code = A.Agent_Code INNER JOIN Orders O ON O.Cust_Code = C.Cust_Code AND O.Agent_Code = A.Agent_Code"; - public static string SelectSql_Alias = @"SELECT A.Agent_Name AS Agent, A.WORKING_AREA AS Agent_Location, C.Cust_Name AS Customer, C.WORKING_AREA AS Customer_Location, O.Agent_Code, O.Cust_Code FROM Agents A INNER JOIN + internal static string SelectSql_Alias = @"SELECT A.Agent_Name AS Agent, A.WORKING_AREA AS Agent_Location, C.Cust_Name AS Customer, C.WORKING_AREA AS Customer_Location, O.Agent_Code, O.Cust_Code FROM Agents A INNER JOIN Customer C ON C.Agent_Code = A.Agent_Code INNER JOIN Orders O ON O.Cust_Code = C.Cust_Code AND O.Agent_Code = A.Agent_Code"; - public static string SelectSql_DataTypes = @"SELECT * FROM DataTypes"; - public static string SelectSql_Strict = @"SELECT A.Agent_Code, A.Agent_Name AS Agent, C.Cust_Code, C.Cust_Name AS Customer, O.Ord_Num, O.Ord_Amount FROM Agents A INNER JOIN + internal static string SelectSql_DataTypes = @"SELECT * FROM DataTypes"; + internal static string SelectSql_Strict = @"SELECT A.Agent_Code, A.Agent_Name AS Agent, C.Cust_Code, C.Cust_Name AS Customer, O.Ord_Num, O.Ord_Amount FROM Agents A INNER JOIN Customer C ON C.Agent_Code = A.Agent_Code INNER JOIN Orders O ON O.Cust_Code = C.Cust_Code AND O.Agent_Code = A.Agent_Code"; + internal static class DDL + { + internal static string Create_Table = @"CREATE TABLE Employee (EmployeeID INT PRIMARY KEY, FirstName NVARCHAR(50), LastName NVARCHAR(50))"; + internal static string Alter_Table = @"ALTER TABLE Employee ADD MiddleName VARCHAR(50)"; + internal static string Comment_Table = @"EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'This table stores employee records', @level0type = N'SCHEMA', @level0name = 'dbo', @level1type = N'TABLE', @level1name = 'Employee'"; + internal static string Comment_Table_Column = @"EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'This column stores employee middle name', @level0type = N'SCHEMA', @level0name = 'dbo', @level1type = N'TABLE', @level1name = 'Employee', @level2type = N'COLUMN', @level2name = 'MiddleName'"; + internal static string Truncate_Table = @"TRUNCATE TABLE Employee"; + internal static string Rename_Table = @"EXEC SP_RENAME Employee, Employees"; + internal static string Drop_Table = @"DROP TABLE Employees"; + internal static string DDL_Execute_check = @"SELECT COUNT(*) AS Table_Count FROM Information_Schema.Tables WHERE LOWER(Table_Schema) = LOWER('{0}') AND LOWER(Table_Name) = LOWER('{1}')"; + internal static string DDL_Table_Comment_check = @"SELECT value AS Table_Comment FROM fn_listextendedproperty(NULL, 'SCHEMA', '{0}', 'TABLE', '{1}', NULL, NULL)"; + internal static string DDL_Table_Column_Comment_check = @"SELECT value AS Table_Column_Comment FROM fn_listextendedproperty(NULL, 'SCHEMA', '{0}', 'TABLE', '{1}', 'COLUMN', 'MiddleName')"; + } + internal static class DML + { + internal static string InsertSql = @"INSERT INTO Agents VALUES ('A020', 'John', 'Wick', '0.11', '010-44536178', '')"; + internal static string UpdateSql = @"UPDATE Agents SET Commission = '0.15' WHERE Agent_Code = 'A020'"; + internal static string DeleteSql = @"DELETE FROM Agents WHERE Agent_Code = 'A020'"; + internal static string VerifyDMLExecution = @"SELECT * FROM Agents WHERE Agent_Code = 'A020'"; + internal static string SelectSql = @"SELECT * FROM Agents"; + } + internal static class DCL + { + internal static string CreateLoginSql_Login_Password = @"CREATE LOGIN {0} WITH PASSWORD = '{1}'"; + internal static string CreateUserSql_User_Login = @"CREATE USER {0} FOR LOGIN {1}"; + internal static string GrantSql_Command_Table_User = @"GRANT {0} ON {1} TO {2}"; + internal static string RevokeSql_Command_Table_User = @"REVOKE {0} ON {1} TO {2}"; + internal static string VerifyPermission_User_Table_Command = @"SELECT COUNT(*) AS HasPermission FROM sys.database_permissions dp + JOIN sys.database_principals pr ON dp.grantee_principal_id = pr.principal_id + JOIN sys.objects obj ON dp.major_id = obj.object_id + WHERE pr.name = '{0}' AND obj.name = '{1}' AND dp.permission_name = '{2}'"; + internal static string RemoveUserSql_User = @"DROP USER {0}"; + internal static string RemoveLoginSql_Login = @"DROP LOGIN {0}"; + } } } - + internal static class MySQLQueries { internal static class Smoke { - public static string SelectSql = @"SELECT DATABASE() AS current_database"; + internal static string SelectSql = @"SELECT DATABASE() AS current_database"; } internal static class TestDB { - public static string SelectSql = @"SELECT * FROM Agents"; - public static string SelectSql_Join = @"SELECT A.Agent_Code, A.Agent_Name, C.Cust_Code, C.Cust_Name, O.Ord_Num, O.Ord_Amount, O.Advance_Amount, O.Ord_Date, O.Ord_Description FROM Agents A INNER JOIN + internal static string SelectSql = @"SELECT * FROM Agents"; + internal static string SelectSql_Join = @"SELECT A.Agent_Code, A.Agent_Name, C.Cust_Code, C.Cust_Name, O.Ord_Num, O.Ord_Amount, O.Advance_Amount, O.Ord_Date, O.Ord_Description FROM Agents A INNER JOIN Customer C ON C.Agent_Code = A.Agent_Code INNER JOIN Orders O ON O.Cust_Code = C.Cust_Code AND O.Agent_Code = A.Agent_Code"; - public static string SelectSql_Alias = @"SELECT A.Agent_Name AS Agent, A.WORKING_AREA AS Agent_Location, C.Cust_Name AS Customer, C.WORKING_AREA AS Customer_Location, O.Agent_Code, O.Cust_Code FROM Agents A INNER JOIN + internal static string SelectSql_Alias = @"SELECT A.Agent_Name AS Agent, A.WORKING_AREA AS Agent_Location, C.Cust_Name AS Customer, C.WORKING_AREA AS Customer_Location, O.Agent_Code, O.Cust_Code FROM Agents A INNER JOIN Customer C ON C.Agent_Code = A.Agent_Code INNER JOIN Orders O ON O.Cust_Code = C.Cust_Code AND O.Agent_Code = A.Agent_Code"; - public static string SelectSql_DataTypes = @"SELECT * FROM DataTypes"; - public static string SelectSql_Strict = @"SELECT A.Agent_Code, A.Agent_Name AS Agent, C.Cust_Code, C.Cust_Name AS Customer, O.Ord_Num, O.Ord_Amount FROM Agents A INNER JOIN + internal static string SelectSql_DataTypes = @"SELECT * FROM DataTypes"; + internal static string SelectSql_Strict = @"SELECT A.Agent_Code, A.Agent_Name AS Agent, C.Cust_Code, C.Cust_Name AS Customer, O.Ord_Num, O.Ord_Amount FROM Agents A INNER JOIN Customer C ON C.Agent_Code = A.Agent_Code INNER JOIN Orders O ON O.Cust_Code = C.Cust_Code AND O.Agent_Code = A.Agent_Code"; + internal static class DDL + { + internal static string Create_Table = @"CREATE TABLE Employee (EmployeeID INT PRIMARY KEY, FirstName NVARCHAR(50), LastName NVARCHAR(50))"; + internal static string Alter_Table = @"ALTER TABLE Employee ADD MiddleName VARCHAR(50)"; + internal static string Comment_Table = @"ALTER TABLE Employee COMMENT = 'This table stores employee records'"; + internal static string Comment_Table_Column = @"ALTER TABLE Employee MODIFY COLUMN MiddleName VARCHAR(50) COMMENT 'This column stores employee middle name'"; + internal static string Truncate_Table = @"TRUNCATE TABLE Employee"; + internal static string Rename_Table = @"ALTER TABLE Employee RENAME TO Employees"; + internal static string Drop_Table = @"DROP TABLE Employees"; + internal static string DDL_Execute_check = @"SELECT COUNT(*) AS Table_Count FROM Information_Schema.Tables WHERE LOWER(Table_Schema) = LOWER('{0}') AND LOWER(Table_Name) = LOWER('{1}')"; + internal static string DDL_Table_Comment_check = @"SELECT Table_Name, Table_Comment AS Table_Comment FROM Information_Schema.Tables WHERE LOWER(Table_Schema) = LOWER('{0}') AND LOWER(Table_Name) = LOWER('{1}')"; + internal static string DDL_Table_Column_Comment_check = @"SELECT Column_Name, Column_Comment AS Table_Column_Comment FROM Information_Schema.Columns WHERE LOWER(Table_Schema) = LOWER('{0}') AND LOWER(Table_Name) = LOWER('{1}')"; + } + internal static class DML + { + internal static string InsertSql = @"INSERT INTO Agents VALUES ('A020', 'John', 'Wick', '0.11', '010-44536178', '')"; + internal static string UpdateSql = @"UPDATE Agents SET Commission = '0.15' WHERE Agent_Code = 'A020'"; + internal static string DeleteSql = @"DELETE FROM Agents WHERE Agent_Code = 'A020'"; + internal static string VerifyDMLExecution = @"SELECT * FROM Agents WHERE Agent_Code = 'A020'"; + internal static string SelectSql = @"SELECT * FROM Agents"; + } + internal static class DCL + { + internal static string CreateUserSql_User_Password = @"CREATE USER '{0}' IDENTIFIED BY '{1}'"; + internal static string GrantSql_Command_Table_User = @"GRANT {0} ON {1} TO '{2}'"; + internal static string RevokeSql_Command_Table_User = @"REVOKE {0} ON {1} FROM '{2}'"; + internal static string VerifyPermission_User = @"SHOW GRANTS FOR '{0}'"; + internal static string RemoveUserSql_User = "DROP USER '{0}'"; + } } } @@ -50,21 +113,51 @@ internal static class OracleQueries { internal static class Smoke { - public static string SelectSql = @"SELECT 'oracle' AS current_database FROM dual"; + internal static string SelectSql = @"SELECT 'oracle' AS current_database FROM dual"; } internal static class TestDB { - public static string SelectSql = @"SELECT * FROM Agents"; - public static string SelectSql_Join = @"SELECT A.Agent_Code, A.Agent_Name, C.Cust_Code, C.Cust_Name, O.Ord_Num, O.Ord_Amount, O.Advance_Amount, O.Ord_Date, O.Ord_Description FROM Agents A INNER JOIN + internal static string SelectSql = @"SELECT * FROM Agents"; + internal static string SelectSql_Join = @"SELECT A.Agent_Code, A.Agent_Name, C.Cust_Code, C.Cust_Name, O.Ord_Num, O.Ord_Amount, O.Advance_Amount, O.Ord_Date, O.Ord_Description FROM Agents A INNER JOIN Customer C ON C.Agent_Code = A.Agent_Code INNER JOIN Orders O ON O.Cust_Code = C.Cust_Code AND O.Agent_Code = A.Agent_Code"; - public static string SelectSql_Alias = @"SELECT A.Agent_Name AS Agent, A.WORKING_AREA AS Agent_Location, C.Cust_Name AS Customer, C.WORKING_AREA AS Customer_Location, O.Agent_Code, O.Cust_Code FROM Agents A INNER JOIN + internal static string SelectSql_Alias = @"SELECT A.Agent_Name AS Agent, A.WORKING_AREA AS Agent_Location, C.Cust_Name AS Customer, C.WORKING_AREA AS Customer_Location, O.Agent_Code, O.Cust_Code FROM Agents A INNER JOIN Customer C ON C.Agent_Code = A.Agent_Code INNER JOIN Orders O ON O.Cust_Code = C.Cust_Code AND O.Agent_Code = A.Agent_Code"; - public static string SelectSql_DataTypes = @"SELECT * FROM DataTypes"; - public static string SelectSql_Strict = @"SELECT A.Agent_Code, A.Agent_Name AS Agent, C.Cust_Code, C.Cust_Name AS Customer, O.Ord_Num, O.Ord_Amount FROM Agents A INNER JOIN + internal static string SelectSql_DataTypes = @"SELECT * FROM DataTypes"; + internal static string SelectSql_Strict = @"SELECT A.Agent_Code, A.Agent_Name AS Agent, C.Cust_Code, C.Cust_Name AS Customer, O.Ord_Num, O.Ord_Amount FROM Agents A INNER JOIN Customer C ON C.Agent_Code = A.Agent_Code INNER JOIN Orders O ON O.Cust_Code = C.Cust_Code AND O.Agent_Code = A.Agent_Code"; + internal static class DDL + { + internal static string Create_Table = @"CREATE TABLE Employee (EmployeeID NUMBER PRIMARY KEY, FirstName NVARCHAR2(50), LastName NVARCHAR2(50))"; + internal static string Alter_Table = @"ALTER TABLE Employee ADD MiddleName VARCHAR(50)"; + internal static string Comment_Table = @"COMMENT ON TABLE Employee IS 'This table stores employee records'"; + internal static string Comment_Table_Column = @"COMMENT ON COLUMN Employee.MiddleName IS 'This column stores employee middle name'"; + internal static string Truncate_Table = @"TRUNCATE TABLE Employee"; + internal static string Rename_Table = @"RENAME Employee TO Employees"; + internal static string Drop_Table = @"DROP TABLE Employees"; + internal static string DDL_Execute_check = @"SELECT COUNT(*) AS Table_Count FROM User_Tables WHERE LOWER(Table_Name) = LOWER('{0}')"; + internal static string DDL_Table_Comment_check = @"SELECT Table_Name, Comments AS Table_Comment FROM All_Tab_Comments WHERE LOWER(Table_Name) = LOWER('{0}')"; + internal static string DDL_Table_Column_Comment_check = @"SELECT Column_Name, Comments AS Table_Column_Comment FROM All_Col_Comments WHERE LOWER(Table_Name) = LOWER('{0}')"; + } + internal static class DML + { + internal static string InsertSql = @"INSERT INTO Agents VALUES ('A020', 'John', 'Wick', '0.11', '010-44536178', '')"; + internal static string UpdateSql = @"UPDATE Agents SET Commission = '0.15' WHERE Agent_Code = 'A020'"; + internal static string DeleteSql = @"DELETE FROM Agents WHERE Agent_Code = 'A020'"; + internal static string VerifyDMLExecution = @"SELECT * FROM Agents WHERE Agent_Code = 'A020'"; + internal static string SelectSql = @"SELECT * FROM Agents"; + } + internal static class DCL + { + internal static string CreateUserSql_User_Password = @"CREATE USER {0} IDENTIFIED BY {1} DEFAULT TABLESPACE USERS TEMPORARY TABLESPACE TEMP"; + internal static string GrantConnectSql_User = @"GRANT CONNECT TO {0}"; + internal static string GrantSql_Command_Table_User = @"GRANT {0} ON {1} TO {2}"; + internal static string RevokeSql_Command_Table_User = @"REVOKE {0} ON {1} FROM {2}"; + internal static string VerifyPermission_User = @"SELECT * FROM DBA_TAB_PRIVS WHERE GRANTEE = UPPER('{0}')"; + internal static string RemoveUserSql_User = @"DROP USER {0} CASCADE"; + } } } @@ -72,21 +165,52 @@ internal static class PostgreSQLQueries { internal static class Smoke { - public static string SelectSql = @"SELECT 'postgres' AS current_database"; + internal static string SelectSql = @"SELECT 'postgres' AS current_database"; } internal static class TestDB { - public static string SelectSql = @"SELECT * FROM Agents"; - public static string SelectSql_Join = @"SELECT A.Agent_Code, A.Agent_Name, C.Cust_Code, C.Cust_Name, O.Ord_Num, O.Ord_Amount, O.Advance_Amount, O.Ord_Date, O.Ord_Description FROM Agents A INNER JOIN + internal static string SelectSql = @"SELECT * FROM Agents"; + internal static string SelectSql_Join = @"SELECT A.Agent_Code, A.Agent_Name, C.Cust_Code, C.Cust_Name, O.Ord_Num, O.Ord_Amount, O.Advance_Amount, O.Ord_Date, O.Ord_Description FROM Agents A INNER JOIN Customer C ON C.Agent_Code = A.Agent_Code INNER JOIN Orders O ON O.Cust_Code = C.Cust_Code AND O.Agent_Code = A.Agent_Code"; - public static string SelectSql_Alias = @"SELECT A.Agent_Name AS Agent, A.WORKING_AREA AS Agent_Location, C.Cust_Name AS Customer, C.WORKING_AREA AS Customer_Location, O.Agent_Code, O.Cust_Code FROM Agents A INNER JOIN + internal static string SelectSql_Alias = @"SELECT A.Agent_Name AS Agent, A.WORKING_AREA AS Agent_Location, C.Cust_Name AS Customer, C.WORKING_AREA AS Customer_Location, O.Agent_Code, O.Cust_Code FROM Agents A INNER JOIN Customer C ON C.Agent_Code = A.Agent_Code INNER JOIN Orders O ON O.Cust_Code = C.Cust_Code AND O.Agent_Code = A.Agent_Code"; - public static string SelectSql_DataTypes = @"SELECT * FROM DataTypes"; - public static string SelectSql_Strict = @"SELECT A.Agent_Code, A.Agent_Name AS Agent, C.Cust_Code, C.Cust_Name AS Customer, O.Ord_Num, O.Ord_Amount FROM Agents A INNER JOIN + internal static string SelectSql_DataTypes = @"SELECT * FROM DataTypes"; + internal static string SelectSql_Strict = @"SELECT A.Agent_Code, A.Agent_Name AS Agent, C.Cust_Code, C.Cust_Name AS Customer, O.Ord_Num, O.Ord_Amount FROM Agents A INNER JOIN Customer C ON C.Agent_Code = A.Agent_Code INNER JOIN Orders O ON O.Cust_Code = C.Cust_Code AND O.Agent_Code = A.Agent_Code"; + internal static class DDL + { + internal static string Create_Table = @"CREATE TABLE Employee (EmployeeID INT PRIMARY KEY, FirstName VARCHAR(50), LastName VARCHAR(50))"; + internal static string Alter_Table = @"ALTER TABLE Employee ADD MiddleName VARCHAR(50)"; + internal static string Comment_Table = @"COMMENT ON TABLE Employee IS 'This table stores employee records'"; + internal static string Comment_Table_Column = @"COMMENT ON COLUMN Employee.MiddleName IS 'This column stores employee middle name'"; + internal static string Truncate_Table = @"TRUNCATE TABLE Employee"; + internal static string Rename_Table = @"ALTER TABLE Employee RENAME TO Employees"; + internal static string Drop_Table = @"DROP TABLE Employees"; + internal static string DDL_Execute_check = @"SELECT COUNT(*) AS Table_Count FROM Information_Schema.Tables WHERE LOWER(Table_Schema) = LOWER('{0}') AND LOWER(Table_Name) = LOWER('{1}')"; + internal static string DDL_Table_Comment_check = @"SELECT Table_Name, Obj_Description(Table_Name::Regclass) AS Table_Comment FROM Information_Schema.Tables WHERE LOWER(Table_Schema) = LOWER('{0}') AND LOWER(Table_Name) = LOWER('{1}')"; + internal static string DDL_Table_Column_Comment_check = @"SELECT Column_Name, Col_Description(Table_Name::Regclass, Ordinal_Position) AS Table_Column_Comment FROM Information_Schema.Columns WHERE LOWER(Table_Schema) = LOWER('{0}') AND LOWER(Table_Name) = LOWER('{1}')"; + } + internal static class DML + { + internal static string InsertSql = @"INSERT INTO Agents VALUES ('A020', 'John', 'Wick', '0.11', '010-44536178', '')"; + internal static string UpdateSql = @"UPDATE Agents SET Commission = '0.15' WHERE Agent_Code = 'A020'"; + internal static string DeleteSql = @"DELETE FROM Agents WHERE Agent_Code = 'A020'"; + internal static string VerifyDMLExecution = @"SELECT * FROM Agents WHERE Agent_Code = 'A020'"; + internal static string SelectSql = @"SELECT * FROM Agents"; + } + internal static class DCL + { + internal static string CreateUserSql_User_Password = @"CREATE USER {0} WITH PASSWORD '{1}'"; + internal static string GrantSql_Command_Table_User = @"GRANT {0} ON {1} TO {2}"; + internal static string RevokeSql_Command_Table_User = @"REVOKE {0} ON {1} FROM {2}"; + internal static string VerifyPermission_User = @"SELECT grantee, privilege_type + FROM information_schema.role_table_grants + WHERE grantee = '{0}'"; + internal static string RemoveUserSql_User = @"DROP USER {0}"; + } } } } diff --git a/QueryDB.Core.Tests/QueryDBExceptionTests.cs b/QueryDB.Core.Tests/QueryDBExceptionTests.cs new file mode 100644 index 0000000..a79dc64 --- /dev/null +++ b/QueryDB.Core.Tests/QueryDBExceptionTests.cs @@ -0,0 +1,49 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using QueryDB.Exceptions; + +namespace QueryDB.Core.Tests +{ + [TestClass] + public class QueryDBExceptionTests : TestBase + { + + #region QueryDBException Tests + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(QUERY_DB_EXCEPTION_TESTS)] + public void ToString_ShouldReturnFormattedString_WhenAdditionalInfoIsPresent() + { + var exception = new QueryDBException("An error occurred", "Critical", "More details"); + + var result = exception.ToString(); + + Assert.AreEqual("Type: Critical, Message: An error occurred, Info: More details", result); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(QUERY_DB_EXCEPTION_TESTS)] + public void ToString_ShouldReturnFormattedString_WithoutAdditionalInfo_WhenAdditionalInfoIsNull() + { + var exception = new QueryDBException("Something went wrong", "Warning", null); + + var result = exception.ToString(); + + Assert.AreEqual("Type: Warning, Message: Something went wrong", result); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(QUERY_DB_EXCEPTION_TESTS)] + public void ToString_ShouldReturnFormattedString_WithoutAdditionalInfo_WhenAdditionalInfoIsEmpty() + { + + var exception = new QueryDBException("Just an update", "Info", ""); + + var result = exception.ToString(); + + Assert.AreEqual("Type: Info, Message: Just an update", result); + } + + #endregion + + } +} diff --git a/QueryDB.Core.Tests/QueryDBTests.cs b/QueryDB.Core.Tests/QueryDBTests.cs new file mode 100644 index 0000000..c847e8e --- /dev/null +++ b/QueryDB.Core.Tests/QueryDBTests.cs @@ -0,0 +1,42 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Collections.Generic; + +namespace QueryDB.Core.Tests +{ + [TestClass] + public class QueryDBTests : TestBase + { + + #region Unknow DB Tests + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(UNKNOW_DB_TESTS)] + public void ExecuteCommand_UnknownDB_ReturnsNegativeOne() + { + string sqlStatement = "DELETE FROM users"; + + var dbContext = new DBContext((DB)999, "some_invalid_connection_string"); + var result = dbContext.ExecuteCommand(sqlStatement); + + Assert.AreEqual(-1, result); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(UNKNOW_DB_TESTS)] + public void ExecuteTransaction_UnknownDB_ReturnsFalse() + { + var sqlStatements = new List + { + "DELETE FROM users" + }; + + var dbContext = new DBContext((DB)999, "some_invalid_connection_string"); + var result = dbContext.ExecuteTransaction(sqlStatements); + + Assert.IsFalse(result); + } + + #endregion + + } +} diff --git a/QueryDB.Core.Tests/TestBase.cs b/QueryDB.Core.Tests/TestBase.cs index 5ef37ad..0163cb9 100644 --- a/QueryDB.Core.Tests/TestBase.cs +++ b/QueryDB.Core.Tests/TestBase.cs @@ -19,6 +19,8 @@ public class TestBase protected const string MYSQL_TESTS = "MYSQL-TESTS"; protected const string ORACLE_TESTS = "ORACLE-TESTS"; protected const string POSTGRESQL_TESTS = "POSTGRESQL-TESTS"; + protected const string QUERY_DB_EXCEPTION_TESTS = "QUERY-DB-EXCEPTION-TESTS"; + protected const string UNKNOW_DB_TESTS = "UNKNOW-DB-TESTS"; [AssemblyInitialize] internal void CheckDockerImages() diff --git a/QueryDB/DBContext.cs b/QueryDB/DBContext.cs index 2f0bb98..2da0106 100644 --- a/QueryDB/DBContext.cs +++ b/QueryDB/DBContext.cs @@ -1,5 +1,9 @@ -using QueryDB.Resources; +using QueryDB.Exceptions; +using QueryDB.Resources; +using System; using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; namespace QueryDB { @@ -113,7 +117,7 @@ public List FetchData(string selectSql, bool upperCaseKeys = fal { var _systemAdapter = new MSSQL.Adapter(); dataList = _systemAdapter.FetchData(selectSql, msSqlDBConnection.SqlConnection, strict); - } + } } else if (Database.Equals(DB.MySQL)) { @@ -142,6 +146,96 @@ public List FetchData(string selectSql, bool upperCaseKeys = fal return dataList; } + /// + /// Executes SQL commands. + /// + /// SQL statement as command. + /// The number of rows affected. + public int ExecuteCommand(string sqlStatement) + { + if (Regex.IsMatch(sqlStatement, "^\\s*SELECT\\s+.*", RegexOptions.IgnoreCase | RegexOptions.Singleline, TimeSpan.FromSeconds(5))) + throw new QueryDBException(QueryDBExceptions.ErrorMessage.UnsupportedSelectExecuteCommand, + QueryDBExceptions.ErrorType.UnsupportedCommand, QueryDBExceptions.AdditionalInfo.UnsupportedSelectExecuteCommand); + if (Database.Equals(DB.MSSQL)) + { + using (var msSqlDBConnection = GetSqlServerConnection()) + { + var _systemAdapter = new MSSQL.Adapter(); + return _systemAdapter.ExecuteCommand(sqlStatement, msSqlDBConnection.SqlConnection); + } + } + else if (Database.Equals(DB.MySQL)) + { + using (var mySqlDBConnection = GetMySqlConnection()) + { + var _systemAdapter = new MySQL.Adapter(); + return _systemAdapter.ExecuteCommand(sqlStatement, mySqlDBConnection.MySqlConnection); + } + } + else if (Database.Equals(DB.Oracle)) + { + using (var oracleDBConnection = GetOracleConnection()) + { + var _systemAdapter = new Oracle.Adapter(); + return _systemAdapter.ExecuteCommand(sqlStatement, oracleDBConnection.OracleConnection); + } + } + else if (Database.Equals(DB.PostgreSQL)) + { + using (var postgreSqlDBConnection = GetPostgreSqlConnection()) + { + var _systemAdapter = new PostgreSQL.Adapter(); + return _systemAdapter.ExecuteCommand(sqlStatement, postgreSqlDBConnection.PostgreSQLConnection); + } + } + return -1; + } + + /// + /// Executes multiple SQL statements within a transaction, ensuring that all statements are executed together. + /// + /// A list of SQL statements to execute. + /// + /// Returns true if all statements are executed successfully and the transaction is committed; + /// false if any statement fails and the transaction is rolled back. + /// + public bool ExecuteTransaction(List sqlStatements) + { + var selectExists = sqlStatements.Any(sqlStatement => Regex.IsMatch(sqlStatement, "^\\s*SELECT\\s+.*", RegexOptions.IgnoreCase | RegexOptions.Singleline, TimeSpan.FromSeconds(5))); + if (selectExists) + throw new QueryDBException(QueryDBExceptions.ErrorMessage.UnsupportedSelectExecuteTransaction, + QueryDBExceptions.ErrorType.UnsupportedCommand, QueryDBExceptions.AdditionalInfo.UnsupportedSelectExecuteTransaction); + if (Database.Equals(DB.MSSQL)) + { + using (var msSqlDBConnection = GetSqlServerConnection()) + { + return MSSQL.Adapter.ExecuteTransaction(sqlStatements, msSqlDBConnection.SqlConnection); + } + } + else if (Database.Equals(DB.MySQL)) + { + using (var mySqlDBConnection = GetMySqlConnection()) + { + return MySQL.Adapter.ExecuteTransaction(sqlStatements, mySqlDBConnection.MySqlConnection); + } + } + else if (Database.Equals(DB.Oracle)) + { + using (var oracleDBConnection = GetOracleConnection()) + { + return Oracle.Adapter.ExecuteTransaction(sqlStatements, oracleDBConnection.OracleConnection); + } + } + else if (Database.Equals(DB.PostgreSQL)) + { + using (var postgreSqlDBConnection = GetPostgreSqlConnection()) + { + return PostgreSQL.Adapter.ExecuteTransaction(sqlStatements, postgreSqlDBConnection.PostgreSQLConnection); + } + } + return false; + } + /// /// Gets 'SQL Server' connection. /// diff --git a/QueryDB/Exceptions/QueryDBException.cs b/QueryDB/Exceptions/QueryDBException.cs new file mode 100644 index 0000000..08a0d96 --- /dev/null +++ b/QueryDB/Exceptions/QueryDBException.cs @@ -0,0 +1,91 @@ +using System; + +namespace QueryDB.Exceptions +{ + /// + /// Represents exceptions specific to QueryDB operations. + /// + public class QueryDBException : Exception + { + /// + /// Gets the type of the error that occurred. + /// + public string ErrorType { get; } + + /// + /// Gets additional information about the error, if available. + /// + public string AdditionalInfo { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The error message that describes the error. + /// The type of the error. + /// Optional additional information about the error. + public QueryDBException(string message, string errorType, string additionalInfo = null) : base(message) + { + ErrorType = errorType; + AdditionalInfo = additionalInfo; + } + + /// + /// Returns a string representation of the exception, including error type and additional information. + /// + /// A string containing the error type, message, and additional information. + public override string ToString() + { + string info = string.IsNullOrEmpty(AdditionalInfo) ? "" : $", Info: {AdditionalInfo}"; + return $"Type: {ErrorType}, Message: {Message}{info}"; + } + } + + /// + /// Provides predefined error types, messages, and additional information for QueryDB exceptions. + /// + internal static class QueryDBExceptions + { + /// + /// Defines standard error types for QueryDB exceptions. + /// + internal static class ErrorType + { + /// + /// Error type indicating an unsupported command. + /// + internal static readonly string UnsupportedCommand = "UnsupportedCommand"; + } + + /// + /// Defines standard error messages for QueryDB exceptions. + /// + internal static class ErrorMessage + { + /// + /// Error message indicating that SELECT queries are not supported in 'ExecuteCommand'. + /// + internal static readonly string UnsupportedSelectExecuteCommand = "SELECT queries are not supported here."; + + /// + /// Error message indicating that SELECT queries are not supported in 'ExecuteTransaction'. + /// + internal static readonly string UnsupportedSelectExecuteTransaction = "SELECT queries are not supported here."; + } + + /// + /// Defines additional information related to QueryDB exceptions. + /// + internal static class AdditionalInfo + { + /// + /// Additional information about unsupported SELECT queries in 'ExecuteCommand'. + /// + internal static readonly string UnsupportedSelectExecuteCommand = "'ExecuteCommand' doesn't support SELECT queries."; + + /// + /// Additional information about unsupported SELECT queries in 'ExecuteTransaction'. + /// + internal static readonly string UnsupportedSelectExecuteTransaction = "'ExecuteTransaction' doesn't support SELECT queries."; + } + } +} diff --git a/QueryDB/IDBContext.cs b/QueryDB/IDBContext.cs index 218e3f3..1186eb2 100644 --- a/QueryDB/IDBContext.cs +++ b/QueryDB/IDBContext.cs @@ -24,5 +24,22 @@ interface IDBContext /// Enables fetch data only for object properties existing in database query result. Default - 'false'. /// List of data rows mapped into object entity into a list for multiple rows of data. List FetchData(string selectSql, bool strict = false) where T : new(); + + /// + /// Executes SQL commands. + /// + /// SQL statement as command. + /// The number of rows affected. + int ExecuteCommand(string sqlStatement); + + /// + /// Executes multiple SQL statements within a transaction, ensuring that all statements are executed together. + /// + /// A list of SQL statements to execute. + /// + /// Returns true if all statements are executed successfully and the transaction is committed; + /// false if any statement fails and the transaction is rolled back. + /// + bool ExecuteTransaction(List sqlStatements); } } diff --git a/QueryDB/MSSQL/Adapter.cs b/QueryDB/MSSQL/Adapter.cs index d69b13d..fe4de1d 100644 --- a/QueryDB/MSSQL/Adapter.cs +++ b/QueryDB/MSSQL/Adapter.cs @@ -27,6 +27,46 @@ internal SqlDataReader GetSqlReader(string cmdText, SqlConnection connection, Co } } + /// + /// Creates and returns a new with the specified command text, + /// connection, and command type. Opens the connection before creating the command. + /// + /// The SQL command text to execute. + /// The to use. + /// The type of the command (e.g., Text, StoredProcedure). + /// A configured instance. + internal SqlCommand GetSqlCommand(string cmdText, SqlConnection connection, CommandType commandType) + { + connection.Open(); + var sqlCommand = new SqlCommand(cmdText, connection) { CommandType = commandType }; + return sqlCommand; + } + + /// + /// Creates and returns a new SQL command associated with the specified connection and transaction. + /// + /// The SQL command text to execute. + /// The SQL database connection. + /// The SQL transaction within which the command should be executed. + /// A new instance configured with the provided connection and transaction. + internal static SqlCommand GetSqlCommand(string cmdText, SqlConnection connection, SqlTransaction transaction) + { + var sqlCommand = new SqlCommand(cmdText, connection, transaction); + return sqlCommand; + } + + /// + /// Initiates and returns a new SQL transaction for the specified database connection. + /// + /// The SQL database connection. + /// A new associated with the provided connection. + internal static SqlTransaction GetSqlTransaction(SqlConnection connection) + { + connection.Open(); + var sqlTransaction = connection.BeginTransaction(); + return sqlTransaction; + } + /// /// Retrieves records for 'Select' queries from the database. /// Converts column names to keys holding values, with multiple database rows returned into a list. @@ -84,6 +124,57 @@ internal List FetchData(string selectSql, SqlConnection connecti } return dataList; } + + /// + /// Executes SQL commands. + /// + /// SQL statement as command. + /// 'Sql' Connection. + /// The number of rows affected. + internal int ExecuteCommand(string sqlStatement, SqlConnection connection) + { + using(var sqlCommand = GetSqlCommand(sqlStatement, connection, CommandType.Text)) + { + return sqlCommand.ExecuteNonQuery(); + } + } + + /// + /// Executes multiple SQL statements within a transaction to ensure atomicity. + /// + /// A list of SQL statements to execute. + /// The SQL database connection. + /// + /// Returns true if the transaction is committed successfully; + /// otherwise, false if an error occurs and the transaction is rolled back. + /// + /// + /// Logs and handles exceptions if any SQL command execution fails. + /// + internal static bool ExecuteTransaction(List sqlStatements, SqlConnection connection) + { + using (SqlTransaction transaction = GetSqlTransaction(connection)) + { + try + { + foreach (var sqlStatement in sqlStatements) + { + using (var sqlCommand = GetSqlCommand(sqlStatement, connection, transaction)) + { + sqlCommand.ExecuteNonQuery(); + } + } + transaction.Commit(); + return true; + } + catch (Exception ex) + { + transaction.Rollback(); + Console.WriteLine($"Transaction rolled back due to error: {ex.Message}"); + return false; + } + } + } } } diff --git a/QueryDB/MySQL/Adapter.cs b/QueryDB/MySQL/Adapter.cs index 8f76660..8721533 100644 --- a/QueryDB/MySQL/Adapter.cs +++ b/QueryDB/MySQL/Adapter.cs @@ -27,6 +27,46 @@ internal MySqlDataReader GetMySqlReader(string cmdText, MySqlConnection connecti } } + /// + /// Creates and returns a new with the specified command text, + /// connection, and command type. Opens the connection before creating the command. + /// + /// The SQL command text to execute. + /// The to use. + /// The type of the command (e.g., Text, StoredProcedure). + /// A configured instance. + internal MySqlCommand GetMySqlCommand(string cmdText, MySqlConnection connection, CommandType commandType) + { + connection.Open(); + var sqlCommand = new MySqlCommand(cmdText, connection) { CommandType = commandType }; + return sqlCommand; + } + + /// + /// Creates and returns a new MySQL command associated with the specified connection and transaction. + /// + /// The SQL command text to execute. + /// The MySQL database connection. + /// The MySQL transaction within which the command should be executed. + /// A new instance configured with the provided connection and transaction. + internal static MySqlCommand GetMySqlCommand(string cmdText, MySqlConnection connection, MySqlTransaction transaction) + { + var sqlCommand = new MySqlCommand(cmdText, connection, transaction); + return sqlCommand; + } + + /// + /// Initiates and returns a new MySQL transaction for the given database connection. + /// + /// The MySQL database connection. + /// A new associated with the provided connection. + internal static MySqlTransaction GetMySqlTransaction(MySqlConnection connection) + { + connection.Open(); + var mySqlTransaction = connection.BeginTransaction(); + return mySqlTransaction; + } + /// /// Retrieves records for 'Select' queries from the database. /// Converts column names to keys holding values, with multiple database rows returned into a list. @@ -84,5 +124,56 @@ internal List FetchData(string selectSql, MySqlConnection connec } return dataList; } + + /// + /// Executes SQL commands. + /// + /// SQL statement as command. + /// 'MySQL' Connection. + /// The number of rows affected. + internal int ExecuteCommand(string sqlStatement, MySqlConnection connection) + { + using (var sqlCommand = GetMySqlCommand(sqlStatement, connection, CommandType.Text)) + { + return sqlCommand.ExecuteNonQuery(); + } + } + + /// + /// Executes multiple SQL statements within a MySQL transaction to ensure atomicity. + /// + /// A list of SQL statements to execute. + /// The MySQL database connection. + /// + /// Returns true if the transaction is committed successfully; + /// otherwise, false if an error occurs and the transaction is rolled back. + /// + /// + /// Logs and handles exceptions if any SQL command execution fails. + /// + internal static bool ExecuteTransaction(List sqlStatements, MySqlConnection connection) + { + using (MySqlTransaction transaction = GetMySqlTransaction(connection)) + { + try + { + foreach (var sqlStatement in sqlStatements) + { + using (var sqlCommand = GetMySqlCommand(sqlStatement, connection, transaction)) + { + sqlCommand.ExecuteNonQuery(); + } + } + transaction.Commit(); + return true; + } + catch (Exception ex) + { + transaction.Rollback(); + Console.WriteLine($"Transaction rolled back due to error: {ex.Message}"); + return false; + } + } + } } } \ No newline at end of file diff --git a/QueryDB/Oracle/Adapter.cs b/QueryDB/Oracle/Adapter.cs index ff6bcb6..1890fd5 100644 --- a/QueryDB/Oracle/Adapter.cs +++ b/QueryDB/Oracle/Adapter.cs @@ -27,6 +27,45 @@ internal OracleDataReader GetOracleReader(string cmdText, OracleConnection conne } } + /// + /// Creates and returns a new with the specified command text, + /// connection, and command type. Opens the connection before creating the command. + /// + /// The SQL command text to execute. + /// The to use. + /// The type of the command (e.g., Text, StoredProcedure). + /// A configured instance. + internal OracleCommand GetOracleCommand(string cmdText, OracleConnection connection, CommandType commandType) + { + connection.Open(); + var sqlCommand = new OracleCommand(cmdText, connection) { CommandType = commandType }; + return sqlCommand; + } + + /// + /// Creates and returns a new Oracle command associated with the specified connection. + /// + /// The SQL command text to execute. + /// The Oracle database connection. + /// A new instance configured with the provided connection. + internal static OracleCommand GetOracleCommand(string cmdText, OracleConnection connection) + { + var sqlCommand = new OracleCommand(cmdText, connection); + return sqlCommand; + } + + /// + /// Initiates and returns a new Oracle transaction for the specified database connection. + /// + /// The Oracle database connection. + /// A new associated with the provided connection. + internal static OracleTransaction GetOracleTransaction(OracleConnection connection) + { + connection.Open(); + var oracleTransaction = connection.BeginTransaction(); + return oracleTransaction; + } + /// /// Retrieves records for 'Select' queries from the database. /// Converts column names to keys holding values, with multiple database rows returned into a list. @@ -91,5 +130,57 @@ internal List FetchData(string selectSql, OracleConnection conne } return dataList; } + + /// + /// Executes SQL commands. + /// + /// SQL statement as command. + /// 'Oracle' Connection. + /// The number of rows affected. + internal int ExecuteCommand(string sqlStatement, OracleConnection connection) + { + using (var sqlCommand = GetOracleCommand(sqlStatement, connection, CommandType.Text)) + { + return sqlCommand.ExecuteNonQuery(); + } + } + + /// + /// Executes multiple SQL statements within an Oracle transaction to ensure atomicity. + /// + /// A list of SQL statements to execute. + /// The Oracle database connection. + /// + /// Returns true if the transaction is committed successfully; + /// otherwise, false if an error occurs and the transaction is rolled back. + /// + /// + /// Logs and handles exceptions if any SQL command execution fails. + /// + internal static bool ExecuteTransaction(List sqlStatements, OracleConnection connection) + { + using (OracleTransaction transaction = GetOracleTransaction(connection)) + { + try + { + foreach (var sqlStatement in sqlStatements) + { + using (var sqlCommand = GetOracleCommand(sqlStatement, connection)) + { + sqlCommand.Transaction = transaction; + sqlCommand.ExecuteNonQuery(); + } + } + transaction.Commit(); + return true; + } + catch (Exception ex) + { + transaction.Rollback(); + Console.WriteLine($"Transaction rolled back due to error: {ex.Message}"); + return false; + } + } + } } } \ No newline at end of file diff --git a/QueryDB/PostgreSQL/Adapter.cs b/QueryDB/PostgreSQL/Adapter.cs index a2e48d9..3077b44 100644 --- a/QueryDB/PostgreSQL/Adapter.cs +++ b/QueryDB/PostgreSQL/Adapter.cs @@ -27,6 +27,46 @@ internal NpgsqlDataReader GetPostgreSqlReader(string cmdText, NpgsqlConnection c } } + /// + /// Creates and returns a new with the specified command text, + /// connection, and command type. Opens the connection before creating the command. + /// + /// The SQL command text to execute. + /// The to use. + /// The type of the command (e.g., Text, StoredProcedure). + /// A configured instance. + internal NpgsqlCommand GetPostgreSqlCommand(string cmdText, NpgsqlConnection connection, CommandType commandType) + { + connection.Open(); + var sqlCommand = new NpgsqlCommand(cmdText, connection) { CommandType = commandType }; + return sqlCommand; + } + + /// + /// Creates and returns a new PostgreSQL command associated with the specified connection and transaction. + /// + /// The SQL command text to execute. + /// The PostgreSQL database connection. + /// The PostgreSQL transaction within which the command should be executed. + /// A new instance configured with the provided connection and transaction. + internal static NpgsqlCommand GetPostgreSqlCommand(string cmdText, NpgsqlConnection connection, NpgsqlTransaction transaction) + { + var sqlCommand = new NpgsqlCommand(cmdText, connection, transaction); + return sqlCommand; + } + + /// + /// Initiates and returns a new PostgreSQL transaction for the specified database connection. + /// + /// The PostgreSQL database connection. + /// A new associated with the provided connection. + internal static NpgsqlTransaction GetPostgreSqlTransaction(NpgsqlConnection connection) + { + connection.Open(); + var npgsqlTransaction = connection.BeginTransaction(); + return npgsqlTransaction; + } + /// /// Retrieves records for 'Select' queries from the database. /// Converts column names to keys holding values, with multiple database rows returned into a list. @@ -84,5 +124,56 @@ internal List FetchData(string selectSql, NpgsqlConnection conne } return dataList; } + + /// + /// Executes SQL commands. + /// + /// SQL statement as command. + /// 'PostgreSQL' Connection. + /// The number of rows affected + internal int ExecuteCommand(string sqlStatement, NpgsqlConnection connection) + { + using (var sqlCommand = GetPostgreSqlCommand(sqlStatement, connection, CommandType.Text)) + { + return sqlCommand.ExecuteNonQuery(); + } + } + + /// + /// Executes multiple SQL statements within a PostgreSQL transaction to ensure atomicity. + /// + /// A list of SQL statements to execute. + /// The PostgreSQL database connection. + /// + /// Returns true if the transaction is committed successfully; + /// otherwise, false if an error occurs and the transaction is rolled back. + /// + /// + /// Logs and handles exceptions if any SQL command execution fails. + /// + internal static bool ExecuteTransaction(List sqlStatements, NpgsqlConnection connection) + { + using (NpgsqlTransaction transaction = GetPostgreSqlTransaction(connection)) + { + try + { + foreach (var sqlStatement in sqlStatements) + { + using (var sqlCommand = GetPostgreSqlCommand(sqlStatement, connection, transaction)) + { + sqlCommand.ExecuteNonQuery(); + } + } + transaction.Commit(); + return true; + } + catch (Exception ex) + { + transaction.Rollback(); + Console.WriteLine($"Transaction rolled back due to error: {ex.Message}"); + return false; + } + } + } } } diff --git a/codecov.yml b/codecov.yml index 9476bd9..49cf3ca 100644 --- a/codecov.yml +++ b/codecov.yml @@ -2,8 +2,12 @@ coverage: range: "80..100" round: down precision: 2 + status: project: default: target: 95% - threshold: 5% \ No newline at end of file + threshold: 5% + branches: + - main + - dev \ No newline at end of file