1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2025-08-12 23:42:47 +03:00
Files
mariadb-columnstore-engine/utils/common/genericparser.h
Alexander Barkov 69da915160 MCOL-4531 New string-to-decimal conversion implementation
This change fixes:

MCOL-4462 CAST(varchar_expr AS DECIMAL(M,N)) returns a wrong result
MCOL-4500 Bit functions processing throws internally trying to cast char into decimal representation
MCOL-4532 CAST(AS DECIMAL) returns a garbage for large values

Also, this change makes string-to-decimal conversion 5-10 times faster,
depending on exact data.
Performance implemenent is achieved by the fact that (unlike in the old
implementation), the new version does not do any "string" object copying.
2021-02-09 13:02:27 +04:00

314 lines
7.0 KiB
C++

/* Copyright (C) 2021 MariaDB Corporation.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2 of
the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA. */
#ifndef GENERICPARSER_H
#define GENERICPARSER_H
#include "conststring.h"
namespace genericparser
{
using utils::ConstString;
class Tokenizer
{
protected:
const char *mStr;
const char *mEnd;
public:
explicit Tokenizer(const char *str, size_t length)
:mStr(str), mEnd(str + length)
{ }
size_t length() const
{
return mEnd - mStr;
}
const char *ptr() const
{
return mStr;
}
bool isSpace() const
{
return mStr < mEnd && mStr[0] == ' ';
}
bool isDigit() const
{
return mStr < mEnd && mStr[0] >= '0' && mStr[0] <= '9';
}
bool isChar(char chr) const
{
return mStr < mEnd && mStr[0] == chr;
}
bool isAnyCharOf(char chr0, char chr1)
{
return mStr < mEnd && (mStr[0] == chr0 || mStr[0] == chr1);
}
ConstString tokenSpaces()
{
if (!isSpace())
return ConstString(nullptr, 0);
const char *start = mStr;
for ( ; isSpace() ; mStr++)
{ }
return ConstString(start, mStr - start);
}
ConstString tokenDigits()
{
if (!isDigit())
return ConstString(nullptr, 0);
const char *start = mStr;
for ( ; isDigit() ; mStr++)
{ }
return ConstString(start, mStr - start);
}
ConstString tokenChar(char chr)
{
if (!isChar(chr))
return ConstString(nullptr, 0);
return ConstString(mStr++, 1);
}
ConstString tokenAnyCharOf(char chr0, char chr1)
{
if (!isAnyCharOf(chr0, chr1))
return ConstString(nullptr, 0);
return ConstString(mStr++, 1);
}
};
class Parser: public Tokenizer
{
protected:
bool mSyntaxError;
public:
explicit Parser(const char *str, size_t length)
:Tokenizer(str, length), mSyntaxError(false)
{ }
explicit Parser(const std::string & str)
:Parser(str.data(), str.length())
{ }
Parser & skipLeadingSpaces()
{
tokenSpaces();
return *this;
}
bool syntaxError() const
{
return mSyntaxError;
}
bool setSyntaxError()
{
mSyntaxError = true;
return false;
}
const char *tokStart() const
{
return mStr;
}
const ConstString tokStartConstString() const
{
return ConstString(mStr, 0);
}
// A helper class template to set the parser syntax error
// if A returned isNull() after parsing.
template<class A>
class SetSyntaxErrorOnNull :public A
{
public:
SetSyntaxErrorOnNull(Parser *p)
:A(p)
{
if (A::isNull())
p->setSyntaxError();
}
};
// A helper class template for a rule in the form: <res> := [ <a> ]
template<class A>
class Opt: public A
{
public:
explicit Opt(const A &rhs)
:A(rhs)
{ }
explicit Opt(Parser *p)
:A(p)
{
if (A::isNull() && !p->syntaxError())
A::operator=(A::empty(p));
}
};
// Letters in the class template names below mean:
// U - unused - the result class does not have the source class inside
// D - derive - the result class derives from the source class
// M - member - the result class adds the source class as a member
// M - mandatory - this part is required during parse time
// O - optional - this part is optional during parse time
// A helper class template for a rule in the form: <res> := <a> <b>
// i.e. both parts are mandatory at parse time
// The value of <a> is not important, and is created
// only temporary on the stack.
// Only the value of <b> is important.
// Example:
// <period> <unsigned integer>
template<class A, class B>
class UD2MM: public B
{
public:
explicit UD2MM(Parser *p)
:B(A(p).isNull() ? B() :SetSyntaxErrorOnNull<B>(p))
{ }
explicit UD2MM(const B & b)
:B(b)
{ }
explicit UD2MM()
:B()
{ }
bool isNull() const { return B::isNull(); }
};
// A helper class template for a rule in the form: <res> := <a> <b>
// i.e. both parts are mandatory at parse time.
template<class A, class B>
class DD2MM: public A,
public B
{
public:
// Sets syntax error if <a> was not followed by <b>
explicit DD2MM(Parser *p)
:A(p),
B(A::isNull() ? B() : SetSyntaxErrorOnNull<B>(p))
{ }
explicit DD2MM(const A & a, const B &b)
:A(b), B(b)
{ }
};
// A helper class template for a rule in the form: <res> := <a> [ <b> ]
// i.e. <a> is mandatory, <b> is optional at parse time.
template<class A, class B>
class DD2MO: public A,
public B
{
public:
explicit DD2MO(Parser *p)
:A(p),
B(A::isNull() ? B() : B(p))
{ }
explicit DD2MO(const A &a, const B &b)
:A(a), B(b)
{ }
};
// A helper class template for a rule in the form: <res> := <a> [ <b> ]
// i.e. <a> is mandatory, <b> is optional at parse time.
// The value of <a> is not important and is not included
// into the target class, e.g.
// <period> [ <unsigned integer> ]
template<class A, class B>
class UD2MO: public B
{
public:
explicit UD2MO(Parser *p)
:B(A(p).isNull() ? B() : B(p))
{ }
explicit UD2MO(const B &b)
:B(b)
{ }
explicit UD2MO()
:B()
{ }
};
// A helper class template for a rule in the form: <res> := <a> [ <b> ]
// i.e. <a> is mandatory, <b> is optional at parse time.
// The result class derives from "A".
// The result class puts "B" as a member.
template<class A, class B>
class DM2MO: public A
{
protected:
B mB;
public:
explicit DM2MO(Parser *p)
:A(p),
mB(A::isNull() ? B() : B(p))
{ }
};
// A helper class template for a rule in the form: <res> := [ <a> ] <b>
// i.e. <a> is optional, <b> is mandatory at parse time.
template<class A,class B>
class DD2OM: public Opt<A>,
public B
{
public:
explicit DD2OM(Parser *p)
:Opt<A>(p), B(p)
{
if (B::isNull())
p->setSyntaxError();
}
explicit DD2OM()
:Opt<A>(A())
{ }
explicit DD2OM(const A & a, const B & b)
:Opt<A>(a), B(b)
{ }
};
// A helper class template for a rule in the form: <res> := a | b
template<class Container, class A, class B>
class Choice2: public Container
{
public:
explicit Choice2(const A & a)
:Container(a)
{ }
explicit Choice2(const B & b)
:Container(b)
{ }
explicit Choice2(Parser *p)
:Container(A(p))
{
if (Container::isNull() && !p->syntaxError())
*this = Choice2(B(p));
}
};
};
} // namespace genericparser
#endif // GENERICPARSER_H