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 /** system property which can be used to set the default allow auto number
127 * insert policy. Defaults to {@code false}.
128 * @usage _general_field_
129 */
130 public static final String ALLOW_AUTONUM_INSERT_PROPERTY =
131 "com.healthmarketscience.jackcess.allowAutoNumberInsert";
132
133 /** system property which can be used to disable expression evaluation
134 * if necessary. Defaults to {@code true}.
135 * @usage _general_field_
136 */
137 public static final String ENABLE_EXPRESSION_EVALUATION_PROPERTY =
138 "com.healthmarketscience.jackcess.enableExpressionEvaluation";
139
140 /** system property which can be used to set the default date/Time type.
141 * Value should be one of {@link DateTimeType} enum values.
142 * @usage _general_field_
143 */
144 public static final String DATE_TIME_TYPE_PROPERTY =
145 "com.healthmarketscience.jackcess.dateTimeType";
146
147 /** (boolean) system property which can be used to allow writing indexes
148 * with unsupported text sort orders. Defaults to {@code false}. When
149 * enabled, instead of failing, an index with an unsupported text sort order
150 * will be written using the general legacy sort order. This allows the
151 * database to be created with all the structure necessary by jackcess, and
152 * then the index can be fixed by using "compact and repair" in MS Access.
153 * @usage _intermediate_field_
154 */
155 public static final String WRITE_BROKEN_INDEX_PROPERTY =
156 "com.healthmarketscience.jackcess.writeBrokenIndex";
157
158 /** (boolean) system property which can be used to allow resolution of
159 * linked databases when no LinkResolver has been configured. Defaults to
160 * {@code false}, in which case {@link LinkResolver#DEFAULT} refuses to open
161 * any linked database. When enabled, {@link LinkResolver#UNRESTRICTED} is
162 * used instead, which opens whatever file name the linking database
163 * specifies. Since that file name comes from the database file itself,
164 * only enable this when the linked database file names can be trusted.
165 * @usage _intermediate_field_
166 */
167 public static final String ALLOW_LINK_RESOLUTION_PROPERTY =
168 "com.healthmarketscience.jackcess.allowLinkResolution";
169
170 /**
171 * Enum which indicates which version of Access created the database.
172 * @usage _general_class_
173 */
174 public enum FileFormat {
175
176 /** A database which was created by MS Access 97 */
177 V1997(".mdb"),
178 /** A database which was most likely created programmatically (e.g. using
179 windows ADOX) */
180 GENERIC_JET4(".mdb"),
181 /** A database which was created by MS Access 2000 */
182 V2000(".mdb"),
183 /** A database which was created by MS Access 2002/2003 */
184 V2003(".mdb"),
185 /** A database which was created by MS Access 2007 */
186 V2007(".accdb"),
187 /** A database which was created by MS Access 2010+ */
188 V2010(".accdb"),
189 /** A database which was created by MS Access 2016+ */
190 V2016(".accdb"),
191 /** A database which was created by MS Access 2019+ (Office 365) */
192 V2019(".accdb"),
193 /** A database which was created by MS Money */
194 MSISAM(".mny");
195
196 private final String _ext;
197
198 private FileFormat(String ext) {
199 _ext = ext;
200 }
201
202 /**
203 * @return the file extension used for database files with this format.
204 */
205 public String getFileExtension() { return _ext; }
206
207 @Override
208 public String toString() {
209 return name() + " [" + DatabaseImpl.getFileFormatDetails(this).getFormat() + "]";
210 }
211 }
212
213 /**
214 * Returns the File underlying this Database
215 */
216 public File getFile();
217
218 /**
219 * Returns the File underlying this Database
220 */
221 public Path getPath();
222
223 /**
224 * @return The names of all of the user tables
225 * @usage _general_method_
226 */
227 public Set<String> getTableNames() throws IOException;
228
229 /**
230 * @return The names of all of the system tables (String). Note, in order
231 * to read these tables, you must use {@link #getSystemTable}.
232 * <i>Extreme care should be taken if modifying these tables
233 * directly!</i>.
234 * @usage _intermediate_method_
235 */
236 public Set<String> getSystemTableNames() throws IOException;
237
238 /**
239 * @return an unmodifiable Iterator of the user Tables in this Database.
240 * @throws UncheckedIOException if an IOException is thrown by one of the
241 * operations, the actual exception will be contained within
242 * @throws ConcurrentModificationException if a table is added to the
243 * database while an Iterator is in use.
244 * @usage _general_method_
245 */
246 @Override
247 public Iterator<Table> iterator();
248
249 /**
250 * @return a Stream using the default Iterator.
251 */
252 default public Stream<Table> stream() {
253 return StreamSupport.stream(spliterator(), false);
254 }
255
256 /**
257 * Convenience method for constructing a new TableIterableBuilder for this
258 * cursor. A TableIterableBuilder provides a variety of options for more
259 * flexible iteration of Tables.
260 */
261 public TableIterableBuilder newIterable();
262
263 /**
264 * @return an Iterable which returns an unmodifiable Iterator of the the
265 * TableMetaData for all tables in this Database.
266 * @throws UncheckedIOException if an IOException is thrown by one of the
267 * operations, the actual exception will be contained within
268 * @throws ConcurrentModificationException if a table is added to the
269 * database while an Iterator is in use.
270 * @usage _intermediate_method_
271 */
272 public Iterable<TableMetaData> newTableMetaDataIterable();
273
274 /**
275 * @return a Stream using the {@link #newTableMetaDataIterable}
276 */
277 default public Stream<TableMetaData> newTableMetaDataStream() {
278 return StreamSupport.stream(
279 newTableMetaDataIterable().spliterator(), false);
280 }
281
282 /**
283 * @param name User table name (case-insensitive)
284 * @return The Table, or null if it doesn't exist (or is a system table)
285 * @usage _general_method_
286 */
287 public Table getTable(String name) throws IOException;
288
289 /**
290 * @param name Table name (case-insensitive), may be any table type
291 * (i.e. includes system or linked tables).
292 * @return The meta data for the table, or null if it doesn't exist
293 * @usage _intermediate_method_
294 */
295 public TableMetaData getTableMetaData(String name) throws IOException;
296
297 /**
298 * Finds all the relationships in the database between the given tables.
299 * @usage _intermediate_method_
300 */
301 public List<Relationship> getRelationships(Tablef="../../../com/healthmarketscience/jackcess/Table.html#Table">Table table1, Table table2)
302 throws IOException;
303
304 /**
305 * Finds all the relationships in the database for the given table.
306 * @usage _intermediate_method_
307 */
308 public List<Relationship> getRelationships(Table table) throws IOException;
309
310 /**
311 * Finds all the relationships in the database in <i>non-system</i> tables.
312 * <p>
313 * Warning, this may load <i>all</i> the Tables (metadata, not data) in the
314 * database which could cause memory issues.
315 * @usage _intermediate_method_
316 */
317 public List<Relationship> getRelationships() throws IOException;
318
319 /**
320 * Finds <i>all</i> the relationships in the database, <i>including system
321 * tables</i>.
322 * <p>
323 * Warning, this may load <i>all</i> the Tables (metadata, not data) in the
324 * database which could cause memory issues.
325 * @usage _intermediate_method_
326 */
327 public List<Relationship> getSystemRelationships()
328 throws IOException;
329
330 /**
331 * Finds all the queries in the database.
332 * @usage _intermediate_method_
333 */
334 public List<Query> getQueries() throws IOException;
335
336 /**
337 * Returns a reference to <i>any</i> available table in this access
338 * database, including system tables.
339 * <p>
340 * Warning, this method is not designed for common use, only for the
341 * occassional time when access to a system table is necessary. Messing
342 * with system tables can strip the paint off your house and give your whole
343 * family a permanent, orange afro. You have been warned.
344 *
345 * @param tableName Table name, may be a system table
346 * @return The table, or {@code null} if it doesn't exist
347 * @usage _intermediate_method_
348 */
349 public Table getSystemTable(String tableName) throws IOException;
350
351 /**
352 * @return the core properties for the database
353 * @usage _general_method_
354 */
355 public PropertyMap getDatabaseProperties() throws IOException;
356
357 /**
358 * @return the summary properties for the database
359 * @usage _general_method_
360 */
361 public PropertyMap getSummaryProperties() throws IOException;
362
363 /**
364 * @return the user-defined properties for the database
365 * @usage _general_method_
366 */
367 public PropertyMap getUserDefinedProperties() throws IOException;
368
369 /**
370 * @return the current database password, or {@code null} if none set.
371 * @usage _general_method_
372 */
373 public String getDatabasePassword() throws IOException;
374
375 /**
376 * Create a new table in this database
377 * @param name Name of the table to create in this database
378 * @param linkedDbName path to the linked database
379 * @param linkedTableName name of the table in the linked database
380 * @usage _general_method_
381 */
382 public void createLinkedTable(String name, String linkedDbName,
383 String linkedTableName)
384 throws IOException;
385
386 /**
387 * Flushes any current changes to the database file (and any linked
388 * databases) to disk.
389 * @usage _general_method_
390 */
391 @Override
392 public void flush() throws IOException;
393
394 /**
395 * Close the database file (and any linked databases). A Database
396 * <b>must</b> be closed after use or changes could be lost and the Database
397 * file corrupted. A Database instance should be treated like any other
398 * external resource which would be closed in a finally block (e.g. an
399 * OutputStream or jdbc Connection).
400 * @usage _general_method_
401 */
402 @Override
403 public void close() throws IOException;
404
405 /**
406 * Gets the currently configured ErrorHandler (always non-{@code null}).
407 * This will be used to handle all errors unless overridden at the Table or
408 * Cursor level.
409 * @usage _intermediate_method_
410 */
411 public ErrorHandler getErrorHandler();
412
413 /**
414 * Sets a new ErrorHandler. If {@code null}, resets to the
415 * {@link ErrorHandler#DEFAULT}.
416 * @usage _intermediate_method_
417 */
418 public void setErrorHandler(ErrorHandler newErrorHandler);
419
420 /**
421 * Gets the currently configured LinkResolver (always non-{@code null}).
422 * This will be used to handle all linked database loading.
423 * @usage _intermediate_method_
424 */
425 public LinkResolver getLinkResolver();
426
427 /**
428 * Sets a new LinkResolver. If {@code null}, resets to the default resolver,
429 * which refuses to open linked databases unless the
430 * {@value #ALLOW_LINK_RESOLUTION_PROPERTY} system property is enabled. Use
431 * {@link LinkResolver#UNRESTRICTED} only when the linked database file names
432 * can be trusted.
433 * @usage _intermediate_method_
434 */
435 public void setLinkResolver(LinkResolver newLinkResolver);
436
437 /**
438 * Returns an unmodifiable view of the currently loaded linked databases,
439 * mapped from the linked database file name to the linked database. This
440 * information may be useful for implementing a LinkResolver.
441 * @usage _intermediate_method_
442 */
443 public Map<String,Database> getLinkedDatabases();
444
445
446 /**
447 * Returns {@code true} if this Database links to the given Table, {@code
448 * false} otherwise.
449 * @usage _general_method_
450 */
451 public boolean isLinkedTable(Table table) throws IOException;
452
453 /**
454 * Gets currently configured TimeZone (always non-{@code null} and aligned
455 * with the ZoneId).
456 * @usage _intermediate_method_
457 */
458 public TimeZone getTimeZone();
459
460 /**
461 * Sets a new TimeZone. If {@code null}, resets to the default value. Note
462 * that setting the TimeZone will alter the ZoneId as well.
463 * @usage _intermediate_method_
464 */
465 public void setTimeZone(TimeZone newTimeZone);
466
467 /**
468 * Gets currently configured ZoneId (always non-{@code null} and aligned
469 * with the TimeZone).
470 * @usage _intermediate_method_
471 */
472 public ZoneId getZoneId();
473
474 /**
475 * Sets a new ZoneId. If {@code null}, resets to the default value. Note
476 * that setting the ZoneId will alter the TimeZone as well.
477 * @usage _intermediate_method_
478 */
479 public void setZoneId(ZoneId newZoneId);
480
481 /**
482 * Gets currently configured Charset (always non-{@code null}).
483 * @usage _intermediate_method_
484 */
485 public Charset getCharset();
486
487 /**
488 * Sets a new Charset. If {@code null}, resets to the default value.
489 * @usage _intermediate_method_
490 */
491 public void setCharset(Charset newCharset);
492
493 /**
494 * Gets currently configured {@link Table.ColumnOrder} (always non-{@code
495 * null}).
496 * @usage _intermediate_method_
497 */
498 public Table.ColumnOrder getColumnOrder();
499
500 /**
501 * Sets a new Table.ColumnOrder. If {@code null}, resets to the default value.
502 * @usage _intermediate_method_
503 */
504 public void setColumnOrder(Table.ColumnOrder newColumnOrder);
505
506 /**
507 * Gets current foreign-key enforcement policy.
508 * @usage _intermediate_method_
509 */
510 public boolean isEnforceForeignKeys();
511
512 /**
513 * Sets a new foreign-key enforcement policy. If {@code null}, resets to
514 * the default value.
515 * @usage _intermediate_method_
516 */
517 public void setEnforceForeignKeys(Boolean newEnforceForeignKeys);
518
519 /**
520 * Gets current allow auto number insert policy. By default, jackcess does
521 * not allow auto numbers to be inserted or updated directly (they are
522 * always handled internally by the Table). Setting this policy to {@code
523 * true} allows the caller to optionally set the value explicitly when
524 * adding or updating rows (if a value is not provided, it will still be
525 * handled internally by the Table). This value can be set database-wide
526 * using {@link #setAllowAutoNumberInsert} and/or on a per-table basis using
527 * {@link Table#setAllowAutoNumberInsert} (and/or on a jvm-wide using the
528 * {@link #ALLOW_AUTONUM_INSERT_PROPERTY} system property). Note that
529 * <i>enabling this feature should be done with care</i> to reduce the
530 * chances of screwing up the database.
531 *
532 * @usage _intermediate_method_
533 */
534 public boolean isAllowAutoNumberInsert();
535
536 /**
537 * Sets the new auto number insert policy for the database (unless
538 * overridden at the Table level). If {@code null}, resets to the default
539 * value.
540 * @usage _intermediate_method_
541 */
542 public void setAllowAutoNumberInsert(Boolean allowAutoNumInsert);
543
544 /**
545 * Gets the current expression evaluation policy. Expression evaluation is
546 * enabled by default but can be disabled if necessary.
547 */
548 public boolean isEvaluateExpressions();
549
550 /**
551 * Sets the current expression evaluation policy. Expression evaluation is
552 * enabled by default but can be disabled if necessary. If {@code null},
553 * resets to the default value.
554 * @usage _intermediate_method_
555 */
556 public void setEvaluateExpressions(Boolean evaluateExpressions);
557
558 /**
559 * Gets the current write broken index policy. See
560 * {@link #WRITE_BROKEN_INDEX_PROPERTY} for details.
561 * @usage _intermediate_method_
562 */
563 public boolean isWriteBrokenIndex();
564
565 /**
566 * Sets the current write broken index policy. If {@code null}, resets to
567 * the default value.
568 * @usage _intermediate_method_
569 */
570 public void setWriteBrokenIndex(Boolean writeBrokenIndex);
571
572 /**
573 * Gets currently configured ColumnValidatorFactory (always non-{@code null}).
574 * @usage _intermediate_method_
575 */
576 public ColumnValidatorFactory getColumnValidatorFactory();
577
578 /**
579 * Sets a new ColumnValidatorFactory. If {@code null}, resets to the
580 * default value. The configured ColumnValidatorFactory will be used to
581 * create ColumnValidator instances on any <i>user</i> tables loaded from
582 * this point onward (this will not be used for system tables).
583 * @usage _intermediate_method_
584 */
585 public void setColumnValidatorFactory(ColumnValidatorFactory newFactory);
586
587 /**
588 * Returns the FileFormat of this database (which may involve inspecting the
589 * database itself).
590 * @throws IllegalStateException if the file format cannot be determined
591 * @usage _general_method_
592 */
593 public FileFormat getFileFormat() throws IOException;
594
595 /**
596 * Returns the EvalConfig for configuring expression evaluation.
597 */
598 public EvalConfig getEvalConfig();
599
600 /**
601 * Gets the currently configured DateTimeType.
602 * @usage _general_method_
603 */
604 public DateTimeType getDateTimeType();
605
606 /**
607 * Sets the DateTimeType. If {@code null}, resets to the default value.
608 * @usage _general_method_
609 */
610 public void setDateTimeType(DateTimeType dateTimeType);
611 }