479 lines
14 KiB
Plaintext

%name VTQL_Parser
//%left EQ LT GT LTE GTE NE IN LIKE.
sql ::= select_statement.
select_statement ::= SELECT(SEL) selectcol_list FRM(F) table_list where_condition order_clause limit_clause end_stmt. {
if(SEL){
$this->out['select'] = SEL;
}
if(F){
$this->out['from'] = F ;
}
if(SEMI){
$this->out['semi_colon'] = SEMI;
}
if($this->out['select']){
$this->buildSelectStmt($this->out);
}
}
selectcol_list ::= selectcolumn_exp COLUMNNAME(CNAME). {
$this->out['column_list'][] = CNAME;
}
selectcol_list ::= ASTERISK(A). {
$this->out['column_list'][] = A;
}
selectcol_list ::= COUNT PARENOPEN ASTERISK PARENCLOSE. {
$this->out['column_list'][] = 'count(*)';
}
selectcolumn_exp ::= selectcol_list COMMA.
selectcolumn_exp ::= .
table_list ::= TABLENAME(TNAME). {
if(!in_array($this->out["column_list"], "*") && !in_array(array_map(strtolower, $this->out["column_list"]), "count(*)")){
if(!in_array("id",$this->out["column_list"])){
$this->out["column_list"][] = "id";
}
}
$moduleName = TNAME;
if(!$moduleName){
$this->syntax_error = true;
throw new WebServiceException(WebServiceErrorCode::$QUERYSYNTAX, "There is an syntax error in query");
}
global $adb;
$handler = vtws_getModuleHandlerFromName($moduleName,$this->user);
$objectMeta = $handler->getMeta();
$this->out['moduleName'] = $moduleName;
$this->out['tableName'] = implode(',',$objectMeta->getEntityTableList());
}
where_condition ::= WHERE(Wh) condition.
where_condition ::= .
condition ::= expr_set expr(E).
expr_set ::= condition LOGICAL_AND(LAND). {
$this->out['where_condition']['operators'][] = LAND;
}
expr_set ::= condition LOGICAL_OR(LOR). {
$this->out['where_condition']['operators'][] = LOR;
}
expr_set ::= .
expr ::= COLUMNNAME(ECNAME) logical_term valuelist.{
$this->out['columnDone']=true;
$this->out['where_condition']['column_names'][] = ECNAME;
if(strcmp(ECNAME, 'id')===0){
$prev = $this->out['where_condition']['column_values'][sizeof($this->out['where_condition']['column_values'])-1];
if(is_array($prev)){
$new = array();
foreach($prev as $ind=>$val){
$val = trim($val,'\'"');
$value = vtws_getIdComponents($val);
$new[] = $value[1];
}
$this->out['where_condition']['column_values'][sizeof($this->out['where_condition']['column_values'])-1] = $new;
}else{
$prev = trim($prev,'\'"');
$value = vtws_getIdComponents($prev);
if(strcasecmp($this->out['where_condition']['column_operators'][sizeof($this->out['where_condition']['column_operators'])-1],'like')===0){
$value[1] = "'".$value[1]."'";
}
$this->out['where_condition']['column_values'][sizeof($this->out['where_condition']['column_values'])-1] = $value[1];
}
}
}
valuelist ::= PARENOPEN valueref PARENCLOSE.
valuelist ::= valueref.
valueref ::= value_exp VALUE(VAL).{
$length = sizeof($this->out['where_condition']['column_values']);
$pos = $length - 1;
if($pos < 0){
$pos = 0;
}
if(strcasecmp($this->out['where_condition']['column_operators'][$pos],"in")===0 &&
!empty($this->out['where_condition']['column_values'][$pos]) && !$this->out['columnDone']){
if(!is_array($this->out['where_condition']['column_values'][$pos])){
$prev = $this->out['where_condition']['column_values'][$pos];
$this->out['where_condition']['column_values'][$pos] = array();
$this->out['where_condition']['column_values'][$pos][] = $prev;
}
$this->out['where_condition']['column_values'][$pos][] = VAL;
}else{
$this->out['columnDone'] = false;
$this->out['where_condition']['column_values'][] = VAL;
}
}
value_exp ::= valueref COMMA.
value_exp ::= .
logical_term ::= EQ. {
$this->out['where_condition']['column_operators'][] = '=';
}
logical_term ::= LT. {
$this->out['where_condition']['column_operators'][] = '<';
}
logical_term ::= GT. {
$this->out['where_condition']['column_operators'][] = '>';
}
logical_term ::= LTE. {
$this->out['where_condition']['column_operators'][] = '<=';
}
logical_term ::= GTE. {
$this->out['where_condition']['column_operators'][] = '>=';
}
logical_term ::= NE. {
$this->out['where_condition']['column_operators'][] = '!=';
}
logical_term ::= IN. {
$this->out['where_condition']['column_operators'][] = 'IN';
}
logical_term ::= LIKE. {
$this->out['where_condition']['column_operators'][] = 'LIKE';
}
order_clause ::= ORDERBY column_group clause.
order_clause ::= .
column_group ::= column_list .
column_list ::= column_exp COLUMNNAME(CN). {
$this->out['orderby'][] = CN;
}
column_exp ::= column_list COMMA.
column_exp ::= .
clause ::= ASC. {
$this->out['sortOrder'] = 'ASC';
}
clause ::= DESC. {
$this->out['sortOrder'] = 'DESC';
}
clause ::= .
limit_clause ::= LIMIT limit_set.
limit_clause ::= .
limit_set ::= VALUE(LV). {
$this->out['limit'][] = LV;
}
limit_set ::= VALUE(LV) COMMA VALUE(LV2). {
$this->out['limit'][] = LV;
$this->out['limit'][] = LV2;
}
end_stmt ::= SEMICOLON(SEMI). {
global $adb;
if(!$this->out['meta']){
$module = $this->out['moduleName'];
$handler = vtws_getModuleHandlerFromName($module,$this->user);
$objectMeta = $handler->getMeta();
$this->out['meta'] = $objectMeta;
$meta = $this->out['meta'];
$fieldcol = $meta->getFieldColumnMapping();
$columns = array();
if(in_array($this->out['column_list'],'*')){
$columns = array_values($fieldcol);
}else if( !in_array(array_map(strcmp, $this->out['column_list']),'count(*)')){
foreach($this->out['column_list'] as $ind=>$field){
$columns[] = $fieldcol[$field];
}
}
if($this->out['where_condition']){
foreach($this->out['where_condition']['column_names'] as $ind=>$field){
$columns[] = $fieldcol[$field];
}
}
$tables = $this->getTables($this->out, $columns);
if(!in_array($objectMeta->getEntityBaseTable(), $tables)){
$tables[] = $objectMeta->getEntityBaseTable();
}
$defaultTableList = $objectMeta->getEntityDefaultTableList();
foreach($defaultTableList as $tableName){
if(!in_array($tableName,$tables)){
array_push($tables,$tableName);
}
}
$firstTable = $objectMeta->getEntityBaseTable();
$tabNameIndex = $objectMeta->getEntityTableIndexList();
$firstIndex = $tabNameIndex[$firstTable];
foreach($tables as $ind=>$table){
if($firstTable!=$table){
if(!isset($tabNameIndex[$table]) && $table == "vtiger_crmentity"){
$this->out['defaultJoinConditions'] = $this->out['defaultJoinConditions']." LEFT JOIN $table ON $firstTable.$firstIndex=$table.crmid";
}else{
$this->out['defaultJoinConditions'] = $this->out['defaultJoinConditions']." LEFT JOIN $table ON $firstTable.$firstIndex=$table.{$tabNameIndex[$table]}";
}
}else{
$this->out['tableName'] = $table;
}
}
}
}
%include_class {
/*
add this rule to add parenthesis support.
condition ::= PARENOPEN expr_set expr(E) PARENCLOSE.
sample format(for contacts) for generated sql object
Array (
[column_list] => c4,c3,c2,c1
[tableName] => vtiger_crmentity,vtiger_contactdetails,vtiger_contactaddress,vtiger_contactsubdetails,vtiger_contactscf,vtiger_customerdetails
[where_condition] => Array (
[column_operators] => Array (
[0] => =
[1] => =
[2] => =
)
[column_names] => Array (
[0] => c1
[1] => c2
[2] => c3
)
[column_values] => Array (
[0] => 'llet me'
[1] => 45
[2] => -1
)
//TO BE DONE
[grouping] => Array (
[0] => Array (
[0] => 1
[1] => 2
)
)
[operators] => Array (
[0] => and
[1] => or
)
)
[orderby] => Array (
[0] => c4
[1] => c5
)
[select] => SELECT
[from] => from
[semi_colon] => ;
)*/
private $out;
public $lex;
private $success ;
private $query ;
private $error_msg;
private $syntax_error;
private $user;
function __construct($user, $lex,$out){
if(!is_array($out)){
$out = array();
}
$this->out = &$out;
$this->lex = $lex;
$this->success = false;
$this->error_msg ='';
$this->query = '';
$this->syntax_error = false;
$this->user = $user;
}
function __toString(){
return $this->value."";
}
function buildSelectStmt($sqlDump){
$meta = $sqlDump['meta'];
$fieldcol = $meta->getFieldColumnMapping();
$columnTable = $meta->getColumnTableMapping();
$this->query = 'SELECT ';
if(in_array($sqlDump['column_list'],'*')){
$i=0;
foreach($fieldcol as $field=>$col){
if($i===0){
$this->query = $this->query.$columnTable[$col].'.'.$col;
$i++;
}else{
$this->query = $this->query.','.$columnTable[$col].'.'.$col;
}
}
}else if(in_array($sqlDump['column_list'],'count(*)')){
$this->query = $this->query." COUNT(*)";
}else{
$i=0;
foreach($sqlDump['column_list'] as $ind=>$field){
if(!$fieldcol[$field]){
throw new WebServiceException(WebServiceErrorCode::$ACCESSDENIED, "Permission to access '.$field.' attribute denied.");
}
if($i===0){
$this->query = $this->query.$columnTable[$fieldcol[$field]].'.'.$fieldcol[$field];
$i++;
}else{
$this->query = $this->query.','.$columnTable[$fieldcol[$field]].'.'.$fieldcol[$field];
}
}
}
$this->query = $this->query.' FROM '.$sqlDump['tableName'].$sqlDump['defaultJoinConditions'];
$deletedQuery = $meta->getEntityDeletedQuery();
$accessControlQuery = $meta->getEntityAccessControlQuery();
$this->query = $this->query.' '.$accessControlQuery;
if($sqlDump['where_condition']){
if((sizeof($sqlDump['where_condition']['column_names']) ==
sizeof($sqlDump['where_condition']['column_values'])) &&
(sizeof($sqlDump['where_condition']['column_operators']) == sizeof($sqlDump['where_condition']['operators'])+1)){
$this->query = $this->query.' WHERE (';
$i=0;
$referenceFields = $meta->getReferenceFieldDetails();
$ownerFields = $meta->getOwnerFields();
for(;$i<sizeof($sqlDump['where_condition']['column_values']);++$i){
if(!$fieldcol[$sqlDump['where_condition']['column_names'][$i]]){
throw new WebServiceException(WebServiceErrorCode::$ACCESSDENIED, "Permission to access ".$sqlDump['where_condition']['column_names'][$i]." attribute denied.");
}
$whereField = $sqlDump['where_condition']['column_names'][$i];
$whereOperator = $sqlDump['where_condition']['column_operators'][$i];
$whereValue = $sqlDump['where_condition']['column_values'][$i];
if(in_array($whereField,array_keys($referenceFields))){
if(is_array($whereValue)){
foreach($whereValue as $index=>$value){
if(strpos($value,'x')===false){
throw new WebServiceException(WebServiceErrorCode::$INVALIDID,"Id specified is incorrect");
}
}
$whereValue = array_map(array($this, 'getReferenceValue'),$whereValue);
}else if(strpos($whereValue,'x')!==false){
$whereValue = $this->getReferenceValue($whereValue);
if(strcasecmp($whereOperator,'like')===0){
$whereValue = "'".$whereValue."'";
}
}else{
throw new WebServiceException(WebServiceErrorCode::$INVALIDID,"Id specified is incorrect");
}
}else if(in_array($whereField,$ownerFields)){
if(is_array($whereValue)){
$groupId = array_map(array($this, 'getOwner'),$whereValue);
}else{
$groupId = $this->getOwner($whereValue);
if(strcasecmp($whereOperator,'like')===0){
$groupId = "'$groupId'";
}
}
$whereValue = $groupId;
}
if(is_array($whereValue)){
$whereValue = "(".implode(',',$whereValue).")";
}elseif(strcasecmp($whereOperator, 'in') === 0){
$whereValue = "($whereValue)";
}
$this->query = $this->query.$columnTable[$fieldcol[$whereField]].'.'.
$fieldcol[$whereField]." ".$whereOperator." ".$whereValue;
if($i <sizeof($sqlDump['where_condition']['column_values'])-1){
$this->query = $this->query.' ';
$this->query = $this->query.$sqlDump['where_condition']['operators'][$i].' ';
}
}
}else{
throw new WebServiceException(WebServiceErrorCode::$QUERYSYNTAX, "columns data inappropriate");
}
$this->query = $this->query.")";
$nextToken = ' AND ';
}else{
if(!empty($deletedQuery)){
$nextToken = " WHERE ";
}
}
if(strcasecmp('calendar',$this->out['moduleName'])===0){
$this->query = $this->query." $nextToken activitytype='Task' AND ";
}elseif(strcasecmp('events',$this->out['moduleName'])===0){
$this->query = $this->query."$nextToken activitytype!='Emails' AND activitytype!='Task' AND ";
}else if(strcasecmp('emails',$this->out['moduleName'])===0){
$this->query = $this->query."$nextToken activitytype='Emails' AND ";
}elseif(!empty($deletedQuery)){
$this->query = $this->query.$nextToken;
}
$this->query = $this->query.' '.$deletedQuery;
if($sqlDump['orderby']){
$i=0;
$this->query = $this->query.' ORDER BY ';
foreach($sqlDump['orderby'] as $ind=>$field){
if($i===0){
$this->query = $this->query.$columnTable[$fieldcol[$field]].".".$fieldcol[$field];
$i++;
}else{
$this->query = $this->query.','.$columnTable[$fieldcol[$field]].".".$fieldcol[$field];
}
}
if($sqlDump['sortOrder']) {
$this->query .= ' '.$sqlDump['sortOrder'];
}
}
if($sqlDump['limit']){
$i=0;
$offset =false;
if(sizeof($sqlDump['limit'])>1){
$offset = true;
}
$this->query = $this->query.' LIMIT ';
foreach($sqlDump['limit'] as $ind=>$field){
if(!$offset){
$field = ($field>100)? 100: $field;
}
if($i===0){
$this->query = $this->query.$field;
$i++;
$offset = false;
}else{
$this->query = $this->query.','.$field;
}
}
}else{
$this->query = $this->query.' LIMIT 100';
}
$this->query = $this->query.';';
}
function getTables($sqlDump,$columns){
$meta = $sqlDump['meta'];
$coltable = $meta->getColumnTableMapping();
$tables = array();
foreach($columns as $ind=>$col){
$tables[$coltable[$col]] = $coltable[$col];
}
$tables = array_keys($tables);
return ($tables);
}
function getReferenceValue($whereValue){
$whereValue = trim($whereValue,'\'"');
$whereValue = vtws_getIdComponents($whereValue);
$whereValue = $whereValue[1];
return $whereValue;
}
function getOwner($whereValue){
$whereValue = trim($whereValue,'\'"');
$whereValue = vtws_getIdComponents($whereValue);
$whereValue = $whereValue[1];
return $whereValue;
}
function isSuccess(){
return $this->success;
}
function getErrorMsg(){
return $this->error_msg;
}
function getQuery(){
return $this->query;
}
function getObjectMetaData(){
return $this->out['meta'];
}
}
//%token_prefix VTQL_
%declare_class {class VTQL_Parser}
%parse_accept {
$this->success = true;
}
%parse_failure {
if(!$this->syntax_error){
throw new WebServiceException(WebServiceErrorCode::$QUERYSYNTAX, "Parsing failed");
}
}
%stack_overflow {
throw new WebServiceException(WebServiceErrorCode::$QUERYSYNTAX, "Parser stack overflow");
}
%syntax_error {
$synMsg = "Syntax Error on line " . $this->lex->linenum . ": token '" .$this->lex->value."' ";
$expect = array();
foreach ($this->yy_get_expected_tokens($yymajor) as $token) {
$expect[] = self::$yyTokenName[$token];
}
$synMsg =$synMsg.('Unexpected ' . $this->tokenName($yymajor) . '(' . $TOKEN
. '), expected one of: ' . implode(',', $expect));
throw new WebServiceException(WebServiceErrorCode::$QUERYSYNTAX, $synMsg);
}