{ ------------------------------------------------------------------------------

  DISQLite3 project to profile a 20 million records insertion.
  Use DISQLite3 Pro for best performance.

  Visit the DISQLite3 Internet site for latest information and updates:

    http://www.yunqa.de

  Copyright (c) 2005-2021 Ralf Junker, Yunqa <delphi@yunqa.de>

------------------------------------------------------------------------------ }

program DISQLite3_20_Million_Export;

{$APPTYPE CONSOLE}
{$I DICompilers.inc}
{$I DISQLite3.inc}

uses
  {$IFDEF FastMM}{$I FastMM_uses.inc}{$ENDIF}
  {$IFDEF HAS_UNITSCOPE}
  System.SysUtils, System.Classes, Winapi.Windows,
  {$ELSE HAS_UNITSCOPE}
  SysUtils, Classes, Windows,
  {$ENDIF HAS_UNITSCOPE}
  DISystemCompat, DISQLite3Api;

{$R ..\..\Yunqa.res}

const
  CSV_FILE_NAME = '..\20_million.csv';
  DB_FILE_NAME = '..\20_million_generate.db3';
  Comma: AnsiChar = ',';
  CRLF: array[0..1] of AnsiChar = (#13, #10);

var
  DB: sqlite3_ptr;
  Stmt: sqlite3_stmt_ptr;
  i, j: Integer;
  s8: Utf8String = '';
  Stream: TFileStream;
  Buffer: TMemoryStream;
  totalsec: Integer;
  tc_start: Cardinal;
begin
  try
    { Initialize the DISQLite3 library prior to using any other DISQLite3
      functionality. See also sqlite3_shutdown() below.}
    sqlite3_initialize;
    try
      tc_start := GetTickCount;
      Stream := TFileStream.Create(CSV_FILE_NAME, fmCreate);
      try
        sqlite3_check(sqlite3_open(DB_FILE_NAME, @DB));
        try
          sqlite3_check(sqlite3_prepare_v2(DB,
            'SELECT * FROM t ORDER BY i1', -1, @Stmt, nil), DB);
          try
            i := 0;
            Buffer := TMemoryStream.Create; // As Temp buffer.
            try
              while sqlite3_check(sqlite3_step(Stmt), DB) = SQLITE_ROW do
                begin
                  // t1
                  Buffer.Write(sqlite3_column_text(Stmt, 0)^,
                    sqlite3_column_bytes(Stmt, 0));
                  Buffer.Write(Comma, Length(Comma));
                  // t2
                  Buffer.Write(sqlite3_column_text(Stmt, 1)^,
                    sqlite3_column_bytes(Stmt, 1));
                  Buffer.Write(Comma, Length(Comma));
                  // i1
                  Buffer.Write(sqlite3_column_text(Stmt, 2)^,
                    sqlite3_column_bytes(Stmt, 2));
                  // r1 - r6
                  for j := 3 to 8 do
                    begin
                      Buffer.Write(Comma, Length(Comma));
                      s8 := FloatToSqlStr(sqlite3_column_double(Stmt, j), '.', 18, 4);
                      Buffer.Write(Pointer(s8)^, Length(s8));
                    end;

                  Buffer.Write(CRLF, SizeOf(CRLF));

                  Inc(i);
                  if i mod $1000 = 0 then
                    begin
                      { Flush buffer to file every $1000 records. }
                      Stream.Write(Buffer.Memory^, Buffer.Position);
                      Buffer.Seek(0, soFromBeginning);
                      Write('.');
                    end;
                end;
              { Flush remaining buffer. }
              Stream.Write(Buffer.Memory^, Buffer.Position);
            finally
              Buffer.Free;
            end;
          finally
            sqlite3_finalize(Stmt);
          end;
        finally
          Stream.Free;
        end;

      finally
        sqlite3_check(sqlite3_close(DB), DB);
        WriteLn;
        totalsec := (GetTickCount - tc_start) div 1000;
        WriteLn('Total Time: ',
          totalsec div 60, ' min, ', totalsec mod 60, 'sec');
      end;

    finally
      { Deallocate any resources that were allocated by
      sqlite3_initialize() above. }
      sqlite3_shutdown;
    end;

  except
    on e: Exception do
      WriteLn(e.Message);
  end;

  WriteLn;
  WriteLn('Done - Press ENTER to Exit');
  ReadLn;
end.

