View Javadoc
1   /*
2   Copyright (c) 2016 James Ahlborn
3   
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7   
8       http://www.apache.org/licenses/LICENSE-2.0
9   
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15  */
16  
17  package com.healthmarketscience.jackcess.impl;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  import java.util.Set;
22  
23  import com.healthmarketscience.jackcess.ColumnBuilder;
24  import com.healthmarketscience.jackcess.DataType;
25  import com.healthmarketscience.jackcess.IndexBuilder;
26  
27  /**
28   * Common helper class used to maintain state during table mutation.
29   *
30   * @author James Ahlborn
31   * @usage _advanced_class_
32   */
33  public abstract class TableMutator extends DBMutator
34  {
35    private ColumnOffsets _colOffsets;
36  
37    protected TableMutator(DatabaseImpl database) {
38      super(database);
39    }
40  
41    public void setColumnOffsets(
42        int fixedOffset, int varOffset, int longVarOffset) {
43      if(_colOffsets == null) {
44        _colOffsets = new ColumnOffsets();
45      }
46      _colOffsets.set(fixedOffset, varOffset, longVarOffset);
47    }
48  
49    public ColumnOffsets getColumnOffsets() {
50      return _colOffsets;
51    }
52  
53    public IndexImpl.ForeignKeyReference getForeignKey(IndexBuilder idx) {
54      return null;
55    }
56  
57    protected void validateColumn(Set<String> colNames, ColumnBuilder column) {
58  
59        column.validate(getFormat());
60        if(!colNames.add(DatabaseImpl.toLookupName(column.getName()))) {
61          throw new IllegalArgumentException(withErrorContext(
62              "duplicate column name: " + column.getName()));
63        }
64  
65        setColumnSortOrder(column);
66    }
67  
68    protected void validateIndex(Set<String> colNames, Set<String> idxNames,
69                                 boolean[] foundPk, IndexBuilder index) {
70  
71      index.validate(colNames, getFormat());
72      if(!idxNames.add(DatabaseImpl.toLookupName(index.getName()))) {
73        throw new IllegalArgumentException(withErrorContext(
74            "duplicate index name: " + index.getName()));
75      }
76      if(index.isPrimaryKey()) {
77        if(foundPk[0]) {
78          throw new IllegalArgumentException(withErrorContext(
79              "found second primary key index: " + index.getName()));
80        }
81        foundPk[0] = true;
82      } else if(index.getType() == IndexImpl.FOREIGN_KEY_INDEX_TYPE) {
83        if(getForeignKey(index) == null) {
84          throw new IllegalArgumentException(withErrorContext(
85              "missing foreign key info for " + index.getName()));
86        }
87      }
88    }
89  
90    protected void validateAutoNumberColumn(Set<DataType> autoTypes,
91                                            ColumnBuilder column)
92    {
93      if(!column.getType().isMultipleAutoNumberAllowed() &&
94         !autoTypes.add(column.getType())) {
95        throw new IllegalArgumentException(withErrorContext(
96            "Can have at most one AutoNumber column of type " + column.getType() +
97            " per table"));
98      }
99    }
100 
101   private void setColumnSortOrder(ColumnBuilder column) {
102       // set the sort order to the db default (if unspecified)
103       if(column.getType().isTextual() && (column.getTextSortOrder() == null)) {
104         column.setTextSortOrder(getDbSortOrder());
105       }
106   }
107 
108   abstract String getTableName();
109 
110   public abstract int getTdefPageNumber();
111 
112   abstract short getColumnNumber(String colName);
113 
114   /**
115    * @return {@code true} if the named column holds a complex type,
116    *         {@code false} if it does not or if there is no such column
117    */
118   abstract boolean isComplexColumn(String colName);
119 
120   public abstract ColumnState getColumnState(ColumnBuilder col);
121 
122   public abstract IndexDataState getIndexDataState(IndexBuilder idx);
123 
124   protected abstract String withErrorContext(String msg);
125 
126   /**
127    * Maintains additional state used during column writing.
128    * @usage _advanced_class_
129    */
130   static final class ColumnOffsets
131   {
132     private short _fixedOffset;
133     private short _varOffset;
134     private short _longVarOffset;
135 
136     public void set(int fixedOffset, int varOffset, int longVarOffset) {
137       _fixedOffset = (short)fixedOffset;
138       _varOffset = (short)varOffset;
139       _longVarOffset = (short)longVarOffset;
140     }
141 
142     public short getNextVariableOffset(ColumnBuilder col) {
143       if(!col.isVariableLength()) {
144         return _varOffset;
145       }
146       if(!col.getType().isLongValue()) {
147         return _varOffset++;
148       }
149       return _longVarOffset++;
150     }
151 
152     public short getNextFixedOffset(ColumnBuilder col) {
153       if(col.storeInNullMask()) {
154         // booleans are stored in null mask, not in fixed data section
155         return 0;
156       }
157       short offset = _fixedOffset;
158       _fixedOffset += col.getFixedDataSize();
159       return offset;
160     }
161   }
162 
163   /**
164    * Maintains additional state used during column creation.
165    * @usage _advanced_class_
166    */
167   static final class ColumnState
168   {
169     private byte _umapOwnedRowNumber;
170     private byte _umapFreeRowNumber;
171     // we always put both usage maps on the same page
172     private int _umapPageNumber;
173     private int _complexId;
174     private byte _extraFlags;
175 
176     public int getComplexId() {
177       return _complexId;
178     }
179 
180     public void setComplexId(int newComplexId) {
181       _complexId = newComplexId;
182     }
183 
184     public byte getExtraFlags() {
185       return _extraFlags;
186     }
187 
188     public void setExtraFlags(byte newExtraFlags) {
189       _extraFlags = newExtraFlags;
190     }
191 
192     public byte getUmapOwnedRowNumber() {
193       return _umapOwnedRowNumber;
194     }
195 
196     public void setUmapOwnedRowNumber(byte newUmapOwnedRowNumber) {
197       _umapOwnedRowNumber = newUmapOwnedRowNumber;
198     }
199 
200     public byte getUmapFreeRowNumber() {
201       return _umapFreeRowNumber;
202     }
203 
204     public void setUmapFreeRowNumber(byte newUmapFreeRowNumber) {
205       _umapFreeRowNumber = newUmapFreeRowNumber;
206     }
207 
208     public int getUmapPageNumber() {
209       return _umapPageNumber;
210     }
211 
212     public void setUmapPageNumber(int newUmapPageNumber) {
213       _umapPageNumber = newUmapPageNumber;
214     }
215   }
216 
217   /**
218    * Maintains additional state used during index data creation.
219    * @usage _advanced_class_
220    */
221   static final class IndexDataState
222   {
223     private final List<IndexBuilder> _indexes = new ArrayList<>();
224     private int _indexDataNumber;
225     private byte _umapRowNumber;
226     private int _umapPageNumber;
227     private int _rootPageNumber;
228 
229     public IndexBuilder getFirstIndex() {
230       // all indexes which have the same backing IndexDataState will have
231       // equivalent columns and flags.
232       return _indexes.get(0);
233     }
234 
235     public List<IndexBuilder> getIndexes() {
236       return _indexes;
237     }
238 
239     public void addIndex(IndexBuilder idx) {
240       _indexes.add(idx);
241     }
242 
243     public int getIndexDataNumber() {
244       return _indexDataNumber;
245     }
246 
247     public void setIndexDataNumber(int newIndexDataNumber) {
248       _indexDataNumber = newIndexDataNumber;
249     }
250 
251     public byte getUmapRowNumber() {
252       return _umapRowNumber;
253     }
254 
255     public void setUmapRowNumber(byte newUmapRowNumber) {
256       _umapRowNumber = newUmapRowNumber;
257     }
258 
259     public int getUmapPageNumber() {
260       return _umapPageNumber;
261     }
262 
263     public void setUmapPageNumber(int newUmapPageNumber) {
264       _umapPageNumber = newUmapPageNumber;
265     }
266 
267     public int getRootPageNumber() {
268       return _rootPageNumber;
269     }
270 
271     public void setRootPageNumber(int newRootPageNumber) {
272       _rootPageNumber = newRootPageNumber;
273     }
274   }
275 }