コード例 #1
0
        /// <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);
        }
コード例 #2
0
        /// <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);
        }
コード例 #3
0
        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);
        }
コード例 #4
0
        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);
        }
コード例 #5
0
        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);
        }
コード例 #6
0
        /// <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);
        }
コード例 #7
0
        /// <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);
        }