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