PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://code.google.com/p/s3db/
PHP | 449 lines | 366 code | 58 blank | 25 comment | 57 complexity | c4b09fe3190ca9f43fdfd631786a98cb MD5 | raw file
  1. <?php
  2. /*
  3. homepage: http://arc.semsol.org/
  4. license: http://arc.semsol.org/license
  5. class: ARC2 RDF Store
  6. author: Benjamin Nowack
  7. version: 2008-07-14 (Tweak: added "query_type" info to query result)
  8. */
  9. ARC2::inc('Class');
  10. class ARC2_Store extends ARC2_Class {
  11. function __construct($a = '', &$caller) {
  12. parent::__construct($a, $caller);
  13. }
  14. function ARC2_Store($a = '', &$caller) {
  15. $this->__construct($a, $caller);
  16. }
  17. function __init() {/* db_con */
  18. parent::__init();
  19. $this->table_lock = 0;
  20. $this->triggers = $this->v('store_triggers', array(), $this->a);
  21. }
  22. /* */
  23. function getName() {
  24. return $this->v('store_name', 'arc', $this->a);
  25. }
  26. function getTablePrefix() {
  27. if (!isset($this->tbl_prefix)) {
  28. $r = $this->v('db_table_prefix', '', $this->a);
  29. $r .= $r ? '_' : '';
  30. $r .= $this->getName() . '_';
  31. $this->tbl_prefix = $r;
  32. }
  33. return $this->tbl_prefix;;
  34. }
  35. /* */
  36. function createDBCon() {
  37. foreach (array('db_host' => 'localhost', 'db_user' => '', 'db_pwd' => '', 'db_name' => '') as $k => $v) {
  38. $this->a[$k] = $this->v($k, $v, $this->a);
  39. }
  40. if (!$db_con = mysql_connect($this->a['db_host'], $this->a['db_user'], $this->a['db_pwd'])) {
  41. return $this->addError(mysql_error());
  42. }
  43. $this->a['db_con'] =& $db_con;
  44. if (!mysql_select_db($this->a['db_name'], $db_con)) {
  45. return $this->addError(mysql_error());
  46. }
  47. if ($this->getDBVersion() >= '04-01-00') {
  48. mysql_query("SET NAMES 'utf8'");
  49. }
  50. return true;
  51. }
  52. function getDBCon() {
  53. if (!isset($this->a['db_con'])) {
  54. if (!$this->createDBCon()) {
  55. return false;
  56. }
  57. }
  58. return $this->a['db_con'];
  59. }
  60. function closeDBCon() {
  61. if ($this->v('db_con', false, $this->a)) {
  62. @mysql_close($this->a['db_con']);
  63. unset($this->a['db_con']);
  64. }
  65. }
  66. function getDBVersion() {
  67. if (!$this->v('db_version')) {
  68. $this->db_version = preg_match("/^([0-9]+)\.([0-9]+)\.([0-9]+)/", mysql_get_server_info(), $m) ? sprintf("%02d-%02d-%02d", $m[1], $m[2], $m[3]) : '00-00-00';
  69. }
  70. return $this->db_version;
  71. }
  72. /* */
  73. function getTables() {
  74. return array('triple', 'g2t', 'id2val', 's2val', 'o2val', 'setting');
  75. return array('triple', 'triple_backup', 'g2t', 'id2val', 's2val', 'o2val', 'setting');
  76. }
  77. /* */
  78. function isSetUp() {
  79. if ($con = $this->getDBCon()) {
  80. $tbl = $this->getTablePrefix() . 'setting';
  81. return mysql_query("SELECT 1 FROM " . $tbl . " LIMIT 0") ? 1 : 0;
  82. }
  83. }
  84. function setUp($force = 0) {
  85. if (($force || !$this->isSetUp()) && ($con = $this->getDBCon())) {
  86. if ($this->getDBVersion() < '04-00-04') {
  87. /* UPDATE + JOINs */
  88. return $this->addError('MySQL version not supported. ARC requires version 4.0.4 or higher.');
  89. }
  90. ARC2::inc('StoreTableManager');
  91. $mgr = new ARC2_StoreTableManager($this->a, $this);
  92. $mgr->createTables();
  93. }
  94. }
  95. /* */
  96. function hasSetting($k) {
  97. $tbl = $this->getTablePrefix() . 'setting';
  98. $sql = "SELECT val FROM " . $tbl . " WHERE k = '" .md5($k). "'";
  99. $rs = mysql_query($sql, $this->getDBCon());
  100. return ($rs && ($row = mysql_fetch_array($rs))) ? 1 : 0;
  101. }
  102. function getSetting($k, $default = 0) {
  103. $tbl = $this->getTablePrefix() . 'setting';
  104. $sql = "SELECT val FROM " . $tbl . " WHERE k = '" .md5($k). "'";
  105. $rs = mysql_query($sql, $this->getDBCon());
  106. if ($rs && ($row = mysql_fetch_array($rs))) {
  107. return unserialize($row['val']);
  108. }
  109. return $default;
  110. }
  111. function setSetting($k, $v) {
  112. $con = $this->getDBCon();
  113. $tbl = $this->getTablePrefix() . 'setting';
  114. if ($this->hasSetting($k)) {
  115. $sql = "UPDATE " .$tbl . " SET val = '" . mysql_real_escape_string(serialize($v)) . "' WHERE k = '" . md5($k) . "'";
  116. }
  117. else {
  118. $sql = "INSERT INTO " . $tbl . " (k, val) VALUES ('" . md5($k) . "', '" . mysql_real_escape_string(serialize($v)) . "')";
  119. }
  120. return mysql_query($sql);
  121. }
  122. function removeSetting($k) {
  123. $tbl = $this->getTablePrefix() . 'setting';
  124. return mysql_query("DELETE FROM " . $tbl . " WHERE k = '" . md5($k) . "'", $this->getDBCon());
  125. }
  126. /* */
  127. function reset($keep_settings = 0) {
  128. $con = $this->getDBCon();
  129. $tbls = $this->getTables();
  130. $prefix = $this->getTablePrefix();
  131. foreach ($tbls as $tbl) {
  132. if ($keep_settings && ($tbl == 'setting')) {
  133. continue;
  134. }
  135. mysql_query('TRUNCATE ' . $prefix . $tbl);
  136. }
  137. }
  138. function drop() {
  139. $con = $this->getDBCon();
  140. $tbls = $this->getTables();
  141. $prefix = $this->getTablePrefix();
  142. foreach ($tbls as $tbl) {
  143. mysql_query('DROP TABLE ' . $prefix . $tbl);
  144. }
  145. }
  146. function insert($doc, $g, $keep_bnode_ids = 0) {
  147. $doc = is_array($doc) ? $this->toTurtle($doc) : $doc;
  148. $infos = array('query' => array('url' => $g, 'target_graph' => $g));
  149. ARC2::inc('StoreLoadQueryHandler');
  150. $h =& new ARC2_StoreLoadQueryHandler($this->a, $this);
  151. $r = $h->runQuery($infos, $doc, $keep_bnode_ids);
  152. $this->processTriggers('insert', $infos);
  153. return $r;
  154. }
  155. function delete($doc, $g) {
  156. if (!$doc) {
  157. $infos = array('query' => array('target_graphs' => array($g)));
  158. ARC2::inc('StoreDeleteQueryHandler');
  159. $h =& new ARC2_StoreDeleteQueryHandler($this->a, $this);
  160. $r = $h->runQuery($infos);
  161. $this->processTriggers('delete', $infos);
  162. return $r;
  163. }
  164. }
  165. function replace($doc, $g, $doc_2) {
  166. return array($this->delete($doc, $g), $this->insert($doc_2, $g));
  167. }
  168. function dump() {
  169. ARC2::inc('StoreDumper');
  170. $d =& new ARC2_StoreDumper($this->a, $this);
  171. $d->dumpSPOG();
  172. }
  173. function createBackup($path, $q = '') {
  174. ARC2::inc('StoreDumper');
  175. $d =& new ARC2_StoreDumper($this->a, $this);
  176. $d->saveSPOG($path, $q);
  177. }
  178. function renameTo($name) {
  179. $con = $this->getDBCon();
  180. $tbls = $this->getTables();
  181. $old_prefix = $this->getTablePrefix();
  182. $new_prefix = $this->v('db_table_prefix', '', $this->a);
  183. $new_prefix .= $new_prefix ? '_' : '';
  184. $new_prefix .= $name . '_';
  185. foreach ($tbls as $tbl) {
  186. $rs = mysql_query('RENAME TABLE ' . $old_prefix . $tbl .' TO ' . $new_prefix . $tbl, $con);
  187. if ($er = mysql_error()) {
  188. return $this->addError($er);
  189. }
  190. }
  191. $this->a['store_name'] = $name;
  192. unset($this->tbl_prefix);
  193. }
  194. function replicateTo($name) {
  195. $conf = array_merge($this->a, array('store_name' => $name));
  196. $new_store = ARC2::getStore($conf);
  197. $new_store->setUp();
  198. $new_store->reset();
  199. $con = $this->getDBCon();
  200. $tbls = $this->getTables();
  201. $old_prefix = $this->getTablePrefix();
  202. $new_prefix = $new_store->getTablePrefix();
  203. foreach ($tbls as $tbl) {
  204. $rs = mysql_query('INSERT IGNORE INTO ' . $new_prefix . $tbl .' SELECT * FROM ' . $old_prefix . $tbl, $con);
  205. if ($er = mysql_error()) {
  206. return $this->addError($er);
  207. }
  208. }
  209. return $new_store->query('SELECT COUNT(*) AS t_count WHERE { ?s ?p ?o}', 'row');
  210. }
  211. /* */
  212. function query($q, $result_format = '', $src = '', $keep_bnode_ids = 0, $log_query = 0) {
  213. if ($log_query) $this->logQuery($q);
  214. $con = $this->getDBCon();
  215. if (preg_match('/^dump/i', $q)) {
  216. $infos = array('query' => array('type' => 'dump'));
  217. }
  218. else {
  219. ARC2::inc('SPARQLPlusParser');
  220. $p = & new ARC2_SPARQLPlusParser($this->a, $this);
  221. $p->parse($q, $src);
  222. $infos = $p->getQueryInfos();
  223. }
  224. if ($result_format == 'infos') return $infos;
  225. $infos['result_format'] = $result_format;
  226. if (!isset($p) || !$p->getErrors()) {
  227. $qt = $infos['query']['type'];
  228. if (!in_array($qt, array('select', 'ask', 'describe', 'construct', 'load', 'insert', 'delete', 'dump'))) {
  229. return $this->addError('Unsupported query type "'.$qt.'"');
  230. }
  231. $t1 = ARC2::mtime();
  232. $r = array('query_type' => $qt, 'result' => $this->runQuery($infos, $qt, $keep_bnode_ids));
  233. $t2 = ARC2::mtime();
  234. $r['query_time'] = $t2 - $t1;
  235. /* query result */
  236. if ($result_format == 'raw') {
  237. return $r['result'];
  238. }
  239. if ($result_format == 'rows') {
  240. return $r['result']['rows'] ? $r['result']['rows'] : array();
  241. }
  242. if ($result_format == 'row') {
  243. return $r['result']['rows'] ? $r['result']['rows'][0] : array();
  244. }
  245. return $r;
  246. }
  247. return 0;
  248. }
  249. function runQuery($infos, $type, $keep_bnode_ids = 0) {
  250. ARC2::inc('Store' . ucfirst($type) . 'QueryHandler');
  251. $cls = 'ARC2_Store' . ucfirst($type) . 'QueryHandler';
  252. $h =& new $cls($this->a, $this);
  253. $r = $h->runQuery($infos, $keep_bnode_ids);
  254. $trigger_r = $this->processTriggers($type, $infos);
  255. return $r;
  256. }
  257. function processTriggers($type, $infos) {
  258. $r = array();
  259. $trigger_defs = $this->triggers;
  260. $this->triggers = array();
  261. if ($triggers = $this->v($type, array(), $trigger_defs)) {
  262. $r['trigger_results'] = array();
  263. $triggers = is_array($triggers) ? $triggers : array($triggers);
  264. foreach ($triggers as $trigger) {
  265. $trigger .= !preg_match('/Trigger$/', $trigger) ? 'Trigger' : '';
  266. if (ARC2::inc(ucfirst($trigger))) {
  267. $cls = 'ARC2_' . ucfirst($trigger);
  268. $config = array_merge($this->a, array('query_infos' => $infos));
  269. $trigger_obj = new $cls($config, $this);
  270. if (method_exists($trigger_obj, 'go')) {
  271. $r['trigger_results'][] = $trigger_obj->go();
  272. }
  273. }
  274. }
  275. }
  276. $this->triggers = $trigger_defs;
  277. return $r;
  278. }
  279. /* */
  280. function getTermID($val, $term = '', $id_col = 'cid') {
  281. $tbl = preg_match('/^(s|o)$/', $term) ? $term . '2val' : 'id2val';
  282. $col = preg_match('/^(s|o)$/', $term) ? $id_col : 'id';
  283. $con = $this->getDBCon();
  284. $sql = "SELECT " . $col . " AS id FROM " . $this->getTablePrefix() . $tbl . " WHERE val = BINARY '" . mysql_real_escape_string($val) . "' LIMIT 1";
  285. if (($rs = mysql_query($sql)) && mysql_num_rows($rs) && ($row = mysql_fetch_array($rs))) {
  286. return $row['id'];
  287. }
  288. return 0;
  289. }
  290. /* */
  291. function getLock($t_out = 10, $t_out_init = '') {
  292. if (!$t_out_init) $t_out_init = $t_out;
  293. $con = $this->getDBCon();
  294. $l_name = $this->a['db_name'] . '.' . $this->getTablePrefix() . '.write_lock';
  295. if ($rs = mysql_query('SELECT IS_FREE_LOCK("' . $l_name. '") AS success')) {
  296. $row = mysql_fetch_array($rs);
  297. if (!$row['success']) {
  298. if ($t_out) {
  299. sleep(1);
  300. return $this->getLock($t_out - 1, $t_out_init);
  301. }
  302. }
  303. elseif ($rs = mysql_query('SELECT GET_LOCK("' . $l_name. '", ' . $t_out_init. ') AS success')) {
  304. $row = mysql_fetch_array($rs);
  305. return $row['success'];
  306. }
  307. }
  308. return 0;
  309. }
  310. function releaseLock() {
  311. $con = $this->getDBCon();
  312. return mysql_query('DO RELEASE_LOCK("' . $this->a['db_name'] . '.' . $this->getTablePrefix() . '.write_lock")');
  313. }
  314. /* */
  315. function optimizeTables($level = 2) {/* 1: triple + g2t, 2: triple + *2val, 3: all tables */
  316. $con = $this->getDBCon();
  317. $pre = $this->getTablePrefix();
  318. $tbls = $this->getTables();
  319. $sql = '';
  320. foreach ($tbls as $tbl) {
  321. if (($level < 3) && preg_match('/(backup|setting)$/', $tbl)) continue;
  322. if (($level < 2) && preg_match('/(val)$/', $tbl)) continue;
  323. $sql .= $sql ? ', ' : 'OPTIMIZE TABLE ';
  324. $sql .= $pre . $tbl;
  325. }
  326. mysql_query($sql);
  327. if ($err = mysql_error()) $this->addError($err . ' in ' . $sql);
  328. }
  329. /* */
  330. function isConsolidated($after = 0) {
  331. return $this->getSetting('store_consolidation_uts') > $after ? 1 : 0;
  332. }
  333. function consolidate($res = '') {
  334. ARC2::inc('StoreInferencer');
  335. $c = new ARC2_StoreInferencer($this->a, $this);
  336. return $c->consolidate($res);
  337. }
  338. function consolidateIFP($ifp, $res = '') {
  339. ARC2::inc('StoreInferencer');
  340. $c = new ARC2_StoreInferencer($this->a, $this);
  341. return $c->consolidateIFP($ifp, $res);
  342. }
  343. function inferLabels($res = '') {
  344. ARC2::inc('StoreInferencer');
  345. $c = new ARC2_StoreInferencer($this->a, $this);
  346. return $c->inferLabels($res);
  347. }
  348. /* */
  349. function changeNamespaceURI($old_uri, $new_uri) {
  350. ARC2::inc('StoreHelper');
  351. $c = new ARC2_StoreHelper($this->a, $this);
  352. return $c->changeNamespaceURI($old_uri, $new_uri);
  353. }
  354. /* */
  355. function getResourceLabel($res) {
  356. $q = '
  357. SELECT ?label WHERE {
  358. <' . $res . '> ?p ?label .
  359. FILTER REGEX(str(?p), "(name|label|title|summary|nick|fn)$", "i")
  360. }
  361. LIMIT 5
  362. ';
  363. $r = '';
  364. if ($rows = $this->query($q, 'rows')) {
  365. foreach ($rows as $row) {
  366. $r = strlen($row['label']) > strlen($r) ? $row['label'] : $r;
  367. }
  368. }
  369. if (!$r && preg_match('/^\_\:/', $res)) {
  370. return 'An unnamed resource';
  371. }
  372. return $r ? $r : preg_replace("/^(.*[\/\#])([^\/\#]+)$/", '\\2', $res);
  373. }
  374. function getResourcePredicates($res) {
  375. $r = array();
  376. if ($rows = $this->query('SELECT DISTINCT ?p WHERE { <' . $res . '> ?p ?o . }', 'rows')) {
  377. foreach ($rows as $row) {
  378. $r[$row['p']] = array();
  379. }
  380. }
  381. return $r;
  382. }
  383. /* */
  384. function logQuery($q) {
  385. $fp = @fopen("arc_query_log.txt", "a");
  386. @fwrite($fp, date('Y-m-d\TH:i:s\Z', time()) . ' : ' . $q . '' . "\n\n");
  387. @fclose($fp);
  388. }
  389. /* */
  390. }