PageRenderTime 59ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/mcs/class/referencesource/System.Data.Linq/SqlClient/Query/QueryConverter.cs

http://github.com/mono/mono
C# | 2887 lines | 2342 code | 301 blank | 244 comment | 771 complexity | 74b75f6e2c6ad9ddcfb9453582b373da MD5 | raw file
Possible License(s): GPL-2.0, CC-BY-SA-3.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, Unlicense, Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. using System;
  2. using System.Globalization;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Data;
  6. using System.Reflection;
  7. using System.Text;
  8. using System.Linq;
  9. using System.Linq.Expressions;
  10. using System.Data.Linq;
  11. using System.Data.Linq.Mapping;
  12. using System.Data.Linq.Provider;
  13. using System.Collections.ObjectModel;
  14. using System.Diagnostics.CodeAnalysis;
  15. namespace System.Data.Linq.SqlClient {
  16. /// <summary>
  17. /// These are application types used to represent types used during intermediate
  18. /// stages of the query building process.
  19. /// </summary>
  20. enum ConverterSpecialTypes {
  21. Row,
  22. Table
  23. }
  24. [Flags]
  25. internal enum ConverterStrategy {
  26. Default = 0x0,
  27. SkipWithRowNumber = 0x1,
  28. CanUseScopeIdentity = 0x2,
  29. CanUseOuterApply = 0x4,
  30. CanUseRowStatus = 0x8,
  31. CanUseJoinOn = 0x10, // Whether or not to use ON clause of JOIN.
  32. CanOutputFromInsert = 0x20
  33. }
  34. [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification="Unknown reason.")]
  35. internal class QueryConverter {
  36. IDataServices services;
  37. Translator translator;
  38. SqlFactory sql;
  39. TypeSystemProvider typeProvider;
  40. bool outerNode;
  41. Dictionary<ParameterExpression, SqlExpression> map;
  42. Dictionary<ParameterExpression, Expression> exprMap;
  43. Dictionary<ParameterExpression, SqlNode> dupMap;
  44. Dictionary<SqlNode, GroupInfo> gmap;
  45. Expression dominatingExpression;
  46. bool allowDeferred;
  47. ConverterStrategy converterStrategy = ConverterStrategy.Default;
  48. class GroupInfo {
  49. internal SqlSelect SelectWithGroup;
  50. internal SqlExpression ElementOnGroupSource;
  51. }
  52. internal ConverterStrategy ConverterStrategy {
  53. get { return converterStrategy; }
  54. set { converterStrategy = value; }
  55. }
  56. private bool UseConverterStrategy(ConverterStrategy strategy) {
  57. return (this.converterStrategy & strategy) == strategy;
  58. }
  59. internal QueryConverter(IDataServices services, TypeSystemProvider typeProvider, Translator translator, SqlFactory sql) {
  60. if (services == null) {
  61. throw Error.ArgumentNull("services");
  62. }
  63. if (sql == null) {
  64. throw Error.ArgumentNull("sql");
  65. }
  66. if (translator == null) {
  67. throw Error.ArgumentNull("translator");
  68. }
  69. if (typeProvider == null) {
  70. throw Error.ArgumentNull("typeProvider");
  71. }
  72. this.services = services;
  73. this.translator = translator;
  74. this.sql = sql;
  75. this.typeProvider = typeProvider;
  76. this.map = new Dictionary<ParameterExpression, SqlExpression>();
  77. this.exprMap = new Dictionary<ParameterExpression, Expression>();
  78. this.dupMap = new Dictionary<ParameterExpression, SqlNode>();
  79. this.gmap = new Dictionary<SqlNode, GroupInfo>();
  80. this.allowDeferred = true;
  81. }
  82. /// <summary>
  83. /// Convert inner expression from C# expression to basic SQL Query.
  84. /// </summary>
  85. /// <param name="node">The expression to convert.</param>
  86. /// <returns>The converted SQL query.</returns>
  87. internal SqlNode ConvertOuter(Expression node) {
  88. this.dominatingExpression = node;
  89. this.outerNode = true;
  90. SqlNode retNode;
  91. if (typeof(ITable).IsAssignableFrom(node.Type)) {
  92. retNode = this.VisitSequence(node);
  93. }
  94. else {
  95. retNode = this.VisitInner(node);
  96. }
  97. if (retNode.NodeType == SqlNodeType.MethodCall) {
  98. // if a tree consists of a single method call expression only, that method
  99. // must be either a mapped stored procedure or a mapped function
  100. throw Error.InvalidMethodExecution(((SqlMethodCall)retNode).Method.Name);
  101. }
  102. // if after conversion the node is an expression, we must
  103. // wrap it in a select
  104. SqlExpression sqlExpression = retNode as SqlExpression;
  105. if (sqlExpression != null) {
  106. retNode = new SqlSelect(sqlExpression, null, this.dominatingExpression);
  107. }
  108. retNode = new SqlIncludeScope(retNode, this.dominatingExpression);
  109. return retNode;
  110. }
  111. internal SqlNode Visit(Expression node) {
  112. bool tempOuterNode = this.outerNode;
  113. this.outerNode = false;
  114. SqlNode result = this.VisitInner(node);
  115. this.outerNode = tempOuterNode;
  116. return result;
  117. }
  118. /// <summary>
  119. /// Convert inner expression from C# expression to basic SQL Query.
  120. /// </summary>
  121. /// <param name="node">The expression to convert.</param>
  122. /// <param name="dominantExpression">Current dominating expression, used for producing meaningful exception text.</param>
  123. /// <returns>The converted SQL query.</returns>
  124. internal SqlNode ConvertInner(Expression node, Expression dominantExpression) {
  125. this.dominatingExpression = dominantExpression;
  126. bool tempOuterNode = this.outerNode;
  127. this.outerNode = false;
  128. SqlNode result = this.VisitInner(node);
  129. this.outerNode = tempOuterNode;
  130. return result;
  131. }
  132. [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Microsoft: Cast is dependent on node type and casts do not happen unecessarily in a single code path.")]
  133. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
  134. private SqlNode VisitInner(Expression node) {
  135. if (node == null) return null;
  136. Expression save = this.dominatingExpression;
  137. this.dominatingExpression = ChooseBestDominatingExpression(this.dominatingExpression, node);
  138. try {
  139. switch (node.NodeType) {
  140. case ExpressionType.New:
  141. return this.VisitNew((NewExpression)node);
  142. case ExpressionType.MemberInit:
  143. return this.VisitMemberInit((MemberInitExpression)node);
  144. case ExpressionType.Negate:
  145. case ExpressionType.NegateChecked:
  146. case ExpressionType.Not:
  147. return this.VisitUnary((UnaryExpression)node);
  148. case ExpressionType.UnaryPlus:
  149. if (node.Type == typeof(TimeSpan))
  150. return this.VisitUnary((UnaryExpression)node);
  151. throw Error.UnrecognizedExpressionNode(node.NodeType);
  152. case ExpressionType.Add:
  153. case ExpressionType.AddChecked:
  154. case ExpressionType.Subtract:
  155. case ExpressionType.SubtractChecked:
  156. case ExpressionType.Multiply:
  157. case ExpressionType.MultiplyChecked:
  158. case ExpressionType.Divide:
  159. case ExpressionType.Modulo:
  160. case ExpressionType.And:
  161. case ExpressionType.AndAlso:
  162. case ExpressionType.Or:
  163. case ExpressionType.OrElse:
  164. case ExpressionType.Power:
  165. case ExpressionType.LessThan:
  166. case ExpressionType.LessThanOrEqual:
  167. case ExpressionType.GreaterThan:
  168. case ExpressionType.GreaterThanOrEqual:
  169. case ExpressionType.Equal:
  170. case ExpressionType.NotEqual:
  171. case ExpressionType.Coalesce:
  172. case ExpressionType.ExclusiveOr:
  173. return this.VisitBinary((BinaryExpression)node);
  174. case ExpressionType.ArrayIndex:
  175. return this.VisitArrayIndex((BinaryExpression)node);
  176. case ExpressionType.TypeIs:
  177. return this.VisitTypeBinary((TypeBinaryExpression)node);
  178. case ExpressionType.Convert:
  179. case ExpressionType.ConvertChecked:
  180. return this.VisitCast((UnaryExpression)node);
  181. case ExpressionType.TypeAs:
  182. return this.VisitAs((UnaryExpression)node);
  183. case ExpressionType.Conditional:
  184. return this.VisitConditional((ConditionalExpression)node);
  185. case ExpressionType.Constant:
  186. return this.VisitConstant((ConstantExpression)node);
  187. case ExpressionType.Parameter:
  188. return this.VisitParameter((ParameterExpression)node);
  189. case ExpressionType.MemberAccess:
  190. return this.VisitMemberAccess((MemberExpression)node);
  191. case ExpressionType.Call:
  192. return this.VisitMethodCall((MethodCallExpression)node);
  193. case ExpressionType.ArrayLength:
  194. return this.VisitArrayLength((UnaryExpression)node);
  195. case ExpressionType.NewArrayInit:
  196. return this.VisitNewArrayInit((NewArrayExpression)node);
  197. case ExpressionType.ListInit:
  198. return this.VisitListInit((ListInitExpression)node);
  199. case ExpressionType.Quote:
  200. return this.Visit(((UnaryExpression)node).Operand);
  201. case ExpressionType.Invoke:
  202. return this.VisitInvocation((InvocationExpression)node);
  203. case ExpressionType.Lambda:
  204. return this.VisitLambda((LambdaExpression)node);
  205. case ExpressionType.RightShift:
  206. case ExpressionType.LeftShift:
  207. throw Error.UnsupportedNodeType(node.NodeType);
  208. case (ExpressionType)InternalExpressionType.Known:
  209. return ((KnownExpression)node).Node;
  210. case (ExpressionType)InternalExpressionType.LinkedTable:
  211. return this.VisitLinkedTable((LinkedTableExpression)node);
  212. default:
  213. throw Error.UnrecognizedExpressionNode(node.NodeType);
  214. }
  215. }
  216. finally {
  217. this.dominatingExpression = save;
  218. }
  219. }
  220. /// <summary>
  221. /// Heuristic which chooses the best Expression root to use for displaying user messages
  222. /// and exception text.
  223. /// </summary>
  224. private static Expression ChooseBestDominatingExpression(Expression last, Expression next) {
  225. if (last == null) {
  226. return next;
  227. }
  228. else if (next == null) {
  229. return last;
  230. }
  231. else {
  232. if (next is MethodCallExpression) {
  233. return next;
  234. }
  235. if (last is MethodCallExpression) {
  236. return last;
  237. }
  238. }
  239. return next;
  240. }
  241. private SqlSelect LockSelect(SqlSelect sel) {
  242. if (sel.Selection.NodeType != SqlNodeType.AliasRef ||
  243. sel.Where != null ||
  244. sel.OrderBy.Count > 0 ||
  245. sel.GroupBy.Count > 0 ||
  246. sel.Having != null ||
  247. sel.Top != null ||
  248. sel.OrderingType != SqlOrderingType.Default ||
  249. sel.IsDistinct) {
  250. SqlAlias alias = new SqlAlias(sel);
  251. SqlAliasRef aref = new SqlAliasRef(alias);
  252. return new SqlSelect(aref, alias, this.dominatingExpression);
  253. }
  254. return sel;
  255. }
  256. private SqlSelect VisitSequence(Expression exp) {
  257. return this.CoerceToSequence(this.Visit(exp));
  258. }
  259. private SqlSelect CoerceToSequence(SqlNode node) {
  260. SqlSelect select = node as SqlSelect;
  261. if (select == null) {
  262. if (node.NodeType == SqlNodeType.Value) {
  263. SqlValue sv = (SqlValue)node;
  264. // Check for ITables.
  265. ITable t = sv.Value as ITable;
  266. if (t != null) {
  267. return this.CoerceToSequence(this.TranslateConstantTable(t, null));
  268. }
  269. // Check for IQueryable.
  270. IQueryable query = sv.Value as IQueryable;
  271. if (query != null) {
  272. Expression fex = Funcletizer.Funcletize(query.Expression);
  273. // IQueryables that return self-referencing Constant expressions cause infinite recursion
  274. if (fex.NodeType != ExpressionType.Constant ||
  275. ((ConstantExpression)fex).Value != query) {
  276. return this.VisitSequence(fex);
  277. }
  278. throw Error.IQueryableCannotReturnSelfReferencingConstantExpression();
  279. }
  280. throw Error.CapturedValuesCannotBeSequences();
  281. }
  282. else if (node.NodeType == SqlNodeType.Multiset || node.NodeType == SqlNodeType.Element) {
  283. return ((SqlSubSelect)node).Select;
  284. }
  285. else if (node.NodeType == SqlNodeType.ClientArray) {
  286. throw Error.ConstructedArraysNotSupported();
  287. }
  288. else if (node.NodeType == SqlNodeType.ClientParameter) {
  289. throw Error.ParametersCannotBeSequences();
  290. }
  291. // this needs to be a sequence expression!
  292. SqlExpression sqlExpr = (SqlExpression)node;
  293. SqlAlias sa = new SqlAlias(sqlExpr);
  294. SqlAliasRef aref = new SqlAliasRef(sa);
  295. return new SqlSelect(aref, sa, this.dominatingExpression);
  296. }
  297. return select;
  298. }
  299. //
  300. // Recursive call to VisitInvocation.
  301. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
  302. private SqlNode VisitInvocation(InvocationExpression invoke) {
  303. LambdaExpression lambda =
  304. (invoke.Expression.NodeType == ExpressionType.Quote)
  305. ? (LambdaExpression)((UnaryExpression)invoke.Expression).Operand
  306. : (invoke.Expression as LambdaExpression);
  307. if (lambda != null) {
  308. // just map arg values into lambda's parameters and evaluate lambda's body
  309. for (int i = 0, n = invoke.Arguments.Count; i < n; i++) {
  310. this.exprMap[lambda.Parameters[i]] = invoke.Arguments[i];
  311. }
  312. return this.VisitInner(lambda.Body);
  313. }
  314. else {
  315. // check for compiled query invocation
  316. SqlExpression expr = this.VisitExpression(invoke.Expression);
  317. if (expr.NodeType == SqlNodeType.Value) {
  318. SqlValue value = (SqlValue)expr;
  319. Delegate d = value.Value as Delegate;
  320. if (d != null) {
  321. CompiledQuery cq = d.Target as CompiledQuery;
  322. if (cq != null) {
  323. return this.VisitInvocation(Expression.Invoke(cq.Expression, invoke.Arguments));
  324. } else if (invoke.Arguments.Count == 0) {
  325. object invokeResult;
  326. try {
  327. invokeResult = d.DynamicInvoke(null);
  328. } catch (System.Reflection.TargetInvocationException e) {
  329. throw e.InnerException;
  330. }
  331. return this.sql.ValueFromObject(invokeResult, invoke.Type, true, this.dominatingExpression);
  332. }
  333. }
  334. }
  335. SqlExpression [] args = new SqlExpression[invoke.Arguments.Count];
  336. for(int i = 0; i<args.Length; ++i) {
  337. args[i] = (SqlExpression)this.Visit(invoke.Arguments[i]);
  338. }
  339. var sca = new SqlClientArray(typeof(object[]), this.typeProvider.From(typeof(object[])), args, this.dominatingExpression);
  340. return sql.MethodCall(invoke.Type, typeof(Delegate).GetMethod("DynamicInvoke"), expr, new SqlExpression[] {sca}, this.dominatingExpression);
  341. }
  342. }
  343. // inline lambda expressions w/o invocation are parameterized queries
  344. private SqlNode VisitLambda(LambdaExpression lambda) {
  345. // turn lambda parameters into client parameters
  346. for (int i = 0, n = lambda.Parameters.Count; i < n; i++) {
  347. ParameterExpression p = lambda.Parameters[i];
  348. if (p.Type == typeof(Type)) {
  349. throw Error.BadParameterType(p.Type);
  350. }
  351. // construct accessor for parameter
  352. ParameterExpression pa = Expression.Parameter(typeof(object[]), "args");
  353. LambdaExpression accessor =
  354. Expression.Lambda(
  355. typeof(Func<,>).MakeGenericType(typeof(object[]), p.Type),
  356. Expression.Convert(
  357. #pragma warning disable 618 // Disable the 'obsolete' warning
  358. Expression.ArrayIndex(pa, Expression.Constant(i)),
  359. p.Type
  360. ),
  361. #pragma warning restore 618
  362. pa
  363. );
  364. SqlClientParameter cp = new SqlClientParameter(p.Type, this.typeProvider.From(p.Type), accessor, this.dominatingExpression);
  365. // map references to lambda's parameter to client parameter node
  366. this.dupMap[p] = cp;
  367. }
  368. // call this so we don't erase 'outerNode' setting
  369. return this.VisitInner(lambda.Body);
  370. }
  371. private SqlExpression VisitExpression(Expression exp) {
  372. SqlNode result = this.Visit(exp);
  373. if (result == null) return null;
  374. SqlExpression x = result as SqlExpression;
  375. if (x != null) return x;
  376. SqlSelect select = result as SqlSelect;
  377. if (select != null) {
  378. SqlSubSelect ms = sql.SubSelect(SqlNodeType.Multiset, select, exp.Type);
  379. return ms;
  380. }
  381. throw Error.UnrecognizedExpressionNode(result);
  382. }
  383. private SqlSelect VisitSelect(Expression sequence, LambdaExpression selector) {
  384. SqlSelect source = this.VisitSequence(sequence);
  385. SqlAlias alias = new SqlAlias(source);
  386. SqlAliasRef aref = new SqlAliasRef(alias);
  387. this.map[selector.Parameters[0]] = aref;
  388. SqlNode project = this.Visit(selector.Body);
  389. SqlSelect pselect = project as SqlSelect;
  390. if (pselect != null) {
  391. return new SqlSelect(sql.SubSelect(SqlNodeType.Multiset, pselect, selector.Body.Type), alias, this.dominatingExpression);
  392. }
  393. else if ((project.NodeType == SqlNodeType.Element || project.NodeType == SqlNodeType.ScalarSubSelect) &&
  394. (this.converterStrategy & ConverterStrategy.CanUseOuterApply) != 0) {
  395. SqlSubSelect sub = (SqlSubSelect)project;
  396. SqlSelect inner = sub.Select;
  397. SqlAlias innerAlias = new SqlAlias(inner);
  398. SqlAliasRef innerRef = new SqlAliasRef(innerAlias);
  399. if (project.NodeType == SqlNodeType.Element) {
  400. inner.Selection = new SqlOptionalValue(
  401. new SqlColumn(
  402. "test",
  403. sql.Unary(
  404. SqlNodeType.OuterJoinedValue,
  405. sql.Value(typeof(int?), this.typeProvider.From(typeof(int)), 1, false, this.dominatingExpression)
  406. )
  407. ),
  408. sql.Unary(SqlNodeType.OuterJoinedValue, inner.Selection)
  409. );
  410. }
  411. else {
  412. inner.Selection = sql.Unary(SqlNodeType.OuterJoinedValue, inner.Selection);
  413. }
  414. SqlJoin join = new SqlJoin(SqlJoinType.OuterApply, alias, innerAlias, null, this.dominatingExpression);
  415. return new SqlSelect(innerRef, join, this.dominatingExpression);
  416. }
  417. else {
  418. SqlExpression expr = project as SqlExpression;
  419. if (expr != null) {
  420. return new SqlSelect(expr, alias, this.dominatingExpression);
  421. }
  422. else {
  423. throw Error.BadProjectionInSelect();
  424. }
  425. }
  426. }
  427. private SqlSelect VisitSelectMany(Expression sequence, LambdaExpression colSelector, LambdaExpression resultSelector) {
  428. SqlSelect seqSelect = this.VisitSequence(sequence);
  429. SqlAlias seqAlias = new SqlAlias(seqSelect);
  430. SqlAliasRef seqRef = new SqlAliasRef(seqAlias);
  431. this.map[colSelector.Parameters[0]] = seqRef;
  432. SqlNode colSelectorNode = this.VisitSequence(colSelector.Body);
  433. SqlAlias selAlias = new SqlAlias(colSelectorNode);
  434. SqlAliasRef selRef = new SqlAliasRef(selAlias);
  435. SqlJoin join = new SqlJoin(SqlJoinType.CrossApply, seqAlias, selAlias, null, this.dominatingExpression);
  436. SqlExpression projection = selRef;
  437. if (resultSelector != null) {
  438. this.map[resultSelector.Parameters[0]] = seqRef;
  439. this.map[resultSelector.Parameters[1]] = selRef;
  440. projection = this.VisitExpression(resultSelector.Body);
  441. }
  442. return new SqlSelect(projection, join, this.dominatingExpression);
  443. }
  444. private SqlSelect VisitJoin(Expression outerSequence, Expression innerSequence, LambdaExpression outerKeySelector, LambdaExpression innerKeySelector, LambdaExpression resultSelector) {
  445. SqlSelect outerSelect = this.VisitSequence(outerSequence);
  446. SqlSelect innerSelect = this.VisitSequence(innerSequence);
  447. SqlAlias outerAlias = new SqlAlias(outerSelect);
  448. SqlAliasRef outerRef = new SqlAliasRef(outerAlias);
  449. SqlAlias innerAlias = new SqlAlias(innerSelect);
  450. SqlAliasRef innerRef = new SqlAliasRef(innerAlias);
  451. this.map[outerKeySelector.Parameters[0]] = outerRef;
  452. SqlExpression outerKey = this.VisitExpression(outerKeySelector.Body);
  453. this.map[innerKeySelector.Parameters[0]] = innerRef;
  454. SqlExpression innerKey = this.VisitExpression(innerKeySelector.Body);
  455. this.map[resultSelector.Parameters[0]] = outerRef;
  456. this.map[resultSelector.Parameters[1]] = innerRef;
  457. SqlExpression result = this.VisitExpression(resultSelector.Body);
  458. SqlExpression condition = sql.Binary(SqlNodeType.EQ, outerKey, innerKey);
  459. SqlSelect select = null;
  460. if ((this.converterStrategy & ConverterStrategy.CanUseJoinOn) != 0) {
  461. SqlJoin join = new SqlJoin(SqlJoinType.Inner, outerAlias, innerAlias, condition, this.dominatingExpression);
  462. select = new SqlSelect(result, join, this.dominatingExpression);
  463. } else {
  464. SqlJoin join = new SqlJoin(SqlJoinType.Cross, outerAlias, innerAlias, null, this.dominatingExpression);
  465. select = new SqlSelect(result, join, this.dominatingExpression);
  466. select.Where = condition;
  467. }
  468. return select;
  469. }
  470. private SqlSelect VisitGroupJoin(Expression outerSequence, Expression innerSequence, LambdaExpression outerKeySelector, LambdaExpression innerKeySelector, LambdaExpression resultSelector) {
  471. SqlSelect outerSelect = this.VisitSequence(outerSequence);
  472. SqlSelect innerSelect = this.VisitSequence(innerSequence);
  473. SqlAlias outerAlias = new SqlAlias(outerSelect);
  474. SqlAliasRef outerRef = new SqlAliasRef(outerAlias);
  475. SqlAlias innerAlias = new SqlAlias(innerSelect);
  476. SqlAliasRef innerRef = new SqlAliasRef(innerAlias);
  477. this.map[outerKeySelector.Parameters[0]] = outerRef;
  478. SqlExpression outerKey = this.VisitExpression(outerKeySelector.Body);
  479. this.map[innerKeySelector.Parameters[0]] = innerRef;
  480. SqlExpression innerKey = this.VisitExpression(innerKeySelector.Body);
  481. // make multiset
  482. SqlExpression pred = sql.Binary(SqlNodeType.EQ, outerKey, innerKey);
  483. SqlSelect select = new SqlSelect(innerRef, innerAlias, this.dominatingExpression);
  484. select.Where = pred;
  485. SqlSubSelect subquery = sql.SubSelect(SqlNodeType.Multiset, select);
  486. // make outer ref & multiset for result-selector params
  487. this.map[resultSelector.Parameters[0]] = outerRef;
  488. this.dupMap[resultSelector.Parameters[1]] = subquery;
  489. SqlExpression result = this.VisitExpression(resultSelector.Body);
  490. return new SqlSelect(result, outerAlias, this.dominatingExpression);
  491. }
  492. private SqlSelect VisitDefaultIfEmpty(Expression sequence) {
  493. SqlSelect select = this.VisitSequence(sequence);
  494. SqlAlias alias = new SqlAlias(select);
  495. SqlAliasRef aliasRef = new SqlAliasRef(alias);
  496. SqlExpression opt = new SqlOptionalValue(
  497. new SqlColumn(
  498. "test",
  499. sql.Unary(SqlNodeType.OuterJoinedValue,
  500. sql.Value(typeof(int?), this.typeProvider.From(typeof(int)), 1, false, this.dominatingExpression)
  501. )
  502. ),
  503. sql.Unary(SqlNodeType.OuterJoinedValue, aliasRef)
  504. );
  505. SqlSelect optSelect = new SqlSelect(opt, alias, this.dominatingExpression);
  506. alias = new SqlAlias(optSelect);
  507. aliasRef = new SqlAliasRef(alias);
  508. SqlExpression litNull = sql.TypedLiteralNull(typeof(string), this.dominatingExpression);
  509. SqlSelect selNull = new SqlSelect(litNull, null, this.dominatingExpression);
  510. SqlAlias aliasNull = new SqlAlias(selNull);
  511. SqlJoin join = new SqlJoin(SqlJoinType.OuterApply, aliasNull, alias, null, this.dominatingExpression);
  512. return new SqlSelect(aliasRef, join, this.dominatingExpression);
  513. }
  514. /// <summary>
  515. /// Rewrite seq.OfType<T> as seq.Select(s=>s as T).Where(p=>p!=null).
  516. /// </summary>
  517. private SqlSelect VisitOfType(Expression sequence, Type ofType) {
  518. SqlSelect select = this.LockSelect(this.VisitSequence(sequence));
  519. SqlAliasRef aref = (SqlAliasRef)select.Selection;
  520. select.Selection = new SqlUnary(SqlNodeType.Treat, ofType, typeProvider.From(ofType), aref, this.dominatingExpression);
  521. select = this.LockSelect(select);
  522. aref = (SqlAliasRef)select.Selection;
  523. // Append the 'is' operator into the WHERE clause.
  524. select.Where = sql.AndAccumulate(select.Where,
  525. sql.Unary(SqlNodeType.IsNotNull, aref, this.dominatingExpression)
  526. );
  527. return select;
  528. }
  529. /// <summary>
  530. /// Rewrite seq.Cast<T> as seq.Select(s=>(T)s).
  531. /// </summary>
  532. private SqlNode VisitSequenceCast(Expression sequence, Type type) {
  533. Type sourceType = TypeSystem.GetElementType(sequence.Type);
  534. ParameterExpression p = Expression.Parameter(sourceType, "pc");
  535. return this.Visit(Expression.Call(
  536. typeof(Enumerable), "Select",
  537. new Type[] {
  538. sourceType, // TSource element type.
  539. type, // TResult element type.
  540. },
  541. sequence,
  542. Expression.Lambda(
  543. Expression.Convert(p, type),
  544. new ParameterExpression[] { p }
  545. ))
  546. );
  547. }
  548. /// <summary>
  549. /// This is the 'is' operator.
  550. /// </summary>
  551. private SqlNode VisitTypeBinary(TypeBinaryExpression b) {
  552. SqlExpression expr = this.VisitExpression(b.Expression);
  553. SqlExpression result = null;
  554. switch (b.NodeType) {
  555. case ExpressionType.TypeIs:
  556. Type ofType = b.TypeOperand;
  557. result = sql.Unary(SqlNodeType.IsNotNull, new SqlUnary(SqlNodeType.Treat, ofType, typeProvider.From(ofType), expr, this.dominatingExpression), this.dominatingExpression);
  558. break;
  559. default:
  560. throw Error.TypeBinaryOperatorNotRecognized();
  561. }
  562. return result;
  563. }
  564. private SqlSelect VisitWhere(Expression sequence, LambdaExpression predicate) {
  565. SqlSelect select = this.LockSelect(this.VisitSequence(sequence));
  566. this.map[predicate.Parameters[0]] = (SqlAliasRef)select.Selection;
  567. select.Where = this.VisitExpression(predicate.Body);
  568. return select;
  569. }
  570. private SqlNode VisitAs(UnaryExpression a) {
  571. SqlNode node = this.Visit(a.Operand);
  572. SqlExpression expr = node as SqlExpression;
  573. if (expr != null) {
  574. return new SqlUnary(SqlNodeType.Treat, a.Type, typeProvider.From(a.Type), expr, a);
  575. }
  576. SqlSelect select = node as SqlSelect;
  577. if (select != null) {
  578. SqlSubSelect ms = sql.SubSelect(SqlNodeType.Multiset, select);
  579. return new SqlUnary(SqlNodeType.Treat, a.Type, typeProvider.From(a.Type), ms, a);
  580. }
  581. throw Error.DidNotExpectAs(a);
  582. }
  583. private SqlNode VisitArrayLength(UnaryExpression c) {
  584. SqlExpression exp = this.VisitExpression(c.Operand);
  585. if (exp.SqlType.IsString || exp.SqlType.IsChar) {
  586. return sql.CLRLENGTH(exp);
  587. }
  588. else {
  589. return sql.DATALENGTH(exp);
  590. }
  591. }
  592. private SqlNode VisitArrayIndex(BinaryExpression b) {
  593. SqlExpression array = this.VisitExpression(b.Left);
  594. SqlExpression index = this.VisitExpression(b.Right);
  595. if (array.NodeType == SqlNodeType.ClientParameter
  596. && index.NodeType == SqlNodeType.Value) {
  597. SqlClientParameter cpArray = (SqlClientParameter)array;
  598. SqlValue vIndex = (SqlValue)index;
  599. return new SqlClientParameter(
  600. b.Type, sql.TypeProvider.From(b.Type),
  601. Expression.Lambda(
  602. #pragma warning disable 618 // Disable the 'obsolete' warning
  603. Expression.ArrayIndex(cpArray.Accessor.Body, Expression.Constant(vIndex.Value, vIndex.ClrType)),
  604. #pragma warning restore 618
  605. cpArray.Accessor.Parameters.ToArray()
  606. ),
  607. this.dominatingExpression
  608. );
  609. }
  610. throw Error.UnrecognizedExpressionNode(b.NodeType);
  611. }
  612. private SqlNode VisitCast(UnaryExpression c) {
  613. if (c.Method != null) {
  614. SqlExpression exp = this.VisitExpression(c.Operand);
  615. return sql.MethodCall(c.Type, c.Method, null, new SqlExpression[] { exp }, dominatingExpression);
  616. }
  617. return this.VisitChangeType(c.Operand, c.Type);
  618. }
  619. private SqlNode VisitChangeType(Expression expression, Type type) {
  620. SqlExpression expr = this.VisitExpression(expression);
  621. return this.ChangeType(expr, type);
  622. }
  623. private SqlNode ConvertDateToDateTime2(SqlExpression expr) {
  624. SqlExpression datetime2 = new SqlVariable(expr.ClrType, expr.SqlType, "DATETIME2", expr.SourceExpression);
  625. return sql.FunctionCall(typeof(DateTime), "CONVERT", new SqlExpression[2] { datetime2, expr }, expr.SourceExpression);
  626. }
  627. private SqlNode ChangeType(SqlExpression expr, Type type) {
  628. if (type == typeof(object)) {
  629. return expr; // Boxing conversion?
  630. }
  631. else if (expr.NodeType == SqlNodeType.Value && ((SqlValue)expr).Value == null) {
  632. return sql.TypedLiteralNull(type, expr.SourceExpression);
  633. }
  634. else if (expr.NodeType == SqlNodeType.ClientParameter) {
  635. SqlClientParameter cp = (SqlClientParameter)expr;
  636. return new SqlClientParameter(
  637. type, sql.TypeProvider.From(type),
  638. Expression.Lambda(Expression.Convert(cp.Accessor.Body, type), cp.Accessor.Parameters.ToArray()),
  639. cp.SourceExpression
  640. );
  641. }
  642. ConversionMethod cm = ChooseConversionMethod(expr.ClrType, type);
  643. switch (cm) {
  644. case ConversionMethod.Convert:
  645. return sql.UnaryConvert(type, typeProvider.From(type), expr, expr.SourceExpression);
  646. case ConversionMethod.Lift:
  647. if (SqlFactory.IsSqlDateType(expr)) {
  648. expr = (SqlExpression) ConvertDateToDateTime2(expr);
  649. }
  650. return new SqlLift(type, expr, this.dominatingExpression);
  651. case ConversionMethod.Ignore:
  652. if (SqlFactory.IsSqlDateType(expr)) {
  653. return ConvertDateToDateTime2(expr);
  654. }
  655. return expr;
  656. case ConversionMethod.Treat:
  657. return new SqlUnary(SqlNodeType.Treat, type, typeProvider.From(type), expr, expr.SourceExpression);
  658. default:
  659. throw Error.UnhandledExpressionType(cm);
  660. }
  661. }
  662. enum ConversionMethod {
  663. Treat,
  664. Ignore,
  665. Convert,
  666. Lift
  667. }
  668. private ConversionMethod ChooseConversionMethod(Type fromType, Type toType) {
  669. Type nnFromType = TypeSystem.GetNonNullableType(fromType);
  670. Type nnToType = TypeSystem.GetNonNullableType(toType);
  671. if (fromType != toType && nnFromType == nnToType) {
  672. return ConversionMethod.Lift;
  673. }
  674. else if (TypeSystem.IsSequenceType(nnFromType) || TypeSystem.IsSequenceType(nnToType)) {
  675. return ConversionMethod.Ignore;
  676. }
  677. ProviderType sfromType = typeProvider.From(nnFromType);
  678. ProviderType stoType = typeProvider.From(nnToType);
  679. bool isRuntimeOnly1 = sfromType.IsRuntimeOnlyType;
  680. bool isRuntimeOnly2 = stoType.IsRuntimeOnlyType;
  681. if (isRuntimeOnly1 || isRuntimeOnly2) {
  682. return ConversionMethod.Treat;
  683. }
  684. if (nnFromType == nnToType // same non-nullable .NET types
  685. || (sfromType.IsString && sfromType.Equals(stoType)) // same SQL string types
  686. || (nnFromType.IsEnum || nnToType.IsEnum) // any .NET enum type
  687. ) {
  688. return ConversionMethod.Ignore;
  689. }
  690. else {
  691. return ConversionMethod.Convert;
  692. }
  693. }
  694. /// <summary>
  695. /// Convert ITable into SqlNodes. If the hierarchy involves inheritance then
  696. /// a type case is built. Abstractly, a type case is a CASE where each WHEN is a possible
  697. /// a typebinding that may be instantianted.
  698. /// </summary>
  699. private SqlNode TranslateConstantTable(ITable table, SqlLink link) {
  700. if (table.Context != this.services.Context) {
  701. throw Error.WrongDataContext();
  702. }
  703. MetaTable metaTable = this.services.Model.GetTable(table.ElementType);
  704. return this.translator.BuildDefaultQuery(metaTable.RowType, this.allowDeferred, link, this.dominatingExpression);
  705. }
  706. private SqlNode VisitLinkedTable(LinkedTableExpression linkedTable) {
  707. return TranslateConstantTable(linkedTable.Table, linkedTable.Link);
  708. }
  709. private SqlNode VisitConstant(ConstantExpression cons) {
  710. // A value constant or null.
  711. Type type = cons.Type;
  712. if (cons.Value == null) {
  713. return sql.TypedLiteralNull(type, this.dominatingExpression);
  714. }
  715. if (type == typeof(object)) {
  716. type = cons.Value.GetType();
  717. }
  718. return sql.ValueFromObject(cons.Value, type, true, this.dominatingExpression);
  719. }
  720. private SqlExpression VisitConditional(ConditionalExpression cond) {
  721. List<SqlWhen> whens = new List<SqlWhen>(1);
  722. whens.Add(new SqlWhen(this.VisitExpression(cond.Test), this.VisitExpression(cond.IfTrue)));
  723. SqlExpression @else = this.VisitExpression(cond.IfFalse);
  724. // combine search cases found in the else clause into a single seach case
  725. while (@else.NodeType == SqlNodeType.SearchedCase) {
  726. SqlSearchedCase sc = (SqlSearchedCase)@else;
  727. whens.AddRange(sc.Whens);
  728. @else = sc.Else;
  729. }
  730. return sql.SearchedCase(whens.ToArray(), @else, this.dominatingExpression);
  731. }
  732. private SqlExpression VisitNew(NewExpression qn) {
  733. if (TypeSystem.IsNullableType(qn.Type) && qn.Arguments.Count == 1 &&
  734. TypeSystem.GetNonNullableType(qn.Type) == qn.Arguments[0].Type) {
  735. return this.VisitCast(Expression.Convert(qn.Arguments[0], qn.Type)) as SqlExpression;
  736. }
  737. else if (qn.Type == typeof(decimal) && qn.Arguments.Count == 1) {
  738. return this.VisitCast(Expression.Convert(qn.Arguments[0], typeof(decimal))) as SqlExpression;
  739. }
  740. MetaType mt = this.services.Model.GetMetaType(qn.Type);
  741. if (mt.IsEntity) {
  742. throw Error.CannotMaterializeEntityType(qn.Type);
  743. }
  744. SqlExpression[] args = null;
  745. if (qn.Arguments.Count > 0) {
  746. args = new SqlExpression[qn.Arguments.Count];
  747. for (int i = 0, n = qn.Arguments.Count; i < n; i++) {
  748. args[i] = this.VisitExpression(qn.Arguments[i]);
  749. }
  750. }
  751. SqlNew tb = sql.New(mt, qn.Constructor, args, PropertyOrFieldOf(qn.Members), null, this.dominatingExpression);
  752. return tb;
  753. }
  754. private SqlExpression VisitMemberInit(MemberInitExpression init) {
  755. MetaType mt = this.services.Model.GetMetaType(init.Type);
  756. if (mt.IsEntity) {
  757. throw Error.CannotMaterializeEntityType(init.Type);
  758. }
  759. SqlExpression[] args = null;
  760. NewExpression qn = init.NewExpression;
  761. if (qn.Type == typeof(decimal) && qn.Arguments.Count == 1) {
  762. return this.VisitCast(Expression.Convert(qn.Arguments[0], typeof(decimal))) as SqlExpression;
  763. }
  764. if (qn.Arguments.Count > 0) {
  765. args = new SqlExpression[qn.Arguments.Count];
  766. for (int i = 0, n = args.Length; i < n; i++) {
  767. args[i] = this.VisitExpression(qn.Arguments[i]);
  768. }
  769. }
  770. int cBindings = init.Bindings.Count;
  771. SqlMemberAssign[] members = new SqlMemberAssign[cBindings];
  772. int[] ordinal = new int[members.Length];
  773. for (int i = 0; i < cBindings; i++) {
  774. MemberAssignment mb = init.Bindings[i] as MemberAssignment;
  775. if (mb != null) {
  776. SqlExpression expr = this.VisitExpression(mb.Expression);
  777. SqlMemberAssign sma = new SqlMemberAssign(mb.Member, expr);
  778. members[i] = sma;
  779. ordinal[i] = mt.GetDataMember(mb.Member).Ordinal;
  780. }
  781. else {
  782. throw Error.UnhandledBindingType(init.Bindings[i].BindingType);
  783. }
  784. }
  785. // put members in type's declaration order
  786. Array.Sort(ordinal, members, 0, members.Length);
  787. SqlNew tb = sql.New(mt, qn.Constructor, args, PropertyOrFieldOf(qn.Members), members, this.dominatingExpression);
  788. return tb;
  789. }
  790. private static IEnumerable<MemberInfo> PropertyOrFieldOf(IEnumerable<MemberInfo> members) {
  791. if (members == null) {
  792. return null;
  793. }
  794. List<MemberInfo> result = new List<MemberInfo>();
  795. foreach (MemberInfo mi in members) {
  796. switch (mi.MemberType) {
  797. case MemberTypes.Method: {
  798. foreach (PropertyInfo pi in mi.DeclaringType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) {
  799. MethodInfo method = mi as MethodInfo;
  800. if (pi.CanRead && pi.GetGetMethod() == method) {
  801. result.Add(pi);
  802. break;
  803. }
  804. }
  805. break;
  806. }
  807. case MemberTypes.Field:
  808. case MemberTypes.Property: {
  809. result.Add(mi);
  810. break;
  811. }
  812. default: {
  813. throw Error.CouldNotConvertToPropertyOrField(mi);
  814. }
  815. }
  816. }
  817. return result;
  818. }
  819. private SqlSelect VisitDistinct(Expression sequence) {
  820. SqlSelect select = this.LockSelect(this.VisitSequence(sequence));
  821. select.IsDistinct = true;
  822. select.OrderingType = SqlOrderingType.Blocked;
  823. return select;
  824. }
  825. private SqlSelect VisitTake(Expression sequence, Expression count) {
  826. // verify that count >= 0
  827. SqlExpression takeExp = this.VisitExpression(count);
  828. if (takeExp.NodeType == SqlNodeType.Value) {
  829. SqlValue constTakeCount = (SqlValue)takeExp;
  830. if (typeof(int).IsAssignableFrom(constTakeCount.Value.GetType()) && ((int)constTakeCount.Value) < 0) {
  831. throw Error.ArgumentOutOfRange("takeCount");
  832. }
  833. }
  834. MethodCallExpression mce = sequence as MethodCallExpression;
  835. if (mce != null && IsSequenceOperatorCall(mce) && mce.Method.Name == "Skip" && mce.Arguments.Count == 2) {
  836. SqlExpression skipExp = this.VisitExpression(mce.Arguments[1]);
  837. // verify that count >= 0
  838. if (skipExp.NodeType == SqlNodeType.Value) {
  839. SqlValue constSkipCount = (SqlValue)skipExp;
  840. if (typeof(int).IsAssignableFrom(constSkipCount.Value.GetType()) && ((int)constSkipCount.Value) < 0) {
  841. throw Error.ArgumentOutOfRange("skipCount");
  842. }
  843. }
  844. SqlSelect select = this.VisitSequence(mce.Arguments[0]);
  845. return this.GenerateSkipTake(select, skipExp, takeExp);
  846. }
  847. else {
  848. SqlSelect select = this.VisitSequence(sequence);
  849. return this.GenerateSkipTake(select, null, takeExp);
  850. }
  851. }
  852. /// <summary>
  853. /// In order for elements of a sequence to be skipped, they must have identity
  854. /// that can be compared. This excludes elements that are sequences and elements
  855. /// that contain sequences.
  856. /// </summary>
  857. private bool CanSkipOnSelection(SqlExpression selection) {
  858. // we can skip over groupings (since we can compare them by key)
  859. if (IsGrouping(selection.ClrType)) {
  860. return true;
  861. }
  862. // we can skip over entities (since we can compare them by primary key)
  863. MetaTable table = this.services.Model.GetTable(selection.ClrType);
  864. if (table != null) {
  865. return true;
  866. }
  867. // sequences that are not primitives are not skippable
  868. if (TypeSystem.IsSequenceType(selection.ClrType) && !selection.SqlType.CanBeColumn) {
  869. return false;
  870. }
  871. switch (selection.NodeType) {
  872. case SqlNodeType.AliasRef: {
  873. SqlNode node = ((SqlAliasRef)selection).Alias.Node;
  874. SqlSelect select = node as SqlSelect;
  875. if (select != null) {
  876. return CanSkipOnSelection(select.Selection);
  877. }
  878. SqlUnion union = node as SqlUnion;
  879. if (union != null) {
  880. bool left = default(bool);
  881. bool right = default(bool);
  882. SqlSelect selectLeft = union.Left as SqlSelect;
  883. if (selectLeft != null) {
  884. left = CanSkipOnSelection(selectLeft.Selection);
  885. }
  886. SqlSelect selectRight = union.Right as SqlSelect;
  887. if (selectRight != null) {
  888. right = CanSkipOnSelection(selectRight.Selection);
  889. }
  890. return left && right;
  891. }
  892. SqlExpression expr = (SqlExpression)node;
  893. return CanSkipOnSelection(expr);
  894. }
  895. case SqlNodeType.New:
  896. SqlNew sn = (SqlNew)selection;
  897. // check each member of the projection for sequences
  898. foreach (SqlMemberAssign ma in sn.Members) {
  899. if (!CanSkipOnSelection(ma.Expression))
  900. return false;
  901. }
  902. if (sn.ArgMembers != null) {
  903. for (int i = 0, n = sn.ArgMembers.Count; i < n; ++i) {
  904. if (!CanSkipOnSelection(sn.Args[i])) {
  905. return false;
  906. }
  907. }
  908. }
  909. break;
  910. }
  911. return true;
  912. }
  913. /// <summary>
  914. /// SQL2000:
  915. /// SELECT *
  916. /// FROM sequence
  917. /// WHERE NOT EXISTS (
  918. /// SELECT TOP count *
  919. /// FROM sequence)
  920. ///
  921. /// SQL2005: SELECT *
  922. /// FROM (SELECT sequence.*,
  923. /// ROW_NUMBER() OVER (ORDER BY order) AS ROW_NUMBER
  924. /// FROM sequence)
  925. /// WHERE ROW_NUMBER > count
  926. /// </summary>
  927. /// <param name="sequence">Sequence containing elements to skip</param>
  928. /// <param name="count">Number of elements to skip</param>
  929. /// <returns>SELECT node</returns>
  930. private SqlSelect VisitSkip(Expression sequence, Expression skipCount) {
  931. SqlExpression skipExp = this.VisitExpression(skipCount);
  932. // verify that count >= 0
  933. if (skipExp.NodeType == SqlNodeType.Value) {
  934. SqlValue constSkipCount = (SqlValue)skipExp;
  935. if (typeof(int).IsAssignableFrom(constSkipCount.Value.GetType()) && ((int)constSkipCount.Value) < 0) {
  936. throw Error.ArgumentOutOfRange("skipCount");
  937. }
  938. }
  939. SqlSelect select = this.VisitSequence(sequence);
  940. return this.GenerateSkipTake(select, skipExp, null);
  941. }
  942. private SqlSelect GenerateSkipTake(SqlSelect sequence, SqlExpression skipExp, SqlExpression takeExp) {
  943. SqlSelect select = this.LockSelect(sequence);
  944. // no skip?
  945. if (skipExp == null) {
  946. if (takeExp != null) {
  947. select.Top = takeExp;
  948. }
  949. return select;
  950. }
  951. SqlAlias alias = new SqlAlias(select);
  952. SqlAliasRef aref = new SqlAliasRef(alias);
  953. if (this.UseConverterStrategy(ConverterStrategy.SkipWithRowNumber)) {
  954. // use ROW_NUMBER() (preferred)
  955. SqlColumn rowNumber = new SqlColumn("ROW_NUMBER", sql.RowNumber(new List<SqlOrderExpression>(), this.dominatingExpression));
  956. SqlColumnRef rowNumberRef = new SqlColumnRef(rowNumber);
  957. select.Row.Columns.Add(rowNumber);
  958. SqlSelect final = new SqlSelect(aref, alias, this.dominatingExpression);
  959. if (takeExp != null) {
  960. // use BETWEEN for skip+take combo (much faster)
  961. final.Where = sql.Between(
  962. rowNumberRef,
  963. sql.Add(skipExp, 1),
  964. sql.Binary(Sql

Large files files are truncated, but you can click here to view the full file