From a3e27f9103e2cc2581be9d0ea0b5fc9ce2158a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Kali=C5=84ski?= Date: Mon, 1 Feb 2021 00:34:09 +0100 Subject: [PATCH] Order views by dependency order in SCRIPT output (#2391) --- .../org/h2/command/dml/ScriptCommand.java | 67 +++++++++++++++++-- .../test/org/h2/test/scripts/testScript.sql | 45 +++++++++++++ 2 files changed, 106 insertions(+), 6 deletions(-) diff --git a/h2/src/main/org/h2/command/dml/ScriptCommand.java b/h2/src/main/org/h2/command/dml/ScriptCommand.java index acae5849dc..d09d75d421 100644 --- a/h2/src/main/org/h2/command/dml/ScriptCommand.java +++ b/h2/src/main/org/h2/command/dml/ScriptCommand.java @@ -20,9 +20,13 @@ import java.util.Arrays; import java.util.Collection; import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; + import org.h2.api.ErrorCode; import org.h2.command.CommandInterface; import org.h2.constraint.Constraint; @@ -57,6 +61,7 @@ import org.h2.table.PlanItem; import org.h2.table.Table; import org.h2.table.TableType; +import org.h2.table.TableView; import org.h2.util.HasSQL; import org.h2.util.IOUtils; import org.h2.util.MathUtils; @@ -231,13 +236,10 @@ public ResultInterface query(long maxrows) { } } - final ArrayList tables = db.getAllTablesAndViews(); - // sort by id, so that views are after tables and views on views - // after the base views - tables.sort(Comparator.comparingInt(Table::getId)); + final ArrayList
sortedTablesAndViews = getSortedTablesAndViews(db); // Generate the DROP XXX ... IF EXISTS - for (Table table : tables) { + for (Table table : sortedTablesAndViews) { if (excludeSchema(table.getSchema())) { continue; } @@ -280,7 +282,7 @@ public ResultInterface query(long maxrows) { // Generate CREATE TABLE and INSERT...VALUES int count = 0; - for (Table table : tables) { + for (Table table : sortedTablesAndViews) { if (excludeSchema(table.getSchema())) { continue; } @@ -382,6 +384,59 @@ public ResultInterface query(long maxrows) { return r; } + // First tables ordered by ID, then views in dependency order + private ArrayList
getSortedTablesAndViews(Database db) { + final ArrayList
allTablesAndViews = db.getAllTablesAndViews(); + allTablesAndViews.sort(Comparator.comparingInt(Table::getId)); + final ArrayList allViews = new ArrayList<>(); + final ArrayList
sortedTablesAndViews = new ArrayList<>(); + for (Table table : allTablesAndViews) { + if (table instanceof TableView) { + allViews.add((TableView) table); + } else { + sortedTablesAndViews.add(table); + } + } + final List sortedViews = new TableViewSorter(allViews).sortByDependencies(); + sortedTablesAndViews.addAll(sortedViews); + return sortedTablesAndViews; + } + + // Topological sort of list of TableViews + public static class TableViewSorter { + private final List views; + private final List order; + private final Map visited; + + public TableViewSorter(List views) { + this.views = views; + this.order = new ArrayList<>(views.size()); + this.visited = new HashMap<>(views.size()); + } + + public List sortByDependencies() { + for (TableView view : views) { + visited.put(view, false); + } + for (TableView view : views) { + if (Boolean.FALSE.equals(visited.get(view))) { + sortHelper(view); + } + } + return order; + } + + private void sortHelper(TableView v) { + visited.replace(v, true); + for (Table table : v.getTables()) { + if (table instanceof TableView && Boolean.FALSE.equals(visited.get(table))) { + sortHelper((TableView) table); + } + } + order.add(v); + } + } + private void dumpDomains(ArrayList schemas) throws IOException { TreeMap> referencingDomains = new TreeMap<>(BY_NAME_COMPARATOR); TreeSet known = new TreeSet<>(BY_NAME_COMPARATOR); diff --git a/h2/src/test/org/h2/test/scripts/testScript.sql b/h2/src/test/org/h2/test/scripts/testScript.sql index f675c75d51..dbbe9a613e 100644 --- a/h2/src/test/org/h2/test/scripts/testScript.sql +++ b/h2/src/test/org/h2/test/scripts/testScript.sql @@ -7103,3 +7103,48 @@ select * from test where "YEAR" in (select distinct "YEAR" from test order by "Y drop table test; > ok + +----- Issue#2390 ----- +CREATE TABLE TEST_TABLE ("ID" NUMBER(19, 0), "TEXT" VARCHAR(100)); +> ok + +CREATE VIEW TEST_VIEW("ID", "TEXT") AS SELECT ID, TEXT FROM TEST_TABLE; +> ok + +ALTER TABLE TEST_TABLE ADD COLUMN ("NEW_COLUMN" VARCHAR(10)); +> ok + +script nodata nopasswords nosettings noversion; +> SCRIPT +> ------------------------------------------------------------------------------------------------------------------------------------ +> CREATE USER IF NOT EXISTS "SA" PASSWORD '' ADMIN; +> CREATE MEMORY TABLE "PUBLIC"."TEST_TABLE"( "ID" NUMERIC(19, 0), "TEXT" CHARACTER VARYING(100), "NEW_COLUMN" CHARACTER VARYING(10) ); +> -- 0 +/- SELECT COUNT(*) FROM PUBLIC.TEST_TABLE; +> CREATE FORCE VIEW "PUBLIC"."TEST_VIEW"("ID", "TEXT") AS SELECT "ID", "TEXT" FROM "PUBLIC"."TEST_TABLE"; +> rows (ordered): 4 + +----- Issue#2391 ----- +CREATE VIEW TEST_VIEW_INNER("ID", "TEXT") AS SELECT ID, TEXT FROM TEST_TABLE; +> ok + +CREATE OR REPLACE FORCE VIEW TEST_VIEW("ID", "TEXT") AS SELECT ID, TEXT FROM TEST_VIEW_INNER; +> ok + +script nodata nopasswords nosettings noversion; +> SCRIPT +> ------------------------------------------------------------------------------------------------------------------------------------ +> CREATE USER IF NOT EXISTS "SA" PASSWORD '' ADMIN; +> CREATE MEMORY TABLE "PUBLIC"."TEST_TABLE"( "ID" NUMERIC(19, 0), "TEXT" CHARACTER VARYING(100), "NEW_COLUMN" CHARACTER VARYING(10) ); +> -- 0 +/- SELECT COUNT(*) FROM PUBLIC.TEST_TABLE; +> CREATE FORCE VIEW "PUBLIC"."TEST_VIEW_INNER"("ID", "TEXT") AS SELECT "ID", "TEXT" FROM "PUBLIC"."TEST_TABLE"; +> CREATE FORCE VIEW "PUBLIC"."TEST_VIEW"("ID", "TEXT") AS SELECT "ID", "TEXT" FROM "PUBLIC"."TEST_VIEW_INNER"; +> rows (ordered): 5 + +DROP VIEW "PUBLIC"."TEST_VIEW"; +> ok + +DROP VIEW "PUBLIC"."TEST_VIEW_INNER"; +> ok + +DROP TABLE "PUBLIC"."TEST_TABLE"; +> ok