Skip to content

Fix udt recognition #33

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DynamicLinqPadPostgreSqlDriver.Shared</RootNamespace>
<AssemblyName>DynamicLinqPadPostgreSqlDriver.Shared</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,21 +111,29 @@ public void TestUserDefinedTypeRecognition()
dynamic dc = ArrangeDataContext(db =>
{
DBConnection.Execute("CREATE TYPE testtype AS (id int, name text, arrayprop integer[]);");
DBConnection.Execute("CREATE TYPE testtype_outer AS (param testtype);");
// Having 2 functions ensures that we correctly test for the existence of a previously created UDT
DBConnection.Execute("CREATE FUNCTION public.echo_udt(testtype) RETURNS testtype AS 'SELECT $1' LANGUAGE SQL;");
DBConnection.Execute("CREATE FUNCTION public.echo_outer_udt(testtype_outer) RETURNS testtype_outer AS 'SELECT $1' LANGUAGE SQL;");
});

Type dcType = dc.GetType();
var userDefinedType = dcType.Assembly.GetType(NameSpace + ".Testtype");
var outerType = dcType.Assembly.GetType(NameSpace + ".Testtype_Outer");

Assert.NotNull(userDefinedType);
Assert.NotNull(outerType);

dynamic customObj = Activator.CreateInstance(userDefinedType);
customObj.Id = 1;
customObj.Name = "1";

// verify function is found and has correct signature
var query = dc.echo_udt(customObj);
dynamic customObjOuter = Activator.CreateInstance(outerType);
customObjOuter.Param = customObj;

// verify function is found and has correct signature
var query = dc.echo_outer_udt(customObjOuter);

// Enumerating 'query' throws something like the following:
// System.NotSupportedExceptionThis .NET type is not supported in Npgsql or your PostgreSQL: LINQPad.User.Testtype
// So for now, user-defined types are recognized, but functions expecting them as parameters cannot be called via LINQPad.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DynamicLinqPadPostgreSqlDriver.Tests</RootNamespace>
<AssemblyName>DynamicLinqPadPostgreSqlDriver.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
Expand Down
8 changes: 4 additions & 4 deletions DynamicLinqPadPostgreSqlDriver.Tests/app.config
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Moq" publicKeyToken="69f491c39445e920" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.7.142.0" newVersion="4.7.142.0" />
<assemblyIdentity name="Moq" publicKeyToken="69f491c39445e920" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.7.142.0" newVersion="4.7.142.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup></configuration>
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DynamicLinqPadPostgreSqlDriver.UI</RootNamespace>
<AssemblyName>DynamicLinqPadPostgreSqlDriver.UI</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DynamicLinqPadPostgreSqlDriver</RootNamespace>
<AssemblyName>DynamicLinqPadPostgreSqlDriver</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
Expand Down
53 changes: 25 additions & 28 deletions DynamicLinqPadPostgreSqlDriver/PostgreSqlFunctionsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,46 +73,43 @@ private FunctionReturnTypeInfo DetermineReturnType(FunctionData func)
return funcReturnTypeInfo;
}

private bool TryCreateUserDefinedType(string argType, out Type mappedArgType)
private Type GetOrCreateUserDefinedType(string argType)
{
bool isArray = argType.StartsWith("_");
var pgTypeName = argType.TrimStart('_');
var udtAttributes = connection.Query(SqlHelper.LoadSql("QueryUdtAttributes.sql"), new { typname = pgTypeName }).ToList();
var typeName = $"{nameSpace}.{cxInfo.GetTypeName(pgTypeName)}";

mappedArgType = null;
if (udtAttributes.Any())
var mappedArgType = moduleBuilder.GetType(typeName);
if (mappedArgType != null)
{
var typeName = $"{nameSpace}.{cxInfo.GetTypeName(pgTypeName)}";
var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
return mappedArgType;
}

var udtAttributes = connection.Query(SqlHelper.LoadSql("QueryUdtAttributes.sql"), new { typname = pgTypeName }).ToList();

var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);

foreach (var attr in udtAttributes)
foreach (var attr in udtAttributes)
{
var attrName = cxInfo.GetColumnName((string)attr.AttributeName);
var attrTypeData = DbTypeData.FromString(attr.AttributeType);
Type attrType = SqlHelper.MapDbTypeToType(attrTypeData.DbType, attrTypeData.UdtName, false, true);
if (attrType == null)
{
var attrName = cxInfo.GetColumnName((string)attr.AttributeName);
var attrTypeData = DbTypeData.FromString(attr.AttributeType);
var attrType = SqlHelper.MapDbTypeToType(attrTypeData.DbType, attrTypeData.UdtName, false, true);
if (attrType == null)
{
throw new InvalidOperationException("Unknown type: " + attr.AttributeType);
}

typeBuilder.DefineField(attrName, attrType, FieldAttributes.Public);
attrType = GetOrCreateUserDefinedType(attrTypeData.DbType);
}

var udt = typeBuilder.CreateType();
typeBuilder.DefineField(attrName, attrType, FieldAttributes.Public);
}

if (isArray)
{
mappedArgType = udt.MakeArrayType();
}
else
{
mappedArgType = typeBuilder.CreateType();
}
var udt = typeBuilder.CreateType();

return true;
if (isArray)
{
return udt.MakeArrayType();
}

return false;
return udt;
}

private bool TryMapToExistingType(string argType, out Type mappedArgType)
Expand Down Expand Up @@ -153,7 +150,7 @@ private List<FunctionArgumentInfo> GetFunctionArgumentInfos(FunctionData func)

if (!TryMapToExistingType(argType, out mappedArgType))
{
TryCreateUserDefinedType(argType, out mappedArgType);
mappedArgType = GetOrCreateUserDefinedType(argType);
}

paramTypes.Add(new FunctionArgumentInfo { Index = i, Name = argName, Type = mappedArgType, DbTypeName = argType});
Expand Down
18 changes: 17 additions & 1 deletion DynamicLinqPadPostgreSqlDriver/SqlHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
using System.Net;
using System.Net.NetworkInformation;
using System.Reflection;
using System.Text.RegularExpressions;

namespace DynamicLinqPadPostgreSqlDriver
{
public static class SqlHelper
{
private static readonly IDictionary<string, string> SqlCache = new Dictionary<string, string>();
private static readonly Regex typeLengthPattern = new Regex("([a-z0-9\\s]+)(\\([0-9]+\\))");

public static string LoadSql(string name)
{
Expand All @@ -36,12 +38,26 @@ public static string LoadSql(string name)
}
}

/// <summary>
/// Deal with mismatched cases and length specifications following the type name
/// </summary>
private static string SanitizeTypeName(string dbType)
{
var sanitizedTypeName = dbType.ToLowerInvariant();
var match = typeLengthPattern.Match(sanitizedTypeName);
if (match.Success)
{
sanitizedTypeName = match.Groups[1].Value.TrimEnd();
}
return sanitizedTypeName;
}

public static Type MapDbTypeToType(string dbType, string udtName, bool nullable, bool useExperimentalTypes)
{
if (dbType == null)
throw new ArgumentNullException(nameof(dbType));

dbType = dbType.ToLower();
dbType = SanitizeTypeName(dbType);

// See the PostgreSQL datatypes as reference:
//
Expand Down