diff --git a/js/messages.php b/js/messages.php
index 1d3e5228c8..b2a9a40df5 100644
--- a/js/messages.php
+++ b/js/messages.php
@@ -326,6 +326,10 @@ $js_messages['strMoveRepeatingGroup'] = '
' . __('The following actions wi
. '' . __('Create the following table') . ' ';
$js_messages['strNewTablePlaceholder'] = 'Enter new table name';
$js_messages['strNewColumnPlaceholder'] = 'Enter column name';
+$js_messages['str3NFNormalization'] = __('Third step of normalization (3NF)');
+$js_messages['strConfirmTd'] = __('Confirm transitive dependencies');
+$js_messages['strSelectedTd'] = __('Selected dependencies are as follows:');
+$js_messages['strNoTdSelected'] = __('No dependencies selected!');
/* For server_variables.js */
$js_messages['strSave'] = __('Save');
diff --git a/js/normalization.js b/js/normalization.js
index e0b1a730f8..ebe7e289a8 100644
--- a/js/normalization.js
+++ b/js/normalization.js
@@ -31,7 +31,39 @@ function appendHtmlColumnsList()
}
);
}
-
+function goTo3NFStep1(newTables)
+{
+ if (Object.keys(newTables).length === 1) {
+ newTables = [PMA_commonParams.get('table')];
+ }
+ $.post(
+ "normalization.php",
+ {
+ "token": PMA_commonParams.get('token'),
+ "ajax_request": true,
+ "db": PMA_commonParams.get('db'),
+ "tables": newTables,
+ "step": '3.1'
+ }, function(data) {
+ $("#page_content h3").html(PMA_messages.str3NFNormalization);
+ $("#mainContent legend").html(data.legendText);
+ $("#mainContent h4").html(data.headText);
+ $("#mainContent p").html(data.subText);
+ $("#mainContent #extra").html(data.extra);
+ $("#extra form").each(function() {
+ form_id = $(this).attr('id');
+ colname = $(this).data('colname');
+ $("#"+form_id+" input[value='"+colname+"']").next().remove();
+ $("#"+form_id+" input[value='"+colname+"']").remove();
+ });
+ $("#mainContent #newCols").html('');
+ $('.tblFooters').html('');
+ if (data.subText !== "") {
+ $('.tblFooters').html(' ');
+ }
+ }
+ );
+}
function goTo2NFStep1() {
$.post(
"normalization.php",
@@ -49,7 +81,14 @@ function goTo2NFStep1() {
$("#mainContent #extra").html(data.extra);
$("#mainContent #newCols").html('');
if (data.subText !== '') {
- $('.tblFooters').html(' ');
+ $('.tblFooters').html(' ');
+ } else {
+ if (normalizeto === '3nf') {
+ $("#mainContent #newCols").html(PMA_messages.strToNextStep);
+ setTimeout(function() {
+ goTo3NFStep1([PMA_commonParams.get('table')]);
+ }, 3000);
+ }
}
});
}
@@ -154,6 +193,7 @@ function goToStep2(extra)
}
);
}
+
function goTo2NFFinish(pd)
{
var tables = {};
@@ -167,6 +207,51 @@ function goTo2NFFinish(pd)
"pd": JSON.stringify(pd),
"newTablesName":JSON.stringify(tables),
"createNewTables2NF":1};
+ $.ajax({
+ type: "GET",
+ url: "normalization.php",
+ data: datastring,
+ async:false,
+ success: function(data) {
+ if (data.success === true) {
+ if(data.queryError === false) {
+ if (normalizeto === '3nf') {
+ $("#pma_navigation_reload").click();
+ goTo3NFStep1(tables);
+ return true;
+ }
+ $("#mainContent legend").html(data.legendText);
+ $("#mainContent h4").html(data.headText);
+ $("#mainContent p").html('');
+ $("#mainContent #extra").html('');
+ $('.tblFooters').html('');
+ } else {
+ PMA_ajaxShowMessage(data.extra, false);
+ }
+ $("#pma_navigation_reload").click();
+ } else {
+ PMA_ajaxShowMessage(data.error, false);
+ }
+ }
+ });
+}
+
+function goTo3NFFinish(newTables)
+{
+ for (var table in newTables) {
+ for (var newtbl in newTables[table]) {
+ updatedname = $('#extra input[name="'+newtbl+'"]').val();
+ newTables[table][updatedname] = newTables[table][newtbl];
+ if (updatedname !== newtbl) {
+ delete newTables[table][newtbl];
+ }
+ }
+ }
+ datastring = {"token": PMA_commonParams.get('token'),
+ "ajax_request": true,
+ "db": PMA_commonParams.get('db'),
+ "newTables":JSON.stringify(newTables),
+ "createNewTables3NF":1};
$.ajax({
type: "GET",
url: "normalization.php",
@@ -202,7 +287,7 @@ function goTo2NFStep2(pd, primary_key)
for (var dependson in pd) {
if (dependson !== primary_key) {
pdFound = true;
- extra += ' '+dependson +" -> "+pd[dependson].toString()+'
';
+ extra += ''+escapeHtml(dependson) +" -> "+escapeHtml(pd[dependson].toString())+'
';
}
}
if(!pdFound) {
@@ -237,12 +322,74 @@ function goTo2NFStep2(pd, primary_key)
});
}
-function processPartialDependancies(primary_key)
+function goTo3NFStep2(pd, tablesTds)
+{
+ $("#newCols").html('');
+ $("#mainContent legend").html(PMA_messages.strStep+' 3.2 '+PMA_messages.strConfirmTd);
+ $("#mainContent h4").html(PMA_messages.strSelectedTd);
+ $("#mainContent p").html(PMA_messages.strPdHintNote);
+ var extra = '';
+ var pdFound = false;
+ for (var table in tablesTds) {
+ for (var i in tablesTds[table]) {
+ dependson = tablesTds[table][i];
+ if (dependson !== '' && dependson !== table) {
+ pdFound = true;
+ extra += '
'+escapeHtml(dependson) +" -> "+escapeHtml(pd[dependson].toString())+'
';
+ }
+ }
+ }
+ if(!pdFound) {
+ extra += '
'+PMA_messages.strNoTdSelected+'
';
+ extra += '
';
+ } else {
+ extra += '';
+ datastring = {"token": PMA_commonParams.get('token'),
+ "ajax_request": true,
+ "db": PMA_commonParams.get('db'),
+ "tables": JSON.stringify(tablesTds),
+ "pd": JSON.stringify(pd),
+ "getNewTables3NF":1};
+ $.ajax({
+ type: "GET",
+ url: "normalization.php",
+ data: datastring,
+ async:false,
+ success: function(data) {
+ data_parsed = $.parseJSON(data.message);
+ if (data.success === true) {
+ extra += data_parsed.html;
+ } else {
+ PMA_ajaxShowMessage(data.error, false);
+ }
+ }
+ });
+ }
+ $("#mainContent #extra").html(extra);
+ $('.tblFooters').html(' ');
+ $("#goTo3NFFinish").click(function(){
+ if (!pdFound) {
+ goTo3NFFinish([]);
+ } else {
+ goTo3NFFinish(data_parsed.newTables);
+ }
+ });
+}
+function processDependencies(primary_key, isTransitive)
{
var pd = {};
+ var tablesTds = {};
var dependsOn;
pd[primary_key] = [];
$("#extra form").each(function() {
+ if (isTransitive === true) {
+ tblname = $(this).data('tablename');
+ primary_key = tblname;
+ if (!(tblname in tablesTds)) {
+ tablesTds[tblname] = [];
+ }
+ tablesTds[tblname].push(primary_key);
+ }
form_id = $(this).attr('id');
$('#'+form_id+' input[type=checkbox]:not(:checked)').removeAttr('checked');
dependsOn = '';
@@ -251,9 +398,7 @@ function processPartialDependancies(primary_key)
$(this).attr("checked","checked");
});
if (dependsOn === '') {
- $('#'+form_id+' input[type=checkbox]').each(function(){
- dependsOn = primary_key;
- });
+ dependsOn = primary_key;
} else {
dependsOn = dependsOn.slice(0, -2);
}
@@ -261,11 +406,24 @@ function processPartialDependancies(primary_key)
pd[dependsOn] = [];
}
pd[dependsOn].push($(this).data('colname'));
+ if (isTransitive === true) {
+ if (!(tblname in tablesTds)) {
+ tablesTds[tblname] = [];
+ }
+ if ($.inArray(dependsOn, tablesTds[tblname]) === -1) {
+ tablesTds[tblname].push(dependsOn);
+ }
+ }
});
backup = $("#mainContent").html();
- goTo2NFStep2(pd, primary_key);
+ if (isTransitive === true) {
+ goTo3NFStep2(pd, tablesTds);
+ } else {
+ goTo2NFStep2(pd, primary_key);
+ }
return false;
}
+
function moveRepeatingGroup(repeatingCols) {
newTable = $("input[name=repeatGroupTable]").val();
newColumn = $("input[name=repeatGroupColumn]").val();
diff --git a/libraries/normalization.lib.php b/libraries/normalization.lib.php
index 8bc15db29b..b657322fa4 100644
--- a/libraries/normalization.lib.php
+++ b/libraries/normalization.lib.php
@@ -337,8 +337,9 @@ function PMA_getHtmlFor2NFstep1($db, $table)
foreach ($columns as $column) {
if (!in_array($column, $pk)) {
$cnt++;
- $extra .= "'" . htmlspecialchars($column)
- . "' depends on: ";
+ $extra .= "" . sprintf(
+ __('\'%1$s\' depends on:'), htmlspecialchars($column)
+ ) . " ";
$extra .= ' ';
@@ -376,7 +377,7 @@ function PMA_getHtmlForNewTables2NF($partialDependencies,$table)
'As per above partial dependencies, in order to put the '
. 'original table \'%1$s\' into Second normal form we need '
. 'to create the following tables:'
- ), $table
+ ), htmlspecialchars($table)
) . '';
$tableName = $table;
$i=1;
@@ -466,6 +467,148 @@ function PMA_createNewTablesFor2NF($partialDependencies, $tablesName, $table, $d
);
}
+/**
+ * build the html for showing the new tables to have in order
+ * to put given tables in 3NF
+ *
+ * @param array $dependencies containing all the dependencies
+ * @param array $tables tables formed after 2NF and need to convert to 3NF
+ * @param string $db current database
+ *
+ * @return array containing html and the list of new tables
+ */
+function PMA_getHtmlForNewTables3NF($dependencies, $tables, $db)
+{
+ $html = "";
+ $i=1;
+ $newTables = array();
+ foreach ($tables as $table=>$arrDependson) {
+ if (count(array_unique($arrDependson)) == 1) {
+ continue;
+ }
+ $primary = PMA_Index::getPrimary($table, $db);
+ $primarycols = $primary->getColumns();
+ $pk = array();
+ foreach ($primarycols as $col) {
+ $pk[] = $col->getName();
+ }
+ $html .= '' . sprintf(
+ __(
+ 'As per above dependencies, in order to put the '
+ . 'original table \'%1$s\' into Third normal form we need '
+ . 'to create the following tables:'
+ ), htmlspecialchars($table)
+ ) . '
';
+ $tableName = $table;
+ $columnList = array();
+ foreach ($arrDependson as $key) {
+ $dependents = $dependencies->$key;
+ if ($key == $table) {
+ $key = implode(', ', $pk);
+ }
+ $tmpTableCols =array_merge(explode(', ', $key), $dependents);
+ sort($tmpTableCols);
+ if (!in_array($tmpTableCols, $columnList)) {
+ $columnList[] = $tmpTableCols;
+ $html .= ' '
+ . '( ' . htmlspecialchars($key) . ' '
+ . (count($dependents)>0?', ':'')
+ . htmlspecialchars(implode(', ', $dependents)) . ' )';
+ $newTables[$table][$tableName] = array(
+ "pk"=>$key, "nonpk"=>implode(', ', $dependents)
+ );
+ $i++;
+ $tableName = 'table' . $i;
+ }
+ }
+ }
+ return array('html'=>$html, 'newTables'=>$newTables);
+}
+
+/**
+ * create new tables or alter existing to get 3NF
+ *
+ * @param array $newTables list of new tables to be created
+ * @param string $db current database
+ *
+ * @return array
+ */
+function PMA_createNewTablesFor3NF($newTables, $db)
+{
+ $queries = array();
+ $dropCols = false;
+ $error = false;
+ $headText = '
' .
+ __('The third step of normalization is complete.')
+ . ' ';
+ if (count((array)$newTables) == 0) {
+ return array(
+ 'legendText'=>__('End of step'), 'headText'=>$headText,
+ 'queryError'=>$error
+ );
+ }
+ $message = '';
+ $GLOBALS['dbi']->selectDb($db, $GLOBALS['userlink']);
+ foreach ($newTables as $originalTable=>$tablesList) {
+ foreach ($tablesList as $table=>$cols) {
+ if ($table != $originalTable) {
+ $quotedPk = implode(
+ ', ', PMA_Util::backquote(explode(', ', $cols->pk))
+ );
+ $quotedNonpk = implode(
+ ', ', PMA_Util::backquote(explode(', ', $cols->nonpk))
+ );
+ $queries[] = 'CREATE TABLE ' . PMA_Util::backquote($table)
+ . ' SELECT DISTINCT ' . $quotedPk
+ . ', ' . $quotedNonpk
+ . ' FROM ' . PMA_Util::backquote($originalTable) . ';';
+ $queries[] = 'ALTER TABLE ' . PMA_Util::backquote($table)
+ . ' ADD PRIMARY KEY(' . $quotedPk . ');';
+ } else {
+ $dropCols = $cols;
+ }
+ }
+ if ($dropCols) {
+ $columns = (array) $GLOBALS['dbi']->getColumnNames(
+ $db, $originalTable, $GLOBALS['userlink']
+ );
+ $colPresent = array_merge(
+ explode(', ', $dropCols->pk), explode(', ', $dropCols->nonpk)
+ );
+ $query = 'ALTER TABLE ' . PMA_Util::backquote($originalTable);
+ foreach ($columns as $col) {
+ if (!in_array($col, $colPresent)) {
+ $query .= ' DROP ' . PMA_Util::backquote($col) . ',';
+ }
+ }
+ $query = trim($query, ', ');
+ $query .= ';';
+ $queries[] = $query;
+ } else {
+ $queries[] = 'DROP TABLE ' . PMA_Util::backquote($originalTable);
+ }
+ $dropCols = false;
+ }
+ foreach ($queries as $query) {
+ if (!$GLOBALS['dbi']->tryQuery($query, $GLOBALS['userlink'])) {
+ $message = PMA_Message::error(__('Error in processing!'));
+ $message->addMessage(' ');
+ $message->addMessage(
+ PMA_Message::rawError(
+ $GLOBALS['dbi']->getError($GLOBALS['userlink'])
+ )
+ );
+ $error = true;
+ break;
+ }
+ }
+ return array(
+ 'legendText'=>__('End of step'), 'headText'=>$headText,
+ 'queryError'=>$error, 'extra'=>$message
+ );
+}
/**
* move the repeating group of columns to a new table
*
@@ -528,6 +671,83 @@ function PMA_moveRepeatingGroup(
'queryError'=>$error, 'message'=>$message
);
}
+
+/**
+ * build html for 3NF step 1 to find the transitive dependencies
+ *
+ * @param string $db current database
+ * @param array $tables tables formed after 2NF and need to process for 3NF
+ *
+ * @return string
+ */
+function PMA_getHtmlFor3NFstep1($db, $tables)
+{
+ $legendText = __('Step 3.') . "1 " . __('Find transitive dependencies');
+ $extra = "";
+ $headText = __(
+ 'Please answer the following question(s) '
+ . 'carefully to obtain a correct normalization.'
+ );
+ $subText = __(
+ 'For each of the column below, '
+ . 'please select the minimal set of columns among given set '
+ . 'whose values combined together are sufficient'
+ . ' to determine the value of the column. '
+ . 'Note: A column may have no transitive dependency, '
+ . 'in that case you don\'t have to select any.'
+ );
+ $cnt=0;
+ foreach ($tables as $key=>$table) {
+ $primary = PMA_Index::getPrimary($table, $db);
+ $primarycols = $primary->getColumns();
+ $selectTdForm = "";
+ $pk = array();
+ foreach ($primarycols as $col) {
+ $pk[] = $col->getName();
+ }
+ $GLOBALS['dbi']->selectDb($db, $GLOBALS['userlink']);
+ $columns = (array) $GLOBALS['dbi']->getColumnNames(
+ $db, $table, $GLOBALS['userlink']
+ );
+ if (count($columns)-count($pk)<=1) {
+ continue;
+ }
+ foreach ($columns as $column) {
+ if (!in_array($column, $pk)) {
+ $selectTdForm .= ' '
+ . '' . htmlspecialchars($column) . ' ';
+ }
+ }
+ foreach ($columns as $column) {
+ if (!in_array($column, $pk)) {
+ $cnt++;
+ $extra .= "" . sprintf(
+ __('\'%1$s\' depends on:'), htmlspecialchars($column)
+ )
+ . " ";
+ $extra .= ' ';
+ }
+ }
+ }
+ if ($extra == "") {
+ $headText = __(
+ "No Transitive dependencies possible as the table "
+ . "doesn't have any non primary key columns"
+ );
+ $subText = "";
+ $extra = "" . __("Table is already in Third normal form!") . " ";
+ }
+ $res = array(
+ 'legendText'=>$legendText, 'headText'=>$headText,
+ 'subText'=>$subText,'extra'=>$extra
+ );
+ return $res;
+}
/**
* get html for options to normalize table
*
diff --git a/normalization.php b/normalization.php
index 86dd19c9d2..c7f7229013 100644
--- a/normalization.php
+++ b/normalization.php
@@ -57,6 +57,13 @@ if (isset($_REQUEST['getNewTables2NF'])) {
exit;
}
+if (isset($_REQUEST['getNewTables3NF'])) {
+ $dependencies = json_decode($_REQUEST['pd']);
+ $tables = json_decode($_REQUEST['tables']);
+ $newTables = PMA_getHtmlForNewTables3NF($dependencies, $tables, $db);
+ echo json_encode($newTables);
+ exit;
+}
$response = PMA_Response::getInstance();
$header = $response->getHeader();
@@ -66,16 +73,6 @@ $scripts->addFile('jquery/jquery.uitablefilter.js');
$normalForm = '1nf';
if (isset($_REQUEST['normalizeTo'])) {
$normalForm = $_REQUEST['normalizeTo'];
- if ($normalForm == '3nf') {
- $response->addHTML(
- ''
- . __('Second/Third step of normalization') . ' '
- . ''
- . 'Coming soon... '
- . 'Wait is worth it :-) '
- );
- exit;
- }
}
if (isset($_REQUEST['createNewTables2NF'])) {
$partialDependencies = json_decode($_REQUEST['pd']);
@@ -84,6 +81,12 @@ if (isset($_REQUEST['createNewTables2NF'])) {
$response->addJSON($res);
exit;
}
+if (isset($_REQUEST['createNewTables3NF'])) {
+ $newtables = json_decode($_REQUEST['newTables']);
+ $res = PMA_createNewTablesFor3NF($newtables, $db);
+ $response->addJSON($res);
+ exit;
+}
if (isset($_POST['repeatingColumns'])) {
$repeatingColumns = $_POST['repeatingColumns'];
$newTable = $_POST['newTable'];
@@ -110,6 +113,10 @@ if (isset($_REQUEST['step1'])) {
} else if (isset($_REQUEST['step']) && $_REQUEST['step'] == 2.1) {
$res = PMA_getHtmlFor2NFstep1($db, $table);
$response->addJSON($res);
+} else if (isset($_REQUEST['step']) && $_REQUEST['step'] == 3.1) {
+ $tables = $_REQUEST['tables'];
+ $res = PMA_getHtmlFor3NFstep1($db, $tables);
+ $response->addJSON($res);
} else {
$response->addHTML(PMA_getHtmlForNormalizetable());
}