Common.php 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Holds the PhpMyAdmin\Database\Designer\Common class
  5. *
  6. * @package PhpMyAdmin-Designer
  7. */
  8. namespace PhpMyAdmin\Database\Designer;
  9. use PhpMyAdmin\DatabaseInterface;
  10. use PhpMyAdmin\Index;
  11. use PhpMyAdmin\Relation;
  12. use PhpMyAdmin\Table;
  13. use PhpMyAdmin\Util;
  14. /**
  15. * Common functions for Designer
  16. *
  17. * @package PhpMyAdmin-Designer
  18. */
  19. class Common
  20. {
  21. /**
  22. * @var Relation $relation
  23. */
  24. private $relation;
  25. /**
  26. * Constructor
  27. */
  28. public function __construct()
  29. {
  30. $this->relation = new Relation();
  31. }
  32. /**
  33. * Retrieves table info and stores it in $GLOBALS['designer']
  34. *
  35. * @return array with table info
  36. */
  37. public function getTablesInfo()
  38. {
  39. $retval = array();
  40. $GLOBALS['designer']['TABLE_NAME'] = array();// that foreach no error
  41. $GLOBALS['designer']['OWNER'] = array();
  42. $GLOBALS['designer']['TABLE_NAME_SMALL'] = array();
  43. $tables = $GLOBALS['dbi']->getTablesFull($GLOBALS['db']);
  44. // seems to be needed later
  45. $GLOBALS['dbi']->selectDb($GLOBALS['db']);
  46. $i = 0;
  47. foreach ($tables as $one_table) {
  48. $GLOBALS['designer']['TABLE_NAME'][$i]
  49. = $GLOBALS['db'] . "." . $one_table['TABLE_NAME'];
  50. $GLOBALS['designer']['OWNER'][$i] = $GLOBALS['db'];
  51. $GLOBALS['designer']['TABLE_NAME_SMALL'][$i] = htmlspecialchars(
  52. $one_table['TABLE_NAME'], ENT_QUOTES
  53. );
  54. $GLOBALS['designer_url']['TABLE_NAME'][$i]
  55. = $GLOBALS['db'] . "." . $one_table['TABLE_NAME'];
  56. $GLOBALS['designer_url']['OWNER'][$i] = $GLOBALS['db'];
  57. $GLOBALS['designer_url']['TABLE_NAME_SMALL'][$i]
  58. = $one_table['TABLE_NAME'];
  59. $GLOBALS['designer_out']['TABLE_NAME'][$i] = htmlspecialchars(
  60. $GLOBALS['db'] . "." . $one_table['TABLE_NAME'], ENT_QUOTES
  61. );
  62. $GLOBALS['designer_out']['OWNER'][$i] = htmlspecialchars(
  63. $GLOBALS['db'], ENT_QUOTES
  64. );
  65. $GLOBALS['designer_out']['TABLE_NAME_SMALL'][$i] = htmlspecialchars(
  66. $one_table['TABLE_NAME'], ENT_QUOTES
  67. );
  68. $GLOBALS['designer']['TABLE_TYPE'][$i] = mb_strtoupper(
  69. $one_table['ENGINE']
  70. );
  71. $DF = $this->relation->getDisplayField($GLOBALS['db'], $one_table['TABLE_NAME']);
  72. if ($DF != '') {
  73. $retval[$GLOBALS['designer_url']["TABLE_NAME_SMALL"][$i]] = $DF;
  74. }
  75. $i++;
  76. }
  77. return $retval;
  78. }
  79. /**
  80. * Retrieves table column info
  81. *
  82. * @return array table column nfo
  83. */
  84. public function getColumnsInfo()
  85. {
  86. $GLOBALS['dbi']->selectDb($GLOBALS['db']);
  87. $tab_column = array();
  88. for ($i = 0, $cnt = count($GLOBALS['designer']["TABLE_NAME"]); $i < $cnt; $i++) {
  89. $fields_rs = $GLOBALS['dbi']->query(
  90. $GLOBALS['dbi']->getColumnsSql(
  91. $GLOBALS['db'],
  92. $GLOBALS['designer_url']["TABLE_NAME_SMALL"][$i],
  93. null,
  94. true
  95. ),
  96. DatabaseInterface::CONNECT_USER,
  97. DatabaseInterface::QUERY_STORE
  98. );
  99. $tbl_name_i = $GLOBALS['designer']['TABLE_NAME'][$i];
  100. $j = 0;
  101. while ($row = $GLOBALS['dbi']->fetchAssoc($fields_rs)) {
  102. $tab_column[$tbl_name_i]['COLUMN_ID'][$j] = $j;
  103. $tab_column[$tbl_name_i]['COLUMN_NAME'][$j] = $row['Field'];
  104. $tab_column[$tbl_name_i]['TYPE'][$j] = $row['Type'];
  105. $tab_column[$tbl_name_i]['NULLABLE'][$j] = $row['Null'];
  106. $j++;
  107. }
  108. }
  109. return $tab_column;
  110. }
  111. /**
  112. * Returns JavaScript code for initializing vars
  113. *
  114. * @return string JavaScript code
  115. */
  116. public function getScriptContr()
  117. {
  118. $GLOBALS['dbi']->selectDb($GLOBALS['db']);
  119. $con = array();
  120. $con["C_NAME"] = array();
  121. $i = 0;
  122. $alltab_rs = $GLOBALS['dbi']->query(
  123. 'SHOW TABLES FROM ' . Util::backquote($GLOBALS['db']),
  124. DatabaseInterface::CONNECT_USER,
  125. DatabaseInterface::QUERY_STORE
  126. );
  127. while ($val = @$GLOBALS['dbi']->fetchRow($alltab_rs)) {
  128. $row = $this->relation->getForeigners($GLOBALS['db'], $val[0], '', 'internal');
  129. if ($row !== false) {
  130. foreach ($row as $field => $value) {
  131. $con['C_NAME'][$i] = '';
  132. $con['DTN'][$i] = urlencode($GLOBALS['db'] . "." . $val[0]);
  133. $con['DCN'][$i] = urlencode($field);
  134. $con['STN'][$i] = urlencode(
  135. $value['foreign_db'] . "." . $value['foreign_table']
  136. );
  137. $con['SCN'][$i] = urlencode($value['foreign_field']);
  138. $i++;
  139. }
  140. }
  141. $row = $this->relation->getForeigners($GLOBALS['db'], $val[0], '', 'foreign');
  142. if ($row !== false) {
  143. foreach ($row['foreign_keys_data'] as $one_key) {
  144. foreach ($one_key['index_list'] as $index => $one_field) {
  145. $con['C_NAME'][$i] = $one_key['constraint'];
  146. $con['DTN'][$i] = urlencode($GLOBALS['db'] . "." . $val[0]);
  147. $con['DCN'][$i] = urlencode($one_field);
  148. $con['STN'][$i] = urlencode(
  149. (isset($one_key['ref_db_name']) ?
  150. $one_key['ref_db_name'] : $GLOBALS['db'])
  151. . "." . $one_key['ref_table_name']
  152. );
  153. $con['SCN'][$i] = urlencode($one_key['ref_index_list'][$index]);
  154. $i++;
  155. }
  156. }
  157. }
  158. }
  159. $ti = 0;
  160. $retval = array();
  161. for ($i = 0, $cnt = count($con["C_NAME"]); $i < $cnt; $i++) {
  162. $c_name_i = $con['C_NAME'][$i];
  163. $dtn_i = $con['DTN'][$i];
  164. $retval[$ti] = array();
  165. $retval[$ti][$c_name_i] = array();
  166. if (in_array($dtn_i, $GLOBALS['designer_url']["TABLE_NAME"])
  167. && in_array($con['STN'][$i], $GLOBALS['designer_url']["TABLE_NAME"])
  168. ) {
  169. $retval[$ti][$c_name_i][$dtn_i] = array();
  170. $retval[$ti][$c_name_i][$dtn_i][$con['DCN'][$i]] = array(
  171. 0 => $con['STN'][$i],
  172. 1 => $con['SCN'][$i]
  173. );
  174. }
  175. $ti++;
  176. }
  177. return $retval;
  178. }
  179. /**
  180. * Returns UNIQUE and PRIMARY indices
  181. *
  182. * @return array unique or primary indices
  183. */
  184. public function getPkOrUniqueKeys()
  185. {
  186. return $this->getAllKeys(true);
  187. }
  188. /**
  189. * Returns all indices
  190. *
  191. * @param bool $unique_only whether to include only unique ones
  192. *
  193. * @return array indices
  194. */
  195. public function getAllKeys($unique_only = false)
  196. {
  197. $keys = array();
  198. foreach ($GLOBALS['designer']['TABLE_NAME_SMALL'] as $I => $table) {
  199. $schema = $GLOBALS['designer']['OWNER'][$I];
  200. // for now, take into account only the first index segment
  201. foreach (Index::getFromTable($table, $schema) as $index) {
  202. if ($unique_only && ! $index->isUnique()) {
  203. continue;
  204. }
  205. $columns = $index->getColumns();
  206. foreach ($columns as $column_name => $dummy) {
  207. $keys[$schema . '.' . $table . '.' . $column_name] = 1;
  208. }
  209. }
  210. }
  211. return $keys;
  212. }
  213. /**
  214. * Return script to create j_tab and h_tab arrays
  215. *
  216. * @return string
  217. */
  218. public function getScriptTabs()
  219. {
  220. $retval = array(
  221. 'j_tabs' => array(),
  222. 'h_tabs' => array()
  223. );
  224. for ($i = 0, $cnt = count($GLOBALS['designer']['TABLE_NAME']); $i < $cnt; $i++) {
  225. $j = 0;
  226. if (Util::isForeignKeySupported($GLOBALS['designer']['TABLE_TYPE'][$i])) {
  227. $j = 1;
  228. }
  229. $retval['j_tabs'][$GLOBALS['designer_url']['TABLE_NAME'][$i]] = $j;
  230. $retval['h_tabs'][$GLOBALS['designer_url']['TABLE_NAME'][$i]] = 1;
  231. }
  232. return $retval;
  233. }
  234. /**
  235. * Returns table positions of a given pdf page
  236. *
  237. * @param int $pg pdf page id
  238. *
  239. * @return array of table positions
  240. */
  241. public function getTablePositions($pg)
  242. {
  243. $cfgRelation = $this->relation->getRelationsParam();
  244. if (! $cfgRelation['pdfwork']) {
  245. return null;
  246. }
  247. $query = "
  248. SELECT CONCAT_WS('.', `db_name`, `table_name`) AS `name`,
  249. `x` AS `X`,
  250. `y` AS `Y`,
  251. 1 AS `V`,
  252. 1 AS `H`
  253. FROM " . Util::backquote($cfgRelation['db'])
  254. . "." . Util::backquote($cfgRelation['table_coords']) . "
  255. WHERE pdf_page_number = " . intval($pg);
  256. $tab_pos = $GLOBALS['dbi']->fetchResult(
  257. $query,
  258. 'name',
  259. null,
  260. DatabaseInterface::CONNECT_CONTROL,
  261. DatabaseInterface::QUERY_STORE
  262. );
  263. return $tab_pos;
  264. }
  265. /**
  266. * Returns page name of a given pdf page
  267. *
  268. * @param int $pg pdf page id
  269. *
  270. * @return string table name
  271. */
  272. public function getPageName($pg)
  273. {
  274. $cfgRelation = $this->relation->getRelationsParam();
  275. if (! $cfgRelation['pdfwork']) {
  276. return null;
  277. }
  278. $query = "SELECT `page_descr`"
  279. . " FROM " . Util::backquote($cfgRelation['db'])
  280. . "." . Util::backquote($cfgRelation['pdf_pages'])
  281. . " WHERE " . Util::backquote('page_nr') . " = " . intval($pg);
  282. $page_name = $GLOBALS['dbi']->fetchResult(
  283. $query,
  284. null,
  285. null,
  286. DatabaseInterface::CONNECT_CONTROL,
  287. DatabaseInterface::QUERY_STORE
  288. );
  289. return count($page_name) ? $page_name[0] : null;
  290. }
  291. /**
  292. * Deletes a given pdf page and its corresponding coordinates
  293. *
  294. * @param int $pg page id
  295. *
  296. * @return boolean success/failure
  297. */
  298. public function deletePage($pg)
  299. {
  300. $cfgRelation = $this->relation->getRelationsParam();
  301. if (! $cfgRelation['pdfwork']) {
  302. return false;
  303. }
  304. $query = "DELETE FROM " . Util::backquote($cfgRelation['db'])
  305. . "." . Util::backquote($cfgRelation['table_coords'])
  306. . " WHERE " . Util::backquote('pdf_page_number') . " = " . intval($pg);
  307. $success = $this->relation->queryAsControlUser(
  308. $query, true, DatabaseInterface::QUERY_STORE
  309. );
  310. if ($success) {
  311. $query = "DELETE FROM " . Util::backquote($cfgRelation['db'])
  312. . "." . Util::backquote($cfgRelation['pdf_pages'])
  313. . " WHERE " . Util::backquote('page_nr') . " = " . intval($pg);
  314. $success = $this->relation->queryAsControlUser(
  315. $query, true, DatabaseInterface::QUERY_STORE
  316. );
  317. }
  318. return (boolean) $success;
  319. }
  320. /**
  321. * Returns the id of the default pdf page of the database.
  322. * Default page is the one which has the same name as the database.
  323. *
  324. * @param string $db database
  325. *
  326. * @return int id of the default pdf page for the database
  327. */
  328. public function getDefaultPage($db)
  329. {
  330. $cfgRelation = $this->relation->getRelationsParam();
  331. if (! $cfgRelation['pdfwork']) {
  332. return null;
  333. }
  334. $query = "SELECT `page_nr`"
  335. . " FROM " . Util::backquote($cfgRelation['db'])
  336. . "." . Util::backquote($cfgRelation['pdf_pages'])
  337. . " WHERE `db_name` = '" . $GLOBALS['dbi']->escapeString($db) . "'"
  338. . " AND `page_descr` = '" . $GLOBALS['dbi']->escapeString($db) . "'";
  339. $default_page_no = $GLOBALS['dbi']->fetchResult(
  340. $query,
  341. null,
  342. null,
  343. DatabaseInterface::CONNECT_CONTROL,
  344. DatabaseInterface::QUERY_STORE
  345. );
  346. if (isset($default_page_no) && count($default_page_no)) {
  347. return intval($default_page_no[0]);
  348. }
  349. return -1;
  350. }
  351. /**
  352. * Get the id of the page to load. If a default page exists it will be returned.
  353. * If no such exists, returns the id of the first page of the database.
  354. *
  355. * @param string $db database
  356. *
  357. * @return int id of the page to load
  358. */
  359. public function getLoadingPage($db)
  360. {
  361. $cfgRelation = $this->relation->getRelationsParam();
  362. if (! $cfgRelation['pdfwork']) {
  363. return null;
  364. }
  365. $page_no = -1;
  366. $default_page_no = $this->getDefaultPage($db);
  367. if ($default_page_no != -1) {
  368. $page_no = $default_page_no;
  369. } else {
  370. $query = "SELECT MIN(`page_nr`)"
  371. . " FROM " . Util::backquote($cfgRelation['db'])
  372. . "." . Util::backquote($cfgRelation['pdf_pages'])
  373. . " WHERE `db_name` = '" . $GLOBALS['dbi']->escapeString($db) . "'";
  374. $min_page_no = $GLOBALS['dbi']->fetchResult(
  375. $query,
  376. null,
  377. null,
  378. DatabaseInterface::CONNECT_CONTROL,
  379. DatabaseInterface::QUERY_STORE
  380. );
  381. if (isset($min_page_no[0]) && count($min_page_no[0])) {
  382. $page_no = $min_page_no[0];
  383. }
  384. }
  385. return intval($page_no);
  386. }
  387. /**
  388. * Creates a new page and returns its auto-incrementing id
  389. *
  390. * @param string $pageName name of the page
  391. * @param string $db name of the database
  392. *
  393. * @return int|null
  394. */
  395. public function createNewPage($pageName, $db)
  396. {
  397. $cfgRelation = $this->relation->getRelationsParam();
  398. if ($cfgRelation['pdfwork']) {
  399. $pageNumber = $this->relation->createPage(
  400. $pageName,
  401. $cfgRelation,
  402. $db
  403. );
  404. return $pageNumber;
  405. }
  406. return null;
  407. }
  408. /**
  409. * Saves positions of table(s) of a given pdf page
  410. *
  411. * @param int $pg pdf page id
  412. *
  413. * @return boolean success/failure
  414. */
  415. public function saveTablePositions($pg)
  416. {
  417. $cfgRelation = $this->relation->getRelationsParam();
  418. if (! $cfgRelation['pdfwork']) {
  419. return false;
  420. }
  421. $query = "DELETE FROM "
  422. . Util::backquote($GLOBALS['cfgRelation']['db'])
  423. . "." . Util::backquote(
  424. $GLOBALS['cfgRelation']['table_coords']
  425. )
  426. . " WHERE `db_name` = '" . $GLOBALS['dbi']->escapeString($_REQUEST['db'])
  427. . "'"
  428. . " AND `pdf_page_number` = '" . $GLOBALS['dbi']->escapeString($pg)
  429. . "'";
  430. $res = $this->relation->queryAsControlUser(
  431. $query,
  432. true,
  433. DatabaseInterface::QUERY_STORE
  434. );
  435. if (!$res) {
  436. return (boolean)$res;
  437. }
  438. foreach ($_REQUEST['t_h'] as $key => $value) {
  439. list($DB, $TAB) = explode(".", $key);
  440. if (!$value) {
  441. continue;
  442. }
  443. $query = "INSERT INTO "
  444. . Util::backquote($GLOBALS['cfgRelation']['db']) . "."
  445. . Util::backquote($GLOBALS['cfgRelation']['table_coords'])
  446. . " (`db_name`, `table_name`, `pdf_page_number`, `x`, `y`)"
  447. . " VALUES ("
  448. . "'" . $GLOBALS['dbi']->escapeString($DB) . "', "
  449. . "'" . $GLOBALS['dbi']->escapeString($TAB) . "', "
  450. . "'" . $GLOBALS['dbi']->escapeString($pg) . "', "
  451. . "'" . $GLOBALS['dbi']->escapeString($_REQUEST['t_x'][$key]) . "', "
  452. . "'" . $GLOBALS['dbi']->escapeString($_REQUEST['t_y'][$key]) . "')";
  453. $res = $this->relation->queryAsControlUser(
  454. $query, true, DatabaseInterface::QUERY_STORE
  455. );
  456. }
  457. return (boolean) $res;
  458. }
  459. /**
  460. * Saves the display field for a table.
  461. *
  462. * @param string $db database name
  463. * @param string $table table name
  464. * @param string $field display field name
  465. *
  466. * @return boolean
  467. */
  468. public function saveDisplayField($db, $table, $field)
  469. {
  470. $cfgRelation = $this->relation->getRelationsParam();
  471. if (!$cfgRelation['displaywork']) {
  472. return false;
  473. }
  474. $upd_query = new Table($table, $db, $GLOBALS['dbi']);
  475. $upd_query->updateDisplayField($field, $cfgRelation);
  476. return true;
  477. }
  478. /**
  479. * Adds a new foreign relation
  480. *
  481. * @param string $db database name
  482. * @param string $T1 foreign table
  483. * @param string $F1 foreign field
  484. * @param string $T2 master table
  485. * @param string $F2 master field
  486. * @param string $on_delete on delete action
  487. * @param string $on_update on update action
  488. * @param string $DB1 database
  489. * @param string $DB2 database
  490. *
  491. * @return array array of success/failure and message
  492. */
  493. public function addNewRelation($db, $T1, $F1, $T2, $F2, $on_delete, $on_update, $DB1, $DB2)
  494. {
  495. $tables = $GLOBALS['dbi']->getTablesFull($DB1, $T1);
  496. $type_T1 = mb_strtoupper($tables[$T1]['ENGINE']);
  497. $tables = $GLOBALS['dbi']->getTablesFull($DB2, $T2);
  498. $type_T2 = mb_strtoupper($tables[$T2]['ENGINE']);
  499. // native foreign key
  500. if (Util::isForeignKeySupported($type_T1)
  501. && Util::isForeignKeySupported($type_T2)
  502. && $type_T1 == $type_T2
  503. ) {
  504. // relation exists?
  505. $existrel_foreign = $this->relation->getForeigners($DB2, $T2, '', 'foreign');
  506. $foreigner = $this->relation->searchColumnInForeigners($existrel_foreign, $F2);
  507. if ($foreigner
  508. && isset($foreigner['constraint'])
  509. ) {
  510. return array(false, __('Error: relationship already exists.'));
  511. }
  512. // note: in InnoDB, the index does not requires to be on a PRIMARY
  513. // or UNIQUE key
  514. // improve: check all other requirements for InnoDB relations
  515. $result = $GLOBALS['dbi']->query(
  516. 'SHOW INDEX FROM ' . Util::backquote($DB1)
  517. . '.' . Util::backquote($T1) . ';'
  518. );
  519. // will be use to emphasis prim. keys in the table view
  520. $index_array1 = array();
  521. while ($row = $GLOBALS['dbi']->fetchAssoc($result)) {
  522. $index_array1[$row['Column_name']] = 1;
  523. }
  524. $GLOBALS['dbi']->freeResult($result);
  525. $result = $GLOBALS['dbi']->query(
  526. 'SHOW INDEX FROM ' . Util::backquote($DB2)
  527. . '.' . Util::backquote($T2) . ';'
  528. );
  529. // will be used to emphasis prim. keys in the table view
  530. $index_array2 = array();
  531. while ($row = $GLOBALS['dbi']->fetchAssoc($result)) {
  532. $index_array2[$row['Column_name']] = 1;
  533. }
  534. $GLOBALS['dbi']->freeResult($result);
  535. if (! empty($index_array1[$F1]) && ! empty($index_array2[$F2])) {
  536. $upd_query = 'ALTER TABLE ' . Util::backquote($DB2)
  537. . '.' . Util::backquote($T2)
  538. . ' ADD FOREIGN KEY ('
  539. . Util::backquote($F2) . ')'
  540. . ' REFERENCES '
  541. . Util::backquote($DB1) . '.'
  542. . Util::backquote($T1) . '('
  543. . Util::backquote($F1) . ')';
  544. if ($on_delete != 'nix') {
  545. $upd_query .= ' ON DELETE ' . $on_delete;
  546. }
  547. if ($on_update != 'nix') {
  548. $upd_query .= ' ON UPDATE ' . $on_update;
  549. }
  550. $upd_query .= ';';
  551. if ($GLOBALS['dbi']->tryQuery($upd_query)) {
  552. return array(true, __('FOREIGN KEY relationship has been added.'));
  553. }
  554. $error = $GLOBALS['dbi']->getError();
  555. return array(
  556. false,
  557. __('Error: FOREIGN KEY relationship could not be added!')
  558. . "<br/>" . $error
  559. );
  560. }
  561. return array(false, __('Error: Missing index on column(s).'));
  562. }
  563. // internal (pmadb) relation
  564. if ($GLOBALS['cfgRelation']['relwork'] == false) {
  565. return array(false, __('Error: Relational features are disabled!'));
  566. }
  567. // no need to recheck if the keys are primary or unique at this point,
  568. // this was checked on the interface part
  569. $q = "INSERT INTO "
  570. . Util::backquote($GLOBALS['cfgRelation']['db'])
  571. . "."
  572. . Util::backquote($GLOBALS['cfgRelation']['relation'])
  573. . "(master_db, master_table, master_field, "
  574. . "foreign_db, foreign_table, foreign_field)"
  575. . " values("
  576. . "'" . $GLOBALS['dbi']->escapeString($DB2) . "', "
  577. . "'" . $GLOBALS['dbi']->escapeString($T2) . "', "
  578. . "'" . $GLOBALS['dbi']->escapeString($F2) . "', "
  579. . "'" . $GLOBALS['dbi']->escapeString($DB1) . "', "
  580. . "'" . $GLOBALS['dbi']->escapeString($T1) . "', "
  581. . "'" . $GLOBALS['dbi']->escapeString($F1) . "')";
  582. if ($this->relation->queryAsControlUser($q, false, DatabaseInterface::QUERY_STORE)
  583. ) {
  584. return array(true, __('Internal relationship has been added.'));
  585. }
  586. $error = $GLOBALS['dbi']->getError(DatabaseInterface::CONNECT_CONTROL);
  587. return array(
  588. false,
  589. __('Error: Internal relationship could not be added!')
  590. . "<br/>" . $error
  591. );
  592. }
  593. /**
  594. * Removes a foreign relation
  595. *
  596. * @param string $T1 foreign db.table
  597. * @param string $F1 foreign field
  598. * @param string $T2 master db.table
  599. * @param string $F2 master field
  600. *
  601. * @return array array of success/failure and message
  602. */
  603. public function removeRelation($T1, $F1, $T2, $F2)
  604. {
  605. list($DB1, $T1) = explode(".", $T1);
  606. list($DB2, $T2) = explode(".", $T2);
  607. $tables = $GLOBALS['dbi']->getTablesFull($DB1, $T1);
  608. $type_T1 = mb_strtoupper($tables[$T1]['ENGINE']);
  609. $tables = $GLOBALS['dbi']->getTablesFull($DB2, $T2);
  610. $type_T2 = mb_strtoupper($tables[$T2]['ENGINE']);
  611. if (Util::isForeignKeySupported($type_T1)
  612. && Util::isForeignKeySupported($type_T2)
  613. && $type_T1 == $type_T2
  614. ) {
  615. // InnoDB
  616. $existrel_foreign = $this->relation->getForeigners($DB2, $T2, '', 'foreign');
  617. $foreigner = $this->relation->searchColumnInForeigners($existrel_foreign, $F2);
  618. if (isset($foreigner['constraint'])) {
  619. $upd_query = 'ALTER TABLE ' . Util::backquote($DB2)
  620. . '.' . Util::backquote($T2) . ' DROP FOREIGN KEY '
  621. . Util::backquote($foreigner['constraint']) . ';';
  622. if ($GLOBALS['dbi']->query($upd_query)) {
  623. return array(true, __('FOREIGN KEY relationship has been removed.'));
  624. }
  625. $error = $GLOBALS['dbi']->getError();
  626. return array(
  627. false,
  628. __('Error: FOREIGN KEY relationship could not be removed!')
  629. . "<br/>" . $error
  630. );
  631. }
  632. }
  633. // internal relations
  634. $delete_query = "DELETE FROM "
  635. . Util::backquote($GLOBALS['cfgRelation']['db']) . "."
  636. . $GLOBALS['cfgRelation']['relation'] . " WHERE "
  637. . "master_db = '" . $GLOBALS['dbi']->escapeString($DB2) . "'"
  638. . " AND master_table = '" . $GLOBALS['dbi']->escapeString($T2) . "'"
  639. . " AND master_field = '" . $GLOBALS['dbi']->escapeString($F2) . "'"
  640. . " AND foreign_db = '" . $GLOBALS['dbi']->escapeString($DB1) . "'"
  641. . " AND foreign_table = '" . $GLOBALS['dbi']->escapeString($T1) . "'"
  642. . " AND foreign_field = '" . $GLOBALS['dbi']->escapeString($F1) . "'";
  643. $result = $this->relation->queryAsControlUser(
  644. $delete_query,
  645. false,
  646. DatabaseInterface::QUERY_STORE
  647. );
  648. if (!$result) {
  649. $error = $GLOBALS['dbi']->getError(DatabaseInterface::CONNECT_CONTROL);
  650. return array(
  651. false,
  652. __('Error: Internal relationship could not be removed!') . "<br/>" . $error
  653. );
  654. }
  655. return array(true, __('Internal relationship has been removed.'));
  656. }
  657. /**
  658. * Save value for a designer setting
  659. *
  660. * @param string $index setting
  661. * @param string $value value
  662. *
  663. * @return bool whether the operation succeeded
  664. */
  665. public function saveSetting($index, $value)
  666. {
  667. $cfgRelation = $this->relation->getRelationsParam();
  668. $cfgDesigner = array(
  669. 'user' => $GLOBALS['cfg']['Server']['user'],
  670. 'db' => $cfgRelation['db'],
  671. 'table' => $cfgRelation['designer_settings']
  672. );
  673. $success = true;
  674. if ($GLOBALS['cfgRelation']['designersettingswork']) {
  675. $orig_data_query = "SELECT settings_data"
  676. . " FROM " . Util::backquote($cfgDesigner['db'])
  677. . "." . Util::backquote($cfgDesigner['table'])
  678. . " WHERE username = '"
  679. . $GLOBALS['dbi']->escapeString($cfgDesigner['user']) . "';";
  680. $orig_data = $GLOBALS['dbi']->fetchSingleRow(
  681. $orig_data_query, 'ASSOC', DatabaseInterface::CONNECT_CONTROL
  682. );
  683. if (! empty($orig_data)) {
  684. $orig_data = json_decode($orig_data['settings_data'], true);
  685. $orig_data[$index] = $value;
  686. $orig_data = json_encode($orig_data);
  687. $save_query = "UPDATE "
  688. . Util::backquote($cfgDesigner['db'])
  689. . "." . Util::backquote($cfgDesigner['table'])
  690. . " SET settings_data = '" . $orig_data . "'"
  691. . " WHERE username = '"
  692. . $GLOBALS['dbi']->escapeString($cfgDesigner['user']) . "';";
  693. $success = $this->relation->queryAsControlUser($save_query);
  694. } else {
  695. $save_data = array($index => $value);
  696. $query = "INSERT INTO "
  697. . Util::backquote($cfgDesigner['db'])
  698. . "." . Util::backquote($cfgDesigner['table'])
  699. . " (username, settings_data)"
  700. . " VALUES('" . $cfgDesigner['user'] . "',"
  701. . " '" . json_encode($save_data) . "');";
  702. $success = $this->relation->queryAsControlUser($query);
  703. }
  704. }
  705. return (bool) $success;
  706. }
  707. }