Common.php 27 KB

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