init web ems all

This commit is contained in:
agtuser
2024-09-27 17:13:36 +08:00
parent 81c97acbe9
commit 5cc56f8078
4263 changed files with 798779 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* holds the PhpMyAdmin\Database\DatabaseList class
*
* @package PhpMyAdmin
*
*/
namespace PhpMyAdmin\Database;
use PhpMyAdmin\ListDatabase;
/**
* holds the DatabaseList class
*
* @package PhpMyAdmin
*/
class DatabaseList
{
/**
* Holds database list
*
* @var ListDatabase
*/
protected $databases = null;
/**
* magic access to protected/inaccessible members/properties
*
* @param string $param parameter name
*
* @return mixed
* @see https://www.php.net/language.oop5.overloading
*/
public function __get($param)
{
switch ($param) {
case 'databases' :
return $this->getDatabaseList();
}
return null;
}
/**
* Accessor to PMA::$databases
*
* @return ListDatabase
*/
public function getDatabaseList()
{
if (null === $this->databases) {
$this->databases = new ListDatabase();
}
return $this->databases;
}
}

View File

@@ -0,0 +1,432 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Database\Designer class
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Database;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Message;
use PhpMyAdmin\Plugins;
use PhpMyAdmin\Plugins\SchemaPlugin;
use PhpMyAdmin\Relation;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
/**
* Set of functions related to database designer
*
* @package PhpMyAdmin
*/
class Designer
{
/**
* @var Relation $relation
*/
private $relation;
/**
* Constructor
*/
public function __construct()
{
$this->relation = new Relation();
}
/**
* Function to get html for displaying the page edit/delete form
*
* @param string $db database name
* @param string $operation 'edit' or 'delete' depending on the operation
*
* @return string html content
*/
public function getHtmlForEditOrDeletePages($db, $operation)
{
$cfgRelation = $this->relation->getRelationsParam();
return Template::get('database/designer/edit_delete_pages')->render([
'db' => $db,
'operation' => $operation,
'pdfwork' => $cfgRelation['pdfwork'],
'pages' => $this->getPageIdsAndNames($db),
]);
}
/**
* Function to get html for displaying the page save as form
*
* @param string $db database name
*
* @return string html content
*/
public function getHtmlForPageSaveAs($db)
{
$cfgRelation = $this->relation->getRelationsParam();
return Template::get('database/designer/page_save_as')->render([
'db' => $db,
'pdfwork' => $cfgRelation['pdfwork'],
'pages' => $this->getPageIdsAndNames($db),
]);
}
/**
* Retrieve IDs and names of schema pages
*
* @param string $db database name
*
* @return array array of schema page id and names
*/
private function getPageIdsAndNames($db)
{
$result = [];
$cfgRelation = $this->relation->getRelationsParam();
if (! $cfgRelation['pdfwork']) {
return $result;
}
$page_query = "SELECT `page_nr`, `page_descr` FROM "
. Util::backquote($cfgRelation['db']) . "."
. Util::backquote($cfgRelation['pdf_pages'])
. " WHERE db_name = '" . $GLOBALS['dbi']->escapeString($db) . "'"
. " ORDER BY `page_descr`";
$page_rs = $this->relation->queryAsControlUser(
$page_query,
false,
DatabaseInterface::QUERY_STORE
);
while ($curr_page = $GLOBALS['dbi']->fetchAssoc($page_rs)) {
$result[intval($curr_page['page_nr'])] = $curr_page['page_descr'];
}
return $result;
}
/**
* Function to get html for displaying the schema export
*
* @param string $db database name
* @param int $page the page to be exported
*
* @return string
*/
public function getHtmlForSchemaExport($db, $page)
{
/* Scan for schema plugins */
/* @var $export_list SchemaPlugin[] */
$export_list = Plugins::getPlugins(
"schema",
'libraries/classes/Plugins/Schema/',
null
);
/* Fail if we didn't find any schema plugin */
if (empty($export_list)) {
return Message::error(
__('Could not load schema plugins, please check your installation!')
)->getDisplay();
}
return Template::get('database/designer/schema_export')
->render(
[
'db' => $db,
'page' => $page,
'export_list' => $export_list
]
);
}
/**
* Returns HTML for including some variable to be accessed by JavaScript
*
* @param array $script_tables array on foreign key support for each table
* @param array $script_contr initialization data array
* @param Designer\DesignerTable[] $script_display_field displayed tables in designer with their display fields
* @param int $display_page page number of the selected page
*
* @return string html
*/
public function getHtmlForJsFields(
array $script_tables,
array $script_contr,
array $script_display_field,
$display_page
) {
$displayedFields = [];
foreach ($script_display_field as $designerTable) {
if ($designerTable->getDisplayField() !== null) {
$displayedFields[$designerTable->getTableName()] = $designerTable->getDisplayField();
}
}
$cfgRelation = $this->relation->getRelationsParam();
$designerConfig = new \stdClass();
$designerConfig->db = $_GET['db'];
$designerConfig->scriptTables = $script_tables;
$designerConfig->scriptContr = $script_contr;
$designerConfig->server = $GLOBALS['server'];
$designerConfig->scriptDisplayField = $displayedFields;
$designerConfig->displayPage = (int) $display_page;
$designerConfig->tablesEnabled = $cfgRelation['pdfwork'];
return Template::get('database/designer/js_fields')->render([
'designer_config' => json_encode($designerConfig)
]);
}
/**
* Returns HTML for the menu bar of the designer page
*
* @param boolean $visualBuilder whether this is visual query builder
* @param string $selectedPage name of the selected page
* @param array $paramsArray array with class name for various buttons
* on side menu
*
* @return string html
*/
public function getPageMenu($visualBuilder, $selectedPage, array $paramsArray)
{
return Template::get('database/designer/side_menu')->render([
'visual_builder' => $visualBuilder,
'selected_page' => $selectedPage,
'params_array' => $paramsArray,
'theme' => $GLOBALS['PMA_Theme'],
]);
}
/**
* Returns array of stored values of Designer Settings
*
* @return array stored values
*/
private function getSideMenuParamsArray()
{
$params = [];
$cfgRelation = $this->relation->getRelationsParam();
if ($cfgRelation['designersettingswork']) {
$query = 'SELECT `settings_data` FROM '
. Util::backquote($cfgRelation['db']) . '.'
. Util::backquote($cfgRelation['designer_settings'])
. ' WHERE ' . Util::backquote('username') . ' = "'
. $GLOBALS['dbi']->escapeString($GLOBALS['cfg']['Server']['user'])
. '";';
$result = $GLOBALS['dbi']->fetchSingleRow($query);
$params = json_decode($result['settings_data'], true);
}
return $params;
}
/**
* Returns class names for various buttons on Designer Side Menu
*
* @return array class names of various buttons
*/
public function returnClassNamesFromMenuButtons()
{
$classes_array = [];
$params_array = $this->getSideMenuParamsArray();
if (isset($params_array['angular_direct'])
&& $params_array['angular_direct'] == 'angular'
) {
$classes_array['angular_direct'] = 'M_butt_Selected_down';
} else {
$classes_array['angular_direct'] = 'M_butt';
}
if (isset($params_array['snap_to_grid'])
&& $params_array['snap_to_grid'] == 'on'
) {
$classes_array['snap_to_grid'] = 'M_butt_Selected_down';
} else {
$classes_array['snap_to_grid'] = 'M_butt';
}
if (isset($params_array['pin_text'])
&& $params_array['pin_text'] == 'true'
) {
$classes_array['pin_text'] = 'M_butt_Selected_down';
} else {
$classes_array['pin_text'] = 'M_butt';
}
if (isset($params_array['relation_lines'])
&& $params_array['relation_lines'] == 'false'
) {
$classes_array['relation_lines'] = 'M_butt_Selected_down';
} else {
$classes_array['relation_lines'] = 'M_butt';
}
if (isset($params_array['small_big_all'])
&& $params_array['small_big_all'] == 'v'
) {
$classes_array['small_big_all'] = 'M_butt_Selected_down';
} else {
$classes_array['small_big_all'] = 'M_butt';
}
if (isset($params_array['side_menu'])
&& $params_array['side_menu'] == 'true'
) {
$classes_array['side_menu'] = 'M_butt_Selected_down';
} else {
$classes_array['side_menu'] = 'M_butt';
}
return $classes_array;
}
/**
* Returns HTML for the canvas element
*
* @return string html
*/
public function getHtmlCanvas()
{
return Template::get('database/designer/canvas')->render();
}
/**
* Return HTML for the table list
*
* @return string html
*/
public function getHtmlTableList()
{
return Template::get('database/designer/table_list')->render([
'theme' => $GLOBALS['PMA_Theme'],
]);
}
/**
* Get HTML to display tables on designer page
*
* @param string $db The database name from the request
* @param array $designerTables The designer tables
* @param array $tab_pos tables positions
* @param int $display_page page number of the selected page
* @param array $tab_column table column info
* @param array $tables_all_keys all indices
* @param array $tables_pk_or_unique_keys unique or primary indices
*
* @return string html
*/
public function getDatabaseTables(
$db,
array $designerTables,
array $tab_pos,
$display_page,
array $tab_column,
array $tables_all_keys,
array $tables_pk_or_unique_keys
) {
return Template::get('database/designer/database_tables')->render([
'db' => $GLOBALS['db'],
'get_db' => $db,
'has_query' => isset($_REQUEST['query']),
'tab_pos' => $tab_pos,
'display_page' => $display_page,
'tab_column' => $tab_column,
'tables_all_keys' => $tables_all_keys,
'tables_pk_or_unique_keys' => $tables_pk_or_unique_keys,
'tables' => $designerTables,
'theme' => $GLOBALS['PMA_Theme'],
]);
}
/**
* Returns HTML for the new relations panel.
*
* @return string html
*/
public function getNewRelationPanel()
{
return Template::get('database/designer/new_relation_panel')
->render();
}
/**
* Returns HTML for the relations delete panel
*
* @return string html
*/
public function getDeleteRelationPanel()
{
return Template::get('database/designer/delete_relation_panel')
->render();
}
/**
* Returns HTML for the options panel
*
* @return string html
*/
public function getOptionsPanel()
{
return Template::get('database/designer/options_panel')->render();
}
/**
* Get HTML for the 'rename to' panel
*
* @return string html
*/
public function getRenameToPanel()
{
return Template::get('database/designer/rename_to_panel')
->render();
}
/**
* Returns HTML for the 'having' panel
*
* @return string html
*/
public function getHavingQueryPanel()
{
return Template::get('database/designer/having_query_panel')
->render();
}
/**
* Returns HTML for the 'aggregate' panel
*
* @return string html
*/
public function getAggregateQueryPanel()
{
return Template::get('database/designer/aggregate_query_panel')
->render();
}
/**
* Returns HTML for the 'where' panel
*
* @return string html
*/
public function getWhereQueryPanel()
{
return Template::get('database/designer/where_query_panel')
->render();
}
/**
* Returns HTML for the query details panel
*
* @param string $db Database name
*
* @return string html
*/
public function getQueryDetails($db)
{
return Template::get('database/designer/query_details')->render([
'db' => $db,
]);
}
}

View File

@@ -0,0 +1,792 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Database\Designer\Common class
*
* @package PhpMyAdmin-Designer
*/
namespace PhpMyAdmin\Database\Designer;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Index;
use PhpMyAdmin\Relation;
use PhpMyAdmin\Table;
use PhpMyAdmin\Util;
use PhpMyAdmin\Database\Designer\DesignerTable;
/**
* Common functions for Designer
*
* @package PhpMyAdmin-Designer
*/
class Common
{
/**
* @var Relation $relation
*/
private $relation;
/**
* Constructor
*/
public function __construct()
{
$this->relation = new Relation();
}
/**
* Retrieves table info and returns it
*
* @param string $db (optional) Filter only a DB ($table is required if you use $db)
* @param string $table (optional) Filter only a table ($db is now required)
* @return DesignerTable[] with table info
*/
public function getTablesInfo($db = null, $table = null)
{
$designerTables = array();
$db = ($db === null) ? $GLOBALS['db'] : $db;
// seems to be needed later
$GLOBALS['dbi']->selectDb($db);
if ($db === null && $table === null) {
$tables = $GLOBALS['dbi']->getTablesFull($db);
} else {
$tables = $GLOBALS['dbi']->getTablesFull($db, $table);
}
foreach ($tables as $one_table) {
$DF = $this->relation->getDisplayField($db, $one_table['TABLE_NAME']);
$DF = is_string($DF) ? $DF : '';
$DF = ($DF !== '') ? $DF : null;
$designerTables[] = new DesignerTable(
$db,
$one_table['TABLE_NAME'],
is_string($one_table['ENGINE']) ? $one_table['ENGINE'] : '',
$DF
);
}
return $designerTables;
}
/**
* Retrieves table column info
*
* @param DesignerTable[] $designerTables The designer tables
* @return array table column nfo
*/
public function getColumnsInfo($designerTables)
{
//$GLOBALS['dbi']->selectDb($GLOBALS['db']);
$tabColumn = array();
foreach($designerTables as $designerTable) {
$fieldsRs = $GLOBALS['dbi']->query(
$GLOBALS['dbi']->getColumnsSql(
$designerTable->getDatabaseName(),
$designerTable->getTableName(),
null,
true
),
DatabaseInterface::CONNECT_USER,
DatabaseInterface::QUERY_STORE
);
$j = 0;
while ($row = $GLOBALS['dbi']->fetchAssoc($fieldsRs)) {
if (! isset($tabColumn[$designerTable->getDbTableString()])) {
$tabColumn[$designerTable->getDbTableString()] = [];
}
$tabColumn[$designerTable->getDbTableString()]['COLUMN_ID'][$j] = $j;
$tabColumn[$designerTable->getDbTableString()]['COLUMN_NAME'][$j] = $row['Field'];
$tabColumn[$designerTable->getDbTableString()]['TYPE'][$j] = $row['Type'];
$tabColumn[$designerTable->getDbTableString()]['NULLABLE'][$j] = $row['Null'];
$j++;
}
}
return $tabColumn;
}
/**
* Returns JavaScript code for initializing vars
*
* @param DesignerTable[] $designerTables The designer tables
* @return string JavaScript code
*/
public function getScriptContr($designerTables)
{
$GLOBALS['dbi']->selectDb($GLOBALS['db']);
$con = array();
$con["C_NAME"] = array();
$i = 0;
$alltab_rs = $GLOBALS['dbi']->query(
'SHOW TABLES FROM ' . Util::backquote($GLOBALS['db']),
DatabaseInterface::CONNECT_USER,
DatabaseInterface::QUERY_STORE
);
while ($val = @$GLOBALS['dbi']->fetchRow($alltab_rs)) {
$row = $this->relation->getForeigners($GLOBALS['db'], $val[0], '', 'internal');
if ($row !== false) {
foreach ($row as $field => $value) {
$con['C_NAME'][$i] = '';
$con['DTN'][$i] = rawurlencode($GLOBALS['db'] . "." . $val[0]);
$con['DCN'][$i] = rawurlencode($field);
$con['STN'][$i] = rawurlencode(
$value['foreign_db'] . "." . $value['foreign_table']
);
$con['SCN'][$i] = rawurlencode($value['foreign_field']);
$i++;
}
}
$row = $this->relation->getForeigners($GLOBALS['db'], $val[0], '', 'foreign');
// We do not have access to the foreign keys if he user has partial access to the columns
if ($row !== false && isset($row['foreign_keys_data'])) {
foreach ($row['foreign_keys_data'] as $one_key) {
foreach ($one_key['index_list'] as $index => $one_field) {
$con['C_NAME'][$i] = rawurlencode($one_key['constraint']);
$con['DTN'][$i] = rawurlencode($GLOBALS['db'] . "." . $val[0]);
$con['DCN'][$i] = rawurlencode($one_field);
$con['STN'][$i] = rawurlencode(
(isset($one_key['ref_db_name']) ?
$one_key['ref_db_name'] : $GLOBALS['db'])
. "." . $one_key['ref_table_name']
);
$con['SCN'][$i] = rawurlencode($one_key['ref_index_list'][$index]);
$i++;
}
}
}
}
$tableDbNames = [];
foreach($designerTables as $designerTable) {
$tableDbNames[] = $designerTable->getDbTableString();
}
$ti = 0;
$retval = array();
for ($i = 0, $cnt = count($con["C_NAME"]); $i < $cnt; $i++) {
$c_name_i = $con['C_NAME'][$i];
$dtn_i = $con['DTN'][$i];
$retval[$ti] = array();
$retval[$ti][$c_name_i] = array();
if (in_array($dtn_i, $tableDbNames) && in_array($con['STN'][$i], $tableDbNames)) {
$retval[$ti][$c_name_i][$dtn_i] = array();
$retval[$ti][$c_name_i][$dtn_i][$con['DCN'][$i]] = array(
0 => $con['STN'][$i],
1 => $con['SCN'][$i]
);
}
$ti++;
}
return $retval;
}
/**
* Returns UNIQUE and PRIMARY indices
*
* @param DesignerTable[] $designerTables The designer tables
* @return array unique or primary indices
*/
public function getPkOrUniqueKeys($designerTables)
{
return $this->getAllKeys($designerTables, true);
}
/**
* Returns all indices
*
* @param DesignerTable[] $designerTables The designer tables
* @param bool $unique_only whether to include only unique ones
*
* @return array indices
*/
public function getAllKeys($designerTables, $unique_only = false)
{
$keys = array();
foreach ($designerTables as $designerTable) {
$schema = $designerTable->getDatabaseName();
// for now, take into account only the first index segment
foreach (Index::getFromTable($designerTable->getTableName(), $schema) as $index) {
if ($unique_only && ! $index->isUnique()) {
continue;
}
$columns = $index->getColumns();
foreach ($columns as $column_name => $dummy) {
$keys[$schema . '.' . $designerTable->getTableName() . '.' . $column_name] = 1;
}
}
}
return $keys;
}
/**
* Return j_tab and h_tab arrays
*
* @param DesignerTable[] $designerTables The designer tables
* @return array
*/
public function getScriptTabs($designerTables)
{
$retval = array(
'j_tabs' => array(),
'h_tabs' => array()
);
foreach($designerTables as $designerTable) {
$key = rawurlencode($designerTable->getDbTableString());
$retval['j_tabs'][$key] = $designerTable->supportsForeignkeys() ? 1 : 0;
$retval['h_tabs'][$key] = 1;
}
return $retval;
}
/**
* Returns table positions of a given pdf page
*
* @param int $pg pdf page id
*
* @return array of table positions
*/
public function getTablePositions($pg)
{
$cfgRelation = $this->relation->getRelationsParam();
if (! $cfgRelation['pdfwork']) {
return array();
}
$query = "
SELECT CONCAT_WS('.', `db_name`, `table_name`) AS `name`,
`db_name` as `dbName`, `table_name` as `tableName`,
`x` AS `X`,
`y` AS `Y`,
1 AS `V`,
1 AS `H`
FROM " . Util::backquote($cfgRelation['db'])
. "." . Util::backquote($cfgRelation['table_coords']) . "
WHERE pdf_page_number = " . intval($pg);
$tab_pos = $GLOBALS['dbi']->fetchResult(
$query,
'name',
null,
DatabaseInterface::CONNECT_CONTROL,
DatabaseInterface::QUERY_STORE
);
return $tab_pos;
}
/**
* Returns page name of a given pdf page
*
* @param int $pg pdf page id
*
* @return string|null table name
*/
public function getPageName($pg)
{
$cfgRelation = $this->relation->getRelationsParam();
if (! $cfgRelation['pdfwork']) {
return null;
}
$query = "SELECT `page_descr`"
. " FROM " . Util::backquote($cfgRelation['db'])
. "." . Util::backquote($cfgRelation['pdf_pages'])
. " WHERE " . Util::backquote('page_nr') . " = " . intval($pg);
$page_name = $GLOBALS['dbi']->fetchResult(
$query,
null,
null,
DatabaseInterface::CONNECT_CONTROL,
DatabaseInterface::QUERY_STORE
);
return ( is_array($page_name) && isset($page_name[0]) ) ? $page_name[0] : null;
}
/**
* Deletes a given pdf page and its corresponding coordinates
*
* @param int $pg page id
*
* @return boolean success/failure
*/
public function deletePage($pg)
{
$cfgRelation = $this->relation->getRelationsParam();
if (! $cfgRelation['pdfwork']) {
return false;
}
$query = "DELETE FROM " . Util::backquote($cfgRelation['db'])
. "." . Util::backquote($cfgRelation['table_coords'])
. " WHERE " . Util::backquote('pdf_page_number') . " = " . intval($pg);
$success = $this->relation->queryAsControlUser(
$query, true, DatabaseInterface::QUERY_STORE
);
if ($success) {
$query = "DELETE FROM " . Util::backquote($cfgRelation['db'])
. "." . Util::backquote($cfgRelation['pdf_pages'])
. " WHERE " . Util::backquote('page_nr') . " = " . intval($pg);
$success = $this->relation->queryAsControlUser(
$query, true, DatabaseInterface::QUERY_STORE
);
}
return (boolean) $success;
}
/**
* Returns the id of the default pdf page of the database.
* Default page is the one which has the same name as the database.
*
* @param string $db database
*
* @return int id of the default pdf page for the database
*/
public function getDefaultPage($db)
{
$cfgRelation = $this->relation->getRelationsParam();
if (! $cfgRelation['pdfwork']) {
return -1;
}
$query = "SELECT `page_nr`"
. " FROM " . Util::backquote($cfgRelation['db'])
. "." . Util::backquote($cfgRelation['pdf_pages'])
. " WHERE `db_name` = '" . $GLOBALS['dbi']->escapeString($db) . "'"
. " AND `page_descr` = '" . $GLOBALS['dbi']->escapeString($db) . "'";
$default_page_no = $GLOBALS['dbi']->fetchResult(
$query,
null,
null,
DatabaseInterface::CONNECT_CONTROL,
DatabaseInterface::QUERY_STORE
);
if (is_array($default_page_no) && isset($default_page_no[0])) {
return intval($default_page_no[0]);
}
return -1;
}
/**
* Get the id of the page to load. If a default page exists it will be returned.
* If no such exists, returns the id of the first page of the database.
*
* @param string $db database
*
* @return int id of the page to load
*/
public function getLoadingPage($db)
{
$cfgRelation = $this->relation->getRelationsParam();
if (! $cfgRelation['pdfwork']) {
return -1;
}
$page_no = -1;
$default_page_no = $this->getDefaultPage($db);
if ($default_page_no != -1) {
$page_no = $default_page_no;
} else {
$query = "SELECT MIN(`page_nr`)"
. " FROM " . Util::backquote($cfgRelation['db'])
. "." . Util::backquote($cfgRelation['pdf_pages'])
. " WHERE `db_name` = '" . $GLOBALS['dbi']->escapeString($db) . "'";
$min_page_no = $GLOBALS['dbi']->fetchResult(
$query,
null,
null,
DatabaseInterface::CONNECT_CONTROL,
DatabaseInterface::QUERY_STORE
);
if (is_array($min_page_no) && isset($min_page_no[0])) {
$page_no = $min_page_no[0];
}
}
return intval($page_no);
}
/**
* Creates a new page and returns its auto-incrementing id
*
* @param string $pageName name of the page
* @param string $db name of the database
*
* @return int|null
*/
public function createNewPage($pageName, $db)
{
$cfgRelation = $this->relation->getRelationsParam();
if ($cfgRelation['pdfwork']) {
$pageNumber = $this->relation->createPage(
$pageName,
$cfgRelation,
$db
);
return $pageNumber;
}
return null;
}
/**
* Saves positions of table(s) of a given pdf page
*
* @param int $pg pdf page id
*
* @return boolean success/failure
*/
public function saveTablePositions($pg)
{
$pageId = $GLOBALS['dbi']->escapeString($pg);
$db = $GLOBALS['dbi']->escapeString($_POST['db']);
$cfgRelation = $this->relation->getRelationsParam();
if (! $cfgRelation['pdfwork']) {
return false;
}
$query = "DELETE FROM "
. Util::backquote($cfgRelation['db'])
. "." . Util::backquote(
$cfgRelation['table_coords']
)
. " WHERE `pdf_page_number` = '" . $pageId . "'";
$res = $this->relation->queryAsControlUser(
$query,
true,
DatabaseInterface::QUERY_STORE
);
if (!$res) {
return (boolean)$res;
}
foreach ($_POST['t_h'] as $key => $value) {
$DB = $_POST['t_db'][$key];
$TAB = $_POST['t_tbl'][$key];
if (!$value) {
continue;
}
$query = "INSERT INTO "
. Util::backquote($cfgRelation['db']) . "."
. Util::backquote($cfgRelation['table_coords'])
. " (`db_name`, `table_name`, `pdf_page_number`, `x`, `y`)"
. " VALUES ("
. "'" . $GLOBALS['dbi']->escapeString($DB) . "', "
. "'" . $GLOBALS['dbi']->escapeString($TAB) . "', "
. "'" . $pageId . "', "
. "'" . $GLOBALS['dbi']->escapeString($_POST['t_x'][$key]) . "', "
. "'" . $GLOBALS['dbi']->escapeString($_POST['t_y'][$key]) . "')";
$res = $this->relation->queryAsControlUser(
$query, true, DatabaseInterface::QUERY_STORE
);
}
return (boolean) $res;
}
/**
* Saves the display field for a table.
*
* @param string $db database name
* @param string $table table name
* @param string $field display field name
*
* @return array<bool,string>
*/
public function saveDisplayField($db, $table, $field)
{
$cfgRelation = $this->relation->getRelationsParam();
if (! $cfgRelation['displaywork']) {
return [
false,
_pgettext(
'phpMyAdmin configuration storage is not configured for "Display Features" on designer when user tries to set a display field.',
'phpMyAdmin configuration storage is not configured for "Display Features".'
),
];
}
$upd_query = new Table($table, $db, $GLOBALS['dbi']);
$upd_query->updateDisplayField($field, $cfgRelation);
return [
true,
null,
];
}
/**
* Adds a new foreign relation
*
* @param string $db database name
* @param string $T1 foreign table
* @param string $F1 foreign field
* @param string $T2 master table
* @param string $F2 master field
* @param string $on_delete on delete action
* @param string $on_update on update action
* @param string $DB1 database
* @param string $DB2 database
*
* @return array array of success/failure and message
*/
public function addNewRelation($db, $T1, $F1, $T2, $F2, $on_delete, $on_update, $DB1, $DB2)
{
$tables = $GLOBALS['dbi']->getTablesFull($DB1, $T1);
$type_T1 = mb_strtoupper($tables[$T1]['ENGINE']);
$tables = $GLOBALS['dbi']->getTablesFull($DB2, $T2);
$type_T2 = mb_strtoupper($tables[$T2]['ENGINE']);
// native foreign key
if (Util::isForeignKeySupported($type_T1)
&& Util::isForeignKeySupported($type_T2)
&& $type_T1 == $type_T2
) {
// relation exists?
$existrel_foreign = $this->relation->getForeigners($DB2, $T2, '', 'foreign');
$foreigner = $this->relation->searchColumnInForeigners($existrel_foreign, $F2);
if ($foreigner
&& isset($foreigner['constraint'])
) {
return array(false, __('Error: relationship already exists.'));
}
// note: in InnoDB, the index does not requires to be on a PRIMARY
// or UNIQUE key
// improve: check all other requirements for InnoDB relations
$result = $GLOBALS['dbi']->query(
'SHOW INDEX FROM ' . Util::backquote($DB1)
. '.' . Util::backquote($T1) . ';'
);
// will be use to emphasis prim. keys in the table view
$index_array1 = array();
while ($row = $GLOBALS['dbi']->fetchAssoc($result)) {
$index_array1[$row['Column_name']] = 1;
}
$GLOBALS['dbi']->freeResult($result);
$result = $GLOBALS['dbi']->query(
'SHOW INDEX FROM ' . Util::backquote($DB2)
. '.' . Util::backquote($T2) . ';'
);
// will be used to emphasis prim. keys in the table view
$index_array2 = array();
while ($row = $GLOBALS['dbi']->fetchAssoc($result)) {
$index_array2[$row['Column_name']] = 1;
}
$GLOBALS['dbi']->freeResult($result);
if (! empty($index_array1[$F1]) && ! empty($index_array2[$F2])) {
$upd_query = 'ALTER TABLE ' . Util::backquote($DB2)
. '.' . Util::backquote($T2)
. ' ADD FOREIGN KEY ('
. Util::backquote($F2) . ')'
. ' REFERENCES '
. Util::backquote($DB1) . '.'
. Util::backquote($T1) . '('
. Util::backquote($F1) . ')';
if ($on_delete != 'nix') {
$upd_query .= ' ON DELETE ' . $on_delete;
}
if ($on_update != 'nix') {
$upd_query .= ' ON UPDATE ' . $on_update;
}
$upd_query .= ';';
if ($GLOBALS['dbi']->tryQuery($upd_query)) {
return array(true, __('FOREIGN KEY relationship has been added.'));
}
$error = $GLOBALS['dbi']->getError();
return array(
false,
__('Error: FOREIGN KEY relationship could not be added!')
. "<br/>" . $error
);
}
return array(false, __('Error: Missing index on column(s).'));
}
// internal (pmadb) relation
if ($GLOBALS['cfgRelation']['relwork'] == false) {
return array(false, __('Error: Relational features are disabled!'));
}
// no need to recheck if the keys are primary or unique at this point,
// this was checked on the interface part
$q = "INSERT INTO "
. Util::backquote($GLOBALS['cfgRelation']['db'])
. "."
. Util::backquote($GLOBALS['cfgRelation']['relation'])
. "(master_db, master_table, master_field, "
. "foreign_db, foreign_table, foreign_field)"
. " values("
. "'" . $GLOBALS['dbi']->escapeString($DB2) . "', "
. "'" . $GLOBALS['dbi']->escapeString($T2) . "', "
. "'" . $GLOBALS['dbi']->escapeString($F2) . "', "
. "'" . $GLOBALS['dbi']->escapeString($DB1) . "', "
. "'" . $GLOBALS['dbi']->escapeString($T1) . "', "
. "'" . $GLOBALS['dbi']->escapeString($F1) . "')";
if ($this->relation->queryAsControlUser($q, false, DatabaseInterface::QUERY_STORE)
) {
return array(true, __('Internal relationship has been added.'));
}
$error = $GLOBALS['dbi']->getError(DatabaseInterface::CONNECT_CONTROL);
return array(
false,
__('Error: Internal relationship could not be added!')
. "<br/>" . $error
);
}
/**
* Removes a foreign relation
*
* @param string $T1 foreign db.table
* @param string $F1 foreign field
* @param string $T2 master db.table
* @param string $F2 master field
*
* @return array array of success/failure and message
*/
public function removeRelation($T1, $F1, $T2, $F2)
{
list($DB1, $T1) = explode(".", $T1);
list($DB2, $T2) = explode(".", $T2);
$tables = $GLOBALS['dbi']->getTablesFull($DB1, $T1);
$type_T1 = mb_strtoupper($tables[$T1]['ENGINE']);
$tables = $GLOBALS['dbi']->getTablesFull($DB2, $T2);
$type_T2 = mb_strtoupper($tables[$T2]['ENGINE']);
if (Util::isForeignKeySupported($type_T1)
&& Util::isForeignKeySupported($type_T2)
&& $type_T1 == $type_T2
) {
// InnoDB
$existrel_foreign = $this->relation->getForeigners($DB2, $T2, '', 'foreign');
$foreigner = $this->relation->searchColumnInForeigners($existrel_foreign, $F2);
if (isset($foreigner['constraint'])) {
$upd_query = 'ALTER TABLE ' . Util::backquote($DB2)
. '.' . Util::backquote($T2) . ' DROP FOREIGN KEY '
. Util::backquote($foreigner['constraint']) . ';';
if ($GLOBALS['dbi']->query($upd_query)) {
return array(true, __('FOREIGN KEY relationship has been removed.'));
}
$error = $GLOBALS['dbi']->getError();
return array(
false,
__('Error: FOREIGN KEY relationship could not be removed!')
. "<br/>" . $error
);
}
}
// internal relations
$delete_query = "DELETE FROM "
. Util::backquote($GLOBALS['cfgRelation']['db']) . "."
. $GLOBALS['cfgRelation']['relation'] . " WHERE "
. "master_db = '" . $GLOBALS['dbi']->escapeString($DB2) . "'"
. " AND master_table = '" . $GLOBALS['dbi']->escapeString($T2) . "'"
. " AND master_field = '" . $GLOBALS['dbi']->escapeString($F2) . "'"
. " AND foreign_db = '" . $GLOBALS['dbi']->escapeString($DB1) . "'"
. " AND foreign_table = '" . $GLOBALS['dbi']->escapeString($T1) . "'"
. " AND foreign_field = '" . $GLOBALS['dbi']->escapeString($F1) . "'";
$result = $this->relation->queryAsControlUser(
$delete_query,
false,
DatabaseInterface::QUERY_STORE
);
if (!$result) {
$error = $GLOBALS['dbi']->getError(DatabaseInterface::CONNECT_CONTROL);
return array(
false,
__('Error: Internal relationship could not be removed!') . "<br/>" . $error
);
}
return array(true, __('Internal relationship has been removed.'));
}
/**
* Save value for a designer setting
*
* @param string $index setting
* @param string $value value
*
* @return bool whether the operation succeeded
*/
public function saveSetting($index, $value)
{
$cfgRelation = $this->relation->getRelationsParam();
$success = true;
if ($cfgRelation['designersettingswork']) {
$cfgDesigner = array(
'user' => $GLOBALS['cfg']['Server']['user'],
'db' => $cfgRelation['db'],
'table' => $cfgRelation['designer_settings']
);
$orig_data_query = "SELECT settings_data"
. " FROM " . Util::backquote($cfgDesigner['db'])
. "." . Util::backquote($cfgDesigner['table'])
. " WHERE username = '"
. $GLOBALS['dbi']->escapeString($cfgDesigner['user']) . "';";
$orig_data = $GLOBALS['dbi']->fetchSingleRow(
$orig_data_query, 'ASSOC', DatabaseInterface::CONNECT_CONTROL
);
if (! empty($orig_data)) {
$orig_data = json_decode($orig_data['settings_data'], true);
$orig_data[$index] = $value;
$orig_data = json_encode($orig_data);
$save_query = "UPDATE "
. Util::backquote($cfgDesigner['db'])
. "." . Util::backquote($cfgDesigner['table'])
. " SET settings_data = '" . $orig_data . "'"
. " WHERE username = '"
. $GLOBALS['dbi']->escapeString($cfgDesigner['user']) . "';";
$success = $this->relation->queryAsControlUser($save_query);
} else {
$save_data = array($index => $value);
$query = "INSERT INTO "
. Util::backquote($cfgDesigner['db'])
. "." . Util::backquote($cfgDesigner['table'])
. " (username, settings_data)"
. " VALUES('" . $GLOBALS['dbi']->escapeString($cfgDesigner['user'])
. "', '" . json_encode($save_data) . "');";
$success = $this->relation->queryAsControlUser($query);
}
}
return (bool) $success;
}
}

View File

@@ -0,0 +1,98 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Holds the PhpMyAdmin\Database\Designer\DesignerTable class
*
* @package PhpMyAdmin-Designer
*/
namespace PhpMyAdmin\Database\Designer;
use PhpMyAdmin\Util;
/**
* Common functions for Designer
*
* @package PhpMyAdmin-Designer
*/
class DesignerTable
{
private $tableName;
private $databaseName;
private $tableEngine;
private $displayField;
/**
* Create a new DesignerTable
*
* @param string $databaseName The database name
* @param string $tableName The table name
* @param string $tableEngine The table engine
* @param string|null $displayField The display field if available
*/
public function __construct(
$databaseName,
$tableName,
$tableEngine,
$displayField
) {
$this->databaseName = $databaseName;
$this->tableName = $tableName;
$this->tableEngine = $tableEngine;
$this->displayField = $displayField;
}
/**
* The table engine supports or not foreign keys
*
* @return bool
*/
public function supportsForeignkeys() {
return Util::isForeignKeySupported($this->tableEngine);
}
/**
* Get the database name
*
* @return string
*/
public function getDatabaseName() {
return $this->databaseName;
}
/**
* Get the table name
*
* @return string
*/
public function getTableName() {
return $this->tableName;
}
/**
* Get the table engine
*
* @return string
*/
public function getTableEngine() {
return $this->tableEngine;
}
/**
* Get the displayed field
*
* @return string
*/
public function getDisplayField()
{
return $this->displayField;
}
/**
* Get the db and table separated with a dot
*
* @return string
*/
public function getDbTableString() {
return $this->databaseName . '.' . $this->tableName;
}
}

View File

@@ -0,0 +1,135 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Handles DB Multi-table query
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Database;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\ParseAnalyze;
use PhpMyAdmin\Sql;
use PhpMyAdmin\Template;
/**
* Class to handle database Multi-table querying
*
* @package PhpMyAdmin
*/
class MultiTableQuery
{
/**
* DatabaseInterface instance
*
* @access private
* @var DatabaseInterface
*/
private $dbi;
/**
* Database name
*
* @access private
* @var string
*/
private $db;
/**
* Default number of columns
*
* @access private
* @var integer
*/
private $defaultNoOfColumns;
/**
* Table names
*
* @access private
* @var array
*/
private $tables;
/**
* Constructor
*
* @param DatabaseInterface $dbi DatabaseInterface instance
* @param string $dbName Database name
* @param integer $defaultNoOfColumns Default number of columns
*/
public function __construct(
DatabaseInterface $dbi,
$dbName,
$defaultNoOfColumns = 3
) {
$this->dbi = $dbi;
$this->db = $dbName;
$this->defaultNoOfColumns = $defaultNoOfColumns;
$this->tables = $this->dbi->getTables($this->db);
}
/**
* Get Multi-Table query page HTML
*
* @return string Multi-Table query page HTML
*/
public function getFormHtml()
{
$tables = [];
foreach($this->tables as $table) {
$tables[$table]['hash'] = md5($table);
$tables[$table]['columns'] = array_keys(
$this->dbi->getColumns($this->db, $table)
);
}
return Template::get('database/multi_table_query/form')->render([
'db' => $this->db,
'tables' => $tables,
'default_no_of_columns' => $this->defaultNoOfColumns,
]);
}
/**
* Displays multi-table query results
*
* @param string $sqlQuery The query to parse
* @param string $db The current database
* @param string $pmaThemeImage Uri of the PMA theme image
*
* @return void
*/
public static function displayResults($sqlQuery, $db, $pmaThemeImage)
{
list(
$analyzedSqlResults,
$db,
$tableFromSql
) = ParseAnalyze::sqlQuery($sqlQuery, $db);
extract($analyzedSqlResults);
$goto = 'db_multi_table_query.php';
$sql = new Sql();
$sql->executeQueryAndSendQueryResponse(
null, // analyzed_sql_results
false, // is_gotofile
$db, // db
null, // table
null, // find_real_end
null, // sql_query_for_bookmark - see below
null, // extra_data
null, // message_to_show
null, // message
null, // sql_data
$goto, // goto
$pmaThemeImage, // pmaThemeImage
null, // disp_query
null, // disp_message
null, // query_type
$sqlQuery, // sql_query
null, // selectedTables
null // complete_query
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,339 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Handles Database Search
*
* @package PhpMyAdmin
*/
namespace PhpMyAdmin\Database;
use PhpMyAdmin\Template;
use PhpMyAdmin\Util;
/**
* Class to handle database search
*
* @package PhpMyAdmin
*/
class Search
{
/**
* Database name
*
* @access private
* @var string
*/
private $db;
/**
* Table Names
*
* @access private
* @var array
*/
private $tablesNamesOnly;
/**
* Type of search
*
* @access private
* @var array
*/
private $searchTypes;
/**
* Already set search type
*
* @access private
* @var integer
*/
private $criteriaSearchType;
/**
* Already set search type's description
*
* @access private
* @var string
*/
private $searchTypeDescription;
/**
* Search string/regexp
*
* @access private
* @var string
*/
private $criteriaSearchString;
/**
* Criteria Tables to search in
*
* @access private
* @var array
*/
private $criteriaTables;
/**
* Restrict the search to this column
*
* @access private
* @var string
*/
private $criteriaColumnName;
/**
* Public Constructor
*
* @param string $db Database name
*/
public function __construct($db)
{
$this->db = $db;
$this->searchTypes = array(
'1' => __('at least one of the words'),
'2' => __('all of the words'),
'3' => __('the exact phrase as substring'),
'4' => __('the exact phrase as whole field'),
'5' => __('as regular expression'),
);
// Sets criteria parameters
$this->setSearchParams();
}
/**
* Sets search parameters
*
* @return void
*/
private function setSearchParams()
{
$this->tablesNamesOnly = $GLOBALS['dbi']->getTables($this->db);
if (empty($_POST['criteriaSearchType'])
|| ! is_string($_POST['criteriaSearchType'])
|| ! array_key_exists(
$_POST['criteriaSearchType'],
$this->searchTypes
)
) {
$this->criteriaSearchType = 1;
unset($_POST['submit_search']);
} else {
$this->criteriaSearchType = (int) $_POST['criteriaSearchType'];
$this->searchTypeDescription
= $this->searchTypes[$_POST['criteriaSearchType']];
}
if (empty($_POST['criteriaSearchString'])
|| ! is_string($_POST['criteriaSearchString'])
) {
$this->criteriaSearchString = '';
unset($_POST['submit_search']);
} else {
$this->criteriaSearchString = $_POST['criteriaSearchString'];
}
$this->criteriaTables = array();
if (empty($_POST['criteriaTables'])
|| ! is_array($_POST['criteriaTables'])
) {
unset($_POST['submit_search']);
} else {
$this->criteriaTables = array_intersect(
$_POST['criteriaTables'], $this->tablesNamesOnly
);
}
if (empty($_POST['criteriaColumnName'])
|| ! is_string($_POST['criteriaColumnName'])
) {
unset($this->criteriaColumnName);
} else {
$this->criteriaColumnName = $GLOBALS['dbi']->escapeString(
$_POST['criteriaColumnName']
);
}
}
/**
* Builds the SQL search query
*
* @param string $table The table name
*
* @return array 3 SQL queries (for count, display and delete results)
*
* @todo can we make use of fulltextsearch IN BOOLEAN MODE for this?
* PMA_backquote
* DatabaseInterface::freeResult
* DatabaseInterface::fetchAssoc
* $GLOBALS['db']
* explode
* count
* strlen
*/
private function getSearchSqls($table)
{
// Statement types
$sqlstr_select = 'SELECT';
$sqlstr_delete = 'DELETE';
// Table to use
$sqlstr_from = ' FROM '
. Util::backquote($GLOBALS['db']) . '.'
. Util::backquote($table);
// Gets where clause for the query
$where_clause = $this->getWhereClause($table);
// Builds complete queries
$sql = array();
$sql['select_columns'] = $sqlstr_select . ' * ' . $sqlstr_from
. $where_clause;
// here, I think we need to still use the COUNT clause, even for
// VIEWs, anyway we have a WHERE clause that should limit results
$sql['select_count'] = $sqlstr_select . ' COUNT(*) AS `count`'
. $sqlstr_from . $where_clause;
$sql['delete'] = $sqlstr_delete . $sqlstr_from . $where_clause;
return $sql;
}
/**
* Provides where clause for building SQL query
*
* @param string $table The table name
*
* @return string The generated where clause
*/
private function getWhereClause($table)
{
// Columns to select
$allColumns = $GLOBALS['dbi']->getColumns($GLOBALS['db'], $table);
$likeClauses = array();
// Based on search type, decide like/regex & '%'/''
$like_or_regex = (($this->criteriaSearchType == 5) ? 'REGEXP' : 'LIKE');
$automatic_wildcard = (($this->criteriaSearchType < 4) ? '%' : '');
// For "as regular expression" (search option 5), LIKE won't be used
// Usage example: If user is searching for a literal $ in a regexp search,
// he should enter \$ as the value.
$criteriaSearchStringEscaped = $GLOBALS['dbi']->escapeString(
$this->criteriaSearchString
);
// Extract search words or pattern
$search_words = (($this->criteriaSearchType > 2)
? array($criteriaSearchStringEscaped)
: explode(' ', $criteriaSearchStringEscaped));
foreach ($search_words as $search_word) {
// Eliminates empty values
if (strlen($search_word) === 0) {
continue;
}
$likeClausesPerColumn = array();
// for each column in the table
foreach ($allColumns as $column) {
if (! isset($this->criteriaColumnName)
|| strlen($this->criteriaColumnName) === 0
|| $column['Field'] == $this->criteriaColumnName
) {
$column = 'CONVERT(' . Util::backquote($column['Field'])
. ' USING utf8)';
$likeClausesPerColumn[] = $column . ' ' . $like_or_regex . ' '
. "'"
. $automatic_wildcard . $search_word . $automatic_wildcard
. "'";
}
} // end for
if (count($likeClausesPerColumn) > 0) {
$likeClauses[] = implode(' OR ', $likeClausesPerColumn);
}
} // end for
// Use 'OR' if 'at least one word' is to be searched, else use 'AND'
$implode_str = ($this->criteriaSearchType == 1 ? ' OR ' : ' AND ');
if (empty($likeClauses)) {
// this could happen when the "inside column" does not exist
// in any selected tables
$where_clause = ' WHERE FALSE';
} else {
$where_clause = ' WHERE ('
. implode(') ' . $implode_str . ' (', $likeClauses)
. ')';
}
return $where_clause;
}
/**
* Displays database search results
*
* @return string HTML for search results
*/
public function getSearchResults()
{
$resultTotal = 0;
$rows = [];
// For each table selected as search criteria
foreach ($this->criteriaTables as $eachTable) {
// Gets the SQL statements
$newSearchSqls = $this->getSearchSqls($eachTable);
// Executes the "COUNT" statement
$resultCount = intval($GLOBALS['dbi']->fetchValue(
$newSearchSqls['select_count']
));
$resultTotal += $resultCount;
// Gets the result row's HTML for a table
$rows[] = [
'table' => htmlspecialchars($eachTable),
'new_search_sqls' => $newSearchSqls,
'result_count' => $resultCount,
];
}
return Template::get('database/search/results')->render([
'db' => $this->db,
'rows' => $rows,
'result_total' => $resultTotal,
'criteria_tables' => $this->criteriaTables,
'criteria_search_string' => htmlspecialchars($this->criteriaSearchString),
'search_type_description' => $this->searchTypeDescription,
]);
}
/**
* Provides the main search form's html
*
* @return string HTML for selection form
*/
public function getSelectionForm()
{
$choices = array(
'1' => $this->searchTypes[1] . ' '
. Util::showHint(
__('Words are separated by a space character (" ").')
),
'2' => $this->searchTypes[2] . ' '
. Util::showHint(
__('Words are separated by a space character (" ").')
),
'3' => $this->searchTypes[3],
'4' => $this->searchTypes[4],
'5' => $this->searchTypes[5] . ' ' . Util::showMySQLDocu('Regexp')
);
return Template::get('database/search/selection_form')->render([
'db' => $this->db,
'choices' => $choices,
'criteria_search_string' => $this->criteriaSearchString,
'criteria_search_type' => $this->criteriaSearchType,
'criteria_tables' => $this->criteriaTables,
'tables_names_only' => $this->tablesNamesOnly,
'criteria_column_name' => isset($this->criteriaColumnName)
? $this->criteriaColumnName : null,
]);
}
/**
* Provides div tags for browsing search results and sql query form.
*
* @return string div tags
*/
public function getResultDivs()
{
return Template::get('database/search/result_divs')->render();
}
}