This is an automated email from the ASF dual-hosted git repository. jimjag pushed a commit to branch hsqldb2 in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit 1333d67c799b27c54be527850670a064eb5a119e Author: Jim Jagielski <[email protected]> AuthorDate: Sat Nov 7 04:33:52 2026 -0500 hsqldb: send the data, LOB and backup files through the storage The patch now throws when the storage class cannot be loaded instead of falling back to the file system. --- main/hsqldb/patches/aoo_integration.patch | 376 ++++++++++++++++++++---------- 1 file changed, 256 insertions(+), 120 deletions(-) diff --git a/main/hsqldb/patches/aoo_integration.patch b/main/hsqldb/patches/aoo_integration.patch index 02ff348dd2..cbd2d43bc4 100644 --- a/main/hsqldb/patches/aoo_integration.patch +++ b/main/hsqldb/patches/aoo_integration.patch @@ -1,160 +1,296 @@ ---- misc/build/hsqldb-2.5.0/hsqldb/src/org/hsqldb/persist/Logger.java 2019-05-31 10:26:20.000000000 -0500 -+++ misc/build/hsqldb-2.5.0/hsqldb/src/org/hsqldb/persist/Logger.java 2026-11-06 10:22:37.173757415 -0500 -@@ -62,6 +62,7 @@ - import org.hsqldb.lib.FileAccess; - import org.hsqldb.lib.FileUtil; - import org.hsqldb.lib.FrameworkLogger; +--- misc/hsqldb/hsqldb-2.5.0/hsqldb/src/org/hsqldb/persist/Logger.java ++++ misc/build/hsqldb-2.5.0/hsqldb/src/org/hsqldb/persist/Logger.java +@@ -34,6 +34,7 @@ + import java.io.File; + import java.io.FileInputStream; + import java.io.IOException; +import java.lang.reflect.Constructor; - import org.hsqldb.lib.HsqlArrayList; - import org.hsqldb.lib.InputStreamInterface; - import org.hsqldb.lib.InputStreamWrapper; -@@ -183,6 +184,26 @@ - this.database = database; - } + import java.text.SimpleDateFormat; + import java.util.Locale; + import java.util.concurrent.atomic.AtomicInteger; +@@ -137,6 +138,7 @@ + private Crypto crypto; + boolean cryptLobs; + public FileAccess fileAccess; ++ private boolean isStoredFileAccess; + String tempDirectoryPath; -+ private static FileAccess getFileAccessCustom(Database database) { -+ String fileAccessClassName = database.urlProperties.getProperty("fileaccess_class_name"); -+ if (fileAccessClassName != null && !fileAccessClassName.isEmpty()) { -+ try { -+ Class<?> fileAccessClass = Class.forName(fileAccessClassName); -+ String storageKey = database.urlProperties.getProperty("storage_key"); -+ if (storageKey != null) { -+ Constructor<?> ctor = fileAccessClass.getConstructor(Object.class); -+ return (FileAccess) ctor.newInstance(storageKey); -+ } else { -+ Constructor<?> ctor = fileAccessClass.getConstructor(); -+ return (FileAccess) ctor.newInstance(); -+ } -+ } catch (Exception e) { -+ return FileUtil.getFileAccess(database.isFilesInJar()); -+ } -+ } -+ return FileUtil.getFileAccess(database.isFilesInJar()); -+ } -+ - /** - * Opens the specified Database object's database files and starts up - * the logging process. <p> -@@ -198,7 +219,7 @@ + // +@@ -198,7 +200,12 @@ boolean hasFileProps = false; boolean hasScript = false; - fileAccess = FileUtil.getFileAccess(database.isFilesInJar()); -+ fileAccess = getFileAccessCustom(database); ++ fileAccess = newStoredFileAccess(); ++ ++ if (fileAccess == null) { ++ fileAccess = FileUtil.getFileAccess(database.isFilesInJar()); ++ } ++ propIsFileDatabase = database.getType().isFileBased(); database.databaseProperties = new HsqlDatabaseProperties(database); propTextAllowFullPath = database.databaseProperties.isPropertyTrue( ---- misc/build/hsqldb-2.5.0/hsqldb/src/org/hsqldb/persist/RAFile.java 2019-04-27 08:55:26.000000000 -0500 -+++ misc/build/hsqldb-2.5.0/hsqldb/src/org/hsqldb/persist/RAFile.java 2026-11-06 10:22:37.174321115 -0500 -@@ -43,6 +43,8 @@ - import org.hsqldb.lib.ArrayUtil; - import org.hsqldb.lib.HsqlByteArrayInputStream; - import org.hsqldb.lib.HsqlByteArrayOutputStream; -+import org.hsqldb.lib.Storage; -+import java.lang.reflect.Constructor; +@@ -283,6 +290,12 @@ + database.databaseProperties.setProperty( + HsqlDatabaseProperties.hsqldb_lock_file, false); + } ++ } ++ ++ // the shadow file used for incremental backup is not reachable through FileAccess ++ if (isStoredFileAccess) { ++ database.databaseProperties.setProperty( ++ HsqlDatabaseProperties.hsqldb_inc_backup, false); + } - /** - * This class is a wrapper for a random access file such as that used for -@@ -93,6 +95,20 @@ - String name, boolean readonly, - int type) throws FileNotFoundException, IOException { + setVariables(); +@@ -391,6 +404,10 @@ + if (database.getType() == DatabaseType.DB_MEM) { + tempDirectoryPath = database.getProperties().getStringProperty( + HsqlDatabaseProperties.hsqldb_temp_directory); ++ } else if (isStoredFileAccess) { ++ ++ // no temporary directory next to the document ++ tempDirectoryPath = null; + } else { + tempDirectoryPath = database.getPath() + ".tmp"; + } +@@ -1102,7 +1119,7 @@ -+ String storageClassName = database.urlProperties.getProperty("storage_class_name"); -+ if (storageClassName != null && !storageClassName.isEmpty()) { -+ try { -+ Class<?> storageClass = Class.forName(storageClassName); -+ String storageKey = database.urlProperties.getProperty("storage_key"); -+ Constructor<?> ctor = storageClass.getConstructor( -+ String.class, Boolean.class, Object.class); -+ Storage storage = (Storage) ctor.newInstance( -+ name, Boolean.valueOf(readonly), storageKey); -+ return new RAStorageWrapper(storage); -+ } catch (Exception e) { -+ // fall through to default implementation -+ } -+ } - if (type == DATA_FILE_JAR) { - return new RAFileInJar(name); - } else if (type == DATA_FILE_TEXT) { -@@ -424,6 +440,81 @@ + public synchronized void setIncrementBackup(boolean val) { + +- if (val == propIncrementBackup) { ++ if (val == propIncrementBackup || isStoredFileAccess) { + return; } - } +@@ -1313,8 +1330,67 @@ + + public FileAccess getFileAccess() { + return fileAccess; ++ } ++ + /** -+ * Wrapper that adapts org.hsqldb.lib.Storage to -+ * org.hsqldb.persist.RandomAccessInterface. -+ * Used for OpenOffice.org storage integration. ++ * True if the database files are kept in a storage supplied by the host ++ * application instead of the file system. + */ -+ static class RAStorageWrapper implements RandomAccessInterface { -+ private final Storage storage; ++ public boolean isStoredFileAccess() { ++ return isStoredFileAccess; ++ } + -+ RAStorageWrapper(Storage storage) { -+ this.storage = storage; -+ } ++ // [email protected] - the database files are accessed through a class ++ // named in the connection properties, with a key identifying the storage ++ static final String url_fileaccess_class_name = "fileaccess_class_name"; ++ static final String url_storage_class_name = "storage_class_name"; ++ static final String url_storage_key = "storage_key"; + -+ public long length() throws IOException { -+ return storage.length(); -+ } ++ static Class<?> loadStorageClass(String name) throws ClassNotFoundException { + -+ public void seek(long position) throws IOException { -+ storage.seek(position); -+ } ++ ClassLoader loader = Thread.currentThread().getContextClassLoader(); + -+ public long getFilePointer() throws IOException { -+ return storage.getFilePointer(); ++ if (loader != null) { ++ try { ++ return loader.loadClass(name); ++ } catch (ClassNotFoundException e) {} + } + -+ public int read() throws IOException { -+ return storage.read(); -+ } ++ return Class.forName(name); + } + ++ private FileAccess newStoredFileAccess() { + -+ public void read(byte[] b, int offset, int length) throws IOException { -+ storage.read(b, offset, length); -+ } ++ String className = ++ database.urlProperties.getProperty(url_fileaccess_class_name); + -+ public void write(byte[] b, int offset, int length) throws IOException { -+ storage.write(b, offset, length); ++ if (className == null) { ++ return null; + } + -+ public int readInt() throws IOException { -+ return storage.readInt(); -+ } ++ try { ++ Class<?> fileAccessClass = loadStorageClass(className); + -+ public void writeInt(int i) throws IOException { -+ storage.writeInt(i); -+ } ++ if (!FileAccess.class.isAssignableFrom(fileAccessClass)) { ++ throw new ClassCastException(className); ++ } + -+ public long readLong() throws IOException { -+ return storage.readLong(); -+ } ++ Constructor<?> constructor = ++ fileAccessClass.getConstructor(Object.class); ++ FileAccess access = (FileAccess) constructor.newInstance( ++ database.urlProperties.getProperty(url_storage_key)); + -+ public void writeLong(long i) throws IOException { -+ storage.writeLong(i); -+ } ++ isStoredFileAccess = true; + -+ public void close() throws IOException { -+ storage.close(); ++ return access; ++ } catch (Throwable t) { ++ throw Error.error(t, ErrorCode.FILE_IO_ERROR, ++ ErrorCode.M_Message_Pair, new Object[] { ++ t.toString(), className ++ }); + } ++ } + -+ public boolean isReadOnly() { -+ return storage.isReadOnly(); -+ } + public boolean isFileDatabase() { + return propIsFileDatabase; + } +--- misc/hsqldb/hsqldb-2.5.0/hsqldb/src/org/hsqldb/persist/RAStorageWrapper.java ++++ misc/build/hsqldb-2.5.0/hsqldb/src/org/hsqldb/persist/RAStorageWrapper.java +@@ -32,7 +32,9 @@ + package org.hsqldb.persist; + + import java.io.IOException; ++import java.lang.reflect.Constructor; + ++import org.hsqldb.Database; + import org.hsqldb.lib.Storage; + + /** +@@ -52,6 +54,36 @@ + this.file = virtualFile; + } + ++ /** ++ * Opens a file in the storage supplied by the host application, using the ++ * class and key given in the connection properties. ++ */ ++ static RAStorageWrapper newInstance(Database database, String name, ++ boolean readonly) throws IOException { + -+ public void synch() { -+ // Storage interface does not have synch, delegate to no-op -+ } ++ String className = ++ database.urlProperties.getProperty(Logger.url_storage_class_name); ++ ++ try { ++ Class<?> storageClass = Logger.loadStorageClass(className); ++ ++ if (!Storage.class.isAssignableFrom(storageClass)) { ++ throw new ClassCastException(className); ++ } ++ ++ Constructor<?> constructor = ++ storageClass.getConstructor(String.class, Boolean.class, ++ Object.class); ++ Storage storage = (Storage) constructor.newInstance(name, ++ Boolean.valueOf(readonly), ++ database.urlProperties.getProperty(Logger.url_storage_key)); + -+ public boolean ensureLength(long newLength) { -+ // Storage interface does not have ensureLength -+ return true; ++ return new RAStorageWrapper(storage); ++ } catch (Exception e) { ++ throw new IOException("invalid storage class: " + e, e); + } ++ } + -+ public boolean setLength(long newLength) { -+ // Storage interface does not have setLength -+ return false; + public long length() throws IOException { + return file.length(); + } +--- misc/hsqldb/hsqldb-2.5.0/hsqldb/src/org/hsqldb/persist/RAFile.java ++++ misc/build/hsqldb-2.5.0/hsqldb/src/org/hsqldb/persist/RAFile.java +@@ -92,6 +92,10 @@ + static RandomAccessInterface newScaledRAFile(Database database, + String name, boolean readonly, + int type) throws FileNotFoundException, IOException { ++ ++ if (type == DATA_FILE_STORED) { ++ return RAStorageWrapper.newInstance(database, name, readonly); ++ } + + if (type == DATA_FILE_JAR) { + return new RAFileInJar(name); +--- misc/hsqldb/hsqldb-2.5.0/hsqldb/src/org/hsqldb/persist/DataFileCache.java ++++ misc/build/hsqldb-2.5.0/hsqldb/src/org/hsqldb/persist/DataFileCache.java +@@ -156,7 +156,7 @@ + cache = new Cache(this); + + try { +- dataFile = new RAFileSimple(database.logger, dataFileName, "rw"); ++ dataFile = newSimpleFile(false); + } catch (Throwable t) { + throw Error.error(ErrorCode.FILE_IO_ERROR, t); + } +@@ -224,6 +224,8 @@ + + if (database.isFilesInJar()) { + fileType = RAFile.DATA_FILE_JAR; ++ } else if (database.logger.isStoredFileAccess()) { ++ fileType = RAFile.DATA_FILE_STORED; + } else if (isNio) { + fileType = RAFile.DATA_FILE_NIO; + } else { +@@ -263,8 +265,7 @@ + boolean isSaved = false; + + if (preexists) { +- dataFile = new RAFileSimple(database.logger, dataFileName, +- "r"); ++ dataFile = newSimpleFile(true); + + long length = dataFile.length(); + boolean wrongVersion = false; +@@ -389,7 +390,20 @@ + ErrorCode.M_DataFileCache_open, new Object[] { + t.toString(), dataFileName + }); + } + } + - private void resetPointer() { ++ private RandomAccessInterface newSimpleFile(boolean readonly) ++ throws IOException { ++ ++ if (database.logger.isStoredFileAccess()) { ++ return RAStorageWrapper.newInstance(database, dataFileName, ++ readonly); + } ++ ++ return new RAFileSimple(database.logger, dataFileName, ++ readonly ? "r" ++ : "rw"); + } + + boolean setDataSpaceManager() { +@@ -1368,7 +1382,29 @@ + * @throws HsqlException + */ + void backupDataFile(boolean newFile) { +- backupFile(database, dataFileName, backupFileName, newFile); ++ ++ if (!database.logger.isStoredFileAccess() || dataFile == null) { ++ backupFile(database, dataFileName, backupFileName, newFile); ++ ++ return; ++ } ++ ++ // the storage keeps one handle per file: reading the data file for ++ // the archive would close the handle held by this cache ++ try { ++ dataFile.close(); ++ backupFile(database, dataFileName, backupFileName, newFile); ++ } catch (IOException e) { ++ throw Error.error(ErrorCode.DATA_FILE_ERROR, e); ++ } finally { ++ try { ++ dataFile = RAFile.newScaledRAFile(database, dataFileName, ++ false, ++ RAFile.DATA_FILE_STORED); ++ } catch (IOException e) { ++ throw Error.error(ErrorCode.DATA_FILE_ERROR, e); ++ } ++ } + } + void backupNewDataFile(boolean newFile) { +--- misc/hsqldb/hsqldb-2.5.0/hsqldb/src/org/hsqldb/persist/LobStoreRAFile.java ++++ misc/build/hsqldb-2.5.0/hsqldb/src/org/hsqldb/persist/LobStoreRAFile.java +@@ -70,8 +70,14 @@ try { + boolean readonly = database.isFilesReadOnly(); + +- file = new RAFileSimple(database.logger, fileName, readonly ? "r" +- : "rws"); ++ if (database.logger.isStoredFileAccess()) { ++ file = RAStorageWrapper.newInstance(database, fileName, ++ readonly); ++ } else { ++ file = new RAFileSimple(database.logger, fileName, ++ readonly ? "r" ++ : "rws"); ++ } + } catch (Throwable t) { + throw Error.error(ErrorCode.DATA_FILE_ERROR, t); + }
