public void ShouldResolveMaxFunction()
        {
            //Arrange
            string rawTsql = "MAX(CAST(timestamp as bigint))";

            IExpressionResolver resolver       = new TsqlFunctionResolver();
            int                      fileIndex = 0;
            CompilerContext          context   = new CompilerContext("xUnit", "irrelevant", "irrelevant", true);
            ReadOnlySpan <TSQLToken> tokens    = TSQLTokenizer.ParseTokens(rawTsql).ToArray();

            //Act
            Expression expression = resolver.Resolve(tokens, ref fileIndex, context);

            //Assert
            Assert.Equal(tokens.Length, fileIndex);
        }
        public void ShouldResolveHashBytesFunction()
        {
            //Arrange
            string rawTsql = "hashbytes ('sha1337', isNull(cast(somecolumn_01 as varchar), '') " +
                             "+ isNull(cast(cast(somecolumn_02 as date) as varchar), '')" +
                             "+ isNull(cast(somecolumn_04 collate Latin1_General_CI_AS as NVARCHAR(64)), ''))";

            IExpressionResolver resolver       = new TsqlFunctionResolver();
            int                      fileIndex = 0;
            CompilerContext          context   = new CompilerContext("xUnit", "irrelevant", "irrelevant", true);
            ReadOnlySpan <TSQLToken> tokens    = TSQLTokenizer.ParseTokens(rawTsql).ToArray();

            //Act
            Expression expression = resolver.Resolve(tokens, ref fileIndex, context);

            //Assert
            Assert.Equal(tokens.Length, fileIndex);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Resolves a database object and adds it to the current context as well
        /// as to the landscape
        /// </summary>
        /// <param name="asTargetTable">If the database object is expected to be a target table standart database and server are
        /// not added. We also may expect an opening bracket directly after the identifier which is the then not count as a function start</param>
        /// <returns>The database object</returns>
        internal static DatabaseObject ResolveDatabaseObject(ReadOnlySpan <TSQLToken> tokens, ref int fileIndex, CompilerContext context, bool asTargetTable = false)
        {
            DatabaseObject dbo;

            if (tokens[fileIndex].Text.Equals("("))
            {
                fileIndex++; //skip '('
                dbo = ResolveDatabaseObject(tokens, ref fileIndex, context, asTargetTable);
                fileIndex++; //skip ')'
            }
            else if (tokens[fileIndex].Text.ToLower().Equals("select"))
            {
                var resolver  = new SelectStatementResolver();
                var statement = resolver.Resolve(tokens, ref fileIndex, context);
                dbo = new DatabaseObject(DatabaseObjectType.SELECTION);
                dbo.Expressions.AddRange(statement.ChildExpressions);
            }
            else if (tokens[fileIndex].Type.Equals(TSQLTokenType.Identifier))
            {
                int    positionBeforeNotationResolution = fileIndex;
                string notation = ResolveDatabaseObjectIdentifier(tokens, ref fileIndex);

                if (tokens.Length > fileIndex && tokens[fileIndex].Text.Equals("(") && !asTargetTable)
                {
                    //In this section we asssume that the database object is returned by a function
                    fileIndex = positionBeforeNotationResolution;
                    var functionResolver = new TsqlFunctionResolver();
                    var function         = functionResolver.Resolve(tokens, ref fileIndex, context);
                    dbo = new DatabaseObject(DatabaseObjectType.SELECTION);
                    dbo.Expressions.Add(function);
                }
                else
                {
                    dbo = new DatabaseObject(notation, context, DatabaseObjectType.REAL, !asTargetTable);
                }
            }
            else if (tokens[fileIndex].Type.Equals(TSQLTokenType.Variable))
            {
                dbo = new DatabaseObject(DatabaseObjectType.VARIABLE)
                {
                    Name     = tokens[fileIndex].Text,
                    Database = InternalConstants.UNRELATED_DATABASE_NAME,
                    Schema   = InternalConstants.UNRELATED_SCHEMA_NAME,
                    Server   = InternalConstants.UNRELATED_SERVER_NAME
                };

                fileIndex++;
            }
            else if (tokens[fileIndex].Type.Equals(TSQLTokenType.StringLiteral) || tokens[fileIndex].Type.Equals(TSQLTokenType.NumericLiteral) || tokens[fileIndex].Type.Equals(TSQLTokenType.BinaryLiteral))
            {
                //The dbo is a set of constants e.g. ('Design Engineer', 'Tool Designer', 'Marketing Assistant')
                var dataset = ResolvesDataSet(tokens, ref fileIndex, context);
                dbo = new DatabaseObject(DatabaseObjectType.SET);
                dbo.Expressions.Add(dataset);
            }
            else if (tokens[fileIndex].Text.Equals("openquery", StringComparison.InvariantCultureIgnoreCase))
            {
                dbo = new OpenQueryStatementResolver().Resolve(tokens, ref fileIndex, context);
            }
            else
            {
                throw new InvalidSqlException("Invalid database object");
            }

            SkipTableHints(tokens, ref fileIndex);

            string alias = ResolveAlias(tokens, ref fileIndex, context);

            if (!string.IsNullOrEmpty(alias))
            {
                dbo.Alias = alias;
            }

            SkipTableHints(tokens, ref fileIndex); //Table hints can appear before OR after an alias

            return(dbo);
        }