Skip to content

Commit 0732a21

Browse files
vbilopavclaude
andcommitted
Replace ParseErrorMode.Throw with Exit (log + exit process), make it the default
Errors in SQL files now log at Error level and exit the process by default, catching SQL mistakes at startup. Use Skip to continue past errors in production. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0c22903 commit 0732a21

8 files changed

Lines changed: 25 additions & 25 deletions

File tree

NpgsqlRestClient/ConfigDefaults.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -944,7 +944,7 @@ private static JsonObject GetSqlFileSourceDefaults()
944944
["FilePattern"] = "",
945945
["CommentsMode"] = "ParseAll",
946946
["CommentScope"] = "All",
947-
["ErrorMode"] = "Skip",
947+
["ErrorMode"] = "Exit",
948948
["ResultPrefix"] = "result"
949949
};
950950
}

NpgsqlRestClient/ConfigSchemaGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ public static partial class ConfigSchemaGenerator
455455
["NpgsqlRest:SqlFileSource:FilePattern"] = "Glob pattern for SQL files, e.g. \"sql/**/*.sql\", \"queries/*.sql\".\nSupports * (any chars), ** (recursive, any including /), ? (single char).\nEmpty string disables the feature.",
456456
["NpgsqlRest:SqlFileSource:CommentsMode"] = "How comment annotations are processed for SQL file endpoints.\nPossible values: Ignore, ParseAll, OnlyWithHttpTag.\nDefault: ParseAll — every SQL file becomes an endpoint, comments configure it.",
457457
["NpgsqlRest:SqlFileSource:CommentScope"] = "Which comments in the SQL file to parse as annotations.\nPossible values: All (default — all comments), Header (only comments before the first statement).",
458-
["NpgsqlRest:SqlFileSource:ErrorMode"] = "Behavior when a SQL file fails to parse or describe.\nPossible values: Skip (default — log error, continue), Throw (halt startup).",
458+
["NpgsqlRest:SqlFileSource:ErrorMode"] = "Behavior when a SQL file fails to parse or describe.\nPossible values: Exit (default — log error, exit process), Skip (log error, continue).",
459459
["NpgsqlRest:SqlFileSource:ResultPrefix"] = "Prefix for result keys in multi-command JSON responses.\nDefault keys are \"result1\", \"result2\", etc. Override per-result with @resultN annotation in the SQL file.",
460460
};
461461

NpgsqlRestClient/ConfigTemplate.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2604,7 +2604,7 @@ public static partial class ConfigSchemaGenerator
26042604
// Behavior when a SQL file fails to parse or describe.
26052605
// Possible values: Skip (default — log error, continue), Throw (halt startup).
26062606
//
2607-
"ErrorMode": "Skip",
2607+
"ErrorMode": "Exit",
26082608
//
26092609
// Prefix for result keys in multi-command JSON responses.
26102610
// Default keys are "result1", "result2", etc.

NpgsqlRestClient/appsettings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2595,7 +2595,7 @@
25952595
// Behavior when a SQL file fails to parse or describe.
25962596
// Possible values: Skip (default — log error, continue), Throw (halt startup).
25972597
//
2598-
"ErrorMode": "Skip",
2598+
"ErrorMode": "Exit",
25992599
//
26002600
// Prefix for result keys in multi-command JSON responses.
26012601
// Default keys are "result1", "result2", etc.

NpgsqlRestTests/SqlFileSourceTests/IntegrationTests/ConfigurationTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ public void DefaultOptions_HasDefaultValues()
2323
{
2424
var options = new SqlFileSourceOptions();
2525
options.FilePattern.Should().NotBeNull();
26-
options.ErrorMode.Should().Be(ParseErrorMode.Skip);
26+
options.ErrorMode.Should().Be(ParseErrorMode.Exit);
2727
}
2828
}

changelog.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,8 @@ Override with the `@path` annotation: `-- @path /custom/path/{id}`
227227

228228
| Mode | Behavior | Use Case |
229229
|---|---|---|
230-
| `ParseErrorMode.Skip` (default) | Logs warning, skips file, continues | Production |
231-
| `ParseErrorMode.Throw` | Throws exception, halts startup | Development / CI |
230+
| `ParseErrorMode.Exit` (default) | Logs error, exits process | Fail-fast — catches SQL errors at startup |
231+
| `ParseErrorMode.Skip` | Logs error, skips file, continues | Production — tolerate partial failures |
232232

233233
Errors caught at startup:
234234
- Parse errors (malformed SQL, unclosed strings/quotes)
@@ -271,7 +271,7 @@ SQL file endpoints support all features available to function/procedure endpoint
271271
| `FilePattern` | string | `""` | Glob pattern for SQL files. Supports `*`, `**` (recursive), `?`. Empty = disabled |
272272
| `CommentsMode` | enum | `ParseAll` | `ParseAll` = every file becomes an endpoint. `OnlyWithHttpTag` = requires `HTTP` annotation |
273273
| `CommentScope` | enum | `All` | `All` = parse all comments. `Header` = only before first statement |
274-
| `ErrorMode` | enum | `Skip` | `Skip` = log + continue. `Throw` = halt startup |
274+
| `ErrorMode` | enum | `Exit` | `Exit` = log error + exit process. `Skip` = log error + continue |
275275
| `ResultPrefix` | string | `"result"` | Prefix for multi-command result keys (e.g., `result1`, `result2`) |
276276

277277
---

plugins/NpgsqlRest.SqlFileSource/SqlFileSource.cs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@ public class SqlFileSource(SqlFileSourceOptions options) : IEndpointSource
5454
}
5555
catch (Exception ex)
5656
{
57-
if (options.ErrorMode == ParseErrorMode.Throw)
57+
NpgsqlRestOptions.Logger?.LogError("SqlFileSource: Error processing file {FilePath}: {Error}", filePath, ex.Message);
58+
if (options.ErrorMode == ParseErrorMode.Exit)
5859
{
59-
throw;
60+
Environment.Exit(1);
6061
}
61-
NpgsqlRestOptions.Logger?.LogError("SqlFileSource: Error processing file {FilePath}: {Error}", filePath, ex.Message);
6262
}
6363

6464
if (result is not null)
@@ -90,12 +90,12 @@ private static (Routine, IRoutineSourceParameterFormatter)? ProcessFile(
9090
{
9191
foreach (var error in parseResult.Errors)
9292
{
93-
if (options.ErrorMode == ParseErrorMode.Throw)
94-
{
95-
throw new InvalidOperationException($"SqlFileSource: {filePath}: {error}");
96-
}
9793
NpgsqlRestOptions.Logger?.LogError("SqlFileSource: {FilePath}: {Error}", filePath, error);
9894
}
95+
if (options.ErrorMode == ParseErrorMode.Exit)
96+
{
97+
Environment.Exit(1);
98+
}
9999
return null;
100100
}
101101

@@ -119,11 +119,11 @@ private static (Routine, IRoutineSourceParameterFormatter)? ProcessFile(
119119
var describeResult = SqlFileDescriber.Describe(connection, stmt, stmtParamCount);
120120
if (describeResult.HasError)
121121
{
122-
if (options.ErrorMode == ParseErrorMode.Throw)
122+
NpgsqlRestOptions.Logger?.LogError("SqlFileSource: {FilePath}: Describe failed: {Error}", filePath, describeResult.Error);
123+
if (options.ErrorMode == ParseErrorMode.Exit)
123124
{
124-
throw new InvalidOperationException($"SqlFileSource: {filePath}: Describe failed: {describeResult.Error}");
125+
Environment.Exit(1);
125126
}
126-
NpgsqlRestOptions.Logger?.LogError("SqlFileSource: {FilePath}: Describe failed: {Error}", filePath, describeResult.Error);
127127
return null;
128128
}
129129
commandDescribes.Add(describeResult);
@@ -140,11 +140,11 @@ private static (Routine, IRoutineSourceParameterFormatter)? ProcessFile(
140140
if (existing != "unknown" && existing != pType)
141141
{
142142
var error = $"Parameter ${i + 1} has conflicting types across statements: '{existing}' vs '{pType}'. Use @param annotation to override.";
143-
if (options.ErrorMode == ParseErrorMode.Throw)
143+
NpgsqlRestOptions.Logger?.LogError("SqlFileSource: {FilePath}: {Error}", filePath, error);
144+
if (options.ErrorMode == ParseErrorMode.Exit)
144145
{
145-
throw new InvalidOperationException($"SqlFileSource: {filePath}: {error}");
146+
Environment.Exit(1);
146147
}
147-
NpgsqlRestOptions.Logger?.LogError("SqlFileSource: {FilePath}: {Error}", filePath, error);
148148
return null;
149149
}
150150
}

plugins/NpgsqlRest.SqlFileSource/SqlFileSourceOptions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ public enum CommentScope
1616
/// </summary>
1717
public enum ParseErrorMode
1818
{
19-
/// <summary>Log error, skip the file, continue — default (production-safe).</summary>
19+
/// <summary>Log error, skip the file, continue.</summary>
2020
Skip,
21-
/// <summary>Throw exception, halt startup (good for dev/CI).</summary>
22-
Throw
21+
/// <summary>Log error and exit the process — default.</summary>
22+
Exit
2323
}
2424

2525
/// <summary>
@@ -48,7 +48,7 @@ public class SqlFileSourceOptions
4848
/// <summary>
4949
/// Behavior when a file fails to parse or describe.
5050
/// </summary>
51-
public ParseErrorMode ErrorMode { get; set; } = ParseErrorMode.Skip;
51+
public ParseErrorMode ErrorMode { get; set; } = ParseErrorMode.Exit;
5252

5353
/// <summary>
5454
/// Prefix for result keys in multi-command JSON responses.

0 commit comments

Comments
 (0)