Search in sources :

Example 6 with ForeignKeyDef

use of herddb.model.ForeignKeyDef in project herddb by diennea.

the class JSQLParserPlanner method buildCreateTableStatement.

private Statement buildCreateTableStatement(String defaultTableSpace, CreateTable s) throws StatementExecutionException {
    String tableSpace = fixMySqlBackTicks(s.getTable().getSchemaName());
    String tableName = fixMySqlBackTicks(s.getTable().getName());
    if (tableSpace == null) {
        tableSpace = defaultTableSpace;
    }
    if (s.getColumnDefinitions() == null) {
        throw new StatementExecutionException("A table must have at least 1 column");
    }
    final boolean isNotExsists = s.isIfNotExists();
    try {
        boolean foundPk = false;
        Table.Builder tablebuilder = Table.builder().uuid(UUID.randomUUID().toString()).name(tableName).tablespace(tableSpace);
        Set<String> primaryKey = new HashSet<>();
        Set<String> simpleUniqueFields = new HashSet<>();
        if (s.getIndexes() != null) {
            for (Index index : s.getIndexes()) {
                if (index.getType().equalsIgnoreCase("PRIMARY KEY")) {
                    for (String n : index.getColumnsNames()) {
                        n = fixMySqlBackTicks(n.toLowerCase());
                        tablebuilder.primaryKey(n);
                        primaryKey.add(n);
                        foundPk = true;
                    }
                }
            }
        }
        int position = 0;
        for (ColumnDefinition cf : s.getColumnDefinitions()) {
            String columnName = fixMySqlBackTicks(cf.getColumnName().toLowerCase());
            int type;
            String dataType = cf.getColDataType().getDataType();
            List<String> columnSpecs = decodeColumnSpecs(cf.getColumnSpecs());
            type = sqlDataTypeToColumnType(dataType, cf.getColDataType().getArgumentsStringList(), columnSpecs);
            Bytes defaultValue = decodeDefaultValue(cf, type);
            if (!columnSpecs.isEmpty()) {
                boolean auto_increment = decodeAutoIncrement(columnSpecs);
                if (columnSpecs.contains("PRIMARY")) {
                    foundPk = true;
                    tablebuilder.primaryKey(columnName, auto_increment);
                }
                if (auto_increment && primaryKey.contains(columnName)) {
                    tablebuilder.primaryKey(columnName, auto_increment);
                }
                boolean isUnique = columnSpecs.contains("UNIQUE");
                if (isUnique) {
                    simpleUniqueFields.add(columnName);
                }
            }
            tablebuilder.column(columnName, type, position++, defaultValue);
        }
        if (!foundPk) {
            tablebuilder.column("_pk", ColumnTypes.LONG, position++, null);
            tablebuilder.primaryKey("_pk", true);
        }
        Table table = tablebuilder.build();
        List<herddb.model.Index> otherIndexes = new ArrayList<>();
        List<herddb.model.ForeignKeyDef> foreignKeys = new ArrayList<>();
        if (s.getIndexes() != null) {
            for (Index index : s.getIndexes()) {
                if (index.getType().equalsIgnoreCase("PRIMARY KEY")) {
                } else if (index.getType().equalsIgnoreCase("INDEX") || index.getType().equalsIgnoreCase("KEY") || index.getType().equalsIgnoreCase("UNIQUE KEY")) {
                    String indexName = fixMySqlBackTicks(index.getName().toLowerCase());
                    String indexType = convertIndexType(null);
                    boolean unique = index.getType().equalsIgnoreCase("UNIQUE KEY");
                    herddb.model.Index.Builder builder = herddb.model.Index.builder().onTable(table).name(indexName).unique(unique).type(indexType).uuid(UUID.randomUUID().toString());
                    for (String columnName : index.getColumnsNames()) {
                        columnName = fixMySqlBackTicks(columnName.toLowerCase());
                        Column column = table.getColumn(columnName);
                        if (column == null) {
                            throw new StatementExecutionException("no such column " + columnName + " on table " + tableName + " in tablespace " + tableSpace);
                        }
                        builder.column(column.name, column.type);
                    }
                    otherIndexes.add(builder.build());
                } else if (index.getType().equals("FOREIGN KEY")) {
                    ForeignKeyIndex fk = (ForeignKeyIndex) index;
                    ForeignKeyDef fkDef = parseForeignKeyIndex(fk, table, tableName, tableSpace);
                    foreignKeys.add(fkDef);
                } else {
                    throw new StatementExecutionException("Unsupported index type " + index.getType());
                }
            }
        }
        for (String col : simpleUniqueFields) {
            herddb.model.Index.Builder builder = herddb.model.Index.builder().onTable(table).name(table.name + "_unique_" + col).unique(true).type(herddb.model.Index.TYPE_BRIN).uuid(UUID.randomUUID().toString()).column(col, table.getColumn(col).type);
            otherIndexes.add(builder.build());
        }
        if (!foreignKeys.isEmpty()) {
            table = table.withForeignKeys(foreignKeys.toArray(new ForeignKeyDef[0]));
        }
        CreateTableStatement statement = new CreateTableStatement(table, otherIndexes, isNotExsists);
        return statement;
    } catch (IllegalArgumentException err) {
        throw new StatementExecutionException("bad table definition: " + err.getMessage(), err);
    }
}
Also used : Table(herddb.model.Table) ShowCreateTableCalculator.calculateShowCreateTable(herddb.sql.functions.ShowCreateTableCalculator.calculateShowCreateTable) CreateTable(net.sf.jsqlparser.statement.create.table.CreateTable) CreateTableStatement(herddb.model.commands.CreateTableStatement) ArrayList(java.util.ArrayList) CreateIndex(net.sf.jsqlparser.statement.create.index.CreateIndex) ForeignKeyIndex(net.sf.jsqlparser.statement.create.table.ForeignKeyIndex) Index(net.sf.jsqlparser.statement.create.table.Index) StatementExecutionException(herddb.model.StatementExecutionException) ColumnDefinition(net.sf.jsqlparser.statement.create.table.ColumnDefinition) Bytes(herddb.utils.Bytes) Column(herddb.model.Column) ForeignKeyDef(herddb.model.ForeignKeyDef) ForeignKeyIndex(net.sf.jsqlparser.statement.create.table.ForeignKeyIndex) HashSet(java.util.HashSet)

Example 7 with ForeignKeyDef

use of herddb.model.ForeignKeyDef in project herddb by diennea.

the class JSQLParserPlanner method buildAlterStatement.

private Statement buildAlterStatement(String defaultTableSpace, Alter alter) throws StatementExecutionException {
    if (alter.getTable() == null) {
        throw new StatementExecutionException("missing table name");
    }
    String tableSpace = alter.getTable().getSchemaName();
    if (tableSpace == null) {
        tableSpace = defaultTableSpace;
    }
    tableSpace = fixMySqlBackTicks(tableSpace);
    List<Column> addColumns = new ArrayList<>();
    List<Column> modifyColumns = new ArrayList<>();
    List<String> dropColumns = new ArrayList<>();
    List<String> dropForeignKeys = new ArrayList<>();
    List<ForeignKeyDef> addForeignKeys = new ArrayList<>();
    String tableName = fixMySqlBackTicks(alter.getTable().getName().toLowerCase());
    if (alter.getAlterExpressions() == null || alter.getAlterExpressions().size() != 1) {
        throw new StatementExecutionException("supported multi-alter operation '" + alter + "'");
    }
    AlterExpression alterExpression = alter.getAlterExpressions().get(0);
    AlterOperation operation = alterExpression.getOperation();
    Boolean changeAutoIncrement = null;
    TableSpaceManager tableSpaceManager = manager.getTableSpaceManager(tableSpace);
    if (tableSpaceManager == null) {
        throw new StatementExecutionException("bad tablespace '" + tableSpace + "'");
    }
    Table table = getTable(defaultTableSpace, alter.getTable());
    switch(operation) {
        case ADD:
            {
                if (alterExpression.getColDataTypeList() != null) {
                    List<AlterExpression.ColumnDataType> cols = alterExpression.getColDataTypeList();
                    for (AlterExpression.ColumnDataType cl : cols) {
                        List<String> columnSpecs = decodeColumnSpecs(cl.getColumnSpecs());
                        int type = sqlDataTypeToColumnType(cl.getColDataType().getDataType(), cl.getColDataType().getArgumentsStringList(), columnSpecs);
                        Column newColumn = Column.column(fixMySqlBackTicks(cl.getColumnName()), type, decodeDefaultValue(cl, type));
                        addColumns.add(newColumn);
                    }
                } else if (alterExpression.getIndex() != null && alterExpression.getIndex() instanceof ForeignKeyIndex) {
                    ForeignKeyDef fkIndex = parseForeignKeyIndex((ForeignKeyIndex) alterExpression.getIndex(), table, tableName, tableSpace);
                    addForeignKeys.add(fkIndex);
                } else {
                    throw new StatementExecutionException("Unrecognized ALTER TABLE ADD ... statement");
                }
            }
            break;
        case DROP:
            if (alterExpression.getColumnName() != null) {
                dropColumns.add(fixMySqlBackTicks(alterExpression.getColumnName()));
            } else if (alterExpression.getConstraintName() != null) {
                dropForeignKeys.add(fixMySqlBackTicks(alterExpression.getConstraintName()));
            } else {
                throw new StatementExecutionException("Unrecognized ALTER TABLE DROP ... statement");
            }
            break;
        case MODIFY:
            {
                List<AlterExpression.ColumnDataType> cols = alterExpression.getColDataTypeList();
                for (AlterExpression.ColumnDataType cl : cols) {
                    String columnName = fixMySqlBackTicks(cl.getColumnName().toLowerCase());
                    Column oldColumn = table.getColumn(columnName);
                    if (oldColumn == null) {
                        throw new StatementExecutionException("bad column " + columnName + " in table " + tableName + " in tablespace '" + tableSpace + "'");
                    }
                    Map<String, AbstractIndexManager> indexes = tableSpaceManager.getIndexesOnTable(tableName);
                    if (indexes != null) {
                        for (AbstractIndexManager am : indexes.values()) {
                            for (String indexedColumn : am.getColumnNames()) {
                                indexedColumn = fixMySqlBackTicks(indexedColumn);
                                if (indexedColumn.equalsIgnoreCase(oldColumn.name)) {
                                    throw new StatementExecutionException("cannot alter indexed " + columnName + " in table " + tableName + " in tablespace '" + tableSpace + "'," + "index name is " + am.getIndexName());
                                }
                            }
                        }
                    }
                    List<String> columnSpecs = decodeColumnSpecs(cl.getColumnSpecs());
                    int newType = sqlDataTypeToColumnType(cl.getColDataType().getDataType(), cl.getColDataType().getArgumentsStringList(), columnSpecs);
                    if (oldColumn.type != newType) {
                        if (ColumnTypes.isNotNullToNullConversion(oldColumn.type, newType)) {
                        // allow change from "STRING NOT NULL" to "STRING NULL"
                        } else if (ColumnTypes.isNullToNotNullConversion(oldColumn.type, newType)) {
                        // allow change from "STRING NULL" to "STRING NOT NULL"
                        // it will require a check on table at execution time
                        } else {
                            throw new StatementExecutionException("cannot change datatype to " + ColumnTypes.typeToString(newType) + " for column " + columnName + " (" + ColumnTypes.typeToString(oldColumn.type) + ") in table " + tableName + " in tablespace '" + tableSpace + "'");
                        }
                    }
                    if (table.isPrimaryKeyColumn(columnName)) {
                        boolean new_auto_increment = decodeAutoIncrement(columnSpecs);
                        if (new_auto_increment && table.primaryKey.length > 1) {
                            throw new StatementExecutionException("cannot add auto_increment flag to " + cl.getColDataType().getDataType() + " for column " + columnName + " in table " + tableName + " in tablespace '" + tableSpace + "'");
                        }
                        if (table.auto_increment != new_auto_increment) {
                            changeAutoIncrement = new_auto_increment;
                        }
                    }
                    Bytes newDefault = oldColumn.defaultValue;
                    if (containsDefaultClause(cl)) {
                        newDefault = decodeDefaultValue(cl, newType);
                    }
                    Column newColumnDef = Column.column(columnName, newType, oldColumn.serialPosition, newDefault);
                    modifyColumns.add(newColumnDef);
                }
            }
            break;
        case CHANGE:
            {
                String columnName = alterExpression.getColOldName();
                List<AlterExpression.ColumnDataType> cols = alterExpression.getColDataTypeList();
                if (cols.size() != 1) {
                    throw new StatementExecutionException("bad CHANGE column " + columnName + " in table " + tableName + " in tablespace '" + tableSpace + "'");
                }
                AlterExpression.ColumnDataType cl = cols.get(0);
                Column oldColumn = table.getColumn(columnName);
                if (oldColumn == null) {
                    throw new StatementExecutionException("bad column " + columnName + " in table " + tableName + " in tablespace '" + tableSpace + "'");
                }
                Map<String, AbstractIndexManager> indexes = tableSpaceManager.getIndexesOnTable(tableName);
                if (indexes != null) {
                    for (AbstractIndexManager am : indexes.values()) {
                        for (String indexedColumn : am.getColumnNames()) {
                            indexedColumn = fixMySqlBackTicks(indexedColumn);
                            if (indexedColumn.equalsIgnoreCase(oldColumn.name)) {
                                throw new StatementExecutionException("cannot alter indexed " + columnName + " in table " + tableName + " in tablespace '" + tableSpace + "'," + "index name is " + am.getIndexName());
                            }
                        }
                    }
                }
                List<String> columnSpecs = decodeColumnSpecs(cl.getColumnSpecs());
                int newType = sqlDataTypeToColumnType(cl.getColDataType().getDataType(), cl.getColDataType().getArgumentsStringList(), columnSpecs);
                if (oldColumn.type != newType) {
                    throw new StatementExecutionException("cannot change datatype to " + ColumnTypes.typeToString(newType) + " for column " + columnName + " (" + ColumnTypes.typeToString(oldColumn.type) + ") in table " + tableName + " in tablespace '" + tableSpace + "'");
                }
                if (table.isPrimaryKeyColumn(columnName)) {
                    boolean new_auto_increment = decodeAutoIncrement(columnSpecs);
                    if (new_auto_increment && table.primaryKey.length > 1) {
                        throw new StatementExecutionException("cannot add auto_increment flag to " + cl.getColDataType().getDataType() + " for column " + columnName + " in table " + tableName + " in tablespace '" + tableSpace + "'");
                    }
                    if (table.auto_increment != new_auto_increment) {
                        changeAutoIncrement = new_auto_increment;
                    }
                }
                String renameTo = fixMySqlBackTicks(cl.getColumnName().toLowerCase());
                if (renameTo != null) {
                    columnName = renameTo;
                }
                Column newColumnDef = Column.column(columnName, newType, oldColumn.serialPosition, oldColumn.defaultValue);
                modifyColumns.add(newColumnDef);
            }
            break;
        default:
            throw new StatementExecutionException("supported alter operation '" + alter + "'");
    }
    return new AlterTableStatement(addColumns, modifyColumns, dropColumns, changeAutoIncrement, tableName.toLowerCase(), tableSpace, null, dropForeignKeys, addForeignKeys);
}
Also used : AlterOperation(net.sf.jsqlparser.statement.alter.AlterOperation) Table(herddb.model.Table) ShowCreateTableCalculator.calculateShowCreateTable(herddb.sql.functions.ShowCreateTableCalculator.calculateShowCreateTable) CreateTable(net.sf.jsqlparser.statement.create.table.CreateTable) AbstractIndexManager(herddb.core.AbstractIndexManager) AlterTableStatement(herddb.model.commands.AlterTableStatement) ArrayList(java.util.ArrayList) StatementExecutionException(herddb.model.StatementExecutionException) AlterExpression(net.sf.jsqlparser.statement.alter.AlterExpression) Bytes(herddb.utils.Bytes) Column(herddb.model.Column) TableSpaceManager(herddb.core.TableSpaceManager) ItemsList(net.sf.jsqlparser.expression.operators.relational.ItemsList) ArrayList(java.util.ArrayList) ExpressionList(net.sf.jsqlparser.expression.operators.relational.ExpressionList) MultiExpressionList(net.sf.jsqlparser.expression.operators.relational.MultiExpressionList) List(java.util.List) SetOperationList(net.sf.jsqlparser.statement.select.SetOperationList) ForeignKeyDef(herddb.model.ForeignKeyDef) ForeignKeyIndex(net.sf.jsqlparser.statement.create.table.ForeignKeyIndex) Map(java.util.Map)

Example 8 with ForeignKeyDef

use of herddb.model.ForeignKeyDef in project herddb by diennea.

the class SysforeignkeysTableManager method buildVirtualRecordList.

@Override
protected Iterable<Record> buildVirtualRecordList(Transaction transaction) {
    List<Table> tables = tableSpaceManager.getAllVisibleTables(transaction);
    List<Record> result = new ArrayList<>();
    for (Table child : tables) {
        if (child.foreignKeys == null) {
            continue;
        }
        String child_table_name = child.name;
        for (ForeignKeyDef fk : child.foreignKeys) {
            Table parent = tables.stream().filter(ta -> ta.uuid.equals(fk.parentTableId)).findAny().orElse(null);
            if (parent == null) {
                continue;
            }
            for (int i = 0; i < fk.columns.length; i++) {
                String child_column_name = fk.columns[i];
                String parent_column_name = fk.parentTableColumns[i];
                String parent_table_name = parent.name;
                String on_delete_action;
                switch(fk.onDeleteAction) {
                    case ForeignKeyDef.ACTION_CASCADE:
                        on_delete_action = "importedKeyCascade";
                        break;
                    case ForeignKeyDef.ACTION_NO_ACTION:
                        on_delete_action = "importedNoAction";
                        break;
                    case ForeignKeyDef.ACTION_SETNULL:
                        on_delete_action = "importedKeySetNull";
                        break;
                    default:
                        on_delete_action = "importedKeyCascade";
                        break;
                }
                String on_update_action;
                switch(fk.onUpdateAction) {
                    case ForeignKeyDef.ACTION_CASCADE:
                        on_update_action = "importedKeyCascade";
                        break;
                    case ForeignKeyDef.ACTION_NO_ACTION:
                        on_update_action = "importedNoAction";
                        break;
                    case ForeignKeyDef.ACTION_SETNULL:
                        on_update_action = "importedKeySetNull";
                        break;
                    default:
                        on_update_action = "importedKeyCascade";
                        break;
                }
                result.add(RecordSerializer.makeRecord(table, "child_table_name", child_table_name, "child_table_column_name", child_column_name, "child_table_cons_name", fk.name, "parent_table_name", parent_table_name, "parent_table_column_name", parent_column_name, "on_delete_action", on_delete_action, "on_update_action", on_update_action, "ordinal_position", (i + 1), "deferred", "importedKeyNotDeferrable"));
            }
        }
    }
    return result;
}
Also used : Table(herddb.model.Table) ArrayList(java.util.ArrayList) Record(herddb.model.Record) ForeignKeyDef(herddb.model.ForeignKeyDef)

Example 9 with ForeignKeyDef

use of herddb.model.ForeignKeyDef in project herddb by diennea.

the class TableManager method validateAlterTable.

@Override
public void validateAlterTable(Table table, StatementEvaluationContext context) throws StatementExecutionException {
    List<String> columnsChangedFromNullToNotNull = new ArrayList<>();
    for (Column c : this.table.columns) {
        Column newColumnSpecs = table.getColumn(c.name);
        if (newColumnSpecs == null) {
            // dropped column
            LOGGER.log(Level.INFO, "Table {0}.{1} dropping column {2}", new Object[] { table.tablespace, table.name, c.name });
        } else if (newColumnSpecs.type == c.type) {
        // no data type change
        } else if (ColumnTypes.isNotNullToNullConversion(c.type, newColumnSpecs.type)) {
            LOGGER.log(Level.INFO, "Table {0}.{1} making column {2} NULLABLE", new Object[] { table.tablespace, table.name, newColumnSpecs.name });
        } else if (ColumnTypes.isNullToNotNullConversion(c.type, newColumnSpecs.type)) {
            LOGGER.log(Level.INFO, "Table {0}.{1} making column {2} NOT NULL", new Object[] { table.tablespace, table.name, newColumnSpecs.name });
            columnsChangedFromNullToNotNull.add(c.name);
        }
    }
    for (final String column : columnsChangedFromNullToNotNull) {
        LOGGER.log(Level.INFO, "Table {0}.{1} validating column {2}, check for NULL values", new Object[] { table.tablespace, table.name, column });
        ScanStatement scan = new ScanStatement(this.table.tablespace, this.table, new Predicate() {

            @Override
            public boolean evaluate(Record record, StatementEvaluationContext context) throws StatementExecutionException {
                return record.getDataAccessor(table).get(column) == null;
            }
        });
        // fast fail
        scan.setLimits(new ScanLimitsImpl(1, 0));
        boolean foundOneNull = false;
        try (DataScanner scanner = this.scan(scan, context, null, false, false)) {
            foundOneNull = scanner.hasNext();
        } catch (DataScannerException err) {
            throw new StatementExecutionException(err);
        }
        if (foundOneNull) {
            throw new StatementExecutionException("Found a record in table " + table.name + " that contains a NULL value for column " + column + " ALTER command is not possible");
        }
    }
    // if we are adding new FK we have to check that the FK is not violated
    if (table.foreignKeys != null) {
        List<ForeignKeyDef> newForeignKeys;
        if (this.table.foreignKeys == null) {
            newForeignKeys = Arrays.asList(table.foreignKeys);
        } else {
            Set<String> currentKfs = Stream.of(this.table.foreignKeys).map(f -> f.name.toLowerCase()).collect(Collectors.toSet());
            newForeignKeys = Stream.of(table.foreignKeys).filter(fk -> !currentKfs.contains(fk.name)).collect(Collectors.toList());
        }
        for (ForeignKeyDef newFk : newForeignKeys) {
            validateForeignKeyConsistency(newFk, context, null);
        }
    }
}
Also used : Arrays(java.util.Arrays) NullLockManager(herddb.utils.NullLockManager) Table(herddb.model.Table) TruncateTableStatement(herddb.model.commands.TruncateTableStatement) DuplicatePrimaryKeyException(herddb.model.DuplicatePrimaryKeyException) TableStatus(herddb.storage.TableStatus) Map(java.util.Map) DataAccessor(herddb.utils.DataAccessor) LogNotAvailableException(herddb.log.LogNotAvailableException) CommitLogResult(herddb.log.CommitLogResult) LogSequenceNumber(herddb.log.LogSequenceNumber) UniqueIndexContraintViolationException(herddb.model.UniqueIndexContraintViolationException) Set(java.util.Set) RecordSerializer(herddb.codec.RecordSerializer) JSQLParserPlanner.delimit(herddb.sql.JSQLParserPlanner.delimit) DataPageMetaData(herddb.core.PageSet.DataPageMetaData) ScanStatement(herddb.model.commands.ScanStatement) Stream(java.util.stream.Stream) StatsLogger(org.apache.bookkeeper.stats.StatsLogger) Bytes(herddb.utils.Bytes) Holder(herddb.utils.Holder) LockHandle(herddb.utils.LockHandle) ForeignKeyViolationException(herddb.model.ForeignKeyViolationException) LogEntry(herddb.log.LogEntry) ArrayList(java.util.ArrayList) TransactionContext(herddb.model.TransactionContext) Transaction(herddb.model.Transaction) ThreadLocalRandom(java.util.concurrent.ThreadLocalRandom) Projection(herddb.model.Projection) ForeignKeyDef(herddb.model.ForeignKeyDef) EnsureLongIncrementAccumulator(herddb.utils.EnsureLongIncrementAccumulator) LogEntryType(herddb.log.LogEntryType) Record(herddb.model.Record) LogEntryFactory(herddb.log.LogEntryFactory) KeyToPageIndex(herddb.index.KeyToPageIndex) DataStorageManager(herddb.storage.DataStorageManager) ColumnTypes(herddb.model.ColumnTypes) ILocalLockManager(herddb.utils.ILocalLockManager) AtomicLong(java.util.concurrent.atomic.AtomicLong) Lock(java.util.concurrent.locks.Lock) IndexOperation(herddb.index.IndexOperation) Column(herddb.model.Column) StampedLock(java.util.concurrent.locks.StampedLock) UpdateStatement(herddb.model.commands.UpdateStatement) ScanLimitsImpl(herddb.model.ScanLimitsImpl) TupleComparator(herddb.model.TupleComparator) ServerConfiguration(herddb.server.ServerConfiguration) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) RecordTooBigException(herddb.model.RecordTooBigException) DMLStatementExecutionResult(herddb.model.DMLStatementExecutionResult) LocalLockManager(herddb.utils.LocalLockManager) Futures(herddb.utils.Futures) DataStorageManagerException(herddb.storage.DataStorageManagerException) Index(herddb.model.Index) InsertStatement(herddb.model.commands.InsertStatement) DataScanner(herddb.model.DataScanner) DDLException(herddb.model.DDLException) RecordFunction(herddb.model.RecordFunction) StatementExecutionException(herddb.model.StatementExecutionException) TableContext(herddb.model.TableContext) Collection(java.util.Collection) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Logger(java.util.logging.Logger) Collectors(java.util.stream.Collectors) List(java.util.List) FullTableScanConsumer(herddb.storage.FullTableScanConsumer) GetStatement(herddb.model.commands.GetStatement) Entry(java.util.Map.Entry) Statement(herddb.model.Statement) LongAdder(java.util.concurrent.atomic.LongAdder) DataScannerException(herddb.model.DataScannerException) GetResult(herddb.model.GetResult) PrimaryIndexSeek(herddb.index.PrimaryIndexSeek) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) Function(java.util.function.Function) BatchOrderedExecutor(herddb.utils.BatchOrderedExecutor) ConcurrentMap(java.util.concurrent.ConcurrentMap) Level(java.util.logging.Level) HashSet(java.util.HashSet) BooleanHolder(herddb.utils.BooleanHolder) ScanLimits(herddb.model.ScanLimits) DeleteStatement(herddb.model.commands.DeleteStatement) Iterator(java.util.Iterator) ReentrantLock(java.util.concurrent.locks.ReentrantLock) Semaphore(java.util.concurrent.Semaphore) DataPageDoesNotExistException(herddb.storage.DataPageDoesNotExistException) Counter(org.apache.bookkeeper.stats.Counter) StatementExecutionResult(herddb.model.StatementExecutionResult) TimeUnit(java.util.concurrent.TimeUnit) Consumer(java.util.function.Consumer) CommitLog(herddb.log.CommitLog) AbstractMap(java.util.AbstractMap) TableConsistencyCheckStatement(herddb.model.commands.TableConsistencyCheckStatement) Predicate(herddb.model.Predicate) StatementEvaluationContext(herddb.model.StatementEvaluationContext) Comparator(java.util.Comparator) Collections(java.util.Collections) SECONDS(java.util.concurrent.TimeUnit.SECONDS) TableManagerStats(herddb.core.stats.TableManagerStats) SystemProperties(herddb.utils.SystemProperties) ArrayList(java.util.ArrayList) ScanLimitsImpl(herddb.model.ScanLimitsImpl) StatementExecutionException(herddb.model.StatementExecutionException) Predicate(herddb.model.Predicate) DataScanner(herddb.model.DataScanner) Column(herddb.model.Column) Record(herddb.model.Record) StatementEvaluationContext(herddb.model.StatementEvaluationContext) ForeignKeyDef(herddb.model.ForeignKeyDef) ScanStatement(herddb.model.commands.ScanStatement) DataScannerException(herddb.model.DataScannerException)

Example 10 with ForeignKeyDef

use of herddb.model.ForeignKeyDef in project herddb by diennea.

the class TableManager method executeForeignKeyConstraintsAsParentTable.

private void executeForeignKeyConstraintsAsParentTable(Table childTable, DataAccessor previousValuesOnParentTable, StatementEvaluationContext context, Transaction transaction, boolean delete) throws StatementExecutionException {
    // invalidated consistently during DML operations.
    for (ForeignKeyDef fk : childTable.foreignKeys) {
        String query = parentForeignKeyQueries.computeIfAbsent(childTable.name + "." + fk.name + ".#" + delete, (l -> {
            if (fk.onDeleteAction == ForeignKeyDef.ACTION_CASCADE && delete) {
                StringBuilder q = new StringBuilder("DELETE FROM ");
                q.append(delimit(childTable.tablespace));
                q.append(".");
                q.append(delimit(childTable.name));
                q.append(" WHERE ");
                for (int i = 0; i < fk.columns.length; i++) {
                    if (i > 0) {
                        q.append(" AND ");
                    }
                    q.append(delimit(fk.columns[i]));
                    q.append("=?");
                }
                return q.toString();
            } else if (fk.onUpdateAction == ForeignKeyDef.ACTION_CASCADE && !delete) {
                // the change is more complex, let's keep it for a future work
                throw new StatementExecutionException("No supported ON UPDATE CASCADE");
            } else if ((fk.onDeleteAction == ForeignKeyDef.ACTION_SETNULL && delete) || (fk.onUpdateAction == ForeignKeyDef.ACTION_SETNULL && !delete)) {
                // delete or update it is the same for SET NULL
                StringBuilder q = new StringBuilder("UPDATE ");
                q.append(delimit(childTable.tablespace));
                q.append(".");
                q.append(delimit(childTable.name));
                q.append(" SET ");
                for (int i = 0; i < fk.columns.length; i++) {
                    if (i > 0) {
                        q.append(",");
                    }
                    q.append(delimit(fk.columns[i]));
                    q.append("= NULL ");
                }
                q.append(" WHERE ");
                for (int i = 0; i < fk.columns.length; i++) {
                    if (i > 0) {
                        q.append(" AND ");
                    }
                    q.append(delimit(fk.columns[i]));
                    q.append("=?");
                }
                return q.toString();
            } else {
                // NO ACTION case, check that there is no matching record in the child table that wouble be invalidated
                // with '*' we are not going to perform projections or copies
                StringBuilder q = new StringBuilder("SELECT * FROM ");
                q.append(delimit(childTable.tablespace));
                q.append(".");
                q.append(delimit(childTable.name));
                q.append(" WHERE ");
                for (int i = 0; i < fk.columns.length; i++) {
                    if (i > 0) {
                        q.append(" AND ");
                    }
                    q.append(delimit(fk.columns[i]));
                    q.append("=?");
                }
                return q.toString();
            }
        }));
        final List<Object> valuesToMatch = new ArrayList<>(fk.parentTableColumns.length);
        for (int i = 0; i < fk.parentTableColumns.length; i++) {
            valuesToMatch.add(previousValuesOnParentTable.get(fk.parentTableColumns[i]));
        }
        TransactionContext tx = transaction != null ? new TransactionContext(transaction.transactionId) : TransactionContext.NO_TRANSACTION;
        if (fk.onDeleteAction == ForeignKeyDef.ACTION_CASCADE && delete || fk.onUpdateAction == ForeignKeyDef.ACTION_CASCADE && !delete || fk.onUpdateAction == ForeignKeyDef.ACTION_SETNULL && !delete || fk.onDeleteAction == ForeignKeyDef.ACTION_SETNULL && delete) {
            tableSpaceManager.getDbmanager().executeSimpleStatement(tableSpaceManager.getTableSpaceName(), query, valuesToMatch, // every record
            -1, // keep read locks in TransactionContext
            true, tx, null);
        } else {
            boolean fkOk;
            try (DataScanner scan = tableSpaceManager.getDbmanager().executeSimpleQuery(tableSpaceManager.getTableSpaceName(), query, valuesToMatch, // only one record
            1, // keep read locks in TransactionContext
            true, tx, null)) {
                List<DataAccessor> resultSet = scan.consume();
                // we are on the parent side of the relation
                // we are okay if there is no matching record
                // TODO: return the list of PKs in order to implement CASCADE operations
                fkOk = resultSet.isEmpty();
            } catch (DataScannerException err) {
                throw new StatementExecutionException(err);
            }
            if (!fkOk) {
                throw new ForeignKeyViolationException(fk.name, "foreignKey " + childTable.name + "." + fk.name + " violated");
            }
        }
    }
}
Also used : Arrays(java.util.Arrays) NullLockManager(herddb.utils.NullLockManager) Table(herddb.model.Table) TruncateTableStatement(herddb.model.commands.TruncateTableStatement) DuplicatePrimaryKeyException(herddb.model.DuplicatePrimaryKeyException) TableStatus(herddb.storage.TableStatus) Map(java.util.Map) DataAccessor(herddb.utils.DataAccessor) LogNotAvailableException(herddb.log.LogNotAvailableException) CommitLogResult(herddb.log.CommitLogResult) LogSequenceNumber(herddb.log.LogSequenceNumber) UniqueIndexContraintViolationException(herddb.model.UniqueIndexContraintViolationException) Set(java.util.Set) RecordSerializer(herddb.codec.RecordSerializer) JSQLParserPlanner.delimit(herddb.sql.JSQLParserPlanner.delimit) DataPageMetaData(herddb.core.PageSet.DataPageMetaData) ScanStatement(herddb.model.commands.ScanStatement) Stream(java.util.stream.Stream) StatsLogger(org.apache.bookkeeper.stats.StatsLogger) Bytes(herddb.utils.Bytes) Holder(herddb.utils.Holder) LockHandle(herddb.utils.LockHandle) ForeignKeyViolationException(herddb.model.ForeignKeyViolationException) LogEntry(herddb.log.LogEntry) ArrayList(java.util.ArrayList) TransactionContext(herddb.model.TransactionContext) Transaction(herddb.model.Transaction) ThreadLocalRandom(java.util.concurrent.ThreadLocalRandom) Projection(herddb.model.Projection) ForeignKeyDef(herddb.model.ForeignKeyDef) EnsureLongIncrementAccumulator(herddb.utils.EnsureLongIncrementAccumulator) LogEntryType(herddb.log.LogEntryType) Record(herddb.model.Record) LogEntryFactory(herddb.log.LogEntryFactory) KeyToPageIndex(herddb.index.KeyToPageIndex) DataStorageManager(herddb.storage.DataStorageManager) ColumnTypes(herddb.model.ColumnTypes) ILocalLockManager(herddb.utils.ILocalLockManager) AtomicLong(java.util.concurrent.atomic.AtomicLong) Lock(java.util.concurrent.locks.Lock) IndexOperation(herddb.index.IndexOperation) Column(herddb.model.Column) StampedLock(java.util.concurrent.locks.StampedLock) UpdateStatement(herddb.model.commands.UpdateStatement) ScanLimitsImpl(herddb.model.ScanLimitsImpl) TupleComparator(herddb.model.TupleComparator) ServerConfiguration(herddb.server.ServerConfiguration) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) RecordTooBigException(herddb.model.RecordTooBigException) DMLStatementExecutionResult(herddb.model.DMLStatementExecutionResult) LocalLockManager(herddb.utils.LocalLockManager) Futures(herddb.utils.Futures) DataStorageManagerException(herddb.storage.DataStorageManagerException) Index(herddb.model.Index) InsertStatement(herddb.model.commands.InsertStatement) DataScanner(herddb.model.DataScanner) DDLException(herddb.model.DDLException) RecordFunction(herddb.model.RecordFunction) StatementExecutionException(herddb.model.StatementExecutionException) TableContext(herddb.model.TableContext) Collection(java.util.Collection) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Logger(java.util.logging.Logger) Collectors(java.util.stream.Collectors) List(java.util.List) FullTableScanConsumer(herddb.storage.FullTableScanConsumer) GetStatement(herddb.model.commands.GetStatement) Entry(java.util.Map.Entry) Statement(herddb.model.Statement) LongAdder(java.util.concurrent.atomic.LongAdder) DataScannerException(herddb.model.DataScannerException) GetResult(herddb.model.GetResult) PrimaryIndexSeek(herddb.index.PrimaryIndexSeek) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) Function(java.util.function.Function) BatchOrderedExecutor(herddb.utils.BatchOrderedExecutor) ConcurrentMap(java.util.concurrent.ConcurrentMap) Level(java.util.logging.Level) HashSet(java.util.HashSet) BooleanHolder(herddb.utils.BooleanHolder) ScanLimits(herddb.model.ScanLimits) DeleteStatement(herddb.model.commands.DeleteStatement) Iterator(java.util.Iterator) ReentrantLock(java.util.concurrent.locks.ReentrantLock) Semaphore(java.util.concurrent.Semaphore) DataPageDoesNotExistException(herddb.storage.DataPageDoesNotExistException) Counter(org.apache.bookkeeper.stats.Counter) StatementExecutionResult(herddb.model.StatementExecutionResult) TimeUnit(java.util.concurrent.TimeUnit) Consumer(java.util.function.Consumer) CommitLog(herddb.log.CommitLog) AbstractMap(java.util.AbstractMap) TableConsistencyCheckStatement(herddb.model.commands.TableConsistencyCheckStatement) Predicate(herddb.model.Predicate) StatementEvaluationContext(herddb.model.StatementEvaluationContext) Comparator(java.util.Comparator) Collections(java.util.Collections) SECONDS(java.util.concurrent.TimeUnit.SECONDS) TableManagerStats(herddb.core.stats.TableManagerStats) SystemProperties(herddb.utils.SystemProperties) DataAccessor(herddb.utils.DataAccessor) ArrayList(java.util.ArrayList) StatementExecutionException(herddb.model.StatementExecutionException) DataScanner(herddb.model.DataScanner) ForeignKeyViolationException(herddb.model.ForeignKeyViolationException) TransactionContext(herddb.model.TransactionContext) ForeignKeyDef(herddb.model.ForeignKeyDef) DataScannerException(herddb.model.DataScannerException)

Aggregations

ForeignKeyDef (herddb.model.ForeignKeyDef)11 Table (herddb.model.Table)11 StatementExecutionException (herddb.model.StatementExecutionException)10 Column (herddb.model.Column)9 ArrayList (java.util.ArrayList)8 CommitLogResult (herddb.log.CommitLogResult)7 LogEntry (herddb.log.LogEntry)7 LogNotAvailableException (herddb.log.LogNotAvailableException)7 DataStorageManagerException (herddb.storage.DataStorageManagerException)7 DDLException (herddb.model.DDLException)6 DataScannerException (herddb.model.DataScannerException)6 Index (herddb.model.Index)6 Record (herddb.model.Record)6 RecordSerializer (herddb.codec.RecordSerializer)5 DataPageMetaData (herddb.core.PageSet.DataPageMetaData)5 TableManagerStats (herddb.core.stats.TableManagerStats)5 IndexOperation (herddb.index.IndexOperation)5 KeyToPageIndex (herddb.index.KeyToPageIndex)5 PrimaryIndexSeek (herddb.index.PrimaryIndexSeek)5 CommitLog (herddb.log.CommitLog)5