Common.php 28 KB

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