Skip to content

Commit 8467e1f

Browse files
authored
Merge pull request linq2db#2180 from linq2db/master
Release 2.9.8
2 parents 6064ab2 + 5d22b36 commit 8467e1f

19 files changed

+1630
-118
lines changed

Build/linq2db.Default.props

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
22
<PropertyGroup>
3-
<Version>2.9.7.0</Version>
3+
<Version>2.9.8.0</Version>
44

55
<Description>LINQ to DB is a data access technology that provides a run-time infrastructure for managing relational data as objects.</Description>
66
<Authors>Igor Tkachev, Ilya Chudin, Svyatoslav Danyliv, Dmitry Lukashenko</Authors>

CODE_OF_CONDUCT.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.
2+
For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct).

Data/Create Scripts/Oracle.sql

+21-2
Original file line numberDiff line numberDiff line change
@@ -593,8 +593,8 @@ INSERT INTO DataTypeTest
593593
Single_, Stream_, String_, UInt16_, UInt32_, UInt64_, Xml_)
594594
VALUES
595595
( NULL, NULL, NULL, NULL, NULL, NULL, NULL,
596-
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
597-
NULL, NULL, NULL, NULL, NULL, NULL, NULL)
596+
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
597+
NULL, NULL, NULL, NULL, NULL, NULL, NULL)
598598
/
599599

600600
INSERT INTO DataTypeTest
@@ -987,3 +987,22 @@ BEGIN
987987
INSERT INTO dbo.AllTypes(char20DataType) VALUES('issue792');
988988
END;
989989
/
990+
991+
CREATE OR REPLACE PACKAGE ISSUE2132 AS
992+
procedure test;
993+
END;
994+
/
995+
996+
CREATE OR REPLACE PACKAGE BODY ISSUE2132 AS
997+
procedure test is
998+
begin
999+
return 4;
1000+
end test;
1001+
END;
1002+
/
1003+
1004+
CREATE OR REPLACE PROCEDURE TEST2132
1005+
BEGIN
1006+
return 6;
1007+
END;
1008+
/

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ However, it's not as heavy as LINQ to SQL or Entity Framework. There is no chang
1313

1414
In other words **LINQ to DB is type-safe SQL**.
1515

16+
linq2db is a [.NET Foundation](https://dotnetfoundation.org/) project.
17+
18+
<img alt=".NET Foundation Logo" src="https://github.com/dotnet/swag/blob/master/logo/dotnetfoundation_v4_horizontal.png" width="250px" >
19+
1620
## Standout Features
1721

1822
- Rich Querying API:
+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using LinqToDB.Extensions;
4+
5+
namespace LinqToDB.Common
6+
{
7+
internal static class ConvertUtils
8+
{
9+
private static readonly IDictionary<Type, ISet<Type>> _alwaysConvert = new Dictionary<Type, ISet<Type>>()
10+
{
11+
{ typeof(byte) , new HashSet<Type>() { typeof(short), typeof(ushort), typeof(int) , typeof(uint), typeof(long), typeof(ulong) } },
12+
{ typeof(sbyte) , new HashSet<Type>() { typeof(short), typeof(ushort), typeof(int) , typeof(uint), typeof(long), typeof(ulong) } },
13+
{ typeof(short) , new HashSet<Type>() { typeof(int) , typeof(uint) , typeof(long), typeof(ulong) } },
14+
{ typeof(ushort), new HashSet<Type>() { typeof(int) , typeof(uint) , typeof(long), typeof(ulong) } },
15+
{ typeof(int) , new HashSet<Type>() { typeof(long) , typeof(ulong) } },
16+
{ typeof(uint) , new HashSet<Type>() { typeof(long) , typeof(ulong) } },
17+
{ typeof(long) , new HashSet<Type>() { } },
18+
{ typeof(ulong) , new HashSet<Type>() { } },
19+
};
20+
21+
private static readonly IDictionary<Type, IDictionary<Type, Tuple<IComparable, IComparable>>> _rangedConvert = new Dictionary<Type, IDictionary<Type, Tuple<IComparable, IComparable>>>()
22+
{
23+
{ typeof(byte) , new Dictionary<Type, Tuple<IComparable, IComparable>>()
24+
{
25+
{ typeof(sbyte), Tuple.Create((IComparable)(byte)0, (IComparable)(byte)sbyte.MaxValue) }
26+
}
27+
},
28+
{ typeof(sbyte) , new Dictionary<Type, Tuple<IComparable, IComparable>>()
29+
{
30+
{ typeof(byte), Tuple.Create((IComparable)(sbyte)0, (IComparable)sbyte.MaxValue) }
31+
}
32+
},
33+
{ typeof(short) , new Dictionary<Type, Tuple<IComparable, IComparable>>()
34+
{
35+
{ typeof(sbyte) , Tuple.Create((IComparable)(short)sbyte.MinValue, (IComparable)(short)sbyte.MaxValue) },
36+
{ typeof(byte ) , Tuple.Create((IComparable)(short)byte .MinValue, (IComparable)(short)byte .MaxValue) },
37+
{ typeof(ushort), Tuple.Create((IComparable)(short)0 , (IComparable) short.MaxValue) },
38+
}
39+
},
40+
{ typeof(ushort) , new Dictionary<Type, Tuple<IComparable, IComparable>>()
41+
{
42+
{ typeof(sbyte) , Tuple.Create((IComparable)(ushort)0, (IComparable)(ushort)sbyte.MaxValue) },
43+
{ typeof(byte ) , Tuple.Create((IComparable)(ushort)0, (IComparable)(ushort)byte .MaxValue) },
44+
{ typeof(short) , Tuple.Create((IComparable)(ushort)0, (IComparable)(ushort)short.MaxValue) },
45+
}
46+
},
47+
{ typeof(int) , new Dictionary<Type, Tuple<IComparable, IComparable>>()
48+
{
49+
{ typeof(sbyte) , Tuple.Create((IComparable)(int)sbyte .MinValue, (IComparable)(int)sbyte .MaxValue) },
50+
{ typeof(byte ) , Tuple.Create((IComparable)(int)byte .MinValue, (IComparable)(int)byte .MaxValue) },
51+
{ typeof(short) , Tuple.Create((IComparable)(int)short .MinValue, (IComparable)(int)short .MaxValue) },
52+
{ typeof(ushort), Tuple.Create((IComparable)(int)ushort.MinValue, (IComparable)(int)ushort .MaxValue) },
53+
{ typeof(uint) , Tuple.Create((IComparable)(int)0 , (IComparable) int .MaxValue) },
54+
}
55+
},
56+
{ typeof(uint) , new Dictionary<Type, Tuple<IComparable, IComparable>>()
57+
{
58+
{ typeof(sbyte ), Tuple.Create((IComparable)(uint)0, (IComparable)(uint)sbyte .MaxValue) },
59+
{ typeof(byte ), Tuple.Create((IComparable)(uint)0, (IComparable)(uint)byte .MaxValue) },
60+
{ typeof(short ), Tuple.Create((IComparable)(uint)0, (IComparable)(uint)short .MaxValue) },
61+
{ typeof(ushort), Tuple.Create((IComparable)(uint)0, (IComparable)(uint)ushort.MaxValue) },
62+
{ typeof(int ), Tuple.Create((IComparable)(uint)0, (IComparable)(uint)int .MaxValue) },
63+
}
64+
},
65+
{ typeof(long) , new Dictionary<Type, Tuple<IComparable, IComparable>>()
66+
{
67+
{ typeof(sbyte ), Tuple.Create((IComparable)(long)sbyte .MinValue, (IComparable)(long)sbyte .MaxValue) },
68+
{ typeof(byte ), Tuple.Create((IComparable)(long)byte .MinValue, (IComparable)(long)byte .MaxValue) },
69+
{ typeof(short ), Tuple.Create((IComparable)(long)short .MinValue, (IComparable)(long)short .MaxValue) },
70+
{ typeof(ushort), Tuple.Create((IComparable)(long)ushort.MinValue, (IComparable)(long)ushort.MaxValue) },
71+
{ typeof(int ), Tuple.Create((IComparable)(long)int .MinValue, (IComparable)(long)int .MaxValue) },
72+
{ typeof(uint ), Tuple.Create((IComparable)(long)uint .MinValue, (IComparable)(long)uint .MaxValue) },
73+
{ typeof(ulong ), Tuple.Create((IComparable)(long)0 , (IComparable) long .MaxValue) },
74+
}
75+
},
76+
{ typeof(ulong) , new Dictionary<Type, Tuple<IComparable, IComparable>>()
77+
{
78+
{ typeof(sbyte ), Tuple.Create((IComparable)(ulong)0, (IComparable)(ulong)sbyte .MaxValue) },
79+
{ typeof(byte ), Tuple.Create((IComparable)(ulong)0, (IComparable)(ulong)byte .MaxValue) },
80+
{ typeof(short ), Tuple.Create((IComparable)(ulong)0, (IComparable)(ulong)short .MaxValue) },
81+
{ typeof(ushort), Tuple.Create((IComparable)(ulong)0, (IComparable)(ulong)ushort.MaxValue) },
82+
{ typeof(int ), Tuple.Create((IComparable)(ulong)0, (IComparable)(ulong)int .MaxValue) },
83+
{ typeof(uint ), Tuple.Create((IComparable)(ulong)0, (IComparable)(ulong)uint .MaxValue) },
84+
{ typeof(long ), Tuple.Create((IComparable)(ulong)0, (IComparable)(ulong)long .MaxValue) },
85+
}
86+
},
87+
};
88+
89+
public static bool TryConvert(object value, Type toType, out object convertedValue)
90+
{
91+
convertedValue = null;
92+
93+
if (value == null)
94+
return toType.IsClassEx() || toType.IsNullable();
95+
96+
var from = value.GetType().ToNullableUnderlying();
97+
var to = toType .ToNullableUnderlying();
98+
99+
if (from == to)
100+
{
101+
convertedValue = value;
102+
return true;
103+
}
104+
105+
if (_alwaysConvert.TryGetValue(from, out var types) && types.Contains(to))
106+
{
107+
convertedValue = Convert.ChangeType(value, to);
108+
return true;
109+
}
110+
111+
if (!_rangedConvert.TryGetValue(from, out var rangedTypes)
112+
|| !rangedTypes.TryGetValue(to, out var ranges))
113+
return false;
114+
115+
if ( ranges.Item1.CompareTo(value) <= 0
116+
&& ranges.Item2.CompareTo(value) >= 0)
117+
{
118+
convertedValue = Convert.ChangeType(value, to);
119+
return true;
120+
}
121+
122+
return false;
123+
}
124+
}
125+
}

Source/LinqToDB/Data/DataConnection.Linq.cs

+18-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace LinqToDB.Data
1010
using Mapping;
1111
using SqlQuery;
1212
using SqlProvider;
13+
using System.Linq;
1314

1415
public partial class DataConnection : IDataContext
1516
{
@@ -68,10 +69,23 @@ IDataContext IDataContext.Clone(bool forNestedQuery)
6869
if (forNestedQuery && _connection != null && IsMarsEnabled)
6970
return new DataConnection(DataProvider, _connection)
7071
{
71-
MappingSchema = MappingSchema,
72-
TransactionAsync = TransactionAsync,
73-
IsMarsEnabled = IsMarsEnabled,
74-
ConnectionString = ConnectionString,
72+
MappingSchema = MappingSchema,
73+
TransactionAsync = TransactionAsync,
74+
IsMarsEnabled = IsMarsEnabled,
75+
ConnectionString = ConnectionString,
76+
OnEntityCreated = OnEntityCreated,
77+
RetryPolicy = RetryPolicy,
78+
CommandTimeout = CommandTimeout,
79+
InlineParameters = InlineParameters,
80+
ThrowOnDisposed = ThrowOnDisposed,
81+
_queryHints = _queryHints?.Count > 0 ? _queryHints.ToList() : null,
82+
OnTraceConnection = OnTraceConnection,
83+
OnClosed = OnClosed,
84+
OnClosing = OnClosing,
85+
OnBeforeConnectionOpen = OnBeforeConnectionOpen,
86+
OnConnectionOpened = OnConnectionOpened,
87+
OnBeforeConnectionOpenAsync = OnBeforeConnectionOpenAsync,
88+
OnConnectionOpenedAsync = OnConnectionOpenedAsync,
7589
};
7690

7791
return (DataConnection)Clone();

Source/LinqToDB/Data/DataConnection.cs

+16-1
Original file line numberDiff line numberDiff line change
@@ -1516,7 +1516,22 @@ public object Clone()
15161516
// will not work for providers that remove security information from connection string
15171517
var connectionString = ConnectionString ?? (connection == null ? _connection?.ConnectionString : null);
15181518

1519-
return new DataConnection(ConfigurationString, DataProvider, connectionString, connection, MappingSchema);
1519+
return new DataConnection(ConfigurationString, DataProvider, connectionString, connection, MappingSchema)
1520+
{
1521+
OnEntityCreated = OnEntityCreated,
1522+
RetryPolicy = RetryPolicy,
1523+
CommandTimeout = CommandTimeout,
1524+
InlineParameters = InlineParameters,
1525+
ThrowOnDisposed = ThrowOnDisposed,
1526+
_queryHints = _queryHints?.Count > 0 ? _queryHints.ToList() : null,
1527+
OnTraceConnection = OnTraceConnection,
1528+
OnClosed = OnClosed,
1529+
OnClosing = OnClosing,
1530+
OnBeforeConnectionOpen = OnBeforeConnectionOpen,
1531+
OnConnectionOpened = OnConnectionOpened,
1532+
OnBeforeConnectionOpenAsync = OnBeforeConnectionOpenAsync,
1533+
OnConnectionOpenedAsync = OnConnectionOpenedAsync,
1534+
};
15201535
}
15211536

15221537
#endregion

Source/LinqToDB/DataContext.cs

+43-6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ namespace LinqToDB
1212
using Data;
1313
using DataProvider;
1414
using Linq;
15+
using LinqToDB.Async;
1516
using Mapping;
1617
using SqlProvider;
1718

@@ -221,6 +222,17 @@ public int CommandTimeout
221222
/// </summary>
222223
DataConnection _dataConnection;
223224

225+
/// <summary>
226+
/// Creates instance of <see cref="DataConnection"/> class, used by context internally.
227+
/// </summary>
228+
/// <returns>New <see cref="DataConnection"/> instance.</returns>
229+
protected virtual DataConnection CreateDataConnection()
230+
{
231+
return ConnectionString != null
232+
? new DataConnection(DataProvider, ConnectionString)
233+
: new DataConnection(ConfigurationString);
234+
}
235+
224236
/// <summary>
225237
/// Returns associated database connection <see cref="DataConnection"/> or create new connection, if connection
226238
/// doesn't exists.
@@ -230,9 +242,7 @@ internal DataConnection GetDataConnection()
230242
{
231243
if (_dataConnection == null)
232244
{
233-
_dataConnection = ConnectionString != null
234-
? new DataConnection(DataProvider, ConnectionString)
235-
: new DataConnection(ConfigurationString);
245+
_dataConnection = CreateDataConnection();
236246

237247
if (_commandTimeout != null)
238248
_dataConnection.CommandTimeout = CommandTimeout;
@@ -302,6 +312,31 @@ Expression IDataContext.GetReaderExpression(MappingSchema mappingSchema, IDataRe
302312
/// <param name="n">Unused.</param>
303313
DataContext(int n) {}
304314

315+
/// <summary>
316+
/// Creates instance of <see cref="DataConnection"/> class, attached to same database connection/transaction.
317+
/// Used by <see cref="IDataContext.Clone(bool)"/> API only if <see cref="DataConnection.IsMarsEnabled"/>
318+
/// is <c>true</c> and there is an active connection associated with current context.
319+
/// <paramref name="dbConnection"/> and <paramref name="dbTransaction"/> parameters are mutually exclusive.
320+
/// One and only one parameter will have value - if there is active transaction, <paramref name="dbTransaction"/>
321+
/// parameter value provided, otherwise <paramref name="dbConnection"/> parameter has value.
322+
/// </summary>
323+
/// <param name="currentConnection"><see cref="DataConnection"/> instance, used by current context instance.</param>
324+
/// <param name="dbTransaction">Transaction, associated with <paramref name="currentConnection"/>.</param>
325+
/// <param name="dbConnection">Connection, associated with <paramref name="dbConnection"/>.</param>
326+
/// <returns>New <see cref="DataConnection"/> instance.</returns>
327+
protected virtual DataConnection CloneDataConnection(
328+
DataConnection currentConnection, // not used by implementation, but could be useful in override
329+
IAsyncDbTransaction dbTransaction,
330+
IAsyncDbConnection dbConnection)
331+
{
332+
// we pass both dataconnection and db connection/transaction, because connection/transaction accessors
333+
// are internal and it is not possible to access them in derived class. And we definitely don't want them
334+
// to be public.
335+
return dbTransaction != null
336+
? new DataConnection(DataProvider, dbTransaction)
337+
: new DataConnection(DataProvider, dbConnection);
338+
}
339+
305340
IDataContext IDataContext.Clone(bool forNestedQuery)
306341
{
307342
var dc = new DataContext(0)
@@ -316,9 +351,11 @@ IDataContext IDataContext.Clone(bool forNestedQuery)
316351
};
317352

318353
if (forNestedQuery && _dataConnection != null && _dataConnection.IsMarsEnabled)
319-
dc._dataConnection = _dataConnection.TransactionAsync != null ?
320-
new DataConnection(DataProvider, _dataConnection.TransactionAsync) :
321-
new DataConnection(DataProvider, _dataConnection.EnsureConnection());
354+
dc._dataConnection = CloneDataConnection(
355+
_dataConnection,
356+
_dataConnection.TransactionAsync,
357+
_dataConnection.TransactionAsync == null ? _dataConnection.EnsureConnection() : null);
358+
322359

323360
dc.QueryHints. AddRange(QueryHints);
324361
dc.NextQueryHints.AddRange(NextQueryHints);

Source/LinqToDB/DataProvider/Oracle/OracleSchemaProvider.cs

+15-10
Original file line numberDiff line numberDiff line change
@@ -252,24 +252,29 @@ private void LoadCurrentUser(DataConnection dataConnection)
252252

253253
protected override List<ProcedureParameterInfo> GetProcedureParameters(DataConnection dataConnection)
254254
{
255+
// uses ALL_ARGUMENTS view
256+
// https://docs.oracle.com/cd/B28359_01/server.111/b28320/statviews_1014.htm#REFRN20015
257+
// SELECT * FROM ALL_ARGUMENTS WHERE DATA_LEVEL = 0 AND (OWNER = :OWNER OR :OWNER is null) AND (OBJECT_NAME = :OBJECTNAME OR :OBJECTNAME is null)
255258
var pps = ((DbConnection)dataConnection.Connection).GetSchema("ProcedureParameters");
256259

260+
// SEQUENCE filter filters-out non-argument records without DATA_TYPE
261+
// check https://llblgen.com/tinyforum/Messages.aspx?ThreadID=22795
257262
return
258263
(
259-
from pp in pps.AsEnumerable()
260-
let schema = pp.Field<string>("OWNER")
261-
let name = pp.Field<string>("OBJECT_NAME")
262-
let direction = pp.Field<string>("IN_OUT")
264+
from pp in pps.AsEnumerable().Where(_ => Converter.ChangeTypeTo<int>(_["SEQUENCE"]) > 0)
265+
let schema = pp.Field<string>("OWNER") // not null
266+
let name = pp.Field<string>("OBJECT_NAME") // nullable (???)
267+
let direction = pp.Field<string>("IN_OUT") // nullable: IN, OUT, IN/OUT
263268
where IncludedSchemas.Count != 0 || ExcludedSchemas.Count != 0 || schema == _currentUser
264269
select new ProcedureParameterInfo
265270
{
266271
ProcedureID = schema + "." + name,
267-
ParameterName = pp.Field<string>("ARGUMENT_NAME"),
268-
DataType = pp.Field<string>("DATA_TYPE"),
269-
Ordinal = Converter.ChangeTypeTo<int> (pp["POSITION"]),
270-
Length = Converter.ChangeTypeTo<long?>(pp["DATA_LENGTH"]),
271-
Precision = Converter.ChangeTypeTo<int?> (pp["DATA_PRECISION"]),
272-
Scale = Converter.ChangeTypeTo<int?> (pp["DATA_SCALE"]),
272+
ParameterName = pp.Field<string>("ARGUMENT_NAME"), // nullable
273+
DataType = pp.Field<string>("DATA_TYPE"), // nullable, but only for sequence = 0
274+
Ordinal = Converter.ChangeTypeTo<int> (pp["POSITION"]), // not null, 0 - return value
275+
Length = Converter.ChangeTypeTo<long?>(pp["DATA_LENGTH"]), // nullable
276+
Precision = Converter.ChangeTypeTo<int?> (pp["DATA_PRECISION"]), // nullable
277+
Scale = Converter.ChangeTypeTo<int?> (pp["DATA_SCALE"]), // nullable
273278
IsIn = direction.StartsWith("IN"),
274279
IsOut = direction.EndsWith("OUT"),
275280
IsNullable = true

Source/LinqToDB/Linq/Builder/AsSubQueryBuilder.cs

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
using System.Linq.Expressions;
1+
using System.Linq;
2+
using System.Linq.Expressions;
23
using LinqToDB.Expressions;
4+
using LinqToDB.Extensions;
35

46
namespace LinqToDB.Linq.Builder
57
{
@@ -14,8 +16,23 @@ protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, Meth
1416
{
1517
var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));
1618
sequence.SelectQuery.DoNotRemove = true;
19+
20+
var elementType = methodCall.Arguments[0].Type.GetGenericArgumentsEx()[0];
21+
if (typeof(IGrouping<,>).IsSameOrParentOf(elementType))
22+
{
23+
// It is special case when we are trying to make subquery from GroupBy
24+
25+
sequence.ConvertToIndex(null, 0, ConvertFlags.Key);
26+
var param = Expression.Parameter(elementType);
27+
var lambda = Expression.Lambda(Expression.PropertyOrField(param, "Key"), param);
28+
29+
sequence = new SubQueryContext(sequence);
30+
sequence = new SelectContext(buildInfo.Parent, lambda, sequence);
31+
}
32+
else
33+
sequence = new SubQueryContext(sequence);
1734

18-
return new SubQueryContext(sequence);
35+
return sequence;
1936
}
2037

2138
protected override SequenceConvertInfo Convert(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo,

0 commit comments

Comments
 (0)