Skip to content

Unable to map a composite over ranges #5650

Description

@vonzshik
[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;

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions