Patcher code from the times while @SMA

main
Inga 🏳‍🌈 13 years ago
parent 89978e1fef
commit 546926900c
  1. 60
      FLocal.sln
  2. 390
      Patcher/ConsoleProcessor.cs
  3. 36
      Patcher/Context.cs
  4. 121
      Patcher/DB/AbstractConstraint.cs
  5. 30
      Patcher/DB/CheckConstraint.cs
  6. 21
      Patcher/DB/ColumnDescription.cs
  7. 50
      Patcher/DB/ColumnOptions.cs
  8. 21
      Patcher/DB/ColumnReference.cs
  9. 44
      Patcher/DB/ForeignKeyConstraint.cs
  10. 45
      Patcher/DB/IDBTraits.cs
  11. 841
      Patcher/DB/OracleDBTraits.cs
  12. 29
      Patcher/DB/OracleFakeTransactionalDBTraits.cs
  13. 320
      Patcher/DB/SQLQueryManager.cs
  14. 21
      Patcher/DB/StoredProcedureBody.cs
  15. 51
      Patcher/DB/StoredProcedureReference.cs
  16. 26
      Patcher/DB/TableDescription.cs
  17. 273
      Patcher/DB/Transaction.cs
  18. 23
      Patcher/DB/TransactionFactory.cs
  19. 30
      Patcher/DB/UniqueConstraint.cs
  20. 21
      Patcher/Data/Command/AbstractColumnCommand.cs
  21. 28
      Patcher/Data/Command/AbstractColumnCommandWithOptions.cs
  22. 59
      Patcher/Data/Command/AbstractCommand.cs
  23. 21
      Patcher/Data/Command/AbstractConstraintCommand.cs
  24. 25
      Patcher/Data/Command/AbstractPersistentCommand.cs
  25. 36
      Patcher/Data/Command/AbstractStoredProcedureCommand.cs
  26. 21
      Patcher/Data/Command/AbstractViewCommand.cs
  27. 62
      Patcher/Data/Command/ChangeStoredProcedureCommand.cs
  28. 39
      Patcher/Data/Command/CreateColumnCommand.cs
  29. 39
      Patcher/Data/Command/CreateConstraintCommand.cs
  30. 45
      Patcher/Data/Command/CreateStoredProcedureCommand.cs
  31. 43
      Patcher/Data/Command/CreateTableCommand.cs
  32. 42
      Patcher/Data/Command/CreateViewCommand.cs
  33. 54
      Patcher/Data/Command/ModifyColumnCommand.cs
  34. 66
      Patcher/Data/Command/RemoveColumnCommand.cs
  35. 39
      Patcher/Data/Command/RemoveConstraintCommand.cs
  36. 50
      Patcher/Data/Command/RemoveStoredProcedureCommand.cs
  37. 43
      Patcher/Data/Command/RemoveViewCommand.cs
  38. 44
      Patcher/Data/Command/SqlCommand.cs
  39. 152
      Patcher/Data/Patch/AbstractPatch.cs
  40. 68
      Patcher/Data/Patch/AtomicPatch.cs
  41. 60
      Patcher/Data/Patch/PatchId.cs
  42. 36
      Patcher/Data/Patch/PersistentPatch.cs
  43. 112
      Patcher/Data/XMLParser.cs
  44. 17
      Patcher/Exceptions/FormattableException.cs
  45. 17
      Patcher/Exceptions/ResourceNotFoundException.cs
  46. 17
      Patcher/Exceptions/XmlResourceNotFoundException.cs
  47. 33
      Patcher/IConfig.cs
  48. 161
      Patcher/Patcher.csproj
  49. 36
      Patcher/Properties/AssemblyInfo.cs
  50. 287
      Patcher/Resources/IPatch.xsd
  51. 35
      Patcher/Resources/Patch_00000_changeprocedure.xml
  52. 18
      Patcher/Resources/Patch_00000_createcheck.xml
  53. 18
      Patcher/Resources/Patch_00000_createcolumn.xml
  54. 21
      Patcher/Resources/Patch_00000_createforeignkey.xml
  55. 39
      Patcher/Resources/Patch_00000_createprocedure.xml
  56. 35
      Patcher/Resources/Patch_00000_createtable.xml
  57. 19
      Patcher/Resources/Patch_00000_createunique.xml
  58. 18
      Patcher/Resources/Patch_00000_createview.xml
  59. 18
      Patcher/Resources/Patch_00000_modifycolumn.xml
  60. 18
      Patcher/Resources/Patch_00000_removecheck.xml
  61. 15
      Patcher/Resources/Patch_00000_removecolumn.xml
  62. 21
      Patcher/Resources/Patch_00000_removeforeignkey.xml
  63. 32
      Patcher/Resources/Patch_00000_removeprocedure.xml
  64. 19
      Patcher/Resources/Patch_00000_removeunique.xml
  65. 17
      Patcher/Resources/Patch_00000_removeview.xml
  66. 51
      Patcher/Resources/ResourcesManager.cs
  67. 971
      Patcher/ThirdParty/NConsoler.cs
  68. 34
      Patcher/Util/Cache.cs
  69. 26
      Patcher/Util/CultureReplacementWrapper.cs
  70. 123
      Patcher/Util/Util.cs

@ -21,80 +21,128 @@ Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "BBCode", "ThirdParty\PJonDe
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TexCompiler", "TexCompiler\TexCompiler.csproj", "{7D2A38F8-C42D-4F9F-AF64-1DDD5842C7EC}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TexCompiler", "TexCompiler\TexCompiler.csproj", "{7D2A38F8-C42D-4F9F-AF64-1DDD5842C7EC}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Patcher", "Patcher\Patcher.csproj", "{78F407CF-416D-4D1F-971C-9A51FEA69B3A}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|x86 = Debug|x86 Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
Release|Mixed Platforms = Release|Mixed Platforms
Release|x86 = Release|x86 Release|x86 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9A902732-E7F1-4F41-869B-316AF2254B36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9A902732-E7F1-4F41-869B-316AF2254B36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9A902732-E7F1-4F41-869B-316AF2254B36}.Debug|Any CPU.Build.0 = Debug|Any CPU {9A902732-E7F1-4F41-869B-316AF2254B36}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9A902732-E7F1-4F41-869B-316AF2254B36}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{9A902732-E7F1-4F41-869B-316AF2254B36}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{9A902732-E7F1-4F41-869B-316AF2254B36}.Debug|x86.ActiveCfg = Debug|Any CPU {9A902732-E7F1-4F41-869B-316AF2254B36}.Debug|x86.ActiveCfg = Debug|Any CPU
{9A902732-E7F1-4F41-869B-316AF2254B36}.Release|Any CPU.ActiveCfg = Release|Any CPU {9A902732-E7F1-4F41-869B-316AF2254B36}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9A902732-E7F1-4F41-869B-316AF2254B36}.Release|Any CPU.Build.0 = Release|Any CPU {9A902732-E7F1-4F41-869B-316AF2254B36}.Release|Any CPU.Build.0 = Release|Any CPU
{9A902732-E7F1-4F41-869B-316AF2254B36}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{9A902732-E7F1-4F41-869B-316AF2254B36}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{9A902732-E7F1-4F41-869B-316AF2254B36}.Release|x86.ActiveCfg = Release|Any CPU {9A902732-E7F1-4F41-869B-316AF2254B36}.Release|x86.ActiveCfg = Release|Any CPU
{6F532626-E9F8-498E-9683-1538E7CD62CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6F532626-E9F8-498E-9683-1538E7CD62CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6F532626-E9F8-498E-9683-1538E7CD62CB}.Debug|Any CPU.Build.0 = Debug|Any CPU {6F532626-E9F8-498E-9683-1538E7CD62CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6F532626-E9F8-498E-9683-1538E7CD62CB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{6F532626-E9F8-498E-9683-1538E7CD62CB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{6F532626-E9F8-498E-9683-1538E7CD62CB}.Debug|x86.ActiveCfg = Debug|Any CPU {6F532626-E9F8-498E-9683-1538E7CD62CB}.Debug|x86.ActiveCfg = Debug|Any CPU
{6F532626-E9F8-498E-9683-1538E7CD62CB}.Release|Any CPU.ActiveCfg = Release|Any CPU {6F532626-E9F8-498E-9683-1538E7CD62CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6F532626-E9F8-498E-9683-1538E7CD62CB}.Release|Any CPU.Build.0 = Release|Any CPU {6F532626-E9F8-498E-9683-1538E7CD62CB}.Release|Any CPU.Build.0 = Release|Any CPU
{6F532626-E9F8-498E-9683-1538E7CD62CB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{6F532626-E9F8-498E-9683-1538E7CD62CB}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{6F532626-E9F8-498E-9683-1538E7CD62CB}.Release|x86.ActiveCfg = Release|Any CPU {6F532626-E9F8-498E-9683-1538E7CD62CB}.Release|x86.ActiveCfg = Release|Any CPU
{CE888859-9E46-41F7-91CE-8EC106F3A625}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CE888859-9E46-41F7-91CE-8EC106F3A625}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CE888859-9E46-41F7-91CE-8EC106F3A625}.Debug|Any CPU.Build.0 = Debug|Any CPU {CE888859-9E46-41F7-91CE-8EC106F3A625}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CE888859-9E46-41F7-91CE-8EC106F3A625}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{CE888859-9E46-41F7-91CE-8EC106F3A625}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{CE888859-9E46-41F7-91CE-8EC106F3A625}.Debug|x86.ActiveCfg = Debug|Any CPU {CE888859-9E46-41F7-91CE-8EC106F3A625}.Debug|x86.ActiveCfg = Debug|Any CPU
{CE888859-9E46-41F7-91CE-8EC106F3A625}.Release|Any CPU.ActiveCfg = Release|Any CPU {CE888859-9E46-41F7-91CE-8EC106F3A625}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CE888859-9E46-41F7-91CE-8EC106F3A625}.Release|Any CPU.Build.0 = Release|Any CPU {CE888859-9E46-41F7-91CE-8EC106F3A625}.Release|Any CPU.Build.0 = Release|Any CPU
{CE888859-9E46-41F7-91CE-8EC106F3A625}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{CE888859-9E46-41F7-91CE-8EC106F3A625}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{CE888859-9E46-41F7-91CE-8EC106F3A625}.Release|x86.ActiveCfg = Release|Any CPU {CE888859-9E46-41F7-91CE-8EC106F3A625}.Release|x86.ActiveCfg = Release|Any CPU
{E38DE5B1-F9C2-43BA-A5DF-0743ABD4DFC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E38DE5B1-F9C2-43BA-A5DF-0743ABD4DFC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E38DE5B1-F9C2-43BA-A5DF-0743ABD4DFC7}.Debug|Any CPU.Build.0 = Debug|Any CPU {E38DE5B1-F9C2-43BA-A5DF-0743ABD4DFC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E38DE5B1-F9C2-43BA-A5DF-0743ABD4DFC7}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{E38DE5B1-F9C2-43BA-A5DF-0743ABD4DFC7}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{E38DE5B1-F9C2-43BA-A5DF-0743ABD4DFC7}.Debug|x86.ActiveCfg = Debug|Any CPU {E38DE5B1-F9C2-43BA-A5DF-0743ABD4DFC7}.Debug|x86.ActiveCfg = Debug|Any CPU
{E38DE5B1-F9C2-43BA-A5DF-0743ABD4DFC7}.Release|Any CPU.ActiveCfg = Release|Any CPU {E38DE5B1-F9C2-43BA-A5DF-0743ABD4DFC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E38DE5B1-F9C2-43BA-A5DF-0743ABD4DFC7}.Release|Any CPU.Build.0 = Release|Any CPU {E38DE5B1-F9C2-43BA-A5DF-0743ABD4DFC7}.Release|Any CPU.Build.0 = Release|Any CPU
{E38DE5B1-F9C2-43BA-A5DF-0743ABD4DFC7}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{E38DE5B1-F9C2-43BA-A5DF-0743ABD4DFC7}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{E38DE5B1-F9C2-43BA-A5DF-0743ABD4DFC7}.Release|x86.ActiveCfg = Release|Any CPU {E38DE5B1-F9C2-43BA-A5DF-0743ABD4DFC7}.Release|x86.ActiveCfg = Release|Any CPU
{D8D6EFEC-76D7-4870-BD71-C90FA6D7368A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D8D6EFEC-76D7-4870-BD71-C90FA6D7368A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D8D6EFEC-76D7-4870-BD71-C90FA6D7368A}.Debug|Any CPU.Build.0 = Debug|Any CPU {D8D6EFEC-76D7-4870-BD71-C90FA6D7368A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8D6EFEC-76D7-4870-BD71-C90FA6D7368A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{D8D6EFEC-76D7-4870-BD71-C90FA6D7368A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{D8D6EFEC-76D7-4870-BD71-C90FA6D7368A}.Debug|x86.ActiveCfg = Debug|Any CPU {D8D6EFEC-76D7-4870-BD71-C90FA6D7368A}.Debug|x86.ActiveCfg = Debug|Any CPU
{D8D6EFEC-76D7-4870-BD71-C90FA6D7368A}.Release|Any CPU.ActiveCfg = Release|Any CPU {D8D6EFEC-76D7-4870-BD71-C90FA6D7368A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D8D6EFEC-76D7-4870-BD71-C90FA6D7368A}.Release|Any CPU.Build.0 = Release|Any CPU {D8D6EFEC-76D7-4870-BD71-C90FA6D7368A}.Release|Any CPU.Build.0 = Release|Any CPU
{D8D6EFEC-76D7-4870-BD71-C90FA6D7368A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{D8D6EFEC-76D7-4870-BD71-C90FA6D7368A}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{D8D6EFEC-76D7-4870-BD71-C90FA6D7368A}.Release|x86.ActiveCfg = Release|Any CPU {D8D6EFEC-76D7-4870-BD71-C90FA6D7368A}.Release|x86.ActiveCfg = Release|Any CPU
{BDC42785-41F7-4C32-880E-A6C43A448054}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BDC42785-41F7-4C32-880E-A6C43A448054}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BDC42785-41F7-4C32-880E-A6C43A448054}.Debug|x86.ActiveCfg = Debug|Any CPU
{BDC42785-41F7-4C32-880E-A6C43A448054}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BDC42785-41F7-4C32-880E-A6C43A448054}.Release|Any CPU.Build.0 = Release|Any CPU
{BDC42785-41F7-4C32-880E-A6C43A448054}.Release|x86.ActiveCfg = Release|Any CPU
{E2ECF86C-C0BA-4782-A485-E267BB213BB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E2ECF86C-C0BA-4782-A485-E267BB213BB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E2ECF86C-C0BA-4782-A485-E267BB213BB2}.Debug|Any CPU.Build.0 = Debug|Any CPU {E2ECF86C-C0BA-4782-A485-E267BB213BB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E2ECF86C-C0BA-4782-A485-E267BB213BB2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{E2ECF86C-C0BA-4782-A485-E267BB213BB2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{E2ECF86C-C0BA-4782-A485-E267BB213BB2}.Debug|x86.ActiveCfg = Debug|Any CPU {E2ECF86C-C0BA-4782-A485-E267BB213BB2}.Debug|x86.ActiveCfg = Debug|Any CPU
{E2ECF86C-C0BA-4782-A485-E267BB213BB2}.Release|Any CPU.ActiveCfg = Release|Any CPU {E2ECF86C-C0BA-4782-A485-E267BB213BB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E2ECF86C-C0BA-4782-A485-E267BB213BB2}.Release|Any CPU.Build.0 = Release|Any CPU {E2ECF86C-C0BA-4782-A485-E267BB213BB2}.Release|Any CPU.Build.0 = Release|Any CPU
{E2ECF86C-C0BA-4782-A485-E267BB213BB2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{E2ECF86C-C0BA-4782-A485-E267BB213BB2}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{E2ECF86C-C0BA-4782-A485-E267BB213BB2}.Release|x86.ActiveCfg = Release|Any CPU {E2ECF86C-C0BA-4782-A485-E267BB213BB2}.Release|x86.ActiveCfg = Release|Any CPU
{208B84AF-A5DD-4C20-83D5-160EA243BA43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {208B84AF-A5DD-4C20-83D5-160EA243BA43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{208B84AF-A5DD-4C20-83D5-160EA243BA43}.Debug|Any CPU.Build.0 = Debug|Any CPU {208B84AF-A5DD-4C20-83D5-160EA243BA43}.Debug|Any CPU.Build.0 = Debug|Any CPU
{208B84AF-A5DD-4C20-83D5-160EA243BA43}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{208B84AF-A5DD-4C20-83D5-160EA243BA43}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{208B84AF-A5DD-4C20-83D5-160EA243BA43}.Debug|x86.ActiveCfg = Debug|Any CPU {208B84AF-A5DD-4C20-83D5-160EA243BA43}.Debug|x86.ActiveCfg = Debug|Any CPU
{208B84AF-A5DD-4C20-83D5-160EA243BA43}.Release|Any CPU.ActiveCfg = Release|Any CPU {208B84AF-A5DD-4C20-83D5-160EA243BA43}.Release|Any CPU.ActiveCfg = Release|Any CPU
{208B84AF-A5DD-4C20-83D5-160EA243BA43}.Release|Any CPU.Build.0 = Release|Any CPU {208B84AF-A5DD-4C20-83D5-160EA243BA43}.Release|Any CPU.Build.0 = Release|Any CPU
{208B84AF-A5DD-4C20-83D5-160EA243BA43}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{208B84AF-A5DD-4C20-83D5-160EA243BA43}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{208B84AF-A5DD-4C20-83D5-160EA243BA43}.Release|x86.ActiveCfg = Release|Any CPU {208B84AF-A5DD-4C20-83D5-160EA243BA43}.Release|x86.ActiveCfg = Release|Any CPU
{09F13185-3B2A-4FCB-AB6A-750112FFF920}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {09F13185-3B2A-4FCB-AB6A-750112FFF920}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{09F13185-3B2A-4FCB-AB6A-750112FFF920}.Debug|Any CPU.Build.0 = Debug|Any CPU {09F13185-3B2A-4FCB-AB6A-750112FFF920}.Debug|Any CPU.Build.0 = Debug|Any CPU
{09F13185-3B2A-4FCB-AB6A-750112FFF920}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{09F13185-3B2A-4FCB-AB6A-750112FFF920}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{09F13185-3B2A-4FCB-AB6A-750112FFF920}.Debug|x86.ActiveCfg = Debug|Any CPU {09F13185-3B2A-4FCB-AB6A-750112FFF920}.Debug|x86.ActiveCfg = Debug|Any CPU
{09F13185-3B2A-4FCB-AB6A-750112FFF920}.Release|Any CPU.ActiveCfg = Release|Any CPU {09F13185-3B2A-4FCB-AB6A-750112FFF920}.Release|Any CPU.ActiveCfg = Release|Any CPU
{09F13185-3B2A-4FCB-AB6A-750112FFF920}.Release|Any CPU.Build.0 = Release|Any CPU {09F13185-3B2A-4FCB-AB6A-750112FFF920}.Release|Any CPU.Build.0 = Release|Any CPU
{09F13185-3B2A-4FCB-AB6A-750112FFF920}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{09F13185-3B2A-4FCB-AB6A-750112FFF920}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{09F13185-3B2A-4FCB-AB6A-750112FFF920}.Release|x86.ActiveCfg = Release|Any CPU {09F13185-3B2A-4FCB-AB6A-750112FFF920}.Release|x86.ActiveCfg = Release|Any CPU
{ACDDD5D1-119F-4B82-8FE6-6515C812795B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ACDDD5D1-119F-4B82-8FE6-6515C812795B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ACDDD5D1-119F-4B82-8FE6-6515C812795B}.Debug|Any CPU.Build.0 = Debug|Any CPU {ACDDD5D1-119F-4B82-8FE6-6515C812795B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ACDDD5D1-119F-4B82-8FE6-6515C812795B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{ACDDD5D1-119F-4B82-8FE6-6515C812795B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{ACDDD5D1-119F-4B82-8FE6-6515C812795B}.Debug|x86.ActiveCfg = Debug|Any CPU {ACDDD5D1-119F-4B82-8FE6-6515C812795B}.Debug|x86.ActiveCfg = Debug|Any CPU
{ACDDD5D1-119F-4B82-8FE6-6515C812795B}.Release|Any CPU.ActiveCfg = Release|Any CPU {ACDDD5D1-119F-4B82-8FE6-6515C812795B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ACDDD5D1-119F-4B82-8FE6-6515C812795B}.Release|Any CPU.Build.0 = Release|Any CPU {ACDDD5D1-119F-4B82-8FE6-6515C812795B}.Release|Any CPU.Build.0 = Release|Any CPU
{ACDDD5D1-119F-4B82-8FE6-6515C812795B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{ACDDD5D1-119F-4B82-8FE6-6515C812795B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{ACDDD5D1-119F-4B82-8FE6-6515C812795B}.Release|x86.ActiveCfg = Release|Any CPU {ACDDD5D1-119F-4B82-8FE6-6515C812795B}.Release|x86.ActiveCfg = Release|Any CPU
{7D2A38F8-C42D-4F9F-AF64-1DDD5842C7EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7D2A38F8-C42D-4F9F-AF64-1DDD5842C7EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7D2A38F8-C42D-4F9F-AF64-1DDD5842C7EC}.Debug|Any CPU.Build.0 = Debug|Any CPU {7D2A38F8-C42D-4F9F-AF64-1DDD5842C7EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7D2A38F8-C42D-4F9F-AF64-1DDD5842C7EC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{7D2A38F8-C42D-4F9F-AF64-1DDD5842C7EC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{7D2A38F8-C42D-4F9F-AF64-1DDD5842C7EC}.Debug|x86.ActiveCfg = Debug|Any CPU {7D2A38F8-C42D-4F9F-AF64-1DDD5842C7EC}.Debug|x86.ActiveCfg = Debug|Any CPU
{7D2A38F8-C42D-4F9F-AF64-1DDD5842C7EC}.Release|Any CPU.ActiveCfg = Release|Any CPU {7D2A38F8-C42D-4F9F-AF64-1DDD5842C7EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D2A38F8-C42D-4F9F-AF64-1DDD5842C7EC}.Release|Any CPU.Build.0 = Release|Any CPU {7D2A38F8-C42D-4F9F-AF64-1DDD5842C7EC}.Release|Any CPU.Build.0 = Release|Any CPU
{7D2A38F8-C42D-4F9F-AF64-1DDD5842C7EC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{7D2A38F8-C42D-4F9F-AF64-1DDD5842C7EC}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{7D2A38F8-C42D-4F9F-AF64-1DDD5842C7EC}.Release|x86.ActiveCfg = Release|Any CPU {7D2A38F8-C42D-4F9F-AF64-1DDD5842C7EC}.Release|x86.ActiveCfg = Release|Any CPU
{78F407CF-416D-4D1F-971C-9A51FEA69B3A}.Debug|Any CPU.ActiveCfg = Debug|x86
{78F407CF-416D-4D1F-971C-9A51FEA69B3A}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{78F407CF-416D-4D1F-971C-9A51FEA69B3A}.Debug|Mixed Platforms.Build.0 = Debug|x86
{78F407CF-416D-4D1F-971C-9A51FEA69B3A}.Debug|x86.ActiveCfg = Debug|x86
{78F407CF-416D-4D1F-971C-9A51FEA69B3A}.Debug|x86.Build.0 = Debug|x86
{78F407CF-416D-4D1F-971C-9A51FEA69B3A}.Release|Any CPU.ActiveCfg = Release|x86
{78F407CF-416D-4D1F-971C-9A51FEA69B3A}.Release|Mixed Platforms.ActiveCfg = Release|x86
{78F407CF-416D-4D1F-971C-9A51FEA69B3A}.Release|Mixed Platforms.Build.0 = Release|x86
{78F407CF-416D-4D1F-971C-9A51FEA69B3A}.Release|x86.ActiveCfg = Release|x86
{78F407CF-416D-4D1F-971C-9A51FEA69B3A}.Release|x86.Build.0 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

@ -0,0 +1,390 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using NConsoler;
using Patcher.Data;
using Patcher.Data.Patch;
using Patcher.DB;
using System.IO;
namespace Patcher
{
public sealed class ConsoleProcessor
{
private const string STATUS_INSTALLING = "installing";
private const string STATUS_INSTALLED = "installed";
private static readonly Regex PatchName = new Regex("^Patch_(?<version>[01-9]+)_(?<author>[a-z]+)\\.xml$", RegexOptions.Compiled | RegexOptions.ExplicitCapture);
private readonly Context context;
private ConsoleProcessor(Context context)
{
this.context = context;
}
public static int Process(IConfig config, Func<IEnumerable<PatchId>> getPatchesList, Func<PatchId, Stream> loadPatch, string[] args)
{
using(new CultureReplacementWrapper(System.Globalization.CultureInfo.InvariantCulture))
{
return Consolery.RunInstance(new ConsoleProcessor(new Context(config, getPatchesList, loadPatch)), args);
}
}
private static int Wrap(bool interactive, Action action)
{
bool success = false;
try
{
action();
success = true;
} catch(Exception e)
{
Console.WriteLine();
Console.WriteLine(e);
}
if(interactive)
{
Console.WriteLine("Press any key to exit...");
Console.ReadKey(true);
}
return success ? 0 : 1;
}
private void InstallPatch(PatchId patchId)
{
AbstractPatch patch = AbstractPatch.LoadById(patchId, this.context);
if(!patch.DoesSupportEnvironment(context.config.EnvironmentName))
{
Console.Write(" (skipping because of unsupported environment) ");
return;
}
using(var transaction = TransactionFactory.Create(context))
{
bool patchExistsInDB;
XDocument rollbackData;
var patchDBData = transaction.ExecuteReader(
string.Format(
"select {1} from {0} where {2} = {3} and {4} = {5}",
transaction.EscapeName(context.config.PatchesTable),
transaction.EscapeName("STATUS"),
transaction.EscapeName("VERSION"),
transaction.MarkParam("pversion"),
transaction.EscapeName("AUTHOR"),
transaction.MarkParam("pauthor")
),
new Dictionary<string, object>
{
{ "pversion", patchId.version },
{ "pauthor", patchId.author },
}
).ToList();
if(patch is PersistentPatch)
{
bool reinstall;
if(patchDBData.Any())
{
var patchInfo = patchDBData.Single();
switch(patchInfo["STATUS"])
{
case STATUS_INSTALLED:
throw new ApplicationException("Patch is already installed");
case STATUS_INSTALLING:
reinstall = true;
break;
default:
throw new ApplicationException(string.Format("Unknown status {0}", patchInfo["STATUS"]));
}
} else
{
reinstall = false;
}
if(reinstall)
{
patchExistsInDB = true;
}
throw new NotImplementedException("Persistent patch installation is not implemented yet");
} else
{
if(patchDBData.Any())
{
throw new ApplicationException("Patch is already installed");
}
patchExistsInDB = false;
rollbackData = patch.Apply(transaction);
}
//System.Threading.Thread.Sleep(1000);
int affectedRows = transaction.ExecuteNonQuery(
String.Format(
patchExistsInDB
? "update {0} set {5} = {6}, {7} = {8} where {1} = {2} and {3} = {4}"
: "insert into {0}({1}, {3}, {5}, {7}) values({2}, {4}, {6}, {8})"
,
transaction.EscapeName(context.config.PatchesTable),
transaction.EscapeName("VERSION"),
transaction.MarkParam("pversion"),
transaction.EscapeName("AUTHOR"),
transaction.MarkParam("pauthor"),
transaction.EscapeName("ROLLBACK_DATA"),
transaction.MarkParam("prollbackdata"),
transaction.EscapeName("STATUS"),
transaction.MarkParam("pstatus")
),
new Dictionary<string, object>
{
{ "pversion", patchId.version },
{ "pauthor", patchId.author },
{ "prollbackdata", rollbackData.ToString() },
{ "pstatus", STATUS_INSTALLED },
}
);
if(affectedRows != 1)
{
throw new ApplicationException("Unable to install patch; are you trying to run two patchers simultaneously?");
}
transaction.Commit();
}
}
private void UninstallPatch(PatchId patchId)
{
using(var transaction = TransactionFactory.Create(context))
{
AbstractPatch patch = AbstractPatch.LoadById(patchId, context);
var patchInstallInfo = transaction.ExecuteReader(
String.Format(
"select {1} from {0} where {2} = {4} and {3} = {5} for update",
context.config.PatchesTable,
"ROLLBACK_DATA",
"VERSION",
"AUTHOR",
transaction.MarkParam("pversion"),
transaction.MarkParam("pauthor")
),
new Dictionary<string, object>
{
{ "pversion", patchId.version },
{ "pauthor", patchId.author },
}
).Single();
patch.Rollback(transaction, XDocument.Parse(patchInstallInfo["ROLLBACK_DATA"]));
System.Threading.Thread.Sleep(1000);
int affectedRows = transaction.ExecuteNonQuery(
String.Format(
"delete from {0} where {1} = {3} and {2} = {4}",
transaction.EscapeName(context.config.PatchesTable),
transaction.EscapeName("VERSION"),
transaction.EscapeName("AUTHOR"),
transaction.MarkParam("pversion"),
transaction.MarkParam("pauthor")
),
new Dictionary<string, object>
{
{ "pversion", patchId.version },
{ "pauthor", patchId.author },
}
);
if(affectedRows != 1)
{
throw new ApplicationException("Unable to install patch; are you trying to run two patchers simultaneously?");
}
transaction.Commit();
}
}
[Action]
public int Apply([Optional(false)] bool firstPatchOnly, [Optional(true)] bool interactive)
{
return Wrap(interactive, () => {
Console.WriteLine("begin");
List<PatchId> patchesToInstall;
List<PatchId> inputPatches = context.getPatchesList();
foreach (var patch in inputPatches)
{
Console.WriteLine("Found patch number {0:version}; author is {0:author}", patch);
}
using (Transaction transaction = TransactionFactory.Create(context))
{
patchesToInstall = (
//We should install patches that didn't finished installing in the first place
from row in transaction.ExecuteReader(
string.Format(
"select {1}, {2} from {0} where {3} = {4} for update",
transaction.EscapeName(context.config.PatchesTable),
transaction.EscapeName("VERSION"),
transaction.EscapeName("AUTHOR"),
transaction.EscapeName("STATUS"),
transaction.MarkParam("pstatus")
),
new Dictionary<string, object>
{
{ "pstatus", STATUS_INSTALLING },
}
)
let patch = new PatchId(int.Parse(row["VERSION"]), row["AUTHOR"])
orderby patch ascending
select patch
)
.Concat(
//Then are going all other patches from resources that did not begin installing
Util.RemoveExtra(
inputPatches,
from row in transaction.ExecuteReader(
string.Format(
"select {1}, {2} from {0} for update",
transaction.EscapeName(context.config.PatchesTable),
transaction.EscapeName("VERSION"),
transaction.EscapeName("AUTHOR")
)
)
let patch = new PatchId(int.Parse(row["VERSION"]), row["AUTHOR"])
orderby patch ascending
select patch
)
)
.ToList();
}
if(firstPatchOnly)
{
patchesToInstall = patchesToInstall.RemoveAllButFirst();
}
foreach (var patch in patchesToInstall)
{
Console.WriteLine("Going to install patch number {0:version}; author is {0:author}", patch);
}
if (patchesToInstall.Count == 0)
{
Console.WriteLine("No patches to install");
} else
{
bool shouldInstall = true;
if (interactive)
{
Console.WriteLine("Press Enter to install {0} patches or any key to exit", patchesToInstall.Count);
var info = Console.ReadKey(true);
if (info.Key != ConsoleKey.Enter) shouldInstall = false;
}
if (shouldInstall)
{
Console.WriteLine("Installing {0} patches...", patchesToInstall.Count);
foreach (var patch in patchesToInstall)
{
Console.Write("Installing patch #{0:version} from {0:author}...", patch);
InstallPatch(patch);
Console.WriteLine("done!");
}
Console.WriteLine("Finished installing patches");
} else
{
Console.WriteLine("Installation cancelled");
}
}
});
}
[Action]
public int Rollback([Optional(false)] bool lastPatchOnly, [Optional(true)] bool interactive)
{
return Wrap(interactive, () => {
Console.WriteLine("begin");
List<PatchId> patchesToRemove;
using (Transaction transaction = TransactionFactory.Create(context))
{
var patchesInstalling = (
from row in transaction.ExecuteReader(
string.Format(
"select {1}, {2} from {0} where {3} = {4} for update",
transaction.EscapeName(context.config.PatchesTable),
transaction.EscapeName("VERSION"),
transaction.EscapeName("AUTHOR"),
transaction.EscapeName("STATUS"),
transaction.MarkParam("pstatus")
),
new Dictionary<string, object>
{
{ "pstatus", STATUS_INSTALLING },
}
)
let patch = new PatchId(int.Parse(row["VERSION"]), row["AUTHOR"])
orderby patch ascending
select patch
).ToList();
if(patchesInstalling.Any())
{
foreach(var patchInstalling in patchesInstalling)
{
Console.WriteLine("Patch #{0:version} from {0:author} is already installing", patchInstalling);
}
throw new ApplicationException("Cannot uninstall patches while there are not installed ones");
}
patchesToRemove = (
from row in transaction.ExecuteReader(
string.Format(
"select {1}, {2} from {0} for update order by {3} desc",
transaction.EscapeName(context.config.PatchesTable),
transaction.EscapeName("VERSION"),
transaction.EscapeName("AUTHOR"),
transaction.EscapeName("INSTALL_DATE")
)
)
let patch = new PatchId(int.Parse(row["VERSION"]), row["AUTHOR"])
select patch
).ToList();
}
if(lastPatchOnly)
{
patchesToRemove = patchesToRemove.RemoveAllButFirst();
}
foreach (var patch in patchesToRemove)
{
Console.WriteLine("Going to uninstall patch number {0:version}; author is {0:author}", patch);
}
if (patchesToRemove.Count == 0)
{
Console.WriteLine("No patches to uninstall");
} else
{
bool shouldInstall = true;
if (interactive)
{
Console.WriteLine("Press Enter to uninstall {0} patches or any key to exit", patchesToRemove.Count);
var info = Console.ReadKey(true);
if (info.Key != ConsoleKey.Enter) shouldInstall = false;
}
if (shouldInstall)
{
Console.WriteLine("Uninstalling {0} patches...", patchesToRemove.Count);
foreach (var patch in patchesToRemove)
{
Console.Write("Uninstalling patch #{0:version} from {0:author}...", patch);
UninstallPatch(patch);
Console.WriteLine("done!");
}
Console.WriteLine("Finished uninstalling patches");
} else
{
Console.WriteLine("Uninstallation cancelled");
}
}
});
}
}
}

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Patcher.Data.Patch;
using Patcher.DB;
using System.IO;
namespace Patcher
{
class Context
{
public readonly IConfig config;
public readonly Func<List<PatchId>> getPatchesList;
public readonly Func<PatchId, Stream> loadPatch;
public readonly IDBTraits DbDriver;
private static readonly Dictionary<string, IDBTraits> DB_DRIVERS = new Dictionary<string, IDBTraits>
{
{ "oracle", OracleDBTraits.instance },
{ "oracle-faketransactional", OracleFakeTransactionalDBTraits.instance },
};
public Context(IConfig config, Func<IEnumerable<PatchId>> getPatchesListUnsorted, Func<PatchId, Stream> loadPatch) {
this.config = config;
this.getPatchesList = () => (from patchId in getPatchesListUnsorted() orderby patchId ascending select patchId).ToList();
this.loadPatch = loadPatch;
this.DbDriver = DB_DRIVERS[config.DbDriverName];
}
}
}

@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Patcher.DB
{
abstract class AbstractConstraint
{
public interface IVisitor
{
void Visit(ForeignKeyConstraint constraint);
void Visit(UniqueConstraint constraint);
void Visit(CheckConstraint constraint);
}
public interface IVisitor<T>
{
T Visit(ForeignKeyConstraint constraint);
T Visit(UniqueConstraint constraint);
T Visit(CheckConstraint constraint);
}
private class Visitor : IVisitor
{
private Action<ForeignKeyConstraint> foreignKeyAction;
private Action<UniqueConstraint> uniqueAction;
private Action<CheckConstraint> checkAction;
public Visitor(
Action<ForeignKeyConstraint> foreignKeyAction
,Action<UniqueConstraint> uniqueAction
,Action<CheckConstraint> checkAction
)
{
this.foreignKeyAction = foreignKeyAction;
this.uniqueAction = uniqueAction;
this.checkAction = checkAction;
}
void IVisitor.Visit(ForeignKeyConstraint constraint)
{
this.foreignKeyAction(constraint);
}
public void Visit(UniqueConstraint constraint)
{
this.uniqueAction(constraint);
}
public void Visit(CheckConstraint constraint)
{
this.checkAction(constraint);
}
}
private class Visitor<T> : IVisitor<T>
{
private Func<ForeignKeyConstraint, T> foreignKeyAction;
private Func<UniqueConstraint, T> uniqueAction;
private Func<CheckConstraint, T> checkAction;
public Visitor(
Func<ForeignKeyConstraint, T> foreignKeyAction
,Func<UniqueConstraint, T> uniqueAction
,Func<CheckConstraint, T> checkAction
)
{
this.foreignKeyAction = foreignKeyAction;
this.uniqueAction = uniqueAction;
this.checkAction = checkAction;
}
T IVisitor<T>.Visit(ForeignKeyConstraint constraint)
{
return this.foreignKeyAction(constraint);
}
public T Visit(UniqueConstraint constraint)
{
return this.uniqueAction(constraint);
}
public T Visit(CheckConstraint constraint)
{
return this.checkAction(constraint);
}
}
abstract public void Accept(IVisitor visitor);
abstract public T Accept<T>(IVisitor<T> visitor);
public void Accept(
Action<ForeignKeyConstraint> foreignKeyAction
,Action<UniqueConstraint> uniqueAction
,Action<CheckConstraint> checkAction
)
{
this.Accept(new Visitor(foreignKeyAction, uniqueAction, checkAction));
}
public T Accept<T>(
Func<ForeignKeyConstraint, T> foreignKeyFunc
,Func<UniqueConstraint, T> uniqueFunc
,Func<CheckConstraint, T> checkFunc
)
{
return this.Accept(new Visitor<T>(foreignKeyFunc, uniqueFunc, checkFunc));
}
public readonly string table;
public readonly string name;
protected AbstractConstraint(string table, string name)
{
this.table = table;
this.name = name;
}
}
}

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Patcher.DB
{
class CheckConstraint : AbstractConstraint
{
public override void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
public override T Accept<T>(IVisitor<T> visitor)
{
return visitor.Visit(this);
}
public readonly string condition;
public CheckConstraint(string table, string name, string condition)
: base(table, name)
{
this.condition = condition;
}
}
}

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Patcher.DB
{
class ColumnDescription
{
public readonly ColumnReference column;
public readonly ColumnOptions options;
public ColumnDescription(ColumnReference column, ColumnOptions options)
{
this.column = column;
this.options = options;
}
}
}

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Patcher.DB
{
class ColumnOptions : IEquatable<ColumnOptions>
{
public readonly string type;
public readonly string defaultValue;
public readonly bool isNotNull;
private string _simpleType
{
get
{
//for better compatibility with size specifiers we strip anything except for plain type name here
string stripped;
if(type.Contains('('))
{
stripped = type.Substring(0, type.IndexOf('('));
}
else
{
stripped = type;
}
return stripped.ToLower();
}
}
public ColumnOptions(string type, string defaultValue, bool isNotNull)
{
this.type = type;
this.defaultValue = defaultValue;
this.isNotNull = isNotNull;
}
#region IEquatable<ColumnOptions> Members
public bool Equals(ColumnOptions other)
{
return (this._simpleType == other._simpleType) && (this.defaultValue == other.defaultValue) && (this.isNotNull == other.isNotNull);
}
#endregion
}
}

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Patcher.DB
{
class ColumnReference
{
public readonly string tableName;
public readonly string columnName;
public ColumnReference(string tableName, string columnName)
{
this.tableName = tableName;
this.columnName = columnName;
}
}
}

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Patcher.DB
{
class ForeignKeyConstraint : AbstractConstraint
{
public override void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
public override T Accept<T>(IVisitor<T> visitor)
{
return visitor.Visit(this);
}
public enum ReferentialAction
{
NoAction,
Cascade,
SetNull,
SetDefault,
}
public readonly string column;
public readonly string referencedTable;
public readonly ReferentialAction onUpdate;
public readonly ReferentialAction onDelete;
public ForeignKeyConstraint(string table, string name, string column, string referencedTable, ReferentialAction onUpdate, ReferentialAction onDelete)
: base(table, name)
{
this.column = column;
this.referencedTable = referencedTable;
this.onUpdate = onUpdate;
this.onDelete = onDelete;
}
}
}

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.IO;
namespace Patcher.DB
{
interface IDBTraits
{
string EscapeName(string name);
string MarkParam(string rawParamName);
string ParamName(string rawParamName);
DbConnection CreateConnection(string connectionString);
StoredProcedureBody GetStoredProcedureBody(Func<DbCommand> commandCreator, StoredProcedureReference procedure);
void ReplaceStoredProcedureBody(Func<DbCommand> commandCreator, StoredProcedureReference procedure, StoredProcedureBody newBody);
void RemoveStoredProcedure(Func<DbCommand> commandCreator, StoredProcedureReference procedure);
void CreateStoredProcedure(Func<DbCommand> commandCreator, StoredProcedureReference procedure, StoredProcedureBody body);
ColumnOptions GetColumnOptions(Func<DbCommand> commandCreator, ColumnReference column);
void RemoveColumn(Func<DbCommand> commandCreator, ColumnReference column);
void CreateColumn(Func<DbCommand> commandCreator, ColumnDescription description);
void ModifyColumn(Func<DbCommand> commandCreator, ColumnDescription description);
void RemoveConstraint(Func<DbCommand> commandCreator, AbstractConstraint constraint);
void CreateConstraint(Func<DbCommand> commandCreator, AbstractConstraint constraint);
void CreateTable(Func<DbCommand> commandCreator, TableDescription table);
void RemoveTable(Func<DbCommand> commandCreator, TableDescription table);
string GetViewBody(Func<DbCommand> commandCreator, string viewName);
void CreateView(Func<DbCommand> commandCreator, string viewName, string body);
void RemoveView(Func<DbCommand> commandCreator, string viewName);
bool IsDDLTransactional
{ get; }
}
}

@ -0,0 +1,841 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Data.OracleClient;
//using Devart.Data.Oracle;
namespace Patcher.DB
{
class OracleDBTraits : IDBTraits
{
public static readonly IDBTraits instance = new OracleDBTraits();
protected OracleDBTraits()
{
}
private static readonly Regex ALPHANUMERIC = new Regex("^[a-zA-Z]\\w*$", RegexOptions.Compiled | RegexOptions.Singleline);
private static string _EscapeName(string name)
{
if(!ALPHANUMERIC.IsMatch(name)) throw new ApplicationException("Name should contain only alphanumeric characters");
return string.Format("\"{0}\"", name);
}
string IDBTraits.EscapeName(string name)
{
return _EscapeName(name);
}
DbConnection IDBTraits.CreateConnection(string connectionString)
{
#pragma warning disable 0618
OracleConnection connection = new OracleConnection(connectionString);
#pragma warning restore 0618
connection.Open();
return connection;
}
string IDBTraits.MarkParam(string paramName)
{
if(!ALPHANUMERIC.IsMatch(paramName)) throw new ApplicationException("Name should contain only alphanumeric characters");
return ":" + paramName;
}
string IDBTraits.ParamName(string paramName)
{
if(!ALPHANUMERIC.IsMatch(paramName)) throw new ApplicationException("Name should contain only alphanumeric characters");
return paramName;
}
private static void AddParam(DbCommand command, string name, DbType type, object value)
{
var param = command.CreateParameter();
param.ParameterName = name;
param.DbType = type;
param.Value = value;
command.Parameters.Add(param);
}
private static bool ParseBoolString(string value)
{
switch(value.ToLower())
{
case "n":
return false;
case "y":
return true;
default:
throw new ApplicationException(string.Format("Unknown value {0}", value));
}
}
private static T CastResult<T>(object value) where T : class
{
if(DBNull.Value.Equals(value))
{
return null;
} else
{
return (T)value;
}
}
private static T? CastScalarResult<T>(object value) where T : struct
{
if(DBNull.Value.Equals(value))
{
return null;
} else
{
return (T)value;
}
}
private static readonly SQLQueryManager _SQLQueryManager = new SQLQueryManager(_EscapeName);
private static string[] GetPackage(Func<DbCommand> commandCreator, string package, string type)
{
using(DbCommand command = commandCreator())
{
command.CommandText = "SELECT LINE, TEXT FROM USER_SOURCE WHERE TYPE = :ptype AND NAME = :pname order by line";
AddParam(command, "ptype", DbType.String, type);
AddParam(command, "pname", DbType.String, package);
List<string> lines = new List<string>();
using(DbDataReader reader = command.ExecuteReader())
{
while(reader.Read())
{
lines.Add(reader.GetValue(reader.GetOrdinal("TEXT")).ToString());
}
}
return lines.ToArray();
}
}
private static readonly Regex PROCEDURE_BODY_HEADER_REGEX = new Regex(
"^procedure" +
"\\s+" +
"(?<name>\\w+)" +
"\\s*" +
"\\(" +
"(?<params>" +
"(\\w+\\s+(in|out)\\s+\\w+)" +
"(\\,\\s*\\w+\\s+(in|out)\\s+\\w+)*" +
")?" +
"\\)$",
RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase | RegexOptions.Compiled
);
private static readonly Regex PROCEDURE_BODY_FOOTER_REGEX = new Regex(
"^end" +
"\\s+" +
"(?<name>\\w+)" +
";$",
RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase | RegexOptions.Compiled
);
private static readonly Dictionary<StoredProcedureReference.ParameterDirection, string> DIRECTIONS = new Dictionary<StoredProcedureReference.ParameterDirection, string>
{
{ StoredProcedureReference.ParameterDirection.In, "in" },
{ StoredProcedureReference.ParameterDirection.Out, "out" },
};
private struct ProcedureBodyPosition
{
public int header;
public int begin;
public int end;
}
private class ProcedureNotFoundException : FormattableException
{
public ProcedureNotFoundException(string message, params object[] args) : base(message, args)
{
}
}
private class ViewNotFoundException : FormattableException
{
public ViewNotFoundException(string message, params object[] args) : base(message, args)
{
}
}
private static int FindProcedureHeader(string[] package, StoredProcedureReference procedure)
{
for(int i=0; i<package.Length; i++)
{
var line = package[i].Trim().TrimEnd(';');
if (PROCEDURE_BODY_HEADER_REGEX.IsMatch(line))
{
var match = PROCEDURE_BODY_HEADER_REGEX.Match(line);
string name = match.Groups["name"].Value;
string[] parameters = match.Groups["params"].Value.Split(',');
if(name == procedure.procedureName)
{
if(parameters.Length != procedure.parameters.Length)
{
throw new ApplicationException(String.Format("Parameters number mismatch for procedure {0}: expected {1}, got {2}", procedure.procedureName, procedure.parameters.Length, parameters.Length));
}
for(int j=0; j<parameters.Length; j++)
{
string[] parts = parameters[j].Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
if(parts[0] != procedure.parameters[j].name)
{
throw new ApplicationException(String.Format("Parameter {1} name mismatch for procedure {0}: expected {2}, got {3}", j, procedure.procedureName, procedure.parameters[j].name, parts[0]));
}
if(parts[1].ToLower() != DIRECTIONS[procedure.parameters[j].direction])
{
throw new ApplicationException(String.Format("Parameter {1} direction mismatch for procedure {0}: expected {2}, got {3}", procedure.procedureName, procedure.parameters[j].name, DIRECTIONS[procedure.parameters[j].direction], parts[1]));
}
if(parts[2] != procedure.parameters[j].type)
{
throw new ApplicationException(String.Format("Parameter {1} type mismatch for procedure {0}: expected {2}, got {3}", j, procedure.procedureName, procedure.parameters[j].type, parts[2]));
}
}
return i;
}
}
}
throw new ProcedureNotFoundException(String.Format("Procedure {0} header not found", procedure.procedureName));
}
private static ProcedureBodyPosition FindProcedureBodyPosition(string[] package, StoredProcedureReference procedure)
{
int header = FindProcedureHeader(package, procedure);
int? begin = null;
int? end = null;
if(package.Length <= header+2)
{
throw new ApplicationException("Unexpected package end");
}
if(package[header+1].Trim().ToLower() != "is")
{
throw new ApplicationException(String.Format("Incorrect line follows procedure {0} header: '{1}'", procedure.procedureName, package[header+1].Trim()));
}
for(int i=header+2; i<package.Length; i++)
{
if(package[i].Trim().ToLower() == "begin")
{
begin = i;
break;
}
}
if(!begin.HasValue)
{
throw new ApplicationException(String.Format("Unable to find body begin for procedure {0}", procedure.procedureName));
}
if(package.Length <= begin+1)
{
throw new ApplicationException("Unexpected package end");
}
for(int i=begin.Value+1; i<package.Length; i++)
{
string line = package[i].Trim();
if(PROCEDURE_BODY_FOOTER_REGEX.IsMatch(line))
{
var match = PROCEDURE_BODY_FOOTER_REGEX.Match(line);
var name = match.Groups["name"].Value;
if(name != procedure.procedureName)
{
throw new ApplicationException(String.Format("Wrong procedure footer: expected {0}, got {1}", procedure.procedureName, name));
}
end = i;
break;
}
}
if(!end.HasValue)
{
throw new ApplicationException("Procedure footer not found");
}
return new ProcedureBodyPosition
{
header = header,
begin = begin.Value,
end = end.Value,
};
}
StoredProcedureBody IDBTraits.GetStoredProcedureBody(Func<DbCommand> commandCreator, StoredProcedureReference procedure)
{
string[] package = GetPackage(commandCreator, procedure.packageName, "PACKAGE BODY");
var position = FindProcedureBodyPosition(package, procedure);
StringBuilder declarationsBuilder = new StringBuilder();
for(int i=position.header+2; i<position.begin; i++)
{
declarationsBuilder.Append(package[i]);
}
StringBuilder bodyBuilder = new StringBuilder();
for(int i=position.begin+1; i<position.end; i++)
{
bodyBuilder.AppendFormat(package[i]);
}
return new StoredProcedureBody(declarationsBuilder.ToString(), bodyBuilder.ToString());
}
private static string FormatStoredProcedureHeader(StoredProcedureReference procedure)
{
return string.Format("procedure {0}({1})", procedure.procedureName, String.Join(", ", (from param in procedure.parameters select String.Format("{0} {1} {2}", param.name, DIRECTIONS[param.direction], param.type)).ToArray()));
}
private static string FormatStoredProcedure(StoredProcedureReference procedure, StoredProcedureBody body)
{
StringBuilder result = new StringBuilder();
result.Append(FormatStoredProcedureHeader(procedure));
result.AppendLine();
result.AppendLine("is");
if(body.declarations.Trim() != "")
{
result.AppendLine(body.declarations.TrimEnd().Trim('\r', '\n'));
}
result.AppendLine("begin");
if(body.body.Trim() != "")
{
result.AppendLine(body.body.TrimEnd().Trim('\r', '\n'));
}
result.AppendFormat("end {0};", procedure.procedureName);
result.AppendLine();
return result.ToString();
}
void IDBTraits.ReplaceStoredProcedureBody(Func<DbCommand> commandCreator, StoredProcedureReference procedure, StoredProcedureBody newBody)
{
string[] package = GetPackage(commandCreator, procedure.packageName, "PACKAGE BODY");
var position = FindProcedureBodyPosition(package, procedure);
StringBuilder packageBuilder = new StringBuilder();
packageBuilder.AppendLine("create or replace");
for(int i=0; i<position.header; i++)
{
packageBuilder.Append(package[i]);
}
packageBuilder.Append(FormatStoredProcedure(procedure, newBody));
for(int i=position.end+1; i<package.Length; i++)
{
packageBuilder.AppendFormat(package[i]);
}
/*Console.WriteLine();
Console.WriteLine("===NEW PACKAGE===");
Console.WriteLine(packageBuilder.ToString());
Console.WriteLine("===END PACKAGE===");*/
using(DbCommand command = commandCreator())
{
command.CommandText = packageBuilder.ToString();
command.ExecuteNonQuery();
}
}
void IDBTraits.RemoveStoredProcedure(Func<DbCommand> commandCreator, StoredProcedureReference procedure)
{
{
string[] packageSpec = GetPackage(commandCreator, procedure.packageName, "PACKAGE");
int position = FindProcedureHeader(packageSpec, procedure);
StringBuilder packageSpecBuilder = new StringBuilder();
packageSpecBuilder.AppendLine("create or replace");
for(int i=0; i<position; i++)
{
packageSpecBuilder.Append(packageSpec[i]);
}
for(int i=position+1; i<packageSpec.Length; i++)
{
packageSpecBuilder.Append(packageSpec[i]);
}
using(DbCommand command = commandCreator())
{
command.CommandText = packageSpecBuilder.ToString();
/*Console.WriteLine();
Console.WriteLine("=====PACKAGE SPEC=====");
Console.WriteLine(command.CommandText);
Console.WriteLine("=====END PACKAGE SPEC=====");*/
command.ExecuteNonQuery();
}
}
{
string[] package = GetPackage(commandCreator, procedure.packageName, "PACKAGE BODY");
var position = FindProcedureBodyPosition(package, procedure);
StringBuilder packageBuilder = new StringBuilder();
packageBuilder.AppendLine("create or replace");
for(int i=0; i<position.header; i++)
{
packageBuilder.Append(package[i]);
}
for(int i=position.end+1; i<package.Length; i++)
{
packageBuilder.Append(package[i]);
}
using(DbCommand command = commandCreator())
{
command.CommandText = packageBuilder.ToString();
/*Console.WriteLine();
Console.WriteLine("=====PACKAGE BODY=====");
Console.WriteLine(command.CommandText);
Console.WriteLine("=====END PACKAGE BODY=====");*/
command.ExecuteNonQuery();
}
}
}
void IDBTraits.CreateView(Func<DbCommand> commandCreator, string viewName, string body)
{
using(DbCommand command = commandCreator())
{
command.CommandText = string.Format("CREATE VIEW {0} as {1}", _EscapeName(viewName), body);
Console.WriteLine(command.CommandText);
command.ExecuteNonQuery();
}
}
void IDBTraits.RemoveView(Func<DbCommand> commandCreator, string viewName)
{
using(DbCommand command = commandCreator())
{
command.CommandText = string.Format("DROP VIEW {0}", _EscapeName(viewName));
Console.WriteLine(command.CommandText);
command.ExecuteNonQuery();
}
}
void IDBTraits.CreateStoredProcedure(Func<DbCommand> commandCreator, StoredProcedureReference procedure, StoredProcedureBody body)
{
string packageEnd = string.Format("end {0};", procedure.packageName);
string packageEndMarker = packageEnd.ToLower();
{
string[] package = GetPackage(commandCreator, procedure.packageName, "PACKAGE BODY");
try
{
var position = FindProcedureBodyPosition(package, procedure);
throw new ApplicationException(String.Format("Procedure {0} body is already declared", procedure.procedureName));
} catch(ProcedureNotFoundException)
{
}
StringBuilder packageBuilder = new StringBuilder();
packageBuilder.AppendLine("create or replace");
for(int i=0; i<package.Length; i++)
{
if(package[i].Trim().ToLower() == packageEndMarker)
{
if(package.Length > i+1)
{
throw new ApplicationException("Unexpected package end");
}
break;
}
packageBuilder.Append(package[i]);
}
packageBuilder.Append(FormatStoredProcedure(procedure, body));
packageBuilder.AppendLine(packageEnd);
using(DbCommand command = commandCreator())
{
command.CommandText = packageBuilder.ToString();
/*Console.WriteLine();
Console.WriteLine("=====PACKAGE BODY=====");
Console.WriteLine(command.CommandText);
Console.WriteLine("=====END PACKAGE BODY=====");*/
command.ExecuteNonQuery();
}
}
{
string[] packageSpec = GetPackage(commandCreator, procedure.packageName, "PACKAGE");
try
{
var position = FindProcedureHeader(packageSpec, procedure);
throw new ApplicationException(String.Format("Procedure {0} is already declared", procedure.procedureName));
} catch(ProcedureNotFoundException)
{
}
StringBuilder packageSpecBuilder = new StringBuilder();
packageSpecBuilder.AppendLine("create or replace");
for(int i=0; i<packageSpec.Length; i++)
{
if(packageSpec[i].Trim().ToLower() == packageEndMarker)
{
if(packageSpec.Length > i+1)
{
throw new ApplicationException("Unexpected package end");
}
break;
}
packageSpecBuilder.Append(packageSpec[i]);
}
packageSpecBuilder.Append(FormatStoredProcedureHeader(procedure));
packageSpecBuilder.AppendLine(";");
packageSpecBuilder.AppendLine(packageEnd);
using(DbCommand command = commandCreator())
{
command.CommandText = packageSpecBuilder.ToString();
/*Console.WriteLine();
Console.WriteLine("=====PACKAGE SPEC=====");
Console.WriteLine(command.CommandText);
Console.WriteLine("=====END PACKAGE SPEC=====");*/
command.ExecuteNonQuery();
}
}
}
string IDBTraits.GetViewBody(Func<DbCommand> commandCreator, string name)
{
using(DbCommand command = commandCreator())
{
command.CommandText = "SELECT TEXT FROM USER_VIEWS WHERE VIEW_NAME = :pname";
AddParam(command, "pname", DbType.String, name);
using(DbDataReader reader = command.ExecuteReader())
{
if(!reader.Read())
{
throw new ViewNotFoundException("View {0} not found", name);
}
return reader.GetValue("TEXT").ToString();
}
}
}
ColumnOptions IDBTraits.GetColumnOptions(Func<DbCommand> commandCreator, ColumnReference column)
{
using(DbCommand command = commandCreator())
{
command.CommandText = "select * from user_tab_columns where table_name = :ptable and column_name = :pcolumn";
AddParam(command, "ptable", DbType.String, column.tableName);
AddParam(command, "pcolumn", DbType.String, column.columnName);
using(DbDataReader reader = command.ExecuteReader())
{
if(!reader.Read())
{
throw new ApplicationException("Column not found");
}
string dataType = (string)reader.GetValue("DATA_TYPE");
decimal? dataLength = CastScalarResult<decimal>(reader.GetValue("DATA_LENGTH"));
decimal? dataPrecision = CastScalarResult<decimal>(reader.GetValue("DATA_PRECISION"));
decimal? dataScale = CastScalarResult<decimal>(reader.GetValue("DATA_SCALE"));
string sizeSpecifier = null;
if(dataPrecision.HasValue)
{
if(dataScale.HasValue)
{
sizeSpecifier = string.Format("{0}, {1}", dataPrecision, dataScale);
}
else
{
sizeSpecifier = dataPrecision.ToString();
}
}
else if(dataLength.HasValue)
{
sizeSpecifier = dataLength.ToString();
}
else
{
throw new ApplicationException("Field without a length");
}
string defaultValue = CastResult<string>(reader.GetValue(reader.GetOrdinal("DATA_DEFAULT")));
return new ColumnOptions(
string.Format("{0}({1})", dataType, sizeSpecifier),
defaultValue != null ? defaultValue.Trim() : null,
!ParseBoolString((string)reader.GetValue(reader.GetOrdinal("NULLABLE")))
);
}
}
}
void IDBTraits.RemoveColumn(Func<DbCommand> commandCreator, ColumnReference column)
{
using(DbCommand command = commandCreator())
{
command.CommandText = _SQLQueryManager.RemoveColumn(column);
command.ExecuteNonQuery();
}
}
void IDBTraits.CreateColumn(Func<DbCommand> commandCreator, ColumnDescription description)
{
using(DbCommand command = commandCreator())
{
command.CommandText = _SQLQueryManager.CreateColumn(description);
command.ExecuteNonQuery();
}
}
void IDBTraits.ModifyColumn(Func<DbCommand> commandCreator, ColumnDescription description)
{
using(DbCommand command = commandCreator())
{
command.CommandText = _SQLQueryManager.ModifyColumnOracleStyle(description);
Console.WriteLine();
Console.WriteLine(command.CommandText);
command.ExecuteNonQuery();
}
}
private static string GetStringRepresentation(ForeignKeyConstraint.ReferentialAction action)
{
switch(action)
{
case ForeignKeyConstraint.ReferentialAction.NoAction:
return "NO ACTION";
case ForeignKeyConstraint.ReferentialAction.Cascade:
return "CASCADE";
case ForeignKeyConstraint.ReferentialAction.SetNull:
return "SET NULL";
case ForeignKeyConstraint.ReferentialAction.SetDefault:
return "SET DEFAULT";
default:
throw new ApplicationException("Unknown referential action");
}
}
private void CheckConstraint(Func<DbCommand> commandCreator, ForeignKeyConstraint constraint)
{
string referenced;
using(DbCommand command = commandCreator())
{
command.CommandText = "SELECT * FROM USER_CONSTRAINTS WHERE TABLE_NAME = :ptable and CONSTRAINT_NAME = :pconstraint";
AddParam(command, "ptable", DbType.String, constraint.table);
AddParam(command, "pconstraint", DbType.String, constraint.name);
using(var reader = command.ExecuteReader())
{
if(!reader.Read())
{
throw new FormattableException("Constraint {0}.{1} not found", constraint.table, constraint.name);
}
if(reader.GetValue("CONSTRAINT_TYPE").ToString() != "R")
{
throw new FormattableException("Constraint {0}.{1} wrong type: expected {2}, got {3}", constraint.table, constraint.name, "R", reader.GetValue("CONSTRAINT_TYPE"));
}
if(reader.GetValue("DELETE_RULE").ToString() != GetStringRepresentation(constraint.onDelete))
{
throw new FormattableException("Constraint {0}.{1} wrong on delete action: expected {2}, got {3}", constraint.table, constraint.name, GetStringRepresentation(constraint.onDelete), reader.GetValue("DELETE_RULE"));
}
referenced = reader.GetValue("R_CONSTRAINT_NAME").ToString();
}
}
using(DbCommand command = commandCreator())
{
command.CommandText = "SELECT * FROM USER_CONSTRAINTS WHERE CONSTRAINT_NAME = :pconstraint AND CONSTRAINT_TYPE = :ptype";
AddParam(command, "pconstraint", DbType.String, referenced);
AddParam(command, "ptype", DbType.String, "P");
using(var reader = command.ExecuteReader())
{
reader.Read();
if(reader.GetValue("TABLE_NAME").ToString() != constraint.referencedTable)
{
throw new FormattableException("Constraint {0}.{1} wrong referenced table: expected {2}, got {3}", constraint.table, constraint.name, constraint.referencedTable, reader.GetValue("TABLE_NAME"));
}
}
}
using(DbCommand command = commandCreator())
{
command.CommandText = "SELECT * FROM USER_CONS_COLUMNS WHERE TABLE_NAME = :ptable AND CONSTRAINT_NAME = :pconstraint";
AddParam(command, "ptable", DbType.String, constraint.table);
AddParam(command, "pconstraint", DbType.String, constraint.name);
using(var reader = command.ExecuteReader())
{
reader.Read();
if(reader.GetValue("COLUMN_NAME").ToString() != constraint.column)
{
throw new FormattableException("Constraint {0}.{1} wrong column: expected {2}, got {3}", constraint.table, constraint.name, constraint.column, reader.GetValue("COLUMN_NAME"));
}
}
}
}
private void CheckConstraint(Func<DbCommand> commandCreator, UniqueConstraint constraint)
{
using(DbCommand command = commandCreator())
{
command.CommandText = "SELECT * FROM USER_CONSTRAINTS WHERE TABLE_NAME = :ptable and CONSTRAINT_NAME = :pconstraint";
AddParam(command, "ptable", DbType.String, constraint.table);
AddParam(command, "pconstraint", DbType.String, constraint.name);
using(var reader = command.ExecuteReader())
{
if(!reader.Read())
{
throw new FormattableException("Constraint {0}.{1} not found", constraint.table, constraint.name);
}
if(reader.GetValue("CONSTRAINT_TYPE").ToString() != "U")
{
throw new FormattableException("Constraint {0}.{1} wrong type: expected {2}, got {3}", constraint.table, constraint.name, "U", reader.GetValue("CONSTRAINT_TYPE"));
}
}
}
using(DbCommand command = commandCreator())
{
command.CommandText = "SELECT * FROM USER_CONS_COLUMNS WHERE TABLE_NAME = :ptable AND CONSTRAINT_NAME = :pconstraint";
AddParam(command, "ptable", DbType.String, constraint.table);
AddParam(command, "pconstraint", DbType.String, constraint.name);
using(var reader = command.ExecuteReader())
{
HashSet<string> dbColumns = new HashSet<string>();
while(reader.Read())
{
dbColumns.Add(reader.GetValue("COLUMN_NAME").ToString());
}
if(!dbColumns.IsSubsetOf(constraint.columns))
{
throw new FormattableException("Some columns are not mentioned in constraint definition: {0}", string.Join(",", dbColumns.Except(constraint.columns).ToArray()));
}
if(!dbColumns.IsSupersetOf(constraint.columns))
{
throw new FormattableException("Some columns are missed in DB: {0}", string.Join(",", constraint.columns.Except(dbColumns).ToArray()));
}
}
}
}
private void CheckConstraint(Func<DbCommand> commandCreator, CheckConstraint constraint)
{
using(DbCommand command = commandCreator())
{
command.CommandText = "SELECT * FROM USER_CONSTRAINTS WHERE TABLE_NAME = :ptable and CONSTRAINT_NAME = :pconstraint";
AddParam(command, "ptable", DbType.String, constraint.table);
AddParam(command, "pconstraint", DbType.String, constraint.name);
using(var reader = command.ExecuteReader())
{
if(!reader.Read())
{
throw new FormattableException("Constraint {0}.{1} not found", constraint.table, constraint.name);
}
if(reader.GetValue("CONSTRAINT_TYPE").ToString() != "C")
{
throw new FormattableException("Constraint {0}.{1} wrong type: expected {2}, got {3}", constraint.table, constraint.name, "C", reader.GetValue("CONSTRAINT_TYPE"));
}
if(reader.GetValue("SEARCH_CONDITION").ToString() != constraint.condition)
{
throw new FormattableException("Constraint {0}.{1} wrong condition: expected {2}, got {3}", constraint.table, constraint.name, constraint.condition, reader.GetValue("SEARCH_CONDITION"));
}
}
}
}
private void CheckConstraint(Func<DbCommand> commandCreator, AbstractConstraint constraint)
{
constraint.Accept(fkc => CheckConstraint(commandCreator, fkc), uc => CheckConstraint(commandCreator, uc), cc => CheckConstraint(commandCreator, cc));
}
public void RemoveConstraint(Func<DbCommand> commandCreator, AbstractConstraint constraint)
{
CheckConstraint(commandCreator, constraint);
using(DbCommand command = commandCreator())
{
command.CommandText = _SQLQueryManager.DropConstraint(constraint);
Console.WriteLine();
Console.WriteLine(command.CommandText);
command.ExecuteNonQuery();
}
}
public void CreateConstraint(Func<DbCommand> commandCreator, AbstractConstraint constraint)
{
using(DbCommand command = commandCreator())
{
command.CommandText = _SQLQueryManager.CreateConstraint(constraint);
Console.WriteLine();
Console.WriteLine(command.CommandText);
command.ExecuteNonQuery();
}
}
public void CreateTable(Func<DbCommand> commandCreator, TableDescription table) {
using(DbCommand command = commandCreator())
{
command.CommandText = _SQLQueryManager.CreateTable(table);
Console.WriteLine();
Console.WriteLine(command.CommandText);
command.ExecuteNonQuery();
}
}
private void CheckTable(Func<DbCommand> commandCreator, TableDescription table) {
HashSet<string> columns = new HashSet<string>(from column in table.columns select column.column.columnName);
columns.Add(table.primaryKey.column.columnName);
using(DbCommand command = commandCreator())
{
command.CommandText = "SELECT * FROM user_tab_columns WHERE TABLE_NAME = :ptable";
AddParam(command, "ptable", DbType.String, table.table);
using(var reader = command.ExecuteReader())
{
HashSet<string> dbColumns = new HashSet<string>();
while(reader.Read())
{
dbColumns.Add(reader.GetValue("COLUMN_NAME").ToString());
}
if(!dbColumns.IsSubsetOf(columns))
{
throw new FormattableException("Some columns are not mentioned in table definition: {0}", string.Join(",", dbColumns.Except(columns).ToArray()));
}
if(!dbColumns.IsSupersetOf(columns))
{
throw new FormattableException("Some columns are missed in DB: {0}", string.Join(",", columns.Except(dbColumns).ToArray()));
}
}
}
var options = (this as IDBTraits).GetColumnOptions(commandCreator, table.primaryKey.column);
Console.WriteLine();
Console.WriteLine("'{0}' vs '{1}'", table.primaryKey.options.type, options.type);
Console.WriteLine("'{0}' vs '{1}'", table.primaryKey.options.defaultValue, options.defaultValue);
Console.WriteLine("'{0}' vs '{1}'", table.primaryKey.options.isNotNull, options.isNotNull);
if(!table.primaryKey.options.Equals((this as IDBTraits).GetColumnOptions(commandCreator, table.primaryKey.column))) {
throw new FormattableException("Column {0} definition mismatch", table.primaryKey.column.columnName);
}
foreach(var column in table.columns) {
options = (this as IDBTraits).GetColumnOptions(commandCreator, column.column);
Console.WriteLine();
Console.WriteLine("'{0}' vs '{1}'", column.options.type, options.type);
Console.WriteLine("'{0}' vs '{1}'", column.options.defaultValue, options.defaultValue);
Console.WriteLine("'{0}' vs '{1}'", column.options.isNotNull, options.isNotNull);
if(!column.options.Equals((this as IDBTraits).GetColumnOptions(commandCreator, column.column))) {
throw new FormattableException("Column {0} definition mismatch", column.column.columnName);
}
}
}
void IDBTraits.RemoveTable(Func<DbCommand> commandCreator, TableDescription table) {
this.CheckTable(commandCreator, table);
using(DbCommand command = commandCreator())
{
command.CommandText = _SQLQueryManager.DropTable(table.table);
Console.WriteLine();
Console.WriteLine(command.CommandText);
command.ExecuteNonQuery();
}
}
bool IDBTraits.IsDDLTransactional
{
get { return false; }
}
}
}

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Patcher.DB
{
/// <summary>
/// Ideally, this class should never be used.
/// It allows patches designed for really transactional DBs (e.g. PostgreSQL) to be applied to Oracle DB.
/// Note that such patches should never be applied automatically to Oracle; manual installation by developer required, because, if something goes wrong, it leaves a system in an incorrect state.
/// </summary>
class OracleFakeTransactionalDBTraits : OracleDBTraits, IDBTraits
{
public new static readonly IDBTraits instance = new OracleFakeTransactionalDBTraits();
protected OracleFakeTransactionalDBTraits() : base()
{
}
bool IDBTraits.IsDDLTransactional
{
get { return true; }
}
}
}

@ -0,0 +1,320 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Patcher;
namespace Patcher.DB
{
/// <summary>
/// This class contains ANSI SQL query builders<br/>
/// </summary>
/// <see cref="http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt"/>
/// <see cref="http://savage.net.au/SQL/sql-2003-2.bnf.html"/>
class SQLQueryManager
{
[Flags]
private enum Specific
{
None = 0x0,
OracleNullExtension = 0x1,
}
[Flags]
private enum ColumnSpecific
{
None = 0x0,
PrimaryKey = 0x1,
}
private readonly Func<string, string> nameEscaper;
public SQLQueryManager(Func<string, string> nameEscaper)
{
this.nameEscaper = nameEscaper;
}
/// <summary>
/// SQL-92, page 262<br/>
/// Note that setting oracleNullExtension to true will make the query non-compliant
/// </summary>
private string _ColumnDefinition(ColumnDescription description, ColumnSpecific columnSpecific, Specific specific)
{
return string.Format(
"{0} {1} {2} {3} {4}",
nameEscaper(description.column.columnName), //column name
description.options.type, //data type | domain name
(description.options.defaultValue != null) ? "DEFAULT " + description.options.defaultValue : "", //NOTE possible SQL injection here! //default clause
String.Join(
" ",
Enumerable.Empty<string>()
.ConcatScalar(description.options.isNotNull ? "NOT NULL" : ((specific & Specific.OracleNullExtension) == Specific.OracleNullExtension ? "NULL" : null))
.ConcatScalar((columnSpecific & ColumnSpecific.PrimaryKey) == ColumnSpecific.PrimaryKey ? "PRIMARY KEY" : null)
.ToArray()
), //column constraint definition
"" //collate clause
);
}
/// <summary>
/// SQL-92, page 283
/// </summary>
private string _AlterTableStatement(string table, string alterTableAction)
{
return string.Format(
"alter table {0} {1}",
nameEscaper(table),
alterTableAction
);
}
/// <summary>
/// SQL-92, page 284
/// </summary>
private string _AddColumnDefinition(ColumnDescription description)
{
return string.Format(
"add {0}",
_ColumnDefinition(description, ColumnSpecific.None, Specific.None)
);
}
/// <summary>
/// SQL-92, page 289
/// </summary>
private string _DropColumnDefinition(ColumnReference column)
{
return string.Format(
"drop column {0} {1}",
nameEscaper(column.columnName), //column name
"" //drop behavior
);
}
/// <summary>
/// SQL-92, page 274
/// </summary>
private static string _ReferentialAction(ForeignKeyConstraint.ReferentialAction action)
{
switch(action)
{
case ForeignKeyConstraint.ReferentialAction.NoAction:
return "NO ACTION";
case ForeignKeyConstraint.ReferentialAction.Cascade:
return "CASCADE";
case ForeignKeyConstraint.ReferentialAction.SetNull:
return "SET NULL";
case ForeignKeyConstraint.ReferentialAction.SetDefault:
return "SET DEFAULT";
default:
throw new ApplicationException(string.Format("Unknown action {0}", action));
}
}
/// <summary>
/// SQL-92, page 274<br/>
/// Note that item 8 on page 276 specifies that if ON UPDATE clause is omitted, DB should act as if "NO ACTION" is implicitly stated<br/>
/// Also, Oracle violates SQL-92 by not supporting explicit ON UPDATE clauses
/// </summary>
private string _UpdateRule(ForeignKeyConstraint.ReferentialAction onUpdate)
{
if(onUpdate == ForeignKeyConstraint.ReferentialAction.NoAction)
{
return "";
}
return string.Format(
"ON UPDATE {0}",
_ReferentialAction(onUpdate)
);
}
/// <summary>
/// SQL-92, page 274<br/>
/// Note that item 9 on page 276 specifies that if ON DELETE clause is omitted, DB should act as if "NO ACTION" is implicitly stated<br/>
/// Also, Oracle violates SQL-92 by not supporting explicit ON DELETE NO ACTION clause
/// </summary>
private string _DeleteRule(ForeignKeyConstraint.ReferentialAction onDelete)
{
if(onDelete == ForeignKeyConstraint.ReferentialAction.NoAction)
{
return "";
}
return string.Format(
"ON DELETE {0}",
_ReferentialAction(onDelete)
);
}
/// <summary>
/// SQL-92, page 274
/// </summary>
private string _ReferentialTriggeredAction(ForeignKeyConstraint constraint)
{
return string.Format(
"{0} {1}",
_UpdateRule(constraint.onUpdate),
_DeleteRule(constraint.onDelete)
);
}
/// <summary>
/// SQL-92, page 274
/// </summary>
private string _ReferentialConstraintDefinition(ForeignKeyConstraint constraint)
{
return string.Format(
"foreign key({0}) references {1} {2}",
nameEscaper(constraint.column),
nameEscaper(constraint.referencedTable),
_ReferentialTriggeredAction(constraint)
);
}
private string _UniqueConstraintDefinition(UniqueConstraint constraint)
{
return string.Format(
"UNIQUE({0})",
string.Join(", ", (from column in constraint.columns select nameEscaper(column)).ToArray())
);
}
private string _CheckConstraintDefinition(CheckConstraint constraint)
{
return string.Format(
"CHECK({0})",
constraint.condition
);
}
/// <summary>
/// SQL-92, page 252
/// </summary>
private string _ConstraintNameDefinition(AbstractConstraint constraint)
{
return string.Format(
"CONSTRAINT {0}",
nameEscaper(constraint.name)
);
}
/// <summary>
/// SQL-92, page 270
/// </summary>
private string _TableConstraint(AbstractConstraint constraint)
{
return constraint.Accept<string>(_ReferentialConstraintDefinition, _UniqueConstraintDefinition, _CheckConstraintDefinition);
}
/// <summary>
/// SQL-92, page 270
/// </summary>
private string _TableConstraintDefinition(AbstractConstraint constraint)
{
return string.Format(
"{0} {1}",
_ConstraintNameDefinition(constraint),
_TableConstraint(constraint)
);
}
/// <summary>
/// SQL-92, page 291
/// </summary>
private string _AddTableConstraintDefinition(AbstractConstraint constraint)
{
return string.Format(
"ADD {0}",
_TableConstraintDefinition(constraint)
);
}
/// <summary>
/// SQL-92, page 292
/// </summary>
private string _DropTableConstraintDefinition(AbstractConstraint constraint)
{
return string.Format(
"DROP CONSTRAINT {0}",
nameEscaper(constraint.name)
);
}
/// <summary>
/// It seems that this feature is not mentioned in any SQL standart and is only implemented in Oracle and MySQL.
/// However, it is similar to other SQL language structures, so it should be implemented in SQLQueryManager, not OracleDBTraits
/// </summary>
private string _ModifyColumnDefinitionOracleStyle(ColumnDescription description)
{
return string.Format(
"modify ({0})",
_ColumnDefinition(description, ColumnSpecific.None, Specific.OracleNullExtension)
);
}
private string _TableElementList(TableDescription table) {
return string.Format(
"({0})",
string.Join(
", ",
Enumerable.Empty<string>()
.ConcatScalar(_ColumnDefinition(table.primaryKey, ColumnSpecific.PrimaryKey, Specific.None))
.Concat(from column in table.columns select _ColumnDefinition(column, ColumnSpecific.None, Specific.None))
.ToArray()
)
);
}
private string _TableDefinition(TableDescription table) {
return string.Format(
"CREATE TABLE {0} {1}",
nameEscaper(table.table),
_TableElementList(table)
);
}
private string _DropTableStatement(string table)
{
return string.Format(
"DROP TABLE {0} {1}",
nameEscaper(table), //table name
"" //drop behaviour
);
}
public string CreateColumn(ColumnDescription description)
{
return _AlterTableStatement(description.column.tableName, _AddColumnDefinition(description));
}
public string RemoveColumn(ColumnReference column)
{
return _AlterTableStatement(column.tableName, _DropColumnDefinition(column));
}
public string ModifyColumnOracleStyle(ColumnDescription description)
{
return _AlterTableStatement(description.column.tableName, _ModifyColumnDefinitionOracleStyle(description));
}
public string CreateConstraint(AbstractConstraint constraint)
{
return _AlterTableStatement(constraint.table, _AddTableConstraintDefinition(constraint));
}
public string DropConstraint(AbstractConstraint constraint)
{
return _AlterTableStatement(constraint.table, _DropTableConstraintDefinition(constraint));
}
public string CreateTable(TableDescription table) {
return _TableDefinition(table);
}
public string DropTable(string table) {
return _DropTableStatement(table);
}
}
}

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Patcher.DB
{
class StoredProcedureBody
{
public readonly string declarations;
public readonly string body;
public StoredProcedureBody(string declarations, string body)
{
this.declarations = declarations;
this.body = body;
}
}
}

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Patcher.DB
{
class StoredProcedureReference
{
public enum ParameterDirection
{
In,
Out,
}
public class ParameterDescription
{
public readonly string name;
public readonly ParameterDirection direction;
public readonly string type;
public ParameterDescription(string name, ParameterDirection direction, string type)
{
this.name = name;
this.direction = direction;
this.type = type;
}
}
public readonly string packageName;
public readonly string procedureName;
public readonly ParameterDescription[] parameters;
public StoredProcedureReference(string packageName, string procedureName, params ParameterDescription[] parameters)
{
this.packageName = packageName;
this.procedureName = procedureName;
this.parameters = parameters;
}
public StoredProcedureReference(string packageName, string procedureName, IEnumerable<ParameterDescription> parameters)
: this(packageName, procedureName, parameters.ToArray())
{
}
}
}

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Patcher.DB
{
class TableDescription
{
public readonly string table;
public readonly ColumnDescription primaryKey;
public readonly ColumnDescription[] columns;
public TableDescription(string table, ColumnDescription primaryKey, params ColumnDescription[] columns) {
this.table = table;
this.primaryKey = primaryKey;
this.columns = columns;
}
public TableDescription(string table, ColumnDescription primaryKey, IEnumerable<ColumnDescription> columns)
: this(table, primaryKey, columns.ToArray()) {
}
}
}

@ -0,0 +1,273 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.IO;
using System.Linq;
using System.Text;
namespace Patcher.DB
{
class Transaction : IDisposable
{
private DbConnection connection;
private DbTransaction transaction;
private readonly IDBTraits traits;
private bool isClosed;
public Transaction(IDBTraits traits, string connectionString)
{
this.traits = traits;
DbConnection _connection = null;
DbTransaction _transaction = null;
try
{
_connection = traits.CreateConnection(connectionString);
_transaction = _connection.BeginTransaction(IsolationLevel.Serializable);
this.connection = _connection;
this.transaction = _transaction;
} catch(Exception)
{
if(_transaction != null)
{
_transaction.Dispose();
this.transaction = null;
}
if(_connection != null)
{
_connection.Dispose();
this.connection = null;
}
throw;
}
}
private void CheckOpened()
{
if(this.isClosed) throw new ApplicationException("Connection is already closed");
}
private DbCommand CreateTextCommand()
{
this.CheckOpened();
DbCommand command = this.connection.CreateCommand();
try
{
command.Transaction = this.transaction;
command.CommandType = CommandType.Text;
return command;
} catch(Exception)
{
command.Dispose();
throw;
}
}
public string MarkParam(string paramName)
{
return this.traits.MarkParam(paramName);
}
public string EscapeName(string name)
{
return this.traits.EscapeName(name);
}
public void Commit()
{
this.CheckOpened();
this.isClosed = true;
this.transaction.Commit();
this.transaction = null;
this.connection.Close();
this.connection = null;
}
public void Dispose()
{
this.isClosed = true;
if(this.transaction != null)
{
try
{
this.transaction.Rollback();
this.transaction.Dispose();
} catch(SystemException)
{
throw;
} catch(Exception e)
{
Console.WriteLine(e.ToString());
}
this.transaction = null;
}
if(this.connection != null)
{
try
{
if(this.connection.State == ConnectionState.Open)
{
this.connection.Close();
}
this.connection.Dispose();
} catch(SystemException)
{
throw;
} catch(Exception e) //Dispose should not throw exceptions
{
Console.WriteLine(e.ToString());
}
this.connection = null;
}
}
private static DbType DetectParameterType(object value)
{
if(value is string)
{
return DbType.String;
}
if((value is decimal) || (value is int) || (value is long) || (value is short))
{
return DbType.Decimal;
}
if((value is float) || (value is double))
{
return DbType.Double;
}
if(value is DateTime)
{
return DbType.DateTime;
}
return DbType.Object;
}
private DbCommand CreateCommand(string commandText, Dictionary<string, object> parameters)
{
DbCommand command = this.CreateTextCommand();
command.CommandText = commandText;
foreach(var kvp in parameters)
{
DbParameter parameter = command.CreateParameter();
parameter.DbType = DetectParameterType(kvp.Value);
parameter.ParameterName = kvp.Key;
parameter.Value = kvp.Value;
command.Parameters.Add(parameter);
}
return command;
}
public IEnumerable<Dictionary<string, string>> ExecuteReader(string commandText, Dictionary<string, object> parameters)
{
using(DbCommand command = this.CreateCommand(commandText, parameters))
{
using(DbDataReader reader = command.ExecuteReader())
{
//Console.WriteLine(String.Join(";", (from i in Enumerable.Range(0, reader.FieldCount) select i + ":" + reader.GetName(i)).ToArray()));
while(reader.Read())
{
yield return (from i in Enumerable.Range(0, reader.FieldCount) select new KeyValuePair<string, string>(reader.GetName(i), reader.GetValue(i).ToString())).ToDictionary();
}
}
}
}
public IEnumerable<Dictionary<string, string>> ExecuteReader(string commandText)
{
return this.ExecuteReader(commandText, new Dictionary<string, object>());
}
public int ExecuteNonQuery(string commandText, Dictionary<string, object> parameters)
{
using(DbCommand command = this.CreateCommand(commandText, parameters))
{
return command.ExecuteNonQuery();
}
}
public int ExecuteNonQuery(string commandText)
{
return this.ExecuteNonQuery(commandText, new Dictionary<string, object>());
}
//SELECT LINE, TEXT FROM ALL_SOURCE WHERE TYPE = 'PACKAGE BODY' AND NAME = '' AND OWNER = (SELECT USER FROM DUAL) for update
public StoredProcedureBody GetStoredProcedureBody(StoredProcedureReference procedure)
{
return this.traits.GetStoredProcedureBody(this.CreateTextCommand, procedure);
}
public void ReplaceStoredProcedureBody(StoredProcedureReference procedure, StoredProcedureBody newBody)
{
this.traits.ReplaceStoredProcedureBody(this.CreateTextCommand, procedure, newBody);
}
public void RemoveStoredProcedure(StoredProcedureReference procedure)
{
this.traits.RemoveStoredProcedure(this.CreateTextCommand, procedure);
}
public void CreateStoredProcedure(StoredProcedureReference procedure, StoredProcedureBody body)
{
this.traits.CreateStoredProcedure(this.CreateTextCommand, procedure, body);
}
public ColumnOptions GetColumnOptions(ColumnReference column)
{
return this.traits.GetColumnOptions(this.CreateTextCommand, column);
}
public void RemoveColumn(ColumnReference column)
{
this.traits.RemoveColumn(this.CreateTextCommand, column);
}
public void CreateColumn(ColumnDescription description)
{
this.traits.CreateColumn(this.CreateTextCommand, description);
}
public void ModifyColumn(ColumnDescription description)
{
this.traits.ModifyColumn(this.CreateTextCommand, description);
}
public void CreateConstraint(AbstractConstraint constraint)
{
this.traits.CreateConstraint(this.CreateTextCommand, constraint);
}
public void RemoveConstraint(AbstractConstraint constraint)
{
this.traits.RemoveConstraint(this.CreateTextCommand, constraint);
}
public void CreateTable(TableDescription table)
{
this.traits.CreateTable(this.CreateTextCommand, table);
}
public void RemoveTable(TableDescription table)
{
this.traits.RemoveTable(this.CreateTextCommand, table);
}
public string GetViewBody(string viewName)
{
return this.traits.GetViewBody(this.CreateTextCommand, viewName);
}
public void CreateView(string viewName, string body)
{
this.traits.CreateView(this.CreateTextCommand, viewName, body);
}
public void RemoveView(string viewName)
{
this.traits.RemoveView(this.CreateTextCommand, viewName);
}
}
}

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Patcher.DB
{
static class TransactionFactory
{
private static Transaction Create(IDBTraits DbDriver, string ConnectionString)
{
return new Transaction(DbDriver, ConnectionString);
}
public static Transaction Create(Context context)
{
return Create(context.DbDriver, context.config.ConnectionString);
}
}
}

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Patcher.DB
{
class UniqueConstraint : AbstractConstraint
{
public override void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
public override T Accept<T>(IVisitor<T> visitor)
{
return visitor.Visit(this);
}
public readonly HashSet<string> columns;
public UniqueConstraint(string table, string name, HashSet<string> columns)
: base(table, name)
{
this.columns = columns;
}
}
}

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
abstract class AbstractColumnCommand : AbstractPersistentCommand
{
protected readonly ColumnReference column;
protected AbstractColumnCommand(int num, XElement inner) : base(num)
{
this.column = XMLParser.ParseColumnReference(inner);
}
}
}

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
abstract class AbstractColumnCommandWithOptions : AbstractColumnCommand
{
protected readonly ColumnOptions options;
protected ColumnDescription description
{
get
{
return new ColumnDescription(this.column, this.options);
}
}
protected AbstractColumnCommandWithOptions(int num, XElement inner) : base(num, inner)
{
this.options = XMLParser.ParseColumnOptions(inner);
}
}
}

@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
abstract class AbstractCommand
{
private static readonly Dictionary<string, Func<int, XElement, AbstractCommand>> creators = new Dictionary<string, Func<int, XElement, AbstractCommand>>
{
{ "sql", SqlCommand.CreateSpecific },
{ "createProcedure", CreateStoredProcedureCommand.CreateSpecific },
{ "changeProcedure", ChangeStoredProcedureCommand.CreateSpecific },
{ "removeProcedure", RemoveStoredProcedureCommand.CreateSpecific },
{ "createColumn", CreateColumnCommand.CreateSpecific },
{ "removeColumn", RemoveColumnCommand.CreateSpecific },
{ "modifyColumn", ModifyColumnCommand.CreateSpecific },
{ "createConstraint", CreateConstraintCommand.CreateSpecific },
{ "removeConstraint", RemoveConstraintCommand.CreateSpecific },
{ "createTable", CreateTableCommand.CreateSpecific },
{ "createView", CreateViewCommand.CreateSpecific },
{ "removeView", RemoveViewCommand.CreateSpecific },
};
public readonly int num;
protected AbstractCommand(int num)
{
this.num = num;
}
public static AbstractCommand Create(XElement data)
{
int num = data.Attribute("num") != null ? int.Parse(data.Attribute("num").Value) : 0;
XElement inner = data.Elements().Single();
return creators[inner.Name.ToString()](num, inner);
}
public abstract IEnumerable<XElement> Apply(Transaction transaction);
public abstract void Rollback(Transaction transaction, XElement commandRollbackInfo);
protected void Execute(string sql, Transaction transaction)
{
try
{
transaction.ExecuteNonQuery(sql);
} catch(Exception e)
{
throw new ApplicationException("Error while executing query <" + sql + ">", e);
}
}
}
}

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
abstract class AbstractConstraintCommand : AbstractPersistentCommand
{
protected readonly AbstractConstraint constraint;
protected AbstractConstraintCommand(int num, XElement inner) : base(num)
{
this.constraint = XMLParser.ParseConstraint(inner);
}
}
}

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
abstract class AbstractPersistentCommand : AbstractCommand
{
protected AbstractPersistentCommand(int num) : base(num)
{
}
public sealed override IEnumerable<XElement> Apply(Transaction transaction)
{
throw new ApplicationException("Cannot apply persistent command");
}
public abstract IEnumerable<XElement> Apply(Transaction transaction, bool forceIntegrity);
}
}

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
abstract class AbstractStoredProcedureCommand : AbstractPersistentCommand
{
private static readonly Dictionary<string, StoredProcedureReference.ParameterDirection> directions = new Dictionary<string, StoredProcedureReference.ParameterDirection>
{
{ "in", StoredProcedureReference.ParameterDirection.In },
{ "out", StoredProcedureReference.ParameterDirection.Out },
};
protected readonly StoredProcedureReference procedure;
protected AbstractStoredProcedureCommand(int num, XElement inner) : base(num)
{
XElement description = inner.Element("procedure");
this.procedure = new StoredProcedureReference(
description.Element("package").Value,
description.Element("name").Value,
from elem in description.Elements("parameter") select new StoredProcedureReference.ParameterDescription(
elem.Element("name").Value,
directions[elem.Element("direction").Value],
elem.Element("type").Value
)
);
}
}
}

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
abstract class AbstractViewCommand : AbstractPersistentCommand
{
protected readonly string viewName;
protected AbstractViewCommand(int num, XElement inner) : base(num)
{
this.viewName = inner.Element("view").Value;
}
}
}

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
class ChangeStoredProcedureCommand : AbstractStoredProcedureCommand
{
private readonly StoredProcedureBody body;
private ChangeStoredProcedureCommand(int num, XElement inner) : base(num, inner)
{
this.body = new StoredProcedureBody(
inner.Element("declarations") != null ? inner.Element("declarations").Value : "",
inner.Element("body").Value
);
}
public static ChangeStoredProcedureCommand CreateSpecific(int num, XElement inner)
{
return new ChangeStoredProcedureCommand(num, inner);
}
public override IEnumerable<XElement> Apply(Transaction transaction, bool forceIntegrity)
{
if(!forceIntegrity)
{
throw new NotImplementedException("Safe stored procedure change is not implemented yet");
}
StoredProcedureBody oldBody = transaction.GetStoredProcedureBody(this.procedure);
/*Console.WriteLine();
Console.WriteLine("===OLD DECLARATIONS===");
Console.WriteLine(oldBody.declarations);
Console.WriteLine("===OLD BODY===");
Console.WriteLine(oldBody.body);
Console.WriteLine("===END===");*/
transaction.ReplaceStoredProcedureBody(this.procedure, this.body);
return new[]
{
new XElement("oldDeclarations", oldBody.declarations),
new XElement("oldBody", oldBody.body),
};
}
public override void Rollback(Transaction transaction, XElement commandRollbackInfo)
{
transaction.ReplaceStoredProcedureBody(
this.procedure,
new StoredProcedureBody(
commandRollbackInfo.Element("oldDeclarations").Value,
commandRollbackInfo.Element("oldBody").Value
)
);
}
}
}

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
class CreateColumnCommand : AbstractColumnCommandWithOptions
{
private CreateColumnCommand(int num, XElement inner) : base(num, inner)
{
}
public static CreateColumnCommand CreateSpecific(int num, XElement inner)
{
return new CreateColumnCommand(num, inner);
}
public override IEnumerable<XElement> Apply(Transaction transaction, bool forceIntegrity)
{
if(!forceIntegrity)
{
throw new NotImplementedException("Safe stored procedure creation is not implemented yet");
}
transaction.CreateColumn(this.description);
return Enumerable.Empty<XElement>();
}
public override void Rollback(Transaction transaction, XElement commandRollbackInfo)
{
transaction.RemoveColumn(this.column);
}
}
}

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
class CreateConstraintCommand : AbstractConstraintCommand
{
private CreateConstraintCommand(int num, XElement inner) : base(num, inner)
{
}
public static CreateConstraintCommand CreateSpecific(int num, XElement inner)
{
return new CreateConstraintCommand(num, inner);
}
public override IEnumerable<XElement> Apply(Transaction transaction, bool forceIntegrity)
{
if(!forceIntegrity)
{
throw new NotImplementedException("Safe stored procedure creation is not implemented yet");
}
transaction.CreateConstraint(this.constraint);
return Enumerable.Empty<XElement>();
}
public override void Rollback(Transaction transaction, XElement commandRollbackInfo)
{
transaction.RemoveConstraint(this.constraint);
}
}
}

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
class CreateStoredProcedureCommand : AbstractStoredProcedureCommand
{
private readonly StoredProcedureBody body;
private CreateStoredProcedureCommand(int num, XElement inner) : base(num, inner)
{
this.body = new StoredProcedureBody(
inner.Element("declarations") != null ? inner.Element("declarations").Value : "",
inner.Element("body").Value
);
}
public static CreateStoredProcedureCommand CreateSpecific(int num, XElement inner)
{
return new CreateStoredProcedureCommand(num, inner);
}
public override IEnumerable<XElement> Apply(Transaction transaction, bool forceIntegrity)
{
if(!forceIntegrity)
{
throw new NotImplementedException("Safe stored procedure creation is not implemented yet");
}
transaction.CreateStoredProcedure(this.procedure, this.body);
return Enumerable.Empty<XElement>();
}
public override void Rollback(Transaction transaction, XElement commandRollbackInfo)
{
transaction.RemoveStoredProcedure(this.procedure);
}
}
}

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
class CreateTableCommand : AbstractPersistentCommand
{
protected readonly TableDescription table;
protected CreateTableCommand(int num, XElement inner) : base(num)
{
this.table = XMLParser.ParseTableDescription(inner);
}
public static CreateTableCommand CreateSpecific(int num, XElement inner)
{
return new CreateTableCommand(num, inner);
}
public override IEnumerable<XElement> Apply(Transaction transaction, bool forceIntegrity)
{
if(!forceIntegrity)
{
throw new NotImplementedException("Safe stored procedure creation is not implemented yet");
}
transaction.CreateTable(this.table);
return Enumerable.Empty<XElement>();
}
public override void Rollback(Transaction transaction, XElement commandRollbackInfo)
{
transaction.RemoveTable(this.table);
}
}
}

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
class CreateViewCommand : AbstractViewCommand
{
private readonly string body;
private CreateViewCommand(int num, XElement inner) : base(num, inner)
{
this.body = inner.Element("body").Value;
}
public static CreateViewCommand CreateSpecific(int num, XElement inner)
{
return new CreateViewCommand(num, inner);
}
public override IEnumerable<XElement> Apply(Transaction transaction, bool forceIntegrity)
{
if(!forceIntegrity)
{
throw new NotImplementedException("Safe stored procedure creation is not implemented yet");
}
transaction.CreateView(this.viewName, this.body);
return Enumerable.Empty<XElement>();
}
public override void Rollback(Transaction transaction, XElement commandRollbackInfo)
{
transaction.RemoveView(this.viewName);
}
}
}

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
class ModifyColumnCommand : AbstractColumnCommandWithOptions
{
private ModifyColumnCommand(int num, XElement inner) : base(num, inner)
{
}
public static ModifyColumnCommand CreateSpecific(int num, XElement inner)
{
return new ModifyColumnCommand(num, inner);
}
public override IEnumerable<XElement> Apply(Transaction transaction, bool forceIntegrity)
{
if(!forceIntegrity)
{
throw new NotImplementedException("Safe stored procedure creation is not implemented yet");
}
var oldOptions = transaction.GetColumnOptions(column);
transaction.ModifyColumn(this.description);
return new[]
{
new XElement("type", oldOptions.type),
oldOptions.defaultValue != null ? new XElement("defaultValue", oldOptions.defaultValue) : null,
oldOptions.isNotNull ? new XElement("isNotNull") : null
};
}
public override void Rollback(Transaction transaction, XElement commandRollbackInfo)
{
transaction.ModifyColumn(
new ColumnDescription(
this.column,
new ColumnOptions(
commandRollbackInfo.Element("type").Value,
commandRollbackInfo.Element("defaultValue") != null ? commandRollbackInfo.Element("defaultValue").Value : null,
commandRollbackInfo.Element("isNotNull") != null
)
)
);
}
}
}

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
class RemoveColumnCommand : AbstractColumnCommand
{
private RemoveColumnCommand(int num, XElement inner) : base(num, inner)
{
}
public static RemoveColumnCommand CreateSpecific(int num, XElement inner)
{
return new RemoveColumnCommand(num, inner);
}
public override IEnumerable<XElement> Apply(Transaction transaction, bool forceIntegrity)
{
if(!forceIntegrity)
{
throw new NotImplementedException("Safe stored procedure creation is not implemented yet");
}
var options = transaction.GetColumnOptions(column);
/*Console.WriteLine();
Console.Write(options.type);
if(options.defaultValue != null)
{
Console.Write(" DEFAULT " + options.defaultValue);
}
if(options.isNotNull)
{
Console.Write(" NOT NULL");
}
Console.WriteLine();
Console.WriteLine("'" + options.defaultValue + "'");*/
transaction.RemoveColumn(column);
return new[]
{
new XElement("type", options.type),
options.defaultValue != null ? new XElement("defaultValue", options.defaultValue) : null,
options.isNotNull ? new XElement("isNotNull") : null
};
}
public override void Rollback(Transaction transaction, XElement commandRollbackInfo)
{
transaction.CreateColumn(
new ColumnDescription(
this.column,
new ColumnOptions(
commandRollbackInfo.Element("type").Value,
commandRollbackInfo.Element("defaultValue") != null ? commandRollbackInfo.Element("defaultValue").Value : null,
commandRollbackInfo.Element("isNotNull") != null
)
)
);
}
}
}

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
class RemoveConstraintCommand : AbstractConstraintCommand
{
private RemoveConstraintCommand(int num, XElement inner) : base(num, inner)
{
}
public static RemoveConstraintCommand CreateSpecific(int num, XElement inner)
{
return new RemoveConstraintCommand(num, inner);
}
public override IEnumerable<XElement> Apply(Transaction transaction, bool forceIntegrity)
{
if(!forceIntegrity)
{
throw new NotImplementedException("Safe stored procedure creation is not implemented yet");
}
transaction.RemoveConstraint(this.constraint);
return Enumerable.Empty<XElement>();
}
public override void Rollback(Transaction transaction, XElement commandRollbackInfo)
{
transaction.CreateConstraint(this.constraint);
}
}
}

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
class RemoveStoredProcedureCommand : AbstractStoredProcedureCommand
{
private RemoveStoredProcedureCommand(int num, XElement inner) : base(num, inner)
{
}
public static RemoveStoredProcedureCommand CreateSpecific(int num, XElement inner)
{
return new RemoveStoredProcedureCommand(num, inner);
}
public override IEnumerable<XElement> Apply(Transaction transaction, bool forceIntegrity)
{
if(!forceIntegrity)
{
throw new NotImplementedException("Safe stored procedure removal is not implemented yet");
}
StoredProcedureBody body = transaction.GetStoredProcedureBody(this.procedure);
transaction.RemoveStoredProcedure(this.procedure);
return new[]
{
new XElement("declarations", body.declarations),
new XElement("body", body.body),
};
}
public override void Rollback(Transaction transaction, XElement commandRollbackInfo)
{
transaction.CreateStoredProcedure(
this.procedure,
new StoredProcedureBody(
commandRollbackInfo.Element("declarations").Value,
commandRollbackInfo.Element("body").Value
)
);
}
}
}

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
class RemoveViewCommand : AbstractViewCommand
{
private RemoveViewCommand(int num, XElement inner) : base(num, inner)
{
}
public static RemoveViewCommand CreateSpecific(int num, XElement inner)
{
return new RemoveViewCommand(num, inner);
}
public override IEnumerable<XElement> Apply(Transaction transaction, bool forceIntegrity)
{
if(!forceIntegrity)
{
throw new NotImplementedException("Safe stored procedure creation is not implemented yet");
}
string body = transaction.GetViewBody(this.viewName);
transaction.RemoveView(this.viewName);
return new XElement[] {
new XElement("body", body),
};
}
public override void Rollback(Transaction transaction, XElement commandRollbackInfo)
{
transaction.CreateView(this.viewName, commandRollbackInfo.Element("body").Value);
}
}
}

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data.Command
{
class SqlCommand : AbstractCommand
{
private readonly string[] installQueries;
private readonly string[] uninstallQueries;
private SqlCommand(int num, XElement inner) : base(num)
{
this.installQueries = (from elem in inner.Element("installSql").Elements() select elem.Value).ToArray();
this.uninstallQueries = (from elem in inner.Elements("uninstallSql").Elements() select elem.Value).ToArray();
}
public static SqlCommand CreateSpecific(int num, XElement inner)
{
return new SqlCommand(num, inner);
}
public override IEnumerable<XElement> Apply(Transaction transaction)
{
for(int i=0; i<this.installQueries.Length; i++)
{
this.Execute(this.installQueries[i], transaction);
}
return Enumerable.Empty<XElement>();
}
public override void Rollback(Transaction transaction, XElement commandRollbackInfo)
{
for(int i=0; i<this.uninstallQueries.Length; i++)
{
this.Execute(this.uninstallQueries[i], transaction);
}
}
}
}

@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using Patcher.Data.Command;
using Patcher.DB;
namespace Patcher.Data.Patch
{
abstract class AbstractPatch
{
private static class Checker
{
private static XmlSchemaSet LoadXsd()
{
XmlSchemaSet schemaSet = new XmlSchemaSet();
using(Stream xsdStream = Resources.ResourcesManager.GetResource("IPatch.xsd"))
{
using(XmlReader xsdReader = XmlReader.Create(xsdStream))
{
schemaSet.Add("", xsdReader);
}
}
schemaSet.Compile();
return schemaSet;
}
private static readonly XmlSchemaSet Xsd = LoadXsd();
public static void Check(XDocument document)
{
document.Validate(Xsd, (sender, e) => { throw new ApplicationException("Intermediate XML validation failed: " + e.Message, e.Exception); });
}
}
public static AbstractPatch LoadById(PatchId id, Context context)
{
return Cache<KeyValuePair<PatchId, Context>, AbstractPatch>.instance.GetValue(new KeyValuePair<PatchId, Context>(id, context), () => _LoadById(id, context));
}
private static AbstractPatch _LoadById(PatchId id, Context context)
{
XDocument data;
using(Stream xmlStream = context.loadPatch(id))
{
using(XmlReader reader = XmlReader.Create(xmlStream))
{
data = XDocument.Load(reader);
}
}
Checker.Check(data);
XElement version = data.Root.Element("version");
string number = version.Element("number").Value;
string author = version.Element("author").Value;
if((number != id.version.ToString()) || (author != id.author))
{
throw new ApplicationException(string.Format("Versions mismatch on patch #{0} from {1} (got #{2} from {3})", id.version, id.author, number, author));
}
HashSet<string> restrictToEnvironments;
if(data.Root.Element("restrictToEnvironments") != null)
{
restrictToEnvironments = new HashSet<string>(from elem in data.Root.Element("restrictToEnvironments").Elements() select elem.Value);
} else
{
restrictToEnvironments = new HashSet<string>();
}
XElement commandSet;
bool isStrictCommandSet;
{
XElement strictCommandSet = data.Root.Element("strictCommandSet");
XElement looseCommandSet = data.Root.Element("looseCommandSet");
if (strictCommandSet != null && looseCommandSet == null)
{
commandSet = strictCommandSet;
isStrictCommandSet = true;
} else if (strictCommandSet == null && looseCommandSet != null)
{
commandSet = looseCommandSet;
isStrictCommandSet = false;
} else if (strictCommandSet != null && looseCommandSet != null)
{
throw new ApplicationException("Malformed XML: both strictCommandSet and looseCommandSet found");
} else
{
throw new ApplicationException("Malformed XML: no CommandSet found");
}
}
var commands = (from elem in commandSet.Elements() select AbstractCommand.Create(elem)).ToArray();
if(isStrictCommandSet)
{
var isPersistent = commands.OfType<AbstractPersistentCommand>().Any();
if (isPersistent)
{
if (commands.Length != 1)
{
throw new ApplicationException("More than one persistent command");
}
if(context.DbDriver.IsDDLTransactional)
{
return new AtomicPatch(commands, true, restrictToEnvironments, context);
} else
{
return new PersistentPatch(commands[0], restrictToEnvironments, context);
}
}
else
{
return new AtomicPatch(commands, true, restrictToEnvironments, context);
}
} else
{
return new AtomicPatch(commands, false, restrictToEnvironments, context);
}
}
private readonly HashSet<string> restrictToEnvironments;
protected readonly Context context;
protected AbstractPatch(HashSet<string> restrictToEnvironments, Context context)
{
this.restrictToEnvironments = restrictToEnvironments;
this.context = context;
}
public abstract XDocument Apply(Transaction transaction);
public abstract void Rollback(Transaction transaction, XDocument rollbackInfo);
public bool DoesSupportEnvironment(string environmentName)
{
if(restrictToEnvironments.Any())
{
return restrictToEnvironments.Contains(environmentName);
} else
{
return true;
}
}
}
}

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using Patcher.Data.Command;
using Patcher.DB;
namespace Patcher.Data.Patch
{
class AtomicPatch : AbstractPatch
{
private readonly AbstractCommand[] commands;
private readonly bool isStrictCommandSet;
public AtomicPatch(AbstractCommand[] commands, bool isStrictCommandSet, HashSet<string> restrictToEnvironments, Context context) : base(restrictToEnvironments, context)
{
this.commands = commands;
this.isStrictCommandSet = isStrictCommandSet;
}
private void CheckDbDriver()
{
if(!this.isStrictCommandSet)
{
if(!context.DbDriver.IsDDLTransactional)
{
throw new ApplicationException("You should use strictCommandSet when working with non-DDL-transactional DB");
}
}
}
public override XDocument Apply(Transaction transaction)
{
this.CheckDbDriver();
List<XElement> rollbackInfos = new List<XElement>();
for(int i=0; i<this.commands.Length; i++)
{
IEnumerable<XElement> commandRollbackInfoContent;
if (commands[i] is AbstractPersistentCommand)
{
commandRollbackInfoContent = ((AbstractPersistentCommand)commands[i]).Apply(transaction, true);
} else
{
commandRollbackInfoContent = commands[i].Apply(transaction);
}
rollbackInfos.Add(new XElement("command", new XAttribute("num", i), commandRollbackInfoContent));
}
return new XDocument(new XElement("rollbackInfo", rollbackInfos.ToArray()));
}
public override void Rollback(Transaction transaction, XDocument rollbackInfo)
{
this.CheckDbDriver();
for(int i=this.commands.Length-1; i>=0; i--)
{
commands[i].Rollback(transaction, (from commandRollbackInfo in rollbackInfo.Root.Elements("command") where commandRollbackInfo.Attribute("num").Value == i.ToString() select commandRollbackInfo).Single());
}
}
}
}

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Patcher.Data.Patch
{
public class PatchId : IComparable<PatchId>, IComparable, IFormattable
{
public readonly int version;
public readonly string author;
public PatchId(int version, string author)
{
this.version = version;
this.author = author;
}
private int _CompareTo(PatchId other)
{
int result = this.version.CompareTo(other.version);
if(result == 0)
{
result = this.author.CompareTo(other.author);
}
return result;
}
int IComparable<PatchId>.CompareTo(PatchId other)
{
return this._CompareTo(other);
}
int IComparable.CompareTo(object obj)
{
if(obj is PatchId)
{
return this._CompareTo((PatchId)obj);
}
throw new ApplicationException("Cannot compare PatchId to " + obj.GetType());
}
public string ToString(string format, IFormatProvider formatProvider)
{
if(String.IsNullOrEmpty(format)) return this.ToString();
switch(format.ToLower())
{
case "version":
return this.version.ToString(formatProvider);
case "author":
return this.author.ToString(formatProvider);
default:
throw new ApplicationException(String.Format("Unknown format {0}", format));
}
}
}
}

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using Patcher.Data.Command;
using Patcher.DB;
namespace Patcher.Data.Patch
{
class PersistentPatch : AbstractPatch
{
private readonly AbstractCommand command;
public PersistentPatch(AbstractCommand command, HashSet<string> restrictToEnvironments, Context context) : base(restrictToEnvironments, context)
{
this.command = command;
throw new NotImplementedException("Persistent patches are not implemented yet");
}
public override XDocument Apply(Transaction transaction)
{
throw new NotImplementedException("Persistent patches are not implemented yet");
}
public override void Rollback(Transaction transaction, XDocument rollbackInfo)
{
throw new NotImplementedException("Persistent patches are not implemented yet");
}
}
}

@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Patcher.DB;
namespace Patcher.Data
{
static class XMLParser
{
private static ForeignKeyConstraint.ReferentialAction ParseReferentialAction(XElement element)
{
if(element == null) {
return ForeignKeyConstraint.ReferentialAction.NoAction;
}
switch(element.Value)
{
case "noAction":
return ForeignKeyConstraint.ReferentialAction.NoAction;
case "cascade":
return ForeignKeyConstraint.ReferentialAction.Cascade;
case "setNull":
return ForeignKeyConstraint.ReferentialAction.SetNull;
case "setDefault":
return ForeignKeyConstraint.ReferentialAction.SetDefault;
default:
throw new FormattableException("Unknown referential action {0}", element.Value);
}
}
public static AbstractConstraint ParseConstraint(XElement element)
{
XElement[] children = element.Elements().ToArray();
if(children.Length != 3)
{
throw new FormattableException("Wrong children count");
}
if(children[0].Name != "table")
{
throw new FormattableException("Expected element name {0}, got {1}", "table", children[0].Name);
}
string table = children[0].Value;
if(children[1].Name != "constraintName")
{
throw new FormattableException("Expected element name {0}, got {1}", "constraintName", children[1].Name);
}
string constraintName = children[1].Value;
XElement specific = children[2];
switch(specific.Name.ToString())
{
case "foreignKey":
return new ForeignKeyConstraint(
table,
constraintName,
specific.Element("column").Value,
specific.Element("referencedTable").Value,
ParseReferentialAction(specific.Element("onUpdate")),
ParseReferentialAction(specific.Element("onDelete"))
);
case "unique":
return new UniqueConstraint(table, constraintName, new HashSet<string>(from elem in specific.Elements("column") select elem.Value));
case "check":
return new CheckConstraint(table, constraintName, specific.Element("condition").Value);
default:
throw new FormattableException("Unknown constraint type {0}", children[2].Name);
}
}
public static ColumnOptions ParseColumnOptions(XElement element)
{
return new ColumnOptions(
element.Element("type").Value,
element.Element("defaultValue") != null ? element.Element("defaultValue").Value : null,
element.Element("isNotNull") != null
);
}
public static ColumnReference ParseColumnReference(XElement element) {
return new ColumnReference(
element.Element("table").Value,
element.Element("column").Value
);
}
private static ColumnDescription ParseColumnDescription(XElement element, string table)
{
return new ColumnDescription(
new ColumnReference(
table,
element.Element("column").Value
),
ParseColumnOptions(element)
);
}
public static TableDescription ParseTableDescription(XElement element) {
string table = element.Element("table").Value;
return new TableDescription(
table,
ParseColumnDescription(element.Element("primaryKey"), table),
from elem in element.Elements("column") select ParseColumnDescription(elem, table)
);
}
}
}

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Patcher
{
class FormattableException : ApplicationException
{
public FormattableException(string message, params object[] data)
: base(string.Format(message, data))
{
}
}
}

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Patcher
{
public class ResourceNotFoundException : ApplicationException
{
public ResourceNotFoundException(string name)
: base("Cannot read resource " + name)
{
}
}
}

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Patcher
{
public class XmlResourceNotFoundException : ApplicationException
{
public XmlResourceNotFoundException(Uri absoluteUri)
: base("XmlResource with uri '" + absoluteUri.ToString() + "' could not be found")
{
}
}
}

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Patcher.Data.Patch;
namespace Patcher
{
public interface IConfig
{
string EnvironmentName
{
get;
}
string DbDriverName
{
get;
}
string ConnectionString
{
get;
}
string PatchesTable
{
get;
}
}
}

@ -0,0 +1,161 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{78F407CF-416D-4D1F-971C-9A51FEA69B3A}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Patcher</RootNamespace>
<AssemblyName>Patcher</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data.OracleClient" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Context.cs" />
<Compile Include="Data\Command\AbstractColumnCommand.cs" />
<Compile Include="Data\Command\AbstractColumnCommandWithOptions.cs" />
<Compile Include="Data\Command\AbstractCommand.cs" />
<Compile Include="Data\Command\AbstractConstraintCommand.cs" />
<Compile Include="Data\Command\AbstractPersistentCommand.cs" />
<Compile Include="Data\Command\AbstractStoredProcedureCommand.cs" />
<Compile Include="Data\Command\AbstractViewCommand.cs" />
<Compile Include="Data\Command\ChangeStoredProcedureCommand.cs" />
<Compile Include="Data\Command\CreateColumnCommand.cs" />
<Compile Include="Data\Command\CreateConstraintCommand.cs" />
<Compile Include="Data\Command\CreateStoredProcedureCommand.cs" />
<Compile Include="Data\Command\CreateTableCommand.cs" />
<Compile Include="Data\Command\CreateViewCommand.cs" />
<Compile Include="Data\Command\ModifyColumnCommand.cs" />
<Compile Include="Data\Command\RemoveColumnCommand.cs" />
<Compile Include="Data\Command\RemoveConstraintCommand.cs" />
<Compile Include="Data\Command\RemoveStoredProcedureCommand.cs" />
<Compile Include="Data\Command\RemoveViewCommand.cs" />
<Compile Include="Data\Command\SqlCommand.cs" />
<Compile Include="Data\Patch\AbstractPatch.cs" />
<Compile Include="Data\Patch\AtomicPatch.cs" />
<Compile Include="Data\Patch\PatchId.cs" />
<Compile Include="Data\Patch\PersistentPatch.cs" />
<Compile Include="Data\XMLParser.cs" />
<Compile Include="DB\AbstractConstraint.cs" />
<Compile Include="DB\CheckConstraint.cs" />
<Compile Include="DB\ColumnDescription.cs" />
<Compile Include="DB\ColumnOptions.cs" />
<Compile Include="DB\ColumnReference.cs" />
<Compile Include="DB\ForeignKeyConstraint.cs" />
<Compile Include="DB\IDBTraits.cs" />
<Compile Include="DB\OracleDBTraits.cs" />
<Compile Include="DB\OracleFakeTransactionalDBTraits.cs" />
<Compile Include="DB\SQLQueryManager.cs" />
<Compile Include="DB\StoredProcedureBody.cs" />
<Compile Include="DB\StoredProcedureReference.cs" />
<Compile Include="DB\TableDescription.cs" />
<Compile Include="DB\Transaction.cs" />
<Compile Include="DB\TransactionFactory.cs" />
<Compile Include="DB\UniqueConstraint.cs" />
<Compile Include="Exceptions\FormattableException.cs" />
<Compile Include="Exceptions\ResourceNotFoundException.cs" />
<Compile Include="Exceptions\XmlResourceNotFoundException.cs" />
<Compile Include="ConsoleProcessor.cs" />
<Compile Include="IConfig.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Resources\ResourcesManager.cs" />
<Compile Include="ThirdParty\NConsoler.cs" />
<Compile Include="Util\Cache.cs" />
<Compile Include="Util\CultureReplacementWrapper.cs" />
<Compile Include="Util\Util.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\IPatch.xsd">
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="Resources\Patch_00000_removeview.xml" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Patch_00000_removeunique.xml" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Patch_00000_removeprocedure.xml" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Patch_00000_removeforeignkey.xml" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Patch_00000_removecolumn.xml" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Patch_00000_removecheck.xml" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Patch_00000_modifycolumn.xml" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Patch_00000_createview.xml" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Patch_00000_createunique.xml" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Patch_00000_createtable.xml" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Patch_00000_createprocedure.xml" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Patch_00000_createforeignkey.xml" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Patch_00000_createcolumn.xml" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Patch_00000_createcheck.xml">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<None Include="Resources\Patch_00000_changeprocedure.xml" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Patcher")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("inga-lovinde")]
[assembly: AssemblyProduct("Patcher")]
[assembly: AssemblyCopyright("Copyright © inga-lovinde 2011-2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("37e7a88a-9a4a-4955-af94-01115270d0d5")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

@ -0,0 +1,287 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="name">
<xs:restriction base="xs:string">
<xs:pattern value="[a-z]+(-[a-z]+)*"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="longDbName">
<xs:restriction base="xs:string">
<xs:pattern value="[a-zA-Z][a-zA-Z01-9_]+(_[a-zA-Z01-9]+)*"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="dbName">
<xs:restriction base="longDbName">
<xs:pattern value=".{2,30}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="dbType">
<xs:restriction base="xs:string">
<xs:pattern value="[a-zA-Z_][a-zA-Z_01-9]+(\([01-9]+(,[01-9]+)?\))?"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="inOrOut">
<xs:restriction base="xs:string">
<xs:enumeration value="in"/>
<xs:enumeration value="out"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="referentialAction">
<xs:restriction base="xs:string">
<xs:enumeration value="noAction"/>
<xs:enumeration value="cascade"/>
<xs:enumeration value="setNull"/>
<xs:enumeration value="setDefault"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="empty"/>
<xs:complexType name="version">
<xs:sequence>
<xs:element name="number" type="xs:nonNegativeInteger" minOccurs="1" maxOccurs="1"/>
<xs:element name="author" type="name" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="restrictToEnvironments">
<xs:sequence>
<xs:element name="name" type="name" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="parameterDescription">
<xs:sequence>
<xs:element name="name" type="dbName" minOccurs="1" maxOccurs="1"/>
<xs:element name="direction" type="inOrOut" minOccurs="1" maxOccurs="1"/>
<xs:element name="type" type="dbType" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="storedProcedureReference">
<xs:sequence>
<xs:element name="package" type="dbName" minOccurs="1" maxOccurs="1"/>
<xs:element name="name" type="dbName" minOccurs="1" maxOccurs="1"/>
<xs:element name="parameter" type="parameterDescription" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="sqlQuery">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<xs:complexType name="sqlQueries">
<xs:sequence>
<xs:element name="query" type="sqlQuery" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="procedureCommandWithBody">
<xs:sequence>
<xs:element name="procedure" type="storedProcedureReference" minOccurs="1" maxOccurs="1"/>
<xs:element name="declarations" type="sqlQuery" minOccurs="0" maxOccurs="1"/>
<xs:element name="body" type="sqlQuery" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="procedureCommandWithoutBody">
<xs:sequence>
<xs:element name="procedure" type="storedProcedureReference" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
<xs:group name="tableNameGroup">
<xs:sequence>
<xs:element name="table" type="dbName" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:group>
<xs:group name="columnNameGroup">
<xs:sequence>
<xs:element name="column" type="dbName" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:group>
<xs:group name="columnReferenceGroup">
<xs:sequence>
<xs:group ref="tableNameGroup" minOccurs="1" maxOccurs="1"/>
<xs:group ref="columnNameGroup" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:group>
<xs:group name="columnOptionsGroup">
<xs:sequence>
<xs:element name="type" type="dbType" minOccurs="1" maxOccurs="1"/>
<xs:element name="defaultValue" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="isNotNull" type="empty" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
</xs:group>
<xs:complexType name="foreignKeyConstraint">
<xs:sequence>
<xs:group ref="columnNameGroup" minOccurs="1" maxOccurs="1"/>
<xs:element name="referencedTable" type="dbName" minOccurs="1" maxOccurs="1"/>
<xs:element name="onUpdate" type="referentialAction" minOccurs="0" maxOccurs="1"/>
<xs:element name="onDelete" type="referentialAction" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="uniqueConstraint">
<xs:sequence>
<xs:group ref="columnNameGroup" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="checkConstraint">
<xs:sequence>
<xs:element name="condition" type="sqlQuery" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="constraint">
<xs:sequence>
<xs:group ref="tableNameGroup" minOccurs="1" maxOccurs="1"/>
<xs:element name="constraintName" type="dbName" minOccurs="1" maxOccurs="1"/>
<xs:choice minOccurs="1" maxOccurs="1">
<xs:element name="foreignKey" type="foreignKeyConstraint" minOccurs="1" maxOccurs="1"/>
<xs:element name="unique" type="uniqueConstraint" minOccurs="1" maxOccurs="1"/>
<xs:element name="check" type="checkConstraint" minOccurs="1" maxOccurs="1"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="columnReference">
<xs:sequence>
<xs:group ref="columnReferenceGroup" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="columnReferenceWithOptions">
<xs:sequence>
<xs:group ref="columnReferenceGroup" minOccurs="1" maxOccurs="1"/>
<xs:group ref="columnOptionsGroup" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="columnNameWithOptions">
<xs:sequence>
<xs:group ref="columnNameGroup" minOccurs="1" maxOccurs="1"/>
<xs:group ref="columnOptionsGroup" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="columnCommand">
<xs:complexContent>
<xs:extension base="columnReference"/>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="columnCommandWithOptions">
<xs:complexContent>
<xs:extension base="columnReferenceWithOptions"/>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="tableDefinition">
<xs:sequence>
<xs:group ref="tableNameGroup"/>
<xs:element name="primaryKey" type="columnNameWithOptions" minOccurs="1" maxOccurs="1"/>
<xs:element name="column" type="columnNameWithOptions" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="tableDefinitionCommand">
<xs:complexContent>
<xs:extension base="tableDefinition"/>
</xs:complexContent>
</xs:complexType>
<xs:group name="viewNameGroup">
<xs:sequence>
<xs:element name="view" type="dbName" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:group>
<xs:group name="viewDefinitionGroup">
<xs:sequence>
<xs:group ref="viewNameGroup" minOccurs="1" maxOccurs="1"/>
<xs:element name="body" type="sqlQuery" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:group>
<xs:complexType name="viewNameCommand">
<xs:sequence>
<xs:group ref="viewNameGroup" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="viewDefinitionCommand">
<xs:sequence>
<xs:group ref="viewDefinitionGroup" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="sqlCommand">
<xs:sequence>
<xs:element name="installSql" type="sqlQueries" minOccurs="1" maxOccurs="1"/>
<xs:element name="uninstallSql" type="sqlQueries" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="command">
<xs:choice>
<xs:element name="sql" type="sqlCommand" minOccurs="1" maxOccurs="1"/>
</xs:choice>
<xs:attribute name="num" type="xs:int" use="optional"/>
</xs:complexType>
<xs:complexType name="persistentCommand">
<xs:choice>
<xs:element name="createProcedure" type="procedureCommandWithBody" minOccurs="1" maxOccurs="1"/>
<xs:element name="changeProcedure" type="procedureCommandWithBody" minOccurs="1" maxOccurs="1"/>
<xs:element name="removeProcedure" type="procedureCommandWithoutBody" minOccurs="1" maxOccurs="1"/>
<xs:element name="createColumn" type="columnCommandWithOptions" minOccurs="1" maxOccurs="1"/>
<xs:element name="modifyColumn" type="columnCommandWithOptions" minOccurs="1" maxOccurs="1"/>
<xs:element name="removeColumn" type="columnCommand" minOccurs="1" maxOccurs="1"/>
<xs:element name="createConstraint" type="constraint" minOccurs="1" maxOccurs="1"/>
<xs:element name="removeConstraint" type="constraint" minOccurs="1" maxOccurs="1"/>
<xs:element name="createTable" type="tableDefinitionCommand" minOccurs="1" maxOccurs="1"/>
<xs:element name="createView" type="viewDefinitionCommand" minOccurs="1" maxOccurs="1"/>
<xs:element name="removeView" type="viewNameCommand" minOccurs="1" maxOccurs="1"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="looseCommandSet">
<xs:choice minOccurs="1" maxOccurs="unbounded">
<xs:element name="command" type="command" minOccurs="1" maxOccurs="1"/>
<xs:element name="persistentCommand" type="persistentCommand" minOccurs="1" maxOccurs="1"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="strictCommandSet">
<xs:choice minOccurs="0" maxOccurs="1">
<xs:element name="command" type="command" minOccurs="1" maxOccurs="unbounded"/>
<xs:element name="persistentCommand" type="persistentCommand" minOccurs="1" maxOccurs="1"/>
</xs:choice>
</xs:complexType>
<xs:element name="patch">
<xs:complexType>
<xs:sequence>
<xs:element name="version" type="version" minOccurs="1" maxOccurs="1"/>
<xs:element name="restrictToEnvironments" type="restrictToEnvironments" minOccurs="0" maxOccurs="1"/>
<xs:choice minOccurs="0" maxOccurs="1">
<xs:element name="looseCommandSet" type="looseCommandSet" minOccurs="1" maxOccurs="1"/>
<xs:element name="strictCommandSet" type="strictCommandSet" minOccurs="1" maxOccurs="1"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8" ?>
<patch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="IPatch.xsd">
<version>
<number>0</number>
<author>changeprocedure</author>
</version>
<strictCommandSet>
<!--persistentCommand>
<changeProcedure>
<procedure>
<package>DATA_IMPORTER</package>
<name>ENR_IMPORTER_QUEUE_GETLIST</name>
<parameter>
<name>p_cur_user</name>
<direction>in</direction>
<type>number</type>
</parameter>
<parameter>
<name>p_cur_out</name>
<direction>out</direction>
<type>sys_refcursor</type>
</parameter>
</procedure>
<declarations>
v_some_var number;
</declarations>
<body>
v_some_var := 0;
open p_cur_out for
select v_some_var from dual;
</body>
</changeProcedure>
</persistentCommand-->
</strictCommandSet>
</patch>

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<patch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="IPatch.xsd">
<version>
<number>0</number>
<author>createcheck</author>
</version>
<strictCommandSet>
<!--persistentCommand>
<createConstraint>
<table>ENR_WTHR_FORECAST_OLD</table>
<constraintName>PATCHER_DEBUG_WTO_CHECK</constraintName>
<check>
<condition>MONTH_ID * PK_WTHR_FORECAST_ID != 271</condition>
</check>
</createConstraint>
</persistentCommand-->
</strictCommandSet>
</patch>

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<patch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="IPatch.xsd">
<version>
<number>0</number>
<author>createcolumn</author>
</version>
<strictCommandSet>
<!--persistentCommand>
<createColumn>
<table>ENR_WTHR_FORECAST_OLD</table>
<column>DOLNIK_ALEXANDER</column>
<type>SMALLINT</type>
<defaultValue>123</defaultValue>
<isNotNull/>
</createColumn>
</persistentCommand-->
</strictCommandSet>
</patch>

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8" ?>
<patch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="IPatch.xsd">
<version>
<number>0</number>
<author>createforeignkey</author>
</version>
<strictCommandSet>
<!--persistentCommand>
<createConstraint>
<table>CF_FLOW_PARAMETERS</table>
<constraintName>FK_CF_FLOW_PARS_PAR_VAL</constraintName>
<foreignKey>
<column>PARAMETER_VALUE</column>
<referencedTable>ENR_MONTH</referencedTable>
<onUpdate>noAction</onUpdate>
<onDelete>noAction</onDelete>
</foreignKey>
</createConstraint>
</persistentCommand-->
</strictCommandSet>
</patch>

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8" ?>
<patch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="IPatch.xsd">
<version>
<number>0</number>
<author>createprocedure</author>
</version>
<restrictToEnvironments>
<name>deva</name>
<name>devb</name>
</restrictToEnvironments>
<strictCommandSet>
<!--persistentCommand>
<createProcedure>
<procedure>
<package>DATA_IMPORTER</package>
<name>TEST_PROCEDURE</name>
<parameter>
<name>p_cur_user</name>
<direction>in</direction>
<type>number</type>
</parameter>
<parameter>
<name>p_cur_out</name>
<direction>out</direction>
<type>sys_refcursor</type>
</parameter>
</procedure>
<declarations>
v_some_var number;
</declarations>
<body>
v_some_var := 0;
open p_cur_out for
select v_some_var from dual;
</body>
</createProcedure>
</persistentCommand-->
</strictCommandSet>
</patch>

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8" ?>
<patch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="IPatch.xsd">
<version>
<number>0</number>
<author>createtable</author>
</version>
<strictCommandSet>
<!--persistentCommand>
<createTable>
<table>ENR_PATCHER_TEST_NEW_TABLE</table>
<primaryKey>
<column>RECORD_ID</column>
<type>raw(16)</type>
<defaultValue>sys_guid()</defaultValue>
<isNotNull/>
</primaryKey>
<column>
<column>NAME</column>
<type>varchar2(32)</type>
<isNotNull/>
</column>
<column>
<column>COUNT</column>
<type>number(12)</type>
<defaultValue>0</defaultValue>
<isNotNull/>
</column>
<column>
<column>THINGY</column>
<type>blob</type>
</column>
</createTable>
</persistentCommand-->
</strictCommandSet>
</patch>

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<patch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="IPatch.xsd">
<version>
<number>0</number>
<author>createunique</author>
</version>
<strictCommandSet>
<!--persistentCommand>
<createConstraint>
<table>ENR_WTHR_FORECAST_OLD</table>
<constraintName>PATCHER_DEBUG_WTO_CHECK</constraintName>
<unique>
<column>PK_WTHR_FORECAST_ID</column>
<column>MONTH_ID</column>
</unique>
</createConstraint>
</persistentCommand-->
</strictCommandSet>
</patch>

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<patch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="IPatch.xsd">
<version>
<number>0</number>
<author>createview</author>
</version>
<restrictToEnvironments>
<name>iprohorov-test</name>
</restrictToEnvironments>
<strictCommandSet>
<persistentCommand>
<createView>
<view>PATCHER_TEST_VIEW</view>
<body>select * from ENR_MONTH where YEAR = 2009</body>
</createView>
</persistentCommand>
</strictCommandSet>
</patch>

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<patch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="IPatch.xsd">
<version>
<number>0</number>
<author>modifycolumn</author>
</version>
<strictCommandSet>
<!--persistentCommand>
<modifyColumn>
<table>ENR_WTHR_FORECAST_OLD</table>
<column>MONTH_ID</column>
<type>NUMBER</type>
<defaultValue>456</defaultValue>
<isNotNull/>
</modifyColumn>
</persistentCommand-->
</strictCommandSet>
</patch>

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<patch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="IPatch.xsd">
<version>
<number>0</number>
<author>removecheck</author>
</version>
<strictCommandSet>
<!--persistentCommand>
<removeConstraint>
<table>ENR_WTHR_FORECAST_OLD</table>
<constraintName>PATCHER_DEBUG_WTO_CHECK</constraintName>
<check>
<condition>MONTH_ID * PK_WTHR_FORECAST_ID != 271</condition>
</check>
</removeConstraint>
</persistentCommand-->
</strictCommandSet>
</patch>

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<patch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="IPatch.xsd">
<version>
<number>0</number>
<author>removecolumn</author>
</version>
<strictCommandSet>
<!--persistentCommand>
<removeColumn>
<table>ENR_WTHR_FORECAST_OLD</table>
<column>DOLNIK_ALEXANDER</column>
</removeColumn>
</persistentCommand-->
</strictCommandSet>
</patch>

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8" ?>
<patch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="IPatch.xsd">
<version>
<number>0</number>
<author>removeforeignkey</author>
</version>
<strictCommandSet>
<!--persistentCommand>
<removeConstraint>
<table>CF_FLOW_PARAMETERS</table>
<constraintName>FK_CF_FLOW_PARS_PAR_VAL</constraintName>
<foreignKey>
<column>PARAMETER_VALUE</column>
<referencedTable>ENR_MONTH</referencedTable>
<onUpdate>noAction</onUpdate>
<onDelete>noAction</onDelete>
</foreignKey>
</removeConstraint>
</persistentCommand-->
</strictCommandSet>
</patch>

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8" ?>
<patch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="IPatch.xsd">
<version>
<number>0</number>
<author>removeprocedure</author>
</version>
<restrictToEnvironments>
<name>deva</name>
<name>developer</name>
<name>devb</name>
</restrictToEnvironments>
<strictCommandSet>
<!--persistentCommand>
<removeProcedure>
<procedure>
<package>DATA_IMPORTER</package>
<name>ENR_IMPORTER_QUEUE_GETLIST</name>
<parameter>
<name>p_cur_user</name>
<direction>in</direction>
<type>number</type>
</parameter>
<parameter>
<name>p_cur_out</name>
<direction>out</direction>
<type>sys_refcursor</type>
</parameter>
</procedure>
</removeProcedure>
</persistentCommand-->
</strictCommandSet>
</patch>

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<patch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="IPatch.xsd">
<version>
<number>0</number>
<author>removeunique</author>
</version>
<strictCommandSet>
<!--persistentCommand>
<removeConstraint>
<table>ENR_WTHR_FORECAST_OLD</table>
<constraintName>PATCHER_DEBUG_WTO_CHECK</constraintName>
<unique>
<column>PK_WTHR_FORECAST_ID</column>
<column>MONTH_ID</column>
</unique>
</removeConstraint>
</persistentCommand-->
</strictCommandSet>
</patch>

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?>
<patch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="IPatch.xsd">
<version>
<number>0</number>
<author>removeview</author>
</version>
<restrictToEnvironments>
<name>iprohorov-test</name>
</restrictToEnvironments>
<strictCommandSet>
<persistentCommand>
<removeView>
<view>PATCHER_TEST_VIEW</view>
</removeView>
</persistentCommand>
</strictCommandSet>
</patch>

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
namespace Patcher.Resources
{
static class ResourcesManager
{
public class XmlUrlResolver : System.Xml.XmlUrlResolver
{
public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
{
//Assembly assembly = Assembly.GetExecutingAssembly();
//return assembly.GetManifestResourceStream(this.GetType(),Path.GetFileName(absoluteUri.AbsolutePath));
// throw new ApplicationException(Path.GetFileName(absoluteUri.AbsolutePath));
try
{
return GetResource(Path.GetFileName(absoluteUri.AbsolutePath)); //note that we ignore all folders structure
} catch(ResourceNotFoundException)
{
throw new XmlResourceNotFoundException(absoluteUri);
}
}
}
public static Stream GetResource(string name)
{
var result = Assembly.GetExecutingAssembly().GetManifestResourceStream(typeof(ResourcesManager), name);
if(result == null)
{
throw new ResourceNotFoundException(name);
}
return result;
}
private static readonly string Namespace = typeof(ResourcesManager).Namespace + ".";
public static IEnumerable<string> GetResourcesList()
{
return
from rawName in Assembly.GetExecutingAssembly().GetManifestResourceNames()
where rawName.StartsWith(Namespace)
select rawName.Substring(Namespace.Length);
}
}
}

@ -0,0 +1,971 @@
//
// NConsoler 0.9.3
// http://nconsoler.csharpus.com
//
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Diagnostics;
using System.Threading;
namespace NConsoler
{
/// <summary>
/// Entry point for NConsoler applications
/// </summary>
public sealed class Consolery
{
/// <summary>
/// Runs an appropriate Action method.
/// Uses the class this call lives in as target type and command line arguments from Environment
/// </summary>
public static int Run()
{
Type declaringType = new StackTrace().GetFrame(1).GetMethod().DeclaringType;
string[] args = new string[Environment.GetCommandLineArgs().Length - 1];
new List<string>(Environment.GetCommandLineArgs()).CopyTo(1, args, 0, Environment.GetCommandLineArgs().Length - 1);
return Run(declaringType, args);
}
/// <summary>
/// Runs an appropriate Action method
/// </summary>
/// <param name="targetType">Type where to search for Action methods</param>
/// <param name="args">Arguments that will be converted to Action method arguments</param>
public static int Run(Type targetType, string[] args)
{
return Run(targetType, args, new ConsoleMessenger());
}
/// <summary>
/// Runs an appropriate Action method
/// </summary>
/// <param name="targetType">Type where to search for Action methods</param>
/// <param name="args">Arguments that will be converted to Action method arguments</param>
/// <param name="messenger">Uses for writing messages instead of Console class methods</param>
public static int Run(Type targetType, string[] args, IMessenger messenger)
{
try
{
return new Consolery(targetType, args, messenger, false, null).RunAction();
}
catch (NConsolerException e)
{
messenger.Write(e.Message);
return 100;
}
}
/// <summary>
/// Runs an appropriate Action method
/// </summary>
/// <param name="targetObject">Object where to search for Action methods</param>
/// <param name="args">Arguments that will be converted to Action method arguments</param>
public static int RunInstance(object targetObject, string[] args)
{
return RunInstance(targetObject, args, new ConsoleMessenger());
}
/// <summary>
/// Runs an appropriate Action method
/// </summary>
/// <param name="targetObject">Object where to search for Action methods</param>
/// <param name="args">Arguments that will be converted to Action method arguments</param>
/// <param name="messenger">Uses for writing messages instead of Console class methods</param>
public static int RunInstance(object targetObject, string[] args, IMessenger messenger)
{
try
{
return new Consolery(targetObject.GetType(), args, messenger, true, targetObject).RunAction();
}
catch (NConsolerException e)
{
messenger.Write(e.Message);
return 100;
}
}
/// <summary>
/// Validates specified type and throws NConsolerException if an error
/// </summary>
/// <param name="targetType">Type where to search for Action methods</param>
public static void Validate(Type targetType)
{
new Consolery(targetType, new string[] {}, new ConsoleMessenger(), false, null).ValidateMetadata();
}
private readonly Type _targetType;
private readonly string[] _args;
private readonly List<MethodInfo> _actionMethods = new List<MethodInfo>();
private readonly IMessenger _messenger;
private readonly bool _isInstance;
private readonly object _targetObject;
private Consolery(Type targetType, string[] args, IMessenger messenger, bool isInstance, object targetObject)
{
#region Parameter Validation
if (targetType == null)
{
throw new ArgumentNullException("targetType");
}
if (args == null)
{
throw new ArgumentNullException("args");
}
if (messenger == null)
{
throw new ArgumentNullException("messenger");
}
if (isInstance && (targetObject == null))
{
throw new AbandonedMutexException("targetObject");
}
#endregion
_targetType = targetType;
_args = args;
_messenger = messenger;
_isInstance = isInstance;
_targetObject = targetObject;
BindingFlags flags;
if(_isInstance)
{
flags = BindingFlags.Instance;
} else
{
flags = BindingFlags.Static;
}
MethodInfo[] methods = _targetType.GetMethods(BindingFlags.Public | flags);
foreach (MethodInfo method in methods)
{
object[] attributes = method.GetCustomAttributes(false);
foreach (object attribute in attributes)
{
if (attribute is ActionAttribute)
{
_actionMethods.Add(method);
break;
}
}
}
}
static object ConvertValue(string value, Type argumentType)
{
if (argumentType == typeof(int))
{
try
{
return Convert.ToInt32(value);
}
catch (FormatException)
{
throw new NConsolerException("Could not convert \"{0}\" to integer", value);
}
catch (OverflowException)
{
throw new NConsolerException("Value \"{0}\" is too big or too small", value);
}
}
if (argumentType == typeof(string))
{
return value;
}
if (argumentType == typeof(bool))
{
try
{
return Convert.ToBoolean(value);
}
catch (FormatException)
{
throw new NConsolerException("Could not convert \"{0}\" to boolean", value);
}
}
if (argumentType == typeof(string[]))
{
return value.Split('+');
}
if (argumentType == typeof(int[]))
{
string[] values = value.Split('+');
int[] valuesArray = new int[values.Length];
for (int i = 0; i < values.Length; i++)
{
valuesArray[i] = (int)ConvertValue(values[i], typeof(int));
}
return valuesArray;
}
if (argumentType == typeof(DateTime))
{
return ConvertToDateTime(value);
}
throw new NConsolerException("Unknown type is used in your method {0}", argumentType.FullName);
}
private static DateTime ConvertToDateTime(string parameter)
{
string[] parts = parameter.Split('-');
if (parts.Length != 3)
{
throw new NConsolerException("Could not convert {0} to Date", parameter);
}
int day = (int)ConvertValue(parts[0], typeof(int));
int month = (int)ConvertValue(parts[1], typeof(int));
int year = (int)ConvertValue(parts[2], typeof(int));
try
{
return new DateTime(year, month, day);
}
catch (ArgumentException)
{
throw new NConsolerException("Could not convert {0} to Date", parameter);
}
}
private static bool CanBeConvertedToDate(string parameter)
{
try
{
ConvertToDateTime(parameter);
return true;
}
catch(NConsolerException)
{
return false;
}
}
private bool SingleActionWithOnlyOptionalParametersSpecified() {
if (IsMulticommand) return false;
MethodInfo method = _actionMethods[0];
return OnlyOptionalParametersSpecified(method);
}
private static bool OnlyOptionalParametersSpecified(MethodBase method)
{
foreach (ParameterInfo parameter in method.GetParameters())
{
if (IsRequired(parameter))
{
return false;
}
}
return true;
}
private int RunAction()
{
ValidateMetadata();
if (IsHelpRequested())
{
PrintUsage();
return 0;
}
MethodInfo currentMethod = GetCurrentMethod();
if (currentMethod == null)
{
PrintUsage();
throw new NConsolerException("Unknown subcommand \"{0}\"", _args[0]);
}
ValidateInput(currentMethod);
return InvokeMethod(currentMethod);
}
private struct ParameterData
{
public readonly int position;
public readonly Type type;
public ParameterData(int position, Type type)
{
this.position = position;
this.type = type;
}
}
private static bool IsRequired(ICustomAttributeProvider info)
{
object[] attributes = info.GetCustomAttributes(typeof(ParameterAttribute), false);
return attributes.Length == 0 || attributes[0].GetType() == typeof(RequiredAttribute);
}
private static bool IsOptional(ICustomAttributeProvider info)
{
return !IsRequired(info);
}
private static OptionalAttribute GetOptional(ICustomAttributeProvider info)
{
object[] attributes = info.GetCustomAttributes(typeof(OptionalAttribute), false);
return (OptionalAttribute)attributes[0];
}
private bool IsMulticommand
{
get
{
return _actionMethods.Count > 1;
}
}
private bool IsHelpRequested()
{
return (_args.Length == 0 && !SingleActionWithOnlyOptionalParametersSpecified())
|| (_args.Length > 0 && (_args[0] == "/?"
|| _args[0] == "/help"
|| _args[0] == "/h"
|| _args[0] == "help"));
}
private int InvokeMethod(MethodInfo method)
{
try
{
return (int)method.Invoke(_isInstance ? _targetObject : null, BuildParameterArray(method));
}
catch (TargetInvocationException e)
{
if (e.InnerException != null)
{
throw new NConsolerException(e.InnerException.Message, e);
}
throw;
}
}
private object[] BuildParameterArray(MethodInfo method)
{
int argumentIndex = IsMulticommand ? 1 : 0;
List<object> parameterValues = new List<object>();
Dictionary<string, ParameterData> aliases = new Dictionary<string, ParameterData>();
foreach (ParameterInfo info in method.GetParameters())
{
if (IsRequired(info))
{
parameterValues.Add(ConvertValue(_args[argumentIndex], info.ParameterType));
}
else
{
OptionalAttribute optional = GetOptional(info);
foreach (string altName in optional.AltNames)
{
aliases.Add(altName.ToLower(),
new ParameterData(parameterValues.Count, info.ParameterType));
}
aliases.Add(info.Name.ToLower(),
new ParameterData(parameterValues.Count, info.ParameterType));
parameterValues.Add(optional.Default);
}
argumentIndex++;
}
foreach (string optionalParameter in OptionalParameters(method))
{
string name = ParameterName(optionalParameter);
string value = ParameterValue(optionalParameter);
parameterValues[aliases[name].position] = ConvertValue(value, aliases[name].type);
}
return parameterValues.ToArray();
}
private IEnumerable<string> OptionalParameters(MethodInfo method)
{
int firstOptionalParameterIndex = RequiredParameterCount(method);
if (IsMulticommand)
{
firstOptionalParameterIndex++;
}
for (int i = firstOptionalParameterIndex; i < _args.Length; i++)
{
yield return _args[i];
}
}
private static int RequiredParameterCount(MethodInfo method)
{
int requiredParameterCount = 0;
foreach (ParameterInfo parameter in method.GetParameters())
{
if (IsRequired(parameter))
{
requiredParameterCount++;
}
}
return requiredParameterCount;
}
private MethodInfo GetCurrentMethod()
{
if (!IsMulticommand)
{
return _actionMethods[0];
}
return GetMethodByName(_args[0].ToLower());
}
private MethodInfo GetMethodByName(string name)
{
foreach (MethodInfo method in _actionMethods)
{
if (method.Name.ToLower() == name)
{
return method;
}
}
return null;
}
private void PrintUsage(MethodInfo method)
{
PrintMethodDescription(method);
Dictionary<string, string> parameters = GetParametersDescriptions(method);
PrintUsageExample(method, parameters);
PrintParametersDescriptions(parameters);
}
private void PrintUsageExample(MethodInfo method, IDictionary<string, string> parameterList)
{
string subcommand = IsMulticommand ? method.Name.ToLower() + " " : String.Empty;
string parameters = String.Join(" ", new List<string>(parameterList.Keys).ToArray());
_messenger.Write("usage: " + ProgramName() + " " + subcommand + parameters);
}
private void PrintMethodDescription(MethodInfo method)
{
string description = GetMethodDescription(method);
if (description == String.Empty) return;
_messenger.Write(description);
}
private static string GetMethodDescription(MethodInfo method)
{
object[] attributes = method.GetCustomAttributes(true);
foreach (object attribute in attributes)
{
if (attribute is ActionAttribute)
{
return ((ActionAttribute)attribute).Description;
}
}
throw new NConsolerException("Method is not marked with an Action attribute");
}
private static Dictionary<string, string> GetParametersDescriptions(MethodInfo method)
{
Dictionary<string, string> parameters = new Dictionary<string, string>();
foreach (ParameterInfo parameter in method.GetParameters())
{
object[] parameterAttributes =
parameter.GetCustomAttributes(typeof(ParameterAttribute), false);
if (parameterAttributes.Length > 0)
{
string name = GetDisplayName(parameter);
ParameterAttribute attribute = (ParameterAttribute)parameterAttributes[0];
parameters.Add(name, attribute.Description);
}
else
{
parameters.Add(parameter.Name, String.Empty);
}
}
return parameters;
}
private void PrintParametersDescriptions(IEnumerable<KeyValuePair<string, string>> parameters)
{
int maxParameterNameLength = MaxKeyLength(parameters);
foreach (KeyValuePair<string, string> pair in parameters)
{
if (pair.Value != String.Empty)
{
int difference = maxParameterNameLength - pair.Key.Length + 2;
_messenger.Write(" " + pair.Key + new String(' ', difference) + pair.Value);
}
}
}
private static int MaxKeyLength(IEnumerable<KeyValuePair<string, string>> parameters)
{
int maxLength = 0;
foreach (KeyValuePair<string, string> pair in parameters)
{
if (pair.Key.Length > maxLength)
{
maxLength = pair.Key.Length;
}
}
return maxLength;
}
private string ProgramName()
{
Assembly entryAssembly = Assembly.GetEntryAssembly();
if (entryAssembly == null)
{
return _targetType.Name.ToLower();
}
return new AssemblyName(entryAssembly.FullName).Name;
}
private void PrintUsage()
{
if (IsMulticommand && !IsSubcommandHelpRequested())
{
PrintGeneralMulticommandUsage();
}
else if (IsMulticommand && IsSubcommandHelpRequested())
{
PrintSubcommandUsage();
}
else
{
PrintUsage(_actionMethods[0]);
}
}
private void PrintSubcommandUsage()
{
MethodInfo method = GetMethodByName(_args[1].ToLower());
if (method == null)
{
PrintGeneralMulticommandUsage();
throw new NConsolerException("Unknown subcommand \"{0}\"", _args[0].ToLower());
}
PrintUsage(method);
}
private bool IsSubcommandHelpRequested()
{
return _args.Length > 0
&& _args[0].ToLower() == "help"
&& _args.Length == 2;
}
private void PrintGeneralMulticommandUsage()
{
_messenger.Write(
String.Format("usage: {0} <subcommand> [args]", ProgramName()));
_messenger.Write(
String.Format("Type '{0} help <subcommand>' for help on a specific subcommand.", ProgramName()));
_messenger.Write(String.Empty);
_messenger.Write("Available subcommands:");
foreach (MethodInfo method in _actionMethods)
{
_messenger.Write(method.Name.ToLower() + " " + GetMethodDescription(method));
}
}
private static string GetDisplayName(ParameterInfo parameter)
{
if (IsRequired(parameter))
{
return parameter.Name;
}
OptionalAttribute optional = GetOptional(parameter);
string parameterName =
(optional.AltNames.Length > 0) ? optional.AltNames[0] : parameter.Name;
if (parameter.ParameterType != typeof(bool))
{
parameterName += ":" + ValueDescription(parameter.ParameterType);
}
return "[/" + parameterName + "]";
}
private static string ValueDescription(Type type)
{
if (type == typeof(int))
{
return "number";
}
if (type == typeof(string))
{
return "value";
}
if (type == typeof(int[]))
{
return "number[+number]";
}
if (type == typeof(string[]))
{
return "value[+value]";
}
if (type == typeof(DateTime))
{
return "dd-mm-yyyy";
}
throw new ArgumentOutOfRangeException(String.Format("Type {0} is unknown", type.Name));
}
#region Validation
private void ValidateInput(MethodInfo method)
{
CheckAllRequiredParametersAreSet(method);
CheckOptionalParametersAreNotDuplicated(method);
CheckUnknownParametersAreNotPassed(method);
}
private void CheckAllRequiredParametersAreSet(MethodInfo method)
{
int minimumArgsLengh = RequiredParameterCount(method);
if (IsMulticommand)
{
minimumArgsLengh++;
}
if (_args.Length < minimumArgsLengh)
{
throw new NConsolerException("Not all required parameters are set");
}
}
private static string ParameterName(string parameter)
{
if (parameter.StartsWith("/-"))
{
return parameter.Substring(2).ToLower();
}
if (parameter.Contains(":"))
{
return parameter.Substring(1, parameter.IndexOf(":") - 1).ToLower();
}
return parameter.Substring(1).ToLower();
}
private static string ParameterValue(string parameter)
{
if (parameter.StartsWith("/-"))
{
return "false";
}
if (parameter.Contains(":"))
{
return parameter.Substring(parameter.IndexOf(":") + 1);
}
return "true";
}
private void CheckOptionalParametersAreNotDuplicated(MethodInfo method)
{
List<string> passedParameters = new List<string>();
foreach (string optionalParameter in OptionalParameters(method))
{
if (!optionalParameter.StartsWith("/"))
{
throw new NConsolerException("Unknown parameter {0}", optionalParameter);
}
string name = ParameterName(optionalParameter);
if (passedParameters.Contains(name))
{
throw new NConsolerException("Parameter with name {0} passed two times", name);
}
passedParameters.Add(name);
}
}
private void CheckUnknownParametersAreNotPassed(MethodInfo method)
{
List<string> parameterNames = new List<string>();
foreach (ParameterInfo parameter in method.GetParameters())
{
if (IsRequired(parameter))
{
continue;
}
parameterNames.Add(parameter.Name.ToLower());
OptionalAttribute optional = GetOptional(parameter);
foreach (string altName in optional.AltNames)
{
parameterNames.Add(altName.ToLower());
}
}
foreach (string optionalParameter in OptionalParameters(method))
{
string name = ParameterName(optionalParameter);
if (!parameterNames.Contains(name.ToLower()))
{
throw new NConsolerException("Unknown parameter name {0}", optionalParameter);
}
}
}
private void ValidateMetadata()
{
CheckAnyActionMethodExists();
IfActionMethodIsSingleCheckMethodHasParameters();
foreach (MethodInfo method in _actionMethods)
{
CheckActionMethodNamesAreNotReserved();
CheckRequiredAndOptionalAreNotAppliedAtTheSameTime(method);
CheckOptionalParametersAreAfterRequiredOnes(method);
CheckOptionalParametersDefaultValuesAreAssignableToRealParameterTypes(method);
CheckOptionalParametersAltNamesAreNotDuplicated(method);
}
}
private void CheckActionMethodNamesAreNotReserved()
{
foreach (MethodInfo method in _actionMethods)
{
if (method.Name.ToLower() == "help")
{
throw new NConsolerException("Method name \"{0}\" is reserved. Please, choose another name", method.Name);
}
}
}
private void CheckAnyActionMethodExists()
{
if (_actionMethods.Count == 0)
{
throw new NConsolerException("Can not find any public static method marked with [Action] attribute in type \"{0}\"", _targetType.Name);
}
}
private void IfActionMethodIsSingleCheckMethodHasParameters()
{
if (_actionMethods.Count == 1 && _actionMethods[0].GetParameters().Length == 0)
{
throw new NConsolerException("[Action] attribute applied once to the method \"{0}\" without parameters. In this case NConsoler should not be used", _actionMethods[0].Name);
}
}
private static void CheckRequiredAndOptionalAreNotAppliedAtTheSameTime(MethodBase method)
{
foreach (ParameterInfo parameter in method.GetParameters())
{
object[] attributes = parameter.GetCustomAttributes(typeof(ParameterAttribute), false);
if (attributes.Length > 1)
{
throw new NConsolerException("More than one attribute is applied to the parameter \"{0}\" in the method \"{1}\"", parameter.Name, method.Name);
}
}
}
private static bool CanBeNull(Type type)
{
return type == typeof(string)
|| type == typeof(string[])
|| type == typeof(int[]);
}
private static void CheckOptionalParametersDefaultValuesAreAssignableToRealParameterTypes(MethodBase method)
{
foreach (ParameterInfo parameter in method.GetParameters())
{
if (IsRequired(parameter))
{
continue;
}
OptionalAttribute optional = GetOptional(parameter);
if (optional.Default != null && optional.Default.GetType() == typeof(string) && CanBeConvertedToDate(optional.Default.ToString()))
{
return;
}
if ((optional.Default == null && !CanBeNull(parameter.ParameterType))
|| (optional.Default != null && !optional.Default.GetType().IsAssignableFrom(parameter.ParameterType)))
{
throw new NConsolerException("Default value for an optional parameter \"{0}\" in method \"{1}\" can not be assigned to the parameter", parameter.Name, method.Name);
}
}
}
private static void CheckOptionalParametersAreAfterRequiredOnes(MethodBase method)
{
bool optionalFound = false;
foreach (ParameterInfo parameter in method.GetParameters())
{
if (IsOptional(parameter))
{
optionalFound = true;
}
else if (optionalFound)
{
throw new NConsolerException("It is not allowed to write a parameter with a Required attribute after a parameter with an Optional one. See method \"{0}\" parameter \"{1}\"", method.Name, parameter.Name);
}
}
}
private static void CheckOptionalParametersAltNamesAreNotDuplicated(MethodBase method)
{
List<string> parameterNames = new List<string>();
foreach (ParameterInfo parameter in method.GetParameters())
{
if (IsRequired(parameter))
{
parameterNames.Add(parameter.Name.ToLower());
}
else
{
if (parameterNames.Contains(parameter.Name.ToLower()))
{
throw new NConsolerException("Found duplicated parameter name \"{0}\" in method \"{1}\". Please check alt names for optional parameters", parameter.Name, method.Name);
}
parameterNames.Add(parameter.Name.ToLower());
OptionalAttribute optional = GetOptional(parameter);
foreach (string altName in optional.AltNames)
{
if (parameterNames.Contains(altName.ToLower()))
{
throw new NConsolerException("Found duplicated parameter name \"{0}\" in method \"{1}\". Please check alt names for optional parameters", altName, method.Name);
}
parameterNames.Add(altName.ToLower());
}
}
}
}
#endregion
}
/// <summary>
/// Used for getting messages from NConsoler
/// </summary>
public interface IMessenger
{
void Write(string message);
}
/// <summary>
/// Uses Console class for message output
/// </summary>
public class ConsoleMessenger : IMessenger
{
public void Write(string message)
{
Console.WriteLine(message);
}
}
/// <summary>
/// Every action method should be marked with this attribute
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class ActionAttribute : Attribute
{
public ActionAttribute()
{
}
public ActionAttribute(string description)
{
_description = description;
}
private string _description = String.Empty;
/// <summary>
/// Description is used for help messages
/// </summary>
public string Description
{
get
{
return _description;
}
set
{
_description = value;
}
}
}
/// <summary>
/// Should not be used directly
/// </summary>
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class ParameterAttribute : Attribute
{
private string _description = String.Empty;
/// <summary>
/// Description is used in help message
/// </summary>
public string Description
{
get
{
return _description;
}
set
{
_description = value;
}
}
protected ParameterAttribute()
{
}
}
/// <summary>
/// Marks an Action method parameter as optional
/// </summary>
public sealed class OptionalAttribute : ParameterAttribute
{
private string[] _altNames;
public string[] AltNames
{
get
{
return _altNames;
}
set
{
_altNames = value;
}
}
private readonly object _defaultValue;
public object Default
{
get
{
return _defaultValue;
}
}
/// <param name="defaultValue">Default value if client doesn't pass this value</param>
/// <param name="altNames">Aliases for parameter</param>
public OptionalAttribute(object defaultValue, params string[] altNames)
{
_defaultValue = defaultValue;
_altNames = altNames;
}
}
/// <summary>
/// Marks an Action method parameter as required
/// </summary>
public sealed class RequiredAttribute : ParameterAttribute
{
}
/// <summary>
/// Can be used for safe exception throwing - NConsoler will catch the exception
/// </summary>
public sealed class NConsolerException : Exception
{
public NConsolerException()
{
}
public NConsolerException(string message, Exception innerException)
: base(message, innerException)
{
}
public NConsolerException(string message, params string[] arguments)
: base(String.Format(message, arguments))
{
}
}
}

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Patcher
{
class Cache<TKey, TValue>
{
public static readonly Cache<TKey, TValue> instance = new Cache<TKey, TValue>();
private readonly Dictionary<TKey, TValue> storage = new Dictionary<TKey, TValue>();
private Cache()
{
}
public TValue GetValue(TKey key, Func<TValue> creator)
{
if(!this.storage.ContainsKey(key))
{
lock(this.storage)
{
if(!this.storage.ContainsKey(key))
{
this.storage[key] = creator();
}
}
}
return this.storage[key];
}
}
}

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
namespace Patcher
{
class CultureReplacementWrapper : IDisposable
{
private readonly CultureInfo originalCulture;
public CultureReplacementWrapper(CultureInfo newCulture)
{
this.originalCulture = System.Threading.Thread.CurrentThread.CurrentCulture;
System.Threading.Thread.CurrentThread.CurrentCulture = newCulture;
}
void IDisposable.Dispose()
{
System.Threading.Thread.CurrentThread.CurrentCulture = this.originalCulture;
}
}
}

@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
namespace Patcher
{
static class Util
{
private static bool RemoveExtra_MoveNext<T>(IEnumerator<T> enumerator) where T : IComparable<T>
{
T previous = enumerator.Current;
if(!enumerator.MoveNext())
{
return false;
}
T current = enumerator.Current;
if(previous.CompareTo(current) >= 0)
{
throw new ApplicationException("Expecting ordered list");
}
return true;
}
private static bool RemoveExtra_ForwardUntil<T>(IEnumerator<T> enumerator, T val) where T : IComparable<T>
{
while (enumerator.MoveNext())
{
if (val.CompareTo(enumerator.Current) <= 0)
{
return true;
}
}
return false;
}
//Note that this method expects unique values in both orderedSource and orderedToRemove; i.e. { 1, 2, 2, 3 } would be incorrect input value
public static IEnumerable<T> RemoveExtra<T>(IEnumerable<T> orderedSource, IEnumerable<T> orderedToRemove) where T : IComparable<T>
{
using(IEnumerator<T> sourceEnumerator = orderedSource.GetEnumerator(), removeEnumerator = orderedToRemove.GetEnumerator())
{
//rourceRemains: whether there are some elements left in source
//removeRemains: whether there are some elements left in toRemove
//Here we position both enumerators on the first element (if exists)
bool sourceRemains = sourceEnumerator.MoveNext();
bool removeRemains = removeEnumerator.MoveNext();
while(sourceRemains && removeRemains)
//In the beginning of each iteration, current element of source has not been processed
//and current element of toRemove is not less than previous element of source (if exists)
{
int comparisonResult = sourceEnumerator.Current.CompareTo(removeEnumerator.Current);
if(comparisonResult < 0)
//If current element of toRemove is greater than current element of source, we should return current element of source and move to the next element of source
{
yield return sourceEnumerator.Current;
sourceRemains = RemoveExtra_MoveNext(sourceEnumerator);
} else if(comparisonResult > 0)
//If current element of toRemove is less than current element of source, we should move to the first element of toRemove which is not less than current element of source, and then repeat the iteration
{
removeRemains = RemoveExtra_ForwardUntil(removeEnumerator, sourceEnumerator.Current);
} else if(comparisonResult == 0)
//If current element of toRemove equals to current element of source, we should skip current element of source
//Also, there is no point in comparing next element of source with the current element of toRemove, because former is definitely larger than the latter
{
sourceRemains = RemoveExtra_MoveNext(sourceEnumerator);
removeRemains = RemoveExtra_MoveNext(removeEnumerator);
} else
{
throw new ApplicationException("Wrong comparison result " + comparisonResult);
}
}
//If there is no elements left in toRemove, then either there were no elements in toRemove or last element of toRemove was less than current element of source
//So we can output all remaining elements of source
while(sourceRemains)
{
yield return sourceEnumerator.Current;
sourceRemains = sourceEnumerator.MoveNext();
}
}
}
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> source)
{
return source.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}
public static List<T> RemoveAllButFirst<T>(this List<T> list)
{
List<T> result = new List<T>();
if(list.Count != 0)
{
result.Add(list.First());
}
return result;
}
public static object GetValue(this DbDataReader reader, string column)
{
return reader.GetValue(reader.GetOrdinal(column));
}
public static IEnumerable<T> ConcatScalar<T>(this IEnumerable<T> source, params T[] toAdd)
{
foreach(T elem in source) {
yield return elem;
}
foreach(T elem in toAdd)
{
if(elem != null)
{
yield return elem;
}
}
}
}
}
Loading…
Cancel
Save