diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/DebuggerTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/DebuggerTests.java index de33b1d3ad..fd4ebfb63b 100644 --- a/jOOQ-test/src/org/jooq/test/_/testcases/DebuggerTests.java +++ b/jOOQ-test/src/org/jooq/test/_/testcases/DebuggerTests.java @@ -36,6 +36,7 @@ package org.jooq.test._.testcases; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static org.jooq.impl.Factory.inline; import static org.jooq.tools.debug.impl.DebuggerFactory.localDebugger; @@ -48,6 +49,8 @@ import java.util.List; import org.jooq.Field; import org.jooq.Record; import org.jooq.Result; +import org.jooq.Schema; +import org.jooq.Table; import org.jooq.TableRecord; import org.jooq.UpdatableRecord; import org.jooq.test.BaseTest; @@ -294,6 +297,31 @@ extends BaseTest schemata = Arrays.asList(context.executor().schemata()); + assertTrue(schemata.contains(schema())); + } + + List> tables1 = Arrays.asList(context.executor().tables()); + assertTrue(tables1.contains(TBook())); + assertTrue(tables1.contains(TAuthor())); + + if (schema() != null) { + List> tables2 = Arrays.asList(context.executor().tables(schema())); + assertTrue(tables2.contains(TBook())); + assertTrue(tables2.contains(TAuthor())); + } + + List> fields1 = Arrays.asList(context.executor().fields()); + assertTrue(fields1.contains(TBook_ID())); + assertTrue(fields1.contains(TBook_TITLE())); + assertTrue(fields1.contains(TAuthor_ID())); + + List> fields2 = Arrays.asList(context.executor().fields(TBook())); + assertTrue(fields2.contains(TBook_ID())); + assertTrue(fields2.contains(TBook_TITLE())); + assertFalse(fields2.contains(TAuthor_ID())); + Result result = context.executor().fetch(create().selectCount().from(TAuthor())); diff --git a/jOOQ/src/main/java/org/jooq/impl/Factory.java b/jOOQ/src/main/java/org/jooq/impl/Factory.java index 8f8442d38b..ad1c3bcdd5 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Factory.java +++ b/jOOQ/src/main/java/org/jooq/impl/Factory.java @@ -955,6 +955,31 @@ public class Factory implements FactoryOperations { // XXX Plain SQL object factory // ------------------------------------------------------------------------- + /** + * Create a qualified schema, given its schema name + *

+ * This constructs a schema reference given the schema's qualified name. + * jOOQ will render the schema name according to your + * {@link Settings#getRenderNameStyle()} settings. Choose + * {@link RenderNameStyle#QUOTED} to prevent syntax errors and/or SQL + * injection. + *

+ * Example:

+     * // This schema...
+     * schemaByName("MY_SCHEMA");
+     *
+     * // ... will render this SQL on SQL Server with RenderNameStyle.QUOTED set
+     * [MY_SCHEMA]
+     * 
+ * + * @param name The schema's reference name. + * @return A schema referenced by name + */ + @Support + public static Schema schemaByName(String name) { + return new SchemaImpl(name); + } + /** * A custom SQL clause that can render arbitrary table expressions. *

@@ -1066,7 +1091,7 @@ public class Factory implements FactoryOperations { *

* Example:

      * // This table...
-     * tableName("MY_SCHEMA", "MY_TABLE");
+     * tableByName("MY_SCHEMA", "MY_TABLE");
      *
      * // ... will render this SQL on SQL Server with RenderNameStyle.QUOTED set
      * [MY_SCHEMA].[MY_TABLE]
@@ -1350,7 +1375,7 @@ public class Factory implements FactoryOperations {
      * 

* Example:

      * // This field...
-     * fieldName("MY_SCHEMA", "MY_TABLE", "MY_FIELD");
+     * fieldByName("MY_SCHEMA", "MY_TABLE", "MY_FIELD");
      *
      * // ... will render this SQL on SQL Server with RenderNameStyle.QUOTED set
      * [MY_SCHEMA].[MY_TABLE].[MY_FIELD]
@@ -1385,7 +1410,7 @@ public class Factory implements FactoryOperations {
      * 

* Example:

      * // This field...
-     * fieldName("MY_SCHEMA", "MY_TABLE", "MY_FIELD");
+     * fieldByName("MY_SCHEMA", "MY_TABLE", "MY_FIELD");
      *
      * // ... will render this SQL on SQL Server with RenderNameStyle.QUOTED set
      * [MY_SCHEMA].[MY_TABLE].[MY_FIELD]
@@ -1421,7 +1446,7 @@ public class Factory implements FactoryOperations {
      * 

* Example:

      * // This field...
-     * fieldName("MY_SCHEMA", "MY_TABLE", "MY_FIELD");
+     * fieldByName("MY_SCHEMA", "MY_TABLE", "MY_FIELD");
      *
      * // ... will render this SQL on SQL Server with RenderNameStyle.QUOTED set
      * [MY_SCHEMA].[MY_TABLE].[MY_FIELD]
diff --git a/jOOQ/src/main/java/org/jooq/tools/debug/QueryExecutor.java b/jOOQ/src/main/java/org/jooq/tools/debug/QueryExecutor.java
index c0d0f4d3aa..7f6faf32d7 100644
--- a/jOOQ/src/main/java/org/jooq/tools/debug/QueryExecutor.java
+++ b/jOOQ/src/main/java/org/jooq/tools/debug/QueryExecutor.java
@@ -36,10 +36,13 @@
  */
 package org.jooq.tools.debug;
 
+import org.jooq.Field;
 import org.jooq.Query;
 import org.jooq.Record;
 import org.jooq.Result;
 import org.jooq.ResultQuery;
+import org.jooq.Schema;
+import org.jooq.Table;
 
 /**
  * A query executor allows to execute queries in any given context.
@@ -49,6 +52,31 @@ import org.jooq.ResultQuery;
  */
 public interface QueryExecutor {
 
+    /**
+     * Fetch all schemata
+     *
+     * @return A list of schemata
+     */
+    Schema[] schemata();
+
+    /**
+     * Fetch all tables, given a list of schemata.
+     *
+     * @param schemata The list of schemata for which to fetch tables. If no
+     *            schema is provided, all tables are fetched.
+     * @return A list of tables
+     */
+    Table[] tables(Schema... schemata);
+
+    /**
+     * Fetch all fields, given a list of tables.
+     *
+     * @param tables The list of tables for which to fetch fields. If no table
+     *            is provided, all fields are fetched.
+     * @return A list of fields.
+     */
+    Field[] fields(Table... tables);
+
     /**
      * Execute a query
      */
diff --git a/jOOQ/src/main/java/org/jooq/tools/debug/impl/ClientDebugger.java b/jOOQ/src/main/java/org/jooq/tools/debug/impl/ClientDebugger.java
index d64d8127c2..a35afe372f 100644
--- a/jOOQ/src/main/java/org/jooq/tools/debug/impl/ClientDebugger.java
+++ b/jOOQ/src/main/java/org/jooq/tools/debug/impl/ClientDebugger.java
@@ -40,10 +40,13 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
+import org.jooq.Field;
 import org.jooq.Query;
 import org.jooq.Record;
 import org.jooq.Result;
 import org.jooq.ResultQuery;
+import org.jooq.Schema;
+import org.jooq.Table;
 import org.jooq.tools.debug.Breakpoint;
 import org.jooq.tools.debug.BreakpointListener;
 import org.jooq.tools.debug.Debugger;
@@ -238,6 +241,21 @@ class ClientDebugger extends AbstractDebuggerObject implements Debugger {
             if (executor == null) {
                 executor = new QueryExecutor() {
 
+                    @Override
+                    public Schema[] schemata() {
+                        return context.getClientDebugger().getCommunicationInterface().syncSend(new CMC_schemata());
+                    }
+
+                    @Override
+                    public Table[] tables(Schema... schemata) {
+                        return context.getClientDebugger().getCommunicationInterface().syncSend(new CMC_tables(schemata));
+                    }
+
+                    @Override
+                    public Field[] fields(Table... tables) {
+                        return context.getClientDebugger().getCommunicationInterface().syncSend(new CMC_fields(tables));
+                    }
+
                     @Override
                     public  Result fetch(ResultQuery query) {
                         return context.getClientDebugger().getCommunicationInterface().syncSend(new CMC_fetch(query));
@@ -254,6 +272,60 @@ class ClientDebugger extends AbstractDebuggerObject implements Debugger {
         }
     }
 
+    static class CMC_schemata extends CommandMessage {
+
+        /**
+         * Generated UID
+         */
+        private static final long    serialVersionUID = -9163573787750356644L;
+
+        CMC_schemata() {
+        }
+
+        @Override
+        public Schema[] run(MessageContext context) {
+            return DebugListener.BREAKPOINT_EXECUTORS.get().schemata();
+        }
+    }
+
+    static class CMC_tables extends CommandMessage[]> {
+
+        /**
+         * Generated UID
+         */
+        private static final long    serialVersionUID = -9163573787750356644L;
+
+        private final Schema[] schemata;
+
+        CMC_tables(Schema[] schemata) {
+            this.schemata = schemata;
+        }
+
+        @Override
+        public Table[] run(MessageContext context) {
+            return DebugListener.BREAKPOINT_EXECUTORS.get().tables(schemata);
+        }
+    }
+
+    static class CMC_fields extends CommandMessage[]> {
+
+        /**
+         * Generated UID
+         */
+        private static final long    serialVersionUID = -9163573787750356644L;
+
+        private final Table[] tables;
+
+        CMC_fields(Table[] tables) {
+            this.tables = tables;
+        }
+
+        @Override
+        public Field[] run(MessageContext context) {
+            return DebugListener.BREAKPOINT_EXECUTORS.get().fields(tables);
+        }
+    }
+
     static class CMC_fetch extends CommandMessage> {
 
         /**
diff --git a/jOOQ/src/main/java/org/jooq/tools/debug/impl/QueryExecutorImpl.java b/jOOQ/src/main/java/org/jooq/tools/debug/impl/QueryExecutorImpl.java
index ace932842d..50c0d142f2 100644
--- a/jOOQ/src/main/java/org/jooq/tools/debug/impl/QueryExecutorImpl.java
+++ b/jOOQ/src/main/java/org/jooq/tools/debug/impl/QueryExecutorImpl.java
@@ -36,11 +36,24 @@
  */
 package org.jooq.tools.debug.impl;
 
+import static org.jooq.impl.Factory.fieldByName;
+import static org.jooq.impl.Factory.schemaByName;
+import static org.jooq.impl.Factory.tableByName;
+
+import java.sql.DatabaseMetaData;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
 import org.jooq.Configuration;
+import org.jooq.Field;
 import org.jooq.Query;
 import org.jooq.Record;
 import org.jooq.Result;
 import org.jooq.ResultQuery;
+import org.jooq.Schema;
+import org.jooq.Table;
+import org.jooq.exception.DataAccessException;
 import org.jooq.impl.Factory;
 import org.jooq.tools.debug.QueryExecutor;
 
@@ -59,18 +72,98 @@ class QueryExecutorImpl implements QueryExecutor {
     }
 
     @Override
-    public int execute(Query query) {
+    public final Schema[] schemata() {
+        try {
+            DatabaseMetaData meta = meta();
+            String[] names = create().fetch(meta.getSchemas()).intoArray(0, String.class);
+            Schema[] result = new Schema[names.length];
+
+            for (int i = 0; i < names.length; i++) {
+                result[i] = schemaByName(names[i]);
+            }
+
+            return result;
+        }
+        catch (SQLException e) {
+            throw new DataAccessException("Cannot fetch schemata", e);
+        }
+    }
+
+    @Override
+    public final Table[] tables(Schema... schemata) {
+        schemata = (schemata != null && schemata.length > 0) ? schemata : new Schema[] { null };
+        List> result = new ArrayList>();
+
+        try {
+            DatabaseMetaData meta = meta();
+
+            for (Schema schema : schemata) {
+                String schemaName = (schema != null) ? schema.getName() : null;
+
+                for (Record record : create().fetch(meta.getTables(null, schemaName, null, null))) {
+                    result.add(tableByName(
+                        record.getValue(1, String.class),
+                        record.getValue(2, String.class)));
+                }
+            }
+
+            return result.toArray(new Table[result.size()]);
+        }
+        catch (SQLException e) {
+            throw new DataAccessException("Cannot fetch tables", e);
+        }
+    }
+
+    @Override
+    public final Field[] fields(Table... tables) {
+        tables = (tables != null && tables.length > 0) ? tables : new Table[] { null };
+        List> result = new ArrayList>();
+
+        try {
+            DatabaseMetaData meta = meta();
+
+            for (Table table : tables) {
+                String schemaName = (table != null && table.getSchema() != null) ? table.getSchema().getName() : null;
+                String tableName = (table != null) ? table.getName() : null;
+
+                for (Record record : create().fetch(meta.getColumns(null, schemaName, tableName, null))) {
+                    // Discover SQLDataType, here?
+                    result.add(fieldByName(
+                        record.getValue(1, String.class),
+                        record.getValue(2, String.class),
+                        record.getValue(3, String.class)));
+                }
+            }
+
+            return result.toArray(new Field[result.size()]);
+        }
+        catch (SQLException e) {
+            throw new DataAccessException("Cannot fetch fields", e);
+        }
+    }
+
+    @Override
+    public final int execute(Query query) {
         create().attach(query);
         return query.execute();
     }
 
     @Override
-    public  Result fetch(ResultQuery query) {
+    public final  Result fetch(ResultQuery query) {
         create().attach(query);
         return query.fetch();
     }
 
-    private Factory create() {
+    private final DatabaseMetaData meta() {
+        try {
+            return create().getConnection().getMetaData();
+        }
+        catch (SQLException e) {
+            throw new DataAccessException("Cannot fetch meta data", e);
+        }
+    }
+
+    private final Factory create() {
         return new Factory(configuration.getConnection(), configuration.getDialect(), configuration.getSettings());
     }
 }