From d409bb80b1f8d0738c47086eb72d8f1dbedc6aae Mon Sep 17 00:00:00 2001 From: Marcel Loose <loose@astron.nl> Date: Mon, 22 Dec 2003 11:03:47 +0000 Subject: [PATCH] %[ER: 65]% Added SQL-like operator like BETWEEN, IN, and LIKE. Fixed couple of bugs. --- .../PL/include/PL/Query/BinaryExprNode.h | 4 +- .../PL/include/PL/Query/ConstExprNode.h | 3 +- LCS/databases/PL/include/PL/Query/Expr.h | 28 +++++++- LCS/databases/PL/include/PL/Query/ExprNode.h | 4 +- .../PL/include/PL/Query/SQLExprNode.h | 19 +++++- .../PL/include/PL/Query/UnaryExprNode.h | 3 + LCS/databases/PL/src/Query/BinaryExprNode.cc | 8 ++- LCS/databases/PL/src/Query/BinaryExprNode.h | 4 +- LCS/databases/PL/src/Query/ConstExprNode.h | 3 +- LCS/databases/PL/src/Query/Expr.cc | 47 ++++++++++++- LCS/databases/PL/src/Query/Expr.h | 28 +++++++- LCS/databases/PL/src/Query/ExprNode.h | 4 +- LCS/databases/PL/src/Query/SQLExprNode.cc | 66 +++++++++++++------ LCS/databases/PL/src/Query/SQLExprNode.h | 19 +++++- LCS/databases/PL/src/Query/UnaryExprNode.cc | 2 +- LCS/databases/PL/src/Query/UnaryExprNode.h | 3 + 16 files changed, 199 insertions(+), 46 deletions(-) diff --git a/LCS/databases/PL/include/PL/Query/BinaryExprNode.h b/LCS/databases/PL/include/PL/Query/BinaryExprNode.h index 95cb3a9d293..45bed3f5bb9 100644 --- a/LCS/databases/PL/include/PL/Query/BinaryExprNode.h +++ b/LCS/databases/PL/include/PL/Query/BinaryExprNode.h @@ -56,8 +56,8 @@ namespace LOFAR private: - // @name The operator - const std::string itsOperand; + // @name The operation + const std::string itsOperation; // @name The operands //@{ diff --git a/LCS/databases/PL/include/PL/Query/ConstExprNode.h b/LCS/databases/PL/include/PL/Query/ConstExprNode.h index 1f91da3589d..4acf1780592 100644 --- a/LCS/databases/PL/include/PL/Query/ConstExprNode.h +++ b/LCS/databases/PL/include/PL/Query/ConstExprNode.h @@ -39,8 +39,9 @@ namespace LOFAR { //# Forward Declarations - // @defgroup ConstExprNode + // @defgroup ConstExprNode Constant Expression Nodes // @ingroup ExprNode + // // These classes represent constant expression nodes. A constant // expression is an expression that can be evaluated compile-time and // has a primitive type like int, double or string. diff --git a/LCS/databases/PL/include/PL/Query/Expr.h b/LCS/databases/PL/include/PL/Query/Expr.h index 6a0ee817e0e..f8934b5a2af 100644 --- a/LCS/databases/PL/include/PL/Query/Expr.h +++ b/LCS/databases/PL/include/PL/Query/Expr.h @@ -37,12 +37,12 @@ namespace LOFAR { namespace PL { + template<typename T> class Collection; + namespace Query { - //# Forward Declarations - - // An Expr represents the WHERE clause of a query. + // This class represents the WHERE clause of a query. class Expr { public: @@ -53,6 +53,7 @@ namespace LOFAR Expr(int value); Expr(double value); Expr(const std::string& value); + Expr(const char* const value); //@} // Construct an Expr from an ExprNode pointer. @@ -67,6 +68,27 @@ namespace LOFAR Expr operator! () const; //@} + //@{ + // The BETWEEN operator is used to test if a value is within an + // interval. + Expr between(const Expr& lhs, const Expr& rhs) const; + Expr notBetween(const Expr& lhs, const Expr& rhs) const; + //@} + + //@{ + // The IN operator is used to test if an expression is contained in a + // set of expressions. + Expr in (const Collection<Expr>& set) const; + Expr notIn(const Collection<Expr>& set) const; + //@} + + //@{ + // The LIKE operator is used to test if a value has a match with a + // pattern expression. + Expr like(const Expr& exp) const; + Expr notLike(const Expr& exp) const; + //@} + private: // @name Arithmetic operators diff --git a/LCS/databases/PL/include/PL/Query/ExprNode.h b/LCS/databases/PL/include/PL/Query/ExprNode.h index 5c4b9cd3b55..20910394667 100644 --- a/LCS/databases/PL/include/PL/Query/ExprNode.h +++ b/LCS/databases/PL/include/PL/Query/ExprNode.h @@ -39,8 +39,8 @@ namespace LOFAR //# Forward Declarations class Expr; - // @defgroup ExprNode - + // @defgroup ExprNode Expression Nodes + // // ExprNode is an abstract base class that represents the node of an // expression query. We will need to derive specific expression node // classes (e.g. ExprNodeBinary) from it. diff --git a/LCS/databases/PL/include/PL/Query/SQLExprNode.h b/LCS/databases/PL/include/PL/Query/SQLExprNode.h index 68aa76ce2ce..b8a08cb99ac 100644 --- a/LCS/databases/PL/include/PL/Query/SQLExprNode.h +++ b/LCS/databases/PL/include/PL/Query/SQLExprNode.h @@ -83,6 +83,8 @@ namespace LOFAR // expression that takes one operator (IN or NOT IN) and two // operands. It is used to check whether the first operand is present in // the second operand. + // \note When \c rhs contains no elements only the \c lhs will be + // returned when you invoke print() on InExprNode. class InExprNode : public ExprNode { public: @@ -116,7 +118,22 @@ namespace LOFAR // The user-supplied pattern can contain the wildcards "*" and "?", // where "*" means any number of characters, and "?" means exactly one // character. These wildcards will be translated to their - // SQL-equivalents "%" and "_" respectively. + // SQL-equivalents "%" and "_" respectively. + // + // We will use the backslash character (\) as escape character in the + // LIKE clause. As the backslash character needs to be escaped itself, + // we always have to provide two backslash characters as the escape + // token. + // + // Hence we get the following substitution scheme: + // \verbatim + // '*' --> '%' + // '?' --> '_' + // '\*' --> '*' + // '\?' --> '?' + // '_' --> '\\_' + // '%' --> '\\%' + // \endverbatim class LikeExprNode : public ExprNode { public: diff --git a/LCS/databases/PL/include/PL/Query/UnaryExprNode.h b/LCS/databases/PL/include/PL/Query/UnaryExprNode.h index 4e91e6ea8c7..552eeccbbe2 100644 --- a/LCS/databases/PL/include/PL/Query/UnaryExprNode.h +++ b/LCS/databases/PL/include/PL/Query/UnaryExprNode.h @@ -48,6 +48,9 @@ namespace LOFAR UnaryExprNode(const std::string& oper, const Expr& value); +// UnaryExprNode(const std::string& oper, +// const ExprNodeSet& set); + virtual ~UnaryExprNode(); virtual void print(std::ostream& os); diff --git a/LCS/databases/PL/src/Query/BinaryExprNode.cc b/LCS/databases/PL/src/Query/BinaryExprNode.cc index 68ab4093fa7..3937d261280 100644 --- a/LCS/databases/PL/src/Query/BinaryExprNode.cc +++ b/LCS/databases/PL/src/Query/BinaryExprNode.cc @@ -32,16 +32,18 @@ namespace LOFAR BinaryExprNode::BinaryExprNode(const std::string& oper, const Expr& lhs, const Expr& rhs) : - itsOperand(oper), + itsOperation(oper), itsLeft(lhs), itsRight(rhs) { } - BinaryExprNode::~BinaryExprNode() {} + BinaryExprNode::~BinaryExprNode() + { + } void BinaryExprNode::print(std::ostream& os) { - os << "(" << itsLeft << itsOperand << itsRight << ")"; + os << "(" << itsLeft << itsOperation << itsRight << ")"; } } // namespace Query diff --git a/LCS/databases/PL/src/Query/BinaryExprNode.h b/LCS/databases/PL/src/Query/BinaryExprNode.h index 95cb3a9d293..45bed3f5bb9 100644 --- a/LCS/databases/PL/src/Query/BinaryExprNode.h +++ b/LCS/databases/PL/src/Query/BinaryExprNode.h @@ -56,8 +56,8 @@ namespace LOFAR private: - // @name The operator - const std::string itsOperand; + // @name The operation + const std::string itsOperation; // @name The operands //@{ diff --git a/LCS/databases/PL/src/Query/ConstExprNode.h b/LCS/databases/PL/src/Query/ConstExprNode.h index 1f91da3589d..4acf1780592 100644 --- a/LCS/databases/PL/src/Query/ConstExprNode.h +++ b/LCS/databases/PL/src/Query/ConstExprNode.h @@ -39,8 +39,9 @@ namespace LOFAR { //# Forward Declarations - // @defgroup ConstExprNode + // @defgroup ConstExprNode Constant Expression Nodes // @ingroup ExprNode + // // These classes represent constant expression nodes. A constant // expression is an expression that can be evaluated compile-time and // has a primitive type like int, double or string. diff --git a/LCS/databases/PL/src/Query/Expr.cc b/LCS/databases/PL/src/Query/Expr.cc index 680b55aac13..300e04797f3 100644 --- a/LCS/databases/PL/src/Query/Expr.cc +++ b/LCS/databases/PL/src/Query/Expr.cc @@ -24,6 +24,7 @@ #include <PL/Query/ConstExprNode.h> #include <PL/Query/UnaryExprNode.h> #include <PL/Query/BinaryExprNode.h> +#include <PL/Query/SQLExprNode.h> #include <iostream> using std::ostream; @@ -56,6 +57,11 @@ namespace LOFAR itsNode(new StringExprNode(value)) { } + + Expr::Expr(const char* const value) : + itsNode(new StringExprNode(value)) + { + } Expr::Expr(ExprNode* const node) : itsNode(node) @@ -79,7 +85,7 @@ namespace LOFAR Expr Expr::operator! () const { - return new UnaryExprNode("NOT", *this); + return new UnaryExprNode("NOT ", *this); } @@ -149,12 +155,47 @@ namespace LOFAR Expr operator&& (const Expr& lhs, const Expr& rhs) { - return new BinaryExprNode("AND", lhs, rhs); + return new BinaryExprNode(" AND ", lhs, rhs); } Expr operator|| (const Expr& lhs, const Expr& rhs) { - return new BinaryExprNode("OR", lhs, rhs); + return new BinaryExprNode(" OR ", lhs, rhs); + } + + + /////////////////////////////////////////////////////////////////// + // SQL-like operators // + /////////////////////////////////////////////////////////////////// + + Expr Expr::between (const Expr& lhs, const Expr& rhs) const + { + return new BetweenExprNode(" BETWEEN ", *this, lhs, rhs); + } + + Expr Expr::notBetween (const Expr& lhs, const Expr& rhs) const + { + return new BetweenExprNode(" NOT BETWEEN ", *this, lhs, rhs); + } + + Expr Expr::in (const Collection<Expr>& set) const + { + return new InExprNode(" IN ", *this, set); + } + + Expr Expr::notIn (const Collection<Expr>& set) const + { + return new InExprNode(" NOT IN ", *this, set); + } + + Expr Expr::like (const Expr& exp) const + { + return new LikeExprNode(" LIKE ", *this, exp); + } + + Expr Expr::notLike (const Expr& exp) const + { + return new LikeExprNode(" NOT LIKE ", *this, exp); } diff --git a/LCS/databases/PL/src/Query/Expr.h b/LCS/databases/PL/src/Query/Expr.h index 6a0ee817e0e..f8934b5a2af 100644 --- a/LCS/databases/PL/src/Query/Expr.h +++ b/LCS/databases/PL/src/Query/Expr.h @@ -37,12 +37,12 @@ namespace LOFAR { namespace PL { + template<typename T> class Collection; + namespace Query { - //# Forward Declarations - - // An Expr represents the WHERE clause of a query. + // This class represents the WHERE clause of a query. class Expr { public: @@ -53,6 +53,7 @@ namespace LOFAR Expr(int value); Expr(double value); Expr(const std::string& value); + Expr(const char* const value); //@} // Construct an Expr from an ExprNode pointer. @@ -67,6 +68,27 @@ namespace LOFAR Expr operator! () const; //@} + //@{ + // The BETWEEN operator is used to test if a value is within an + // interval. + Expr between(const Expr& lhs, const Expr& rhs) const; + Expr notBetween(const Expr& lhs, const Expr& rhs) const; + //@} + + //@{ + // The IN operator is used to test if an expression is contained in a + // set of expressions. + Expr in (const Collection<Expr>& set) const; + Expr notIn(const Collection<Expr>& set) const; + //@} + + //@{ + // The LIKE operator is used to test if a value has a match with a + // pattern expression. + Expr like(const Expr& exp) const; + Expr notLike(const Expr& exp) const; + //@} + private: // @name Arithmetic operators diff --git a/LCS/databases/PL/src/Query/ExprNode.h b/LCS/databases/PL/src/Query/ExprNode.h index 5c4b9cd3b55..20910394667 100644 --- a/LCS/databases/PL/src/Query/ExprNode.h +++ b/LCS/databases/PL/src/Query/ExprNode.h @@ -39,8 +39,8 @@ namespace LOFAR //# Forward Declarations class Expr; - // @defgroup ExprNode - + // @defgroup ExprNode Expression Nodes + // // ExprNode is an abstract base class that represents the node of an // expression query. We will need to derive specific expression node // classes (e.g. ExprNodeBinary) from it. diff --git a/LCS/databases/PL/src/Query/SQLExprNode.cc b/LCS/databases/PL/src/Query/SQLExprNode.cc index 6a19816a5fb..2a7d30f815c 100644 --- a/LCS/databases/PL/src/Query/SQLExprNode.cc +++ b/LCS/databases/PL/src/Query/SQLExprNode.cc @@ -67,18 +67,18 @@ namespace LOFAR void InExprNode::print(std::ostream& os) { - ostringstream oss; - Collection<Expr>::const_iterator it; - for (it = itsRight.begin(); it != itsRight.end(); ++it) { - oss << *it << ","; - } - // Convert oss to a string; strip last comma if there is one - string s(oss.str()); - string::size_type idx; - if ((idx = s.rfind(",")) != string::npos) { - s.erase(idx); + os << "(" << itsLeft; + if (!itsRight.empty()) { + ostringstream oss; + Collection<Expr>::const_iterator it; + for (it = itsRight.begin(); it != itsRight.end(); ++it) { + oss << *it << ","; + } + string s(oss.str()); // convert oss to a string + s.erase(s.size()-1); // strip trailing comma + os << itsOperation << "(" << s << ")"; } - os << "(" << itsLeft << itsOperation << "(" << s << "))"; + os << ")"; } @@ -95,18 +95,42 @@ namespace LOFAR void LikeExprNode::print(std::ostream& os) { - // Scan the pattern expression for occurrences of "*" and "?". - // We have to check whether these characters are escaped! - std::string s; - bool isEscaped = false; -// for(string::size_type idx = 0; idx < itsRight.size(); ++idx) { -// isEscaped = (itsRight[idx] == '\\'); -// if (isEscaped) { - -// } -// } + ostringstream oss; + oss << itsRight; + string rhs(oss.str()); + string pattern; + + // Scan the pattern expression in itsRight for occurrences of wildcard + // characters and make the proper substitutions. + for(string::const_iterator it = rhs.begin(); it != rhs.end(); ++it) { + switch (*it) { + case '*': + pattern += "%"; + break; + case '?': + pattern += "_"; + break; + case '%': + pattern += "\\\\%"; + break; + case '_': + pattern += "\\\\_"; + break; + case '\\': + if (++it != rhs.end()) + if (*it == '\\') pattern += "\\\\\\\\"; + else pattern += *it; + break; + default: + pattern += *it; + break; + } + } + + os << "(" << itsLeft << itsOperation << pattern << " ESCAPE '\\\\')"; } + } // namespace Query } // namespace PL diff --git a/LCS/databases/PL/src/Query/SQLExprNode.h b/LCS/databases/PL/src/Query/SQLExprNode.h index 68aa76ce2ce..b8a08cb99ac 100644 --- a/LCS/databases/PL/src/Query/SQLExprNode.h +++ b/LCS/databases/PL/src/Query/SQLExprNode.h @@ -83,6 +83,8 @@ namespace LOFAR // expression that takes one operator (IN or NOT IN) and two // operands. It is used to check whether the first operand is present in // the second operand. + // \note When \c rhs contains no elements only the \c lhs will be + // returned when you invoke print() on InExprNode. class InExprNode : public ExprNode { public: @@ -116,7 +118,22 @@ namespace LOFAR // The user-supplied pattern can contain the wildcards "*" and "?", // where "*" means any number of characters, and "?" means exactly one // character. These wildcards will be translated to their - // SQL-equivalents "%" and "_" respectively. + // SQL-equivalents "%" and "_" respectively. + // + // We will use the backslash character (\) as escape character in the + // LIKE clause. As the backslash character needs to be escaped itself, + // we always have to provide two backslash characters as the escape + // token. + // + // Hence we get the following substitution scheme: + // \verbatim + // '*' --> '%' + // '?' --> '_' + // '\*' --> '*' + // '\?' --> '?' + // '_' --> '\\_' + // '%' --> '\\%' + // \endverbatim class LikeExprNode : public ExprNode { public: diff --git a/LCS/databases/PL/src/Query/UnaryExprNode.cc b/LCS/databases/PL/src/Query/UnaryExprNode.cc index 343f8cdee74..d68350e9f92 100644 --- a/LCS/databases/PL/src/Query/UnaryExprNode.cc +++ b/LCS/databases/PL/src/Query/UnaryExprNode.cc @@ -42,7 +42,7 @@ namespace LOFAR void UnaryExprNode::print(std::ostream& os) { - os << itsOperation << "(" << itsOperand << ")"; + os << "(" << itsOperation << itsOperand << ")"; } } // namespace Query diff --git a/LCS/databases/PL/src/Query/UnaryExprNode.h b/LCS/databases/PL/src/Query/UnaryExprNode.h index 4e91e6ea8c7..552eeccbbe2 100644 --- a/LCS/databases/PL/src/Query/UnaryExprNode.h +++ b/LCS/databases/PL/src/Query/UnaryExprNode.h @@ -48,6 +48,9 @@ namespace LOFAR UnaryExprNode(const std::string& oper, const Expr& value); +// UnaryExprNode(const std::string& oper, +// const ExprNodeSet& set); + virtual ~UnaryExprNode(); virtual void print(std::ostream& os); -- GitLab