Skip to content

Commit fc9490f

Browse files
Felipeobnclaude
andcommitted
feat: compare triggers and add application icon
- Extract SQL triggers (sys.sql_modules, type TR) and compare like other objects - Sync script: DROP / CREATE OR ALTER TRIGGER with correct dependency ordering - Add multi-size appicon.ico wired via ApplicationIcon; windows use the exe icon Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 3433885 commit fc9490f

10 files changed

Lines changed: 60 additions & 15 deletions

File tree

PROJECT.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
Aplicação **desktop C# WinForms** (.NET 9, Windows) que compara a **estrutura de
1010
dois bancos SQL Server** (banco A × banco B) e gera scripts de sincronização.
1111

12-
Compara 4 tipos de objeto: **Tabelas, Views, Procedures, Índices**.
12+
Compara 5 tipos de objeto: **Tabelas, Views, Procedures, Índices, Triggers**.
1313

1414
Fluxo: conectar nos 2 bancos → extrair schema → comparar → ver diferenças com diff
1515
visual (estilo WinMerge) → gerar/executar script de sincronização A→B ou B→A.
@@ -97,8 +97,9 @@ A UI precisa de ambiente gráfico Windows e bancos SQL Server reais para testar.
9797
Se `ConsiderColumnOrder == false`, tabelas que diferem só na ordem das colunas
9898
são tratadas como Equal (assinatura com colunas ordenadas).
9999
- **Script de sync:** `SET XACT_ABORT ON` + `BEGIN TRAN` + batches separados por `GO`
100-
+ `COMMIT`. Ordem: DROP (proc→view→idx→tbl), depois CREATE/ALTER (tbl→idx→view→proc).
101-
Views/Procs alteradas usam `CREATE OR ALTER`. Índices alterados: DROP+CREATE.
100+
+ `COMMIT`. Ordem: DROP (trigger→proc→view→idx→tbl), depois CREATE/ALTER
101+
(tbl→idx→view→proc→trigger). Views/Procs/Triggers alterados usam `CREATE OR ALTER`.
102+
Índices alterados: DROP+CREATE.
102103
**Tabelas que existem nos 2 bancos e diferem → `TableAlterGenerator`** gera
103104
ALTER COLUMN / ADD / DROP COLUMN + ajuste de DEFAULT (constraint).
104105
- **Execução:** `ScriptExecutor` separa por `GO`, ignora o controle de transação do
@@ -122,7 +123,8 @@ A UI precisa de ambiente gráfico Windows e bancos SQL Server reais para testar.
122123

123124
## 7. Estado atual — FEITO
124125

125-
- [x] Extração de Tabelas/Views/Procedures/Índices (sem data de geração)
126+
- [x] Extração de Tabelas/Views/Procedures/Índices/**Triggers** (sem data de geração)
127+
- [x] Ícone da aplicação (`appicon.ico` + `ApplicationIcon`; janelas usam o ícone do exe)
126128
- [x] Comparação + status por objeto
127129
- [x] Diff visual lado-a-lado (WinMerge-like) no DataGridView
128130
- [x] Geração de script A→B e B→A (completo) e **por objeto** (menu de contexto)

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
Aplicação **desktop C# WinForms (.NET 9, Windows)** que compara a **estrutura de dois
77
bancos SQL Server** e gera scripts de sincronização DDL.
88

9-
Compara quatro tipos de objeto: **Tabelas, Views, Procedures e Índices**.
9+
Compara cinco tipos de objeto: **Tabelas, Views, Procedures, Índices e Triggers**.
1010

1111
> Fluxo: conectar nos 2 bancos → extrair schema → comparar → ver as diferenças em um
1212
> diff visual lado a lado (estilo WinMerge) → gerar/executar o script de sincronização.
@@ -15,8 +15,8 @@ Compara quatro tipos de objeto: **Tabelas, Views, Procedures e Índices**.
1515

1616
## Recursos
1717

18-
- **Comparação de schema** de Tabelas, Views, Procedures e Índices, com status por
19-
objeto: igual, diferente, só no banco da esquerda ou só no da direita.
18+
- **Comparação de schema** de Tabelas, Views, Procedures, Índices e Triggers, com
19+
status por objeto: igual, diferente, só no banco da esquerda ou só no da direita.
2020
- **Diff visual lado a lado** com realce por linha (inserido / removido / alterado).
2121
- **Geração de script de sincronização** DDL, com opção de:
2222
- **só gerar** (copiar para a área de transferência ou salvar `.sql`); ou

SQLStructDiff.Core/Models/DbObjectType.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ public enum DbObjectType
66
Table,
77
View,
88
Procedure,
9-
Index
9+
Index,
10+
Trigger
1011
}

SQLStructDiff.Core/Services/SchemaExtractor.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public async Task<DatabaseSchema> ExtractAsync(ConnectionInfo info, Cancellation
5858
var objects = new List<DbObject>();
5959
objects.AddRange(await ExtractModulesAsync(conn, DbObjectType.View, "V", ct));
6060
objects.AddRange(await ExtractModulesAsync(conn, DbObjectType.Procedure, "P", ct));
61+
objects.AddRange(await ExtractModulesAsync(conn, DbObjectType.Trigger, "TR", ct));
6162
objects.AddRange(await ExtractTablesAsync(conn, ct));
6263
objects.AddRange(await ExtractIndexesAsync(conn, ct));
6364

SQLStructDiff.Core/Services/SyncScriptGenerator.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public enum SyncDirection
2121
/// </summary>
2222
public sealed partial class SyncScriptGenerator
2323
{
24-
[GeneratedRegex(@"^\s*CREATE\s+(VIEW|PROCEDURE|PROC)\b", RegexOptions.IgnoreCase)]
24+
[GeneratedRegex(@"^\s*CREATE\s+(VIEW|PROCEDURE|PROC|TRIGGER)\b", RegexOptions.IgnoreCase)]
2525
private static partial Regex CreateModule();
2626

2727
public string Generate(IEnumerable<ObjectComparison> selected, SyncDirection direction,
@@ -44,17 +44,19 @@ public string Generate(IEnumerable<ObjectComparison> selected, SyncDirection dir
4444
sb.AppendLine("GO");
4545
sb.AppendLine();
4646

47-
// Ordem para DROP: procedures -> views -> índices -> tabelas
47+
// Ordem para DROP: triggers -> procedures -> views -> índices -> tabelas
48+
EmitDrops(sb, items, direction, DbObjectType.Trigger);
4849
EmitDrops(sb, items, direction, DbObjectType.Procedure);
4950
EmitDrops(sb, items, direction, DbObjectType.View);
5051
EmitDrops(sb, items, direction, DbObjectType.Index);
5152
EmitDrops(sb, items, direction, DbObjectType.Table);
5253

53-
// Ordem para CREATE/ALTER: tabelas -> índices -> views -> procedures
54+
// Ordem para CREATE/ALTER: tabelas -> índices -> views -> procedures -> triggers
5455
EmitCreates(sb, items, direction, DbObjectType.Table);
5556
EmitCreates(sb, items, direction, DbObjectType.Index);
5657
EmitCreates(sb, items, direction, DbObjectType.View);
5758
EmitCreates(sb, items, direction, DbObjectType.Procedure);
59+
EmitCreates(sb, items, direction, DbObjectType.Trigger);
5860

5961
sb.AppendLine("COMMIT TRANSACTION;");
6062
sb.AppendLine("GO");
@@ -123,8 +125,8 @@ private void EmitCreates(StringBuilder sb, List<ObjectComparison> items,
123125

124126
private string CreateStatement(DbObject source, CompareStatus status)
125127
{
126-
// Views/Procedures alteradas usam CREATE OR ALTER.
127-
if (source.Type is DbObjectType.View or DbObjectType.Procedure)
128+
// Views/Procedures/Triggers alterados usam CREATE OR ALTER.
129+
if (source.Type is DbObjectType.View or DbObjectType.Procedure or DbObjectType.Trigger)
128130
{
129131
var ddl = source.RawDefinition;
130132
if (status == CompareStatus.Different && CreateModule().IsMatch(ddl))
@@ -136,16 +138,17 @@ private string CreateStatement(DbObject source, CompareStatus status)
136138
return source.RawDefinition;
137139
}
138140

139-
// Views e Procedures são "módulos": seus scripts saem sem os comentários de marcação.
141+
// Views, Procedures e Triggers são "módulos": seus scripts saem sem os comentários de marcação.
140142
private static bool IsModule(DbObjectType type) =>
141-
type is DbObjectType.View or DbObjectType.Procedure;
143+
type is DbObjectType.View or DbObjectType.Procedure or DbObjectType.Trigger;
142144

143145
private static string DropStatement(ObjectComparison c)
144146
{
145147
return c.Type switch
146148
{
147149
DbObjectType.Procedure => $"DROP PROCEDURE IF EXISTS {c.FullName};",
148150
DbObjectType.View => $"DROP VIEW IF EXISTS {c.FullName};",
151+
DbObjectType.Trigger => $"DROP TRIGGER IF EXISTS {c.FullName};",
149152
DbObjectType.Table => $"DROP TABLE IF EXISTS {c.FullName};",
150153
DbObjectType.Index => DropIndex(c),
151154
_ => $"-- DROP não suportado para {c.Type}"

SQLStructDiff.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<Nullable>enable</Nullable>
77
<UseWindowsForms>true</UseWindowsForms>
88
<ImplicitUsings>enable</ImplicitUsings>
9+
<ApplicationIcon>appicon.ico</ApplicationIcon>
910
</PropertyGroup>
1011

1112
<!-- O Core vive numa subpasta; impede que o glob recursivo da UI engula seus fontes/obj. -->

UI/AppIcon.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
namespace SQLStructDiff.UI;
2+
3+
/// <summary>
4+
/// Ícone da aplicação para as janelas. Carrega o ícone embutido no executável
5+
/// (definido por &lt;ApplicationIcon&gt; no csproj), em cache.
6+
/// </summary>
7+
internal static class AppIcon
8+
{
9+
private static Icon? _icon;
10+
private static bool _tried;
11+
12+
/// <summary>Ícone do app, ou null se não for possível carregá-lo (ex.: design-time).</summary>
13+
public static Icon? Value
14+
{
15+
get
16+
{
17+
if (_tried) return _icon;
18+
_tried = true;
19+
try
20+
{
21+
var exe = Environment.ProcessPath;
22+
if (!string.IsNullOrEmpty(exe))
23+
_icon = Icon.ExtractAssociatedIcon(exe);
24+
}
25+
catch
26+
{
27+
_icon = null; // não impede a UI de abrir
28+
}
29+
return _icon;
30+
}
31+
}
32+
}

UI/ConnectionForm.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public ConnectionForm()
1818
if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
1919
return;
2020

21+
if (AppIcon.Value is { } icon) Icon = icon;
22+
2123
_settings = _store.Load();
2224
_panelA.Initialize("A", _settings, _store, OnSettingsChanged);
2325
_panelB.Initialize("B", _settings, _store, OnSettingsChanged);

UI/MainForm.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ public MainForm(ConnectionInfo connA, ConnectionInfo connB,
4242
_nameA = connA.DisplayName;
4343
_nameB = connB.DisplayName;
4444

45+
if (AppIcon.Value is { } icon) Icon = icon;
46+
4547
Text = $"SQLStructDiff — {connA.FullDisplayName} × {connB.FullDisplayName}";
4648
_txtA.HeaderText = _nameA;
4749
_txtB.HeaderText = _nameB;
@@ -380,6 +382,7 @@ private void SetControlsEnabled(bool enabled)
380382
DbObjectType.View => "Views",
381383
DbObjectType.Procedure => "Procedures",
382384
DbObjectType.Index => "Índices",
385+
DbObjectType.Trigger => "Triggers",
383386
_ => type.ToString()
384387
};
385388

appicon.ico

14.5 KB
Binary file not shown.

0 commit comments

Comments
 (0)