Skip to content

Commit dff7860

Browse files
committed
2.8.2 initial
1 parent 81d8406 commit dff7860

7 files changed

Lines changed: 168 additions & 18 deletions

File tree

NpgsqlRest/MiddlewareExtension.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ await options.ValidateParametersAsync(new(
216216
}
217217
if (options.ValidateParameters is not null || options.ValidateParametersAsync is not null)
218218
{
219-
if (options.ValidateParameters is not null)
219+
if (options.ValidateParameters is not null && parameter.Value is not null)
220220
{
221221
options.ValidateParameters(new(
222222
context,
@@ -228,7 +228,7 @@ await options.ValidateParametersAsync(new(
228228
queryStringValues: qsValue,
229229
jsonBodyNode: null));
230230
}
231-
if (options.ValidateParametersAsync is not null)
231+
if (options.ValidateParametersAsync is not null && parameter.Value is not null)
232232
{
233233
await options.ValidateParametersAsync(new(
234234
context,

NpgsqlRest/NpgsqlRest.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@
2828
<GenerateDocumentationFile>true</GenerateDocumentationFile>
2929
<PackageReadmeFile>README.MD</PackageReadmeFile>
3030
<DocumentationFile>bin\$(Configuration)\$(AssemblyName).xml</DocumentationFile>
31-
<Version>2.8.1</Version>
32-
<AssemblyVersion>2.8.1</AssemblyVersion>
33-
<FileVersion>2.8.1</FileVersion>
34-
<PackageVersion>2.8.1</PackageVersion>
31+
<Version>2.8.2</Version>
32+
<AssemblyVersion>2.8.2</AssemblyVersion>
33+
<FileVersion>2.8.2</FileVersion>
34+
<PackageVersion>2.8.2</PackageVersion>
3535
</PropertyGroup>
3636

3737
<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">

NpgsqlRest/RoutineSource.cs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Npgsql;
33
using NpgsqlTypes;
44
using NpgsqlRest.Extensions;
5+
using System.Text.RegularExpressions;
56

67
namespace NpgsqlRest;
78

@@ -85,11 +86,67 @@ public class RoutineSource(
8586
returnTypeDescriptor = returnRecordTypes.Select(x => new TypeDescriptor(x)).ToArray();
8687
}
8788
var returnRecordNames = reader.Get<string[]>(9);//"return_record_names");
88-
var paramDefaults = reader.Get<string?[]>(15);//"param_defaults");
89+
8990
bool isUnnamedRecord = reader.Get<bool>(11);// "is_unnamed_record");
9091
var routineType = type.GetEnum<RoutineType>();
9192
var callIdent = routineType == RoutineType.Procedure ? "call " : "select ";
9293
var paramCount = reader.Get<int>(12);// "param_count");
94+
95+
var argumentDef = reader.Get<string>(15);
96+
string?[] paramDefaults = new string?[paramCount];
97+
bool[] hasParamDefaults = new bool[paramCount];
98+
99+
if (string.IsNullOrEmpty(argumentDef))
100+
{
101+
for (int i = 0; i < paramCount; i++)
102+
{
103+
paramDefaults[i] = null;
104+
hasParamDefaults[i] = false;
105+
}
106+
}
107+
else
108+
{
109+
const string defaultArgExp = " DEFAULT ";
110+
for (int i = 0; i < paramCount; i++)
111+
{
112+
string paramName = paramNames[i] ?? "";
113+
string? nextParamName = i < paramCount - 1 ? paramNames[i + 1] : null;
114+
115+
int startIndex = argumentDef.IndexOf(paramName);
116+
int endIndex = nextParamName != null ? argumentDef.IndexOf(string.Concat(", " + nextParamName, " ")) : argumentDef.Length;
117+
118+
if (startIndex != -1 && endIndex != -1 && startIndex < endIndex)
119+
{
120+
string paramDef = argumentDef[startIndex..endIndex];
121+
122+
int defaultIndex = paramDef.IndexOf(defaultArgExp);
123+
if (defaultIndex != -1)
124+
{
125+
string defaultValue = paramDef[(defaultIndex + 9)..].Trim();
126+
127+
if (defaultValue.EndsWith(','))
128+
{
129+
defaultValue = defaultValue[..^1].Trim();
130+
}
131+
132+
paramDefaults[i] = defaultValue;
133+
hasParamDefaults[i] = true;
134+
}
135+
else
136+
{
137+
paramDefaults[i] = null;
138+
hasParamDefaults[i] = false;
139+
}
140+
}
141+
else
142+
{
143+
paramDefaults[i] = null;
144+
hasParamDefaults[i] = false;
145+
}
146+
}
147+
148+
}
149+
93150
var returnRecordCount = reader.Get<int>(8);// "return_record_count");
94151
var variadic = reader.Get<bool>(16);// "has_variadic");
95152
var expression = string.Concat(
@@ -166,7 +223,7 @@ public class RoutineSource(
166223
paramCount: paramCount,
167224
paramNames: paramNames,
168225
paramTypeDescriptor: paramTypes
169-
.Select((x, i) => new TypeDescriptor(x, hasDefault: paramDefaults[i] is not null))
226+
.Select((x, i) => new TypeDescriptor(x, hasDefault: hasParamDefaults[i] is true))
170227
.ToArray(),
171228

172229
expression: expression,

NpgsqlRest/RoutineSourceParameterFormatter.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,23 @@ public string AppendCommandParameter(ref NpgsqlRestParameter parameter, ref int
1111
{
1212
if (count == 1)
1313
{
14-
return string.Concat("$1", suffix, ")");
14+
return parameter.ActualName is null ?
15+
string.Concat("$1", suffix, ")") :
16+
string.Concat(parameter.ActualName, "=>$1", suffix, ")");
1517
}
16-
return string.Concat("$1", suffix);
18+
return parameter.ActualName is null ?
19+
string.Concat("$1", suffix) :
20+
string.Concat(parameter.ActualName, "=>$1", suffix);
1721
}
1822
if (index == count - 1)
1923
{
20-
return string.Concat(",$", (index + 1).ToString(), suffix, ")");
24+
return parameter.ActualName is null ?
25+
string.Concat(",", "$", (index + 1).ToString(), suffix, ")") :
26+
string.Concat(",", parameter.ActualName, "=>$", (index + 1).ToString(), suffix, ")");
2127
}
22-
return string.Concat(",$", (index + 1).ToString(), suffix);
28+
return parameter.ActualName is null ?
29+
string.Concat(",", "$", (index + 1).ToString(), suffix) :
30+
string.Concat(",", parameter.ActualName, "=>$", (index + 1).ToString(), suffix);
2331
}
2432

2533
public string? AppendEmpty() => ")";

NpgsqlRest/RoutineSourceQuery.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,8 @@ order by p.ordinal_position
4747
'{}'::text[]
4848
)
4949
end as out_param_types,
50-
51-
coalesce(
52-
array_agg(p.parameter_default order by p.ordinal_position) filter(where p.parameter_mode = 'IN' or p.parameter_mode = 'INOUT'),
53-
'{}'::text[]
54-
) as in_param_defaults,
50+
51+
pg_get_function_arguments(proc.oid) as arguments_def,
5552
5653
case when proc.provariadic <> 0 then true else false end as has_variadic,
5754
@@ -140,7 +137,7 @@ when array_length(coalesce(cte1.out_param_types, cte2.out_param_types), 1) is nu
140137
array_length(in_params, 1) as param_count,
141138
in_params as param_names,
142139
in_param_types as param_types,
143-
in_param_defaults as param_defaults,
140+
arguments_def,
144141
has_variadic,
145142
pg_get_functiondef(oid) as definition
146143
from cte1

NpgsqlRestTests/DefaultParametersTests.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ language sql
5252
$$
5353
select _p;
5454
$$;
55+
56+
57+
create function get_two_default_params(_p1 text = 'abc', _p2 text = 'xyz')
58+
returns text
59+
language sql
60+
as
61+
$$
62+
select _p1 || _p2;
63+
$$;
5564
");
5665
}
5766
}
@@ -204,4 +213,48 @@ public async Task Test_case_single_default_params_NullBody()
204213
result?.Content?.Headers?.ContentType?.MediaType.Should().Be("text/plain");
205214
response.Should().Be("xyz");
206215
}
216+
217+
[Fact]
218+
public async Task Test_get_two_default_params__1()
219+
{
220+
using var result = await test.Client.GetAsync("/api/get-two-default-params/?p1=aa&p2=bb");
221+
var response = await result.Content.ReadAsStringAsync();
222+
223+
result?.StatusCode.Should().Be(HttpStatusCode.OK);
224+
result?.Content?.Headers?.ContentType?.MediaType.Should().Be("text/plain");
225+
response.Should().Be("aabb");
226+
}
227+
228+
[Fact]
229+
public async Task Test_get_two_default_params__2()
230+
{
231+
using var result = await test.Client.GetAsync("/api/get-two-default-params/?p1=aa");
232+
var response = await result.Content.ReadAsStringAsync();
233+
234+
result?.StatusCode.Should().Be(HttpStatusCode.OK);
235+
result?.Content?.Headers?.ContentType?.MediaType.Should().Be("text/plain");
236+
response.Should().Be("aaxyz");
237+
}
238+
239+
[Fact]
240+
public async Task Test_get_two_default_params__3()
241+
{
242+
using var result = await test.Client.GetAsync("/api/get-two-default-params/?p2=bb");
243+
var response = await result.Content.ReadAsStringAsync();
244+
245+
result?.StatusCode.Should().Be(HttpStatusCode.OK);
246+
result?.Content?.Headers?.ContentType?.MediaType.Should().Be("text/plain");
247+
response.Should().Be("abcbb");
248+
}
249+
250+
[Fact]
251+
public async Task Test_get_two_default_params__4()
252+
{
253+
using var result = await test.Client.GetAsync("/api/get-two-default-params/");
254+
var response = await result.Content.ReadAsStringAsync();
255+
256+
result?.StatusCode.Should().Be(HttpStatusCode.OK);
257+
result?.Content?.Headers?.ContentType?.MediaType.Should().Be("text/plain");
258+
response.Should().Be("abcxyz");
259+
}
207260
}

changelog.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,41 @@ Note: For a changelog for a client application [see the client application page
44

55
---
66

7+
## Version [2.8.2](https://github.com/vb-consulting/NpgsqlRest/tree/2.8.2) (2024-06-09)
8+
9+
[Full Changelog](https://github.com/vb-consulting/NpgsqlRest/compare/2.8.1...2.8.2)
10+
11+
### Fixed bug with default parameters
12+
13+
Using a routine that has a default parameters and supplying one of the parameters would sometimes caused mixing of the parameter order. For example, if the routine is defined as:
14+
15+
```sql
16+
create function get_two_default_params(
17+
_p1 text = 'abc',
18+
_p2 text = 'xyz'
19+
)
20+
returns text
21+
language sql
22+
as
23+
$$
24+
select _p1 || _p2;
25+
$$;
26+
```
27+
28+
And invoking it with only second parameter parameter (p2) would mix p1 and p2 and wrongly assume that the first parameter is the second parameter and vice versa.
29+
30+
This is now fixed and the parameters are correctly assigned.
31+
32+
### Fixed bug with default parameters for PostgreSQL roles that are not super-users.
33+
34+
When using a PostgreSQL role that is not a super-user, the default parameters were not correctly assigned.
35+
36+
This may be a bug in PostgreSQL itself (reported), column `parameter_default` in system table `information_schema.parameters` always returns `null` for roles that are not super-users.
37+
38+
Workaround is implemented and tested to use the `pg_get_function_arguments` function and then to parse the default values from the function definition.
39+
40+
---
41+
742
## Version [2.8.1](https://github.com/vb-consulting/NpgsqlRest/tree/2.8.1) (2024-05-10)
843

944
[Full Changelog](https://github.com/vb-consulting/NpgsqlRest/compare/2.8.0...2.8.1)

0 commit comments

Comments
 (0)