/// <summary> /// Translate a NewInstance(Element(X)) expression into /// "select top(1) * from X" /// </summary> /// <param name="e"></param> /// <returns></returns> private ISqlFragment VisitCollectionConstructor(DbNewInstanceExpression e) { Debug.Assert(e.Arguments.Count <= 1); if (e.Arguments.Count == 1 && e.Arguments[0].ExpressionKind == DbExpressionKind.Element) { DbElementExpression elementExpr = e.Arguments[0] as DbElementExpression; SqlSelectStatement result = VisitExpressionEnsureSqlStatement(elementExpr.Argument); if (!IsCompatible(result, DbExpressionKind.Element)) { Symbol fromSymbol; TypeUsage inputType = MetadataHelpers.GetElementTypeUsage(elementExpr.Argument.ResultType); result = CreateNewSelectStatement(result, "element", inputType, out fromSymbol); AddFromSymbol(result, "element", fromSymbol, false); } result.Top.SetTopCount(1); return(result); } // Otherwise simply build this out as a union-all ladder CollectionType collectionType = MetadataHelpers.GetEdmType <CollectionType>(e.ResultType); Debug.Assert(collectionType != null); bool isScalarElement = MetadataHelpers.IsPrimitiveType(collectionType.TypeUsage); SqlBuilder resultSql = new SqlBuilder(); string separator = ""; // handle empty table if (e.Arguments.Count == 0) { Debug.Assert(isScalarElement); resultSql.Append(" select cast(null as "); resultSql.Append(MetadataHelpers.GetSqlPrimitiveType(collectionType.TypeUsage)); resultSql.Append(") as x from (select 1) as y where 1=0"); } foreach (DbExpression arg in e.Arguments) { resultSql.Append(separator); resultSql.Append(" select "); resultSql.Append(arg.Accept(this)); // For scalar elements, no alias is appended yet. Add this. if (isScalarElement) { resultSql.Append(" as x "); } separator = " union all "; } return(resultSql); }
/// <summary> /// This is called from <see cref="GenerateSql(DbQueryCommandTree)"/> and nodes which require a /// select statement as an argument e.g. <see cref="Visit(DbIsEmptyExpression)"/>, /// <see cref="Visit(DbUnionAllExpression)"/>. /// /// SqlGenerator needs its child to have a proper alias if the child is /// just an extent or a join. /// /// The normal relational nodes result in complete valid SQL statements. /// For the rest, we need to treat them as there was a dummy /// <code> /// -- originally {expression} /// -- change that to /// SELECT * /// FROM {expression} as c /// </code> /// /// DbLimitExpression needs to start the statement but not add the default columns /// </summary> /// <param name="e"></param> /// <param name="addDefaultColumns"></param> /// <returns></returns> private SqlSelectStatement VisitExpressionEnsureSqlStatement(DbExpression e, bool addDefaultColumns) { Debug.Assert(MetadataHelpers.IsCollectionType(e.ResultType.EdmType)); SqlSelectStatement result; switch (e.ExpressionKind) { case DbExpressionKind.Project: case DbExpressionKind.Filter: case DbExpressionKind.GroupBy: case DbExpressionKind.Sort: result = e.Accept(this) as SqlSelectStatement; break; default: string inputVarName = "c"; // any name will do - this is my random choice. symbolTable.EnterScope(); TypeUsage type = null; switch (e.ExpressionKind) { case DbExpressionKind.Scan: case DbExpressionKind.CrossJoin: case DbExpressionKind.FullOuterJoin: case DbExpressionKind.InnerJoin: case DbExpressionKind.LeftOuterJoin: case DbExpressionKind.CrossApply: case DbExpressionKind.OuterApply: // It used to be type = e.ResultType. type = MetadataHelpers.GetElementTypeUsage(e.ResultType); break; default: Debug.Assert(MetadataHelpers.IsCollectionType(e.ResultType.EdmType)); type = MetadataHelpers.GetEdmType <CollectionType>(e.ResultType).TypeUsage; break; } Symbol fromSymbol; result = VisitInputExpression(e, inputVarName, type, out fromSymbol); AddFromSymbol(result, inputVarName, fromSymbol); symbolTable.ExitScope(); break; } if (addDefaultColumns && result.Select.IsEmpty) { AddDefaultColumns(result); } return(result); }
private static Type[] PrepareTypeCoercions(DbCommandTree commandTree) { var queryTree = commandTree as DbQueryCommandTree; if (queryTree != null) { var projectExpression = queryTree.Query as DbProjectExpression; if (projectExpression != null) { var resultsType = projectExpression.Projection.ResultType.EdmType; var resultsAsStructuralType = resultsType as StructuralType; if (resultsAsStructuralType != null) { var result = new Type[resultsAsStructuralType.Members.Count]; for (int i = 0; i < resultsAsStructuralType.Members.Count; i++) { var member = resultsAsStructuralType.Members[i]; result[i] = ((PrimitiveType)member.TypeUsage.EdmType).ClrEquivalentType; } return(result); } } } var functionTree = commandTree as DbFunctionCommandTree; if (functionTree != null) { if (functionTree.ResultType != null) { var elementType = MetadataHelpers.GetElementTypeUsage(functionTree.ResultType).EdmType; if (MetadataHelpers.IsRowType(elementType)) { var members = ((RowType)elementType).Members; var result = new Type[members.Count]; for (int i = 0; i < members.Count; i++) { var member = members[i]; var primitiveType = (PrimitiveType)member.TypeUsage.EdmType; result[i] = primitiveType.ClrEquivalentType; } return(result); } else if (MetadataHelpers.IsPrimitiveType(elementType)) { return(new Type[] { ((PrimitiveType)elementType).ClrEquivalentType }); } } } return(null); }
public override ISqlFragment Visit(DbDistinctExpression e) { var result = VisitExpressionEnsureSqlStatement(e.Argument); if (!IsCompatible(result, e.ExpressionKind)) { Symbol fromSymbol; TypeUsage inputType = MetadataHelpers.GetElementTypeUsage(e.Argument.ResultType); result = CreateNewSelectStatement(result, "distinct", inputType, out fromSymbol); AddFromSymbol(result, "distinct", fromSymbol, false); } result.IsDistinct = true; return(result); }
private static Type[] PrepareTypeCoercions(DbCommandTree commandTree) { var queryTree = commandTree as DbQueryCommandTree; if (queryTree != null) { var projectExpression = queryTree.Query as DbProjectExpression; if (projectExpression != null) { var resultsType = projectExpression.Projection.ResultType.EdmType; var resultsAsStructuralType = resultsType as StructuralType; if (resultsAsStructuralType != null) { var members = resultsAsStructuralType.Members; return(members.Select(ExtractExpectedTypeForCoercion).ToArray()); } } } var functionTree = commandTree as DbFunctionCommandTree; if (functionTree != null) { if (functionTree.ResultType != null) { Debug.Assert(MetadataHelpers.IsCollectionType(functionTree.ResultType.EdmType), "Result type of a function is expected to be a collection of RowType or PrimitiveType"); var typeUsage = MetadataHelpers.GetElementTypeUsage(functionTree.ResultType); var elementType = typeUsage.EdmType; if (MetadataHelpers.IsRowType(elementType)) { var members = ((RowType)elementType).Members; return(members.Select(ExtractExpectedTypeForCoercion).ToArray()); } else if (MetadataHelpers.IsPrimitiveType(elementType)) { return(new[] { MakeTypeCoercion(((PrimitiveType)elementType).ClrEquivalentType, typeUsage) }); } else { Debug.Fail("Result type of a function is expected to be a collection of RowType or PrimitiveType"); } } } return(null); }
/// <summary> /// Translates to TOP expression. /// </summary> /// <param name="e"></param> /// <returns>A <see cref="SqlBuilder"/></returns> public override ISqlFragment Visit(DbLimitExpression e) { Debug.Assert(e.Limit is DbConstantExpression || e.Limit is DbParameterReferenceExpression, "DbLimitExpression.Limit is of invalid expression type"); var result = VisitExpressionEnsureSqlStatement(e.Argument, false); if (!IsCompatible(result, e.ExpressionKind)) { TypeUsage inputType = MetadataHelpers.GetElementTypeUsage(e.Argument.ResultType); Symbol fromSymbol; result = CreateNewSelectStatement(result, "first", inputType, out fromSymbol); AddFromSymbol(result, "first", fromSymbol, false); } result.Top.TopCount = HandleCountExpression(e.Limit); return(result); }
/// <summary> /// Create a SampleCommand object, given the provider manifest and command tree /// </summary> private DbCommand CreateCommand(DbProviderManifest manifest, DbCommandTree commandTree) { if (manifest == null) { throw new ArgumentNullException("manifest"); } if (commandTree == null) { throw new ArgumentNullException("commandTree"); } FbCommand command = new FbCommand(); #region Type coercions // Set expected column types for DbQueryCommandTree DbQueryCommandTree queryTree = commandTree as DbQueryCommandTree; if (queryTree != null) { DbProjectExpression projectExpression = queryTree.Query as DbProjectExpression; if (projectExpression != null) { EdmType resultsType = projectExpression.Projection.ResultType.EdmType; StructuralType resultsAsStructuralType = resultsType as StructuralType; if (resultsAsStructuralType != null) { command.ExpectedColumnTypes = new PrimitiveType[resultsAsStructuralType.Members.Count]; for (int ordinal = 0; ordinal < resultsAsStructuralType.Members.Count; ordinal++) { EdmMember member = resultsAsStructuralType.Members[ordinal]; PrimitiveType primitiveType = member.TypeUsage.EdmType as PrimitiveType; command.ExpectedColumnTypes[ordinal] = primitiveType; } } } } // Set expected column types for DbFunctionCommandTree DbFunctionCommandTree functionTree = commandTree as DbFunctionCommandTree; if (functionTree != null) { if (functionTree.ResultType != null) { Debug.Assert(MetadataHelpers.IsCollectionType(functionTree.ResultType.EdmType), "Result type of a function is expected to be a collection of RowType or PrimitiveType"); EdmType elementType = MetadataHelpers.GetElementTypeUsage(functionTree.ResultType).EdmType; if (MetadataHelpers.IsRowType(elementType)) { ReadOnlyMetadataCollection <EdmMember> members = ((RowType)elementType).Members; command.ExpectedColumnTypes = new PrimitiveType[members.Count]; for (int ordinal = 0; ordinal < members.Count; ordinal++) { EdmMember member = members[ordinal]; PrimitiveType primitiveType = (PrimitiveType)member.TypeUsage.EdmType; command.ExpectedColumnTypes[ordinal] = primitiveType; } } else if (MetadataHelpers.IsPrimitiveType(elementType)) { command.ExpectedColumnTypes = new PrimitiveType[1]; command.ExpectedColumnTypes[0] = (PrimitiveType)elementType; } else { Debug.Fail("Result type of a function is expected to be a collection of RowType or PrimitiveType"); } } } #endregion List <DbParameter> parameters; CommandType commandType; command.CommandText = SqlGenerator.GenerateSql(commandTree, out parameters, out commandType); command.CommandType = commandType; // Get the function (if any) implemented by the command tree since this influences our interpretation of parameters EdmFunction function = null; if (commandTree is DbFunctionCommandTree) { function = ((DbFunctionCommandTree)commandTree).EdmFunction; } // Now make sure we populate the command's parameters from the CQT's parameters: foreach (KeyValuePair <string, TypeUsage> queryParameter in commandTree.Parameters) { FbParameter parameter; // Use the corresponding function parameter TypeUsage where available (currently, the SSDL facets and // type trump user-defined facets and type in the EntityCommand). FunctionParameter functionParameter; if (null != function && function.Parameters.TryGetValue(queryParameter.Key, false, out functionParameter)) { parameter = CreateSqlParameter(functionParameter.Name, functionParameter.TypeUsage, functionParameter.Mode, DBNull.Value); } else { parameter = CreateSqlParameter(queryParameter.Key, queryParameter.Value, ParameterMode.In, DBNull.Value); } command.Parameters.Add(parameter); } // Now add parameters added as part of SQL gen (note: this feature is only safe for DML SQL gen which // does not support user parameters, where there is no risk of name collision) if (null != parameters && 0 < parameters.Count) { if (!(commandTree is DbInsertCommandTree) && !(commandTree is DbUpdateCommandTree) && !(commandTree is DbDeleteCommandTree)) { throw new InvalidOperationException("SqlGenParametersNotPermitted"); } foreach (DbParameter parameter in parameters) { command.Parameters.Add(parameter); } } return(command); }