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 }