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 }