All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jooq.meta.extensions.ddl.DDLDatabase Maven / Gradle / Ivy

There is a newer version: 3.19.15
Show newest version
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Other licenses:
 * -----------------------------------------------------------------------------
 * Commercial licenses for this work are available. These replace the above
 * Apache-2.0 license and offer limited warranties, support, maintenance, and
 * commercial database integrations.
 *
 * For more information, please visit: https://www.jooq.org/legal/licensing
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package org.jooq.meta.extensions.ddl;

// ...
import static org.jooq.conf.SettingsTools.renderLocale;
import static org.jooq.impl.DSL.name;
import static org.jooq.tools.StringUtils.isBlank;

import java.io.File;
import java.io.Reader;
import java.sql.SQLException;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jooq.DSLContext;
import org.jooq.FilePattern;
import org.jooq.FilePattern.Sort;
import org.jooq.Name;
import org.jooq.Name.Quoted;
import org.jooq.Queries;
import org.jooq.Query;
import org.jooq.ResultQuery;
import org.jooq.Source;
import org.jooq.VisitContext;
import org.jooq.VisitListener;
import org.jooq.conf.ParseUnknownFunctions;
import org.jooq.conf.Settings;
import org.jooq.exception.DataAccessException;
import org.jooq.impl.DSL;
import org.jooq.impl.ParserException;
import org.jooq.meta.extensions.AbstractInterpretingDatabase;
import org.jooq.tools.JooqLogger;
import org.jooq.tools.jdbc.JDBCUtils;

import org.h2.api.ErrorCode;

/**
 * The DDL database.
 * 

* This meta data source parses a set of SQL scripts, translates them to the H2 * dialect and runs them on an in-memory H2 database before reverse engineering * the outcome. *

* The SQL scripts are located in the scripts scripts property * available from {@link #getProperties()}. * * @author Lukas Eder */ public class DDLDatabase extends AbstractInterpretingDatabase { private static final JooqLogger log = JooqLogger.getLogger(DDLDatabase.class); private static final Pattern P_NAME = Pattern.compile("(?s:.*?\"([^\"]*)\".*)"); private boolean logExecutedQueries = true; private boolean logExecutionResults = true; @Override protected void export() throws Exception { Settings defaultSettings = new Settings(); String scripts = getProperties().getProperty("scripts"); String sql = getProperties().getProperty("sql"); String encoding = getProperties().getProperty("encoding", "UTF-8"); String sort = getProperties().getProperty("sort", "semantic").toLowerCase(); final String defaultNameCase = getProperties().getProperty("defaultNameCase", "as_is").toUpperCase(); boolean parseIgnoreComments = !"false".equalsIgnoreCase(getProperties().getProperty("parseIgnoreComments")); String parseIgnoreCommentStart = getProperties().getProperty("parseIgnoreCommentStart", defaultSettings.getParseIgnoreCommentStart()); String parseIgnoreCommentStop = getProperties().getProperty("parseIgnoreCommentStop", defaultSettings.getParseIgnoreCommentStop()); logExecutedQueries = !"false".equalsIgnoreCase(getProperties().getProperty("logExecutedQueries")); logExecutionResults = !"false".equalsIgnoreCase(getProperties().getProperty("logExecutionResults")); if (isBlank(scripts) && isBlank(sql)) { scripts = ""; sql = ""; log.warn("No scripts defined", "It is recommended that you provide an explicit script directory to scan"); } try { final DSLContext ctx = DSL.using(connection(), new Settings() .withParseIgnoreComments(parseIgnoreComments) .withParseIgnoreCommentStart(parseIgnoreCommentStart) .withParseIgnoreCommentStop(parseIgnoreCommentStop) .withParseUnknownFunctions(ParseUnknownFunctions.IGNORE) ); // [#7771] [#8011] Ignore all parsed storage clauses when executing the statements ctx.data("org.jooq.ddl.ignore-storage-clauses", true); // [#8910] Parse things a bit differently for use with the DDLDatabase ctx.data("org.jooq.ddl.parse-for-ddldatabase", true); if (!"AS_IS".equals(defaultNameCase)) { ctx.configuration().set(new VisitListener() { @Override public void visitStart(VisitContext vc) { if (vc.queryPart() instanceof Name n) { Name[] parts = n.parts(); boolean changed = false; for (int i = 0; i < parts.length; i++) { // [#9931] [#12752] This explicitly excludes the new Quoted.SYSTEM // flag for DSL.systemName() names if (parts[i].quoted() == Quoted.UNQUOTED) { parts[i] = DSL.quotedName( "UPPER".equals(defaultNameCase) ? parts[i].first().toUpperCase(renderLocale(ctx.settings())) : parts[i].first().toLowerCase(renderLocale(ctx.settings())) ); changed = true; } } if (changed) vc.queryPart(DSL.name(parts)); } } }); } if (!isBlank(sql)) load(ctx, Source.of(sql)); if (!isBlank(scripts)) new FilePattern() .encoding(encoding) .basedir(new File(getBasedir())) .pattern(scripts) .sort(Sort.of(sort)) .load(source -> DDLDatabase.this.load(ctx, source)); } catch (ParserException e) { log.error("An exception occurred while parsing script source : " + scripts + ". Please report this error to https://jooq.org/bug", e); throw e; } } private void load(DSLContext ctx, Source source) { Reader r = null; try { Scanner s = new Scanner(r = source.reader()).useDelimiter("\\A"); Queries queries = ctx.parser().parse(s.hasNext() ? s.next() : ""); for (Query query : queries) { repeat: for (;;) { try { if (logExecutedQueries) log.info(query); if (logExecutedQueries && logExecutionResults) if (query instanceof ResultQuery) log.info("\n" + ((ResultQuery) query).fetch()); else log.info("Update count: " + query.execute()); // [#10008] Execute all queries. Could have FOR UPDATE or other side effects else query.execute(); break repeat; } catch (DataAccessException e) { // [#7039] Auto create missing schemas. We're using the if (Integer.toString(ErrorCode.SCHEMA_NOT_FOUND_1).equals(e.sqlState())) { SQLException cause = e.getCause(SQLException.class); if (cause != null) { Matcher m = P_NAME.matcher(cause.getMessage()); if (m.find()) { Query createSchema = ctx.createSchemaIfNotExists(name(m.group(1))); createSchema.execute(); log.info(createSchema); continue repeat; } } } throw e; } } } } catch (DataAccessException e) { // [#9138] Make users aware of the new parse ignore comment syntax log.error("DDLDatabase Error", "Your SQL string could not be parsed or interpreted. This may have a variety of reasons, including:\n" + "- The jOOQ parser doesn't understand your SQL\n" + "- The jOOQ DDL simulation logic (translating to H2) cannot simulate your SQL\n" + "\n" + "If you think this is a bug or a feature worth requesting, please report it here: https://jooq.org/bug\n" + "\n" + "As a workaround, you can use the Settings.parseIgnoreComments syntax documented here:\n" + "https://www.jooq.org/doc/latest/manual/sql-building/dsl-context/custom-settings/settings-parser/"); throw e; } finally { JDBCUtils.safeClose(r); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy