anchor.diff:          _:Error -> {error, Error}
       end.
   
  +-spec foldbu(fun(), term(), tuple()) -> {error, term()} | binary().
  +foldbu(Fun, Ctx, PTree) when is_function(Fun, 2) ->
  +    try foldi(bottom_up, PTree, Fun, Ctx) of
  +        {error,_} = Error -> Error;
  +        {Sql, null_fun = Ctx} -> list_to_binary(string:strip(Sql));
  +        {_, NewCtx} -> NewCtx
  +    catch
  +        _:Error -> {error, Error}
  +    end.
  +
   %
   % SELECT
   %
  -foldi({select, Opts} = ST, Fun, Ctx)
  +foldi(FType, {select, Opts} = ST, Fun, Ctx)
    when is_function(Fun, 2) ->
  -    NewCtx = Fun(ST, Ctx),
  +    NewCtx = case FType of
  +        top_down -> Fun(ST, Ctx);
  +        bottom_up -> Ctx
  +    end,
       {NewOs, NewCtx1} = lists:foldl(fun(O, {Acc, CtxAcc}) ->
  -            {SubAcc, CtxAcc1} = foldi(O, Fun, CtxAcc),
  +            {SubAcc, CtxAcc1} = foldi(FType, O, Fun, CtxAcc),
               {Acc++[SubAcc], CtxAcc1}
           end,
           {[], NewCtx},
           Opts),
  -    {"select "++lists:flatten(NewOs), NewCtx1};
  +    NewCtx2 = case FType of
  +        top_down -> NewCtx1;
  +        bottom_up -> Fun(ST, NewCtx1)
  +    end,
  +    {"select "++lists:flatten(NewOs), NewCtx2};
   
   %
   % INSERT
   %
  -foldi({insert, Tab, {cols, Cols}, {values, Values}, Return} = ST, Fun, Ctx)
  +foldi(FType, {insert, Tab, {cols, Cols}, {values, Values}, Return} = ST, Fun, Ctx)
    when is_binary(Tab), is_function(Fun, 2) ->
  -    NewCtx = Fun(ST, Ctx),
  +    NewCtx = case FType of
  +        top_down -> Fun(ST, Ctx);
  +        bottom_up -> Ctx
  +    end,
       {CStrs, NewCtx1} = lists:foldl(fun(C, {Acc, CtxAcc}) ->
               case C of
                   C when is_binary(C) ->
                       {Acc++[binary_to_list(C)], Fun(C, CtxAcc)};
                   C ->
  -                    {CT, CtxAcc1} = foldi(C, Fun, CtxAcc),
  +                    {CT, CtxAcc1} = foldi(FType, C, Fun, CtxAcc),
                       {Acc++[CT], CtxAcc1}
               end
           end,
  
positive.code:                     CtxAcc1 = Fun(C, CtxAcc),
                      CtxAcc2 = Fun(T, CtxAcc1),
                      CtxAcc3 = Fun(N, CtxAcc2),
                      {SubAcc, CtxAcc4} = foldi(O, Fun, CtxAcc3),
                      {Acc ++ [lists:flatten([binary_to_list(C), " ", atom_to_list(T), "(", N, ") ", SubAcc])]
                      , CtxAcc4};
                  {C, {T, N, N1}, O} when is_binary(C) ->
  
negative.code:                                                      , 'truncate table', 'update', 'delete'
                                                       , 'grant', 'revoke']) of
                          true ->
                              {SubAcc, CtxAcc1} = foldi(FType, A, Fun, CtxAcc),
                              {Acc++["(" ++ string:strip(SubAcc) ++ ")"], CtxAcc1};
                          _ ->
                              {SubAcc, CtxAcc1} = foldi(FType, A, Fun, CtxAcc),
                              {Acc++[SubAcc], CtxAcc1}
                      end;
                  A ->
                      {SubAcc, CtxAcc1} = foldi(FType, A, Fun, CtxAcc),
                      {Acc++[SubAcc], CtxAcc1}
              end
          end,
          {[], NewCtx1},
          Args),
      NewCtx3 = case FType of
          top_down -> NewCtx2;
          bottom_up -> Fun(ST, NewCtx2)
      end,
      {binary_to_list(N) ++ "(" ++
      string:join(ArgsStr, ", ")
      ++ ")"
      , NewCtx3};
  
  % hierarchical query
  foldi(_FType, {'hierarchical query', {}} = ST, Fun, Ctx)
   when is_function(Fun,2) ->
      {""
      , Fun(ST, Ctx)};
  foldi(FType, {'hierarchical query', {Part1, Part2}} = ST, Fun, Ctx)
   when is_function(Fun,2) ->
      NewCtx = case FType of
          top_down -> Fun(ST, Ctx);
          bottom_up -> Ctx
      end,
      {Part1Str, NewCtx1} = foldi(FType, Part1, Fun, NewCtx),
      {Part2Str, NewCtx2} = foldi(FType, Part2, Fun, NewCtx1),
      NewCtx3 = case FType of
          top_down -> NewCtx2;
          bottom_up -> Fun(ST, NewCtx2)
      end,
      {lists:flatten([Part1Str, " ", Part2Str])
      , NewCtx3};
  foldi(FType, {'start with', StartWith} = ST, Fun, Ctx)
   when is_function(Fun,2) ->
      NewCtx = case FType of
          top_down -> Fun(ST, Ctx);
          bottom_up -> Ctx
      end,
      {StartWithStr, NewCtx1} = foldi(FType, StartWith, Fun, NewCtx),
      NewCtx2 = case FType of
          top_down -> NewCtx1;
          bottom_up -> Fun(ST, NewCtx1)
      end,
      {lists:flatten([" start with ", StartWithStr])
      , NewCtx2};
  foldi(FType, {'connect by', NoCycle, ConnectBy} = ST, Fun, Ctx)
   when is_function(Fun,2) ->
      NewCtx = case FType of
          top_down -> Fun(ST, Ctx);
          bottom_up -> Ctx
      end,
      {NoCycleStr, NewCtx1} = foldi(FType, NoCycle, Fun, NewCtx),
      {ConnectByStr, NewCtx2} = foldi(FType, ConnectBy, Fun, NewCtx1),
      NewCtx3 = case FType of
          top_down -> NewCtx2;
          bottom_up -> Fun(ST, NewCtx2)
      end,
      {lists:flatten(["connect by "
                    , if byte_size(NoCycle) > 0 -> NoCycleStr++" "; true -> "" end
                    , ConnectByStr])
      , NewCtx3};
  foldi(FType, {'prior', Field} = ST, Fun, Ctx)
   when is_function(Fun,2) ->
      NewCtx = case FType of
          top_down -> Fun(ST, Ctx);
          bottom_up -> Ctx
      end,
      {FieldsStr, NewCtx1} = foldi(FType, Field, Fun, NewCtx),
      NewCtx2 = case FType of
          top_down -> NewCtx1;
          bottom_up -> Fun(ST, NewCtx1)
      end,
      {lists:flatten(["prior ", FieldsStr])
      , NewCtx2};
  
  % lists
  foldi(FType, {'list', Elms} = ST, Fun, Ctx)
   when is_function(Fun,2) ->
      NewCtx = case FType of
          top_down -> Fun(ST, Ctx);
          bottom_up -> Ctx
      end,
      {ElmsStr, NewCtx1} = lists:foldl(fun(E, {Acc, CtxAcc}) ->
              case E of
                  E when is_binary(E) -> {Acc++[binary_to_list(E)], Fun(E, CtxAcc)};
                  E ->
                      {SubAcc, CtxAcc1} = foldi(FType, E, Fun, CtxAcc),
                      {Acc++[SubAcc], CtxAcc1}
              end
          end,
          {[], NewCtx},
          Elms),
      NewCtx2 = case FType of
          top_down -> NewCtx1;
          bottom_up -> Fun(ST, NewCtx1)
      end,
      {"(" ++
       string:join(ElmsStr, ", ")
       ++ ")"
      , NewCtx2};
  
  foldi(FType, {'param', P} = ST, Fun, Ctx)
   when is_function(Fun,2) ->
      NewCtx = case FType of
          top_down -> Fun(ST, Ctx);
          bottom_up -> Ctx
      end,
      NewCtx1 = Fun(P, NewCtx),
      NewCtx2 = case FType of
          top_down -> NewCtx1;
          bottom_up -> Fun(ST, NewCtx1)
      end,
      case P of
          P when is_binary(P) -> {binary_to_list(P), NewCtx2};
          P -> {P, NewCtx2}
      end;
  
  foldi(FType, {'case', When, Then, Else} = ST, Fun, Ctx)
   when is_function(Fun,2) ->
      NewCtx = case FType of
          top_down -> Fun(ST, Ctx);
          bottom_up -> Ctx
      end,
      {WhenStr, NewCtx1} = foldi(FType, When, Fun, NewCtx),
      {ThenStr, NewCtx2} = foldi(FType, Then, Fun, NewCtx1),
      {ElseStr, NewCtx3} = case Else of
          {} -> {"", NewCtx2};
          Else ->
              {EStr, NewCtx21} = foldi(FType, Else, Fun, NewCtx2),
              {" else " ++ EStr, NewCtx21}
      end,
      NewCtx4 = case FType of
          top_down -> NewCtx3;
          bottom_up -> Fun(ST, NewCtx3)
      end,
      {"case when " ++WhenStr++" then "++ThenStr++ElseStr++" end"
      , NewCtx4};
  
  %
  % UNSUPPORTED
  %
  foldi(_FType, PTree, Fun, Ctx)
   when is_function(Fun,2) ->
      Fun(PTree, Ctx),
      io:format(user, "Parse tree not supported ~p~n", [PTree]),
  