123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- /*
- Copyright (c) 2016 James Ahlborn
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
-
- package com.healthmarketscience.jackcess.impl;
-
- import java.io.IOException;
- import java.nio.ByteBuffer;
- import java.util.ArrayList;
- import java.util.EnumSet;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Set;
-
- import com.healthmarketscience.jackcess.ColumnBuilder;
- import com.healthmarketscience.jackcess.DataType;
- import com.healthmarketscience.jackcess.IndexBuilder;
-
- /**
- * Helper class used to maintain state during table mutation.
- *
- * @author James Ahlborn
- * @usage _advanced_class_
- */
- public class TableUpdater extends TableMutator
- {
- private final TableImpl _table;
-
- private ColumnBuilder _column;
- private IndexBuilder _index;
- private int _origTdefLen;
- private int _addedTdefLen;
- private List<Integer> _nextPages = new ArrayList<Integer>(1);
- private ColumnState _colState;
- private IndexDataState _idxDataState;
- private IndexImpl.ForeignKeyReference _fkReference;
-
- public TableUpdater(TableImpl table) {
- super(table.getDatabase());
- _table = table;
- }
-
- public ColumnBuilder getColumn() {
- return _column;
- }
-
- public IndexBuilder getIndex() {
- return _index;
- }
-
- @Override
- String getTableName() {
- return _table.getName();
- }
-
- @Override
- public int getTdefPageNumber() {
- return _table.getTableDefPageNumber();
- }
-
- @Override
- short getColumnNumber(String colName) {
- for(ColumnImpl col : _table.getColumns()) {
- if(col.getName().equalsIgnoreCase(colName)) {
- return col.getColumnNumber();
- }
- }
- return IndexData.COLUMN_UNUSED;
- }
-
- @Override
- public ColumnState getColumnState(ColumnBuilder col) {
- return ((col == _column) ? _colState : null);
- }
-
- @Override
- public IndexDataState getIndexDataState(IndexBuilder idx) {
- return ((idx == _index) ? _idxDataState : null);
- }
-
- void setForeignKey(IndexImpl.ForeignKeyReference fkReference) {
- _fkReference = fkReference;
- }
-
- @Override
- public IndexImpl.ForeignKeyReference getForeignKey(IndexBuilder idx) {
- return ((idx == _index) ? _fkReference : null);
- }
-
- int getAddedTdefLen() {
- return _addedTdefLen;
- }
-
- void addTdefLen(int add) {
- _addedTdefLen += add;
- }
-
- void setOrigTdefLen(int len) {
- _origTdefLen = len;
- }
-
- List<Integer> getNextPages() {
- return _nextPages;
- }
-
- void resetTdefInfo() {
- _addedTdefLen = 0;
- _origTdefLen = 0;
- _nextPages.clear();
- }
-
- public ColumnImpl addColumn(ColumnBuilder column) throws IOException {
-
- _column = column;
-
- validateAddColumn();
-
- // assign column number and do some assorted column bookkeeping
- short columnNumber = (short)_table.getMaxColumnCount();
- _column.setColumnNumber(columnNumber);
- if(_column.getType().isLongValue()) {
- _colState = new ColumnState();
- }
-
- getPageChannel().startExclusiveWrite();
- try {
-
- return _table.mutateAddColumn(this);
-
- } finally {
- getPageChannel().finishWrite();
- }
- }
-
- public IndexImpl addIndex(IndexBuilder index) throws IOException {
- return addIndex(index, false, (byte)0, (byte)0);
- }
-
- IndexImpl addIndex(IndexBuilder index, boolean isInternal, byte ignoreIdxFlags,
- byte ignoreColFlags)
- throws IOException
- {
- _index = index;
-
- if(!isInternal) {
- validateAddIndex();
- }
-
- // assign index number and do some assorted index bookkeeping
- int indexNumber = _table.getLogicalIndexCount();
- _index.setIndexNumber(indexNumber);
-
- // initialize backing index state
- initIndexDataState(ignoreIdxFlags, ignoreColFlags);
-
- if(!isInternal) {
- getPageChannel().startExclusiveWrite();
- } else {
- getPageChannel().startWrite();
- }
- try {
-
- if(_idxDataState.getIndexDataNumber() == _table.getIndexCount()) {
- // we need a new backing index data
- _table.mutateAddIndexData(this);
-
- // we need to modify the table def again when adding the Index, so reset
- resetTdefInfo();
- }
-
- return _table.mutateAddIndex(this);
-
- } finally {
- getPageChannel().finishWrite();
- }
- }
-
- boolean validateUpdatedTdef(ByteBuffer tableBuffer) {
- // sanity check the updates
- return((_origTdefLen + _addedTdefLen) == tableBuffer.limit());
- }
-
- private void validateAddColumn() {
-
- if(_column == null) {
- throw new IllegalArgumentException(withErrorContext(
- "Cannot add column with no column"));
- }
- if((_table.getColumnCount() + 1) > getFormat().MAX_COLUMNS_PER_TABLE) {
- throw new IllegalArgumentException(withErrorContext(
- "Cannot add column to table with " +
- getFormat().MAX_COLUMNS_PER_TABLE + " columns"));
- }
-
- Set<String> colNames = getColumnNames();
- // next, validate the column definition
- validateColumn(colNames, _column);
-
- if(_column.isAutoNumber()) {
- // for most autonumber types, we can only have one of each type
- Set<DataType> autoTypes = EnumSet.noneOf(DataType.class);
- for(ColumnImpl column : _table.getAutoNumberColumns()) {
- autoTypes.add(column.getType());
- }
-
- validateAutoNumberColumn(autoTypes, _column);
- }
- }
-
- private void validateAddIndex() {
-
- if(_index == null) {
- throw new IllegalArgumentException(withErrorContext(
- "Cannot add index with no index"));
- }
- if((_table.getLogicalIndexCount() + 1) > getFormat().MAX_INDEXES_PER_TABLE) {
- throw new IllegalArgumentException(withErrorContext(
- "Cannot add index to table with " +
- getFormat().MAX_INDEXES_PER_TABLE + " indexes"));
- }
-
- boolean foundPk[] = new boolean[1];
- Set<String> idxNames = getIndexNames(_table, foundPk);
- // next, validate the index definition
- validateIndex(getColumnNames(), idxNames, foundPk, _index);
- }
-
- private Set<String> getColumnNames() {
- Set<String> colNames = new HashSet<String>();
- for(ColumnImpl column : _table.getColumns()) {
- colNames.add(column.getName().toUpperCase());
- }
- return colNames;
- }
-
- static Set<String> getIndexNames(TableImpl table, boolean[] foundPk) {
- Set<String> idxNames = new HashSet<String>();
- for(IndexImpl index : table.getIndexes()) {
- idxNames.add(index.getName().toUpperCase());
- if(index.isPrimaryKey() && (foundPk != null)) {
- foundPk[0] = true;
- }
- }
- return idxNames;
- }
-
- private void initIndexDataState(byte ignoreIdxFlags, byte ignoreColFlags) {
-
- _idxDataState = new IndexDataState();
- _idxDataState.addIndex(_index);
-
- // search for an existing index which matches the given index (in terms of
- // the backing data)
- IndexData idxData = findIndexData(
- _index, _table, ignoreIdxFlags, ignoreColFlags);
-
- int idxDataNumber = ((idxData != null) ?
- idxData.getIndexDataNumber() :
- _table.getIndexCount());
-
- _idxDataState.setIndexDataNumber(idxDataNumber);
- }
-
- static IndexData findIndexData(IndexBuilder idx, TableImpl table,
- byte ignoreIdxFlags, byte ignoreColFlags)
- {
- for(IndexData idxData : table.getIndexDatas()) {
- if(sameIndexData(idx, idxData, ignoreIdxFlags, ignoreColFlags)) {
- return idxData;
- }
- }
- return null;
- }
-
- private static boolean sameIndexData(IndexBuilder idx1, IndexData idx2,
- byte ignoreIdxFlags, byte ignoreColFlags) {
- // index data can be combined if flags match and columns (and col flags)
- // match
- if((idx1.getFlags() | ignoreIdxFlags) !=
- (idx2.getIndexFlags() | ignoreIdxFlags)) {
- return false;
- }
-
- if(idx1.getColumns().size() != idx2.getColumns().size()) {
- return false;
- }
-
- for(int i = 0; i < idx1.getColumns().size(); ++i) {
- IndexBuilder.Column col1 = idx1.getColumns().get(i);
- IndexData.ColumnDescriptor col2 = idx2.getColumns().get(i);
-
- if(!sameIndexData(col1, col2, ignoreColFlags)) {
- return false;
- }
- }
-
- return true;
- }
-
- private static boolean sameIndexData(
- IndexBuilder.Column col1, IndexData.ColumnDescriptor col2,
- int ignoreColFlags) {
- return (col1.getName().equals(col2.getName()) &&
- ((col1.getFlags() | ignoreColFlags) ==
- (col2.getFlags() | ignoreColFlags)));
- }
-
- @Override
- protected String withErrorContext(String msg) {
- String objStr = "";
- if(_column != null) {
- objStr = ";Column=" + _column.getName();
- } else if(_index != null) {
- objStr = ";Index=" + _index.getName();
- }
- return msg + "(Table=" + _table.getName() + objStr + ")";
- }
- }
|