diff --git a/DynamicLinqPadPostgreSqlDriver.Shared/DynamicLinqPadPostgreSqlDriver.Shared.csproj b/DynamicLinqPadPostgreSqlDriver.Shared/DynamicLinqPadPostgreSqlDriver.Shared.csproj
index c2eb50e..409faae 100644
--- a/DynamicLinqPadPostgreSqlDriver.Shared/DynamicLinqPadPostgreSqlDriver.Shared.csproj
+++ b/DynamicLinqPadPostgreSqlDriver.Shared/DynamicLinqPadPostgreSqlDriver.Shared.csproj
@@ -9,8 +9,9 @@
Properties
DynamicLinqPadPostgreSqlDriver.Shared
DynamicLinqPadPostgreSqlDriver.Shared
- v4.5.2
+ v4.6
512
+
true
diff --git a/DynamicLinqPadPostgreSqlDriver.Tests/DynamicAssemblyGeneration/TestPostgreSqlFunctions.cs b/DynamicLinqPadPostgreSqlDriver.Tests/DynamicAssemblyGeneration/TestPostgreSqlFunctions.cs
index 10039bd..7422533 100644
--- a/DynamicLinqPadPostgreSqlDriver.Tests/DynamicAssemblyGeneration/TestPostgreSqlFunctions.cs
+++ b/DynamicLinqPadPostgreSqlDriver.Tests/DynamicAssemblyGeneration/TestPostgreSqlFunctions.cs
@@ -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.
diff --git a/DynamicLinqPadPostgreSqlDriver.Tests/DynamicLinqPadPostgreSqlDriver.Tests.csproj b/DynamicLinqPadPostgreSqlDriver.Tests/DynamicLinqPadPostgreSqlDriver.Tests.csproj
index e0e542a..c3a3251 100644
--- a/DynamicLinqPadPostgreSqlDriver.Tests/DynamicLinqPadPostgreSqlDriver.Tests.csproj
+++ b/DynamicLinqPadPostgreSqlDriver.Tests/DynamicLinqPadPostgreSqlDriver.Tests.csproj
@@ -11,10 +11,11 @@
Properties
DynamicLinqPadPostgreSqlDriver.Tests
DynamicLinqPadPostgreSqlDriver.Tests
- v4.5.2
+ v4.6
512
+
true
diff --git a/DynamicLinqPadPostgreSqlDriver.Tests/app.config b/DynamicLinqPadPostgreSqlDriver.Tests/app.config
index 6e16f08..cc47454 100644
--- a/DynamicLinqPadPostgreSqlDriver.Tests/app.config
+++ b/DynamicLinqPadPostgreSqlDriver.Tests/app.config
@@ -1,11 +1,11 @@
-
+
-
-
+
+
-
\ No newline at end of file
+
diff --git a/DynamicLinqPadPostgreSqlDriver.UI/DynamicLinqPadPostgreSqlDriver.UI.csproj b/DynamicLinqPadPostgreSqlDriver.UI/DynamicLinqPadPostgreSqlDriver.UI.csproj
index 8a0dcc2..c6cb0f4 100644
--- a/DynamicLinqPadPostgreSqlDriver.UI/DynamicLinqPadPostgreSqlDriver.UI.csproj
+++ b/DynamicLinqPadPostgreSqlDriver.UI/DynamicLinqPadPostgreSqlDriver.UI.csproj
@@ -9,7 +9,7 @@
Properties
DynamicLinqPadPostgreSqlDriver.UI
DynamicLinqPadPostgreSqlDriver.UI
- v4.5.2
+ v4.6
512
{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
4
diff --git a/DynamicLinqPadPostgreSqlDriver/DynamicLinqPadPostgreSqlDriver.csproj b/DynamicLinqPadPostgreSqlDriver/DynamicLinqPadPostgreSqlDriver.csproj
index f619a34..956f148 100644
--- a/DynamicLinqPadPostgreSqlDriver/DynamicLinqPadPostgreSqlDriver.csproj
+++ b/DynamicLinqPadPostgreSqlDriver/DynamicLinqPadPostgreSqlDriver.csproj
@@ -9,7 +9,7 @@
Properties
DynamicLinqPadPostgreSqlDriver
DynamicLinqPadPostgreSqlDriver
- v4.5.2
+ v4.6
512
diff --git a/DynamicLinqPadPostgreSqlDriver/PostgreSqlFunctionsProvider.cs b/DynamicLinqPadPostgreSqlDriver/PostgreSqlFunctionsProvider.cs
index 3a35599..9d16a0a 100644
--- a/DynamicLinqPadPostgreSqlDriver/PostgreSqlFunctionsProvider.cs
+++ b/DynamicLinqPadPostgreSqlDriver/PostgreSqlFunctionsProvider.cs
@@ -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)
@@ -153,7 +150,7 @@ private List 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});
diff --git a/DynamicLinqPadPostgreSqlDriver/SqlHelper.cs b/DynamicLinqPadPostgreSqlDriver/SqlHelper.cs
index 420a7bb..aa4872a 100644
--- a/DynamicLinqPadPostgreSqlDriver/SqlHelper.cs
+++ b/DynamicLinqPadPostgreSqlDriver/SqlHelper.cs
@@ -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 SqlCache = new Dictionary();
+ private static readonly Regex typeLengthPattern = new Regex("([a-z0-9\\s]+)(\\([0-9]+\\))");
public static string LoadSql(string name)
{
@@ -36,12 +38,26 @@ public static string LoadSql(string name)
}
}
+ ///
+ /// Deal with mismatched cases and length specifications following the type name
+ ///
+ 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:
//