View Javadoc
1   /*
2   Copyright (c) 2013 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;
18  
19  import java.io.Closeable;
20  import java.io.File;
21  import java.io.Flushable;
22  import java.io.IOException;
23  import java.nio.charset.Charset;
24  import java.nio.file.Path;
25  import java.time.ZoneId;
26  import java.util.ConcurrentModificationException;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  import java.util.TimeZone;
32  import java.util.stream.Stream;
33  import java.util.stream.StreamSupport;
34  
35  import com.healthmarketscience.jackcess.expr.EvalConfig;
36  import com.healthmarketscience.jackcess.impl.DatabaseImpl;
37  import com.healthmarketscience.jackcess.query.Query;
38  import com.healthmarketscience.jackcess.util.ColumnValidatorFactory;
39  import com.healthmarketscience.jackcess.util.ErrorHandler;
40  import com.healthmarketscience.jackcess.util.LinkResolver;
41  import com.healthmarketscience.jackcess.util.TableIterableBuilder;
42  
43  /**
44   * An Access database instance.  A new instance can be instantiated by opening
45   * an existing database file ({@link DatabaseBuilder#open(File)}) or creating
46   * a new database file ({@link DatabaseBuilder#create(Database.FileFormat,File)}) (for
47   * more advanced opening/creating use {@link DatabaseBuilder}).  Once a
48   * Database has been opened, you can interact with the data via the relevant
49   * {@link Table}.  When a Database instance is no longer useful, it should
50   * <b>always</b> be closed ({@link #close}) to avoid corruption.
51   * <p>
52   * Database instances (and all the related objects) are <i>not</i>
53   * thread-safe.  However, separate Database instances (and their respective
54   * objects) can be used by separate threads without a problem.
55   * <p>
56   * Database instances do not implement any "transactional" support, and
57   * therefore concurrent editing of the same database file by multiple Database
58   * instances (or with outside programs such as MS Access) <i>will generally
59   * result in database file corruption</i>.
60   *
61   * @author James Ahlborn
62   * @usage _general_class_
63   */
64  public interface Database extends Iterable<Table>, Closeable, Flushable
65  {
66    /** default value for the auto-sync value ({@code true}).  this is slower,
67     *  but leaves more chance of a useable database in the face of failures.
68     * @usage _general_field_
69     */
70    public static final boolean DEFAULT_AUTO_SYNC = true;
71  
72    /**
73     * the default sort order for table columns.
74     * @usage _intermediate_field_
75     */
76    public static final Table.ColumnOrder DEFAULT_COLUMN_ORDER =
77      Table.ColumnOrder.DATA;
78  
79    /** system property which can be used to set the default TimeZone used for
80     *  date calculations.
81     * @usage _general_field_
82     */
83    public static final String TIMEZONE_PROPERTY =
84      "com.healthmarketscience.jackcess.timeZone";
85  
86    /** system property prefix which can be used to set the default Charset
87     *  used for text data (full property includes the JetFormat version).
88     * @usage _general_field_
89     */
90    public static final String CHARSET_PROPERTY_PREFIX =
91      "com.healthmarketscience.jackcess.charset.";
92  
93    /** system property which can be used to set the path from which classpath
94     *  resources are loaded (must end with a "/" if non-empty).  Default value
95     *  is {@value com.healthmarketscience.jackcess.impl.DatabaseImpl#DEFAULT_RESOURCE_PATH}
96     *  if unspecified.
97     * @usage _general_field_
98     */
99    public static final String RESOURCE_PATH_PROPERTY =
100     "com.healthmarketscience.jackcess.resourcePath";
101 
102   /** (boolean) system property which can be used to indicate that the current
103    *  vm has a poor nio implementation (specifically for
104    *  {@code FileChannel.transferFrom})
105    * @usage _intermediate_field_
106    */
107   public static final String BROKEN_NIO_PROPERTY =
108     "com.healthmarketscience.jackcess.brokenNio";
109 
110   /** system property which can be used to set the default sort order for
111    *  table columns.  Value should be one of {@link Table.ColumnOrder} enum
112    *  values.
113    * @usage _intermediate_field_
114    */
115   public static final String COLUMN_ORDER_PROPERTY =
116     "com.healthmarketscience.jackcess.columnOrder";
117 
118   /** system property which can be used to set the default enforcement of
119    * foreign-key relationships.  Defaults to {@code true}.
120    * @usage _general_field_
121    */
122   public static final String FK_ENFORCE_PROPERTY =
123     "com.healthmarketscience.jackcess.enforceForeignKeys";
124 
125   /** system property which can be used to set the default allow auto number
126    * insert policy.  Defaults to {@code false}.
127    * @usage _general_field_
128    */
129   public static final String ALLOW_AUTONUM_INSERT_PROPERTY =
130     "com.healthmarketscience.jackcess.allowAutoNumberInsert";
131 
132   /** system property which can be used to disable expression evaluation
133    * if necessary.  Defaults to {@code true}.
134    * @usage _general_field_
135    */
136   public static final String ENABLE_EXPRESSION_EVALUATION_PROPERTY =
137     "com.healthmarketscience.jackcess.enableExpressionEvaluation";
138 
139   /** system property which can be used to set the default date/Time type.
140    * Value should be one of {@link DateTimeType} enum values.
141    * @usage _general_field_
142    */
143   public static final String DATE_TIME_TYPE_PROPERTY =
144     "com.healthmarketscience.jackcess.dateTimeType";
145 
146   /** (boolean) system property which can be used to allow writing indexes
147    * with unsupported text sort orders.  Defaults to {@code false}.  When
148    * enabled, instead of failing, an index with an unsupported text sort order
149    * will be written using the general legacy sort order.  This allows the
150    * database to be created with all the structure necessary by jackcess, and
151    * then the index can be fixed by using "compact and repair" in MS Access.
152    * @usage _intermediate_field_
153    */
154   public static final String WRITE_BROKEN_INDEX_PROPERTY =
155     "com.healthmarketscience.jackcess.writeBrokenIndex";
156 
157   /**
158    * Enum which indicates which version of Access created the database.
159    * @usage _general_class_
160    */
161   public enum FileFormat {
162 
163     /** A database which was created by MS Access 97 */
164     V1997(".mdb"),
165     /** A database which was most likely created programmatically (e.g. using
166         windows ADOX) */
167     GENERIC_JET4(".mdb"),
168     /** A database which was created by MS Access 2000 */
169     V2000(".mdb"),
170     /** A database which was created by MS Access 2002/2003 */
171     V2003(".mdb"),
172     /** A database which was created by MS Access 2007 */
173     V2007(".accdb"),
174     /** A database which was created by MS Access 2010+ */
175     V2010(".accdb"),
176     /** A database which was created by MS Access 2016+ */
177     V2016(".accdb"),
178     /** A database which was created by MS Access 2019+ (Office 365) */
179     V2019(".accdb"),
180     /** A database which was created by MS Money */
181     MSISAM(".mny");
182 
183     private final String _ext;
184 
185     private FileFormat(String ext) {
186       _ext = ext;
187     }
188 
189     /**
190      * @return the file extension used for database files with this format.
191      */
192     public String getFileExtension() { return _ext; }
193 
194     @Override
195     public String toString() {
196       return name() + " [" + DatabaseImpl.getFileFormatDetails(this).getFormat() + "]";
197     }
198   }
199 
200   /**
201    * Returns the File underlying this Database
202    */
203   public File getFile();
204 
205   /**
206    * Returns the File underlying this Database
207    */
208   public Path getPath();
209 
210   /**
211    * @return The names of all of the user tables
212    * @usage _general_method_
213    */
214   public Set<String> getTableNames() throws IOException;
215 
216   /**
217    * @return The names of all of the system tables (String).  Note, in order
218    *         to read these tables, you must use {@link #getSystemTable}.
219    *         <i>Extreme care should be taken if modifying these tables
220    *         directly!</i>.
221    * @usage _intermediate_method_
222    */
223   public Set<String> getSystemTableNames() throws IOException;
224 
225   /**
226    * @return an unmodifiable Iterator of the user Tables in this Database.
227    * @throws RuntimeIOException if an IOException is thrown by one of the
228    *         operations, the actual exception will be contained within
229    * @throws ConcurrentModificationException if a table is added to the
230    *         database while an Iterator is in use.
231    * @usage _general_method_
232    */
233   @Override
234   public Iterator<Table> iterator();
235 
236   /**
237    * @return a Stream using the default Iterator.
238    */
239   default public Stream<Table> stream() {
240     return StreamSupport.stream(spliterator(), false);
241   }
242 
243   /**
244    * Convenience method for constructing a new TableIterableBuilder for this
245    * cursor.  A TableIterableBuilder provides a variety of options for more
246    * flexible iteration of Tables.
247    */
248   public TableIterableBuilder newIterable();
249 
250   /**
251    * @return an Iterable which returns an unmodifiable Iterator of the the
252    *         TableMetaData for all tables in this Database.
253    * @throws RuntimeIOException if an IOException is thrown by one of the
254    *         operations, the actual exception will be contained within
255    * @throws ConcurrentModificationException if a table is added to the
256    *         database while an Iterator is in use.
257    * @usage _intermediate_method_
258    */
259   public Iterable<TableMetaData> newTableMetaDataIterable();
260 
261   /**
262    * @return a Stream using the {@link #newTableMetaDataIterable}
263    */
264   default public Stream<TableMetaData> newTableMetaDataStream() {
265     return StreamSupport.stream(
266         newTableMetaDataIterable().spliterator(), false);
267   }
268 
269   /**
270    * @param name User table name (case-insensitive)
271    * @return The Table, or null if it doesn't exist (or is a system table)
272    * @usage _general_method_
273    */
274   public Table getTable(String name) throws IOException;
275 
276   /**
277    * @param name Table name (case-insensitive), may be any table type
278    *             (i.e. includes system or linked tables).
279    * @return The meta data for the table, or null if it doesn't exist
280    * @usage _intermediate_method_
281    */
282   public TableMetaData getTableMetaData(String name) throws IOException;
283 
284   /**
285    * Finds all the relationships in the database between the given tables.
286    * @usage _intermediate_method_
287    */
288   public List<Relationship> getRelationships(Tablef="../../../com/healthmarketscience/jackcess/Table.html#Table">Table table1, Table table2)
289     throws IOException;
290 
291   /**
292    * Finds all the relationships in the database for the given table.
293    * @usage _intermediate_method_
294    */
295   public List<Relationship> getRelationships(Table table) throws IOException;
296 
297   /**
298    * Finds all the relationships in the database in <i>non-system</i> tables.
299    * <p>
300    * Warning, this may load <i>all</i> the Tables (metadata, not data) in the
301    * database which could cause memory issues.
302    * @usage _intermediate_method_
303    */
304   public List<Relationship> getRelationships() throws IOException;
305 
306   /**
307    * Finds <i>all</i> the relationships in the database, <i>including system
308    * tables</i>.
309    * <p>
310    * Warning, this may load <i>all</i> the Tables (metadata, not data) in the
311    * database which could cause memory issues.
312    * @usage _intermediate_method_
313    */
314   public List<Relationship> getSystemRelationships()
315     throws IOException;
316 
317   /**
318    * Finds all the queries in the database.
319    * @usage _intermediate_method_
320    */
321   public List<Query> getQueries() throws IOException;
322 
323   /**
324    * Returns a reference to <i>any</i> available table in this access
325    * database, including system tables.
326    * <p>
327    * Warning, this method is not designed for common use, only for the
328    * occassional time when access to a system table is necessary.  Messing
329    * with system tables can strip the paint off your house and give your whole
330    * family a permanent, orange afro.  You have been warned.
331    *
332    * @param tableName Table name, may be a system table
333    * @return The table, or {@code null} if it doesn't exist
334    * @usage _intermediate_method_
335    */
336   public Table getSystemTable(String tableName) throws IOException;
337 
338   /**
339    * @return the core properties for the database
340    * @usage _general_method_
341    */
342   public PropertyMap getDatabaseProperties() throws IOException;
343 
344   /**
345    * @return the summary properties for the database
346    * @usage _general_method_
347    */
348   public PropertyMap getSummaryProperties() throws IOException;
349 
350   /**
351    * @return the user-defined properties for the database
352    * @usage _general_method_
353    */
354   public PropertyMap getUserDefinedProperties() throws IOException;
355 
356   /**
357    * @return the current database password, or {@code null} if none set.
358    * @usage _general_method_
359    */
360   public String getDatabasePassword() throws IOException;
361 
362   /**
363    * Create a new table in this database
364    * @param name Name of the table to create in this database
365    * @param linkedDbName path to the linked database
366    * @param linkedTableName name of the table in the linked database
367    * @usage _general_method_
368    */
369   public void createLinkedTable(String name, String linkedDbName,
370                                 String linkedTableName)
371     throws IOException;
372 
373   /**
374    * Flushes any current changes to the database file (and any linked
375    * databases) to disk.
376    * @usage _general_method_
377    */
378   @Override
379   public void flush() throws IOException;
380 
381   /**
382    * Close the database file (and any linked databases).  A Database
383    * <b>must</b> be closed after use or changes could be lost and the Database
384    * file corrupted.  A Database instance should be treated like any other
385    * external resource which would be closed in a finally block (e.g. an
386    * OutputStream or jdbc Connection).
387    * @usage _general_method_
388    */
389   @Override
390   public void close() throws IOException;
391 
392   /**
393    * Gets the currently configured ErrorHandler (always non-{@code null}).
394    * This will be used to handle all errors unless overridden at the Table or
395    * Cursor level.
396    * @usage _intermediate_method_
397    */
398   public ErrorHandler getErrorHandler();
399 
400   /**
401    * Sets a new ErrorHandler.  If {@code null}, resets to the
402    * {@link ErrorHandler#DEFAULT}.
403    * @usage _intermediate_method_
404    */
405   public void setErrorHandler(ErrorHandler newErrorHandler);
406 
407   /**
408    * Gets the currently configured LinkResolver (always non-{@code null}).
409    * This will be used to handle all linked database loading.
410    * @usage _intermediate_method_
411    */
412   public LinkResolver getLinkResolver();
413 
414   /**
415    * Sets a new LinkResolver.  If {@code null}, resets to the
416    * {@link LinkResolver#DEFAULT}.
417    * @usage _intermediate_method_
418    */
419   public void setLinkResolver(LinkResolver newLinkResolver);
420 
421   /**
422    * Returns an unmodifiable view of the currently loaded linked databases,
423    * mapped from the linked database file name to the linked database.  This
424    * information may be useful for implementing a LinkResolver.
425    * @usage _intermediate_method_
426    */
427   public Map<String,Database> getLinkedDatabases();
428 
429 
430   /**
431    * Returns {@code true} if this Database links to the given Table, {@code
432    * false} otherwise.
433    * @usage _general_method_
434    */
435   public boolean isLinkedTable(Table table) throws IOException;
436 
437   /**
438    * Gets currently configured TimeZone (always non-{@code null} and aligned
439    * with the ZoneId).
440    * @usage _intermediate_method_
441    */
442   public TimeZone getTimeZone();
443 
444   /**
445    * Sets a new TimeZone.  If {@code null}, resets to the default value.  Note
446    * that setting the TimeZone will alter the ZoneId as well.
447    * @usage _intermediate_method_
448    */
449   public void setTimeZone(TimeZone newTimeZone);
450 
451   /**
452    * Gets currently configured ZoneId (always non-{@code null} and aligned
453    * with the TimeZone).
454    * @usage _intermediate_method_
455    */
456   public ZoneId getZoneId();
457 
458   /**
459    * Sets a new ZoneId.  If {@code null}, resets to the default value.  Note
460    * that setting the ZoneId will alter the TimeZone as well.
461    * @usage _intermediate_method_
462    */
463   public void setZoneId(ZoneId newZoneId);
464 
465   /**
466    * Gets currently configured Charset (always non-{@code null}).
467    * @usage _intermediate_method_
468    */
469   public Charset getCharset();
470 
471   /**
472    * Sets a new Charset.  If {@code null}, resets to the default value.
473    * @usage _intermediate_method_
474    */
475   public void setCharset(Charset newCharset);
476 
477   /**
478    * Gets currently configured {@link Table.ColumnOrder} (always non-{@code
479    * null}).
480    * @usage _intermediate_method_
481    */
482   public Table.ColumnOrder getColumnOrder();
483 
484   /**
485    * Sets a new Table.ColumnOrder.  If {@code null}, resets to the default value.
486    * @usage _intermediate_method_
487    */
488   public void setColumnOrder(Table.ColumnOrder newColumnOrder);
489 
490   /**
491    * Gets current foreign-key enforcement policy.
492    * @usage _intermediate_method_
493    */
494   public boolean isEnforceForeignKeys();
495 
496   /**
497    * Sets a new foreign-key enforcement policy.  If {@code null}, resets to
498    * the default value.
499    * @usage _intermediate_method_
500    */
501   public void setEnforceForeignKeys(Boolean newEnforceForeignKeys);
502 
503   /**
504    * Gets current allow auto number insert policy.  By default, jackcess does
505    * not allow auto numbers to be inserted or updated directly (they are
506    * always handled internally by the Table).  Setting this policy to {@code
507    * true} allows the caller to optionally set the value explicitly when
508    * adding or updating rows (if a value is not provided, it will still be
509    * handled internally by the Table).  This value can be set database-wide
510    * using {@link #setAllowAutoNumberInsert} and/or on a per-table basis using
511    * {@link Table#setAllowAutoNumberInsert} (and/or on a jvm-wide using the
512    * {@link #ALLOW_AUTONUM_INSERT_PROPERTY} system property).  Note that
513    * <i>enabling this feature should be done with care</i> to reduce the
514    * chances of screwing up the database.
515    *
516    * @usage _intermediate_method_
517    */
518   public boolean isAllowAutoNumberInsert();
519 
520   /**
521    * Sets the new auto number insert policy for the database (unless
522    * overridden at the Table level).  If {@code null}, resets to the default
523    * value.
524    * @usage _intermediate_method_
525    */
526   public void setAllowAutoNumberInsert(Boolean allowAutoNumInsert);
527 
528   /**
529    * Gets the current expression evaluation policy.  Expression evaluation is
530    * enabled by default but can be disabled if necessary.
531    */
532   public boolean isEvaluateExpressions();
533 
534   /**
535    * Sets the current expression evaluation policy.  Expression evaluation is
536    * enabled by default but can be disabled if necessary.  If {@code null},
537    * resets to the default value.
538    * @usage _intermediate_method_
539    */
540   public void setEvaluateExpressions(Boolean evaluateExpressions);
541 
542   /**
543    * Gets the current write broken index policy.  See
544    * {@link #WRITE_BROKEN_INDEX_PROPERTY} for details.
545    * @usage _intermediate_method_
546    */
547   public boolean isWriteBrokenIndex();
548 
549   /**
550    * Sets the current write broken index policy.  If {@code null}, resets to
551    * the default value.
552    * @usage _intermediate_method_
553    */
554   public void setWriteBrokenIndex(Boolean writeBrokenIndex);
555 
556   /**
557    * Gets currently configured ColumnValidatorFactory (always non-{@code null}).
558    * @usage _intermediate_method_
559    */
560   public ColumnValidatorFactory getColumnValidatorFactory();
561 
562   /**
563    * Sets a new ColumnValidatorFactory.  If {@code null}, resets to the
564    * default value.  The configured ColumnValidatorFactory will be used to
565    * create ColumnValidator instances on any <i>user</i> tables loaded from
566    * this point onward (this will not be used for system tables).
567    * @usage _intermediate_method_
568    */
569   public void setColumnValidatorFactory(ColumnValidatorFactory newFactory);
570 
571   /**
572    * Returns the FileFormat of this database (which may involve inspecting the
573    * database itself).
574    * @throws IllegalStateException if the file format cannot be determined
575    * @usage _general_method_
576    */
577   public FileFormat getFileFormat() throws IOException;
578 
579   /**
580    * Returns the EvalConfig for configuring expression evaluation.
581    */
582   public EvalConfig getEvalConfig();
583 
584   /**
585    * Gets the currently configured DateTimeType.
586    * @usage _general_method_
587    */
588   public DateTimeType getDateTimeType();
589 
590   /**
591    * Sets the DateTimeType.  If {@code null}, resets to the default value.
592    * @usage _general_method_
593    */
594   public void setDateTimeType(DateTimeType dateTimeType);
595 }