         defaultGraph = store.graphs().next() ?? Term(iri: "tag:kasei.us,2018:default-graph")
         warn("Using default graph \(defaultGraph)")
     }
+    let planner     = QueryPlanner(store: store, dataset: store.dataset(withDefault: defaultGraph))
+    let plan        = try planner.plan(query: query)
+    print("Query plan:")
+    print(plan.serialize())
+}
+
+func explain<D : PageDatabase>(_ database: D, query: Query, graph: Term? = nil, verbose: Bool) throws {
+    print("- explaining query")
+    try database.read { (m) in
+        print("- mediator: \(m)")
+        //        let store       = try MediatedLanguagePageQuadStore(mediator: m, acceptLanguages: [("en", 1.0), ("", 0.5)])
+        let store       = try MediatedPageQuadStore(mediator: m)
+        try explain(in: store, query: query, graph: graph, verbose: verbose)
+    }
+}
+
+/// Evaluate the supplied Query against the database's QuadStore and print the results.
+/// If a graph argument is given, use it as the initial active graph.
+///
+/// - parameter query: The query to evaluate.
+/// - parameter graph: The graph name to use as the initial active graph.
+/// - parameter verbose: A flag indicating whether verbose debugging should be emitted during query evaluation.
+func query<Q: QuadStoreProtocol>(in store: Q, query: Query, graph: Term? = nil, verbose: Bool) throws -> Int {
+    let startTime = getCurrentTime()
+    var defaultGraph: Term
+    if let g = graph {
+        defaultGraph = g
+    } else {
+        // if there are no graphs in the database, it doesn't matter what the default graph is.
+        defaultGraph = store.graphs().next() ?? Term(iri: "tag:kasei.us,2018:default-graph")
+        warn("Using default graph \(defaultGraph)")
+    }
     let dataset = store.dataset(withDefault: defaultGraph)
-    let e           = SimpleQueryEvaluator(store: store, dataset: dataset, verbose: verbose)
-    if let mtime = try e.effectiveVersion(matching: query) {
-        let date = getDateString(seconds: mtime)
-        if verbose {
-            print("# Last-Modified: \(date)")
-        }
-    }
+
+    if true {
+        let e       = SimpleQueryEvaluator(store: store, dataset: dataset, verbose: verbose)
+        if let mtime = try e.effectiveVersion(matching: query) {
+            let date = getDateString(seconds: mtime)
+            if verbose {
+                print("# Last-Modified: \(date)")
+            }
+        }
+    }
+
+    let e       = QueryPlanEvaluator(store: store, dataset: dataset)
     let results = try e.evaluate(query: query)
     let count = printResult(results)
     if verbose {

        let path = NSURL(fileURLWithPath: filename).absoluteString
        #endif
        let graph   = defaultGraphTerm ?? Term(value: path, type: .iri)
        
        iris.insert(graph)
        
        let parser = RDFParserCombined()
        count = try parser.parse(file: filename, base: graph.value) { (s, p, o) in
            for term in [s, p, o] {

                let q = try p.parseQuery()
                print("Parsed query:")
                print(q.serialize())
                switch (config.type, config.languageAware) {
                case (.filePageDatabase(_), false):
                    let s = qs as! PageQuadStore<FilePageDatabase>
                    try explain(in: s, query: q, graph: graph, verbose: verbose)
                case (.filePageDatabase(_), true):
                    let s = qs as! LanguagePageQuadStore<FilePageDatabase>
                    try explain(in: s, query: q, graph: graph, verbose: verbose)
                case (.memoryDatabase, false):
                    let s = qs as! MemoryQuadStore
                    try explain(in: s, query: q, graph: graph, verbose: verbose)
                case (.memoryDatabase, true):
                    let s = qs as! LanguageMemoryQuadStore
                    try explain(in: s, query: q, graph: graph, verbose: verbose)
                case (.sqliteFileDatabase(_), false), (.sqliteMemoryDatabase, false):
                    let s = qs as! SQLiteQuadStore
                    try explain(in: s, query: q, graph: graph, verbose: verbose)
                case (.sqliteFileDatabase(_), true), (.sqliteMemoryDatabase, true):
                    let s = qs as! SQLiteLanguageQuadStore
                    try explain(in: s, query: q, graph: graph, verbose: verbose)
                }
            } catch let e {
                warn("*** Failed to explain query: \(e)")
            }
