CsvSchema now extends MapSchema, rather than implementing Schema directly. This removes a lot of code, and allows the schema to contain explicit tables, including views.
diff --git a/pom.xml b/pom.xml
index bf50031..4eef3ac 100644
--- a/pom.xml
+++ b/pom.xml
@@ -82,7 +82,7 @@
     <dependency>
       <groupId>net.hydromatic</groupId>
       <artifactId>optiq</artifactId>
-      <version>0.3.7</version>
+      <version>TRUNK-SNAPSHOT</version>
     </dependency>
     <dependency>
       <groupId>net.hydromatic</groupId>
diff --git a/src/main/java/net/hydromatic/optiq/impl/csv/CsvSchema.java b/src/main/java/net/hydromatic/optiq/impl/csv/CsvSchema.java
index b6c9fa8..38dd4eb 100644
--- a/src/main/java/net/hydromatic/optiq/impl/csv/CsvSchema.java
+++ b/src/main/java/net/hydromatic/optiq/impl/csv/CsvSchema.java
@@ -17,13 +17,11 @@
 */
 package net.hydromatic.optiq.impl.csv;
 
-import net.hydromatic.linq4j.*;
 import net.hydromatic.linq4j.expressions.Expression;
 
 import net.hydromatic.optiq.*;
 import net.hydromatic.optiq.impl.TableInSchemaImpl;
-import net.hydromatic.optiq.impl.java.JavaTypeFactory;
-import net.hydromatic.optiq.jdbc.OptiqConnection;
+import net.hydromatic.optiq.impl.java.MapSchema;
 
 import org.eigenbase.reltype.RelDataType;
 
@@ -34,13 +32,9 @@
  * Schema mapped onto a directory of CSV files. Each table in the schema
  * is a CSV file in that directory.
  */
-public class CsvSchema implements Schema {
-  private final Schema parentSchema;
+public class CsvSchema extends MapSchema {
   final File directoryFile;
-  private final Expression expression;
   private final boolean smart;
-  final JavaTypeFactory typeFactory;
-  private Map<String, TableInSchema> map;
 
   /**
    * Creates a CSV schema.
@@ -58,83 +52,39 @@
       Expression expression,
       boolean smart)
   {
-    this.parentSchema = parentSchema;
+    super(parentSchema, expression);
     this.directoryFile = directoryFile;
-    this.expression = expression;
     this.smart = smart;
-    this.typeFactory = ((OptiqConnection) getQueryProvider()).getTypeFactory();
   }
 
-  public Expression getExpression() {
-    return expression;
-  }
-
-  public List<TableFunction> getTableFunctions(String name) {
-    return Collections.emptyList();
-  }
-
-  public QueryProvider getQueryProvider() {
-    return parentSchema.getQueryProvider();
-  }
-
-  public Collection<TableInSchema> getTables() {
-    return computeMap().values();
-  }
-
-  public <T> Table<T> getTable(String name, Class<T> elementType) {
-    final TableInSchema tableInSchema = computeMap().get(name);
-    //noinspection unchecked
-    return tableInSchema == null
-        ? null
-        : (Table) tableInSchema.getTable(elementType);
-  }
-
-  public Map<String, List<TableFunction>> getTableFunctions() {
-    // this kind of schema does not have table functions
-    return Collections.emptyMap();
-  }
-
-  public Schema getSubSchema(String name) {
-    // this kind of schema does not have sub-schemas
-    return null;
-  }
-
-  public Collection<String> getSubSchemaNames() {
-    // this kind of schema does not have sub-schemas
-    return Collections.emptyList();
-  }
-
-  /** Returns the map of tables by name, populating the map on first use. */
-  private synchronized Map<String, TableInSchema> computeMap() {
-    if (map == null) {
-      map = new HashMap<String, TableInSchema>();
-      File[] files = directoryFile.listFiles(
-          new FilenameFilter() {
-            public boolean accept(File dir, String name) {
-              return name.endsWith(".csv");
-            }
-          });
-      for (File file : files) {
-        String tableName = file.getName();
-        if (tableName.endsWith(".csv")) {
-          tableName = tableName.substring(
-              0, tableName.length() - ".csv".length());
-        }
-        final List<CsvFieldType> fieldTypes = new ArrayList<CsvFieldType>();
-        final RelDataType rowType =
-            CsvTable.deduceRowType(typeFactory, file, fieldTypes);
-        final CsvTable table;
-        if (smart) {
-          table = new CsvSmartTable(this, tableName, file, rowType, fieldTypes);
-        } else {
-          table = new CsvTable(this, tableName, file, rowType, fieldTypes);
-        }
-        map.put(
-            tableName,
-            new TableInSchemaImpl(this, tableName, TableType.TABLE, table));
+  @Override
+  protected Collection<TableInSchema> initialTables() {
+    final List<TableInSchema> list = new ArrayList<TableInSchema>();
+    File[] files = directoryFile.listFiles(
+        new FilenameFilter() {
+          public boolean accept(File dir, String name) {
+            return name.endsWith(".csv");
+          }
+        });
+    for (File file : files) {
+      String tableName = file.getName();
+      if (tableName.endsWith(".csv")) {
+        tableName = tableName.substring(
+            0, tableName.length() - ".csv".length());
       }
+      final List<CsvFieldType> fieldTypes = new ArrayList<CsvFieldType>();
+      final RelDataType rowType =
+          CsvTable.deduceRowType(typeFactory, file, fieldTypes);
+      final CsvTable table;
+      if (smart) {
+        table = new CsvSmartTable(this, tableName, file, rowType, fieldTypes);
+      } else {
+        table = new CsvTable(this, tableName, file, rowType, fieldTypes);
+      }
+      list.add(
+          new TableInSchemaImpl(this, tableName, TableType.TABLE, table));
     }
-    return map;
+    return list;
   }
 }
 
diff --git a/src/main/java/net/hydromatic/optiq/impl/csv/CsvSmartTable.java b/src/main/java/net/hydromatic/optiq/impl/csv/CsvSmartTable.java
index dca6e3a..b6abbee 100644
--- a/src/main/java/net/hydromatic/optiq/impl/csv/CsvSmartTable.java
+++ b/src/main/java/net/hydromatic/optiq/impl/csv/CsvSmartTable.java
@@ -23,7 +23,6 @@
 import org.eigenbase.reltype.RelDataType;
 
 import java.io.File;
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -37,16 +36,6 @@
     super(schema, tableName, file, rowType, fieldTypes);
   }
 
-  /** Creates a table based on a CSV file, deducing its column types by reading
-   * the first line of the file. */
-  public static CsvSmartTable create(CsvSchema schema, File file,
-      String tableName) {
-    final List<CsvFieldType> fieldTypes = new ArrayList<CsvFieldType>();
-    final RelDataType rowType =
-        deduceRowType(schema.typeFactory, file, fieldTypes);
-    return new CsvSmartTable(schema, tableName, file, rowType, fieldTypes);
-  }
-
   public RelNode toRel(
       RelOptTable.ToRelContext context,
       RelOptTable relOptTable)
diff --git a/src/test/resources/model-with-view.json b/src/test/resources/model-with-view.json
new file mode 100644
index 0000000..8174a3f
--- /dev/null
+++ b/src/test/resources/model-with-view.json
@@ -0,0 +1,21 @@
+{
+  version: '1.0',
+  defaultSchema: 'SALES',
+  schemas: [
+    {
+      name: 'SALES',
+      type: 'custom',
+      factory: 'net.hydromatic.optiq.impl.csv.CsvSchemaFactory',
+      operand: {
+        directory: 'target/test-classes/sales'
+      },
+      tables: [
+        {
+          name: 'FEMALE_EMPS',
+          type: 'view',
+          sql: 'SELECT * FROM emps WHERE gender = \'F\''
+        }
+      ]
+    }
+  ]
+}