danielebarchiesi@4: danielebarchiesi@4: * @package ARC2 danielebarchiesi@4: * @version 2010-11-16 danielebarchiesi@4: * danielebarchiesi@4: */ danielebarchiesi@4: danielebarchiesi@4: ARC2::inc('StoreQueryHandler'); danielebarchiesi@4: danielebarchiesi@4: class ARC2_StoreSelectQueryHandler extends ARC2_StoreQueryHandler { danielebarchiesi@4: danielebarchiesi@4: function __construct($a, &$caller) {/* caller has to be a store */ danielebarchiesi@4: parent::__construct($a, $caller); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function __init() {/* db_con */ danielebarchiesi@4: parent::__init(); danielebarchiesi@4: $this->store = $this->caller; danielebarchiesi@4: $con = $this->store->getDBCon(); danielebarchiesi@4: $this->handler_type = 'select'; danielebarchiesi@4: $this->engine_type = $this->v('store_engine_type', 'MyISAM', $this->a); danielebarchiesi@4: $this->cache_results = $this->v('store_cache_results', 0, $this->a); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function runQuery($infos) { danielebarchiesi@4: $con = $this->store->getDBCon(); danielebarchiesi@4: $rf = $this->v('result_format', '', $infos); danielebarchiesi@4: $this->infos = $infos; danielebarchiesi@4: $this->infos['null_vars'] = array(); danielebarchiesi@4: $this->indexes = array(); danielebarchiesi@4: $this->pattern_order_offset = 0; danielebarchiesi@4: $q_sql = $this->getSQL(); danielebarchiesi@4: danielebarchiesi@4: /* debug result formats */ danielebarchiesi@4: if ($rf == 'sql') return $q_sql; danielebarchiesi@4: if ($rf == 'structure') return $this->infos; danielebarchiesi@4: if ($rf == 'index') return $this->indexes; danielebarchiesi@4: /* create intermediate results (ID-based) */ danielebarchiesi@4: $tmp_tbl = $this->createTempTable($q_sql); danielebarchiesi@4: /* join values */ danielebarchiesi@4: $r = $this->getFinalQueryResult($q_sql, $tmp_tbl); danielebarchiesi@4: /* remove intermediate results */ danielebarchiesi@4: if (!$this->cache_results) { danielebarchiesi@4: $this->queryDB('DROP TABLE IF EXISTS ' . $tmp_tbl, $con); danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getSQL() { danielebarchiesi@4: $r = ''; danielebarchiesi@4: $nl = "\n"; danielebarchiesi@4: $this->buildInitialIndexes(); danielebarchiesi@4: foreach ($this->indexes as $i => $index) { danielebarchiesi@4: $this->index = array_merge($this->getEmptyIndex(), $index); danielebarchiesi@4: $this->analyzeIndex($this->getPattern('0')); danielebarchiesi@4: $sub_r = $this->getQuerySQL(); danielebarchiesi@4: $r .= $r ? $nl . 'UNION' . $this->getDistinctSQL() . $nl : ''; danielebarchiesi@4: $r .= $this->is_union_query ? '(' . $sub_r . ')' : $sub_r; danielebarchiesi@4: $this->indexes[$i] = $this->index; danielebarchiesi@4: } danielebarchiesi@4: $r .= $this->is_union_query ? $this->getLIMITSQL() : ''; danielebarchiesi@4: if ($this->v('order_infos', 0, $this->infos['query'])) { danielebarchiesi@4: $r = preg_replace('/SELECT(\s+DISTINCT)?\s*/', 'SELECT\\1 NULL AS `_pos_`, ', $r); danielebarchiesi@4: } danielebarchiesi@4: $pd_count = $this->problematicDependencies(); danielebarchiesi@4: if ($pd_count) { danielebarchiesi@4: /* re-arranging the patterns sometimes reduces the LEFT JOIN dependencies */ danielebarchiesi@4: $set_sql = 0; danielebarchiesi@4: if (!$this->pattern_order_offset) $set_sql = 1; danielebarchiesi@4: if (!$set_sql && ($pd_count < $this->opt_sql_pd_count)) $set_sql = 1; danielebarchiesi@4: if (!$set_sql && ($pd_count == $this->opt_sql_pd_count) && (strlen($r) < strlen($this->opt_sql))) $set_sql = 1; danielebarchiesi@4: if ($set_sql) { danielebarchiesi@4: $this->opt_sql = $r; danielebarchiesi@4: $this->opt_sql_pd_count = $pd_count; danielebarchiesi@4: } danielebarchiesi@4: $this->pattern_order_offset++; danielebarchiesi@4: if ($this->pattern_order_offset > 5) { danielebarchiesi@4: return $this->opt_sql; danielebarchiesi@4: } danielebarchiesi@4: return $this->getSQL(); danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function buildInitialIndexes() { danielebarchiesi@4: $this->dependency_log = array(); danielebarchiesi@4: $this->index = $this->getEmptyIndex(); danielebarchiesi@4: $this->buildIndex($this->infos['query']['pattern'], 0); danielebarchiesi@4: $tmp = $this->index; danielebarchiesi@4: $this->analyzeIndex($this->getPattern('0')); danielebarchiesi@4: $this->initial_index = $this->index; danielebarchiesi@4: $this->index = $tmp; danielebarchiesi@4: $this->is_union_query = $this->index['union_branches'] ? 1 : 0; danielebarchiesi@4: $this->indexes = $this->is_union_query ? $this->getUnionIndexes($this->index) : array($this->index); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function createTempTable($q_sql) { danielebarchiesi@4: $con = $this->store->getDBCon(); danielebarchiesi@4: $v = $this->store->getDBVersion(); danielebarchiesi@4: if ($this->cache_results) { danielebarchiesi@4: $tbl = $this->store->getTablePrefix() . 'Q' . md5($q_sql); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $tbl = $this->store->getTablePrefix() . 'Q' . md5($q_sql . time() . uniqid(rand())); danielebarchiesi@4: } danielebarchiesi@4: if (strlen($tbl) > 64) $tbl = 'Q' . md5($tbl); danielebarchiesi@4: $tmp_sql = 'CREATE TEMPORARY TABLE ' . $tbl . ' ( ' . $this->getTempTableDef($tbl, $q_sql) . ') '; danielebarchiesi@4: $tmp_sql .= (($v < '04-01-00') && ($v >= '04-00-18')) ? 'ENGINE' : (($v >= '04-01-02') ? 'ENGINE' : 'TYPE'); danielebarchiesi@4: $tmp_sql .= '=' . $this->engine_type;/* HEAP doesn't support AUTO_INCREMENT, and MySQL breaks on MEMORY sometimes */ danielebarchiesi@4: if (!$this->queryDB($tmp_sql, $con) && !$this->queryDB(str_replace('CREATE TEMPORARY', 'CREATE', $tmp_sql), $con)) { danielebarchiesi@4: return $this->addError(mysql_error($con)); danielebarchiesi@4: } danielebarchiesi@4: mysql_unbuffered_query('INSERT INTO ' . $tbl . ' ' . "\n" . $q_sql, $con); danielebarchiesi@4: if ($er = mysql_error($con)) $this->addError($er); danielebarchiesi@4: return $tbl; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getEmptyIndex() { danielebarchiesi@4: return array( danielebarchiesi@4: 'from' => array(), danielebarchiesi@4: 'join' => array(), danielebarchiesi@4: 'left_join' => array(), danielebarchiesi@4: 'vars' => array(), 'graph_vars' => array(), 'graph_uris' => array(), danielebarchiesi@4: 'bnodes' => array(), danielebarchiesi@4: 'triple_patterns' => array(), danielebarchiesi@4: 'sub_joins' => array(), danielebarchiesi@4: 'constraints' => array(), danielebarchiesi@4: 'union_branches'=> array(), danielebarchiesi@4: 'patterns' => array(), danielebarchiesi@4: 'havings' => array() danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getTempTableDef($tmp_tbl, $q_sql) { danielebarchiesi@4: $col_part = preg_replace('/^SELECT\s*(DISTINCT)?(.*)FROM.*$/s', '\\2', $q_sql); danielebarchiesi@4: $parts = explode(',', $col_part); danielebarchiesi@4: $has_order_infos = $this->v('order_infos', 0, $this->infos['query']); danielebarchiesi@4: $r = ''; danielebarchiesi@4: $added = array(); danielebarchiesi@4: foreach ($parts as $part) { danielebarchiesi@4: if (preg_match('/\.?(.+)\s+AS\s+`(.+)`/U', trim($part), $m) && !isset($added[$m[2]])) { danielebarchiesi@4: $col = $m[1]; danielebarchiesi@4: $alias = $m[2]; danielebarchiesi@4: if ($alias == '_pos_') continue; danielebarchiesi@4: $r .= $r ? ',' : ''; danielebarchiesi@4: $r .= "\n `" . $alias . "` int UNSIGNED"; danielebarchiesi@4: $added[$alias] = 1; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if ($has_order_infos) { danielebarchiesi@4: $r = "\n" . '`_pos_` mediumint NOT NULL AUTO_INCREMENT PRIMARY KEY, ' . $r; danielebarchiesi@4: } danielebarchiesi@4: return $r ? $r . "\n" : ''; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getFinalQueryResult($q_sql, $tmp_tbl) { danielebarchiesi@4: /* var names */ danielebarchiesi@4: $vars = array(); danielebarchiesi@4: $aggregate_vars = array(); danielebarchiesi@4: foreach ($this->infos['query']['result_vars'] as $entry) { danielebarchiesi@4: if ($entry['aggregate']) { danielebarchiesi@4: $vars[] = $entry['alias']; danielebarchiesi@4: $aggregate_vars[] = $entry['alias']; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $vars[] = $entry['var']; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: /* result */ danielebarchiesi@4: $r = array('variables' => $vars); danielebarchiesi@4: $v_sql = $this->getValueSQL($tmp_tbl, $q_sql); danielebarchiesi@4: //echo "\n\n" . $v_sql; danielebarchiesi@4: $t1 = ARC2::mtime(); danielebarchiesi@4: $con = $this->store->getDBCon(); danielebarchiesi@4: $rs = mysql_unbuffered_query($v_sql, $con); danielebarchiesi@4: if ($er = mysql_error($con)) { danielebarchiesi@4: $this->addError($er); danielebarchiesi@4: } danielebarchiesi@4: $t2 = ARC2::mtime(); danielebarchiesi@4: $rows = array(); danielebarchiesi@4: $types = array(0 => 'uri', 1 => 'bnode', 2 => 'literal'); danielebarchiesi@4: if ($rs) { danielebarchiesi@4: while ($pre_row = mysql_fetch_array($rs)) { danielebarchiesi@4: $row = array(); danielebarchiesi@4: foreach ($vars as $var) { danielebarchiesi@4: if (isset($pre_row[$var])) { danielebarchiesi@4: $row[$var] = $pre_row[$var]; danielebarchiesi@4: $row[$var . ' type'] = isset($pre_row[$var . ' type']) ? $types[$pre_row[$var . ' type']] : (in_array($var, $aggregate_vars) ? 'literal' : 'uri'); danielebarchiesi@4: if (isset($pre_row[$var . ' lang_dt']) && ($lang_dt = $pre_row[$var . ' lang_dt'])) { danielebarchiesi@4: if (preg_match('/^([a-z]+(\-[a-z0-9]+)*)$/i', $lang_dt)) { danielebarchiesi@4: $row[$var . ' lang'] = $lang_dt; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $row[$var . ' datatype'] = $lang_dt; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if ($row || !$vars) { danielebarchiesi@4: $rows[] = $row; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: $r['rows'] = $rows; danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function buildIndex($pattern, $id) { danielebarchiesi@4: $pattern['id'] = $id; danielebarchiesi@4: $type = $this->v('type', '', $pattern); danielebarchiesi@4: if (($type == 'filter') && $this->v('constraint', 0, $pattern)) { danielebarchiesi@4: $sub_pattern = $pattern['constraint']; danielebarchiesi@4: $sub_pattern['parent_id'] = $id; danielebarchiesi@4: $sub_id = $id . '_0'; danielebarchiesi@4: $this->buildIndex($sub_pattern, $sub_id); danielebarchiesi@4: $pattern['constraint'] = $sub_id; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $sub_patterns = $this->v('patterns', array(), $pattern); danielebarchiesi@4: $keys = array_keys($sub_patterns); danielebarchiesi@4: $spc = count($sub_patterns); danielebarchiesi@4: if (($spc > 4) && $this->pattern_order_offset) { danielebarchiesi@4: $keys = array(); danielebarchiesi@4: for ($i = 0 ; $i < $spc; $i++) { danielebarchiesi@4: $keys[$i] = $i + $this->pattern_order_offset; danielebarchiesi@4: while ($keys[$i] >= $spc) $keys[$i] -= $spc; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: foreach ($keys as $i => $key) { danielebarchiesi@4: $sub_pattern = $sub_patterns[$key]; danielebarchiesi@4: $sub_pattern['parent_id'] = $id; danielebarchiesi@4: $sub_id = $id . '_' . $key; danielebarchiesi@4: $this->buildIndex($sub_pattern, $sub_id); danielebarchiesi@4: $pattern['patterns'][$i] = $sub_id; danielebarchiesi@4: if ($type == 'union') { danielebarchiesi@4: $this->index['union_branches'][] = $sub_id; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: $this->index['patterns'][$id] = $pattern; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function analyzeIndex($pattern) { danielebarchiesi@4: $type = $this->v('type', '', $pattern); danielebarchiesi@4: if (!$type) { danielebarchiesi@4: //echo ''; danielebarchiesi@4: return false; danielebarchiesi@4: } danielebarchiesi@4: $type = $pattern['type']; danielebarchiesi@4: $id = $pattern['id']; danielebarchiesi@4: /* triple */ danielebarchiesi@4: if ($type == 'triple') { danielebarchiesi@4: foreach (array('s', 'p', 'o') as $term) { danielebarchiesi@4: if ($pattern[$term . '_type'] == 'var') { danielebarchiesi@4: $val = $pattern[$term]; danielebarchiesi@4: $this->index['vars'][$val] = array_merge($this->v($val, array(), $this->index['vars']), array(array('table' => $pattern['id'], 'col' =>$term))); danielebarchiesi@4: } danielebarchiesi@4: if ($pattern[$term . '_type'] == 'bnode') { danielebarchiesi@4: $val = $pattern[$term]; danielebarchiesi@4: $this->index['bnodes'][$val] = array_merge($this->v($val, array(), $this->index['bnodes']), array(array('table' => $pattern['id'], 'col' =>$term))); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: $this->index['triple_patterns'][] = $pattern['id']; danielebarchiesi@4: /* joins */ danielebarchiesi@4: if ($this->isOptionalPattern($id)) { danielebarchiesi@4: $this->index['left_join'][] = $id; danielebarchiesi@4: } danielebarchiesi@4: elseif (!$this->index['from']) { danielebarchiesi@4: $this->index['from'][] = $id; danielebarchiesi@4: } danielebarchiesi@4: elseif (!$this->getJoinInfos($id)) { danielebarchiesi@4: $this->index['from'][] = $id; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $this->index['join'][] = $id; danielebarchiesi@4: } danielebarchiesi@4: /* graph infos, graph vars */ danielebarchiesi@4: $this->index['patterns'][$id]['graph_infos'] = $this->getGraphInfos($id); danielebarchiesi@4: foreach ($this->index['patterns'][$id]['graph_infos'] as $info) { danielebarchiesi@4: if ($info['type'] == 'graph') { danielebarchiesi@4: if ($info['var']) { danielebarchiesi@4: $val = $info['var']['value']; danielebarchiesi@4: $this->index['graph_vars'][$val] = array_merge($this->v($val, array(), $this->index['graph_vars']), array(array('table' => $id))); danielebarchiesi@4: } danielebarchiesi@4: elseif ($info['uri']) { danielebarchiesi@4: $val = $info['uri']; danielebarchiesi@4: $this->index['graph_uris'][$val] = array_merge($this->v($val, array(), $this->index['graph_uris']), array(array('table' => $id))); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: $sub_ids = $this->v('patterns', array(), $pattern); danielebarchiesi@4: foreach ($sub_ids as $sub_id) { danielebarchiesi@4: $this->analyzeIndex($this->getPattern($sub_id)); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getGraphInfos($id) { danielebarchiesi@4: $r = array(); danielebarchiesi@4: if ($id) { danielebarchiesi@4: $pattern = $this->index['patterns'][$id]; danielebarchiesi@4: $type = $pattern['type']; danielebarchiesi@4: /* graph */ danielebarchiesi@4: if ($type == 'graph') { danielebarchiesi@4: $r[] = array('type' => 'graph', 'var' => $pattern['var'], 'uri' => $pattern['uri']); danielebarchiesi@4: } danielebarchiesi@4: $p_pattern = $this->index['patterns'][$pattern['parent_id']]; danielebarchiesi@4: if (isset($p_pattern['graph_infos'])) { danielebarchiesi@4: return array_merge($p_pattern['graph_infos'], $r); danielebarchiesi@4: } danielebarchiesi@4: return array_merge($this->getGraphInfos($pattern['parent_id']), $r); danielebarchiesi@4: } danielebarchiesi@4: /* FROM / FROM NAMED */ danielebarchiesi@4: else { danielebarchiesi@4: if (isset($this->infos['query']['dataset'])) { danielebarchiesi@4: foreach ($this->infos['query']['dataset'] as $set) { danielebarchiesi@4: $r[] = array_merge(array('type' => 'dataset'), $set); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getPattern($id) { danielebarchiesi@4: if (is_array($id)) { danielebarchiesi@4: return $id; danielebarchiesi@4: } danielebarchiesi@4: return $this->v($id, array(), $this->index['patterns']); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getInitialPattern($id) { danielebarchiesi@4: return $this->v($id, array(), $this->initial_index['patterns']); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getUnionIndexes($pre_index) { danielebarchiesi@4: $r = array(); danielebarchiesi@4: $branches = array(); danielebarchiesi@4: $min_depth = 1000; danielebarchiesi@4: /* only process branches with minimum depth */ danielebarchiesi@4: foreach ($pre_index['union_branches'] as $id) { danielebarchiesi@4: $branches[$id] = count(preg_split('/\_/', $id)); danielebarchiesi@4: $min_depth = min($min_depth, $branches[$id]); danielebarchiesi@4: } danielebarchiesi@4: foreach ($branches as $branch_id => $depth) { danielebarchiesi@4: if ($depth == $min_depth) { danielebarchiesi@4: $union_id = preg_replace('/\_[0-9]+$/', '', $branch_id); danielebarchiesi@4: $index = array('keeping' => $branch_id, 'union_branches' => array(), 'patterns' => $pre_index['patterns']); danielebarchiesi@4: $old_branches = $index['patterns'][$union_id]['patterns']; danielebarchiesi@4: $skip_id = ($old_branches[0] == $branch_id) ? $old_branches[1] : $old_branches[0]; danielebarchiesi@4: $index['patterns'][$union_id]['type'] = 'group'; danielebarchiesi@4: $index['patterns'][$union_id]['patterns'] = array($branch_id); danielebarchiesi@4: $has_sub_unions = 0; danielebarchiesi@4: foreach ($index['patterns'] as $pattern_id => $pattern) { danielebarchiesi@4: if (preg_match('/^' .$skip_id. '/', $pattern_id)) { danielebarchiesi@4: unset($index['patterns'][$pattern_id]); danielebarchiesi@4: } danielebarchiesi@4: elseif ($pattern['type'] == 'union') { danielebarchiesi@4: foreach ($pattern['patterns'] as $sub_union_branch_id) { danielebarchiesi@4: $index['union_branches'][] = $sub_union_branch_id; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if ($index['union_branches']) { danielebarchiesi@4: $r = array_merge($r, $this->getUnionIndexes($index)); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $r[] = $index; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function isOptionalPattern($id) { danielebarchiesi@4: $pattern = $this->getPattern($id); danielebarchiesi@4: if ($this->v('type', '', $pattern) == 'optional') { danielebarchiesi@4: return 1; danielebarchiesi@4: } danielebarchiesi@4: if ($this->v('parent_id', '0', $pattern) == '0') { danielebarchiesi@4: return 0; danielebarchiesi@4: } danielebarchiesi@4: return $this->isOptionalPattern($pattern['parent_id']); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getOptionalPattern($id) { danielebarchiesi@4: $pn = $this->getPattern($id); danielebarchiesi@4: do { danielebarchiesi@4: $pn = $this->getPattern($pn['parent_id']); danielebarchiesi@4: } while ($pn['parent_id'] && ($pn['type'] != 'optional')); danielebarchiesi@4: return $pn['id']; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function sameOptional($id, $id2) { danielebarchiesi@4: return $this->getOptionalPattern($id) == $this->getOptionalPattern($id2); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function isUnionPattern($id) { danielebarchiesi@4: $pattern = $this->getPattern($id); danielebarchiesi@4: if ($this->v('type', '', $pattern) == 'union') { danielebarchiesi@4: return 1; danielebarchiesi@4: } danielebarchiesi@4: if ($this->v('parent_id', '0', $pattern) == '0') { danielebarchiesi@4: return 0; danielebarchiesi@4: } danielebarchiesi@4: return $this->isUnionPattern($pattern['parent_id']); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getValueTable($col) { danielebarchiesi@4: return $this->store->getTablePrefix() . (preg_match('/^(s|o)$/', $col) ? $col . '2val' : 'id2val'); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getGraphTable() { danielebarchiesi@4: return $this->store->getTablePrefix() . 'g2t'; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getQuerySQL() { danielebarchiesi@4: $nl = "\n"; danielebarchiesi@4: $where_sql = $this->getWHERESQL(); /* pre-fills $index['sub_joins'] $index['constraints'] */ danielebarchiesi@4: $order_sql = $this->getORDERSQL(); /* pre-fills $index['sub_joins'] $index['constraints'] */ danielebarchiesi@4: return '' . danielebarchiesi@4: ($this->is_union_query ? 'SELECT' : 'SELECT' . $this->getDistinctSQL()) . $nl . danielebarchiesi@4: $this->getResultVarsSQL() . $nl . /* fills $index['sub_joins'] */ danielebarchiesi@4: $this->getFROMSQL() . danielebarchiesi@4: $this->getAllJoinsSQL() . danielebarchiesi@4: $this->getWHERESQL() . danielebarchiesi@4: $this->getGROUPSQL() . danielebarchiesi@4: $this->getORDERSQL() . danielebarchiesi@4: ($this->is_union_query ? '' : $this->getLIMITSQL()) . danielebarchiesi@4: $nl . danielebarchiesi@4: ''; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getDistinctSQL() { danielebarchiesi@4: if ($this->is_union_query) { danielebarchiesi@4: return ($this->v('distinct', 0, $this->infos['query']) || $this->v('reduced', 0, $this->infos['query'])) ? '' : ' ALL'; danielebarchiesi@4: } danielebarchiesi@4: return ($this->v('distinct', 0, $this->infos['query']) || $this->v('reduced', 0, $this->infos['query'])) ? ' DISTINCT' : ''; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getResultVarsSQL() { danielebarchiesi@4: $r = ''; danielebarchiesi@4: $vars = $this->infos['query']['result_vars']; danielebarchiesi@4: $nl = "\n"; danielebarchiesi@4: $added = array(); danielebarchiesi@4: foreach ($vars as $var) { danielebarchiesi@4: $var_name = $var['var']; danielebarchiesi@4: $tbl_alias = ''; danielebarchiesi@4: if ($tbl_infos = $this->getVarTableInfos($var_name, 0)) { danielebarchiesi@4: $tbl = $tbl_infos['table']; danielebarchiesi@4: $col = $tbl_infos['col']; danielebarchiesi@4: $tbl_alias = $tbl_infos['table_alias']; danielebarchiesi@4: } danielebarchiesi@4: elseif ($var_name == 1) {/* ASK query */ danielebarchiesi@4: $r .= '1 AS `success`'; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $this->addError('Result variable "' .$var_name. '" not used in query.'); danielebarchiesi@4: } danielebarchiesi@4: if ($tbl_alias) { danielebarchiesi@4: /* aggregate */ danielebarchiesi@4: if ($var['aggregate']) { danielebarchiesi@4: $conv_code = ''; danielebarchiesi@4: if (strtolower($var['aggregate']) != 'count') { danielebarchiesi@4: $tbl_alias = 'V_' . $tbl . '_' . $col . '.val'; danielebarchiesi@4: $conv_code = '0 + '; danielebarchiesi@4: } danielebarchiesi@4: if (!isset($added[$var['alias']])) { danielebarchiesi@4: $r .= $r ? ',' . $nl . ' ' : ' '; danielebarchiesi@4: $distinct_code = (strtolower($var['aggregate']) == 'count') && $this->v('distinct', 0, $this->infos['query']) ? 'DISTINCT ' : ''; danielebarchiesi@4: $r .= $var['aggregate'] . '(' . $conv_code . $distinct_code . $tbl_alias. ') AS `' . $var['alias'] . '`'; danielebarchiesi@4: $added[$var['alias']] = 1; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: /* normal var */ danielebarchiesi@4: else { danielebarchiesi@4: if (!isset($added[$var_name])) { danielebarchiesi@4: $r .= $r ? ',' . $nl . ' ' : ' '; danielebarchiesi@4: $r .= $tbl_alias . ' AS `' . $var_name . '`'; danielebarchiesi@4: $is_s = ($col == 's'); danielebarchiesi@4: $is_p = ($col == 'p'); danielebarchiesi@4: $is_o = ($col == 'o'); danielebarchiesi@4: if ($tbl_alias == 'NULL') { danielebarchiesi@4: /* type / add in UNION queries? */ danielebarchiesi@4: if ($is_s || $is_o) { danielebarchiesi@4: $r .= ', ' . $nl . ' NULL AS `' . $var_name . ' type`'; danielebarchiesi@4: } danielebarchiesi@4: /* lang_dt / always add it in UNION queries, the var may be used as s/p/o */ danielebarchiesi@4: if ($is_o || $this->is_union_query) { danielebarchiesi@4: $r .= ', ' . $nl . ' NULL AS `' . $var_name . ' lang_dt`'; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: /* type */ danielebarchiesi@4: if ($is_s || $is_o) { danielebarchiesi@4: $r .= ', ' . $nl . ' ' .$tbl_alias . '_type AS `' . $var_name . ' type`'; danielebarchiesi@4: } danielebarchiesi@4: /* lang_dt / always add it in UNION queries, the var may be used as s/p/o */ danielebarchiesi@4: if ($is_o) { danielebarchiesi@4: $r .= ', ' . $nl . ' ' .$tbl_alias . '_lang_dt AS `' . $var_name . ' lang_dt`'; danielebarchiesi@4: } danielebarchiesi@4: elseif ($this->is_union_query) { danielebarchiesi@4: $r .= ', ' . $nl . ' NULL AS `' . $var_name . ' lang_dt`'; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: $added[$var_name] = 1; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if (!in_array($tbl_alias, $this->index['sub_joins'])) { danielebarchiesi@4: $this->index['sub_joins'][] = $tbl_alias; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return $r ? $r : '1 AS `success`'; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getVarTableInfos($var, $ignore_initial_index = 1) { danielebarchiesi@4: if ($var == '*') { danielebarchiesi@4: return array('table' => '', 'col' => '', 'table_alias' => '*'); danielebarchiesi@4: } danielebarchiesi@4: if ($infos = $this->v($var, 0, $this->index['vars'])) { danielebarchiesi@4: $infos[0]['table_alias'] = 'T_' . $infos[0]['table'] . '.' . $infos[0]['col']; danielebarchiesi@4: return $infos[0]; danielebarchiesi@4: } danielebarchiesi@4: if ($infos = $this->v($var, 0, $this->index['graph_vars'])) { danielebarchiesi@4: $infos[0]['col'] = 'g'; danielebarchiesi@4: $infos[0]['table_alias'] = 'G_' . $infos[0]['table'] . '.' . $infos[0]['col']; danielebarchiesi@4: return $infos[0]; danielebarchiesi@4: } danielebarchiesi@4: if ($this->is_union_query && !$ignore_initial_index) { danielebarchiesi@4: if (($infos = $this->v($var, 0, $this->initial_index['vars'])) || ($infos = $this->v($var, 0, $this->initial_index['graph_vars']))) { danielebarchiesi@4: if (!in_array($var, $this->infos['null_vars'])) { danielebarchiesi@4: $this->infos['null_vars'][] = $var; danielebarchiesi@4: } danielebarchiesi@4: $infos[0]['table_alias'] = 'NULL'; danielebarchiesi@4: $infos[0]['col'] = !isset($infos[0]['col']) ? '' : $infos[0]['col']; danielebarchiesi@4: return $infos[0]; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return 0; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getFROMSQL() { danielebarchiesi@4: $from_ids = $this->index['from']; danielebarchiesi@4: $r = ''; danielebarchiesi@4: foreach ($from_ids as $from_id) { danielebarchiesi@4: $r .= $r ? ', ' : ''; danielebarchiesi@4: $r .= $this->getTripleTable($from_id) . ' T_' . $from_id; danielebarchiesi@4: } danielebarchiesi@4: /* MySQL 5 requires parentheses in case of multiple tables */ danielebarchiesi@4: /* MySQL >5.5 (?) does not allow parentheses in case of a single table anymore! */ danielebarchiesi@4: $r = (count($from_ids) > 1) ? '(' . $r . ')' : $r; danielebarchiesi@4: return $r ? 'FROM ' . $r : ''; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getOrderedJoinIDs() { danielebarchiesi@4: return array_merge($this->index['from'], $this->index['join'], $this->index['left_join']); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getJoinInfos($id) { danielebarchiesi@4: $r = array(); danielebarchiesi@4: $tbl_ids = $this->getOrderedJoinIDs(); danielebarchiesi@4: $pattern = $this->getPattern($id); danielebarchiesi@4: foreach ($tbl_ids as $tbl_id) { danielebarchiesi@4: $tbl_pattern = $this->getPattern($tbl_id); danielebarchiesi@4: if ($tbl_id != $id) { danielebarchiesi@4: foreach (array('s', 'p', 'o') as $tbl_term) { danielebarchiesi@4: foreach (array('var', 'bnode', 'uri') as $term_type) { danielebarchiesi@4: if ($tbl_pattern[$tbl_term . '_type'] == $term_type) { danielebarchiesi@4: foreach (array('s', 'p', 'o') as $term) { danielebarchiesi@4: if (($pattern[$term . '_type'] == $term_type) && ($tbl_pattern[$tbl_term] == $pattern[$term])) { danielebarchiesi@4: $r[] = array('term' => $term, 'join_tbl' => $tbl_id, 'join_term' => $tbl_term); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getAllJoinsSQL() { danielebarchiesi@4: $js = $this->getJoins(); danielebarchiesi@4: $ljs = $this->getLeftJoins(); danielebarchiesi@4: $entries = array_merge($js, $ljs); danielebarchiesi@4: $id2code = array(); danielebarchiesi@4: foreach ($entries as $entry) { danielebarchiesi@4: if (preg_match('/([^\s]+) ON (.*)/s', $entry, $m)) { danielebarchiesi@4: $id2code[$m[1]] = $entry; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: $deps = array(); danielebarchiesi@4: foreach ($id2code as $id => $code) { danielebarchiesi@4: $deps[$id]['rank'] = 0; danielebarchiesi@4: foreach ($id2code as $other_id => $other_code) { danielebarchiesi@4: $deps[$id]['rank'] += ($id != $other_id) && preg_match('/' . $other_id . '/', $code) ? 1 : 0; danielebarchiesi@4: $deps[$id][$other_id] = ($id != $other_id) && preg_match('/' . $other_id . '/', $code) ? 1 : 0; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: $r = ''; danielebarchiesi@4: do { danielebarchiesi@4: /* get next 0-rank */ danielebarchiesi@4: $next_id = 0; danielebarchiesi@4: foreach ($deps as $id => $infos) { danielebarchiesi@4: if ($infos['rank'] == 0) { danielebarchiesi@4: $next_id = $id; danielebarchiesi@4: break; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if ($next_id) { danielebarchiesi@4: $r .= "\n" . $id2code[$next_id]; danielebarchiesi@4: unset($deps[$next_id]); danielebarchiesi@4: foreach ($deps as $id => $infos) { danielebarchiesi@4: $deps[$id]['rank'] = 0; danielebarchiesi@4: unset($deps[$id][$next_id]); danielebarchiesi@4: foreach ($infos as $k => $v) { danielebarchiesi@4: if (!in_array($k, array('rank', $next_id))) { danielebarchiesi@4: $deps[$id]['rank'] += $v; danielebarchiesi@4: $deps[$id][$k] = $v; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: while ($next_id); danielebarchiesi@4: if ($deps) { danielebarchiesi@4: $this->addError('Not all patterns could be rewritten to SQL JOINs'); danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getJoins() { danielebarchiesi@4: $r = array(); danielebarchiesi@4: $nl = "\n"; danielebarchiesi@4: foreach ($this->index['join'] as $id) { danielebarchiesi@4: $sub_r = $this->getJoinConditionSQL($id); danielebarchiesi@4: $r[] = 'JOIN ' . $this->getTripleTable($id) . ' T_' . $id . ' ON (' . $sub_r . $nl . ')'; danielebarchiesi@4: } danielebarchiesi@4: foreach (array_merge($this->index['from'], $this->index['join']) as $id) { danielebarchiesi@4: if ($sub_r = $this->getRequiredSubJoinSQL($id)) { danielebarchiesi@4: $r[] = $sub_r; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getLeftJoins() { danielebarchiesi@4: $r = array(); danielebarchiesi@4: $nl = "\n"; danielebarchiesi@4: foreach ($this->index['left_join'] as $id) { danielebarchiesi@4: $sub_r = $this->getJoinConditionSQL($id); danielebarchiesi@4: $r[] = 'LEFT JOIN ' . $this->getTripleTable($id) . ' T_' . $id . ' ON (' . $sub_r . $nl . ')'; danielebarchiesi@4: } danielebarchiesi@4: foreach ($this->index['left_join'] as $id) { danielebarchiesi@4: if ($sub_r = $this->getRequiredSubJoinSQL($id, 'LEFT')) { danielebarchiesi@4: $r[] = $sub_r; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getJoinConditionSQL($id) { danielebarchiesi@4: $r = ''; danielebarchiesi@4: $nl = "\n"; danielebarchiesi@4: $infos = $this->getJoinInfos($id); danielebarchiesi@4: $pattern = $this->getPattern($id); danielebarchiesi@4: danielebarchiesi@4: $tbl = 'T_' . $id; danielebarchiesi@4: /* core dependency */ danielebarchiesi@4: $d_tbls = $this->getDependentJoins($id); danielebarchiesi@4: foreach ($d_tbls as $d_tbl) { danielebarchiesi@4: if (preg_match('/^T_([0-9\_]+)\.[spo]+/', $d_tbl, $m) && ($m[1] != $id)) { danielebarchiesi@4: if ($this->isJoinedBefore($m[1], $id) && !in_array($m[1], array_merge($this->index['from'], $this->index['join']))) { danielebarchiesi@4: $r .= $r ? $nl . ' AND ' : $nl . ' '; danielebarchiesi@4: $r .= '(' . $d_tbl . ' IS NOT NULL)'; danielebarchiesi@4: } danielebarchiesi@4: $this->logDependency($id, $d_tbl); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: /* triple-based join info */ danielebarchiesi@4: foreach ($infos as $info) { danielebarchiesi@4: if ($this->isJoinedBefore($info['join_tbl'], $id) && $this->joinDependsOn($id, $info['join_tbl'])) { danielebarchiesi@4: $r .= $r ? $nl . ' AND ' : $nl . ' '; danielebarchiesi@4: $r .= '(' . $tbl . '.' . $info['term'] . ' = T_' . $info['join_tbl'] . '.' . $info['join_term'] . ')'; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: /* filters etc */ danielebarchiesi@4: if ($sub_r = $this->getPatternSQL($pattern, 'join__T_' . $id)) { danielebarchiesi@4: $r .= $r ? $nl . ' AND ' . $sub_r : $nl . ' ' . '(' . $sub_r . ')'; danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * A log of identified table join dependencies in getJoinConditionSQL danielebarchiesi@4: * danielebarchiesi@4: */ danielebarchiesi@4: danielebarchiesi@4: function logDependency($id, $tbl) { danielebarchiesi@4: if (!isset($this->dependency_log[$id])) $this->dependency_log[$id] = array(); danielebarchiesi@4: if (!in_array($tbl, $this->dependency_log[$id])) { danielebarchiesi@4: $this->dependency_log[$id][] = $tbl; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * checks whether entries in the dependecy log could perhaps be optimized danielebarchiesi@4: * (triggers re-ordering of patterns danielebarchiesi@4: */ danielebarchiesi@4: danielebarchiesi@4: function problematicDependencies() { danielebarchiesi@4: foreach ($this->dependency_log as $id => $tbls) { danielebarchiesi@4: if (count($tbls) > 1) return count($tbls); danielebarchiesi@4: } danielebarchiesi@4: return 0; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function isJoinedBefore($tbl_1, $tbl_2) { danielebarchiesi@4: $tbl_ids = $this->getOrderedJoinIDs(); danielebarchiesi@4: foreach ($tbl_ids as $id) { danielebarchiesi@4: if ($id == $tbl_1) { danielebarchiesi@4: return 1; danielebarchiesi@4: } danielebarchiesi@4: if ($id == $tbl_2) { danielebarchiesi@4: return 0; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function joinDependsOn($id, $id2) { danielebarchiesi@4: if (in_array($id2, array_merge($this->index['from'], $this->index['join']))) { danielebarchiesi@4: return 1; danielebarchiesi@4: } danielebarchiesi@4: $d_tbls = $this->getDependentJoins($id2); danielebarchiesi@4: //echo $id . ' :: ' . $id2 . '=>' . print_r($d_tbls, 1); danielebarchiesi@4: foreach ($d_tbls as $d_tbl) { danielebarchiesi@4: if (preg_match('/^T_' .$id. '\./', $d_tbl)) { danielebarchiesi@4: return 1; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return 0; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getDependentJoins($id) { danielebarchiesi@4: $r = array(); danielebarchiesi@4: /* sub joins */ danielebarchiesi@4: foreach ($this->index['sub_joins'] as $alias) { danielebarchiesi@4: if (preg_match('/^(T|V|G)_' . $id . '/', $alias)) { danielebarchiesi@4: $r[] = $alias; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: /* siblings in shared optional */ danielebarchiesi@4: $o_id = $this->getOptionalPattern($id); danielebarchiesi@4: foreach ($this->index['sub_joins'] as $alias) { danielebarchiesi@4: if (preg_match('/^(T|V|G)_' . $o_id . '/', $alias) && !in_array($alias, $r)) { danielebarchiesi@4: $r[] = $alias; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: foreach ($this->index['left_join'] as $alias) { danielebarchiesi@4: if (preg_match('/^' . $o_id . '/', $alias) && !in_array($alias, $r)) { danielebarchiesi@4: $r[] = 'T_' . $alias . '.s'; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getRequiredSubJoinSQL($id, $prefix = '') {/* id is a triple pattern id. Optional FILTERS and GRAPHs are getting added to the join directly */ danielebarchiesi@4: $nl = "\n"; danielebarchiesi@4: $r = ''; danielebarchiesi@4: foreach ($this->index['sub_joins'] as $alias) { danielebarchiesi@4: if (preg_match('/^V_' . $id . '_([a-z\_]+)\.val$/', $alias, $m)) { danielebarchiesi@4: $col = $m[1]; danielebarchiesi@4: $sub_r = ''; danielebarchiesi@4: if ($this->isOptionalPattern($id)) { danielebarchiesi@4: $pattern = $this->getPattern($id); danielebarchiesi@4: do { danielebarchiesi@4: $pattern = $this->getPattern($pattern['parent_id']); danielebarchiesi@4: } while ($pattern['parent_id'] && ($pattern['type'] != 'optional')); danielebarchiesi@4: $sub_r = $this->getPatternSQL($pattern, 'sub_join__V_' . $id); danielebarchiesi@4: } danielebarchiesi@4: $sub_r = $sub_r ? $nl . ' AND (' . $sub_r . ')' : ''; danielebarchiesi@4: /* lang dt only on literals */ danielebarchiesi@4: if ($col == 'o_lang_dt') { danielebarchiesi@4: $sub_sub_r = 'T_' . $id . '.o_type = 2'; danielebarchiesi@4: $sub_r .= $nl . ' AND (' . $sub_sub_r . ')'; danielebarchiesi@4: } danielebarchiesi@4: //$cur_prefix = $prefix ? $prefix . ' ' : 'STRAIGHT_'; danielebarchiesi@4: $cur_prefix = $prefix ? $prefix . ' ' : ''; danielebarchiesi@4: if ($col == 'g') { danielebarchiesi@4: $r .= trim($cur_prefix . 'JOIN '. $this->getValueTable($col) . ' V_' .$id . '_' . $col. ' ON (' .$nl. ' (G_' . $id . '.' . $col. ' = V_' . $id. '_' . $col. '.id) ' . $sub_r . $nl . ')'); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $r .= trim($cur_prefix . 'JOIN '. $this->getValueTable($col) . ' V_' .$id . '_' . $col. ' ON (' .$nl. ' (T_' . $id . '.' . $col. ' = V_' . $id. '_' . $col. '.id) ' . $sub_r . $nl . ')'); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: elseif (preg_match('/^G_' . $id . '\.g$/', $alias, $m)) { danielebarchiesi@4: $pattern = $this->getPattern($id); danielebarchiesi@4: $sub_r = $this->getPatternSQL($pattern, 'graph_sub_join__G_' . $id); danielebarchiesi@4: $sub_r = $sub_r ? $nl . ' AND ' . $sub_r : ''; danielebarchiesi@4: /* dataset restrictions */ danielebarchiesi@4: $gi = $this->getGraphInfos($id); danielebarchiesi@4: $sub_sub_r = ''; danielebarchiesi@4: $added_gts = array(); danielebarchiesi@4: foreach ($gi as $set) { danielebarchiesi@4: if (isset($set['graph']) && !in_array($set['graph'], $added_gts)) { danielebarchiesi@4: $sub_sub_r .= $sub_sub_r !== '' ? ',' : ''; danielebarchiesi@4: $sub_sub_r .= $this->getTermID($set['graph'], 'g'); danielebarchiesi@4: $added_gts[] = $set['graph']; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: $sub_r .= ($sub_sub_r !== '') ? $nl . ' AND (G_' . $id . '.g IN (' . $sub_sub_r . '))' : ''; // /* ' . str_replace('#' , '::', $set['graph']) . ' */'; danielebarchiesi@4: /* other graph join conditions */ danielebarchiesi@4: foreach ($this->index['graph_vars'] as $var => $occurs) { danielebarchiesi@4: $occur_tbls = array(); danielebarchiesi@4: foreach ($occurs as $occur) { danielebarchiesi@4: $occur_tbls[] = $occur['table']; danielebarchiesi@4: if ($occur['table'] == $id) break; danielebarchiesi@4: } danielebarchiesi@4: foreach($occur_tbls as $tbl) { danielebarchiesi@4: if (($tbl != $id) && in_array($id, $occur_tbls) && $this->isJoinedBefore($tbl, $id)) { danielebarchiesi@4: $sub_r .= $nl . ' AND (G_' .$id. '.g = G_' .$tbl. '.g)'; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: //$cur_prefix = $prefix ? $prefix . ' ' : 'STRAIGHT_'; danielebarchiesi@4: $cur_prefix = $prefix ? $prefix . ' ' : ''; danielebarchiesi@4: $r .= trim($cur_prefix . 'JOIN '. $this->getGraphTable() . ' G_' .$id . ' ON (' .$nl. ' (T_' . $id . '.t = G_' .$id. '.t)' . $sub_r . $nl . ')'); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getWHERESQL() { danielebarchiesi@4: $r = ''; danielebarchiesi@4: $nl = "\n"; danielebarchiesi@4: /* standard constraints */ danielebarchiesi@4: $sub_r = $this->getPatternSQL($this->getPattern('0'), 'where'); danielebarchiesi@4: /* additional constraints */ danielebarchiesi@4: foreach ($this->index['from'] as $id) { danielebarchiesi@4: if ($sub_sub_r = $this->getConstraintSQL($id)) { danielebarchiesi@4: $sub_r .= $sub_r ? $nl . ' AND ' . $sub_sub_r : $sub_sub_r; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: $r .= $sub_r ? $sub_r : ''; danielebarchiesi@4: /* left join dependencies */ danielebarchiesi@4: foreach ($this->index['left_join'] as $id) { danielebarchiesi@4: $d_joins = $this->getDependentJoins($id); danielebarchiesi@4: $added = array(); danielebarchiesi@4: $d_aliases = array(); danielebarchiesi@4: //echo $id . ' =>' . print_r($d_joins, 1); danielebarchiesi@4: $id_alias = 'T_' . $id . '.s'; danielebarchiesi@4: foreach ($d_joins as $alias) { danielebarchiesi@4: if (preg_match('/^(T|V|G)_([0-9\_]+)(_[spo])?\.([a-z\_]+)/', $alias, $m)) { danielebarchiesi@4: $tbl_type = $m[1]; danielebarchiesi@4: $tbl_pattern_id = $m[2]; danielebarchiesi@4: $suffix = $m[3]; danielebarchiesi@4: if (($tbl_pattern_id >= $id) && $this->sameOptional($tbl_pattern_id, $id)) {/* get rid of dependency permutations and nested optionals */ danielebarchiesi@4: if (!in_array($tbl_type . '_' . $tbl_pattern_id . $suffix, $added)) { danielebarchiesi@4: $sub_r .= $sub_r ? ' AND ' : ''; danielebarchiesi@4: $sub_r .= $alias . ' IS NULL'; danielebarchiesi@4: $d_aliases[] = $alias; danielebarchiesi@4: $added[] = $tbl_type . '_' . $tbl_pattern_id . $suffix; danielebarchiesi@4: $id_alias = ($tbl_pattern_id == $id) ? $alias : $id_alias; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if (count($d_aliases) > 2) {/* @@todo fix this! */ danielebarchiesi@4: $sub_r1 = ' /* '.$id_alias.' dependencies */'; danielebarchiesi@4: $sub_r2 = '((' . $id_alias . ' IS NULL) OR (CONCAT(' . join(', ', $d_aliases) . ') IS NOT NULL))'; danielebarchiesi@4: $r .= $r ? $nl . $sub_r1 . $nl . ' AND ' .$sub_r2 : $sub_r1 . $nl . $sub_r2; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return $r ? $nl . 'WHERE ' . $r : ''; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function addConstraintSQLEntry($id, $sql) { danielebarchiesi@4: if (!isset($this->index['constraints'][$id])) { danielebarchiesi@4: $this->index['constraints'][$id] = array(); danielebarchiesi@4: } danielebarchiesi@4: if (!in_array($sql, $this->index['constraints'][$id])) { danielebarchiesi@4: $this->index['constraints'][$id][] = $sql; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getConstraintSQL($id) { danielebarchiesi@4: $r = ''; danielebarchiesi@4: $nl = "\n"; danielebarchiesi@4: $constraints = $this->v($id, array(), $this->index['constraints']); danielebarchiesi@4: foreach ($constraints as $constraint) { danielebarchiesi@4: $r .= $r ? $nl . ' AND ' . $constraint : $constraint; danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getPatternSQL($pattern, $context) { danielebarchiesi@4: $type = $this->v('type', '', $pattern); danielebarchiesi@4: if (!$type) { danielebarchiesi@4: return ''; danielebarchiesi@4: } danielebarchiesi@4: $m = 'get' . ucfirst($type) . 'PatternSQL'; danielebarchiesi@4: return method_exists($this, $m) ? $this->$m($pattern, $context) : $this->getDefaultPatternSQL($pattern, $context); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getDefaultPatternSQL($pattern, $context) { danielebarchiesi@4: $r = ''; danielebarchiesi@4: $nl = "\n"; danielebarchiesi@4: $sub_ids = $this->v('patterns', array(), $pattern); danielebarchiesi@4: foreach ($sub_ids as $sub_id) { danielebarchiesi@4: $sub_r = $this->getPatternSQL($this->getPattern($sub_id), $context); danielebarchiesi@4: $r .= ($r && $sub_r) ? $nl . ' AND (' . $sub_r . ')' : ($sub_r ? $sub_r : ''); danielebarchiesi@4: } danielebarchiesi@4: return $r ? $r : ''; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getTriplePatternSQL($pattern, $context) { danielebarchiesi@4: $r = ''; danielebarchiesi@4: $nl = "\n"; danielebarchiesi@4: $id = $pattern['id']; danielebarchiesi@4: /* s p o */ danielebarchiesi@4: $vars = array(); danielebarchiesi@4: foreach (array('s', 'p', 'o') as $term) { danielebarchiesi@4: $sub_r = ''; danielebarchiesi@4: $type = $pattern[$term . '_type']; danielebarchiesi@4: if ($type == 'uri') { danielebarchiesi@4: $term_id = $this->getTermID($pattern[$term], $term); danielebarchiesi@4: $sub_r = '(T_' . $id . '.' . $term . ' = ' . $term_id . ') /* ' . preg_replace('/[\#\*\>]/' , '::', $pattern[$term]) . ' */'; danielebarchiesi@4: } danielebarchiesi@4: elseif ($type == 'literal') { danielebarchiesi@4: $term_id = $this->getTermID($pattern[$term], $term); danielebarchiesi@4: $sub_r = '(T_' . $id . '.' . $term . ' = ' . $term_id . ') /* ' . preg_replace('/[\#\n\*\>]/' , ' ', $pattern[$term]) . ' */'; danielebarchiesi@4: if (($lang_dt = $this->v1($term . '_lang', '', $pattern)) || ($lang_dt = $this->v1($term . '_datatype', '', $pattern))) { danielebarchiesi@4: $lang_dt_id = $this->getTermID($lang_dt); danielebarchiesi@4: $sub_r .= $nl . ' AND (T_' . $id . '.' .$term. '_lang_dt = ' . $lang_dt_id . ') /* ' . preg_replace('/[\#\*\>]/' , '::', $lang_dt) . ' */'; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: elseif ($type == 'var') { danielebarchiesi@4: $val = $pattern[$term]; danielebarchiesi@4: if (isset($vars[$val])) {/* repeated var in pattern */ danielebarchiesi@4: $sub_r = '(T_' . $id . '.' . $term . '=' . 'T_' . $id . '.' . $vars[$val] . ')'; danielebarchiesi@4: } danielebarchiesi@4: $vars[$val] = $term; danielebarchiesi@4: if ($infos = $this->v($val, 0, $this->index['graph_vars'])) {/* graph var in triple pattern */ danielebarchiesi@4: $sub_r .= $sub_r ? $nl . ' AND ' : ''; danielebarchiesi@4: $tbl = $infos[0]['table']; danielebarchiesi@4: $sub_r .= 'G_' . $tbl . '.g = T_' . $id . '.' . $term; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if ($sub_r) { danielebarchiesi@4: if (preg_match('/^(join)/', $context) || (preg_match('/^where/', $context) && in_array($id, $this->index['from']))) { danielebarchiesi@4: $r .= $r ? $nl . ' AND ' . $sub_r : $sub_r; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: /* g */ danielebarchiesi@4: if ($infos = $pattern['graph_infos']) { danielebarchiesi@4: $tbl_alias = 'G_' . $id . '.g'; danielebarchiesi@4: if (!in_array($tbl_alias, $this->index['sub_joins'])) { danielebarchiesi@4: $this->index['sub_joins'][] = $tbl_alias; danielebarchiesi@4: } danielebarchiesi@4: $sub_r = array('graph_var' => '', 'graph_uri' => '', 'from' => '', 'from_named' => ''); danielebarchiesi@4: foreach ($infos as $info) { danielebarchiesi@4: $type = $info['type']; danielebarchiesi@4: if ($type == 'graph') { danielebarchiesi@4: if ($info['uri']) { danielebarchiesi@4: $term_id = $this->getTermID($info['uri'], 'g'); danielebarchiesi@4: $sub_r['graph_uri'] .= $sub_r['graph_uri'] ? $nl . ' AND ' : ''; danielebarchiesi@4: $sub_r['graph_uri'] .= '(' .$tbl_alias. ' = ' . $term_id . ') /* ' . preg_replace('/[\#\*\>]/' , '::', $info['uri']) . ' */'; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if ($sub_r['from'] && $sub_r['from_named']) { danielebarchiesi@4: $sub_r['from_named'] = ''; danielebarchiesi@4: } danielebarchiesi@4: if (!$sub_r['from'] && !$sub_r['from_named']) { danielebarchiesi@4: $sub_r['graph_var'] = ''; danielebarchiesi@4: } danielebarchiesi@4: if (preg_match('/^(graph_sub_join)/', $context)) { danielebarchiesi@4: foreach ($sub_r as $g_type => $g_sql) { danielebarchiesi@4: if ($g_sql) { danielebarchiesi@4: $r .= $r ? $nl . ' AND ' . $g_sql : $g_sql; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: /* optional sibling filters? */ danielebarchiesi@4: if (preg_match('/^(join|sub_join)/', $context) && $this->isOptionalPattern($id)) { danielebarchiesi@4: $o_pattern = $pattern; danielebarchiesi@4: do { danielebarchiesi@4: $o_pattern = $this->getPattern($o_pattern['parent_id']); danielebarchiesi@4: } while ($o_pattern['parent_id'] && ($o_pattern['type'] != 'optional')); danielebarchiesi@4: if ($sub_r = $this->getPatternSQL($o_pattern, 'optional_filter' . preg_replace('/^(.*)(__.*)$/', '\\2', $context))) { danielebarchiesi@4: $r .= $r ? $nl . ' AND ' . $sub_r : $sub_r; danielebarchiesi@4: } danielebarchiesi@4: /* created constraints */ danielebarchiesi@4: if ($sub_r = $this->getConstraintSQL($id)) { danielebarchiesi@4: $r .= $r ? $nl . ' AND ' . $sub_r : $sub_r; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: /* result */ danielebarchiesi@4: if (preg_match('/^(where)/', $context) && $this->isOptionalPattern($id)) { danielebarchiesi@4: return ''; danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getFilterPatternSQL($pattern, $context) { danielebarchiesi@4: $r = ''; danielebarchiesi@4: $id = $pattern['id']; danielebarchiesi@4: $constraint_id = $this->v1('constraint', '', $pattern); danielebarchiesi@4: $constraint = $this->getPattern($constraint_id); danielebarchiesi@4: $constraint_type = $constraint['type']; danielebarchiesi@4: if ($constraint_type == 'built_in_call') { danielebarchiesi@4: $r = $this->getBuiltInCallSQL($constraint, $context); danielebarchiesi@4: } danielebarchiesi@4: elseif ($constraint_type == 'expression') { danielebarchiesi@4: $r = $this->getExpressionSQL($constraint, $context, '', 'filter'); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $m = 'get' . ucfirst($constraint_type) . 'ExpressionSQL'; danielebarchiesi@4: if (method_exists($this, $m)) { danielebarchiesi@4: $r = $this->$m($constraint, $context, '', 'filter'); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if ($this->isOptionalPattern($id) && !preg_match('/^(join|optional_filter)/', $context)) { danielebarchiesi@4: return ''; danielebarchiesi@4: } danielebarchiesi@4: /* unconnected vars in FILTERs eval to false */ danielebarchiesi@4: $sub_r = $this->hasUnconnectedFilterVars($id); danielebarchiesi@4: if ($sub_r) { danielebarchiesi@4: if ($sub_r == 'alias') { danielebarchiesi@4: if (!in_array($r, $this->index['havings'])) $this->index['havings'][] = $r; danielebarchiesi@4: return ''; danielebarchiesi@4: } danielebarchiesi@4: elseif (preg_match('/^T([^\s]+\.)g (.*)$/s', $r, $m)) {/* graph filter */ danielebarchiesi@4: return 'G' . $m[1] . 't ' . $m[2]; danielebarchiesi@4: } danielebarchiesi@4: elseif (preg_match('/^\(*V[^\s]+_g\.val .*$/s', $r, $m)) {/* graph value filter, @@improveMe */ danielebarchiesi@4: //return $r; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: return 'FALSE'; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: /* some really ugly tweaks */ danielebarchiesi@4: /* empty language filter: FILTER ( lang(?v) = '' ) */ danielebarchiesi@4: $r = preg_replace('/\(\/\* language call \*\/ ([^\s]+) = ""\)/s', '((\\1 = "") OR (\\1 LIKE "%:%"))', $r); danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Checks if vars in the given (filter) pattern are used within the filter's scope. danielebarchiesi@4: */ danielebarchiesi@4: danielebarchiesi@4: function hasUnconnectedFilterVars($filter_pattern_id) { danielebarchiesi@4: $scope_id = $this->getFilterScope($filter_pattern_id); danielebarchiesi@4: $vars = $this->getFilterVars($filter_pattern_id); danielebarchiesi@4: $r = 0; danielebarchiesi@4: foreach ($vars as $var_name) { danielebarchiesi@4: if ($this->isUsedTripleVar($var_name, $scope_id)) continue; danielebarchiesi@4: if ($this->isAliasVar($var_name)) { danielebarchiesi@4: $r = 'alias'; danielebarchiesi@4: break; danielebarchiesi@4: } danielebarchiesi@4: $r = 1; danielebarchiesi@4: break; danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Returns the given filter pattern's scope (the id of the parent group pattern). danielebarchiesi@4: */ danielebarchiesi@4: danielebarchiesi@4: function getFilterScope($filter_pattern_id) { danielebarchiesi@4: $patterns = $this->initial_index['patterns']; danielebarchiesi@4: $r = ''; danielebarchiesi@4: foreach ($patterns as $id => $p) { danielebarchiesi@4: /* the id has to be sub-part of the given filter id */ danielebarchiesi@4: if (!preg_match('/^' . $id . '.+/', $filter_pattern_id)) continue; danielebarchiesi@4: /* we are looking for a group or union */ danielebarchiesi@4: if (!preg_match('/^(group|union)$/', $p['type'])) continue; danielebarchiesi@4: /* we are looking for the longest/deepest match */ danielebarchiesi@4: if (strlen($id) > strlen($r)) $r = $id; danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Builds a list of vars used in the given (filter) pattern. danielebarchiesi@4: */ danielebarchiesi@4: danielebarchiesi@4: function getFilterVars($filter_pattern_id) { danielebarchiesi@4: $r = array(); danielebarchiesi@4: $patterns = $this->initial_index['patterns']; danielebarchiesi@4: /* find vars in the given filter (i.e. the given id is part of their pattern id) */ danielebarchiesi@4: foreach ($patterns as $id => $p) { danielebarchiesi@4: if (!preg_match('/^' . $filter_pattern_id . '.+/', $id)) continue; danielebarchiesi@4: $var_name = ''; danielebarchiesi@4: if ($p['type'] == 'var') { danielebarchiesi@4: $var_name = $p['value']; danielebarchiesi@4: } danielebarchiesi@4: elseif (($p['type'] == 'built_in_call') && ($p['call'] == 'bound')) { danielebarchiesi@4: $var_name = $p['args'][0]['value']; danielebarchiesi@4: } danielebarchiesi@4: if ($var_name && !in_array($var_name, $r)) { danielebarchiesi@4: $r[] = $var_name; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Checks if $var_name appears as result projection alias. danielebarchiesi@4: */ danielebarchiesi@4: danielebarchiesi@4: function isAliasVar($var_name) { danielebarchiesi@4: foreach ($this->infos['query']['result_vars'] as $r_var) { danielebarchiesi@4: if ($r_var['alias'] == $var_name) return 1; danielebarchiesi@4: } danielebarchiesi@4: return 0; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Checks if $var_name is used in a triple pattern in the given scope danielebarchiesi@4: */ danielebarchiesi@4: danielebarchiesi@4: function isUsedTripleVar($var_name, $scope_id = '0') { danielebarchiesi@4: $patterns = $this->initial_index['patterns']; danielebarchiesi@4: foreach ($patterns as $id => $p) { danielebarchiesi@4: if ($p['type'] != 'triple') continue; danielebarchiesi@4: if (!preg_match('/^' . $scope_id . '.+/', $id)) continue; danielebarchiesi@4: foreach (array('s', 'p', 'o') as $term) { danielebarchiesi@4: if ($p[$term . '_type'] != 'var') continue; danielebarchiesi@4: if ($p[$term] == $var_name) return 1; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getExpressionSQL($pattern, $context, $val_type = '', $parent_type = '') { danielebarchiesi@4: $r = ''; danielebarchiesi@4: $nl = "\n"; danielebarchiesi@4: $type = $this->v1('type', '', $pattern); danielebarchiesi@4: $sub_type = $this->v1('sub_type', $type, $pattern); danielebarchiesi@4: if (preg_match('/^(and|or)$/', $sub_type)) { danielebarchiesi@4: foreach ($pattern['patterns'] as $sub_id) { danielebarchiesi@4: $sub_pattern = $this->getPattern($sub_id); danielebarchiesi@4: $sub_pattern_type = $sub_pattern['type']; danielebarchiesi@4: if ($sub_pattern_type == 'built_in_call') { danielebarchiesi@4: $sub_r = $this->getBuiltInCallSQL($sub_pattern, $context, '', $parent_type); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $sub_r = $this->getExpressionSQL($sub_pattern, $context, '', $parent_type); danielebarchiesi@4: } danielebarchiesi@4: if ($sub_r) { danielebarchiesi@4: $r .= $r ? ' ' . strtoupper($sub_type). ' (' .$sub_r. ')' : '(' . $sub_r . ')'; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: elseif ($sub_type == 'built_in_call') { danielebarchiesi@4: $r = $this->getBuiltInCallSQL($pattern, $context, $val_type, $parent_type); danielebarchiesi@4: } danielebarchiesi@4: elseif (preg_match('/literal/', $sub_type)) { danielebarchiesi@4: $r = $this->getLiteralExpressionSQL($pattern, $context, $val_type, $parent_type); danielebarchiesi@4: } danielebarchiesi@4: elseif ($sub_type) { danielebarchiesi@4: $m = 'get' . ucfirst($sub_type) . 'ExpressionSQL'; danielebarchiesi@4: if (method_exists($this, $m)) { danielebarchiesi@4: $r = $this->$m($pattern, $context, '', $parent_type); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: /* skip expressions that reference non-yet-joined tables */ danielebarchiesi@4: if (preg_match('/__(T|V|G)_(.+)$/', $context, $m)) { danielebarchiesi@4: $context_pattern_id = $m[2]; danielebarchiesi@4: $context_table_type = $m[1]; danielebarchiesi@4: if (preg_match_all('/((T|V|G)(\_[0-9])+)/', $r, $m)) { danielebarchiesi@4: $aliases = $m[1]; danielebarchiesi@4: $keep = 1; danielebarchiesi@4: foreach ($aliases as $alias) { danielebarchiesi@4: if (preg_match('/(T|V|G)_(.*)$/', $alias, $m)) { danielebarchiesi@4: $tbl_type = $m[1]; danielebarchiesi@4: $tbl = $m[2]; danielebarchiesi@4: if (!$this->isJoinedBefore($tbl, $context_pattern_id)) { danielebarchiesi@4: $keep = 0; danielebarchiesi@4: } danielebarchiesi@4: elseif (($context_pattern_id == $tbl) && preg_match('/(TV)/', $context_table_type . $tbl_type)) { danielebarchiesi@4: $keep = 0; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: $r = $keep ? $r : ''; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return $r ? '(' . $r . ')' : $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function detectExpressionValueType($pattern_ids) { danielebarchiesi@4: foreach ($pattern_ids as $id) { danielebarchiesi@4: $pattern = $this->getPattern($id); danielebarchiesi@4: $type = $this->v('type', '', $pattern); danielebarchiesi@4: if (($type == 'literal') && isset($pattern['datatype'])) { danielebarchiesi@4: if (in_array($pattern['datatype'], array($this->xsd . 'integer', $this->xsd . 'float', $this->xsd . 'double'))) { danielebarchiesi@4: return 'numeric'; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return ''; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getRelationalExpressionSQL($pattern, $context, $val_type = '', $parent_type = '') { danielebarchiesi@4: $r = ''; danielebarchiesi@4: $val_type = $this->detectExpressionValueType($pattern['patterns']); danielebarchiesi@4: $op = $pattern['operator']; danielebarchiesi@4: foreach ($pattern['patterns'] as $sub_id) { danielebarchiesi@4: $sub_pattern = $this->getPattern($sub_id); danielebarchiesi@4: $sub_pattern['parent_op'] = $op; danielebarchiesi@4: $sub_type = $sub_pattern['type']; danielebarchiesi@4: $m = ($sub_type == 'built_in_call') ? 'getBuiltInCallSQL' : 'get' . ucfirst($sub_type) . 'ExpressionSQL'; danielebarchiesi@4: $m = str_replace('ExpressionExpression', 'Expression', $m); danielebarchiesi@4: $sub_r = method_exists($this, $m) ? $this->$m($sub_pattern, $context, $val_type, 'relational') : ''; danielebarchiesi@4: $r .= $r ? ' ' . $op . ' ' . $sub_r : $sub_r; danielebarchiesi@4: } danielebarchiesi@4: return $r ? '(' . $r . ')' : $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getAdditiveExpressionSQL($pattern, $context, $val_type = '', $parent_type = '') { danielebarchiesi@4: $r = ''; danielebarchiesi@4: $val_type = $this->detectExpressionValueType($pattern['patterns']); danielebarchiesi@4: foreach ($pattern['patterns'] as $sub_id) { danielebarchiesi@4: $sub_pattern = $this->getPattern($sub_id); danielebarchiesi@4: $sub_type = $this->v('type', '', $sub_pattern); danielebarchiesi@4: $m = ($sub_type == 'built_in_call') ? 'getBuiltInCallSQL' : 'get' . ucfirst($sub_type) . 'ExpressionSQL'; danielebarchiesi@4: $m = str_replace('ExpressionExpression', 'Expression', $m); danielebarchiesi@4: $sub_r = method_exists($this, $m) ? $this->$m($sub_pattern, $context, $val_type, 'additive') : ''; danielebarchiesi@4: $r .= $r ? ' ' . $sub_r : $sub_r; danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getMultiplicativeExpressionSQL($pattern, $context, $val_type = '', $parent_type = '') { danielebarchiesi@4: $r = ''; danielebarchiesi@4: $val_type = $this->detectExpressionValueType($pattern['patterns']); danielebarchiesi@4: foreach ($pattern['patterns'] as $sub_id) { danielebarchiesi@4: $sub_pattern = $this->getPattern($sub_id); danielebarchiesi@4: $sub_type = $sub_pattern['type']; danielebarchiesi@4: $m = ($sub_type == 'built_in_call') ? 'getBuiltInCallSQL' : 'get' . ucfirst($sub_type) . 'ExpressionSQL'; danielebarchiesi@4: $m = str_replace('ExpressionExpression', 'Expression', $m); danielebarchiesi@4: $sub_r = method_exists($this, $m) ? $this->$m($sub_pattern, $context, $val_type, 'multiplicative') : ''; danielebarchiesi@4: $r .= $r ? ' ' . $sub_r : $sub_r; danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getVarExpressionSQL($pattern, $context, $val_type = '', $parent_type = '') { danielebarchiesi@4: $var = $pattern['value']; danielebarchiesi@4: $info = $this->getVarTableInfos($var); danielebarchiesi@4: if (!$tbl = $info['table']) { danielebarchiesi@4: /* might be an aggregate var */ danielebarchiesi@4: $vars = $this->infos['query']['result_vars']; danielebarchiesi@4: foreach ($vars as $test_var) { danielebarchiesi@4: if ($test_var['alias'] == $pattern['value']) { danielebarchiesi@4: return '`' . $pattern['value'] . '`'; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return ''; danielebarchiesi@4: } danielebarchiesi@4: $col = $info['col']; danielebarchiesi@4: if (($context == 'order') && ($col == 'o')) { danielebarchiesi@4: $tbl_alias = 'T_' . $tbl . '.o_comp'; danielebarchiesi@4: } danielebarchiesi@4: elseif ($context == 'sameterm') { danielebarchiesi@4: $tbl_alias = 'T_' . $tbl . '.' . $col; danielebarchiesi@4: } danielebarchiesi@4: elseif (($parent_type == 'relational') && ($col == 'o') && (preg_match('/[\<\>]/', $this->v('parent_op', '', $pattern)))) { danielebarchiesi@4: $tbl_alias = 'T_' . $tbl . '.o_comp'; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $tbl_alias = 'V_' . $tbl . '_' . $col . '.val'; danielebarchiesi@4: if (!in_array($tbl_alias, $this->index['sub_joins'])) { danielebarchiesi@4: $this->index['sub_joins'][] = $tbl_alias; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: $op = $this->v('operator', '', $pattern); danielebarchiesi@4: if (preg_match('/^(filter|and)/', $parent_type)) { danielebarchiesi@4: if ($op == '!') { danielebarchiesi@4: $r = '(((' . $tbl_alias . ' = 0) AND (CONCAT("1", ' . $tbl_alias . ') != 1))'; /* 0 and no string */ danielebarchiesi@4: $r .= ' OR (' . $tbl_alias . ' IN ("", "false")))'; /* or "", or "false" */ danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $r = '((' . $tbl_alias . ' != 0)'; /* not null */ danielebarchiesi@4: $r .= ' OR ((CONCAT("1", ' . $tbl_alias . ') = 1) AND (' . $tbl_alias . ' NOT IN ("", "false"))))'; /* string, and not "" or "false" */ danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $r = trim($op . ' ' . $tbl_alias); danielebarchiesi@4: if ($val_type == 'numeric') { danielebarchiesi@4: if (preg_match('/__(T|V|G)_(.+)$/', $context, $m)) { danielebarchiesi@4: $context_pattern_id = $m[2]; danielebarchiesi@4: $context_table_type = $m[1]; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $context_pattern_id = $pattern['id']; danielebarchiesi@4: $context_table_type = 'T'; danielebarchiesi@4: } danielebarchiesi@4: if ($this->isJoinedBefore($tbl, $context_pattern_id)) { danielebarchiesi@4: $add = ($tbl != $context_pattern_id) ? 1 : 0; danielebarchiesi@4: $add = (!$add && ($context_table_type == 'V')) ? 1 : 0; danielebarchiesi@4: if ($add) { danielebarchiesi@4: $this->addConstraintSQLEntry($context_pattern_id, '(' .$r. ' = "0" OR ' . $r . '*1.0 != 0)'); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getUriExpressionSQL($pattern, $context, $val_type = '') { danielebarchiesi@4: $val = $pattern['uri']; danielebarchiesi@4: $r = $pattern['operator']; danielebarchiesi@4: $r .= is_numeric($val) ? ' ' . $val : ' "' . mysql_real_escape_string($val, $this->store->getDBCon()) . '"'; danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getLiteralExpressionSQL($pattern, $context, $val_type = '', $parent_type = '') { danielebarchiesi@4: $val = $pattern['value']; danielebarchiesi@4: $r = $pattern['operator']; danielebarchiesi@4: if (is_numeric($val) && $this->v('datatype', 0, $pattern)) { danielebarchiesi@4: $r .= ' ' . $val; danielebarchiesi@4: } danielebarchiesi@4: elseif (preg_match('/^(true|false)$/i', $val) && ($this->v1('datatype', '', $pattern) == 'http://www.w3.org/2001/XMLSchema#boolean')) { danielebarchiesi@4: $r .= ' ' . strtoupper($val); danielebarchiesi@4: } danielebarchiesi@4: elseif ($parent_type == 'regex') { danielebarchiesi@4: $sub_r = mysql_real_escape_string($val, $this->store->getDBCon()); danielebarchiesi@4: $r .= ' "' . preg_replace('/\x5c\x5c/', '\\', $sub_r) . '"'; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $r .= ' "' . mysql_real_escape_string($val, $this->store->getDBCon()) . '"'; danielebarchiesi@4: } danielebarchiesi@4: if (($lang_dt = $this->v1('lang', '', $pattern)) || ($lang_dt = $this->v1('datatype', '', $pattern))) { danielebarchiesi@4: /* try table/alias via var in siblings */ danielebarchiesi@4: if ($var = $this->findSiblingVarExpression($pattern['id'])) { danielebarchiesi@4: if (isset($this->index['vars'][$var])) { danielebarchiesi@4: $infos = $this->index['vars'][$var]; danielebarchiesi@4: foreach ($infos as $info) { danielebarchiesi@4: if ($info['col'] == 'o') { danielebarchiesi@4: $tbl = $info['table']; danielebarchiesi@4: $term_id = $this->getTermID($lang_dt); danielebarchiesi@4: if ($pattern['operator'] != '!=') { danielebarchiesi@4: if (preg_match('/__(T|V|G)_(.+)$/', $context, $m)) { danielebarchiesi@4: $context_pattern_id = $m[2]; danielebarchiesi@4: $context_table_type = $m[1]; danielebarchiesi@4: } danielebarchiesi@4: elseif ($context == 'where') { danielebarchiesi@4: $context_pattern_id = $tbl; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $context_pattern_id = $pattern['id']; danielebarchiesi@4: } danielebarchiesi@4: if ($tbl == $context_pattern_id) {/* @todo better dependency check */ danielebarchiesi@4: if ($term_id || ($lang_dt != 'http://www.w3.org/2001/XMLSchema#integer')) {/* skip if simple int, but no id */ danielebarchiesi@4: $this->addConstraintSQLEntry($context_pattern_id, 'T_' . $tbl . '.o_lang_dt = ' . $term_id . ' /* ' . preg_replace('/[\#\*\>]/' , '::', $lang_dt) . ' */'); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: break; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return trim($r); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function findSiblingVarExpression($id) { danielebarchiesi@4: $pattern = $this->getPattern($id); danielebarchiesi@4: do { danielebarchiesi@4: $pattern = $this->getPattern($pattern['parent_id']); danielebarchiesi@4: } while ($pattern['parent_id'] && ($pattern['type'] != 'expression')); danielebarchiesi@4: $sub_patterns = $this->v('patterns', array(), $pattern); danielebarchiesi@4: foreach ($sub_patterns as $sub_id) { danielebarchiesi@4: $sub_pattern = $this->getPattern($sub_id); danielebarchiesi@4: if ($sub_pattern['type'] == 'var') { danielebarchiesi@4: return $sub_pattern['value']; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return ''; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getFunctionExpressionSQL($pattern, $context, $val_type = '', $parent_type = '') { danielebarchiesi@4: $fnc_uri = $pattern['uri']; danielebarchiesi@4: $op = $this->v('operator', '', $pattern); danielebarchiesi@4: if ($op) $op .= ' '; danielebarchiesi@4: if ($this->allow_extension_functions) { danielebarchiesi@4: /* mysql functions */ danielebarchiesi@4: if (preg_match('/^http\:\/\/web\-semantics\.org\/ns\/mysql\/(.*)$/', $fnc_uri, $m)) { danielebarchiesi@4: $fnc_name = strtoupper($m[1]); danielebarchiesi@4: $sub_r = ''; danielebarchiesi@4: foreach ($pattern['args'] as $arg) { danielebarchiesi@4: $sub_r .= $sub_r ? ', ' : ''; danielebarchiesi@4: $sub_r .= $this->getExpressionSQL($arg, $context, $val_type, $parent_type); danielebarchiesi@4: } danielebarchiesi@4: return $op . $fnc_name . '(' . $sub_r . ')'; danielebarchiesi@4: } danielebarchiesi@4: /* any other: ignore */ danielebarchiesi@4: } danielebarchiesi@4: /* simple type conversions */ danielebarchiesi@4: if (strpos($fnc_uri, 'http://www.w3.org/2001/XMLSchema#') === 0) { danielebarchiesi@4: return $op . $this->getExpressionSQL($pattern['args'][0], $context, $val_type, $parent_type); danielebarchiesi@4: } danielebarchiesi@4: return ''; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getBuiltInCallSQL($pattern, $context) { danielebarchiesi@4: $call = $pattern['call']; danielebarchiesi@4: $m = 'get' . ucfirst($call) . 'CallSQL'; danielebarchiesi@4: if (method_exists($this, $m)) { danielebarchiesi@4: return $this->$m($pattern, $context); danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $this->addError('Unknown built-in call "' . $call . '"'); danielebarchiesi@4: } danielebarchiesi@4: return ''; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getBoundCallSQL($pattern, $context) { danielebarchiesi@4: $r = ''; danielebarchiesi@4: $var = $pattern['args'][0]['value']; danielebarchiesi@4: $info = $this->getVarTableInfos($var); danielebarchiesi@4: if (!$tbl = $info['table']) { danielebarchiesi@4: return ''; danielebarchiesi@4: } danielebarchiesi@4: $col = $info['col']; danielebarchiesi@4: $tbl_alias = 'T_' . $tbl . '.' . $col; danielebarchiesi@4: if ($pattern['operator'] == '!') { danielebarchiesi@4: return $tbl_alias . ' IS NULL'; danielebarchiesi@4: } danielebarchiesi@4: return $tbl_alias . ' IS NOT NULL'; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getHasTypeCallSQL($pattern, $context, $type) { danielebarchiesi@4: $r = ''; danielebarchiesi@4: $var = $pattern['args'][0]['value']; danielebarchiesi@4: $info = $this->getVarTableInfos($var); danielebarchiesi@4: if (!$tbl = $info['table']) { danielebarchiesi@4: return ''; danielebarchiesi@4: } danielebarchiesi@4: $col = $info['col']; danielebarchiesi@4: $tbl_alias = 'T_' . $tbl . '.' . $col . '_type'; danielebarchiesi@4: return $tbl_alias . ' ' .$this->v('operator', '', $pattern) . '= ' . $type; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getIsliteralCallSQL($pattern, $context) { danielebarchiesi@4: return $this->getHasTypeCallSQL($pattern, $context, 2); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getIsblankCallSQL($pattern, $context) { danielebarchiesi@4: return $this->getHasTypeCallSQL($pattern, $context, 1); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getIsiriCallSQL($pattern, $context) { danielebarchiesi@4: return $this->getHasTypeCallSQL($pattern, $context, 0); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getIsuriCallSQL($pattern, $context) { danielebarchiesi@4: return $this->getHasTypeCallSQL($pattern, $context, 0); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getStrCallSQL($pattern, $context) { danielebarchiesi@4: $sub_pattern = $pattern['args'][0]; danielebarchiesi@4: $sub_type = $sub_pattern['type']; danielebarchiesi@4: $m = 'get' . ucfirst($sub_type) . 'ExpressionSQL'; danielebarchiesi@4: if (method_exists($this, $m)) { danielebarchiesi@4: return $this->$m($sub_pattern, $context); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getFunctionCallSQL($pattern, $context) { danielebarchiesi@4: $f_uri = $pattern['uri']; danielebarchiesi@4: if (preg_match('/(integer|double|float|string)$/', $f_uri)) {/* skip conversions */ danielebarchiesi@4: $sub_pattern = $pattern['args'][0]; danielebarchiesi@4: $sub_type = $sub_pattern['type']; danielebarchiesi@4: $m = 'get' . ucfirst($sub_type) . 'ExpressionSQL'; danielebarchiesi@4: if (method_exists($this, $m)) { danielebarchiesi@4: return $this->$m($sub_pattern, $context); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getLangDatatypeCallSQL($pattern, $context) { danielebarchiesi@4: $r = ''; danielebarchiesi@4: if (isset($pattern['patterns'])) { /* proceed with first argument only (assumed as base type for type promotion) */ danielebarchiesi@4: $sub_pattern = array('args' => array($pattern['patterns'][0])); danielebarchiesi@4: return $this->getLangDatatypeCallSQL($sub_pattern, $context); danielebarchiesi@4: } danielebarchiesi@4: if (!isset($pattern['args'])) { danielebarchiesi@4: return 'FALSE'; danielebarchiesi@4: } danielebarchiesi@4: $sub_type = $pattern['args'][0]['type']; danielebarchiesi@4: if ($sub_type != 'var') { danielebarchiesi@4: return $this->getLangDatatypeCallSQL($pattern['args'][0], $context); danielebarchiesi@4: } danielebarchiesi@4: $var = $pattern['args'][0]['value']; danielebarchiesi@4: $info = $this->getVarTableInfos($var); danielebarchiesi@4: if (!$tbl = $info['table']) { danielebarchiesi@4: return ''; danielebarchiesi@4: } danielebarchiesi@4: $col = 'o_lang_dt'; danielebarchiesi@4: $tbl_alias = 'V_' . $tbl . '_' . $col . '.val'; danielebarchiesi@4: if (!in_array($tbl_alias, $this->index['sub_joins'])) { danielebarchiesi@4: $this->index['sub_joins'][] = $tbl_alias; danielebarchiesi@4: } danielebarchiesi@4: $op = $this->v('operator', '', $pattern); danielebarchiesi@4: $r = trim($op . ' ' . $tbl_alias); danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getDatatypeCallSQL($pattern, $context) { danielebarchiesi@4: return '/* datatype call */ ' . $this->getLangDatatypeCallSQL($pattern, $context); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getLangCallSQL($pattern, $context) { danielebarchiesi@4: return '/* language call */ ' . $this->getLangDatatypeCallSQL($pattern, $context); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getLangmatchesCallSQL($pattern, $context) { danielebarchiesi@4: if (count($pattern['args']) == 2) { danielebarchiesi@4: $arg_1 = $pattern['args'][0]; danielebarchiesi@4: $arg_2 = $pattern['args'][1]; danielebarchiesi@4: $sub_r_1 = $this->getBuiltInCallSQL($arg_1, $context);/* adds value join */ danielebarchiesi@4: $sub_r_2 = $this->getExpressionSQL($arg_2, $context); danielebarchiesi@4: $op = $this->v('operator', '', $pattern); danielebarchiesi@4: if (preg_match('/^([\"\'])([^\'\"]+)/', $sub_r_2, $m)) { danielebarchiesi@4: if ($m[2] == '*') { danielebarchiesi@4: $r = ($op == '!') ? 'NOT (' . $sub_r_1 . ' REGEXP "^[a-zA-Z\-]+$"' . ')' : $sub_r_1 . ' REGEXP "^[a-zA-Z\-]+$"'; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $r = ($op == '!') ? $sub_r_1 . ' NOT LIKE ' . $m[1] . $m[2] . '%' . $m[1] : $sub_r_1 . ' LIKE ' . $m[1] . $m[2] . '%' . $m[1]; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $r = ($op == '!') ? $sub_r_1 . ' NOT LIKE CONCAT(' . $sub_r_2 . ', "%")' : $sub_r_1 . ' LIKE CONCAT(' . $sub_r_2 . ', "%")'; danielebarchiesi@4: } danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: return ''; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getSametermCallSQL($pattern, $context) { danielebarchiesi@4: if (count($pattern['args']) == 2) { danielebarchiesi@4: $arg_1 = $pattern['args'][0]; danielebarchiesi@4: $arg_2 = $pattern['args'][1]; danielebarchiesi@4: $sub_r_1 = $this->getExpressionSQL($arg_1, 'sameterm'); danielebarchiesi@4: $sub_r_2 = $this->getExpressionSQL($arg_2, 'sameterm'); danielebarchiesi@4: $op = $this->v('operator', '', $pattern); danielebarchiesi@4: $r = $sub_r_1 . ' ' . $op . '= ' . $sub_r_2; danielebarchiesi@4: return $r; danielebarchiesi@4: } danielebarchiesi@4: return ''; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: function getRegexCallSQL($pattern, $context) { danielebarchiesi@4: $ac = count($pattern['args']); danielebarchiesi@4: if ($ac >= 2) { danielebarchiesi@4: foreach ($pattern['args'] as $i => $arg) { danielebarchiesi@4: $var = 'sub_r_' . ($i + 1); danielebarchiesi@4: $$var = $this->getExpressionSQL($arg, $context, '', 'regex'); danielebarchiesi@4: } danielebarchiesi@4: $sub_r_3 = (isset($sub_r_3) && preg_match('/[\"\'](.+)[\"\']/', $sub_r_3, $m)) ? strtolower($m[1]) : ''; danielebarchiesi@4: $op = ($this->v('operator', '', $pattern) == '!') ? ' NOT' : ''; danielebarchiesi@4: if (!$sub_r_1 || !$sub_r_2) return ''; danielebarchiesi@4: $is_simple_search = preg_match('/^[\(\"]+(\^)?([a-z0-9\_\-\s]+)(\$)?[\)\"]+$/is', $sub_r_2, $m); danielebarchiesi@4: $is_simple_search = preg_match('/^[\(\"]+(\^)?([^\\\*\[\]\}\{\(\)\"\'\?\+\.]+)(\$)?[\)\"]+$/is', $sub_r_2, $m); danielebarchiesi@4: $is_o_search = preg_match('/o\.val\)*$/', $sub_r_1); danielebarchiesi@4: /* fulltext search (may have "|") */ danielebarchiesi@4: if ($is_simple_search && $is_o_search && !$op && (strlen($m[2]) > 8) && $this->store->hasFulltextIndex()) { danielebarchiesi@4: /* MATCH variations */ danielebarchiesi@4: if (($val_parts = preg_split('/\|/', $m[2]))) { danielebarchiesi@4: return 'MATCH(' . trim($sub_r_1, '()') . ') AGAINST("' . join(' ', $val_parts) . '")'; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: return 'MATCH(' . trim($sub_r_1, '()') . ') AGAINST("' . $m[2] . '")'; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if (preg_match('/\|/', $sub_r_2)) $is_simple_search = 0; danielebarchiesi@4: /* LIKE */ danielebarchiesi@4: if ($is_simple_search && ($sub_r_3 == 'i')) { danielebarchiesi@4: $sub_r_2 = $m[1] ? $m[2] : '%' . $m[2]; danielebarchiesi@4: $sub_r_2 .= isset($m[3]) && $m[3] ? '' : '%'; danielebarchiesi@4: return $sub_r_1 . $op . ' LIKE "' . $sub_r_2 . '"'; danielebarchiesi@4: } danielebarchiesi@4: /* REGEXP */ danielebarchiesi@4: $opt = ($sub_r_3 == 'i') ? '' : 'BINARY '; danielebarchiesi@4: return $sub_r_1 . $op . ' REGEXP ' . $opt . $sub_r_2; danielebarchiesi@4: } danielebarchiesi@4: return ''; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getGROUPSQL() { danielebarchiesi@4: $r = ''; danielebarchiesi@4: $nl = "\n"; danielebarchiesi@4: $infos = $this->v('group_infos', array(), $this->infos['query']); danielebarchiesi@4: foreach ($infos as $info) { danielebarchiesi@4: $var = $info['value']; danielebarchiesi@4: if ($tbl_infos = $this->getVarTableInfos($var, 0)) { danielebarchiesi@4: $tbl_alias = $tbl_infos['table_alias']; danielebarchiesi@4: $r .= $r ? ', ' : 'GROUP BY '; danielebarchiesi@4: $r .= $tbl_alias; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: $hr = ''; danielebarchiesi@4: foreach ($this->index['havings'] as $having) { danielebarchiesi@4: $hr .= $hr ? ' AND' : ' HAVING'; danielebarchiesi@4: $hr .= '(' . $having . ')'; danielebarchiesi@4: } danielebarchiesi@4: $r .= $hr; danielebarchiesi@4: return $r ? $nl . $r : $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getORDERSQL() { danielebarchiesi@4: $r = ''; danielebarchiesi@4: $nl = "\n"; danielebarchiesi@4: $infos = $this->v('order_infos', array(), $this->infos['query']); danielebarchiesi@4: foreach ($infos as $info) { danielebarchiesi@4: $type = $info['type']; danielebarchiesi@4: $ms = array('expression' => 'getExpressionSQL', 'built_in_call' => 'getBuiltInCallSQL', 'function_call' => 'getFunctionCallSQL'); danielebarchiesi@4: $m = isset($ms[$type]) ? $ms[$type] : 'get' . ucfirst($type) . 'ExpressionSQL'; danielebarchiesi@4: if (method_exists($this, $m)) { danielebarchiesi@4: $sub_r = '(' . $this->$m($info, 'order') . ')'; danielebarchiesi@4: $sub_r .= $this->v('direction', '', $info) == 'desc' ? ' DESC' : ''; danielebarchiesi@4: $r .= $r ? ',' .$nl . $sub_r : $sub_r; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: return $r ? $nl . 'ORDER BY ' . $r : ''; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getLIMITSQL() { danielebarchiesi@4: $r = ''; danielebarchiesi@4: $nl = "\n"; danielebarchiesi@4: $limit = $this->v('limit', -1, $this->infos['query']); danielebarchiesi@4: $offset = $this->v('offset', -1, $this->infos['query']); danielebarchiesi@4: if ($limit != -1) { danielebarchiesi@4: $offset = ($offset == -1) ? 0 : mysql_real_escape_string($offset, $this->store->getDBCon()); danielebarchiesi@4: $r = 'LIMIT ' . $offset . ',' . $limit; danielebarchiesi@4: } danielebarchiesi@4: elseif ($offset != -1) { danielebarchiesi@4: $r = 'LIMIT ' . mysql_real_escape_string($offset, $this->store->getDBCon()) . ',999999999999'; /* mysql doesn't support stand-alone offsets .. */ danielebarchiesi@4: } danielebarchiesi@4: return $r ? $nl . $r : ''; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: function getValueSQL($q_tbl, $q_sql) { danielebarchiesi@4: $r = ''; danielebarchiesi@4: /* result vars */ danielebarchiesi@4: $vars = $this->infos['query']['result_vars']; danielebarchiesi@4: $nl = "\n"; danielebarchiesi@4: $v_tbls = array('JOIN' => array(), 'LEFT JOIN' => array()); danielebarchiesi@4: $vc = 1; danielebarchiesi@4: foreach ($vars as $var) { danielebarchiesi@4: $var_name = $var['var']; danielebarchiesi@4: $r .= $r ? ',' . $nl . ' ' : ' '; danielebarchiesi@4: $col = ''; danielebarchiesi@4: $tbl = ''; danielebarchiesi@4: if ($var_name != '*') { danielebarchiesi@4: if (in_array($var_name, $this->infos['null_vars'])) { danielebarchiesi@4: if (isset($this->initial_index['vars'][$var_name])) { danielebarchiesi@4: $col = $this->initial_index['vars'][$var_name][0]['col']; danielebarchiesi@4: $tbl = $this->initial_index['vars'][$var_name][0]['table']; danielebarchiesi@4: } danielebarchiesi@4: if (isset($this->initial_index['graph_vars'][$var_name])) { danielebarchiesi@4: $col = 'g'; danielebarchiesi@4: $tbl = $this->initial_index['graph_vars'][$var_name][0]['table']; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: elseif (isset($this->index['vars'][$var_name])) { danielebarchiesi@4: $col = $this->index['vars'][$var_name][0]['col']; danielebarchiesi@4: $tbl = $this->index['vars'][$var_name][0]['table']; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if ($var['aggregate']) { danielebarchiesi@4: $r .= 'TMP.`' . $var['alias'] . '`'; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $join_type = in_array($tbl, array_merge($this->index['from'], $this->index['join'])) ? 'JOIN' : 'LEFT JOIN';/* val may be NULL */ danielebarchiesi@4: $v_tbls[$join_type][] = array('t_col' => $col, 'q_col' => $var_name, 'vc' => $vc); danielebarchiesi@4: $r .= 'V' . $vc . '.val AS `' . $var_name . '`'; danielebarchiesi@4: if (in_array($col, array('s', 'o'))) { danielebarchiesi@4: if (strpos($q_sql, '`' . $var_name . ' type`')) { danielebarchiesi@4: $r .= ', ' . $nl . ' TMP.`' . $var_name . ' type` AS `' . $var_name . ' type`'; danielebarchiesi@4: //$r .= ', ' . $nl . ' CASE TMP.`' . $var_name . ' type` WHEN 2 THEN "literal" WHEN 1 THEN "bnode" ELSE "uri" END AS `' . $var_name . ' type`'; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $r .= ', ' . $nl . ' NULL AS `' . $var_name . ' type`'; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: $vc++; danielebarchiesi@4: if ($col == 'o') { danielebarchiesi@4: $v_tbls[$join_type][] = array('t_col' => 'id', 'q_col' => $var_name . ' lang_dt', 'vc' => $vc); danielebarchiesi@4: if (strpos($q_sql, '`' . $var_name . ' lang_dt`')) { danielebarchiesi@4: $r .= ', ' .$nl. ' V' . $vc . '.val AS `' . $var_name . ' lang_dt`'; danielebarchiesi@4: $vc++; danielebarchiesi@4: } danielebarchiesi@4: else { danielebarchiesi@4: $r .= ', ' .$nl. ' NULL AS `' . $var_name . ' lang_dt`'; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: if (!$r) $r = '*'; danielebarchiesi@4: /* from */ danielebarchiesi@4: $r .= $nl . 'FROM (' . $q_tbl . ' TMP)'; danielebarchiesi@4: foreach (array('JOIN', 'LEFT JOIN') as $join_type) { danielebarchiesi@4: foreach ($v_tbls[$join_type] as $v_tbl) { danielebarchiesi@4: $tbl = $this->getValueTable($v_tbl['t_col']); danielebarchiesi@4: $var_name = preg_replace('/^([^\s]+)(.*)$/', '\\1', $v_tbl['q_col']); danielebarchiesi@4: $cur_join_type = in_array($var_name, $this->infos['null_vars']) ? 'LEFT JOIN' : $join_type; danielebarchiesi@4: if (!strpos($q_sql, '`' . $v_tbl['q_col'].'`')) continue; danielebarchiesi@4: $r .= $nl . ' ' . $cur_join_type . ' ' . $tbl . ' V' . $v_tbl['vc'] . ' ON ( danielebarchiesi@4: (V' . $v_tbl['vc'] . '.id = TMP.`' . $v_tbl['q_col'].'`) danielebarchiesi@4: )'; danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: /* create pos columns, id needed */ danielebarchiesi@4: if ($this->v('order_infos', array(), $this->infos['query'])) { danielebarchiesi@4: $r .= $nl . ' ORDER BY _pos_'; danielebarchiesi@4: } danielebarchiesi@4: return 'SELECT' . $nl . $r; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /* */ danielebarchiesi@4: danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: