[Test]
public async Task CompositeOverRange()
{
await using var adminConnection = await OpenConnectionAsync();
var type = await GetTempTypeName(adminConnection);
var rangeType = await GetTempTypeName(adminConnection);
await adminConnection.ExecuteNonQueryAsync($"CREATE TYPE {type} AS (x int, some_text text); CREATE TYPE {rangeType} AS RANGE(subtype={type})");
var dataSourceBuilder = CreateDataSourceBuilder();
dataSourceBuilder.MapComposite<SomeComposite>(type);
dataSourceBuilder.EnableUnmappedTypes();
await using var dataSource = dataSourceBuilder.Build();
await using var connection = await dataSource.OpenConnectionAsync();
var composite1 = new SomeComposite
{
SomeText = "foo",
X = 8
};
var composite2 = new SomeComposite
{
SomeText = "bar",
X = 42
};
await AssertType(
connection,
new NpgsqlRange<SomeComposite>(composite1, composite2),
"[\"(8,foo)\",\"(42,bar)\"]",
rangeType,
npgsqlDbType: null,
isDefaultForWriting: false);
}
Throws:
System.NotSupportedException : The data type name 'temp_type2' isn't present in your database. You may need to install an extension or upgrade to a newer version.
at Npgsql.NpgsqlParameter.<ResolveTypeInfo>g__ThrowNotSupported|111_1(String dataTypeName) in C:\reps\npgsql\src\Npgsql\NpgsqlParameter.cs:line 602
...
The problem is the order in which we load the types from the database:
|
ORDER BY CASE |
|
WHEN typtype IN ('b', 'e', 'p') THEN 0 -- First base types, enums, pseudo-types |
|
WHEN typtype = 'r' THEN 1 -- Ranges after |
|
WHEN typtype = 'm' THEN 2 -- Multiranges after |
|
WHEN typtype = 'c' THEN 3 -- Composites after |
|
WHEN typtype = 'd' AND elemtyptype <> 'a' THEN 4 -- Domains over non-arrays after |
|
WHEN typtype = 'a' THEN 5 -- Arrays after |
|
WHEN typtype = 'd' AND elemtyptype = 'a' THEN 6 -- Domains over arrays last |
|
END;"; |
Because ranges are loaded before composites, we can't find the base type, in which case we're just going to skip that range:
|
case 'r': // Range |
|
{ |
|
Debug.Assert(elemtypoid > 0); |
|
if (!byOID.TryGetValue(elemtypoid, out var subtypePostgresType)) |
|
{ |
|
_connectionLogger.LogTrace("Range type '{RangeTypeName}' refers to unknown subtype with OID {ElementTypeOID}, skipping", |
|
typname, elemtypoid); |
|
continue; |
|
} |
|
|
|
var rangeType = new PostgresRangeType(nspname, typname, oid, subtypePostgresType); |
|
byOID[rangeType.OID] = rangeType; |
|
continue; |
|
} |
Ideally, we should have a list of unmapped types, which we should attempt to map after we load everything. But since no one complained before, we just can load composites before ranges, as they're always loaded.
|
case 'c': // Composite |
|
var compositeType = new PostgresCompositeType(nspname, typname, oid); |
|
byOID[compositeType.OID] = compositeType; |
|
continue; |
Throws:
The problem is the order in which we load the types from the database:
npgsql/src/Npgsql/PostgresDatabaseInfo.cs
Lines 153 to 161 in 132a42d
Because ranges are loaded before composites, we can't find the base type, in which case we're just going to skip that range:
npgsql/src/Npgsql/PostgresDatabaseInfo.cs
Lines 360 to 373 in 132a42d
Ideally, we should have a list of unmapped types, which we should attempt to map after we load everything. But since no one complained before, we just can load composites before ranges, as they're always loaded.
npgsql/src/Npgsql/PostgresDatabaseInfo.cs
Lines 400 to 403 in 132a42d