Beispiel #1
0
        /// <summary>
        /// Read tokens, get the first column name, handle . character
        /// </summary>
        /// <param name="tokens"></param>
        /// <returns>column object that contains table name and column name</returns>
        public static Column GetQualifiedColumnName(ref List <TSQLToken> tokens)
        {
            TSQLToken possibleColName = tokens[0];

            tokens.RemoveAt(0);

            if (tokens.Count != 0 && tokens[0].Text == ".")
            {
                tokens.RemoveAt(0);
                TSQLToken actualColName = tokens[0];
                tokens.RemoveAt(0);
                string tableName = possibleColName.Text;
                return(new Column
                {
                    ColumnName = actualColName.Text,
                    TableName = tableName,
                    _TokenType = possibleColName.Type
                });
            }
            return(new Column
            {
                ColumnName = possibleColName.Text,
                _TokenType = possibleColName.Type
            });
        }
Beispiel #2
0
        public bool MoveNext()
        {
            CheckDisposed();

            _current = null;

            if (_hasMore)
            {
                if (IncludeWhitespace)
                {
                    _hasMore = _charReader.Read();
                }
                else
                {
                    _hasMore = _charReader.ReadNextNonWhitespace();
                }

                if (_hasMore)
                {
                    SetCurrent();
                }
            }

            return(_hasMore);
        }
Beispiel #3
0
 public static void CompareTokens(TSQLToken expected, TSQLToken actual)
 {
     Assert.AreEqual(expected.BeginPosition, actual.BeginPosition, "Token begin position does not match.");
     Assert.AreEqual(expected.EndPosition, actual.EndPosition, "Token end position does not match.");
     Assert.AreEqual(expected.Text, actual.Text, "Token text does not match.");
     Assert.AreEqual(expected.GetType(), actual.GetType());
 }
Beispiel #4
0
        private static QueryType GetQueryType(TSQLToken token)
        {
            QueryType queryType;

            switch (token.Text.ToUpper())
            {
            case "SELECT":
                queryType = QueryType.Select;
                break;

            case "INSERT":
                queryType = QueryType.Insert;
                break;

            case "UPDATE":
                queryType = QueryType.Update;
                break;

            case "DELETE":
                queryType = QueryType.Delete;
                break;

            default:
                return(QueryType.Unknown);
            }

            return(queryType);
        }
        private string GetFunctionExpression(TSQLToken token, string definition)
        {
            int startIndex       = startIndex = token.BeginPosition;
            int functionEndIndex = functionEndIndex = this.FindFunctionEndIndex(startIndex + token.Text.Length, definition);

            string functionExpression = null;

            if (functionEndIndex != -1)
            {
                functionExpression = definition.Substring(startIndex, functionEndIndex - startIndex + 1);
            }

            return(functionExpression);
        }
Beispiel #6
0
        /// <summary>
        /// Remove first item and check if it is same as keyword
        /// </summary>
        /// <param name="tokens"></param>
        /// <param name="keyword"></param>
        public static void PopAndCheck(ref List <TSQLToken> tokens, string keyword)
        {
            if (tokens.Count == 0)
            {
                throw new Exception("tokens size is 0");
            }

            TSQLToken first = tokens[0];

            tokens.RemoveAt(0);
            if (first.Text.ToLower() != keyword)
            {
                throw new Exception(string.Format("{0} != {1}", first.Text, keyword));
            }
        }
Beispiel #7
0
        /// <summary>
        /// Read tokens, get the table name, handle . character
        /// </summary>
        /// <param name="tokens"></param>
        /// <returns>table object that contains table name and database name</returns>
        public static Table GetQualifiedTableName(ref List <TSQLToken> tokens, string connectedDB)
        {
            TSQLToken possibleColName = tokens[0];

            tokens.RemoveAt(0);

            if (tokens.Count != 0 && tokens[0].Text == ".")
            {
                tokens.RemoveAt(0);
                TSQLToken actualColName = tokens[0];
                tokens.RemoveAt(0);
                string databaseName = possibleColName.Text;
                return(new Table(actualColName.Text, databaseName));
            }
            return(new Table(possibleColName.Text, connectedDB));
        }
Beispiel #8
0
        public ITSQLStatementParser Create(TSQLToken token)
        {
            if (token.Type == TSQLTokenType.Keyword)
            {
                TSQLKeywords keyword = token.AsKeyword.Keyword;

                if (keyword == TSQLKeywords.SELECT)
                {
                    return(new TSQLSelectStatementParser());
                }
                // not fully implemented yet
                //else if (keyword == TSQLKeywords.INSERT)
                //{
                //	return new TSQLInsertStatementParser();
                //}
                //else if (keyword == TSQLKeywords.UPDATE)
                //{
                //	return new TSQLUpdateStatementParser();
                //}
                //else if (keyword == TSQLKeywords.DELETE)
                //{
                //	return new TSQLDeleteStatementParser();
                //}
                //else if (keyword == TSQLKeywords.MERGE)
                //{
                //	return new TSQLMergeStatementParser();
                //}
                //else if (keyword == TSQLKeywords.WITH)
                //{
                //	return new TSQLWithAggregateStatementParser();
                //}
                else
                {
                    return(new TSQLUnknownStatementParser());
                }
            }
            else
            {
                return(new TSQLUnknownStatementParser());

                // not fully implemented yet
                //return new TSQLExecuteStatementParser();
            }
        }
        /// <summary>
        /// Go rules:
        ///  can be first word followed by comments
        /// cannot be first full word on line along with other script.
        /// cannot be multiple go on same line.
        /// may be last full word on line: remove just that word
        /// Can be last full word on line followed only by comment
        /// </summary>
        /// <param name="script">Script contains go (not just naive check, go is in _statements)</param>
        /// <returns></returns>
        private ImmutableList <string> ProcessStatements(string script)
        {
            int pos = 0;

            foreach (TSQLStatement statement in _statements)
            {
                TSQLToken goToken = statement.Tokens.FirstOrDefault(token => TokenContainsGo(token));
                if (goToken != null)
                {
                    _results.Add(script.Substring(pos, goToken.BeginPosition - pos));
                    pos = goToken.EndPosition + 1;
                }
            }
            if (pos < script.Length) // if last thing was go, this won't enter
            {
                _results.Add(script.Substring(pos, script.Length - pos));
            }
            return(ImmutableList.Create(_results.ToArray()));
        }
Beispiel #10
0
        public ITSQLStatementParser Create(TSQLToken token)
        {
            if (token.Type == TSQLTokenType.Keyword)
            {
                TSQLKeywords keyword = token.AsKeyword.Keyword;

                if (keyword == TSQLKeywords.SELECT)
                {
                    return(new TSQLSelectStatementParser());
                }
                else
                {
                    return(new TSQLUnknownStatementParser());
                }
            }
            else
            {
                return(new TSQLUnknownStatementParser());

                // TODO: check for an EXEC without the keyword
            }
        }
Beispiel #11
0
 private static bool SkipToken(TSQLToken token)
 => token.Type == TSQLTokenType.Identifier ||
 (token.Type == TSQLTokenType.Keyword && token.Text.ToUpper(CultureInfo.InvariantCulture) == "AS");
 private static bool TokenContainsGo(TSQLToken token)
 => token.AsKeyword == null ? false : token.AsKeyword.Text.Equals(GO, StringComparison.CurrentCultureIgnoreCase);
Beispiel #13
0
        private void SetCurrent()
        {
            characterHolder.Length = 0;
            // TODO: review Position property viability for situations like network streams
            int startPosition = _charReader.Position;

            if (
                IncludeWhitespace &&
                char.IsWhiteSpace(_charReader.Current))
            {
                do
                {
                    characterHolder.Append(_charReader.Current);
                } while (
                    _charReader.Read() &&
                    char.IsWhiteSpace(_charReader.Current));

                if (!_charReader.EOF)
                {
                    _charReader.Putback();
                }
            }
            else
            {
                characterHolder.Append(_charReader.Current);

                switch (_charReader.Current)
                {
                // period can signal the start of a numeric literal if followed by a number
                case '.':
                {
                    if (_charReader.Read())
                    {
                        if (
                            _charReader.Current == '0' ||
                            _charReader.Current == '1' ||
                            _charReader.Current == '2' ||
                            _charReader.Current == '3' ||
                            _charReader.Current == '4' ||
                            _charReader.Current == '5' ||
                            _charReader.Current == '6' ||
                            _charReader.Current == '7' ||
                            _charReader.Current == '8' ||
                            _charReader.Current == '9'
                            )
                        {
                            characterHolder.Append(_charReader.Current);

                            goto case '0';
                        }
                        else
                        {
                            _charReader.Putback();
                        }
                    }

                    break;
                }

                // all single character sequences with no optional double character sequence
                case ',':
                case ';':
                case '(':
                case ')':
                case '~':
                {
                    break;
                }

                // --
                // -=
                // -
                case '-':
                {
                    if (_charReader.Read())
                    {
                        if (_charReader.Current == '-')
                        {
                            do
                            {
                                characterHolder.Append(_charReader.Current);
                            } while (
                                _charReader.Read() &&
                                _charReader.Current != '\r' &&
                                _charReader.Current != '\n');

                            if (!_charReader.EOF)
                            {
                                _charReader.Putback();
                            }
                        }
                        else if (_charReader.Current == '=')
                        {
                            characterHolder.Append(_charReader.Current);
                        }
                        else
                        {
                            _charReader.Putback();
                        }
                    }

                    break;
                }

                // /* */
                // /=
                // /
                case '/':
                {
                    if (_charReader.Read())
                    {
                        if (_charReader.Current == '*')
                        {
                            characterHolder.Append(_charReader.Current);

                            // supporting nested comments
                            int currentLevel = 1;

                            bool lastWasStar  = false;
                            bool lastWasSlash = false;

                            while (
                                _charReader.Read() &&
                                (
                                    currentLevel > 1 ||
                                    // */
                                    !(
                                        lastWasStar &&
                                        _charReader.Current == '/'
                                        )
                                ))
                            {
                                // /*
                                if (
                                    lastWasSlash &&
                                    _charReader.Current == '*')
                                {
                                    currentLevel++;
                                    lastWasSlash = false;
                                    lastWasStar  = false;
                                }
                                // */
                                else if (
                                    lastWasStar &&
                                    _charReader.Current == '/')
                                {
                                    currentLevel--;
                                    lastWasSlash = false;
                                    lastWasStar  = false;
                                }
                                else
                                {
                                    lastWasSlash = _charReader.Current == '/';
                                    lastWasStar  = _charReader.Current == '*';
                                }

                                characterHolder.Append(_charReader.Current);
                            }

                            if (!_charReader.EOF)
                            {
                                characterHolder.Append(_charReader.Current);
                            }
                        }
                        else if (_charReader.Current == '=')
                        {
                            characterHolder.Append(_charReader.Current);
                        }
                        else
                        {
                            _charReader.Putback();
                        }
                    }

                    break;
                }

                // <>
                // <=
                // <
                case '<':
                {
                    if (_charReader.Read())
                    {
                        if (
                            _charReader.Current == '>' ||
                            _charReader.Current == '='
                            )
                        {
                            characterHolder.Append(_charReader.Current);
                        }
                        else
                        {
                            _charReader.Putback();
                        }
                    }

                    break;
                }

                // !=
                // !<
                // !>
                case '!':
                {
                    if (_charReader.Read())
                    {
                        if (
                            _charReader.Current == '=' ||
                            _charReader.Current == '<' ||
                            _charReader.Current == '>'
                            )
                        {
                            characterHolder.Append(_charReader.Current);
                        }
                        else
                        {
                            _charReader.Putback();
                        }
                    }

                    break;
                }

                // =*
                // =
                case '=':
                {
                    if (_charReader.Read())
                    {
                        if (
                            _charReader.Current == '*'
                            )
                        {
                            characterHolder.Append(_charReader.Current);
                        }
                        else
                        {
                            _charReader.Putback();
                        }
                    }

                    break;
                }

                // &=
                case '&':
                // |=
                case '|':
                // ^=
                case '^':
                // +=
                case '+':
                // *=
                case '*':
                // %=
                case '%':
                // >=
                case '>':
                {
                    if (_charReader.Read())
                    {
                        if (_charReader.Current == '=')
                        {
                            characterHolder.Append(_charReader.Current);
                        }
                        else
                        {
                            _charReader.Putback();
                        }
                    }

                    break;
                }

                // N''
                case 'N':
                {
                    if (_charReader.Read())
                    {
                        if (_charReader.Current == '\'')
                        {
                            characterHolder.Append(_charReader.Current);

                            goto case '\'';
                        }
                        else
                        {
                            _charReader.Putback();

                            goto default;
                        }
                    }

                    break;
                }

                // ::
                case ':':
                {
                    if (_charReader.Read())
                    {
                        if (_charReader.Current == ':')
                        {
                            characterHolder.Append(_charReader.Current);
                        }
                        else
                        {
                            _charReader.Putback();
                        }
                    }

                    break;
                }

                // ''
                case '\'':
                // ""
                case '\"':
                // [dbo]
                case '[':
                {
                    char escapeChar;

                    if (_charReader.Current == '[')
                    {
                        escapeChar = ']';
                    }
                    else
                    {
                        escapeChar = _charReader.Current;
                    }

                    bool stillEscaped;

                    // read until '
                    // UNLESS the ' is doubled up
                    do
                    {
                        while (
                            _charReader.Read() &&
                            _charReader.Current != escapeChar)
                        {
                            characterHolder.Append(_charReader.Current);
                        }

                        if (!_charReader.EOF)
                        {
                            characterHolder.Append(_charReader.Current);
                        }

                        stillEscaped =
                            !_charReader.EOF &&
                            _charReader.Read() &&
                            _charReader.Current == escapeChar;

                        if (stillEscaped)
                        {
                            characterHolder.Append(_charReader.Current);
                        }
                    } while (stillEscaped);

                    if (!_charReader.EOF)
                    {
                        _charReader.Putback();
                    }

                    break;
                }

                // 0 can start a numeric or binary literal with different parsing logic
                // 0x69048AEFDD010E
                // 0x
                case '0':
                {
                    if (_charReader.Read())
                    {
                        if (
                            _charReader.Current == 'x' ||
                            _charReader.Current == 'X')
                        {
                            characterHolder.Append(_charReader.Current);

                            bool foundEnd = false;

                            while (
                                !foundEnd &&
                                _charReader.Read())
                            {
                                switch (_charReader.Current)
                                {
                                case '0':
                                case '1':
                                case '2':
                                case '3':
                                case '4':
                                case '5':
                                case '6':
                                case '7':
                                case '8':
                                case '9':
                                case 'a':
                                case 'b':
                                case 'c':
                                case 'd':
                                case 'e':
                                case 'f':
                                case 'A':
                                case 'B':
                                case 'C':
                                case 'D':
                                case 'E':
                                case 'F':
                                {
                                    characterHolder.Append(_charReader.Current);

                                    break;
                                }

                                // backslash line continuation
                                // https://docs.microsoft.com/en-us/sql/t-sql/language-elements/sql-server-utilities-statements-backslash?view=sql-server-2017
                                case '\\':
                                {
                                    characterHolder.Append(_charReader.Current);

                                    if (
                                        !foundEnd &&
                                        _charReader.Read())
                                    {
                                        // should be \r or \n
                                        characterHolder.Append(_charReader.Current);

                                        if (_charReader.Current == '\r')
                                        {
                                            if (
                                                !foundEnd &&
                                                _charReader.Read())
                                            {
                                                // should be \n
                                                characterHolder.Append(_charReader.Current);
                                            }
                                        }
                                    }

                                    break;
                                }

                                default:
                                {
                                    foundEnd = true;

                                    break;
                                }
                                }
                            }

                            if (foundEnd)
                            {
                                _charReader.Putback();
                            }
                        }
                        else
                        {
                            _charReader.Putback();

                            goto case '1';
                        }
                    }

                    break;
                }

                // numeric literals
                // 1894.1204
                // 0.5E-2
                // 123E-3
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                {
                    bool foundEnd    = false;
                    bool foundPeriod = false;

                    while (
                        !foundEnd &&
                        _charReader.Read())
                    {
                        switch (_charReader.Current)
                        {
                        case 'e':
                        case 'E':
                        {
                            characterHolder.Append(_charReader.Current);

                            if (_charReader.Read())
                            {
                                switch (_charReader.Current)
                                {
                                case '-':
                                case '+':
                                {
                                    characterHolder.Append(_charReader.Current);

                                    break;
                                }

                                default:
                                {
                                    _charReader.Putback();

                                    break;
                                }
                                }
                            }

                            while (
                                !foundEnd &&
                                _charReader.Read())
                            {
                                switch (_charReader.Current)
                                {
                                case '0':
                                case '1':
                                case '2':
                                case '3':
                                case '4':
                                case '5':
                                case '6':
                                case '7':
                                case '8':
                                case '9':
                                {
                                    characterHolder.Append(_charReader.Current);

                                    break;
                                }

                                default:
                                {
                                    foundEnd = true;

                                    break;
                                }
                                }
                            }

                            break;
                        }

                        case '.':
                        {
                            if (foundPeriod)
                            {
                                foundEnd = true;
                            }
                            else
                            {
                                characterHolder.Append(_charReader.Current);

                                foundPeriod = true;
                            }

                            break;
                        }

                        case '0':
                        case '1':
                        case '2':
                        case '3':
                        case '4':
                        case '5':
                        case '6':
                        case '7':
                        case '8':
                        case '9':
                        {
                            characterHolder.Append(_charReader.Current);

                            break;
                        }

                        // running into a special character signals the end of a previous grouping of normal characters
                        default:
                        {
                            foundEnd = true;

                            break;
                        }
                        }
                    }

                    if (foundEnd)
                    {
                        _charReader.Putback();
                    }

                    break;
                }

                // $45.56
                // $IDENTITY
                case '$':
                {
                    if (_charReader.Read())
                    {
                        if (
                            _charReader.Current == '-' ||
                            _charReader.Current == '+' ||
                            _charReader.Current == '.' ||
                            _charReader.Current == '0' ||
                            _charReader.Current == '1' ||
                            _charReader.Current == '2' ||
                            _charReader.Current == '3' ||
                            _charReader.Current == '4' ||
                            _charReader.Current == '5' ||
                            _charReader.Current == '6' ||
                            _charReader.Current == '7' ||
                            _charReader.Current == '8' ||
                            _charReader.Current == '9'
                            )
                        {
                            _charReader.Putback();

                            goto case '£';
                        }
                        else
                        {
                            _charReader.Putback();

                            goto default;
                        }
                    }

                    break;
                }

                // other Unicode currency symbols recognized by SSMS
                case '£':
                case '¢':
                case '¤':
                case '¥':
                case '€':
                case '₡':
                case '₱':
                case '﷼':
                case '₩':
                case '₮':
                case '₨':
                case '₫':
                case '฿':
                case '៛':
                case '₪':
                case '₭':
                case '₦':
                case '৲':
                case '৳':
                case '﹩':
                case '₠':
                case '₢':
                case '₣':
                case '₤':
                case '₥':
                case '₧':
                case '₯':
                case '₰':
                case '$':
                case '¢':
                case '£':
                case '¥':
                case '₩':
                {
                    bool foundEnd    = false;
                    bool foundPeriod = false;

                    if (_charReader.Read())
                    {
                        switch (_charReader.Current)
                        {
                        case '-':
                        case '+':
                        {
                            characterHolder.Append(_charReader.Current);

                            break;
                        }

                        default:
                        {
                            _charReader.Putback();

                            break;
                        }
                        }
                    }

                    while (
                        !foundEnd &&
                        _charReader.Read())
                    {
                        switch (_charReader.Current)
                        {
                        case '.':
                        {
                            if (foundPeriod)
                            {
                                foundEnd = true;
                            }
                            else
                            {
                                characterHolder.Append(_charReader.Current);

                                foundPeriod = true;
                            }

                            break;
                        }

                        case '0':
                        case '1':
                        case '2':
                        case '3':
                        case '4':
                        case '5':
                        case '6':
                        case '7':
                        case '8':
                        case '9':
                        {
                            characterHolder.Append(_charReader.Current);

                            break;
                        }

                        default:
                        {
                            foundEnd = true;

                            break;
                        }
                        }
                    }

                    if (foundEnd)
                    {
                        _charReader.Putback();
                    }

                    break;
                }

                default:
                {
                    bool foundEnd = false;

                    while (
                        !foundEnd &&
                        _charReader.Read())
                    {
                        switch (_charReader.Current)
                        {
                        // running into a special character signals the end of a previous grouping of normal characters
                        case ' ':
                        case '\t':
                        case '\r':
                        case '\n':
                        case '.':
                        case ',':
                        case ';':
                        case '(':
                        case ')':
                        case '+':
                        case '-':
                        case '*':
                        case '=':
                        case '/':
                        case '<':
                        case '>':
                        case '!':
                        case '%':
                        case '^':
                        case '&':
                        case '|':
                        case '~':
                        case ':':
                        case '[':
                        // Backslash (Line Continuation)
                        case '\\':
                        case '£':
                        case '¢':
                        case '¤':
                        case '¥':
                        case '€':
                        case '₡':
                        case '₱':
                        case '﷼':
                        case '₩':
                        case '₮':
                        case '₨':
                        case '₫':
                        case '฿':
                        case '៛':
                        case '₪':
                        case '₭':
                        case '₦':
                        case '৲':
                        case '৳':
                        case '﹩':
                        case '₠':
                        case '₢':
                        case '₣':
                        case '₤':
                        case '₥':
                        case '₧':
                        case '₯':
                        case '₰':
                        case '$':
                        case '¢':
                        case '£':
                        case '¥':
                        case '₩':
                        {
                            foundEnd = true;

                            break;
                        }

                        default:
                        {
                            characterHolder.Append(_charReader.Current);

                            break;
                        }
                        }
                    }

                    if (foundEnd)
                    {
                        _charReader.Putback();
                    }

                    break;
                }
                }
            }

            _current = new TSQLTokenFactory().Parse(
                characterHolder.ToString(),
                startPosition,
                startPosition + characterHolder.Length - 1,
                UseQuotedIdentifiers);
        }
        public System.Drawing.Color GetColorForToken(
            TSQLToken token)
        {
            if (token.Type == TSQLTokenType.Keyword)
            {
                if (token.AsKeyword.Keyword.In(
                        TSQLKeywords.NULL,
                        TSQLKeywords.AND,
                        TSQLKeywords.OR,
                        TSQLKeywords.NOT,
                        TSQLKeywords.LEFT,
                        TSQLKeywords.RIGHT,
                        TSQLKeywords.INNER,
                        TSQLKeywords.OUTER,
                        TSQLKeywords.JOIN,
                        TSQLKeywords.ALL,
                        TSQLKeywords.ANY,
                        TSQLKeywords.SOME,
                        TSQLKeywords.BETWEEN,
                        TSQLKeywords.CROSS,
                        TSQLKeywords.EXISTS,
                        TSQLKeywords.IN,
                        TSQLKeywords.IS,
                        TSQLKeywords.LIKE,
                        TSQLKeywords.PIVOT,
                        TSQLKeywords.UNPIVOT
                        ))
                {
                    return(System.Drawing.Color.Gray);
                }

                if (token.AsKeyword.Keyword.In(
                        TSQLKeywords.UPDATE,
                        TSQLKeywords.COLLATE
                        ))
                {
                    return(System.Drawing.Color.Fuchsia);
                }

                return(System.Drawing.Color.Blue);
            }

            if (
                token.Type == TSQLTokenType.Character ||
                token.Type == TSQLTokenType.Operator)
            {
                return(System.Drawing.Color.Gray);
            }

            if (
                token.Type == TSQLTokenType.SingleLineComment ||
                token.Type == TSQLTokenType.MultilineComment)
            {
                return(System.Drawing.Color.Green);
            }

            if (token.Type == TSQLTokenType.StringLiteral)
            {
                return(System.Drawing.Color.Red);
            }

            if (token.Type == TSQLTokenType.SystemIdentifier ||
                token.Type == TSQLTokenType.SystemVariable)
            {
                return(System.Drawing.Color.Fuchsia);
            }

            // https://docs.microsoft.com/en-us/sql/t-sql/functions/functions
            if (token.Type == TSQLTokenType.Identifier &&
                !token.Text.StartsWith("[") &&
                KnownFunctions.ContainsKey(token.AsIdentifier.Name.ToUpper()))
            {
                return(System.Drawing.Color.Fuchsia);
            }

            if (token.Type == TSQLTokenType.Identifier &&
                !token.Text.StartsWith("[") &&
                PseudoKeywords.ContainsKey(token.AsIdentifier.Name.ToUpper()))
            {
                return(System.Drawing.Color.Blue);
            }

            return(System.Drawing.Color.Black);
        }
Beispiel #15
0
 public void Putback()
 {
     _hasExtra   = true;
     _extraToken = _current;
     _hasMore    = true;
 }