#383: support negative numbers in OQL

Enhancements: support MySQL bitwise operators (&, |, ^, <<, >>) and hexadecimal numbers (up to 64-bit).

SVN:trunk[2709]
This commit is contained in:
Denis Flaven
2013-04-29 08:51:01 +00:00
parent 29060f7b5e
commit 6feb62d728
7 changed files with 691 additions and 517 deletions

View File

@@ -383,7 +383,7 @@ class ScalarExpression extends UnaryExpression
{
public function __construct($value)
{
if (!is_scalar($value) && !is_null($value))
if (!is_scalar($value) && !is_null($value) && (!$value instanceof OqlHexValue))
{
throw new CoreException('Attempt to create a scalar expression from a non scalar', array('var_type'=>gettype($value)));
}

View File

@@ -17,6 +17,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* OQL syntax analyzer, to be used prior to run the lexical analyzer
*
@@ -119,6 +120,11 @@ class OQLLexerRaw
'/\G-/ ',
'/\GAND/ ',
'/\GOR/ ',
'/\G\\|/ ',
'/\G&/ ',
'/\G\\^/ ',
'/\G<</ ',
'/\G>>/ ',
'/\G,/ ',
'/\G\\(/ ',
'/\G\\)/ ',
@@ -168,7 +174,8 @@ class OQLLexerRaw
'/\GABOVE STRICT/ ',
'/\GNOT ABOVE/ ',
'/\GNOT ABOVE STRICT/ ',
'/\G(0x[0-9a-fA-F]+|[0-9]+)/ ',
'/\G(0x[0-9a-fA-F]+)/ ',
'/\G([0-9]+)/ ',
'/\G\"([^\\\\\"]|\\\\\"|\\\\\\\\)*\"|'.chr(94).chr(39).'([^\\\\'.chr(39).']|\\\\'.chr(39).'|\\\\\\\\)*'.chr(39).'/ ',
'/\G([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/ ',
'/\G:([_a-zA-Z][_a-zA-Z0-9]*->[_a-zA-Z][_a-zA-Z0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)/ ',
@@ -333,269 +340,299 @@ class OQLLexerRaw
function yy_r1_13($yy_subpatterns)
{
$this->token = OQLParser::COMA;
$this->token = OQLParser::BITWISE_OR;
}
function yy_r1_14($yy_subpatterns)
{
$this->token = OQLParser::PAR_OPEN;
$this->token = OQLParser::BITWISE_AND;
}
function yy_r1_15($yy_subpatterns)
{
$this->token = OQLParser::PAR_CLOSE;
$this->token = OQLParser::BITWISE_XOR;
}
function yy_r1_16($yy_subpatterns)
{
$this->token = OQLParser::REGEXP;
$this->token = OQLParser::BITWISE_LEFT_SHIFT;
}
function yy_r1_17($yy_subpatterns)
{
$this->token = OQLParser::EQ;
$this->token = OQLParser::BITWISE_RIGHT_SHIFT;
}
function yy_r1_18($yy_subpatterns)
{
$this->token = OQLParser::NOT_EQ;
$this->token = OQLParser::COMA;
}
function yy_r1_19($yy_subpatterns)
{
$this->token = OQLParser::GT;
$this->token = OQLParser::PAR_OPEN;
}
function yy_r1_20($yy_subpatterns)
{
$this->token = OQLParser::LT;
$this->token = OQLParser::PAR_CLOSE;
}
function yy_r1_21($yy_subpatterns)
{
$this->token = OQLParser::GE;
$this->token = OQLParser::REGEXP;
}
function yy_r1_22($yy_subpatterns)
{
$this->token = OQLParser::LE;
$this->token = OQLParser::EQ;
}
function yy_r1_23($yy_subpatterns)
{
$this->token = OQLParser::LIKE;
$this->token = OQLParser::NOT_EQ;
}
function yy_r1_24($yy_subpatterns)
{
$this->token = OQLParser::NOT_LIKE;
$this->token = OQLParser::GT;
}
function yy_r1_25($yy_subpatterns)
{
$this->token = OQLParser::IN;
$this->token = OQLParser::LT;
}
function yy_r1_26($yy_subpatterns)
{
$this->token = OQLParser::NOT_IN;
$this->token = OQLParser::GE;
}
function yy_r1_27($yy_subpatterns)
{
$this->token = OQLParser::INTERVAL;
$this->token = OQLParser::LE;
}
function yy_r1_28($yy_subpatterns)
{
$this->token = OQLParser::F_IF;
$this->token = OQLParser::LIKE;
}
function yy_r1_29($yy_subpatterns)
{
$this->token = OQLParser::F_ELT;
$this->token = OQLParser::NOT_LIKE;
}
function yy_r1_30($yy_subpatterns)
{
$this->token = OQLParser::F_COALESCE;
$this->token = OQLParser::IN;
}
function yy_r1_31($yy_subpatterns)
{
$this->token = OQLParser::F_ISNULL;
$this->token = OQLParser::NOT_IN;
}
function yy_r1_32($yy_subpatterns)
{
$this->token = OQLParser::F_CONCAT;
$this->token = OQLParser::INTERVAL;
}
function yy_r1_33($yy_subpatterns)
{
$this->token = OQLParser::F_SUBSTR;
$this->token = OQLParser::F_IF;
}
function yy_r1_34($yy_subpatterns)
{
$this->token = OQLParser::F_TRIM;
$this->token = OQLParser::F_ELT;
}
function yy_r1_35($yy_subpatterns)
{
$this->token = OQLParser::F_DATE;
$this->token = OQLParser::F_COALESCE;
}
function yy_r1_36($yy_subpatterns)
{
$this->token = OQLParser::F_DATE_FORMAT;
$this->token = OQLParser::F_ISNULL;
}
function yy_r1_37($yy_subpatterns)
{
$this->token = OQLParser::F_CURRENT_DATE;
$this->token = OQLParser::F_CONCAT;
}
function yy_r1_38($yy_subpatterns)
{
$this->token = OQLParser::F_NOW;
$this->token = OQLParser::F_SUBSTR;
}
function yy_r1_39($yy_subpatterns)
{
$this->token = OQLParser::F_TIME;
$this->token = OQLParser::F_TRIM;
}
function yy_r1_40($yy_subpatterns)
{
$this->token = OQLParser::F_TO_DAYS;
$this->token = OQLParser::F_DATE;
}
function yy_r1_41($yy_subpatterns)
{
$this->token = OQLParser::F_FROM_DAYS;
$this->token = OQLParser::F_DATE_FORMAT;
}
function yy_r1_42($yy_subpatterns)
{
$this->token = OQLParser::F_YEAR;
$this->token = OQLParser::F_CURRENT_DATE;
}
function yy_r1_43($yy_subpatterns)
{
$this->token = OQLParser::F_MONTH;
$this->token = OQLParser::F_NOW;
}
function yy_r1_44($yy_subpatterns)
{
$this->token = OQLParser::F_DAY;
$this->token = OQLParser::F_TIME;
}
function yy_r1_45($yy_subpatterns)
{
$this->token = OQLParser::F_HOUR;
$this->token = OQLParser::F_TO_DAYS;
}
function yy_r1_46($yy_subpatterns)
{
$this->token = OQLParser::F_MINUTE;
$this->token = OQLParser::F_FROM_DAYS;
}
function yy_r1_47($yy_subpatterns)
{
$this->token = OQLParser::F_SECOND;
$this->token = OQLParser::F_YEAR;
}
function yy_r1_48($yy_subpatterns)
{
$this->token = OQLParser::F_DATE_ADD;
$this->token = OQLParser::F_MONTH;
}
function yy_r1_49($yy_subpatterns)
{
$this->token = OQLParser::F_DATE_SUB;
$this->token = OQLParser::F_DAY;
}
function yy_r1_50($yy_subpatterns)
{
$this->token = OQLParser::F_ROUND;
$this->token = OQLParser::F_HOUR;
}
function yy_r1_51($yy_subpatterns)
{
$this->token = OQLParser::F_FLOOR;
$this->token = OQLParser::F_MINUTE;
}
function yy_r1_52($yy_subpatterns)
{
$this->token = OQLParser::F_INET_ATON;
$this->token = OQLParser::F_SECOND;
}
function yy_r1_53($yy_subpatterns)
{
$this->token = OQLParser::F_INET_NTOA;
$this->token = OQLParser::F_DATE_ADD;
}
function yy_r1_54($yy_subpatterns)
{
$this->token = OQLParser::BELOW;
$this->token = OQLParser::F_DATE_SUB;
}
function yy_r1_55($yy_subpatterns)
{
$this->token = OQLParser::BELOW_STRICT;
$this->token = OQLParser::F_ROUND;
}
function yy_r1_56($yy_subpatterns)
{
$this->token = OQLParser::NOT_BELOW;
$this->token = OQLParser::F_FLOOR;
}
function yy_r1_57($yy_subpatterns)
{
$this->token = OQLParser::NOT_BELOW_STRICT;
$this->token = OQLParser::F_INET_ATON;
}
function yy_r1_58($yy_subpatterns)
{
$this->token = OQLParser::ABOVE;
$this->token = OQLParser::F_INET_NTOA;
}
function yy_r1_59($yy_subpatterns)
{
$this->token = OQLParser::ABOVE_STRICT;
$this->token = OQLParser::BELOW;
}
function yy_r1_60($yy_subpatterns)
{
$this->token = OQLParser::NOT_ABOVE;
$this->token = OQLParser::BELOW_STRICT;
}
function yy_r1_61($yy_subpatterns)
{
$this->token = OQLParser::NOT_ABOVE_STRICT;
$this->token = OQLParser::NOT_BELOW;
}
function yy_r1_62($yy_subpatterns)
{
$this->token = OQLParser::NUMVAL;
$this->token = OQLParser::NOT_BELOW_STRICT;
}
function yy_r1_63($yy_subpatterns)
{
$this->token = OQLParser::STRVAL;
$this->token = OQLParser::ABOVE;
}
function yy_r1_64($yy_subpatterns)
{
$this->token = OQLParser::NAME;
$this->token = OQLParser::ABOVE_STRICT;
}
function yy_r1_65($yy_subpatterns)
{
$this->token = OQLParser::VARNAME;
$this->token = OQLParser::NOT_ABOVE;
}
function yy_r1_66($yy_subpatterns)
{
$this->token = OQLParser::NOT_ABOVE_STRICT;
}
function yy_r1_67($yy_subpatterns)
{
$this->token = OQLParser::HEXVAL;
}
function yy_r1_68($yy_subpatterns)
{
$this->token = OQLParser::NUMVAL;
}
function yy_r1_69($yy_subpatterns)
{
$this->token = OQLParser::STRVAL;
}
function yy_r1_70($yy_subpatterns)
{
$this->token = OQLParser::NAME;
}
function yy_r1_71($yy_subpatterns)
{
$this->token = OQLParser::VARNAME;
}
function yy_r1_72($yy_subpatterns)
{
$this->token = OQLParser::DOT;

View File

@@ -95,6 +95,11 @@ math_plus = "+"
math_minus = "-"
log_and = "AND"
log_or = "OR"
bitwise_and = "&"
bitwise_or = "|"
bitwise_xor = "^"
bitwise_leftshift = "<<"
bitwise_rightshift = ">>"
regexp = "REGEXP"
eq = "="
not_eq = "!="
@@ -156,8 +161,11 @@ not_above_strict = "NOT ABOVE STRICT"
//
// numval = /([0-9]+|0x[0-9a-fA-F]+)/
// Does not work either, the hexadecimal numbers are not matched properly
// The following seems to work...
numval = /(0x[0-9a-fA-F]+|[0-9]+)/
// Anyhow let's distinguish the hexadecimal values from decimal integers, hex numbers will be stored as strings
// and passed as-is to MySQL which enables us to pass 64-bit values without messing with them in PHP
//
hexval = /(0x[0-9a-fA-F]+)/
numval = /([0-9]+)/
strval = /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/
name = /([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/
varname = /:([_a-zA-Z][_a-zA-Z0-9]*->[_a-zA-Z][_a-zA-Z0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)/
@@ -204,6 +212,21 @@ log_and {
log_or {
$this->token = OQLParser::LOG_OR;
}
bitwise_or {
$this->token = OQLParser::BITWISE_OR;
}
bitwise_and {
$this->token = OQLParser::BITWISE_AND;
}
bitwise_xor {
$this->token = OQLParser::BITWISE_XOR;
}
bitwise_leftshift {
$this->token = OQLParser::BITWISE_LEFT_SHIFT;
}
bitwise_rightshift {
$this->token = OQLParser::BITWISE_RIGHT_SHIFT;
}
coma {
$this->token = OQLParser::COMA;
}
@@ -351,6 +374,9 @@ not_above {
not_above_strict {
$this->token = OQLParser::NOT_ABOVE_STRICT;
}
hexval {
$this->token = OQLParser::HEXVAL;
}
numval {
$this->token = OQLParser::NUMVAL;
}

File diff suppressed because it is too large Load Diff

View File

@@ -164,19 +164,23 @@ name(A) ::= NAME(X). {
}
A = new OqlName($name, $this->m_iColPrev);
}
num_value(A) ::= NUMVAL(X). {A=X;}
num_value(A) ::= NUMVAL(X). {A=(int)X;}
num_value(A) ::= MATH_MINUS NUMVAL(X). {A=(int)-X;}
num_value(A) ::= HEXVAL(X). {A=new OqlHexValue(X);}
str_value(A) ::= STRVAL(X). {A=stripslashes(substr(X, 1, strlen(X) - 2));}
operator1(A) ::= num_operator1(X). {A=X;}
operator1(A) ::= bitwise_operator1(X). {A=X;}
operator2(A) ::= num_operator2(X). {A=X;}
operator2(A) ::= str_operator(X). {A=X;}
operator2(A) ::= REGEXP(X). {A=X;}
operator2(A) ::= EQ(X). {A=X;}
operator2(A) ::= NOT_EQ(X). {A=X;}
operator3(A) ::= LOG_AND(X). {A=X;}
operator3(A) ::= bitwise_operator3(X). {A=X;}
operator4(A) ::= LOG_OR(X). {A=X;}
operator4(A) ::= bitwise_operator4(X). {A=X;}
num_operator1(A) ::= MATH_DIV(X). {A=X;}
num_operator1(A) ::= MATH_MULT(X). {A=X;}
@@ -190,6 +194,12 @@ num_operator2(A) ::= LE(X). {A=X;}
str_operator(A) ::= LIKE(X). {A=X;}
str_operator(A) ::= NOT_LIKE(X). {A=X;}
bitwise_operator1(A) ::= BITWISE_LEFT_SHIFT(X). {A=X;}
bitwise_operator1(A) ::= BITWISE_RIGHT_SHIFT(X). {A=X;}
bitwise_operator3(A) ::= BITWISE_AND(X). {A=X;}
bitwise_operator4(A) ::= BITWISE_OR(X). {A=X;}
bitwise_operator4(A) ::= BITWISE_XOR(X). {A=X;}
list_operator(A) ::= IN(X). {A=X;}
list_operator(A) ::= NOT_IN(X). {A=X;}

View File

@@ -54,6 +54,27 @@ class OqlName
}
}
/**
*
* Store hexadecimal values as strings so that we can support 64-bit values
*
*/
class OqlHexValue
{
protected $m_sValue;
public function __construct($sValue)
{
$this->m_sValue = $sValue;
}
public function __toString()
{
return $this->m_sValue;
}
}
class OqlJoinSpec
{
protected $m_oClass;

View File

@@ -140,6 +140,12 @@ class TestOQLParser extends TestFunction
$aQueries = array(
'SELECT toto' => true,
'SELECT toto WHERE toto.a = 1' => true,
'SELECT toto WHERE toto.a = -1' => true,
'SELECT toto WHERE toto.a = (1-1)' => true,
'SELECT toto WHERE toto.a = (-1+3)' => true,
'SELECT toto WHERE toto.a = (3+-1)' => true,
'SELECT toto WHERE toto.a = (3--1)' => true,
'SELECT toto WHERE toto.a = (3++1)' => false,
'SELECT toto WHERE toto.a = 0xC' => true,
'SELECT toto WHERE toto.a = \'AXDVFS0xCZ32\'' => true,
'SELECT toto WHERE toto.a = :myparameter' => true,
@@ -150,7 +156,12 @@ class TestOQLParser extends TestFunction
'SELECT toto WHHHERE toto.a = "1"' => false,
'SELECT toto WHERE toto.a == "1"' => false,
'SELECT toto WHERE toto.a % 1' => false,
//'SELECT toto WHERE toto.a LIKE 1' => false,
'SELECT toto WHERE toto.a & 1' => true, // bitwise and
'SELECT toto WHERE toto.a | 1' => true, // bitwise or
'SELECT toto WHERE toto.a ^ 1' => true, // bitwise xor
'SELECT toto WHERE toto.a << 1' => true, // bitwise left shift
'SELECT toto WHERE toto.a >> 1' => true, // bitwise right shift
//'SELECT toto WHERE toto.a LIKE 1' => false,
'SELECT toto WHERE toto.a like \'arg\'' => false,
'SELECT toto WHERE toto.a NOT LIKE "That\'s it"' => true,
'SELECT toto WHERE toto.a NOT LIKE "That\'s "it""' => false,
@@ -184,7 +195,8 @@ class TestOQLParser extends TestFunction
"SELECT A JOIN B ON A.myB = B.id WHERE A.col1 + B.col2 * B.col1 = A.col2" => true,
"SELECT A JOIN B ON A.myB = B.id WHERE A.col1 + (B.col2 * B.col1) = A.col2" => true,
"SELECT A JOIN B ON A.myB = B.id WHERE (A.col1 + B.col2) * B.col1 = A.col2" => true,
"SELECT A JOIN B ON A.myB = B.id WHERE (A.col1 & B.col2) = A.col2" => true,
'SELECT Device AS D_ JOIN Site AS S_ ON D_.site = S_.id WHERE S_.country = "Francia"' => true,
// Several objects in a row...