@@ -319,15 +319,48 @@ defmodule Ecto.Adapters.SQLite3.Connection do
319319
320320 @ impl true
321321 def explain_query ( conn , query , params , opts ) do
322- case query ( conn , build_explain_query ( query ) , params , opts ) do
322+ type = Keyword . get ( opts , :type , :query_plan )
323+
324+ case query ( conn , build_explain_query ( query , type ) , params , opts ) do
323325 { :ok , % Exqlite.Result { } = result } ->
324- { :ok , SQL . format_table ( result ) }
326+ case type do
327+ :query_plan -> { :ok , format_query_plan_explain ( result ) }
328+ :instructions -> { :ok , SQL . format_table ( result ) }
329+ end
325330
326331 error ->
327332 error
328333 end
329334 end
330335
336+ defp build_explain_query ( query , :query_plan ) do
337+ IO . iodata_to_binary ( [ "EXPLAIN QUERY PLAN" , query ] )
338+ end
339+
340+ defp build_explain_query ( query , :instructions ) do
341+ IO . iodata_to_binary ( [ "EXPLAIN " , query ] )
342+ end
343+
344+ # Mimics the ASCII format of the sqlite CLI
345+ defp format_query_plan_explain ( % { rows: rows } ) do
346+ { lines , _ } =
347+ rows
348+ |> Enum . chunk_every ( 2 , 1 , [ nil ] )
349+ |> Enum . map_reduce ( 0 , fn [ [ id , parent , _ , text ] , next ] , depth ->
350+ { branch , next_depth } =
351+ case { id , parent , next } do
352+ { id , _ , [ _ , id , _ , _ ] } -> { "|--" , depth + 1 }
353+ { _ , p , [ _ , p , _ , _ ] } -> { "|--" , depth }
354+ _ -> { "`--" , depth - 1 }
355+ end
356+
357+ formatted_line = String . duplicate ( "| " , depth ) <> branch <> text
358+ { formatted_line , next_depth }
359+ end )
360+
361+ Enum . join ( [ "QUERY PLAN" | lines ] , "\n " )
362+ end
363+
331364 ##
332365 ## DDL
333366 ##
@@ -654,10 +687,6 @@ defmodule Ecto.Adapters.SQLite3.Connection do
654687 { "SELECT name FROM sqlite_master WHERE type='table' AND name=? LIMIT 1" , [ table ] }
655688 end
656689
657- def build_explain_query ( query ) do
658- IO . iodata_to_binary ( [ "EXPLAIN " , query ] )
659- end
660-
661690 ##
662691 ## Query generation
663692 ##
0 commit comments