package org.krakenapps.sqlengine.bdb;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.krakenapps.sqlengine.DatabaseHandle;
import org.krakenapps.sqlengine.TableSchemaManager;
import org.krakenapps.sqlparser.ast.AlterTableStatement;
import org.krakenapps.sqlparser.ast.ColumnConstraint;
import org.krakenapps.sqlparser.ast.ColumnConstraintDefinition;
import org.krakenapps.sqlparser.ast.ColumnDefinition;
import org.krakenapps.sqlparser.ast.DescTableStatement;
import org.krakenapps.sqlparser.ast.DropTableStatement;
import org.krakenapps.sqlparser.ast.NotNullConstraint;
import org.krakenapps.sqlparser.ast.PrimaryKeyConstraint;
import org.krakenapps.sqlparser.ast.ReferencesSpecification;
import org.krakenapps.sqlparser.ast.ShowTablesStatement;
import org.krakenapps.sqlparser.ast.TableDefinition;
import org.krakenapps.sqlparser.ast.UniqueConstraint;
public class DdlHandler {
public static void handleCreateTable(DatabaseHandle handle, TableDefinition stmt) throws SQLException {
validateCreateTable(handle, stmt);
TableSchemaManager tsm = handle.getTableSchemaManager();
tsm.createTable(stmt);
}
private static void validateCreateTable(DatabaseHandle handle, TableDefinition stmt) throws SQLException {
// check duplicated table name
TableSchemaManager tsm = handle.getTableSchemaManager();
if (tsm.getTableNames().contains(stmt.getTableName()))
throw new SQLException("table [" + stmt.getTableName() + "] already exists");
// check duplicated column name
Set<String> names = new HashSet<String>();
for (ColumnDefinition def : stmt.getColumnDefinitions()) {
if (names.contains(def.getColumnName()))
throw new SQLException("duplicated column name [" + def.getColumnName() + "]");
names.add(def.getColumnName());
}
// check FK validity (should be same type and pri key)
for (ColumnDefinition def : stmt.getColumnDefinitions()) {
checkForeignKeyValidity(tsm, def);
}
// force not null to primary key
for (ColumnDefinition def : stmt.getColumnDefinitions()) {
forcePrimaryKeyNotNull(def);
}
}
private static void checkForeignKeyValidity(TableSchemaManager tsm, ColumnDefinition columnDefinition)
throws SQLException {
for (ColumnConstraintDefinition cdef : columnDefinition.getConstraints()) {
ColumnConstraint c = cdef.getColumnConstraint();
if (!(c instanceof ReferencesSpecification))
continue;
String columnName = columnDefinition.getColumnName();
ReferencesSpecification ref = (ReferencesSpecification) c;
TableDefinition refTableDef = tsm.getTableSchema(ref.getTableName());
if (refTableDef == null) {
throw new SQLException(columnName + " references " + ref.getTableName()
+ " table, but table does not exist");
}
for (String refColumnName : ref.getColumns()) {
ColumnDefinition refCol = findColumn(refColumnName, refTableDef.getColumnDefinitions());
if (refCol == null)
throw new SQLException(columnName + " references " + ref.getTableName() + "(" + refColumnName
+ "), but column does not exist");
// check if refCol is primary key
if (!refCol.getDataType().equals(columnDefinition.getDataType()))
throw new SQLException(columnName + " references " + ref.getTableName() + "(" + refColumnName
+ "), but data type does not match");
}
}
}
private static ColumnDefinition findColumn(String name, List<ColumnDefinition> columns) {
for (ColumnDefinition col : columns)
if (col.getColumnName().equals(name))
return col;
return null;
}
private static void forcePrimaryKeyNotNull(ColumnDefinition def) {
boolean hasNotNull = false;
boolean hasPrimaryKey = false;
for (ColumnConstraintDefinition cdef : def.getConstraints()) {
ColumnConstraint c = cdef.getColumnConstraint();
if (c instanceof NotNullConstraint)
hasNotNull = true;
if (c instanceof PrimaryKeyConstraint)
hasPrimaryKey = true;
}
// force not null
if (hasPrimaryKey && !hasNotNull)
def.getConstraints().add(new ColumnConstraintDefinition(new NotNullConstraint()));
}
public static void handleAlterTable(DatabaseHandle handle, AlterTableStatement stmt) throws SQLException {
TableSchemaManager tsm = handle.getTableSchemaManager();
// check if table exists
if (!tsm.getTableNames().contains(stmt.getTableName()))
throw new SQLException("table [" + stmt.getTableName() + "] does not exist");
tsm.alterTable(stmt);
}
public static void handleDropTable(DatabaseHandle handle, DropTableStatement stmt) throws SQLException {
TableSchemaManager tsm = handle.getTableSchemaManager();
// check if table exists
if (!tsm.getTableNames().contains(stmt.getTableName()))
throw new SQLException("table [" + stmt.getTableName() + "] does not exist");
// drop
tsm.dropTable(stmt.getTableName());
}
public static Iterator<Row> handleDescTable(DatabaseHandle handle, DescTableStatement stmt, ResultMetadata metadata)
throws SQLException {
TableSchemaManager tsm = handle.getTableSchemaManager();
List<Row> rows = new ArrayList<Row>();
metadata.addColumn("Column Name");
metadata.addColumn("Data Type");
metadata.addColumn("Nullable");
metadata.addColumn("Key");
TableDefinition def = tsm.getTableSchema(stmt.getTableName());
if (def == null)
return null;
for (ColumnDefinition cd : def.getColumnDefinitions()) {
Row row = new Row();
row.add(cd.getColumnName());
row.add(cd.getDataType());
row.add(getNullable(cd.getConstraints()));
row.add(getKey(cd.getConstraints()));
rows.add(row);
}
return rows.iterator();
}
private static String getNullable(List<ColumnConstraintDefinition> defs) {
for (ColumnConstraintDefinition def : defs)
if (def.getColumnConstraint() instanceof NotNullConstraint)
return "N";
return "Y";
}
private static String getKey(List<ColumnConstraintDefinition> defs) {
for (ColumnConstraintDefinition def : defs) {
ColumnConstraint c = def.getColumnConstraint();
if (c instanceof PrimaryKeyConstraint)
return "PRI";
if (c instanceof ReferencesSpecification)
return "FOR";
if (c instanceof UniqueConstraint)
return "UNI";
}
return "";
}
public static Iterator<Row> handleShowTable(DatabaseHandle handle, ShowTablesStatement stmt, ResultMetadata metadata) {
TableSchemaManager tsm = handle.getTableSchemaManager();
List<Row> rows = new ArrayList<Row>();
metadata.addColumn("Table Name");
int i = 0;
for (String tableName : tsm.getTableNames()) {
Row row = new Row();
row.add(tableName);
rows.add(row);
metadata.updateDisplaySize(i, tableName.length());
i++;
}
return rows.iterator();
}
}