PageRenderTime 69ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/common/arc2/store/ARC2_Store.php

https://github.com/tematres/TemaTres-Vocabulary-Server
PHP | 723 lines | 600 code | 78 blank | 45 comment | 132 complexity | f05aec8f03f6218c2ab2dd99bc733c0e MD5 | raw file
Possible License(s): LGPL-2.1, MIT, CC-BY-3.0
  1. <?php
  2. /**
  3. * ARC2 RDF Store
  4. *
  5. * @author Benjamin Nowack <bnowack@semsol.com>
  6. * @license http://arc.semsol.org/license
  7. * @homepage <http://arc.semsol.org/>
  8. * @package ARC2
  9. */
  10. ARC2::inc('Class');
  11. class ARC2_Store extends ARC2_Class {
  12. function __construct($a, &$caller) {
  13. parent::__construct($a, $caller);
  14. }
  15. function __init() {/* db_con */
  16. parent::__init();
  17. $this->table_lock = 0;
  18. $this->triggers = $this->v('store_triggers', array(), $this->a);
  19. $this->queue_queries = $this->v('store_queue_queries', 0, $this->a);
  20. $this->is_win = (strtolower(substr(PHP_OS, 0, 3)) == 'win') ? true : false;
  21. $this->max_split_tables = $this->v('store_max_split_tables', 10, $this->a);
  22. $this->split_predicates = $this->v('store_split_predicates', array(), $this->a);
  23. }
  24. /* */
  25. function getName() {
  26. return $this->v('store_name', 'arc', $this->a);
  27. }
  28. function getTablePrefix() {
  29. if (!isset($this->tbl_prefix)) {
  30. $r = $this->v('db_table_prefix', '', $this->a);
  31. $r .= $r ? '_' : '';
  32. $r .= $this->getName() . '_';
  33. $this->tbl_prefix = $r;
  34. }
  35. return $this->tbl_prefix;;
  36. }
  37. /* */
  38. function createDBCon() {
  39. foreach (array('db_host' => 'localhost', 'db_user' => '', 'db_pwd' => '', 'db_name' => '') as $k => $v) {
  40. $this->a[$k] = $this->v($k, $v, $this->a);
  41. }
  42. if (!$db_con = mysqli_connect($this->a['db_host'], $this->a['db_user'], $this->a['db_pwd'])) {
  43. return $this->addError(mysqli_error($db_con));
  44. }
  45. $this->a['db_con'] = $db_con;
  46. if (!mysqli_query( $db_con, "USE " . $this->a['db_name'])) {
  47. $fixed = 0;
  48. /* try to create it */
  49. if ($this->a['db_name']) {
  50. $this->queryDB("
  51. CREATE DATABASE IF NOT EXISTS " . $this->a['db_name'] . "
  52. DEFAULT CHARACTER SET utf8
  53. DEFAULT COLLATE utf8_general_ci
  54. ", $db_con, 1
  55. );
  56. if (mysqli_query( $db_con, "USE " . $this->a['db_name'])) {
  57. $this->queryDB("SET NAMES 'utf8'", $db_con);
  58. $fixed = 1;
  59. }
  60. }
  61. if (!$fixed) {
  62. return $this->addError(mysqli_error($db_con));
  63. }
  64. }
  65. if (preg_match('/^utf8/', $this->getCollation())) {
  66. $this->queryDB("SET NAMES 'utf8'", $db_con);
  67. }
  68. // This is RDF, we may need many JOINs...
  69. $this->queryDB("SET SESSION SQL_BIG_SELECTS=1", $db_con);
  70. return true;
  71. }
  72. function getDBCon($force = 0) {
  73. if ($force || !isset($this->a['db_con'])) {
  74. if (!$this->createDBCon()) {
  75. return false;
  76. }
  77. }
  78. if (!$force && !@mysqli_thread_id($this->a['db_con'])) return $this->getDBCon(1);
  79. return $this->a['db_con'];
  80. }
  81. function closeDBCon() {
  82. if ($this->v('db_con', false, $this->a)) {
  83. @mysqli_close($this->a['db_con']);
  84. }
  85. unset($this->a['db_con']);
  86. }
  87. function getDBVersion() {
  88. if (!$this->v('db_version')) {
  89. $this->db_version = preg_match("/^([0-9]+)\.([0-9]+)\.([0-9]+)/", mysqli_get_server_info($this->getDBCon()), $m) ? sprintf("%02d-%02d-%02d", $m[1], $m[2], $m[3]) : '00-00-00';
  90. }
  91. return $this->db_version;
  92. }
  93. /* */
  94. function getCollation() {
  95. $rs = $this->queryDB('SHOW TABLE STATUS LIKE "' . $this->getTablePrefix(). 'setting"', $this->getDBCon());
  96. return ($rs && ($row = mysqli_fetch_array($rs)) && isset($row['Collation'])) ? $row['Collation'] : '';
  97. }
  98. function getColumnType() {
  99. if (!$this->v('column_type')) {
  100. $tbl = $this->getTablePrefix() . 'g2t';
  101. $rs = $this->queryDB('SHOW COLUMNS FROM ' . $tbl . ' LIKE "t"', $this->getDBCon());
  102. $row = $rs ? mysqli_fetch_array($rs) : array('Type' => 'mediumint');
  103. $this->column_type = preg_match('/mediumint/', $row['Type']) ? 'mediumint' : 'int';
  104. }
  105. return $this->column_type;
  106. }
  107. /* */
  108. function hasHashColumn($tbl) {
  109. $var_name = 'has_hash_column_' . $tbl;
  110. if (!isset($this->$var_name)) {
  111. $tbl = $this->getTablePrefix() . $tbl;
  112. $rs = $this->queryDB('SHOW COLUMNS FROM ' . $tbl . ' LIKE "val_hash"', $this->getDBCon());
  113. $this->$var_name = ($rs && mysqli_fetch_array($rs));
  114. }
  115. return $this->$var_name;
  116. }
  117. /* */
  118. function hasFulltextIndex() {
  119. if (!isset($this->has_fulltext_index)) {
  120. $this->has_fulltext_index = 0;
  121. $tbl = $this->getTablePrefix() . 'o2val';
  122. $rs = $this->queryDB('SHOW INDEX FROM ' . $tbl, $this->getDBCon());
  123. while ($row = mysqli_fetch_array($rs)) {
  124. if ($row['Column_name'] != 'val') continue;
  125. if ($row['Index_type'] != 'FULLTEXT') continue;
  126. $this->has_fulltext_index = 1;
  127. break;
  128. }
  129. }
  130. return $this->has_fulltext_index;
  131. }
  132. function enableFulltextSearch() {
  133. if ($this->hasFulltextIndex()) return 1;
  134. $tbl = $this->getTablePrefix() . 'o2val';
  135. $this->queryDB('CREATE FULLTEXT INDEX vft ON ' . $tbl . '(val(128))', $this->getDBCon(), 1);
  136. }
  137. function disableFulltextSearch() {
  138. if (!$this->hasFulltextIndex()) return 1;
  139. $tbl = $this->getTablePrefix() . 'o2val';
  140. $this->queryDB('DROP INDEX vft ON ' . $tbl, $this->getDBCon());
  141. }
  142. /* */
  143. function countDBProcesses() {
  144. return ($rs = $this->queryDB('SHOW PROCESSLIST', $this->getDBCon())) ? mysqli_num_rows($rs) : 0;
  145. }
  146. function killDBProcesses($needle = '', $runtime = 30) {
  147. $dbcon = $this->getDBCon();
  148. /* make sure needle is sql */
  149. if (preg_match('/\?.+ WHERE/i', $needle, $m)) {
  150. $needle = $this->query($needle, 'sql');
  151. }
  152. $rs = $this->queryDB('SHOW FULL PROCESSLIST', $dbcon);
  153. $ref_tbl = $this->getTablePrefix() . 'triple';
  154. while ($row = mysqli_fetch_array($rs)) {
  155. if ($row['Time'] < $runtime) continue;
  156. if (!preg_match('/^\s*(INSERT|SELECT) /s', $row['Info'])) continue; /* only basic queries */
  157. if (!strpos($row['Info'], $ref_tbl . ' ')) continue; /* only from this store */
  158. $kill = 0;
  159. if ($needle && (strpos($row['Info'], $needle) !== false)) $kill = 1;
  160. if (!$needle) $kill = 1;
  161. if (!$kill) continue;
  162. $this->queryDB('KILL ' . $row['Id'], $dbcon);
  163. }
  164. }
  165. /* */
  166. function getTables() {
  167. return array('triple', 'g2t', 'id2val', 's2val', 'o2val', 'setting');
  168. }
  169. /* */
  170. function isSetUp() {
  171. if (($con = $this->getDBCon())) {
  172. $tbl = $this->getTablePrefix() . 'setting';
  173. return $this->queryDB("SELECT 1 FROM " . $tbl . " LIMIT 0", $con) ? 1 : 0;
  174. }
  175. }
  176. function setUp($force = 0) {
  177. if (($force || !$this->isSetUp()) && ($con = $this->getDBCon())) {
  178. if ($this->getDBVersion() < '04-00-04') {
  179. /* UPDATE + JOINs */
  180. return $this->addError('MySQL version not supported. ARC requires version 4.0.4 or higher.');
  181. }
  182. ARC2::inc('StoreTableManager');
  183. $mgr = new ARC2_StoreTableManager($this->a, $this);
  184. $mgr->createTables();
  185. }
  186. }
  187. function extendColumns() {
  188. ARC2::inc('StoreTableManager');
  189. $mgr = new ARC2_StoreTableManager($this->a, $this);
  190. $mgr->extendColumns();
  191. $this->column_type = 'int';
  192. }
  193. function splitTables() {
  194. ARC2::inc('StoreTableManager');
  195. $mgr = new ARC2_StoreTableManager($this->a, $this);
  196. $mgr->splitTables();
  197. }
  198. /* */
  199. function hasSetting($k) {
  200. $tbl = $this->getTablePrefix() . 'setting';
  201. $sql = "SELECT val FROM " . $tbl . " WHERE k = '" .md5($k). "'";
  202. $rs = $this->queryDB($sql, $this->getDBCon());
  203. return ($rs && ($row = mysqli_fetch_array($rs))) ? 1 : 0;
  204. }
  205. function getSetting($k, $default = 0) {
  206. $tbl = $this->getTablePrefix() . 'setting';
  207. $sql = "SELECT val FROM " . $tbl . " WHERE k = '" .md5($k). "'";
  208. $rs = $this->queryDB($sql, $this->getDBCon());
  209. if ($rs && ($row = mysqli_fetch_array($rs))) {
  210. return unserialize($row['val']);
  211. }
  212. return $default;
  213. }
  214. function setSetting($k, $v) {
  215. $con = $this->getDBCon();
  216. $tbl = $this->getTablePrefix() . 'setting';
  217. if ($this->hasSetting($k)) {
  218. $sql = "UPDATE " .$tbl . " SET val = '" . mysqli_real_escape_string( $con, serialize($v)) . "' WHERE k = '" . md5($k) . "'";
  219. }
  220. else {
  221. $sql = "INSERT INTO " . $tbl . " (k, val) VALUES ('" . md5($k) . "', '" . mysqli_real_escape_string( $con, serialize($v)) . "')";
  222. }
  223. return $this->queryDB($sql, $con);
  224. }
  225. function removeSetting($k) {
  226. $tbl = $this->getTablePrefix() . 'setting';
  227. return $this->queryDB("DELETE FROM " . $tbl . " WHERE k = '" . md5($k) . "'", $this->getDBCon());
  228. }
  229. function getQueueTicket() {
  230. if (!$this->queue_queries) return 1;
  231. $t = 'ticket_' . substr(md5(uniqid(rand())), 0, 10);
  232. $con = $this->getDBCon();
  233. /* lock */
  234. $rs = $this->queryDB('LOCK TABLES ' . $this->getTablePrefix() . 'setting WRITE', $con);
  235. /* queue */
  236. $queue = $this->getSetting('query_queue', array());
  237. $queue[] = $t;
  238. $this->setSetting('query_queue', $queue);
  239. $this->queryDB('UNLOCK TABLES', $con);
  240. /* loop */
  241. $lc = 0;
  242. $queue = $this->getSetting('query_queue', array());
  243. while ($queue && ($queue[0] != $t) && ($lc < 30)) {
  244. if ($this->is_win) {
  245. sleep(1);
  246. $lc++;
  247. }
  248. else {
  249. usleep(100000);
  250. $lc += 0.1;
  251. }
  252. $queue = $this->getSetting('query_queue', array());
  253. }
  254. return ($lc < 30) ? $t : 0;
  255. }
  256. function removeQueueTicket($t) {
  257. if (!$this->queue_queries) return 1;
  258. $con = $this->getDBCon();
  259. /* lock */
  260. $this->queryDB('LOCK TABLES ' . $this->getTablePrefix() . 'setting WRITE', $con);
  261. /* queue */
  262. $vals = $this->getSetting('query_queue', array());
  263. $pos = array_search($t, $vals);
  264. $queue = ($pos < (count($vals) - 1)) ? array_slice($vals, $pos + 1) : array();
  265. $this->setSetting('query_queue', $queue);
  266. $this->queryDB('UNLOCK TABLES', $con);
  267. }
  268. /* */
  269. function reset($keep_settings = 0) {
  270. $con = $this->getDBCon();
  271. $tbls = $this->getTables();
  272. $prefix = $this->getTablePrefix();
  273. /* remove split tables */
  274. $ps = $this->getSetting('split_predicates', array());
  275. foreach ($ps as $p) {
  276. $tbl = 'triple_' . abs(crc32($p));
  277. $this->queryDB('DROP TABLE ' . $prefix . $tbl, $con);
  278. }
  279. $this->removeSetting('split_predicates');
  280. /* truncate tables */
  281. foreach ($tbls as $tbl) {
  282. if ($keep_settings && ($tbl == 'setting')) {
  283. continue;
  284. }
  285. $this->queryDB('TRUNCATE ' . $prefix . $tbl, $con);
  286. }
  287. }
  288. function drop() {
  289. $con = $this->getDBCon();
  290. $tbls = $this->getTables();
  291. $prefix = $this->getTablePrefix();
  292. foreach ($tbls as $tbl) {
  293. $this->queryDB('DROP TABLE ' . $prefix . $tbl, $con);
  294. }
  295. }
  296. function insert($doc, $g, $keep_bnode_ids = 0) {
  297. $doc = is_array($doc) ? $this->toTurtle($doc) : $doc;
  298. $infos = array('query' => array('url' => $g, 'target_graph' => $g));
  299. ARC2::inc('StoreLoadQueryHandler');
  300. $h = new ARC2_StoreLoadQueryHandler($this->a, $this);
  301. $r = $h->runQuery($infos, $doc, $keep_bnode_ids);
  302. $this->processTriggers('insert', $infos);
  303. return $r;
  304. }
  305. function delete($doc, $g) {
  306. if (!$doc) {
  307. $infos = array('query' => array('target_graphs' => array($g)));
  308. ARC2::inc('StoreDeleteQueryHandler');
  309. $h = new ARC2_StoreDeleteQueryHandler($this->a, $this);
  310. $r = $h->runQuery($infos);
  311. $this->processTriggers('delete', $infos);
  312. return $r;
  313. }
  314. }
  315. function replace($doc, $g, $doc_2) {
  316. return array($this->delete($doc, $g), $this->insert($doc_2, $g));
  317. }
  318. function dump() {
  319. ARC2::inc('StoreDumper');
  320. $d = new ARC2_StoreDumper($this->a, $this);
  321. $d->dumpSPOG();
  322. }
  323. function createBackup($path, $q = '') {
  324. ARC2::inc('StoreDumper');
  325. $d = new ARC2_StoreDumper($this->a, $this);
  326. $d->saveSPOG($path, $q);
  327. }
  328. function renameTo($name) {
  329. $con = $this->getDBCon();
  330. $tbls = $this->getTables();
  331. $old_prefix = $this->getTablePrefix();
  332. $new_prefix = $this->v('db_table_prefix', '', $this->a);
  333. $new_prefix .= $new_prefix ? '_' : '';
  334. $new_prefix .= $name . '_';
  335. foreach ($tbls as $tbl) {
  336. $rs = $this->queryDB('RENAME TABLE ' . $old_prefix . $tbl .' TO ' . $new_prefix . $tbl, $con);
  337. $err = mysqli_error($con);
  338. if (!empty($err)) {
  339. return $this->addError($err);
  340. }
  341. }
  342. $this->a['store_name'] = $name;
  343. unset($this->tbl_prefix);
  344. }
  345. function replicateTo($name) {
  346. $conf = array_merge($this->a, array('store_name' => $name));
  347. $new_store = ARC2::getStore($conf);
  348. $new_store->setUp();
  349. $new_store->reset();
  350. $con = $this->getDBCon();
  351. $tbls = $this->getTables();
  352. $old_prefix = $this->getTablePrefix();
  353. $new_prefix = $new_store->getTablePrefix();
  354. foreach ($tbls as $tbl) {
  355. $rs = $this->queryDB('INSERT IGNORE INTO ' . $new_prefix . $tbl .' SELECT * FROM ' . $old_prefix . $tbl, $con);
  356. $err = mysqli_error($con);
  357. if (!empty($err)) {
  358. return $this->addError($err);
  359. }
  360. }
  361. return $new_store->query('SELECT COUNT(*) AS t_count WHERE { ?s ?p ?o}', 'row');
  362. }
  363. /* */
  364. function query($q, $result_format = '', $src = '', $keep_bnode_ids = 0, $log_query = 0) {
  365. if ($log_query) $this->logQuery($q);
  366. $con = $this->getDBCon();
  367. if (preg_match('/^dump/i', $q)) {
  368. $infos = array('query' => array('type' => 'dump'));
  369. }
  370. else {
  371. ARC2::inc('SPARQLPlusParser');
  372. $p = new ARC2_SPARQLPlusParser($this->a, $this);
  373. $p->parse($q, $src);
  374. $infos = $p->getQueryInfos();
  375. }
  376. if ($result_format == 'infos') return $infos;
  377. $infos['result_format'] = $result_format;
  378. if (!isset($p) || !$p->getErrors()) {
  379. $qt = $infos['query']['type'];
  380. if (!in_array($qt, array('select', 'ask', 'describe', 'construct', 'load', 'insert', 'delete', 'dump'))) {
  381. return $this->addError('Unsupported query type "'.$qt.'"');
  382. }
  383. $t1 = ARC2::mtime();
  384. $r = array('query_type' => $qt, 'result' => $this->runQuery($infos, $qt, $keep_bnode_ids, $q));
  385. $t2 = ARC2::mtime();
  386. $r['query_time'] = $t2 - $t1;
  387. /* query result */
  388. if ($result_format == 'raw') {
  389. return $r['result'];
  390. }
  391. if ($result_format == 'rows') {
  392. return $r['result']['rows'] ? $r['result']['rows'] : array();
  393. }
  394. if ($result_format == 'row') {
  395. return $r['result']['rows'] ? $r['result']['rows'][0] : array();
  396. }
  397. return $r;
  398. }
  399. return 0;
  400. }
  401. function runQuery($infos, $type, $keep_bnode_ids = 0, $q = '') {
  402. ARC2::inc('Store' . ucfirst($type) . 'QueryHandler');
  403. $cls = 'ARC2_Store' . ucfirst($type) . 'QueryHandler';
  404. $h = new $cls($this->a, $this);
  405. $ticket = 1;
  406. $r = array();
  407. if ($q && ($type == 'select')) $ticket = $this->getQueueTicket($q);
  408. if ($ticket) {
  409. if ($type == 'load') {/* the LoadQH supports raw data as 2nd parameter */
  410. $r = $h->runQuery($infos, '', $keep_bnode_ids);
  411. }
  412. else {
  413. $r = $h->runQuery($infos, $keep_bnode_ids);
  414. }
  415. }
  416. if ($q && ($type == 'select')) $this->removeQueueTicket($ticket);
  417. $trigger_r = $this->processTriggers($type, $infos);
  418. return $r;
  419. }
  420. function processTriggers($type, $infos) {
  421. $r = array();
  422. $trigger_defs = $this->triggers;
  423. $this->triggers = array();
  424. $triggers = $this->v($type, array(), $trigger_defs);
  425. if ($triggers) {
  426. $r['trigger_results'] = array();
  427. $triggers = is_array($triggers) ? $triggers : array($triggers);
  428. $trigger_inc_path = $this->v('store_triggers_path', '', $this->a);
  429. foreach ($triggers as $trigger) {
  430. $trigger .= !preg_match('/Trigger$/', $trigger) ? 'Trigger' : '';
  431. if (ARC2::inc(ucfirst($trigger), $trigger_inc_path)) {
  432. $cls = 'ARC2_' . ucfirst($trigger);
  433. $config = array_merge($this->a, array('query_infos' => $infos));
  434. $trigger_obj = new $cls($config, $this);
  435. if (method_exists($trigger_obj, 'go')) {
  436. $r['trigger_results'][] = $trigger_obj->go();
  437. }
  438. }
  439. }
  440. }
  441. $this->triggers = $trigger_defs;
  442. return $r;
  443. }
  444. /* */
  445. function getValueHash($val, $_32bit = false) {
  446. $hash = crc32($val);
  447. if ($_32bit && ($hash & 0x80000000)) {
  448. $hash = sprintf("%u", $hash);
  449. }
  450. $hash = abs($hash);
  451. return $hash;
  452. }
  453. function getTermID($val, $term = '') {
  454. /* mem cache */
  455. if (!isset($this->term_id_cache) || (count(array_keys($this->term_id_cache)) > 100)) {
  456. $this->term_id_cache = array();
  457. }
  458. if (!isset($this->term_id_cache[$term])) {
  459. $this->term_id_cache[$term] = array();
  460. }
  461. $tbl = preg_match('/^(s|o)$/', $term) ? $term . '2val' : 'id2val';
  462. /* cached? */
  463. if ((strlen($val) < 100) && isset($this->term_id_cache[$term][$val])) {
  464. return $this->term_id_cache[$term][$val];
  465. }
  466. $con = $this->getDBCon();
  467. $r = 0;
  468. /* via hash */
  469. if (preg_match('/^(s2val|o2val)$/', $tbl) && $this->hasHashColumn($tbl)) {
  470. $sql = "SELECT id, val FROM " . $this->getTablePrefix() . $tbl . " WHERE val_hash = '" . $this->getValueHash($val) . "' ORDER BY id";
  471. $rs = $this->queryDB($sql, $con);
  472. if (!$rs || !mysqli_num_rows($rs)) {// try 32 bit version
  473. $sql = "SELECT id, val FROM " . $this->getTablePrefix() . $tbl . " WHERE val_hash = '" . $this->getValueHash($val, true) . "' ORDER BY id";
  474. $rs = $this->queryDB($sql, $con);
  475. }
  476. if (($rs = $this->queryDB($sql, $con)) && mysqli_num_rows($rs)) {
  477. while ($row = mysqli_fetch_array($rs)) {
  478. if ($row['val'] == $val) {
  479. $r = $row['id'];
  480. break;
  481. }
  482. }
  483. }
  484. }
  485. /* exact match */
  486. else {
  487. $sql = "SELECT id FROM " . $this->getTablePrefix() . $tbl . " WHERE val = BINARY '" . mysqli_real_escape_string( $con, $val) . "' LIMIT 1";
  488. if (($rs = $this->queryDB($sql, $con)) && mysqli_num_rows($rs) && ($row = mysqli_fetch_array($rs))) {
  489. $r = $row['id'];
  490. }
  491. }
  492. if ($r && (strlen($val) < 100)) {
  493. $this->term_id_cache[$term][$val] = $r;
  494. }
  495. return $r;
  496. }
  497. function getIDValue($id, $term = '') {
  498. $tbl = preg_match('/^(s|o)$/', $term) ? $term . '2val' : 'id2val';
  499. $con = $this->getDBCon();
  500. $sql = "SELECT val FROM " . $this->getTablePrefix() . $tbl . " WHERE id = " . mysqli_real_escape_string( $con, $id) . " LIMIT 1";
  501. if (($rs = $this->queryDB($sql, $con)) && mysqli_num_rows($rs) && ($row = mysqli_fetch_array($rs))) {
  502. return $row['val'];
  503. }
  504. return 0;
  505. }
  506. /* */
  507. function getLock($t_out = 10, $t_out_init = '') {
  508. if (!$t_out_init) $t_out_init = $t_out;
  509. $con = $this->getDBCon();
  510. $l_name = $this->a['db_name'] . '.' . $this->getTablePrefix() . '.write_lock';
  511. $rs = $this->queryDB('SELECT IS_FREE_LOCK("' . $l_name. '") AS success', $con);
  512. if ($rs) {
  513. $row = mysqli_fetch_array($rs);
  514. if (!$row['success']) {
  515. if ($t_out) {
  516. sleep(1);
  517. return $this->getLock($t_out - 1, $t_out_init);
  518. }
  519. }
  520. else {
  521. $rs = $this->queryDB('SELECT GET_LOCK("' . $l_name. '", ' . $t_out_init. ') AS success', $con);
  522. if ($rs) {
  523. $row = mysqli_fetch_array($rs);
  524. return $row['success'];
  525. }
  526. }
  527. }
  528. return 0;
  529. }
  530. function releaseLock() {
  531. $con = $this->getDBCon();
  532. return $this->queryDB('DO RELEASE_LOCK("' . $this->a['db_name'] . '.' . $this->getTablePrefix() . '.write_lock")', $con);
  533. }
  534. /* */
  535. function processTables($level = 2, $operation = 'optimize') {/* 1: triple + g2t, 2: triple + *2val, 3: all tables */
  536. $con = $this->getDBCon();
  537. $pre = $this->getTablePrefix();
  538. $tbls = $this->getTables();
  539. $sql = '';
  540. foreach ($tbls as $tbl) {
  541. if (($level < 3) && preg_match('/(backup|setting)$/', $tbl)) continue;
  542. if (($level < 2) && preg_match('/(val)$/', $tbl)) continue;
  543. $sql .= $sql ? ', ' : strtoupper($operation) . ' TABLE ';
  544. $sql .= $pre . $tbl;
  545. }
  546. $this->queryDB($sql, $con);
  547. $err = mysqli_error($con);
  548. if (!empty($err)) {
  549. $this->addError($err . ' in ' . $sql);
  550. }
  551. }
  552. function optimizeTables($level = 2) {
  553. if ($this->v('ignore_optimization')) return 1;
  554. return $this->processTables($level, 'optimize');
  555. }
  556. function checkTables($level = 2) {
  557. return $this->processTables($level, 'check');
  558. }
  559. function repairTables($level = 2) {
  560. return $this->processTables($level, 'repair');
  561. }
  562. /* */
  563. function changeNamespaceURI($old_uri, $new_uri) {
  564. ARC2::inc('StoreHelper');
  565. $c = new ARC2_StoreHelper($this->a, $this);
  566. return $c->changeNamespaceURI($old_uri, $new_uri);
  567. }
  568. /* */
  569. function getResourceLabel($res, $unnamed_label = 'An unnamed resource') {
  570. if (!isset($this->resource_labels)) $this->resource_labels = array();
  571. if (isset($this->resource_labels[$res])) return $this->resource_labels[$res];
  572. if (!preg_match('/^[a-z0-9\_]+\:[^\s]+$/si', $res)) return $res;/* literal */
  573. $ps = $this->getLabelProps();
  574. if ($this->getSetting('store_label_properties', '-') != md5(serialize($ps))) {
  575. $this->inferLabelProps($ps);
  576. }
  577. //$sub_q .= $sub_q ? ' || ' : '';
  578. //$sub_q .= 'REGEX(str(?p), "(last_name|name|fn|title|label)$", "i")';
  579. $q = 'SELECT ?label WHERE { <' . $res . '> ?p ?label . ?p a <http://semsol.org/ns/arc#LabelProperty> } LIMIT 3';
  580. $r = '';
  581. $rows = $this->query($q, 'rows');
  582. foreach ($rows as $row) {
  583. $r = strlen($row['label']) > strlen($r) ? $row['label'] : $r;
  584. }
  585. if (!$r && preg_match('/^\_\:/', $res)) {
  586. return $unnamed_label;
  587. }
  588. $r = $r ? $r : preg_replace("/^(.*[\/\#])([^\/\#]+)$/", '\\2', str_replace('#self', '', $res));
  589. $r = str_replace('_', ' ', $r);
  590. $r = preg_replace_callback('/([a-z])([A-Z])/', function($matches) {
  591. return $matches[1] . ' ' . strtolower($matches[2]);
  592. }, $r);
  593. $this->resource_labels[$res] = $r;
  594. return $r;
  595. }
  596. function getLabelProps() {
  597. return array_merge(
  598. $this->v('rdf_label_properties' , array(), $this->a),
  599. array(
  600. 'http://www.w3.org/2000/01/rdf-schema#label',
  601. 'http://xmlns.com/foaf/0.1/name',
  602. 'http://purl.org/dc/elements/1.1/title',
  603. 'http://purl.org/rss/1.0/title',
  604. 'http://www.w3.org/2004/02/skos/core#prefLabel',
  605. 'http://xmlns.com/foaf/0.1/nick',
  606. )
  607. );
  608. }
  609. function inferLabelProps($ps) {
  610. $this->query('DELETE FROM <label-properties>');
  611. $sub_q = '';
  612. foreach ($ps as $p) {
  613. $sub_q .= ' <' . $p . '> a <http://semsol.org/ns/arc#LabelProperty> . ';
  614. }
  615. $this->query('INSERT INTO <label-properties> { ' . $sub_q. ' }');
  616. $this->setSetting('store_label_properties', md5(serialize($ps)));
  617. }
  618. /* */
  619. function getResourcePredicates($res) {
  620. $r = array();
  621. $rows = $this->query('SELECT DISTINCT ?p WHERE { <' . $res . '> ?p ?o . }', 'rows');
  622. foreach ($rows as $row) {
  623. $r[$row['p']] = array();
  624. }
  625. return $r;
  626. }
  627. function getDomains($p) {
  628. $r = array();
  629. foreach($this->query('SELECT DISTINCT ?type WHERE {?s <' . $p . '> ?o ; a ?type . }', 'rows') as $row) {
  630. $r[] = $row['type'];
  631. }
  632. return $r;
  633. }
  634. function getPredicateRange($p) {
  635. $row = $this->query('SELECT ?val WHERE {<' . $p . '> rdfs:range ?val . } LIMIT 1', 'row');
  636. return $row ? $row['val'] : '';
  637. }
  638. /* */
  639. function logQuery($q) {
  640. $fp = @fopen("arc_query_log.txt", "a");
  641. @fwrite($fp, date('Y-m-d\TH:i:s\Z', time()) . ' : ' . $q . '' . "\n\n");
  642. @fclose($fp);
  643. }
  644. /* */
  645. }