Skip to content

HHH-19098 Disable implicit loading of the default import script #9693

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,14 @@ public interface SchemaToolingSettings {
@Deprecated
String HBM2DDL_IMPORT_FILES = "hibernate.hbm2ddl.import_files";

/**
* Specifies if the default {@code /import.sql} script file should not be executed
* when {@link #HBM2DDL_IMPORT_FILES} is not specified and {@value #HBM2DDL_AUTO} is set to {@code create} or {@code create-drop}.
*
* The default value is {@code false}.
*/
String HBM2DDL_SKIP_DEFAULT_IMPORT_FILE = "hibernate.hbm2ddl.skip_default_import_file";

/**
* Specifies whether to automatically create also the database schema/catalog.
* The default is false.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@
import static org.hibernate.cfg.AvailableSettings.HBM2DDL_IMPORT_FILES;
import static org.hibernate.cfg.AvailableSettings.HBM2DDL_LOAD_SCRIPT_SOURCE;
import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_LOAD_SCRIPT_SOURCE;
import static org.hibernate.cfg.SchemaToolingSettings.HBM2DDL_SKIP_DEFAULT_IMPORT_FILE;
import static org.hibernate.internal.util.collections.CollectionHelper.setOfSize;
import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean;
import static org.hibernate.internal.util.config.ConfigurationHelper.getString;
import static org.hibernate.tool.schema.internal.Helper.applyScript;
import static org.hibernate.tool.schema.internal.Helper.applySqlStrings;
Expand Down Expand Up @@ -597,11 +599,24 @@ private void applyImportSources(
commandExtractor,
dialect,
formatter,
hasDefaultImportFileScriptBeenExecuted ? "" : DEFAULT_IMPORT_FILE,
hasDefaultImportFileScriptBeenExecuted ? "" : getDefaultImportFile( options ),
targets
);
}

private String getDefaultImportFile(ExecutionOptions options) {
if ( skipDefaultFileImport( options ) ) {
return "";
}
else {
return DEFAULT_IMPORT_FILE;
}
}

private static boolean skipDefaultFileImport(ExecutionOptions options) {
return getBoolean( HBM2DDL_SKIP_DEFAULT_IMPORT_FILE, options.getConfigurationValues(), false );
}

/**
* In principle, we should format the commands in the import script if the
* {@code format} parameter is {@code true}, and since it's supposed to be
Expand Down Expand Up @@ -642,16 +657,19 @@ private boolean applyImportScript(
formatter,
targets
);
return containsDefaultImportFile( importScriptInput );
return containsDefaultImportFile( importScriptInput, options );
}
else {
return false;
}
}

private boolean containsDefaultImportFile(ScriptSourceInput importScriptInput) {
private boolean containsDefaultImportFile(ScriptSourceInput importScriptInput,ExecutionOptions options ) {
if ( skipDefaultFileImport( options ) ) {
return false;
}
final URL defaultImportFileUrl = getClassLoaderService().locateResource( DEFAULT_IMPORT_FILE );
return defaultImportFileUrl != null && importScriptInput.containsScript(defaultImportFileUrl);
return defaultImportFileUrl != null && importScriptInput.containsScript( defaultImportFileUrl );
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.tool.schema;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.bytecode.enhance.spi.Enhancer;
import org.hibernate.testing.util.ServiceRegistryUtil;
import org.hibernate.tool.schema.SourceType;
import org.hibernate.tool.schema.TargetType;
import org.hibernate.tool.schema.internal.SchemaCreatorImpl;
import org.hibernate.tool.schema.spi.CommandAcceptanceException;
import org.hibernate.tool.schema.spi.ContributableMatcher;
import org.hibernate.tool.schema.spi.ExceptionHandler;
import org.hibernate.tool.schema.spi.ExecutionOptions;
import org.hibernate.tool.schema.spi.ScriptSourceInput;
import org.hibernate.tool.schema.spi.ScriptTargetOutput;
import org.hibernate.tool.schema.spi.SourceDescriptor;
import org.hibernate.tool.schema.spi.TargetDescriptor;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.hibernate.cfg.SchemaToolingSettings.HBM2DDL_SKIP_DEFAULT_IMPORT_FILE;
import static org.hibernate.tool.schema.internal.SchemaCreatorImpl.DEFAULT_IMPORT_FILE;

public class SkipDefaultImportFileExecutionTest {
private File defaultImportFile;
private StandardServiceRegistry serviceRegistry;
private static final String COMMAND = "INSERT INTO TEST_ENTITY (id, name) values (1,'name')";


@BeforeEach
public void setUp() throws Exception {
defaultImportFile = createDefaultImportFile( "import.sql" );
serviceRegistry = ServiceRegistryUtil.serviceRegistryBuilder(
new BootstrapServiceRegistryBuilder().applyClassLoader(
toClassLoader( defaultImportFile.getParentFile() ) ).build()
).build();
}

@AfterEach
public void tearDown() {
serviceRegistry.close();
if ( defaultImportFile.exists() ) {
try {
Files.delete( defaultImportFile.toPath() );
}
catch (IOException e) {
throw new RuntimeException( e );
}
}
}

@Test
public void testImportScriptIsNotExecutedWhitSkipDefaultImportFileSetting() {
assertThat( serviceRegistry.getService( ClassLoaderService.class )
.locateResource( DEFAULT_IMPORT_FILE ) ).isNotNull();

TargetDescriptorImpl targetDescriptor = TargetDescriptorImpl.INSTANCE;

createSchema( targetDescriptor );

TestScriptTargetOutput targetOutput = (TestScriptTargetOutput) targetDescriptor.getScriptTargetOutput();

assertNoInsertCommandsFromDeaultImportFileHasBeenExecuted( targetOutput );
}

private static void assertNoInsertCommandsFromDeaultImportFileHasBeenExecuted(TestScriptTargetOutput targetOutput) {
assertThat( targetOutput.getInsertCommands().size() ).isEqualTo( 0 )
.as( "Despite setting hibernate.hbm2ddl.skip_default_import_file to true the default import.sql file insert command hs been executed" );
}

private void createSchema(TargetDescriptorImpl targetDescriptor) {

final Metadata mappings = buildMappings( serviceRegistry );

final SchemaCreatorImpl schemaCreator = new SchemaCreatorImpl( serviceRegistry );

final Map<String, Object> options = new HashMap<>();
options.put( HBM2DDL_SKIP_DEFAULT_IMPORT_FILE, "true" );
schemaCreator.doCreation(
mappings,
new ExecutionOptionsTestImpl( options ),
ContributableMatcher.ALL,
SourceDescriptorImpl.INSTANCE,
targetDescriptor
);
}

private static File createDefaultImportFile(@SuppressWarnings("SameParameterValue") String fileName)
throws Exception {
final Path tmp = Files.createTempDirectory( "default_import" );
final File file = new File( tmp.toString() + File.separator + fileName );

try (final FileWriter myWriter = new FileWriter( file )) {
myWriter.write( COMMAND );
}

return file;
}

private static ClassLoader toClassLoader(File classesDir) {
final URI classesDirUri = classesDir.toURI();
try {
final URL url = classesDirUri.toURL();
return new URLClassLoader( new URL[] {url}, Enhancer.class.getClassLoader() );
}
catch (MalformedURLException e) {
throw new RuntimeException( "Unable to resolve classpath entry to URL : " + classesDir.getAbsolutePath(),
e );
}
}

private Metadata buildMappings(StandardServiceRegistry registry) {
return new MetadataSources( registry )
.addAnnotatedClass( TestEntity.class )
.buildMetadata();
}

@Entity(name = "TestEntity")
@Table(name = "TEST_ENTITY")
public static class TestEntity {
@Id
private long id;

private String name;
}

public class ExecutionOptionsTestImpl implements ExecutionOptions, ExceptionHandler {

Check notice

Code scanning / CodeQL

Inner class could be static Note test

ExecutionOptionsTestImpl should be made static, since the enclosing instance is not used.
Map<String, Object> configValues;

public ExecutionOptionsTestImpl(Map<String, Object> configValues) {
this.configValues = configValues;
}

@Override
public Map<String, Object> getConfigurationValues() {
return configValues;
}

@Override
public boolean shouldManageNamespaces() {
return true;
}

@Override
public ExceptionHandler getExceptionHandler() {
return this;
}

@Override
public void handleException(CommandAcceptanceException exception) {
throw exception;
}
}

private static class SourceDescriptorImpl implements SourceDescriptor {
/**
* Singleton access
*/
public static final SourceDescriptorImpl INSTANCE = new SourceDescriptorImpl();

@Override
public SourceType getSourceType() {
return SourceType.METADATA;
}

@Override
public ScriptSourceInput getScriptSourceInput() {
return null;
}
}

private static class TargetDescriptorImpl implements TargetDescriptor {
/**
* Singleton access
*/
public static final TargetDescriptorImpl INSTANCE = new TargetDescriptorImpl();

@Override
public EnumSet<TargetType> getTargetTypes() {
return EnumSet.of( TargetType.SCRIPT );
}

@Override
public ScriptTargetOutput getScriptTargetOutput() {
return TestScriptTargetOutput.INSTANCE;
}
}

private static class TestScriptTargetOutput implements ScriptTargetOutput {

public static final TestScriptTargetOutput INSTANCE = new TestScriptTargetOutput();

List<String> insertCommands = new ArrayList<>();

@Override
public void prepare() {

}

@Override
public void accept(String command) {
if ( command.toLowerCase().startsWith( "insert" ) ) {
insertCommands.add( command );
}
}

@Override
public void release() {

}

public List<String> getInsertCommands() {
return insertCommands;
}
}
}
Loading