[jOOQ/jOOQ#8528] Initial work on DDL simulation
Adds a new `DDLInterpreterMetaProvider` (might eventually replace `DDLMetaProvider`) which interprets DDL scripts by first parsing them using jOOQ's `Parser`. To extract information the implementation classes like `CreateTableImpl` have new package private accessors. To avoid name conflicts with statically imported methods (e.g. `DSL#table()`), the accessor methods have a `$`-prefix. This is just a temporary solution until the object query model API is implemented, at which point these methods will be deleted again. The `DDLInterpreter` for now just implements very basic `CREATE TABLE`, `ALTER TABLE`, and `DROP TABLE` support. The implementation will be completed incrementally.
This commit is contained in:
parent
e37d77087a
commit
7c98a5a5d8
@ -139,7 +139,12 @@ abstract class AbstractMeta implements Meta, Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract List<Schema> getSchemas0() throws DataAccessException;
|
||||
protected List<Schema> getSchemas0() throws DataAccessException {
|
||||
List<Schema> result = new ArrayList<>();
|
||||
for (Catalog catalog : getCatalogs())
|
||||
result.addAll(catalog.getSchemas());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List<Table<?>> getTables(String name) {
|
||||
@ -176,7 +181,12 @@ abstract class AbstractMeta implements Meta, Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract List<Table<?>> getTables0() throws DataAccessException;
|
||||
protected List<Table<?>> getTables0() throws DataAccessException {
|
||||
List<Table<?>> result = new ArrayList<>();
|
||||
for (Schema schema : getSchemas())
|
||||
result.addAll(schema.getTables());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List<Sequence<?>> getSequences(String name) {
|
||||
@ -213,7 +223,12 @@ abstract class AbstractMeta implements Meta, Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract List<Sequence<?>> getSequences0() throws DataAccessException;
|
||||
protected List<Sequence<?>> getSequences0() throws DataAccessException {
|
||||
List<Sequence<?>> result = new ArrayList<>();
|
||||
for (Schema schema : getSchemas())
|
||||
result.addAll(schema.getSequences());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List<UniqueKey<?>> getPrimaryKeys() throws DataAccessException {
|
||||
@ -226,7 +241,13 @@ abstract class AbstractMeta implements Meta, Serializable {
|
||||
cachedPrimaryKeys = new ArrayList<>(getPrimaryKeys0());
|
||||
}
|
||||
|
||||
protected abstract List<UniqueKey<?>> getPrimaryKeys0() throws DataAccessException;
|
||||
protected List<UniqueKey<?>> getPrimaryKeys0() throws DataAccessException {
|
||||
List<UniqueKey<?>> result = new ArrayList<>();
|
||||
for (Table<?> table : getTables())
|
||||
if (table.getPrimaryKey() != null)
|
||||
result.add(table.getPrimaryKey());
|
||||
return result;
|
||||
}
|
||||
|
||||
private final <T extends Named> List<T> get(Name name, Iterable<T> i, Map<Name, T> qualified, Map<Name, List<T>> unqualified) {
|
||||
if (qualified.isEmpty()) {
|
||||
|
||||
@ -261,6 +261,10 @@ final class AlterTableImpl extends AbstractRowCountQuery implements
|
||||
this.ifExists = ifExists;
|
||||
}
|
||||
|
||||
final Table<?> $table() { return table; }
|
||||
final Field<?> $addColumn() { return addColumn; }
|
||||
final DataType<?> $addColumnType() { return addColumnType; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// XXX: DSL API
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@ -168,6 +168,8 @@ implements
|
||||
super(name, null);
|
||||
}
|
||||
|
||||
final Field<?>[] $primaryKey() { return primaryKey; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// XXX: QueryPart API
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@ -182,6 +182,12 @@ final class CreateTableImpl extends AbstractRowCountQuery implements
|
||||
this.indexes = new ArrayList<>();
|
||||
}
|
||||
|
||||
final Table<?> $table() { return table; }
|
||||
final Select<?> $select() { return select; }
|
||||
final List<Field<?>> $columnFields() { return columnFields; }
|
||||
final List<DataType<?>> $columnTypes() { return columnTypes; }
|
||||
final List<Constraint> $constraints() { return constraints; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// XXX: DSL API
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
273
jOOQ/src/main/java/org/jooq/impl/DDLInterpreter.java
Normal file
273
jOOQ/src/main/java/org/jooq/impl/DDLInterpreter.java
Normal file
@ -0,0 +1,273 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* http://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
|
||||
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
|
||||
* database integrations.
|
||||
*
|
||||
* For more information, please visit: http://www.jooq.org/licenses
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
import static org.jooq.impl.DSL.unquotedName;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jooq.Catalog;
|
||||
import org.jooq.Constraint;
|
||||
import org.jooq.DataType;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.Meta;
|
||||
import org.jooq.Name;
|
||||
import org.jooq.Name.Quoted;
|
||||
import org.jooq.Query;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.Schema;
|
||||
import org.jooq.Table;
|
||||
import org.jooq.TableField;
|
||||
import org.jooq.UniqueKey;
|
||||
import org.jooq.exception.DataAccessException;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
final class DDLInterpreter {
|
||||
|
||||
private final Map<Name, MutableCatalog> catalogs = new LinkedHashMap<>();
|
||||
|
||||
private final MutableCatalog defaultCatalog;
|
||||
private final MutableSchema defaultSchema;
|
||||
private MutableSchema currentSchema;
|
||||
|
||||
DDLInterpreter() {
|
||||
defaultCatalog = new MutableCatalog(null);
|
||||
catalogs.put(defaultCatalog.getUnqualifiedName(), defaultCatalog);
|
||||
defaultSchema = new MutableSchema(null, defaultCatalog);
|
||||
currentSchema = defaultSchema;
|
||||
}
|
||||
|
||||
Meta meta() {
|
||||
return new AbstractMeta() {
|
||||
@Override
|
||||
protected List<Catalog> getCatalogs0() throws DataAccessException {
|
||||
return new ArrayList<>(catalogs.values());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
final void accept(Query query) {
|
||||
if (query instanceof CreateTableImpl)
|
||||
accept0((CreateTableImpl) query);
|
||||
else if (query instanceof AlterTableImpl)
|
||||
accept0((AlterTableImpl) query);
|
||||
else if (query instanceof DropTableImpl)
|
||||
accept0((DropTableImpl) query);
|
||||
else
|
||||
throw new UnsupportedOperationException(query.getSQL());
|
||||
}
|
||||
|
||||
private final void accept0(CreateTableImpl query) {
|
||||
Table<?> table = query.$table();
|
||||
MutableSchema schema = getSchema(table.getSchema(), true);
|
||||
|
||||
// TODO ifNotExists
|
||||
MutableTable t = new MutableTable(table.getUnqualifiedName(), schema);
|
||||
List<Field<?>> columns = query.$columnFields();
|
||||
if (!columns.isEmpty())
|
||||
for (int i = 0; i < columns.size(); i++) {
|
||||
Field<?> column = columns.get(i);
|
||||
t.addColumn(column.getUnqualifiedName(), query.$columnTypes().get(i));
|
||||
}
|
||||
else if (query.$select() != null)
|
||||
for (Field<?> column : query.$select().fields())
|
||||
t.addColumn(column.getUnqualifiedName(), column.getDataType());
|
||||
|
||||
for (Constraint constraint : query.$constraints())
|
||||
if (constraint instanceof ConstraintImpl) {
|
||||
ConstraintImpl impl = (ConstraintImpl) constraint;
|
||||
t.primaryKey(impl.$primaryKey());
|
||||
}
|
||||
else
|
||||
// XXX log warning?
|
||||
;
|
||||
}
|
||||
|
||||
private final void accept0(AlterTableImpl query) {
|
||||
Table<?> table = query.$table();
|
||||
MutableSchema schema = getSchema(table.getSchema(), false);
|
||||
|
||||
Field<?> addColumn = query.$addColumn();
|
||||
if (addColumn != null) {
|
||||
MutableTable existing = schema.getTable(table.getUnqualifiedName());
|
||||
existing.addColumn(addColumn.getUnqualifiedName(), query.$addColumnType());
|
||||
}
|
||||
}
|
||||
|
||||
private final void accept0(DropTableImpl query) {
|
||||
Table<?> table = query.$table();
|
||||
MutableSchema schema = getSchema(table.getSchema(), false);
|
||||
|
||||
// TODO schema == null
|
||||
MutableTable oldTable = schema.dropTable(table.getUnqualifiedName());
|
||||
// TODO oldTable == null
|
||||
}
|
||||
|
||||
private final MutableSchema getSchema(Schema input, boolean create) {
|
||||
if (input == null)
|
||||
return currentSchema;
|
||||
|
||||
MutableCatalog catalog = defaultCatalog;
|
||||
if (input.getCatalog() != null) {
|
||||
Name catalogName = input.getCatalog().getUnqualifiedName();
|
||||
if ((catalog = catalogs.get(catalogName)) == null && create)
|
||||
catalogs.put(catalogName, catalog = new MutableCatalog(catalogName));
|
||||
}
|
||||
|
||||
if (catalog == null)
|
||||
return null;
|
||||
|
||||
MutableSchema schema = defaultSchema;
|
||||
Name schemaName = input.getUnqualifiedName();
|
||||
if ((schema = catalog.getSchema(schemaName)) == null && create)
|
||||
// TODO createSchemaIfNotExists should probably be configurable
|
||||
schema = new MutableSchema(schemaName, catalog);
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
private static Name normalize(Name name) {
|
||||
if (name == null)
|
||||
return null;
|
||||
if (name instanceof UnqualifiedName) {
|
||||
if (name.quoted() == Quoted.QUOTED)
|
||||
return name;
|
||||
String lowerCase = name.first().toLowerCase();
|
||||
return name.first() == lowerCase ? name : unquotedName(lowerCase);
|
||||
}
|
||||
|
||||
Name[] parts = name.parts();
|
||||
for (int i = 0; i < parts.length; i++)
|
||||
parts[i] = normalize(parts[i]);
|
||||
return DSL.name(parts);
|
||||
}
|
||||
|
||||
private static class MutableCatalog extends CatalogImpl {
|
||||
private List<MutableSchema> schemas = new ArrayList<>();
|
||||
|
||||
MutableCatalog(Name name) {
|
||||
super(normalize(name));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<Schema> getSchemas() {
|
||||
return Collections.unmodifiableList((List<Schema>) (List<?>) schemas);
|
||||
}
|
||||
|
||||
MutableSchema getSchema(Name name) {
|
||||
for (MutableSchema schema : schemas)
|
||||
if (schema.getUnqualifiedName().equals(name))
|
||||
return schema;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MutableSchema extends SchemaImpl {
|
||||
private final MutableCatalog catalog;
|
||||
private final List<MutableTable> tables = new ArrayList<>();
|
||||
|
||||
MutableSchema(Name name, MutableCatalog catalog) {
|
||||
super(normalize(name), null);
|
||||
this.catalog = catalog;
|
||||
catalog.schemas.add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Catalog getCatalog() {
|
||||
return catalog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Table<?>> getTables() {
|
||||
return Collections.unmodifiableList(tables);
|
||||
}
|
||||
|
||||
public MutableTable getTable(Name name) {
|
||||
for (MutableTable table : tables)
|
||||
if (table.getUnqualifiedName().equals(name))
|
||||
return table;
|
||||
return null;
|
||||
}
|
||||
|
||||
public MutableTable dropTable(Name name) {
|
||||
name = normalize(name);
|
||||
for (int i = 0; i < tables.size(); i++)
|
||||
if (tables.get(i).getUnqualifiedName().equals(name))
|
||||
return tables.remove(i);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MutableTable extends TableImpl<Record> {
|
||||
|
||||
private UniqueKey<Record> primaryKey;
|
||||
|
||||
public MutableTable(Name name, MutableSchema schema) {
|
||||
super(normalize(name), schema);
|
||||
schema.tables.add(this);
|
||||
}
|
||||
|
||||
public void addColumn(Name name, DataType<?> dataType) {
|
||||
createField(normalize(name), dataType);
|
||||
}
|
||||
|
||||
public void primaryKey(Field<?>[] primaryKeyFields) {
|
||||
if (primaryKeyFields != null)
|
||||
this.primaryKey = new UniqueKeyImpl(this, copiedFields(primaryKeyFields));
|
||||
}
|
||||
|
||||
private final TableField<?, ?>[] copiedFields(Field<?>[] input) {
|
||||
TableField<?, ?>[] result = new TableField[input.length];
|
||||
for (int i = 0; i < input.length; i++)
|
||||
result[i] = (TableField<?, ?>) field(normalize(input[i].getUnqualifiedName()));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UniqueKey<Record> getPrimaryKey() {
|
||||
return primaryKey;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
111
jOOQ/src/main/java/org/jooq/impl/DDLInterpreterMetaProvider.java
Normal file
111
jOOQ/src/main/java/org/jooq/impl/DDLInterpreterMetaProvider.java
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* http://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
|
||||
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
|
||||
* database integrations.
|
||||
*
|
||||
* For more information, please visit: http://www.jooq.org/licenses
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.util.Scanner;
|
||||
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.DSLContext;
|
||||
import org.jooq.Meta;
|
||||
import org.jooq.MetaProvider;
|
||||
import org.jooq.Queries;
|
||||
import org.jooq.Query;
|
||||
import org.jooq.Source;
|
||||
import org.jooq.tools.JooqLogger;
|
||||
|
||||
/**
|
||||
* {@link MetaProvider} implementation which can {@link MetaProvider#provide()
|
||||
* provide} a {@link Meta} implementation based on a set of DDL scripts as the
|
||||
* input.
|
||||
* <p>
|
||||
* In contrast to {@link DDLMetaProvider} this implementation interprets the DDL
|
||||
* scripts.
|
||||
*
|
||||
* @author Knut Wannheden
|
||||
*/
|
||||
final class DDLInterpreterMetaProvider implements MetaProvider {
|
||||
|
||||
private static final JooqLogger log = JooqLogger.getLogger(DDLInterpreterMetaProvider.class);
|
||||
|
||||
// FIXME this exception is obviously a hack we need to remove again...
|
||||
private final Configuration configuration;
|
||||
private final Source[] scripts;
|
||||
|
||||
public DDLInterpreterMetaProvider(Configuration configuration, Source... scripts) {
|
||||
this.configuration = configuration == null ? new DefaultConfiguration() : configuration;
|
||||
this.scripts = scripts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Meta provide() {
|
||||
final DDLInterpreter interpreter = new DDLInterpreter();
|
||||
Configuration localConfiguration = configuration.derive();
|
||||
DSLContext ctx = DSL.using(localConfiguration);
|
||||
for (Source script : scripts)
|
||||
loadScript(ctx, script, interpreter);
|
||||
|
||||
return interpreter.meta();
|
||||
}
|
||||
|
||||
private final void loadScript(DSLContext ctx, Source source, DDLInterpreter interpreter) {
|
||||
Reader reader = source.reader();
|
||||
try {
|
||||
Scanner s = new Scanner(reader).useDelimiter("\\A");
|
||||
Queries queries = ctx.parser().parse(s.hasNext() ? s.next() : "");
|
||||
|
||||
for (Query query : queries) {
|
||||
interpreter.accept(query);
|
||||
log.info(query);
|
||||
}
|
||||
}
|
||||
catch (ParserException e) {
|
||||
log.error("An exception occurred while parsing a DDL script: " + e.getMessage()
|
||||
+ ". Please report this error to https://github.com/jOOQ/jOOQ/issues/new", e);
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
if (reader != null)
|
||||
try {
|
||||
reader.close();
|
||||
}
|
||||
catch (Exception ignore) {}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -98,39 +98,6 @@ final class DetachedMeta extends AbstractMeta {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final List<Schema> getSchemas0() throws DataAccessException {
|
||||
List<Schema> result = new ArrayList<>();
|
||||
for (Catalog catalog : getCatalogs())
|
||||
result.addAll(catalog.getSchemas());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final List<Table<?>> getTables0() throws DataAccessException {
|
||||
List<Table<?>> result = new ArrayList<>();
|
||||
for (Schema schema : getSchemas())
|
||||
result.addAll(schema.getTables());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final List<Sequence<?>> getSequences0() throws DataAccessException {
|
||||
List<Sequence<?>> result = new ArrayList<>();
|
||||
for (Schema schema : getSchemas())
|
||||
result.addAll(schema.getSequences());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final List<UniqueKey<?>> getPrimaryKeys0() throws DataAccessException {
|
||||
List<UniqueKey<?>> result = new ArrayList<>();
|
||||
for (Table<?> table : getTables())
|
||||
if (table.getPrimaryKey() != null)
|
||||
result.add(table.getPrimaryKey());
|
||||
return result;
|
||||
}
|
||||
|
||||
private static class DetachedCatalog extends CatalogImpl {
|
||||
private static final long serialVersionUID = 7979890261252183486L;
|
||||
|
||||
|
||||
@ -97,6 +97,8 @@ final class DropTableImpl extends AbstractRowCountQuery implements
|
||||
this.temporary = temporary;
|
||||
}
|
||||
|
||||
final Table<?> $table() { return table; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// XXX: DSL API
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
Loading…
Reference in New Issue
Block a user