mORMot and Open Source friends
Check-in [71278eef20]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:added TSQLDBConnection.NewTableFromRows() method to dump a SQL statement result into a new table of any database (may be used for replication)
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 71278eef2063846be3bb7216d080d956de1927c3
User & Date: abouchez 2013-01-24 14:34:38
Context
2013-01-25
18:54
fixed ticket [231cce565d] about incorrect JSON object returned when an error is raised check-in: 3b91fe0a32 user: abouchez tags: trunk
2013-01-24
14:34
added TSQLDBConnection.NewTableFromRows() method to dump a SQL statement result into a new table of any database (may be used for replication) check-in: 71278eef20 user: abouchez tags: trunk
12:14
SynDBFireBird enhancements:
  • added DefaultPageSize property
  • properly handle one single connection when working in embedded mode
  • added IsFirebirdFile() global function to recognize a FireBird database file
check-in: 4ef8e6d6ef user: abouchez tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SQLite3/Samples/12 - SynDB Explorer/SynDBExplorerExportTables.pas.

84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
            if SQL<>'' then
              SQL := ' where '+SQL;
            SQL := aProps.SQLSelectAll(Table,Fields,ExcludeTypes)+SQL;
            Rows := aProps.NewThreadSafeStatement;
            try
              Rows.Execute(SQL,true);
              Table := StringReplaceAll(StringReplaceAll(Table,' ',''),'-','');
              inc(result,RowsToSQLite3(Conn,Table,Rows,true));
            finally
              Rows.Free;
            end;
          end;
        except
          on E: Exception do
            ShowMessage(E.Message,true);






|







84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
            if SQL<>'' then
              SQL := ' where '+SQL;
            SQL := aProps.SQLSelectAll(Table,Fields,ExcludeTypes)+SQL;
            Rows := aProps.NewThreadSafeStatement;
            try
              Rows.Execute(SQL,true);
              Table := StringReplaceAll(StringReplaceAll(Table,' ',''),'-','');
              inc(result,Conn.NewTableFromRows(Table,Rows,true));
            finally
              Rows.Free;
            end;
          end;
        except
          on E: Exception do
            ShowMessage(E.Message,true);

Changes to SynDB.pas.

154
155
156
157
158
159
160


161
162
163
164
165
166
167
....
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025








1026
1027
1028
1029
1030
1031
1032
....
2712
2713
2714
2715
2716
2717
2718

2719
2720
2721
2722
2723
2724
2725
....
2787
2788
2789
2790
2791
2792
2793

















































2794
2795
2796
2797
2798
2799
2800
....
2822
2823
2824
2825
2826
2827
2828

2829
2830
2831
2832
2833
2834
2835
2836






2837
2838
2839
2840
2841
2842
2843
    to retrieve advanced information about database indexes (e.g. for indexes
    created after multiple columns)
  - added TSQLDBConnectionProperties.SQLTableName() method
  - added TSQLDBConnectionPropertiesThreadSafe.ForceOnlyOneSharedConnection
    property to by-pass internal thread-pool (e.g. for embedded engines)
  - added dFirebird kind of database in TSQLDBDefinition enumerate, and added
    associated SQL requests to retrieve metadata


  - TSQLDBConnectionProperties.SQLSelectAll() now handles spaces in table names
  - TSQLDBStatement.GetParamValueAsText() will truncate to a given number of
    chars the returned text
  - added missing ColumnToVarData() method to ISQLDBRows interface
  - now trim any spaces when retrieving database schema text values
  - fixed ticket [4c68975022] about broken SQL statement when logging active

................................................................................
    // - this default implementation will call the NewStatement method, and
    // implement handle statement caching is UseCache=true - in this case,
    // the TSQLDBStatement.Reset method shall have been overriden to allow
    // binding and execution of the very same prepared statement
    // - this method should return nil in case of error, or a prepared statement
    // instance in case of success
    function NewStatementPrepared(const aSQL: RawUTF8;
      ExpectResults: Boolean): ISQLDBStatement; virtual; 
    /// begin a Transaction for this connection
    // - this default implementation will check and set TransactionCount
    procedure StartTransaction; virtual; 
    /// commit changes of a Transaction for this connection
    // - StartTransaction method must have been called before
    // - this default implementation will check and set TransactionCount
    procedure Commit; virtual;
    /// discard changes of a Transaction for this connection
    // - StartTransaction method must have been called before
    // - this default implementation will check and set TransactionCount
    procedure Rollback; virtual;








    /// number of nested StartTransaction calls
    // - equals 0 if no transaction is active
    property TransactionCount: integer read fTransactionCount;
    /// TRUE if StartTransaction has been called
    // - check if TransactionCount>0
    property InTransaction: boolean read GetInTransaction;
    /// the current Date and Time, as retrieved from the server
................................................................................
    finally
      Disconnect;
    end;
  except
    on E: Exception do
      SynDBLog.Add.Log(sllError,E);
  end;

end;

function TSQLDBConnection.GetInTransaction: boolean;
begin
  result := TransactionCount>0;
end;

................................................................................

procedure TSQLDBConnection.StartTransaction;
begin
  CheckConnection;
  inc(fTransactionCount);
end;



















































{ TSQLDBConnectionProperties }

constructor TSQLDBConnectionProperties.Create(const aServerName, aDatabaseName,
  aUserID, aPassWord: RawUTF8);
var aDBMS: TSQLDBDefinition;
begin
................................................................................

function TSQLDBConnectionProperties.Execute(const aSQL: RawUTF8;
  const Params: array of const
  {$ifndef LVCL}{$ifndef DELPHI5OROLDER}; RowsVariant: PVariant=nil{$endif}{$endif}): ISQLDBRows;
var Query: TSQLDBStatement;
begin
  Query := NewThreadSafeStatement;

  Query.Execute(aSQL,true,Params);
{$ifndef LVCL}
{$ifndef DELPHI5OROLDER}
  if RowsVariant<>nil then
    RowsVariant^ := Query.RowData;
{$endif}
{$endif}
  result := Query;






end;

function TSQLDBConnectionProperties.ExecuteNoResult(const aSQL: RawUTF8;
  const Params: array of const): integer;
begin
  with NewThreadSafeStatement do
  try






>
>







 







|


|








>
>
>
>
>
>
>
>







 







>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
|
|
|
|
|
|
|
|
>
>
>
>
>
>







154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
....
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
....
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
....
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
....
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
    to retrieve advanced information about database indexes (e.g. for indexes
    created after multiple columns)
  - added TSQLDBConnectionProperties.SQLTableName() method
  - added TSQLDBConnectionPropertiesThreadSafe.ForceOnlyOneSharedConnection
    property to by-pass internal thread-pool (e.g. for embedded engines)
  - added dFirebird kind of database in TSQLDBDefinition enumerate, and added
    associated SQL requests to retrieve metadata
  - added TSQLDBConnection.NewTableFromRows() method to dump a SQL statement
    result into a new table of any database (may be used for replication)
  - TSQLDBConnectionProperties.SQLSelectAll() now handles spaces in table names
  - TSQLDBStatement.GetParamValueAsText() will truncate to a given number of
    chars the returned text
  - added missing ColumnToVarData() method to ISQLDBRows interface
  - now trim any spaces when retrieving database schema text values
  - fixed ticket [4c68975022] about broken SQL statement when logging active

................................................................................
    // - this default implementation will call the NewStatement method, and
    // implement handle statement caching is UseCache=true - in this case,
    // the TSQLDBStatement.Reset method shall have been overriden to allow
    // binding and execution of the very same prepared statement
    // - this method should return nil in case of error, or a prepared statement
    // instance in case of success
    function NewStatementPrepared(const aSQL: RawUTF8;
      ExpectResults: Boolean): ISQLDBStatement; virtual;
    /// begin a Transaction for this connection
    // - this default implementation will check and set TransactionCount
    procedure StartTransaction; virtual;
    /// commit changes of a Transaction for this connection
    // - StartTransaction method must have been called before
    // - this default implementation will check and set TransactionCount
    procedure Commit; virtual;
    /// discard changes of a Transaction for this connection
    // - StartTransaction method must have been called before
    // - this default implementation will check and set TransactionCount
    procedure Rollback; virtual;

    /// direct export of a DB statement rows into a new table of this database
    // - the corresponding table will be created within the current connection,
    // if it does not exist
    // - INSERTs will be nested within a transaction if WithinTransaction is TRUE
    function NewTableFromRows(const TableName: RawUTF8;
      Rows: TSQLDBStatement; WithinTransaction: boolean): integer;

    /// number of nested StartTransaction calls
    // - equals 0 if no transaction is active
    property TransactionCount: integer read fTransactionCount;
    /// TRUE if StartTransaction has been called
    // - check if TransactionCount>0
    property InTransaction: boolean read GetInTransaction;
    /// the current Date and Time, as retrieved from the server
................................................................................
    finally
      Disconnect;
    end;
  except
    on E: Exception do
      SynDBLog.Add.Log(sllError,E);
  end;
  inherited;
end;

function TSQLDBConnection.GetInTransaction: boolean;
begin
  result := TransactionCount>0;
end;

................................................................................

procedure TSQLDBConnection.StartTransaction;
begin
  CheckConnection;
  inc(fTransactionCount);
end;

function TSQLDBConnection.NewTableFromRows(const TableName: RawUTF8;
  Rows: TSQLDBStatement; WithinTransaction: boolean): integer;
var Fields: TSQLDBColumnPropertyDynArray;
    aTableName, SQL: RawUTF8;
    Tables: TRawUTF8DynArray;
    Ins: TSQLDBStatement;
begin
  result := 0;
  if (self=nil) or (Rows=nil) or (Rows.ColumnCount=0) then
    exit;
  if (PosEx('.',TableName)=0) and (PosEx(' ',TableName)=0)  then
    aTablename := TableName else
    aTableName := '`'+TableName+'`';
  if WithinTransaction then
    StartTransaction; // MUCH faster within a transaction
  try
    Ins := nil;
    try
      while Rows.Step do begin
        // init when first row of data is available
        if Ins=nil then begin
          SQL := Rows.ColumnsToSQLInsert(aTableName,Fields);
          Properties.GetTableNames(Tables);
          if FindRawUTF8(Tables,TableName,false)<0 then
            with Properties do
              ExecuteNoResult(SQLCreate(aTableName,Fields),[]);
          Ins := NewStatement;
          Ins.Prepare(SQL,false);
        end;
        // write row data
        Ins.BindFromRows(Fields,Rows);
        Ins.ExecutePrepared;
        Ins.Reset;
        inc(result);
      end;
      if WithinTransaction then
        Commit;
    finally
      Ins.Free;
    end;
  except
    on Exception do begin
      if WithinTransaction then
        Rollback;
      raise;
    end;
  end;
end;


{ TSQLDBConnectionProperties }

constructor TSQLDBConnectionProperties.Create(const aServerName, aDatabaseName,
  aUserID, aPassWord: RawUTF8);
var aDBMS: TSQLDBDefinition;
begin
................................................................................

function TSQLDBConnectionProperties.Execute(const aSQL: RawUTF8;
  const Params: array of const
  {$ifndef LVCL}{$ifndef DELPHI5OROLDER}; RowsVariant: PVariant=nil{$endif}{$endif}): ISQLDBRows;
var Query: TSQLDBStatement;
begin
  Query := NewThreadSafeStatement;
  try
    Query.Execute(aSQL,true,Params);
    {$ifndef LVCL}
    {$ifndef DELPHI5OROLDER}
    if RowsVariant<>nil then
      RowsVariant^ := Query.RowData;
    {$endif}
    {$endif}
    result := Query;
  except
    on Exception do begin
      Query.Free;
      raise;
    end;
  end;
end;

function TSQLDBConnectionProperties.ExecuteNoResult(const aSQL: RawUTF8;
  const Params: array of const): integer;
begin
  with NewThreadSafeStatement do
  try

Changes to SynDBSQLite3.pas.

66
67
68
69
70
71
72


73
74
75
76
77
78
79
...
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
...
372
373
374
375
376
377
378
379

380
381
382
383
384
385
386
  - added TSQLDBSQLite3Connection.Synchronous property
  - code refactoring, especially about error handling and ODBC integration

  Version 1.18
  - statement cache refactoring: cache logic is now at SynDB unit level
  - fixed ticket [4c68975022] about broken SQL statement when logging active
  - added TSQLDBSQLite3ConnectionProperties.SQLTableName() overriden method



}

{$I Synopse.inc} // define HASINLINE USETYPEINFO CPU32 CPU64 OWNNORMTOUPPER

interface

................................................................................
     - will use WR.Expand to guess the expected output format
     - fast overriden implementation with no temporary variable
     - BLOB field value is saved as Base64, in the '"\uFFF0base64encodedbinary"
       format and contains true BLOB data }
    procedure ColumnsToJSON(WR: TJSONWriter); override;
  end;

/// direct export of a DB statement rows into a SQLite3 database
// - the corresponding table will be created within the specified DB connection
// - transaction will be created if WithinTransaction is set to TRUE
function RowsToSQLite3(Connection: TSQLDBSQLite3Connection; const TableName: RawUTF8;
  Rows: TSQLDBStatement; WithinTransaction: boolean=true): integer; overload;

/// direct export of a DB statement rows into a SQLite3 database
// - the corresponding table will be created within the specified DB file
function RowsToSQLite3(const Dest: TFileName; const TableName: RawUTF8;
  Rows: TSQLDBStatement; UseMormotCollations: boolean): integer; overload;


implementation

function RowsToSQLite3(Connection: TSQLDBSQLite3Connection; const TableName: RawUTF8;
  Rows: TSQLDBStatement; WithinTransaction: boolean): integer;
var Fields: TSQLDBColumnPropertyDynArray;
    aTableName, SQL: RawUTF8;
    Ins: TSQLDBStatement;
begin
  result := 0;
  if (Connection=nil) or (Rows=nil) or (Rows.ColumnCount=0) then
    exit;
  if (PosEx('.',TableName)=0) and (PosEx(' ',TableName)=0)  then
    aTablename := TableName else
    aTableName := '`'+TableName+'`';
  if WithinTransaction then
    Connection.StartTransaction; // MUCH faster within a transaction
  try
    Ins := nil;
    while Rows.Step do begin
      // init when first row of data is available
      if Ins=nil then begin
        SQL := Rows.ColumnsToSQLInsert(aTableName,Fields); // get layout
        Connection.DB.Execute(Connection.Properties.SQLCreate(aTableName,Fields)); // create table
        Ins := Connection.NewStatement;
        Ins.Prepare(SQL,false); // prepare INSERT
      end;
      // write row data
      Ins.BindFromRows(Fields,Rows);
      Ins.ExecutePrepared;
      Ins.Reset;
      inc(result);
    end;
    if WithinTransaction then
      Connection.Commit;
  except
    on Exception do begin
      if WithinTransaction then
        Connection.Rollback;
      raise;
    end;
  end;
end;

function RowsToSQLite3(const Dest: TFileName; const TableName: RawUTF8;
  Rows: TSQLDBStatement; UseMormotCollations: boolean): integer; overload;
var DB: TSQLDBSQLite3ConnectionProperties;
    Conn: TSQLDBSQLite3Connection;
begin
  result := 0;
................................................................................
    exit;
  // we do not call DeleteFile(Dest) since DB may be completed on purpose
  DB := TSQLDBSQLite3ConnectionProperties.Create(StringToUTF8(Dest),'','','');
  try
    DB.UseMormotCollations := UseMormotCollations;
    Conn := DB.MainConnection as TSQLDBSQLite3Connection;
    Conn.Connect;
    result := RowsToSQLite3(Conn,TableName,Rows,true);

  finally
    DB.Free;
  end;
end;

{ TSQLDBSQLite3ConnectionProperties }







>
>







 







<
<
<
<
<
<



|




<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|
>







66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
...
305
306
307
308
309
310
311






312
313
314
315
316
317
318
319








































320
321
322
323
324
325
326
...
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
  - added TSQLDBSQLite3Connection.Synchronous property
  - code refactoring, especially about error handling and ODBC integration

  Version 1.18
  - statement cache refactoring: cache logic is now at SynDB unit level
  - fixed ticket [4c68975022] about broken SQL statement when logging active
  - added TSQLDBSQLite3ConnectionProperties.SQLTableName() overriden method
  - overloaded function RowsToSQLite3() is now moved as generic
    TSQLDBConnection.NewTableFromRows() method

}

{$I Synopse.inc} // define HASINLINE USETYPEINFO CPU32 CPU64 OWNNORMTOUPPER

interface

................................................................................
     - will use WR.Expand to guess the expected output format
     - fast overriden implementation with no temporary variable
     - BLOB field value is saved as Base64, in the '"\uFFF0base64encodedbinary"
       format and contains true BLOB data }
    procedure ColumnsToJSON(WR: TJSONWriter); override;
  end;







/// direct export of a DB statement rows into a SQLite3 database
// - the corresponding table will be created within the specified DB file
function RowsToSQLite3(const Dest: TFileName; const TableName: RawUTF8;
  Rows: TSQLDBStatement; UseMormotCollations: boolean): integer; 


implementation










































function RowsToSQLite3(const Dest: TFileName; const TableName: RawUTF8;
  Rows: TSQLDBStatement; UseMormotCollations: boolean): integer; overload;
var DB: TSQLDBSQLite3ConnectionProperties;
    Conn: TSQLDBSQLite3Connection;
begin
  result := 0;
................................................................................
    exit;
  // we do not call DeleteFile(Dest) since DB may be completed on purpose
  DB := TSQLDBSQLite3ConnectionProperties.Create(StringToUTF8(Dest),'','','');
  try
    DB.UseMormotCollations := UseMormotCollations;
    Conn := DB.MainConnection as TSQLDBSQLite3Connection;
    Conn.Connect;
    result := Conn.NewTableFromRows(TableName,Rows,true);
    Conn.Disconnect;
  finally
    DB.Free;
  end;
end;

{ TSQLDBSQLite3ConnectionProperties }