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

/s3db3.5.10/pearlib/arc/store/ARC2_StoreSelectQueryHandler.php

https://code.google.com/p/s3db/
PHP | 1592 lines | 1427 code | 84 blank | 81 comment | 296 complexity | 1486533d96ed67e7ba7613a998572a97 MD5 | raw file

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

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

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