anchor.diff:  %% {ok, 2}
   %%
   -define(SQERL_DEFAULT_BATCH_SIZE, 100).
  +%% Backend DBs limit what we can name a statement.
  +%% No upper case, no $...
   -define(SQERL_ADHOC_INSERT_STMT_ATOM, '__adhoc_insert').
  -
  -%% TODO: What if some inserts in a batch fail? Error out or continue?
  -%% TODO: Transactionality? Retries?
  -%% TODO: parallel inserts?
   
   adhoc_insert(Table, Rows) ->
       adhoc_insert(Table, Rows, ?SQERL_DEFAULT_BATCH_SIZE).
  
positive.code:     {Columns, RowsValues} = extract_insert_data(Rows),
      adhoc_insert(Table, Columns, RowsValues, BatchSize).
  
  adhoc_insert(Table, Columns, RowsValues, BatchSize) when BatchSize > 0 ->
      NumRows = length(RowsValues),
      bulk_insert(Table, Columns, RowsValues, NumRows, BatchSize).
  
  bulk_insert(_Table, _Columns, _RowsValues, 0, _BatchSize) ->
      %% 0 rows means we're done!
      {ok, 0};
  bulk_insert(Table, Columns, RowsValues, NumRows, BatchSize) when NumRows < BatchSize ->
      %% Do just one bulk insert since we have less than BatchSize rows
      SQL = sqerl_adhoc:insert(Table, Columns, NumRows, param_style()),
      %% Need to flatten list of row data (list of lists)
      %% to a flat list of parameters to the query
      Parameters = lists:flatten(RowsValues),
      execute(SQL, Parameters);
  bulk_insert(Table, Columns, RowsValues, NumRows, BatchSize) when NumRows >= BatchSize ->
      %% We have at least one batch, so we'll prepare an insert statement
      %% for batch size. Then adhoc_prepared_insert will iterate over batches.
      SQL = sqerl_adhoc:insert(Table, Columns, BatchSize, param_style()),
      PrepInsertUnprepare = fun(Cn) ->
          ok = sqerl_client:prepare(Cn, ?SQERL_ADHOC_INSERT_STMT_ATOM, SQL),
          try
              adhoc_prepared_insert(Cn, RowsValues, NumRows, BatchSize)
          after
              sqerl_client:unprepare(Cn, ?SQERL_ADHOC_INSERT_STMT_ATOM)
          end
      end,
      {ok, Count, RemainingRowsValues} = with_db(PrepInsertUnprepare),
      %% We've taken care of full batches. We could have some remaining rows
      %% that didn't add up to a full batch. We'll do one insert for them.
      RemainingNumRows = NumRows - Count,
      {ok, RemainingCount} = bulk_insert(Table, Columns, RemainingRowsValues, RemainingNumRows, BatchSize),
      {ok, Count + RemainingCount}.
  
  %% @doc Insert data with insert statement already prepared
  adhoc_prepared_insert(Cn, RowsValues, NumRows, BatchSize) ->
      adhoc_prepared_insert(Cn, RowsValues, NumRows, BatchSize, 0).
  
  adhoc_prepared_insert(Cn, RowsValues, NumRows, BatchSize, CountSoFar) when NumRows >= BatchSize ->
      {RowsValuesToInsert, Rest} = lists:split(BatchSize, RowsValues),
      %% Need to flatten list of row data (list of lists)
      %% to a flat list of parameters to the query
      Parameters = lists:flatten(RowsValuesToInsert),
      {ok, Count} = sqerl_client:execute(Cn, ?SQERL_ADHOC_INSERT_STMT_ATOM, Parameters),
      adhoc_prepared_insert(Cn, Rest, NumRows - Count, BatchSize, CountSoFar + Count);
  adhoc_prepared_insert(_Cn, RowsValues, NumRows, BatchSize, CountSoFar) when NumRows < BatchSize ->
      {ok, CountSoFar, RowsValues}.
  
  %% @doc Extract insert data from Rows (list of proplists).
  %% Assumes all rows have the same format.
  
negative.code: %% {[<<"id">>,<<"name">>],[[1,<<"Joe">>],[2,<<"Jeff">>]]}
  %%
  -spec extract_insert_data([[{binary(), any()}]]) -> {[binary()], [[any()]]}.
  extract_insert_data([]) ->
      {[], []};
  extract_insert_data(Rows) ->
      Columns = [C || {C, _V} <- hd(Rows)],
      RowsValues = [[V || {_C, V} <- Row] || Row <- Rows],
      {Columns, RowsValues}.
  
  