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