PageRenderTime 49ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/elgg/mod/foafssl/arc/store/ARC2_StoreSelectQueryHandler.php

https://bitbucket.org/rhizomatik/lorea_production/
PHP | 1755 lines | 1527 code | 117 blank | 111 comment | 395 complexity | fd0373749a2f175631c773523a2c2824 MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0, BSD-3-Clause, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * ARC2 RDF Store SELECT Query Handler
  4. *
  5. * @author Benjamin Nowack
  6. * @license http://arc.semsol.org/license
  7. * @homepage <http://arc.semsol.org/>
  8. * @package ARC2
  9. * @version 2010-04-21
  10. *
  11. */
  12. ARC2::inc('StoreQueryHandler');
  13. class ARC2_StoreSelectQueryHandler extends ARC2_StoreQueryHandler {
  14. function __construct($a = '', &$caller) {/* caller has to be a store */
  15. parent::__construct($a, $caller);
  16. }
  17. function ARC2_StoreSelectQueryHandler($a = '', &$caller) {
  18. $this->__construct($a, $caller);
  19. }
  20. function __init() {/* db_con */
  21. parent::__init();
  22. $this->store =& $this->caller;
  23. $con = $this->store->getDBCon();
  24. $this->handler_type = 'select';
  25. $this->engine_type = $this->v('store_engine_type', 'MyISAM', $this->a);
  26. $this->cache_results = $this->v('store_cache_results', 0, $this->a);
  27. }
  28. /* */
  29. function runQuery($infos) {
  30. $con = $this->store->getDBCon();
  31. $rf = $this->v('result_format', '', $infos);
  32. $this->infos = $infos;
  33. $this->infos['null_vars'] = array();
  34. $this->indexes = array();
  35. $this->pattern_order_offset = 0;
  36. $q_sql = $this->getSQL();
  37. /* debug result formats */
  38. if ($rf == 'sql') return $q_sql;
  39. if ($rf == 'structure') return $this->infos;
  40. if ($rf == 'index') return $this->indexes;
  41. /* create intermediate results (ID-based) */
  42. $tmp_tbl = $this->createTempTable($q_sql);
  43. /* join values */
  44. $r = $this->getFinalQueryResult($q_sql, $tmp_tbl);
  45. /* remove intermediate results */
  46. if (!$this->cache_results) {
  47. mysql_query('DROP TABLE IF EXISTS ' . $tmp_tbl, $con);
  48. }
  49. return $r;
  50. }
  51. function getSQL() {
  52. $r = '';
  53. $nl = "\n";
  54. $this->buildInitialIndexes();
  55. foreach ($this->indexes as $i => $index) {
  56. $this->index = array_merge($this->getEmptyIndex(), $index);
  57. $this->analyzeIndex($this->getPattern('0'));
  58. $sub_r = $this->getQuerySQL();
  59. $r .= $r ? $nl . 'UNION' . $this->getDistinctSQL() . $nl : '';
  60. $r .= $this->is_union_query ? '(' . $sub_r . ')' : $sub_r;
  61. $this->indexes[$i] = $this->index;
  62. }
  63. $r .= $this->is_union_query ? $this->getLIMITSQL() : '';
  64. if ($this->v('order_infos', 0, $this->infos['query'])) {
  65. $r = preg_replace('/SELECT(\s+DISTINCT)?\s*/', 'SELECT\\1 NULL AS `_pos_`, ', $r);
  66. }
  67. if ($pd_count = $this->problematicDependencies()) {
  68. /* re-arranging the patterns sometimes reduces the LEFT JOIN dependencies */
  69. $set_sql = 0;
  70. if (!$this->pattern_order_offset) $set_sql = 1;
  71. if (!$set_sql && ($pd_count < $this->opt_sql_pd_count)) $set_sql = 1;
  72. if (!$set_sql && ($pd_count == $this->opt_sql_pd_count) && (strlen($r) < strlen($this->opt_sql))) $set_sql = 1;
  73. if ($set_sql) {
  74. $this->opt_sql = $r;
  75. $this->opt_sql_pd_count = $pd_count;
  76. }
  77. $this->pattern_order_offset++;
  78. if ($this->pattern_order_offset > 5) {
  79. return $this->opt_sql;
  80. }
  81. return $this->getSQL();
  82. }
  83. return $r;
  84. }
  85. function buildInitialIndexes() {
  86. $this->dependency_log = array();
  87. $this->index = $this->getEmptyIndex();
  88. $this->buildIndex($this->infos['query']['pattern'], 0);
  89. $tmp = $this->index;
  90. $this->analyzeIndex($this->getPattern('0'));
  91. $this->initial_index = $this->index;
  92. $this->index = $tmp;
  93. $this->is_union_query = $this->index['union_branches'] ? 1 : 0;
  94. $this->indexes = $this->is_union_query ? $this->getUnionIndexes($this->index) : array($this->index);
  95. }
  96. function createTempTable($q_sql) {
  97. $con = $this->store->getDBCon();
  98. $v = $this->store->getDBVersion();
  99. if ($this->cache_results) {
  100. $tbl = $this->store->getTablePrefix() . 'Q' . md5($q_sql);
  101. }
  102. else {
  103. $tbl = $this->store->getTablePrefix() . 'Q' . md5($q_sql . time() . uniqid(rand()));
  104. }
  105. if (strlen($tbl) > 64) $tbl = 'Q' . md5($tbl);
  106. $tmp_sql = 'CREATE TEMPORARY TABLE ' . $tbl . ' ( ' . $this->getTempTableDef($tbl, $q_sql) . ') ';
  107. $tmp_sql .= (($v < '04-01-00') && ($v >= '04-00-18')) ? 'ENGINE' : (($v >= '04-01-02') ? 'ENGINE' : 'TYPE');
  108. $tmp_sql .= '=' . $this->engine_type;/* HEAP doesn't support AUTO_INCREMENT, and MySQL breaks on MEMORY sometimes */
  109. if (!mysql_query($tmp_sql, $con) && !mysql_query(str_replace('CREATE TEMPORARY', 'CREATE', $tmp_sql), $con)) {
  110. return $this->addError(mysql_error($con));
  111. }
  112. mysql_unbuffered_query('INSERT INTO ' . $tbl . ' ' . "\n" . $q_sql, $con);
  113. if ($er = mysql_error($con)) $this->addError($er);
  114. return $tbl;
  115. }
  116. function getEmptyIndex() {
  117. return array(
  118. 'from' => array(),
  119. 'join' => array(),
  120. 'left_join' => array(),
  121. 'vars' => array(), 'graph_vars' => array(), 'graph_uris' => array(),
  122. 'bnodes' => array(),
  123. 'triple_patterns' => array(),
  124. 'sub_joins' => array(),
  125. 'constraints' => array(),
  126. 'union_branches'=> array(),
  127. 'patterns' => array(),
  128. 'havings' => array()
  129. );
  130. }
  131. function getTempTableDef($tmp_tbl, $q_sql) {
  132. $col_part = preg_replace('/^SELECT\s*(DISTINCT)?(.*)FROM.*$/s', '\\2', $q_sql);
  133. $parts = explode(',', $col_part);
  134. $has_order_infos = $this->v('order_infos', 0, $this->infos['query']);
  135. $r = '';
  136. $added = array();
  137. foreach ($parts as $part) {
  138. if (preg_match('/\.?(.+)\s+AS\s+`(.+)`/U', trim($part), $m) && !isset($added[$m[2]])) {
  139. $col = $m[1];
  140. $alias = $m[2];
  141. if ($alias == '_pos_') continue;
  142. $r .= $r ? ',' : '';
  143. $r .= "\n `" . $alias . "` int UNSIGNED";
  144. $added[$alias] = 1;
  145. }
  146. }
  147. if ($has_order_infos) {
  148. $r = "\n" . '`_pos_` mediumint NOT NULL AUTO_INCREMENT PRIMARY KEY, ' . $r;
  149. }
  150. return $r ? $r . "\n" : '';
  151. }
  152. function getFinalQueryResult($q_sql, $tmp_tbl) {
  153. /* var names */
  154. $vars = array();
  155. $aggregate_vars = array();
  156. foreach ($this->infos['query']['result_vars'] as $entry) {
  157. if ($entry['aggregate']) {
  158. $vars[] = $entry['alias'];
  159. $aggregate_vars[] = $entry['alias'];
  160. }
  161. else {
  162. $vars[] = $entry['var'];
  163. }
  164. }
  165. /* result */
  166. $r = array('variables' => $vars);
  167. $v_sql = $this->getValueSQL($tmp_tbl, $q_sql);
  168. //echo "\n\n" . $v_sql;
  169. $t1 = ARC2::mtime();
  170. $con = $this->store->getDBCon();
  171. $rs = mysql_unbuffered_query($v_sql, $con);
  172. if ($er = mysql_error($con)) {
  173. $this->addError($er);
  174. }
  175. $t2 = ARC2::mtime();
  176. $rows = array();
  177. $types = array(0 => 'uri', 1 => 'bnode', 2 => 'literal');
  178. if ($rs) {
  179. while ($pre_row = mysql_fetch_array($rs)) {
  180. $row = array();
  181. foreach ($vars as $var) {
  182. if (isset($pre_row[$var])) {
  183. $row[$var] = $pre_row[$var];
  184. $row[$var . ' type'] = isset($pre_row[$var . ' type']) ? $types[$pre_row[$var . ' type']] : (in_array($var, $aggregate_vars) ? 'literal' : 'uri');
  185. if (isset($pre_row[$var . ' lang_dt']) && ($lang_dt = $pre_row[$var . ' lang_dt'])) {
  186. if (preg_match('/^([a-z]+(\-[a-z0-9]+)*)$/i', $lang_dt)) {
  187. $row[$var . ' lang'] = $lang_dt;
  188. }
  189. else {
  190. $row[$var . ' datatype'] = $lang_dt;
  191. }
  192. }
  193. }
  194. }
  195. if ($row || !$vars) {
  196. $rows[] = $row;
  197. }
  198. }
  199. }
  200. $r['rows'] = $rows;
  201. return $r;
  202. }
  203. /* */
  204. function buildIndex($pattern, $id) {
  205. $pattern['id'] = $id;
  206. $type = $this->v('type', '', $pattern);
  207. if (($type == 'filter') && $this->v('constraint', 0, $pattern)) {
  208. $sub_pattern = $pattern['constraint'];
  209. $sub_pattern['parent_id'] = $id;
  210. $sub_id = $id . '_0';
  211. $this->buildIndex($sub_pattern, $sub_id);
  212. $pattern['constraint'] = $sub_id;
  213. }
  214. else {
  215. $sub_patterns = $this->v('patterns', array(), $pattern);
  216. $keys = array_keys($sub_patterns);
  217. $spc = count($sub_patterns);
  218. if (($spc > 4) && $this->pattern_order_offset) {
  219. $keys = array();
  220. for ($i = 0 ; $i < $spc; $i++) {
  221. $keys[$i] = $i + $this->pattern_order_offset;
  222. while ($keys[$i] >= $spc) $keys[$i] -= $spc;
  223. }
  224. }
  225. foreach ($keys as $i => $key) {
  226. $sub_pattern = $sub_patterns[$key];
  227. $sub_pattern['parent_id'] = $id;
  228. $sub_id = $id . '_' . $key;
  229. $this->buildIndex($sub_pattern, $sub_id);
  230. $pattern['patterns'][$i] = $sub_id;
  231. if ($type == 'union') {
  232. $this->index['union_branches'][] = $sub_id;
  233. }
  234. }
  235. }
  236. $this->index['patterns'][$id] = $pattern;
  237. }
  238. /* */
  239. function analyzeIndex($pattern) {
  240. $type = $pattern['type'];
  241. $id = $pattern['id'];
  242. /* triple */
  243. if ($type == 'triple') {
  244. foreach (array('s', 'p', 'o') as $term) {
  245. if ($pattern[$term . '_type'] == 'var') {
  246. $val = $pattern[$term];
  247. $this->index['vars'][$val] = array_merge($this->v($val, array(), $this->index['vars']), array(array('table' => $pattern['id'], 'col' =>$term)));
  248. }
  249. if ($pattern[$term . '_type'] == 'bnode') {
  250. $val = $pattern[$term];
  251. $this->index['bnodes'][$val] = array_merge($this->v($val, array(), $this->index['bnodes']), array(array('table' => $pattern['id'], 'col' =>$term)));
  252. }
  253. }
  254. $this->index['triple_patterns'][] = $pattern['id'];
  255. /* joins */
  256. if ($this->isOptionalPattern($id)) {
  257. $this->index['left_join'][] = $id;
  258. }
  259. elseif (!$this->index['from']) {
  260. $this->index['from'][] = $id;
  261. }
  262. elseif (!$this->getJoinInfos($id)) {
  263. $this->index['from'][] = $id;
  264. }
  265. else {
  266. $this->index['join'][] = $id;
  267. }
  268. /* graph infos, graph vars */
  269. $this->index['patterns'][$id]['graph_infos'] = $this->getGraphInfos($id);
  270. foreach ($this->index['patterns'][$id]['graph_infos'] as $info) {
  271. if ($info['type'] == 'graph') {
  272. if ($info['var']) {
  273. $val = $info['var']['value'];
  274. $this->index['graph_vars'][$val] = array_merge($this->v($val, array(), $this->index['graph_vars']), array(array('table' => $id)));
  275. }
  276. elseif ($info['uri']) {
  277. $val = $info['uri'];
  278. $this->index['graph_uris'][$val] = array_merge($this->v($val, array(), $this->index['graph_uris']), array(array('table' => $id)));
  279. }
  280. }
  281. }
  282. }
  283. $sub_ids = $this->v('patterns', array(), $pattern);
  284. foreach ($sub_ids as $sub_id) {
  285. $this->analyzeIndex($this->getPattern($sub_id));
  286. }
  287. }
  288. /* */
  289. function getGraphInfos($id) {
  290. $r = array();
  291. if ($id) {
  292. $pattern = $this->index['patterns'][$id];
  293. $type = $pattern['type'];
  294. /* graph */
  295. if ($type == 'graph') {
  296. $r[] = array('type' => 'graph', 'var' => $pattern['var'], 'uri' => $pattern['uri']);
  297. }
  298. $p_pattern = $this->index['patterns'][$pattern['parent_id']];
  299. if (isset($p_pattern['graph_infos'])) {
  300. return array_merge($p_pattern['graph_infos'], $r);
  301. }
  302. return array_merge($this->getGraphInfos($pattern['parent_id']), $r);
  303. }
  304. /* FROM / FROM NAMED */
  305. else {
  306. if (isset($this->infos['query']['dataset'])) {
  307. foreach ($this->infos['query']['dataset'] as $set) {
  308. $r[] = array_merge(array('type' => 'dataset'), $set);
  309. }
  310. }
  311. }
  312. return $r;
  313. }
  314. /* */
  315. function getPattern($id) {
  316. if (is_array($id)) {
  317. return $id;
  318. }
  319. return $this->v($id, array(), $this->index['patterns']);
  320. }
  321. function getInitialPattern($id) {
  322. return $this->v($id, array(), $this->initial_index['patterns']);
  323. }
  324. /* */
  325. function getUnionIndexes($pre_index) {
  326. $r = array();
  327. $branches = array();
  328. $min_length = 1000;
  329. foreach ($pre_index['union_branches'] as $id) {
  330. $branches[$id] = strlen($id);
  331. $min_length = min($min_length, strlen($id));
  332. }
  333. foreach ($branches as $branch_id => $length) {
  334. if ($length == $min_length) {
  335. $union_id = substr($branch_id, 0, -2);
  336. $index = array('keeping' => $branch_id, 'union_branches' => array(), 'patterns' => $pre_index['patterns']);
  337. $old_branches = $index['patterns'][$union_id]['patterns'];
  338. $skip_id = ($old_branches[0] == $branch_id) ? $old_branches[1] : $old_branches[0];
  339. $index['patterns'][$union_id]['type'] = 'group';
  340. $index['patterns'][$union_id]['patterns'] = array($branch_id);
  341. $has_sub_unions = 0;
  342. foreach ($index['patterns'] as $pattern_id => $pattern) {
  343. if (preg_match('/^' .$skip_id. '/', $pattern_id)) {
  344. unset($index['patterns'][$pattern_id]);
  345. }
  346. elseif ($pattern['type'] == 'union') {
  347. foreach ($pattern['patterns'] as $sub_union_branch_id) {
  348. $index['union_branches'][] = $sub_union_branch_id;
  349. }
  350. }
  351. }
  352. if ($index['union_branches']) {
  353. $r = array_merge($r, $this->getUnionIndexes($index));
  354. }
  355. else {
  356. $r[] = $index;
  357. }
  358. }
  359. }
  360. return $r;
  361. }
  362. /* */
  363. function isOptionalPattern($id) {
  364. $pattern = $this->getPattern($id);
  365. if ($this->v('type', '', $pattern) == 'optional') {
  366. return 1;
  367. }
  368. if ($this->v('parent_id', '0', $pattern) == '0') {
  369. return 0;
  370. }
  371. return $this->isOptionalPattern($pattern['parent_id']);
  372. }
  373. function getOptionalPattern($id) {
  374. $pn = $this->getPattern($id);
  375. do {
  376. $pn = $this->getPattern($pn['parent_id']);
  377. } while ($pn['parent_id'] && ($pn['type'] != 'optional'));
  378. return $pn['id'];
  379. }
  380. function sameOptional($id, $id2) {
  381. return $this->getOptionalPattern($id) == $this->getOptionalPattern($id2);
  382. }
  383. /* */
  384. function isUnionPattern($id) {
  385. $pattern = $this->getPattern($id);
  386. if ($this->v('type', '', $pattern) == 'union') {
  387. return 1;
  388. }
  389. if ($this->v('parent_id', '0', $pattern) == '0') {
  390. return 0;
  391. }
  392. return $this->isUnionPattern($pattern['parent_id']);
  393. }
  394. /* */
  395. function getValueTable($col) {
  396. return $this->store->getTablePrefix() . (preg_match('/^(s|o)$/', $col) ? $col . '2val' : 'id2val');
  397. }
  398. function getGraphTable() {
  399. return $this->store->getTablePrefix() . 'g2t';
  400. }
  401. /* */
  402. function getQuerySQL() {
  403. $nl = "\n";
  404. $where_sql = $this->getWHERESQL(); /* pre-fills $index['sub_joins'] $index['constraints'] */
  405. $order_sql = $this->getORDERSQL(); /* pre-fills $index['sub_joins'] $index['constraints'] */
  406. return '' .
  407. ($this->is_union_query ? 'SELECT' : 'SELECT' . $this->getDistinctSQL()) . $nl .
  408. $this->getResultVarsSQL() . $nl . /* fills $index['sub_joins'] */
  409. $this->getFROMSQL() .
  410. $this->getAllJoinsSQL() .
  411. $this->getWHERESQL() .
  412. $this->getGROUPSQL() .
  413. $this->getORDERSQL() .
  414. ($this->is_union_query ? '' : $this->getLIMITSQL()) .
  415. $nl .
  416. '';
  417. }
  418. /* */
  419. function getDistinctSQL() {
  420. if ($this->is_union_query) {
  421. return ($this->v('distinct', 0, $this->infos['query']) || $this->v('reduced', 0, $this->infos['query'])) ? '' : ' ALL';
  422. }
  423. return ($this->v('distinct', 0, $this->infos['query']) || $this->v('reduced', 0, $this->infos['query'])) ? ' DISTINCT' : '';
  424. }
  425. /* */
  426. function getResultVarsSQL() {
  427. $r = '';
  428. $vars = $this->infos['query']['result_vars'];
  429. $nl = "\n";
  430. $added = array();
  431. foreach ($vars as $var) {
  432. $var_name = $var['var'];
  433. $tbl_alias = '';
  434. if ($tbl_infos = $this->getVarTableInfos($var_name, 0)) {
  435. $tbl = $tbl_infos['table'];
  436. $col = $tbl_infos['col'];
  437. $tbl_alias = $tbl_infos['table_alias'];
  438. }
  439. elseif ($var_name == 1) {/* ASK query */
  440. $r .= '1 AS `success`';
  441. }
  442. else {
  443. $this->addError('Result variable "' .$var_name. '" not used in query.');
  444. }
  445. if ($tbl_alias) {
  446. /* aggregate */
  447. if ($var['aggregate']) {
  448. $conv_code = '';
  449. if (strtolower($var['aggregate']) != 'count') {
  450. $tbl_alias = 'V_' . $tbl . '_' . $col . '.val';
  451. $conv_code = '0 + ';
  452. }
  453. if (!isset($added[$var['alias']])) {
  454. $r .= $r ? ',' . $nl . ' ' : ' ';
  455. $distinct_code = (strtolower($var['aggregate']) == 'count') && $this->v('distinct', 0, $this->infos['query']) ? 'DISTINCT ' : '';
  456. $r .= $var['aggregate'] . '(' . $conv_code . $distinct_code . $tbl_alias. ') AS `' . $var['alias'] . '`';
  457. $added[$var['alias']] = 1;
  458. }
  459. }
  460. /* normal var */
  461. else {
  462. if (!isset($added[$var_name])) {
  463. $r .= $r ? ',' . $nl . ' ' : ' ';
  464. $r .= $tbl_alias . ' AS `' . $var_name . '`';
  465. $is_s = ($col == 's');
  466. $is_p = ($col == 'p');
  467. $is_o = ($col == 'o');
  468. if ($tbl_alias == 'NULL') {
  469. /* type / add in UNION queries? */
  470. if ($is_s || $is_o) {
  471. $r .= ', ' . $nl . ' NULL AS `' . $var_name . ' type`';
  472. }
  473. /* lang_dt / always add it in UNION queries, the var may be used as s/p/o */
  474. if ($is_o || $this->is_union_query) {
  475. $r .= ', ' . $nl . ' NULL AS `' . $var_name . ' lang_dt`';
  476. }
  477. }
  478. else {
  479. /* type */
  480. if ($is_s || $is_o) {
  481. $r .= ', ' . $nl . ' ' .$tbl_alias . '_type AS `' . $var_name . ' type`';
  482. }
  483. /* lang_dt / always add it in UNION queries, the var may be used as s/p/o */
  484. if ($is_o) {
  485. $r .= ', ' . $nl . ' ' .$tbl_alias . '_lang_dt AS `' . $var_name . ' lang_dt`';
  486. }
  487. elseif ($this->is_union_query) {
  488. $r .= ', ' . $nl . ' NULL AS `' . $var_name . ' lang_dt`';
  489. }
  490. }
  491. $added[$var_name] = 1;
  492. }
  493. }
  494. if (!in_array($tbl_alias, $this->index['sub_joins'])) {
  495. $this->index['sub_joins'][] = $tbl_alias;
  496. }
  497. }
  498. }
  499. return $r ? $r : '1 AS `success`';
  500. }
  501. function getVarTableInfos($var, $ignore_initial_index = 1) {
  502. if ($var == '*') {
  503. return array('table' => '', 'col' => '', 'table_alias' => '*');
  504. }
  505. if ($infos = $this->v($var, 0, $this->index['vars'])) {
  506. $infos[0]['table_alias'] = 'T_' . $infos[0]['table'] . '.' . $infos[0]['col'];
  507. return $infos[0];
  508. }
  509. if ($infos = $this->v($var, 0, $this->index['graph_vars'])) {
  510. $infos[0]['col'] = 'g';
  511. $infos[0]['table_alias'] = 'G_' . $infos[0]['table'] . '.' . $infos[0]['col'];
  512. return $infos[0];
  513. }
  514. if ($this->is_union_query && !$ignore_initial_index) {
  515. if (($infos = $this->v($var, 0, $this->initial_index['vars'])) || ($infos = $this->v($var, 0, $this->initial_index['graph_vars']))) {
  516. if (!in_array($var, $this->infos['null_vars'])) {
  517. $this->infos['null_vars'][] = $var;
  518. }
  519. $infos[0]['table_alias'] = 'NULL';
  520. $infos[0]['col'] = !isset($infos[0]['col']) ? '' : $infos[0]['col'];
  521. return $infos[0];
  522. }
  523. }
  524. return 0;
  525. }
  526. /* */
  527. function getFROMSQL() {
  528. $r = '';
  529. foreach ($this->index['from'] as $id) {
  530. $r .= $r ? ', ' : 'FROM (';
  531. $r .= $this->getTripleTable($id) . ' T_' . $id;
  532. }
  533. return $r ? $r . ')' : '';
  534. }
  535. /* */
  536. function getOrderedJoinIDs() {
  537. return array_merge($this->index['from'], $this->index['join'], $this->index['left_join']);
  538. }
  539. function getJoinInfos($id) {
  540. $r = array();
  541. $tbl_ids = $this->getOrderedJoinIDs();
  542. $pattern = $this->getPattern($id);
  543. foreach ($tbl_ids as $tbl_id) {
  544. $tbl_pattern = $this->getPattern($tbl_id);
  545. if ($tbl_id != $id) {
  546. foreach (array('s', 'p', 'o') as $tbl_term) {
  547. foreach (array('var', 'bnode', 'uri') as $term_type) {
  548. if ($tbl_pattern[$tbl_term . '_type'] == $term_type) {
  549. foreach (array('s', 'p', 'o') as $term) {
  550. if (($pattern[$term . '_type'] == $term_type) && ($tbl_pattern[$tbl_term] == $pattern[$term])) {
  551. $r[] = array('term' => $term, 'join_tbl' => $tbl_id, 'join_term' => $tbl_term);
  552. }
  553. }
  554. }
  555. }
  556. }
  557. }
  558. }
  559. return $r;
  560. }
  561. function getAllJoinsSQL() {
  562. $js = $this->getJoins();
  563. $ljs = $this->getLeftJoins();
  564. $entries = array_merge($js, $ljs);
  565. $id2code = array();
  566. foreach ($entries as $entry) {
  567. if (preg_match('/([^\s]+) ON (.*)/s', $entry, $m)) {
  568. $id2code[$m[1]] = $entry;
  569. }
  570. }
  571. $deps = array();
  572. foreach ($id2code as $id => $code) {
  573. $deps[$id]['rank'] = 0;
  574. foreach ($id2code as $other_id => $other_code) {
  575. $deps[$id]['rank'] += ($id != $other_id) && preg_match('/' . $other_id . '/', $code) ? 1 : 0;
  576. $deps[$id][$other_id] = ($id != $other_id) && preg_match('/' . $other_id . '/', $code) ? 1 : 0;
  577. }
  578. }
  579. $r = '';
  580. do {
  581. /* get next 0-rank */
  582. $next_id = 0;
  583. foreach ($deps as $id => $infos) {
  584. if ($infos['rank'] == 0) {
  585. $next_id = $id;
  586. break;
  587. }
  588. }
  589. if ($next_id) {
  590. $r .= "\n" . $id2code[$next_id];
  591. unset($deps[$next_id]);
  592. foreach ($deps as $id => $infos) {
  593. $deps[$id]['rank'] = 0;
  594. unset($deps[$id][$next_id]);
  595. foreach ($infos as $k => $v) {
  596. if (!in_array($k, array('rank', $next_id))) {
  597. $deps[$id]['rank'] += $v;
  598. $deps[$id][$k] = $v;
  599. }
  600. }
  601. }
  602. }
  603. }
  604. while ($next_id);
  605. if ($deps) {
  606. $this->addError('Not all patterns could be rewritten to SQL JOINs');
  607. }
  608. return $r;
  609. }
  610. function getJoins() {
  611. $r = array();
  612. $nl = "\n";
  613. foreach ($this->index['join'] as $id) {
  614. $sub_r = $this->getJoinConditionSQL($id);
  615. $r[] = 'JOIN ' . $this->getTripleTable($id) . ' T_' . $id . ' ON (' . $sub_r . $nl . ')';
  616. }
  617. foreach (array_merge($this->index['from'], $this->index['join']) as $id) {
  618. if ($sub_r = $this->getRequiredSubJoinSQL($id)) {
  619. $r[] = $sub_r;
  620. }
  621. }
  622. return $r;
  623. }
  624. function getLeftJoins() {
  625. $r = array();
  626. $nl = "\n";
  627. foreach ($this->index['left_join'] as $id) {
  628. $sub_r = $this->getJoinConditionSQL($id);
  629. $r[] = 'LEFT JOIN ' . $this->getTripleTable($id) . ' T_' . $id . ' ON (' . $sub_r . $nl . ')';
  630. }
  631. foreach ($this->index['left_join'] as $id) {
  632. if ($sub_r = $this->getRequiredSubJoinSQL($id, 'LEFT')) {
  633. $r[] = $sub_r;
  634. }
  635. }
  636. return $r;
  637. }
  638. function getJoinConditionSQL($id) {
  639. $r = '';
  640. $nl = "\n";
  641. $infos = $this->getJoinInfos($id);
  642. $pattern = $this->getPattern($id);
  643. $tbl = 'T_' . $id;
  644. /* core dependency */
  645. $d_tbls = $this->getDependentJoins($id);
  646. foreach ($d_tbls as $d_tbl) {
  647. if (preg_match('/^T_([0-9\_]+)\.[spo]+/', $d_tbl, $m) && ($m[1] != $id)) {
  648. if ($this->isJoinedBefore($m[1], $id) && !in_array($m[1], array_merge($this->index['from'], $this->index['join']))) {
  649. $r .= $r ? $nl . ' AND ' : $nl . ' ';
  650. $r .= '(' . $d_tbl . ' IS NOT NULL)';
  651. }
  652. $this->logDependency($id, $d_tbl);
  653. }
  654. }
  655. /* triple-based join info */
  656. foreach ($infos as $info) {
  657. if ($this->isJoinedBefore($info['join_tbl'], $id) && $this->joinDependsOn($id, $info['join_tbl'])) {
  658. $r .= $r ? $nl . ' AND ' : $nl . ' ';
  659. $r .= '(' . $tbl . '.' . $info['term'] . ' = T_' . $info['join_tbl'] . '.' . $info['join_term'] . ')';
  660. }
  661. }
  662. /* filters etc */
  663. if ($sub_r = $this->getPatternSQL($pattern, 'join__T_' . $id)) {
  664. $r .= $r ? $nl . ' AND ' . $sub_r : $nl . ' ' . '(' . $sub_r . ')';
  665. }
  666. return $r;
  667. }
  668. /**
  669. * A log of identified table join dependencies in getJoinConditionSQL
  670. *
  671. */
  672. function logDependency($id, $tbl) {
  673. if (!isset($this->dependency_log[$id])) $this->dependency_log[$id] = array();
  674. if (!in_array($tbl, $this->dependency_log[$id])) {
  675. $this->dependency_log[$id][] = $tbl;
  676. }
  677. }
  678. /**
  679. * checks whether entries in the dependecy log could perhaps be optimized
  680. * (triggers re-ordering of patterns
  681. */
  682. function problematicDependencies() {
  683. foreach ($this->dependency_log as $id => $tbls) {
  684. if (count($tbls) > 1) return count($tbls);
  685. }
  686. return 0;
  687. }
  688. function isJoinedBefore($tbl_1, $tbl_2) {
  689. $tbl_ids = $this->getOrderedJoinIDs();
  690. foreach ($tbl_ids as $id) {
  691. if ($id == $tbl_1) {
  692. return 1;
  693. }
  694. if ($id == $tbl_2) {
  695. return 0;
  696. }
  697. }
  698. }
  699. function joinDependsOn($id, $id2) {
  700. if (in_array($id2, array_merge($this->index['from'], $this->index['join']))) {
  701. return 1;
  702. }
  703. $d_tbls = $this->getDependentJoins($id2);
  704. //echo $id . ' :: ' . $id2 . '=>' . print_r($d_tbls, 1);
  705. foreach ($d_tbls as $d_tbl) {
  706. if (preg_match('/^T_' .$id. '\./', $d_tbl)) {
  707. return 1;
  708. }
  709. }
  710. return 0;
  711. }
  712. function getDependentJoins($id) {
  713. $r = array();
  714. /* sub joins */
  715. foreach ($this->index['sub_joins'] as $alias) {
  716. if (preg_match('/^(T|V|G)_' . $id . '/', $alias)) {
  717. $r[] = $alias;
  718. }
  719. }
  720. /* siblings in shared optional */
  721. $o_id = $this->getOptionalPattern($id);
  722. foreach ($this->index['sub_joins'] as $alias) {
  723. if (preg_match('/^(T|V|G)_' . $o_id . '/', $alias) && !in_array($alias, $r)) {
  724. $r[] = $alias;
  725. }
  726. }
  727. foreach ($this->index['left_join'] as $alias) {
  728. if (preg_match('/^' . $o_id . '/', $alias) && !in_array($alias, $r)) {
  729. $r[] = 'T_' . $alias . '.s';
  730. }
  731. }
  732. return $r;
  733. }
  734. /* */
  735. function getRequiredSubJoinSQL($id, $prefix = '') {/* id is a triple pattern id. Optional FILTERS and GRAPHs are getting added to the join directly */
  736. $nl = "\n";
  737. $r = '';
  738. foreach ($this->index['sub_joins'] as $alias) {
  739. if (preg_match('/^V_' . $id . '_([a-z\_]+)\.val$/', $alias, $m)) {
  740. $col = $m[1];
  741. $sub_r = '';
  742. if ($this->isOptionalPattern($id)) {
  743. $pattern = $this->getPattern($id);
  744. do {
  745. $pattern = $this->getPattern($pattern['parent_id']);
  746. } while ($pattern['parent_id'] && ($pattern['type'] != 'optional'));
  747. $sub_r = $this->getPatternSQL($pattern, 'sub_join__V_' . $id);
  748. }
  749. $sub_r = $sub_r ? $nl . ' AND (' . $sub_r . ')' : '';
  750. /* lang dt only on literals */
  751. if ($col == 'o_lang_dt') {
  752. $sub_sub_r = 'T_' . $id . '.o_type = 2';
  753. $sub_r .= $nl . ' AND (' . $sub_sub_r . ')';
  754. }
  755. //$cur_prefix = $prefix ? $prefix . ' ' : 'STRAIGHT_';
  756. $cur_prefix = $prefix ? $prefix . ' ' : '';
  757. if ($col == 'g') {
  758. $r .= trim($cur_prefix . 'JOIN '. $this->getValueTable($col) . ' V_' .$id . '_' . $col. ' ON (' .$nl. ' (G_' . $id . '.' . $col. ' = V_' . $id. '_' . $col. '.id) ' . $sub_r . $nl . ')');
  759. }
  760. else {
  761. $r .= trim($cur_prefix . 'JOIN '. $this->getValueTable($col) . ' V_' .$id . '_' . $col. ' ON (' .$nl. ' (T_' . $id . '.' . $col. ' = V_' . $id. '_' . $col. '.id) ' . $sub_r . $nl . ')');
  762. }
  763. }
  764. elseif (preg_match('/^G_' . $id . '\.g$/', $alias, $m)) {
  765. $pattern = $this->getPattern($id);
  766. $sub_r = $this->getPatternSQL($pattern, 'graph_sub_join__G_' . $id);
  767. $sub_r = $sub_r ? $nl . ' AND ' . $sub_r : '';
  768. /* dataset restrictions */
  769. $gi = $this->getGraphInfos($id);
  770. $sub_sub_r = '';
  771. $added_gts = array();
  772. foreach ($gi as $set) {
  773. if (isset($set['graph']) && !in_array($set['graph'], $added_gts)) {
  774. $sub_sub_r .= $sub_sub_r !== '' ? ',' : '';
  775. $sub_sub_r .= $this->getTermID($set['graph'], 'g');
  776. $added_gts[] = $set['graph'];
  777. }
  778. }
  779. $sub_r .= ($sub_sub_r !== '') ? $nl . ' AND (G_' . $id . '.g IN (' . $sub_sub_r . '))' : ''; // /* ' . str_replace('#' , '::', $set['graph']) . ' */';
  780. /* other graph join conditions */
  781. foreach ($this->index['graph_vars'] as $var => $occurs) {
  782. $occur_tbls = array();
  783. foreach ($occurs as $occur) {
  784. $occur_tbls[] = $occur['table'];
  785. if ($occur['table'] == $id) break;
  786. }
  787. foreach($occur_tbls as $tbl) {
  788. if (($tbl != $id) && in_array($id, $occur_tbls) && $this->isJoinedBefore($tbl, $id)) {
  789. $sub_r .= $nl . ' AND (G_' .$id. '.g = G_' .$tbl. '.g)';
  790. }
  791. }
  792. }
  793. //$cur_prefix = $prefix ? $prefix . ' ' : 'STRAIGHT_';
  794. $cur_prefix = $prefix ? $prefix . ' ' : '';
  795. $r .= trim($cur_prefix . 'JOIN '. $this->getGraphTable() . ' G_' .$id . ' ON (' .$nl. ' (T_' . $id . '.t = G_' .$id. '.t)' . $sub_r . $nl . ')');
  796. }
  797. }
  798. return $r;
  799. }
  800. /* */
  801. function getWHERESQL() {
  802. $r = '';
  803. $nl = "\n";
  804. /* standard constraints */
  805. $sub_r = $this->getPatternSQL($this->getPattern('0'), 'where');
  806. /* additional constraints */
  807. foreach ($this->index['from'] as $id) {
  808. if ($sub_sub_r = $this->getConstraintSQL($id)) {
  809. $sub_r .= $sub_r ? $nl . ' AND ' . $sub_sub_r : $sub_sub_r;
  810. }
  811. }
  812. $r .= $sub_r ? $sub_r : '';
  813. /* left join dependencies */
  814. foreach ($this->index['left_join'] as $id) {
  815. $d_joins = $this->getDependentJoins($id);
  816. $added = array();
  817. $d_aliases = array();
  818. //echo $id . ' =>' . print_r($d_joins, 1);
  819. $id_alias = 'T_' . $id . '.s';
  820. foreach ($d_joins as $alias) {
  821. if (preg_match('/^(T|V|G)_([0-9\_]+)(_[spo])?\.([a-z\_]+)/', $alias, $m)) {
  822. $tbl_type = $m[1];
  823. $tbl_pattern_id = $m[2];
  824. $suffix = $m[3];
  825. if (($tbl_pattern_id >= $id) && $this->sameOptional($tbl_pattern_id, $id)) {/* get rid of dependency permutations and nested optionals */
  826. if (!in_array($tbl_type . '_' . $tbl_pattern_id . $suffix, $added)) {
  827. $sub_r .= $sub_r ? ' AND ' : '';
  828. $sub_r .= $alias . ' IS NULL';
  829. $d_aliases[] = $alias;
  830. $added[] = $tbl_type . '_' . $tbl_pattern_id . $suffix;
  831. $id_alias = ($tbl_pattern_id == $id) ? $alias : $id_alias;
  832. }
  833. }
  834. }
  835. }
  836. if (count($d_aliases) > 2) {/* @@todo fix this! */
  837. $sub_r1 = ' /* '.$id_alias.' dependencies */';
  838. $sub_r2 = '((' . $id_alias . ' IS NULL) OR (CONCAT(' . join(', ', $d_aliases) . ') IS NOT NULL))';
  839. $r .= $r ? $nl . $sub_r1 . $nl . ' AND ' .$sub_r2 : $sub_r1 . $nl . $sub_r2;
  840. }
  841. }
  842. return $r ? $nl . 'WHERE ' . $r : '';
  843. }
  844. /* */
  845. function addConstraintSQLEntry($id, $sql) {
  846. if (!isset($this->index['constraints'][$id])) {
  847. $this->index['constraints'][$id] = array();
  848. }
  849. if (!in_array($sql, $this->index['constraints'][$id])) {
  850. $this->index['constraints'][$id][] = $sql;
  851. }
  852. }
  853. function getConstraintSQL($id) {
  854. $r = '';
  855. $nl = "\n";
  856. $constraints = $this->v($id, array(), $this->index['constraints']);
  857. foreach ($constraints as $constraint) {
  858. $r .= $r ? $nl . ' AND ' . $constraint : $constraint;
  859. }
  860. return $r;
  861. }
  862. /* */
  863. function getPatternSQL($pattern, $context) {
  864. $type = $pattern['type'];
  865. $m = 'get' . ucfirst($type) . 'PatternSQL';
  866. return method_exists($this, $m) ? $this->$m($pattern, $context) : $this->getDefaultPatternSQL($pattern, $context);
  867. }
  868. function getDefaultPatternSQL($pattern, $context) {
  869. $r = '';
  870. $nl = "\n";
  871. $sub_ids = $this->v('patterns', array(), $pattern);
  872. foreach ($sub_ids as $sub_id) {
  873. $sub_r = $this->getPatternSQL($this->getPattern($sub_id), $context);
  874. $r .= ($r && $sub_r) ? $nl . ' AND (' . $sub_r . ')' : ($sub_r ? $sub_r : '');
  875. }
  876. return $r ? $r : '';
  877. }
  878. function getTriplePatternSQL($pattern, $context) {
  879. $r = '';
  880. $nl = "\n";
  881. $id = $pattern['id'];
  882. /* s p o */
  883. $vars = array();
  884. foreach (array('s', 'p', 'o') as $term) {
  885. $sub_r = '';
  886. $type = $pattern[$term . '_type'];
  887. if ($type == 'uri') {
  888. $term_id = $this->getTermID($pattern[$term], $term);
  889. $sub_r = '(T_' . $id . '.' . $term . ' = ' . $term_id . ') /* ' . str_replace('#' , '::', $pattern[$term]) . ' */';
  890. }
  891. elseif ($type == 'literal') {
  892. $term_id = $this->getTermID($pattern[$term], $term);
  893. $sub_r = '(T_' . $id . '.' . $term . ' = ' . $term_id . ') /* ' . preg_replace('/[\#\n]/' , ' ', $pattern[$term]) . ' */';
  894. if (($lang_dt = $this->v1($term . '_lang', '', $pattern)) || ($lang_dt = $this->v1($term . '_datatype', '', $pattern))) {
  895. $lang_dt_id = $this->getTermID($lang_dt);
  896. $sub_r .= $nl . ' AND (T_' . $id . '.' .$term. '_lang_dt = ' . $lang_dt_id . ') /* ' . str_replace('#' , '::', $lang_dt) . ' */';
  897. }
  898. }
  899. elseif ($type == 'var') {
  900. $val = $pattern[$term];
  901. if (isset($vars[$val])) {/* repeated var in pattern */
  902. $sub_r = '(T_' . $id . '.' . $term . '=' . 'T_' . $id . '.' . $vars[$val] . ')';
  903. }
  904. $vars[$val] = $term;
  905. if ($infos = $this->v($val, 0, $this->index['graph_vars'])) {/* graph var in triple pattern */
  906. $sub_r .= $sub_r ? $nl . ' AND ' : '';
  907. $tbl = $infos[0]['table'];
  908. $sub_r .= 'G_' . $tbl . '.g = T_' . $id . '.' . $term;
  909. }
  910. }
  911. if ($sub_r) {
  912. if (preg_match('/^(join)/', $context) || (preg_match('/^where/', $context) && in_array($id, $this->index['from']))) {
  913. $r .= $r ? $nl . ' AND ' . $sub_r : $sub_r;
  914. }
  915. }
  916. }
  917. /* g */
  918. if ($infos = $pattern['graph_infos']) {
  919. $tbl_alias = 'G_' . $id . '.g';
  920. if (!in_array($tbl_alias, $this->index['sub_joins'])) {
  921. $this->index['sub_joins'][] = $tbl_alias;
  922. }
  923. $sub_r = array('graph_var' => '', 'graph_uri' => '', 'from' => '', 'from_named' => '');
  924. foreach ($infos as $info) {
  925. $type = $info['type'];
  926. if ($type == 'graph') {
  927. if ($info['uri']) {
  928. $term_id = $this->getTermID($info['uri'], 'g');
  929. $sub_r['graph_uri'] .= $sub_r['graph_uri'] ? $nl . ' AND ' : '';
  930. $sub_r['graph_uri'] .= '(' .$tbl_alias. ' = ' . $term_id . ') /* ' . str_replace('#' , '::', $info['uri']) . ' */';
  931. }
  932. }
  933. }
  934. if ($sub_r['from'] && $sub_r['from_named']) {
  935. $sub_r['from_named'] = '';
  936. }
  937. if (!$sub_r['from'] && !$sub_r['from_named']) {
  938. $sub_r['graph_var'] = '';
  939. }
  940. if (preg_match('/^(graph_sub_join)/', $context)) {
  941. foreach ($sub_r as $g_type => $g_sql) {
  942. if ($g_sql) {
  943. $r .= $r ? $nl . ' AND ' . $g_sql : $g_sql;
  944. }
  945. }
  946. }
  947. }
  948. /* optional sibling filters? */
  949. if (preg_match('/^(join|sub_join)/', $context) && $this->isOptionalPattern($id)) {
  950. $o_pattern = $pattern;
  951. do {
  952. $o_pattern = $this->getPattern($o_pattern['parent_id']);
  953. } while ($o_pattern['parent_id'] && ($o_pattern['type'] != 'optional'));
  954. if ($sub_r = $this->getPatternSQL($o_pattern, 'optional_filter' . preg_replace('/^(.*)(__.*)$/', '\\2', $context))) {
  955. $r .= $r ? $nl . ' AND ' . $sub_r : $sub_r;
  956. }
  957. /* created constraints */
  958. if ($sub_r = $this->getConstraintSQL($id)) {
  959. $r .= $r ? $nl . ' AND ' . $sub_r : $sub_r;
  960. }
  961. }
  962. /* result */
  963. if (preg_match('/^(where)/', $context) && $this->isOptionalPattern($id)) {
  964. return '';
  965. }
  966. return $r;
  967. }
  968. /* */
  969. function getFilterPatternSQL($pattern, $context) {
  970. $r = '';
  971. $id = $pattern['id'];
  972. $constraint_id = $this->v1('constraint', '', $pattern);
  973. $constraint = $this->getPattern($constraint_id);
  974. $constraint_type = $constraint['type'];
  975. if ($constraint_type == 'built_in_call') {
  976. $r = $this->getBuiltInCallSQL($constraint, $context);
  977. }
  978. elseif ($constraint_type == 'expression') {
  979. $r = $this->getExpressionSQL($constraint, $context, '', 'filter');
  980. }
  981. else {
  982. $m = 'get' . ucfirst($constraint_type) . 'ExpressionSQL';
  983. if (method_exists($this, $m)) {
  984. $r = $this->$m($constraint, $context, '', 'filter');
  985. }
  986. }
  987. if ($this->isOptionalPattern($id) && !preg_match('/^(join|optional_filter)/', $context)) {
  988. return '';
  989. }
  990. /* unconnected vars in FILTERs eval to false */
  991. if ($sub_r = $this->hasUnconnectedFilterVars($id)) {
  992. if ($sub_r == 'alias') {
  993. if (!in_array($r, $this->index['havings'])) $this->index['havings'][] = $r;
  994. return '';
  995. }
  996. elseif (preg_match('/^T([^\s]+\.)g (.*)$/s', $r, $m)) {/* graph filter */
  997. return 'G' . $m[1] . 't ' . $m[2];
  998. }
  999. elseif (preg_match('/^\(*V[^\s]+_g\.val .*$/s', $r, $m)) {/* graph value filter, @@improveMe */
  1000. //return $r;
  1001. }
  1002. else {
  1003. return 'FALSE';
  1004. }
  1005. }
  1006. /* some really ugly tweaks */
  1007. /* empty language filter: FILTER ( lang(?v) = '' ) */
  1008. $r = preg_replace('/\(\/\* language call \*\/ ([^\s]+) = ""\)/s', '((\\1 = "") OR (\\1 LIKE "%:%"))', $r);
  1009. return $r;
  1010. }
  1011. /* */
  1012. function hasUnconnectedFilterVars($filter_id) {
  1013. $pattern = $this->getInitialPattern($filter_id);
  1014. $gp = $this->getInitialPattern($pattern['parent_id']);
  1015. $vars = array();
  1016. foreach ($this->initial_index['patterns'] as $id => $p) {
  1017. /* vars in given filter */
  1018. if (preg_match('/^' .$filter_id. '.+/', $id)) {
  1019. if ($p['type'] == 'var') {
  1020. $vars[$p['value']][] = 'filter';
  1021. }
  1022. if (($p['type'] == 'built_in_call') && ($p['call'] == 'bound')) {
  1023. $vars[$p['args'][0]['value']][] = 'filter';
  1024. }
  1025. }
  1026. /* triple patterns if their scope is in the parent path of the filter */
  1027. if ($p['type'] == 'triple') {
  1028. $tp = $p;
  1029. do {
  1030. $proceed = 1;
  1031. $tp = $this->getInitialPattern($tp['parent_id']);
  1032. if ($tp['type'] == 'group') {
  1033. $proceed = 0;
  1034. if (isset($tp['parent_id']) && ($p_tp = $this->getInitialPattern($tp['parent_id'])) && ($p_tp['type'] == 'union')) {
  1035. $proceed = 1;
  1036. }
  1037. }
  1038. } while ($proceed);
  1039. $tp_id = $tp['id'];
  1040. $fp_id = $filter_id;
  1041. $ok = 0;
  1042. do {
  1043. $fp = $this->getInitialPattern($fp_id);
  1044. $fp_id = $fp['parent_id'];
  1045. if (($fp['type'] != 'group') && ($fp_id === $tp_id)) {
  1046. $ok = 1;
  1047. break;
  1048. }
  1049. } while (($fp['parent_id'] != $fp['id']) && ($fp['type'] != 'group'));
  1050. if ($ok) {
  1051. foreach (array('s', 'p', 'o') as $term) {
  1052. if ($p[$term . '_type'] == 'var') {
  1053. $vars[$p[$term]][] = 'triple';
  1054. }
  1055. }
  1056. }
  1057. }
  1058. }
  1059. foreach ($vars as $var => $types) {
  1060. if (!in_array('triple', $types)) {
  1061. /* might be an alias */
  1062. $r = 1;
  1063. foreach ($this->infos['query']['result_vars'] as $r_var) {
  1064. if ($r_var['alias'] == $var) {
  1065. $r = 'alias';
  1066. break;
  1067. }
  1068. //if ($r_var['alias'] == $var) $r = 0;
  1069. }
  1070. /* filter */
  1071. //if (in_array('filter', $types)) $r = 0;
  1072. if ($r) return $r;
  1073. }
  1074. }
  1075. return 0;
  1076. }
  1077. /* */
  1078. function getExpressionSQL($pattern, $context, $val_type = '', $parent_type = '') {
  1079. $r = '';
  1080. $nl = "\n";
  1081. $type = $this->v1('type', '', $pattern);
  1082. $sub_type = $this->v1('sub_type', $type, $pattern);
  1083. if (preg_match('/^(and|or)$/', $sub_type)) {
  1084. foreach ($pattern['patterns'] as $sub_id) {
  1085. $sub_pattern = $this->getPattern($sub_id);
  1086. $sub_pattern_type = $sub_pattern['type'];
  1087. if ($sub_pattern_type == 'built_in_call') {
  1088. $sub_r = $this->getBuiltInCallSQL($sub_pattern, $context, '', $parent_type);
  1089. }
  1090. else {
  1091. $sub_r = $this->getExpressionSQL($sub_pattern, $context, '', $parent_type);
  1092. }
  1093. if ($sub_r) {
  1094. $r .= $r ? ' ' . strtoupper($sub_type). ' (' .$sub_r. ')' : '(' . $sub_r . ')';
  1095. }
  1096. }
  1097. }
  1098. elseif ($sub_type == 'built_in_call') {
  1099. $r = $this->getBuiltInCallSQL($pattern, $context, $val_type, $parent_type);
  1100. }
  1101. elseif (preg_match('/literal/', $sub_type)) {
  1102. $r = $this->getLiteralExpressionSQL($pattern, $context, $val_type, $parent_type);
  1103. }
  1104. elseif ($sub_type) {
  1105. $m = 'get' . ucfirst($sub_type) . 'ExpressionSQL';
  1106. if (method_exists($this, $m)) {
  1107. $r = $this->$m($pattern, $context, '', $parent_type);
  1108. }
  1109. }
  1110. /* skip expressions that reference non-yet-joined tables */
  1111. if (preg_match('/__(T|V|G)_(.+)$/', $context, $m)) {
  1112. $context_pattern_id = $m[2];
  1113. $context_table_type = $m[1];
  1114. if (preg_match_all('/((T|V|G)(\_[0-9])+)/', $r, $m)) {
  1115. $aliases = $m[1];
  1116. $keep = 1;
  1117. foreach ($aliases as $alias) {
  1118. if (preg_match('/(T|V|G)_(.*)$/', $alias, $m)) {
  1119. $tbl_type = $m[1];
  1120. $tbl = $m[2];
  1121. if (!$this->isJoinedBefore($tbl, $context_pattern_id)) {
  1122. $keep = 0;
  1123. }
  1124. elseif (($context_pattern_id == $tbl) && preg_match('/(TV)/', $context_table_type . $tbl_type)) {
  1125. $keep = 0;
  1126. }
  1127. }
  1128. }
  1129. $r = $keep ? $r : '';
  1130. }
  1131. }
  1132. return $r ? '(' . $r . ')' : $r;
  1133. }
  1134. function detectExpressionValueType($pattern_ids) {
  1135. foreach ($pattern_ids as $id) {
  1136. $pattern = $this->getPattern($id);
  1137. $type = $this->v('type', '', $pattern);
  1138. if (($type == 'literal') && isset($pattern['datatype'])) {
  1139. if (in_array($pattern['datatype'], array($this->xsd . 'integer', $this->xsd . 'float', $this->xsd . 'double'))) {
  1140. return 'numeric';
  1141. }
  1142. }
  1143. }
  1144. return '';
  1145. }
  1146. /* */
  1147. function getRelationalExpressionSQL($pattern, $context, $val_type = '', $parent_type = '') {
  1148. $r = '';
  1149. $val_type = $this->detectExpressionValueType($pattern['patterns']);
  1150. $op = $pattern['operator'];
  1151. foreach ($pattern['patterns'] as $sub_id) {
  1152. $sub_pattern = $this->getPattern($sub_id);
  1153. $sub_pattern['parent_op'] = $op;
  1154. $sub_type = $sub_pattern['type'];
  1155. $m = ($sub_type == 'built_in_call') ? 'getBuiltInCallSQL' : 'get' . ucfirst($sub_type) . 'ExpressionSQL';
  1156. $m = str_replace('ExpressionExpression', 'Expression', $m);
  1157. $sub_r = method_exists($this, $m) ? $this->$m($sub_pattern, $context, $val_type, 'relational') : '';
  1158. $r .= $r ? ' ' . $op . ' ' . $sub_r : $sub_r;
  1159. }
  1160. return $r ? '(' . $r . ')' : $r;
  1161. }
  1162. function getAdditiveExpressionSQL($pattern, $context, $val_type = '', $parent_type = '') {
  1163. $r = '';
  1164. $val_type = $this->detectExpressionValueType($pattern['patterns']);
  1165. foreach ($pattern['patterns'] as $sub_id) {
  1166. $sub_pattern = $this->getPattern($sub_id);
  1167. $sub_type = $this->v('type', '', $sub_pattern);
  1168. $m = ($sub_type == 'built_in_call') ? 'getBuiltInCallSQL' : 'get' . ucfirst($sub_type) . 'ExpressionSQL';
  1169. $m = str_replace('ExpressionExpression', 'Expression', $m);
  1170. $sub_r = method_exists($this, $m) ? $this->$m($sub_pattern, $context, $val_type, 'additive') : '';
  1171. $r .= $r ? ' ' . $sub_r : $sub_r;
  1172. }
  1173. return $r;
  1174. }
  1175. function getMultiplicativeExpressionSQL($pattern, $context, $val_type = '', $parent_type = '') {
  1176. $r = '';
  1177. $val_type = $this->detectExpressionValueType($pattern['patterns']);
  1178. foreach ($pattern['patterns'] as $sub_id) {
  1179. $sub_pattern = $this->getPattern($sub_id);
  1180. $sub_type = $sub_pattern['type'];
  1181. $m = ($sub_type == 'built_in_call') ? 'getBuiltInCallSQL' : 'get' . ucfirst($sub_type) . 'ExpressionSQL';
  1182. $m = str_replace('ExpressionExpression', 'Expression', $m);
  1183. $sub_r = method_exists($this, $m) ? $this->$m($sub_pattern, $context, $val_type, 'multiplicative') : '';
  1184. $r .= $r ? ' ' . $sub_r : $sub_r;
  1185. }
  1186. return $r;
  1187. }
  1188. /* */
  1189. function getVarExpressionSQL($pattern, $context, $val_type = '', $parent_type = '') {
  1190. $var = $pattern['value'];
  1191. $info = $this->getVarTableInfos($var);
  1192. if (!$tbl = $info['table']) {
  1193. /* might be an aggregate var */
  1194. $vars = $this->infos['query']['result_vars'];
  1195. foreach ($vars as $test_var) {
  1196. if ($test_var['alias'] == $pattern['value']) {
  1197. return '`' . $pattern['value'] . '`';
  1198. }
  1199. }
  1200. return '';
  1201. }
  1202. $col = $info['col'];
  1203. if (($context == 'order') && ($col == 'o')) {
  1204. $tbl_alias = 'T_' . $tbl . '.o_comp';
  1205. }
  1206. elseif ($context == 'sameterm') {
  1207. $tbl_alias = 'T_' . $tbl . '.' . $col;
  1208. }
  1209. elseif (($parent_type == 'relational') && ($col == 'o') && (preg_match('/[\<\>]/', $this->v('parent_op', '', $pattern)))) {
  1210. $tbl_alias = 'T_' . $tbl . '.o_comp';
  1211. }
  1212. else {
  1213. $tbl_alias = 'V_' . $tbl . '_' . $col . '.val';
  1214. if (!in_array($tbl_alias, $this->index['sub_joins'])) {
  1215. $this->index['sub_joins'][] = $tbl_alias;
  1216. }
  1217. }
  1218. $op = $this->v('operator', '', $pattern);
  1219. if (preg_match('/^(filter|and)/', $parent_type)) {
  1220. if ($op == '!') {
  1221. $r = '(((' . $tbl_alias . ' = 0) AND (CONCAT("1", ' . $tbl_alias . ') != 1))'; /* 0 and no string */
  1222. $r .= ' OR (' . $tbl_alias . ' IN ("", "false")))'; /* or "", or "false" */
  1223. }
  1224. else {
  1225. $r = '((' . $tbl_alias . ' != 0)'; /* not null */
  1226. $r .= ' OR ((CONCAT("1", ' . $tbl_alias . ') = 1) AND (' . $tbl_alias . ' NOT IN ("", "false"))))'; /* string, and not "" or "false" */
  1227. }
  1228. }
  1229. else {
  1230. $r = trim($op . ' ' . $tbl_alias);
  1231. if ($val_type == 'numeric') {
  1232. if (preg_match('/__(T|V|G)_(.+)$/', $context, $m)) {
  1233. $context_pattern_id = $m[2];
  1234. $context_table_type = $m[1];
  1235. }
  1236. else {
  1237. $context_pattern_id = $pattern['id'];
  1238. $context_table_type = 'T';
  1239. }
  1240. if ($this->isJoinedBefore($tbl, $context_pattern_id)) {
  1241. $add = ($tbl != $context_pattern_id) ? 1 : 0;
  1242. $add = (!$add && ($context_table_type == 'V')) ? 1 : 0;
  1243. if ($add) {
  1244. $this->addConstraintSQLEntry($context_pattern_id, '(' .$r. ' = "0" OR ' . $r . '*1.0 != 0)');
  1245. }
  1246. }
  1247. }
  1248. }
  1249. return $r;
  1250. }
  1251. /* */
  1252. function getUriExpressionSQL($pattern, $context, $val_type = '') {
  1253. $val = $pattern['uri'];
  1254. $r = $pattern['operator'];
  1255. $r .= is_numeric($val) ? ' ' . $val : ' "' . mysql_real_escape_string($val, $this->store->getDBCon()) . '"';
  1256. return $r;
  1257. }
  1258. /* */
  1259. function getLiteralExpressionSQL($pattern, $context, $val_type = '', $parent_type = '') {
  1260. $val = $pattern['value'];
  1261. $r = $pattern['operator'];
  1262. if (is_numeric($val) && $this->v('datatype', 0, $pattern)) {
  1263. $r .= ' ' . $val;
  1264. }
  1265. elseif (preg_match('/^(true|false)$/i', $val) && ($this->v1('datatype', '', $pattern) == 'http://www.w3.org/2001/XMLSchema#boolean')) {
  1266. $r .= ' ' . strtoupper($val);
  1267. }
  1268. elseif ($parent_type == 'regex') {
  1269. $sub_r = mysql_real_escape_string($val, $this->store->getDBCon());
  1270. $r .= ' "' . preg_replace('/\x5c\x5c/', '\\', $sub_r) . '"';
  1271. }
  1272. else {
  1273. $r .= ' "' . mysql_real_escape_string($val, $this->store->getDBCon()) . '"';
  1274. }
  1275. if (($lang_dt = $this->v1('lang', '', $pattern)) || ($lang_dt = $this->v1('datatype', '', $pattern))) {
  1276. /* try table/alias via var in siblings */
  1277. if ($var = $this->findSiblingVarExpression($pattern['id'])) {
  1278. if (isset($this->index['vars'][$var])) {
  1279. $infos = $this->index['vars'][$var];
  1280. foreach ($infos as $info) {
  1281. if ($info['col'] == 'o') {
  1282. $tbl = $info['table'];
  1283. $term_id = $this->getTermID($lang_dt);
  1284. if ($pattern['operator'] != '!=') {
  1285. if (preg_match('/__(T|V|G)_(.+)$/', $context, $m)) {
  1286. $context_pattern_id = $m[2];
  1287. $context_table_type = $m[1];
  1288. }
  1289. elseif ($context == 'where') {
  1290. $context_pattern_id = $tbl;
  1291. }
  1292. else {
  1293. $context_pattern_id = $pattern['id'];
  1294. }
  1295. if ($tbl == $context_pattern_id) {/* @todo better dependency check */
  1296. if ($term_id || ($lang_dt != 'http://www.w3.org/2001/XMLSchema#integer')) {/* skip if simple int, but no id */
  1297. $this->addConstraintSQLEntry($context_pattern_id, 'T_' . $tbl . '.o_lang_dt = ' . $term_id . ' /* ' . str_replace('#' , '::', $lang_dt) . ' */');
  1298. }
  1299. }
  1300. }
  1301. break;
  1302. }
  1303. }
  1304. }
  1305. }
  1306. }
  1307. return trim($r);
  1308. }
  1309. function findSiblingVarExpression($id) {
  1310. $pattern = $this->getPattern($id);
  1311. do {
  1312. $pattern = $this->getPattern($pattern['parent_id']);
  1313. } while ($pattern['parent_id'] && ($pattern['type'] != 'expression'));
  1314. $sub_patterns = $this->v('patterns', array(), $pattern);
  1315. foreach ($sub_patterns as $sub_id) {
  1316. $sub_pattern = $this->getPattern($sub_id);
  1317. if ($sub_pattern['type'] == 'var') {
  1318. return $sub_pattern['value'];
  1319. }
  1320. }
  1321. return '';
  1322. }
  1323. /* */
  1324. function getFunctionExpressionSQL($pattern, $context, $val_type = '', $parent_type = '') {
  1325. $fnc_uri = $pattern['uri'];
  1326. $op = $this->v('operator', '', $pattern);
  1327. if ($op) $op .= ' ';
  1328. if ($this->allow_extension_functions) {
  1329. /* mysql functions */
  1330. if (preg_match('/^http\:\/\/web\-semantics\.org\/ns\/mysql\/(.*)$/', $fnc_uri, $m)) {
  1331. $fnc_name = strtoupper($m[1]);
  1332. $sub_r = '';
  1333. foreach ($pattern['args'] as $arg) {
  1334. $sub_r .= $sub_r ? ', ' : '';
  1335. $sub_r

Large files files are truncated, but you can click here to view the full file