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 }